Skip to content

Commit 015767f

Browse files
authoredMay 2, 2024··
feat: ✨ add support for playwright 1.43 (#358)
- Replaces `inputs/outputs` with `props/on` to align with Playwright CT for other frameworks. - Drops spying system (which is not possible anymore as we can't change `mount` returned value with the new version of playwright)
1 parent 5c60044 commit 015767f

16 files changed

+444
-634
lines changed
 

‎nx.json

+12-45
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,16 @@
2828
},
2929
"targetDefaults": {
3030
"build": {
31-
"dependsOn": [
32-
"^build"
33-
],
34-
"inputs": [
35-
"production",
36-
"^production"
37-
],
31+
"dependsOn": ["^build"],
32+
"inputs": ["production", "^production"],
3833
"cache": true
3934
},
4035
"e2e": {
41-
"inputs": [
42-
"default",
43-
"^production"
44-
],
36+
"inputs": ["default", "^production"],
4537
"cache": true
4638
},
4739
"@nx/jest:jest": {
48-
"inputs": [
49-
"default",
50-
"^production",
51-
"{workspaceRoot}/jest.preset.js"
52-
],
40+
"inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"],
5341
"cache": true,
5442
"options": {
5543
"passWithNoTests": true
@@ -62,43 +50,27 @@
6250
}
6351
},
6452
"@nx/eslint:lint": {
65-
"inputs": [
66-
"default",
67-
"{workspaceRoot}/.eslintrc.json"
68-
],
53+
"inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
6954
"cache": true
7055
},
7156
"@nx/rollup:rollup": {
7257
"cache": true,
73-
"dependsOn": [
74-
"^build"
75-
],
76-
"inputs": [
77-
"production",
78-
"^production"
79-
]
58+
"dependsOn": ["^build"],
59+
"inputs": ["production", "^production"]
8060
},
8161
"@nx/vite:test": {
8262
"cache": true,
83-
"inputs": [
84-
"default",
85-
"^production"
86-
]
63+
"inputs": ["default", "^production"]
8764
},
8865
"@nx/js:release-publish": {
89-
"dependsOn": [
90-
"build"
91-
],
66+
"dependsOn": ["build"],
9267
"options": {
9368
"packageRoot": "dist/{projectRoot}"
9469
}
9570
}
9671
},
9772
"namedInputs": {
98-
"default": [
99-
"{projectRoot}/**/*",
100-
"sharedGlobals"
101-
],
73+
"default": ["{projectRoot}/**/*", "sharedGlobals"],
10274
"sharedGlobals": [
10375
"{workspaceRoot}/workspace.json",
10476
"{workspaceRoot}/tsconfig.base.json",
@@ -135,10 +107,7 @@
135107
"releaseTagPattern": "{projectName}-{version}"
136108
},
137109
"swc-angular": {
138-
"projects": [
139-
"swc-angular",
140-
"swc-angular-plugin"
141-
],
110+
"projects": ["swc-angular", "swc-angular-plugin"],
142111
"projectsRelationship": "fixed",
143112
"releaseTagPattern": "swc-angular-{version}"
144113
}
@@ -163,9 +132,7 @@
163132
"plugins": [
164133
{
165134
"plugin": "@nx/vite/plugin",
166-
"exclude": [
167-
"tests/swc-angular-wide/**/*"
168-
]
135+
"exclude": ["tests/swc-angular-wide/**/*"]
169136
}
170137
]
171138
}

‎package.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
"@angular/platform-browser": "17.3.2",
4545
"@angular/platform-browser-dynamic": "17.3.2",
4646
"@angular/router": "17.3.2",
47+
"@babel/core": "7.23.9",
48+
"@babel/helper-plugin-utils": "^7.24.5",
49+
"@types/babel__helper-plugin-utils": "^7.10.3",
4750
"rxjs": "7.8.1",
4851
"tslib": "2.4.0",
4952
"zone.js": "0.14.4"
@@ -61,6 +64,7 @@
6164
"@angular/language-service": "17.3.2",
6265
"@commitlint/cli": "^19.0.0",
6366
"@commitlint/config-angular": "^19.0.0",
67+
"@jscutlery/playwright-ct-angular": "link:dist/packages/playwright-ct-angular",
6468
"@monodon/rust": "1.4.0",
6569
"@nx/angular": "18.3.2",
6670
"@nx/cypress": "18.3.2",
@@ -76,13 +80,14 @@
7680
"@nx/vite": "18.3.2",
7781
"@nx/web": "18.3.2",
7882
"@nx/workspace": "18.3.2",
79-
"@playwright/experimental-ct-core": "^1.39.0",
80-
"@playwright/test": "^1.39.0",
83+
"@playwright/experimental-ct-core": "1.43.1",
84+
"@playwright/test": "1.43.1",
8185
"@schematics/angular": "17.3.6",
8286
"@swc-node/register": "1.9.0",
8387
"@swc/core": "1.4.17",
8488
"@swc/helpers": "0.5.11",
8589
"@swc/jest": "0.2.36",
90+
"@types/babel__core": "^7.20.5",
8691
"@types/jest": "29.5.12",
8792
"@types/node": "^20.0.0",
8893
"@typescript-eslint/eslint-plugin": "7.8.0",

‎packages/playwright-ct-angular/README.md

+37-36
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# Playwright Component Testing for Angular _(experimental)_
22

3-
This library brings **Angular support** to [Playwright's **experimental** Component Testing](https://playwright.dev/docs/test-components).
3+
This library brings **Angular support** to [Playwright's **experimental
4+
** Component Testing](https://playwright.dev/docs/test-components).
45

56
This will allow us to test our Angular components with Playwright without building the whole app and with more control.
67

78
`@jscutlery/playwright-ct-angular` currently supports:
89

910
-**Testing [Versatile Angular Components](https://marmicode.io/blog/versatile-angular)**
10-
- 🎛 **Passing type-safe inputs to the tested components**
11+
- 🎛 **Passing type-safe inputs to the tested components**
1112
- 🎭 **Spying on component outputs in a type-safe fashion**
1213

1314
https://user-images.githubusercontent.com/2674658/206226065-ba856329-dda7-43b1-9c28-4416b190f4d4.mp4
@@ -39,37 +40,19 @@ import { expect, test } from '@jscutlery/playwright-ct-angular';
3940
import { GreetingsComponent } from './greetings.component';
4041

4142
test('<jc-greetings> should be polite', async ({ mount }) => {
42-
const locator = await mount(GreetingsComponent, {inputs: {name: 'Edouard'}});
43+
const locator = await mount(GreetingsComponent, { props: { name: 'Edouard' } });
4344
expect(locator).toHaveText('👋 Hello Edouard!');
4445
});
4546
```
4647

47-
## 🎭 Spying on outputs
48-
49-
`mount()` will automatically create a `jest-mock`'s spy, subscribe to the outputs given through the `spyOutputs`
50-
option and return them in the `spies` property, in a type-safe way showing only the outputs that are spied on.
51-
52-
Note that the `spyOutputs` is type-safe and will only allow properties that exist on the component.
53-
54-
```ts
55-
import { expect, test } from '@jscutlery/playwright-ct-angular';
56-
import { NameEditorComponent } from './name-editor.component';
57-
58-
test('<jc-name-editor> should be polite', async ({ mount }) => {
59-
const locator = await mount(NameEditorComponent, {spyOutputs: ['nameChange']});
60-
61-
await locator.getByLabel('Name').type('Edouard');
62-
63-
expect(locator.spies.nameChange).lastCalledWith('Edouard');
64-
});
65-
```
66-
6748
### Passing output callbacks
6849

69-
We can also pass custom output callback functions for some extreme cases or if we want to use a custom spy implementation for example or just debug.
50+
We can also pass custom output callback functions for some extreme cases or if we want to use a custom spy
51+
implementation for example or just debug.
52+
7053
```ts
7154
await mount(NameEditorComponent, {
72-
outputs: {
55+
on: {
7356
nameChange(name) {
7457
console.log(name);
7558
}
@@ -89,7 +72,7 @@ import { RecipeSearchTestContainer } from './recipe-search.test-container';
8972

9073
test('...', async ({ mount }) => {
9174
await mount(RecipeSearchTestContainer, {
92-
inputs: {
75+
props: {
9376
recipes: [
9477
beer,
9578
burger
@@ -140,24 +123,30 @@ We can also customize the shared `playwright/index.html` nearby.
140123
### Specific Styles
141124

142125
If we want to load some specific styles for a single test, we might prefer using a test container component:
126+
143127
```ts
144128
import styles from './some-styles.css';
129+
145130
@Component({
146131
template: '<jc-greetings></jc-greetings>',
147132
encapsulation: ViewEncapsulation.None,
148133
styles: [styles]
149134
})
150-
class GreetingsTestContainer {}
135+
class GreetingsTestContainer {
136+
}
151137
```
152138

153139
### Angular Material & Angular Libraries with styles
154140

155141
As mentioned in [Versatile Angular Style Blog Post](https://marmicode.io/blog/versatile-angular),
156-
Angular Material and other Angular libraries might use a [Conditional "style" Export](https://nodejs.org/api/packages.html#conditional-exports)
142+
Angular Material and other Angular libraries might use
143+
a [Conditional "style" Export](https://nodejs.org/api/packages.html#conditional-exports)
157144
that allows us to import prebuilt styles
158-
_(Cf. [Angular Package Format](https://angular.io/guide/angular-package-format) [managing assets in a library](https://angular.io/guide/creating-libraries#managing-assets-in-a-library))_.
145+
_(
146+
Cf. [Angular Package Format](https://angular.io/guide/angular-package-format) [managing assets in a library](https://angular.io/guide/creating-libraries#managing-assets-in-a-library))_.
159147

160148
In that case, we can add the following configuration to our `playwright-ct.config.ts`:
149+
161150
```ts
162151
const config: PlaywrightTestConfig = {
163152
// ...
@@ -174,11 +163,13 @@ const config: PlaywrightTestConfig = {
174163
```
175164

176165
## More examples
166+
177167
Cf. [/tests/playwright-ct-angular-wide/src](https://github.com/jscutlery/devkit/tree/main/tests/playwright-ct-angular-wide/src)
178168

179169
# ⚠️ Known Limitations
180170

181-
The way Playwright Component Testing works is different from the way things work with Karma, Jest, Vitest, Cypress etc...
171+
The way Playwright Component Testing works is different from the way things work with Karma, Jest, Vitest, Cypress
172+
etc...
182173
Playwright Component Testing tests run in a Node.js environment while the component is rendered in a browser.
183174

184175
This causes a couple of limitations as we can't directly access the `TestBed's` or the component's internals,
@@ -187,13 +178,15 @@ and we can only exchange serializable data with the component.
187178
## 🪄 The Magic Behind the Scenes
188179

189180
The magical workaround behind the scenes is that at build time:
181+
190182
1. Playwright analyses all the calls to `mount()`,
191183
2. it grabs the first parameter (the component class),
192184
3. replaces the component class with a unique string (constructed from the component class name and es-module),
193185
4. adds the component's es-module to Vite entrypoints,
194186
5. and finally creates a map matching each unique string to the right es-module.
195187

196-
This way, when calling `mount()`, Playwright with communicate the unique string to the browser who will know which es-module to load.
188+
This way, when calling `mount()`, Playwright with communicate the unique string to the browser who will know which
189+
es-module to load.
197190

198191
Cf. https://youtu.be/y3YxX4sFJbM
199192
Cf. https://github.com/microsoft/playwright/blob/cac67fb94f2c8a0ee82878054c39790e660f17ca/packages/playwright-test/src/tsxTransform.ts#L153
@@ -212,36 +205,43 @@ await mount(cmp);
212205

213206
```ts
214207
// 🛑 this won't work
215-
test(MyComponent.name, async ({ mount }) => {});
208+
test(MyComponent.name, async ({ mount }) => {
209+
});
216210
```
217211

218212
### ...declare components in the same file.
219213

220214
```ts
221215
// 🛑 this won't work
222-
@Component({...})
223-
class GreetingsComponent {}
216+
@Component({ ... })
217+
class GreetingsComponent {
218+
}
224219

225220
test('<jc-greetings>', async ({ mount }) => {
226221
await mount(GreetingsComponent);
227222
});
228223
```
229224

230225
### ...pass any symbol other than a component class
226+
231227
This makes the following impossible:
228+
232229
- passing providers to the `mount()` function
233230
- passing modules to the `mount()` function (this is what currently makes the usage of mounting templates impossible)
234231
- use non-standalone components
235232

236233
### ...use non-[Versatile Angular Style](https://marmicode.io/blog/versatile-angular)
234+
237235
We'll need a vite plugin to support traditional DI, external templates and stylesheets etc...
238236

239237
## 🔮 Future workarounds
240238

241239
If you really can't make it to [Versatile Angular Style](https://marmicode.io/blog/versatile-angular),
242-
we can think of a couple of workarounds like using a vite plugin like **Brandon Roberts' [vite-plugin-angular](https://github.com/analogjs/analog/tree/main/packages/vite-plugin-angular)**
240+
we can think of a couple of workarounds like using a vite plugin like **Brandon
241+
Roberts' [vite-plugin-angular](https://github.com/analogjs/analog/tree/main/packages/vite-plugin-angular)**
243242

244-
We could also implement some custom transform that registers the providers and import modules, like Playwright does for the component class.
243+
We could also implement some custom transform that registers the providers and import modules, like Playwright does for
244+
the component class.
245245

246246
# Setup
247247

@@ -265,6 +265,7 @@ yarn add -D @jscutlery/playwright-ct-angular @playwright/test # or npm install -
265265
## 🛠 Configure
266266

267267
Update `playwright-ct-config.ts` and replace:
268+
268269
```ts
269270
import type { PlaywrightTestConfig } from '@playwright/experimental-ct-react';
270271
import { devices } from '@playwright/experimental-ct-react';

‎packages/playwright-ct-angular/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "@jscutlery/playwright-ct-angular",
33
"version": "0.3.0",
44
"dependencies": {
5+
"@babel/core": "~7.23.0",
56
"jest-mock": "^28.1.3",
67
"rxjs": "^7.5.7",
78
"vite": "^3.2.1"

0 commit comments

Comments
 (0)
Please sign in to comment.