@skyramp/mcp 0.3.8 → 0.4.0-rc.1
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 -1
- package/build/commands/commandLibrary.js +3 -3
- package/build/commands/recommendTestsAndExecuteCommand.d.ts +1 -1
- package/build/commands/recommendTestsAndExecuteCommand.js +35 -20
- package/build/commands/testThisEndpointCommand.js +35 -19
- package/build/index.js +9 -3
- package/build/playwright/blueprintDigest.d.ts +15 -0
- package/build/playwright/blueprintDigest.js +152 -0
- package/build/playwright/blueprintDigestStore.d.ts +31 -0
- package/build/playwright/blueprintDigestStore.js +117 -0
- package/build/playwright/registerPlaywrightTools.js +60 -12
- package/build/playwright/traceRecordingPrompt.js +8 -7
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +9 -8
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +24 -2
- package/build/prompts/promptAssets.d.ts +20 -0
- package/build/prompts/promptAssets.js +55 -0
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +19 -5
- package/build/prompts/test-maintenance/actionsInstructions.d.ts +4 -0
- package/build/prompts/test-maintenance/actionsInstructions.js +14 -2
- package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +0 -10
- package/build/prompts/test-maintenance/drift-analysis-prompt.js +2 -11
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +8 -4
- package/build/prompts/test-recommendation/diffExecutionPlan.d.ts +5 -22
- package/build/prompts/test-recommendation/diffExecutionPlan.js +37 -465
- package/build/prompts/test-recommendation/recommendationSections.d.ts +7 -17
- package/build/prompts/test-recommendation/recommendationSections.js +67 -309
- package/build/prompts/test-recommendation/recommendationShared.d.ts +19 -47
- package/build/prompts/test-recommendation/recommendationShared.js +49 -155
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.d.ts +0 -5
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +10 -153
- package/build/prompts/test-recommendation/test-recommendation-prompt.d.ts +2 -29
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +32 -457
- package/build/prompts/testbot/planDeclarations.d.ts +6 -0
- package/build/prompts/testbot/planDeclarations.js +9 -0
- package/build/prompts/testbot/testbot-prompts.d.ts +8 -0
- package/build/prompts/testbot/testbot-prompts.js +256 -381
- package/build/recommendation/answers.d.ts +35 -0
- package/build/recommendation/answers.js +96 -0
- package/build/recommendation/registerPlan.d.ts +49 -0
- package/build/recommendation/registerPlan.js +117 -0
- package/build/recommendation/runVerifiers.d.ts +10 -0
- package/build/recommendation/runVerifiers.js +49 -0
- package/build/recommendation/subjectStep.d.ts +42 -0
- package/build/recommendation/subjectStep.js +86 -0
- package/build/recommendation/types.d.ts +163 -0
- package/build/recommendation/types.js +20 -0
- package/build/recommendation/verifierContracts.d.ts +382 -0
- package/build/recommendation/verifierContracts.js +263 -0
- package/build/recommendation/verifiers/changedFile.d.ts +2 -0
- package/build/recommendation/verifiers/changedFile.js +82 -0
- package/build/recommendation/verifiers/citedPath.d.ts +12 -0
- package/build/recommendation/verifiers/citedPath.js +35 -0
- package/build/recommendation/verifiers/coverage.d.ts +7 -0
- package/build/recommendation/verifiers/coverage.js +617 -0
- package/build/recommendation/verifiers/deliveredMatchesPlan.d.ts +11 -0
- package/build/recommendation/verifiers/deliveredMatchesPlan.js +33 -0
- package/build/recommendation/verifiers/endpointGrounded.d.ts +17 -0
- package/build/recommendation/verifiers/endpointGrounded.js +128 -0
- package/build/recommendation/verifiers/existingCoverage.d.ts +6 -0
- package/build/recommendation/verifiers/existingCoverage.js +51 -0
- package/build/recommendation/verifiers/expectedOutcome.d.ts +31 -0
- package/build/recommendation/verifiers/expectedOutcome.js +105 -0
- package/build/recommendation/verifiers/removedElementGuarded.d.ts +2 -0
- package/build/recommendation/verifiers/removedElementGuarded.js +57 -0
- package/build/recommendation/verifiers/reportedCategory.d.ts +26 -0
- package/build/recommendation/verifiers/reportedCategory.js +84 -0
- package/build/recommendation/verifiers/screenRoute.d.ts +10 -0
- package/build/recommendation/verifiers/screenRoute.js +118 -0
- package/build/recommendation/verifiers/statedDifference.d.ts +6 -0
- package/build/recommendation/verifiers/statedDifference.js +140 -0
- package/build/recommendation/verifiers/uiElementGrounded.d.ts +7 -0
- package/build/recommendation/verifiers/uiElementGrounded.js +318 -0
- package/build/resources/analysisResources.js +1 -114
- package/build/resources/testbotResource.js +23 -13
- package/build/services/ModularizationService.js +2 -1
- package/build/services/TestDiscoveryService.d.ts +3 -72
- package/build/services/TestDiscoveryService.js +10 -303
- package/build/services/containerEnv.d.ts +1 -1
- package/build/services/containerEnv.js +12 -0
- package/build/skills/fixTestImportErrorsSkill.d.ts +13 -0
- package/build/skills/fixTestImportErrorsSkill.js +20 -0
- package/build/toolNames.d.ts +1 -0
- package/build/toolNames.js +1 -0
- package/build/tools/code-refactor/enhanceAssertionsTool.js +3 -3
- package/build/tools/code-refactor/modularizationTool.js +2 -1
- package/build/tools/executeSkyrampTestTool.d.ts +80 -0
- package/build/tools/executeSkyrampTestTool.js +246 -19
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +6 -0
- package/build/tools/generate-tests/generateContractRestTool.js +3 -3
- package/build/tools/generate-tests/planGuard.d.ts +2 -2
- package/build/tools/generate-tests/planGuard.js +78 -18
- package/build/tools/one-click/oneClickTool.d.ts +0 -1
- package/build/tools/one-click/oneClickTool.js +0 -5
- package/build/tools/submitReportTool.d.ts +48 -42
- package/build/tools/submitReportTool.js +576 -193
- package/build/tools/test-management/actionsTool.js +72 -4
- package/build/tools/test-management/analyzeChangesTool.d.ts +144 -48
- package/build/tools/test-management/analyzeChangesTool.js +212 -1219
- package/build/tools/test-management/analyzeTestHealthTool.js +13 -24
- package/build/tools/test-management/index.d.ts +1 -0
- package/build/tools/test-management/index.js +1 -0
- package/build/tools/test-management/registerTestPlanTool.d.ts +795 -172
- package/build/tools/test-management/registerTestPlanTool.js +609 -542
- package/build/tools/test-management/resolveScreenTool.d.ts +75 -0
- package/build/tools/test-management/resolveScreenTool.js +289 -0
- package/build/types/BlueprintDigest.d.ts +34 -0
- package/build/types/BlueprintDigest.js +1 -0
- package/build/types/RepositoryAnalysis.d.ts +20 -1559
- package/build/types/RepositoryAnalysis.js +2 -58
- package/build/types/StepMethod.d.ts +40 -0
- package/build/types/StepMethod.js +77 -0
- package/build/types/TestAnalysis.d.ts +12 -0
- package/build/types/TestExecution.d.ts +4 -0
- package/build/types/TestRecommendation.d.ts +24 -24
- package/build/types/TestRecommendation.js +91 -89
- package/build/types/TestbotPromptOptions.d.ts +0 -4
- package/build/types/TestbotReport.d.ts +64 -2
- package/build/utils/AnalysisStateManager.d.ts +79 -113
- package/build/utils/AnalysisStateManager.js +147 -57
- package/build/utils/assertion-verify/api-shared-lints.js +1 -1
- package/build/utils/assertion-verify/metrics.js +85 -36
- package/build/utils/assertion-verify/ui-lints.d.ts +0 -5
- package/build/utils/assertion-verify/ui-lints.js +32 -0
- package/build/utils/branchDiff.d.ts +63 -31
- package/build/utils/branchDiff.js +242 -94
- package/build/utils/containedPath.d.ts +18 -0
- package/build/utils/containedPath.js +73 -0
- package/build/utils/dartRouteExtractor.d.ts +18 -34
- package/build/utils/dartRouteExtractor.js +101 -173
- package/build/utils/featureFlags.d.ts +12 -0
- package/build/utils/featureFlags.js +14 -0
- package/build/utils/frontendSelectors.d.ts +48 -27
- package/build/utils/frontendSelectors.js +241 -80
- package/build/utils/pathMatching.d.ts +2 -4
- package/build/utils/pathMatching.js +2 -4
- package/build/utils/planMatchKeys.d.ts +38 -47
- package/build/utils/planMatchKeys.js +143 -81
- package/build/utils/rebaselineSnapshots.d.ts +24 -0
- package/build/utils/rebaselineSnapshots.js +65 -0
- package/build/utils/removedUiElements.d.ts +22 -0
- package/build/utils/removedUiElements.js +106 -0
- package/build/utils/reportVerification.d.ts +2 -6
- package/build/utils/reportVerification.js +61 -2
- package/build/utils/screenRoutes.d.ts +66 -0
- package/build/utils/screenRoutes.js +727 -0
- package/build/utils/sourceRouteExtractor.js +320 -112
- package/build/utils/testFileClassification.d.ts +11 -2
- package/build/utils/testFileClassification.js +44 -2
- package/build/utils/testFixtures.d.ts +5 -0
- package/build/utils/testFixtures.js +13 -0
- package/build/utils/utils.d.ts +0 -1
- package/build/utils/utils.js +0 -11
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +12 -12
- package/node_modules/playwright/lib/mcp/skyramp/assertHiddenTool.js +56 -0
- package/node_modules/playwright/lib/mcp/skyramp/assertTool.js +2 -1
- package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +10 -0
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +4 -1
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +160 -1
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +4 -2
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +1 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderSignalProcessor.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder.js +5 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.-Id052Lr.js → index.B7KbSQcC.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +1 -1
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderSignalProcessor.ts +7 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder.ts +6 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +4 -3
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/plugin.json +6 -0
- package/plugin/prompts/declaring-a-plan.md +20 -0
- package/plugin/prompts/generate-tests/context-fetching.md +4 -0
- package/plugin/prompts/generate-tests/execution-plan.md +63 -0
- package/plugin/prompts/generate-tests/generation.md +108 -0
- package/plugin/prompts/generate-tests/path-parameters.md +1 -0
- package/plugin/prompts/generate-tests/reasoning-protocol.md +17 -0
- package/plugin/prompts/generate-tests/tool-workflow-variants.md +61 -0
- package/plugin/prompts/generate-tests/tool-workflows.md +65 -0
- package/plugin/prompts/plan-tests.md +42 -0
- package/plugin/prompts/testbot-task1.md +82 -0
- package/plugin/skills/fix-test-import-errors/SKILL.md +98 -0
- package/build/prompts/test-recommendation/analysisOutputPrompt.d.ts +0 -84
- package/build/prompts/test-recommendation/analysisOutputPrompt.js +0 -369
- package/build/prompts/test-recommendation/fullRepoCatalog.d.ts +0 -7
- package/build/prompts/test-recommendation/fullRepoCatalog.js +0 -283
- package/build/prompts/test-recommendation/scopeAssessment.d.ts +0 -81
- package/build/prompts/test-recommendation/scopeAssessment.js +0 -359
- package/build/recommendation/budgeters/diversityBalancedBudgeter.d.ts +0 -7
- package/build/recommendation/budgeters/diversityBalancedBudgeter.js +0 -105
- package/build/recommendation/budgeters/fixedNBudgeter.d.ts +0 -7
- package/build/recommendation/budgeters/fixedNBudgeter.js +0 -11
- package/build/recommendation/budgeters/shared.d.ts +0 -32
- package/build/recommendation/budgeters/shared.js +0 -246
- package/build/recommendation/discriminators.d.ts +0 -37
- package/build/recommendation/discriminators.js +0 -379
- package/build/recommendation/diversity.d.ts +0 -47
- package/build/recommendation/diversity.js +0 -101
- package/build/recommendation/planRanker.d.ts +0 -65
- package/build/recommendation/planRanker.js +0 -83
- package/build/recommendation/testFixtures.d.ts +0 -25
- package/build/recommendation/testFixtures.js +0 -45
- package/build/types/FrontendIntegration.d.ts +0 -28
- package/build/types/FrontendIntegration.js +0 -22
- package/build/types/Recommendation.d.ts +0 -146
- package/build/types/Recommendation.js +0 -74
- package/build/utils/changedRoutes.d.ts +0 -29
- package/build/utils/changedRoutes.js +0 -87
- package/build/utils/frontendIntegration.d.ts +0 -9
- package/build/utils/frontendIntegration.js +0 -243
- package/build/utils/importerHop.d.ts +0 -135
- package/build/utils/importerHop.js +0 -489
- package/build/utils/pathAffinityClassification.d.ts +0 -49
- package/build/utils/pathAffinityClassification.js +0 -180
- package/build/utils/pythonMountPrefixes.d.ts +0 -25
- package/build/utils/pythonMountPrefixes.js +0 -347
- package/build/utils/repoScanner.d.ts +0 -34
- package/build/utils/repoScanner.js +0 -300
- package/build/utils/routeParsers.d.ts +0 -95
- package/build/utils/routeParsers.js +0 -951
- package/build/utils/scenarioDrafting.d.ts +0 -92
- package/build/utils/scenarioDrafting.js +0 -951
- package/build/utils/subjectEndpoints.d.ts +0 -19
- package/build/utils/subjectEndpoints.js +0 -98
- package/build/utils/uiPageEnumerator.d.ts +0 -172
- package/build/utils/uiPageEnumerator.js +0 -474
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fixErrorsInstruction } from "../skills/fixTestImportErrorsSkill.js";
|
|
1
2
|
import { TestType } from "../types/TestTypes.js";
|
|
2
3
|
import { getModularizationPrompt as getModularizationPromptForUI } from "../prompts/modularization/ui-test-modularization.js";
|
|
3
4
|
import { getModularizationPrompt as getModularizationPromptForIntegration } from "../prompts/modularization/integration-test-modularization.js";
|
|
@@ -47,7 +48,7 @@ The test file \`${params.testFile}\` will remain unchanged. No further action is
|
|
|
47
48
|
1. Read the test file using the 'read_file' tool
|
|
48
49
|
2. Create the modularized version following the instructions above
|
|
49
50
|
3. Use the 'write' tool (NOT search_replace) to save the complete modularized code to the file
|
|
50
|
-
4. After modularization is complete,
|
|
51
|
+
4. After modularization is complete, if there are any errors, ${fixErrorsInstruction()}`,
|
|
51
52
|
},
|
|
52
53
|
],
|
|
53
54
|
};
|
|
@@ -4,41 +4,6 @@ interface TestDiscoveryResult {
|
|
|
4
4
|
/** Paths of external files that were deemed relevant to the PR (score > 0). */
|
|
5
5
|
relevantExternalTestPaths: string[];
|
|
6
6
|
}
|
|
7
|
-
export interface TestDiscoveryOptions {
|
|
8
|
-
/**
|
|
9
|
-
* Resource names derived from endpoints in changed files (e.g. ["orders", "products"]).
|
|
10
|
-
* - Non-empty array: external files partitioned by relevance; only relevant files
|
|
11
|
-
* get full endpoint extraction. May be the sentinel `["unknown"]` when endpoints
|
|
12
|
-
* exist but resource names are unresolvable — most files score 0 (low-relevance)
|
|
13
|
-
* and are excluded; only the few that happen to match the sentinel are kept.
|
|
14
|
-
* - Empty array `[]`: PR mode with no endpoints from diff or scanner — API/integration
|
|
15
|
-
* external tests are excluded entirely; UI tests may still be promoted via changedFrontendFiles.
|
|
16
|
-
* - `undefined`: full-repo mode — external tests capped at MAX_EXTERNAL_FULL_REPO.
|
|
17
|
-
*/
|
|
18
|
-
changedResources?: string[];
|
|
19
|
-
/** Raw changed symbol names (e.g. ["DeploymentCreate"]). Content-grep fallback so an
|
|
20
|
-
* external test that references a changed type by name — but whose path/URL matches no
|
|
21
|
-
* changed resource (e.g. a schema unit test) — is still surfaced (SKYR-3924). */
|
|
22
|
-
changedSymbols?: string[];
|
|
23
|
-
/** True when `changedResources` are precise (derived from specific changed schema/model
|
|
24
|
-
* symbols, not the `["unknown"]` sentinel or a broad set). Lets the URL content-match
|
|
25
|
-
* bucket skip the MAX_CONTENT_PROMOTED cap — a direct HTTP call to the changed endpoint
|
|
26
|
-
* is high-confidence, and the specific resource set naturally bounds the match count. */
|
|
27
|
-
preciseResources?: boolean;
|
|
28
|
-
/** Repo-relative paths of the changed frontend (non-test) files — their presence is the
|
|
29
|
-
* frontend-change signal that drives UI-test promotion (Step 2). When present, promotion
|
|
30
|
-
* is scoped to tests whose name/path/imports are relevant to these files — a frontend PR
|
|
31
|
-
* must not pull in the whole monorepo's UI test suite (SKYR-3941). When absent, Step 2 is
|
|
32
|
-
* skipped (no UI promotion). */
|
|
33
|
-
changedFrontendFiles?: string[];
|
|
34
|
-
/** Selector literals (data-testid values, CSS class tokens) added or removed by the diff.
|
|
35
|
-
* UI-test promotion pass 3 ("selector-edge"): a page object / spec couples to a changed
|
|
36
|
-
* component by SELECTOR, not by filename or import, so passes 1-2 miss it. Any external
|
|
37
|
-
* test-owned file (including page objects, which are not *.test/*.spec) whose body
|
|
38
|
-
* references one of these literals is promoted. Both added and removed values are passed
|
|
39
|
-
* so a page object still holding a renamed selector's OLD value is caught. */
|
|
40
|
-
changedSelectors?: string[];
|
|
41
|
-
}
|
|
42
7
|
export declare class TestDiscoveryService {
|
|
43
8
|
private readonly EXCLUDED_DIRS;
|
|
44
9
|
private readonly SKYRAMP_MARKER;
|
|
@@ -50,44 +15,10 @@ export declare class TestDiscoveryService {
|
|
|
50
15
|
* Uses fast-glob for cross-platform file scanning, then classifies discovered files
|
|
51
16
|
* as Skyramp-generated tests, external tests, or not-a-test during processing.
|
|
52
17
|
*
|
|
53
|
-
* External
|
|
54
|
-
*
|
|
55
|
-
* - `[]` empty array (PR mode, scanner found no endpoints): skip external tests entirely
|
|
56
|
-
* rather than flooding context with irrelevant files.
|
|
57
|
-
* - `undefined` (full-repo mode, no diff): cap at MAX_EXTERNAL_FULL_REPO.
|
|
58
|
-
*/
|
|
59
|
-
discoverTests(testDir: string, options?: TestDiscoveryOptions): Promise<TestDiscoveryResult>;
|
|
60
|
-
/**
|
|
61
|
-
* Score an external test file's relevance to a set of changed resource names.
|
|
62
|
-
* Tokenises the last two segments of the file path (split by /, \, -, _, .)
|
|
63
|
-
* and counts overlapping tokens with the changed resources.
|
|
64
|
-
* Example: "test_orders_api.py" vs ["orders"] → score 1.
|
|
65
|
-
*/
|
|
66
|
-
private scoreRelevance;
|
|
67
|
-
/**
|
|
68
|
-
* Derive relevance tokens from the changed frontend files, for scoring UI tests in Step 2
|
|
69
|
-
* the same way scoreRelevance scores against changed API resources. For each file we emit:
|
|
70
|
-
* - the basename stem (e.g. "OrderSearch" → "ordersearch") — matches co-located tests like
|
|
71
|
-
* OrderSearch.test.tsx, the dominant convention;
|
|
72
|
-
* - its sub-tokens, split on delimiters AND camelCase boundaries ("OrderSearch" →
|
|
73
|
-
* "order","search") — matches hyphenated/underscored test names like order-search.spec.tsx;
|
|
74
|
-
* - the parent directory name — the signal when the basename is generic (orders/index.tsx).
|
|
75
|
-
* Generic tokens (index, components, utils, …) and tokens shorter than 3 chars are dropped so
|
|
76
|
-
* a shared dir or an index file doesn't match every UI test in the repo.
|
|
77
|
-
*/
|
|
78
|
-
private deriveFrontendResourceTokens;
|
|
79
|
-
private readonly MAX_CONTENT_PROMOTED;
|
|
80
|
-
private readonly MAX_UI_PROMOTED;
|
|
81
|
-
private readonly UI_IMPORT_MATCH_WEIGHT;
|
|
82
|
-
private readonly GENERIC_FRONTEND_TOKENS;
|
|
83
|
-
/**
|
|
84
|
-
* Partition external test files into relevant (score > 0) and low-relevance (score = 0).
|
|
85
|
-
* Two-pass: filename token overlap first (primary); content-based endpoint path scan
|
|
86
|
-
* as a capped fallback for tests misnamed relative to the endpoint they exercise
|
|
87
|
-
* (e.g. test_checkout_flow.py testing /api/orders scores 0 by name but matches in content).
|
|
88
|
-
* Content matches are capped at MAX_CONTENT_PROMOTED to bound token impact.
|
|
18
|
+
* External tests are capped at MAX_EXTERNAL_FULL_REPO: nothing narrows them by
|
|
19
|
+
* relevance any more.
|
|
89
20
|
*/
|
|
90
|
-
|
|
21
|
+
discoverTests(testDir: string): Promise<TestDiscoveryResult>;
|
|
91
22
|
/**
|
|
92
23
|
* Process test files in parallel batches with concurrency control
|
|
93
24
|
* @param isExternal When true, uses external test metadata extraction
|
|
@@ -3,7 +3,6 @@ import * as path from "path";
|
|
|
3
3
|
import { logger } from "../utils/logger.js";
|
|
4
4
|
import { TestSource } from "../types/TestAnalysis.js";
|
|
5
5
|
import { TestType } from "../types/TestTypes.js";
|
|
6
|
-
import { buildPathSignatures } from "../utils/pathSignatures.js";
|
|
7
6
|
import fg from "fast-glob";
|
|
8
7
|
import { isDiscoveredTestFile } from "../utils/testFileClassification.js";
|
|
9
8
|
export class TestDiscoveryService {
|
|
@@ -40,13 +39,10 @@ export class TestDiscoveryService {
|
|
|
40
39
|
* Uses fast-glob for cross-platform file scanning, then classifies discovered files
|
|
41
40
|
* as Skyramp-generated tests, external tests, or not-a-test during processing.
|
|
42
41
|
*
|
|
43
|
-
* External
|
|
44
|
-
*
|
|
45
|
-
* - `[]` empty array (PR mode, scanner found no endpoints): skip external tests entirely
|
|
46
|
-
* rather than flooding context with irrelevant files.
|
|
47
|
-
* - `undefined` (full-repo mode, no diff): cap at MAX_EXTERNAL_FULL_REPO.
|
|
42
|
+
* External tests are capped at MAX_EXTERNAL_FULL_REPO: nothing narrows them by
|
|
43
|
+
* relevance any more.
|
|
48
44
|
*/
|
|
49
|
-
async discoverTests(testDir
|
|
45
|
+
async discoverTests(testDir) {
|
|
50
46
|
logger.info(`Starting test discovery in: ${testDir}`);
|
|
51
47
|
const stats = fs.statSync(testDir, { throwIfNoEntry: false });
|
|
52
48
|
if (!stats)
|
|
@@ -59,144 +55,13 @@ export class TestDiscoveryService {
|
|
|
59
55
|
// Process Skyramp tests (content already cached from classification)
|
|
60
56
|
const skyrampTests = await this.processFilesInBatches(classified.skyramp, false, classified.contentCache);
|
|
61
57
|
skyrampTests.forEach(t => { t.source = TestSource.Skyramp; });
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
// and get name-only entries, so external coverage is preserved without context flood.
|
|
70
|
-
//
|
|
71
|
-
// PR mode + truly no endpoints (changedResources is empty array []):
|
|
72
|
-
// Diff contained no endpoints at all (new, modified, or removed) — skip external
|
|
73
|
-
// tests entirely rather than flooding the prompt with hundreds of irrelevant files.
|
|
74
|
-
//
|
|
75
|
-
// Full-repo mode (changedResources is undefined):
|
|
76
|
-
// No diff context — all external files treated as potentially relevant.
|
|
77
|
-
// Cap at MAX_EXTERNAL_FULL_REPO to avoid reading hundreds of files.
|
|
78
|
-
const { changedResources, changedSymbols, preciseResources, changedFrontendFiles, changedSelectors } = options;
|
|
79
|
-
let relevantExternal;
|
|
80
|
-
// Includes .e2e.ts — Playwright/Cypress tests that navigate pages. These get
|
|
81
|
-
// testType="e2e" from path detection but are never named *.test.ts or *.spec.ts
|
|
82
|
-
// so without explicit promotion they'd be excluded from stateData.existingTests,
|
|
83
|
-
// making the server-side symbol grep unable to find them.
|
|
84
|
-
const UI_TEST_EXT = /\.(test|spec|e2e)\.(tsx|jsx|ts|js)$/;
|
|
85
|
-
const SEC_EXT = /\.(snap|ambr|txt|yml|yaml)$/;
|
|
86
|
-
// Step 1 — API relevance partitioning (driven by changedResources).
|
|
87
|
-
// Two-pass: filename token overlap first, then content-based endpoint path scan
|
|
88
|
-
// for tests that are misnamed relative to the endpoint they exercise.
|
|
89
|
-
if (changedResources?.length) {
|
|
90
|
-
({ relevant: relevantExternal } =
|
|
91
|
-
this.partitionByRelevance(classified.external, changedResources, classified.contentCache, changedSymbols, preciseResources));
|
|
92
|
-
}
|
|
93
|
-
else if (changedResources !== undefined) {
|
|
94
|
-
relevantExternal = [];
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
relevantExternal = classified.external.slice(0, this.MAX_EXTERNAL_FULL_REPO);
|
|
98
|
-
}
|
|
99
|
-
// Step 2 — UI test promotion, driven by the presence of changed frontend files and
|
|
100
|
-
// independent of Step 1. Scoped by relevance to those files, mirroring Step 1's API
|
|
101
|
-
// discipline: a frontend PR must not pull in the whole monorepo's UI test suite (SKYR-3941).
|
|
102
|
-
// Absent a changed-frontend-file list, Step 2 is skipped (no UI promotion).
|
|
103
|
-
if (changedFrontendFiles?.length) {
|
|
104
|
-
const alreadyRelevant = new Set(relevantExternal);
|
|
105
|
-
const uiCandidates = classified.external.filter(f => UI_TEST_EXT.test(f) && !alreadyRelevant.has(f));
|
|
106
|
-
// Pass 1 — filename/path relevance: tokens derived from the changed frontend files.
|
|
107
|
-
const feTokens = this.deriveFrontendResourceTokens(changedFrontendFiles);
|
|
108
|
-
// Pass 2 — import-edge relevance: module-path signatures of the changed files, matched
|
|
109
|
-
// against test bodies so a test that imports a changed component (from './PriceLabel')
|
|
110
|
-
// is caught even when its name shares no token — the recall case a filename-only filter
|
|
111
|
-
// drops (a checkout test that renders a changed child component). Reuses the same
|
|
112
|
-
// buildPathSignatures used by frontendIntegration/importerHop; this is Step 2's analog
|
|
113
|
-
// of Step 1's content-scan fallback. Skip generic basenames (index/app/…): their
|
|
114
|
-
// signature (`/index'`) would match every barrel import — those are already covered by
|
|
115
|
-
// pass 1's parent-directory token.
|
|
116
|
-
const importSignatures = changedFrontendFiles
|
|
117
|
-
.filter(f => {
|
|
118
|
-
const stem = path.basename(f).replace(/\.[^.]+$/, "").toLowerCase();
|
|
119
|
-
return stem.length >= 3 && !this.GENERIC_FRONTEND_TOKENS.has(stem);
|
|
120
|
-
})
|
|
121
|
-
.flatMap(f => buildPathSignatures(f));
|
|
122
|
-
let promoted;
|
|
123
|
-
if (feTokens.length === 0 && importSignatures.length === 0) {
|
|
124
|
-
// No relevance signal is derivable — every changed frontend file is a generic
|
|
125
|
-
// entrypoint (index.tsx / app.tsx / …) whose tokens are filtered out, and none
|
|
126
|
-
// yields an import signature. A root/entrypoint change is broadly impactful, so
|
|
127
|
-
// fall back to promoting all UI candidates rather than degenerate to zero — still
|
|
128
|
-
// bounded by MAX_UI_PROMOTED so it can't re-flood a large monorepo's suite.
|
|
129
|
-
promoted = uiCandidates.slice(0, this.MAX_UI_PROMOTED);
|
|
130
|
-
const dropped = uiCandidates.length - promoted.length;
|
|
131
|
-
logger.info(`UI test promotion: no relevance signal from generic-only frontend changes — capped promote-all fallback (${promoted.length}/${uiCandidates.length} promoted${dropped > 0 ? `, ${dropped} dropped by MAX_UI_PROMOTED` : ""})`);
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
let scored = uiCandidates
|
|
135
|
-
.map(f => {
|
|
136
|
-
const nameScore = this.scoreRelevance(f, feTokens);
|
|
137
|
-
const content = classified.contentCache.get(f) ?? "";
|
|
138
|
-
const importsChanged = content.length > 0 && importSignatures.some(sig => content.includes(sig));
|
|
139
|
-
return { f, score: nameScore + (importsChanged ? this.UI_IMPORT_MATCH_WEIGHT : 0) };
|
|
140
|
-
})
|
|
141
|
-
.filter(x => x.score > 0)
|
|
142
|
-
.sort((a, b) => b.score - a.score)
|
|
143
|
-
.map(x => x.f);
|
|
144
|
-
if (scored.length > this.MAX_UI_PROMOTED) {
|
|
145
|
-
logger.info(`UI test promotion: ${scored.length} tests matched ${changedFrontendFiles.length} changed frontend file(s); capping to ${this.MAX_UI_PROMOTED} highest-scoring (SKYR-3941 backstop)`);
|
|
146
|
-
scored = scored.slice(0, this.MAX_UI_PROMOTED);
|
|
147
|
-
}
|
|
148
|
-
logger.info(`UI test promotion scoped to ${scored.length}/${uiCandidates.length} UI test(s) relevant to changed frontend files`);
|
|
149
|
-
promoted = scored;
|
|
150
|
-
}
|
|
151
|
-
// Pass 3 — selector-edge: a page object / spec couples to a changed component by
|
|
152
|
-
// SELECTOR (data-testid value or CSS class), not by filename or import, so passes
|
|
153
|
-
// 1-2 miss it. Crucially the candidate pool here is the FULL external set, not just
|
|
154
|
-
// UI_TEST_EXT: a page object like `table.component.ts` is classified external (via
|
|
155
|
-
// the page-objects/ dir pattern) with content cached, but is not a *.test/*.spec
|
|
156
|
-
// file, so it never enters `uiCandidates`. Promote any not-yet-relevant external
|
|
157
|
-
// file whose body references a changed selector literal (matched against both the
|
|
158
|
-
// added and removed diff values, so a page object still holding a renamed selector's
|
|
159
|
-
// OLD value is caught). Bounded by MAX_UI_PROMOTED.
|
|
160
|
-
// Combined cap: MAX_UI_PROMOTED is a single Step-2 ceiling, so pass 3 may only add up
|
|
161
|
-
// to what pass 1/2 left — otherwise the two independent caps could promote 2x and
|
|
162
|
-
// re-flood discovery (the exact failure SKYR-3941 guards against). Compute the headroom
|
|
163
|
-
// up front and skip the O(external × selectors) content scan entirely when pass 1/2
|
|
164
|
-
// already filled the budget.
|
|
165
|
-
const selectorHeadroom = Math.max(0, this.MAX_UI_PROMOTED - promoted.length);
|
|
166
|
-
if (changedSelectors?.length && selectorHeadroom > 0) {
|
|
167
|
-
const alreadyPromoted = new Set([...alreadyRelevant, ...promoted]);
|
|
168
|
-
const selectorMatched = classified.external
|
|
169
|
-
.filter((f) => {
|
|
170
|
-
if (alreadyPromoted.has(f))
|
|
171
|
-
return false;
|
|
172
|
-
const content = classified.contentCache.get(f) ?? "";
|
|
173
|
-
return content.length > 0 && changedSelectors.some((sel) => content.includes(sel));
|
|
174
|
-
})
|
|
175
|
-
// Sort for a deterministic cap — filesystem/glob traversal order is not stable
|
|
176
|
-
// across environments, so which files survive MAX_UI_PROMOTED must not depend on it.
|
|
177
|
-
.sort();
|
|
178
|
-
if (selectorMatched.length > 0) {
|
|
179
|
-
const capped = selectorMatched.slice(0, selectorHeadroom);
|
|
180
|
-
logger.info(`UI test promotion: ${selectorMatched.length} test/page-object file(s) reference a changed selector (pass 3, selector-edge)` +
|
|
181
|
-
(capped.length < selectorMatched.length ? `; added ${capped.length} (MAX_UI_PROMOTED headroom after pass 1/2)` : ""));
|
|
182
|
-
if (capped.length > 0)
|
|
183
|
-
promoted = [...promoted, ...capped];
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
const promotedSet = new Set(promoted);
|
|
187
|
-
// Promote snapshot/fixture siblings of scored AND newly promoted UI tests.
|
|
188
|
-
// Include promoted files so siblings are found on UI-only PRs where
|
|
189
|
-
// relevantExternal started empty before Step 2.
|
|
190
|
-
const relevantTestBasenames = new Set([...relevantExternal, ...promoted].filter(f => UI_TEST_EXT.test(f)).map(f => path.basename(f)));
|
|
191
|
-
const promotedSecondary = classified.external.filter(f => !alreadyRelevant.has(f) &&
|
|
192
|
-
!promotedSet.has(f) &&
|
|
193
|
-
relevantTestBasenames.has(path.basename(f).replace(SEC_EXT, '')));
|
|
194
|
-
const allPromoted = [...promoted, ...promotedSecondary];
|
|
195
|
-
if (allPromoted.length > 0) {
|
|
196
|
-
logger.info(`Promoting ${promoted.length} UI test + ${promotedSecondary.length} secondary file(s) for content reading`);
|
|
197
|
-
relevantExternal = [...relevantExternal, ...allPromoted];
|
|
198
|
-
}
|
|
199
|
-
}
|
|
58
|
+
// No diff context reaches this service: its one caller passes no options, so
|
|
59
|
+
// every external file is potentially relevant and the cap is what bounds the
|
|
60
|
+
// read set.
|
|
61
|
+
const relevantExternal = classified.external.slice(0, this.MAX_EXTERNAL_FULL_REPO);
|
|
62
|
+
// There is no Step 2 any more. It promoted UI tests by matching filename tokens
|
|
63
|
+
// against changed FRONTEND files, a list built by classifying paths on
|
|
64
|
+
// extension and directory. Every external test is already loaded.
|
|
200
65
|
// Free content cache for external files not selected for full extraction.
|
|
201
66
|
const relevantSet = new Set(relevantExternal);
|
|
202
67
|
for (const f of classified.external) {
|
|
@@ -212,164 +77,6 @@ export class TestDiscoveryService {
|
|
|
212
77
|
relevantExternalTestPaths: relevantExternal,
|
|
213
78
|
};
|
|
214
79
|
}
|
|
215
|
-
/**
|
|
216
|
-
* Score an external test file's relevance to a set of changed resource names.
|
|
217
|
-
* Tokenises the last two segments of the file path (split by /, \, -, _, .)
|
|
218
|
-
* and counts overlapping tokens with the changed resources.
|
|
219
|
-
* Example: "test_orders_api.py" vs ["orders"] → score 1.
|
|
220
|
-
*/
|
|
221
|
-
scoreRelevance(filePath, changedResources) {
|
|
222
|
-
const normalized = filePath.toLowerCase().replace(/\\/g, "/");
|
|
223
|
-
// Use the last two path segments to avoid false matches from deeply-nested dirs
|
|
224
|
-
const segments = normalized.split("/").slice(-2).join("/");
|
|
225
|
-
const tokens = new Set(segments.split(/[/\-_.]+/).filter(Boolean));
|
|
226
|
-
// Expand tokens with plural/singular variants so "deployment" matches
|
|
227
|
-
// "deployments" and vice versa. Only strip trailing "s" when the result
|
|
228
|
-
// is ≥4 chars (avoids "pas"→"pa") and doesn't end in "ss" (avoids "pass"→"pas").
|
|
229
|
-
const expanded = new Set(tokens);
|
|
230
|
-
for (const t of tokens) {
|
|
231
|
-
if (t.endsWith("s") && !t.endsWith("ss") && t.length > 4)
|
|
232
|
-
expanded.add(t.slice(0, -1));
|
|
233
|
-
else if (!t.endsWith("s"))
|
|
234
|
-
expanded.add(t + "s");
|
|
235
|
-
}
|
|
236
|
-
return changedResources.filter(r => {
|
|
237
|
-
const rLower = r.toLowerCase();
|
|
238
|
-
if (expanded.has(rLower))
|
|
239
|
-
return true;
|
|
240
|
-
// Compound resource names (e.g. "order-items") — all parts must appear as tokens.
|
|
241
|
-
const parts = rLower.split(/[-_]/);
|
|
242
|
-
return parts.length > 1 && parts.every(p => p.length >= 3 && expanded.has(p));
|
|
243
|
-
}).length;
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Derive relevance tokens from the changed frontend files, for scoring UI tests in Step 2
|
|
247
|
-
* the same way scoreRelevance scores against changed API resources. For each file we emit:
|
|
248
|
-
* - the basename stem (e.g. "OrderSearch" → "ordersearch") — matches co-located tests like
|
|
249
|
-
* OrderSearch.test.tsx, the dominant convention;
|
|
250
|
-
* - its sub-tokens, split on delimiters AND camelCase boundaries ("OrderSearch" →
|
|
251
|
-
* "order","search") — matches hyphenated/underscored test names like order-search.spec.tsx;
|
|
252
|
-
* - the parent directory name — the signal when the basename is generic (orders/index.tsx).
|
|
253
|
-
* Generic tokens (index, components, utils, …) and tokens shorter than 3 chars are dropped so
|
|
254
|
-
* a shared dir or an index file doesn't match every UI test in the repo.
|
|
255
|
-
*/
|
|
256
|
-
deriveFrontendResourceTokens(changedFrontendFiles) {
|
|
257
|
-
const out = new Set();
|
|
258
|
-
const add = (t) => {
|
|
259
|
-
const v = t.toLowerCase();
|
|
260
|
-
if (v.length >= 3 && !this.GENERIC_FRONTEND_TOKENS.has(v))
|
|
261
|
-
out.add(v);
|
|
262
|
-
};
|
|
263
|
-
for (const f of changedFrontendFiles) {
|
|
264
|
-
const segs = f.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
265
|
-
const base = segs[segs.length - 1] ?? "";
|
|
266
|
-
const stem = base.replace(/\.[^.]+$/, ""); // drop extension
|
|
267
|
-
add(stem); // whole stem (e.g. "ordersearch", "order-search")
|
|
268
|
-
for (const t of stem.split(/[-_.]+|(?<=[a-z0-9])(?=[A-Z])/))
|
|
269
|
-
add(t); // sub-tokens
|
|
270
|
-
const parent = segs[segs.length - 2];
|
|
271
|
-
if (parent && !parent.startsWith("__"))
|
|
272
|
-
add(parent);
|
|
273
|
-
}
|
|
274
|
-
return [...out];
|
|
275
|
-
}
|
|
276
|
-
// Max additional tests promoted via content-based scoring on top of filename matches.
|
|
277
|
-
// Content matches are lower-confidence than filename matches — cap to avoid token bloat.
|
|
278
|
-
MAX_CONTENT_PROMOTED = 5;
|
|
279
|
-
// Backstop cap on scoped UI-test promotion (Step 2). The relevance filter against the
|
|
280
|
-
// changed frontend files does the real bounding; this only guards a pathological match
|
|
281
|
-
// (e.g. a change to a widely-shared component). Highest-scoring tests survive truncation.
|
|
282
|
-
MAX_UI_PROMOTED = 50;
|
|
283
|
-
// Weight added to a UI test's relevance score when it imports a changed frontend file by
|
|
284
|
-
// module path (import-edge match, Step 2 pass 2). Set high above any filename-token score
|
|
285
|
-
// so import matches — the strongest "this test exercises the changed code" signal —
|
|
286
|
-
// always survive the MAX_UI_PROMOTED cap ahead of name-only matches.
|
|
287
|
-
UI_IMPORT_MATCH_WEIGHT = 100;
|
|
288
|
-
// Generic path/name tokens that carry no feature signal — excluded when deriving relevance
|
|
289
|
-
// tokens from changed frontend files so a changed index.tsx or a shared "components" dir
|
|
290
|
-
// doesn't match every UI test in the repo.
|
|
291
|
-
GENERIC_FRONTEND_TOKENS = new Set([
|
|
292
|
-
"index", "app", "main", "root", "styles", "style", "css", "scss", "types", "type",
|
|
293
|
-
"constants", "utils", "util", "helpers", "helper", "components", "component", "pages",
|
|
294
|
-
"page", "routes", "route", "src", "common", "shared", "hooks", "hook", "lib", "context",
|
|
295
|
-
"providers", "provider", "store", "stores", "api", "test", "tests", "spec", "specs",
|
|
296
|
-
"mocks", "mock", "fixtures", "fixture", "assets", "config",
|
|
297
|
-
]);
|
|
298
|
-
/**
|
|
299
|
-
* Partition external test files into relevant (score > 0) and low-relevance (score = 0).
|
|
300
|
-
* Two-pass: filename token overlap first (primary); content-based endpoint path scan
|
|
301
|
-
* as a capped fallback for tests misnamed relative to the endpoint they exercise
|
|
302
|
-
* (e.g. test_checkout_flow.py testing /api/orders scores 0 by name but matches in content).
|
|
303
|
-
* Content matches are capped at MAX_CONTENT_PROMOTED to bound token impact.
|
|
304
|
-
*/
|
|
305
|
-
partitionByRelevance(files, changedResources, contentCache, changedSymbols, preciseResources) {
|
|
306
|
-
const nameMatched = [];
|
|
307
|
-
const symbolMatched = [];
|
|
308
|
-
const contentCandidates = [];
|
|
309
|
-
// Precompile content patterns once — avoids 20 resources × 150 files = 3K compilations.
|
|
310
|
-
const contentPatterns = contentCache
|
|
311
|
-
? changedResources.map(r => {
|
|
312
|
-
// Escape all regex metacharacters, then restore [-/] as a character class
|
|
313
|
-
// for hyphens and slashes that appear in resource names like "order-items".
|
|
314
|
-
const escaped = r.toLowerCase()
|
|
315
|
-
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") // escape regex metacharacters
|
|
316
|
-
.replace(/-|\//g, "[-/]"); // treat hyphens/slashes interchangeably
|
|
317
|
-
return new RegExp(`["'\`][^"'\`]*/${escaped}`, "i");
|
|
318
|
-
})
|
|
319
|
-
: [];
|
|
320
|
-
// Exact, case-sensitive word-boundary match on a changed type name (e.g.
|
|
321
|
-
// `DeploymentCreate`). Catches an external test that references the changed schema/model
|
|
322
|
-
// by name but whose path/URL matches no changed resource — a schema unit test
|
|
323
|
-
// constructing it directly (SKYR-3924). High-confidence, so not subject to the content cap.
|
|
324
|
-
const symbolPatterns = contentCache
|
|
325
|
-
? (changedSymbols ?? []).map(s => new RegExp(`\\b${s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`))
|
|
326
|
-
: [];
|
|
327
|
-
// Endpoint-call patterns: an HTTP-method call to a path containing the changed resource
|
|
328
|
-
// (e.g. `client.post("/deployments/")`). A direct call to the changed endpoint is
|
|
329
|
-
// high-confidence — distinct from a file that merely mentions the resource string.
|
|
330
|
-
const endpointCallPatterns = contentCache
|
|
331
|
-
? changedResources.map(r => {
|
|
332
|
-
const escaped = r.toLowerCase()
|
|
333
|
-
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
334
|
-
.replace(/-|\//g, "[-/]");
|
|
335
|
-
return new RegExp(`\\b(?:get|post|put|patch|delete|request|fetch)\\b[^\\n]{0,60}["'\`][^"'\`]*/${escaped}`, "i");
|
|
336
|
-
})
|
|
337
|
-
: [];
|
|
338
|
-
const strongContent = [];
|
|
339
|
-
for (const f of files) {
|
|
340
|
-
if (this.scoreRelevance(f, changedResources) > 0) {
|
|
341
|
-
nameMatched.push(f);
|
|
342
|
-
continue;
|
|
343
|
-
}
|
|
344
|
-
const content = contentCache?.get(f) ?? "";
|
|
345
|
-
if (!content)
|
|
346
|
-
continue;
|
|
347
|
-
if (symbolPatterns.some(p => p.test(content))) {
|
|
348
|
-
symbolMatched.push(f);
|
|
349
|
-
}
|
|
350
|
-
else if (endpointCallPatterns.some(p => p.test(content))) {
|
|
351
|
-
strongContent.push(f); // direct call to the changed endpoint — high-confidence
|
|
352
|
-
}
|
|
353
|
-
else if (contentPatterns.some(p => p.test(content))) {
|
|
354
|
-
contentCandidates.push(f); // loose mention of the resource — low-confidence, capped
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
// Precise resource sets (schema-diff preload): keep high-confidence endpoint-call matches
|
|
358
|
-
// uncapped (bounded by the specific resource), still cap loose mentions. Otherwise cap all
|
|
359
|
-
// content matches together (original behavior).
|
|
360
|
-
const contentMatched = preciseResources
|
|
361
|
-
? [...strongContent, ...contentCandidates.slice(0, this.MAX_CONTENT_PROMOTED)]
|
|
362
|
-
: [...strongContent, ...contentCandidates].slice(0, this.MAX_CONTENT_PROMOTED);
|
|
363
|
-
if (symbolMatched.length > 0 || contentMatched.length > 0) {
|
|
364
|
-
// Report what actually landed in contentMatched — the non-precise branch caps
|
|
365
|
-
// strong+loose together, so strongContent may be truncated too. Deriving both
|
|
366
|
-
// counts from contentMatched.length avoids a misleading negative "loose" count.
|
|
367
|
-
const strongIn = Math.min(strongContent.length, contentMatched.length);
|
|
368
|
-
const looseIn = contentMatched.length - strongIn;
|
|
369
|
-
logger.info(`Relevance: ${symbolMatched.length} symbol + ${strongIn} endpoint-call + ${looseIn} loose content`);
|
|
370
|
-
}
|
|
371
|
-
return { relevant: [...nameMatched, ...symbolMatched, ...contentMatched] };
|
|
372
|
-
}
|
|
373
80
|
/**
|
|
374
81
|
* Process test files in parallel batches with concurrency control
|
|
375
82
|
* @param isExternal When true, uses external test metadata extraction
|
|
@@ -22,4 +22,4 @@ export declare function isValidEnvVarName(name: string): boolean;
|
|
|
22
22
|
/**
|
|
23
23
|
* Build the environment variable array for the Docker executor container.
|
|
24
24
|
*/
|
|
25
|
-
export declare function buildContainerEnv(options: Pick<TestExecutionOptions, "token" | "language" | "useHostNetwork">, saveStoragePath?: string, hostEnv?: Record<string, string | undefined>, passthroughNames?: string[]): string[];
|
|
25
|
+
export declare function buildContainerEnv(options: Pick<TestExecutionOptions, "token" | "language" | "useHostNetwork" | "rebaselineSnapshots">, saveStoragePath?: string, hostEnv?: Record<string, string | undefined>, passthroughNames?: string[]): string[];
|
|
@@ -77,6 +77,18 @@ export function buildContainerEnv(options, saveStoragePath, hostEnv = process.en
|
|
|
77
77
|
...(options.token ? [`SKYRAMP_TEST_TOKEN=${options.token}`] : []),
|
|
78
78
|
"SKYRAMP_IN_DOCKER=true",
|
|
79
79
|
];
|
|
80
|
+
// Visual-snapshot baselines this run replaces (SKYR-4298). SmartPlaywright
|
|
81
|
+
// reads SKYRAMP_UPDATE_SNAPSHOTS and re-captures each named baseline through
|
|
82
|
+
// its first-run path instead of comparing. Set here, first-class, because the
|
|
83
|
+
// SKYRAMP_ prefix is reserved from workspace passthrough below — nothing else
|
|
84
|
+
// can put it in the container. Omitted entirely when nothing is to refresh, so
|
|
85
|
+
// the default stays "compare".
|
|
86
|
+
const rebaseline = (options.rebaselineSnapshots ?? [])
|
|
87
|
+
.map((n) => n.trim())
|
|
88
|
+
.filter(Boolean);
|
|
89
|
+
if (rebaseline.length > 0) {
|
|
90
|
+
env.push(`SKYRAMP_UPDATE_SNAPSHOTS=${rebaseline.join(",")}`);
|
|
91
|
+
}
|
|
80
92
|
// Skyramp-generated tests are standalone HTTP tests that never need host repo
|
|
81
93
|
// conftest.py files or pytest configuration. --noconftest prevents loading any
|
|
82
94
|
// conftest in the test directory tree (avoids missing deps like boto3, django).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `fix-test-import-errors` skill ships in `plugin/skills/` at the package
|
|
3
|
+
* root (SKYR-4296). It carries the same instructions `skyramp_fix_errors`
|
|
4
|
+
* returns, so an agent that loads the plugin follows the skill and an agent
|
|
5
|
+
* that does not keeps calling the tool. The tool itself is unchanged.
|
|
6
|
+
*/
|
|
7
|
+
export declare const FIX_TEST_IMPORT_ERRORS_SKILL = "fix-test-import-errors";
|
|
8
|
+
/**
|
|
9
|
+
* The sentence a prompt uses to send the agent to the fix-errors instructions.
|
|
10
|
+
* With the plugin loaded it names the skill and states the override contract:
|
|
11
|
+
* a project skill of the same name wins over the plugin copy.
|
|
12
|
+
*/
|
|
13
|
+
export declare function fixErrorsInstruction(): string;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { isSkillsLoaded } from "../utils/featureFlags.js";
|
|
2
|
+
/**
|
|
3
|
+
* The `fix-test-import-errors` skill ships in `plugin/skills/` at the package
|
|
4
|
+
* root (SKYR-4296). It carries the same instructions `skyramp_fix_errors`
|
|
5
|
+
* returns, so an agent that loads the plugin follows the skill and an agent
|
|
6
|
+
* that does not keeps calling the tool. The tool itself is unchanged.
|
|
7
|
+
*/
|
|
8
|
+
export const FIX_TEST_IMPORT_ERRORS_SKILL = "fix-test-import-errors";
|
|
9
|
+
const FIX_ERRORS_TOOL = "skyramp_fix_errors";
|
|
10
|
+
/**
|
|
11
|
+
* The sentence a prompt uses to send the agent to the fix-errors instructions.
|
|
12
|
+
* With the plugin loaded it names the skill and states the override contract:
|
|
13
|
+
* a project skill of the same name wins over the plugin copy.
|
|
14
|
+
*/
|
|
15
|
+
export function fixErrorsInstruction() {
|
|
16
|
+
if (isSkillsLoaded()) {
|
|
17
|
+
return `invoke the \`${FIX_TEST_IMPORT_ERRORS_SKILL}\` skill (a project skill with the same name takes precedence over the plugin copy)`;
|
|
18
|
+
}
|
|
19
|
+
return `call the \`${FIX_ERRORS_TOOL}\` tool`;
|
|
20
|
+
}
|
package/build/toolNames.d.ts
CHANGED
|
@@ -17,3 +17,4 @@ export declare const TOOL_INTEGRATION_TEST_GENERATION = "skyramp_integration_tes
|
|
|
17
17
|
export declare const TOOL_E2E_TEST_GENERATION = "skyramp_e2e_test_generation";
|
|
18
18
|
export declare const TOOL_UI_TEST_GENERATION = "skyramp_ui_test_generation";
|
|
19
19
|
export declare const TOOL_REGISTER_TEST_PLAN = "skyramp_register_test_plan";
|
|
20
|
+
export declare const TOOL_RESOLVE_SCREEN = "skyramp_resolve_screen";
|
package/build/toolNames.js
CHANGED
|
@@ -17,3 +17,4 @@ export const TOOL_INTEGRATION_TEST_GENERATION = "skyramp_integration_test_genera
|
|
|
17
17
|
export const TOOL_E2E_TEST_GENERATION = "skyramp_e2e_test_generation";
|
|
18
18
|
export const TOOL_UI_TEST_GENERATION = "skyramp_ui_test_generation";
|
|
19
19
|
export const TOOL_REGISTER_TEST_PLAN = "skyramp_register_test_plan";
|
|
20
|
+
export const TOOL_RESOLVE_SCREEN = "skyramp_resolve_screen";
|
|
@@ -16,7 +16,7 @@ import { stageGeneratedPaths } from "../../utils/gitStaging.js";
|
|
|
16
16
|
const TOOL_NAME = "skyramp_enhance_assertions";
|
|
17
17
|
const TESTBOT_UI_CHECKS = `
|
|
18
18
|
### Additional Testbot-Specific Checks
|
|
19
|
-
- If no suitable selector exists in the generated file for an assertion you need to add, go back and call \`browser_assert\` on the live page to record it with a valid selector, then re-export and regenerate.
|
|
19
|
+
- If no suitable selector exists in the generated file for an assertion you need to add, go back and call \`browser_assert\` on the live page to record it with a valid selector, then re-export and regenerate. Do not re-record an assertion that is expected to fail today; add it here with the blueprint's selector.
|
|
20
20
|
- **After executing a UI test that documents a bug from \`issuesFound\`**: if it passed when you expected it to fail, the assertions are too weak — add a stronger \`expect()\` that directly targets the buggy behavior. This counts as the single allowed retry under the 2-attempt cap — do NOT re-run more than once.
|
|
21
21
|
`;
|
|
22
22
|
function buildAssertionInstructions(testFile, testType, enhanceType) {
|
|
@@ -44,7 +44,7 @@ function buildAutoApplyInstructions(testFile, testType, enhanceType) {
|
|
|
44
44
|
`Read the file, apply the type-specific assertion guidance below, and write the file back directly.`,
|
|
45
45
|
`Add assertions after each send_request/sendRequest status-code assertion.`,
|
|
46
46
|
`Use SDK helper: Python \`skyramp.get_response_value(response, "json.path")\`, JS \`getValue(response, "json.path")\`.`,
|
|
47
|
-
`Do NOT restructure, reformat, add comments
|
|
47
|
+
`Do NOT restructure, reformat, or add comments. You may add an import when a new assertion needs a helper — from \`@skyramp/skyramp\` or from the test's own helper module. Only add assertion lines.`,
|
|
48
48
|
``,
|
|
49
49
|
`Type-specific assertion guidance:`,
|
|
50
50
|
typeSpecificInstructions,
|
|
@@ -169,7 +169,7 @@ export function registerEnhanceAssertionsTool(server) {
|
|
|
169
169
|
`Shared assertion rules (apply all that fit):`,
|
|
170
170
|
renderSharedAssertionRules(),
|
|
171
171
|
``,
|
|
172
|
-
`Do NOT restructure, reformat, add comments
|
|
172
|
+
`Do NOT restructure, reformat, or add comments. You may add an import when a new assertion needs a helper — from \`@skyramp/skyramp\` or from the test's own helper module. Only add assertion lines.`,
|
|
173
173
|
].join("\n");
|
|
174
174
|
const result = {
|
|
175
175
|
content: [{ type: "text", text: compactInstructions }],
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { fixErrorsInstruction } from "../../skills/fixTestImportErrorsSkill.js";
|
|
2
3
|
import fs from "fs";
|
|
3
4
|
import { logger } from "../../utils/logger.js";
|
|
4
5
|
import { ProgrammingLanguage, TestType } from "../../types/TestTypes.js";
|
|
@@ -39,7 +40,7 @@ export function registerModularizationTool(server) {
|
|
|
39
40
|
|
|
40
41
|
**MANDATORY**: ONLY MODULARIZE THE TEST IF THE IS TRACE BASED FLAG IS SET TO TRUE ELSE DO NOT MODULARIZE THE TEST AND LEAVE THE TEST FILE AS IS.
|
|
41
42
|
**CRITICAL**: NON TRACE BASED TESTS ARE ALREADY MODULARIZED AND DO NOT NEED MODULARIZATION.
|
|
42
|
-
After modularization, if errors remain,
|
|
43
|
+
After modularization, if errors remain, ${fixErrorsInstruction()}.
|
|
43
44
|
|
|
44
45
|
**CRITICAL**: This tool expects you to complete the modularization by writing the refactored code back to the file.`,
|
|
45
46
|
inputSchema: modularizationSchema,
|