Skip to content

Commit dd3492c

Browse files
author
Khaled Osman
committed
Merge remote-tracking branch 'leo/redux-toolkit' into upgrade-codebase
2 parents 9292e4e + 1d889c6 commit dd3492c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+8497
-2128
lines changed

.editorconfig

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# editorconfig.org
2+
root = true
3+
4+
[*]
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = true
11+
12+
[*.md]
13+
trim_trailing_whitespace = false

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ node_modules
55

66
# testing
77
coverage
8+
.nyc_output
89

910
# production
1011
build
@@ -16,3 +17,6 @@ npm-debug.log
1617
.idea
1718

1819
.eslintcache
20+
21+
cypress/videos
22+
cypress/screenshots

.husky/pre-commit

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/sh
2+
. "$(dirname "$0")/_/husky.sh"
3+
4+
npx pretty-quick --staged

.prettierrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"singleQuote": true
3+
}

cypress.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"baseUrl": "http://localhost:4100",
3+
"env": {
4+
"apiUrl": "https://conduit.productionready.io/api",
5+
"username": "warren_boyd",
6+
"email": "[email protected]",
7+
"password": "Pa$$w0rd!"
8+
}
9+
}

cypress/README.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Cypress.io end-to-end tests 🚀
2+
3+
[Cypress.io](https://www.cypress.io) is an open source, MIT licensed end-to-end test runner
4+
5+
## Folder structure
6+
7+
These folders hold the end-to-end tests and supporting files for the [Cypress Test Runner](https://github.com/cypress-io/cypress).
8+
9+
- [fixtures](fixtures) folder holds optional JSON data for mocking, [read more](https://on.cypress.io/fixture)
10+
- [integration](integration) holds the actual test files, [read more](https://on.cypress.io/writing-and-organizing-tests)
11+
- [plugins](plugins) allow you to customize how tests are loaded, [read more](https://on.cypress.io/plugins)
12+
- [support](support) file runs before all tests and is a great place to write or load additional custom commands, [read more](https://on.cypress.io/writing-and-organizing-tests#Support-file)
13+
14+
## `cypress.json` file
15+
16+
You can configure project options in the [../cypress.json](../cypress.json) file, see [Cypress configuration doc](https://on.cypress.io/configuration).
17+
18+
## More information
19+
20+
- [https://github.com/cypress.io/cypress](https://github.com/cypress.io/cypress)
21+
- [https://docs.cypress.io/](https://docs.cypress.io/)
22+
- [Writing your first Cypress test](https://on.cypress.io/intro)

cypress/fixtures/example.json

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "Using fixtures to represent data",
3+
"email": "[email protected]",
4+
"body": "Fixtures are a great way to mock data for responses to routes"
5+
}

cypress/integration/article-spec.js

+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
// enables intelligent code completion for Cypress commands
2+
// https://on.cypress.io/intelligent-code-completion
3+
/// <reference types="cypress" />
4+
/// <reference types="@testing-library/cypress" />
5+
import faker from 'faker';
6+
7+
describe('Article page', () => {
8+
beforeEach(() => {
9+
cy.intercept('GET', '**/articles?*')
10+
.as('getAllArticles')
11+
.intercept('GET', '**/articles/*/comments')
12+
.as('getCommentsForArticle')
13+
.intercept('GET', '**/articles/*')
14+
.as('getArticle')
15+
.visit('/');
16+
17+
cy.wait('@getAllArticles').get('.preview-link').first().click();
18+
});
19+
20+
it('should show the article', () => {
21+
cy.wait('@getArticle')
22+
.its('response.body.article')
23+
.then((article) => {
24+
cy.location('pathname').should('equal', `/article/${article.slug}`);
25+
26+
cy.findByRole('heading', { name: article.title }).should('be.visible');
27+
28+
cy.get('.article-meta').within(() => {
29+
cy.findByRole('img', { name: article.author.username }).should(
30+
'be.visible'
31+
);
32+
33+
cy.findByText(article.author.username).should('be.visible');
34+
});
35+
36+
cy.get('.article-content .col-xs-12')
37+
.children()
38+
.first()
39+
.should('not.be.empty');
40+
41+
cy.get('.tag-list')
42+
.children()
43+
.should('have.length', article.tagList.length);
44+
});
45+
});
46+
47+
it('should require to be logged to comment', () => {
48+
cy.findByText(/to add comments on this article/i).should('be.visible');
49+
});
50+
});
51+
52+
describe('Article page (authenticated)', () => {
53+
const commentPlaceholder = 'Write a comment...';
54+
const postCommentButton = 'Post Comment';
55+
56+
beforeEach(() => {
57+
cy.intercept('GET', '**/articles?*')
58+
.as('getAllArticles')
59+
.intercept('GET', '**/articles/*/comments')
60+
.as('getCommentsForArticle')
61+
.intercept('GET', '**/articles/*')
62+
.as('getArticle')
63+
.intercept('POST', '**/articles/*/comments')
64+
.as('createComment')
65+
.intercept('DELETE', '**/articles/*/comments/*')
66+
.as('deleteComment')
67+
.visit('/')
68+
.login();
69+
70+
cy.wait('@getAllArticles').get('.preview-link').first().click();
71+
});
72+
73+
it('should show the comment box', () => {
74+
cy.wait(['@getArticle', '@getCommentsForArticle']);
75+
76+
cy.get('.comment-form').should('exist');
77+
});
78+
79+
it('should add a new comment', () => {
80+
const comment = faker.lorem.paragraph();
81+
82+
cy.wait(['@getArticle', '@getCommentsForArticle']);
83+
84+
cy.findByPlaceholderText(commentPlaceholder).type(comment);
85+
86+
cy.findByRole('button', { name: postCommentButton }).click();
87+
88+
cy.wait('@createComment').its('response.statusCode').should('equal', 200);
89+
90+
cy.wait(100)
91+
.get('.card:not(form)')
92+
.first()
93+
.within(() => {
94+
cy.findByText(comment).should('exist');
95+
});
96+
});
97+
98+
it('should validate the comment box', () => {
99+
cy.wait(['@getArticle', '@getCommentsForArticle']);
100+
101+
cy.findByRole('button', { name: postCommentButton }).click();
102+
103+
cy.wait('@createComment').its('response.statusCode').should('equal', 422);
104+
105+
cy.get('.error-messages').within(() => {
106+
cy.findAllByRole('listitem').should('have.length', 1);
107+
});
108+
});
109+
110+
it('should remove my own comment', () => {
111+
const comment = faker.lorem.sentence();
112+
113+
cy.wait(['@getArticle', '@getCommentsForArticle']);
114+
115+
cy.findByPlaceholderText(commentPlaceholder).type(comment);
116+
117+
cy.findByRole('button', { name: postCommentButton }).click();
118+
119+
cy.wait('@createComment');
120+
121+
cy.findByText(comment)
122+
.as('comment')
123+
.parent()
124+
.parent()
125+
.find('.mod-options i')
126+
.click();
127+
128+
cy.wait('@deleteComment').its('response.statusCode').should('equal', 200);
129+
130+
cy.findByText(comment).should('not.exist');
131+
});
132+
});
133+
134+
describe('Article page (author)', () => {
135+
const commentPlaceholder = 'Write a comment...';
136+
const postCommentButton = 'Post Comment';
137+
138+
beforeEach(() => {
139+
cy.intercept('GET', '**/articles/*')
140+
.as('getArticle')
141+
.intercept('GET', '**/articles/*/comments')
142+
.as('getCommentsForArticle')
143+
.intercept('POST', '**/articles/*/comments')
144+
.as('createComment')
145+
.intercept('DELETE', '**/articles/*')
146+
.as('deleteArticle');
147+
148+
cy.visit('/')
149+
.login()
150+
.createArticle()
151+
.then((article) => {
152+
cy.visit(`/article/${article.slug}`);
153+
154+
cy.wait(['@getArticle', '@getCommentsForArticle']);
155+
});
156+
});
157+
158+
it('should add a new comment', () => {
159+
const comment = faker.lorem.paragraph();
160+
161+
cy.findByPlaceholderText(commentPlaceholder).type(comment);
162+
163+
cy.findByRole('button', { name: postCommentButton }).click();
164+
165+
cy.wait('@createComment').its('response.statusCode').should('equal', 200);
166+
});
167+
168+
it('should remove my article', () => {
169+
cy.findByRole('button', {
170+
name: /delete article/i,
171+
}).click();
172+
173+
cy.wait('@deleteArticle').its('response.statusCode').should('equal', 200);
174+
175+
cy.location('pathname').should('equal', '/');
176+
});
177+
});

0 commit comments

Comments
 (0)