agent-directives 0.2.1 → 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.
@@ -0,0 +1,216 @@
1
+ ---
2
+ name: angular-testing
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
+ required: false
6
+ category: angular
7
+ tools:
8
+ - claude
9
+ - copilot
10
+ - codex
11
+ - cursor
12
+ source_urls:
13
+ - https://angular.dev/guide/testing
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
+ applies_to:
19
+ - src/app/**/*.spec.ts
20
+ - src/app/**/*.test.ts
21
+ - src/**/*.spec.ts
22
+ ---
23
+
24
+ # Angular Testing Rules
25
+
26
+ **Load when:** Adding, changing, or reviewing Angular tests, or making Angular behavior changes that should be covered by tests.
27
+
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.
31
+
32
+ ## Sources
33
+
34
+ - Angular Testing Guide — https://angular.dev/guide/testing
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
39
+
40
+ ## Rules
41
+
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.
209
+
210
+ ## Evidence
211
+
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.
@@ -40,10 +40,10 @@ Delete this comment block when done.
40
40
 
41
41
  The root file provides project-specific context plus compact routing pointers: commands, repo layout, local constraints, and any client-specific workflow reminders.
42
42
 
43
- Workflow path selection, directive loading, skill loading, and evidence requirements live in `directives/adaptive-routing.md`.
43
+ Workflow path selection, directive loading, skill loading, rule selection, and evidence requirements live in `directives/adaptive-routing.md`.
44
44
 
45
45
  After routing, report:
46
- `Route: <path>; using <directive/skill files>; evidence: <checks>.`
46
+ `Route: <path>; using <directive/skill files>; rules: <rule files or none>; evidence: <checks>.`
47
47
 
48
48
  When adaptive routing selects Full Path or another route that invokes the full
49
49
  phase sequence, no skipping steps:
@@ -40,10 +40,10 @@ Delete this comment block when done.
40
40
 
41
41
  The root file provides project-specific context plus compact routing pointers: commands, repo layout, local constraints, and any client-specific workflow reminders.
42
42
 
43
- Workflow path selection, directive loading, skill loading, and evidence requirements live in `directives/adaptive-routing.md`.
43
+ Workflow path selection, directive loading, skill loading, rule selection, and evidence requirements live in `directives/adaptive-routing.md`.
44
44
 
45
45
  After routing, report:
46
- `Route: <path>; using <directive/skill files>; evidence: <checks>.`
46
+ `Route: <path>; using <directive/skill files>; rules: <rule files or none>; evidence: <checks>.`
47
47
 
48
48
  When adaptive routing selects Full Path or another route that invokes the full
49
49
  phase sequence, no skipping steps:
@@ -59,10 +59,10 @@ one commit. Each behavior gets its own test-commit + implementation-commit pair.
59
59
 
60
60
  The root file provides project-specific context plus compact routing pointers: commands, repo layout, local constraints, and any client-specific workflow reminders.
61
61
 
62
- Workflow path selection, directive loading, skill loading, and evidence requirements live in `directives/adaptive-routing.md`.
62
+ Workflow path selection, directive loading, skill loading, rule selection, and evidence requirements live in `directives/adaptive-routing.md`.
63
63
 
64
64
  After routing, report:
65
- `Route: <path>; using <directive/skill files>; evidence: <checks>.`
65
+ `Route: <path>; using <directive/skill files>; rules: <rule files or none>; evidence: <checks>.`
66
66
 
67
67
  ## Forbidden
68
68