Skip to content

Commit 14cf000

Browse files
authored
feat: revive angular ct (#59)
* feat: revive angular ct
1 parent 27e09bd commit 14cf000

31 files changed

+6482
-6599
lines changed

.github/workflows/deploy.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ jobs:
1111
steps:
1212
- uses: pnpm/action-setup@v2
1313
with:
14-
version: '8.x'
14+
version: "9.x"
1515

16-
- uses: actions/checkout@v3
16+
- uses: actions/checkout@v4
1717

18-
- uses: actions/setup-node@v3
18+
- uses: actions/setup-node@v4
1919
with:
20-
node-version: '16.x'
21-
registry-url: 'https://registry.npmjs.org'
22-
scope: '@sand4rt'
20+
node-version: "18.x"
21+
registry-url: "https://registry.npmjs.org"
22+
scope: "@sand4rt"
2323

2424
- run: pnpm publish --filter @sand4rt/experimental-ct-angular --access public --no-git-checks
2525
env:

.github/workflows/test.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
name: Test
22
on:
33
push:
4-
branches: [ main, master ]
4+
branches: [main, master]
55
pull_request:
6-
branches: [ main, master ]
6+
branches: [main, master]
77
jobs:
88
test:
99
timeout-minutes: 60
1010
runs-on: ubuntu-latest
1111
steps:
1212
- uses: pnpm/action-setup@v2
1313
with:
14-
version: '8.x'
14+
version: "9.x"
1515

16-
- uses: actions/checkout@v3
16+
- uses: actions/checkout@v4
1717

18-
- uses: actions/setup-node@v3
18+
- uses: actions/setup-node@v4
1919
with:
20-
node-version: '16.x'
20+
node-version: "18.x"
2121

2222
- name: Install dependencies
2323
run: pnpm install --frozen-lockfile
@@ -26,12 +26,12 @@ jobs:
2626
run: pnpm build
2727

2828
- name: Install Playwright Browsers
29-
run: npx playwright install --with-deps
29+
run: pnpm dlx playwright install --with-deps
3030

3131
- name: Run Playwright tests
3232
run: pnpm test
3333

34-
- uses: actions/upload-artifact@v3
34+
- uses: actions/upload-artifact@v4
3535
if: always()
3636
with:
3737
name: playwright-report

ct-angular/package.json

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,28 @@
77
"start": "ng serve",
88
"build": "ng build",
99
"watch": "ng build --watch --configuration development",
10-
"test": "npx playwright test"
10+
"test": "npx playwright test",
11+
"typecheck": "tsc --noEmit"
1112
},
1213
"dependencies": {
13-
"@angular/animations": "^15.1.0",
14-
"@angular/common": "^15.1.0",
15-
"@angular/compiler": "^15.1.0",
16-
"@angular/core": "^15.1.0",
17-
"@angular/forms": "^15.1.0",
18-
"@angular/platform-browser": "^15.1.0",
19-
"@angular/platform-browser-dynamic": "^15.1.0",
20-
"@angular/router": "^15.1.0",
21-
"rxjs": "~7.8.0",
22-
"tslib": "^2.3.0",
23-
"zone.js": "~0.12.0"
14+
"@analogjs/vite-plugin-angular": "1.17.0",
15+
"@angular/animations": "17.3.8",
16+
"@angular/common": "17.3.8",
17+
"@angular/compiler": "17.3.8",
18+
"@angular/core": "17.3.8",
19+
"@angular/forms": "17.3.8",
20+
"@angular/platform-browser": "17.3.8",
21+
"@angular/platform-browser-dynamic": "17.3.8",
22+
"@angular/router": "17.3.8",
23+
"rxjs": "7.8.1",
24+
"tslib": "2.6.2",
25+
"zone.js": "0.14.5"
2426
},
2527
"devDependencies": {
26-
"@angular-devkit/build-angular": "^15.1.0",
27-
"@angular/cli": "~15.1.0",
28-
"@angular/compiler-cli": "^15.1.0",
29-
"@playwright/test": "1.41.2",
3028
"@sand4rt/experimental-ct-angular": "workspace:*",
31-
"typescript": "~4.9.4"
29+
"@angular-devkit/build-angular": "17.3.7",
30+
"@angular/cli": "17.3.7",
31+
"@angular/compiler-cli": "17.3.8",
32+
"typescript": "5.4.3"
3233
}
3334
}

ct-angular/playwright.config.mts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import angular from '@analogjs/vite-plugin-angular';
18+
import { defineConfig, devices } from '@sand4rt/experimental-ct-angular';
19+
import { resolve } from 'path';
20+
21+
export default defineConfig({
22+
testDir: 'tests',
23+
forbidOnly: !!process.env['CI'],
24+
retries: process.env['CI'] ? 2 : 0,
25+
reporter: process.env['CI'] ? 'html' : 'line',
26+
use: {
27+
trace: 'on-first-retry',
28+
ctViteConfig: {
29+
plugins: [angular({
30+
tsconfig: resolve('./tsconfig.spec.json'),
31+
}) as any], // TODO: remove any and resolve various installed conflicting Vite versions
32+
resolve: {
33+
alias: {
34+
'@': resolve('./src'),
35+
}
36+
}
37+
}
38+
},
39+
projects: [
40+
{
41+
name: 'chromium',
42+
use: { ...devices['Desktop Chrome'] },
43+
},
44+
{
45+
name: 'firefox',
46+
use: { ...devices['Desktop Firefox'] },
47+
},
48+
{
49+
name: 'webkit',
50+
use: { ...devices['Desktop Safari'] },
51+
},
52+
],
53+
});

ct-angular/playwright.config.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

ct-angular/playwright/index.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,37 @@
1-
import { beforeMount, afterMount } from '@sand4rt/experimental-ct-angular/hooks';
2-
import { provideRouter } from '@angular/router';
3-
import { ButtonComponent } from '@/components/button.component';
1+
import 'zone.js';
2+
import '@/assets/styles.css';
43
import { TOKEN } from '@/components/inject.component';
54
import { routes } from '@/router';
6-
import '@/assets/styles.css';
5+
import { APP_INITIALIZER, inject } from '@angular/core';
6+
import { Router, provideRouter } from '@angular/router';
7+
import { afterMount, beforeMount } from '@sand4rt/experimental-ct-angular/hooks';
8+
import { BrowserPlatformLocation, PlatformLocation } from '@angular/common';
79

810
export type HooksConfig = {
911
routing?: boolean;
1012
injectToken?: boolean;
1113
};
1214

1315
beforeMount<HooksConfig>(async ({ hooksConfig, TestBed }) => {
14-
TestBed.configureTestingModule({
15-
imports: [ButtonComponent],
16-
});
17-
1816
if (hooksConfig?.routing)
1917
TestBed.configureTestingModule({
20-
providers: [provideRouter(routes)],
18+
providers: [
19+
provideRouter(routes),
20+
{ provide: PlatformLocation, useExisting: BrowserPlatformLocation },
21+
{
22+
provide: APP_INITIALIZER,
23+
multi: true,
24+
useFactory() {
25+
const router = inject(Router);
26+
return () => router.initialNavigation();
27+
}
28+
}
29+
],
2130
});
2231

2332
if (hooksConfig?.injectToken)
2433
TestBed.configureTestingModule({
25-
providers: [{ provide: TOKEN, useValue: { text: 'has been overwritten' }}]
34+
providers: [{ provide: TOKEN, useValue: { text: 'has been overwritten' } }]
2635
})
2736
});
2837

ct-angular/src/app.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Component } from '@angular/core';
2-
import { RouterModule } from '@angular/router';
2+
import { RouterLink, RouterOutlet } from '@angular/router';
33

44
@Component({
55
standalone: true,
6-
imports: [RouterModule],
6+
imports: [RouterLink, RouterOutlet],
77
selector: 'app-root',
88
template: `
99
<header>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Component, input } from '@angular/core';
2+
3+
@Component({
4+
standalone: true,
5+
selector: 'app-button-signals',
6+
template: `
7+
<button>{{title()}}</button>
8+
`
9+
})
10+
export class ButtonSignalsComponent {
11+
title = input.required<string>();
12+
}

ct-angular/src/components/button.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
22

33
@Component({
44
standalone: true,
5-
selector: 'button-component',
5+
selector: 'app-button',
66
templateUrl: './button.component.html',
77
})
88
export class ButtonComponent {
9-
@Input() title!: string;
9+
@Input({required: true}) title!: string;
1010
@Output() submit = new EventEmitter();
1111
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1>Not Inlined</h1>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ChangeDetectionStrategy, Component } from '@angular/core';
2+
3+
@Component({
4+
changeDetection: ChangeDetectionStrategy.OnPush,
5+
standalone: true,
6+
templateUrl: './not-inlined.component.html',
7+
})
8+
export class NotInlinedComponent {}

ct-angular/src/components/output.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Subject, finalize } from "rxjs";
88
})
99
export class OutputComponent {
1010
@Output() answerChange = new Subject().pipe(
11-
/* Detect when observable is unsubscribed from,
11+
/* Detect when observable is unsubscribed from,
1212
* and set a global variable `hasUnsubscribed` to true. */
1313
finalize(() => ((this._window as any).hasUnsubscribed = true))
1414
);

ct-angular/tests/angular-router.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ test('navigate to a page by clicking a link', async ({ page, mount }) => {
77
hooksConfig: { routing: true },
88
});
99
await expect(component.getByRole('main')).toHaveText('Login');
10-
await expect(page).toHaveURL('/');
1110
await component.getByRole('link', { name: 'Dashboard' }).click();
1211
await expect(component.getByRole('main')).toHaveText('Dashboard');
1312
await expect(page).toHaveURL('/dashboard');

ct-angular/tests/events.spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { test, expect } from '@sand4rt/experimental-ct-angular';
22
import { ButtonComponent } from '@/components/button.component';
3+
import { OutputComponent } from '@/components/output.component';
34

45
test('emit an submit event when the button is clicked', async ({ mount }) => {
56
const messages: string[] = [];
@@ -14,3 +15,47 @@ test('emit an submit event when the button is clicked', async ({ mount }) => {
1415
await component.click();
1516
expect(messages).toEqual(['hello']);
1617
});
18+
19+
test('replace existing listener when new listener is set', async ({
20+
mount,
21+
}) => {
22+
let called = false;
23+
24+
const component = await mount(ButtonComponent, {
25+
props: {
26+
title: 'Submit',
27+
},
28+
on: {
29+
submit() { },
30+
},
31+
});
32+
33+
component.update({
34+
on: {
35+
submit() {
36+
called = true;
37+
},
38+
},
39+
});
40+
41+
await component.click();
42+
expect(called).toBe(true);
43+
});
44+
45+
test('unsubscribe from events when the component is unmounted', async ({
46+
mount,
47+
page,
48+
}) => {
49+
const component = await mount(OutputComponent, {
50+
on: {
51+
answerChange() { },
52+
},
53+
});
54+
55+
await component.unmount();
56+
57+
/* Check that the output observable had been unsubscribed from
58+
* as it sets a global variable `hasUnusbscribed` to true
59+
* when it detects unsubscription. Cf. OutputComponent. */
60+
expect(await page.evaluate(() => (window as any).hasUnsubscribed)).toBe(true);
61+
});

ct-angular/tests/injection.spec.ts renamed to ct-angular/tests/hooks.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { test, expect } from '@sand4rt/experimental-ct-angular';
1+
import { expect, test } from '@sand4rt/experimental-ct-angular';
22
import type { HooksConfig } from 'playwright';
33
import { InjectComponent } from '@/components/inject.component';
44

5-
test('inject a token', async ({ page, mount }) => {
5+
test('inject a token', async ({ mount }) => {
66
const component = await mount<HooksConfig>(InjectComponent, {
77
hooksConfig: { injectToken: true },
88
});

0 commit comments

Comments
 (0)