@su-record/vibe 3.2.14 → 3.2.15
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/dist/infra/lib/codex-proxy.d.ts +126 -0
- package/dist/infra/lib/codex-proxy.d.ts.map +1 -1
- package/dist/infra/lib/codex-proxy.js +16 -16
- package/dist/infra/lib/codex-proxy.js.map +1 -1
- package/dist/infra/lib/figma/audit.d.ts.map +1 -1
- package/dist/infra/lib/figma/audit.js +0 -2
- package/dist/infra/lib/figma/audit.js.map +1 -1
- package/dist/infra/lib/figma/extract.d.ts +6 -1
- package/dist/infra/lib/figma/extract.d.ts.map +1 -1
- package/dist/infra/lib/figma/extract.js +17 -15
- package/dist/infra/lib/figma/extract.js.map +1 -1
- package/dist/infra/lib/figma/types.d.ts +103 -0
- package/dist/infra/lib/figma/types.d.ts.map +1 -1
- package/dist/infra/lib/figma/types.js +8 -0
- package/dist/infra/lib/figma/types.js.map +1 -1
- package/hooks/scripts/clone-extract.js +16 -1
- package/hooks/scripts/code-check.js +12 -5
- package/hooks/scripts/lib/dispatcher.js +43 -3
- package/package.json +10 -8
- package/dist/__tests__/agent-model-sync.test.d.ts +0 -2
- package/dist/__tests__/agent-model-sync.test.d.ts.map +0 -1
- package/dist/__tests__/agent-model-sync.test.js +0 -71
- package/dist/__tests__/agent-model-sync.test.js.map +0 -1
- package/dist/__tests__/architecture.test.d.ts +0 -2
- package/dist/__tests__/architecture.test.d.ts.map +0 -1
- package/dist/__tests__/architecture.test.js +0 -207
- package/dist/__tests__/architecture.test.js.map +0 -1
- package/dist/__tests__/engines-contract.test.d.ts +0 -2
- package/dist/__tests__/engines-contract.test.d.ts.map +0 -1
- package/dist/__tests__/engines-contract.test.js +0 -72
- package/dist/__tests__/engines-contract.test.js.map +0 -1
- package/dist/__tests__/instruction-drift.test.d.ts +0 -2
- package/dist/__tests__/instruction-drift.test.d.ts.map +0 -1
- package/dist/__tests__/instruction-drift.test.js +0 -116
- package/dist/__tests__/instruction-drift.test.js.map +0 -1
- package/dist/__tests__/skill-namespace.test.d.ts +0 -2
- package/dist/__tests__/skill-namespace.test.d.ts.map +0 -1
- package/dist/__tests__/skill-namespace.test.js +0 -60
- package/dist/__tests__/skill-namespace.test.js.map +0 -1
- package/dist/__tests__/stakes-contract.test.d.ts +0 -2
- package/dist/__tests__/stakes-contract.test.d.ts.map +0 -1
- package/dist/__tests__/stakes-contract.test.js +0 -173
- package/dist/__tests__/stakes-contract.test.js.map +0 -1
- package/dist/__tests__/stuck-semantics.test.d.ts +0 -2
- package/dist/__tests__/stuck-semantics.test.d.ts.map +0 -1
- package/dist/__tests__/stuck-semantics.test.js +0 -161
- package/dist/__tests__/stuck-semantics.test.js.map +0 -1
- package/dist/__tests__/wiring-integrity.test.d.ts +0 -2
- package/dist/__tests__/wiring-integrity.test.d.ts.map +0 -1
- package/dist/__tests__/wiring-integrity.test.js +0 -160
- package/dist/__tests__/wiring-integrity.test.js.map +0 -1
- package/dist/cli/commands/info.test.d.ts +0 -2
- package/dist/cli/commands/info.test.d.ts.map +0 -1
- package/dist/cli/commands/info.test.js +0 -74
- package/dist/cli/commands/info.test.js.map +0 -1
- package/dist/cli/commands/upgrade.test.d.ts +0 -2
- package/dist/cli/commands/upgrade.test.d.ts.map +0 -1
- package/dist/cli/commands/upgrade.test.js +0 -99
- package/dist/cli/commands/upgrade.test.js.map +0 -1
- package/dist/cli/design/design-md-parser.test.d.ts +0 -2
- package/dist/cli/design/design-md-parser.test.d.ts.map +0 -1
- package/dist/cli/design/design-md-parser.test.js +0 -149
- package/dist/cli/design/design-md-parser.test.js.map +0 -1
- package/dist/cli/detect.characterization.test.d.ts +0 -7
- package/dist/cli/detect.characterization.test.d.ts.map +0 -1
- package/dist/cli/detect.characterization.test.js +0 -294
- package/dist/cli/detect.characterization.test.js.map +0 -1
- package/dist/cli/postinstall/fs-utils.test.d.ts +0 -2
- package/dist/cli/postinstall/fs-utils.test.d.ts.map +0 -1
- package/dist/cli/postinstall/fs-utils.test.js +0 -259
- package/dist/cli/postinstall/fs-utils.test.js.map +0 -1
- package/dist/cli/setup/CodexHooks.test.d.ts +0 -2
- package/dist/cli/setup/CodexHooks.test.d.ts.map +0 -1
- package/dist/cli/setup/CodexHooks.test.js +0 -52
- package/dist/cli/setup/CodexHooks.test.js.map +0 -1
- package/dist/cli/setup/GlobalInstaller.test.d.ts +0 -2
- package/dist/cli/setup/GlobalInstaller.test.d.ts.map +0 -1
- package/dist/cli/setup/GlobalInstaller.test.js +0 -16
- package/dist/cli/setup/GlobalInstaller.test.js.map +0 -1
- package/dist/cli/utils/cli-detector.test.d.ts +0 -2
- package/dist/cli/utils/cli-detector.test.d.ts.map +0 -1
- package/dist/cli/utils/cli-detector.test.js +0 -52
- package/dist/cli/utils/cli-detector.test.js.map +0 -1
- package/dist/infra/lib/SkillFrontmatter.test.d.ts +0 -2
- package/dist/infra/lib/SkillFrontmatter.test.d.ts.map +0 -1
- package/dist/infra/lib/SkillFrontmatter.test.js +0 -60
- package/dist/infra/lib/SkillFrontmatter.test.js.map +0 -1
- package/dist/infra/lib/__tests__/AutomationLevel.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/AutomationLevel.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/AutomationLevel.test.js +0 -297
- package/dist/infra/lib/__tests__/AutomationLevel.test.js.map +0 -1
- package/dist/infra/lib/__tests__/ComponentRegistry.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/ComponentRegistry.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/ComponentRegistry.test.js +0 -99
- package/dist/infra/lib/__tests__/ComponentRegistry.test.js.map +0 -1
- package/dist/infra/lib/__tests__/DecisionTracer.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/DecisionTracer.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/DecisionTracer.test.js +0 -274
- package/dist/infra/lib/__tests__/DecisionTracer.test.js.map +0 -1
- package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.js +0 -350
- package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.js.map +0 -1
- package/dist/infra/lib/__tests__/LoopBreaker.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/LoopBreaker.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/LoopBreaker.test.js +0 -340
- package/dist/infra/lib/__tests__/LoopBreaker.test.js.map +0 -1
- package/dist/infra/lib/__tests__/VerificationLoop.test.d.ts +0 -2
- package/dist/infra/lib/__tests__/VerificationLoop.test.d.ts.map +0 -1
- package/dist/infra/lib/__tests__/VerificationLoop.test.js +0 -486
- package/dist/infra/lib/__tests__/VerificationLoop.test.js.map +0 -1
- package/dist/infra/lib/config/GlobalConfigManager.test.d.ts +0 -2
- package/dist/infra/lib/config/GlobalConfigManager.test.d.ts.map +0 -1
- package/dist/infra/lib/config/GlobalConfigManager.test.js +0 -9
- package/dist/infra/lib/config/GlobalConfigManager.test.js.map +0 -1
- package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.d.ts +0 -2
- package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.d.ts.map +0 -1
- package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.js +0 -43
- package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.js.map +0 -1
- package/dist/infra/lib/embedding/__tests__/VectorStore.test.d.ts +0 -2
- package/dist/infra/lib/embedding/__tests__/VectorStore.test.d.ts.map +0 -1
- package/dist/infra/lib/embedding/__tests__/VectorStore.test.js +0 -94
- package/dist/infra/lib/embedding/__tests__/VectorStore.test.js.map +0 -1
- package/dist/infra/lib/embedding/__tests__/cosine.test.d.ts +0 -2
- package/dist/infra/lib/embedding/__tests__/cosine.test.d.ts.map +0 -1
- package/dist/infra/lib/embedding/__tests__/cosine.test.js +0 -73
- package/dist/infra/lib/embedding/__tests__/cosine.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/deprecation.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/deprecation.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/deprecation.test.js +0 -251
- package/dist/infra/lib/evolution/__tests__/deprecation.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/eval.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/eval.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/eval.test.js +0 -603
- package/dist/infra/lib/evolution/__tests__/eval.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/generation.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/generation.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/generation.test.js +0 -396
- package/dist/infra/lib/evolution/__tests__/generation.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/insight.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/insight.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/insight.test.js +0 -204
- package/dist/infra/lib/evolution/__tests__/insight.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/integration.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/integration.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/integration.test.js +0 -206
- package/dist/infra/lib/evolution/__tests__/integration.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/lifecycle.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/lifecycle.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/lifecycle.test.js +0 -350
- package/dist/infra/lib/evolution/__tests__/lifecycle.test.js.map +0 -1
- package/dist/infra/lib/evolution/__tests__/parity.test.d.ts +0 -2
- package/dist/infra/lib/evolution/__tests__/parity.test.d.ts.map +0 -1
- package/dist/infra/lib/evolution/__tests__/parity.test.js +0 -319
- package/dist/infra/lib/evolution/__tests__/parity.test.js.map +0 -1
- package/dist/infra/lib/llm/auth/AuthProfileManager.test.d.ts +0 -5
- package/dist/infra/lib/llm/auth/AuthProfileManager.test.d.ts.map +0 -1
- package/dist/infra/lib/llm/auth/AuthProfileManager.test.js +0 -168
- package/dist/infra/lib/llm/auth/AuthProfileManager.test.js.map +0 -1
- package/dist/infra/lib/llm/auth/TokenRefresher.test.d.ts +0 -2
- package/dist/infra/lib/llm/auth/TokenRefresher.test.d.ts.map +0 -1
- package/dist/infra/lib/llm/auth/TokenRefresher.test.js +0 -120
- package/dist/infra/lib/llm/auth/TokenRefresher.test.js.map +0 -1
- package/dist/infra/lib/llm/timeout.test.d.ts +0 -2
- package/dist/infra/lib/llm/timeout.test.d.ts.map +0 -1
- package/dist/infra/lib/llm/timeout.test.js +0 -50
- package/dist/infra/lib/llm/timeout.test.js.map +0 -1
- package/dist/infra/lib/llm-availability.test.d.ts +0 -2
- package/dist/infra/lib/llm-availability.test.d.ts.map +0 -1
- package/dist/infra/lib/llm-availability.test.js +0 -37
- package/dist/infra/lib/llm-availability.test.js.map +0 -1
- package/dist/infra/lib/memory/KnowledgeGraph.test.d.ts +0 -2
- package/dist/infra/lib/memory/KnowledgeGraph.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/KnowledgeGraph.test.js +0 -189
- package/dist/infra/lib/memory/KnowledgeGraph.test.js.map +0 -1
- package/dist/infra/lib/memory/MemorySearch.test.d.ts +0 -2
- package/dist/infra/lib/memory/MemorySearch.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/MemorySearch.test.js +0 -149
- package/dist/infra/lib/memory/MemorySearch.test.js.map +0 -1
- package/dist/infra/lib/memory/MemoryStorage.test.d.ts +0 -2
- package/dist/infra/lib/memory/MemoryStorage.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/MemoryStorage.test.js +0 -198
- package/dist/infra/lib/memory/MemoryStorage.test.js.map +0 -1
- package/dist/infra/lib/memory/SessionRAGRetriever.test.d.ts +0 -2
- package/dist/infra/lib/memory/SessionRAGRetriever.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/SessionRAGRetriever.test.js +0 -180
- package/dist/infra/lib/memory/SessionRAGRetriever.test.js.map +0 -1
- package/dist/infra/lib/memory/SessionRAGStore.test.d.ts +0 -2
- package/dist/infra/lib/memory/SessionRAGStore.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/SessionRAGStore.test.js +0 -326
- package/dist/infra/lib/memory/SessionRAGStore.test.js.map +0 -1
- package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.d.ts +0 -2
- package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.js +0 -114
- package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.js.map +0 -1
- package/dist/infra/lib/memory/__tests__/ReflectionStore.test.d.ts +0 -2
- package/dist/infra/lib/memory/__tests__/ReflectionStore.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/__tests__/ReflectionStore.test.js +0 -196
- package/dist/infra/lib/memory/__tests__/ReflectionStore.test.js.map +0 -1
- package/dist/infra/lib/memory/index.test.d.ts +0 -2
- package/dist/infra/lib/memory/index.test.d.ts.map +0 -1
- package/dist/infra/lib/memory/index.test.js +0 -23
- package/dist/infra/lib/memory/index.test.js.map +0 -1
- package/dist/infra/lib/telemetry/SkillTelemetry.test.d.ts +0 -2
- package/dist/infra/lib/telemetry/SkillTelemetry.test.d.ts.map +0 -1
- package/dist/infra/lib/telemetry/SkillTelemetry.test.js +0 -124
- package/dist/infra/lib/telemetry/SkillTelemetry.test.js.map +0 -1
- package/dist/infra/lib/ui-ux/Bm25Engine.test.d.ts +0 -2
- package/dist/infra/lib/ui-ux/Bm25Engine.test.d.ts.map +0 -1
- package/dist/infra/lib/ui-ux/Bm25Engine.test.js +0 -159
- package/dist/infra/lib/ui-ux/Bm25Engine.test.js.map +0 -1
- package/dist/infra/lib/ui-ux/CsvDataLoader.test.d.ts +0 -2
- package/dist/infra/lib/ui-ux/CsvDataLoader.test.d.ts.map +0 -1
- package/dist/infra/lib/ui-ux/CsvDataLoader.test.js +0 -141
- package/dist/infra/lib/ui-ux/CsvDataLoader.test.js.map +0 -1
- package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.d.ts +0 -2
- package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.d.ts.map +0 -1
- package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.js +0 -370
- package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.js.map +0 -1
- package/dist/infra/lib/ui-ux/SearchService.test.d.ts +0 -2
- package/dist/infra/lib/ui-ux/SearchService.test.d.ts.map +0 -1
- package/dist/infra/lib/ui-ux/SearchService.test.js +0 -215
- package/dist/infra/lib/ui-ux/SearchService.test.js.map +0 -1
- package/dist/test-helpers/index.d.ts +0 -36
- package/dist/test-helpers/index.d.ts.map +0 -1
- package/dist/test-helpers/index.js +0 -85
- package/dist/test-helpers/index.js.map +0 -1
- package/dist/test-helpers/index.test.d.ts +0 -2
- package/dist/test-helpers/index.test.d.ts.map +0 -1
- package/dist/test-helpers/index.test.js +0 -92
- package/dist/test-helpers/index.test.js.map +0 -1
- package/dist/tests/curated-release-notes.test.d.ts +0 -2
- package/dist/tests/curated-release-notes.test.d.ts.map +0 -1
- package/dist/tests/curated-release-notes.test.js +0 -160
- package/dist/tests/curated-release-notes.test.js.map +0 -1
- package/dist/tools/convention/analyzeComplexity.test.d.ts +0 -2
- package/dist/tools/convention/analyzeComplexity.test.d.ts.map +0 -1
- package/dist/tools/convention/analyzeComplexity.test.js +0 -207
- package/dist/tools/convention/analyzeComplexity.test.js.map +0 -1
- package/dist/tools/convention/validateCodeQuality.test.d.ts +0 -2
- package/dist/tools/convention/validateCodeQuality.test.d.ts.map +0 -1
- package/dist/tools/convention/validateCodeQuality.test.js +0 -230
- package/dist/tools/convention/validateCodeQuality.test.js.map +0 -1
- package/dist/tools/loop/validateLoopDefinition.test.d.ts +0 -14
- package/dist/tools/loop/validateLoopDefinition.test.d.ts.map +0 -1
- package/dist/tools/loop/validateLoopDefinition.test.js +0 -229
- package/dist/tools/loop/validateLoopDefinition.test.js.map +0 -1
- package/dist/tools/spec/executionPacket.test.d.ts +0 -2
- package/dist/tools/spec/executionPacket.test.d.ts.map +0 -1
- package/dist/tools/spec/executionPacket.test.js +0 -374
- package/dist/tools/spec/executionPacket.test.js.map +0 -1
- package/dist/tools/spec/prdParser.test.d.ts +0 -6
- package/dist/tools/spec/prdParser.test.d.ts.map +0 -1
- package/dist/tools/spec/prdParser.test.js +0 -332
- package/dist/tools/spec/prdParser.test.js.map +0 -1
- package/dist/tools/spec/specGenerator.test.d.ts +0 -2
- package/dist/tools/spec/specGenerator.test.d.ts.map +0 -1
- package/dist/tools/spec/specGenerator.test.js +0 -130
- package/dist/tools/spec/specGenerator.test.js.map +0 -1
- package/dist/tools/spec/traceabilityMatrix.path-resolution.test.d.ts +0 -10
- package/dist/tools/spec/traceabilityMatrix.path-resolution.test.d.ts.map +0 -1
- package/dist/tools/spec/traceabilityMatrix.path-resolution.test.js +0 -120
- package/dist/tools/spec/traceabilityMatrix.path-resolution.test.js.map +0 -1
- package/dist/tools/spec/traceabilityMatrix.test.d.ts +0 -6
- package/dist/tools/spec/traceabilityMatrix.test.d.ts.map +0 -1
- package/dist/tools/spec/traceabilityMatrix.test.js +0 -359
- package/dist/tools/spec/traceabilityMatrix.test.js.map +0 -1
- package/hooks/scripts/__tests__/.vibe/command-log.txt +0 -3
- package/hooks/scripts/__tests__/anchor-inbox.test.js +0 -119
- package/hooks/scripts/__tests__/auto-test-debounce.test.js +0 -145
- package/hooks/scripts/__tests__/clone-behaviors.test.js +0 -152
- package/hooks/scripts/__tests__/clone-merge-responsive.test.js +0 -106
- package/hooks/scripts/__tests__/code-check-detectors.test.js +0 -213
- package/hooks/scripts/__tests__/code-check-false-positive.test.js +0 -74
- package/hooks/scripts/__tests__/code-check-memory-write.test.js +0 -81
- package/hooks/scripts/__tests__/codex-hook-adapter.test.js +0 -64
- package/hooks/scripts/__tests__/curation-index.test.js +0 -157
- package/hooks/scripts/__tests__/dispatcher-inprocess.test.js +0 -99
- package/hooks/scripts/__tests__/figma-extract.test.js +0 -338
- package/hooks/scripts/__tests__/fixtures/seq-harness.js +0 -13
- package/hooks/scripts/__tests__/fixtures/seq-step.js +0 -22
- package/hooks/scripts/__tests__/hook-payload.test.js +0 -29
- package/hooks/scripts/__tests__/llm-orchestrate-antigravity.test.js +0 -66
- package/hooks/scripts/__tests__/loop-ledger.test.js +0 -321
- package/hooks/scripts/__tests__/post-edit-dispatcher.test.js +0 -139
- package/hooks/scripts/__tests__/pre-tool-guard.test.js +0 -523
- package/hooks/scripts/__tests__/recipe-extractor.test.js +0 -244
- package/hooks/scripts/__tests__/run-ledger-verify-required.test.js +0 -151
- package/hooks/scripts/__tests__/run-ledger.test.js +0 -481
- package/hooks/scripts/__tests__/scope-from-spec.test.js +0 -215
- package/hooks/scripts/__tests__/sentinel-guard.test.js +0 -263
- package/hooks/scripts/__tests__/step-counter.test.js +0 -459
- package/hooks/scripts/__tests__/stop-dispatcher-sequential.test.js +0 -74
- package/hooks/scripts/__tests__/utils-npm-root.test.js +0 -99
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* auto-test.js debounce 테스트
|
|
3
|
-
*
|
|
4
|
-
* 검증 대상:
|
|
5
|
-
* - debounce 모드에서 쿨다운 내 + 소스 미변경 → 스킵
|
|
6
|
-
* - 쿨다운 만료 → 재실행
|
|
7
|
-
* - 소스 변경 → 재실행
|
|
8
|
-
* - mode='off' → 실행 안 함
|
|
9
|
-
* - mode='always' → debounce 없이 항상 실행 시도
|
|
10
|
-
*/
|
|
11
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
12
|
-
import { fileURLToPath } from 'url';
|
|
13
|
-
import path from 'path';
|
|
14
|
-
import fs from 'fs';
|
|
15
|
-
import os from 'os';
|
|
16
|
-
import { createHash } from 'crypto';
|
|
17
|
-
|
|
18
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
-
|
|
20
|
-
let tmpDir;
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vibe-autotest-debounce-'));
|
|
23
|
-
});
|
|
24
|
-
afterEach(() => {
|
|
25
|
-
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
// debounce 로직 단위 테스트 (auto-test.js와 동일 알고리즘)
|
|
29
|
-
const DEBOUNCE_COOLDOWN_MS = 120_000;
|
|
30
|
-
|
|
31
|
-
function fileHash(filePath) {
|
|
32
|
-
try {
|
|
33
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
34
|
-
return createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
35
|
-
} catch {
|
|
36
|
-
return '';
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function makeDebounceHelpers(stateFile) {
|
|
41
|
-
function readState() {
|
|
42
|
-
try {
|
|
43
|
-
if (fs.existsSync(stateFile)) return JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
44
|
-
} catch { /* ignore */ }
|
|
45
|
-
return {};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function writeState(state) {
|
|
49
|
-
try {
|
|
50
|
-
fs.mkdirSync(path.dirname(stateFile), { recursive: true });
|
|
51
|
-
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf-8');
|
|
52
|
-
} catch { /* ignore */ }
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function shouldSkip(testFile, srcFile) {
|
|
56
|
-
try {
|
|
57
|
-
const state = readState();
|
|
58
|
-
const entry = state[testFile];
|
|
59
|
-
if (!entry) return false;
|
|
60
|
-
const elapsed = Date.now() - entry.lastRun;
|
|
61
|
-
if (elapsed > DEBOUNCE_COOLDOWN_MS) return false;
|
|
62
|
-
const currentHash = fileHash(srcFile);
|
|
63
|
-
if (currentHash !== entry.srcHash) return false;
|
|
64
|
-
return true;
|
|
65
|
-
} catch {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function updateState(testFile, srcFile) {
|
|
71
|
-
const state = readState();
|
|
72
|
-
state[testFile] = { lastRun: Date.now(), srcHash: fileHash(srcFile) };
|
|
73
|
-
writeState(state);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return { shouldSkip, updateState, readState };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
describe('auto-test debounce: 핵심 로직', () => {
|
|
80
|
-
it('첫 실행: state 없음 → 스킵 안 함', () => {
|
|
81
|
-
const stateFile = path.join(tmpDir, 'auto-test-state.json');
|
|
82
|
-
const { shouldSkip } = makeDebounceHelpers(stateFile);
|
|
83
|
-
const testFile = path.join(tmpDir, 'foo.test.ts');
|
|
84
|
-
const srcFile = path.join(tmpDir, 'foo.ts');
|
|
85
|
-
fs.writeFileSync(srcFile, 'const x = 1;\n', 'utf-8');
|
|
86
|
-
expect(shouldSkip(testFile, srcFile)).toBe(false);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('state 업데이트 후 쿨다운 내 + 소스 미변경 → 스킵', () => {
|
|
90
|
-
const stateFile = path.join(tmpDir, 'auto-test-state.json');
|
|
91
|
-
const { shouldSkip, updateState } = makeDebounceHelpers(stateFile);
|
|
92
|
-
const testFile = path.join(tmpDir, 'foo.test.ts');
|
|
93
|
-
const srcFile = path.join(tmpDir, 'foo.ts');
|
|
94
|
-
fs.writeFileSync(srcFile, 'const x = 1;\n', 'utf-8');
|
|
95
|
-
updateState(testFile, srcFile);
|
|
96
|
-
// 소스 변경 없음 + 즉시 재호출 → 스킵
|
|
97
|
-
expect(shouldSkip(testFile, srcFile)).toBe(true);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('소스 변경 후 → 스킵 안 함 (재실행)', () => {
|
|
101
|
-
const stateFile = path.join(tmpDir, 'auto-test-state.json');
|
|
102
|
-
const { shouldSkip, updateState } = makeDebounceHelpers(stateFile);
|
|
103
|
-
const testFile = path.join(tmpDir, 'foo.test.ts');
|
|
104
|
-
const srcFile = path.join(tmpDir, 'foo.ts');
|
|
105
|
-
fs.writeFileSync(srcFile, 'const x = 1;\n', 'utf-8');
|
|
106
|
-
updateState(testFile, srcFile);
|
|
107
|
-
// 소스 변경
|
|
108
|
-
fs.writeFileSync(srcFile, 'const x = 2;\n', 'utf-8');
|
|
109
|
-
expect(shouldSkip(testFile, srcFile)).toBe(false);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('쿨다운 만료 → 스킵 안 함 (재실행)', () => {
|
|
113
|
-
const stateFile = path.join(tmpDir, 'auto-test-state.json');
|
|
114
|
-
const { shouldSkip, updateState, readState } = makeDebounceHelpers(stateFile);
|
|
115
|
-
const testFile = path.join(tmpDir, 'foo.test.ts');
|
|
116
|
-
const srcFile = path.join(tmpDir, 'foo.ts');
|
|
117
|
-
fs.writeFileSync(srcFile, 'const x = 1;\n', 'utf-8');
|
|
118
|
-
updateState(testFile, srcFile);
|
|
119
|
-
|
|
120
|
-
// lastRun을 과거로 조작 (쿨다운 초과)
|
|
121
|
-
const state = readState();
|
|
122
|
-
state[testFile].lastRun = Date.now() - DEBOUNCE_COOLDOWN_MS - 1000;
|
|
123
|
-
fs.mkdirSync(path.dirname(stateFile), { recursive: true });
|
|
124
|
-
fs.writeFileSync(stateFile, JSON.stringify(state), 'utf-8');
|
|
125
|
-
|
|
126
|
-
expect(shouldSkip(testFile, srcFile)).toBe(false);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('state 파일 없어도 fail-open (스킵 안 함)', () => {
|
|
130
|
-
const stateFile = path.join(tmpDir, 'nonexistent', 'state.json');
|
|
131
|
-
const { shouldSkip } = makeDebounceHelpers(stateFile);
|
|
132
|
-
const testFile = path.join(tmpDir, 'foo.test.ts');
|
|
133
|
-
const srcFile = path.join(tmpDir, 'foo.ts');
|
|
134
|
-
expect(shouldSkip(testFile, srcFile)).toBe(false);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
describe('auto-test: state 파일 경로 패턴', () => {
|
|
139
|
-
it('state 파일이 .vibe/metrics/ 하위에 위치', () => {
|
|
140
|
-
// 경로 패턴 검증 (실제 PROJECT_DIR은 다름)
|
|
141
|
-
const expectedPattern = /\.vibe[/\\]metrics[/\\]auto-test-state\.json$/;
|
|
142
|
-
const stateFile = path.join('/some/project', '.vibe', 'metrics', 'auto-test-state.json');
|
|
143
|
-
expect(expectedPattern.test(stateFile)).toBe(true);
|
|
144
|
-
});
|
|
145
|
-
});
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* clone active-interaction-sweep helpers
|
|
3
|
-
* - diffStyles: before/after computed-style differ (clone-extract.js)
|
|
4
|
-
* - sectionHasNode / behaviorsBlock: behavior→section attachment (clone-spec.js)
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect } from 'vitest';
|
|
8
|
-
import { collectSubUrls, diffStyles } from '../clone-extract.js';
|
|
9
|
-
import { sectionHasNode, behaviorsBlock } from '../clone-spec.js';
|
|
10
|
-
|
|
11
|
-
describe('diffStyles', () => {
|
|
12
|
-
it('reports only changed props with from/to', () => {
|
|
13
|
-
const a = { 'background-color': 'rgba(0, 0, 0, 0)', height: '80px' };
|
|
14
|
-
const b = { 'background-color': 'rgb(255, 255, 255)', height: '80px' };
|
|
15
|
-
expect(diffStyles(a, b)).toEqual({
|
|
16
|
-
'background-color': { from: 'rgba(0, 0, 0, 0)', to: 'rgb(255, 255, 255)' },
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('captures props that appear or disappear', () => {
|
|
21
|
-
const d = diffStyles({}, { 'box-shadow': '0 4px 20px rgba(0,0,0,0.1)' });
|
|
22
|
-
expect(d['box-shadow']).toEqual({ from: null, to: '0 4px 20px rgba(0,0,0,0.1)' });
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('returns empty when nothing changed or a snapshot is missing', () => {
|
|
26
|
-
expect(diffStyles({ color: 'red' }, { color: 'red' })).toEqual({});
|
|
27
|
-
expect(diffStyles(null, { color: 'red' })).toEqual({});
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
describe('collectSubUrls', () => {
|
|
32
|
-
it('keeps same-origin locale menu links and drops footer/external links', () => {
|
|
33
|
-
const urls = collectSubUrls('https://www.lgdisplay.com/kor', [
|
|
34
|
-
{ href: '/kor/company/info/outline/greeting', text: '기업개요' },
|
|
35
|
-
{ href: '/kor/product/tv/oled', text: 'TV' },
|
|
36
|
-
{ href: '/eng', text: 'ENG' },
|
|
37
|
-
{ href: '/kor/privacy', text: '개인정보처리방침' },
|
|
38
|
-
{ href: 'https://news.lgdisplay.com/news', text: 'Newsroom' },
|
|
39
|
-
{ href: 'https://www.youtube.com/@lgdisplay', text: 'You Tube' },
|
|
40
|
-
{ href: '#top', text: 'TOP' },
|
|
41
|
-
]);
|
|
42
|
-
|
|
43
|
-
expect(urls).toEqual([
|
|
44
|
-
'https://www.lgdisplay.com/kor',
|
|
45
|
-
'https://www.lgdisplay.com/kor/company/info/outline/greeting',
|
|
46
|
-
'https://www.lgdisplay.com/kor/product/tv/oled',
|
|
47
|
-
]);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('deduplicates links after dropping query strings and hashes', () => {
|
|
51
|
-
const urls = collectSubUrls('https://example.com/kor', [
|
|
52
|
-
{ href: '/kor/company?utm_source=x', text: 'Company' },
|
|
53
|
-
{ href: '/kor/company#ceo', text: 'Company' },
|
|
54
|
-
]);
|
|
55
|
-
|
|
56
|
-
expect(urls).toEqual([
|
|
57
|
-
'https://example.com/kor',
|
|
58
|
-
'https://example.com/kor/company',
|
|
59
|
-
]);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe('behavior → section attachment', () => {
|
|
64
|
-
const section = {
|
|
65
|
-
name: 'Header',
|
|
66
|
-
children: [{ tag: 'header', classes: 'site-nav top', children: [{ tag: 'button', classes: 'tab', children: [] }] }],
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
it('matches a node by tag and class', () => {
|
|
70
|
-
expect(sectionHasNode(section, 'header', 'site-nav')).toBe(true);
|
|
71
|
-
expect(sectionHasNode(section, 'header', 'missing')).toBe(false);
|
|
72
|
-
expect(sectionHasNode(section, 'button')).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('attaches a scroll behavior to the matching section only', () => {
|
|
76
|
-
const behaviors = {
|
|
77
|
-
scroll: [{ label: 'header.site-nav', tag: 'header', cls: 'site-nav', triggerScrollY: 700, changed: { height: { from: '80px', to: '56px' } } }],
|
|
78
|
-
interactive: [{ kind: 'tab-group', count: 3, tabLabels: ['A', 'B', 'C'], contentSwapsOnClick: true }],
|
|
79
|
-
};
|
|
80
|
-
const block = behaviorsBlock(section, behaviors);
|
|
81
|
-
expect(block).toContain('Scroll-triggered');
|
|
82
|
-
expect(block).toContain('height');
|
|
83
|
-
expect(block).toContain('content SWAPS on click');
|
|
84
|
-
|
|
85
|
-
const footer = { name: 'Footer', children: [{ tag: 'p', classes: '', children: [] }] };
|
|
86
|
-
expect(behaviorsBlock(footer, behaviors)).toBeNull();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('renders hover / in-view / time-driven / scroll-lib behavior kinds', () => {
|
|
90
|
-
const behaviors = {
|
|
91
|
-
scroll: [],
|
|
92
|
-
interactive: [],
|
|
93
|
-
hover: [{ label: 'a.cta', tag: 'a', cls: 'cta', transition: 'all 0.3s ease 0s', changed: { 'background-color': { from: 'rgb(0, 0, 0)', to: 'rgb(255, 0, 0)' } } }],
|
|
94
|
-
inview: [{ label: 'div.fade', tag: 'div', cls: 'fade', triggerY: 1400, changed: { opacity: { from: '0', to: '1' } } }],
|
|
95
|
-
timeDriven: [{ label: 'div.slide', tag: 'div', cls: 'slide', mutations: 12, kinds: ['class'] }],
|
|
96
|
-
scrollLib: { name: 'lenis', evidence: 'html.lenis' },
|
|
97
|
-
};
|
|
98
|
-
const hero = {
|
|
99
|
-
name: 'Hero',
|
|
100
|
-
children: [{ tag: 'div', classes: 'fade slide', children: [{ tag: 'a', classes: 'cta', children: [] }] }],
|
|
101
|
-
};
|
|
102
|
-
const block = behaviorsBlock(hero, behaviors);
|
|
103
|
-
expect(block).toContain('**Hover** on `a.cta`');
|
|
104
|
-
expect(block).toContain('transition: `all 0.3s ease 0s`');
|
|
105
|
-
expect(block).toContain('**In-view entrance** on `div.fade` (enters at ~1400px)');
|
|
106
|
-
expect(block).toContain('opacity: `0` → `1`');
|
|
107
|
-
expect(block).toContain('**Time-driven** `div.slide` — 12 mutations/3s');
|
|
108
|
-
expect(block).toContain('lenis');
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('renders a page-level scroll-lib note even when no section-scoped behavior matches', () => {
|
|
112
|
-
const onlyLib = { scroll: [], interactive: [], scrollLib: { name: 'lenis', evidence: 'window.Lenis' } };
|
|
113
|
-
const footer = { name: 'Footer', children: [{ tag: 'p', classes: '', children: [] }] };
|
|
114
|
-
expect(behaviorsBlock(footer, onlyLib)).toContain('lenis');
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('suppresses the default transition value on hover entries', () => {
|
|
118
|
-
const behaviors = {
|
|
119
|
-
hover: [{ label: 'button.x', tag: 'button', cls: 'x', transition: 'all 0s ease 0s', changed: { color: { from: 'rgb(0, 0, 0)', to: 'rgb(9, 9, 9)' } } }],
|
|
120
|
-
};
|
|
121
|
-
const sec = { name: 'S', children: [{ tag: 'button', classes: 'x', children: [] }] };
|
|
122
|
-
expect(behaviorsBlock(sec, behaviors)).not.toContain('transition:');
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
describe('clone-spec CLI --real-content', () => {
|
|
127
|
-
const runCli = async (extraArgs) => {
|
|
128
|
-
const [fs, os, path, url, cp] = await Promise.all([
|
|
129
|
-
import('fs'), import('os'), import('path'), import('url'), import('child_process'),
|
|
130
|
-
]);
|
|
131
|
-
const tmp = fs.default.mkdtempSync(path.default.join(os.default.tmpdir(), 'clone-spec-'));
|
|
132
|
-
const sections = path.default.join(tmp, 'sections.json');
|
|
133
|
-
fs.default.writeFileSync(sections, JSON.stringify({
|
|
134
|
-
meta: { feature: 'f', bp: 'mo', url: 'https://x.test' },
|
|
135
|
-
sections: [{ name: 'Hero', nodeRef: '0', tag: 'section', css: {}, children: [] }],
|
|
136
|
-
}));
|
|
137
|
-
const script = url.default.fileURLToPath(new URL('../clone-spec.js', import.meta.url));
|
|
138
|
-
cp.default.execFileSync('node', [script, sections, `--out=${tmp}/specs`, ...extraArgs]);
|
|
139
|
-
return fs.default.readFileSync(path.default.join(tmp, 'specs', 'Hero.spec.md'), 'utf8');
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
it('defaults to placeholder replacement wording', async () => {
|
|
143
|
-
const spec = await runCli([]);
|
|
144
|
-
expect(spec).toContain('(replace copyrighted copy with placeholders)');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('switches to verbatim wording with --real-content', async () => {
|
|
148
|
-
const spec = await runCli(['--real-content']);
|
|
149
|
-
expect(spec).toContain('(verbatim — user confirmed rights to reuse this copy)');
|
|
150
|
-
expect(spec).not.toContain('(replace copyrighted copy with placeholders)');
|
|
151
|
-
});
|
|
152
|
-
});
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* clone-merge-responsive — MO/PC SCSS → mobile-first 병합
|
|
3
|
-
* - mergeRules/emitMerged: 순수 병합 코어
|
|
4
|
-
* - CLI: fixture 디렉토리 → sections/tokens/index/class-plan 병합 산출
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { describe, it, expect } from 'vitest';
|
|
8
|
-
import fs from 'fs';
|
|
9
|
-
import os from 'os';
|
|
10
|
-
import path from 'path';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
import { execFileSync } from 'child_process';
|
|
13
|
-
import { mergeRules, emitMerged } from '../clone-merge-responsive.js';
|
|
14
|
-
import { parseScss } from '../clone-validate.js';
|
|
15
|
-
|
|
16
|
-
const SCRIPT = fileURLToPath(new URL('../clone-merge-responsive.js', import.meta.url));
|
|
17
|
-
|
|
18
|
-
const MO = `.f__hero { display: flex; flex-direction: column; padding-top: 24px; }
|
|
19
|
-
.f__hero__title { font-size: 28px; color: rgb(0, 0, 0); }`;
|
|
20
|
-
|
|
21
|
-
const PC = `.f__hero { display: flex; flex-direction: row; padding-top: 64px; }
|
|
22
|
-
.f__hero__title { font-size: 48px; color: rgb(0, 0, 0); }
|
|
23
|
-
.f__hero__side { width: 320px; }`;
|
|
24
|
-
|
|
25
|
-
describe('mergeRules', () => {
|
|
26
|
-
const { base, media, stats } = mergeRules(parseScss(MO), parseScss(PC));
|
|
27
|
-
|
|
28
|
-
it('keeps MO declarations as the mobile-first base', () => {
|
|
29
|
-
expect(base.get('.f__hero')).toEqual({
|
|
30
|
-
display: 'flex', 'flex-direction': 'column', 'padding-top': '24px',
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('puts only differing PC declarations into the media bucket', () => {
|
|
35
|
-
expect(media.get('.f__hero')).toEqual({ 'flex-direction': 'row', 'padding-top': '64px' });
|
|
36
|
-
// color는 양쪽 동일 → media에서 제외
|
|
37
|
-
expect(media.get('.f__hero__title')).toEqual({ 'font-size': '48px' });
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('moves PC-only selectors wholly into the media bucket', () => {
|
|
41
|
-
expect(base.has('.f__hero__side')).toBe(false);
|
|
42
|
-
expect(media.get('.f__hero__side')).toEqual({ width: '320px' });
|
|
43
|
-
expect(stats.pcOnly).toBe(1);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
describe('emitMerged', () => {
|
|
48
|
-
it('emits base + a single min-width block without duplicating shared declarations', () => {
|
|
49
|
-
const { scss } = emitMerged(parseScss(MO), parseScss(PC), 1024, '// merged');
|
|
50
|
-
expect(scss).toContain('@media (min-width: 1024px)');
|
|
51
|
-
expect(scss.match(/@media/g)).toHaveLength(1);
|
|
52
|
-
// 양 BP 동일 선언은 base에 1번만 등장
|
|
53
|
-
expect(scss.match(/display: flex;/g)).toHaveLength(1);
|
|
54
|
-
expect(scss.match(/color: rgb\(0, 0, 0\);/g)).toHaveLength(1);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('omits the media block when PC has no differences', () => {
|
|
58
|
-
const { scss } = emitMerged(parseScss(MO), parseScss(MO), 1024, '// same');
|
|
59
|
-
expect(scss).not.toContain('@media');
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe('CLI file-level merge', () => {
|
|
64
|
-
const write = (dir, rel, content) => {
|
|
65
|
-
const f = path.join(dir, rel);
|
|
66
|
-
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
67
|
-
fs.writeFileSync(f, content);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
it('merges section dirs and writes index/tokens/class-plan', () => {
|
|
71
|
-
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'clone-merge-'));
|
|
72
|
-
const mo = path.join(tmp, 'mo');
|
|
73
|
-
const pc = path.join(tmp, 'pc');
|
|
74
|
-
const out = path.join(tmp, 'merged');
|
|
75
|
-
write(mo, 'sections/_hero.scss', MO);
|
|
76
|
-
write(pc, 'sections/_hero.scss', PC);
|
|
77
|
-
write(pc, 'sections/_footer.scss', '.f__footer { height: 200px; }'); // PC 전용 섹션
|
|
78
|
-
write(mo, '_tokens.scss', ':root { --c-1: #000; --sp-1: 16px; }');
|
|
79
|
-
write(pc, '_tokens.scss', ':root { --c-1: #000; --sp-1: 32px; }');
|
|
80
|
-
write(mo, 'class-plan.json', JSON.stringify({ 0: 'f__hero' }));
|
|
81
|
-
write(pc, 'class-plan.json', JSON.stringify({ 0: 'f__hero', 0.9: 'f__footer' }));
|
|
82
|
-
|
|
83
|
-
execFileSync('node', [SCRIPT, `--mo=${mo}`, `--pc=${pc}`, `--out=${out}`, '--breakpoint=1024']);
|
|
84
|
-
|
|
85
|
-
const hero = fs.readFileSync(path.join(out, 'sections/_hero.scss'), 'utf8');
|
|
86
|
-
expect(hero).toContain('flex-direction: column');
|
|
87
|
-
expect(hero).toContain('@media (min-width: 1024px)');
|
|
88
|
-
|
|
89
|
-
// PC 전용 섹션 파일은 통째로 media 블록 안
|
|
90
|
-
const footer = fs.readFileSync(path.join(out, 'sections/_footer.scss'), 'utf8');
|
|
91
|
-
expect(footer).toContain('@media (min-width: 1024px)');
|
|
92
|
-
expect(footer).toContain('height: 200px');
|
|
93
|
-
|
|
94
|
-
const tokens = fs.readFileSync(path.join(out, '_tokens.scss'), 'utf8');
|
|
95
|
-
expect(tokens).toContain('--sp-1: 16px'); // base = MO
|
|
96
|
-
expect(tokens).toContain('--sp-1: 32px'); // PC diff → media
|
|
97
|
-
|
|
98
|
-
const index = fs.readFileSync(path.join(out, 'index.scss'), 'utf8');
|
|
99
|
-
expect(index).toContain(`@use './sections/hero'`);
|
|
100
|
-
expect(index).toContain(`@use './sections/footer'`);
|
|
101
|
-
|
|
102
|
-
const plan = JSON.parse(fs.readFileSync(path.join(out, 'class-plan.json'), 'utf8'));
|
|
103
|
-
expect(plan['0']).toBe('f__hero');
|
|
104
|
-
expect(plan['0.9']).toBe('f__footer');
|
|
105
|
-
});
|
|
106
|
-
});
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* code-check.js 정밀 탐지기 테스트
|
|
3
|
-
*
|
|
4
|
-
* 검증 대상:
|
|
5
|
-
* - any 탐지: 단어 경계 기반 (false positive: 'company', 'anything' 등)
|
|
6
|
-
* - any 탐지: `: any`, `as any`, `@ts-ignore`, `<any>` (true positive)
|
|
7
|
-
* - console.log 정책: 경로별 허용/차단
|
|
8
|
-
* - run-ledger verifyRequired round-trip + pass 시 clear
|
|
9
|
-
* - auto-commit verifyRequired skip
|
|
10
|
-
*/
|
|
11
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
12
|
-
import { fileURLToPath } from 'url';
|
|
13
|
-
import path from 'path';
|
|
14
|
-
import fs from 'fs';
|
|
15
|
-
import os from 'os';
|
|
16
|
-
|
|
17
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
|
|
19
|
-
// ─── any 탐지 정규식 (code-check.js와 동일) ──────────────────────────────
|
|
20
|
-
const P1_DETECTORS = [
|
|
21
|
-
/:\s*any\b/,
|
|
22
|
-
/\bas\s+any\b/,
|
|
23
|
-
/<any[\s,>]/,
|
|
24
|
-
/@ts-ignore\b/,
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
function detectsAny(line) {
|
|
28
|
-
return P1_DETECTORS.some(re => re.test(line));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
describe('any 탐지: true positive', () => {
|
|
32
|
-
it(': any — 타입 어노테이션', () => {
|
|
33
|
-
expect(detectsAny('function foo(x: any): void {}')).toBe(true);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it(': any — 공백 포함', () => {
|
|
37
|
-
expect(detectsAny('const x: any = 1;')).toBe(true);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('as any — 타입 캐스트', () => {
|
|
41
|
-
expect(detectsAny('const y = x as any;')).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('<any> — 제네릭', () => {
|
|
45
|
-
expect(detectsAny('const z = foo<any>();')).toBe(true);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('<any, — 제네릭 다중 파라미터', () => {
|
|
49
|
-
expect(detectsAny('const m = new Map<any, string>();')).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('@ts-ignore — 라인 직전', () => {
|
|
53
|
-
expect(detectsAny('// @ts-ignore')).toBe(true);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('@ts-ignore — 인라인', () => {
|
|
57
|
-
expect(detectsAny(' @ts-ignore ')).toBe(true);
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('any 탐지: false negative (탐지 안 됨)', () => {
|
|
62
|
-
it('"company" 단어 포함 — 탐지 안 됨', () => {
|
|
63
|
-
expect(detectsAny('const company = "Acme";')).toBe(false);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('"anything" 식별자 — 탐지 안 됨', () => {
|
|
67
|
-
expect(detectsAny('if (anything) return;')).toBe(false);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('"manyThings" — 탐지 안 됨', () => {
|
|
71
|
-
expect(detectsAny('const manyThings = [];')).toBe(false);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('"fantasy" — 탐지 안 됨', () => {
|
|
75
|
-
expect(detectsAny('const fantasy = "world";')).toBe(false);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('문자열 리터럴 "any" — 정규식은 문자열 내부도 매칭할 수 있음 (알려진 한계, 문서화)', () => {
|
|
79
|
-
// NOTE: 현재 구현은 문자열 내 "any"를 완전히 제외하지 않음.
|
|
80
|
-
// 실제 코드에서 ": any" 패턴이 문자열로 존재하는 경우는 극히 드물어 허용.
|
|
81
|
-
// 이 테스트는 동작을 문서화하는 것이 목적.
|
|
82
|
-
const result = detectsAny('const msg = "type: any is bad";');
|
|
83
|
-
// 문자열 내 ": any" 도 탐지될 수 있음 — false positive지만 드문 케이스
|
|
84
|
-
expect(typeof result).toBe('boolean');
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// ─── console.log 경로 정책 ────────────────────────────────────────────────
|
|
89
|
-
describe('console.log 허용 경로 정책', () => {
|
|
90
|
-
// 허용 glob 패턴 (code-check.js 기본값과 동일)
|
|
91
|
-
const DEFAULT_ALLOW_GLOBS = [
|
|
92
|
-
'hooks/scripts/**',
|
|
93
|
-
'scripts/**',
|
|
94
|
-
'**/cli/**',
|
|
95
|
-
'**/*.test.*',
|
|
96
|
-
'**/*.spec.*',
|
|
97
|
-
'**/__tests__/**',
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
function globToRegExp(glob) {
|
|
101
|
-
const normalized = glob.replace(/\\/g, '/');
|
|
102
|
-
let out = '';
|
|
103
|
-
for (let i = 0; i < normalized.length; i++) {
|
|
104
|
-
const c = normalized[i];
|
|
105
|
-
if (c === '*') {
|
|
106
|
-
if (normalized[i + 1] === '*') {
|
|
107
|
-
out += '.*';
|
|
108
|
-
i++;
|
|
109
|
-
if (normalized[i + 1] === '/') i++;
|
|
110
|
-
} else {
|
|
111
|
-
out += '[^/]*';
|
|
112
|
-
}
|
|
113
|
-
} else if (c === '?') {
|
|
114
|
-
out += '[^/]';
|
|
115
|
-
} else if ('.+^$()|{}[]\\'.includes(c)) {
|
|
116
|
-
out += '\\' + c;
|
|
117
|
-
} else {
|
|
118
|
-
out += c;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return new RegExp('^' + out + '$');
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function isConsoleAllowed(relPath) {
|
|
125
|
-
return DEFAULT_ALLOW_GLOBS.some(g => globToRegExp(g).test(relPath));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
it('hooks/scripts/auto-test.js → 허용', () => {
|
|
129
|
-
expect(isConsoleAllowed('hooks/scripts/auto-test.js')).toBe(true);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('hooks/scripts/lib/run-ledger.js → 허용', () => {
|
|
133
|
-
expect(isConsoleAllowed('hooks/scripts/lib/run-ledger.js')).toBe(true);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('src/cli/index.ts → 허용 (**/cli/**)', () => {
|
|
137
|
-
expect(isConsoleAllowed('src/cli/index.ts')).toBe(true);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('src/tools/foo.test.ts → 허용 (**/*.test.*)', () => {
|
|
141
|
-
expect(isConsoleAllowed('src/tools/foo.test.ts')).toBe(true);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('src/__tests__/bar.ts → 허용 (**/__tests__/**)', () => {
|
|
145
|
-
expect(isConsoleAllowed('src/__tests__/bar.ts')).toBe(true);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('src/tools/convention/validateCodeQuality.ts → 차단', () => {
|
|
149
|
-
expect(isConsoleAllowed('src/tools/convention/validateCodeQuality.ts')).toBe(false);
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it('src/infra/lib/MemoryManager.ts → 차단', () => {
|
|
153
|
-
expect(isConsoleAllowed('src/infra/lib/MemoryManager.ts')).toBe(false);
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// ─── 확장자 게이트 (문서 안의 코드 스니펫 오탐 방지) ──────────────────────
|
|
158
|
-
/**
|
|
159
|
-
* SKILL.md 등 문서에 인용된 `console.log(` 는 커밋되면 안 되는 디버그 코드가
|
|
160
|
-
* 아니라 예시다. 확장자 게이트가 없으면 문서를 편집할 때마다 고칠 수도 없는
|
|
161
|
-
* P1 이 주입되고, 반복되는 오탐이 게이트 신뢰도를 깎는다.
|
|
162
|
-
*
|
|
163
|
-
* 재구현이 아니라 실제 code-check.js 의 run() 을 통과시켜 검증한다.
|
|
164
|
-
*/
|
|
165
|
-
describe('console.log 확장자 게이트', () => {
|
|
166
|
-
let tempDir;
|
|
167
|
-
let runCodeCheck;
|
|
168
|
-
|
|
169
|
-
beforeEach(async () => {
|
|
170
|
-
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vibe-codecheck-ext-'));
|
|
171
|
-
({ run: runCodeCheck } = await import('../code-check.js'));
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
afterEach(() => {
|
|
175
|
-
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
/** 허용 glob 에 걸리지 않는 경로를 쓰기 위해 src/feature/ 아래에 만든다 */
|
|
179
|
-
function writeFixture(name, body) {
|
|
180
|
-
const dir = path.join(tempDir, 'src', 'feature');
|
|
181
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
182
|
-
const full = path.join(dir, name);
|
|
183
|
-
fs.writeFileSync(full, body);
|
|
184
|
-
return full;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
it('마크다운 안의 console.log 는 findings 를 만들지 않는다', async () => {
|
|
188
|
-
const filePath = writeFixture(
|
|
189
|
-
'guide.md',
|
|
190
|
-
['# Guide', '', '```bash', 'node -e "console.log(1)"', '```', ''].join('\n'),
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
const { findings } = await runCodeCheck({ filePath });
|
|
194
|
-
|
|
195
|
-
expect(findings.filter(f => /console\.log/.test(f))).toEqual([]);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it('JSON 안의 console.log 문자열도 findings 를 만들지 않는다', async () => {
|
|
199
|
-
const filePath = writeFixture('fixture.json', '{ "cmd": "console.log(1)" }\n');
|
|
200
|
-
|
|
201
|
-
const { findings } = await runCodeCheck({ filePath });
|
|
202
|
-
|
|
203
|
-
expect(findings.filter(f => /console\.log/.test(f))).toEqual([]);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
it('코드 확장자에서는 여전히 탐지한다 (게이트가 탐지 자체를 죽이지 않음)', async () => {
|
|
207
|
-
const filePath = writeFixture('service.ts', 'export const f = () => { console.log(1); };\n');
|
|
208
|
-
|
|
209
|
-
const { findings } = await runCodeCheck({ filePath });
|
|
210
|
-
|
|
211
|
-
expect(findings.some(f => /console\.log/.test(f))).toBe(true);
|
|
212
|
-
});
|
|
213
|
-
});
|