carlos / todomvc-bakeoff Public

Clone
git clone https://amadan.net/carlos/todomvc-bakeoff

Plain git — no account needed to clone.

Download

Download this file

1// Ported from tastejs/todomvc cypress/e2e/spec.cy.js. Each test runs in a
2// fresh browser context: a fresh cookie, so a fresh empty list. Persistence
3// is checked by reloading, not by reading localStorage, and routing uses
4// real paths. Only "save edits on blur" and "cancel edits on escape" are
5// skipped with JavaScript off.
6import { test, expect } from '@playwright/test';
7import {
8 jsOn, todos, addTodo, addDefaultTodos, toggle, toggleAll, clearCompleted,
9 startEditing, submitEdit, gotoFilter,
10} from './actions';
11
12const ONE = 'buy some cheese';
13const TWO = 'feed the cat';
14const THREE = 'book a doctors appointment';
15const DEFAULTS = [ONE, TWO, THREE];
16
17test.beforeEach(async ({ page }) => {
18 await page.goto('/');
19 await expect(page.locator('h1')).toHaveText(/^todos$/i);
20 await expect(todos(page)).toHaveCount(0);
21});
22
23test.describe('When page is initially opened', () => {
24 test('should focus on the todo input field', async ({ page }) => {
25 await expect(page.locator('.new-todo')).toBeFocused();
26 });
27});
28
29test.describe('No Todos', () => {
30 test('starts with nothing', async ({ page }) => {
31 await expect(todos(page)).toHaveCount(0);
32 });
33
34 test('should hide #main and #footer', async ({ page }) => {
35 await expect(page.locator('.main')).toBeHidden();
36 await expect(page.locator('.footer')).toBeHidden();
37 });
38});
39
40test.describe('New Todo', () => {
41 test('should allow me to add todo items', async ({ page }, ti) => {
42 await addTodo(page, ti, ONE);
43 await expect(todos(page).nth(0).locator('label')).toHaveText(ONE);
44 await addTodo(page, ti, TWO);
45 await expect(todos(page).nth(1).locator('label')).toHaveText(TWO);
46 });
47
48 test('should clear text input field when an item is added', async ({ page }, ti) => {
49 await addTodo(page, ti, ONE);
50 await expect(page.locator('.new-todo')).toHaveValue('');
51 });
52
53 test('should append new items to the bottom of the list', async ({ page }, ti) => {
54 await addDefaultTodos(page, ti, DEFAULTS);
55 await expect(page.locator('.todo-count')).toContainText('3');
56 await expect(todos(page).locator('label')).toHaveText(DEFAULTS);
57 });
58
59 test('should trim text input', async ({ page }, ti) => {
60 await addTodo(page, ti, ` ${ONE} `);
61 await expect(todos(page)).toHaveCount(1);
62 expect(await todos(page).nth(0).locator('label').textContent()).toBe(ONE);
63 });
64
65 test('should show #main and #footer when items added', async ({ page }, ti) => {
66 await addTodo(page, ti, ONE);
67 await expect(page.locator('.main')).toBeVisible();
68 await expect(page.locator('.footer')).toBeVisible();
69 });
70});
71
72test.describe('Mark all as completed', () => {
73 test.beforeEach(async ({ page }, ti) => addDefaultTodos(page, ti, DEFAULTS));
74
75 test('should allow me to mark all items as completed', async ({ page }, ti) => {
76 await toggleAll(page, ti);
77 for (let i = 0; i < 3; i++) await expect(todos(page).nth(i)).toHaveClass(/\bcompleted\b/);
78 });
79
80 test('should allow me to clear the complete state of all items', async ({ page }, ti) => {
81 await toggleAll(page, ti);
82 await toggleAll(page, ti);
83 for (let i = 0; i < 3; i++) await expect(todos(page).nth(i)).not.toHaveClass(/\bcompleted\b/);
84 });
85
86 test('complete all checkbox should update state when items are completed / cleared', async ({ page }, ti) => {
87 await toggleAll(page, ti);
88 await expect(page.locator('.toggle-all')).toBeChecked();
89 await toggle(page, ti, todos(page).nth(0));
90 await expect(page.locator('.toggle-all')).not.toBeChecked();
91 await toggle(page, ti, todos(page).nth(0));
92 await expect(page.locator('.toggle-all')).toBeChecked();
93 });
94});
95
96test.describe('Item', () => {
97 test('should allow me to mark items as complete', async ({ page }, ti) => {
98 await addDefaultTodos(page, ti, [ONE, TWO]);
99 await toggle(page, ti, todos(page).nth(0));
100 await expect(todos(page).nth(0)).toHaveClass(/\bcompleted\b/);
101 await expect(todos(page).nth(1)).not.toHaveClass(/\bcompleted\b/);
102 await toggle(page, ti, todos(page).nth(1));
103 await expect(todos(page).nth(0)).toHaveClass(/\bcompleted\b/);
104 await expect(todos(page).nth(1)).toHaveClass(/\bcompleted\b/);
105 });
106
107 test('should allow me to un-mark items as complete', async ({ page }, ti) => {
108 await addDefaultTodos(page, ti, [ONE, TWO]);
109 await toggle(page, ti, todos(page).nth(0));
110 await expect(todos(page).nth(0)).toHaveClass(/\bcompleted\b/);
111 await expect(todos(page).nth(1)).not.toHaveClass(/\bcompleted\b/);
112 await toggle(page, ti, todos(page).nth(0));
113 await expect(todos(page).nth(0)).not.toHaveClass(/\bcompleted\b/);
114 await expect(todos(page).nth(1)).not.toHaveClass(/\bcompleted\b/);
115 });
116
117 test('should allow me to edit an item', async ({ page }, ti) => {
118 await addDefaultTodos(page, ti, DEFAULTS);
119 const input = await startEditing(page, ti, 1);
120 await expect(input).toHaveValue(TWO);
121 await submitEdit(page, ti, input, 'buy some sausages');
122 await expect(todos(page).locator('label')).toHaveText([ONE, 'buy some sausages', THREE]);
123 });
124});
125
126test.describe('Editing', () => {
127 test.beforeEach(async ({ page }, ti) => addDefaultTodos(page, ti, DEFAULTS));
128
129 test('should hide other controls when editing', async ({ page }, ti) => {
130 await startEditing(page, ti, 1);
131 await expect(todos(page).nth(1).locator('.toggle')).toBeHidden();
132 await expect(todos(page).nth(1).locator('label')).toBeHidden();
133 });
134
135 test('should save edits on blur', async ({ page }, ti) => {
136 test.skip(!jsOn(ti), 'blur saving needs JavaScript');
137 const input = await startEditing(page, ti, 1);
138 await input.fill('buy some sausages');
139 await Promise.all([page.waitForResponse((r) => r.request().method() === 'POST'), input.blur()]);
140 await expect(todos(page).locator('label')).toHaveText([ONE, 'buy some sausages', THREE]);
141 });
142
143 test('should trim entered text', async ({ page }, ti) => {
144 const input = await startEditing(page, ti, 1);
145 await submitEdit(page, ti, input, ' buy some sausages ');
146 await expect(todos(page).locator('label')).toHaveText([ONE, 'buy some sausages', THREE]);
147 expect(await todos(page).nth(1).locator('label').textContent()).toBe('buy some sausages');
148 });
149
150 test('should remove the item if an empty text string was entered', async ({ page }, ti) => {
151 const input = await startEditing(page, ti, 1);
152 await submitEdit(page, ti, input, '');
153 await expect(todos(page)).toHaveCount(2);
154 });
155
156 test('should cancel edits on escape', async ({ page }, ti) => {
157 test.skip(!jsOn(ti), 'escape to cancel needs JavaScript');
158 const input = await startEditing(page, ti, 1);
159 await input.fill('foo');
160 await input.press('Escape');
161 await expect(todos(page).locator('label')).toHaveText(DEFAULTS);
162 });
163});
164
165test.describe('Counter', () => {
166 test('should display the current number of todo items', async ({ page }, ti) => {
167 await addTodo(page, ti, ONE);
168 await expect(page.locator('.todo-count')).toHaveText('1 item left');
169 await addTodo(page, ti, TWO);
170 await expect(page.locator('.todo-count')).toHaveText('2 items left');
171 });
172});
173
174test.describe('Clear completed button', () => {
175 test.beforeEach(async ({ page }, ti) => addDefaultTodos(page, ti, DEFAULTS));
176
177 test('should display the correct text', async ({ page }, ti) => {
178 await toggle(page, ti, todos(page).nth(0));
179 await expect(page.locator('.clear-completed')).toHaveText('Clear completed');
180 });
181
182 test('should remove completed items when clicked', async ({ page }, ti) => {
183 await toggle(page, ti, todos(page).nth(1));
184 await clearCompleted(page, ti);
185 await expect(todos(page).locator('label')).toHaveText([ONE, THREE]);
186 });
187
188 test('should be hidden when there are no items that are completed', async ({ page }, ti) => {
189 await toggle(page, ti, todos(page).nth(1));
190 await expect(page.locator('.clear-completed')).toBeVisible();
191 await clearCompleted(page, ti);
192 await expect(page.locator('.clear-completed')).toBeHidden();
193 });
194});
195
196test.describe('Persistence', () => {
197 test('should persist its data', async ({ page }, ti) => {
198 await addDefaultTodos(page, ti, [ONE, TWO]);
199 await toggle(page, ti, todos(page).nth(0));
200 const check = async () => {
201 await expect(todos(page).nth(0)).toContainText(ONE);
202 await expect(todos(page).nth(0)).toHaveClass(/\bcompleted\b/);
203 await expect(todos(page).nth(1)).toContainText(TWO);
204 await expect(todos(page).nth(1)).not.toHaveClass(/\bcompleted\b/);
205 };
206 await check();
207 await page.reload();
208 await check();
209 });
210});
211
212test.describe('Routing', () => {
213 test.beforeEach(async ({ page }, ti) => addDefaultTodos(page, ti, DEFAULTS));
214
215 test('should allow me to display active items', async ({ page }, ti) => {
216 await toggle(page, ti, todos(page).nth(1));
217 await gotoFilter(page, 'Active');
218 await expect(todos(page).locator('label')).toHaveText([ONE, THREE]);
219 });
220
221 test('should respect the back button', async ({ page }, ti) => {
222 await toggle(page, ti, todos(page).nth(1));
223 await gotoFilter(page, 'All');
224 await expect(todos(page)).toHaveCount(3);
225 await gotoFilter(page, 'Active');
226 await gotoFilter(page, 'Completed');
227 await expect(todos(page)).toHaveCount(1);
228 await page.goBack();
229 await expect(todos(page)).toHaveCount(2);
230 await page.goBack();
231 await expect(todos(page)).toHaveCount(3);
232 });
233
234 test('should allow me to display completed items', async ({ page }, ti) => {
235 await toggle(page, ti, todos(page).nth(1));
236 await gotoFilter(page, 'Completed');
237 await expect(todos(page)).toHaveCount(1);
238 });
239
240 test('should allow me to display all items', async ({ page }, ti) => {
241 await toggle(page, ti, todos(page).nth(1));
242 await gotoFilter(page, 'Active');
243 await gotoFilter(page, 'Completed');
244 await gotoFilter(page, 'All');
245 await expect(todos(page)).toHaveCount(3);
246 });
247
248 test('should highlight the currently applied filter', async ({ page }) => {
249 const link = (name: string) => page.locator('.filters a', { hasText: name });
250 await expect(link('All')).toHaveClass(/\bselected\b/);
251 await gotoFilter(page, 'Active');
252 await expect(link('Active')).toHaveClass(/\bselected\b/);
253 await gotoFilter(page, 'Completed');
254 await expect(link('Completed')).toHaveClass(/\bselected\b/);
255 });
256});
257