chore: improve number widget tests (#2223)
This commit is contained in:
parent
2d59602660
commit
19824fd1dd
@ -1,117 +1,164 @@
|
||||
import '../utils/dismiss-local-backup';
|
||||
|
||||
describe('Editorial Workflow', () => {
|
||||
const workflowStatus = { draft: 'Drafts', review: 'In Review', ready: 'Ready' }
|
||||
const editorStatus = { draft: 'Draft', review: 'In review', ready: 'Ready' }
|
||||
const entry1 = { title: 'first title', body: 'first body' }
|
||||
const entry2 = { title: 'second title', body: 'second body' }
|
||||
const entry3 = { title: 'third title', body: 'third body' }
|
||||
const setting1 = { limit: 10, author: 'John Doe' }
|
||||
const setting2 = { name: 'Andrew Wommack', description: 'A Gospel Teacher' }
|
||||
const workflowStatus = { draft: 'Drafts', review: 'In Review', ready: 'Ready' };
|
||||
const editorStatus = { draft: 'Draft', review: 'In review', ready: 'Ready' };
|
||||
const entry1 = { title: 'first title', body: 'first body' };
|
||||
const entry2 = { title: 'second title', body: 'second body' };
|
||||
const entry3 = { title: 'third title', body: 'third body' };
|
||||
const setting1 = { limit: 10, author: 'John Doe' };
|
||||
const setting2 = { name: 'Andrew Wommack', description: 'A Gospel Teacher' };
|
||||
const notifications = {
|
||||
saved: 'Entry saved',
|
||||
published: 'Entry published',
|
||||
updated: 'Entry status updated',
|
||||
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', () => {
|
||||
function login() {
|
||||
cy.viewport(1200, 1200)
|
||||
cy.visit('/')
|
||||
cy.contains('button', 'Login').click()
|
||||
cy.viewport(1200, 1200);
|
||||
cy.visit('/');
|
||||
cy.contains('button', 'Login').click();
|
||||
}
|
||||
|
||||
function createPost({ title, body }) {
|
||||
cy.contains('a', 'New Post').click()
|
||||
cy.get('input').first().type(title)
|
||||
cy.get('[data-slate-editor]').click().type(body)
|
||||
cy.get('input').first().click()
|
||||
cy.contains('button', 'Save').click()
|
||||
assertNotification(notifications.saved)
|
||||
cy.contains('a', 'New Post').click();
|
||||
cy.get('input')
|
||||
.first()
|
||||
.type(title);
|
||||
cy.get('[data-slate-editor]')
|
||||
.click()
|
||||
.type(body);
|
||||
cy.get('input')
|
||||
.first()
|
||||
.click();
|
||||
cy.contains('button', 'Save').click();
|
||||
assertNotification(notifications.saved);
|
||||
}
|
||||
|
||||
function validateObjectFields({ limit, author }) {
|
||||
cy.get('a[href^="#/collections/settings"]').click()
|
||||
cy.get('a[href^="#/collections/settings/entries/general"]').click()
|
||||
cy.get('a[href^="#/collections/settings"]').click();
|
||||
cy.get('a[href^="#/collections/settings/entries/general"]').click();
|
||||
cy.get('input[type=number]').type(limit);
|
||||
cy.contains('button', 'Save').click()
|
||||
assertNotification(notifications.error.missingField)
|
||||
cy.contains('label', 'Default Author').click()
|
||||
cy.focused().type(author)
|
||||
cy.contains('button', 'Save').click()
|
||||
assertNotification(notifications.saved)
|
||||
cy.contains('button', 'Save').click();
|
||||
assertNotification(notifications.error.missingField);
|
||||
cy.contains('label', 'Default Author').click();
|
||||
cy.focused().type(author);
|
||||
cy.contains('button', 'Save').click();
|
||||
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 }) {
|
||||
cy.get('a[href^="#/collections/settings"]').click()
|
||||
cy.get('a[href^="#/collections/settings/entries/authors"]').click()
|
||||
cy.contains('button', 'Add').click()
|
||||
cy.contains('button', 'Save').click()
|
||||
assertNotification(notifications.error.missingField)
|
||||
cy.get('input').eq(2).type(name)
|
||||
cy.get('[data-slate-editor]').eq(2).type(description)
|
||||
cy.contains('button', 'Save').click()
|
||||
cy.get('a[href^="#/collections/settings"]').click();
|
||||
cy.get('a[href^="#/collections/settings/entries/authors"]').click();
|
||||
cy.contains('button', 'Add').click();
|
||||
cy.contains('button', 'Save').click();
|
||||
assertNotification(notifications.error.missingField);
|
||||
cy.get('input')
|
||||
.eq(2)
|
||||
.type(name);
|
||||
cy.get('[data-slate-editor]')
|
||||
.eq(2)
|
||||
.type(description);
|
||||
cy.contains('button', 'Save').click();
|
||||
}
|
||||
|
||||
function exitEditor() {
|
||||
cy.contains('a[href^="#/collections/"]', 'Writing in').click()
|
||||
cy.contains('a[href^="#/collections/"]', 'Writing in').click();
|
||||
}
|
||||
|
||||
function deleteEntryInEditor() {
|
||||
cy.contains('button', 'Delete').click()
|
||||
assertNotification(notifications.deletedUnpublished)
|
||||
cy.contains('button', 'Delete').click();
|
||||
assertNotification(notifications.deletedUnpublished);
|
||||
}
|
||||
|
||||
function assertEntryDeleted(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) => {
|
||||
expect(titles).not.to.include(el.text())
|
||||
})
|
||||
expect(titles).not.to.include(el.text());
|
||||
});
|
||||
} else {
|
||||
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) {
|
||||
createPost(entry)
|
||||
deleteEntryInEditor()
|
||||
createPost(entry);
|
||||
deleteEntryInEditor();
|
||||
}
|
||||
|
||||
function createPostAndExit(entry) {
|
||||
createPost(entry)
|
||||
exitEditor()
|
||||
createPost(entry);
|
||||
exitEditor();
|
||||
}
|
||||
|
||||
function createPublishedPost(entry) {
|
||||
createPost(entry)
|
||||
updateWorkflowStatusInEditor(editorStatus.ready)
|
||||
publishInEditor()
|
||||
createPost(entry);
|
||||
updateWorkflowStatusInEditor(editorStatus.ready);
|
||||
publishInEditor();
|
||||
}
|
||||
|
||||
function createPublishedPostAndExit(entry) {
|
||||
createPublishedPost(entry)
|
||||
exitEditor()
|
||||
createPublishedPost(entry);
|
||||
exitEditor();
|
||||
}
|
||||
|
||||
function validateObjectFieldsAndExit(setting) {
|
||||
validateObjectFields(setting)
|
||||
exitEditor()
|
||||
validateObjectFields(setting);
|
||||
exitEditor();
|
||||
}
|
||||
|
||||
function validateNestedObjectFieldsAndExit(setting) {
|
||||
validateNestedObjectFields(setting);
|
||||
exitEditor();
|
||||
}
|
||||
|
||||
function validateListFieldsAndExit(setting) {
|
||||
validateListFields(setting)
|
||||
exitEditor()
|
||||
validateListFields(setting);
|
||||
exitEditor();
|
||||
}
|
||||
|
||||
function goToWorkflow() {
|
||||
cy.contains('a', 'Workflow').click()
|
||||
cy.contains('a', 'Workflow').click();
|
||||
}
|
||||
|
||||
function goToCollections() {
|
||||
cy.contains('a', 'Content').click()
|
||||
cy.contains('a', 'Content').click();
|
||||
}
|
||||
|
||||
function updateWorkflowStatus({ title }, fromColumnHeading, toColumnHeading) {
|
||||
@ -121,157 +168,182 @@ describe('Editorial Workflow', () => {
|
||||
.trigger('dragstart', {
|
||||
dataTransfer: {},
|
||||
force: true,
|
||||
})
|
||||
});
|
||||
cy.contains('h2', toColumnHeading)
|
||||
.parent()
|
||||
.trigger('drop', {
|
||||
dataTransfer: {},
|
||||
force: true,
|
||||
})
|
||||
assertNotification(notifications.updated)
|
||||
});
|
||||
assertNotification(notifications.updated);
|
||||
}
|
||||
|
||||
function assertWorkflowStatus({ title }, status) {
|
||||
cy.contains('h2', status)
|
||||
.parent()
|
||||
.contains('a', title)
|
||||
.contains('a', title);
|
||||
}
|
||||
|
||||
function updateWorkflowStatusInEditor(newStatus) {
|
||||
cy.contains('[role="button"]', 'Set status').as('setStatusButton')
|
||||
cy.get('@setStatusButton').parent().within(() => {
|
||||
cy.get('@setStatusButton').click()
|
||||
cy.contains('[role="menuitem"] span', newStatus).click()
|
||||
})
|
||||
assertNotification(notifications.updated)
|
||||
cy.contains('[role="button"]', 'Set status').as('setStatusButton');
|
||||
cy.get('@setStatusButton')
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('@setStatusButton').click();
|
||||
cy.contains('[role="menuitem"] span', newStatus).click();
|
||||
});
|
||||
assertNotification(notifications.updated);
|
||||
}
|
||||
|
||||
function assertWorkflowStatusInEditor(status) {
|
||||
cy.contains('[role="button"]', 'Set status').as('setStatusButton')
|
||||
cy.get('@setStatusButton').parent().within(() => {
|
||||
cy.get('@setStatusButton').click()
|
||||
cy.contains('[role="menuitem"] span', status).parent().within(() => {
|
||||
cy.get('svg')
|
||||
})
|
||||
cy.get('@setStatusButton').click()
|
||||
})
|
||||
cy.contains('[role="button"]', 'Set status').as('setStatusButton');
|
||||
cy.get('@setStatusButton')
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('@setStatusButton').click();
|
||||
cy.contains('[role="menuitem"] span', status)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('svg');
|
||||
});
|
||||
cy.get('@setStatusButton').click();
|
||||
});
|
||||
}
|
||||
|
||||
function publishWorkflowEntry({ title }) {
|
||||
cy.contains('h2', workflowStatus.ready).parent().within(() => {
|
||||
cy.contains('a', title).parent().within(() => {
|
||||
cy.contains('button', 'Publish new entry').click({ force: true })
|
||||
})
|
||||
})
|
||||
assertNotification(notifications.published)
|
||||
cy.contains('h2', workflowStatus.ready)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.contains('a', title)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.contains('button', 'Publish new entry').click({ force: true });
|
||||
});
|
||||
});
|
||||
assertNotification(notifications.published);
|
||||
}
|
||||
|
||||
function assertPublishedEntry(entry) {
|
||||
if (Array.isArray(entry)) {
|
||||
const entries = entry.reverse()
|
||||
const entries = entry.reverse();
|
||||
cy.get('a h2').then(els => {
|
||||
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 {
|
||||
cy.get('a h2').first().contains(entry.title)
|
||||
cy.get('a h2')
|
||||
.first()
|
||||
.contains(entry.title);
|
||||
}
|
||||
}
|
||||
|
||||
function assertNotification(message) {
|
||||
if (Array.isArray(message)) {
|
||||
const messages = message.reverse()
|
||||
const messages = message.reverse();
|
||||
cy.get('.notif__container div')
|
||||
.should('have.length.of', messages.length, )
|
||||
.should('have.length.of', messages.length)
|
||||
.each((el, idx) => {
|
||||
cy.wrap(el).contains(messages[idx]).invoke('hide')
|
||||
})
|
||||
cy.wrap(el)
|
||||
.contains(messages[idx])
|
||||
.invoke('hide');
|
||||
});
|
||||
} else {
|
||||
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() {
|
||||
cy.url().should('contain', '/#/collections/posts')
|
||||
cy.contains('h2', 'Collections')
|
||||
cy.url().should('contain', '/#/collections/posts');
|
||||
cy.contains('h2', 'Collections');
|
||||
}
|
||||
|
||||
it('successfully loads', () => {
|
||||
login()
|
||||
})
|
||||
login();
|
||||
});
|
||||
|
||||
it('can create an entry', () => {
|
||||
login()
|
||||
createPostAndExit(entry1)
|
||||
})
|
||||
login();
|
||||
createPostAndExit(entry1);
|
||||
});
|
||||
|
||||
it('can validate object fields', () => {
|
||||
login()
|
||||
validateObjectFieldsAndExit(setting1)
|
||||
})
|
||||
login();
|
||||
validateObjectFieldsAndExit(setting1);
|
||||
});
|
||||
|
||||
it('can validate fields nested in an object field', () => {
|
||||
login();
|
||||
validateNestedObjectFieldsAndExit(setting1);
|
||||
});
|
||||
|
||||
it('can validate list fields', () => {
|
||||
login()
|
||||
validateListFieldsAndExit(setting2)
|
||||
})
|
||||
login();
|
||||
validateListFieldsAndExit(setting2);
|
||||
});
|
||||
|
||||
it('can publish an editorial workflow entry', () => {
|
||||
login()
|
||||
createPostAndExit(entry1)
|
||||
goToWorkflow()
|
||||
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready)
|
||||
publishWorkflowEntry(entry1)
|
||||
})
|
||||
login();
|
||||
createPostAndExit(entry1);
|
||||
goToWorkflow();
|
||||
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
|
||||
publishWorkflowEntry(entry1);
|
||||
});
|
||||
|
||||
it('can change workflow status', () => {
|
||||
login()
|
||||
createPostAndExit(entry1)
|
||||
goToWorkflow()
|
||||
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.review)
|
||||
updateWorkflowStatus(entry1, workflowStatus.review, workflowStatus.ready)
|
||||
updateWorkflowStatus(entry1, workflowStatus.ready, workflowStatus.review)
|
||||
updateWorkflowStatus(entry1, workflowStatus.review, workflowStatus.draft)
|
||||
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready)
|
||||
})
|
||||
login();
|
||||
createPostAndExit(entry1);
|
||||
goToWorkflow();
|
||||
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.review);
|
||||
updateWorkflowStatus(entry1, workflowStatus.review, workflowStatus.ready);
|
||||
updateWorkflowStatus(entry1, workflowStatus.ready, workflowStatus.review);
|
||||
updateWorkflowStatus(entry1, workflowStatus.review, workflowStatus.draft);
|
||||
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
|
||||
});
|
||||
|
||||
it('can change status on and publish multiple entries', () => {
|
||||
login()
|
||||
createPostAndExit(entry1)
|
||||
createPostAndExit(entry2)
|
||||
createPostAndExit(entry3)
|
||||
goToWorkflow()
|
||||
updateWorkflowStatus(entry3, workflowStatus.draft, workflowStatus.ready)
|
||||
updateWorkflowStatus(entry2, workflowStatus.draft, workflowStatus.ready)
|
||||
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready)
|
||||
publishWorkflowEntry(entry3)
|
||||
publishWorkflowEntry(entry2)
|
||||
publishWorkflowEntry(entry1)
|
||||
goToCollections()
|
||||
assertPublishedEntry([entry3, entry2, entry1])
|
||||
})
|
||||
login();
|
||||
createPostAndExit(entry1);
|
||||
createPostAndExit(entry2);
|
||||
createPostAndExit(entry3);
|
||||
goToWorkflow();
|
||||
updateWorkflowStatus(entry3, workflowStatus.draft, workflowStatus.ready);
|
||||
updateWorkflowStatus(entry2, workflowStatus.draft, workflowStatus.ready);
|
||||
updateWorkflowStatus(entry1, workflowStatus.draft, workflowStatus.ready);
|
||||
publishWorkflowEntry(entry3);
|
||||
publishWorkflowEntry(entry2);
|
||||
publishWorkflowEntry(entry1);
|
||||
goToCollections();
|
||||
assertPublishedEntry([entry3, entry2, entry1]);
|
||||
});
|
||||
|
||||
it('can delete an entry', () => {
|
||||
login()
|
||||
createPost(entry1)
|
||||
deleteEntryInEditor()
|
||||
assertOnCollectionsPage()
|
||||
assertEntryDeleted(entry1)
|
||||
})
|
||||
login();
|
||||
createPost(entry1);
|
||||
deleteEntryInEditor();
|
||||
assertOnCollectionsPage();
|
||||
assertEntryDeleted(entry1);
|
||||
});
|
||||
|
||||
it('can update workflow status from within the editor', () => {
|
||||
login()
|
||||
createPost(entry1)
|
||||
assertWorkflowStatusInEditor(editorStatus.draft)
|
||||
updateWorkflowStatusInEditor(editorStatus.review)
|
||||
assertWorkflowStatusInEditor(editorStatus.review)
|
||||
updateWorkflowStatusInEditor(editorStatus.ready)
|
||||
assertWorkflowStatusInEditor(editorStatus.ready)
|
||||
exitEditor()
|
||||
goToWorkflow()
|
||||
assertWorkflowStatus(entry1, workflowStatus.ready)
|
||||
})
|
||||
})
|
||||
})
|
||||
login();
|
||||
createPost(entry1);
|
||||
assertWorkflowStatusInEditor(editorStatus.draft);
|
||||
updateWorkflowStatusInEditor(editorStatus.review);
|
||||
assertWorkflowStatusInEditor(editorStatus.review);
|
||||
updateWorkflowStatusInEditor(editorStatus.ready);
|
||||
assertWorkflowStatusInEditor(editorStatus.ready);
|
||||
exitEditor();
|
||||
goToWorkflow();
|
||||
assertWorkflowStatus(entry1, workflowStatus.ready);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
9
cypress/utils/README.md
Normal file
9
cypress/utils/README.md
Normal 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
|
17
cypress/utils/dismiss-local-backup.js
Normal file
17
cypress/utils/dismiss-local-backup.js
Normal 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;
|
||||
}
|
||||
});
|
@ -60,7 +60,13 @@ collections: # A list of collections the CMS should be able to edit
|
||||
name: posts
|
||||
widget: 'object'
|
||||
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 Thumbnail', name: thumb, widget: image, class: 'thumb', required: false }
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user