@smartsoft001-mobilems/claude-plugins 2.66.0 → 2.68.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.
Files changed (32) hide show
  1. package/package.json +1 -1
  2. package/plugins/flow/.claude-plugin/plugin.json +1 -15
  3. package/plugins/flow-legacy/.claude-plugin/README.md +143 -0
  4. package/plugins/flow-legacy/.claude-plugin/merge-permissions.js +80 -0
  5. package/plugins/flow-legacy/.claude-plugin/plugin.json +5 -0
  6. package/plugins/flow-legacy/.claude-plugin/settings.template.json +75 -0
  7. package/plugins/flow-legacy/agents/angular-component-scaffolder.md +323 -0
  8. package/plugins/flow-legacy/agents/angular-directive-builder.md +258 -0
  9. package/plugins/flow-legacy/agents/angular-guard-builder.md +322 -0
  10. package/plugins/flow-legacy/agents/angular-pipe-builder.md +227 -0
  11. package/plugins/flow-legacy/agents/angular-resolver-builder.md +332 -0
  12. package/plugins/flow-legacy/agents/angular-service-builder.md +271 -0
  13. package/plugins/flow-legacy/agents/angular-state-builder.md +473 -0
  14. package/plugins/flow-legacy/agents/shared-impl-orchestrator.md +161 -0
  15. package/plugins/flow-legacy/agents/shared-impl-reporter.md +204 -0
  16. package/plugins/flow-legacy/agents/shared-linear-subtask-iterator.md +187 -0
  17. package/plugins/flow-legacy/agents/shared-tdd-developer.md +304 -0
  18. package/plugins/flow-legacy/agents/shared-test-runner.md +131 -0
  19. package/plugins/flow-legacy/agents/shared-ui-classifier.md +137 -0
  20. package/plugins/flow-legacy/commands/commit.md +162 -0
  21. package/plugins/flow-legacy/commands/impl.md +495 -0
  22. package/plugins/flow-legacy/commands/plan.md +488 -0
  23. package/plugins/flow-legacy/commands/push.md +470 -0
  24. package/plugins/flow-legacy/skills/a11y-audit/SKILL.md +214 -0
  25. package/plugins/flow-legacy/skills/angular-patterns/SKILL.md +361 -0
  26. package/plugins/flow-legacy/skills/browser-capture/SKILL.md +238 -0
  27. package/plugins/flow-legacy/skills/debug-helper/SKILL.md +387 -0
  28. package/plugins/flow-legacy/skills/linear-suggestion/SKILL.md +132 -0
  29. package/plugins/flow-legacy/skills/maia-files-delete/SKILL.md +59 -0
  30. package/plugins/flow-legacy/skills/maia-files-upload/SKILL.md +57 -0
  31. package/plugins/flow-legacy/skills/nx-conventions/SKILL.md +371 -0
  32. package/plugins/flow-legacy/skills/test-unit/SKILL.md +494 -0
@@ -0,0 +1,494 @@
1
+ ---
2
+ name: test-unit
3
+ description: Write unit tests following project conventions for Angular 14 legacy projects. Generates Jest tests for components, services, and NestJS using AAA pattern.
4
+ allowed-tools:
5
+ - Bash
6
+ - Read
7
+ - Write
8
+ - Edit
9
+ - Glob
10
+ - Grep
11
+ ---
12
+
13
+ # Unit Test Skill (Angular 14 Legacy)
14
+
15
+ Write unit tests following project conventions using Jest framework with AAA (Arrange-Act-Assert) pattern for Angular 14 legacy projects.
16
+
17
+ ## Testing Framework
18
+
19
+ - **Framework**: Jest for all unit tests
20
+ - **File naming**: `{name}.spec.ts` alongside source files
21
+ - **Test runner**: Nx 14 (`nx test {project}`)
22
+
23
+ ## Test File Location
24
+
25
+ Place test files next to the source files they test:
26
+
27
+ ```
28
+ feature/
29
+ ├── feature.service.ts
30
+ ├── feature.service.spec.ts
31
+ ├── feature.component.ts
32
+ └── feature.component.spec.ts
33
+ ```
34
+
35
+ ## Naming Convention
36
+
37
+ - **Describe blocks**: `@{package-name}: ClassName`
38
+ - **Test format**: `it('should...')` with clear behavior description
39
+
40
+ ## Test Structure (AAA Pattern)
41
+
42
+ Always use Arrange-Act-Assert pattern with blank line separation (no comments):
43
+
44
+ ```typescript
45
+ it('should perform expected operation', () => {
46
+ const input = 'test';
47
+
48
+ const result = service.performOperation(input);
49
+
50
+ expect(result).toBe('expected output');
51
+ });
52
+ ```
53
+
54
+ ## Angular 14 Component Testing
55
+
56
+ ### Basic Component Test (NgModule-based)
57
+
58
+ ```typescript
59
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
60
+ import { NO_ERRORS_SCHEMA } from '@angular/core';
61
+
62
+ import { FeatureComponent } from './feature.component';
63
+
64
+ describe('@mms/shared-angular: FeatureComponent', () => {
65
+ let component: FeatureComponent;
66
+ let fixture: ComponentFixture<FeatureComponent>;
67
+
68
+ beforeEach(async () => {
69
+ await TestBed.configureTestingModule({
70
+ declarations: [FeatureComponent], // NOT imports (not standalone)
71
+ schemas: [NO_ERRORS_SCHEMA],
72
+ }).compileComponents();
73
+
74
+ fixture = TestBed.createComponent(FeatureComponent);
75
+ component = fixture.componentInstance;
76
+ fixture.detectChanges();
77
+ });
78
+
79
+ it('should create', () => {
80
+ expect(component).toBeTruthy();
81
+ });
82
+
83
+ it('should display expected content', () => {
84
+ component.title = 'Test Title';
85
+ fixture.detectChanges();
86
+
87
+ const compiled = fixture.nativeElement;
88
+ expect(compiled.querySelector('h1')?.textContent).toContain('Test Title');
89
+ });
90
+ });
91
+ ```
92
+
93
+ ### Testing Components with @Input/@Output
94
+
95
+ ```typescript
96
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
97
+ import { NO_ERRORS_SCHEMA } from '@angular/core';
98
+
99
+ import { FormFieldComponent } from './form-field.component';
100
+
101
+ describe('@mms/shared-angular: FormFieldComponent', () => {
102
+ let component: FormFieldComponent;
103
+ let fixture: ComponentFixture<FormFieldComponent>;
104
+
105
+ beforeEach(async () => {
106
+ await TestBed.configureTestingModule({
107
+ declarations: [FormFieldComponent],
108
+ schemas: [NO_ERRORS_SCHEMA],
109
+ }).compileComponents();
110
+
111
+ fixture = TestBed.createComponent(FormFieldComponent);
112
+ component = fixture.componentInstance;
113
+ });
114
+
115
+ it('should accept input value', () => {
116
+ component.label = 'Test Label';
117
+ fixture.detectChanges();
118
+
119
+ expect(component.label).toBe('Test Label');
120
+ });
121
+
122
+ it('should emit output event', () => {
123
+ const spy = jest.fn();
124
+ component.changed.subscribe(spy);
125
+
126
+ component.onValueChange('new value');
127
+
128
+ expect(spy).toHaveBeenCalledWith('new value');
129
+ });
130
+
131
+ it('should support two-way binding', () => {
132
+ const valueChangeSpy = jest.fn();
133
+ component.valueChange.subscribe(valueChangeSpy);
134
+
135
+ component.value = 'initial';
136
+ component.onInputChange('updated');
137
+
138
+ expect(valueChangeSpy).toHaveBeenCalledWith('updated');
139
+ });
140
+ });
141
+ ```
142
+
143
+ ### Testing with BehaviorSubject State
144
+
145
+ ```typescript
146
+ import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
147
+ import { of, BehaviorSubject } from 'rxjs';
148
+
149
+ import { ListComponent } from './list.component';
150
+ import { StateService } from './state.service';
151
+
152
+ describe('@mms/shared-angular: ListComponent', () => {
153
+ let component: ListComponent;
154
+ let fixture: ComponentFixture<ListComponent>;
155
+ let mockStateService: {
156
+ items$: BehaviorSubject<Item[]>;
157
+ isLoading$: BehaviorSubject<boolean>;
158
+ };
159
+
160
+ beforeEach(async () => {
161
+ mockStateService = {
162
+ items$: new BehaviorSubject<Item[]>([]),
163
+ isLoading$: new BehaviorSubject<boolean>(false),
164
+ };
165
+
166
+ await TestBed.configureTestingModule({
167
+ declarations: [ListComponent],
168
+ providers: [
169
+ { provide: StateService, useValue: mockStateService },
170
+ ],
171
+ }).compileComponents();
172
+
173
+ fixture = TestBed.createComponent(ListComponent);
174
+ component = fixture.componentInstance;
175
+ fixture.detectChanges();
176
+ });
177
+
178
+ it('should display items from state', () => {
179
+ mockStateService.items$.next([
180
+ { id: '1', name: 'Item 1' },
181
+ { id: '2', name: 'Item 2' },
182
+ ]);
183
+ fixture.detectChanges();
184
+
185
+ const items = fixture.nativeElement.querySelectorAll('li');
186
+ expect(items.length).toBe(2);
187
+ });
188
+
189
+ it('should show loading spinner', () => {
190
+ mockStateService.isLoading$.next(true);
191
+ fixture.detectChanges();
192
+
193
+ const spinner = fixture.nativeElement.querySelector('app-spinner');
194
+ expect(spinner).toBeTruthy();
195
+ });
196
+ });
197
+ ```
198
+
199
+ ### Testing with Services (Constructor DI)
200
+
201
+ ```typescript
202
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
203
+ import { of } from 'rxjs';
204
+
205
+ import { FeatureComponent } from './feature.component';
206
+ import { DataService } from './data.service';
207
+
208
+ describe('@mms/shared-angular: FeatureComponent', () => {
209
+ let component: FeatureComponent;
210
+ let fixture: ComponentFixture<FeatureComponent>;
211
+ let mockDataService: jest.Mocked<DataService>;
212
+
213
+ beforeEach(async () => {
214
+ mockDataService = {
215
+ getData: jest.fn().mockReturnValue(of(['item1', 'item2'])),
216
+ saveData: jest.fn().mockReturnValue(of(true)),
217
+ } as unknown as jest.Mocked<DataService>;
218
+
219
+ await TestBed.configureTestingModule({
220
+ declarations: [FeatureComponent],
221
+ providers: [{ provide: DataService, useValue: mockDataService }],
222
+ }).compileComponents();
223
+
224
+ fixture = TestBed.createComponent(FeatureComponent);
225
+ component = fixture.componentInstance;
226
+ fixture.detectChanges();
227
+ });
228
+
229
+ it('should load data on init', () => {
230
+ expect(mockDataService.getData).toHaveBeenCalled();
231
+ expect(component.items).toEqual(['item1', 'item2']);
232
+ });
233
+ });
234
+ ```
235
+
236
+ ## Angular 14 Service Testing
237
+
238
+ ### Service with BehaviorSubject
239
+
240
+ ```typescript
241
+ import { TestBed } from '@angular/core/testing';
242
+ import { take } from 'rxjs/operators';
243
+
244
+ import { StateService } from './state.service';
245
+
246
+ describe('@mms/shared-angular: StateService', () => {
247
+ let service: StateService;
248
+
249
+ beforeEach(() => {
250
+ TestBed.configureTestingModule({
251
+ providers: [StateService],
252
+ });
253
+ service = TestBed.inject(StateService);
254
+ });
255
+
256
+ it('should be created', () => {
257
+ expect(service).toBeTruthy();
258
+ });
259
+
260
+ it('should emit initial state', (done) => {
261
+ service.items$.pipe(take(1)).subscribe(items => {
262
+ expect(items).toEqual([]);
263
+ done();
264
+ });
265
+ });
266
+
267
+ it('should update items', (done) => {
268
+ const newItems = [{ id: '1', name: 'Test' }];
269
+
270
+ service.setItems(newItems);
271
+
272
+ service.items$.pipe(take(1)).subscribe(items => {
273
+ expect(items).toEqual(newItems);
274
+ done();
275
+ });
276
+ });
277
+
278
+ it('should return current value', () => {
279
+ const items = [{ id: '1', name: 'Test' }];
280
+ service.setItems(items);
281
+
282
+ expect(service.currentItems).toEqual(items);
283
+ });
284
+ });
285
+ ```
286
+
287
+ ### Service with HTTP (Angular 14 HttpClientTestingModule)
288
+
289
+ ```typescript
290
+ import { TestBed } from '@angular/core/testing';
291
+ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
292
+
293
+ import { DataService } from './data.service';
294
+
295
+ describe('@mms/shared-angular: DataService', () => {
296
+ let service: DataService;
297
+ let httpMock: HttpTestingController;
298
+
299
+ beforeEach(() => {
300
+ TestBed.configureTestingModule({
301
+ imports: [HttpClientTestingModule],
302
+ providers: [DataService],
303
+ });
304
+
305
+ service = TestBed.inject(DataService);
306
+ httpMock = TestBed.inject(HttpTestingController);
307
+ });
308
+
309
+ afterEach(() => {
310
+ httpMock.verify();
311
+ });
312
+
313
+ it('should be created', () => {
314
+ expect(service).toBeTruthy();
315
+ });
316
+
317
+ it('should fetch data', () => {
318
+ const mockData = [{ id: 1, name: 'Test' }];
319
+
320
+ service.getData().subscribe((data) => {
321
+ expect(data).toEqual(mockData);
322
+ });
323
+
324
+ const req = httpMock.expectOne('/api/data');
325
+ expect(req.request.method).toBe('GET');
326
+ req.flush(mockData);
327
+ });
328
+ });
329
+ ```
330
+
331
+ ## NestJS Service Testing
332
+
333
+ ### Basic Service Test
334
+
335
+ ```typescript
336
+ import { TestingModule, Test } from '@nestjs/testing';
337
+
338
+ import { FeatureService } from './feature.service';
339
+
340
+ describe('@mms/module-name: FeatureService', () => {
341
+ let service: FeatureService;
342
+ let module: TestingModule;
343
+
344
+ beforeEach(async () => {
345
+ module = await Test.createTestingModule({
346
+ providers: [FeatureService],
347
+ }).compile();
348
+
349
+ service = module.get<FeatureService>(FeatureService);
350
+ });
351
+
352
+ afterEach(async () => {
353
+ await module.close();
354
+ });
355
+
356
+ it('should be defined', () => {
357
+ expect(service).toBeDefined();
358
+ });
359
+
360
+ it('should perform expected operation', () => {
361
+ const input = 'test';
362
+
363
+ const result = service.performOperation(input);
364
+
365
+ expect(result).toBe('expected output');
366
+ });
367
+ });
368
+ ```
369
+
370
+ ## Mocking Patterns
371
+
372
+ ### Service Mock
373
+
374
+ ```typescript
375
+ const mockService = {
376
+ getData: jest.fn().mockReturnValue(of(mockData)),
377
+ createItem: jest.fn().mockReturnValue(of(mockItem)),
378
+ updateItem: jest.fn().mockReturnValue(of(updatedMockItem)),
379
+ };
380
+ ```
381
+
382
+ ### LocalStorage Mock
383
+
384
+ ```typescript
385
+ let localStorageMock: any;
386
+
387
+ beforeEach(() => {
388
+ localStorageMock = (() => {
389
+ let store: Record<string, string> = {};
390
+ return {
391
+ getItem: jest.fn((key: string) => store[key] || null),
392
+ setItem: jest.fn((key: string, value: string) => {
393
+ store[key] = value;
394
+ }),
395
+ removeItem: jest.fn((key: string) => {
396
+ delete store[key];
397
+ }),
398
+ clear: jest.fn(() => {
399
+ store = {};
400
+ }),
401
+ };
402
+ })();
403
+ Object.defineProperty(window, 'localStorage', { value: localStorageMock });
404
+ });
405
+ ```
406
+
407
+ ### Router Mock
408
+
409
+ ```typescript
410
+ const mockRouter = {
411
+ navigate: jest.fn(),
412
+ navigateByUrl: jest.fn(),
413
+ createUrlTree: jest.fn(),
414
+ };
415
+
416
+ // In providers
417
+ { provide: Router, useValue: mockRouter }
418
+ ```
419
+
420
+ ### ActivatedRoute Mock
421
+
422
+ ```typescript
423
+ const mockActivatedRoute = {
424
+ params: of({ id: '123' }),
425
+ queryParams: of({ filter: 'active' }),
426
+ snapshot: {
427
+ params: { id: '123' },
428
+ queryParams: { filter: 'active' },
429
+ },
430
+ };
431
+
432
+ // In providers
433
+ { provide: ActivatedRoute, useValue: mockActivatedRoute }
434
+ ```
435
+
436
+ ## Test Commands (Nx 14)
437
+
438
+ ```bash
439
+ # Run tests for specific project
440
+ nx test web
441
+ nx test shared-angular
442
+
443
+ # Run tests in watch mode
444
+ nx test web --watch
445
+
446
+ # Run tests with coverage
447
+ nx test web --coverage
448
+
449
+ # Run all tests
450
+ nx run-many --target=test
451
+
452
+ # Run specific test file
453
+ nx test shared-angular --testFile=feature.service.spec.ts
454
+ ```
455
+
456
+ ## What to Test
457
+
458
+ - Business logic in services
459
+ - Component behavior and state changes
460
+ - Public API contracts (@Input/@Output)
461
+ - BehaviorSubject state management
462
+ - Error handling scenarios
463
+ - Input validation
464
+ - Edge cases and boundary conditions
465
+
466
+ ## What NOT to Test
467
+
468
+ - Third-party libraries
469
+ - Framework internals (Angular/NestJS)
470
+ - Simple getters/setters without logic
471
+ - Auto-generated code
472
+ - Private methods directly (test through public API)
473
+
474
+ ## Workflow
475
+
476
+ 1. **Read source file** to understand the code being tested
477
+ 2. **Create test file** with `.spec.ts` extension next to source
478
+ 3. **Write describe block** with package and class name
479
+ 4. **Add beforeEach** with TestBed setup (declarations, not imports)
480
+ 5. **Write tests** using AAA pattern
481
+ 6. **Run tests** with `nx test {project}`
482
+ 7. **Fix failures** and ensure all pass
483
+
484
+ ## Best Practices (Angular 14)
485
+
486
+ 1. **One assertion per test** when practical
487
+ 2. **Descriptive test names** that explain behavior
488
+ 3. **Independent tests** - no shared state between tests
489
+ 4. **Mock external dependencies** - isolate unit under test
490
+ 5. **Test edge cases** - empty arrays, null values, boundaries
491
+ 6. **Keep tests fast** - avoid real HTTP calls or timers
492
+ 7. **Clean up** - use afterEach for cleanup when needed
493
+ 8. **Use declarations** - components are NOT standalone in Angular 14
494
+ 9. **Mock BehaviorSubjects** - not signals (no signals in Angular 14)