Skip to content

Commit 76665f3

Browse files
test(devtools): add e2e tests for devtools
1 parent 6759f9d commit 76665f3

9 files changed

+141
-37
lines changed

.github/workflows/build.yml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ jobs:
2121
- run: pnpm install --frozen-lockfile
2222
- run: pnpm run lint:all
2323
- run: pnpm run test:all
24+
- run: pnpm run test:e2e
2425
- run: pnpm run build:all
2526
- run: ./integration-tests.sh
2627

apps/demo/e2e/devtools.spec.ts

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { test, expect } from '@playwright/test';
2+
import { Action } from '@ngrx/store';
3+
4+
test('has title', async ({ page }) => {
5+
await page.goto('');
6+
7+
await page.evaluate(() => {
8+
window['devtoolsSpy'] = [];
9+
10+
window['__REDUX_DEVTOOLS_EXTENSION__'] = {
11+
connect: () => {
12+
return {
13+
send: (data: Action) => {
14+
window['devtoolsSpy'].push(data);
15+
},
16+
};
17+
},
18+
};
19+
});
20+
await page.getByRole('link', { name: 'DevTools' }).click();
21+
await page
22+
.getByRole('row', { name: 'Go for a walk' })
23+
.getByRole('checkbox')
24+
.click();
25+
await page
26+
.getByRole('row', { name: 'Exercise' })
27+
.getByRole('checkbox')
28+
.click();
29+
30+
await expect(
31+
page.getByRole('region', { name: 'Go for a walk' })
32+
).toBeVisible();
33+
await expect(page.getByRole('region', { name: 'Exercise' })).toBeVisible();
34+
35+
await page
36+
.getByRole('row', { name: 'Go for a walk' })
37+
.getByRole('checkbox')
38+
.click();
39+
await page
40+
.getByRole('row', { name: 'Exercise' })
41+
.getByRole('checkbox')
42+
.click();
43+
44+
await expect(
45+
page.getByRole('region', { name: 'Go for a walk' })
46+
).toBeHidden();
47+
await expect(page.getByRole('region', { name: 'Exercise' })).toBeHidden();
48+
49+
const devtoolsActions = await page.evaluate(() => window['devtoolsSpy']);
50+
51+
expect(devtoolsActions).toEqual([
52+
{ type: 'add todo' },
53+
{ type: 'select todo 1' },
54+
{ type: 'Store Update' },
55+
{ type: 'select todo 4' },
56+
{ type: 'Store Update' },
57+
{ type: 'select todo 1' },
58+
{ type: 'Store Update' },
59+
{ type: 'select todo 4' },
60+
{ type: 'Store Update' },
61+
]);
62+
});

apps/demo/eslint.config.cjs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
const playwright = require('eslint-plugin-playwright');
12
const nx = require('@nx/eslint-plugin');
23
const baseConfig = require('../../eslint.config.cjs');
34

45
module.exports = [
6+
playwright.configs['flat/recommended'],
7+
58
...baseConfig,
69
...nx.configs['flat/angular'],
710
...nx.configs['flat/angular-template'],
@@ -25,5 +28,10 @@ module.exports = [
2528
},
2629
],
2730
},
28-
}
31+
},
32+
{
33+
files: ['**/*.ts', '**/*.js'],
34+
// Override or add rules here
35+
rules: {},
36+
},
2937
];

apps/demo/jest.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
export default {
32
displayName: 'demo',
43
preset: '../../jest.preset.js',
@@ -13,6 +12,7 @@ export default {
1312
},
1413
],
1514
},
15+
testPathIgnorePatterns: ['e2e'],
1616
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
1717
snapshotSerializers: [
1818
'jest-preset-angular/build/serializers/no-ng-attributes',

apps/demo/playwright.config.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { defineConfig, devices } from '@playwright/test';
2+
import * as path from 'node:path';
3+
4+
// For CI, you may want to set BASE_URL to the deployed application.
5+
const baseURL = process.env['BASE_URL'] || 'http://localhost:4200';
6+
7+
/**
8+
* Read environment variables from file.
9+
* https://github.com/motdotla/dotenv
10+
*/
11+
// require('dotenv').config();
12+
13+
/**
14+
* See https://playwright.dev/docs/test-configuration.
15+
*/
16+
export default defineConfig({
17+
use: {
18+
baseURL,
19+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
20+
trace: 'on-first-retry',
21+
},
22+
/* Run your local dev server before starting the tests */
23+
webServer: {
24+
command: 'pnpm start',
25+
url: 'http://localhost:4200',
26+
reuseExistingServer: true,
27+
cwd: path.join(__dirname, '../..'),
28+
},
29+
projects: [
30+
{
31+
name: 'chromium',
32+
use: { ...devices['Desktop Chrome'] },
33+
},
34+
],
35+
});

apps/demo/project.json

+12-16
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,14 @@
88
"targets": {
99
"build": {
1010
"executor": "@angular-devkit/build-angular:application",
11-
"outputs": [
12-
"{options.outputPath}"
13-
],
11+
"outputs": ["{options.outputPath}"],
1412
"options": {
1513
"outputPath": "dist/apps/demo",
1614
"index": "apps/demo/src/index.html",
1715
"browser": "apps/demo/src/main.ts",
18-
"polyfills": [
19-
"zone.js"
20-
],
16+
"polyfills": ["zone.js"],
2117
"tsConfig": "apps/demo/tsconfig.app.json",
22-
"assets": [
23-
"apps/demo/src/favicon.ico",
24-
"apps/demo/src/assets"
25-
],
18+
"assets": ["apps/demo/src/favicon.ico", "apps/demo/src/assets"],
2619
"styles": [
2720
"@angular/material/prebuilt-themes/deeppurple-amber.css",
2821
"apps/demo/src/styles.css"
@@ -73,18 +66,21 @@
7366
},
7467
"lint": {
7568
"executor": "@nx/eslint:lint",
76-
"outputs": [
77-
"{options.outputFile}"
78-
]
69+
"outputs": ["{options.outputFile}"]
7970
},
8071
"test": {
8172
"executor": "@nx/jest:jest",
82-
"outputs": [
83-
"{workspaceRoot}/coverage/{projectRoot}"
84-
],
73+
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
8574
"options": {
8675
"jestConfig": "apps/demo/jest.config.ts"
8776
}
77+
},
78+
"e2e": {
79+
"executor": "@nx/playwright:playwright",
80+
"outputs": ["{workspaceRoot}/dist/.playwright/apps/demo"],
81+
"options": {
82+
"config": "apps/demo/playwright.config.ts"
83+
}
8884
}
8985
}
9086
}

apps/demo/src/app/devtools/todo-detail.component.ts

+11-12
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ const TodoDetailStore = signalStore(
2323

2424
@Component({
2525
selector: 'demo-todo-detail',
26-
template: ` <mat-card>
27-
<mat-card-title>{{ todo().name }}</mat-card-title>
28-
<mat-card-content>
29-
<textarea>{{ todo().description }}</textarea>
30-
</mat-card-content>
31-
</mat-card>`,
26+
template: ` <section [attr.aria-label]="todo().name">
27+
<mat-card>
28+
<mat-card-title>{{ todo().name }}</mat-card-title>
29+
<mat-card-content>
30+
<textarea>{{ todo().description }}</textarea>
31+
</mat-card-content>
32+
</mat-card>
33+
</section>`,
3234
imports: [MatCardModule],
3335
providers: [TodoDetailStore],
3436
styles: `
@@ -42,11 +44,8 @@ export class TodoDetailComponent {
4244
todo = input.required<Todo>();
4345

4446
constructor() {
45-
effect(
46-
() => {
47-
renameDevtoolsName(this.#todoDetailStore, `todo-${this.todo().id}`);
48-
},
49-
{ allowSignalWrites: true }
50-
);
47+
effect(() => {
48+
renameDevtoolsName(this.#todoDetailStore, `todo-${this.todo().id}`);
49+
});
5150
}
5251
}

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"start": "nx serve demo",
77
"lint:all": "nx run-many --targets=lint",
88
"test:all": "nx run-many --targets=test",
9+
"test:e2e": "nx e2e demo",
910
"build:all": "nx run-many --targets=build",
1011
"verify:all": "nx run-many --targets=lint,test,build"
1112
},
@@ -63,7 +64,7 @@
6364
"autoprefixer": "^10.4.19",
6465
"eslint": "^9.8.0",
6566
"eslint-config-prettier": "^9.0.0",
66-
"eslint-plugin-playwright": "^0.15.3",
67+
"eslint-plugin-playwright": "^1.6.2",
6768
"eslint-plugin-unused-imports": "^4.1.4",
6869
"husky": "^9.0.11",
6970
"jest": "^29.7.0",

pnpm-lock.yaml

+8-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)