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.
- package/README.md +1 -1
- package/directives/adaptive-routing.md +22 -8
- package/dist/manifest.d.ts +1 -0
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js.map +1 -1
- package/manifest.json +95 -7
- package/package.json +1 -1
- package/rules/angular/coding-style.md +176 -0
- package/rules/angular/components-and-templates.md +113 -15
- package/rules/angular/patterns.md +300 -0
- package/rules/angular/project-structure.md +115 -11
- package/rules/angular/security.md +159 -0
- package/rules/angular/testing.md +198 -15
package/rules/angular/testing.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: angular-testing
|
|
3
|
-
description: Angular testing
|
|
4
|
-
version: 1.
|
|
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/
|
|
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/**/*.
|
|
18
|
-
- src
|
|
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
|
-
##
|
|
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
|
-
|
|
33
|
+
## Sources
|
|
28
34
|
|
|
29
35
|
- Angular Testing Guide — https://angular.dev/guide/testing
|
|
30
|
-
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
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.
|
|
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.
|