describe('Counter example API works', () => { let server, apollo; before(() => { server = getServer(); apollo = getApollo(); }); step('Has GraphiQL endpoint', () => { return chai .request(server) .get('/graphiql') .end((err, res) => { res.status.should.be(200); res.body.should.be('{}'); }); }); step('Responds to counter get GraphQL query', async () => { let result = await apollo.query({ query: COUNTER_QUERY }); result.data.should.deep.equal({ counter: { amount: 5, __typename: 'Counter' } }); }); step('Increments counter on GraphQL mutation', async () => { let result = await apollo.mutate({ mutation: ADD_COUNTER, variables: { amount: 2 } }); result.data.should.deep.equal({ addCounter: { amount: 7, __typename: 'Counter' } }); }); step('Triggers subscription on GraphQL mutation', done => { apollo.mutate({ mutation: ADD_COUNTER, variables: { amount: 1 } }); apollo .subscribe({ query: COUNTER_SUBSCRIPTION, variables: {} }) .subscribe({ next(data) { data.should.deep.equal({ data: { counterUpdated: { amount: 8, __typename: 'Counter' } } }); done(); } }); }); });
describe('User UI works', () => { const renderer = new Renderer({}); let app; let content; step('User page renders on mount', () => { app = renderer.mount(Routes); renderer.history.push('/profile'); content = app.find('#content'); expect(content).to.not.be.empty; }); });
describe('Upload API works', () => { let server, apollo; before(() => { server = getServer(); apollo = getApollo(); }); step('Has GraphiQL endpoint', () => { return chai .request(server) .get('/graphiql') .end((err, res) => { res.status.should.be(200); res.body.should.be('{}'); }); }); });
describe('Post and comments example API works', () => { let apollo; before(() => { apollo = getApollo(); }); step('Query post list works', async () => { let result = await apollo.query({ query: POSTS_QUERY, variables: { limit: 1, after: 0 } }); expect(result.data).to.deep.equal({ posts: { totalCount: 20, edges: [ { cursor: 20, node: { id: 20, title: 'Post title 20', content: 'Post content 20', __typename: 'Post' }, __typename: 'PostEdges' } ], pageInfo: { endCursor: 20, hasNextPage: true, __typename: 'PostPageInfo' }, __typename: 'Posts' } }); }); step('Query single post with comments works', async () => { let result = await apollo.query({ query: POST_QUERY, variables: { id: 1 } }); expect(result.data).to.deep.equal({ post: { id: 1, title: 'Post title 1', content: 'Post content 1', __typename: 'Post', comments: [ { id: 1, content: 'Comment title 1 for post 1', __typename: 'Comment' }, { id: 2, content: 'Comment title 2 for post 1', __typename: 'Comment' } ] } }); }); step('Publishes post on add', done => { apollo.mutate({ mutation: ADD_POST, variables: { input: { title: 'New post 1', content: 'New post content 1' } } }); let subscription; subscription = apollo .subscribe({ query: POSTS_SUBSCRIPTION, variables: { endCursor: 10 } }) .subscribe({ next(data) { expect(data).to.deep.equal({ data: { postsUpdated: { mutation: 'CREATED', node: { id: 21, title: 'New post 1', content: 'New post content 1', __typename: 'Post' }, __typename: 'UpdatePostPayload' } } }); subscription.unsubscribe(); done(); } }); }); step('Adding post works', async () => { let result = await apollo.query({ query: POSTS_QUERY, variables: { limit: 1, after: 0 }, fetchPolicy: 'network-only' }); expect(result.data.posts).to.have.property('totalCount', 21); expect(result.data.posts).to.have.nested.property('edges[0].node.title', 'New post 1'); expect(result.data.posts).to.have.nested.property('edges[0].node.content', 'New post content 1'); }); step('Publishes post on update', done => { apollo.mutate({ mutation: EDIT_POST, variables: { input: { id: 21, title: 'New post 2', content: 'New post content 2' } } }); let subscription; subscription = apollo .subscribe({ query: POSTS_SUBSCRIPTION, variables: { endCursor: 10 } }) .subscribe({ next(data) { expect(data).to.deep.equal({ data: { postsUpdated: { mutation: 'UPDATED', node: { id: 21, title: 'New post 2', content: 'New post content 2', __typename: 'Post' }, __typename: 'UpdatePostPayload' } } }); subscription.unsubscribe(); done(); } }); }); step('Updating post works', async () => { let result = await apollo.query({ query: POSTS_QUERY, variables: { limit: 1, after: 0 }, fetchPolicy: 'network-only' }); expect(result.data.posts).to.have.property('totalCount', 21); expect(result.data.posts).to.have.nested.property('edges[0].node.title', 'New post 2'); expect(result.data.posts).to.have.nested.property('edges[0].node.content', 'New post content 2'); }); step('Publishes post on removal', done => { apollo.mutate({ mutation: DELETE_POST, variables: { id: '21' } }); let subscription; subscription = apollo .subscribe({ query: POSTS_SUBSCRIPTION, variables: { endCursor: 10 } }) .subscribe({ next(data) { expect(data).to.deep.equal({ data: { postsUpdated: { mutation: 'DELETED', node: { id: 21, title: 'New post 2', content: 'New post content 2', __typename: 'Post' }, __typename: 'UpdatePostPayload' } } }); subscription.unsubscribe(); done(); } }); }); step('Deleting post works', async () => { let result = await apollo.query({ query: POSTS_QUERY, variables: { limit: 2, after: 0 }, fetchPolicy: 'network-only' }); expect(result.data.posts).to.have.property('totalCount', 20); expect(result.data.posts).to.have.nested.property('edges[0].node.title', 'Post title 20'); expect(result.data.posts).to.have.nested.property('edges[0].node.content', 'Post content 20'); }); });
describe('Posts and comments example UI works', () => { const renderer = new Renderer(mocks, {}); let app; let content; beforeEach(() => { // Reset spy mutations on each step Object.keys(mutations).forEach(key => delete mutations[key]); if (app) { app.update(); content = app.find('#content').last(); } }); step('Posts page renders without data', () => { app = renderer.mount(); content = app.find('#content').last(); renderer.history.push('/posts'); content.text().should.equal('Loading...'); }); step('Posts page renders with data', () => { expect(content.text()).to.include('Post title 1'); expect(content.text()).to.include('Post title 2'); expect(content.text()).to.include('2 / 4'); }); step('Clicking load more works', () => { const loadMoreButton = content.find('#load-more').last(); loadMoreButton.simulate('click'); }); step('Clicking load more loads more posts', () => { expect(content.text()).to.include('Post title 3'); expect(content.text()).to.include('Post title 4'); expect(content.text()).to.include('4 / 4'); }); step('Check subscribed to post list updates', () => { expect(renderer.getSubscriptions(POSTS_SUBSCRIPTION)).has.lengthOf(1); }); step('Updates post list on post delete from subscription', () => { const subscription = renderer.getSubscriptions(POSTS_SUBSCRIPTION)[0]; subscription.next({ data: { postsUpdated: { mutation: 'DELETED', node: createNode(2), __typename: 'UpdatePostPayload' } } }); expect(content.text()).to.not.include('Post title 2'); expect(content.text()).to.include('3 / 3'); }); step('Updates post list on post create from subscription', () => { const subscription = renderer.getSubscriptions(POSTS_SUBSCRIPTION)[0]; subscription.next( _.cloneDeep({ data: { postsUpdated: { mutation: 'CREATED', node: createNode(2), __typename: 'UpdatePostPayload' } } }) ); expect(content.text()).to.include('Post title 2'); expect(content.text()).to.include('4 / 4'); }); step('Clicking delete optimistically removes post', () => { mutations.deletePost = (obj, { id }) => { return createNode(id); }; const deleteButtons = content.find('.delete-button'); expect(deleteButtons).has.lengthOf(12); deleteButtons.last().simulate('click'); expect(content.text()).to.not.include('Post title 4'); expect(content.text()).to.include('3 / 3'); }); step('Clicking delete removes the post', () => { expect(content.text()).to.include('Post title 3'); expect(content.text()).to.not.include('Post title 4'); expect(content.text()).to.include('3 / 3'); }); step('Clicking on post works', () => { const postLinks = content.find('.post-link'); postLinks.last().simulate('click', { button: 0 }); }); step('Clicking on post opens post form', () => { const postForm = content.find('form[name="post"]'); expect(content.text()).to.include('Edit Post'); expect( postForm .find('[name="title"]') .last() .instance().value ).to.equal('Post title 3'); expect( postForm .find('[name="content"]') .last() .instance().value ).to.equal('Post content 3'); }); step('Check subscribed to post updates', () => { expect(renderer.getSubscriptions(POST_SUBSCRIPTION)).has.lengthOf(1); }); step('Updates post form on post updated from subscription', () => { const subscription = renderer.getSubscriptions(POST_SUBSCRIPTION)[0]; subscription.next({ data: { postUpdated: { id: '3', title: 'Post title 203', content: 'Post content 204', __typename: 'Post' } } }); const postForm = content.find('form[name="post"]'); expect( postForm .find('[name="title"]') .last() .instance().value ).to.equal('Post title 203'); expect( postForm .find('[name="content"]') .last() .instance().value ).to.equal('Post content 204'); }); step('Post editing form works', done => { mutations.editPost = (obj, { input }) => { expect(input.id).to.equal(3); expect(input.title).to.equal('Post title 33'); expect(input.content).to.equal('Post content 33'); done(); return input; }; const postForm = app.find('form[name="post"]').last(); postForm .find('[name="title"]') .last() .simulate('change', { target: { name: 'title', value: 'Post title 33' } }); postForm .find('[name="content"]') .last() .simulate('change', { target: { name: 'content', value: 'Post content 33' } }); postForm.simulate('submit'); }); step('Check opening post by URL', () => { renderer.history.push('/post/3'); }); step('Opening post by URL works', () => { const postForm = content.find('form[name="post"]'); expect(content.text()).to.include('Edit Post'); expect( postForm .find('[name="title"]') .last() .instance().value ).to.equal('Post title 33'); expect( postForm .find('[name="content"]') .last() .instance().value ).to.equal('Post content 33'); expect(content.text()).to.include('Edit Post'); }); step('Comment adding works', done => { mutations.addComment = (obj, { input }) => { expect(input.postId).to.equal(3); expect(input.content).to.equal('Post comment 24'); done(); return input; }; const commentForm = content.find('form[name="comment"]'); commentForm .find('[name="content"]') .last() .simulate('change', { target: { name: 'content', value: 'Post comment 24' } }); commentForm.last().simulate('submit'); }); step('Comment adding works after submit', () => { expect(content.text()).to.include('Post comment 24'); }); step('Updates comment form on comment added got from subscription', () => { const subscription = renderer.getSubscriptions(COMMENT_SUBSCRIPTION)[0]; subscription.next({ data: { commentUpdated: { mutation: 'CREATED', id: 3003, postId: 3, node: { id: 3003, content: 'Post comment 3', __typename: 'Comment' }, __typename: 'UpdateCommentPayload' } } }); expect(content.text()).to.include('Post comment 3'); }); step('Updates comment form on comment deleted got from subscription', () => { const subscription = renderer.getSubscriptions(COMMENT_SUBSCRIPTION)[0]; subscription.next({ data: { commentUpdated: { mutation: 'DELETED', id: 3003, postId: 3, node: { id: 3003, content: 'Post comment 3', __typename: 'Comment' }, __typename: 'UpdateCommentPayload' } } }); expect(content.text()).to.not.include('Post comment 3'); }); step('Comment deleting optimistically removes comment', () => { const deleteButtons = content.find('.delete-comment'); expect(deleteButtons).has.lengthOf(9); deleteButtons.last().simulate('click'); app.update(); content = app.find('#content').last(); expect(content.text()).to.not.include('Post comment 24'); expect(content.find('.delete-comment')).has.lengthOf(6); }); step('Clicking comment delete removes the comment', () => { expect(content.text()).to.not.include('Post comment 24'); expect(content.find('.delete-comment')).has.lengthOf(6); }); step('Comment editing works', async done => { mutations.editComment = (obj, { input }) => { expect(input.postId).to.equal(3); expect(input.content).to.equal('Edited comment 2'); done(); return input; }; const editButtons = content.find('.edit-comment'); expect(editButtons).has.lengthOf(6); editButtons.last().simulate('click'); editButtons.last().simulate('click'); const commentForm = content.find('form[name="comment"]'); expect( commentForm .find('[name="content"]') .last() .instance().value ).to.equal('Post comment 2'); commentForm .find('[name="content"]') .last() .simulate('change', { target: { name: 'content', value: 'Edited comment 2' } }); commentForm.simulate('submit'); }); step('Comment editing works', () => { expect(content.text()).to.include('Edited comment 2'); }); step('Clicking back button takes to post list', () => { expect(content.text()).to.include('Edited comment 2'); const backButton = content.find('#back-button'); backButton.last().simulate('click', { button: 0 }); app.update(); content = app.find('#content').last(); expect(content.text()).to.include('Post title 3'); }); });