Skip to content

Commit

Permalink
test(cypress): Add integration tests (prevwong#193)
Browse files Browse the repository at this point in the history
* add cypress

* add tests

* make basic example testable with testids

* update the cypress version

* add .DS_Store to root gitignore

* added new line

* moved drag and drop commands to separate file

* fix flaky test

Co-authored-by: Prev Wong <prevwong@gmail.com>
  • Loading branch information
ankri and prevwong authored Feb 18, 2021
1 parent 195ea30 commit 0c4f4ed
Show file tree
Hide file tree
Showing 21 changed files with 1,837 additions and 158 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ lib
.now
.vscode
.idea

.DS_Store
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"
}
146 changes: 146 additions & 0 deletions cypress/integration/frame.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
describe('Frame', () => {
beforeEach(() => {
cy.visit('/');
});

it('should be possible to move the elements around in the frame', () => {
// at the start the root should have 4 elements
cy.getByTestId('root-container')
.as('root-container')
.children()
.should('have.length', 4);

// we want to drop the "Click me" Button inside the CardBottom
// before that the CardBottom should only have one button
cy.getByTestId('card-bottom')
.as('card-bottom')
.children()
.should('have.length', 1);
// we verify that the button is draggable and drop it inside the CardBottom
cy.getByTestId('frame-button')
.should('have.attr', 'draggable', 'true')
.dragAndDrop('@card-bottom', {
position: 'inside',
});
// CardBottom now should have 2 elements
cy.get('@card-bottom').children().should('have.length', 2);

// we want to drop the "Hi world!" Text as second element in the CardTop
// before that we verify that CardTop has two children
cy.getByTestId('card-top')
.as('card-top')
.children()
.should('have.length', 2);

cy.getByTestId('card-top-text-1').as('card-top-text-1');
cy.getByTestId('frame-text')
.should('have.attr', 'draggable', 'true')
.dragAndDrop('@card-top-text-1', {
position: 'below',
});
cy.get('@card-top').children().should('have.length', 3);

// the root should now only have 2 children (the Card and the Container)
cy.get('@root-container').children().should('have.length', 2);

// we now want to drop the Card inside the Container
cy.getByTestId('frame-container')
.as('frame-container')
.children()
.should('have.length', 1);
cy.getByTestId('frame-card').dragAndDrop('@frame-container', {
position: 'inside',
});
cy.get('@frame-container').children().should('have.length', 2);
cy.get('@root-container').children().should('have.length', 1);
});

it('should not be possible to drag anything inside the frame when the editor is disabled', () => {
// we disable the editor
cy.contains('Enable').click();

// no element should be draggable
cy.getByTestId('frame-card').should('have.attr', 'draggable', 'false');
cy.getByTestId('card-top-text-1').should('have.attr', 'draggable', 'false');
cy.getByTestId('card-top-text-2').should('have.attr', 'draggable', 'false');
cy.getByTestId('card-bottom-button').should(
'have.attr',
'draggable',
'false'
);
cy.getByTestId('frame-button').should('have.attr', 'draggable', 'false');
cy.getByTestId('frame-text').should('have.attr', 'draggable', 'false');
cy.getByTestId('frame-container').should('have.attr', 'draggable', 'false');
cy.getByTestId('frame-container-text').should(
'have.attr',
'draggable',
'false'
);
});

it('should be possible to delete every element', () => {
// to keep it DRY we use this simple helper
const deleteByTestId = (testId: string) => {
cy.getByTestId(testId).click();
cy.contains('Delete').click();
};

// these calls all follow the same schema:
// 1. verify that the container has a certain number of children before we delete
// 2. select the element we want to delete
// 3. delete the element
// 4. verify that the container now has one less element

cy.getByTestId('root-container')
.as('root-container')
.children()
.should('have.length', 4);

cy.getByTestId('card-top').children().should('have.length', 2);
deleteByTestId('card-top-text-1');
deleteByTestId('card-top-text-2');
cy.getByTestId('card-top').children().should('have.length', 0);

cy.getByTestId('card-bottom').children().should('have.length', 1);
deleteByTestId('card-bottom-button');
cy.getByTestId('card-bottom').children().should('have.length', 0);

deleteByTestId('frame-card');
cy.get('@root-container').children().should('have.length', 3);

deleteByTestId('frame-button');
cy.get('@root-container').children().should('have.length', 2);

deleteByTestId('frame-text');
cy.get('@root-container').children().should('have.length', 1);

cy.getByTestId('frame-container').children().should('have.length', 1);
deleteByTestId('frame-container-text');
cy.getByTestId('frame-container').children().should('have.length', 0);

deleteByTestId('frame-container');
cy.get('@root-container').children().should('have.length', 0);
});

it('should not be possible to delete an element when the editor is disabled', () => {
// first we disable the editor
cy.contains('Enable').click();

// then we check that the SettingsPanel does not open for any of the elements
[
'card-top-text-1',
'card-top-text-2',
'card-bottom-button',
'card-top',
'card-bottom',
'frame-card',
'frame-button',
'frame-text',
'frame-container-text',
'frame-container',
].forEach((testId) => {
cy.getByTestId(testId).click();
cy.contains('Delete').should('not.exist');
});
});
});
206 changes: 206 additions & 0 deletions cypress/integration/history.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
describe('Test History', () => {
beforeEach(() => {
cy.visit('/');

cy.contains('Undo').as('undo-button');
cy.contains('Redo').as('redo-button');

// before any changes have been made, both undo and redo should be disabled
cy.get('@undo-button').should('be.disabled');
cy.get('@redo-button').should('be.disabled');

// since we need the root-container in (almost) every test we load it here
cy.getByTestId('root-container')
.as('root')
.children()
.should('have.length', 4);
});

it('should be possible to undo and redo changes to props', () => {
// first we want to test if we can undo changes made to props
const originalText = 'Hi world!';
cy.getByTestId('frame-text')
.as('frame-text')
.should('have.text', originalText);

const newText = 'Hello world!';
// The Text component uses a ContentEditable inside, so we need to click the <p> to be able to enter text
cy.get('@frame-text').find('p').click().type(`{selectAll}${newText}`);
cy.get('@frame-text').should('have.text', newText);

cy.get('@redo-button').should('be.disabled');
cy.get('@undo-button').should('not.be.disabled').click();

cy.get('@frame-text').should('have.text', originalText);

cy.get('@undo-button').should('be.disabled');
cy.get('@redo-button').should('not.be.disabled').click();
cy.get('@frame-text').should('have.text', newText);
});

it('should be possible to undo and redo changes made via drag and drop', () => {
cy.getByTestId('root-container')
.as('root')
.children()
.should('have.length', 4);

// The first plan is to drag every component into the frame-container component
cy.getByTestId('frame-container')
.as('container')
.children()
.should('have.length', 1);

// TODO currently I do not know why but without the wait. test gets flaky
// and fails randomly because cypress is not able to drag the card into the container
cy.wait(100);
cy.getByTestId('frame-card')
.as('card')
.dragAndDrop('@container', { position: 'inside' });
cy.getByTestId('frame-button')
.as('button')
.dragAndDrop('@container', { position: 'inside' });

cy.getByTestId('frame-text')
.as('text')
.dragAndDrop('@container', { position: 'inside' });

// afterwards all the elements should be inside the Container
cy.get('@root').children().should('have.length', 1);
cy.get('@container').children().should('have.length', 4);

cy.get('@redo-button').should('be.disabled');
cy.get('@undo-button').should('not.be.disabled').click();

// we now want to undo all the changes and check along the way, that undoing works as expected
cy.get('@redo-button').should('not.be.disabled');

cy.get('@root').children().should('have.length', 2);
cy.get('@container').children().should('have.length', 3);

cy.get('@undo-button').click();
cy.get('@root').children().should('have.length', 3);
cy.get('@container').children().should('have.length', 2);

cy.get('@undo-button').click();
cy.get('@root').children().should('have.length', 4);
cy.get('@container').children().should('have.length', 1);

cy.get('@undo-button').should('be.disabled');
cy.get('@redo-button').should('not.be.disabled');

// now we want to redo 2 out of 3 times ...
cy.get('@redo-button').click();
cy.get('@redo-button').click();
cy.get('@redo-button').should('not.be.disabled');

cy.getByTestId('card-top-text-1').as('card-top-text-1');
cy.get('@text').dragAndDrop('@card-top-text-1', {
position: 'below',
});

// ... so we can verify that the remaining redo gets overwritten correctly
cy.get('@redo-button').should('be.disabled');

// now we undo 3 times to get back to the initial state
cy.get('@undo-button').click();
cy.get('@undo-button').click();
cy.get('@undo-button').click();

cy.get('@root').children().should('have.length', 4);
});

it('should be possible to undo and redo deleted elements', () => {
// in this test we remove every element
['frame-card', 'frame-button', 'frame-text', 'frame-container'].forEach(
(testId) => {
// we click on the left side to not accidentally select a component inside Card or Container
cy.getByTestId(testId).click('left');
cy.contains('Delete').click();
}
);

cy.get('@undo-button').should('not.be.disabled');
cy.get('@root').children().should('have.length', 0);

// we undo the deletions
cy.get('@undo-button').click();
cy.get('@undo-button').click();
cy.get('@undo-button').click();
cy.get('@undo-button').click();

cy.get('@root').children().should('have.length', 4);

cy.get('@undo-button').should('be.disabled');
cy.get('@redo-button').should('not.be.disabled');

// and redo the deletions
cy.get('@redo-button').click();
cy.get('@redo-button').click();
cy.get('@redo-button').click();
cy.get('@redo-button').click();

cy.get('@root').children().should('have.length', 0);
});

it('should be possible to drag elements from the toolbox and undo those changes', () => {
// we drag every component from the toolbox onto the root
[
'toolbox-button',
'toolbox-text',
'toolbox-container',
'toolbox-card',
].forEach((testId) => {
cy.getByTestId(testId).dragAndDrop('@root', {
position: 'inside',
});
});

// the root now should have 4 more elements
cy.get('@root').children().should('have.length', 8);

// and the undo button should not be disabled
cy.get('@undo-button').should('not.be.disabled');
cy.get('@redo-button').should('be.disabled');

// clicking the undo button 4 times should reset the changes
cy.get('@undo-button').click();
cy.get('@undo-button').click();
cy.get('@undo-button').click();
cy.get('@undo-button').click();

cy.get('@undo-button').should('be.disabled');
cy.get('@redo-button').should('not.be.disabled');

cy.get('@root').children().should('have.length', 4);

// clicking the redo button 4 times should result in 8 elements under root again
cy.get('@redo-button').click();
cy.get('@redo-button').click();
cy.get('@redo-button').click();
cy.get('@redo-button').click();

cy.get('@root').children().should('have.length', 8);
});

it('should not be possible to undo/redo changes when the editor is disabled', () => {
// we delete both the button an the text from the frame
cy.getByTestId('frame-button').click();
cy.contains('Delete').click();

cy.getByTestId('frame-text').click();
cy.contains('Delete').click();

// we undo the deletion of the Text element to have a redo action available
cy.get('@undo-button').click();

cy.get('@undo-button').should('not.be.disabled');
cy.get('@redo-button').should('not.be.disabled');

// we disable the editor
cy.contains('Enable').click();

// undo / redo should now be impossible
cy.get('@undo-button').should('be.disabled');
cy.get('@redo-button').should('be.disabled');
});
});
Loading

0 comments on commit 0c4f4ed

Please sign in to comment.