sumulige-claude 1.5.1 → 1.6.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 (223) hide show
  1. package/.claude/hooks/hook-registry.json +0 -15
  2. package/.claude/rules/coding-style.md +18 -7
  3. package/.claude/rules/hooks.md +15 -4
  4. package/.claude/rules/performance.md +15 -5
  5. package/.claude/rules/security.md +140 -4
  6. package/.claude/rules/testing.md +138 -9
  7. package/.claude/rules/web-design-standard.md +16 -5
  8. package/.claude/skills/algorithmic-art/metadata.yaml +28 -0
  9. package/.claude/skills/api-tester/SKILL.md +61 -0
  10. package/.claude/skills/api-tester/examples/basic.md +3 -0
  11. package/.claude/skills/api-tester/metadata.yaml +30 -0
  12. package/.claude/skills/api-tester/templates/default.md +3 -0
  13. package/.claude/skills/brand-guidelines/metadata.yaml +26 -0
  14. package/.claude/skills/canvas-design/metadata.yaml +27 -0
  15. package/.claude/skills/code-reviewer-123/SKILL.md +61 -0
  16. package/.claude/skills/code-reviewer-123/examples/basic.md +3 -0
  17. package/.claude/skills/code-reviewer-123/metadata.yaml +30 -0
  18. package/.claude/skills/code-reviewer-123/templates/default.md +3 -0
  19. package/.claude/skills/doc-coauthoring/metadata.yaml +27 -0
  20. package/.claude/skills/docx/metadata.yaml +30 -0
  21. package/.claude/skills/frontend-design/metadata.yaml +28 -0
  22. package/.claude/skills/internal-comms/metadata.yaml +28 -0
  23. package/.claude/skills/mcp-builder/metadata.yaml +26 -0
  24. package/.claude/skills/my-skill/SKILL.md +61 -0
  25. package/.claude/skills/my-skill/examples/basic.md +3 -0
  26. package/.claude/skills/my-skill/metadata.yaml +30 -0
  27. package/.claude/skills/my-skill/templates/default.md +3 -0
  28. package/.claude/skills/pdf/metadata.yaml +29 -0
  29. package/.claude/skills/pptx/metadata.yaml +29 -0
  30. package/.claude/skills/react-best-practices/metadata.yaml +26 -0
  31. package/.claude/skills/react-node-practices/SKILL.md +409 -0
  32. package/.claude/skills/react-node-practices/metadata.yaml +56 -0
  33. package/.claude/skills/skill-creator/metadata.yaml +25 -0
  34. package/.claude/skills/slack-gif-creator/metadata.yaml +28 -0
  35. package/.claude/skills/test-skill-name/SKILL.md +61 -0
  36. package/.claude/skills/test-skill-name/examples/basic.md +3 -0
  37. package/.claude/skills/test-skill-name/metadata.yaml +30 -0
  38. package/.claude/skills/test-skill-name/templates/default.md +3 -0
  39. package/.claude/skills/test-workflow/metadata.yaml +32 -0
  40. package/.claude/skills/theme-factory/metadata.yaml +26 -0
  41. package/.claude/skills/threejs-fundamentals/metadata.yaml +27 -0
  42. package/.claude/skills/web-artifacts-builder/metadata.yaml +30 -0
  43. package/.claude/skills/web-design-guidelines/metadata.yaml +26 -0
  44. package/.claude/skills/webapp-testing/metadata.yaml +26 -0
  45. package/.claude/skills/xlsx/metadata.yaml +29 -0
  46. package/LICENSE +21 -0
  47. package/README.md +280 -529
  48. package/cli.js +19 -3
  49. package/package.json +29 -3
  50. package/template/.codex/README.md +69 -0
  51. package/template/.codex/config.toml +56 -0
  52. package/template/AGENTS.md +94 -0
  53. package/.claude/.kickoff-hint.txt +0 -52
  54. package/.claude/.sumulige-claude-version +0 -1
  55. package/.claude/.version +0 -1
  56. package/.claude/AGENTS.md +0 -42
  57. package/.claude/ANCHORS.md +0 -40
  58. package/.claude/CLAUDE.md +0 -138
  59. package/.claude/MEMORY.md +0 -69
  60. package/.claude/PROJECT_LOG.md +0 -101
  61. package/.claude/THINKING_CHAIN_GUIDE.md +0 -287
  62. package/.claude/USAGE.md +0 -175
  63. package/.claude/boris-optimizations.md +0 -167
  64. package/.claude/handoffs/INDEX.md +0 -21
  65. package/.claude/handoffs/LATEST.md +0 -76
  66. package/.claude/handoffs/handoff_2026-01-22T13-07-04-757Z.md +0 -76
  67. package/.claude/quality-gate.json +0 -82
  68. package/.claude/rag/skill-index.json +0 -135
  69. package/.claude/settings.json +0 -99
  70. package/.claude/settings.local.json +0 -175
  71. package/.claude/templates/PROJECT_KICKOFF.md +0 -89
  72. package/.claude/templates/PROJECT_PROPOSAL.md +0 -227
  73. package/.claude/templates/TASK_PLAN.md +0 -121
  74. package/.claude/templates/hooks/README.md +0 -302
  75. package/.claude/templates/hooks/hook.sh.template +0 -94
  76. package/.claude/templates/hooks/user-prompt-submit.cjs.template +0 -116
  77. package/.claude/templates/hooks/user-response-submit.cjs.template +0 -94
  78. package/.claude/templates/hooks/validate.js +0 -173
  79. package/.claude/templates/tasks/develop.md +0 -69
  80. package/.claude/templates/tasks/research.md +0 -64
  81. package/.claude/templates/tasks/test.md +0 -96
  82. package/.claude/thinking-routes/.last-sync +0 -1
  83. package/.claude/thinking-routes/QUICKREF.md +0 -98
  84. package/.claude/workflow/document-scanner.js +0 -426
  85. package/.claude/workflow/knowledge-engine.js +0 -941
  86. package/.claude/workflow/notebooklm/browser.js +0 -1028
  87. package/.claude/workflow/phases/phase1-research.js +0 -578
  88. package/.claude/workflow/phases/phase1-research.ts +0 -465
  89. package/.claude/workflow/phases/phase2-approve.js +0 -722
  90. package/.claude/workflow/phases/phase3-plan.js +0 -1200
  91. package/.claude/workflow/phases/phase4-develop.js +0 -894
  92. package/.claude/workflow/search-cache.js +0 -230
  93. package/.claude/workflow/templates/approval.md +0 -315
  94. package/.claude/workflow/templates/development.md +0 -377
  95. package/.claude/workflow/templates/planning.md +0 -328
  96. package/.claude/workflow/templates/research.md +0 -250
  97. package/.claude/workflow/types.js +0 -37
  98. package/.claude/workflow/web-search.js +0 -278
  99. package/.claude-plugin/marketplace.json +0 -71
  100. package/.github/workflows/sync-skills.yml +0 -74
  101. package/.versionrc +0 -25
  102. package/AGENTS.md +0 -580
  103. package/CHANGELOG.md +0 -481
  104. package/CLAUDE-template.md +0 -114
  105. package/DEV_TOOLS_GUIDE.md +0 -190
  106. package/PROJECT_STRUCTURE.md +0 -266
  107. package/Q&A.md +0 -325
  108. package/config/defaults.json +0 -34
  109. package/config/official-skills.json +0 -183
  110. package/config/quality-gate.json +0 -67
  111. package/config/skill-categories.json +0 -40
  112. package/config/version-manifest.json +0 -85
  113. package/demos/power-3d-scatter.html +0 -683
  114. package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +0 -36
  115. package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +0 -36
  116. package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +0 -36
  117. package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +0 -36
  118. package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +0 -36
  119. package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +0 -36
  120. package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +0 -36
  121. package/development/knowledge-base/.index.clean.json +0 -1
  122. package/development/knowledge-base/.index.json +0 -486
  123. package/development/knowledge-base/test-best-practices.md +0 -29
  124. package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +0 -160
  125. package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +0 -160
  126. package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +0 -160
  127. package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +0 -160
  128. package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +0 -160
  129. package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +0 -160
  130. package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +0 -160
  131. package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +0 -160
  132. package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +0 -160
  133. package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +0 -160
  134. package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +0 -226
  135. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +0 -345
  136. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +0 -284
  137. package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +0 -14
  138. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +0 -35
  139. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +0 -34
  140. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +0 -5
  141. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +0 -60
  142. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +0 -25
  143. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +0 -70
  144. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +0 -48
  145. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +0 -20
  146. package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +0 -21
  147. package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +0 -160
  148. package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +0 -226
  149. package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +0 -345
  150. package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +0 -284
  151. package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +0 -14
  152. package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +0 -160
  153. package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +0 -178
  154. package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +0 -377
  155. package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +0 -442
  156. package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +0 -800
  157. package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +0 -625
  158. package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +0 -830
  159. package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +0 -957
  160. package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +0 -381
  161. package/development/todos/.state.json +0 -19
  162. package/development/todos/INDEX.md +0 -63
  163. package/development/todos/active/_README.md +0 -49
  164. package/development/todos/archived/_README.md +0 -11
  165. package/development/todos/backlog/_README.md +0 -11
  166. package/development/todos/backlog/mcp-integration.md +0 -35
  167. package/development/todos/completed/_README.md +0 -11
  168. package/development/todos/completed/boris-optimizations.md +0 -39
  169. package/development/todos/completed/develop/local-knowledge-index.md +0 -85
  170. package/development/todos/completed/develop/todo-system.md +0 -47
  171. package/development/todos/completed/develop/web-search-integration.md +0 -83
  172. package/development/todos/completed/test/phase1-e2e-test.md +0 -103
  173. package/docs/DEVELOPMENT.md +0 -461
  174. package/docs/MARKETPLACE.md +0 -352
  175. package/docs/RELEASE.md +0 -93
  176. package/jest.config.js +0 -63
  177. package/lib/commands.js +0 -3588
  178. package/lib/config-manager.js +0 -441
  179. package/lib/config-schema.js +0 -408
  180. package/lib/config-validator.js +0 -330
  181. package/lib/config.js +0 -122
  182. package/lib/errors.js +0 -305
  183. package/lib/incremental-sync.js +0 -274
  184. package/lib/marketplace.js +0 -487
  185. package/lib/migrations.js +0 -154
  186. package/lib/permission-audit.js +0 -255
  187. package/lib/quality-gate.js +0 -431
  188. package/lib/quality-rules.js +0 -373
  189. package/lib/utils.js +0 -150
  190. package/lib/version-check.js +0 -169
  191. package/lib/version-manifest.js +0 -171
  192. package/project-paradigm.md +0 -313
  193. package/prompts/how-to-find.md +0 -163
  194. package/prompts/linus-architect.md +0 -71
  195. package/prompts/software-architect.md +0 -173
  196. package/prompts/web-designer.md +0 -249
  197. package/scripts/fix-hooks.mjs +0 -97
  198. package/scripts/sync-external.mjs +0 -298
  199. package/scripts/sync-to-home.sh +0 -108
  200. package/scripts/update-registry.mjs +0 -325
  201. package/sources.yaml +0 -83
  202. package/tests/README.md +0 -263
  203. package/tests/commands.test.js +0 -1086
  204. package/tests/config-manager.test.js +0 -677
  205. package/tests/config-schema.test.js +0 -425
  206. package/tests/config-validator.test.js +0 -436
  207. package/tests/config.test.js +0 -100
  208. package/tests/errors.test.js +0 -477
  209. package/tests/manual/phase1-e2e.sh +0 -389
  210. package/tests/manual/phase2-test-cases.md +0 -311
  211. package/tests/manual/phase3-test-cases.md +0 -309
  212. package/tests/manual/phase4-test-cases.md +0 -414
  213. package/tests/manual/test-cases.md +0 -417
  214. package/tests/marketplace.test.js +0 -420
  215. package/tests/migrations.test.js +0 -187
  216. package/tests/quality-gate.test.js +0 -679
  217. package/tests/quality-rules.test.js +0 -619
  218. package/tests/sync-external.test.js +0 -214
  219. package/tests/update-registry.test.js +0 -251
  220. package/tests/utils.test.js +0 -171
  221. package/tests/version-check.test.js +0 -75
  222. package/tests/web-search.test.js +0 -392
  223. package/thinkinglens-silent.md +0 -138
@@ -1,679 +0,0 @@
1
- /**
2
- * Quality Gate 模块单元测试
3
- */
4
-
5
- const fs = require('fs');
6
- const path = require('path');
7
- const os = require('os');
8
-
9
- describe('Quality Gate Module', () => {
10
- const {
11
- QualityGate,
12
- ConsoleReporter,
13
- JsonReporter,
14
- MarkdownReporter,
15
- checkOrThrow
16
- } = require('../lib/quality-gate');
17
-
18
- const { QualityGateError } = require('../lib/errors');
19
-
20
- // Temporary test directory
21
- const tempDir = path.join(os.tmpdir(), 'smc-gate-test-' + Date.now());
22
- const cleanJsFile = path.join(tempDir, 'clean.js');
23
- const dirtyJsFile = path.join(tempDir, 'dirty.js');
24
- const largeJsFile = path.join(tempDir, 'large.js');
25
- const emptyJsFile = path.join(tempDir, 'empty.js');
26
- const subDir = path.join(tempDir, 'sub');
27
- const subDirFile = path.join(subDir, 'file.js');
28
-
29
- beforeAll(() => {
30
- fs.mkdirSync(tempDir, { recursive: true });
31
- fs.mkdirSync(subDir, { recursive: true });
32
-
33
- // Create test files
34
- fs.writeFileSync(cleanJsFile, 'const x = 1;\nconst y = 2;\n');
35
- fs.writeFileSync(dirtyJsFile, 'const x = 1; \nconst y = 2;\t\n');
36
-
37
- // Create large file
38
- const lines = ['// Large file'];
39
- for (let i = 0; i < 850; i++) {
40
- lines.push(`const line${i} = ${i};`);
41
- }
42
- fs.writeFileSync(largeJsFile, lines.join('\n'));
43
-
44
- fs.writeFileSync(emptyJsFile, '\n');
45
- fs.writeFileSync(subDirFile, 'const x = 1;\n');
46
- });
47
-
48
- afterAll(() => {
49
- if (fs.existsSync(tempDir)) {
50
- fs.rmSync(tempDir, { recursive: true, force: true });
51
- }
52
- });
53
-
54
- describe('QualityGate', () => {
55
- describe('constructor', () => {
56
- it('should create gate with default options', () => {
57
- const gate = new QualityGate();
58
-
59
- expect(gate.projectDir).toBeDefined();
60
- expect(gate.config).toBeDefined();
61
- expect(gate.reporters).toBeDefined();
62
- expect(gate.reporters.length).toBeGreaterThan(0);
63
- });
64
-
65
- it('should accept custom project directory', () => {
66
- const gate = new QualityGate({ projectDir: tempDir });
67
-
68
- expect(gate.projectDir).toBe(tempDir);
69
- });
70
-
71
- it('should accept custom config', () => {
72
- const customConfig = {
73
- enabled: true,
74
- severity: 'error',
75
- rules: []
76
- };
77
-
78
- const gate = new QualityGate({ config: customConfig });
79
-
80
- expect(gate.config).toEqual(customConfig);
81
- });
82
-
83
- it('should accept custom reporters', () => {
84
- const customReporter = {
85
- report: jest.fn()
86
- };
87
-
88
- const gate = new QualityGate({ reporters: [customReporter] });
89
-
90
- expect(gate.reporters).toHaveLength(1);
91
- expect(gate.reporters[0]).toBe(customReporter);
92
- });
93
- });
94
-
95
- describe('check', () => {
96
- let gate;
97
-
98
- beforeEach(() => {
99
- gate = new QualityGate({
100
- projectDir: tempDir,
101
- reporters: [] // Suppress output during tests
102
- });
103
- });
104
-
105
- it('should return check result object', async () => {
106
- const result = await gate.check();
107
-
108
- expect(result).toBeDefined();
109
- expect(typeof result.passed).toBe('boolean');
110
- expect(Array.isArray(result.results)).toBe(true);
111
- expect(result.summary).toBeDefined();
112
- });
113
-
114
- it('should scan project files', async () => {
115
- const result = await gate.check();
116
-
117
- expect(result.summary.filesChecked).toBeGreaterThan(0);
118
- });
119
-
120
- it('should run quality rules', async () => {
121
- const result = await gate.check();
122
-
123
- expect(result.summary.rulesRun).toBeGreaterThan(0);
124
- });
125
-
126
- it('should detect issues in files', async () => {
127
- const result = await gate.check();
128
-
129
- // Large file should trigger line-count-limit
130
- const lineCountIssues = result.results.filter(r => r.rule === 'line-count-limit');
131
- expect(lineCountIssues.length).toBeGreaterThan(0);
132
- });
133
-
134
- it('should count issues by severity', async () => {
135
- const result = await gate.check();
136
-
137
- expect(result.summary).toMatchObject({
138
- total: expect.any(Number),
139
- critical: expect.any(Number),
140
- error: expect.any(Number),
141
- warn: expect.any(Number),
142
- info: expect.any(Number)
143
- });
144
- });
145
-
146
- it('should pass when severity is critical and no critical issues', async () => {
147
- const result = await gate.check({ severity: 'critical' });
148
-
149
- // Should pass if no critical issues
150
- expect(typeof result.passed).toBe('boolean');
151
- });
152
-
153
- it('should fail when blocking issues exist', async () => {
154
- // Use a file with known issues
155
- const gateWithIssues = new QualityGate({
156
- projectDir: tempDir,
157
- reporters: [],
158
- config: {
159
- rules: [
160
- { id: 'line-count-limit', enabled: true, severity: 'error' }
161
- ]
162
- }
163
- });
164
-
165
- const result = await gateWithIssues.check({ severity: 'error' });
166
-
167
- // Large file should cause error severity issue
168
- if (result.summary.error > 0) {
169
- expect(result.passed).toBe(false);
170
- }
171
- });
172
-
173
- it('should respect severity threshold', async () => {
174
- const resultWarn = await gate.check({ severity: 'warn' });
175
- const resultCritical = await gate.check({ severity: 'critical' });
176
-
177
- // Critical threshold is less strict than warn (fewer blocking issues)
178
- // So critical should pass more often than warn
179
- if (resultWarn.passed === false && resultCritical.passed === true) {
180
- // This is the expected case: warn fails but critical passes
181
- expect(true).toBe(true);
182
- } else {
183
- // If both pass or both fail, that's also acceptable
184
- expect(resultCritical.passed).toBe(resultWarn.passed);
185
- }
186
- });
187
-
188
- it('should check specific files when provided', async () => {
189
- const result = await gate.check({ files: [cleanJsFile] });
190
-
191
- expect(result.summary.filesChecked).toBe(1);
192
- });
193
-
194
- it('should run specific rules when provided', async () => {
195
- const result = await gate.check({
196
- rules: [{ id: 'no-empty-files', enabled: true }]
197
- });
198
-
199
- expect(result.summary.rulesRun).toBe(1);
200
- });
201
-
202
- it('should apply auto-fixes when requested', async () => {
203
- // Create a file with trailing whitespace for auto-fix test
204
- const fixableFile = path.join(tempDir, 'fixable.js');
205
- fs.writeFileSync(fixableFile, 'const x = 1; \nconst y = 2;\t\n');
206
-
207
- const result = await gate.check({
208
- files: [fixableFile],
209
- fix: true
210
- });
211
-
212
- // The file should now be fixed
213
- const content = fs.readFileSync(fixableFile, 'utf-8');
214
- const hasTrailing = content.match(/[ \t]$/gm);
215
- expect(hasTrailing).toBeNull();
216
- });
217
-
218
- it('should track fixed issues in summary', async () => {
219
- const fixableFile = path.join(tempDir, 'fixable2.js');
220
- fs.writeFileSync(fixableFile, 'const x = 1; \n');
221
-
222
- const result = await gate.check({
223
- files: [fixableFile],
224
- fix: true
225
- });
226
-
227
- expect(result.summary).toHaveProperty('fixed');
228
- });
229
- });
230
-
231
- describe('_getProjectFiles', () => {
232
- it('should ignore common directories', async () => {
233
- // Create node_modules directory
234
- const nodeModulesDir = path.join(tempDir, 'node_modules');
235
- fs.mkdirSync(nodeModulesDir, { recursive: true });
236
- fs.writeFileSync(path.join(nodeModulesDir, 'index.js'), 'ignored');
237
-
238
- const gate = new QualityGate({ projectDir: tempDir, reporters: [] });
239
- const result = await gate.check();
240
-
241
- // Should not include node_modules files
242
- const nodeModulesFiles = result.results.filter(r =>
243
- r.file.includes('node_modules')
244
- );
245
- expect(nodeModulesFiles).toHaveLength(0);
246
-
247
- fs.rmSync(nodeModulesDir, { recursive: true, force: true });
248
- });
249
-
250
- it('should check supported file extensions', async () => {
251
- const gate = new QualityGate({ projectDir: tempDir, reporters: [] });
252
- const result = await gate.check();
253
-
254
- // Should have checked .js files
255
- const jsFiles = result.results.filter(r => r.file.endsWith('.js'));
256
- expect(jsFiles.length).toBeGreaterThan(0);
257
- });
258
- });
259
-
260
- describe('_getActiveRules', () => {
261
- it('should load rules from config file', () => {
262
- const configDir = path.join(tempDir, '.claude');
263
- fs.mkdirSync(configDir, { recursive: true });
264
-
265
- fs.writeFileSync(
266
- path.join(configDir, 'quality-gate.json'),
267
- JSON.stringify({
268
- enabled: true,
269
- severity: 'warn',
270
- rules: [
271
- { id: 'line-count-limit', enabled: true, severity: 'error' }
272
- ]
273
- })
274
- );
275
-
276
- const gate = new QualityGate({ projectDir: tempDir, reporters: [] });
277
- const rules = gate._getActiveRules();
278
-
279
- expect(rules.length).toBeGreaterThan(0);
280
-
281
- fs.rmSync(configDir, { recursive: true, force: true });
282
- });
283
-
284
- it('should use default config when file doesn\'t exist', () => {
285
- const gate = new QualityGate({ projectDir: tempDir, reporters: [] });
286
- const rules = gate._getActiveRules();
287
-
288
- expect(rules.length).toBeGreaterThan(0);
289
- });
290
- });
291
- });
292
-
293
- describe('ConsoleReporter', () => {
294
- let reporter;
295
- let consoleSpy;
296
-
297
- beforeEach(() => {
298
- reporter = new ConsoleReporter();
299
- consoleSpy = jest.spyOn(console, 'log').mockImplementation();
300
- });
301
-
302
- afterEach(() => {
303
- consoleSpy.mockRestore();
304
- });
305
-
306
- it('should report pass status', () => {
307
- const result = {
308
- passed: true,
309
- summary: {
310
- filesChecked: 10,
311
- total: 0,
312
- critical: 0,
313
- error: 0,
314
- warn: 0
315
- },
316
- results: []
317
- };
318
-
319
- reporter.report(result);
320
-
321
- expect(consoleSpy).toHaveBeenCalledWith(
322
- expect.stringContaining('PASS')
323
- );
324
- });
325
-
326
- it('should report fail status', () => {
327
- const result = {
328
- passed: false,
329
- summary: {
330
- filesChecked: 10,
331
- total: 5,
332
- critical: 1,
333
- error: 2,
334
- warn: 2
335
- },
336
- results: [
337
- {
338
- file: '/path/to/file.js',
339
- ruleName: 'Test Rule',
340
- severity: 'error',
341
- message: 'Test error',
342
- fix: 'Fix it'
343
- }
344
- ]
345
- };
346
-
347
- reporter.report(result);
348
-
349
- expect(consoleSpy).toHaveBeenCalledWith(
350
- expect.stringContaining('FAIL')
351
- );
352
- expect(consoleSpy).toHaveBeenCalledWith(
353
- expect.stringContaining('Test Rule')
354
- );
355
- });
356
-
357
- it('should display file paths', () => {
358
- const result = {
359
- passed: false,
360
- summary: {
361
- filesChecked: 1,
362
- total: 1,
363
- critical: 0,
364
- error: 1,
365
- warn: 0
366
- },
367
- results: [
368
- {
369
- file: path.join(tempDir, 'test.js'),
370
- ruleName: 'Line Count',
371
- severity: 'error',
372
- message: 'Too many lines',
373
- pass: false
374
- }
375
- ]
376
- };
377
-
378
- reporter.report(result);
379
-
380
- expect(consoleSpy).toHaveBeenCalledWith(
381
- expect.stringContaining('test.js')
382
- );
383
- });
384
-
385
- it('should show fix suggestions', () => {
386
- const result = {
387
- passed: false,
388
- summary: {
389
- filesChecked: 1,
390
- total: 1,
391
- critical: 0,
392
- error: 1,
393
- warn: 0
394
- },
395
- results: [
396
- {
397
- file: '/path/to/file.js',
398
- ruleName: 'Test Rule',
399
- severity: 'warn',
400
- message: 'Test warning',
401
- pass: false,
402
- fix: 'Apply this fix'
403
- }
404
- ]
405
- };
406
-
407
- reporter.report(result);
408
-
409
- expect(consoleSpy).toHaveBeenCalledWith(
410
- expect.stringContaining('Fix:')
411
- );
412
- expect(consoleSpy).toHaveBeenCalledWith(
413
- expect.stringContaining('Apply this fix')
414
- );
415
- });
416
- });
417
-
418
- describe('JsonReporter', () => {
419
- let reporter;
420
- let consoleSpy;
421
-
422
- beforeEach(() => {
423
- reporter = new JsonReporter();
424
- consoleSpy = jest.spyOn(console, 'log').mockImplementation();
425
- });
426
-
427
- afterEach(() => {
428
- consoleSpy.mockRestore();
429
- });
430
-
431
- it('should output JSON', () => {
432
- const result = {
433
- passed: true,
434
- summary: { filesChecked: 5, total: 0 },
435
- results: []
436
- };
437
-
438
- reporter.report(result);
439
-
440
- const output = consoleSpy.mock.calls[0][0];
441
-
442
- expect(() => JSON.parse(output)).not.toThrow();
443
- });
444
-
445
- it('should include all result data', () => {
446
- const result = {
447
- passed: false,
448
- summary: {
449
- filesChecked: 1,
450
- total: 1,
451
- critical: 0,
452
- error: 1,
453
- warn: 0
454
- },
455
- results: [
456
- {
457
- file: '/test.js',
458
- rule: 'test-rule',
459
- ruleName: 'Test Rule',
460
- severity: 'error',
461
- message: 'Test error',
462
- pass: false
463
- }
464
- ]
465
- };
466
-
467
- reporter.report(result);
468
-
469
- const output = JSON.parse(consoleSpy.mock.calls[0][0]);
470
-
471
- expect(output.passed).toBe(false);
472
- expect(output.summary).toBeDefined();
473
- expect(output.results).toHaveLength(1);
474
- });
475
- });
476
-
477
- describe('MarkdownReporter', () => {
478
- let reporter;
479
- let consoleSpy;
480
-
481
- beforeEach(() => {
482
- reporter = new MarkdownReporter();
483
- consoleSpy = jest.spyOn(console, 'log').mockImplementation();
484
- });
485
-
486
- afterEach(() => {
487
- consoleSpy.mockRestore();
488
- });
489
-
490
- it('should output markdown format', () => {
491
- const result = {
492
- passed: true,
493
- summary: {
494
- filesChecked: 5,
495
- total: 0,
496
- critical: 0,
497
- error: 0,
498
- warn: 0
499
- },
500
- results: []
501
- };
502
-
503
- reporter.report(result);
504
-
505
- expect(consoleSpy).toHaveBeenCalledWith(
506
- expect.stringContaining('# Quality Gate Report')
507
- );
508
- });
509
-
510
- it('should show pass/fail status with emoji', () => {
511
- const passResult = {
512
- passed: true,
513
- summary: { filesChecked: 1, total: 0, critical: 0, error: 0, warn: 0 },
514
- results: []
515
- };
516
-
517
- reporter.report(passResult);
518
-
519
- expect(consoleSpy).toHaveBeenCalledWith(
520
- expect.stringContaining(':white_check_mark:')
521
- );
522
-
523
- jest.clearAllMocks();
524
-
525
- const failResult = {
526
- passed: false,
527
- summary: { filesChecked: 1, total: 1, critical: 0, error: 1, warn: 0 },
528
- results: [
529
- {
530
- file: '/test.js',
531
- ruleName: 'Test Rule',
532
- severity: 'error',
533
- message: 'Test error',
534
- pass: false
535
- }
536
- ]
537
- };
538
-
539
- reporter.report(failResult);
540
-
541
- expect(consoleSpy).toHaveBeenCalledWith(
542
- expect.stringContaining(':x:')
543
- );
544
- });
545
-
546
- it('should include summary section', () => {
547
- const result = {
548
- passed: true,
549
- summary: {
550
- filesChecked: 10,
551
- total: 5,
552
- critical: 0,
553
- error: 2,
554
- warn: 3
555
- },
556
- results: []
557
- };
558
-
559
- reporter.report(result);
560
-
561
- expect(consoleSpy).toHaveBeenCalledWith(
562
- expect.stringContaining('## Summary')
563
- );
564
- });
565
- });
566
-
567
- describe('checkOrThrow', () => {
568
- it('should return result when check passes', async () => {
569
- const result = await checkOrThrow({
570
- projectDir: tempDir,
571
- severity: 'critical'
572
- });
573
-
574
- expect(result).toBeDefined();
575
- expect(typeof result.passed).toBe('boolean');
576
- });
577
-
578
- it('should throw QualityGateError when check fails', async () => {
579
- // Create a gate that will definitely fail
580
- const failDir = path.join(os.tmpdir(), 'smc-fail-test-' + Date.now());
581
- fs.mkdirSync(failDir, { recursive: true });
582
-
583
- // Create a large file that will fail line-count-limit
584
- const largeFile = path.join(failDir, 'large.js');
585
- const lines = [];
586
- for (let i = 0; i < 900; i++) {
587
- lines.push(`const x = ${i};`);
588
- }
589
- fs.writeFileSync(largeFile, lines.join('\n'));
590
-
591
- await expect(async () => {
592
- await checkOrThrow({
593
- projectDir: failDir,
594
- severity: 'error'
595
- });
596
- }).rejects.toThrow(QualityGateError);
597
-
598
- fs.rmSync(failDir, { recursive: true, force: true });
599
- });
600
-
601
- it('should include result in thrown error', async () => {
602
- const failDir = path.join(os.tmpdir(), 'smc-fail-test2-' + Date.now());
603
- fs.mkdirSync(failDir, { recursive: true });
604
-
605
- const largeFile = path.join(failDir, 'large.js');
606
- const lines = [];
607
- for (let i = 0; i < 900; i++) {
608
- lines.push(`const x = ${i};`);
609
- }
610
- fs.writeFileSync(largeFile, lines.join('\n'));
611
-
612
- try {
613
- await checkOrThrow({
614
- projectDir: failDir,
615
- severity: 'error'
616
- });
617
- fail('Should have thrown');
618
- } catch (e) {
619
- expect(e instanceof QualityGateError).toBe(true);
620
- expect(e.results).toBeDefined();
621
- } finally {
622
- fs.rmSync(failDir, { recursive: true, force: true });
623
- }
624
- });
625
- });
626
-
627
- describe('Integration Tests', () => {
628
- it('should handle complete check workflow', async () => {
629
- const gate = new QualityGate({
630
- projectDir: tempDir,
631
- reporters: []
632
- });
633
-
634
- const result = await gate.check();
635
-
636
- expect(result).toMatchObject({
637
- passed: expect.any(Boolean),
638
- results: expect.any(Array),
639
- summary: expect.objectContaining({
640
- filesChecked: expect.any(Number),
641
- rulesRun: expect.any(Number),
642
- total: expect.any(Number)
643
- })
644
- });
645
- });
646
-
647
- it('should handle files with various issues', async () => {
648
- const gate = new QualityGate({
649
- projectDir: tempDir,
650
- reporters: []
651
- });
652
-
653
- const result = await gate.check();
654
-
655
- // Should find issues in dirty file
656
- const dirtyFileIssues = result.results.filter(r =>
657
- r.file === dirtyJsFile || r.file.endsWith(path.basename(dirtyJsFile))
658
- );
659
-
660
- expect(dirtyFileIssues.length).toBeGreaterThan(0);
661
- });
662
-
663
- it('should handle empty project directory', async () => {
664
- const emptyDir = path.join(os.tmpdir(), 'smc-empty-' + Date.now());
665
- fs.mkdirSync(emptyDir, { recursive: true });
666
-
667
- const gate = new QualityGate({
668
- projectDir: emptyDir,
669
- reporters: []
670
- });
671
-
672
- const result = await gate.check();
673
-
674
- expect(result.summary.filesChecked).toBe(0);
675
-
676
- fs.rmSync(emptyDir, { recursive: true, force: true });
677
- });
678
- });
679
- });