agent-directives 0.3.0 → 0.4.1

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