chore: setup coupling graph generation
This commit is contained in:
parent
ad7b3d3d16
commit
9e3239d742
3 changed files with 87 additions and 438 deletions
85
graph.svg
Normal file
85
graph.svg
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 8.0.2 (20230410.1723)
|
||||
-->
|
||||
<!-- Title: G Pages: 1 -->
|
||||
<svg width="677pt" height="230pt"
|
||||
viewBox="0.00 0.00 677.20 230.20" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(21.6 208.6)">
|
||||
<title>G</title>
|
||||
<polygon fill="#111111" stroke="none" points="-21.6,21.6 -21.6,-208.6 655.6,-208.6 655.6,21.6 -21.6,21.6"/>
|
||||
<!-- components/Button/Button.stories.tsx -->
|
||||
<g id="node1" class="node">
|
||||
<title>components/Button/Button.stories.tsx</title>
|
||||
<path fill="none" stroke="#c6c5fe" d="M233.33,-64C233.33,-64 7.67,-64 7.67,-64 3.83,-64 0,-60.17 0,-56.33 0,-56.33 0,-48.67 0,-48.67 0,-44.83 3.83,-41 7.67,-41 7.67,-41 233.33,-41 233.33,-41 237.17,-41 241,-44.83 241,-48.67 241,-48.67 241,-56.33 241,-56.33 241,-60.17 237.17,-64 233.33,-64"/>
|
||||
<text text-anchor="middle" x="120.5" y="-48.8" font-family="Arial" font-size="14.00" fill="#c6c5fe">components/Button/Button.stories.tsx</text>
|
||||
</g>
|
||||
<!-- components/Button/Button.tsx -->
|
||||
<g id="node2" class="node">
|
||||
<title>components/Button/Button.tsx</title>
|
||||
<path fill="none" stroke="#cfffac" d="M466.33,-43C466.33,-43 284.67,-43 284.67,-43 280.83,-43 277,-39.17 277,-35.33 277,-35.33 277,-27.67 277,-27.67 277,-23.83 280.83,-20 284.67,-20 284.67,-20 466.33,-20 466.33,-20 470.17,-20 474,-23.83 474,-27.67 474,-27.67 474,-35.33 474,-35.33 474,-39.17 470.17,-43 466.33,-43"/>
|
||||
<text text-anchor="middle" x="375.5" y="-27.8" font-family="Arial" font-size="14.00" fill="#cfffac">components/Button/Button.tsx</text>
|
||||
</g>
|
||||
<!-- components/Button/Button.stories.tsx->components/Button/Button.tsx -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>components/Button/Button.stories.tsx->components/Button/Button.tsx</title>
|
||||
<path fill="none" stroke="#757575" d="M241.33,-42.55C249.59,-41.87 257.89,-41.18 266.08,-40.5"/>
|
||||
<polygon fill="#757575" stroke="#757575" points="266.1,-43.93 275.78,-39.61 265.53,-36.95 266.1,-43.93"/>
|
||||
</g>
|
||||
<!-- components/Tooltip/Tooltip.tsx -->
|
||||
<g id="node3" class="node">
|
||||
<title>components/Tooltip/Tooltip.tsx</title>
|
||||
<path fill="none" stroke="#cfffac" d="M211.33,-105C211.33,-105 29.67,-105 29.67,-105 25.83,-105 22,-101.17 22,-97.33 22,-97.33 22,-89.67 22,-89.67 22,-85.83 25.83,-82 29.67,-82 29.67,-82 211.33,-82 211.33,-82 215.17,-82 219,-85.83 219,-89.67 219,-89.67 219,-97.33 219,-97.33 219,-101.17 215.17,-105 211.33,-105"/>
|
||||
<text text-anchor="middle" x="120.5" y="-89.8" font-family="Arial" font-size="14.00" fill="#cfffac">components/Tooltip/Tooltip.tsx</text>
|
||||
</g>
|
||||
<!-- pages/_app.test.tsx -->
|
||||
<g id="node4" class="node">
|
||||
<title>pages/_app.test.tsx</title>
|
||||
<path fill="none" stroke="#c6c5fe" d="M179.83,-146C179.83,-146 61.17,-146 61.17,-146 57.33,-146 53.5,-142.17 53.5,-138.33 53.5,-138.33 53.5,-130.67 53.5,-130.67 53.5,-126.83 57.33,-123 61.17,-123 61.17,-123 179.83,-123 179.83,-123 183.67,-123 187.5,-126.83 187.5,-130.67 187.5,-130.67 187.5,-138.33 187.5,-138.33 187.5,-142.17 183.67,-146 179.83,-146"/>
|
||||
<text text-anchor="middle" x="120.5" y="-130.8" font-family="Arial" font-size="14.00" fill="#c6c5fe">pages/_app.test.tsx</text>
|
||||
</g>
|
||||
<!-- pages/_app.tsx -->
|
||||
<g id="node5" class="node">
|
||||
<title>pages/_app.tsx</title>
|
||||
<path fill="none" stroke="#c6c5fe" d="M421.83,-146C421.83,-146 329.17,-146 329.17,-146 325.33,-146 321.5,-142.17 321.5,-138.33 321.5,-138.33 321.5,-130.67 321.5,-130.67 321.5,-126.83 325.33,-123 329.17,-123 329.17,-123 421.83,-123 421.83,-123 425.67,-123 429.5,-126.83 429.5,-130.67 429.5,-130.67 429.5,-138.33 429.5,-138.33 429.5,-142.17 425.67,-146 421.83,-146"/>
|
||||
<text text-anchor="middle" x="375.5" y="-130.8" font-family="Arial" font-size="14.00" fill="#c6c5fe">pages/_app.tsx</text>
|
||||
</g>
|
||||
<!-- pages/_app.test.tsx->pages/_app.tsx -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>pages/_app.test.tsx->pages/_app.tsx</title>
|
||||
<path fill="none" stroke="#757575" d="M187.97,-134.5C225.82,-134.5 272.99,-134.5 310.43,-134.5"/>
|
||||
<polygon fill="#757575" stroke="#757575" points="310.21,-138 320.21,-134.5 310.21,-131 310.21,-138"/>
|
||||
</g>
|
||||
<!-- styles/tailwind.css -->
|
||||
<g id="node6" class="node">
|
||||
<title>styles/tailwind.css</title>
|
||||
<path fill="none" stroke="#cfffac" d="M626.33,-146C626.33,-146 517.67,-146 517.67,-146 513.83,-146 510,-142.17 510,-138.33 510,-138.33 510,-130.67 510,-130.67 510,-126.83 513.83,-123 517.67,-123 517.67,-123 626.33,-123 626.33,-123 630.17,-123 634,-126.83 634,-130.67 634,-130.67 634,-138.33 634,-138.33 634,-142.17 630.17,-146 626.33,-146"/>
|
||||
<text text-anchor="middle" x="572" y="-130.8" font-family="Arial" font-size="14.00" fill="#cfffac">styles/tailwind.css</text>
|
||||
</g>
|
||||
<!-- pages/_app.tsx->styles/tailwind.css -->
|
||||
<g id="edge3" class="edge">
|
||||
<title>pages/_app.tsx->styles/tailwind.css</title>
|
||||
<path fill="none" stroke="#757575" d="M429.91,-134.5C451.26,-134.5 476.19,-134.5 499.1,-134.5"/>
|
||||
<polygon fill="#757575" stroke="#757575" points="498.81,-138 508.81,-134.5 498.81,-131 498.81,-138"/>
|
||||
</g>
|
||||
<!-- pages/api/health.ts -->
|
||||
<g id="node7" class="node">
|
||||
<title>pages/api/health.ts</title>
|
||||
<path fill="none" stroke="#cfffac" d="M177.83,-187C177.83,-187 63.17,-187 63.17,-187 59.33,-187 55.5,-183.17 55.5,-179.33 55.5,-179.33 55.5,-171.67 55.5,-171.67 55.5,-167.83 59.33,-164 63.17,-164 63.17,-164 177.83,-164 177.83,-164 181.67,-164 185.5,-167.83 185.5,-171.67 185.5,-171.67 185.5,-179.33 185.5,-179.33 185.5,-183.17 181.67,-187 177.83,-187"/>
|
||||
<text text-anchor="middle" x="120.5" y="-171.8" font-family="Arial" font-size="14.00" fill="#cfffac">pages/api/health.ts</text>
|
||||
</g>
|
||||
<!-- pages/index.tsx -->
|
||||
<g id="node8" class="node">
|
||||
<title>pages/index.tsx</title>
|
||||
<path fill="none" stroke="#c6c5fe" d="M167.83,-23C167.83,-23 73.17,-23 73.17,-23 69.33,-23 65.5,-19.17 65.5,-15.33 65.5,-15.33 65.5,-7.67 65.5,-7.67 65.5,-3.83 69.33,0 73.17,0 73.17,0 167.83,0 167.83,0 171.67,0 175.5,-3.83 175.5,-7.67 175.5,-7.67 175.5,-15.33 175.5,-15.33 175.5,-19.17 171.67,-23 167.83,-23"/>
|
||||
<text text-anchor="middle" x="120.5" y="-7.8" font-family="Arial" font-size="14.00" fill="#c6c5fe">pages/index.tsx</text>
|
||||
</g>
|
||||
<!-- pages/index.tsx->components/Button/Button.tsx -->
|
||||
<g id="edge4" class="edge">
|
||||
<title>pages/index.tsx->components/Button/Button.tsx</title>
|
||||
<path fill="none" stroke="#757575" d="M175.77,-15.79C202.08,-17.87 234.64,-20.44 265.75,-22.9"/>
|
||||
<polygon fill="#757575" stroke="#757575" points="265.43,-26.47 275.67,-23.77 265.98,-19.49 265.43,-26.47"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
|
|
@ -18,7 +18,8 @@
|
|||
"e2e:ui": "playwright test --ui",
|
||||
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
|
||||
"postinstall": "npx patch-package -y",
|
||||
"preinstall": "npx only-allow yarn --yes"
|
||||
"preinstall": "npx only-allow yarn --yes",
|
||||
"coupling-graph": "npx madge --extensions js,jsx,ts,tsx,css,md,mdx ./ --exclude '.next|tailwind.config.js|reset.d.ts|prettier.config.js|postcss.config.js|playwright.config.ts|next.config.js|next-env.d.ts|instrumentation.ts|e2e/|README.md|.storybook/|.eslintrc.js' --image graph.svg"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-accordion": "^1.1.1",
|
||||
|
|
|
|||
|
|
@ -1,437 +0,0 @@
|
|||
import { test, expect, type Page } from '@playwright/test';
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('https://demo.playwright.dev/todomvc');
|
||||
});
|
||||
|
||||
const TODO_ITEMS = [
|
||||
'buy some cheese',
|
||||
'feed the cat',
|
||||
'book a doctors appointment'
|
||||
];
|
||||
|
||||
test.describe('New Todo', () => {
|
||||
test('should allow me to add todo items', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create 1st todo.
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Make sure the list only has one todo item.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText([
|
||||
TODO_ITEMS[0]
|
||||
]);
|
||||
|
||||
// Create 2nd todo.
|
||||
await newTodo.fill(TODO_ITEMS[1]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Make sure the list now has two todo items.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText([
|
||||
TODO_ITEMS[0],
|
||||
TODO_ITEMS[1]
|
||||
]);
|
||||
|
||||
await checkNumberOfTodosInLocalStorage(page, 2);
|
||||
});
|
||||
|
||||
test('should clear text input field when an item is added', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create one todo item.
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
// Check that input is empty.
|
||||
await expect(newTodo).toBeEmpty();
|
||||
await checkNumberOfTodosInLocalStorage(page, 1);
|
||||
});
|
||||
|
||||
test('should append new items to the bottom of the list', async ({ page }) => {
|
||||
// Create 3 items.
|
||||
await createDefaultTodos(page);
|
||||
|
||||
// create a todo count locator
|
||||
const todoCount = page.getByTestId('todo-count')
|
||||
|
||||
// Check test using different methods.
|
||||
await expect(page.getByText('3 items left')).toBeVisible();
|
||||
await expect(todoCount).toHaveText('3 items left');
|
||||
await expect(todoCount).toContainText('3');
|
||||
await expect(todoCount).toHaveText(/3/);
|
||||
|
||||
// Check all items in one call.
|
||||
await expect(page.getByTestId('todo-title')).toHaveText(TODO_ITEMS);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Mark all as completed', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should allow me to mark all items as completed', async ({ page }) => {
|
||||
// Complete all todos.
|
||||
await page.getByLabel('Mark all as complete').check();
|
||||
|
||||
// Ensure all todos have 'completed' class.
|
||||
await expect(page.getByTestId('todo-item')).toHaveClass(['completed', 'completed', 'completed']);
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should allow me to clear the complete state of all items', async ({ page }) => {
|
||||
const toggleAll = page.getByLabel('Mark all as complete');
|
||||
// Check and then immediately uncheck.
|
||||
await toggleAll.check();
|
||||
await toggleAll.uncheck();
|
||||
|
||||
// Should be no completed classes.
|
||||
await expect(page.getByTestId('todo-item')).toHaveClass(['', '', '']);
|
||||
});
|
||||
|
||||
test('complete all checkbox should update state when items are completed / cleared', async ({ page }) => {
|
||||
const toggleAll = page.getByLabel('Mark all as complete');
|
||||
await toggleAll.check();
|
||||
await expect(toggleAll).toBeChecked();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
|
||||
// Uncheck first todo.
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
await firstTodo.getByRole('checkbox').uncheck();
|
||||
|
||||
// Reuse toggleAll locator and make sure its not checked.
|
||||
await expect(toggleAll).not.toBeChecked();
|
||||
|
||||
await firstTodo.getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 3);
|
||||
|
||||
// Assert the toggle all is checked again.
|
||||
await expect(toggleAll).toBeChecked();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Item', () => {
|
||||
|
||||
test('should allow me to mark items as complete', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create two items.
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
// Check first item.
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
await firstTodo.getByRole('checkbox').check();
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
|
||||
// Check second item.
|
||||
const secondTodo = page.getByTestId('todo-item').nth(1);
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await secondTodo.getByRole('checkbox').check();
|
||||
|
||||
// Assert completed class.
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
await expect(secondTodo).toHaveClass('completed');
|
||||
});
|
||||
|
||||
test('should allow me to un-mark items as complete', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// Create two items.
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
const firstTodo = page.getByTestId('todo-item').nth(0);
|
||||
const secondTodo = page.getByTestId('todo-item').nth(1);
|
||||
const firstTodoCheckbox = firstTodo.getByRole('checkbox');
|
||||
|
||||
await firstTodoCheckbox.check();
|
||||
await expect(firstTodo).toHaveClass('completed');
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
await firstTodoCheckbox.uncheck();
|
||||
await expect(firstTodo).not.toHaveClass('completed');
|
||||
await expect(secondTodo).not.toHaveClass('completed');
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 0);
|
||||
});
|
||||
|
||||
test('should allow me to edit an item', async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
const secondTodo = todoItems.nth(1);
|
||||
await secondTodo.dblclick();
|
||||
await expect(secondTodo.getByRole('textbox', { name: 'Edit' })).toHaveValue(TODO_ITEMS[1]);
|
||||
await secondTodo.getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await secondTodo.getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
// Explicitly assert the new text value.
|
||||
await expect(todoItems).toHaveText([
|
||||
TODO_ITEMS[0],
|
||||
'buy some sausages',
|
||||
TODO_ITEMS[2]
|
||||
]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Editing', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should hide other controls when editing', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item').nth(1);
|
||||
await todoItem.dblclick();
|
||||
await expect(todoItem.getByRole('checkbox')).not.toBeVisible();
|
||||
await expect(todoItem.locator('label', {
|
||||
hasText: TODO_ITEMS[1],
|
||||
})).not.toBeVisible();
|
||||
await checkNumberOfTodosInLocalStorage(page, 3);
|
||||
});
|
||||
|
||||
test('should save edits on blur', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).dispatchEvent('blur');
|
||||
|
||||
await expect(todoItems).toHaveText([
|
||||
TODO_ITEMS[0],
|
||||
'buy some sausages',
|
||||
TODO_ITEMS[2],
|
||||
]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
|
||||
test('should trim entered text', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill(' buy some sausages ');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
await expect(todoItems).toHaveText([
|
||||
TODO_ITEMS[0],
|
||||
'buy some sausages',
|
||||
TODO_ITEMS[2],
|
||||
]);
|
||||
await checkTodosInLocalStorage(page, 'buy some sausages');
|
||||
});
|
||||
|
||||
test('should remove the item if an empty text string was entered', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Enter');
|
||||
|
||||
await expect(todoItems).toHaveText([
|
||||
TODO_ITEMS[0],
|
||||
TODO_ITEMS[2],
|
||||
]);
|
||||
});
|
||||
|
||||
test('should cancel edits on escape', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).dblclick();
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).fill('buy some sausages');
|
||||
await todoItems.nth(1).getByRole('textbox', { name: 'Edit' }).press('Escape');
|
||||
await expect(todoItems).toHaveText(TODO_ITEMS);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Counter', () => {
|
||||
test('should display the current number of todo items', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
// create a todo count locator
|
||||
const todoCount = page.getByTestId('todo-count')
|
||||
|
||||
await newTodo.fill(TODO_ITEMS[0]);
|
||||
await newTodo.press('Enter');
|
||||
|
||||
await expect(todoCount).toContainText('1');
|
||||
|
||||
await newTodo.fill(TODO_ITEMS[1]);
|
||||
await newTodo.press('Enter');
|
||||
await expect(todoCount).toContainText('2');
|
||||
|
||||
await checkNumberOfTodosInLocalStorage(page, 2);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Clear completed button', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
});
|
||||
|
||||
test('should display the correct text', async ({ page }) => {
|
||||
await page.locator('.todo-list li .toggle').first().check();
|
||||
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should remove completed items when clicked', async ({ page }) => {
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
await todoItems.nth(1).getByRole('checkbox').check();
|
||||
await page.getByRole('button', { name: 'Clear completed' }).click();
|
||||
await expect(todoItems).toHaveCount(2);
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
});
|
||||
|
||||
test('should be hidden when there are no items that are completed', async ({ page }) => {
|
||||
await page.locator('.todo-list li .toggle').first().check();
|
||||
await page.getByRole('button', { name: 'Clear completed' }).click();
|
||||
await expect(page.getByRole('button', { name: 'Clear completed' })).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Persistence', () => {
|
||||
test('should persist its data', async ({ page }) => {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
for (const item of TODO_ITEMS.slice(0, 2)) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
|
||||
const todoItems = page.getByTestId('todo-item');
|
||||
const firstTodoCheck = todoItems.nth(0).getByRole('checkbox');
|
||||
await firstTodoCheck.check();
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
await expect(firstTodoCheck).toBeChecked();
|
||||
await expect(todoItems).toHaveClass(['completed', '']);
|
||||
|
||||
// Ensure there is 1 completed item.
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
// Now reload.
|
||||
await page.reload();
|
||||
await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]);
|
||||
await expect(firstTodoCheck).toBeChecked();
|
||||
await expect(todoItems).toHaveClass(['completed', '']);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Routing', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await createDefaultTodos(page);
|
||||
// make sure the app had a chance to save updated todos in storage
|
||||
// before navigating to a new view, otherwise the items can get lost :(
|
||||
// in some frameworks like Durandal
|
||||
await checkTodosInLocalStorage(page, TODO_ITEMS[0]);
|
||||
});
|
||||
|
||||
test('should allow me to display active items', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item');
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
await expect(todoItem).toHaveCount(2);
|
||||
await expect(todoItem).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]);
|
||||
});
|
||||
|
||||
test('should respect the back button', async ({ page }) => {
|
||||
const todoItem = page.getByTestId('todo-item');
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
|
||||
await test.step('Showing all items', async () => {
|
||||
await page.getByRole('link', { name: 'All' }).click();
|
||||
await expect(todoItem).toHaveCount(3);
|
||||
});
|
||||
|
||||
await test.step('Showing active items', async () => {
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
});
|
||||
|
||||
await test.step('Showing completed items', async () => {
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
});
|
||||
|
||||
await expect(todoItem).toHaveCount(1);
|
||||
await page.goBack();
|
||||
await expect(todoItem).toHaveCount(2);
|
||||
await page.goBack();
|
||||
await expect(todoItem).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('should allow me to display completed items', async ({ page }) => {
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
await expect(page.getByTestId('todo-item')).toHaveCount(1);
|
||||
});
|
||||
|
||||
test('should allow me to display all items', async ({ page }) => {
|
||||
await page.getByTestId('todo-item').nth(1).getByRole('checkbox').check();
|
||||
await checkNumberOfCompletedTodosInLocalStorage(page, 1);
|
||||
await page.getByRole('link', { name: 'Active' }).click();
|
||||
await page.getByRole('link', { name: 'Completed' }).click();
|
||||
await page.getByRole('link', { name: 'All' }).click();
|
||||
await expect(page.getByTestId('todo-item')).toHaveCount(3);
|
||||
});
|
||||
|
||||
test('should highlight the currently applied filter', async ({ page }) => {
|
||||
await expect(page.getByRole('link', { name: 'All' })).toHaveClass('selected');
|
||||
|
||||
//create locators for active and completed links
|
||||
const activeLink = page.getByRole('link', { name: 'Active' });
|
||||
const completedLink = page.getByRole('link', { name: 'Completed' });
|
||||
await activeLink.click();
|
||||
|
||||
// Page change - active items.
|
||||
await expect(activeLink).toHaveClass('selected');
|
||||
await completedLink.click();
|
||||
|
||||
// Page change - completed items.
|
||||
await expect(completedLink).toHaveClass('selected');
|
||||
});
|
||||
});
|
||||
|
||||
async function createDefaultTodos(page: Page) {
|
||||
// create a new todo locator
|
||||
const newTodo = page.getByPlaceholder('What needs to be done?');
|
||||
|
||||
for (const item of TODO_ITEMS) {
|
||||
await newTodo.fill(item);
|
||||
await newTodo.press('Enter');
|
||||
}
|
||||
}
|
||||
|
||||
async function checkNumberOfTodosInLocalStorage(page: Page, expected: number) {
|
||||
return await page.waitForFunction(e => {
|
||||
return JSON.parse(localStorage['react-todos']).length === e;
|
||||
}, expected);
|
||||
}
|
||||
|
||||
async function checkNumberOfCompletedTodosInLocalStorage(page: Page, expected: number) {
|
||||
return await page.waitForFunction(e => {
|
||||
return JSON.parse(localStorage['react-todos']).filter((todo: any) => todo.completed).length === e;
|
||||
}, expected);
|
||||
}
|
||||
|
||||
async function checkTodosInLocalStorage(page: Page, title: string) {
|
||||
return await page.waitForFunction(t => {
|
||||
return JSON.parse(localStorage['react-todos']).map((todo: any) => todo.title).includes(t);
|
||||
}, title);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue