@samsara-dev/appwright 0.9.13 → 0.9.15
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/CHANGELOG.md +18 -0
- package/dist/gptDriver/index.d.ts +2 -1
- package/dist/gptDriver/index.d.ts.map +1 -1
- package/dist/gptDriver/index.js +17 -1
- package/dist/tests/gptDriver.spec.d.ts +2 -0
- package/dist/tests/gptDriver.spec.d.ts.map +1 -0
- package/dist/tests/gptDriver.spec.js +131 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# appwright
|
|
2
2
|
|
|
3
|
+
## 0.9.15
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 7e70815: Fix stale testId in GPT Driver when using persistent device fixtures
|
|
8
|
+
|
|
9
|
+
Update testId on the cached gpt-driver-node instance before every API call
|
|
10
|
+
so persistent device fixtures (scope: "worker") report the correct test in
|
|
11
|
+
GPT Driver sessions. Previously the testId was baked in on first use and
|
|
12
|
+
never updated, causing all subsequent tests and resetToHome teardowns to
|
|
13
|
+
report the same stale testId on the MobileBoost dashboard.
|
|
14
|
+
|
|
15
|
+
## 0.9.14
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- c7b7722: Switch GPT Driver cachingMode from FULL_SCREEN to INTERACTION_REGION
|
|
20
|
+
|
|
3
21
|
## 0.9.13
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
|
@@ -2,7 +2,7 @@ import type { Client as WebDriverClient } from "webdriver";
|
|
|
2
2
|
import type { GptDriverConfig } from "../types";
|
|
3
3
|
/**
|
|
4
4
|
* Options for aiExecute. Intentionally exposes only appiumHandler;
|
|
5
|
-
* cachingMode is set globally at provider init (
|
|
5
|
+
* cachingMode is set globally at provider init (INTERACTION_REGION),
|
|
6
6
|
* and useSmartLoop is not yet surfaced.
|
|
7
7
|
*/
|
|
8
8
|
export interface AiExecuteOptions {
|
|
@@ -32,6 +32,7 @@ export declare class GptDriverProvider implements GptDriverApi {
|
|
|
32
32
|
private webDriverClient;
|
|
33
33
|
private options?;
|
|
34
34
|
private driver;
|
|
35
|
+
private testIdWarned;
|
|
35
36
|
constructor(webDriverClient: WebDriverClient, options?: GptDriverConfig | undefined);
|
|
36
37
|
private getAppiumUrl;
|
|
37
38
|
private getDriver;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gptDriver/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,WAAW,CAAC;AAG3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAIhD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAClE,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;CAC9D;AAED;;;;;;;;;GASG;AACH,qBAAa,iBAAkB,YAAW,YAAY;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/gptDriver/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,WAAW,CAAC;AAG3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAIhD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAClE,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;CAC9D;AAED;;;;;;;;;GASG;AACH,qBAAa,iBAAkB,YAAW,YAAY;IAKlD,OAAO,CAAC,eAAe;IACvB,OAAO,CAAC,OAAO,CAAC;IALlB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,YAAY,CAAS;gBAGnB,eAAe,EAAE,eAAe,EAChC,OAAO,CAAC,EAAE,eAAe,YAAA;IAGnC,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,SAAS;IA0BjB,OAAO,CAAC,aAAa;IA8BrB,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,kBAAkB;IAS1B,OAAO,CAAC,aAAa;IAMf,SAAS,CACb,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,IAAI,CAAC;IAkBV,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxC,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/C,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAajE,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAWnE"}
|
package/dist/gptDriver/index.js
CHANGED
|
@@ -77,6 +77,7 @@ let GptDriverProvider = (() => {
|
|
|
77
77
|
webDriverClient = __runInitializers(this, _instanceExtraInitializers);
|
|
78
78
|
options;
|
|
79
79
|
driver = null;
|
|
80
|
+
testIdWarned = false;
|
|
80
81
|
constructor(webDriverClient, options) {
|
|
81
82
|
this.webDriverClient = webDriverClient;
|
|
82
83
|
this.options = options;
|
|
@@ -100,7 +101,7 @@ let GptDriverProvider = (() => {
|
|
|
100
101
|
// gpt-driver-node expects webdriverio Browser type but webdriver Client is API-compatible at runtime
|
|
101
102
|
driver: this.webDriverClient,
|
|
102
103
|
serverConfig: { url: this.getAppiumUrl() },
|
|
103
|
-
cachingMode: "
|
|
104
|
+
cachingMode: "INTERACTION_REGION",
|
|
104
105
|
testId: test_1.default.info()?.testId ?? `test-${Date.now()}`,
|
|
105
106
|
...(this.options?.additionalUserContext != null && {
|
|
106
107
|
additionalUserContext: this.options.additionalUserContext,
|
|
@@ -115,6 +116,21 @@ let GptDriverProvider = (() => {
|
|
|
115
116
|
test_1.default.skip(true, "GPT Driver not configured. Set GPT_DRIVER_API_KEY environment variable.");
|
|
116
117
|
throw new Error("GPT Driver not configured");
|
|
117
118
|
}
|
|
119
|
+
// Update testId to current test context so persistent device fixtures
|
|
120
|
+
// report the correct test in GPT Driver API calls.
|
|
121
|
+
// gpt-driver-node declares testId as private in TS but it's a plain
|
|
122
|
+
// JS property at runtime — safe to mutate directly.
|
|
123
|
+
// TODO: Replace with public setTestId() if gpt-driver-node exposes one.
|
|
124
|
+
const currentTestId = test_1.default.info()?.testId;
|
|
125
|
+
if (currentTestId && "testId" in driver) {
|
|
126
|
+
driver.testId = currentTestId;
|
|
127
|
+
}
|
|
128
|
+
else if (currentTestId && !this.testIdWarned) {
|
|
129
|
+
console.warn("[GptDriver] Cannot update testId — property not found on driver instance. " +
|
|
130
|
+
"GPT Driver sessions may be attributed to the wrong test. " +
|
|
131
|
+
"Check if gpt-driver-node renamed or removed the testId field.");
|
|
132
|
+
this.testIdWarned = true;
|
|
133
|
+
}
|
|
118
134
|
return driver;
|
|
119
135
|
}
|
|
120
136
|
validateInstruction(instruction, methodName) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gptDriver.spec.d.ts","sourceRoot":"","sources":["../../src/tests/gptDriver.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vitest_1 = require("vitest");
|
|
7
|
+
const test_1 = __importDefault(require("@playwright/test"));
|
|
8
|
+
// --- Mock gpt-driver-node ---
|
|
9
|
+
// vi.hoisted runs before vi.mock hoisting, so variables are available in the factory
|
|
10
|
+
const { MockGptDriver, mockAiExecute } = vitest_1.vi.hoisted(() => {
|
|
11
|
+
const mockAiExecute = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
12
|
+
// Must use function (not arrow) so it can be called with `new`
|
|
13
|
+
const MockGptDriver = vitest_1.vi.fn(function (config) {
|
|
14
|
+
this.aiExecute = mockAiExecute;
|
|
15
|
+
this.assert = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
16
|
+
this.assertBulk = vitest_1.vi.fn().mockResolvedValue(undefined);
|
|
17
|
+
this.checkBulk = vitest_1.vi.fn().mockResolvedValue({});
|
|
18
|
+
this.extract = vitest_1.vi.fn().mockResolvedValue({});
|
|
19
|
+
this.testId = config.testId;
|
|
20
|
+
});
|
|
21
|
+
return { MockGptDriver, mockAiExecute };
|
|
22
|
+
});
|
|
23
|
+
vitest_1.vi.mock("gpt-driver-node", () => ({ default: MockGptDriver }));
|
|
24
|
+
// --- Mock Playwright test helpers ---
|
|
25
|
+
let currentTestId = "test-initial";
|
|
26
|
+
test_1.default.step = vitest_1.vi.fn(async (_name, body) => await body());
|
|
27
|
+
test_1.default.info = () => currentTestId ? { testId: currentTestId } : undefined;
|
|
28
|
+
test_1.default.skip = vitest_1.vi.fn();
|
|
29
|
+
const gptDriver_1 = require("../gptDriver");
|
|
30
|
+
function createProvider() {
|
|
31
|
+
return new gptDriver_1.GptDriverProvider({
|
|
32
|
+
options: {
|
|
33
|
+
protocol: "http",
|
|
34
|
+
hostname: "localhost",
|
|
35
|
+
port: 4723,
|
|
36
|
+
path: "/wd/hub",
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
(0, vitest_1.describe)("GptDriverProvider", () => {
|
|
41
|
+
(0, vitest_1.beforeEach)(() => {
|
|
42
|
+
vitest_1.vi.clearAllMocks();
|
|
43
|
+
currentTestId = "test-initial";
|
|
44
|
+
process.env.GPT_DRIVER_API_KEY = "fake-key";
|
|
45
|
+
});
|
|
46
|
+
(0, vitest_1.afterEach)(() => {
|
|
47
|
+
delete process.env.GPT_DRIVER_API_KEY;
|
|
48
|
+
});
|
|
49
|
+
(0, vitest_1.describe)("testId tracks current test context", () => {
|
|
50
|
+
(0, vitest_1.test)("initializes testId from first call's test context", async () => {
|
|
51
|
+
const provider = createProvider();
|
|
52
|
+
currentTestId = "test-first";
|
|
53
|
+
await provider.aiExecute("login");
|
|
54
|
+
const driver = provider.driver;
|
|
55
|
+
(0, vitest_1.expect)(driver.testId).toBe("test-first");
|
|
56
|
+
});
|
|
57
|
+
(0, vitest_1.test)("updates testId when a different test uses the same device", async () => {
|
|
58
|
+
const provider = createProvider();
|
|
59
|
+
// Test A runs
|
|
60
|
+
currentTestId = "test-aaa";
|
|
61
|
+
await provider.aiExecute("tap the submit button");
|
|
62
|
+
(0, vitest_1.expect)(provider.driver.testId).toBe("test-aaa");
|
|
63
|
+
// Test B runs on the same persistent device
|
|
64
|
+
currentTestId = "test-bbb";
|
|
65
|
+
await provider.aiExecute("verify the results screen");
|
|
66
|
+
(0, vitest_1.expect)(provider.driver.testId).toBe("test-bbb");
|
|
67
|
+
});
|
|
68
|
+
(0, vitest_1.test)("fixture teardown uses the current test's ID, not the first", async () => {
|
|
69
|
+
const provider = createProvider();
|
|
70
|
+
// Test A runs
|
|
71
|
+
currentTestId = "test-aaa";
|
|
72
|
+
await provider.aiExecute("perform action");
|
|
73
|
+
// Fixture teardown runs in Test A's context
|
|
74
|
+
await provider.aiExecute("navigate back to home");
|
|
75
|
+
(0, vitest_1.expect)(provider.driver.testId).toBe("test-aaa");
|
|
76
|
+
// Test B starts with new context
|
|
77
|
+
currentTestId = "test-bbb";
|
|
78
|
+
await provider.aiExecute("perform another action");
|
|
79
|
+
(0, vitest_1.expect)(provider.driver.testId).toBe("test-bbb");
|
|
80
|
+
// Fixture teardown runs in Test B's context
|
|
81
|
+
await provider.aiExecute("navigate back to home");
|
|
82
|
+
(0, vitest_1.expect)(provider.driver.testId).toBe("test-bbb");
|
|
83
|
+
});
|
|
84
|
+
(0, vitest_1.test)("preserves testId when test.info() returns undefined", async () => {
|
|
85
|
+
const provider = createProvider();
|
|
86
|
+
currentTestId = "test-known";
|
|
87
|
+
await provider.aiExecute("do something");
|
|
88
|
+
(0, vitest_1.expect)(provider.driver.testId).toBe("test-known");
|
|
89
|
+
// Worker teardown — no test context
|
|
90
|
+
currentTestId = undefined;
|
|
91
|
+
await provider.aiExecute("cleanup");
|
|
92
|
+
(0, vitest_1.expect)(provider.driver.testId).toBe("test-known");
|
|
93
|
+
});
|
|
94
|
+
(0, vitest_1.test)("warns once if testId property is missing from driver", async () => {
|
|
95
|
+
const provider = createProvider();
|
|
96
|
+
const warnSpy = vitest_1.vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
97
|
+
currentTestId = "test-aaa";
|
|
98
|
+
await provider.aiExecute("initialize");
|
|
99
|
+
// Remove testId to simulate SDK rename/removal
|
|
100
|
+
delete provider.driver.testId;
|
|
101
|
+
currentTestId = "test-bbb";
|
|
102
|
+
await provider.aiExecute("second call");
|
|
103
|
+
(0, vitest_1.expect)(warnSpy).toHaveBeenCalledTimes(1);
|
|
104
|
+
(0, vitest_1.expect)(warnSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining("Cannot update testId"));
|
|
105
|
+
// Should not warn again on subsequent calls
|
|
106
|
+
currentTestId = "test-ccc";
|
|
107
|
+
await provider.aiExecute("third call");
|
|
108
|
+
(0, vitest_1.expect)(warnSpy).toHaveBeenCalledTimes(1);
|
|
109
|
+
warnSpy.mockRestore();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
(0, vitest_1.describe)("lazy singleton", () => {
|
|
113
|
+
(0, vitest_1.test)("creates GptDriver instance only once", async () => {
|
|
114
|
+
const provider = createProvider();
|
|
115
|
+
currentTestId = "test-1";
|
|
116
|
+
await provider.aiExecute("first");
|
|
117
|
+
currentTestId = "test-2";
|
|
118
|
+
await provider.aiExecute("second");
|
|
119
|
+
currentTestId = "test-3";
|
|
120
|
+
await provider.aiExecute("third");
|
|
121
|
+
(0, vitest_1.expect)(MockGptDriver).toHaveBeenCalledTimes(1);
|
|
122
|
+
});
|
|
123
|
+
(0, vitest_1.test)("does not create GptDriver when API key is missing", async () => {
|
|
124
|
+
delete process.env.GPT_DRIVER_API_KEY;
|
|
125
|
+
const provider = createProvider();
|
|
126
|
+
// Should call test.skip and throw
|
|
127
|
+
await (0, vitest_1.expect)(provider.aiExecute("anything")).rejects.toThrow("GPT Driver not configured");
|
|
128
|
+
(0, vitest_1.expect)(MockGptDriver).not.toHaveBeenCalled();
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|