@skyramp/mcp 0.3.2-rc.pom-3 → 0.3.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/build/commands/commandLibrary.d.ts +1 -0
- package/build/commands/commandLibrary.js +19 -13
- package/build/commands/localDevTestChangesCommand.d.ts +15 -0
- package/build/commands/localDevTestChangesCommand.js +201 -0
- package/build/index.js +80 -6
- package/build/prompts/code-reuse.js +3 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +1 -0
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +17 -0
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +11 -9
- package/build/prompts/local-dev/local-dev-plan.d.ts +35 -0
- package/build/prompts/local-dev/local-dev-plan.js +429 -0
- package/build/prompts/local-dev/local-dev-prompts.d.ts +4 -0
- package/build/prompts/local-dev/local-dev-prompts.js +190 -0
- package/build/prompts/pom-aware-code-reuse.js +12 -0
- package/build/prompts/prompt-utils.d.ts +8 -0
- package/build/prompts/prompt-utils.js +33 -0
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +37 -14
- package/build/prompts/sut-setup/shared.js +16 -12
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +21 -29
- package/build/prompts/test-recommendation/scopeAssessment.d.ts +24 -7
- package/build/prompts/test-recommendation/scopeAssessment.js +111 -12
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +41 -4
- package/build/prompts/testbot/testbot-prompts.d.ts +0 -5
- package/build/prompts/testbot/testbot-prompts.js +32 -52
- package/build/recommendation/planRanker.d.ts +15 -2
- package/build/recommendation/planRanker.js +76 -5
- package/build/resources/testbotResource.js +2 -1
- package/build/services/AnalyticsService.d.ts +1 -1
- package/build/services/TestExecutionService.d.ts +2 -1
- package/build/services/TestExecutionService.js +8 -3
- package/build/services/TestGenerationService.d.ts +2 -2
- package/build/services/TestGenerationService.js +39 -21
- package/build/services/containerEnv.js +3 -1
- package/build/tool-phases.js +6 -0
- package/build/tools/code-refactor/codeReuseTool.js +43 -4
- package/build/tools/code-refactor/enhanceAssertionsTool.js +68 -18
- package/build/tools/code-refactor/reuse-outcome.d.ts +109 -0
- package/build/tools/code-refactor/reuse-outcome.js +158 -0
- package/build/tools/code-refactor/reuse-state.d.ts +45 -0
- package/build/tools/code-refactor/reuse-state.js +140 -0
- package/build/tools/enrichTestWithMocksTool.d.ts +28 -0
- package/build/tools/enrichTestWithMocksTool.js +726 -0
- package/build/tools/executeSkyrampTestTool.d.ts +11 -0
- package/build/tools/executeSkyrampTestTool.js +62 -21
- package/build/tools/generate-tests/batchMockGenerationTool.d.ts +106 -0
- package/build/tools/generate-tests/batchMockGenerationTool.js +545 -0
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +25 -23
- package/build/tools/generate-tests/generateContractRestTool.js +2 -2
- package/build/tools/generate-tests/generateE2ERestTool.d.ts +1 -1
- package/build/tools/generate-tests/generateE2ERestTool.js +1 -1
- package/build/tools/generate-tests/generateLoadRestTool.d.ts +1 -1
- package/build/tools/generate-tests/generateLoadRestTool.js +1 -1
- package/build/tools/generate-tests/generateMockRestTool.d.ts +179 -6
- package/build/tools/generate-tests/generateMockRestTool.js +391 -22
- package/build/tools/generate-tests/loadTestSchema.d.ts +1 -1
- package/build/tools/generate-tests/planGuard.js +2 -22
- package/build/tools/generateEnrichedIntegrationTestTool.d.ts +25 -0
- package/build/tools/generateEnrichedIntegrationTestTool.js +235 -0
- package/build/tools/localDevWorkerComposeTool.d.ts +24 -0
- package/build/tools/localDevWorkerComposeTool.js +264 -0
- package/build/tools/one-click/oneClickTool.d.ts +13 -0
- package/build/tools/one-click/oneClickTool.js +195 -24
- package/build/tools/preflightMockCheckTool.d.ts +2 -0
- package/build/tools/preflightMockCheckTool.js +96 -0
- package/build/tools/queryProxyMocksTool.d.ts +70 -0
- package/build/tools/queryProxyMocksTool.js +522 -0
- package/build/tools/runExistingTestsTool.d.ts +31 -0
- package/build/tools/runExistingTestsTool.js +214 -13
- package/build/tools/submitReportTool.js +38 -3
- package/build/tools/test-management/analyzeChangesTool.d.ts +2 -1
- package/build/tools/test-management/analyzeChangesTool.js +63 -40
- package/build/tools/test-management/registerTestPlanTool.js +55 -7
- package/build/tools/trace/startTraceCollectionTool.js +3 -3
- package/build/types/FrontendIntegration.d.ts +3 -0
- package/build/types/FrontendIntegration.js +3 -0
- package/build/types/OneClickCommands.d.ts +1 -1
- package/build/types/Recommendation.d.ts +20 -0
- package/build/types/Recommendation.js +32 -6
- package/build/types/RepositoryAnalysis.d.ts +131 -14
- package/build/types/RepositoryAnalysis.js +16 -2
- package/build/types/ReuseOutcome.d.ts +63 -0
- package/build/types/ReuseOutcome.js +33 -0
- package/build/types/TestExecution.d.ts +1 -0
- package/build/types/TestTypes.d.ts +25 -7
- package/build/types/TestTypes.js +22 -6
- package/build/types/TestbotReport.d.ts +7 -0
- package/build/types/index.d.ts +2 -0
- package/build/types/index.js +1 -0
- package/build/utils/AnalysisStateManager.d.ts +26 -0
- package/build/utils/AnalysisStateManager.js +31 -1
- package/build/utils/analyze-openapi.js +18 -1
- package/build/utils/branchDiff.d.ts +17 -1
- package/build/utils/branchDiff.js +99 -14
- package/build/utils/featureFlags.d.ts +31 -0
- package/build/utils/featureFlags.js +37 -0
- package/build/utils/frontendIntegration.js +8 -1
- package/build/utils/grpcMockValidation.d.ts +1 -0
- package/build/utils/grpcMockValidation.js +49 -0
- package/build/utils/httpMethodValidation.d.ts +4 -0
- package/build/utils/httpMethodValidation.js +15 -0
- package/build/utils/logger.js +1 -1
- package/build/utils/mockCompatibility.d.ts +49 -0
- package/build/utils/mockCompatibility.js +82 -0
- package/build/utils/pom-scope/pom-files.js +7 -0
- package/build/utils/pom-verify/bindings.js +11 -1
- package/build/utils/pom-verify/re-exports.d.ts +10 -0
- package/build/utils/pom-verify/re-exports.js +61 -0
- package/build/utils/pom-verify/resolve.d.ts +4 -1
- package/build/utils/pom-verify/resolve.js +7 -4
- package/build/utils/pom-verify/verify.d.ts +5 -0
- package/build/utils/pom-verify/verify.js +25 -3
- package/build/utils/progress.js +10 -5
- package/build/utils/proxy-terminal.js +3 -3
- package/build/utils/routeParsers.d.ts +3 -9
- package/build/utils/routeParsers.js +79 -4
- package/build/utils/utils.js +2 -2
- package/build/utils/versions.d.ts +4 -3
- package/build/utils/versions.js +3 -1
- package/build/utils/workspaceAuth.d.ts +46 -0
- package/build/utils/workspaceAuth.js +156 -1
- package/build/workspace/testSuites.d.ts +2 -1
- package/build/workspace/testSuites.js +1 -1
- package/build/workspace/workspace.d.ts +108 -32
- package/build/workspace/workspace.js +32 -4
- package/package.json +5 -2
- package/build/adapters/jestAdapter.test.d.ts +0 -1
- package/build/adapters/jestAdapter.test.js +0 -93
- package/build/adapters/mochaAdapter.test.d.ts +0 -1
- package/build/adapters/mochaAdapter.test.js +0 -63
- package/build/adapters/playwrightAdapter.test.d.ts +0 -1
- package/build/adapters/playwrightAdapter.test.js +0 -169
- package/build/adapters/pytestAdapter.test.d.ts +0 -1
- package/build/adapters/pytestAdapter.test.js +0 -90
- package/build/prompts/code-reuse.test.d.ts +0 -1
- package/build/prompts/code-reuse.test.js +0 -62
- package/build/prompts/pom-aware-code-reuse.test.d.ts +0 -1
- package/build/prompts/pom-aware-code-reuse.test.js +0 -11
- package/build/prompts/test-maintenance/drift-analysis-prompt.test.d.ts +0 -1
- package/build/prompts/test-maintenance/drift-analysis-prompt.test.js +0 -51
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.d.ts +0 -1
- package/build/prompts/test-recommendation/analysisOutputPrompt.test.js +0 -264
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.d.ts +0 -1
- package/build/prompts/test-recommendation/mergeEnrichedScenarios.test.js +0 -126
- package/build/prompts/test-recommendation/promptPlan.test.d.ts +0 -1
- package/build/prompts/test-recommendation/promptPlan.test.js +0 -336
- package/build/prompts/test-recommendation/scopeAssessment.test.d.ts +0 -1
- package/build/prompts/test-recommendation/scopeAssessment.test.js +0 -371
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.d.ts +0 -1
- package/build/prompts/test-recommendation/test-recommendation-prompt.test.js +0 -1925
- package/build/prompts/testbot/testbot-prompts.test.d.ts +0 -1
- package/build/prompts/testbot/testbot-prompts.test.js +0 -451
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.d.ts +0 -1
- package/build/recommendation/budgeters/diversityBalancedBudgeter.test.js +0 -138
- package/build/recommendation/budgeters/fixedNBudgeter.test.d.ts +0 -1
- package/build/recommendation/budgeters/fixedNBudgeter.test.js +0 -66
- package/build/recommendation/discriminators.test.d.ts +0 -1
- package/build/recommendation/discriminators.test.js +0 -324
- package/build/recommendation/diversity.test.d.ts +0 -1
- package/build/recommendation/diversity.test.js +0 -77
- package/build/recommendation/planRanker.test.d.ts +0 -1
- package/build/recommendation/planRanker.test.js +0 -110
- package/build/resources/testbotResource.test.d.ts +0 -1
- package/build/resources/testbotResource.test.js +0 -44
- package/build/services/AnalyticsService.test.d.ts +0 -1
- package/build/services/AnalyticsService.test.js +0 -86
- package/build/services/ScenarioGenerationService.integration.test.d.ts +0 -1
- package/build/services/ScenarioGenerationService.integration.test.js +0 -162
- package/build/services/ScenarioGenerationService.test.d.ts +0 -1
- package/build/services/ScenarioGenerationService.test.js +0 -414
- package/build/services/TestDiscoveryService.test.d.ts +0 -1
- package/build/services/TestDiscoveryService.test.js +0 -983
- package/build/services/TestExecutionService.test.d.ts +0 -1
- package/build/services/TestExecutionService.test.js +0 -1032
- package/build/services/TestGenerationService.test.d.ts +0 -1
- package/build/services/TestGenerationService.test.js +0 -578
- package/build/tool-phase-coverage.test.d.ts +0 -1
- package/build/tool-phase-coverage.test.js +0 -49
- package/build/tools/code-refactor/codeReuseTool.test.d.ts +0 -1
- package/build/tools/code-refactor/codeReuseTool.test.js +0 -572
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateBatchScenarioRestTool.test.js +0 -433
- package/build/tools/generate-tests/generateContractRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateContractRestTool.test.js +0 -142
- package/build/tools/generate-tests/generateIntegrationRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateIntegrationRestTool.test.js +0 -159
- package/build/tools/generate-tests/generateLoadRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateLoadRestTool.test.js +0 -169
- package/build/tools/generate-tests/generateUIRestTool.test.d.ts +0 -1
- package/build/tools/generate-tests/generateUIRestTool.test.js +0 -32
- package/build/tools/generate-tests/planGuard.test.d.ts +0 -1
- package/build/tools/generate-tests/planGuard.test.js +0 -185
- package/build/tools/generate-tests/scenarioLint.test.d.ts +0 -1
- package/build/tools/generate-tests/scenarioLint.test.js +0 -100
- package/build/tools/runExistingTestsTool.test.d.ts +0 -1
- package/build/tools/runExistingTestsTool.test.js +0 -379
- package/build/tools/submitReportTool.test.d.ts +0 -1
- package/build/tools/submitReportTool.test.js +0 -1428
- package/build/tools/test-management/actionsTool.test.d.ts +0 -1
- package/build/tools/test-management/actionsTool.test.js +0 -436
- package/build/tools/test-management/analyzeChangesTool.test.d.ts +0 -1
- package/build/tools/test-management/analyzeChangesTool.test.js +0 -522
- package/build/tools/test-management/analyzeTestHealthTool.test.d.ts +0 -1
- package/build/tools/test-management/analyzeTestHealthTool.test.js +0 -385
- package/build/tools/test-management/registerTestPlanTool.test.d.ts +0 -1
- package/build/tools/test-management/registerTestPlanTool.test.js +0 -296
- package/build/tools/trace/resolveSaveStoragePath.test.d.ts +0 -1
- package/build/tools/trace/resolveSaveStoragePath.test.js +0 -17
- package/build/tools/trace/resolveSessionPaths.test.d.ts +0 -1
- package/build/tools/trace/resolveSessionPaths.test.js +0 -104
- package/build/tools/trace/sessionState.test.d.ts +0 -1
- package/build/tools/trace/sessionState.test.js +0 -17
- package/build/tools/workspace/initializeWorkspaceTool.test.d.ts +0 -1
- package/build/tools/workspace/initializeWorkspaceTool.test.js +0 -139
- package/build/tools/workspace/serviceUpsert.test.d.ts +0 -1
- package/build/tools/workspace/serviceUpsert.test.js +0 -50
- package/build/utils/AnalysisStateManager.test.d.ts +0 -1
- package/build/utils/AnalysisStateManager.test.js +0 -134
- package/build/utils/dartRouteExtractor.test.d.ts +0 -1
- package/build/utils/dartRouteExtractor.test.js +0 -307
- package/build/utils/docker.test.d.ts +0 -1
- package/build/utils/docker.test.js +0 -114
- package/build/utils/featureFlags.test.d.ts +0 -1
- package/build/utils/featureFlags.test.js +0 -81
- package/build/utils/fileWalk.test.d.ts +0 -1
- package/build/utils/fileWalk.test.js +0 -252
- package/build/utils/frontendIntegration.test.d.ts +0 -1
- package/build/utils/frontendIntegration.test.js +0 -229
- package/build/utils/frontendSelectors.test.d.ts +0 -1
- package/build/utils/frontendSelectors.test.js +0 -118
- package/build/utils/gitStaging.test.d.ts +0 -1
- package/build/utils/gitStaging.test.js +0 -111
- package/build/utils/httpDefaults.test.d.ts +0 -1
- package/build/utils/httpDefaults.test.js +0 -21
- package/build/utils/importerHop.test.d.ts +0 -1
- package/build/utils/importerHop.test.js +0 -469
- package/build/utils/pathAffinityClassification.test.d.ts +0 -1
- package/build/utils/pathAffinityClassification.test.js +0 -208
- package/build/utils/planMatchKeys.test.d.ts +0 -1
- package/build/utils/planMatchKeys.test.js +0 -123
- package/build/utils/pom-scope/index.test.d.ts +0 -1
- package/build/utils/pom-scope/index.test.js +0 -278
- package/build/utils/pom-scope/pom-files.test.d.ts +0 -1
- package/build/utils/pom-scope/pom-files.test.js +0 -29
- package/build/utils/pom-scope/scoring.test.d.ts +0 -1
- package/build/utils/pom-scope/scoring.test.js +0 -39
- package/build/utils/pom-scope/selector-extractor.test.d.ts +0 -1
- package/build/utils/pom-scope/selector-extractor.test.js +0 -67
- package/build/utils/pom-verify/bindings.test.d.ts +0 -1
- package/build/utils/pom-verify/bindings.test.js +0 -164
- package/build/utils/pom-verify/calls.test.d.ts +0 -1
- package/build/utils/pom-verify/calls.test.js +0 -61
- package/build/utils/pom-verify/resolve.test.d.ts +0 -1
- package/build/utils/pom-verify/resolve.test.js +0 -114
- package/build/utils/pom-verify/verify.test.d.ts +0 -1
- package/build/utils/pom-verify/verify.test.js +0 -374
- package/build/utils/pr-comment-parser.test.d.ts +0 -1
- package/build/utils/pr-comment-parser.test.js +0 -428
- package/build/utils/progress.test.d.ts +0 -1
- package/build/utils/progress.test.js +0 -37
- package/build/utils/projectMetadata.test.d.ts +0 -1
- package/build/utils/projectMetadata.test.js +0 -172
- package/build/utils/pythonMountPrefixes.test.d.ts +0 -1
- package/build/utils/pythonMountPrefixes.test.js +0 -113
- package/build/utils/repoScanner.test.d.ts +0 -1
- package/build/utils/repoScanner.test.js +0 -190
- package/build/utils/reportVerification.test.d.ts +0 -1
- package/build/utils/reportVerification.test.js +0 -185
- package/build/utils/routeParsers.test.d.ts +0 -1
- package/build/utils/routeParsers.test.js +0 -1011
- package/build/utils/scenarioDrafting.test.d.ts +0 -1
- package/build/utils/scenarioDrafting.test.js +0 -876
- package/build/utils/sourceRouteExtractor.test.d.ts +0 -1
- package/build/utils/sourceRouteExtractor.test.js +0 -738
- package/build/utils/telemetry.test.d.ts +0 -1
- package/build/utils/telemetry.test.js +0 -70
- package/build/utils/trace-parser.test.d.ts +0 -6
- package/build/utils/trace-parser.test.js +0 -140
- package/build/utils/uiPageEnumerator.test.d.ts +0 -1
- package/build/utils/uiPageEnumerator.test.js +0 -824
- package/build/utils/utils.test.d.ts +0 -1
- package/build/utils/utils.test.js +0 -103
- package/build/utils/walkerCharacterization.test.d.ts +0 -1
- package/build/utils/walkerCharacterization.test.js +0 -233
- package/build/utils/workspaceAuth.test.d.ts +0 -1
- package/build/utils/workspaceAuth.test.js +0 -260
- package/build/workspace/testSuites.test.d.ts +0 -1
- package/build/workspace/testSuites.test.js +0 -23
- package/build/workspace/workspace.test.d.ts +0 -1
- package/build/workspace/workspace.test.js +0 -264
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as os from "os";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
import { collectFiles, walkDir, walkSourceFiles } from "./fileWalk.js";
|
|
5
|
-
/** Materialize a file tree under a fresh temp dir. Keys are relative paths;
|
|
6
|
-
* a value of `null` creates an (empty) directory instead of a file. */
|
|
7
|
-
function makeTree(files) {
|
|
8
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), "filewalk-"));
|
|
9
|
-
for (const [rel, content] of Object.entries(files)) {
|
|
10
|
-
const abs = path.join(root, rel);
|
|
11
|
-
if (content === null) {
|
|
12
|
-
fs.mkdirSync(abs, { recursive: true });
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
16
|
-
fs.writeFileSync(abs, content);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return root;
|
|
20
|
-
}
|
|
21
|
-
/** Sort + make paths relative to root for stable comparison. */
|
|
22
|
-
function rel(root, abs) {
|
|
23
|
-
return abs.map((p) => path.relative(root, p).replace(/\\/g, "/")).sort();
|
|
24
|
-
}
|
|
25
|
-
// `chmod 000` only makes a directory unreadable where the OS enforces the
|
|
26
|
-
// permission bits: not on Windows, and not for root (which bypasses them). Skip
|
|
27
|
-
// the best-effort-on-unreadable-dir tests where the setup can't actually fail a
|
|
28
|
-
// readdir, rather than let them false-fail. `it`/`it.skip` chosen at load time.
|
|
29
|
-
const permsEnforced = process.platform !== "win32" &&
|
|
30
|
-
(typeof process.getuid !== "function" || process.getuid() !== 0);
|
|
31
|
-
const itIfPerms = permsEnforced ? it : it.skip;
|
|
32
|
-
/** Collect the absolute file paths a walkDir generator yields. */
|
|
33
|
-
function walkPaths(root, opts) {
|
|
34
|
-
const seen = [];
|
|
35
|
-
for (const [, full] of walkDir(root, opts))
|
|
36
|
-
seen.push(full);
|
|
37
|
-
return seen;
|
|
38
|
-
}
|
|
39
|
-
describe("walkDir", () => {
|
|
40
|
-
it("yields every file depth-first as [entry, absolute path]", () => {
|
|
41
|
-
const root = makeTree({
|
|
42
|
-
"a.txt": "a",
|
|
43
|
-
"sub/b.txt": "b",
|
|
44
|
-
"sub/deep/c.txt": "c",
|
|
45
|
-
});
|
|
46
|
-
const seen = [];
|
|
47
|
-
for (const [entry, full] of walkDir(root)) {
|
|
48
|
-
expect(entry.isFile()).toBe(true);
|
|
49
|
-
expect(path.isAbsolute(full)).toBe(true);
|
|
50
|
-
seen.push(full);
|
|
51
|
-
}
|
|
52
|
-
expect(rel(root, seen)).toEqual(["a.txt", "sub/b.txt", "sub/deep/c.txt"]);
|
|
53
|
-
});
|
|
54
|
-
it("yields only files, never directory entries", () => {
|
|
55
|
-
const root = makeTree({ "sub/b.txt": "b", emptydir: null });
|
|
56
|
-
const names = [...walkDir(root)].map(([entry]) => entry.name);
|
|
57
|
-
expect(names).toEqual(["b.txt"]);
|
|
58
|
-
});
|
|
59
|
-
it("honors maxDepth = 0 (root files only, no descent)", () => {
|
|
60
|
-
const root = makeTree({ "a.txt": "a", "sub/b.txt": "b" });
|
|
61
|
-
expect(rel(root, walkPaths(root, { maxDepth: 0 }))).toEqual(["a.txt"]);
|
|
62
|
-
});
|
|
63
|
-
it("honors maxDepth = 1 (root + one level down)", () => {
|
|
64
|
-
const root = makeTree({
|
|
65
|
-
"a.txt": "a",
|
|
66
|
-
"sub/b.txt": "b",
|
|
67
|
-
"sub/deep/c.txt": "c",
|
|
68
|
-
});
|
|
69
|
-
expect(rel(root, walkPaths(root, { maxDepth: 1 }))).toEqual([
|
|
70
|
-
"a.txt",
|
|
71
|
-
"sub/b.txt",
|
|
72
|
-
]);
|
|
73
|
-
});
|
|
74
|
-
it("skips directories for which shouldSkipDir returns true", () => {
|
|
75
|
-
const root = makeTree({
|
|
76
|
-
"keep/a.txt": "a",
|
|
77
|
-
"node_modules/x.txt": "x",
|
|
78
|
-
});
|
|
79
|
-
const out = walkPaths(root, {
|
|
80
|
-
shouldSkipDir: (entry) => entry.name === "node_modules",
|
|
81
|
-
});
|
|
82
|
-
expect(rel(root, out)).toEqual(["keep/a.txt"]);
|
|
83
|
-
});
|
|
84
|
-
it("passes the directory's absolute path to shouldSkipDir", () => {
|
|
85
|
-
const root = makeTree({ "sub/a.txt": "a" });
|
|
86
|
-
[
|
|
87
|
-
...walkDir(root, {
|
|
88
|
-
shouldSkipDir: (entry, full) => {
|
|
89
|
-
expect(path.isAbsolute(full)).toBe(true);
|
|
90
|
-
expect(path.basename(full)).toBe(entry.name);
|
|
91
|
-
return false;
|
|
92
|
-
},
|
|
93
|
-
}),
|
|
94
|
-
];
|
|
95
|
-
});
|
|
96
|
-
it("stops immediately when the consumer breaks (no further reads)", () => {
|
|
97
|
-
const root = makeTree({
|
|
98
|
-
"1.txt": "",
|
|
99
|
-
"2.txt": "",
|
|
100
|
-
"3.txt": "",
|
|
101
|
-
"sub/4.txt": "",
|
|
102
|
-
});
|
|
103
|
-
const seen = [];
|
|
104
|
-
for (const [, full] of walkDir(root)) {
|
|
105
|
-
seen.push(full);
|
|
106
|
-
break; // stop after the very first file
|
|
107
|
-
}
|
|
108
|
-
expect(seen.length).toBe(1);
|
|
109
|
-
});
|
|
110
|
-
it("yields all files when the consumer never breaks", () => {
|
|
111
|
-
const root = makeTree({ "1.txt": "", "2.txt": "" });
|
|
112
|
-
expect(walkPaths(root).length).toBe(2);
|
|
113
|
-
});
|
|
114
|
-
itIfPerms("is best-effort: an unreadable subdirectory is skipped, not fatal", () => {
|
|
115
|
-
const root = makeTree({ "a.txt": "a", "bad/b.txt": "b", "c.txt": "c" });
|
|
116
|
-
const badDir = path.join(root, "bad");
|
|
117
|
-
fs.chmodSync(badDir, 0o000);
|
|
118
|
-
try {
|
|
119
|
-
// Should not throw despite `bad` being unreadable, and should skip its
|
|
120
|
-
// contents while still visiting siblings.
|
|
121
|
-
expect(rel(root, walkPaths(root))).toEqual(["a.txt", "c.txt"]);
|
|
122
|
-
}
|
|
123
|
-
finally {
|
|
124
|
-
fs.chmodSync(badDir, 0o755);
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
it("does not throw when the root itself is unreadable/missing", () => {
|
|
128
|
-
expect(() => walkPaths(path.join(os.tmpdir(), "filewalk-does-not-exist-xyz"))).not.toThrow();
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
describe("walkSourceFiles (characterization — existing contract)", () => {
|
|
132
|
-
it("collects only files matching one of the extensions, as absolute paths", () => {
|
|
133
|
-
const root = makeTree({
|
|
134
|
-
"a.ts": "",
|
|
135
|
-
"b.tsx": "",
|
|
136
|
-
"c.js": "",
|
|
137
|
-
"d.py": "",
|
|
138
|
-
"sub/e.ts": "",
|
|
139
|
-
});
|
|
140
|
-
const out = walkSourceFiles(root, {
|
|
141
|
-
extensions: [".ts", ".tsx"],
|
|
142
|
-
skipDirs: new Set(),
|
|
143
|
-
});
|
|
144
|
-
expect(out.every((p) => path.isAbsolute(p))).toBe(true);
|
|
145
|
-
expect(rel(root, out)).toEqual(["a.ts", "b.tsx", "sub/e.ts"]);
|
|
146
|
-
});
|
|
147
|
-
it("skips directories named in skipDirs (matched on basename)", () => {
|
|
148
|
-
const root = makeTree({
|
|
149
|
-
"a.ts": "",
|
|
150
|
-
"node_modules/x.ts": "",
|
|
151
|
-
"src/b.ts": "",
|
|
152
|
-
});
|
|
153
|
-
const out = walkSourceFiles(root, {
|
|
154
|
-
extensions: [".ts"],
|
|
155
|
-
skipDirs: new Set(["node_modules"]),
|
|
156
|
-
});
|
|
157
|
-
expect(rel(root, out)).toEqual(["a.ts", "src/b.ts"]);
|
|
158
|
-
});
|
|
159
|
-
it("stops at exactly `limit` files when the cap is hit", () => {
|
|
160
|
-
const root = makeTree({
|
|
161
|
-
"a.ts": "",
|
|
162
|
-
"b.ts": "",
|
|
163
|
-
"c.ts": "",
|
|
164
|
-
"d.ts": "",
|
|
165
|
-
});
|
|
166
|
-
const out = walkSourceFiles(root, {
|
|
167
|
-
extensions: [".ts"],
|
|
168
|
-
skipDirs: new Set(),
|
|
169
|
-
limit: 2,
|
|
170
|
-
});
|
|
171
|
-
expect(out.length).toBe(2);
|
|
172
|
-
});
|
|
173
|
-
it("returns all matches when under the limit", () => {
|
|
174
|
-
const root = makeTree({ "a.ts": "", "b.ts": "" });
|
|
175
|
-
const out = walkSourceFiles(root, {
|
|
176
|
-
extensions: [".ts"],
|
|
177
|
-
skipDirs: new Set(),
|
|
178
|
-
limit: 100,
|
|
179
|
-
});
|
|
180
|
-
expect(out.length).toBe(2);
|
|
181
|
-
});
|
|
182
|
-
it("does NOT skip dotfile directories by default", () => {
|
|
183
|
-
const root = makeTree({ ".hidden/a.ts": "", "b.ts": "" });
|
|
184
|
-
const out = walkSourceFiles(root, {
|
|
185
|
-
extensions: [".ts"],
|
|
186
|
-
skipDirs: new Set(),
|
|
187
|
-
});
|
|
188
|
-
expect(rel(root, out)).toEqual([".hidden/a.ts", "b.ts"]);
|
|
189
|
-
});
|
|
190
|
-
itIfPerms("is best-effort on unreadable directories", () => {
|
|
191
|
-
const root = makeTree({ "a.ts": "", "bad/b.ts": "" });
|
|
192
|
-
const badDir = path.join(root, "bad");
|
|
193
|
-
fs.chmodSync(badDir, 0o000);
|
|
194
|
-
try {
|
|
195
|
-
const out = walkSourceFiles(root, {
|
|
196
|
-
extensions: [".ts"],
|
|
197
|
-
skipDirs: new Set(),
|
|
198
|
-
});
|
|
199
|
-
expect(rel(root, out)).toEqual(["a.ts"]);
|
|
200
|
-
}
|
|
201
|
-
finally {
|
|
202
|
-
fs.chmodSync(badDir, 0o755);
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
describe("collectFiles", () => {
|
|
207
|
-
it("collects files matching the predicate as absolute paths", () => {
|
|
208
|
-
const root = makeTree({ "a.dart": "", "b.txt": "", "sub/c.dart": "" });
|
|
209
|
-
const out = collectFiles(root, {
|
|
210
|
-
match: (entry) => entry.name.endsWith(".dart"),
|
|
211
|
-
});
|
|
212
|
-
expect(out.every((p) => path.isAbsolute(p))).toBe(true);
|
|
213
|
-
expect(rel(root, out)).toEqual(["a.dart", "sub/c.dart"]);
|
|
214
|
-
});
|
|
215
|
-
it("passes the absolute path to the matcher", () => {
|
|
216
|
-
const root = makeTree({ "keep/x.ts": "", "modules/y/index.ts": "" });
|
|
217
|
-
const out = collectFiles(root, {
|
|
218
|
-
match: (_entry, full) => full.includes(`${path.sep}modules${path.sep}`),
|
|
219
|
-
});
|
|
220
|
-
expect(rel(root, out)).toEqual(["modules/y/index.ts"]);
|
|
221
|
-
});
|
|
222
|
-
it("returns exactly `limit` files when the cap is hit", () => {
|
|
223
|
-
const root = makeTree({ "a.ts": "", "b.ts": "", "c.ts": "" });
|
|
224
|
-
const out = collectFiles(root, {
|
|
225
|
-
match: (entry) => entry.name.endsWith(".ts"),
|
|
226
|
-
limit: 2,
|
|
227
|
-
});
|
|
228
|
-
expect(out.length).toBe(2);
|
|
229
|
-
});
|
|
230
|
-
it("returns nothing for `limit: 0` (does not collect a single file)", () => {
|
|
231
|
-
const root = makeTree({ "a.ts": "", "b.ts": "" });
|
|
232
|
-
const out = collectFiles(root, {
|
|
233
|
-
match: () => true,
|
|
234
|
-
limit: 0,
|
|
235
|
-
});
|
|
236
|
-
expect(out).toEqual([]);
|
|
237
|
-
});
|
|
238
|
-
it("forwards maxDepth and shouldSkipDir to the walk", () => {
|
|
239
|
-
const root = makeTree({
|
|
240
|
-
"a.ts": "",
|
|
241
|
-
"skip/b.ts": "",
|
|
242
|
-
"sub/deep/c.ts": "",
|
|
243
|
-
});
|
|
244
|
-
const out = collectFiles(root, {
|
|
245
|
-
match: (entry) => entry.name.endsWith(".ts"),
|
|
246
|
-
maxDepth: 1,
|
|
247
|
-
shouldSkipDir: (entry) => entry.name === "skip",
|
|
248
|
-
});
|
|
249
|
-
// maxDepth: 1 excludes sub/deep/c.ts; shouldSkipDir excludes skip/b.ts.
|
|
250
|
-
expect(rel(root, out)).toEqual(["a.ts"]);
|
|
251
|
-
});
|
|
252
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as os from "os";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
import { execFileSync } from "child_process";
|
|
5
|
-
import { checkFrontendFileIntegration } from "./frontendIntegration.js";
|
|
6
|
-
/** Creates a temp git repo, writes the given files, and stages them so
|
|
7
|
-
* `git grep` (which only searches tracked files by default) can find them. */
|
|
8
|
-
function makeFixtureRepo(files) {
|
|
9
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "frontend-integration-"));
|
|
10
|
-
execFileSync("git", ["init", "-q"], { cwd: dir });
|
|
11
|
-
for (const [relPath, content] of Object.entries(files)) {
|
|
12
|
-
const abs = path.join(dir, relPath);
|
|
13
|
-
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
14
|
-
fs.writeFileSync(abs, content);
|
|
15
|
-
}
|
|
16
|
-
execFileSync("git", ["add", "-A"], { cwd: dir });
|
|
17
|
-
return dir;
|
|
18
|
-
}
|
|
19
|
-
/** Same as makeFixtureRepo but NOT a git repo, to exercise the manual-walk
|
|
20
|
-
* fallback (`git grep` fails outright when the directory isn't a repo). */
|
|
21
|
-
function makeNonGitFixture(files) {
|
|
22
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "frontend-integration-nogit-"));
|
|
23
|
-
for (const [relPath, content] of Object.entries(files)) {
|
|
24
|
-
const abs = path.join(dir, relPath);
|
|
25
|
-
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
26
|
-
fs.writeFileSync(abs, content);
|
|
27
|
-
}
|
|
28
|
-
return dir;
|
|
29
|
-
}
|
|
30
|
-
describe("checkFrontendFileIntegration", () => {
|
|
31
|
-
it("marks a component imported by a production file as integrated, listing the importer", () => {
|
|
32
|
-
const repo = makeFixtureRepo({
|
|
33
|
-
"src/components/Foo.tsx": `export function Foo() { return null; }\n`,
|
|
34
|
-
"src/components/Bar.tsx": `import { Foo } from './Foo';\nexport function Bar() { return Foo(); }\n`,
|
|
35
|
-
});
|
|
36
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Foo.tsx"]);
|
|
37
|
-
expect(result.integrated).toBe(true);
|
|
38
|
-
expect(result.reason).toBe("imported");
|
|
39
|
-
expect(result.importers).toEqual(["src/components/Bar.tsx"]);
|
|
40
|
-
});
|
|
41
|
-
it("marks a component imported ONLY by test files as unintegrated", () => {
|
|
42
|
-
const repo = makeFixtureRepo({
|
|
43
|
-
"src/components/Foo.tsx": `export function Foo() { return null; }\n`,
|
|
44
|
-
"src/components/Foo.test.tsx": `import { Foo } from './Foo';\ntest('renders', () => Foo());\n`,
|
|
45
|
-
"src/components/__tests__/Foo.spec.tsx": `import { Foo } from './Foo';\n`,
|
|
46
|
-
});
|
|
47
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Foo.tsx"]);
|
|
48
|
-
expect(result.integrated).toBe(false);
|
|
49
|
-
expect(result.reason).toBe("no-importers");
|
|
50
|
-
expect(result.importers).toEqual([]);
|
|
51
|
-
});
|
|
52
|
-
it("short-circuits route/entrypoint files as integrated without grepping", () => {
|
|
53
|
-
const repo = makeFixtureRepo({
|
|
54
|
-
"src/pages/Dashboard.tsx": `export default function Dashboard() { return null; }\n`,
|
|
55
|
-
});
|
|
56
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/pages/Dashboard.tsx"]);
|
|
57
|
-
expect(result).toEqual({
|
|
58
|
-
file: "src/pages/Dashboard.tsx",
|
|
59
|
-
integrated: true,
|
|
60
|
-
importers: [],
|
|
61
|
-
reason: "route-or-entrypoint",
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
it("counts a re-export barrel as an importer", () => {
|
|
65
|
-
const repo = makeFixtureRepo({
|
|
66
|
-
"src/components/Foo.tsx": `export function Foo() { return null; }\n`,
|
|
67
|
-
"src/components/index.ts": `export { Foo } from './Foo';\n`,
|
|
68
|
-
});
|
|
69
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Foo.tsx"]);
|
|
70
|
-
expect(result.integrated).toBe(true);
|
|
71
|
-
expect(result.importers).toEqual(["src/components/index.ts"]);
|
|
72
|
-
});
|
|
73
|
-
it("counts an oversized single-line bundle referencing the module path as an importer, unread", () => {
|
|
74
|
-
// A minified >512KB one-line file. Node never opens candidates — git
|
|
75
|
-
// grep -l only returns the path — so file size cannot cause a large
|
|
76
|
-
// allocation or blow the subprocess buffer.
|
|
77
|
-
const hugeBundle = `var m=require("./components/Foo");` + "a".repeat(600 * 1024);
|
|
78
|
-
const repo = makeFixtureRepo({
|
|
79
|
-
"src/components/Foo.tsx": `export function Foo() { return null; }\n`,
|
|
80
|
-
"src/vendor/bundle.js": hugeBundle,
|
|
81
|
-
});
|
|
82
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Foo.tsx"]);
|
|
83
|
-
expect(result.integrated).toBe(true);
|
|
84
|
-
expect(result.importers).toEqual(["src/vendor/bundle.js"]);
|
|
85
|
-
});
|
|
86
|
-
it("resolves importers of an oversized changed file (the changed file is never read)", () => {
|
|
87
|
-
const hugeComponent = `export function Foo() { return null; }\n// ` + "b".repeat(600 * 1024) + `\n`;
|
|
88
|
-
const repo = makeFixtureRepo({
|
|
89
|
-
"src/components/Foo.tsx": hugeComponent,
|
|
90
|
-
"src/components/Bar.tsx": `import Foo from './Foo';\nexport function Bar() { return Foo(); }\n`,
|
|
91
|
-
});
|
|
92
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Foo.tsx"]);
|
|
93
|
-
expect(result.integrated).toBe(true);
|
|
94
|
-
expect(result.reason).toBe("imported");
|
|
95
|
-
expect(result.importers).toEqual(["src/components/Bar.tsx"]);
|
|
96
|
-
});
|
|
97
|
-
it("catches multiline imports (the from-line carries the path signature)", () => {
|
|
98
|
-
const repo = makeFixtureRepo({
|
|
99
|
-
"src/components/Foo.tsx": `export function Foo() { return null; }\n`,
|
|
100
|
-
"src/components/Bar.tsx": `import {\n Foo,\n} from "./Foo";\nexport function Bar() { return Foo(); }\n`,
|
|
101
|
-
});
|
|
102
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Foo.tsx"]);
|
|
103
|
-
expect(result.integrated).toBe(true);
|
|
104
|
-
expect(result.importers).toEqual(["src/components/Bar.tsx"]);
|
|
105
|
-
});
|
|
106
|
-
it("does not count bare-name mentions (comments, strings) as importers", () => {
|
|
107
|
-
// The precision property of the path-signature design: a dead component
|
|
108
|
-
// whose NAME appears in prose elsewhere must still read as unintegrated.
|
|
109
|
-
const repo = makeFixtureRepo({
|
|
110
|
-
"src/components/Foo.tsx": `export function Foo() { return null; }\n`,
|
|
111
|
-
"src/components/Other.tsx": `// TODO: consolidate with Foo someday\nconst label = "Foo";\nexport function Other() { return label; }\n`,
|
|
112
|
-
});
|
|
113
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Foo.tsx"]);
|
|
114
|
-
expect(result.integrated).toBe(false);
|
|
115
|
-
expect(result.reason).toBe("no-importers");
|
|
116
|
-
expect(result.importers).toEqual([]);
|
|
117
|
-
});
|
|
118
|
-
it("does not short-circuit components under a generic top-level app/ monorepo dir", () => {
|
|
119
|
-
// Regression: appsmith's repo root is `app/` — matching it as a framework
|
|
120
|
-
// route dir made the check inert for the whole repo (eval 28618761653).
|
|
121
|
-
const repo = makeFixtureRepo({
|
|
122
|
-
"app/client/src/components/Orphan.tsx": `export function Orphan() { return null; }\n`,
|
|
123
|
-
});
|
|
124
|
-
const [result] = checkFrontendFileIntegration(repo, ["app/client/src/components/Orphan.tsx"]);
|
|
125
|
-
expect(result.integrated).toBe(false);
|
|
126
|
-
expect(result.reason).toBe("no-importers");
|
|
127
|
-
});
|
|
128
|
-
it("still short-circuits Next.js App Router route files via their basenames", () => {
|
|
129
|
-
const repo = makeFixtureRepo({
|
|
130
|
-
"src/app/dashboard/page.tsx": `export default function Dashboard() { return null; }\n`,
|
|
131
|
-
});
|
|
132
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/app/dashboard/page.tsx"]);
|
|
133
|
-
expect(result.integrated).toBe(true);
|
|
134
|
-
expect(result.reason).toBe("route-or-entrypoint");
|
|
135
|
-
});
|
|
136
|
-
it("fails open (scan-error, integrated: true) when the changed file does not exist on disk", () => {
|
|
137
|
-
const repo = makeFixtureRepo({
|
|
138
|
-
"src/components/Bar.tsx": `export function Bar() { return null; }\n`,
|
|
139
|
-
});
|
|
140
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Deleted.tsx"]);
|
|
141
|
-
expect(result).toEqual({
|
|
142
|
-
file: "src/components/Deleted.tsx",
|
|
143
|
-
integrated: true,
|
|
144
|
-
importers: [],
|
|
145
|
-
reason: "scan-error",
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
it("finds an importer of a .d.ts declaration file", () => {
|
|
149
|
-
// Regression: path.extname("Foo.d.ts") is ".ts", so a naive basename/ext
|
|
150
|
-
// split would build a signature for "Foo.d" instead of "Foo" and never
|
|
151
|
-
// match a real `from "./Foo"` import.
|
|
152
|
-
const repo = makeFixtureRepo({
|
|
153
|
-
"src/components/Foo.d.ts": `export declare function Foo(): null;\n`,
|
|
154
|
-
"src/components/Bar.tsx": `import { Foo } from './Foo';\nexport function Bar() { return Foo(); }\n`,
|
|
155
|
-
});
|
|
156
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Foo.d.ts"]);
|
|
157
|
-
expect(result.integrated).toBe(true);
|
|
158
|
-
expect(result.reason).toBe("imported");
|
|
159
|
-
expect(result.importers).toEqual(["src/components/Bar.tsx"]);
|
|
160
|
-
});
|
|
161
|
-
describe("basename short-circuit precision", () => {
|
|
162
|
-
it("does NOT short-circuit a folder-per-component index.tsx as a route/entrypoint", () => {
|
|
163
|
-
// Regression: matching "index"/"main"/"app" basenames anywhere in the
|
|
164
|
-
// tree (not just at the root or src/) would short-circuit the common
|
|
165
|
-
// React folder-per-component convention (Button/index.tsx) as always
|
|
166
|
-
// integrated, defeating the check for exactly the files it targets.
|
|
167
|
-
const repo = makeFixtureRepo({
|
|
168
|
-
"src/components/Button/index.tsx": `export function Button() { return null; }\n`,
|
|
169
|
-
});
|
|
170
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Button/index.tsx"]);
|
|
171
|
-
expect(result.integrated).toBe(false);
|
|
172
|
-
expect(result.reason).toBe("no-importers");
|
|
173
|
-
});
|
|
174
|
-
it("still short-circuits a root-level src/index.tsx entrypoint", () => {
|
|
175
|
-
const repo = makeFixtureRepo({
|
|
176
|
-
"src/index.tsx": `export default function App() { return null; }\n`,
|
|
177
|
-
});
|
|
178
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/index.tsx"]);
|
|
179
|
-
expect(result).toEqual({
|
|
180
|
-
file: "src/index.tsx",
|
|
181
|
-
integrated: true,
|
|
182
|
-
importers: [],
|
|
183
|
-
reason: "route-or-entrypoint",
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
it("does NOT short-circuit a page.tsx outside an app/ segment", () => {
|
|
187
|
-
// "page"/"layout" are App Router-specific; outside an app/ segment they
|
|
188
|
-
// are ordinary component filenames with no framework meaning.
|
|
189
|
-
const repo = makeFixtureRepo({
|
|
190
|
-
"src/components/page.tsx": `export function Page() { return null; }\n`,
|
|
191
|
-
});
|
|
192
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/page.tsx"]);
|
|
193
|
-
expect(result.integrated).toBe(false);
|
|
194
|
-
expect(result.reason).toBe("no-importers");
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
describe("manual-walk fallback (non-git repos)", () => {
|
|
198
|
-
it("falls back to a manual walk and finds an importer when the directory isn't a git repo", () => {
|
|
199
|
-
const repo = makeNonGitFixture({
|
|
200
|
-
"src/components/Foo.tsx": `export function Foo() { return null; }\n`,
|
|
201
|
-
"src/components/Bar.tsx": `import { Foo } from './Foo';\nexport function Bar() { return Foo(); }\n`,
|
|
202
|
-
});
|
|
203
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Foo.tsx"]);
|
|
204
|
-
expect(result.integrated).toBe(true);
|
|
205
|
-
expect(result.reason).toBe("imported");
|
|
206
|
-
expect(result.importers).toEqual(["src/components/Bar.tsx"]);
|
|
207
|
-
});
|
|
208
|
-
it("fails open (scan-error) when the fallback walk is truncated before finding an importer", () => {
|
|
209
|
-
// Regression: the module doc promises fail-open on any scan error/
|
|
210
|
-
// uncertainty, but a truncated manual walk used to just return its
|
|
211
|
-
// partial (empty) result set, reporting a confident "no-importers"
|
|
212
|
-
// even though an importer could exist past the file cap.
|
|
213
|
-
const files = {
|
|
214
|
-
"src/components/Foo.tsx": `export function Foo() { return null; }\n`,
|
|
215
|
-
};
|
|
216
|
-
for (let i = 0; i < 3005; i++) {
|
|
217
|
-
files[`src/generated/File${i}.tsx`] = `export const noop${i} = 0;\n`;
|
|
218
|
-
}
|
|
219
|
-
const repo = makeNonGitFixture(files);
|
|
220
|
-
const [result] = checkFrontendFileIntegration(repo, ["src/components/Foo.tsx"]);
|
|
221
|
-
expect(result).toEqual({
|
|
222
|
-
file: "src/components/Foo.tsx",
|
|
223
|
-
integrated: true,
|
|
224
|
-
importers: [],
|
|
225
|
-
reason: "scan-error",
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "@jest/globals";
|
|
2
|
-
import { extractChangedSelectors } from "./frontendSelectors.js";
|
|
3
|
-
describe("extractChangedSelectors", () => {
|
|
4
|
-
it("captures both the old (removed) and new (added) value of a renamed data-testid", () => {
|
|
5
|
-
const diff = [
|
|
6
|
-
"@@ -175,7 +175,7 @@",
|
|
7
|
-
'- dataTestId="confirmation-modal-confirm-button"',
|
|
8
|
-
'+ dataTestId="confirmation-modal-primary-button"',
|
|
9
|
-
].join("\n");
|
|
10
|
-
const sel = extractChangedSelectors(diff);
|
|
11
|
-
// dataTestId isn't a data-* attribute, but both are distinctive kebab class-ish tokens
|
|
12
|
-
expect(sel).toEqual(expect.arrayContaining([
|
|
13
|
-
"confirmation-modal-confirm-button",
|
|
14
|
-
"confirmation-modal-primary-button",
|
|
15
|
-
]));
|
|
16
|
-
});
|
|
17
|
-
it("captures data-testid attribute values", () => {
|
|
18
|
-
const diff = '+ <input data-testid="members-search-field" />\n- <input data-testid="members-search-input" />';
|
|
19
|
-
const sel = extractChangedSelectors(diff);
|
|
20
|
-
expect(sel).toEqual(expect.arrayContaining(["members-search-field", "members-search-input"]));
|
|
21
|
-
});
|
|
22
|
-
it("captures CSS class rename with BEM `--` and multi-segment kebab", () => {
|
|
23
|
-
const diff = [
|
|
24
|
-
'- <div className="t--table-filter-columns-dropdown">',
|
|
25
|
-
'+ <div className="t--tbl-filter-column-select">',
|
|
26
|
-
'+ <button className="ads-button--primary">',
|
|
27
|
-
].join("\n");
|
|
28
|
-
const sel = extractChangedSelectors(diff);
|
|
29
|
-
expect(sel).toEqual(expect.arrayContaining([
|
|
30
|
-
"t--table-filter-columns-dropdown",
|
|
31
|
-
"t--tbl-filter-column-select",
|
|
32
|
-
"ads-button--primary",
|
|
33
|
-
]));
|
|
34
|
-
});
|
|
35
|
-
it("captures the BEM base prefix of a dynamically-composed modifier class", () => {
|
|
36
|
-
const diff = [
|
|
37
|
-
"- className={clsx(ButtonClassName, className)}",
|
|
38
|
-
"+ className={clsx(ButtonClassName, kind && `ads-button--${kind}`, className)}",
|
|
39
|
-
].join("\n");
|
|
40
|
-
const sel = extractChangedSelectors(diff);
|
|
41
|
-
// no concrete `ads-button--primary` literal exists in the diff — capture the base prefix
|
|
42
|
-
expect(sel).toContain("ads-button--");
|
|
43
|
-
});
|
|
44
|
-
it("captures the BEM base from string-concatenation too, but leaves STATIC modifiers to the kebab pass", () => {
|
|
45
|
-
const concat = extractChangedSelectors('+ const cls = "ads-button--" + kind;');
|
|
46
|
-
expect(concat).toContain("ads-button--");
|
|
47
|
-
// a fully static modifier is captured whole (not as a bare `foo-bar--` prefix)
|
|
48
|
-
const staticMod = extractChangedSelectors('+ <div className="t--tbl-filter-column-select" />');
|
|
49
|
-
expect(staticMod).toContain("t--tbl-filter-column-select");
|
|
50
|
-
expect(staticMod).not.toContain("t--tbl-filter-column-select--");
|
|
51
|
-
});
|
|
52
|
-
it("captures getByTestId arguments", () => {
|
|
53
|
-
const diff = "+ const btn = page.getByTestId('workflow-settings-modal');";
|
|
54
|
-
expect(extractChangedSelectors(diff)).toContain("workflow-settings-modal");
|
|
55
|
-
});
|
|
56
|
-
it("drops single-hyphen styling utilities and short/generic tokens", () => {
|
|
57
|
-
const diff = [
|
|
58
|
-
'+ <div className="flex-start space-between text-align" />',
|
|
59
|
-
'+ const gap = "sans-serif";',
|
|
60
|
-
].join("\n");
|
|
61
|
-
expect(extractChangedSelectors(diff)).toEqual([]);
|
|
62
|
-
});
|
|
63
|
-
it("drops multi-hyphen utility / design-token classes (numeric scale + utility roots)", () => {
|
|
64
|
-
const diff = [
|
|
65
|
-
'- <div className="text-gray-500 bg-blue-600 flex-row-reverse" />',
|
|
66
|
-
'+ <div className="text-gray-600 bg-blue-700 grid-cols-3" />',
|
|
67
|
-
].join("\n");
|
|
68
|
-
const sel = extractChangedSelectors(diff);
|
|
69
|
-
for (const util of ["text-gray-500", "text-gray-600", "bg-blue-600", "bg-blue-700", "flex-row-reverse", "grid-cols-3"]) {
|
|
70
|
-
expect(sel).not.toContain(util);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
it("keeps semantic multi-hyphen identifiers and BEM classes (not utility)", () => {
|
|
74
|
-
const diff = [
|
|
75
|
-
'+ <div className="confirmation-modal-confirm-button user-profile-header" />',
|
|
76
|
-
'+ <div className="t--tbl-filter-column-select" />',
|
|
77
|
-
].join("\n");
|
|
78
|
-
const sel = extractChangedSelectors(diff);
|
|
79
|
-
expect(sel).toEqual(expect.arrayContaining([
|
|
80
|
-
"confirmation-modal-confirm-button",
|
|
81
|
-
"user-profile-header",
|
|
82
|
-
"t--tbl-filter-column-select",
|
|
83
|
-
]));
|
|
84
|
-
});
|
|
85
|
-
it("ignores context (unchanged) lines and file headers", () => {
|
|
86
|
-
const diff = [
|
|
87
|
-
"--- a/src/Component.tsx",
|
|
88
|
-
"+++ b/src/Component.tsx",
|
|
89
|
-
' <div className="t--unchanged-context-selector" />', // context line (leading space)
|
|
90
|
-
'+ <div className="t--added-real-selector" />',
|
|
91
|
-
].join("\n");
|
|
92
|
-
const sel = extractChangedSelectors(diff);
|
|
93
|
-
expect(sel).toContain("t--added-real-selector");
|
|
94
|
-
expect(sel).not.toContain("t--unchanged-context-selector");
|
|
95
|
-
expect(sel).not.toContain("Component"); // +++/--- headers ignored
|
|
96
|
-
});
|
|
97
|
-
it("does not leak a partial prefix from an interpolated template-literal attribute value", () => {
|
|
98
|
-
const diff = [
|
|
99
|
-
"+ <input data-testid={`members-search-${id}`} />",
|
|
100
|
-
"+ <button data-testid={`confirmation-modal-${kind}`} />",
|
|
101
|
-
].join("\n");
|
|
102
|
-
const sel = extractChangedSelectors(diff);
|
|
103
|
-
expect(sel).not.toContain("members-search-");
|
|
104
|
-
expect(sel).not.toContain("confirmation-modal-");
|
|
105
|
-
});
|
|
106
|
-
it("does not capture non-literal expression attribute values (data-testid={someVar})", () => {
|
|
107
|
-
const diff = [
|
|
108
|
-
"+ <div data-testid={someVariableName} />",
|
|
109
|
-
"+ <div data-testid={'members-search-field'} />",
|
|
110
|
-
].join("\n");
|
|
111
|
-
const sel = extractChangedSelectors(diff);
|
|
112
|
-
expect(sel).not.toContain("someVariableName"); // variable name, not a literal
|
|
113
|
-
expect(sel).toContain("members-search-field"); // quoted literal still captured
|
|
114
|
-
});
|
|
115
|
-
it("returns [] for an empty diff", () => {
|
|
116
|
-
expect(extractChangedSelectors("")).toEqual([]);
|
|
117
|
-
});
|
|
118
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|