Skip to content

Conversation

@replete
Copy link
Contributor

@replete replete commented Nov 22, 2025

Hi Joe, hope you are well.

Picking up a new Angular project and noticed Angular 21 just came out, decided to try updating to 21.

Obviously no pressure to land this - I wanted to check out the new features.

  • Updated Angular and dependencies to 21
  • Updated npm dependencies
    • Fix stylelint breaking change (added rules: {} to config)
  • Added npm package-lock.json peer:true change ...shakes fist at dependabot
  • Zoneless change detection:
    • Migrated to Angular 21's zoneless change detection (provideZonelessChangeDetection())
    • Removed zone.js dependency - smaller bundle size (-40kB)
  • Karma to Vitest migration:
    • Angular 21 defaults to Vitest, so migrated from Karma
    • Removed Karma/Jasmine dependencies and config files
    • Tests now run via ng test which uses Vitest under the hood
    • VS Code Vitest extension workaround:

Commits build on each other, may want to avoid Karma-Vitest migration for now given the hack I had to write to make IDE integration work. I like how fast the unit tests run though, and without a Chrome dependency. Spent a lot of time this year with Playwright, but felt like too much for this so I didn't add that though I probably will in my project.

Cheers,

Phil

@joematthews joematthews self-requested a review November 22, 2025 17:39
@joematthews
Copy link
Owner

@replete this looks amazing. Let me get caught up a bit and review.

As for playwright, I did add an e2e testing section to the README.md that goes over enabling playwright and the eslint config for it. Can you think of project-agnostic configuration above and beyond this to include in either the repo or the README.md for playwright?

@replete

This comment was marked as resolved.

@replete
Copy link
Contributor Author

replete commented Nov 23, 2025

playwright single-framework tradeoffs

I have done some investigations and discovered significant tradeoffs with my 'one testing framework' idea:

Pros:

  • Single framework for unit/API/integration/E2E/a11y/perf tests
  • Fewer dependencies, simpler developer onboarding
  • Unified config, assertions, reporting, CI/CD
  • Still fast for unit tests (browser binaries only load when actually needed)

Cons:

  • Against Angular conventions - harder team onboarding?
  • No Angular schematic migrations when framework updates
  • Custom TestBed apparatus needed ... which we will have to maintain
  • Vitest has better watch mode/HMR for TDD workflow (watches test and source files)

I think the Angular team would have considered this for sure, though they've not published it. So I'm writing off this idea, though it is still appealing and technically feasible.

Others seem to be working on this too, suggesting this idea is not without merits:

Hopefully the Vitest IDE integration gets fixed with a vitest plugin or something to unify the testbed with ng-test.

ng e2e thoughts

A mature project with proper testing is likely to require Playwright with multiple projects (e2e, integration, smoke, security, a11y, api, perf) - its quite a journey and probably only relevant for some using this project. Chunky deps (inc browsers) - how many people are thinking about e2es when cloning a starter template, and when there are problems do you want to maintain it...

ng e2e community schematic did a fair job of getting a starting point together though, although for my multi-suite projects I'd not have root folders for test projects - if we did something slightly different would that work against those schematic migrations in future, also being community-driven perhaps quality not as high (not sure)...

So the playwright schematic did work after I made some changes:

Screenshot 2025-11-23 at 12 45 15

... I'll share a branch in a bit with this for you to look at

EDIT: Here's a branch that adds playwright/e2e atop this PR

@joematthews
Copy link
Owner

joematthews commented Nov 24, 2025

@replete

I was testing out the playwright branch and ran into an issue with npm run e2e:all. It works fine the first time, but after that it errors out:

Error: Project(s) "e2e-report", "e2e-test-results" not found.
Available projects: "e2e-chromium", "e2e-firefox", "e2e-webkit"

Looks like the glob pattern e2e-* in angular.json is picking up the output directories from playwright.config.ts (e2e-report and e2e-test-results) after the first test run and treating them as projects on subsequent runs.

Curious what you think the best approach is here to avoid the collision?

Also might be worth adding a note in the README's e2e section about running npx playwright install first if folks don't have the browsers installed yet. Could save some confusion for new users.

Let me know what you think!

@replete
Copy link
Contributor Author

replete commented Nov 27, 2025

@joematthews Somehow I completely missed that.
I pushed a commit to the playwright branch that places the artifact folders into ./.playwright. A bit tidier.

Interesting tradeoffs with a starter template, keep it simple with sensible defaults, vs inevitable conventions needed when the project scales. Some of my decisions might be unnecessary for the scope of the project to be honest, e.g. the .e2e.test.ts - that is mainly for purposes of fuzzing in a project with many types of test in the same area - npm run test "login.e2e" (or "login.int", "login.sec" etc..). Easily out of scope really. If you want to land that it might be simpler to keep *.test.ts (because they are actual tests) for playwright e2es, and *.spec.ts for the unit tests (specifications). The line can become blurry... you can see why you might wish to keep it optional.

@karcherm
Copy link

karcherm commented Dec 4, 2025

I just cloned your template, and found that your vitest workaround doesn't work perfect.

Setup:

C:\REDACTED>git clone --depth=1 --origin=upstream https://github.com/replete/extreme-angular.git -b upgrade-to-angular-21 xa
Cloning into 'xa'...
remote: Enumerating objects: 48, done.
remote: Counting objects: 100% (48/48), done.
remote: Compressing objects: 100% (42/42), done.
remote: Total 48 (delta 1), reused 26 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (48/48), 150.24 KiB | 6.53 MiB/s, done.
Resolving deltas: 100% (1/1), done.
C:\REDACTED>cd xa

C:\REDACTED\xa>npm i
...
added 945 packages, and audited 946 packages in 13s
...

Running test using ng test works:

C:\REDACTED\xa>npx ng test --watch=false
Initial chunk files | Names         | Raw size
spec-app.js         | spec-app      | 47.97 kB |
chunk-XSGC6WRG.js   | -             |  4.05 kB |
init-testbed.js     | init-testbed  |  1.20 kB |
styles.css          | styles        | 57 bytes |

                    | Initial total | 53.28 kB

Application bundle generation complete. [4.120 seconds] - 2025-12-04T11:45:04.515Z


 RUN  v4.0.14 C:/REDACTED/xa

 ✓  extreme-angular  src/app/app.spec.ts (2 tests) 101ms
   ✓ App (2)
     ✓ should create the app 74ms
     ✓ should render title 25ms

 Test Files  1 passed (1)
      Tests  2 passed (2)
   Start at  12:45:06
   Duration  11.71s (transform 109ms, setup 659ms, import 261ms, tests 101ms, environment 9.99s)

Running tests using vitest fails:

C:\REDACTED\xa>npx vitest --watch=false
[vitest] Rebuilding Angular tests...
Initial chunk files | Names         | Raw size
spec-app.js         | spec-app      | 47.97 kB |
chunk-XSGC6WRG.js   | -             |  4.05 kB |
init-testbed.js     | init-testbed  |  1.20 kB |
styles.css          | styles        | 57 bytes |

                    | Initial total | 53.28 kB

Application bundle generation complete. [1.315 seconds] - 2025-12-04T11:46:14.365Z

Build output files successfully dumped to 'C:\REDACTED\xa\.angular\cache\21.0.1\extreme-angular\unit-test\output-files'.

 RUN  v4.0.14 C:/REDACTED/xa

 ✓  extreme-angular  src/app/app.spec.ts (2 tests) 84ms
   ✓ App (2)
     ✓ should create the app 60ms
     ✓ should render title 23ms

 Test Files  1 passed (1)
      Tests  2 passed (2)
   Start at  12:46:14
   Duration  1.21s (transform 119ms, setup 282ms, import 156ms, tests 84ms, environment 543ms)


 RUN  v4.0.14 C:/REDACTED/xa

 ❯ src/app/app.spec.ts (2 tests | 2 failed) 6ms
   ❯ App (2)
     × should create the app 4ms
     × should render title 1ms

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 FAIL  src/app/app.spec.ts > App > should create the app
 FAIL  src/app/app.spec.ts > App > should render title
Error: Component 'App' is not resolved:
 - templateUrl: ./app.html
 - styleUrl: ./app.scss
Did you run and wait for 'resolveComponentResources()'?
 ❯ Function.get REDACTED/xa/node_modules/@angular/core/fesm2022/_debug_node-chunk.mjs:17706:17
 ❯ getComponentDef REDACTED/xa/node_modules/@angular/core/fesm2022/testing.mjs:1145:16
 ❯ isStandaloneComponent REDACTED/xa/node_modules/@angular/core/fesm2022/testing.mjs:1141:15
 ❯ queueTypesFromModulesArrayRecur REDACTED/xa/node_modules/@angular/core/fesm2022/testing.mjs:925:20
 ❯ TestBedCompiler.queueTypesFromModulesArray REDACTED/xa/node_modules/@angular/core/fesm2022/testing.mjs:943:5
 ❯ TestBedCompiler.configureTestingModule REDACTED/xa/node_modules/@angular/core/fesm2022/testing.mjs:567:12
 ❯ TestBedImpl.configureTestingModule REDACTED/xa/node_modules/@angular/core/fesm2022/testing.mjs:1393:19
 ❯ Function.configureTestingModule .REDACTED/xa/node_modules/@angular/core/fesm2022/testing.mjs:1264:33
 ❯ src/app/app.spec.ts:7:19
      5| describe('App', () => {
      6|   beforeEach(async () => {
      7|     await TestBed.configureTestingModule({
       |                   ^
      8|       imports: [App],
      9|       providers: [provideZonelessChangeDetection()],

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯


 Test Files  1 failed (1)
      Tests  2 failed (2)
   Start at  12:46:15
   Duration  1.15s (transform 59ms, setup 363ms, import 87ms, tests 6ms, environment 540ms)

Obviously, the vitest failure propagates into VS Code with the Vitest extenstion.

https://medium.com/@italo.masserano/how-to-use-angular-20-experimental-vitest-support-outside-of-ng-test-72447c69a6b7 , which seems to address a similar topic, somehow does call resolveComponentResources in a beforeAll test hook, which your template does not do.

Versions:

Angular CLI       : 21.0.1
Angular           : 21.0.1
Node.js           : 22.21.1
Package Manager   : npm 10.9.4
Operating System  : win32 x64

┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package                   │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/build            │ 21.0.1            │ ^21.0.1           │
│ @angular/cli              │ 21.0.1            │ ^21.0.1           │
│ @angular/common           │ 21.0.1            │ ^21.0.1           │
│ @angular/compiler         │ 21.0.1            │ ^21.0.1           │
│ @angular/compiler-cli     │ 21.0.1            │ ^21.0.1           │
│ @angular/core             │ 21.0.1            │ ^21.0.1           │
│ @angular/forms            │ 21.0.1            │ ^21.0.1           │
│ @angular/platform-browser │ 21.0.1            │ ^21.0.1           │
│ @angular/router           │ 21.0.1            │ ^21.0.1           │
│ rxjs                      │ 7.8.2             │ ~7.8.2            │
│ typescript                │ 5.9.3             │ ~5.9.3            │
│ vitest                    │ 4.0.14            │ ^4.0.14           │
└───────────────────────────┴───────────────────┴───────────────────┘

@karcherm
Copy link

karcherm commented Dec 4, 2025

The failure seems to be related to Windows as host OS. Your template works fine in node:22.21.1 from dockerhub, which is based on Debian bookworm.

@karcherm
Copy link

karcherm commented Dec 4, 2025

Indeed. In Windows, I have unit-test\output-files\spec-app.js, while in Linux, I have unit-test/output-files/spec-app-app.js. This merge request requires the latter name.

In Windows, the output-files filename seems to be not that easily predictable. I created src/app/foo/app.spec.ts, which resultet in unit-test/output-files/spec-app.js and unit-test/output-files/spec-app-2.js. Possibly this behaviour can be disabled in Angular (risking overly long filenames that may break on Windows).

@replete
Copy link
Contributor Author

replete commented Dec 5, 2025

@karcherm Thanks for the info. I read the angular cli source and can see the issue, it's an angular bug. I have added a workaround, but I do not have Windows therefore I can not test it. If you have Windows, and a devcontainer it would be easiest for you to test this, and fix it if it isn't quite right. Hope that helps.

@karcherm
Copy link

karcherm commented Dec 6, 2025

Thanks for the patch. I didn't test corner cases like repeated filenames, but running vitest on the base repository now works!

C:\REDACTED\xa>npx vitest --watch=false
[vitest] Rebuilding Angular tests...
Initial chunk files | Names         | Raw size
spec-app.js         | spec-app      | 47.97 kB |
chunk-XSGC6WRG.js   | -             |  4.05 kB |
init-testbed.js     | init-testbed  |  1.20 kB |
styles.css          | styles        | 57 bytes |

                    | Initial total | 53.27 kB

Application bundle generation complete. [1.798 seconds] - 2025-12-06T10:57:55.031Z

Build output files successfully dumped to 'C:\REDACTED\xa\.angular\cache\21.0.2\extreme-angular\unit-test\output-files'.

 RUN  v4.0.15 C:/REDACTED/xa

 ✓  extreme-angular  src/app/app.spec.ts (2 tests) 131ms
   ✓ App (2)
     ✓ should create the app 80ms
     ✓ should render title 48ms

 Test Files  1 passed (1)
      Tests  2 passed (2)
   Start at  11:57:55
   Duration  2.15s (transform 226ms, setup 662ms, import 318ms, tests 131ms, environment 832ms)


 RUN  v4.0.15 C:/REDACTED/xa

 ✓ src/app/app.spec.ts (2 tests) 97ms
   ✓ App (2)
     ✓ should create the app 61ms
     ✓ should render title 35ms

 Test Files  1 passed (1)
      Tests  2 passed (2)
   Start at  11:57:57
   Duration  2.18s (transform 248ms, setup 667ms, import 170ms, tests 97ms, environment 1.07s)

@karcherm
Copy link

karcherm commented Dec 6, 2025

BTW: Did you file a bug on angular-cli? If not, would you like me to do that?

@replete
Copy link
Contributor Author

replete commented Dec 6, 2025

@karcherm Thanks for the update.
There already is an angular bug tracked somewhere (I saw it), but I am uninterested in tracking a bug that broke a temporary vscode extension hack on Windows. The hack is a bonus to this PR to be honest and probably shouldn't make it in. Feels out of scope for this project (clean angular template) and we wouldn't want to maintain the hack probably. The hack will probably be quite slow at scale. Might stick with karma because of this oversight with Vitest 🤷
Out of the box Angular does not support Vitest Explorer due to their builder architecture. Hopefully they fix it 🤞

@joematthews
Copy link
Owner

@replete This looks great — thanks for putting in the work on the zoneless migration and wrestling with the Vitest situation. Really appreciate you being upfront about it being a hack.

I've got some adjustments on a branch:
replete/extreme-angular@upgrade-to-angular-21...joematthews:extreme-angular:align-with-angular-21-defaults

Fair warning: the diff looks noisy because I reformatted all files after adding singleQuote: true to prettier. If you click into the individual commits you'll get a cleaner picture — one has the config/structural changes, the other is just the formatting pass.

Vitest workaround

You called it — I went ahead and removed vitest.config.ts and vitest.setup.ts. The Windows issue @karcherm ran into kind of proved the point about fragility.

To be clear: we're still using Vitest for testing — ng test runs through Angular's @angular/build:unit-test builder which uses Vitest under the hood. What we lose is the VS Code Test Explorer integration, since the extension needs a vitest.config.ts to hook into. No need to switch back to Karma. I'd rather wait for Angular to sort out the IDE story upstream than maintain a workaround that could break in weird ways.

tsconfig.json structure

Matched the structure from a fresh ng new project — added files: [] with references. This broke tsc-files (throws TS6306), so I pulled it from lint-staged. Type checking still happens via npm run lint and CI.

tsconfig.node.json

Added this to catch any root-level *.ts files for ESLint. Even though we axed vitest.config.ts, figured it's worth keeping for when users add their own config files.

Prettier

Added printWidth: 100 and singleQuote: true to the prettier config and updated the README.md — these are Angular 21 defaults now, so I wanted to be clear about what's "ours" (plugins, htmlWhitespaceSensitivity) vs upstream.

Other bits

  • Dropped noImplicitAny (redundant with strict: true)
  • Dropped typeCheckHostBindings (Angular 21 defaults to true)
  • Dropped @standard-schema/spec (wasn't being used)
  • Updated Node.js engines to match Angular 21
  • Added packageManager field
  • Fixed README to include --zoneless in the ng new command

Goal is keeping extreme-angular as a clean layer on top of ng new --zoneless and making sure it is all compatible with Angular's docs. Let me know what you think!

@karcherm
Copy link

karcherm commented Dec 6, 2025

What we lose is the VS Code Test Explorer integration, since the extension needs a vitest.config.ts to hook into. No need to switch back to Karma. I'd rather wait for Angular to sort out the IDE story upstream than maintain a workaround that could break in weird ways.

Fair point. On the other hand, I was researching how to integrate Angular 21 tests with Visual Studio Code, and that's how I found extreme-angular. @replete You might consider putting the vitest workaround into a dedicated package (I guess these files can be dropped by applying a schematic to the project, but I'm talking out of my behind here), because they are useful on its own, even without the other tool integrations provided by extreme-angular.

@joematthews
Copy link
Owner

joematthews commented Dec 6, 2025

Hey @replete — didn't want your vitest workaround to just vanish, so I put together a doc based on it:

https://github.com/joematthews/extreme-angular/blob/align-with-angular-21-defaults/docs/vitest-ide-workaround.md

I added a link for it to the testing section of the README.md.

Figured that way anyone who wants Test Explorer can still set it up. You're credited at the bottom — hope that's cool! Let me know what you think.

@replete
Copy link
Contributor Author

replete commented Dec 6, 2025

@joematthews No worries, happy to contribute.

I could have been clearer about the Vitest hack that I didn't actually expect it to land, was expecting some feedback from you and further changes for the PR but I understand the preference to tidy it up separately for integration.

tsconfig: Great. I did intend to check against a newly generated project but it slipped my mind.
prettier: Please to hear - the first thing I changed when using the template on my project was configure singlequotes.

I did wonder if it would be worth mentioning how to configure isolated relaxed rules. When evaluating component libraries I ended up creating a sandbox folder and a custom tsconfig.sandbox.ts with relaxed rules so I could actually evaluate foreign code in my project without faffing. e.g. what's the DX on adding Material UI and not being able to build the project... haven't tested Material UI but assume it will be similar...

@joematthews
Copy link
Owner

Alright, let's get this merged — you've done the hard work here.

I'll follow up with a cleanup PR for the tsconfig/prettier stuff and the vitest workaround doc. The relaxed rules idea for evaluating libraries sounds useful too, could be another good one for ./docs down the road.

@joematthews joematthews merged commit 2ef4c56 into joematthews:main Dec 6, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for Vitest VSCode Plugin and similar

3 participants