Revise editor publish logic (#14546)

* revise publish logic

* refactor arguments
This commit is contained in:
Suzanne Aitchison 2021-08-20 09:04:11 +01:00 committed by GitHub
parent 0d6cfa7501
commit f49a6ce6d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 291 additions and 17 deletions

View file

@ -44,7 +44,7 @@ function processPayload(payload) {
return neededPayload;
}
export function submitArticle(payload, clearStorage, errorCb, failureCb) {
export function submitArticle({ payload, onSuccess, onError }) {
const method = payload.id ? 'PUT' : 'POST';
const url = payload.id ? `/articles/${payload.id}` : '/articles';
fetch(url, {
@ -62,14 +62,13 @@ export function submitArticle(payload, clearStorage, errorCb, failureCb) {
.then((response) => response.json())
.then((response) => {
if (response.current_state_path) {
clearStorage();
onSuccess();
window.location.replace(response.current_state_path);
} else {
// If there is an error and the method is POST, we know they are trying to publish.
errorCb(response, method === 'POST');
onError(response);
}
})
.catch(failureCb);
.catch(onError);
}
function generateUploadFormdata(payload) {

View file

@ -262,18 +262,38 @@ export class ArticleForm extends Component {
onPublish = (e) => {
e.preventDefault();
this.setState({ submitting: true, published: true });
const { state } = this;
state.published = true;
submitArticle(state, this.removeLocalStorage, this.handleArticleError);
this.setState({ submitting: true });
const payload = {
...this.state,
published: true,
};
submitArticle({
payload,
onSuccess: () => {
this.removeLocalStorage();
this.setState({ published: true, submitting: false });
},
onError: this.handleArticleError,
});
};
onSaveDraft = (e) => {
e.preventDefault();
this.setState({ submitting: true, published: false });
const { state } = this;
state.published = false;
submitArticle(state, this.removeLocalStorage, this.handleArticleError);
this.setState({ submitting: true });
const payload = {
...this.state,
published: false,
};
submitArticle({
payload,
onSuccess: () => {
this.removeLocalStorage();
this.setState({ published: false, submitting: false });
},
onError: this.handleArticleError,
});
};
onClearChanges = (e) => {
@ -310,14 +330,11 @@ export class ArticleForm extends Component {
});
};
handleArticleError = (response, publishFailed = false) => {
handleArticleError = (response) => {
window.scrollTo(0, 0);
const { published } = this.state;
this.setState({
errors: response,
submitting: false,
// Even if it's an update that failed, published will still be set to true
published: published && !publishFailed,
});
};

View file

@ -0,0 +1,258 @@
describe('Publish or save a post', () => {
describe('v1 Editor', () => {
const validPublishedArticleContent =
'---\ntitle: Test title\npublished: true\ndescription:\ntags:\n---\nSome content';
const invalidPublishedArticleContent =
'---\ntitle: Test title\npublished: true\ndescription:\ntags:\n---\nSome content {%tag %}';
const validDraftArticleContent =
'---\ntitle: Test title\npublished: false\ndescription:\ntags:\n---\nSome content';
beforeEach(() => {
cy.testSetup();
cy.fixture('users/articleEditorV1User.json').as('user');
cy.get('@user').then((user) => {
cy.loginAndVisit(user, '/new');
});
});
it('Publishes a post without errors', () => {
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Content')
.clear()
.type(validPublishedArticleContent);
cy.findByRole('button', { name: 'Save changes' }).click();
});
// The post should now be published
cy.findByRole('heading', { name: 'Test title' });
cy.findByRole('heading', { name: 'Discussion (0)' });
cy.findByRole('link', { name: 'Edit' });
cy.findByRole('link', { name: 'Manage' });
cy.findByRole('link', { name: 'Stats' });
});
it('Saves a draft without errors', () => {
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Content')
.clear()
.type(validDraftArticleContent);
cy.findByRole('button', { name: 'Save changes' }).click();
});
// The Draft view should be shown
cy.findByText(/Unpublished Post/);
cy.findByRole('heading', { name: 'Test title' });
cy.findByRole('link', { name: 'Click to edit' });
});
it('Shows an error message when markdown is incorrect', () => {
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Content')
.clear()
.type(invalidPublishedArticleContent, {
parseSpecialCharSequences: false,
});
cy.findByRole('button', { name: 'Save changes' }).click();
});
cy.findByRole('heading', { name: 'Whoops, something went wrong:' });
// We should still be on the form page
cy.findByRole('form', { name: /^Edit post$/i });
});
it('Shows an error message when network request fails', () => {
cy.intercept('POST', '/articles', { statusCode: 500 });
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Content')
.clear()
.type(validPublishedArticleContent);
cy.findByRole('button', { name: 'Save changes' }).click();
});
cy.findByRole('heading', { name: 'Whoops, something went wrong:' });
// We should still be on the form page
cy.findByRole('form', { name: /^Edit post$/i });
});
});
describe('v2 Editor', () => {
beforeEach(() => {
cy.testSetup();
cy.fixture('users/articleEditorV2User.json').as('user');
cy.get('@user').then((user) => {
cy.loginAndVisit(user, '/new');
});
});
it('Publishes a post without errors', () => {
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Title').clear().type('Test title');
cy.findByLabelText('Post Content').clear().type('something');
cy.findByRole('button', { name: 'Publish' }).click();
});
// The post should now be published
cy.findByRole('heading', { name: 'Test title' });
cy.findByRole('heading', { name: 'Discussion (0)' });
cy.findByRole('link', { name: 'Edit' });
cy.findByRole('link', { name: 'Manage' });
cy.findByRole('link', { name: 'Stats' });
});
it('Saves a draft without errors', () => {
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Title').clear().type('Test title');
cy.findByLabelText('Post Content').clear().type('something');
cy.findByRole('button', { name: 'Save draft' }).click();
});
// The Draft view should be shown
cy.findByText(/Unpublished Post/);
cy.findByRole('heading', { name: 'Test title' });
cy.findByRole('link', { name: 'Click to edit' });
});
it('Shows an error message when publishing with incorrect markdown', () => {
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Title').clear().type('Test title');
cy.findByLabelText('Post Content').clear().type('{% tag %}', {
parseSpecialCharSequences: false,
});
cy.findByRole('button', { name: 'Publish' }).click();
});
cy.findByRole('heading', { name: 'Whoops, something went wrong:' });
// We should still be on the form page
cy.findByRole('form', { name: /^Edit post$/i });
cy.findByRole('button', { name: 'Publish' });
cy.findByRole('button', { name: 'Save draft' });
});
it('Shows an error message when saving draft with incorrect markdown', () => {
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Title').clear().type('Test title');
cy.findByLabelText('Post Content').clear().type('{% tag %}', {
parseSpecialCharSequences: false,
});
cy.findByRole('button', { name: 'Save draft' }).click();
});
cy.findByRole('heading', { name: 'Whoops, something went wrong:' });
// We should still be on the form page
cy.findByRole('form', { name: /^Edit post$/i });
cy.findByRole('button', { name: 'Publish' });
cy.findByRole('button', { name: 'Save draft' });
});
it('Shows an error message when publishing and network request fails', () => {
cy.intercept('POST', '/articles', { statusCode: 500 });
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Title').clear().type('Test title');
cy.findByLabelText('Post Content').clear().type('something');
cy.findByRole('button', { name: 'Publish' }).click();
});
cy.findByRole('heading', { name: 'Whoops, something went wrong:' });
// We should still be on the form page
cy.findByRole('form', { name: /^Edit post$/i });
cy.findByRole('button', { name: 'Publish' });
cy.findByRole('button', { name: 'Save draft' });
});
it('Shows an error message when saving draft and network request fails', () => {
cy.intercept('POST', '/articles', { statusCode: 500 });
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Title').clear().type('Test title');
cy.findByLabelText('Post Content').clear().type('something');
cy.findByRole('button', { name: 'Save draft' }).click();
});
cy.findByRole('heading', { name: 'Whoops, something went wrong:' });
// We should still be on the form page
cy.findByRole('form', { name: /^Edit post$/i });
cy.findByRole('button', { name: 'Publish' });
cy.findByRole('button', { name: 'Save draft' });
});
it('Maintains draft status when editing a draft fails', () => {
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Title').clear().type('Test title');
cy.findByLabelText('Post Content').clear().type('something');
cy.findByRole('button', { name: 'Save draft' }).click();
});
// Check we are on the draft post page, and choose to edit
cy.findByText(/Unpublished Post/);
cy.findByRole('link', { name: 'Click to edit' }).click();
cy.findByLabelText('Post Content').clear().type('something else');
cy.intercept('PUT', '/articles/*', { statusCode: 500 });
cy.findByRole('button', { name: 'Save draft' }).click();
// We should still be on the form page and see the draft publish/save options
cy.findByRole('form', { name: /^Edit post$/i });
cy.findByRole('button', { name: 'Publish' });
cy.findByRole('button', { name: 'Save draft' });
// Check post is still draft in dashboard
cy.visitAndWaitForUserSideEffects('/dashboard');
cy.findByRole('heading', { name: 'Dashboard' });
cy.findByRole('link', { name: 'Draft' });
});
it('Maintains draft status when publishing a draft fails', () => {
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Title').clear().type('Test title');
cy.findByLabelText('Post Content').clear().type('something');
cy.findByRole('button', { name: 'Save draft' }).click();
});
// Check we are on the draft post page, and choose to edit and publish
cy.findByText(/Unpublished Post/);
cy.findByRole('link', { name: 'Click to edit' }).click();
cy.findByLabelText('Post Content').clear().type('something else');
cy.intercept('PUT', '/articles/*', { statusCode: 500 });
cy.findByRole('button', { name: 'Publish' }).click();
// We should still be on the form page and see the draft publish/save options
cy.findByRole('form', { name: /^Edit post$/i });
cy.findByRole('button', { name: 'Publish' });
cy.findByRole('button', { name: 'Save draft' });
// Check post is still draft in dashboard
cy.visitAndWaitForUserSideEffects('/dashboard');
cy.findByRole('heading', { name: 'Dashboard' });
cy.findByRole('link', { name: 'Draft' });
});
it('Maintains published status when editing a published post fails', () => {
cy.findByRole('form', { name: /^Edit post$/i }).within(() => {
cy.findByLabelText('Post Title').clear().type('Test title');
cy.findByLabelText('Post Content').clear().type('something');
cy.findByRole('button', { name: 'Publish' }).click();
});
// Wait for published post page, and choose to edit
cy.findByRole('heading', { name: 'Test title' });
cy.findByRole('heading', { name: 'Discussion (0)' });
cy.findByRole('link', { name: 'Edit' }).click();
cy.findByLabelText('Post Content').clear().type('something else');
cy.intercept('PUT', '/articles/*', { statusCode: 500 });
cy.findByRole('button', { name: 'Save changes' }).click();
// We should still be on the form page and see the draft publish/save options
cy.findByRole('form', { name: /^Edit post$/i });
cy.findByRole('button', { name: 'Save changes' });
cy.findByRole('button', { name: 'Publish' }).should('not.exist');
cy.findByRole('button', { name: 'Save draft' }).should('not.exist');
// Check post is still published in dashboard
cy.visitAndWaitForUserSideEffects('/dashboard');
cy.findByRole('heading', { name: 'Dashboard' });
cy.findByRole('link', { name: 'Draft' }).should('not.exist');
});
});
});