@stablyai/playwright-base 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/extract.d.ts +2 -2
- package/dist/ai/extract.js +23 -56
- package/dist/ai/verify-prompt.js +15 -18
- package/dist/expect.d.ts +2 -2
- package/dist/expect.js +13 -58
- package/dist/image-compare.d.ts +5 -0
- package/dist/image-compare.js +41 -0
- package/dist/index.d.ts +53 -11
- package/dist/index.js +5 -15
- package/dist/playwright-augment/augment.d.ts +1 -1
- package/dist/playwright-augment/augment.js +22 -31
- package/dist/playwright-augment/methods/agent.d.ts +1 -1
- package/dist/playwright-augment/methods/agent.js +3 -6
- package/dist/playwright-augment/methods/auto-heal.d.ts +16 -0
- package/dist/playwright-augment/methods/auto-heal.js +7 -0
- package/dist/playwright-augment/methods/extract.d.ts +2 -2
- package/dist/playwright-augment/methods/extract.js +6 -11
- package/dist/playwright-augment/methods/test-info.d.ts +16 -0
- package/dist/playwright-augment/methods/test-info.js +96 -0
- package/dist/playwright-type-predicates.d.ts +1 -1
- package/dist/playwright-type-predicates.js +8 -12
- package/dist/runtime.js +4 -9
- package/dist/screenshot.d.ts +3 -0
- package/dist/screenshot.js +38 -0
- package/package.json +12 -6
- package/src/ai/extract.ts +19 -19
- package/src/ai/verify-prompt.ts +8 -8
- package/src/expect.ts +15 -72
- package/src/image-compare.ts +69 -0
- package/src/index.ts +53 -11
- package/src/playwright-augment/augment.ts +33 -33
- package/src/playwright-augment/methods/agent.ts +2 -2
- package/src/playwright-augment/methods/extract.ts +2 -2
- package/src/playwright-type-predicates.ts +7 -7
- package/src/runtime.ts +1 -1
- package/src/screenshot.ts +52 -0
- package/tsconfig.json +15 -5
package/dist/ai/extract.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Locator, Page } from
|
|
2
|
-
import * as z4 from
|
|
1
|
+
import type { Locator, Page } from "@stablyai/internal-playwright-test";
|
|
2
|
+
import * as z4 from "zod/v4/core";
|
|
3
3
|
export type ExtractSchema = z4.$ZodType;
|
|
4
4
|
export type SchemaOutput<T extends ExtractSchema> = z4.output<T>;
|
|
5
5
|
type ExtractSubject = Page | Locator;
|
package/dist/ai/extract.js
CHANGED
|
@@ -1,73 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.extract = extract;
|
|
37
|
-
const z4 = __importStar(require("zod/v4/core"));
|
|
38
|
-
const zod_1 = require("zod");
|
|
39
|
-
const runtime_1 = require("../runtime");
|
|
40
|
-
const EXTRACT_ENDPOINT = 'https://api.stably.ai/internal/v1/extract';
|
|
41
|
-
const zSuccess = zod_1.z.object({ value: zod_1.z.unknown() });
|
|
42
|
-
const zError = zod_1.z.object({ error: zod_1.z.string() });
|
|
1
|
+
import * as z4 from "zod/v4/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { requireApiKey } from "../runtime";
|
|
4
|
+
const EXTRACT_ENDPOINT = "https://api.stably.ai/internal/v1/extract";
|
|
5
|
+
const zSuccess = z.object({ value: z.unknown() });
|
|
6
|
+
const zError = z.object({ error: z.string() });
|
|
43
7
|
class ExtractValidationError extends Error {
|
|
8
|
+
issues;
|
|
44
9
|
constructor(message, issues) {
|
|
45
10
|
super(message);
|
|
46
11
|
this.issues = issues;
|
|
47
|
-
this.name =
|
|
12
|
+
this.name = "ExtractValidationError";
|
|
48
13
|
}
|
|
49
14
|
}
|
|
50
15
|
async function validateWithSchema(schema, value) {
|
|
51
16
|
const result = await z4.safeParseAsync(schema, value);
|
|
52
17
|
if (!result.success) {
|
|
53
|
-
throw new ExtractValidationError(
|
|
18
|
+
throw new ExtractValidationError("Validation failed", result.error.issues);
|
|
54
19
|
}
|
|
55
20
|
return result.data;
|
|
56
21
|
}
|
|
57
|
-
async function extract({ prompt, pageOrLocator, schema, }) {
|
|
22
|
+
export async function extract({ prompt, pageOrLocator, schema, }) {
|
|
58
23
|
const jsonSchema = schema ? z4.toJSONSchema(schema) : undefined;
|
|
59
|
-
const apiKey =
|
|
24
|
+
const apiKey = requireApiKey();
|
|
60
25
|
const form = new FormData();
|
|
61
|
-
form.append(
|
|
26
|
+
form.append("prompt", prompt);
|
|
62
27
|
if (jsonSchema) {
|
|
63
|
-
form.append(
|
|
28
|
+
form.append("jsonSchema", JSON.stringify(jsonSchema));
|
|
64
29
|
}
|
|
65
|
-
const pngBuffer = await pageOrLocator.screenshot({ type:
|
|
30
|
+
const pngBuffer = await pageOrLocator.screenshot({ type: "png" }); // Buffer
|
|
66
31
|
const u8 = Uint8Array.from(pngBuffer); // strips Buffer type → plain Uint8Array
|
|
67
|
-
const blob = new Blob([u8], { type:
|
|
68
|
-
form.append(
|
|
32
|
+
const blob = new Blob([u8], { type: "image/png" });
|
|
33
|
+
form.append("image", blob, "screenshot.png");
|
|
69
34
|
const response = await fetch(EXTRACT_ENDPOINT, {
|
|
70
|
-
method:
|
|
35
|
+
method: "POST",
|
|
71
36
|
headers: {
|
|
72
37
|
Authorization: `Bearer ${apiKey}`,
|
|
73
38
|
},
|
|
@@ -76,10 +41,12 @@ async function extract({ prompt, pageOrLocator, schema, }) {
|
|
|
76
41
|
const parsed = await response.json().catch(() => undefined);
|
|
77
42
|
if (response.ok) {
|
|
78
43
|
const { value } = zSuccess.parse(parsed);
|
|
79
|
-
return
|
|
80
|
-
|
|
81
|
-
|
|
44
|
+
return schema
|
|
45
|
+
? await validateWithSchema(schema, value)
|
|
46
|
+
: typeof value === "string"
|
|
47
|
+
? value
|
|
48
|
+
: JSON.stringify(value);
|
|
82
49
|
}
|
|
83
50
|
const err = zError.safeParse(parsed);
|
|
84
|
-
throw new Error(`Extract failed (${response.status})${err.success ? `: ${err.data.error}` :
|
|
51
|
+
throw new Error(`Extract failed (${response.status})${err.success ? `: ${err.data.error}` : ""}`);
|
|
85
52
|
}
|
package/dist/ai/verify-prompt.js
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const zSuccess = zod_1.z.object({
|
|
8
|
-
success: zod_1.z.boolean(),
|
|
9
|
-
reason: zod_1.z.string().optional(),
|
|
1
|
+
const PROMPT_ASSERTION_ENDPOINT = "https://api.stably.ai/internal/v1/assert";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { requireApiKey } from "../runtime";
|
|
4
|
+
const zSuccess = z.object({
|
|
5
|
+
success: z.boolean(),
|
|
6
|
+
reason: z.string().optional(),
|
|
10
7
|
});
|
|
11
|
-
const zError =
|
|
12
|
-
error:
|
|
8
|
+
const zError = z.object({
|
|
9
|
+
error: z.string(),
|
|
13
10
|
});
|
|
14
|
-
async function verifyPrompt({ prompt, screenshot, }) {
|
|
15
|
-
const apiKey =
|
|
11
|
+
export async function verifyPrompt({ prompt, screenshot, }) {
|
|
12
|
+
const apiKey = requireApiKey();
|
|
16
13
|
const form = new FormData();
|
|
17
|
-
form.append(
|
|
14
|
+
form.append("prompt", prompt);
|
|
18
15
|
const u8 = Uint8Array.from(screenshot);
|
|
19
|
-
const blob = new Blob([u8], { type:
|
|
20
|
-
form.append(
|
|
16
|
+
const blob = new Blob([u8], { type: "image/png" });
|
|
17
|
+
form.append("image", blob, "screenshot.png");
|
|
21
18
|
const response = await fetch(PROMPT_ASSERTION_ENDPOINT, {
|
|
22
|
-
method:
|
|
19
|
+
method: "POST",
|
|
23
20
|
headers: {
|
|
24
21
|
Authorization: `Bearer ${apiKey}`,
|
|
25
22
|
},
|
|
@@ -34,5 +31,5 @@ async function verifyPrompt({ prompt, screenshot, }) {
|
|
|
34
31
|
};
|
|
35
32
|
}
|
|
36
33
|
const err = zError.safeParse(parsed);
|
|
37
|
-
throw new Error(`Verify prompt failed (${response.status})${err.success ? `: ${err.data.error}` :
|
|
34
|
+
throw new Error(`Verify prompt failed (${response.status})${err.success ? `: ${err.data.error}` : ""}`);
|
|
38
35
|
}
|
package/dist/expect.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Locator, Page } from
|
|
2
|
-
import type { ScreenshotPromptOptions } from
|
|
1
|
+
import type { Locator, Page } from "@stablyai/internal-playwright-test";
|
|
2
|
+
import type { ScreenshotPromptOptions } from "./index";
|
|
3
3
|
type MatcherContext = {
|
|
4
4
|
isNot: boolean;
|
|
5
5
|
message?: () => string;
|
package/dist/expect.js
CHANGED
|
@@ -1,75 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const playwright_type_predicates_1 = require("./playwright-type-predicates");
|
|
5
|
-
const verify_prompt_1 = require("./ai/verify-prompt");
|
|
1
|
+
import { isLocator, isPage } from "./playwright-type-predicates";
|
|
2
|
+
import { verifyPrompt } from "./ai/verify-prompt";
|
|
3
|
+
import { takeStableScreenshot } from "./screenshot";
|
|
6
4
|
function createFailureMessage({ targetType, condition, didPass, isNot, reason, }) {
|
|
7
|
-
const expectation = isNot ?
|
|
8
|
-
const result = didPass ?
|
|
5
|
+
const expectation = isNot ? "not to satisfy" : "to satisfy";
|
|
6
|
+
const result = didPass ? "it did" : "it did not";
|
|
9
7
|
let message = `Expected ${targetType} ${expectation} ${JSON.stringify(condition)}, but ${result}.`;
|
|
10
8
|
if (reason) {
|
|
11
9
|
message += `\n\nReason: ${reason}`;
|
|
12
10
|
}
|
|
13
11
|
return message;
|
|
14
12
|
}
|
|
15
|
-
|
|
16
|
-
if (a.byteLength !== b.byteLength) {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
for (let index = 0; index < a.byteLength; index += 1) {
|
|
20
|
-
if (a[index] !== b[index]) {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
async function takeStableScreenshot(target, options) {
|
|
27
|
-
var _a;
|
|
28
|
-
const page = (0, playwright_type_predicates_1.isPage)(target) ? target : target.page();
|
|
29
|
-
// Use a small budget for stabilization within the overall assertion timeout.
|
|
30
|
-
// We allocate up to 25% of the total timeout (bounded between 300ms and 2000ms).
|
|
31
|
-
const totalTimeout = (_a = options === null || options === void 0 ? void 0 : options.timeout) !== null && _a !== void 0 ? _a : 5000;
|
|
32
|
-
// Budget is 25% of the total timeout
|
|
33
|
-
const stabilizationBudgetMs = Math.floor(totalTimeout * 0.25);
|
|
34
|
-
const stabilityBudgetMs = Math.min(2000, Math.max(300, stabilizationBudgetMs));
|
|
35
|
-
const deadline = Date.now() + stabilityBudgetMs;
|
|
36
|
-
let actual;
|
|
37
|
-
let previous;
|
|
38
|
-
const pollIntervals = [0, 100, 250, 500];
|
|
39
|
-
let isFirstIteration = true;
|
|
40
|
-
while (true) {
|
|
41
|
-
if (Date.now() >= deadline)
|
|
42
|
-
break;
|
|
43
|
-
const delay = pollIntervals.length ? pollIntervals.shift() : 1000;
|
|
44
|
-
if (delay) {
|
|
45
|
-
await page.waitForTimeout(delay);
|
|
46
|
-
}
|
|
47
|
-
previous = actual;
|
|
48
|
-
const rawScreenshot = await target.screenshot(options);
|
|
49
|
-
actual = Uint8Array.from(rawScreenshot);
|
|
50
|
-
if (!isFirstIteration &&
|
|
51
|
-
actual &&
|
|
52
|
-
previous &&
|
|
53
|
-
areScreenshotsEqual(actual, previous)) {
|
|
54
|
-
return actual;
|
|
55
|
-
}
|
|
56
|
-
isFirstIteration = false;
|
|
57
|
-
}
|
|
58
|
-
return actual !== null && actual !== void 0 ? actual : Uint8Array.from(await target.screenshot(options));
|
|
59
|
-
}
|
|
60
|
-
exports.stablyPlaywrightMatchers = {
|
|
13
|
+
export const stablyPlaywrightMatchers = {
|
|
61
14
|
async toMatchScreenshotPrompt(received, condition, options) {
|
|
62
|
-
const target =
|
|
63
|
-
|
|
15
|
+
const target = isPage(received)
|
|
16
|
+
? received
|
|
17
|
+
: isLocator(received)
|
|
18
|
+
? received
|
|
64
19
|
: undefined;
|
|
65
20
|
if (!target) {
|
|
66
21
|
// Should never happen
|
|
67
|
-
throw new Error(
|
|
22
|
+
throw new Error("toMatchScreenshotPrompt only supports Playwright Page and Locator instances.");
|
|
68
23
|
}
|
|
69
|
-
const targetType =
|
|
24
|
+
const targetType = isPage(target) ? "page" : "locator";
|
|
70
25
|
// Wait for two consecutive identical screenshots before sending to AI
|
|
71
26
|
const screenshot = await takeStableScreenshot(target, options);
|
|
72
|
-
const verifyResult = await
|
|
27
|
+
const verifyResult = await verifyPrompt({ prompt: condition, screenshot });
|
|
73
28
|
return {
|
|
74
29
|
pass: verifyResult.pass,
|
|
75
30
|
message: () => createFailureMessage({
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import pixelmatch from "pixelmatch";
|
|
2
|
+
import { PNG } from "pngjs";
|
|
3
|
+
import * as jpeg from "jpeg-js";
|
|
4
|
+
const isPng = (buffer) => {
|
|
5
|
+
return (buffer.length >= 8 &&
|
|
6
|
+
buffer[0] === 0x89 &&
|
|
7
|
+
buffer[1] === 0x50 &&
|
|
8
|
+
buffer[2] === 0x4e &&
|
|
9
|
+
buffer[3] === 0x47 &&
|
|
10
|
+
buffer[4] === 0x0d &&
|
|
11
|
+
buffer[5] === 0x0a &&
|
|
12
|
+
buffer[6] === 0x1a &&
|
|
13
|
+
buffer[7] === 0x0a);
|
|
14
|
+
};
|
|
15
|
+
const isJpeg = (buffer) => {
|
|
16
|
+
return buffer.length >= 2 && buffer[0] === 0xff && buffer[1] === 0xd8;
|
|
17
|
+
};
|
|
18
|
+
const decodeImage = (buffer) => {
|
|
19
|
+
if (isPng(buffer)) {
|
|
20
|
+
const png = PNG.sync.read(buffer);
|
|
21
|
+
return { data: png.data, width: png.width, height: png.height };
|
|
22
|
+
}
|
|
23
|
+
if (isJpeg(buffer)) {
|
|
24
|
+
const img = jpeg.decode(buffer, { maxMemoryUsageInMB: 1024 });
|
|
25
|
+
return { data: img.data, width: img.width, height: img.height };
|
|
26
|
+
}
|
|
27
|
+
// Default to PNG decode; if it fails upstream, treat as different sizes
|
|
28
|
+
const png = PNG.sync.read(buffer);
|
|
29
|
+
return { data: png.data, width: png.width, height: png.height };
|
|
30
|
+
};
|
|
31
|
+
export const imagesAreSimilar = ({ image1, image2, threshold, }) => {
|
|
32
|
+
const decodedImage1 = decodeImage(image1);
|
|
33
|
+
const decodedImage2 = decodeImage(image2);
|
|
34
|
+
if (decodedImage1.width !== decodedImage2.width ||
|
|
35
|
+
decodedImage1.height !== decodedImage2.height) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const diffRgbaData = new Uint8Array(decodedImage1.width * decodedImage1.height * 4);
|
|
39
|
+
const numDiffPixels = pixelmatch(decodedImage1.data, decodedImage2.data, diffRgbaData, decodedImage1.width, decodedImage1.height, { threshold });
|
|
40
|
+
return numDiffPixels === 0;
|
|
41
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,27 +1,69 @@
|
|
|
1
|
-
import type { Page } from
|
|
2
|
-
import type { LocatorDescribeOptions } from
|
|
3
|
-
import type { ExtractSchema, SchemaOutput } from
|
|
4
|
-
import { augmentBrowser, augmentBrowserContext, augmentBrowserType, augmentLocator, augmentPage } from
|
|
5
|
-
import { stablyPlaywrightMatchers } from
|
|
6
|
-
import { requireApiKey } from
|
|
7
|
-
export { setApiKey } from
|
|
8
|
-
export type { LocatorDescribeOptions } from
|
|
9
|
-
export type { ExtractSchema, SchemaOutput } from
|
|
10
|
-
export type ScreenshotPromptOptions = import(
|
|
1
|
+
import type { Page } from "@stablyai/internal-playwright-test";
|
|
2
|
+
import type { LocatorDescribeOptions } from "./playwright-augment/augment";
|
|
3
|
+
import type { ExtractSchema, SchemaOutput } from "./ai/extract";
|
|
4
|
+
import { augmentBrowser, augmentBrowserContext, augmentBrowserType, augmentLocator, augmentPage } from "./playwright-augment/augment";
|
|
5
|
+
import { stablyPlaywrightMatchers } from "./expect";
|
|
6
|
+
import { requireApiKey } from "./runtime";
|
|
7
|
+
export { setApiKey } from "./runtime";
|
|
8
|
+
export type { LocatorDescribeOptions } from "./playwright-augment/augment";
|
|
9
|
+
export type { ExtractSchema, SchemaOutput } from "./ai/extract";
|
|
10
|
+
export type ScreenshotPromptOptions = import("@stablyai/internal-playwright-test").PageAssertionsToHaveScreenshotOptions;
|
|
11
11
|
export { augmentBrowser, augmentBrowserContext, augmentBrowserType, augmentLocator, augmentPage, stablyPlaywrightMatchers, requireApiKey, };
|
|
12
12
|
export interface Expect<T = Page> {
|
|
13
13
|
toMatchScreenshotPrompt(condition: string, options?: ScreenshotPromptOptions): Promise<void>;
|
|
14
14
|
}
|
|
15
|
-
declare module
|
|
15
|
+
declare module "@stablyai/internal-playwright-test" {
|
|
16
16
|
interface Locator {
|
|
17
|
+
/**
|
|
18
|
+
* Extracts information from this locator using Stably AI.
|
|
19
|
+
*
|
|
20
|
+
* Takes a screenshot of the locator and uses AI to extract information based on the
|
|
21
|
+
* provided prompt. When a schema is provided, the extracted data is validated and
|
|
22
|
+
* typed according to the schema.
|
|
23
|
+
*
|
|
24
|
+
* @param prompt - A natural language description of what information to extract
|
|
25
|
+
* @returns A string containing the extracted information
|
|
26
|
+
*/
|
|
17
27
|
extract(prompt: string): Promise<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Extracts information from this locator using Stably AI.
|
|
30
|
+
*
|
|
31
|
+
* Takes a screenshot of the locator and uses AI to extract information based on the
|
|
32
|
+
* provided prompt. The extracted data is validated and typed according to the schema.
|
|
33
|
+
*
|
|
34
|
+
* @param prompt - A natural language description of what information to extract
|
|
35
|
+
* @param options - Configuration object containing the Zod schema for validation
|
|
36
|
+
* @param options.schema - Zod schema to validate and type the extracted data
|
|
37
|
+
* @returns Typed data matching the provided schema
|
|
38
|
+
*/
|
|
18
39
|
extract<T extends ExtractSchema>(prompt: string, options: {
|
|
19
40
|
schema: T;
|
|
20
41
|
}): Promise<SchemaOutput<T>>;
|
|
21
42
|
describe(description: string, options?: LocatorDescribeOptions): Locator;
|
|
22
43
|
}
|
|
23
44
|
interface Page {
|
|
45
|
+
/**
|
|
46
|
+
* Extracts information from this page using Stably AI.
|
|
47
|
+
*
|
|
48
|
+
* Takes a screenshot of the page and uses AI to extract information based on the
|
|
49
|
+
* provided prompt. When a schema is provided, the extracted data is validated and
|
|
50
|
+
* typed according to the schema.
|
|
51
|
+
*
|
|
52
|
+
* @param prompt - A natural language description of what information to extract
|
|
53
|
+
* @returns A string containing the extracted information
|
|
54
|
+
*/
|
|
24
55
|
extract(prompt: string): Promise<string>;
|
|
56
|
+
/**
|
|
57
|
+
* Extracts information from this page using Stably AI.
|
|
58
|
+
*
|
|
59
|
+
* Takes a screenshot of the page and uses AI to extract information based on the
|
|
60
|
+
* provided prompt. The extracted data is validated and typed according to the schema.
|
|
61
|
+
*
|
|
62
|
+
* @param prompt - A natural language description of what information to extract
|
|
63
|
+
* @param options - Configuration object containing the Zod schema for validation
|
|
64
|
+
* @param options.schema - Zod schema to validate and type the extracted data
|
|
65
|
+
* @returns Typed data matching the provided schema
|
|
66
|
+
*/
|
|
25
67
|
extract<T extends ExtractSchema>(prompt: string, options: {
|
|
26
68
|
schema: T;
|
|
27
69
|
}): Promise<SchemaOutput<T>>;
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Object.defineProperty(exports, "augmentBrowserContext", { enumerable: true, get: function () { return augment_1.augmentBrowserContext; } });
|
|
7
|
-
Object.defineProperty(exports, "augmentBrowserType", { enumerable: true, get: function () { return augment_1.augmentBrowserType; } });
|
|
8
|
-
Object.defineProperty(exports, "augmentLocator", { enumerable: true, get: function () { return augment_1.augmentLocator; } });
|
|
9
|
-
Object.defineProperty(exports, "augmentPage", { enumerable: true, get: function () { return augment_1.augmentPage; } });
|
|
10
|
-
const expect_1 = require("./expect");
|
|
11
|
-
Object.defineProperty(exports, "stablyPlaywrightMatchers", { enumerable: true, get: function () { return expect_1.stablyPlaywrightMatchers; } });
|
|
12
|
-
const runtime_1 = require("./runtime");
|
|
13
|
-
Object.defineProperty(exports, "requireApiKey", { enumerable: true, get: function () { return runtime_1.requireApiKey; } });
|
|
14
|
-
var runtime_2 = require("./runtime");
|
|
15
|
-
Object.defineProperty(exports, "setApiKey", { enumerable: true, get: function () { return runtime_2.setApiKey; } });
|
|
1
|
+
import { augmentBrowser, augmentBrowserContext, augmentBrowserType, augmentLocator, augmentPage, } from "./playwright-augment/augment";
|
|
2
|
+
import { stablyPlaywrightMatchers } from "./expect";
|
|
3
|
+
import { requireApiKey } from "./runtime";
|
|
4
|
+
export { setApiKey } from "./runtime";
|
|
5
|
+
export { augmentBrowser, augmentBrowserContext, augmentBrowserType, augmentLocator, augmentPage, stablyPlaywrightMatchers, requireApiKey, };
|
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const agent_1 = require("./methods/agent");
|
|
10
|
-
const LOCATOR_PATCHED = Symbol.for('stably.playwright.locatorPatched');
|
|
11
|
-
const LOCATOR_DESCRIBE_WRAPPED = Symbol.for('stably.playwright.locatorDescribeWrapped');
|
|
12
|
-
const PAGE_PATCHED = Symbol.for('stably.playwright.pagePatched');
|
|
13
|
-
const CONTEXT_PATCHED = Symbol.for('stably.playwright.contextPatched');
|
|
14
|
-
const BROWSER_PATCHED = Symbol.for('stably.playwright.browserPatched');
|
|
15
|
-
const BROWSER_TYPE_PATCHED = Symbol.for('stably.playwright.browserTypePatched');
|
|
1
|
+
import { createLocatorExtract, createPageExtract } from "./methods/extract";
|
|
2
|
+
import { createAgentStub } from "./methods/agent";
|
|
3
|
+
const LOCATOR_PATCHED = Symbol.for("stably.playwright.locatorPatched");
|
|
4
|
+
const LOCATOR_DESCRIBE_WRAPPED = Symbol.for("stably.playwright.locatorDescribeWrapped");
|
|
5
|
+
const PAGE_PATCHED = Symbol.for("stably.playwright.pagePatched");
|
|
6
|
+
const CONTEXT_PATCHED = Symbol.for("stably.playwright.contextPatched");
|
|
7
|
+
const BROWSER_PATCHED = Symbol.for("stably.playwright.browserPatched");
|
|
8
|
+
const BROWSER_TYPE_PATCHED = Symbol.for("stably.playwright.browserTypePatched");
|
|
16
9
|
function defineHiddenProperty(target, key, value) {
|
|
17
10
|
Object.defineProperty(target, key, {
|
|
18
11
|
value,
|
|
@@ -21,13 +14,13 @@ function defineHiddenProperty(target, key, value) {
|
|
|
21
14
|
writable: true,
|
|
22
15
|
});
|
|
23
16
|
}
|
|
24
|
-
function augmentLocator(locator) {
|
|
17
|
+
export function augmentLocator(locator) {
|
|
25
18
|
if (locator[LOCATOR_PATCHED]) {
|
|
26
19
|
return locator;
|
|
27
20
|
}
|
|
28
|
-
defineHiddenProperty(locator,
|
|
21
|
+
defineHiddenProperty(locator, "extract", createLocatorExtract(locator));
|
|
29
22
|
const markerTarget = locator;
|
|
30
|
-
if (typeof locator.describe ===
|
|
23
|
+
if (typeof locator.describe === "function" &&
|
|
31
24
|
!markerTarget[LOCATOR_DESCRIBE_WRAPPED]) {
|
|
32
25
|
const originalDescribe = locator.describe.bind(locator);
|
|
33
26
|
locator.describe = ((description, options) => {
|
|
@@ -40,7 +33,7 @@ function augmentLocator(locator) {
|
|
|
40
33
|
defineHiddenProperty(locator, LOCATOR_PATCHED, true);
|
|
41
34
|
return locator;
|
|
42
35
|
}
|
|
43
|
-
function augmentPage(page) {
|
|
36
|
+
export function augmentPage(page) {
|
|
44
37
|
if (page[PAGE_PATCHED]) {
|
|
45
38
|
return page;
|
|
46
39
|
}
|
|
@@ -49,12 +42,11 @@ function augmentPage(page) {
|
|
|
49
42
|
const locator = originalLocator(...args);
|
|
50
43
|
return augmentLocator(locator);
|
|
51
44
|
});
|
|
52
|
-
defineHiddenProperty(page,
|
|
45
|
+
defineHiddenProperty(page, "extract", createPageExtract(page));
|
|
53
46
|
defineHiddenProperty(page, PAGE_PATCHED, true);
|
|
54
47
|
return page;
|
|
55
48
|
}
|
|
56
|
-
function augmentBrowserContext(context) {
|
|
57
|
-
var _a;
|
|
49
|
+
export function augmentBrowserContext(context) {
|
|
58
50
|
if (context[CONTEXT_PATCHED]) {
|
|
59
51
|
return context;
|
|
60
52
|
}
|
|
@@ -63,17 +55,17 @@ function augmentBrowserContext(context) {
|
|
|
63
55
|
const page = await originalNewPage(...args);
|
|
64
56
|
return augmentPage(page);
|
|
65
57
|
});
|
|
66
|
-
const originalPages =
|
|
58
|
+
const originalPages = context.pages?.bind(context);
|
|
67
59
|
if (originalPages) {
|
|
68
60
|
context.pages = (() => originalPages().map((page) => augmentPage(page)));
|
|
69
61
|
}
|
|
70
62
|
if (!context.agent) {
|
|
71
|
-
defineHiddenProperty(context,
|
|
63
|
+
defineHiddenProperty(context, "agent", createAgentStub());
|
|
72
64
|
}
|
|
73
65
|
defineHiddenProperty(context, CONTEXT_PATCHED, true);
|
|
74
66
|
return context;
|
|
75
67
|
}
|
|
76
|
-
function augmentBrowser(browser) {
|
|
68
|
+
export function augmentBrowser(browser) {
|
|
77
69
|
if (browser[BROWSER_PATCHED]) {
|
|
78
70
|
return browser;
|
|
79
71
|
}
|
|
@@ -90,13 +82,12 @@ function augmentBrowser(browser) {
|
|
|
90
82
|
const originalContexts = browser.contexts.bind(browser);
|
|
91
83
|
browser.contexts = (() => originalContexts().map((context) => augmentBrowserContext(context)));
|
|
92
84
|
if (!browser.agent) {
|
|
93
|
-
defineHiddenProperty(browser,
|
|
85
|
+
defineHiddenProperty(browser, "agent", createAgentStub());
|
|
94
86
|
}
|
|
95
87
|
defineHiddenProperty(browser, BROWSER_PATCHED, true);
|
|
96
88
|
return browser;
|
|
97
89
|
}
|
|
98
|
-
function augmentBrowserType(browserType) {
|
|
99
|
-
var _a, _b, _c;
|
|
90
|
+
export function augmentBrowserType(browserType) {
|
|
100
91
|
if (browserType[BROWSER_TYPE_PATCHED]) {
|
|
101
92
|
return browserType;
|
|
102
93
|
}
|
|
@@ -105,21 +96,21 @@ function augmentBrowserType(browserType) {
|
|
|
105
96
|
const browser = await originalLaunch(...args);
|
|
106
97
|
return augmentBrowser(browser);
|
|
107
98
|
});
|
|
108
|
-
const originalConnect =
|
|
99
|
+
const originalConnect = browserType.connect?.bind(browserType);
|
|
109
100
|
if (originalConnect) {
|
|
110
101
|
browserType.connect = (async (...args) => {
|
|
111
102
|
const browser = await originalConnect(...args);
|
|
112
103
|
return augmentBrowser(browser);
|
|
113
104
|
});
|
|
114
105
|
}
|
|
115
|
-
const originalConnectOverCDP =
|
|
106
|
+
const originalConnectOverCDP = browserType.connectOverCDP?.bind(browserType);
|
|
116
107
|
if (originalConnectOverCDP) {
|
|
117
108
|
browserType.connectOverCDP = (async (...args) => {
|
|
118
109
|
const browser = await originalConnectOverCDP(...args);
|
|
119
110
|
return augmentBrowser(browser);
|
|
120
111
|
});
|
|
121
112
|
}
|
|
122
|
-
const originalLaunchPersistentContext =
|
|
113
|
+
const originalLaunchPersistentContext = browserType.launchPersistentContext?.bind(browserType);
|
|
123
114
|
if (originalLaunchPersistentContext) {
|
|
124
115
|
browserType.launchPersistentContext = (async (...args) => {
|
|
125
116
|
const context = await originalLaunchPersistentContext(...args);
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.createAgentStub = createAgentStub;
|
|
4
|
-
const runtime_1 = require("../../runtime");
|
|
5
|
-
function createAgentStub() {
|
|
1
|
+
import { requireApiKey } from "../../runtime";
|
|
2
|
+
export function createAgentStub() {
|
|
6
3
|
return async (prompt, options) => {
|
|
7
|
-
|
|
4
|
+
requireApiKey();
|
|
8
5
|
void prompt;
|
|
9
6
|
void options;
|
|
10
7
|
return { success: true };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Locator, Page } from "playwright";
|
|
2
|
+
export type LocatorAutoHealContext = {
|
|
3
|
+
description: string;
|
|
4
|
+
error: unknown;
|
|
5
|
+
locator: Locator;
|
|
6
|
+
method: string;
|
|
7
|
+
args: unknown[];
|
|
8
|
+
page: Page;
|
|
9
|
+
};
|
|
10
|
+
export type LocatorAutoHealResult = {
|
|
11
|
+
locator?: Locator;
|
|
12
|
+
retry?: boolean;
|
|
13
|
+
} | void;
|
|
14
|
+
export type LocatorAutoHealHandler = (context: LocatorAutoHealContext) => LocatorAutoHealResult | Promise<LocatorAutoHealResult>;
|
|
15
|
+
export declare function setLocatorAutoHealHandler(handler: LocatorAutoHealHandler | undefined): void;
|
|
16
|
+
export declare function getLocatorAutoHealHandler(): LocatorAutoHealHandler | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Locator, Page } from
|
|
2
|
-
import { type ExtractSchema, type SchemaOutput } from
|
|
1
|
+
import type { Locator, Page } from "@stablyai/internal-playwright-test";
|
|
2
|
+
import { type ExtractSchema, type SchemaOutput } from "../../ai/extract";
|
|
3
3
|
type ExtractOptions<T extends ExtractSchema> = {
|
|
4
4
|
schema: T;
|
|
5
5
|
};
|
|
@@ -1,21 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createPageExtract = exports.createLocatorExtract = void 0;
|
|
4
|
-
const extract_1 = require("../../ai/extract");
|
|
1
|
+
import { extract, } from "../../ai/extract";
|
|
5
2
|
function createExtract(pageOrLocator) {
|
|
6
3
|
const impl = (async (prompt, options) => {
|
|
7
|
-
if (options
|
|
8
|
-
return
|
|
4
|
+
if (options?.schema) {
|
|
5
|
+
return extract({
|
|
9
6
|
prompt,
|
|
10
7
|
schema: options.schema,
|
|
11
8
|
pageOrLocator,
|
|
12
9
|
});
|
|
13
10
|
}
|
|
14
|
-
return
|
|
11
|
+
return extract({ prompt, pageOrLocator });
|
|
15
12
|
});
|
|
16
13
|
return impl;
|
|
17
14
|
}
|
|
18
|
-
const createLocatorExtract = (locator) => createExtract(locator);
|
|
19
|
-
|
|
20
|
-
const createPageExtract = (page) => createExtract(page);
|
|
21
|
-
exports.createPageExtract = createPageExtract;
|
|
15
|
+
export const createLocatorExtract = (locator) => createExtract(locator);
|
|
16
|
+
export const createPageExtract = (page) => createExtract(page);
|