@smartsoft001/pro-claude-plugins 0.2.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.
- package/.claude-plugin/marketplace.json +26 -0
- package/README.md +56 -0
- package/package.json +14 -0
- package/plugins/flow/.claude-plugin/README.md +91 -0
- package/plugins/flow/.claude-plugin/merge-permissions.js +115 -0
- package/plugins/flow/.claude-plugin/plugin.json +5 -0
- package/plugins/flow/.claude-plugin/settings.template.json +86 -0
- package/plugins/flow/agents/angular-component-scaffolder.md +176 -0
- package/plugins/flow/agents/angular-directive-builder.md +154 -0
- package/plugins/flow/agents/angular-guard-builder.md +244 -0
- package/plugins/flow/agents/angular-jest-test-writer.md +475 -0
- package/plugins/flow/agents/angular-pipe-builder.md +170 -0
- package/plugins/flow/agents/angular-resolver-builder.md +287 -0
- package/plugins/flow/agents/angular-service-builder.md +162 -0
- package/plugins/flow/agents/angular-signal-state-builder.md +340 -0
- package/plugins/flow/agents/angular-test-diagnostician.md +279 -0
- package/plugins/flow/agents/angular-testbed-configurator.md +315 -0
- package/plugins/flow/agents/arch-scaffolder.md +278 -0
- package/plugins/flow/agents/e2e-data-testid-injector.md +195 -0
- package/plugins/flow/agents/e2e-project-scaffolder.md +194 -0
- package/plugins/flow/agents/e2e-test-writer.md +357 -0
- package/plugins/flow/agents/shared-agent-evolver.md +294 -0
- package/plugins/flow/agents/shared-build-verifier.md +160 -0
- package/plugins/flow/agents/shared-config-updater.md +310 -0
- package/plugins/flow/agents/shared-coverage-enforcer.md +184 -0
- package/plugins/flow/agents/shared-error-handler.md +217 -0
- package/plugins/flow/agents/shared-file-creator.md +344 -0
- package/plugins/flow/agents/shared-impl-orchestrator.md +312 -0
- package/plugins/flow/agents/shared-impl-reporter.md +368 -0
- package/plugins/flow/agents/shared-impl-teammate.md +201 -0
- package/plugins/flow/agents/shared-linear-subtask-iterator.md +337 -0
- package/plugins/flow/agents/shared-logic-implementer.md +244 -0
- package/plugins/flow/agents/shared-maia-api.md +26 -0
- package/plugins/flow/agents/shared-parallelization-analyzer.md +343 -0
- package/plugins/flow/agents/shared-performance-validator.md +168 -0
- package/plugins/flow/agents/shared-plan-perspective.md +271 -0
- package/plugins/flow/agents/shared-project-standardizer.md +205 -0
- package/plugins/flow/agents/shared-report-synthesizer.md +188 -0
- package/plugins/flow/agents/shared-review-teammate.md +189 -0
- package/plugins/flow/agents/shared-security-scanner.md +186 -0
- package/plugins/flow/agents/shared-style-enforcer.md +230 -0
- package/plugins/flow/agents/shared-subtask-dependency-analyzer.md +189 -0
- package/plugins/flow/agents/shared-tdd-developer.md +350 -0
- package/plugins/flow/agents/shared-team-coordinator.md +192 -0
- package/plugins/flow/agents/shared-test-fixer.md +186 -0
- package/plugins/flow/agents/shared-test-runner.md +191 -0
- package/plugins/flow/agents/shared-ui-classifier.md +263 -0
- package/plugins/flow/agents/shared-verification-orchestrator.md +194 -0
- package/plugins/flow/agents/shared-verification-runner.md +140 -0
- package/plugins/flow/agents/ui-a11y-validator.md +305 -0
- package/plugins/flow/agents/ui-screenshot-reporter.md +329 -0
- package/plugins/flow/agents/ui-web-designer.md +214 -0
- package/plugins/flow/scripts/compute-session-tokens.sh +39 -0
- package/plugins/flow/skills/a11y-audit/SKILL.md +214 -0
- package/plugins/flow/skills/angular-patterns/SKILL.md +191 -0
- package/plugins/flow/skills/browser-capture/SKILL.md +238 -0
- package/plugins/flow/skills/commit/SKILL.md +259 -0
- package/plugins/flow/skills/debug-helper/SKILL.md +375 -0
- package/plugins/flow/skills/impl/SKILL.md +992 -0
- package/plugins/flow/skills/karpathy-guidelines/SKILL.md +72 -0
- package/plugins/flow/skills/linear-suggestion/SKILL.md +132 -0
- package/plugins/flow/skills/maia-files-delete/SKILL.md +59 -0
- package/plugins/flow/skills/maia-files-upload/SKILL.md +57 -0
- package/plugins/flow/skills/nx-conventions/SKILL.md +326 -0
- package/plugins/flow/skills/plan/SKILL.md +1033 -0
- package/plugins/flow/skills/push/SKILL.md +759 -0
- package/plugins/flow/skills/review/SKILL.md +443 -0
- package/plugins/flow/skills/test-e2e/SKILL.md +164 -0
- package/plugins/flow/skills/test-unit/SKILL.md +456 -0
- package/plugins/flow-external/.claude-plugin/README.md +146 -0
- package/plugins/flow-external/.claude-plugin/merge-permissions.js +94 -0
- package/plugins/flow-external/.claude-plugin/plugin.json +5 -0
- package/plugins/flow-external/.claude-plugin/settings.template.json +78 -0
- package/plugins/flow-external/agents/angular-component-scaffolder.md +271 -0
- package/plugins/flow-external/agents/angular-directive-builder.md +134 -0
- package/plugins/flow-external/agents/angular-guard-builder.md +73 -0
- package/plugins/flow-external/agents/angular-pipe-builder.md +90 -0
- package/plugins/flow-external/agents/angular-resolver-builder.md +79 -0
- package/plugins/flow-external/agents/angular-service-builder.md +168 -0
- package/plugins/flow-external/agents/angular-state-builder.md +263 -0
- package/plugins/flow-external/agents/shared-build-verifier.md +67 -0
- package/plugins/flow-external/agents/shared-impl-orchestrator.md +90 -0
- package/plugins/flow-external/agents/shared-impl-reporter.md +135 -0
- package/plugins/flow-external/agents/shared-linear-subtask-iterator.md +70 -0
- package/plugins/flow-external/agents/shared-ui-classifier.md +38 -0
- package/plugins/flow-external/agents/ui-web-designer.md +203 -0
- package/plugins/flow-external/scripts/compute-session-tokens.sh +39 -0
- package/plugins/flow-external/skills/a11y-audit/SKILL.md +97 -0
- package/plugins/flow-external/skills/angular-cli-conventions/SKILL.md +268 -0
- package/plugins/flow-external/skills/angular-patterns/SKILL.md +182 -0
- package/plugins/flow-external/skills/browser-capture/SKILL.md +74 -0
- package/plugins/flow-external/skills/commit/SKILL.md +114 -0
- package/plugins/flow-external/skills/debug-helper/SKILL.md +135 -0
- package/plugins/flow-external/skills/impl/SKILL.md +218 -0
- package/plugins/flow-external/skills/karpathy-guidelines/SKILL.md +72 -0
- package/plugins/flow-external/skills/linear-suggestion/SKILL.md +62 -0
- package/plugins/flow-external/skills/maia-files-delete/SKILL.md +34 -0
- package/plugins/flow-external/skills/maia-files-upload/SKILL.md +41 -0
- package/plugins/flow-external/skills/plan/SKILL.md +334 -0
- package/plugins/flow-external/skills/push/SKILL.md +232 -0
- package/plugins/flow-legacy/.claude-plugin/README.md +143 -0
- package/plugins/flow-legacy/.claude-plugin/merge-permissions.js +94 -0
- package/plugins/flow-legacy/.claude-plugin/plugin.json +5 -0
- package/plugins/flow-legacy/.claude-plugin/settings.template.json +79 -0
- package/plugins/flow-legacy/agents/angular-component-scaffolder.md +327 -0
- package/plugins/flow-legacy/agents/angular-directive-builder.md +253 -0
- package/plugins/flow-legacy/agents/angular-guard-builder.md +326 -0
- package/plugins/flow-legacy/agents/angular-pipe-builder.md +238 -0
- package/plugins/flow-legacy/agents/angular-resolver-builder.md +339 -0
- package/plugins/flow-legacy/agents/angular-service-builder.md +271 -0
- package/plugins/flow-legacy/agents/angular-state-builder.md +480 -0
- package/plugins/flow-legacy/agents/shared-impl-orchestrator.md +174 -0
- package/plugins/flow-legacy/agents/shared-impl-reporter.md +232 -0
- package/plugins/flow-legacy/agents/shared-linear-subtask-iterator.md +198 -0
- package/plugins/flow-legacy/agents/shared-tdd-developer.md +307 -0
- package/plugins/flow-legacy/agents/shared-test-runner.md +133 -0
- package/plugins/flow-legacy/agents/shared-ui-classifier.md +145 -0
- package/plugins/flow-legacy/scripts/compute-session-tokens.sh +39 -0
- package/plugins/flow-legacy/skills/a11y-audit/SKILL.md +214 -0
- package/plugins/flow-legacy/skills/angular-patterns/SKILL.md +346 -0
- package/plugins/flow-legacy/skills/browser-capture/SKILL.md +238 -0
- package/plugins/flow-legacy/skills/commit/SKILL.md +215 -0
- package/plugins/flow-legacy/skills/debug-helper/SKILL.md +395 -0
- package/plugins/flow-legacy/skills/impl/SKILL.md +710 -0
- package/plugins/flow-legacy/skills/karpathy-guidelines/SKILL.md +72 -0
- package/plugins/flow-legacy/skills/linear-suggestion/SKILL.md +132 -0
- package/plugins/flow-legacy/skills/maia-files-delete/SKILL.md +59 -0
- package/plugins/flow-legacy/skills/maia-files-upload/SKILL.md +57 -0
- package/plugins/flow-legacy/skills/nx-conventions/SKILL.md +368 -0
- package/plugins/flow-legacy/skills/plan/SKILL.md +742 -0
- package/plugins/flow-legacy/skills/push/SKILL.md +645 -0
- package/plugins/flow-legacy/skills/test-unit/SKILL.md +500 -0
- package/plugins/smart-pro/.claude-plugin/README.md +115 -0
- package/plugins/smart-pro/.claude-plugin/merge-permissions.js +129 -0
- package/plugins/smart-pro/.claude-plugin/plugin.json +5 -0
- package/plugins/smart-pro/.claude-plugin/settings.template.json +94 -0
- package/plugins/smart-pro/agents/angular-components/AGENT.md +214 -0
- package/plugins/smart-pro/hooks/CONFIG.md +198 -0
- package/plugins/smart-pro/hooks/README.md +139 -0
- package/plugins/smart-pro/hooks/audit_logger.py +107 -0
- package/plugins/smart-pro/hooks/auto_format.sh +11 -0
- package/plugins/smart-pro/hooks/hooks.json +79 -0
- package/plugins/smart-pro/hooks/safety_validator.py +129 -0
- package/plugins/smart-pro/hooks/sensitive_file_blocker.py +58 -0
- package/plugins/smart-pro/hooks/skill_validator.py +220 -0
- package/plugins/smart-pro/skills/angular-components-action-panel/SKILL.md +159 -0
- package/plugins/smart-pro/skills/angular-components-avatar/SKILL.md +142 -0
- package/plugins/smart-pro/skills/angular-components-badge/SKILL.md +110 -0
- package/plugins/smart-pro/skills/angular-components-breadcrumbs/SKILL.md +91 -0
- package/plugins/smart-pro/skills/angular-components-button/SKILL.md +146 -0
- package/plugins/smart-pro/skills/angular-components-button-group/SKILL.md +121 -0
- package/plugins/smart-pro/skills/angular-components-calendar/SKILL.md +78 -0
- package/plugins/smart-pro/skills/angular-components-card/SKILL.md +110 -0
- package/plugins/smart-pro/skills/angular-components-card-heading/SKILL.md +105 -0
- package/plugins/smart-pro/skills/angular-components-command-palette/SKILL.md +168 -0
- package/plugins/smart-pro/skills/angular-components-container/SKILL.md +93 -0
- package/plugins/smart-pro/skills/angular-components-description-list/SKILL.md +102 -0
- package/plugins/smart-pro/skills/angular-components-details/SKILL.md +70 -0
- package/plugins/smart-pro/skills/angular-components-divider/SKILL.md +110 -0
- package/plugins/smart-pro/skills/angular-components-drawer/SKILL.md +136 -0
- package/plugins/smart-pro/skills/angular-components-dropdown/SKILL.md +131 -0
- package/plugins/smart-pro/skills/angular-components-empty-state/SKILL.md +103 -0
- package/plugins/smart-pro/skills/angular-components-feed/SKILL.md +110 -0
- package/plugins/smart-pro/skills/angular-components-form/SKILL.md +77 -0
- package/plugins/smart-pro/skills/angular-components-grid-list/SKILL.md +91 -0
- package/plugins/smart-pro/skills/angular-components-input/SKILL.md +118 -0
- package/plugins/smart-pro/skills/angular-components-list/SKILL.md +75 -0
- package/plugins/smart-pro/skills/angular-components-list-container/SKILL.md +106 -0
- package/plugins/smart-pro/skills/angular-components-media-object/SKILL.md +133 -0
- package/plugins/smart-pro/skills/angular-components-modal/SKILL.md +159 -0
- package/plugins/smart-pro/skills/angular-components-multi-column-layout/SKILL.md +84 -0
- package/plugins/smart-pro/skills/angular-components-navbar/SKILL.md +93 -0
- package/plugins/smart-pro/skills/angular-components-notification/SKILL.md +147 -0
- package/plugins/smart-pro/skills/angular-components-page/SKILL.md +162 -0
- package/plugins/smart-pro/skills/angular-components-page-heading/SKILL.md +96 -0
- package/plugins/smart-pro/skills/angular-components-paging/SKILL.md +86 -0
- package/plugins/smart-pro/skills/angular-components-progress-bars/SKILL.md +94 -0
- package/plugins/smart-pro/skills/angular-components-searchbar/SKILL.md +113 -0
- package/plugins/smart-pro/skills/angular-components-section-heading/SKILL.md +156 -0
- package/plugins/smart-pro/skills/angular-components-select-menu/SKILL.md +101 -0
- package/plugins/smart-pro/skills/angular-components-sidebar-layout/SKILL.md +121 -0
- package/plugins/smart-pro/skills/angular-components-sidebar-navigation/SKILL.md +80 -0
- package/plugins/smart-pro/skills/angular-components-sign-in-form/SKILL.md +91 -0
- package/plugins/smart-pro/skills/angular-components-stacked-layout/SKILL.md +114 -0
- package/plugins/smart-pro/skills/angular-components-stacked-list/SKILL.md +93 -0
- package/plugins/smart-pro/skills/angular-components-stats/SKILL.md +87 -0
- package/plugins/smart-pro/skills/angular-components-table/SKILL.md +98 -0
- package/plugins/smart-pro/skills/angular-components-tabs/SKILL.md +99 -0
- package/plugins/smart-pro/skills/angular-components-textarea/SKILL.md +111 -0
- package/plugins/smart-pro/skills/angular-components-toggle/SKILL.md +109 -0
- package/plugins/smart-pro/skills/angular-components-vertical-navigation/SKILL.md +139 -0
- package/plugins/smart-pro/skills/audit-log/SKILL.md +82 -0
- package/plugins/smart-pro/skills/format-code/SKILL.md +46 -0
- package/plugins/smart-pro/skills/project-conventions/SKILL.md +90 -0
- package/plugins/smart-pro/skills/safety-check/SKILL.md +0 -0
- package/src/index.d.ts +6 -0
- package/src/index.js +7 -0
- package/src/index.js.map +1 -0
|
@@ -0,0 +1,500 @@
|
|
|
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 {
|
|
147
|
+
ComponentFixture,
|
|
148
|
+
TestBed,
|
|
149
|
+
fakeAsync,
|
|
150
|
+
tick,
|
|
151
|
+
} from '@angular/core/testing';
|
|
152
|
+
import { of, BehaviorSubject } from 'rxjs';
|
|
153
|
+
|
|
154
|
+
import { ListComponent } from './list.component';
|
|
155
|
+
import { StateService } from './state.service';
|
|
156
|
+
|
|
157
|
+
describe('@mms/shared-angular: ListComponent', () => {
|
|
158
|
+
let component: ListComponent;
|
|
159
|
+
let fixture: ComponentFixture<ListComponent>;
|
|
160
|
+
let mockStateService: {
|
|
161
|
+
items$: BehaviorSubject<Item[]>;
|
|
162
|
+
isLoading$: BehaviorSubject<boolean>;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
beforeEach(async () => {
|
|
166
|
+
mockStateService = {
|
|
167
|
+
items$: new BehaviorSubject<Item[]>([]),
|
|
168
|
+
isLoading$: new BehaviorSubject<boolean>(false),
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
await TestBed.configureTestingModule({
|
|
172
|
+
declarations: [ListComponent],
|
|
173
|
+
providers: [{ provide: StateService, useValue: mockStateService }],
|
|
174
|
+
}).compileComponents();
|
|
175
|
+
|
|
176
|
+
fixture = TestBed.createComponent(ListComponent);
|
|
177
|
+
component = fixture.componentInstance;
|
|
178
|
+
fixture.detectChanges();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should display items from state', () => {
|
|
182
|
+
mockStateService.items$.next([
|
|
183
|
+
{ id: '1', name: 'Item 1' },
|
|
184
|
+
{ id: '2', name: 'Item 2' },
|
|
185
|
+
]);
|
|
186
|
+
fixture.detectChanges();
|
|
187
|
+
|
|
188
|
+
const items = fixture.nativeElement.querySelectorAll('li');
|
|
189
|
+
expect(items.length).toBe(2);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('should show loading spinner', () => {
|
|
193
|
+
mockStateService.isLoading$.next(true);
|
|
194
|
+
fixture.detectChanges();
|
|
195
|
+
|
|
196
|
+
const spinner = fixture.nativeElement.querySelector('app-spinner');
|
|
197
|
+
expect(spinner).toBeTruthy();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Testing with Services (Constructor DI)
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|
206
|
+
import { of } from 'rxjs';
|
|
207
|
+
|
|
208
|
+
import { FeatureComponent } from './feature.component';
|
|
209
|
+
import { DataService } from './data.service';
|
|
210
|
+
|
|
211
|
+
describe('@mms/shared-angular: FeatureComponent', () => {
|
|
212
|
+
let component: FeatureComponent;
|
|
213
|
+
let fixture: ComponentFixture<FeatureComponent>;
|
|
214
|
+
let mockDataService: jest.Mocked<DataService>;
|
|
215
|
+
|
|
216
|
+
beforeEach(async () => {
|
|
217
|
+
mockDataService = {
|
|
218
|
+
getData: jest.fn().mockReturnValue(of(['item1', 'item2'])),
|
|
219
|
+
saveData: jest.fn().mockReturnValue(of(true)),
|
|
220
|
+
} as unknown as jest.Mocked<DataService>;
|
|
221
|
+
|
|
222
|
+
await TestBed.configureTestingModule({
|
|
223
|
+
declarations: [FeatureComponent],
|
|
224
|
+
providers: [{ provide: DataService, useValue: mockDataService }],
|
|
225
|
+
}).compileComponents();
|
|
226
|
+
|
|
227
|
+
fixture = TestBed.createComponent(FeatureComponent);
|
|
228
|
+
component = fixture.componentInstance;
|
|
229
|
+
fixture.detectChanges();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should load data on init', () => {
|
|
233
|
+
expect(mockDataService.getData).toHaveBeenCalled();
|
|
234
|
+
expect(component.items).toEqual(['item1', 'item2']);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Angular 14 Service Testing
|
|
240
|
+
|
|
241
|
+
### Service with BehaviorSubject
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import { TestBed } from '@angular/core/testing';
|
|
245
|
+
import { take } from 'rxjs/operators';
|
|
246
|
+
|
|
247
|
+
import { StateService } from './state.service';
|
|
248
|
+
|
|
249
|
+
describe('@mms/shared-angular: StateService', () => {
|
|
250
|
+
let service: StateService;
|
|
251
|
+
|
|
252
|
+
beforeEach(() => {
|
|
253
|
+
TestBed.configureTestingModule({
|
|
254
|
+
providers: [StateService],
|
|
255
|
+
});
|
|
256
|
+
service = TestBed.inject(StateService);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('should be created', () => {
|
|
260
|
+
expect(service).toBeTruthy();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should emit initial state', (done) => {
|
|
264
|
+
service.items$.pipe(take(1)).subscribe((items) => {
|
|
265
|
+
expect(items).toEqual([]);
|
|
266
|
+
done();
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should update items', (done) => {
|
|
271
|
+
const newItems = [{ id: '1', name: 'Test' }];
|
|
272
|
+
|
|
273
|
+
service.setItems(newItems);
|
|
274
|
+
|
|
275
|
+
service.items$.pipe(take(1)).subscribe((items) => {
|
|
276
|
+
expect(items).toEqual(newItems);
|
|
277
|
+
done();
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should return current value', () => {
|
|
282
|
+
const items = [{ id: '1', name: 'Test' }];
|
|
283
|
+
service.setItems(items);
|
|
284
|
+
|
|
285
|
+
expect(service.currentItems).toEqual(items);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Service with HTTP (Angular 14 HttpClientTestingModule)
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
import { TestBed } from '@angular/core/testing';
|
|
294
|
+
import {
|
|
295
|
+
HttpClientTestingModule,
|
|
296
|
+
HttpTestingController,
|
|
297
|
+
} from '@angular/common/http/testing';
|
|
298
|
+
|
|
299
|
+
import { DataService } from './data.service';
|
|
300
|
+
|
|
301
|
+
describe('@mms/shared-angular: DataService', () => {
|
|
302
|
+
let service: DataService;
|
|
303
|
+
let httpMock: HttpTestingController;
|
|
304
|
+
|
|
305
|
+
beforeEach(() => {
|
|
306
|
+
TestBed.configureTestingModule({
|
|
307
|
+
imports: [HttpClientTestingModule],
|
|
308
|
+
providers: [DataService],
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
service = TestBed.inject(DataService);
|
|
312
|
+
httpMock = TestBed.inject(HttpTestingController);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
afterEach(() => {
|
|
316
|
+
httpMock.verify();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('should be created', () => {
|
|
320
|
+
expect(service).toBeTruthy();
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('should fetch data', () => {
|
|
324
|
+
const mockData = [{ id: 1, name: 'Test' }];
|
|
325
|
+
|
|
326
|
+
service.getData().subscribe((data) => {
|
|
327
|
+
expect(data).toEqual(mockData);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const req = httpMock.expectOne('/api/data');
|
|
331
|
+
expect(req.request.method).toBe('GET');
|
|
332
|
+
req.flush(mockData);
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## NestJS Service Testing
|
|
338
|
+
|
|
339
|
+
### Basic Service Test
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import { TestingModule, Test } from '@nestjs/testing';
|
|
343
|
+
|
|
344
|
+
import { FeatureService } from './feature.service';
|
|
345
|
+
|
|
346
|
+
describe('@mms/module-name: FeatureService', () => {
|
|
347
|
+
let service: FeatureService;
|
|
348
|
+
let module: TestingModule;
|
|
349
|
+
|
|
350
|
+
beforeEach(async () => {
|
|
351
|
+
module = await Test.createTestingModule({
|
|
352
|
+
providers: [FeatureService],
|
|
353
|
+
}).compile();
|
|
354
|
+
|
|
355
|
+
service = module.get<FeatureService>(FeatureService);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
afterEach(async () => {
|
|
359
|
+
await module.close();
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it('should be defined', () => {
|
|
363
|
+
expect(service).toBeDefined();
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it('should perform expected operation', () => {
|
|
367
|
+
const input = 'test';
|
|
368
|
+
|
|
369
|
+
const result = service.performOperation(input);
|
|
370
|
+
|
|
371
|
+
expect(result).toBe('expected output');
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## Mocking Patterns
|
|
377
|
+
|
|
378
|
+
### Service Mock
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
const mockService = {
|
|
382
|
+
getData: jest.fn().mockReturnValue(of(mockData)),
|
|
383
|
+
createItem: jest.fn().mockReturnValue(of(mockItem)),
|
|
384
|
+
updateItem: jest.fn().mockReturnValue(of(updatedMockItem)),
|
|
385
|
+
};
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### LocalStorage Mock
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
let localStorageMock: any;
|
|
392
|
+
|
|
393
|
+
beforeEach(() => {
|
|
394
|
+
localStorageMock = (() => {
|
|
395
|
+
let store: Record<string, string> = {};
|
|
396
|
+
return {
|
|
397
|
+
getItem: jest.fn((key: string) => store[key] || null),
|
|
398
|
+
setItem: jest.fn((key: string, value: string) => {
|
|
399
|
+
store[key] = value;
|
|
400
|
+
}),
|
|
401
|
+
removeItem: jest.fn((key: string) => {
|
|
402
|
+
delete store[key];
|
|
403
|
+
}),
|
|
404
|
+
clear: jest.fn(() => {
|
|
405
|
+
store = {};
|
|
406
|
+
}),
|
|
407
|
+
};
|
|
408
|
+
})();
|
|
409
|
+
Object.defineProperty(window, 'localStorage', { value: localStorageMock });
|
|
410
|
+
});
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Router Mock
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
const mockRouter = {
|
|
417
|
+
navigate: jest.fn(),
|
|
418
|
+
navigateByUrl: jest.fn(),
|
|
419
|
+
createUrlTree: jest.fn(),
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
// In providers
|
|
423
|
+
{ provide: Router, useValue: mockRouter }
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### ActivatedRoute Mock
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
const mockActivatedRoute = {
|
|
430
|
+
params: of({ id: '123' }),
|
|
431
|
+
queryParams: of({ filter: 'active' }),
|
|
432
|
+
snapshot: {
|
|
433
|
+
params: { id: '123' },
|
|
434
|
+
queryParams: { filter: 'active' },
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// In providers
|
|
439
|
+
{ provide: ActivatedRoute, useValue: mockActivatedRoute }
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## Test Commands (Nx 14)
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
# Run tests for specific project
|
|
446
|
+
nx test web
|
|
447
|
+
nx test shared-angular
|
|
448
|
+
|
|
449
|
+
# Run tests in watch mode
|
|
450
|
+
nx test web --watch
|
|
451
|
+
|
|
452
|
+
# Run tests with coverage
|
|
453
|
+
nx test web --coverage
|
|
454
|
+
|
|
455
|
+
# Run all tests
|
|
456
|
+
nx run-many --target=test
|
|
457
|
+
|
|
458
|
+
# Run specific test file
|
|
459
|
+
nx test shared-angular --testFile=feature.service.spec.ts
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## What to Test
|
|
463
|
+
|
|
464
|
+
- Business logic in services
|
|
465
|
+
- Component behavior and state changes
|
|
466
|
+
- Public API contracts (@Input/@Output)
|
|
467
|
+
- BehaviorSubject state management
|
|
468
|
+
- Error handling scenarios
|
|
469
|
+
- Input validation
|
|
470
|
+
- Edge cases and boundary conditions
|
|
471
|
+
|
|
472
|
+
## What NOT to Test
|
|
473
|
+
|
|
474
|
+
- Third-party libraries
|
|
475
|
+
- Framework internals (Angular/NestJS)
|
|
476
|
+
- Simple getters/setters without logic
|
|
477
|
+
- Auto-generated code
|
|
478
|
+
- Private methods directly (test through public API)
|
|
479
|
+
|
|
480
|
+
## Workflow
|
|
481
|
+
|
|
482
|
+
1. **Read source file** to understand the code being tested
|
|
483
|
+
2. **Create test file** with `.spec.ts` extension next to source
|
|
484
|
+
3. **Write describe block** with package and class name
|
|
485
|
+
4. **Add beforeEach** with TestBed setup (declarations, not imports)
|
|
486
|
+
5. **Write tests** using AAA pattern
|
|
487
|
+
6. **Run tests** with `nx test {project}`
|
|
488
|
+
7. **Fix failures** and ensure all pass
|
|
489
|
+
|
|
490
|
+
## Best Practices (Angular 14)
|
|
491
|
+
|
|
492
|
+
1. **One assertion per test** when practical
|
|
493
|
+
2. **Descriptive test names** that explain behavior
|
|
494
|
+
3. **Independent tests** - no shared state between tests
|
|
495
|
+
4. **Mock external dependencies** - isolate unit under test
|
|
496
|
+
5. **Test edge cases** - empty arrays, null values, boundaries
|
|
497
|
+
6. **Keep tests fast** - avoid real HTTP calls or timers
|
|
498
|
+
7. **Clean up** - use afterEach for cleanup when needed
|
|
499
|
+
8. **Use declarations** - components are NOT standalone in Angular 14
|
|
500
|
+
9. **Mock BehaviorSubjects** - not signals (no signals in Angular 14)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Smart Plugin for Claude Code
|
|
2
|
+
|
|
3
|
+
Smart-pro integration - safety validation, audit logging, auto-formatting hooks, and project convention skills.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
| Feature | Type | Description |
|
|
8
|
+
|---------|------|-------------|
|
|
9
|
+
| Safety validation | Hook | Blocks destructive bash commands (`rm -rf /`, etc.) |
|
|
10
|
+
| Sensitive file protection | Hook | Blocks access to `.env`, credentials, secrets |
|
|
11
|
+
| Audit logging | Hook | Logs all Claude actions to `.claude/audit_logs/` |
|
|
12
|
+
| Skill validation | Hook | Validates skill file structure (soft warnings) |
|
|
13
|
+
| Auto-formatting | Hook | Formats files after Write/Edit operations |
|
|
14
|
+
| Safety check | Skill (background) | Safety rules knowledge for Claude |
|
|
15
|
+
| Project conventions | Skill (background) | Monorepo conventions and patterns |
|
|
16
|
+
| Audit log viewer | Skill (user-invocable) | `/smart:audit-log` to query audit logs |
|
|
17
|
+
| Format code | Skill (user-invocable) | `/smart:format-code` to run formatting |
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
claude plugin update smart-pro@smart-pro --scope project
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Architecture
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
smart/
|
|
29
|
+
├── .claude-plugin/
|
|
30
|
+
│ ├── plugin.json # Plugin metadata
|
|
31
|
+
│ └── README.md # This file
|
|
32
|
+
├── hooks/
|
|
33
|
+
│ ├── hooks.json # Hook configuration
|
|
34
|
+
│ ├── safety_validator.py # Blocks destructive commands
|
|
35
|
+
│ ├── sensitive_file_blocker.py # Blocks access to sensitive files
|
|
36
|
+
│ ├── audit_logger.py # Logs all Claude actions
|
|
37
|
+
│ ├── skill_validator.py # Validates skill file structure
|
|
38
|
+
│ ├── auto_format.sh # Formats code after changes
|
|
39
|
+
│ ├── CONFIG.md # Hook customization guide
|
|
40
|
+
│ └── README.md # Hook documentation
|
|
41
|
+
├── skills/
|
|
42
|
+
│ ├── safety-check/SKILL.md # Background: safety rules
|
|
43
|
+
│ ├── audit-log/SKILL.md # User-invocable: /smart:audit-log
|
|
44
|
+
│ ├── format-code/SKILL.md # User-invocable: /smart:format-code
|
|
45
|
+
│ ├── project-conventions/SKILL.md # Background: project knowledge
|
|
46
|
+
│ ├── angular-components-button/SKILL.md # Background: button API
|
|
47
|
+
│ └── angular-components-card/SKILL.md # Background: card API
|
|
48
|
+
├── _legacy/
|
|
49
|
+
│ ├── settings.template.json # Deprecated settings template
|
|
50
|
+
│ └── merge-permissions.js # Deprecated merge script
|
|
51
|
+
└── MIGRATION.md # Migration guide from legacy
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Skills
|
|
55
|
+
|
|
56
|
+
### User-Invocable
|
|
57
|
+
|
|
58
|
+
| Skill | Command | Purpose |
|
|
59
|
+
|-------|---------|---------|
|
|
60
|
+
| audit-log | `/smart:audit-log [today\|yesterday\|YYYYMMDD]` | Query audit logs |
|
|
61
|
+
| format-code | `/smart:format-code` | Run `npm run format` |
|
|
62
|
+
|
|
63
|
+
### Background (automatic)
|
|
64
|
+
|
|
65
|
+
| Skill | Purpose |
|
|
66
|
+
|-------|---------|
|
|
67
|
+
| safety-check | Blocked patterns and sensitive file rules |
|
|
68
|
+
| project-conventions | Nx monorepo structure, conventions, patterns |
|
|
69
|
+
| angular-components-button | Button component API, variants, and usage patterns |
|
|
70
|
+
| angular-components-card | Card component API, variants, and usage patterns |
|
|
71
|
+
|
|
72
|
+
## Hooks
|
|
73
|
+
|
|
74
|
+
Hooks are configured in `hooks/hooks.json` and run automatically:
|
|
75
|
+
|
|
76
|
+
### PreToolUse
|
|
77
|
+
|
|
78
|
+
| Hook | Matcher | Purpose |
|
|
79
|
+
|------|---------|---------|
|
|
80
|
+
| `safety_validator.py` | Bash\|Write\|Edit | Blocks destructive commands |
|
|
81
|
+
| `sensitive_file_blocker.py` | Read\|Edit\|Write | Blocks sensitive files |
|
|
82
|
+
| `audit_logger.py` | * | Logs all events |
|
|
83
|
+
| `skill_validator.py` | Write\|Edit | Validates skill files |
|
|
84
|
+
|
|
85
|
+
### PostToolUse
|
|
86
|
+
|
|
87
|
+
| Hook | Matcher | Purpose |
|
|
88
|
+
|------|---------|---------|
|
|
89
|
+
| `auto_format.sh` | Write\|Edit | Runs formatting pipeline |
|
|
90
|
+
| `audit_logger.py` | * | Logs all events |
|
|
91
|
+
|
|
92
|
+
### UserPromptSubmit
|
|
93
|
+
|
|
94
|
+
| Hook | Purpose |
|
|
95
|
+
|------|---------|
|
|
96
|
+
| `audit_logger.py` | Logs user prompts |
|
|
97
|
+
|
|
98
|
+
## Audit Logs
|
|
99
|
+
|
|
100
|
+
Logs are stored in `.claude/audit_logs/YYYYMMDD_audit.jsonl`:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{"timestamp":"2024-01-15T10:30:00Z","event_type":"PreToolUse","session_id":"abc123","event_data":{...}}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Customization
|
|
107
|
+
|
|
108
|
+
See `hooks/CONFIG.md` for:
|
|
109
|
+
- Adding custom blocked patterns
|
|
110
|
+
- Changing log location
|
|
111
|
+
- Creating new hooks
|
|
112
|
+
|
|
113
|
+
## Migration from Legacy
|
|
114
|
+
|
|
115
|
+
If upgrading from the old `settings.template.json` approach, see `MIGRATION.md`.
|