├── .babelrc
├── .github
└── workflows
│ └── valid.yml
├── .gitignore
├── LICENSE
├── README.md
├── cypress
└── integration
│ └── smoke-test.spec.js
├── db.json
├── json-server.json
├── package-lock.json
├── package.json
├── routes.json
├── src
├── components
│ ├── Footer.js
│ ├── TodoApp.js
│ ├── TodoForm.js
│ └── TodoList.js
├── index.html
├── index.js
├── lib
│ ├── service.js
│ └── utils.js
└── styles.css
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets":[
3 | "env", "react"
4 | ],
5 | "plugins": [
6 | "transform-object-rest-spread"
7 | ]
8 | }
--------------------------------------------------------------------------------
/.github/workflows/valid.yml:
--------------------------------------------------------------------------------
1 | name: Validate initial app state
2 |
3 | on: push
4 |
5 | jobs:
6 | single-run:
7 | runs-on: ubuntu-16.04
8 | steps:
9 | - name: Checkout
10 | uses: actions/checkout@v2
11 | - name: Cypress run
12 | uses: cypress-io/github-action@v1
13 | with:
14 | build: npm run build
15 | start: npm run serve
16 | config-file: false
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | logs
4 | *.log
5 | npm-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 |
9 | .env
10 |
11 | build
12 |
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 avanslaars
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Build and test an application using Cypress
2 |
3 | This repository is the starting point for an official Cypress tutorial. We encourage you to clone this repo and follow along.
4 |
--------------------------------------------------------------------------------
/cypress/integration/smoke-test.spec.js:
--------------------------------------------------------------------------------
1 | const hostUrl = "http://localhost:3030";
2 | describe("The application loads", () => {
3 | it("navigates to the / route", () => {
4 | cy.visit(hostUrl);
5 | });
6 |
7 | it("has the basic Todo list container", () => {
8 | cy.visit(hostUrl);
9 | cy.get(".todo-list").should("exist");
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/db.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/json-server.json:
--------------------------------------------------------------------------------
1 | {
2 | "port": 3030,
3 | "watch": false,
4 | "static": "./build",
5 | "routes": "./routes.json"
6 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cypress-tutorial-build-todo-starter",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "webpack",
8 | "watch": "webpack --watch",
9 | "serve": "json-server db.json",
10 | "dev": "concurrently \"npm run watch\" \"npm run serve\""
11 | },
12 | "keywords": [],
13 | "author": "",
14 | "license": "ISC",
15 | "dependencies": {
16 | "axios": "0.19.2",
17 | "concurrently": "3.6.1",
18 | "json-server": "0.16.1",
19 | "react": "16.13.1",
20 | "react-dom": "16.13.1",
21 | "react-router-dom": "4.2.2"
22 | },
23 | "devDependencies": {
24 | "babel-core": "6.26.3",
25 | "babel-loader": "7.1.5",
26 | "babel-plugin-transform-object-rest-spread": "6.26.0",
27 | "babel-preset-env": "1.7.0",
28 | "babel-preset-react": "6.24.1",
29 | "css-loader": "0.28.11",
30 | "cypress": "3.5.0",
31 | "html-webpack-plugin": "2.30.1",
32 | "style-loader": "0.19.1",
33 | "webpack": "3.12.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/routes.json:
--------------------------------------------------------------------------------
1 | {
2 | "/api/*": "/$1"
3 | }
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Link} from 'react-router-dom'
3 |
4 | export default props =>
5 |
17 |
--------------------------------------------------------------------------------
/src/components/TodoApp.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react'
2 | import {BrowserRouter as Router, Route} from 'react-router-dom'
3 | import TodoForm from './TodoForm'
4 | import TodoList from './TodoList'
5 | import Footer from './Footer'
6 |
7 |
8 | export default class TodoApp extends Component {
9 | constructor(props) {
10 | super(props)
11 |
12 | this.state = {
13 | todos: []
14 | }
15 | }
16 |
17 |
18 |
19 | render () {
20 | return (
21 |
22 |
23 |
27 |
30 |
31 |
32 |
33 | )
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/TodoForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | export default props =>
4 |
10 |
--------------------------------------------------------------------------------
/src/components/TodoList.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const TodoItem = props =>
4 |
5 |
6 |
7 |
10 |
11 |
12 |
13 |
14 | export default props =>
15 |
16 | {props.todos.map(todo => )}
17 |
18 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | React todoMVC with Cypress Tests
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom'
3 | import TodoApp from './components/TodoApp'
4 | import './styles.css'
5 |
6 | ReactDOM.render(, document.getElementById('app'))
--------------------------------------------------------------------------------
/src/lib/service.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/cypress-tutorial-build-todo-starter/39de647e6bf961eec3fa9d2b8d69fb2ba3f391d8/src/lib/service.js
--------------------------------------------------------------------------------
/src/lib/utils.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/cypress-tutorial-build-todo-starter/39de647e6bf961eec3fa9d2b8d69fb2ba3f391d8/src/lib/utils.js
--------------------------------------------------------------------------------
/src/styles.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | margin: 0;
4 | padding: 0;
5 | }
6 |
7 | button {
8 | margin: 0;
9 | padding: 0;
10 | border: 0;
11 | background: none;
12 | font-size: 100%;
13 | vertical-align: baseline;
14 | font-family: inherit;
15 | font-weight: inherit;
16 | color: inherit;
17 | -webkit-appearance: none;
18 | appearance: none;
19 | -webkit-font-smoothing: antialiased;
20 | -moz-font-smoothing: antialiased;
21 | font-smoothing: antialiased;
22 | }
23 |
24 | body {
25 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
26 | line-height: 1.4em;
27 | background: #f5f5f5;
28 | color: #4d4d4d;
29 | min-width: 230px;
30 | max-width: 550px;
31 | margin: 0 auto;
32 | -webkit-font-smoothing: antialiased;
33 | -moz-font-smoothing: antialiased;
34 | font-smoothing: antialiased;
35 | font-weight: 300;
36 | }
37 |
38 | button,
39 | input[type="checkbox"] {
40 | outline: none;
41 | }
42 |
43 | .hidden {
44 | display: none;
45 | }
46 |
47 | .todoapp {
48 | background: #fff;
49 | margin: 130px 0 40px 0;
50 | position: relative;
51 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
52 | 0 25px 50px 0 rgba(0, 0, 0, 0.1);
53 | }
54 |
55 | .todoapp input::-webkit-input-placeholder {
56 | font-style: italic;
57 | font-weight: 300;
58 | color: #e6e6e6;
59 | }
60 |
61 | .todoapp input::-moz-placeholder {
62 | font-style: italic;
63 | font-weight: 300;
64 | color: #e6e6e6;
65 | }
66 |
67 | .todoapp input::input-placeholder {
68 | font-style: italic;
69 | font-weight: 300;
70 | color: #e6e6e6;
71 | }
72 |
73 | .todoapp h1 {
74 | position: absolute;
75 | top: -155px;
76 | width: 100%;
77 | font-size: 100px;
78 | font-weight: 100;
79 | text-align: center;
80 | color: rgba(175, 47, 47, 0.15);
81 | -webkit-text-rendering: optimizeLegibility;
82 | -moz-text-rendering: optimizeLegibility;
83 | text-rendering: optimizeLegibility;
84 | }
85 |
86 | .new-todo,
87 | .edit {
88 | position: relative;
89 | margin: 0;
90 | width: 100%;
91 | font-size: 24px;
92 | font-family: inherit;
93 | font-weight: inherit;
94 | line-height: 1.4em;
95 | border: 0;
96 | outline: none;
97 | color: inherit;
98 | padding: 6px;
99 | border: 1px solid #999;
100 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
101 | box-sizing: border-box;
102 | -webkit-font-smoothing: antialiased;
103 | -moz-font-smoothing: antialiased;
104 | font-smoothing: antialiased;
105 | }
106 |
107 | .new-todo {
108 | padding: 16px 16px 16px 60px;
109 | border: none;
110 | background: rgba(0, 0, 0, 0.003);
111 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
112 | }
113 |
114 | .main {
115 | position: relative;
116 | z-index: 2;
117 | border-top: 1px solid #e6e6e6;
118 | }
119 |
120 | label[for='toggle-all'] {
121 | display: none;
122 | }
123 |
124 | .toggle-all {
125 | position: absolute;
126 | top: -55px;
127 | left: -12px;
128 | width: 60px;
129 | height: 34px;
130 | text-align: center;
131 | border: none; /* Mobile Safari */
132 | }
133 |
134 | .toggle-all:before {
135 | content: '❯';
136 | font-size: 22px;
137 | color: #e6e6e6;
138 | padding: 10px 27px 10px 27px;
139 | }
140 |
141 | .toggle-all:checked:before {
142 | color: #737373;
143 | }
144 |
145 | .todo-list {
146 | margin: 0;
147 | padding: 0;
148 | list-style: none;
149 | }
150 |
151 | .todo-list li {
152 | position: relative;
153 | font-size: 24px;
154 | border-bottom: 1px solid #ededed;
155 | }
156 |
157 | .todo-list li:last-child {
158 | border-bottom: none;
159 | }
160 |
161 | .todo-list li.editing {
162 | border-bottom: none;
163 | padding: 0;
164 | }
165 |
166 | .todo-list li.editing .edit {
167 | display: block;
168 | width: 506px;
169 | padding: 13px 17px 12px 17px;
170 | margin: 0 0 0 43px;
171 | }
172 |
173 | .todo-list li.editing .view {
174 | display: none;
175 | }
176 |
177 | .todo-list li .toggle {
178 | text-align: center;
179 | width: 40px;
180 | /* auto, since non-WebKit browsers doesn't support input styling */
181 | height: auto;
182 | position: absolute;
183 | top: 0;
184 | bottom: 0;
185 | margin: auto 0;
186 | border: none; /* Mobile Safari */
187 | -webkit-appearance: none;
188 | appearance: none;
189 | }
190 |
191 | .todo-list li .toggle:after {
192 | content: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='-10 -18 100 135'%3E%3Ccircle cx='50' cy='50' r='50' fill='none' stroke='%23ededed' stroke-width='3'/%3E%3C/svg%3E");
193 | }
194 |
195 | .todo-list li .toggle:checked:after {
196 | content: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='-10 -18 100 135'%3E%3Ccircle cx='50' cy='50' r='50' fill='none' stroke='%23bddad5' stroke-width='3'/%3E%3Cpath fill='%235dc2af' d='M72 25L42 71 27 56l-4 4 20 20 34-52z'/%3E%3C/svg%3E");
197 | }
198 |
199 | .todo-list li label {
200 | white-space: pre-line;
201 | word-break: break-all;
202 | padding: 15px 60px 15px 15px;
203 | margin-left: 45px;
204 | display: block;
205 | line-height: 1.2;
206 | transition: color 0.4s;
207 | }
208 |
209 | .todo-list li.completed label {
210 | color: #d9d9d9;
211 | text-decoration: line-through;
212 | }
213 |
214 | .todo-list li .destroy {
215 | display: none;
216 | position: absolute;
217 | top: 0;
218 | right: 10px;
219 | bottom: 0;
220 | width: 40px;
221 | height: 40px;
222 | margin: auto 0;
223 | font-size: 30px;
224 | color: #cc9a9a;
225 | margin-bottom: 11px;
226 | transition: color 0.2s ease-out;
227 | }
228 |
229 | .todo-list li .destroy:hover {
230 | color: #af5b5e;
231 | }
232 |
233 | .todo-list li .destroy:after {
234 | content: '×';
235 | }
236 |
237 | .todo-list li:hover .destroy {
238 | display: block;
239 | }
240 |
241 | span.error {
242 | width: 200px;
243 | position: absolute;
244 | top: 25px;
245 | left: 50%;
246 | margin-left: -100px;
247 | font-size: 2em;
248 | font-weight: bolder;
249 | border: solid 1px #E44234;
250 | border-radius: 5px;
251 | padding-top: 5px;
252 | padding-bottom: 5px;
253 | text-align: center;
254 | background: #E44234;
255 | color: white;
256 | }
257 |
258 | .footer {
259 | color: #777;
260 | padding: 10px 15px;
261 | height: 20px;
262 | text-align: center;
263 | border-top: 1px solid #e6e6e6;
264 | }
265 |
266 | .footer:before {
267 | content: '';
268 | position: absolute;
269 | right: 0;
270 | bottom: 0;
271 | left: 0;
272 | height: 50px;
273 | overflow: hidden;
274 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
275 | 0 8px 0 -3px #f6f6f6,
276 | 0 9px 1px -3px rgba(0, 0, 0, 0.2),
277 | 0 16px 0 -6px #f6f6f6,
278 | 0 17px 2px -6px rgba(0, 0, 0, 0.2);
279 | }
280 |
281 | .todo-count {
282 | float: left;
283 | text-align: left;
284 | }
285 |
286 | .todo-count strong {
287 | font-weight: 300;
288 | }
289 |
290 | .filters {
291 | margin: 0;
292 | padding: 0;
293 | list-style: none;
294 | position: absolute;
295 | right: 0;
296 | left: 0;
297 | }
298 |
299 | .filters li {
300 | display: inline;
301 | }
302 |
303 | .filters li a {
304 | color: inherit;
305 | margin: 3px;
306 | padding: 3px 7px;
307 | text-decoration: none;
308 | border: 1px solid transparent;
309 | border-radius: 3px;
310 | }
311 |
312 | .filters li a.selected,
313 | .filters li a:hover {
314 | border-color: rgba(175, 47, 47, 0.1);
315 | }
316 |
317 | .filters li a.selected {
318 | border-color: rgba(175, 47, 47, 0.2);
319 | }
320 |
321 | .info {
322 | margin: 65px auto 0;
323 | color: #bfbfbf;
324 | font-size: 10px;
325 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
326 | text-align: center;
327 | }
328 |
329 | .info p {
330 | line-height: 1;
331 | }
332 |
333 | .info a {
334 | color: inherit;
335 | text-decoration: none;
336 | font-weight: 400;
337 | }
338 |
339 | .info a:hover {
340 | text-decoration: underline;
341 | }
342 |
343 | /*
344 | Hack to remove background from Mobile Safari.
345 | Can't use it globally since it destroys checkboxes in Firefox
346 | */
347 | @media screen and (-webkit-min-device-pixel-ratio:0) {
348 | .toggle-all,
349 | .todo-list li .toggle {
350 | background: none;
351 | }
352 |
353 | .todo-list li .toggle {
354 | height: 40px;
355 | }
356 |
357 | .toggle-all {
358 | -webkit-transform: rotate(90deg);
359 | transform: rotate(90deg);
360 | -webkit-appearance: none;
361 | appearance: none;
362 | }
363 | }
364 |
365 | @media (max-width: 430px) {
366 | .footer {
367 | height: 50px;
368 | }
369 |
370 | .filters {
371 | bottom: 10px;
372 | }
373 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const HtmlWebpackPlugin = require('html-webpack-plugin')
3 |
4 | const HtmlWebpackPluginConfig = new HtmlWebpackPlugin({
5 | template: './src/index.html',
6 | filename: 'index.html',
7 | inject: 'body'
8 | })
9 |
10 | module.exports = {
11 | entry: './src/index.js',
12 | output: {
13 | path: path.join(__dirname, 'build'),
14 | filename: 'app.bundle.js'
15 | },
16 | module: {
17 | loaders: [
18 | { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
19 | { test: /\.css$/, use: ['style-loader', 'css-loader'], exclude: /node_modules/ }
20 | ]
21 | },
22 | devtool: 'source-map',
23 | plugins: [HtmlWebpackPluginConfig]
24 | }
25 |
--------------------------------------------------------------------------------