Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cypress tests to test Basic example #193

Merged
merged 10 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
add cypress
  • Loading branch information
ankri committed Feb 16, 2021
commit f66ce8d173a82f90ce6bc35b886f1c82877f2b33
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ lib
.now
.vscode
.idea
cypress/videos/*
cypress/screenshots/*
3 changes: 3 additions & 0 deletions cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"baseUrl": "http://localhost:3002"
}
21 changes: 21 additions & 0 deletions cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}
252 changes: 252 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
declare global {
namespace Cypress {
interface Chainable {
getByTestId(
selector: string,
...additionalArguments: any[]
): Chainable<Element>;

getDropIndicator(
indicator: 'error' | 'success',
indicatorColors?: Record<'error' | 'success', string>
): Chainable<Element>;

dragAndDrop(
target: string,
options?: Partial<DropOptions>
): Chainable<Element>;

dragOver(
target: string,
options?: Partial<DropOptions>
): Chainable<Element>;

drop(): Chainable<Element>;
}
}
}

export interface DropOptions {
position: 'right' | 'left' | 'right' | 'above' | 'below' | 'inside';
}

/**
* Will select elements that have a data-cy=$test-id attribute
* Usage: `cy.getByTestId('test-id')`
* @example HTML: <div data-cy="test-id" /> Selector: cy.getByTestId('test-id')
*/
Cypress.Commands.add('getByTestId', (selector, ...args) => {
return cy.get(`[data-cy="${selector}"]`, ...args);
});

/**
* Get either the 'success' or 'error' drop indicator
* Use the indicatorColors parameter when you have overwritten the indicator colors
* @example cy.getDropIndicator('success')
*/
Cypress.Commands.add(
'getDropIndicator',
(
indicator: 'error' | 'success',
indicatorColors: Record<'error' | 'success', string> = {
error: 'red',
success: 'rgb(98, 196, 98)',
}
) => {
// in some cases rendering the indicator is slower than cypress, so we have to add a short wait
cy.wait(250);

return cy.get('div').filter((index, element) => {
return (
// the next.js render indicator shares some of the styles but additionally has an id
element.id === '' &&
element.classList.length === 0 &&
element.style.position === 'fixed' &&
element.style.zIndex === '99999' &&
element.style.backgroundColor === indicatorColors[indicator]
);
});
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move all the dnd-related commands (ie: DummyDataTransfer, getDropIndicator, drop etc) into a separate file; maybe dnd.ts and then just import that here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. In phase 2 I want to publish the actions in dnd.ts as a separate @craft/cypress package :)

);

class DummyDataTransfer {
data = {};

dropEffect = 'move';

effectAllowed = 'all';
files = [];
items = [];
types = [];

clearData(format) {
if (format) {
delete this.data[format];

const index = this.types.indexOf(format);
delete this.types[index];
delete this.data[index];
} else {
this.data = {};
}
}

setData(format, data) {
this.data[format] = data;
this.items.push(data);
this.types.push(format);
}

getData(format) {
if (format in this.data) {
return this.data[format];
}

return '';
}

setDragImage(img, xOffset, yOffset) {}
}

export const getCoordinatesFromPos = (
dom: HTMLElement,
position: DropOptions['position']
) => {
const { left, top, width, height } = dom.getBoundingClientRect();

switch (position) {
case 'right': {
return {
x: left + width,
y: top + height / 2,
};
}
case 'left': {
return {
x: left,
y: top + height / 2,
};
}
case 'above': {
return {
x: left + width / 2,
y: top,
};
}
case 'below': {
return {
x: left + width / 2,
y: top + height,
};
}
case 'inside': {
return {
x: left + 10,
y: top + 10,
};
}
default: {
return {
x: 0,
y: 0,
};
}
}
};

/**
* Use this command to drag and drop a source component onto/into a target component
* If you want to drop into a component use { position: 'inside' } as options parameter
*
* @example cy.get('source').dragAndDrop('target')
* @example cy.get('source').dragAndDrop('target-container', { position: 'inside' })
* @example cy.get('source').dragAndDrop('target', { position: 'bottom' })
*/
Cypress.Commands.add(
'dragAndDrop',
{
prevSubject: true,
},
(subject, target, opts: Partial<DropOptions> = { position: 'right' }) => {
cy.get(subject).dragOver(target, opts);
cy.get(subject).drop();
}
);

/**
* Use this command if you want to drag a source component over a target component.
* You can use the same positions as in the `dragAndDrop` command.
* This command is especially useful when you want to verify that the correct drop indicator is rendered.
* You can use the `drop` command afterwards on the source component to drop the component.
* If you just want to drag and drop a component, use the `dragAndDrop` command instead.
*
* @example ```
* cy.get('source').dragOver('target');
* cy.getDropIndicator('success').should('exist');
* cy.get('source').drop();
* ```
*/
Cypress.Commands.add(
'dragOver',
{
prevSubject: true,
},
(subject, target, opts: Partial<DropOptions> = { position: 'right' }) => {
const { position } = {
...opts,
};

cy.get(target).as('target');
cy.get('@target').then(($target) => {
const dom = $target[0];
const { x, y } = getCoordinatesFromPos(dom, position);

const dataTransfer = new DummyDataTransfer();
cy.get('@target').parent().as('parent');

cy.get(subject)
.trigger('mousedown', { force: true })
.trigger('dragstart', {
force: true,
dataTransfer,
});

if (position === 'inside') {
cy.get('@target').trigger('dragenter', {
clientX: Math.floor(x),
clientY: Math.floor(y),
dataTransfer,
force: true,
});
} else {
cy.get('@parent').trigger('dragenter', {
clientX: Math.floor(x),
clientY: Math.floor(y),
dataTransfer,
force: true,
});
}
});
}
);

/**
* Use this command to drop a component that has previously been dragged with the command `dragOver`
*
* If you just want to drag and drop a component use the `dragAndDrop` command instead.
*
* @example ```
* cy.get('source').dragOver('target');
* cy.get('source').drop();
* ```

*/
Cypress.Commands.add(
'drop',
{
prevSubject: true,
},
(subject) => {
cy.get(subject).trigger('dragend', { force: true });
}
);
20 changes: 20 additions & 0 deletions cypress/support/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands';

// Alternatively you can use CommonJS syntax:
// require('./commands')
10 changes: 10 additions & 0 deletions cypress/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress"]
},
"include": [
"**/*.ts"
]
}
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"release": "run-s lint clean build test release:npm",
"release:npm": "./scripts/release.sh",
"publish:beta": "lerna version prerelease --preid beta --force-publish --yes",
"cypress:run": "cypress run",
"cypress:open": "cypress open",
"test": "jest",
"test:watch": "cross-env NODE_ENV=test jest --watchAll",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
Expand All @@ -40,6 +42,7 @@
"@typescript-eslint/parser": "^2.14.0",
"babel-eslint": "^10.0.3",
"cross-env": "^6.0.3",
"cypress": "^6.4.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"eslint": "^6.8.0",
Expand Down
Loading