sumulige-claude 1.5.1 → 1.5.2
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/hooks/hook-registry.json +0 -15
- package/.claude/rules/coding-style.md +18 -7
- package/.claude/rules/hooks.md +15 -4
- package/.claude/rules/performance.md +15 -5
- package/.claude/rules/security.md +140 -4
- package/.claude/rules/testing.md +138 -9
- package/.claude/rules/web-design-standard.md +16 -5
- package/.claude/skills/algorithmic-art/metadata.yaml +28 -0
- package/.claude/skills/api-tester/SKILL.md +61 -0
- package/.claude/skills/api-tester/examples/basic.md +3 -0
- package/.claude/skills/api-tester/metadata.yaml +30 -0
- package/.claude/skills/api-tester/templates/default.md +3 -0
- package/.claude/skills/brand-guidelines/metadata.yaml +26 -0
- package/.claude/skills/canvas-design/metadata.yaml +27 -0
- package/.claude/skills/code-reviewer-123/SKILL.md +61 -0
- package/.claude/skills/code-reviewer-123/examples/basic.md +3 -0
- package/.claude/skills/code-reviewer-123/metadata.yaml +30 -0
- package/.claude/skills/code-reviewer-123/templates/default.md +3 -0
- package/.claude/skills/doc-coauthoring/metadata.yaml +27 -0
- package/.claude/skills/docx/metadata.yaml +30 -0
- package/.claude/skills/frontend-design/metadata.yaml +28 -0
- package/.claude/skills/internal-comms/metadata.yaml +28 -0
- package/.claude/skills/mcp-builder/metadata.yaml +26 -0
- package/.claude/skills/my-skill/SKILL.md +61 -0
- package/.claude/skills/my-skill/examples/basic.md +3 -0
- package/.claude/skills/my-skill/metadata.yaml +30 -0
- package/.claude/skills/my-skill/templates/default.md +3 -0
- package/.claude/skills/pdf/metadata.yaml +29 -0
- package/.claude/skills/pptx/metadata.yaml +29 -0
- package/.claude/skills/react-best-practices/metadata.yaml +26 -0
- package/.claude/skills/react-node-practices/SKILL.md +409 -0
- package/.claude/skills/react-node-practices/metadata.yaml +56 -0
- package/.claude/skills/skill-creator/metadata.yaml +25 -0
- package/.claude/skills/slack-gif-creator/metadata.yaml +28 -0
- package/.claude/skills/test-skill-name/SKILL.md +61 -0
- package/.claude/skills/test-skill-name/examples/basic.md +3 -0
- package/.claude/skills/test-skill-name/metadata.yaml +30 -0
- package/.claude/skills/test-skill-name/templates/default.md +3 -0
- package/.claude/skills/test-workflow/metadata.yaml +32 -0
- package/.claude/skills/theme-factory/metadata.yaml +26 -0
- package/.claude/skills/threejs-fundamentals/metadata.yaml +27 -0
- package/.claude/skills/web-artifacts-builder/metadata.yaml +30 -0
- package/.claude/skills/web-design-guidelines/metadata.yaml +26 -0
- package/.claude/skills/webapp-testing/metadata.yaml +26 -0
- package/.claude/skills/xlsx/metadata.yaml +29 -0
- package/LICENSE +21 -0
- package/cli.js +1 -1
- package/package.json +25 -3
- package/.claude/.kickoff-hint.txt +0 -52
- package/.claude/.sumulige-claude-version +0 -1
- package/.claude/.version +0 -1
- package/.claude/AGENTS.md +0 -42
- package/.claude/ANCHORS.md +0 -40
- package/.claude/CLAUDE.md +0 -138
- package/.claude/MEMORY.md +0 -69
- package/.claude/PROJECT_LOG.md +0 -101
- package/.claude/THINKING_CHAIN_GUIDE.md +0 -287
- package/.claude/USAGE.md +0 -175
- package/.claude/boris-optimizations.md +0 -167
- package/.claude/handoffs/INDEX.md +0 -21
- package/.claude/handoffs/LATEST.md +0 -76
- package/.claude/handoffs/handoff_2026-01-22T13-07-04-757Z.md +0 -76
- package/.claude/quality-gate.json +0 -82
- package/.claude/rag/skill-index.json +0 -135
- package/.claude/settings.json +0 -99
- package/.claude/settings.local.json +0 -175
- package/.claude/templates/PROJECT_KICKOFF.md +0 -89
- package/.claude/templates/PROJECT_PROPOSAL.md +0 -227
- package/.claude/templates/TASK_PLAN.md +0 -121
- package/.claude/templates/hooks/README.md +0 -302
- package/.claude/templates/hooks/hook.sh.template +0 -94
- package/.claude/templates/hooks/user-prompt-submit.cjs.template +0 -116
- package/.claude/templates/hooks/user-response-submit.cjs.template +0 -94
- package/.claude/templates/hooks/validate.js +0 -173
- package/.claude/templates/tasks/develop.md +0 -69
- package/.claude/templates/tasks/research.md +0 -64
- package/.claude/templates/tasks/test.md +0 -96
- package/.claude/thinking-routes/.last-sync +0 -1
- package/.claude/thinking-routes/QUICKREF.md +0 -98
- package/.claude/workflow/document-scanner.js +0 -426
- package/.claude/workflow/knowledge-engine.js +0 -941
- package/.claude/workflow/notebooklm/browser.js +0 -1028
- package/.claude/workflow/phases/phase1-research.js +0 -578
- package/.claude/workflow/phases/phase1-research.ts +0 -465
- package/.claude/workflow/phases/phase2-approve.js +0 -722
- package/.claude/workflow/phases/phase3-plan.js +0 -1200
- package/.claude/workflow/phases/phase4-develop.js +0 -894
- package/.claude/workflow/search-cache.js +0 -230
- package/.claude/workflow/templates/approval.md +0 -315
- package/.claude/workflow/templates/development.md +0 -377
- package/.claude/workflow/templates/planning.md +0 -328
- package/.claude/workflow/templates/research.md +0 -250
- package/.claude/workflow/types.js +0 -37
- package/.claude/workflow/web-search.js +0 -278
- package/.claude-plugin/marketplace.json +0 -71
- package/.github/workflows/sync-skills.yml +0 -74
- package/.versionrc +0 -25
- package/AGENTS.md +0 -580
- package/CHANGELOG.md +0 -481
- package/CLAUDE-template.md +0 -114
- package/DEV_TOOLS_GUIDE.md +0 -190
- package/PROJECT_STRUCTURE.md +0 -266
- package/Q&A.md +0 -325
- package/config/defaults.json +0 -34
- package/config/official-skills.json +0 -183
- package/config/quality-gate.json +0 -67
- package/config/skill-categories.json +0 -40
- package/config/version-manifest.json +0 -85
- package/demos/power-3d-scatter.html +0 -683
- package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +0 -36
- package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +0 -36
- package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +0 -36
- package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +0 -36
- package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +0 -36
- package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +0 -36
- package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +0 -36
- package/development/knowledge-base/.index.clean.json +0 -1
- package/development/knowledge-base/.index.json +0 -486
- package/development/knowledge-base/test-best-practices.md +0 -29
- package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +0 -226
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +0 -345
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +0 -284
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +0 -14
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +0 -35
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +0 -34
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +0 -5
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +0 -60
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +0 -25
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +0 -70
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +0 -48
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +0 -20
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +0 -21
- package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +0 -226
- package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +0 -345
- package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +0 -284
- package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +0 -14
- package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +0 -160
- package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +0 -178
- package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +0 -377
- package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +0 -442
- package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +0 -800
- package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +0 -625
- package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +0 -830
- package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +0 -957
- package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +0 -381
- package/development/todos/.state.json +0 -19
- package/development/todos/INDEX.md +0 -63
- package/development/todos/active/_README.md +0 -49
- package/development/todos/archived/_README.md +0 -11
- package/development/todos/backlog/_README.md +0 -11
- package/development/todos/backlog/mcp-integration.md +0 -35
- package/development/todos/completed/_README.md +0 -11
- package/development/todos/completed/boris-optimizations.md +0 -39
- package/development/todos/completed/develop/local-knowledge-index.md +0 -85
- package/development/todos/completed/develop/todo-system.md +0 -47
- package/development/todos/completed/develop/web-search-integration.md +0 -83
- package/development/todos/completed/test/phase1-e2e-test.md +0 -103
- package/docs/DEVELOPMENT.md +0 -461
- package/docs/MARKETPLACE.md +0 -352
- package/docs/RELEASE.md +0 -93
- package/jest.config.js +0 -63
- package/lib/commands.js +0 -3588
- package/lib/config-manager.js +0 -441
- package/lib/config-schema.js +0 -408
- package/lib/config-validator.js +0 -330
- package/lib/config.js +0 -122
- package/lib/errors.js +0 -305
- package/lib/incremental-sync.js +0 -274
- package/lib/marketplace.js +0 -487
- package/lib/migrations.js +0 -154
- package/lib/permission-audit.js +0 -255
- package/lib/quality-gate.js +0 -431
- package/lib/quality-rules.js +0 -373
- package/lib/utils.js +0 -150
- package/lib/version-check.js +0 -169
- package/lib/version-manifest.js +0 -171
- package/project-paradigm.md +0 -313
- package/prompts/how-to-find.md +0 -163
- package/prompts/linus-architect.md +0 -71
- package/prompts/software-architect.md +0 -173
- package/prompts/web-designer.md +0 -249
- package/scripts/fix-hooks.mjs +0 -97
- package/scripts/sync-external.mjs +0 -298
- package/scripts/sync-to-home.sh +0 -108
- package/scripts/update-registry.mjs +0 -325
- package/sources.yaml +0 -83
- package/tests/README.md +0 -263
- package/tests/commands.test.js +0 -1086
- package/tests/config-manager.test.js +0 -677
- package/tests/config-schema.test.js +0 -425
- package/tests/config-validator.test.js +0 -436
- package/tests/config.test.js +0 -100
- package/tests/errors.test.js +0 -477
- package/tests/manual/phase1-e2e.sh +0 -389
- package/tests/manual/phase2-test-cases.md +0 -311
- package/tests/manual/phase3-test-cases.md +0 -309
- package/tests/manual/phase4-test-cases.md +0 -414
- package/tests/manual/test-cases.md +0 -417
- package/tests/marketplace.test.js +0 -420
- package/tests/migrations.test.js +0 -187
- package/tests/quality-gate.test.js +0 -679
- package/tests/quality-rules.test.js +0 -619
- package/tests/sync-external.test.js +0 -214
- package/tests/update-registry.test.js +0 -251
- package/tests/utils.test.js +0 -171
- package/tests/version-check.test.js +0 -75
- package/tests/web-search.test.js +0 -392
- package/thinkinglens-silent.md +0 -138
|
@@ -1,619 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Quality Rules 模块单元测试
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
const os = require('os');
|
|
8
|
-
|
|
9
|
-
describe('Quality Rules Module', () => {
|
|
10
|
-
const { RuleRegistry, registry } = require('../lib/quality-rules');
|
|
11
|
-
|
|
12
|
-
// Create temporary test files
|
|
13
|
-
const tempDir = path.join(os.tmpdir(), 'smc-rules-test-' + Date.now());
|
|
14
|
-
const testJsFile = path.join(tempDir, 'test.js');
|
|
15
|
-
const testPyFile = path.join(tempDir, 'test.py');
|
|
16
|
-
const testMdFile = path.join(tempDir, 'test.md');
|
|
17
|
-
const testJsonFile = path.join(tempDir, 'test.json');
|
|
18
|
-
const largeJsFile = path.join(tempDir, 'large.js');
|
|
19
|
-
const emptyJsFile = path.join(tempDir, 'empty.js');
|
|
20
|
-
const rulesJsonFile = path.join(tempDir, 'rules.json');
|
|
21
|
-
const rulesYamlFile = path.join(tempDir, 'rules.yaml');
|
|
22
|
-
|
|
23
|
-
beforeAll(() => {
|
|
24
|
-
fs.mkdirSync(tempDir, { recursive: true });
|
|
25
|
-
|
|
26
|
-
// Create test files with various content
|
|
27
|
-
fs.writeFileSync(testJsFile, 'console.log("hello");\nconsole.log("world");\nconst x = 1;\n');
|
|
28
|
-
fs.writeFileSync(testPyFile, '# Python file\nprint("hello")');
|
|
29
|
-
fs.writeFileSync(testMdFile, '# Title\n\nContent here\n');
|
|
30
|
-
fs.writeFileSync(testJsonFile, '{"key": "value"}');
|
|
31
|
-
|
|
32
|
-
// Create a large JavaScript file (over 800 lines)
|
|
33
|
-
const lines = ['// Large file'];
|
|
34
|
-
for (let i = 0; i < 850; i++) {
|
|
35
|
-
lines.push(`const line${i} = ${i};`);
|
|
36
|
-
}
|
|
37
|
-
fs.writeFileSync(largeJsFile, lines.join('\n'));
|
|
38
|
-
|
|
39
|
-
// Create an empty file
|
|
40
|
-
fs.writeFileSync(emptyJsFile, '\n\n');
|
|
41
|
-
|
|
42
|
-
// Create custom rules JSON file
|
|
43
|
-
fs.writeFileSync(rulesJsonFile, JSON.stringify({
|
|
44
|
-
rules: [
|
|
45
|
-
{
|
|
46
|
-
id: 'custom-rule-1',
|
|
47
|
-
name: 'Custom Rule 1',
|
|
48
|
-
severity: 'error',
|
|
49
|
-
enabled: true,
|
|
50
|
-
description: 'A custom rule',
|
|
51
|
-
check: () => ({ pass: true, skip: true })
|
|
52
|
-
}
|
|
53
|
-
]
|
|
54
|
-
}));
|
|
55
|
-
|
|
56
|
-
// Create custom rules YAML file
|
|
57
|
-
fs.writeFileSync(rulesYamlFile, `
|
|
58
|
-
rules:
|
|
59
|
-
- id: custom-yaml-rule
|
|
60
|
-
name: Custom YAML Rule
|
|
61
|
-
severity: warn
|
|
62
|
-
enabled: true
|
|
63
|
-
description: A custom YAML rule
|
|
64
|
-
`);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
afterAll(() => {
|
|
68
|
-
if (fs.existsSync(tempDir)) {
|
|
69
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
describe('RuleRegistry', () => {
|
|
74
|
-
let customRegistry;
|
|
75
|
-
|
|
76
|
-
beforeEach(() => {
|
|
77
|
-
customRegistry = new RuleRegistry();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
describe('register', () => {
|
|
81
|
-
it('should register a new rule', () => {
|
|
82
|
-
const rule = customRegistry.register('test-rule', {
|
|
83
|
-
name: 'Test Rule',
|
|
84
|
-
severity: 'warn',
|
|
85
|
-
check: () => ({ pass: true })
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
expect(rule.id).toBe('test-rule');
|
|
89
|
-
expect(rule.name).toBe('Test Rule');
|
|
90
|
-
expect(rule.severity).toBe('warn');
|
|
91
|
-
expect(rule.enabled).toBe(true);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('should use id as default name', () => {
|
|
95
|
-
const rule = customRegistry.register('no-name', {
|
|
96
|
-
check: () => ({ pass: true })
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
expect(rule.name).toBe('no-name');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should use default severity', () => {
|
|
103
|
-
const rule = customRegistry.register('default-severity', {
|
|
104
|
-
check: () => ({ pass: true })
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
expect(rule.severity).toBe('warn');
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should enable rule by default', () => {
|
|
111
|
-
const rule = customRegistry.register('enabled-default', {
|
|
112
|
-
enabled: undefined,
|
|
113
|
-
check: () => ({ pass: true })
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
expect(rule.enabled).toBe(true);
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should allow explicit disable', () => {
|
|
120
|
-
const rule = customRegistry.register('disabled-rule', {
|
|
121
|
-
enabled: false,
|
|
122
|
-
check: () => ({ pass: true })
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
expect(rule.enabled).toBe(false);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('should store config object', () => {
|
|
129
|
-
const rule = customRegistry.register('config-rule', {
|
|
130
|
-
config: { maxLines: 100 },
|
|
131
|
-
check: () => ({ pass: true })
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
expect(rule.config).toEqual({ maxLines: 100 });
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it('should return registered rule', () => {
|
|
138
|
-
const registered = customRegistry.register('return-test', {
|
|
139
|
-
name: 'Return Test',
|
|
140
|
-
check: () => ({ pass: true })
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
expect(registered).toBeDefined();
|
|
144
|
-
expect(registered.id).toBe('return-test');
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
describe('get', () => {
|
|
149
|
-
beforeEach(() => {
|
|
150
|
-
customRegistry.register('get-test', {
|
|
151
|
-
name: 'Get Test',
|
|
152
|
-
check: () => ({ pass: true })
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it('should get existing rule', () => {
|
|
157
|
-
const rule = customRegistry.get('get-test');
|
|
158
|
-
|
|
159
|
-
expect(rule).toBeDefined();
|
|
160
|
-
expect(rule.id).toBe('get-test');
|
|
161
|
-
expect(rule.name).toBe('Get Test');
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('should return null for non-existent rule', () => {
|
|
165
|
-
const rule = customRegistry.get('does-not-exist');
|
|
166
|
-
|
|
167
|
-
expect(rule).toBeNull();
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
describe('has', () => {
|
|
172
|
-
beforeEach(() => {
|
|
173
|
-
customRegistry.register('has-test', {
|
|
174
|
-
check: () => ({ pass: true })
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('should return true for existing rule', () => {
|
|
179
|
-
expect(customRegistry.has('has-test')).toBe(true);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('should return false for non-existent rule', () => {
|
|
183
|
-
expect(customRegistry.has('does-not-exist')).toBe(false);
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
describe('getAll', () => {
|
|
188
|
-
beforeEach(() => {
|
|
189
|
-
customRegistry.register('rule-1', {
|
|
190
|
-
severity: 'error',
|
|
191
|
-
enabled: true,
|
|
192
|
-
category: 'code-style',
|
|
193
|
-
check: () => ({ pass: true })
|
|
194
|
-
});
|
|
195
|
-
customRegistry.register('rule-2', {
|
|
196
|
-
severity: 'warn',
|
|
197
|
-
enabled: false,
|
|
198
|
-
category: 'code-style',
|
|
199
|
-
check: () => ({ pass: true })
|
|
200
|
-
});
|
|
201
|
-
customRegistry.register('rule-3', {
|
|
202
|
-
severity: 'warn',
|
|
203
|
-
enabled: true,
|
|
204
|
-
category: 'quality',
|
|
205
|
-
check: () => ({ pass: true })
|
|
206
|
-
});
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('should return all rules when no filter provided', () => {
|
|
210
|
-
const rules = customRegistry.getAll();
|
|
211
|
-
|
|
212
|
-
expect(rules.length).toBeGreaterThan(0);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('should filter by severity', () => {
|
|
216
|
-
const errorRules = customRegistry.getAll({ severity: 'error' });
|
|
217
|
-
|
|
218
|
-
expect(errorRules.length).toBeGreaterThan(0);
|
|
219
|
-
expect(errorRules.every(r => r.severity === 'error')).toBe(true);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('should filter by enabled status', () => {
|
|
223
|
-
const enabledRules = customRegistry.getAll({ enabled: true });
|
|
224
|
-
const disabledRules = customRegistry.getAll({ enabled: false });
|
|
225
|
-
|
|
226
|
-
expect(enabledRules.every(r => r.enabled === true)).toBe(true);
|
|
227
|
-
expect(disabledRules.every(r => r.enabled === false)).toBe(true);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('should filter by category', () => {
|
|
231
|
-
const styleRules = customRegistry.getAll({ category: 'code-style' });
|
|
232
|
-
|
|
233
|
-
expect(styleRules.every(r => r.category === 'code-style')).toBe(true);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('should combine multiple filters', () => {
|
|
237
|
-
const rules = customRegistry.getAll({
|
|
238
|
-
enabled: true,
|
|
239
|
-
severity: 'warn'
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
expect(rules.every(r => r.enabled === true && r.severity === 'warn')).toBe(true);
|
|
243
|
-
});
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
describe('setEnabled', () => {
|
|
247
|
-
it('should enable a disabled rule', () => {
|
|
248
|
-
customRegistry.register('to-enable', {
|
|
249
|
-
enabled: false,
|
|
250
|
-
check: () => ({ pass: true })
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
customRegistry.setEnabled('to-enable', true);
|
|
254
|
-
const rule = customRegistry.get('to-enable');
|
|
255
|
-
|
|
256
|
-
expect(rule.enabled).toBe(true);
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
it('should disable an enabled rule', () => {
|
|
260
|
-
customRegistry.register('to-disable', {
|
|
261
|
-
enabled: true,
|
|
262
|
-
check: () => ({ pass: true })
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
customRegistry.setEnabled('to-disable', false);
|
|
266
|
-
const rule = customRegistry.get('to-disable');
|
|
267
|
-
|
|
268
|
-
expect(rule.enabled).toBe(false);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it('should do nothing for non-existent rule', () => {
|
|
272
|
-
expect(() => {
|
|
273
|
-
customRegistry.setEnabled('non-existent', true);
|
|
274
|
-
}).not.toThrow();
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
describe('updateConfig', () => {
|
|
279
|
-
it('should update rule config', () => {
|
|
280
|
-
customRegistry.register('config-update', {
|
|
281
|
-
config: { maxLines: 100 },
|
|
282
|
-
check: () => ({ pass: true })
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
customRegistry.updateConfig('config-update', { maxLines: 200, newSize: 500 });
|
|
286
|
-
const rule = customRegistry.get('config-update');
|
|
287
|
-
|
|
288
|
-
expect(rule.config.maxLines).toBe(200);
|
|
289
|
-
expect(rule.config.newSize).toBe(500);
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
it('should merge configs', () => {
|
|
293
|
-
customRegistry.register('merge-config', {
|
|
294
|
-
config: { a: 1, b: 2 },
|
|
295
|
-
check: () => ({ pass: true })
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
customRegistry.updateConfig('merge-config', { b: 3, c: 4 });
|
|
299
|
-
const rule = customRegistry.get('merge-config');
|
|
300
|
-
|
|
301
|
-
expect(rule.config.a).toBe(1);
|
|
302
|
-
expect(rule.config.b).toBe(3);
|
|
303
|
-
expect(rule.config.c).toBe(4);
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
it('should do nothing for non-existent rule', () => {
|
|
307
|
-
expect(() => {
|
|
308
|
-
customRegistry.updateConfig('non-existent', {});
|
|
309
|
-
}).not.toThrow();
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
describe('loadFromFile', () => {
|
|
314
|
-
it('should load rules from JSON file', () => {
|
|
315
|
-
customRegistry.loadFromFile(rulesJsonFile);
|
|
316
|
-
|
|
317
|
-
expect(customRegistry.has('custom-rule-1')).toBe(true);
|
|
318
|
-
const rule = customRegistry.get('custom-rule-1');
|
|
319
|
-
expect(rule.name).toBe('Custom Rule 1');
|
|
320
|
-
expect(rule.severity).toBe('error');
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it('should update existing rule from file', () => {
|
|
324
|
-
customRegistry.register('custom-rule-1', {
|
|
325
|
-
name: 'Original Name',
|
|
326
|
-
severity: 'warn',
|
|
327
|
-
check: () => ({ pass: true })
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
customRegistry.loadFromFile(rulesJsonFile);
|
|
331
|
-
|
|
332
|
-
const rule = customRegistry.get('custom-rule-1');
|
|
333
|
-
expect(rule.name).toBe('Custom Rule 1');
|
|
334
|
-
expect(rule.severity).toBe('error');
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
it('should do nothing for non-existent file', () => {
|
|
338
|
-
expect(() => {
|
|
339
|
-
customRegistry.loadFromFile('/does/not/exist.json');
|
|
340
|
-
}).not.toThrow();
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
it('should load rules from YAML file (if yaml available)', () => {
|
|
344
|
-
const hasYaml = (() => {
|
|
345
|
-
try {
|
|
346
|
-
require('yaml');
|
|
347
|
-
return true;
|
|
348
|
-
} catch {
|
|
349
|
-
return false;
|
|
350
|
-
}
|
|
351
|
-
})();
|
|
352
|
-
|
|
353
|
-
customRegistry.loadFromFile(rulesYamlFile);
|
|
354
|
-
|
|
355
|
-
if (hasYaml) {
|
|
356
|
-
expect(customRegistry.has('custom-yaml-rule')).toBe(true);
|
|
357
|
-
}
|
|
358
|
-
// If yaml not available, just verify it doesn't crash
|
|
359
|
-
});
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
describe('Global Registry', () => {
|
|
364
|
-
it('should export global registry instance', () => {
|
|
365
|
-
expect(registry).toBeDefined();
|
|
366
|
-
expect(registry instanceof RuleRegistry).toBe(true);
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
it('should export convenience functions', () => {
|
|
370
|
-
const {
|
|
371
|
-
register,
|
|
372
|
-
get,
|
|
373
|
-
has,
|
|
374
|
-
getAll,
|
|
375
|
-
setEnabled,
|
|
376
|
-
updateConfig,
|
|
377
|
-
loadFromFile
|
|
378
|
-
} = require('../lib/quality-rules');
|
|
379
|
-
|
|
380
|
-
expect(typeof register).toBe('function');
|
|
381
|
-
expect(typeof get).toBe('function');
|
|
382
|
-
expect(typeof has).toBe('function');
|
|
383
|
-
expect(typeof getAll).toBe('function');
|
|
384
|
-
expect(typeof setEnabled).toBe('function');
|
|
385
|
-
expect(typeof updateConfig).toBe('function');
|
|
386
|
-
expect(typeof loadFromFile).toBe('function');
|
|
387
|
-
});
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
describe('Built-in Rules', () => {
|
|
391
|
-
describe('file-size-limit', () => {
|
|
392
|
-
const rule = registry.get('file-size-limit');
|
|
393
|
-
|
|
394
|
-
it('should exist', () => {
|
|
395
|
-
expect(rule).toBeDefined();
|
|
396
|
-
expect(rule.id).toBe('file-size-limit');
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
it('should pass small files', () => {
|
|
400
|
-
const result = rule.check(testJsFile, rule.config);
|
|
401
|
-
expect(result.pass).toBe(true);
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
it('should fail files exceeding size limit', () => {
|
|
405
|
-
// Create a large temporary file
|
|
406
|
-
const largeFile = path.join(tempDir, 'large-size.js');
|
|
407
|
-
const largeContent = 'x'.repeat(900000); // ~900KB
|
|
408
|
-
fs.writeFileSync(largeFile, largeContent);
|
|
409
|
-
|
|
410
|
-
const result = rule.check(largeFile, { maxSize: 800 * 1024 });
|
|
411
|
-
|
|
412
|
-
expect(result.pass).toBe(false);
|
|
413
|
-
expect(result.message).toContain('exceeds limit');
|
|
414
|
-
|
|
415
|
-
fs.unlinkSync(largeFile);
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
describe('line-count-limit', () => {
|
|
420
|
-
const rule = registry.get('line-count-limit');
|
|
421
|
-
|
|
422
|
-
it('should exist', () => {
|
|
423
|
-
expect(rule).toBeDefined();
|
|
424
|
-
expect(rule.id).toBe('line-count-limit');
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
it('should pass files under limit', () => {
|
|
428
|
-
const result = rule.check(testJsFile, rule.config);
|
|
429
|
-
expect(result.pass).toBe(true);
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
it('should fail files exceeding line limit', () => {
|
|
433
|
-
const result = rule.check(largeJsFile, { maxLines: 800 });
|
|
434
|
-
|
|
435
|
-
expect(result.pass).toBe(false);
|
|
436
|
-
expect(result.message).toContain('exceeds line limit');
|
|
437
|
-
});
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
describe('no-console-logs', () => {
|
|
441
|
-
const rule = registry.get('no-console-logs');
|
|
442
|
-
|
|
443
|
-
it('should exist', () => {
|
|
444
|
-
expect(rule).toBeDefined();
|
|
445
|
-
expect(rule.id).toBe('no-console-logs');
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
it('should detect console.log statements', () => {
|
|
449
|
-
const result = rule.check(testJsFile);
|
|
450
|
-
|
|
451
|
-
expect(result.pass).toBe(false);
|
|
452
|
-
expect(result.message).toContain('console statement');
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
it('should skip non-JS files', () => {
|
|
456
|
-
const result = rule.check(testMdFile);
|
|
457
|
-
|
|
458
|
-
expect(result.pass).toBe(true);
|
|
459
|
-
expect(result.skip).toBe(true);
|
|
460
|
-
});
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
describe('todo-comments', () => {
|
|
464
|
-
const rule = registry.get('todo-comments');
|
|
465
|
-
|
|
466
|
-
it('should exist', () => {
|
|
467
|
-
expect(rule).toBeDefined();
|
|
468
|
-
expect(rule.id).toBe('todo-comments');
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
it('should find TODO comments', () => {
|
|
472
|
-
const todoFile = path.join(tempDir, 'todo.js');
|
|
473
|
-
fs.writeFileSync(todoFile, '// TODO: fix this\n// FIXME: later\n');
|
|
474
|
-
|
|
475
|
-
const result = rule.check(todoFile);
|
|
476
|
-
|
|
477
|
-
expect(result.pass).toBe(true); // TODO is just informational
|
|
478
|
-
expect(result.message).toContain('TODO');
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
it('should skip non-code files', () => {
|
|
482
|
-
const result = rule.check(testJsonFile);
|
|
483
|
-
|
|
484
|
-
expect(result.pass).toBe(true);
|
|
485
|
-
expect(result.skip).toBe(true);
|
|
486
|
-
});
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
describe('directory-depth', () => {
|
|
490
|
-
const rule = registry.get('directory-depth');
|
|
491
|
-
|
|
492
|
-
it('should exist', () => {
|
|
493
|
-
expect(rule).toBeDefined();
|
|
494
|
-
expect(rule.id).toBe('directory-depth');
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
it('should pass normal depth paths', () => {
|
|
498
|
-
const result = rule.check(testJsFile, { maxDepth: 10 });
|
|
499
|
-
|
|
500
|
-
expect(result.pass).toBe(true);
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
it('should fail deep paths', () => {
|
|
504
|
-
const deepPath = '/a/b/c/d/e/f/g/h/i/j/k/l/file.js';
|
|
505
|
-
const result = rule.check(deepPath, { maxDepth: 6 });
|
|
506
|
-
|
|
507
|
-
expect(result.pass).toBe(false);
|
|
508
|
-
expect(result.message).toContain('depth');
|
|
509
|
-
});
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
describe('no-empty-files', () => {
|
|
513
|
-
const rule = registry.get('no-empty-files');
|
|
514
|
-
|
|
515
|
-
it('should exist', () => {
|
|
516
|
-
expect(rule).toBeDefined();
|
|
517
|
-
expect(rule.id).toBe('no-empty-files');
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
it('should pass files with content', () => {
|
|
521
|
-
const result = rule.check(testJsFile, rule.config);
|
|
522
|
-
|
|
523
|
-
expect(result.pass).toBe(true);
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
it('should fail empty or near-empty files', () => {
|
|
527
|
-
const result = rule.check(emptyJsFile, { minLines: 3 });
|
|
528
|
-
|
|
529
|
-
expect(result.pass).toBe(false);
|
|
530
|
-
expect(result.message).toContain('line');
|
|
531
|
-
});
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
describe('no-trailing-whitespace', () => {
|
|
535
|
-
const rule = registry.get('no-trailing-whitespace');
|
|
536
|
-
|
|
537
|
-
it('should exist', () => {
|
|
538
|
-
expect(rule).toBeDefined();
|
|
539
|
-
expect(rule.id).toBe('no-trailing-whitespace');
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
it('should pass files without trailing whitespace', () => {
|
|
543
|
-
const cleanFile = path.join(tempDir, 'clean.js');
|
|
544
|
-
fs.writeFileSync(cleanFile, 'const x = 1;\nconst y = 2;\n');
|
|
545
|
-
|
|
546
|
-
const result = rule.check(cleanFile);
|
|
547
|
-
|
|
548
|
-
expect(result.pass).toBe(true);
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
it('should detect trailing whitespace', () => {
|
|
552
|
-
const dirtyFile = path.join(tempDir, 'dirty.js');
|
|
553
|
-
fs.writeFileSync(dirtyFile, 'const x = 1; \nconst y = 2;\t\n');
|
|
554
|
-
|
|
555
|
-
const result = rule.check(dirtyFile);
|
|
556
|
-
|
|
557
|
-
expect(result.pass).toBe(false);
|
|
558
|
-
expect(result.message).toContain('Trailing whitespace');
|
|
559
|
-
expect(result.autoFix).toBe(true);
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
it('should skip non-text files', () => {
|
|
563
|
-
const result = rule.check(testJsonFile);
|
|
564
|
-
|
|
565
|
-
// JSON is not in the check list, so it should be skipped
|
|
566
|
-
expect(result.pass).toBe(true);
|
|
567
|
-
});
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
describe('function-length', () => {
|
|
571
|
-
const rule = registry.get('function-length');
|
|
572
|
-
|
|
573
|
-
it('should exist', () => {
|
|
574
|
-
expect(rule).toBeDefined();
|
|
575
|
-
expect(rule.id).toBe('function-length');
|
|
576
|
-
});
|
|
577
|
-
|
|
578
|
-
it('should be disabled by default', () => {
|
|
579
|
-
expect(rule.enabled).toBe(false);
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
it('should skip non-JS files', () => {
|
|
583
|
-
const result = rule.check(testPyFile);
|
|
584
|
-
|
|
585
|
-
expect(result.pass).toBe(true);
|
|
586
|
-
expect(result.skip).toBe(true);
|
|
587
|
-
});
|
|
588
|
-
});
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
describe('Rule Execution', () => {
|
|
592
|
-
it('should handle non-existent files by throwing or returning error', () => {
|
|
593
|
-
const rule = registry.get('line-count-limit');
|
|
594
|
-
|
|
595
|
-
// The rule will try to read the file, which will throw
|
|
596
|
-
expect(() => {
|
|
597
|
-
rule.check('/does/not/exist.js');
|
|
598
|
-
}).toThrow();
|
|
599
|
-
});
|
|
600
|
-
|
|
601
|
-
it('should handle various file extensions', () => {
|
|
602
|
-
const consoleRule = registry.get('no-console-logs');
|
|
603
|
-
|
|
604
|
-
// Should check JS files
|
|
605
|
-
let result = consoleRule.check(testJsFile);
|
|
606
|
-
expect(result.pass).toBe(false); // Has console.log
|
|
607
|
-
|
|
608
|
-
// Should check TS files
|
|
609
|
-
const tsFile = path.join(tempDir, 'test.ts');
|
|
610
|
-
fs.writeFileSync(tsFile, 'console.log("test");');
|
|
611
|
-
result = consoleRule.check(tsFile);
|
|
612
|
-
expect(result.pass).toBe(false);
|
|
613
|
-
|
|
614
|
-
// Should skip markdown
|
|
615
|
-
result = consoleRule.check(testMdFile);
|
|
616
|
-
expect(result.skip).toBe(true);
|
|
617
|
-
});
|
|
618
|
-
});
|
|
619
|
-
});
|