retestkit 1.4.1 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +59 -40
- package/dist/config.js +8 -8
- package/dist/config.js.map +1 -1
- package/dist/logger.js +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +21 -21
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/templates/mcp/retest-crawl.md +7 -0
- package/{src/prompts/templates/mcp/webtest-discover-flows.md → dist/prompts/templates/mcp/retest-discover-flows.md} +1 -1
- package/{src/prompts/templates/mcp/webtest-discover.md → dist/prompts/templates/mcp/retest-discover.md} +2 -2
- package/dist/prompts/templates/mcp/retest-full-workflow.md +12 -0
- package/{src/prompts/templates/mcp/webtest-generate-tests.md → dist/prompts/templates/mcp/retest-generate-tests.md} +1 -1
- package/{src/prompts/templates/mcp/webtest-run-test.md → dist/prompts/templates/mcp/retest-run-test.md} +1 -1
- package/{src/prompts/templates/mcp/webtest-start.md → dist/prompts/templates/mcp/retest-start.md} +1 -1
- package/{src → dist}/prompts/templates/sampling/system-prefix.md +1 -1
- package/dist/resources/index.js +7 -7
- package/dist/resources/index.js.map +1 -1
- package/dist/schemas/config.js +2 -2
- package/dist/schemas/config.js.map +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/index.js.map +1 -1
- package/dist/server.js +3 -3
- package/dist/server.js.map +1 -1
- package/dist/test-utils/mock-context.js +22 -22
- package/dist/test-utils/mock-context.js.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +5 -5
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/retest/crawl.d.ts.map +1 -0
- package/dist/tools/{webtest → retest}/crawl.js +7 -7
- package/dist/tools/retest/crawl.js.map +1 -0
- package/dist/tools/retest/discover-features.d.ts.map +1 -0
- package/dist/tools/{webtest → retest}/discover-features.js +6 -6
- package/dist/tools/retest/discover-features.js.map +1 -0
- package/dist/tools/retest/discover-flows.d.ts.map +1 -0
- package/dist/tools/{webtest → retest}/discover-flows.js +6 -6
- package/dist/tools/retest/discover-flows.js.map +1 -0
- package/dist/tools/retest/generate-tests.d.ts.map +1 -0
- package/dist/tools/{webtest → retest}/generate-tests.js +5 -5
- package/dist/tools/retest/generate-tests.js.map +1 -0
- package/dist/tools/retest/index.d.ts.map +1 -0
- package/dist/tools/retest/index.js.map +1 -0
- package/dist/tools/retest/run-test-case.d.ts.map +1 -0
- package/dist/tools/{webtest → retest}/run-test-case.js +3 -3
- package/dist/tools/retest/run-test-case.js.map +1 -0
- package/dist/tools/retest/schemas.d.ts.map +1 -0
- package/dist/tools/retest/schemas.js.map +1 -0
- package/dist/tools/retest/start-analysis.d.ts.map +1 -0
- package/dist/tools/{webtest → retest}/start-analysis.js +5 -5
- package/dist/tools/retest/start-analysis.js.map +1 -0
- package/dist/workspace/index.js +8 -8
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/types.d.ts +2 -2
- package/dist/workspace/types.d.ts.map +1 -1
- package/package.json +6 -2
- package/.claude/commands/openspec/apply.md +0 -23
- package/.claude/commands/openspec/archive.md +0 -27
- package/.claude/commands/openspec/proposal.md +0 -28
- package/.gemini/commands/openspec/apply.toml +0 -21
- package/.gemini/commands/openspec/archive.toml +0 -25
- package/.gemini/commands/openspec/proposal.toml +0 -26
- package/.github/prompts/openspec-apply.prompt.md +0 -22
- package/.github/prompts/openspec-archive.prompt.md +0 -26
- package/.github/prompts/openspec-proposal.prompt.md +0 -27
- package/.github/workflows/release.yml +0 -33
- package/.kilocode/workflows/openspec-apply.md +0 -17
- package/.kilocode/workflows/openspec-archive.md +0 -21
- package/.kilocode/workflows/openspec-proposal.md +0 -22
- package/.mcp.json +0 -23
- package/.opencode/command/openspec-apply.md +0 -25
- package/.opencode/command/openspec-archive.md +0 -28
- package/.opencode/command/openspec-proposal.md +0 -30
- package/.roo/commands/openspec-apply.md +0 -20
- package/.roo/commands/openspec-archive.md +0 -24
- package/.roo/commands/openspec-proposal.md +0 -25
- package/.vscode/mcp.json +0 -23
- package/AGENTS.md +0 -18
- package/CLAUDE.md +0 -18
- package/dist/tools/webtest/crawl.d.ts.map +0 -1
- package/dist/tools/webtest/crawl.js.map +0 -1
- package/dist/tools/webtest/discover-features.d.ts.map +0 -1
- package/dist/tools/webtest/discover-features.js.map +0 -1
- package/dist/tools/webtest/discover-flows.d.ts.map +0 -1
- package/dist/tools/webtest/discover-flows.js.map +0 -1
- package/dist/tools/webtest/generate-tests.d.ts.map +0 -1
- package/dist/tools/webtest/generate-tests.js.map +0 -1
- package/dist/tools/webtest/index.d.ts.map +0 -1
- package/dist/tools/webtest/index.js.map +0 -1
- package/dist/tools/webtest/run-test-case.d.ts.map +0 -1
- package/dist/tools/webtest/run-test-case.js.map +0 -1
- package/dist/tools/webtest/schemas.d.ts.map +0 -1
- package/dist/tools/webtest/schemas.js.map +0 -1
- package/dist/tools/webtest/start-analysis.d.ts.map +0 -1
- package/dist/tools/webtest/start-analysis.js.map +0 -1
- package/openspec/AGENTS.md +0 -456
- package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/proposal.md +0 -33
- package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/specs/webtest-resources/spec.md +0 -27
- package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/specs/webtest-tools/spec.md +0 -304
- package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/tasks.md +0 -43
- package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/design.md +0 -209
- package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/proposal.md +0 -41
- package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/specs/mcp-server-core/spec.md +0 -183
- package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/tasks.md +0 -112
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/design.md +0 -333
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/proposal.md +0 -66
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/mcp-server-core/spec.md +0 -129
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-lifecycle/spec.md +0 -138
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-logging/spec.md +0 -211
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-prompts/spec.md +0 -157
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-resources/spec.md +0 -213
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-sampling/spec.md +0 -257
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-tools/spec.md +0 -501
- package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/tasks.md +0 -264
- package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/proposal.md +0 -24
- package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/specs/webtest-tools/spec.md +0 -80
- package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/tasks.md +0 -8
- package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/design.md +0 -90
- package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/proposal.md +0 -28
- package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/specs/webtest-sampling/spec.md +0 -90
- package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/tasks.md +0 -33
- package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/design.md +0 -558
- package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/proposal.md +0 -119
- package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/specs/webtest-resources/spec.md +0 -109
- package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/specs/webtest-tools/spec.md +0 -121
- package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/tasks.md +0 -133
- package/openspec/changes/extract-prompts-to-markdown/design.md +0 -86
- package/openspec/changes/extract-prompts-to-markdown/proposal.md +0 -50
- package/openspec/changes/extract-prompts-to-markdown/specs/webtest-prompts/spec.md +0 -74
- package/openspec/changes/extract-prompts-to-markdown/tasks.md +0 -40
- package/openspec/changes/refactor-webtest-naming/design.md +0 -95
- package/openspec/changes/refactor-webtest-naming/proposal.md +0 -66
- package/openspec/changes/refactor-webtest-naming/specs/webtest-prompts/spec.md +0 -79
- package/openspec/changes/refactor-webtest-naming/specs/webtest-resources/spec.md +0 -80
- package/openspec/changes/refactor-webtest-naming/specs/webtest-sampling/spec.md +0 -122
- package/openspec/changes/refactor-webtest-naming/specs/webtest-tools/spec.md +0 -113
- package/openspec/changes/refactor-webtest-naming/tasks.md +0 -119
- package/openspec/changes/rename-package-to-retest/proposal.md +0 -52
- package/openspec/changes/rename-package-to-retest/specs/mcp-server-core/spec.md +0 -53
- package/openspec/changes/rename-package-to-retest/specs/retest-lifecycle/spec.md +0 -68
- package/openspec/changes/rename-package-to-retest/specs/retest-logging/spec.md +0 -35
- package/openspec/changes/rename-package-to-retest/specs/retest-prompts/spec.md +0 -159
- package/openspec/changes/rename-package-to-retest/specs/retest-resources/spec.md +0 -251
- package/openspec/changes/rename-package-to-retest/specs/retest-sampling/spec.md +0 -99
- package/openspec/changes/rename-package-to-retest/specs/retest-tools/spec.md +0 -295
- package/openspec/changes/rename-package-to-retest/tasks.md +0 -71
- package/openspec/project.md +0 -31
- package/openspec/specs/mcp-server-core/spec.md +0 -178
- package/openspec/specs/webtest-lifecycle/spec.md +0 -136
- package/openspec/specs/webtest-logging/spec.md +0 -209
- package/openspec/specs/webtest-prompts/spec.md +0 -155
- package/openspec/specs/webtest-resources/spec.md +0 -248
- package/openspec/specs/webtest-sampling/spec.md +0 -344
- package/openspec/specs/webtest-tools/spec.md +0 -282
- package/release.config.js +0 -9
- package/src/config.test.ts +0 -96
- package/src/config.ts +0 -32
- package/src/elicitation/index.test.ts +0 -399
- package/src/elicitation/index.ts +0 -171
- package/src/elicitation/types.ts +0 -68
- package/src/index.ts +0 -83
- package/src/lifecycle/index.test.ts +0 -260
- package/src/lifecycle/index.ts +0 -101
- package/src/logger.redaction.test.ts +0 -322
- package/src/logger.test.ts +0 -123
- package/src/logger.ts +0 -229
- package/src/playwright-client/index.ts +0 -392
- package/src/playwright-client/types.ts +0 -99
- package/src/progress/index.test.ts +0 -327
- package/src/progress/index.ts +0 -170
- package/src/progress/types.ts +0 -25
- package/src/prompts/index.test.ts +0 -451
- package/src/prompts/index.ts +0 -246
- package/src/prompts/loader.test.ts +0 -100
- package/src/prompts/loader.ts +0 -59
- package/src/prompts/templates/mcp/webtest-crawl.md +0 -7
- package/src/prompts/templates/mcp/webtest-full-workflow.md +0 -12
- package/src/resources/index.ts +0 -250
- package/src/resources/subscriptions.ts +0 -37
- package/src/sampling/index.test.ts +0 -414
- package/src/sampling/index.ts +0 -286
- package/src/sampling/prompts.ts +0 -194
- package/src/sampling/types.ts +0 -60
- package/src/schemas/config.ts +0 -39
- package/src/security/index.test.ts +0 -441
- package/src/security/index.ts +0 -361
- package/src/security/security-scenarios.test.ts +0 -468
- package/src/server.ts +0 -211
- package/src/test-utils/index.ts +0 -6
- package/src/test-utils/mock-context.ts +0 -426
- package/src/test-utils/mock-playwright-client.ts +0 -422
- package/src/tools/index.ts +0 -11
- package/src/tools/webtest/crawl.test.ts +0 -834
- package/src/tools/webtest/crawl.ts +0 -901
- package/src/tools/webtest/discover-features.ts +0 -412
- package/src/tools/webtest/discover-flows.ts +0 -408
- package/src/tools/webtest/generate-tests.test.ts +0 -532
- package/src/tools/webtest/generate-tests.ts +0 -425
- package/src/tools/webtest/index.ts +0 -7
- package/src/tools/webtest/integration.test.ts +0 -536
- package/src/tools/webtest/run-test-case.test.ts +0 -659
- package/src/tools/webtest/run-test-case.ts +0 -508
- package/src/tools/webtest/schemas.ts +0 -201
- package/src/tools/webtest/start-analysis.test.ts +0 -151
- package/src/tools/webtest/start-analysis.ts +0 -158
- package/src/transports/http.ts +0 -19
- package/src/transports/index.ts +0 -30
- package/src/transports/stdio.ts +0 -7
- package/src/types/capabilities.test.ts +0 -193
- package/src/types/capabilities.ts +0 -50
- package/src/types/context.ts +0 -21
- package/src/types/tool.ts +0 -11
- package/src/workspace/index.ts +0 -945
- package/src/workspace/markdown.ts +0 -272
- package/src/workspace/types.ts +0 -186
- package/tests/integration/server.test.ts +0 -89
- package/tests/integration/tools.test.ts +0 -99
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -9
- package/vitest.integration.config.ts +0 -10
- /package/{src → dist}/prompts/templates/sampling/crawl-action.md +0 -0
- /package/{src → dist}/prompts/templates/sampling/feature-discovery.md +0 -0
- /package/{src → dist}/prompts/templates/sampling/flow-discovery.md +0 -0
- /package/{src → dist}/prompts/templates/sampling/page-content-wrapper.md +0 -0
- /package/{src → dist}/prompts/templates/sampling/test-evaluation.md +0 -0
- /package/{src → dist}/prompts/templates/sampling/test-generation.md +0 -0
- /package/dist/tools/{webtest → retest}/crawl.d.ts +0 -0
- /package/dist/tools/{webtest → retest}/discover-features.d.ts +0 -0
- /package/dist/tools/{webtest → retest}/discover-flows.d.ts +0 -0
- /package/dist/tools/{webtest → retest}/generate-tests.d.ts +0 -0
- /package/dist/tools/{webtest → retest}/index.d.ts +0 -0
- /package/dist/tools/{webtest → retest}/index.js +0 -0
- /package/dist/tools/{webtest → retest}/run-test-case.d.ts +0 -0
- /package/dist/tools/{webtest → retest}/schemas.d.ts +0 -0
- /package/dist/tools/{webtest → retest}/schemas.js +0 -0
- /package/dist/tools/{webtest → retest}/start-analysis.d.ts +0 -0
|
@@ -1,659 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit Tests for webtest_run_tests tool (Phase 7.8)
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
6
|
-
import { createRunTestCaseTool } from "./run-test-case.js";
|
|
7
|
-
import {
|
|
8
|
-
createMockContext,
|
|
9
|
-
type MockContext,
|
|
10
|
-
} from "../../test-utils/index.js";
|
|
11
|
-
|
|
12
|
-
describe("webtest_run_tests", () => {
|
|
13
|
-
let context: MockContext;
|
|
14
|
-
let tool: ReturnType<typeof createRunTestCaseTool>;
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
context = createMockContext();
|
|
18
|
-
tool = createRunTestCaseTool(() => context as any);
|
|
19
|
-
|
|
20
|
-
// Set up workspace with tests
|
|
21
|
-
context.workspaceManager.readWorkspaceIndex = vi.fn().mockResolvedValue({
|
|
22
|
-
url: "https://shop.example.com",
|
|
23
|
-
domain: "shop.example.com",
|
|
24
|
-
focus: "Test checkout flow",
|
|
25
|
-
crawls: [],
|
|
26
|
-
analysis: {
|
|
27
|
-
appAnalysisUri: "webtest://test/analysis/app-analysis.md",
|
|
28
|
-
flowsUri: "webtest://test/analysis/flows.md",
|
|
29
|
-
},
|
|
30
|
-
tests: {
|
|
31
|
-
testsUri: "webtest://test/tests/tests.md",
|
|
32
|
-
testCount: 2,
|
|
33
|
-
},
|
|
34
|
-
runs: [],
|
|
35
|
-
limits: { maxSteps: 100, maxMinutes: 30, maxPages: 50 },
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// Set up resource reading for tests (markdown with YAML frontmatter)
|
|
39
|
-
context.resourceManager.readResource = vi.fn().mockResolvedValue({
|
|
40
|
-
text: `---
|
|
41
|
-
tests:
|
|
42
|
-
- id: test-browse-001
|
|
43
|
-
name: Browse products successfully
|
|
44
|
-
category: happy_path
|
|
45
|
-
purpose: Verify user can browse product list
|
|
46
|
-
preconditions:
|
|
47
|
-
- User is on home page
|
|
48
|
-
steps:
|
|
49
|
-
- stepNumber: 1
|
|
50
|
-
action: Click
|
|
51
|
-
target: a.products-link
|
|
52
|
-
expected: Products page loads
|
|
53
|
-
- stepNumber: 2
|
|
54
|
-
action: Verify
|
|
55
|
-
expected: Product list is visible
|
|
56
|
-
expectedOutcomes:
|
|
57
|
-
- Products page is displayed
|
|
58
|
-
tags:
|
|
59
|
-
- browse
|
|
60
|
-
- id: test-checkout-001
|
|
61
|
-
name: Complete checkout
|
|
62
|
-
category: happy_path
|
|
63
|
-
purpose: Verify user can complete checkout
|
|
64
|
-
preconditions: []
|
|
65
|
-
steps:
|
|
66
|
-
- stepNumber: 1
|
|
67
|
-
action: Navigate to
|
|
68
|
-
target: /cart
|
|
69
|
-
expected: Cart page loads
|
|
70
|
-
expectedOutcomes:
|
|
71
|
-
- Order is placed
|
|
72
|
-
tags:
|
|
73
|
-
- checkout
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
# Test Cases
|
|
77
|
-
|
|
78
|
-
## Browse products successfully
|
|
79
|
-
...
|
|
80
|
-
`,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// Set up evaluation result
|
|
84
|
-
context.samplingClient.createMessage = vi.fn().mockResolvedValue({
|
|
85
|
-
success: true,
|
|
86
|
-
data: {
|
|
87
|
-
passed: true,
|
|
88
|
-
reasoning: "Expected outcome matched",
|
|
89
|
-
confidence: 0.95,
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// Connect playwright
|
|
94
|
-
context.playwrightClient.isConnected = vi.fn().mockReturnValue(true);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe("tool metadata", () => {
|
|
98
|
-
it("has correct name", () => {
|
|
99
|
-
expect(tool.name).toBe("webtest_run_test");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("has a description", () => {
|
|
103
|
-
expect(tool.description).toBeDefined();
|
|
104
|
-
expect(tool.description.length).toBeGreaterThan(0);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it("has an input schema", () => {
|
|
108
|
-
expect(tool.inputSchema).toBeDefined();
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
describe("handler - validation", () => {
|
|
113
|
-
it("returns error for non-existent workspace", async () => {
|
|
114
|
-
context.workspaceManager.workspaceExists = vi.fn().mockResolvedValue(false);
|
|
115
|
-
|
|
116
|
-
const result = await tool.handler({
|
|
117
|
-
analysisId: "00000000-0000-0000-0000-000000000000",
|
|
118
|
-
testCaseId: "test-1",
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
expect(result.isError).toBe(true);
|
|
122
|
-
expect(result.content[0].text).toContain("not found");
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("returns error when no tests exist", async () => {
|
|
126
|
-
context.workspaceManager.readWorkspaceIndex = vi.fn().mockResolvedValue({
|
|
127
|
-
url: "https://example.com",
|
|
128
|
-
domain: "example.com",
|
|
129
|
-
crawls: [],
|
|
130
|
-
analysis: null,
|
|
131
|
-
tests: null,
|
|
132
|
-
runs: [],
|
|
133
|
-
limits: { maxSteps: 100, maxMinutes: 30, maxPages: 50 },
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
const result = await tool.handler({
|
|
137
|
-
analysisId: context.testAnalysisId,
|
|
138
|
-
testCaseId: "test-1",
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
expect(result.isError).toBe(true);
|
|
142
|
-
expect(result.content[0].text).toContain("No tests found");
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it("returns error for non-existent test case", async () => {
|
|
146
|
-
const result = await tool.handler({
|
|
147
|
-
analysisId: context.testAnalysisId,
|
|
148
|
-
testCaseId: "non-existent-test",
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
expect(result.isError).toBe(true);
|
|
152
|
-
expect(result.content[0].text).toContain("not found");
|
|
153
|
-
expect(result.content[0].text).toContain("Available tests");
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it("returns error when tests fail to load", async () => {
|
|
157
|
-
context.resourceManager.readResource = vi.fn().mockRejectedValue(
|
|
158
|
-
new Error("File not found")
|
|
159
|
-
);
|
|
160
|
-
|
|
161
|
-
const result = await tool.handler({
|
|
162
|
-
analysisId: context.testAnalysisId,
|
|
163
|
-
testCaseId: "test-browse-001",
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
expect(result.isError).toBe(true);
|
|
167
|
-
expect(result.content[0].text).toContain("Error loading");
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
describe("handler - test execution", () => {
|
|
172
|
-
it("creates test run in workspace", async () => {
|
|
173
|
-
await tool.handler({
|
|
174
|
-
analysisId: context.testAnalysisId,
|
|
175
|
-
testCaseId: "test-browse-001",
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
expect(context.workspaceManager.createTestRun).toHaveBeenCalledWith(
|
|
179
|
-
context.testAnalysisId,
|
|
180
|
-
expect.objectContaining({
|
|
181
|
-
testCaseId: "test-browse-001",
|
|
182
|
-
testName: "Browse products successfully",
|
|
183
|
-
})
|
|
184
|
-
);
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
it("connects to playwright if not connected", async () => {
|
|
188
|
-
context.playwrightClient.isConnected = vi.fn().mockReturnValue(false);
|
|
189
|
-
|
|
190
|
-
await tool.handler({
|
|
191
|
-
analysisId: context.testAnalysisId,
|
|
192
|
-
testCaseId: "test-browse-001",
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
expect(context.playwrightClient.connect).toHaveBeenCalled();
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it("navigates to workspace URL first", async () => {
|
|
199
|
-
await tool.handler({
|
|
200
|
-
analysisId: context.testAnalysisId,
|
|
201
|
-
testCaseId: "test-browse-001",
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
expect(context.playwrightClient.navigate).toHaveBeenCalledWith(
|
|
205
|
-
"https://shop.example.com"
|
|
206
|
-
);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it("executes click actions", async () => {
|
|
210
|
-
await tool.handler({
|
|
211
|
-
analysisId: context.testAnalysisId,
|
|
212
|
-
testCaseId: "test-browse-001",
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
// When no element/ref provided, falls back to target as element with empty ref
|
|
216
|
-
expect(context.playwrightClient.click).toHaveBeenCalledWith("a.products-link", "");
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
it("returns runId in response", async () => {
|
|
220
|
-
const result = await tool.handler({
|
|
221
|
-
analysisId: context.testAnalysisId,
|
|
222
|
-
testCaseId: "test-browse-001",
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
expect(result.isError).toBeFalsy();
|
|
226
|
-
const content = JSON.parse(result.content[0].text!);
|
|
227
|
-
expect(content.runId).toBeDefined();
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it("returns test status", async () => {
|
|
231
|
-
const result = await tool.handler({
|
|
232
|
-
analysisId: context.testAnalysisId,
|
|
233
|
-
testCaseId: "test-browse-001",
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
const content = JSON.parse(result.content[0].text!);
|
|
237
|
-
expect(content.status).toBeDefined();
|
|
238
|
-
expect(["passed", "failed", "error"]).toContain(content.status);
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it("returns step results", async () => {
|
|
242
|
-
const result = await tool.handler({
|
|
243
|
-
analysisId: context.testAnalysisId,
|
|
244
|
-
testCaseId: "test-browse-001",
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
const content = JSON.parse(result.content[0].text!);
|
|
248
|
-
expect(content.stepResults).toBeInstanceOf(Array);
|
|
249
|
-
expect(content.stepResults.length).toBeGreaterThan(0);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it("returns summary with step counts", async () => {
|
|
253
|
-
const result = await tool.handler({
|
|
254
|
-
analysisId: context.testAnalysisId,
|
|
255
|
-
testCaseId: "test-browse-001",
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
const content = JSON.parse(result.content[0].text!);
|
|
259
|
-
expect(content.summary).toBeDefined();
|
|
260
|
-
expect(content.summary.totalSteps).toBeGreaterThan(0);
|
|
261
|
-
expect(content.summary.passed).toBeDefined();
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
describe("handler - evidence capture", () => {
|
|
266
|
-
it("captures screenshot at each step by default", async () => {
|
|
267
|
-
await tool.handler({
|
|
268
|
-
analysisId: context.testAnalysisId,
|
|
269
|
-
testCaseId: "test-browse-001",
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
expect(context.playwrightClient.screenshot).toHaveBeenCalled();
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it("captures snapshot at each step by default", async () => {
|
|
276
|
-
await tool.handler({
|
|
277
|
-
analysisId: context.testAnalysisId,
|
|
278
|
-
testCaseId: "test-browse-001",
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
expect(context.playwrightClient.snapshot).toHaveBeenCalled();
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
it("saves evidence to workspace", async () => {
|
|
285
|
-
await tool.handler({
|
|
286
|
-
analysisId: context.testAnalysisId,
|
|
287
|
-
testCaseId: "test-browse-001",
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
expect(context.workspaceManager.saveTestStepEvidence).toHaveBeenCalled();
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
it("skips evidence when captureEvidence is false", async () => {
|
|
294
|
-
vi.clearAllMocks();
|
|
295
|
-
|
|
296
|
-
// Need to re-mock after clear
|
|
297
|
-
context.playwrightClient.navigate = vi.fn().mockResolvedValue(undefined);
|
|
298
|
-
|
|
299
|
-
await tool.handler({
|
|
300
|
-
analysisId: context.testAnalysisId,
|
|
301
|
-
testCaseId: "test-browse-001",
|
|
302
|
-
runOptions: {
|
|
303
|
-
captureEvidence: false,
|
|
304
|
-
stopOnFailure: true,
|
|
305
|
-
retryFailedSteps: false,
|
|
306
|
-
},
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
expect(context.workspaceManager.saveTestStepEvidence).not.toHaveBeenCalled();
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
describe("handler - assertion evaluation", () => {
|
|
314
|
-
it("evaluates step assertions via sampling", async () => {
|
|
315
|
-
await tool.handler({
|
|
316
|
-
analysisId: context.testAnalysisId,
|
|
317
|
-
testCaseId: "test-browse-001",
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
expect(context.samplingClient.createMessage).toHaveBeenCalled();
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it("marks step as failed when assertion fails", async () => {
|
|
324
|
-
context.samplingClient.createMessage = vi.fn().mockResolvedValue({
|
|
325
|
-
success: true,
|
|
326
|
-
data: {
|
|
327
|
-
passed: false,
|
|
328
|
-
reasoning: "Expected product list but saw error page",
|
|
329
|
-
confidence: 0.9,
|
|
330
|
-
},
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
const result = await tool.handler({
|
|
334
|
-
analysisId: context.testAnalysisId,
|
|
335
|
-
testCaseId: "test-browse-001",
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
const content = JSON.parse(result.content[0].text!);
|
|
339
|
-
expect(content.status).toBe("failed");
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
it("continues without sampling when unavailable", async () => {
|
|
343
|
-
context.samplingClient.hasSampling = vi.fn().mockReturnValue(false);
|
|
344
|
-
|
|
345
|
-
const result = await tool.handler({
|
|
346
|
-
analysisId: context.testAnalysisId,
|
|
347
|
-
testCaseId: "test-browse-001",
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
// Should still pass (no assertion evaluation)
|
|
351
|
-
expect(result.isError).toBeFalsy();
|
|
352
|
-
});
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
describe("handler - stop on failure", () => {
|
|
356
|
-
it("stops on first failure by default", async () => {
|
|
357
|
-
context.playwrightClient.click = vi.fn()
|
|
358
|
-
.mockRejectedValueOnce(new Error("Element not found"))
|
|
359
|
-
.mockResolvedValue(undefined);
|
|
360
|
-
|
|
361
|
-
const result = await tool.handler({
|
|
362
|
-
analysisId: context.testAnalysisId,
|
|
363
|
-
testCaseId: "test-browse-001",
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
const content = JSON.parse(result.content[0].text!);
|
|
367
|
-
expect(content.status).toBe("error");
|
|
368
|
-
expect(content.summary.skipped).toBeGreaterThan(0);
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
it("continues on failure when stopOnFailure is false", async () => {
|
|
372
|
-
context.playwrightClient.click = vi.fn()
|
|
373
|
-
.mockRejectedValueOnce(new Error("Element not found"))
|
|
374
|
-
.mockResolvedValue(undefined);
|
|
375
|
-
|
|
376
|
-
const result = await tool.handler({
|
|
377
|
-
analysisId: context.testAnalysisId,
|
|
378
|
-
testCaseId: "test-browse-001",
|
|
379
|
-
runOptions: {
|
|
380
|
-
captureEvidence: true,
|
|
381
|
-
stopOnFailure: false,
|
|
382
|
-
retryFailedSteps: false,
|
|
383
|
-
},
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
const content = JSON.parse(result.content[0].text!);
|
|
387
|
-
// All steps should have been attempted
|
|
388
|
-
expect(content.summary.skipped).toBe(0);
|
|
389
|
-
});
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
describe("handler - retry failed steps", () => {
|
|
393
|
-
it("retries failed steps when enabled", async () => {
|
|
394
|
-
let callCount = 0;
|
|
395
|
-
context.playwrightClient.click = vi.fn().mockImplementation(() => {
|
|
396
|
-
callCount++;
|
|
397
|
-
if (callCount === 1) {
|
|
398
|
-
return Promise.reject(new Error("Transient error"));
|
|
399
|
-
}
|
|
400
|
-
return Promise.resolve();
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
const result = await tool.handler({
|
|
404
|
-
analysisId: context.testAnalysisId,
|
|
405
|
-
testCaseId: "test-browse-001",
|
|
406
|
-
runOptions: {
|
|
407
|
-
captureEvidence: true,
|
|
408
|
-
stopOnFailure: true,
|
|
409
|
-
retryFailedSteps: true,
|
|
410
|
-
},
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
// Should have retried and passed
|
|
414
|
-
const content = JSON.parse(result.content[0].text!);
|
|
415
|
-
expect(content.status).toBe("passed");
|
|
416
|
-
});
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
describe("handler - cancellation", () => {
|
|
420
|
-
it("handles cancellation gracefully", async () => {
|
|
421
|
-
const { CancellationError } = await import("../../progress/index.js");
|
|
422
|
-
context.cancellationRegistry.checkCancelled = vi.fn().mockImplementation((id) => {
|
|
423
|
-
throw new CancellationError(id);
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
const result = await tool.handler({
|
|
427
|
-
analysisId: context.testAnalysisId,
|
|
428
|
-
testCaseId: "test-browse-001",
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
expect(result.isError).toBeFalsy();
|
|
432
|
-
const content = JSON.parse(result.content[0].text!);
|
|
433
|
-
expect(content.status).toBe("cancelled");
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
it("registers for cancellation on start", async () => {
|
|
437
|
-
await tool.handler({
|
|
438
|
-
analysisId: context.testAnalysisId,
|
|
439
|
-
testCaseId: "test-browse-001",
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
expect(context.cancellationRegistry.register).toHaveBeenCalled();
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
it("unregisters cancellation on completion", async () => {
|
|
446
|
-
await tool.handler({
|
|
447
|
-
analysisId: context.testAnalysisId,
|
|
448
|
-
testCaseId: "test-browse-001",
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
expect(context.cancellationRegistry.unregister).toHaveBeenCalled();
|
|
452
|
-
});
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
describe("handler - progress reporting", () => {
|
|
456
|
-
it("emits progress for each step", async () => {
|
|
457
|
-
await tool.handler({
|
|
458
|
-
analysisId: context.testAnalysisId,
|
|
459
|
-
testCaseId: "test-browse-001",
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
expect(context.progressEmitter.emit).toHaveBeenCalled();
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
it("includes step info in progress", async () => {
|
|
466
|
-
await tool.handler({
|
|
467
|
-
analysisId: context.testAnalysisId,
|
|
468
|
-
testCaseId: "test-browse-001",
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
expect(context.progressEmitter.emit).toHaveBeenCalledWith(
|
|
472
|
-
expect.objectContaining({
|
|
473
|
-
progress: expect.any(Number),
|
|
474
|
-
total: expect.any(Number),
|
|
475
|
-
message: expect.stringContaining("Step"),
|
|
476
|
-
})
|
|
477
|
-
);
|
|
478
|
-
});
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
describe("handler - resource notifications", () => {
|
|
482
|
-
it("notifies on test run creation", async () => {
|
|
483
|
-
vi.clearAllMocks();
|
|
484
|
-
|
|
485
|
-
await tool.handler({
|
|
486
|
-
analysisId: context.testAnalysisId,
|
|
487
|
-
testCaseId: "test-browse-001",
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
expect(context.resourceManager.notifyListChanged).toHaveBeenCalled();
|
|
491
|
-
});
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
describe("handler - action execution", () => {
|
|
495
|
-
it("executes navigate actions", async () => {
|
|
496
|
-
await tool.handler({
|
|
497
|
-
analysisId: context.testAnalysisId,
|
|
498
|
-
testCaseId: "test-checkout-001",
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
expect(context.playwrightClient.navigate).toHaveBeenCalledWith("/cart");
|
|
502
|
-
});
|
|
503
|
-
|
|
504
|
-
it("handles type actions", async () => {
|
|
505
|
-
context.resourceManager.readResource = vi.fn().mockResolvedValue({
|
|
506
|
-
text: `---
|
|
507
|
-
tests:
|
|
508
|
-
- id: test-type-001
|
|
509
|
-
name: Type in field
|
|
510
|
-
category: happy_path
|
|
511
|
-
purpose: Test typing
|
|
512
|
-
preconditions: []
|
|
513
|
-
steps:
|
|
514
|
-
- stepNumber: 1
|
|
515
|
-
action: Type
|
|
516
|
-
target: input.search
|
|
517
|
-
value: test query
|
|
518
|
-
expectedOutcomes:
|
|
519
|
-
- Text is entered
|
|
520
|
-
---
|
|
521
|
-
|
|
522
|
-
# Test Cases
|
|
523
|
-
...
|
|
524
|
-
`,
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
await tool.handler({
|
|
528
|
-
analysisId: context.testAnalysisId,
|
|
529
|
-
testCaseId: "test-type-001",
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
// When no element/ref provided, falls back to target as element with empty ref
|
|
533
|
-
expect(context.playwrightClient.type).toHaveBeenCalledWith("input.search", "", "test query");
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
it("handles fill actions", async () => {
|
|
537
|
-
context.resourceManager.readResource = vi.fn().mockResolvedValue({
|
|
538
|
-
text: `---
|
|
539
|
-
tests:
|
|
540
|
-
- id: test-fill-001
|
|
541
|
-
name: Fill field
|
|
542
|
-
category: happy_path
|
|
543
|
-
purpose: Test filling
|
|
544
|
-
preconditions: []
|
|
545
|
-
steps:
|
|
546
|
-
- stepNumber: 1
|
|
547
|
-
action: Fill
|
|
548
|
-
target: input.email
|
|
549
|
-
value: test@example.com
|
|
550
|
-
expectedOutcomes:
|
|
551
|
-
- Field is filled
|
|
552
|
-
---
|
|
553
|
-
|
|
554
|
-
# Test Cases
|
|
555
|
-
...
|
|
556
|
-
`,
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
await tool.handler({
|
|
560
|
-
analysisId: context.testAnalysisId,
|
|
561
|
-
testCaseId: "test-fill-001",
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
// When no element/ref provided, falls back to target as element with empty ref
|
|
565
|
-
expect(context.playwrightClient.fill).toHaveBeenCalledWith("input.email", "", "test@example.com");
|
|
566
|
-
});
|
|
567
|
-
});
|
|
568
|
-
|
|
569
|
-
describe("handler - error handling", () => {
|
|
570
|
-
it("handles playwright connection errors", async () => {
|
|
571
|
-
context.playwrightClient.isConnected = vi.fn().mockReturnValue(false);
|
|
572
|
-
context.playwrightClient.connect = vi.fn().mockRejectedValue(
|
|
573
|
-
new Error("Connection failed")
|
|
574
|
-
);
|
|
575
|
-
|
|
576
|
-
const result = await tool.handler({
|
|
577
|
-
analysisId: context.testAnalysisId,
|
|
578
|
-
testCaseId: "test-browse-001",
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
expect(result.isError).toBe(true);
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
it("handles step execution errors", async () => {
|
|
585
|
-
context.playwrightClient.click = vi.fn().mockRejectedValue(
|
|
586
|
-
new Error("Element not found")
|
|
587
|
-
);
|
|
588
|
-
|
|
589
|
-
const result = await tool.handler({
|
|
590
|
-
analysisId: context.testAnalysisId,
|
|
591
|
-
testCaseId: "test-browse-001",
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
const content = JSON.parse(result.content[0].text!);
|
|
595
|
-
expect(content.status).toBe("error");
|
|
596
|
-
expect(content.stepResults[0].errorMessage).toBeDefined();
|
|
597
|
-
});
|
|
598
|
-
});
|
|
599
|
-
|
|
600
|
-
describe("handler - next steps", () => {
|
|
601
|
-
it("returns success next steps when test passes", async () => {
|
|
602
|
-
const result = await tool.handler({
|
|
603
|
-
analysisId: context.testAnalysisId,
|
|
604
|
-
testCaseId: "test-browse-001",
|
|
605
|
-
});
|
|
606
|
-
|
|
607
|
-
const content = JSON.parse(result.content[0].text!);
|
|
608
|
-
expect(content.nextSteps).toBeInstanceOf(Array);
|
|
609
|
-
expect(content.nextSteps.some((s: string) => s.includes("passed"))).toBe(true);
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
it("returns investigation steps when test fails", async () => {
|
|
613
|
-
context.samplingClient.createMessage = vi.fn().mockResolvedValue({
|
|
614
|
-
success: true,
|
|
615
|
-
data: {
|
|
616
|
-
passed: false,
|
|
617
|
-
reasoning: "Element not visible",
|
|
618
|
-
confidence: 0.9,
|
|
619
|
-
},
|
|
620
|
-
});
|
|
621
|
-
|
|
622
|
-
const result = await tool.handler({
|
|
623
|
-
analysisId: context.testAnalysisId,
|
|
624
|
-
testCaseId: "test-browse-001",
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
const content = JSON.parse(result.content[0].text!);
|
|
628
|
-
expect(content.nextSteps.some((s: string) => s.includes("Investigate"))).toBe(true);
|
|
629
|
-
});
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
describe("handler - logging", () => {
|
|
633
|
-
it("logs test execution start", async () => {
|
|
634
|
-
await tool.handler({
|
|
635
|
-
analysisId: context.testAnalysisId,
|
|
636
|
-
testCaseId: "test-browse-001",
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
expect(context.logger.info).toHaveBeenCalledWith(
|
|
640
|
-
expect.stringContaining("test case"),
|
|
641
|
-
expect.any(Object)
|
|
642
|
-
);
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
it("logs completion with results", async () => {
|
|
646
|
-
await tool.handler({
|
|
647
|
-
analysisId: context.testAnalysisId,
|
|
648
|
-
testCaseId: "test-browse-001",
|
|
649
|
-
});
|
|
650
|
-
|
|
651
|
-
expect(context.logger.info).toHaveBeenCalledWith(
|
|
652
|
-
expect.stringContaining("completed"),
|
|
653
|
-
expect.objectContaining({
|
|
654
|
-
status: expect.any(String),
|
|
655
|
-
})
|
|
656
|
-
);
|
|
657
|
-
});
|
|
658
|
-
});
|
|
659
|
-
});
|