forked from prevwong/craft.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(cypress): Add integration tests (prevwong#193)
* 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
Showing
21 changed files
with
1,837 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,7 @@ lib | |
.now | ||
.vscode | ||
.idea | ||
|
||
.DS_Store | ||
cypress/videos/* | ||
cypress/screenshots/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"baseUrl": "http://localhost:3002" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | ||
}); | ||
}); |
Oops, something went wrong.