chore: improve number widget tests (#2223)

This commit is contained in:
Luis Correia 2019-03-25 18:17:26 +00:00 committed by Shawn Erquhart
parent 2d59602660
commit 19824fd1dd
4 changed files with 258 additions and 154 deletions

View File

@ -1,117 +1,164 @@
import '../utils/dismiss-local-backup';
describe('Editorial Workflow', () => { describe('Editorial Workflow', () => {
const workflowStatus = { draft: 'Drafts', review: 'In Review', ready: 'Ready' } const workflowStatus = { draft: 'Drafts', review: 'In Review', ready: 'Ready' };
const editorStatus = { draft: 'Draft', review: 'In review', ready: 'Ready' } const editorStatus = { draft: 'Draft', review: 'In review', ready: 'Ready' };
const entry1 = { title: 'first title', body: 'first body' } const entry1 = { title: 'first title', body: 'first body' };
const entry2 = { title: 'second title', body: 'second body' } const entry2 = { title: 'second title', body: 'second body' };
const entry3 = { title: 'third title', body: 'third body' } const entry3 = { title: 'third title', body: 'third body' };
const setting1 = { limit: 10, author: 'John Doe' } const setting1 = { limit: 10, author: 'John Doe' };
const setting2 = { name: 'Andrew Wommack', description: 'A Gospel Teacher' } const setting2 = { name: 'Andrew Wommack', description: 'A Gospel Teacher' };
const notifications = { const notifications = {
saved: 'Entry saved', saved: 'Entry saved',
published: 'Entry published', published: 'Entry published',
updated: 'Entry status updated', updated: 'Entry status updated',
deletedUnpublished: 'Unpublished changes deleted', deletedUnpublished: 'Unpublished changes deleted',
error: { missingField: 'Oops, you\'ve missed a required field. Please complete before saving.' } error: {
} missingField: "Oops, you've missed a required field. Please complete before saving.",
},
validation: {
range: {
fieldLabel: 'Number of posts on frontpage',
message: 'Number of posts on frontpage must be between 1 and 10.',
},
},
};
describe('Test Backend', () => { describe('Test Backend', () => {
function login() { function login() {
cy.viewport(1200, 1200) cy.viewport(1200, 1200);
cy.visit('/') cy.visit('/');
cy.contains('button', 'Login').click() cy.contains('button', 'Login').click();
} }
function createPost({ title, body }) { function createPost({ title, body }) {
cy.contains('a', 'New Post').click() cy.contains('a', 'New Post').click();
cy.get('input').first().type(title) cy.get('input')
cy.get('[data-slate-editor]').click().type(body) .first()
cy.get('input').first().click() .type(title);
cy.contains('button', 'Save').click() cy.get('[data-slate-editor]')
assertNotification(notifications.saved) .click()
.type(body);
cy.get('input')
.first()
.click();
cy.contains('button', 'Save').click();
assertNotification(notifications.saved);
} }
function validateObjectFields({ limit, author }) { function validateObjectFields({ limit, author }) {
cy.get('a[href^="#/collections/settings"]').click() cy.get('a[href^="#/collections/settings"]').click();
cy.get('a[href^="#/collections/settings/entries/general"]').click() cy.get('a[href^="#/collections/settings/entries/general"]').click();
cy.get('input[type=number]').type(limit); cy.get('input[type=number]').type(limit);
cy.contains('button', 'Save').click() cy.contains('button', 'Save').click();
assertNotification(notifications.error.missingField) assertNotification(notifications.error.missingField);
cy.contains('label', 'Default Author').click() cy.contains('label', 'Default Author').click();
cy.focused().type(author) cy.focused().type(author);
cy.contains('button', 'Save').click() cy.contains('button', 'Save').click();
assertNotification(notifications.saved) assertNotification(notifications.saved);
}
function validateNestedObjectFields({ limit, author }) {
cy.get('a[href^="#/collections/settings"]').click();
cy.get('a[href^="#/collections/settings/entries/general"]').click();
cy.contains('label', 'Default Author').click();
cy.focused().type(author);
cy.contains('button', 'Save').click();
assertNotification(notifications.error.missingField);
cy.get('input[type=number]').type(limit + 1);
cy.contains('button', 'Save').click();
assertFieldValidationError(notifications.validation.range);
cy.get('input[type=number]')
.clear()
.type(-1);
cy.contains('button', 'Save').click();
assertFieldValidationError(notifications.validation.range);
cy.get('input[type=number]')
.clear()
.type(limit);
cy.contains('button', 'Save').click();
assertNotification(notifications.saved);
} }
function validateListFields({ name, description }) { function validateListFields({ name, description }) {
cy.get('a[href^="#/collections/settings"]').click() cy.get('a[href^="#/collections/settings"]').click();
cy.get('a[href^="#/collections/settings/entries/authors"]').click() cy.get('a[href^="#/collections/settings/entries/authors"]').click();
cy.contains('button', 'Add').click() cy.contains('button', 'Add').click();
cy.contains('button', 'Save').click() cy.contains('button', 'Save').click();
assertNotification(notifications.error.missingField) assertNotification(notifications.error.missingField);
cy.get('input').eq(2).type(name) cy.get('input')
cy.get('[data-slate-editor]').eq(2).type(description) .eq(2)
cy.contains('button', 'Save').click() .type(name);
cy.get('[data-slate-editor]')
.eq(2)
.type(description);
cy.contains('button', 'Save').click();
} }
function exitEditor() { function exitEditor() {
cy.contains('a[href^="#/collections/"]', 'Writing in').click() cy.contains('a[href^="#/collections/"]', 'Writing in').click();
} }
function deleteEntryInEditor() { function deleteEntryInEditor() {
cy.contains('button', 'Delete').click() cy.contains('button', 'Delete').click();
assertNotification(notifications.deletedUnpublished) assertNotification(notifications.deletedUnpublished);
} }
function assertEntryDeleted(entry) { function assertEntryDeleted(entry) {
if (Array.isArray(entry)) { if (Array.isArray(entry)) {
const titles = entry.map(e => e.title) const titles = entry.map(e => e.title);
cy.get('a h2').each((el, idx) => { cy.get('a h2').each((el, idx) => {
expect(titles).not.to.include(el.text()) expect(titles).not.to.include(el.text());
}) });
} else { } else {
cy.get('a h2').each((el, idx) => { cy.get('a h2').each((el, idx) => {
expect(entry.title).not.to.equal(el.text()) expect(entry.title).not.to.equal(el.text());
}) });
} }
} }
function createAndDeletePost(entry) { function createAndDeletePost(entry) {
createPost(entry) createPost(entry);
deleteEntryInEditor() deleteEntryInEditor();
} }
function createPostAndExit(entry) { function createPostAndExit(entry) {
createPost(entry) createPost(entry);
exitEditor() exitEditor();
} }
function createPublishedPost(entry) { function createPublishedPost(entry) {
createPost(entry) createPost(entry);
updateWorkflowStatusInEditor(editorStatus.ready) updateWorkflowStatusInEditor(editorStatus.ready);
publishInEditor() publishInEditor();
} }
function createPublishedPostAndExit(entry) { function createPublishedPostAndExit(entry) {
createPublishedPost(entry) createPublishedPost(entry);
exitEditor() exitEditor();
} }
function validateObjectFieldsAndExit(setting) { function validateObjectFieldsAndExit(setting) {
validateObjectFields(setting) validateObjectFields(setting);
exitEditor() exitEditor();
}
function validateNestedObjectFieldsAndExit(setting) {
validateNestedObjectFields(setting);
exitEditor();
} }
function validateListFieldsAndExit(setting) { function validateListFieldsAndExit(setting) {
validateListFields(setting) validateListFields(setting);
exitEditor() exitEditor();
} }
function goToWorkflow() { function goToWorkflow() {
cy.contains('a', 'Workflow').click() cy.contains('a', 'Workflow').click();
} }
function goToCollections() { function goToCollections() {
cy.contains('a', 'Content').click() cy.contains('a', 'Content').click();
} }
function updateWorkflowStatus({ title }, fromColumnHeading, toColumnHeading) { function updateWorkflowStatus({ title }, fromColumnHeading, toColumnHeading) {
@ -121,157 +168,182 @@ describe('Editorial Workflow', () => {
.trigger('dragstart', { .trigger('dragstart', {
dataTransfer: {}, dataTransfer: {},
force: true, force: true,
}) });
cy.contains('h2', toColumnHeading) cy.contains('h2', toColumnHeading)
.parent() .parent()
.trigger('drop', { .trigger('drop', {
dataTransfer: {}, dataTransfer: {},
force: true, force: true,
}) });
assertNotification(notifications.updated) assertNotification(notifications.updated);
} }
function assertWorkflowStatus({ title }, status) { function assertWorkflowStatus({ title }, status) {
cy.contains('h2', status) cy.contains('h2', status)
.parent() .parent()
.contains('a', title) .contains('a', title);
} }
function updateWorkflowStatusInEditor(newStatus) { function updateWorkflowStatusInEditor(newStatus) {
cy.contains('[role="button"]', 'Set status').as('setStatusButton') cy.contains('[role="button"]', 'Set status').as('setStatusButton');
cy.get('@setStatusButton').parent().within(() => { cy.get('@setStatusButton')
cy.get('@setStatusButton').click() .parent()
cy.contains('[role="menuitem"] span', newStatus).click() .within(() => {
}) cy.get('@setStatusButton').click();
assertNotification(notifications.updated) cy.contains('[role="menuitem"] span', newStatus).click();
});
assertNotification(notifications.updated);
} }
function assertWorkflowStatusInEditor(status) { function assertWorkflowStatusInEditor(status) {
cy.contains('[role="button"]', 'Set status').as('setStatusButton') cy.contains('[role="button"]', 'Set status').as('setStatusButton');
cy.get('@setStatusButton').parent().within(() => { cy.get('@setStatusButton')
cy.get('@setStatusButton').click() .parent()
cy.contains('[role="menuitem"] span', status).parent().within(() => { .within(() => {
cy.get('svg') cy.get('@setStatusButton').click();
}) cy.contains('[role="menuitem"] span', status)
cy.get('@setStatusButton').click() .parent()
}) .within(() => {
cy.get('svg');
});
cy.get('@setStatusButton').click();
});
} }
function publishWorkflowEntry({ title }) { function publishWorkflowEntry({ title }) {
cy.contains('h2', workflowStatus.ready).parent().within(() => { cy.contains('h2', workflowStatus.ready)
cy.contains('a', title).parent().within(() => { .parent()
cy.contains('button', 'Publish new entry').click({ force: true }) .within(() => {
}) cy.contains('a', title)
}) .parent()
assertNotification(notifications.published) .within(() => {
cy.contains('button', 'Publish new entry').click({ force: true });
});
});
assertNotification(notifications.published);
} }
function assertPublishedEntry(entry) { function assertPublishedEntry(entry) {
if (Array.isArray(entry)) { if (Array.isArray(entry)) {
const entries = entry.reverse() const entries = entry.reverse();
cy.get('a h2').then(els => { cy.get('a h2').then(els => {
cy.wrap(els.slice(0, entries.length)).each((el, idx) => { cy.wrap(els.slice(0, entries.length)).each((el, idx) => {
cy.wrap(el).contains(entries[idx].title) cy.wrap(el).contains(entries[idx].title);
}) });
}) });
} else { } else {
cy.get('a h2').first().contains(entry.title) cy.get('a h2')
.first()
.contains(entry.title);
} }
} }
function assertNotification(message) { function assertNotification(message) {
if (Array.isArray(message)) { if (Array.isArray(message)) {
const messages = message.reverse() const messages = message.reverse();
cy.get('.notif__container div') cy.get('.notif__container div')
.should('have.length.of', messages.length, ) .should('have.length.of', messages.length)
.each((el, idx) => { .each((el, idx) => {
cy.wrap(el).contains(messages[idx]).invoke('hide') cy.wrap(el)
}) .contains(messages[idx])
.invoke('hide');
});
} else { } else {
cy.get('.notif__container').within(() => { cy.get('.notif__container').within(() => {
cy.contains(message).invoke('hide') cy.contains(message).invoke('hide');
}) });
} }
} }
function assertFieldValidationError({ message, fieldLabel }) {
cy.contains('label', fieldLabel)
.siblings('ul[class*=ControlErrorsList]')
.contains(message);
}
function assertOnCollectionsPage() { function assertOnCollectionsPage() {
cy.url().should('contain', '/#/collections/posts') cy.url().should('contain', '/#/collections/posts');
cy.contains('h2', 'Collections') cy.contains('h2', 'Collections');
} }
it('successfully loads', () => { it('successfully loads', () => {
login() login();
}) });
it('can create an entry', () => { it('can create an entry', () => {
login() login();
createPostAndExit(entry1) createPostAndExit(entry1);
}) });
it('can validate object fields', () => { it('can validate object fields', () => {
login() login();
validateObjectFieldsAndExit(setting1) validateObjectFieldsAndExit(setting1);
}) });
it('can validate fields nested in an object field', () => {
login();
validateNestedObjectFieldsAndExit(setting1);
});
it('can validate list fields', () => { it('can validate list fields', () => {
login() login();
validateListFieldsAndExit(setting2) validateListFieldsAndExit(setting2);
}) });
it('can publish an editorial workflow entry', () => { it('can publish an editorial workflow entry', () => {
login() login();
createPostAndExit(entry1) createPostAndExit(entry1);
goToWorkflow() goToWorkflow();
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready) updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
publishWorkflowEntry(entry1) publishWorkflowEntry(entry1);
}) });
it('can change workflow status', () => { it('can change workflow status', () => {
login() login();
createPostAndExit(entry1) createPostAndExit(entry1);
goToWorkflow() goToWorkflow();
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.review) updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.review);
updateWorkflowStatus(entry1, workflowStatus.review, workflowStatus.ready) updateWorkflowStatus(entry1, workflowStatus.review, workflowStatus.ready);
updateWorkflowStatus(entry1, workflowStatus.ready, workflowStatus.review) updateWorkflowStatus(entry1, workflowStatus.ready, workflowStatus.review);
updateWorkflowStatus(entry1, workflowStatus.review, workflowStatus.draft) updateWorkflowStatus(entry1, workflowStatus.review, workflowStatus.draft);
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready) updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
}) });
it('can change status on and publish multiple entries', () => { it('can change status on and publish multiple entries', () => {
login() login();
createPostAndExit(entry1) createPostAndExit(entry1);
createPostAndExit(entry2) createPostAndExit(entry2);
createPostAndExit(entry3) createPostAndExit(entry3);
goToWorkflow() goToWorkflow();
updateWorkflowStatus(entry3, workflowStatus.draft, workflowStatus.ready) updateWorkflowStatus(entry3, workflowStatus.draft, workflowStatus.ready);
updateWorkflowStatus(entry2, workflowStatus.draft, workflowStatus.ready) updateWorkflowStatus(entry2, workflowStatus.draft, workflowStatus.ready);
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready) updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
publishWorkflowEntry(entry3) publishWorkflowEntry(entry3);
publishWorkflowEntry(entry2) publishWorkflowEntry(entry2);
publishWorkflowEntry(entry1) publishWorkflowEntry(entry1);
goToCollections() goToCollections();
assertPublishedEntry([entry3, entry2, entry1]) assertPublishedEntry([entry3, entry2, entry1]);
}) });
it('can delete an entry', () => { it('can delete an entry', () => {
login() login();
createPost(entry1) createPost(entry1);
deleteEntryInEditor() deleteEntryInEditor();
assertOnCollectionsPage() assertOnCollectionsPage();
assertEntryDeleted(entry1) assertEntryDeleted(entry1);
}) });
it('can update workflow status from within the editor', () => { it('can update workflow status from within the editor', () => {
login() login();
createPost(entry1) createPost(entry1);
assertWorkflowStatusInEditor(editorStatus.draft) assertWorkflowStatusInEditor(editorStatus.draft);
updateWorkflowStatusInEditor(editorStatus.review) updateWorkflowStatusInEditor(editorStatus.review);
assertWorkflowStatusInEditor(editorStatus.review) assertWorkflowStatusInEditor(editorStatus.review);
updateWorkflowStatusInEditor(editorStatus.ready) updateWorkflowStatusInEditor(editorStatus.ready);
assertWorkflowStatusInEditor(editorStatus.ready) assertWorkflowStatusInEditor(editorStatus.ready);
exitEditor() exitEditor();
goToWorkflow() goToWorkflow();
assertWorkflowStatus(entry1, workflowStatus.ready) assertWorkflowStatus(entry1, workflowStatus.ready);
}) });
}) });
}) });

9
cypress/utils/README.md Normal file
View File

@ -0,0 +1,9 @@
## Utilities for integration tests
Utils in this dir must be explicitly included in each spec file, e.g.:
```
import '../utils/dismiss-local-backup';
```
For routines to be executed on all tests, please use the `cypress/plugins.index.js` file instead: https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Plugin-files

View File

@ -0,0 +1,17 @@
import { getPhrases } from 'Constants/defaultPhrases';
// Prevents unsaved changes in dev local storage from being used
Cypress.on('window:confirm', message => {
const {
editor: {
editor: { confirmLoadBackup },
},
} = getPhrases();
switch (message) {
case confirmLoadBackup:
return false;
default:
return true;
}
});

View File

@ -60,7 +60,13 @@ collections: # A list of collections the CMS should be able to edit
name: posts name: posts
widget: 'object' widget: 'object'
fields: fields:
- { label: 'Number of posts on frontpage', name: front_limit, widget: number } - {
label: 'Number of posts on frontpage',
name: front_limit,
widget: number,
min: 1,
max: 10,
}
- { label: 'Default Author', name: author, widget: string } - { label: 'Default Author', name: author, widget: string }
- { label: 'Default Thumbnail', name: thumb, widget: image, class: 'thumb', required: false } - { label: 'Default Thumbnail', name: thumb, widget: image, class: 'thumb', required: false }