playwright-mimic 0.1.0 → 0.1.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/README.md +695 -446
- package/dist/agentic/agent.d.ts +106 -0
- package/dist/agentic/agent.d.ts.map +1 -0
- package/dist/agentic/agent.js +528 -0
- package/dist/agentic/agent.js.map +1 -0
- package/dist/agentic/index.d.ts +13 -0
- package/dist/agentic/index.d.ts.map +1 -0
- package/dist/agentic/index.js +12 -0
- package/dist/agentic/index.js.map +1 -0
- package/dist/agentic/planner.d.ts +41 -0
- package/dist/agentic/planner.d.ts.map +1 -0
- package/dist/agentic/planner.js +136 -0
- package/dist/agentic/planner.js.map +1 -0
- package/dist/agentic/react.d.ts +35 -0
- package/dist/agentic/react.d.ts.map +1 -0
- package/dist/agentic/react.js +170 -0
- package/dist/agentic/react.js.map +1 -0
- package/dist/agentic/recovery.d.ts +55 -0
- package/dist/agentic/recovery.d.ts.map +1 -0
- package/dist/agentic/recovery.js +200 -0
- package/dist/agentic/recovery.js.map +1 -0
- package/dist/agentic/reflection.d.ts +40 -0
- package/dist/agentic/reflection.d.ts.map +1 -0
- package/dist/agentic/reflection.js +142 -0
- package/dist/agentic/reflection.js.map +1 -0
- package/dist/agentic/types.d.ts +177 -0
- package/dist/agentic/types.d.ts.map +1 -0
- package/dist/agentic/types.js +8 -0
- package/dist/agentic/types.js.map +1 -0
- package/dist/agentic/wait.d.ts +50 -0
- package/dist/agentic/wait.d.ts.map +1 -0
- package/dist/agentic/wait.js +140 -0
- package/dist/agentic/wait.js.map +1 -0
- package/dist/agentic-mimic.d.ts +56 -0
- package/dist/agentic-mimic.d.ts.map +1 -0
- package/dist/agentic-mimic.js +98 -0
- package/dist/agentic-mimic.js.map +1 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -9
- package/dist/index.js.map +1 -1
- package/dist/mimic/actionType.d.ts +7 -0
- package/dist/mimic/actionType.d.ts.map +1 -0
- package/dist/mimic/actionType.js +44 -0
- package/dist/mimic/actionType.js.map +1 -0
- package/dist/mimic/annotations.d.ts +21 -0
- package/dist/mimic/annotations.d.ts.map +1 -0
- package/dist/mimic/annotations.js +36 -0
- package/dist/mimic/annotations.js.map +1 -0
- package/dist/mimic/cli.d.ts +15 -0
- package/dist/mimic/cli.d.ts.map +1 -0
- package/dist/mimic/cli.js +17 -0
- package/dist/mimic/cli.js.map +1 -0
- package/dist/mimic/click.d.ts +39 -0
- package/dist/mimic/click.d.ts.map +1 -0
- package/dist/mimic/click.js +348 -0
- package/dist/mimic/click.js.map +1 -0
- package/dist/mimic/forms.d.ts +52 -0
- package/dist/mimic/forms.d.ts.map +1 -0
- package/dist/mimic/forms.js +511 -0
- package/dist/mimic/forms.js.map +1 -0
- package/dist/mimic/markers.d.ts +133 -0
- package/dist/mimic/markers.d.ts.map +1 -0
- package/dist/mimic/markers.js +589 -0
- package/dist/mimic/markers.js.map +1 -0
- package/dist/mimic/navigation.d.ts +19 -0
- package/dist/mimic/navigation.d.ts.map +1 -0
- package/dist/mimic/navigation.js +136 -0
- package/dist/mimic/navigation.js.map +1 -0
- package/dist/mimic/playwrightCodeGenerator.d.ts +55 -0
- package/dist/mimic/playwrightCodeGenerator.d.ts.map +1 -0
- package/dist/mimic/playwrightCodeGenerator.js +270 -0
- package/dist/mimic/playwrightCodeGenerator.js.map +1 -0
- package/dist/mimic/replay.d.ts +21 -0
- package/dist/mimic/replay.d.ts.map +1 -0
- package/dist/mimic/replay.js +142 -0
- package/dist/mimic/replay.js.map +1 -0
- package/dist/mimic/schema/action.d.ts +315 -0
- package/dist/mimic/schema/action.d.ts.map +1 -0
- package/dist/mimic/schema/action.js +186 -0
- package/dist/mimic/schema/action.js.map +1 -0
- package/dist/mimic/selector.d.ts +143 -0
- package/dist/mimic/selector.d.ts.map +1 -0
- package/dist/mimic/selector.js +1515 -0
- package/dist/mimic/selector.js.map +1 -0
- package/dist/mimic/selectorDescriptor.d.ts +25 -0
- package/dist/mimic/selectorDescriptor.d.ts.map +1 -0
- package/dist/mimic/selectorDescriptor.js +32 -0
- package/dist/mimic/selectorDescriptor.js.map +1 -0
- package/dist/mimic/selectorSerialization.d.ts +18 -0
- package/dist/mimic/selectorSerialization.d.ts.map +1 -0
- package/dist/mimic/selectorSerialization.js +32 -0
- package/dist/mimic/selectorSerialization.js.map +1 -0
- package/dist/mimic/selectorTypes.d.ts +122 -0
- package/dist/mimic/selectorTypes.d.ts.map +1 -0
- package/dist/mimic/selectorTypes.js +2 -0
- package/dist/mimic/selectorTypes.js.map +1 -0
- package/dist/mimic/selectorUtils.d.ts +52 -0
- package/dist/mimic/selectorUtils.d.ts.map +1 -0
- package/dist/mimic/selectorUtils.js +251 -0
- package/dist/mimic/selectorUtils.js.map +1 -0
- package/dist/mimic/storage.d.ts +110 -0
- package/dist/mimic/storage.d.ts.map +1 -0
- package/dist/mimic/storage.js +409 -0
- package/dist/mimic/storage.js.map +1 -0
- package/dist/mimic/types.d.ts +85 -0
- package/dist/mimic/types.d.ts.map +1 -0
- package/dist/mimic/types.js +7 -0
- package/dist/mimic/types.js.map +1 -0
- package/dist/mimic.d.ts +29 -4
- package/dist/mimic.d.ts.map +1 -1
- package/dist/mimic.js +530 -32
- package/dist/mimic.js.map +1 -1
- package/dist/mimicry.d.ts +4 -4
- package/dist/mimicry.d.ts.map +1 -1
- package/dist/mimicry.js +22 -13
- package/dist/mimicry.js.map +1 -1
- package/package.json +30 -6
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { generateText, Output } from 'ai';
|
|
2
|
+
import { zNavigationAction, } from './schema/action.js';
|
|
3
|
+
import { countTokens } from '../utils/token-counter.js';
|
|
4
|
+
import { addAnnotation } from './annotations.js';
|
|
5
|
+
import { generateNavigationCode } from './playwrightCodeGenerator.js';
|
|
6
|
+
export const getNavigationAction = async (_page, brain, action, testContext) => {
|
|
7
|
+
// Build context description for the prompt
|
|
8
|
+
const contextDescription = testContext ? `
|
|
9
|
+
**Test Context:**
|
|
10
|
+
- Current URL: ${testContext.currentState.url}
|
|
11
|
+
- Current Page Title: ${testContext.currentState.pageTitle}
|
|
12
|
+
- Step ${testContext.currentStepIndex + 1} of ${testContext.totalSteps}
|
|
13
|
+
${testContext.previousSteps.length > 0 ? `
|
|
14
|
+
**Previous Steps Executed:**
|
|
15
|
+
${testContext.previousSteps.map((prevStep, idx) => `${idx + 1}. Step ${prevStep.stepIndex + 1}: "${prevStep.stepText}" (${prevStep.actionKind}${prevStep.url ? ` → ${prevStep.url}` : ''})`).join('\n')}
|
|
16
|
+
` : ''}
|
|
17
|
+
` : '';
|
|
18
|
+
const res = await generateText({
|
|
19
|
+
model: brain,
|
|
20
|
+
maxRetries: 3,
|
|
21
|
+
prompt: `You are an expert in converting Gherkin test steps into structured browser automation action objects using Playwright.
|
|
22
|
+
|
|
23
|
+
Your task is to process a single Gherkin step and determine whether it represents a **navigation** action. this can be any of the following:
|
|
24
|
+
- navigate to a page (this requires a url, if no url is provided, go for an option below)
|
|
25
|
+
- closePage: close the current page
|
|
26
|
+
- goBack: go back to the previous page, or navigate back in the browser history
|
|
27
|
+
- goForward: go forward to the next page, or navigate forward in the browser history
|
|
28
|
+
- refresh: refresh the current page, or reload the page
|
|
29
|
+
|
|
30
|
+
${contextDescription}
|
|
31
|
+
**Input Gherkin step:** ${action}
|
|
32
|
+
|
|
33
|
+
**Instructions:**
|
|
34
|
+
1. Determine the navigation type and extract the URL if applicable
|
|
35
|
+
2. For navigate/openPage actions, determine if the step indicates opening in a new window/tab:
|
|
36
|
+
- If the step mentions "new window", "new tab", "open in new", "in a new page", or similar phrases, set newWindow: true
|
|
37
|
+
- Otherwise, set newWindow: false or omit it (defaults to false)
|
|
38
|
+
3. Provide a clear, human-readable description of what navigation is happening
|
|
39
|
+
- For navigate/openPage: "Navigate to [page name or URL]" (e.g., "Navigate to login page", "Navigate to https://example.com")
|
|
40
|
+
- For navigate/openPage with newWindow: "Navigate to [page name or URL] in new window/tab" (e.g., "Navigate to login page in new window")
|
|
41
|
+
- Do not hallucinate the domain, if none are mentioned, just pass the uri (e.g., "/login")
|
|
42
|
+
- For goBack: "Go back to previous page in browser history" (the system will add the specific URL information)
|
|
43
|
+
- For goForward: "Go forward to next page in browser history" (the system will add the specific URL information)
|
|
44
|
+
- For refresh: "Refresh the current page" (the system will add the specific URL information)
|
|
45
|
+
- For closePage: "Close the current browser page/tab" (the system will add the specific URL information)
|
|
46
|
+
|
|
47
|
+
`,
|
|
48
|
+
output: Output.object({ schema: zNavigationAction, name: 'navigation' }),
|
|
49
|
+
});
|
|
50
|
+
await countTokens(res);
|
|
51
|
+
return res.output;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Execute a navigation action on the page with plain English annotation
|
|
55
|
+
*
|
|
56
|
+
* This function performs browser navigation actions (navigate, go back, refresh, etc.)
|
|
57
|
+
* and adds test annotations for better traceability and validation.
|
|
58
|
+
*
|
|
59
|
+
* @param page - Playwright Page object to perform navigation on
|
|
60
|
+
* @param navigationAction - Navigation action containing type, parameters, and description
|
|
61
|
+
* @param testInfo - Playwright TestInfo for adding annotations (optional)
|
|
62
|
+
* @param gherkinStep - The original Gherkin step for annotation type (optional)
|
|
63
|
+
* @returns Promise that resolves to the navigation action (for snapshot storage)
|
|
64
|
+
*/
|
|
65
|
+
export const executeNavigationAction = async (page, navigationAction, testInfo, gherkinStep) => {
|
|
66
|
+
// Use LLM-generated description or build a default one
|
|
67
|
+
const actionDescription = navigationAction.description || 'navigation action';
|
|
68
|
+
// Generate Playwright code equivalent
|
|
69
|
+
const playwrightCode = generateNavigationCode(navigationAction.type, navigationAction.params.url ?? undefined, navigationAction.params.newWindow);
|
|
70
|
+
switch (navigationAction.type) {
|
|
71
|
+
case 'openPage':
|
|
72
|
+
case 'navigate':
|
|
73
|
+
// Check if we need to open in a new window/tab
|
|
74
|
+
if (navigationAction.params.newWindow) {
|
|
75
|
+
// Create a new page in the same browser context
|
|
76
|
+
const context = page.context();
|
|
77
|
+
const newPage = await context.newPage();
|
|
78
|
+
addAnnotation(testInfo, gherkinStep, `→ ${actionDescription} in new window/tab and waiting for page to load completely`, playwrightCode);
|
|
79
|
+
await newPage.goto(navigationAction.params.url, { waitUntil: 'networkidle' });
|
|
80
|
+
// Note: The new page is created and navigated, but the original page reference remains unchanged
|
|
81
|
+
// The new page is available in the browser context and can be accessed via context.pages()
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
addAnnotation(testInfo, gherkinStep, `→ ${actionDescription} and waiting for page to load completely`, playwrightCode);
|
|
85
|
+
await page.goto(navigationAction.params.url, { waitUntil: 'networkidle' });
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
case 'closePage':
|
|
89
|
+
// Only close page if explicitly requested - be very careful with this action
|
|
90
|
+
// Check if page is still open before closing
|
|
91
|
+
if (page.isClosed()) {
|
|
92
|
+
addAnnotation(testInfo, gherkinStep, `→ Page is already closed, cannot close again`, playwrightCode);
|
|
93
|
+
return navigationAction;
|
|
94
|
+
}
|
|
95
|
+
// Capture current URL for better traceability
|
|
96
|
+
const currentUrlBeforeClose = page.url();
|
|
97
|
+
addAnnotation(testInfo, gherkinStep, `→ ${actionDescription} (closing page at ${currentUrlBeforeClose})`, playwrightCode);
|
|
98
|
+
await page.close();
|
|
99
|
+
break;
|
|
100
|
+
case 'goBack':
|
|
101
|
+
// Capture current URL before going back for better traceability
|
|
102
|
+
const currentUrlBeforeBack = page.url();
|
|
103
|
+
await page.goBack();
|
|
104
|
+
try {
|
|
105
|
+
const urlAfterBack = page.url();
|
|
106
|
+
addAnnotation(testInfo, gherkinStep, `→ ${actionDescription} (from ${currentUrlBeforeBack} to ${urlAfterBack})`, playwrightCode);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
addAnnotation(testInfo, gherkinStep, `→ ${actionDescription} (from ${currentUrlBeforeBack})`, playwrightCode);
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
case 'goForward':
|
|
113
|
+
// Capture current URL before going forward for better traceability
|
|
114
|
+
const currentUrlBeforeForward = page.url();
|
|
115
|
+
await page.goForward();
|
|
116
|
+
try {
|
|
117
|
+
const urlAfterForward = page.url();
|
|
118
|
+
addAnnotation(testInfo, gherkinStep, `→ ${actionDescription} (from ${currentUrlBeforeForward} to ${urlAfterForward})`, playwrightCode);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
addAnnotation(testInfo, gherkinStep, `→ ${actionDescription} (from ${currentUrlBeforeForward})`, playwrightCode);
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
case 'refresh':
|
|
125
|
+
// Capture current URL for better traceability
|
|
126
|
+
const currentUrlBeforeRefresh = page.url();
|
|
127
|
+
addAnnotation(testInfo, gherkinStep, `→ ${actionDescription} to reload all content (refreshing ${currentUrlBeforeRefresh})`, playwrightCode);
|
|
128
|
+
await page.reload();
|
|
129
|
+
break;
|
|
130
|
+
default:
|
|
131
|
+
throw new Error(`Unknown navigation action type: ${navigationAction.type}`);
|
|
132
|
+
}
|
|
133
|
+
// Return the action for snapshot storage
|
|
134
|
+
return navigationAction;
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../src/mimic/navigation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright Code Generator
|
|
3
|
+
*
|
|
4
|
+
* Converts SelectorDescriptor and action information into equivalent Playwright code strings
|
|
5
|
+
* for test annotations and documentation purposes.
|
|
6
|
+
*/
|
|
7
|
+
import type { SelectorDescriptor } from './selectorTypes.js';
|
|
8
|
+
/**
|
|
9
|
+
* Convert a SelectorDescriptor to a Playwright code string
|
|
10
|
+
*
|
|
11
|
+
* Generates the equivalent Playwright locator code that would be used to select
|
|
12
|
+
* the element described by the SelectorDescriptor.
|
|
13
|
+
*
|
|
14
|
+
* @param descriptor - SelectorDescriptor to convert
|
|
15
|
+
* @param baseVar - Base variable name to use (default: 'page')
|
|
16
|
+
* @returns Playwright code string representing the locator
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const code = selectorToPlaywrightCode({
|
|
21
|
+
* type: 'role',
|
|
22
|
+
* role: 'button',
|
|
23
|
+
* name: 'Submit'
|
|
24
|
+
* });
|
|
25
|
+
* // Returns: "page.getByRole('button', { name: 'Submit' })"
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function selectorToPlaywrightCode(descriptor: SelectorDescriptor, baseVar?: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Generate Playwright code for a click action
|
|
31
|
+
*
|
|
32
|
+
* @param selectorCode - Playwright locator code string
|
|
33
|
+
* @param clickType - Type of click action ('left', 'right', 'double', 'middle', 'hover')
|
|
34
|
+
* @returns Playwright code string for the click action
|
|
35
|
+
*/
|
|
36
|
+
export declare function generateClickCode(selectorCode: string, clickType: 'primary' | 'secondary' | 'tertiary' | 'double' | 'hover'): string;
|
|
37
|
+
/**
|
|
38
|
+
* Generate Playwright code for a form action
|
|
39
|
+
*
|
|
40
|
+
* @param selectorCode - Playwright locator code string
|
|
41
|
+
* @param actionType - Type of form action
|
|
42
|
+
* @param value - Value to use for the action (if applicable)
|
|
43
|
+
* @returns Playwright code string for the form action
|
|
44
|
+
*/
|
|
45
|
+
export declare function generateFormCode(selectorCode: string, actionType: 'keypress' | 'type' | 'fill' | 'select' | 'uncheck' | 'check' | 'click' | 'setInputFiles' | 'clear', value?: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Generate Playwright code for a navigation action
|
|
48
|
+
*
|
|
49
|
+
* @param actionType - Type of navigation action
|
|
50
|
+
* @param url - URL for navigate/openPage actions (optional)
|
|
51
|
+
* @param newWindow - If true, opens URL in a new browser window/tab (optional)
|
|
52
|
+
* @returns Playwright code string for the navigation action
|
|
53
|
+
*/
|
|
54
|
+
export declare function generateNavigationCode(actionType: 'openPage' | 'navigate' | 'closePage' | 'goBack' | 'goForward' | 'refresh', url?: string, newWindow?: boolean): string;
|
|
55
|
+
//# sourceMappingURL=playwrightCodeGenerator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwrightCodeGenerator.d.ts","sourceRoot":"","sources":["../../src/mimic/playwrightCodeGenerator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAG7D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,kBAAkB,EAC9B,OAAO,GAAE,MAAe,GACvB,MAAM,CA6JR;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,GACnE,MAAM,CAeR;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,eAAe,GAAG,OAAO,EAC/G,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CAwBR;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,UAAU,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,EACtF,GAAG,CAAC,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,OAAO,GAClB,MAAM,CA4BR"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright Code Generator
|
|
3
|
+
*
|
|
4
|
+
* Converts SelectorDescriptor and action information into equivalent Playwright code strings
|
|
5
|
+
* for test annotations and documentation purposes.
|
|
6
|
+
*/
|
|
7
|
+
import { jsonToStringOrRegex } from './selectorSerialization.js';
|
|
8
|
+
/**
|
|
9
|
+
* Convert a SelectorDescriptor to a Playwright code string
|
|
10
|
+
*
|
|
11
|
+
* Generates the equivalent Playwright locator code that would be used to select
|
|
12
|
+
* the element described by the SelectorDescriptor.
|
|
13
|
+
*
|
|
14
|
+
* @param descriptor - SelectorDescriptor to convert
|
|
15
|
+
* @param baseVar - Base variable name to use (default: 'page')
|
|
16
|
+
* @returns Playwright code string representing the locator
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const code = selectorToPlaywrightCode({
|
|
21
|
+
* type: 'role',
|
|
22
|
+
* role: 'button',
|
|
23
|
+
* name: 'Submit'
|
|
24
|
+
* });
|
|
25
|
+
* // Returns: "page.getByRole('button', { name: 'Submit' })"
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function selectorToPlaywrightCode(descriptor, baseVar = 'page') {
|
|
29
|
+
let code = baseVar;
|
|
30
|
+
// Build base locator code based on selector type
|
|
31
|
+
switch (descriptor.type) {
|
|
32
|
+
case 'testid':
|
|
33
|
+
code += `.getByTestId(${JSON.stringify(descriptor.value)})`;
|
|
34
|
+
break;
|
|
35
|
+
case 'role':
|
|
36
|
+
if (descriptor.name !== undefined) {
|
|
37
|
+
const nameValue = jsonToStringOrRegex(descriptor.name);
|
|
38
|
+
const isRegex = nameValue instanceof RegExp;
|
|
39
|
+
if (isRegex) {
|
|
40
|
+
// For regex, show the pattern
|
|
41
|
+
const pattern = nameValue.source;
|
|
42
|
+
const flags = nameValue.flags;
|
|
43
|
+
code += `.getByRole(${JSON.stringify(descriptor.role)}, { name: /${pattern}/${flags} })`;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// For string, include exact option if specified
|
|
47
|
+
const options = [`name: ${JSON.stringify(nameValue)}`];
|
|
48
|
+
if (descriptor.exact) {
|
|
49
|
+
options.push('exact: true');
|
|
50
|
+
}
|
|
51
|
+
code += `.getByRole(${JSON.stringify(descriptor.role)}, { ${options.join(', ')} })`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
code += `.getByRole(${JSON.stringify(descriptor.role)})`;
|
|
56
|
+
}
|
|
57
|
+
// Add .nth() if specified (for radio groups, checkbox groups, etc.)
|
|
58
|
+
if (descriptor.nth !== undefined) {
|
|
59
|
+
code += `.nth(${descriptor.nth})`;
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
case 'label':
|
|
63
|
+
{
|
|
64
|
+
const labelValue = jsonToStringOrRegex(descriptor.value);
|
|
65
|
+
const isRegex = labelValue instanceof RegExp;
|
|
66
|
+
if (isRegex) {
|
|
67
|
+
const pattern = labelValue.source;
|
|
68
|
+
const flags = labelValue.flags;
|
|
69
|
+
code += `.getByLabel(/${pattern}/${flags})`;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const options = [];
|
|
73
|
+
if (descriptor.exact) {
|
|
74
|
+
options.push('exact: true');
|
|
75
|
+
}
|
|
76
|
+
const optionsStr = options.length > 0 ? `, { ${options.join(', ')} }` : '';
|
|
77
|
+
code += `.getByLabel(${JSON.stringify(labelValue)}${optionsStr})`;
|
|
78
|
+
}
|
|
79
|
+
// Add .nth() if specified
|
|
80
|
+
if (descriptor.nth !== undefined) {
|
|
81
|
+
code += `.nth(${descriptor.nth})`;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
case 'placeholder':
|
|
86
|
+
{
|
|
87
|
+
const placeholderValue = jsonToStringOrRegex(descriptor.value);
|
|
88
|
+
const isRegex = placeholderValue instanceof RegExp;
|
|
89
|
+
if (isRegex) {
|
|
90
|
+
const pattern = placeholderValue.source;
|
|
91
|
+
const flags = placeholderValue.flags;
|
|
92
|
+
code += `.getByPlaceholder(/${pattern}/${flags})`;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const options = [];
|
|
96
|
+
if (descriptor.exact) {
|
|
97
|
+
options.push('exact: true');
|
|
98
|
+
}
|
|
99
|
+
const optionsStr = options.length > 0 ? `, { ${options.join(', ')} }` : '';
|
|
100
|
+
code += `.getByPlaceholder(${JSON.stringify(placeholderValue)}${optionsStr})`;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
case 'alt':
|
|
105
|
+
{
|
|
106
|
+
const altValue = jsonToStringOrRegex(descriptor.value);
|
|
107
|
+
const isRegex = altValue instanceof RegExp;
|
|
108
|
+
if (isRegex) {
|
|
109
|
+
const pattern = altValue.source;
|
|
110
|
+
const flags = altValue.flags;
|
|
111
|
+
code += `.getByAltText(/${pattern}/${flags})`;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const options = [];
|
|
115
|
+
if (descriptor.exact) {
|
|
116
|
+
options.push('exact: true');
|
|
117
|
+
}
|
|
118
|
+
const optionsStr = options.length > 0 ? `, { ${options.join(', ')} }` : '';
|
|
119
|
+
code += `.getByAltText(${JSON.stringify(altValue)}${optionsStr})`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
case 'title':
|
|
124
|
+
{
|
|
125
|
+
const titleValue = jsonToStringOrRegex(descriptor.value);
|
|
126
|
+
const isRegex = titleValue instanceof RegExp;
|
|
127
|
+
if (isRegex) {
|
|
128
|
+
const pattern = titleValue.source;
|
|
129
|
+
const flags = titleValue.flags;
|
|
130
|
+
code += `.getByTitle(/${pattern}/${flags})`;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const options = [];
|
|
134
|
+
if (descriptor.exact) {
|
|
135
|
+
options.push('exact: true');
|
|
136
|
+
}
|
|
137
|
+
const optionsStr = options.length > 0 ? `, { ${options.join(', ')} }` : '';
|
|
138
|
+
code += `.getByTitle(${JSON.stringify(titleValue)}${optionsStr})`;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
break;
|
|
142
|
+
case 'text':
|
|
143
|
+
{
|
|
144
|
+
const textValue = jsonToStringOrRegex(descriptor.value);
|
|
145
|
+
const isRegex = textValue instanceof RegExp;
|
|
146
|
+
if (isRegex) {
|
|
147
|
+
const pattern = textValue.source;
|
|
148
|
+
const flags = textValue.flags;
|
|
149
|
+
code += `.getByText(/${pattern}/${flags})`;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
const options = [];
|
|
153
|
+
if (descriptor.exact) {
|
|
154
|
+
options.push('exact: true');
|
|
155
|
+
}
|
|
156
|
+
const optionsStr = options.length > 0 ? `, { ${options.join(', ')} }` : '';
|
|
157
|
+
code += `.getByText(${JSON.stringify(textValue)}${optionsStr})`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
case 'css':
|
|
162
|
+
code += `.locator(${JSON.stringify(descriptor.selector)})`;
|
|
163
|
+
break;
|
|
164
|
+
default:
|
|
165
|
+
const _exhaustive = descriptor;
|
|
166
|
+
void _exhaustive;
|
|
167
|
+
throw new Error(`Unknown selector type: ${descriptor.type}`);
|
|
168
|
+
}
|
|
169
|
+
// Handle nested child selectors recursively
|
|
170
|
+
if (descriptor.child) {
|
|
171
|
+
// For nested selectors, chain the child selector
|
|
172
|
+
code = selectorToPlaywrightCode(descriptor.child, code);
|
|
173
|
+
}
|
|
174
|
+
return code;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Generate Playwright code for a click action
|
|
178
|
+
*
|
|
179
|
+
* @param selectorCode - Playwright locator code string
|
|
180
|
+
* @param clickType - Type of click action ('left', 'right', 'double', 'middle', 'hover')
|
|
181
|
+
* @returns Playwright code string for the click action
|
|
182
|
+
*/
|
|
183
|
+
export function generateClickCode(selectorCode, clickType) {
|
|
184
|
+
switch (clickType) {
|
|
185
|
+
case 'primary':
|
|
186
|
+
return `await ${selectorCode}.click();`;
|
|
187
|
+
case 'secondary':
|
|
188
|
+
return `await ${selectorCode}.click({ button: 'right' });`;
|
|
189
|
+
case 'double':
|
|
190
|
+
return `await ${selectorCode}.dblclick();`;
|
|
191
|
+
case 'tertiary':
|
|
192
|
+
return `await ${selectorCode}.click({ button: 'middle' });`;
|
|
193
|
+
case 'hover':
|
|
194
|
+
return `await ${selectorCode}.hover();`;
|
|
195
|
+
default:
|
|
196
|
+
throw new Error(`Unknown click type: ${clickType}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Generate Playwright code for a form action
|
|
201
|
+
*
|
|
202
|
+
* @param selectorCode - Playwright locator code string
|
|
203
|
+
* @param actionType - Type of form action
|
|
204
|
+
* @param value - Value to use for the action (if applicable)
|
|
205
|
+
* @returns Playwright code string for the form action
|
|
206
|
+
*/
|
|
207
|
+
export function generateFormCode(selectorCode, actionType, value) {
|
|
208
|
+
switch (actionType) {
|
|
209
|
+
case 'fill':
|
|
210
|
+
return `await ${selectorCode}.fill(${JSON.stringify(value || '')});`;
|
|
211
|
+
case 'type':
|
|
212
|
+
// Type action uses page.keyboard, not the element selector
|
|
213
|
+
return `await page.keyboard.type(${JSON.stringify(value || '')});`;
|
|
214
|
+
case 'select':
|
|
215
|
+
return `await ${selectorCode}.selectOption(${JSON.stringify(value || '')});`;
|
|
216
|
+
case 'check':
|
|
217
|
+
return `await ${selectorCode}.check();`;
|
|
218
|
+
case 'uncheck':
|
|
219
|
+
return `await ${selectorCode}.uncheck();`;
|
|
220
|
+
case 'click':
|
|
221
|
+
return `await ${selectorCode}.click();`;
|
|
222
|
+
case 'clear':
|
|
223
|
+
return `await ${selectorCode}.clear();`;
|
|
224
|
+
case 'setInputFiles':
|
|
225
|
+
return `await ${selectorCode}.setInputFiles(${JSON.stringify(value || '')});`;
|
|
226
|
+
case 'keypress':
|
|
227
|
+
return `await page.keyboard.press(${JSON.stringify(value || '')});`;
|
|
228
|
+
default:
|
|
229
|
+
throw new Error(`Unknown form action type: ${actionType}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Generate Playwright code for a navigation action
|
|
234
|
+
*
|
|
235
|
+
* @param actionType - Type of navigation action
|
|
236
|
+
* @param url - URL for navigate/openPage actions (optional)
|
|
237
|
+
* @param newWindow - If true, opens URL in a new browser window/tab (optional)
|
|
238
|
+
* @returns Playwright code string for the navigation action
|
|
239
|
+
*/
|
|
240
|
+
export function generateNavigationCode(actionType, url, newWindow) {
|
|
241
|
+
switch (actionType) {
|
|
242
|
+
case 'openPage':
|
|
243
|
+
case 'navigate':
|
|
244
|
+
if (newWindow) {
|
|
245
|
+
// Generate code for opening in a new window/tab
|
|
246
|
+
if (url) {
|
|
247
|
+
return `const newPage = await page.context().newPage();\nawait newPage.goto(${JSON.stringify(url)}, { waitUntil: 'networkidle' });`;
|
|
248
|
+
}
|
|
249
|
+
return `const newPage = await page.context().newPage();\nawait newPage.goto(url, { waitUntil: 'networkidle' });`;
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
// Generate code for normal navigation
|
|
253
|
+
if (url) {
|
|
254
|
+
return `await page.goto(${JSON.stringify(url)}, { waitUntil: 'networkidle' });`;
|
|
255
|
+
}
|
|
256
|
+
return `await page.goto(url, { waitUntil: 'networkidle' });`;
|
|
257
|
+
}
|
|
258
|
+
case 'closePage':
|
|
259
|
+
return `await page.close();`;
|
|
260
|
+
case 'goBack':
|
|
261
|
+
return `await page.goBack();`;
|
|
262
|
+
case 'goForward':
|
|
263
|
+
return `await page.goForward();`;
|
|
264
|
+
case 'refresh':
|
|
265
|
+
return `await page.reload();`;
|
|
266
|
+
default:
|
|
267
|
+
throw new Error(`Unknown navigation action type: ${actionType}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=playwrightCodeGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwrightCodeGenerator.js","sourceRoot":"","sources":["../../src/mimic/playwrightCodeGenerator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot Replay Module
|
|
3
|
+
*
|
|
4
|
+
* Replays test steps from a snapshot without using LLM calls.
|
|
5
|
+
* This provides fast, deterministic test execution for previously successful tests.
|
|
6
|
+
*/
|
|
7
|
+
import { Page, TestInfo } from '@playwright/test';
|
|
8
|
+
import type { Snapshot } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Replay a complete test from a snapshot
|
|
11
|
+
*
|
|
12
|
+
* Executes all steps in the snapshot without making LLM calls.
|
|
13
|
+
* This is much faster than normal execution since it skips AI analysis.
|
|
14
|
+
*
|
|
15
|
+
* @param page - Playwright Page object
|
|
16
|
+
* @param snapshot - Snapshot containing steps to replay
|
|
17
|
+
* @param testInfo - Playwright TestInfo for annotations (optional)
|
|
18
|
+
* @returns Promise that resolves when all steps are replayed
|
|
19
|
+
*/
|
|
20
|
+
export declare function replayFromSnapshot(page: Page, snapshot: Snapshot, testInfo?: TestInfo): Promise<void>;
|
|
21
|
+
//# sourceMappingURL=replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../src/mimic/replay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAQ,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,YAAY,CAAC;AAUzD;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,QAAQ,EAClB,QAAQ,CAAC,EAAE,QAAQ,GAClB,OAAO,CAAC,IAAI,CAAC,CA6Bf"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot Replay Module
|
|
3
|
+
*
|
|
4
|
+
* Replays test steps from a snapshot without using LLM calls.
|
|
5
|
+
* This provides fast, deterministic test execution for previously successful tests.
|
|
6
|
+
*/
|
|
7
|
+
import { test } from '@playwright/test';
|
|
8
|
+
import { executeNavigationAction } from './navigation.js';
|
|
9
|
+
import { executeClickAction } from './click.js';
|
|
10
|
+
import { executeFormAction } from './forms.js';
|
|
11
|
+
import { getMimic } from './markers.js';
|
|
12
|
+
import { getFromSelector } from './selectorUtils.js';
|
|
13
|
+
/**
|
|
14
|
+
* Replay a complete test from a snapshot
|
|
15
|
+
*
|
|
16
|
+
* Executes all steps in the snapshot without making LLM calls.
|
|
17
|
+
* This is much faster than normal execution since it skips AI analysis.
|
|
18
|
+
*
|
|
19
|
+
* @param page - Playwright Page object
|
|
20
|
+
* @param snapshot - Snapshot containing steps to replay
|
|
21
|
+
* @param testInfo - Playwright TestInfo for annotations (optional)
|
|
22
|
+
* @returns Promise that resolves when all steps are replayed
|
|
23
|
+
*/
|
|
24
|
+
export async function replayFromSnapshot(page, snapshot, testInfo) {
|
|
25
|
+
// Get steps array for replay (support both new and old format)
|
|
26
|
+
// New format: use steps array (which is built from stepsByHash for backward compatibility)
|
|
27
|
+
// Old format: use steps array directly
|
|
28
|
+
const stepsToReplay = snapshot.steps || [];
|
|
29
|
+
if (stepsToReplay.length === 0) {
|
|
30
|
+
console.warn('⚠️ Snapshot has no steps to replay');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Replay each step in order
|
|
34
|
+
for (const step of stepsToReplay) {
|
|
35
|
+
await test.step(step.stepText, async () => {
|
|
36
|
+
switch (step.actionKind) {
|
|
37
|
+
case 'navigation':
|
|
38
|
+
await replayNavigationStep(page, step, testInfo);
|
|
39
|
+
break;
|
|
40
|
+
case 'click':
|
|
41
|
+
await replayClickStep(page, step, testInfo);
|
|
42
|
+
break;
|
|
43
|
+
case 'form update':
|
|
44
|
+
await replayFormStep(page, step, testInfo);
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
throw new Error(`Unknown action kind in snapshot: ${step.actionKind}`);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Replay a navigation step from snapshot
|
|
54
|
+
*
|
|
55
|
+
* @param page - Playwright Page object
|
|
56
|
+
* @param step - Snapshot step containing navigation action
|
|
57
|
+
* @param testInfo - Playwright TestInfo for annotations (optional)
|
|
58
|
+
* @returns Promise that resolves when navigation is complete
|
|
59
|
+
*/
|
|
60
|
+
async function replayNavigationStep(page, step, testInfo) {
|
|
61
|
+
const actionDetails = step.actionDetails;
|
|
62
|
+
await executeNavigationAction(page, actionDetails, testInfo, step.stepText);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Replay a click step from snapshot
|
|
66
|
+
*
|
|
67
|
+
* Reconstructs the locator from stored target element information.
|
|
68
|
+
*
|
|
69
|
+
* @param page - Playwright Page object
|
|
70
|
+
* @param step - Snapshot step containing click action
|
|
71
|
+
* @param testInfo - Playwright TestInfo for annotations (optional)
|
|
72
|
+
* @returns Promise that resolves when click is complete
|
|
73
|
+
*/
|
|
74
|
+
async function replayClickStep(page, step, testInfo) {
|
|
75
|
+
const actionDetails = step.actionDetails;
|
|
76
|
+
// Reconstruct the target element from snapshot using best selector
|
|
77
|
+
if (!step.targetElement || !step.targetElement.selector) {
|
|
78
|
+
throw new Error(`Snapshot step ${step.stepIndex} (click) is missing targetElement with selector`);
|
|
79
|
+
}
|
|
80
|
+
// Use the stored selector descriptor (primary) to reconstruct the locator
|
|
81
|
+
// Fall back to mimicId if selector fails
|
|
82
|
+
let element;
|
|
83
|
+
try {
|
|
84
|
+
element = getFromSelector(page, step.targetElement.selector);
|
|
85
|
+
// Verify the element exists
|
|
86
|
+
await element.waitFor({ timeout: 5000 });
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
// Selector might be stale, fall back to marker ID
|
|
90
|
+
if (step.targetElement.mimicId !== undefined) {
|
|
91
|
+
console.warn(`Stored selector failed for step ${step.stepIndex}, using marker ID ${step.targetElement.mimicId}`);
|
|
92
|
+
element = getMimic(page, step.targetElement.mimicId);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
throw new Error(`Snapshot step ${step.stepIndex} (click) selector failed and no mimicId fallback available`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Find the selected candidate from the click action result
|
|
99
|
+
// The first candidate should be the one we used originally
|
|
100
|
+
const selectedCandidate = actionDetails.candidates[0];
|
|
101
|
+
if (!selectedCandidate) {
|
|
102
|
+
throw new Error(`Snapshot step ${step.stepIndex} (click) has no candidates in actionDetails`);
|
|
103
|
+
}
|
|
104
|
+
await executeClickAction(element, actionDetails, selectedCandidate, testInfo, step.stepText);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Replay a form step from snapshot
|
|
108
|
+
*
|
|
109
|
+
* Reconstructs the form element locator from stored target element information.
|
|
110
|
+
*
|
|
111
|
+
* @param page - Playwright Page object
|
|
112
|
+
* @param step - Snapshot step containing form action
|
|
113
|
+
* @param testInfo - Playwright TestInfo for annotations (optional)
|
|
114
|
+
* @returns Promise that resolves when form action is complete
|
|
115
|
+
*/
|
|
116
|
+
async function replayFormStep(page, step, testInfo) {
|
|
117
|
+
const actionDetails = step.actionDetails;
|
|
118
|
+
// Reconstruct the target element from snapshot using best selector
|
|
119
|
+
if (!step.targetElement || !step.targetElement.selector) {
|
|
120
|
+
throw new Error(`Snapshot step ${step.stepIndex} (form) is missing targetElement with selector`);
|
|
121
|
+
}
|
|
122
|
+
// Use the stored selector descriptor (primary) to reconstruct the locator
|
|
123
|
+
// Fall back to mimicId if selector fails
|
|
124
|
+
let element;
|
|
125
|
+
try {
|
|
126
|
+
element = getFromSelector(page, step.targetElement.selector);
|
|
127
|
+
// Verify the element exists
|
|
128
|
+
await element.waitFor({ timeout: 5000 });
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
// Selector might be stale, fall back to marker ID
|
|
132
|
+
if (step.targetElement.mimicId !== undefined) {
|
|
133
|
+
console.warn(`Stored selector failed for step ${step.stepIndex}, using marker ID ${step.targetElement.mimicId}`);
|
|
134
|
+
element = getMimic(page, step.targetElement.mimicId);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
throw new Error(`Snapshot step ${step.stepIndex} (form) selector failed and no mimicId fallback available`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
await executeFormAction(page, actionDetails, element, testInfo, step.stepText);
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=replay.js.map
|