@fasttest-ai/qa-agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ // CLI entry point — forwards to compiled TypeScript
3
+ import "../dist/index.js";
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Browser action executor — navigate, click, fill, screenshot, etc.
3
+ * Reference implementation from Python qa_browser_service.py → _execute_step()
4
+ */
5
+ import type { Page } from "playwright";
6
+ export interface ActionResult {
7
+ success: boolean;
8
+ error?: string;
9
+ data?: Record<string, unknown>;
10
+ }
11
+ export declare function navigate(page: Page, url: string): Promise<ActionResult>;
12
+ export declare function click(page: Page, selector: string): Promise<ActionResult>;
13
+ export declare function fill(page: Page, selector: string, value: string): Promise<ActionResult>;
14
+ export declare function hover(page: Page, selector: string): Promise<ActionResult>;
15
+ export declare function selectOption(page: Page, selector: string, value: string): Promise<ActionResult>;
16
+ export declare function waitFor(page: Page, selector: string, timeoutMs?: number): Promise<ActionResult>;
17
+ export declare function screenshot(page: Page, fullPage?: boolean): Promise<string>;
18
+ export declare function getSnapshot(page: Page): Promise<Record<string, unknown>>;
19
+ export declare function getConsoleLogs(page: Page): Promise<string[]>;
20
+ export type AssertionType = "element_visible" | "element_hidden" | "text_contains" | "text_equals" | "url_contains" | "url_equals" | "element_count" | "attribute_value";
21
+ export interface AssertionParams {
22
+ type: AssertionType;
23
+ selector?: string;
24
+ text?: string;
25
+ url?: string;
26
+ count?: number;
27
+ attribute?: string;
28
+ value?: string;
29
+ }
30
+ export interface AssertionResult {
31
+ pass: boolean;
32
+ actual?: string | number | boolean;
33
+ error?: string;
34
+ }
35
+ export declare function assertPage(page: Page, params: AssertionParams): Promise<AssertionResult>;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Browser action executor — navigate, click, fill, screenshot, etc.
3
+ * Reference implementation from Python qa_browser_service.py → _execute_step()
4
+ */
5
+ export async function navigate(page, url) {
6
+ try {
7
+ await page.goto(url, { waitUntil: "networkidle", timeout: 30_000 });
8
+ return {
9
+ success: true,
10
+ data: {
11
+ title: await page.title(),
12
+ url: page.url(),
13
+ },
14
+ };
15
+ }
16
+ catch (err) {
17
+ return { success: false, error: String(err) };
18
+ }
19
+ }
20
+ export async function click(page, selector) {
21
+ try {
22
+ await page.click(selector, { timeout: 10_000 });
23
+ await page.waitForLoadState("networkidle", { timeout: 10_000 }).catch(() => { });
24
+ return { success: true };
25
+ }
26
+ catch (err) {
27
+ return { success: false, error: String(err) };
28
+ }
29
+ }
30
+ export async function fill(page, selector, value) {
31
+ try {
32
+ await page.fill(selector, value, { timeout: 10_000 });
33
+ return { success: true };
34
+ }
35
+ catch (err) {
36
+ return { success: false, error: String(err) };
37
+ }
38
+ }
39
+ export async function hover(page, selector) {
40
+ try {
41
+ await page.hover(selector, { timeout: 10_000 });
42
+ return { success: true };
43
+ }
44
+ catch (err) {
45
+ return { success: false, error: String(err) };
46
+ }
47
+ }
48
+ export async function selectOption(page, selector, value) {
49
+ try {
50
+ await page.selectOption(selector, value, { timeout: 10_000 });
51
+ return { success: true };
52
+ }
53
+ catch (err) {
54
+ return { success: false, error: String(err) };
55
+ }
56
+ }
57
+ export async function waitFor(page, selector, timeoutMs = 10_000) {
58
+ try {
59
+ await page.waitForSelector(selector, { timeout: timeoutMs });
60
+ return { success: true };
61
+ }
62
+ catch (err) {
63
+ return { success: false, error: String(err) };
64
+ }
65
+ }
66
+ export async function screenshot(page, fullPage = false) {
67
+ const buffer = await page.screenshot({
68
+ type: "jpeg",
69
+ quality: 80,
70
+ fullPage,
71
+ });
72
+ return buffer.toString("base64");
73
+ }
74
+ export async function getSnapshot(page) {
75
+ // Use Playwright's built-in aria snapshot for accessibility tree
76
+ const ariaSnapshot = await page.locator("body").ariaSnapshot().catch(() => "");
77
+ return {
78
+ url: page.url(),
79
+ title: await page.title(),
80
+ accessibilityTree: ariaSnapshot,
81
+ };
82
+ }
83
+ export async function getConsoleLogs(page) {
84
+ // Console logs must be captured via event listener — return empty for now.
85
+ // The actual collection is done in the MCP server via page.on('console').
86
+ return [];
87
+ }
88
+ export async function assertPage(page, params) {
89
+ try {
90
+ switch (params.type) {
91
+ case "element_visible": {
92
+ const visible = await page.isVisible(params.selector, { timeout: 5000 });
93
+ return { pass: visible, actual: visible };
94
+ }
95
+ case "element_hidden": {
96
+ const visible = await page.isVisible(params.selector);
97
+ return { pass: !visible, actual: !visible };
98
+ }
99
+ case "text_contains": {
100
+ const loc = page.locator(params.selector);
101
+ const count = await loc.count();
102
+ if (count === 0)
103
+ return { pass: false, error: "Element not found" };
104
+ const text = await loc.first().textContent();
105
+ const contains = text?.includes(params.text ?? "") ?? false;
106
+ return { pass: contains, actual: text ?? "" };
107
+ }
108
+ case "text_equals": {
109
+ const loc = page.locator(params.selector);
110
+ const count = await loc.count();
111
+ if (count === 0)
112
+ return { pass: false, error: "Element not found" };
113
+ const text = (await loc.first().textContent())?.trim() ?? "";
114
+ return { pass: text === params.text, actual: text };
115
+ }
116
+ case "url_contains": {
117
+ const currentUrl = page.url();
118
+ return { pass: currentUrl.includes(params.text ?? ""), actual: currentUrl };
119
+ }
120
+ case "url_equals": {
121
+ const currentUrl = page.url();
122
+ return { pass: currentUrl === params.url, actual: currentUrl };
123
+ }
124
+ case "element_count": {
125
+ const loc = page.locator(params.selector);
126
+ const elCount = await loc.count();
127
+ return { pass: elCount === (params.count ?? 1), actual: elCount };
128
+ }
129
+ case "attribute_value": {
130
+ const loc = page.locator(params.selector);
131
+ const elCount = await loc.count();
132
+ if (elCount === 0)
133
+ return { pass: false, error: "Element not found" };
134
+ const attrVal = await loc.first().getAttribute(params.attribute ?? "");
135
+ return { pass: attrVal === params.value, actual: attrVal ?? "" };
136
+ }
137
+ default:
138
+ return { pass: false, error: `Unknown assertion type: ${params.type}` };
139
+ }
140
+ }
141
+ catch (err) {
142
+ return { pass: false, error: String(err) };
143
+ }
144
+ }
145
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAU,EAAE,GAAW;IACpD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QACpE,OAAO;YACL,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,KAAK,EAAE,MAAM,IAAI,CAAC,KAAK,EAAE;gBACzB,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE;aAChB;SACF,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAChD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAU,EAAE,QAAgB;IACtD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAChD,MAAM,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAChF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAChD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAU,EAAE,QAAgB,EAAE,KAAa;IACpE,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QACtD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAChD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,IAAU,EAAE,QAAgB;IACtD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAChD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAChD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAU,EAAE,QAAgB,EAAE,KAAa;IAC5E,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAChD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAU,EAAE,QAAgB,EAAE,YAAoB,MAAM;IACpF,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAChD,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAU,EAAE,WAAoB,KAAK;IACpE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC;QACnC,IAAI,EAAE,MAAM;QACZ,OAAO,EAAE,EAAE;QACX,QAAQ;KACT,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAU;IAC1C,iEAAiE;IACjE,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,YAAY,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC/E,OAAO;QACL,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE;QACf,KAAK,EAAE,MAAM,IAAI,CAAC,KAAK,EAAE;QACzB,iBAAiB,EAAE,YAAY;KAChC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,IAAU;IAC7C,2EAA2E;IAC3E,0EAA0E;IAC1E,OAAO,EAAE,CAAC;AACZ,CAAC;AA4BD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAU,EAAE,MAAuB;IAClE,IAAI,CAAC;QACH,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,iBAAiB,CAAC,CAAC,CAAC;gBACvB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC1E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YAC5C,CAAC;YACD,KAAK,gBAAgB,CAAC,CAAC,CAAC;gBACtB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAS,CAAC,CAAC;gBACvD,OAAO,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC;YAC9C,CAAC;YACD,KAAK,eAAe,CAAC,CAAC,CAAC;gBACrB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAS,CAAC,CAAC;gBAC3C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;gBAChC,IAAI,KAAK,KAAK,CAAC;oBAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;gBACpE,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC;gBAC7C,MAAM,QAAQ,GAAG,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,IAAI,KAAK,CAAC;gBAC5D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;YAChD,CAAC;YACD,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAS,CAAC,CAAC;gBAC3C,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;gBAChC,IAAI,KAAK,KAAK,CAAC;oBAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;gBACpE,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;gBAC7D,OAAO,EAAE,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YACtD,CAAC;YACD,KAAK,cAAc,CAAC,CAAC,CAAC;gBACpB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC9B,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YAC9E,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC9B,OAAO,EAAE,IAAI,EAAE,UAAU,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;YACjE,CAAC;YACD,KAAK,eAAe,CAAC,CAAC,CAAC;gBACrB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAS,CAAC,CAAC;gBAC3C,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;gBAClC,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YACpE,CAAC;YACD,KAAK,iBAAiB,CAAC,CAAC,CAAC;gBACvB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAS,CAAC,CAAC;gBAC3C,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;gBAClC,IAAI,OAAO,KAAK,CAAC;oBAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;gBACtE,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;gBACvE,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;YACnE,CAAC;YACD;gBACE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,2BAA2B,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QAC5E,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC7C,CAAC;AACH,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Playwright browser manager — launch, reuse, and close browser instances.
3
+ * Manages browser contexts and pages with session persistence.
4
+ */
5
+ import { type Page } from "playwright";
6
+ export type BrowserType = "chromium" | "firefox" | "webkit";
7
+ export interface BrowserManagerOptions {
8
+ browserType?: BrowserType;
9
+ headless?: boolean;
10
+ orgSlug?: string;
11
+ }
12
+ export declare class BrowserManager {
13
+ private browser;
14
+ private context;
15
+ private page;
16
+ private browserType;
17
+ private headless;
18
+ private orgSlug;
19
+ constructor(options?: BrowserManagerOptions);
20
+ ensureBrowser(): Promise<Page>;
21
+ getPage(): Promise<Page>;
22
+ newContext(): Promise<Page>;
23
+ saveSession(name: string): Promise<string>;
24
+ restoreSession(name: string): Promise<Page>;
25
+ close(): Promise<void>;
26
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Playwright browser manager — launch, reuse, and close browser instances.
3
+ * Manages browser contexts and pages with session persistence.
4
+ */
5
+ import { chromium, firefox, webkit } from "playwright";
6
+ import * as fs from "node:fs";
7
+ import * as path from "node:path";
8
+ import * as os from "node:os";
9
+ const SESSION_DIR = path.join(os.homedir(), ".qa-agent", "sessions");
10
+ export class BrowserManager {
11
+ browser = null;
12
+ context = null;
13
+ page = null;
14
+ browserType;
15
+ headless;
16
+ orgSlug;
17
+ constructor(options = {}) {
18
+ this.browserType = options.browserType ?? "chromium";
19
+ this.headless = options.headless ?? true;
20
+ this.orgSlug = options.orgSlug ?? "default";
21
+ }
22
+ async ensureBrowser() {
23
+ if (this.page && !this.page.isClosed()) {
24
+ return this.page;
25
+ }
26
+ if (!this.browser || !this.browser.isConnected()) {
27
+ const launcher = this.browserType === "firefox"
28
+ ? firefox
29
+ : this.browserType === "webkit"
30
+ ? webkit
31
+ : chromium;
32
+ this.browser = await launcher.launch({
33
+ headless: this.headless,
34
+ args: this.browserType === "chromium" ? ["--disable-blink-features=AutomationControlled"] : [],
35
+ });
36
+ }
37
+ if (!this.context) {
38
+ this.context = await this.browser.newContext({
39
+ viewport: { width: 1280, height: 720 },
40
+ ignoreHTTPSErrors: true,
41
+ });
42
+ }
43
+ this.page = await this.context.newPage();
44
+ return this.page;
45
+ }
46
+ async getPage() {
47
+ return this.ensureBrowser();
48
+ }
49
+ async newContext() {
50
+ if (!this.browser || !this.browser.isConnected()) {
51
+ await this.ensureBrowser();
52
+ }
53
+ // Close old context/page if open
54
+ if (this.page && !this.page.isClosed()) {
55
+ await this.page.close().catch(() => { });
56
+ }
57
+ if (this.context) {
58
+ await this.context.close().catch(() => { });
59
+ }
60
+ this.context = await this.browser.newContext({
61
+ viewport: { width: 1280, height: 720 },
62
+ ignoreHTTPSErrors: true,
63
+ });
64
+ this.page = await this.context.newPage();
65
+ return this.page;
66
+ }
67
+ async saveSession(name) {
68
+ if (!this.context) {
69
+ throw new Error("No browser context — nothing to save");
70
+ }
71
+ const dir = path.join(SESSION_DIR, this.orgSlug);
72
+ fs.mkdirSync(dir, { recursive: true });
73
+ const filePath = path.join(dir, `${name}.json`);
74
+ const state = await this.context.storageState();
75
+ fs.writeFileSync(filePath, JSON.stringify(state, null, 2));
76
+ return filePath;
77
+ }
78
+ async restoreSession(name) {
79
+ const filePath = path.join(SESSION_DIR, this.orgSlug, `${name}.json`);
80
+ if (!fs.existsSync(filePath)) {
81
+ throw new Error(`Session "${name}" not found at ${filePath}`);
82
+ }
83
+ const state = JSON.parse(fs.readFileSync(filePath, "utf-8"));
84
+ if (!this.browser || !this.browser.isConnected()) {
85
+ await this.ensureBrowser();
86
+ }
87
+ if (this.page && !this.page.isClosed()) {
88
+ await this.page.close().catch(() => { });
89
+ }
90
+ if (this.context) {
91
+ await this.context.close().catch(() => { });
92
+ }
93
+ this.context = await this.browser.newContext({
94
+ storageState: state,
95
+ viewport: { width: 1280, height: 720 },
96
+ ignoreHTTPSErrors: true,
97
+ });
98
+ this.page = await this.context.newPage();
99
+ return this.page;
100
+ }
101
+ async close() {
102
+ if (this.page && !this.page.isClosed()) {
103
+ await this.page.close().catch(() => { });
104
+ }
105
+ if (this.context) {
106
+ await this.context.close().catch(() => { });
107
+ }
108
+ if (this.browser) {
109
+ await this.browser.close().catch(() => { });
110
+ }
111
+ this.page = null;
112
+ this.context = null;
113
+ this.browser = null;
114
+ }
115
+ }
116
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * HTTP client to Cloud API — authenticates with the org's API key.
3
+ */
4
+ export interface CloudClientOptions {
5
+ apiKey: string;
6
+ baseUrl?: string;
7
+ }
8
+ export interface RunResponse {
9
+ execution_id: string;
10
+ status: string;
11
+ base_url: string | null;
12
+ test_cases: TestCasePayload[];
13
+ }
14
+ export interface TestCasePayload {
15
+ id: string;
16
+ name: string;
17
+ steps: TestStep[];
18
+ assertions: TestAssertion[];
19
+ test_data: Record<string, unknown>[];
20
+ timeout_seconds: number;
21
+ }
22
+ export interface TestStep {
23
+ action: string;
24
+ selector?: string;
25
+ value?: string;
26
+ url?: string;
27
+ description?: string;
28
+ timeout?: number;
29
+ fields?: Record<string, string>;
30
+ condition?: string;
31
+ type?: string;
32
+ text?: string;
33
+ count?: number;
34
+ attribute?: string;
35
+ }
36
+ export interface TestAssertion {
37
+ type: string;
38
+ selector?: string;
39
+ text?: string;
40
+ url?: string;
41
+ count?: number;
42
+ attribute?: string;
43
+ value?: string;
44
+ expected_value?: string;
45
+ description?: string;
46
+ }
47
+ export declare class CloudClient {
48
+ private apiKey;
49
+ private baseUrl;
50
+ constructor(options: CloudClientOptions);
51
+ private request;
52
+ get<T>(path: string): Promise<T>;
53
+ post<T>(path: string, body?: unknown): Promise<T>;
54
+ health(): Promise<{
55
+ status: string;
56
+ }>;
57
+ getOrg(): Promise<Record<string, unknown>>;
58
+ listProjects(): Promise<Record<string, unknown>>;
59
+ startExploration(body: {
60
+ url: string;
61
+ max_pages?: number;
62
+ focus?: string;
63
+ }): Promise<Record<string, unknown>>;
64
+ startConversation(body: {
65
+ description: string;
66
+ url?: string;
67
+ }): Promise<Record<string, unknown>>;
68
+ answerConversation(sessionId: string, answers: string): Promise<Record<string, unknown>>;
69
+ approveConversation(sessionId: string, exclude?: number[]): Promise<Record<string, unknown>>;
70
+ /** Start a test run — returns execution_id + test cases to execute locally. */
71
+ startRun(body: {
72
+ suite_id: string;
73
+ environment_id?: string;
74
+ browser?: string;
75
+ test_case_ids?: string[];
76
+ }): Promise<RunResponse>;
77
+ /** Report the result of a single test case. */
78
+ reportResult(executionId: string, body: {
79
+ test_case_id: string;
80
+ status: string;
81
+ duration_ms?: number;
82
+ error_message?: string;
83
+ screenshots?: string[];
84
+ console_logs?: string[];
85
+ step_results?: Record<string, unknown>[];
86
+ }): Promise<{
87
+ status: string;
88
+ }>;
89
+ /** Mark a suite execution as complete. */
90
+ completeExecution(executionId: string, status?: string): Promise<Record<string, unknown>>;
91
+ /** Cancel a running execution. */
92
+ cancelExecution(executionId: string): Promise<Record<string, unknown>>;
93
+ /** Get execution status. */
94
+ getExecutionStatus(executionId: string): Promise<Record<string, unknown>>;
95
+ /** Set the org's GitHub token. */
96
+ setGithubToken(token: string): Promise<Record<string, unknown>>;
97
+ /** Post test results as a PR comment. */
98
+ postPrComment(body: {
99
+ pr_url: string;
100
+ execution_id: string;
101
+ status: string;
102
+ total: number;
103
+ passed: number;
104
+ failed: number;
105
+ skipped: number;
106
+ duration_seconds: number;
107
+ test_results: Array<{
108
+ name?: string;
109
+ status?: string;
110
+ error?: string;
111
+ }>;
112
+ healed?: Array<{
113
+ original_selector?: string;
114
+ new_selector?: string;
115
+ strategy?: string;
116
+ confidence?: number;
117
+ }>;
118
+ }): Promise<Record<string, unknown>>;
119
+ }
package/dist/cloud.js ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ * HTTP client to Cloud API — authenticates with the org's API key.
3
+ */
4
+ export class CloudClient {
5
+ apiKey;
6
+ baseUrl;
7
+ constructor(options) {
8
+ this.apiKey = options.apiKey;
9
+ this.baseUrl = (options.baseUrl ?? "https://api.qa-agent.dev").replace(/\/$/, "");
10
+ }
11
+ async request(method, path, body) {
12
+ const url = `${this.baseUrl}/api/v1${path}`;
13
+ const headers = {
14
+ "x-api-key": this.apiKey,
15
+ "Content-Type": "application/json",
16
+ };
17
+ const init = { method, headers };
18
+ if (body !== undefined) {
19
+ init.body = JSON.stringify(body);
20
+ }
21
+ const resp = await fetch(url, init);
22
+ if (!resp.ok) {
23
+ const text = await resp.text();
24
+ throw new Error(`Cloud API ${method} ${path} → ${resp.status}: ${text}`);
25
+ }
26
+ return (await resp.json());
27
+ }
28
+ async get(path) {
29
+ return this.request("GET", path);
30
+ }
31
+ async post(path, body) {
32
+ return this.request("POST", path, body);
33
+ }
34
+ // --- Health ---
35
+ async health() {
36
+ const url = `${this.baseUrl}/health`;
37
+ const resp = await fetch(url);
38
+ return (await resp.json());
39
+ }
40
+ // --- Organization ---
41
+ async getOrg() {
42
+ return this.get("/orgs/me");
43
+ }
44
+ // --- Projects ---
45
+ async listProjects() {
46
+ return this.get("/qa/projects/");
47
+ }
48
+ // --- Exploration ---
49
+ async startExploration(body) {
50
+ return this.post("/explore/", body);
51
+ }
52
+ // --- Conversation (Phase 4) ---
53
+ async startConversation(body) {
54
+ return this.post("/conversation/start", body);
55
+ }
56
+ async answerConversation(sessionId, answers) {
57
+ return this.post(`/conversation/${sessionId}/answer`, { answers });
58
+ }
59
+ async approveConversation(sessionId, exclude) {
60
+ return this.post(`/conversation/${sessionId}/approve`, { exclude });
61
+ }
62
+ // --- Execution (Phase 3) ---
63
+ /** Start a test run — returns execution_id + test cases to execute locally. */
64
+ async startRun(body) {
65
+ return this.post("/qa/execution/run", body);
66
+ }
67
+ /** Report the result of a single test case. */
68
+ async reportResult(executionId, body) {
69
+ return this.post(`/qa/execution/executions/${executionId}/results`, body);
70
+ }
71
+ /** Mark a suite execution as complete. */
72
+ async completeExecution(executionId, status) {
73
+ return this.post(`/qa/execution/executions/${executionId}/complete`, { status });
74
+ }
75
+ /** Cancel a running execution. */
76
+ async cancelExecution(executionId) {
77
+ return this.post(`/qa/execution/executions/${executionId}/cancel`, {});
78
+ }
79
+ /** Get execution status. */
80
+ async getExecutionStatus(executionId) {
81
+ return this.get(`/qa/execution/executions/${executionId}`);
82
+ }
83
+ // --- GitHub (Phase 6) ---
84
+ /** Set the org's GitHub token. */
85
+ async setGithubToken(token) {
86
+ return this.request("PUT", "/qa/github/token", { github_token: token });
87
+ }
88
+ /** Post test results as a PR comment. */
89
+ async postPrComment(body) {
90
+ return this.post("/qa/github/pr-comment", body);
91
+ }
92
+ }
93
+ //# sourceMappingURL=cloud.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud.js","sourceRoot":"","sources":["../src/cloud.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Self-healing module — locator repair cascade.
3
+ *
4
+ * When a test step fails with a selector error, the healer tries:
5
+ * 1. data-testid (confidence: 98%)
6
+ * 2. ARIA labels (confidence: 95%)
7
+ * 3. Text content (confidence: 90%)
8
+ * 4. Structural (confidence: 85%)
9
+ * 5. AI-generated (confidence: 75%) — via Cloud LLM call
10
+ *
11
+ * After a successful heal the pattern is stored in the Cloud DB so
12
+ * future failures with the same signature are fixed instantly.
13
+ */
14
+ import type { Page } from "playwright";
15
+ import type { CloudClient } from "./cloud.js";
16
+ export interface HealResult {
17
+ healed: boolean;
18
+ newSelector?: string;
19
+ strategy?: string;
20
+ confidence?: number;
21
+ error?: string;
22
+ }
23
+ /**
24
+ * Attempt to heal a broken selector by trying 5 strategies in order.
25
+ * Returns the first working selector with its confidence score.
26
+ */
27
+ export declare function healSelector(page: Page, cloud: CloudClient, originalSelector: string, failureType: string, errorMessage: string, pageUrl: string): Promise<HealResult>;