Yicong-Huang commented on code in PR #5170:
URL: https://github.com/apache/texera/pull/5170#discussion_r3294120800
##########
frontend/TESTING.md:
##########
@@ -0,0 +1,340 @@
+# Frontend testing guide
+
+Canonical reference for writing, running, and maintaining unit tests in
`frontend/`. Written for both human contributors and AI agents — read it on
demand when [`AGENTS.md`](AGENTS.md)'s rules need a deeper recipe, the mental
model behind a constraint, or troubleshooting steps.
+
+For repo-wide testing philosophy (TDD, characterization tests, "every test
must cover a specific failure mode") see [`../AGENTS.md`](../AGENTS.md) "Tests
come first".
+
+## Contents
+
+1. [The stack](#the-stack)
+2. [Running tests](#running-tests)
+3. [Why `detectChanges()` is the coverage
switch](#why-detectchanges-is-the-coverage-switch)
+4. [Recipes](#recipes)
+5. [Standalone components](#standalone-components)
+6. [jsdom vs browser mode](#jsdom-vs-browser-mode)
+7. [Mocking](#mocking)
+8. [Anti-patterns](#anti-patterns)
+9. [Coverage troubleshooting](#coverage-troubleshooting)
+10. [References](#references)
+
+## The stack
+
+| Layer | Choice
|
+| ------------------------ |
--------------------------------------------------------------------------------------------------------------------------------------
|
+| Test framework | Vitest
|
+| Angular test integration | `@angular/build:unit-test` builder (two targets
in `angular.json`: `gui:test` and `gui:test-browser`)
|
+| Default DOM | jsdom
|
+| Real-browser DOM | `@vitest/browser` + Playwright (Chromium,
headless)
|
+| Coverage | `@vitest/coverage-v8`
|
+| Test setup | `src/test-zone-setup.ts` wraps `it`/`test` in an
Angular ProxyZone (Vitest does not provide one and Angular's `fakeAsync`
requires it) |
+| Globals | `globals: true` in `vitest.config.ts`, so
`describe / it / expect / vi / beforeEach` come from the runtime — no per-file
imports |
+
+`src/main.test.ts` is intentionally a near-empty `export {}`. The `unit-test`
builder uses `buildTarget`'s `main` to seed the bundle graph; if it pointed at
the real `main.ts`, every component declared in `AppModule` would be
type-checked for every spec, surfacing template errors for components no active
spec touches. Keeping `main.test.ts` empty narrows the graph to what each spec
actually imports.
+
+## Running tests
+
+```bash
+# default — jsdom, watch off
+yarn test
+
+# the same, with coverage in lcov form (CI shape)
+yarn test:ci
+
+# only the specs routed to real browser DOM (Playwright Chromium)
+ng run gui:test-browser
+
+# coverage report you can open in a browser
+yarn test -- --coverage --coverage.reporter=html
+# then open coverage/index.html
+```
+
+Single-file and watch loops use Vitest's own filtering:
+
+```bash
+ng test --test-file
src/app/workspace/component/workflow-editor/mini-map/mini-map.component.spec.ts
+```
+
+## Why `detectChanges()` is the coverage switch
+
+Angular's Ivy compiler turns each component template into a TypeScript
function:
+
+```ts
+function MiniMapComponent_Template(rf, ctx) {
+ if (rf & 1) {
+ // creation pass
+ ɵɵelementStart(0, "div");
+ ɵɵlistener("click", () => ctx.onClick());
+ ɵɵtext(1);
+ ɵɵelementEnd();
+ }
+ if (rf & 2) {
+ // update pass
+ ɵɵadvance(1);
+ ɵɵtextInterpolate(ctx.label);
+ }
+}
+```
+
+This function is **not** invoked by the component constructor; it runs only
during change detection. The Vite build emits source maps that map each `ɵɵ…`
call back to the `.html` line that produced it, and v8 coverage records hits
against that source-mapped location.
+
+Consequences:
+
+- `TestBed.createComponent(C)` alone covers the constructor but leaves the
template at 0 %.
+- A single `fixture.detectChanges()` runs the creation pass and the first
update pass; most ordinary `.component.html` files jump to 70 – 90 % from this
one call.
+- Branches gated by `*ngIf="cond"` need a second `detectChanges()` with `cond`
toggled to cover the other side. The same applies to `*ngFor` over an empty vs
non-empty array, and to `[ngSwitch]` cases.
+
+If `.component.html` shows 0 % after your spec runs, you almost certainly hit
one of the [anti-patterns](#anti-patterns) — most often the constructor
compiled and "should create" passed but `detectChanges` was never reached.
+
+## Recipes
+
+### A. Minimum viable spec
+
+Use this as the starting point for any new component. It already covers the
template's creation pass.
+
+```ts
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { HttpClientTestingModule } from "@angular/common/http/testing";
+import { commonTestProviders } from "../../../common/testing/test-utils";
+import { MyComponent } from "./my.component";
+
+describe("MyComponent", () => {
+ let fixture: ComponentFixture<MyComponent>;
+ let component: MyComponent;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [MyComponent, HttpClientTestingModule],
+ providers: [...commonTestProviders],
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MyComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it("should create", () => {
+ expect(component).toBeTruthy();
+ });
+});
+```
+
+Working reference:
`src/app/workspace/component/workflow-editor/mini-map/mini-map.component.spec.ts`
(63 lines, achieves ~87 % template coverage off this pattern alone).
Review Comment:
Thanks. I think we can delete this line as it is not necessary.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]