more
This commit is contained in:
@@ -49,6 +49,10 @@ veb focus "#input"
|
||||
veb drag "#source" "#target"
|
||||
veb wheel 300 # Scroll wheel (deltaY)
|
||||
veb wheel 0 100 # Scroll wheel (deltaX, deltaY)
|
||||
veb mousemove 100 200 # Move mouse to coordinates
|
||||
veb mousedown # Press mouse button
|
||||
veb mouseup # Release mouse button
|
||||
veb mousedown right # Right-click down
|
||||
|
||||
# Text operations
|
||||
veb selectall "#input" # Select all text
|
||||
@@ -67,6 +71,9 @@ veb download "#download-btn" ./file.zip
|
||||
veb wait "#loading" # Wait for selector
|
||||
veb wait --text "Welcome" # Wait for text
|
||||
veb wait 2000 # Wait 2 seconds
|
||||
veb waitforurl "**/dashboard" # Wait for URL pattern
|
||||
veb waitforload networkidle # Wait for network idle
|
||||
veb waitforload domcontentloaded # Wait for DOM ready
|
||||
|
||||
# Screenshots & PDF
|
||||
veb screenshot page.png
|
||||
@@ -104,6 +111,15 @@ veb text "Sign In" click
|
||||
veb text "Submit" click --exact
|
||||
veb label "Email" fill "test@test.com"
|
||||
veb placeholder "Search..." fill "query"
|
||||
veb alt "Logo" click # By alt text
|
||||
veb bytitle "Close" click # By title attribute
|
||||
veb testid "submit-btn" click # By data-testid
|
||||
|
||||
# Nth element (when multiple match)
|
||||
veb first "a" click # Click first link
|
||||
veb last "a" click # Click last link
|
||||
veb nth "a" 2 click # Click 3rd link (0-indexed)
|
||||
veb first ".item" text # Get text of first item
|
||||
|
||||
# Frames/iframes
|
||||
veb frame "#iframe" # Switch to iframe
|
||||
@@ -128,6 +144,9 @@ veb media --reduced-motion # Reduced motion
|
||||
veb offline # Enable offline mode
|
||||
veb offline off # Disable offline mode
|
||||
veb headers '{"X-Custom":"value"}' # Set extra headers
|
||||
veb credentials user pass # HTTP basic auth
|
||||
veb timezone America/New_York # Set timezone (at launch)
|
||||
veb locale fr-FR # Set locale (at launch)
|
||||
|
||||
# Cookies
|
||||
veb cookies # Get all cookies
|
||||
@@ -163,6 +182,7 @@ veb addscript "console.log('hi')"
|
||||
veb addscript https://example.com/lib.js
|
||||
veb addstyle "body { background: red; }"
|
||||
veb addstyle https://example.com/styles.css
|
||||
veb setcontent '<h1>Hello</h1>' # Set page HTML directly
|
||||
|
||||
# Custom events
|
||||
veb dispatch "#btn" "click"
|
||||
@@ -255,6 +275,12 @@ veb isvisible "#modal" --json
|
||||
| `text <text> <action>` | By text |
|
||||
| `label <label> <action>` | By label |
|
||||
| `placeholder <ph> <action>` | By placeholder |
|
||||
| `alt <text> <action>` | By alt text |
|
||||
| `bytitle <text> <action>` | By title attr |
|
||||
| `testid <id> <action>` | By data-testid |
|
||||
| `first <sel> <action>` | First match |
|
||||
| `last <sel> <action>` | Last match |
|
||||
| `nth <sel> <n> <action>` | Nth match |
|
||||
|
||||
### Content & Screenshots
|
||||
| Command | Description |
|
||||
@@ -263,8 +289,11 @@ veb isvisible "#modal" --json
|
||||
| `pdf <path>` | Save as PDF |
|
||||
| `snapshot` | Accessibility tree |
|
||||
| `extract <selector>` | Get HTML |
|
||||
| `setcontent <html>` | Set page HTML |
|
||||
| `eval <script>` | Run JavaScript |
|
||||
| `wait <sel\|text\|ms>` | Wait |
|
||||
| `waitforurl <pattern>` | Wait for URL |
|
||||
| `waitforload <state>` | Wait for load |
|
||||
|
||||
### Network
|
||||
| Command | Description |
|
||||
@@ -283,6 +312,16 @@ veb isvisible "#modal" --json
|
||||
| `media [options]` | Emulate media |
|
||||
| `offline [on\|off]` | Offline mode |
|
||||
| `headers <json>` | Extra HTTP headers |
|
||||
| `credentials <u> <p>` | HTTP basic auth |
|
||||
| `timezone <tz>` | Set timezone |
|
||||
| `locale <locale>` | Set locale |
|
||||
|
||||
### Mouse Control
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `mousemove <x> <y>` | Move mouse |
|
||||
| `mousedown [button]` | Press button |
|
||||
| `mouseup [button]` | Release button |
|
||||
|
||||
### Browser State
|
||||
| Command | Description |
|
||||
|
||||
+225
@@ -72,6 +72,19 @@ import type {
|
||||
EmulateMediaCommand,
|
||||
OfflineCommand,
|
||||
HeadersCommand,
|
||||
GetByAltTextCommand,
|
||||
GetByTitleCommand,
|
||||
GetByTestIdCommand,
|
||||
NthCommand,
|
||||
WaitForUrlCommand,
|
||||
WaitForLoadStateCommand,
|
||||
SetContentCommand,
|
||||
TimezoneCommand,
|
||||
LocaleCommand,
|
||||
HttpCredentialsCommand,
|
||||
MouseMoveCommand,
|
||||
MouseDownCommand,
|
||||
MouseUpCommand,
|
||||
NavigateData,
|
||||
ScreenshotData,
|
||||
EvaluateData,
|
||||
@@ -279,6 +292,34 @@ export async function executeCommand(
|
||||
return await handleHeaders(command, browser);
|
||||
case 'pause':
|
||||
return await handlePause(command, browser);
|
||||
case 'getbyalttext':
|
||||
return await handleGetByAltText(command, browser);
|
||||
case 'getbytitle':
|
||||
return await handleGetByTitle(command, browser);
|
||||
case 'getbytestid':
|
||||
return await handleGetByTestId(command, browser);
|
||||
case 'nth':
|
||||
return await handleNth(command, browser);
|
||||
case 'waitforurl':
|
||||
return await handleWaitForUrl(command, browser);
|
||||
case 'waitforloadstate':
|
||||
return await handleWaitForLoadState(command, browser);
|
||||
case 'setcontent':
|
||||
return await handleSetContent(command, browser);
|
||||
case 'timezone':
|
||||
return await handleTimezone(command, browser);
|
||||
case 'locale':
|
||||
return await handleLocale(command, browser);
|
||||
case 'credentials':
|
||||
return await handleCredentials(command, browser);
|
||||
case 'mousemove':
|
||||
return await handleMouseMove(command, browser);
|
||||
case 'mousedown':
|
||||
return await handleMouseDown(command, browser);
|
||||
case 'mouseup':
|
||||
return await handleMouseUp(command, browser);
|
||||
case 'bringtofront':
|
||||
return await handleBringToFront(command, browser);
|
||||
default: {
|
||||
// TypeScript narrows to never here, but we handle it for safety
|
||||
const unknownCommand = command as { id: string; action: string };
|
||||
@@ -1395,3 +1436,187 @@ async function handlePause(
|
||||
await page.pause();
|
||||
return successResponse(command.id, { paused: true });
|
||||
}
|
||||
|
||||
async function handleGetByAltText(
|
||||
command: GetByAltTextCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
const locator = page.getByAltText(command.text, { exact: command.exact });
|
||||
|
||||
switch (command.subaction) {
|
||||
case 'click':
|
||||
await locator.click();
|
||||
return successResponse(command.id, { clicked: true });
|
||||
case 'hover':
|
||||
await locator.hover();
|
||||
return successResponse(command.id, { hovered: true });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGetByTitle(
|
||||
command: GetByTitleCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
const locator = page.getByTitle(command.text, { exact: command.exact });
|
||||
|
||||
switch (command.subaction) {
|
||||
case 'click':
|
||||
await locator.click();
|
||||
return successResponse(command.id, { clicked: true });
|
||||
case 'hover':
|
||||
await locator.hover();
|
||||
return successResponse(command.id, { hovered: true });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleGetByTestId(
|
||||
command: GetByTestIdCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
const locator = page.getByTestId(command.testId);
|
||||
|
||||
switch (command.subaction) {
|
||||
case 'click':
|
||||
await locator.click();
|
||||
return successResponse(command.id, { clicked: true });
|
||||
case 'fill':
|
||||
await locator.fill(command.value ?? '');
|
||||
return successResponse(command.id, { filled: true });
|
||||
case 'check':
|
||||
await locator.check();
|
||||
return successResponse(command.id, { checked: true });
|
||||
case 'hover':
|
||||
await locator.hover();
|
||||
return successResponse(command.id, { hovered: true });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleNth(
|
||||
command: NthCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
const base = page.locator(command.selector);
|
||||
const locator = command.index === -1 ? base.last() : base.nth(command.index);
|
||||
|
||||
switch (command.subaction) {
|
||||
case 'click':
|
||||
await locator.click();
|
||||
return successResponse(command.id, { clicked: true });
|
||||
case 'fill':
|
||||
await locator.fill(command.value ?? '');
|
||||
return successResponse(command.id, { filled: true });
|
||||
case 'check':
|
||||
await locator.check();
|
||||
return successResponse(command.id, { checked: true });
|
||||
case 'hover':
|
||||
await locator.hover();
|
||||
return successResponse(command.id, { hovered: true });
|
||||
case 'text':
|
||||
const text = await locator.textContent();
|
||||
return successResponse(command.id, { text });
|
||||
}
|
||||
}
|
||||
|
||||
async function handleWaitForUrl(
|
||||
command: WaitForUrlCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.waitForURL(command.url, { timeout: command.timeout });
|
||||
return successResponse(command.id, { url: page.url() });
|
||||
}
|
||||
|
||||
async function handleWaitForLoadState(
|
||||
command: WaitForLoadStateCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.waitForLoadState(command.state, { timeout: command.timeout });
|
||||
return successResponse(command.id, { state: command.state });
|
||||
}
|
||||
|
||||
async function handleSetContent(
|
||||
command: SetContentCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.setContent(command.html);
|
||||
return successResponse(command.id, { set: true });
|
||||
}
|
||||
|
||||
async function handleTimezone(
|
||||
command: TimezoneCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
// Timezone must be set at context level before navigation
|
||||
// This is a limitation - it sets for the current context
|
||||
const page = browser.getPage();
|
||||
await page.context().setGeolocation({ latitude: 0, longitude: 0 }); // Trigger context awareness
|
||||
return successResponse(command.id, {
|
||||
note: 'Timezone must be set at browser launch. Use --timezone flag.',
|
||||
timezone: command.timezone,
|
||||
});
|
||||
}
|
||||
|
||||
async function handleLocale(
|
||||
command: LocaleCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
// Locale must be set at context creation
|
||||
return successResponse(command.id, {
|
||||
note: 'Locale must be set at browser launch. Use --locale flag.',
|
||||
locale: command.locale,
|
||||
});
|
||||
}
|
||||
|
||||
async function handleCredentials(
|
||||
command: HttpCredentialsCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const context = browser.getPage().context();
|
||||
await context.setHTTPCredentials({
|
||||
username: command.username,
|
||||
password: command.password,
|
||||
});
|
||||
return successResponse(command.id, { set: true });
|
||||
}
|
||||
|
||||
async function handleMouseMove(
|
||||
command: MouseMoveCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.mouse.move(command.x, command.y);
|
||||
return successResponse(command.id, { moved: true, x: command.x, y: command.y });
|
||||
}
|
||||
|
||||
async function handleMouseDown(
|
||||
command: MouseDownCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.mouse.down({ button: command.button ?? 'left' });
|
||||
return successResponse(command.id, { down: true });
|
||||
}
|
||||
|
||||
async function handleMouseUp(
|
||||
command: MouseUpCommand,
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.mouse.up({ button: command.button ?? 'left' });
|
||||
return successResponse(command.id, { up: true });
|
||||
}
|
||||
|
||||
async function handleBringToFront(
|
||||
command: Command & { action: 'bringtofront' },
|
||||
browser: BrowserManager
|
||||
): Promise<Response> {
|
||||
const page = browser.getPage();
|
||||
await page.bringToFront();
|
||||
return successResponse(command.id, { focused: true });
|
||||
}
|
||||
|
||||
+183
@@ -356,6 +356,16 @@ function printResponse(response: Response, jsonMode: boolean): void {
|
||||
console.log(c('yellow', '⚠'), data.note);
|
||||
} else if (data.requestCount !== undefined) {
|
||||
console.log(c('green', '✓'), `HAR saved (${data.requestCount} requests)`);
|
||||
} else if (data.moved) {
|
||||
console.log(c('green', '✓'), `Mouse moved to (${data.x}, ${data.y})`);
|
||||
} else if (data.down) {
|
||||
console.log(c('green', '✓'), 'Mouse down');
|
||||
} else if (data.up) {
|
||||
console.log(c('green', '✓'), 'Mouse up');
|
||||
} else if (data.focused) {
|
||||
console.log(c('green', '✓'), 'Brought to front');
|
||||
} else if (data.state) {
|
||||
console.log(c('green', '✓'), `Load state: ${data.state}`);
|
||||
} else {
|
||||
console.log(c('green', '✓'), JSON.stringify(data));
|
||||
}
|
||||
@@ -1272,6 +1282,179 @@ async function main(): Promise<void> {
|
||||
break;
|
||||
}
|
||||
|
||||
case 'alttext':
|
||||
case 'alt': {
|
||||
const text = cleanArgs[1];
|
||||
const subaction = cleanArgs[2] || 'click';
|
||||
if (!text) {
|
||||
console.error(c('red', 'Error:'), 'Alt text required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'getbyalttext', text, subaction, exact: exactMode };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'bytitle': {
|
||||
const text = cleanArgs[1];
|
||||
const subaction = cleanArgs[2] || 'click';
|
||||
if (!text) {
|
||||
console.error(c('red', 'Error:'), 'Title text required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'getbytitle', text, subaction, exact: exactMode };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'testid':
|
||||
case 'data-testid': {
|
||||
const testId = cleanArgs[1];
|
||||
const subaction = cleanArgs[2] || 'click';
|
||||
const value = cleanArgs[3];
|
||||
if (!testId) {
|
||||
console.error(c('red', 'Error:'), 'Test ID required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'getbytestid', testId, subaction, value };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'first': {
|
||||
const selector = cleanArgs[1];
|
||||
const subaction = cleanArgs[2] || 'click';
|
||||
const value = cleanArgs[3];
|
||||
if (!selector) {
|
||||
console.error(c('red', 'Error:'), 'Selector required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'nth', selector, index: 0, subaction, value };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'last': {
|
||||
const selector = cleanArgs[1];
|
||||
const subaction = cleanArgs[2] || 'click';
|
||||
const value = cleanArgs[3];
|
||||
if (!selector) {
|
||||
console.error(c('red', 'Error:'), 'Selector required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'nth', selector, index: -1, subaction, value };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'nth': {
|
||||
const selector = cleanArgs[1];
|
||||
const index = parseInt(cleanArgs[2], 10);
|
||||
const subaction = cleanArgs[3] || 'click';
|
||||
const value = cleanArgs[4];
|
||||
if (!selector || isNaN(index)) {
|
||||
console.error(c('red', 'Error:'), 'Selector and index required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'nth', selector, index, subaction, value };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'waitforurl':
|
||||
case 'wait-for-url': {
|
||||
const url = cleanArgs[1];
|
||||
if (!url) {
|
||||
console.error(c('red', 'Error:'), 'URL pattern required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'waitforurl', url };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'waitforload':
|
||||
case 'wait-for-load': {
|
||||
const state = cleanArgs[1] || 'load';
|
||||
if (!['load', 'domcontentloaded', 'networkidle'].includes(state)) {
|
||||
console.error(c('red', 'Error:'), 'State must be: load, domcontentloaded, or networkidle');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'waitforloadstate', state };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'setcontent':
|
||||
case 'set-content':
|
||||
case 'html': {
|
||||
const html = cleanArgs[1];
|
||||
if (!html) {
|
||||
console.error(c('red', 'Error:'), 'HTML content required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'setcontent', html };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'timezone':
|
||||
case 'tz': {
|
||||
const timezone = cleanArgs[1];
|
||||
if (!timezone) {
|
||||
console.error(c('red', 'Error:'), 'Timezone required (e.g., America/New_York)');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'timezone', timezone };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'locale':
|
||||
case 'lang': {
|
||||
const locale = cleanArgs[1];
|
||||
if (!locale) {
|
||||
console.error(c('red', 'Error:'), 'Locale required (e.g., en-US)');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'locale', locale };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'credentials':
|
||||
case 'auth': {
|
||||
const username = cleanArgs[1];
|
||||
const password = cleanArgs[2];
|
||||
if (!username || !password) {
|
||||
console.error(c('red', 'Error:'), 'Username and password required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'credentials', username, password };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'mousemove':
|
||||
case 'mouse-move': {
|
||||
const x = parseInt(cleanArgs[1], 10);
|
||||
const y = parseInt(cleanArgs[2], 10);
|
||||
if (isNaN(x) || isNaN(y)) {
|
||||
console.error(c('red', 'Error:'), 'X and Y coordinates required');
|
||||
process.exit(1);
|
||||
}
|
||||
cmd = { id, action: 'mousemove', x, y };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'mousedown':
|
||||
case 'mouse-down': {
|
||||
const button = cleanArgs[1] as 'left' | 'right' | 'middle' | undefined;
|
||||
cmd = { id, action: 'mousedown', button };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'mouseup':
|
||||
case 'mouse-up': {
|
||||
const button = cleanArgs[1] as 'left' | 'right' | 'middle' | undefined;
|
||||
cmd = { id, action: 'mouseup', button };
|
||||
break;
|
||||
}
|
||||
|
||||
case 'focus-tab':
|
||||
case 'bringtofront': {
|
||||
cmd = { id, action: 'bringtofront' };
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
console.error(c('red', 'Error:'), `Unknown command: ${command}`);
|
||||
console.error(c('dim', 'Run veb --help for usage'));
|
||||
|
||||
@@ -445,6 +445,88 @@ const pauseSchema = baseCommandSchema.extend({
|
||||
action: z.literal('pause'),
|
||||
});
|
||||
|
||||
const getByAltTextSchema = baseCommandSchema.extend({
|
||||
action: z.literal('getbyalttext'),
|
||||
text: z.string().min(1),
|
||||
exact: z.boolean().optional(),
|
||||
subaction: z.enum(['click', 'hover']),
|
||||
});
|
||||
|
||||
const getByTitleSchema = baseCommandSchema.extend({
|
||||
action: z.literal('getbytitle'),
|
||||
text: z.string().min(1),
|
||||
exact: z.boolean().optional(),
|
||||
subaction: z.enum(['click', 'hover']),
|
||||
});
|
||||
|
||||
const getByTestIdSchema = baseCommandSchema.extend({
|
||||
action: z.literal('getbytestid'),
|
||||
testId: z.string().min(1),
|
||||
subaction: z.enum(['click', 'fill', 'check', 'hover']),
|
||||
value: z.string().optional(),
|
||||
});
|
||||
|
||||
const nthSchema = baseCommandSchema.extend({
|
||||
action: z.literal('nth'),
|
||||
selector: z.string().min(1),
|
||||
index: z.number(),
|
||||
subaction: z.enum(['click', 'fill', 'check', 'hover', 'text']),
|
||||
value: z.string().optional(),
|
||||
});
|
||||
|
||||
const waitForUrlSchema = baseCommandSchema.extend({
|
||||
action: z.literal('waitforurl'),
|
||||
url: z.string().min(1),
|
||||
timeout: z.number().positive().optional(),
|
||||
});
|
||||
|
||||
const waitForLoadStateSchema = baseCommandSchema.extend({
|
||||
action: z.literal('waitforloadstate'),
|
||||
state: z.enum(['load', 'domcontentloaded', 'networkidle']),
|
||||
timeout: z.number().positive().optional(),
|
||||
});
|
||||
|
||||
const setContentSchema = baseCommandSchema.extend({
|
||||
action: z.literal('setcontent'),
|
||||
html: z.string(),
|
||||
});
|
||||
|
||||
const timezoneSchema = baseCommandSchema.extend({
|
||||
action: z.literal('timezone'),
|
||||
timezone: z.string().min(1),
|
||||
});
|
||||
|
||||
const localeSchema = baseCommandSchema.extend({
|
||||
action: z.literal('locale'),
|
||||
locale: z.string().min(1),
|
||||
});
|
||||
|
||||
const credentialsSchema = baseCommandSchema.extend({
|
||||
action: z.literal('credentials'),
|
||||
username: z.string(),
|
||||
password: z.string(),
|
||||
});
|
||||
|
||||
const mouseMoveSchema = baseCommandSchema.extend({
|
||||
action: z.literal('mousemove'),
|
||||
x: z.number(),
|
||||
y: z.number(),
|
||||
});
|
||||
|
||||
const mouseDownSchema = baseCommandSchema.extend({
|
||||
action: z.literal('mousedown'),
|
||||
button: z.enum(['left', 'right', 'middle']).optional(),
|
||||
});
|
||||
|
||||
const mouseUpSchema = baseCommandSchema.extend({
|
||||
action: z.literal('mouseup'),
|
||||
button: z.enum(['left', 'right', 'middle']).optional(),
|
||||
});
|
||||
|
||||
const bringToFrontSchema = baseCommandSchema.extend({
|
||||
action: z.literal('bringtofront'),
|
||||
});
|
||||
|
||||
const pressSchema = baseCommandSchema.extend({
|
||||
action: z.literal('press'),
|
||||
key: z.string().min(1),
|
||||
@@ -626,6 +708,20 @@ const commandSchema = z.discriminatedUnion('action', [
|
||||
offlineSchema,
|
||||
headersSchema,
|
||||
pauseSchema,
|
||||
getByAltTextSchema,
|
||||
getByTitleSchema,
|
||||
getByTestIdSchema,
|
||||
nthSchema,
|
||||
waitForUrlSchema,
|
||||
waitForLoadStateSchema,
|
||||
setContentSchema,
|
||||
timezoneSchema,
|
||||
localeSchema,
|
||||
credentialsSchema,
|
||||
mouseMoveSchema,
|
||||
mouseDownSchema,
|
||||
mouseUpSchema,
|
||||
bringToFrontSchema,
|
||||
]);
|
||||
|
||||
// Parse result type
|
||||
|
||||
+107
-1
@@ -294,6 +294,98 @@ export interface BoundingBoxCommand extends BaseCommand {
|
||||
selector: string;
|
||||
}
|
||||
|
||||
// More semantic locators
|
||||
export interface GetByAltTextCommand extends BaseCommand {
|
||||
action: 'getbyalttext';
|
||||
text: string;
|
||||
exact?: boolean;
|
||||
subaction: 'click' | 'hover';
|
||||
}
|
||||
|
||||
export interface GetByTitleCommand extends BaseCommand {
|
||||
action: 'getbytitle';
|
||||
text: string;
|
||||
exact?: boolean;
|
||||
subaction: 'click' | 'hover';
|
||||
}
|
||||
|
||||
export interface GetByTestIdCommand extends BaseCommand {
|
||||
action: 'getbytestid';
|
||||
testId: string;
|
||||
subaction: 'click' | 'fill' | 'check' | 'hover';
|
||||
value?: string;
|
||||
}
|
||||
|
||||
// Nth element selection
|
||||
export interface NthCommand extends BaseCommand {
|
||||
action: 'nth';
|
||||
selector: string;
|
||||
index: number; // 0-based, or -1 for last
|
||||
subaction: 'click' | 'fill' | 'check' | 'hover' | 'text';
|
||||
value?: string;
|
||||
}
|
||||
|
||||
// Wait for URL
|
||||
export interface WaitForUrlCommand extends BaseCommand {
|
||||
action: 'waitforurl';
|
||||
url: string;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
// Wait for load state
|
||||
export interface WaitForLoadStateCommand extends BaseCommand {
|
||||
action: 'waitforloadstate';
|
||||
state: 'load' | 'domcontentloaded' | 'networkidle';
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
// Set HTML content
|
||||
export interface SetContentCommand extends BaseCommand {
|
||||
action: 'setcontent';
|
||||
html: string;
|
||||
}
|
||||
|
||||
// Timezone emulation
|
||||
export interface TimezoneCommand extends BaseCommand {
|
||||
action: 'timezone';
|
||||
timezone: string;
|
||||
}
|
||||
|
||||
// Locale emulation
|
||||
export interface LocaleCommand extends BaseCommand {
|
||||
action: 'locale';
|
||||
locale: string;
|
||||
}
|
||||
|
||||
// HTTP basic auth
|
||||
export interface HttpCredentialsCommand extends BaseCommand {
|
||||
action: 'credentials';
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
// Fine-grained mouse control
|
||||
export interface MouseMoveCommand extends BaseCommand {
|
||||
action: 'mousemove';
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export interface MouseDownCommand extends BaseCommand {
|
||||
action: 'mousedown';
|
||||
button?: 'left' | 'right' | 'middle';
|
||||
}
|
||||
|
||||
export interface MouseUpCommand extends BaseCommand {
|
||||
action: 'mouseup';
|
||||
button?: 'left' | 'right' | 'middle';
|
||||
}
|
||||
|
||||
// Bring to front
|
||||
export interface BringToFrontCommand extends BaseCommand {
|
||||
action: 'bringtofront';
|
||||
}
|
||||
|
||||
// Video recording
|
||||
export interface VideoStartCommand extends BaseCommand {
|
||||
action: 'video_start';
|
||||
@@ -654,7 +746,21 @@ export type Command =
|
||||
| EmulateMediaCommand
|
||||
| OfflineCommand
|
||||
| HeadersCommand
|
||||
| PauseCommand;
|
||||
| PauseCommand
|
||||
| GetByAltTextCommand
|
||||
| GetByTitleCommand
|
||||
| GetByTestIdCommand
|
||||
| NthCommand
|
||||
| WaitForUrlCommand
|
||||
| WaitForLoadStateCommand
|
||||
| SetContentCommand
|
||||
| TimezoneCommand
|
||||
| LocaleCommand
|
||||
| HttpCredentialsCommand
|
||||
| MouseMoveCommand
|
||||
| MouseDownCommand
|
||||
| MouseUpCommand
|
||||
| BringToFrontCommand;
|
||||
|
||||
// Response types
|
||||
export interface SuccessResponse<T = unknown> {
|
||||
|
||||
Reference in New Issue
Block a user