agent-directives 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: angular-testing
3
- description: Angular testing rules for component, service, and integration-style Angular changes.
4
- version: 1.0.0
3
+ description: Concrete Angular testing patterns TestBed configuration, signal-input harnesses, router and HTTP testing utilities, and behavior-first test design.
4
+ version: 1.1.0
5
5
  required: false
6
6
  category: angular
7
7
  tools:
@@ -11,33 +11,206 @@ tools:
11
11
  - cursor
12
12
  source_urls:
13
13
  - https://angular.dev/guide/testing
14
- - https://angular.dev/style-guide
14
+ - https://angular.dev/guide/testing/components-basics
15
+ - https://angular.dev/guide/testing/services
16
+ - https://angular.dev/guide/testing/http
17
+ - https://material.angular.io/cdk/test-harnesses/overview
15
18
  applies_to:
16
19
  - src/app/**/*.spec.ts
17
- - src/app/**/*.component.ts
18
- - src/app/**/*.service.ts
20
+ - src/app/**/*.test.ts
21
+ - src/**/*.spec.ts
19
22
  ---
20
23
 
21
24
  # Angular Testing Rules
22
25
 
23
26
  **Load when:** Adding, changing, or reviewing Angular tests, or making Angular behavior changes that should be covered by tests.
24
27
 
25
- ## Sources
28
+ ## Version Awareness
29
+
30
+ Check which test runner the project uses before writing tests — Angular projects today commonly run Jasmine + Karma, Jest, or Vitest. Inspect `angular.json` (`test` target builder) and `package.json` scripts. Use the matchers, spies, and lifecycle hooks of the configured runner; do not import Jest APIs into a Jasmine project or vice versa.
26
31
 
27
- Track source material explicitly so future updates can verify whether the rule is stale:
32
+ ## Sources
28
33
 
29
34
  - Angular Testing Guide — https://angular.dev/guide/testing
30
- - Angular Style Guide — https://angular.dev/style-guide
35
+ - Component Testing Basics — https://angular.dev/guide/testing/components-basics
36
+ - Service Testing — https://angular.dev/guide/testing/services
37
+ - HttpClient Testing — https://angular.dev/guide/testing/http
38
+ - CDK Test Harnesses — https://material.angular.io/cdk/test-harnesses/overview
31
39
 
32
40
  ## Rules
33
41
 
34
- - Prefer behavior-focused tests over implementation-detail tests. Assert visible output, emitted events, service effects, and observable state rather than private methods.
35
- - For component changes, update or add component tests unless existing coverage already proves the changed behavior.
36
- - For service changes, test the service contract and failure paths without over-mocking project-owned logic.
37
- - Keep test setup local and readable. Do not add shared testing helpers unless at least two call sites need the same project-specific setup.
38
- - Preserve the project's current Angular test runner and utilities. Do not migrate Karma/Jasmine/Jest/Vitest/Testing Library choices as part of a feature fix.
39
- - Avoid brittle snapshots for Angular templates unless the project already uses them and the assertion is intentionally structural.
42
+ ### What to test
43
+
44
+ - **Components:** input/output bindings, rendered output for each meaningful state, user interactions (via harnesses or `DebugElement.triggerEventHandler`).
45
+ - **Services:** every public method, error paths, HTTP interactions, and any caching/state behavior.
46
+ - **Pipes:** pure transformation plain unit tests, no `TestBed` needed.
47
+ - **Guards / resolvers:** allowed and denied branches, redirect targets, and dependency interactions.
48
+ - **Directives:** host bindings, host listeners, and any DOM side effects.
49
+
50
+ Prefer behavior over implementation: assert visible output, emitted events, and observable state — not private methods or rendered class names that exist only to drive other tests.
51
+
52
+ ### TestBed setup for standalone components
53
+
54
+ ```typescript
55
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
56
+ import { provideHttpClient } from '@angular/common/http';
57
+ import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing';
58
+ import { UserCardComponent } from './user-card.component';
59
+
60
+ describe('UserCardComponent', () => {
61
+ let fixture: ComponentFixture<UserCardComponent>;
62
+ let component: UserCardComponent;
63
+ let httpMock: HttpTestingController;
64
+
65
+ beforeEach(async () => {
66
+ await TestBed.configureTestingModule({
67
+ imports: [UserCardComponent], // import the standalone component directly
68
+ providers: [provideHttpClient(), provideHttpClientTesting()],
69
+ }).compileComponents();
70
+
71
+ fixture = TestBed.createComponent(UserCardComponent);
72
+ component = fixture.componentInstance;
73
+ httpMock = TestBed.inject(HttpTestingController);
74
+ });
75
+
76
+ afterEach(() => httpMock.verify());
77
+ });
78
+ ```
79
+
80
+ - Import standalone components directly into `imports`. Do not stub them into a fake NgModule.
81
+ - Use `provideHttpClient()` + `provideHttpClientTesting()` for any code path that uses `HttpClient`. Do not `HttpClientModule` on standalone-first projects.
82
+
83
+ ### Signal inputs
84
+
85
+ Set signal-based inputs via `componentRef.setInput()`; assigning to the field directly will not work.
86
+
87
+ ```typescript
88
+ it('renders the user name', () => {
89
+ fixture.componentRef.setInput('user', { id: '1', name: 'Ada' });
90
+ fixture.detectChanges();
91
+ expect(fixture.nativeElement.querySelector('[data-testid="name"]')?.textContent).toContain('Ada');
92
+ });
93
+ ```
94
+
95
+ ### Signal-based outputs
96
+
97
+ ```typescript
98
+ it('emits select on click', () => {
99
+ const events: string[] = [];
100
+ fixture.componentRef.setInput('user', { id: '1', name: 'Ada' });
101
+ fixture.componentInstance.select.subscribe(id => events.push(id));
102
+
103
+ fixture.nativeElement.querySelector('button')!.click();
104
+ expect(events).toEqual(['1']);
105
+ });
106
+ ```
107
+
108
+ ### Component harnesses
109
+
110
+ Prefer CDK component harnesses over raw DOM queries for UI interaction — they are resilient to markup changes.
111
+
112
+ ```typescript
113
+ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
114
+ import { MatButtonHarness } from '@angular/material/button/testing';
115
+
116
+ const loader = TestbedHarnessEnvironment.loader(fixture);
117
+ const saveButton = await loader.getHarness(MatButtonHarness.with({ text: 'Save' }));
118
+ await saveButton.click();
119
+ ```
120
+
121
+ ### Router testing
122
+
123
+ ```typescript
124
+ import { RouterTestingHarness } from '@angular/router/testing';
125
+ import { provideRouter } from '@angular/router';
126
+
127
+ it('binds :id route param to component input', async () => {
128
+ TestBed.configureTestingModule({
129
+ providers: [provideRouter([{ path: 'users/:id', component: UserDetailComponent }])],
130
+ });
131
+ const harness = await RouterTestingHarness.create();
132
+ const detail = await harness.navigateByUrl('/users/42', UserDetailComponent);
133
+ expect(detail.userId()).toBe('42');
134
+ });
135
+ ```
136
+
137
+ - Use `RouterTestingHarness` for routed components. Do not stub `ActivatedRoute` with hand-rolled mocks unless the test specifically exercises route metadata edge cases.
138
+
139
+ ### HTTP testing
140
+
141
+ ```typescript
142
+ it('GETs the user list', () => {
143
+ const service = TestBed.inject(UserService);
144
+ let result: User[] = [];
145
+
146
+ service.getUsers().subscribe(users => (result = users));
147
+
148
+ const req = httpMock.expectOne('/api/users');
149
+ expect(req.request.method).toBe('GET');
150
+ req.flush([{ id: '1', name: 'Ada' }]);
151
+
152
+ expect(result).toEqual([{ id: '1', name: 'Ada' }]);
153
+ });
154
+ ```
155
+
156
+ - One `expectOne` per request. Call `httpMock.verify()` in `afterEach` so unexpected requests fail the test.
157
+
158
+ ### Async testing
159
+
160
+ - `fakeAsync` + `tick()` / `flush()` for deterministic, controllable async (timers, RxJS schedulers).
161
+ - `waitForAsync` + `fixture.whenStable()` for tests that genuinely need real microtasks.
162
+ - Avoid `setTimeout` in tests outside `fakeAsync` — flake risk.
163
+
164
+ ```typescript
165
+ it('debounces the search', fakeAsync(() => {
166
+ component.search('a');
167
+ component.search('ab');
168
+ tick(300);
169
+ expect(searchSpy).toHaveBeenCalledTimes(1);
170
+ expect(searchSpy).toHaveBeenCalledWith('ab');
171
+ }));
172
+ ```
173
+
174
+ ### Service tests
175
+
176
+ Inject services directly — no fixture required.
177
+
178
+ ```typescript
179
+ describe('UserService', () => {
180
+ let service: UserService;
181
+ let httpMock: HttpTestingController;
182
+
183
+ beforeEach(() => {
184
+ TestBed.configureTestingModule({
185
+ providers: [provideHttpClient(), provideHttpClientTesting()],
186
+ });
187
+ service = TestBed.inject(UserService);
188
+ httpMock = TestBed.inject(HttpTestingController);
189
+ });
190
+
191
+ afterEach(() => httpMock.verify());
192
+ });
193
+ ```
194
+
195
+ ### Test design
196
+
197
+ - Keep setup local and readable. Add shared testing helpers only when at least two call sites need the same project-specific setup.
198
+ - Use `data-testid` (or the project's existing convention) for stable selectors. Do not assert on CSS classes or text that changes with copy edits.
199
+ - Avoid brittle template snapshots unless the project already uses them and the assertion is intentionally structural.
200
+ - Do not migrate the project's test runner, assertion library, or harness choices as part of a feature change.
201
+
202
+ ## Anti-patterns to refuse
203
+
204
+ - Setting signal inputs by assignment (`component.user = ...`) instead of `componentRef.setInput()`.
205
+ - Importing `HttpClientTestingModule` / `RouterTestingModule` on standalone-first projects when `provideHttpClientTesting` / `provideRouter` + `RouterTestingHarness` are available.
206
+ - Spying on private methods or asserting on internal state to validate that a component "did the right thing."
207
+ - Wrapping every test in `fakeAsync` reflexively, then forgetting `tick()` and getting silent timeouts.
208
+ - New shared "test utils" modules created for one consumer.
40
209
 
41
210
  ## Evidence
42
211
 
43
- Use the narrowest configured test command that covers the touched behavior, then broaden only when needed. If no narrow command exists, use the project test script or Angular CLI test target and state the scope.
212
+ Use the narrowest configured test command that covers the touched behavior, then broaden only when needed.
213
+
214
+ - File-scoped: `ng test --include='**/user-card.component.spec.ts'` or the project's equivalent.
215
+ - Project-scoped: `ng test --no-watch --code-coverage` (or the project test script) in CI mode.
216
+ - If no narrow command exists, run the full project test script and state the scope.