@empiricalrun/test-gen 0.35.3 → 0.35.5
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 +13 -0
- package/browser-injected-scripts/annotate-elements.js +59 -12
- package/browser-injected-scripts/annotate-elements.spec.ts +258 -0
- package/dist/agent/codegen/run.d.ts +8 -0
- package/dist/agent/codegen/run.d.ts.map +1 -1
- package/dist/agent/codegen/run.js +38 -33
- package/dist/agent/codegen/update-flow.d.ts +9 -0
- package/dist/agent/codegen/update-flow.d.ts.map +1 -1
- package/dist/agent/codegen/update-flow.js +44 -40
- package/dist/agent/master/run.js +1 -1
- package/dist/browser-injected-scripts/annotate-elements.js +59 -12
- package/dist/browser-injected-scripts/annotate-elements.spec.ts +258 -0
- package/dist/evals/add-scenario-agent.evals.d.ts +4 -0
- package/dist/evals/add-scenario-agent.evals.d.ts.map +1 -0
- package/dist/evals/add-scenario-agent.evals.js +23 -0
- package/dist/evals/update-scenario-agent.evals.d.ts +4 -0
- package/dist/evals/update-scenario-agent.evals.d.ts.map +1 -0
- package/dist/evals/update-scenario-agent.evals.js +49 -0
- package/package.json +5 -2
- package/playwright.config.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @empiricalrun/test-gen
|
|
2
2
|
|
|
3
|
+
## 0.35.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 8e18e5b: feat: add scenario code agent evals
|
|
8
|
+
- d6f9de2: fix: add tests for annotation script
|
|
9
|
+
|
|
10
|
+
## 0.35.4
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 4bddc82: fix: annotate all non-blocked elements, outside the viewport too
|
|
15
|
+
|
|
3
16
|
## 0.35.3
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
|
@@ -22,20 +22,56 @@ window.annotateClickableElements = function annotateClickableElements(
|
|
|
22
22
|
let annotationsContainer = null;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Checks if
|
|
26
|
-
*
|
|
25
|
+
* Checks if the provided DOM element is clickable by ensuring that
|
|
26
|
+
* no other elements are covering it, regardless of its current
|
|
27
|
+
* viewport visibility.
|
|
28
|
+
*
|
|
27
29
|
* @param {Element} element - The DOM element to check.
|
|
28
|
-
* @returns {boolean}
|
|
30
|
+
* @returns {boolean} - Returns true if the element is clickable; otherwise, false.
|
|
29
31
|
*/
|
|
30
|
-
|
|
32
|
+
|
|
33
|
+
function isElementClickNotBlocked(element) {
|
|
31
34
|
const rect = element.getBoundingClientRect();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
|
|
36
|
+
// Calculate the center point of the element
|
|
37
|
+
const centerX = rect.left + rect.width / 2;
|
|
38
|
+
const centerY = rect.top + rect.height / 2;
|
|
39
|
+
|
|
40
|
+
// check if element is within the viewport
|
|
41
|
+
if (
|
|
42
|
+
centerX < 0 ||
|
|
43
|
+
centerY < 0 ||
|
|
44
|
+
centerX > (window.innerWidth || document.documentElement.clientWidth) ||
|
|
45
|
+
centerY > (window.innerHeight || document.documentElement.clientHeight)
|
|
46
|
+
) {
|
|
47
|
+
// Determine the viewport dimensions
|
|
48
|
+
const viewportWidth =
|
|
49
|
+
window.innerWidth || document.documentElement.clientWidth;
|
|
50
|
+
const viewportHeight =
|
|
51
|
+
window.innerHeight || document.documentElement.clientHeight;
|
|
52
|
+
|
|
53
|
+
// Calculate the new scroll positions to bring the element into the center of the viewport
|
|
54
|
+
const newScrollX = centerX - viewportWidth / 2;
|
|
55
|
+
const newScrollY = centerY - viewportHeight / 2;
|
|
56
|
+
|
|
57
|
+
// Scroll the window to the new positions
|
|
58
|
+
window.scrollTo({
|
|
59
|
+
top: newScrollY,
|
|
60
|
+
left: newScrollX,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const newRect = element.getBoundingClientRect();
|
|
64
|
+
const newCenterX = newRect.left + newRect.width / 2;
|
|
65
|
+
const newCenterY = newRect.top + newRect.height / 2;
|
|
66
|
+
const topElement = document.elementFromPoint(newCenterX, newCenterY);
|
|
67
|
+
return element.contains(topElement);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Retrieve the topmost element at the center point
|
|
71
|
+
const topElement = document.elementFromPoint(centerX, centerY);
|
|
72
|
+
|
|
73
|
+
// Check if the topmost element is the target element or one of its descendants
|
|
74
|
+
return element.contains(topElement);
|
|
39
75
|
}
|
|
40
76
|
|
|
41
77
|
/**
|
|
@@ -408,7 +444,18 @@ window.annotateClickableElements = function annotateClickableElements(
|
|
|
408
444
|
document.body.appendChild(annotationsContainer);
|
|
409
445
|
|
|
410
446
|
const clickableElements = Array.from(document.querySelectorAll("*")).filter(
|
|
411
|
-
(el) =>
|
|
447
|
+
(el) => {
|
|
448
|
+
const isClickable = isElementClickable(el);
|
|
449
|
+
|
|
450
|
+
const originalScrollX = window.scrollX;
|
|
451
|
+
const originalScrollY = window.scrollY;
|
|
452
|
+
|
|
453
|
+
const isClickNotBlocked = isElementClickNotBlocked(el);
|
|
454
|
+
// Restore the original scroll positions
|
|
455
|
+
window.scrollTo(originalScrollX, originalScrollY);
|
|
456
|
+
|
|
457
|
+
return isClickable && isClickNotBlocked;
|
|
458
|
+
},
|
|
412
459
|
);
|
|
413
460
|
const hints = generateHintStrings(
|
|
414
461
|
hintCharacterSet,
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import { test } from "@playwright/test";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
test("should annotate all links on empirical landing page", async ({
|
|
7
|
+
page,
|
|
8
|
+
}) => {
|
|
9
|
+
await page.goto(
|
|
10
|
+
"https://assets-test.empirical.run/selector-hints-testing/dom-1.html",
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
await page.addScriptTag({
|
|
14
|
+
path: path.resolve(__dirname, "./annotate-elements.js"),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const annotations = await page.evaluate(() => {
|
|
18
|
+
const { annotations } = window.annotateClickableElements();
|
|
19
|
+
|
|
20
|
+
return Object.entries(annotations).map(([hint, config]) => ({
|
|
21
|
+
hint,
|
|
22
|
+
innerText: config.node.innerText,
|
|
23
|
+
tagName: config.node.tagName,
|
|
24
|
+
href: config.node.href,
|
|
25
|
+
}));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test.expect(annotations).toEqual([
|
|
29
|
+
{
|
|
30
|
+
hint: "A",
|
|
31
|
+
innerText: "Empirical",
|
|
32
|
+
tagName: "A",
|
|
33
|
+
href: "https://assets-test.empirical.run/",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
hint: "B",
|
|
37
|
+
innerText: "Blog",
|
|
38
|
+
tagName: "A",
|
|
39
|
+
href: "https://assets-test.empirical.run/blog",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
hint: "C",
|
|
43
|
+
innerText: "Contact us",
|
|
44
|
+
tagName: "A",
|
|
45
|
+
href: "https://assets-test.empirical.run/contact",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
hint: "D",
|
|
49
|
+
href: "https://dash.empirical.run/",
|
|
50
|
+
innerText: "Login ↗\n(opens in a new tab)",
|
|
51
|
+
tagName: "A",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
hint: "E",
|
|
55
|
+
innerText: "Get early access",
|
|
56
|
+
tagName: "A",
|
|
57
|
+
href: "https://assets-test.empirical.run/contact",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
hint: "F",
|
|
61
|
+
innerText: "Playwright\n(opens in a new tab)",
|
|
62
|
+
tagName: "A",
|
|
63
|
+
href: "https://github.com/microsoft/playwright",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
hint: "G",
|
|
67
|
+
innerText: "Meet with us",
|
|
68
|
+
tagName: "A",
|
|
69
|
+
href: "https://assets-test.empirical.run/contact",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
hint: "H",
|
|
73
|
+
innerText: "Privacy Policy",
|
|
74
|
+
tagName: "A",
|
|
75
|
+
href: "https://assets-test.empirical.run/privacy.html",
|
|
76
|
+
},
|
|
77
|
+
]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("should annotate all important items on quizizz page", async ({
|
|
81
|
+
page,
|
|
82
|
+
}) => {
|
|
83
|
+
await page.goto(
|
|
84
|
+
"https://assets-test.empirical.run/selector-hints-testing/dom-2/index.html",
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
await page.addScriptTag({
|
|
88
|
+
path: path.resolve(__dirname, "./annotate-elements.js"),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const annotations = await page.evaluate(() => {
|
|
92
|
+
const { annotations } = window.annotateClickableElements();
|
|
93
|
+
|
|
94
|
+
return Object.entries(annotations).map(([hint, config]) => ({
|
|
95
|
+
hint,
|
|
96
|
+
innerText: config.node.innerText.toLowerCase().trim(),
|
|
97
|
+
tagName: config.node.tagName,
|
|
98
|
+
testId: config.node.getAttribute("data-testid"),
|
|
99
|
+
href: config.node.href,
|
|
100
|
+
}));
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test
|
|
104
|
+
.expect(annotations.find((item) => item.innerText.includes("enter code")))
|
|
105
|
+
.toBeTruthy();
|
|
106
|
+
test
|
|
107
|
+
.expect(annotations.find((item) => item.innerText.includes("get help")))
|
|
108
|
+
.toBeTruthy();
|
|
109
|
+
test
|
|
110
|
+
.expect(
|
|
111
|
+
annotations.find(
|
|
112
|
+
(item) =>
|
|
113
|
+
item.innerText.includes("create") &&
|
|
114
|
+
item.testId === "create-content-button",
|
|
115
|
+
),
|
|
116
|
+
)
|
|
117
|
+
.toBeTruthy();
|
|
118
|
+
test
|
|
119
|
+
.expect(
|
|
120
|
+
annotations.find(
|
|
121
|
+
(item) =>
|
|
122
|
+
item.innerText.includes("explore") &&
|
|
123
|
+
item.href === "https://quizizz.com/admin",
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
.toBeTruthy();
|
|
127
|
+
test
|
|
128
|
+
.expect(
|
|
129
|
+
annotations.find(
|
|
130
|
+
(item) =>
|
|
131
|
+
item.innerText.includes("library") &&
|
|
132
|
+
item.href === "https://quizizz.com/admin/my-library/createdByMe",
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
.toBeTruthy();
|
|
136
|
+
test
|
|
137
|
+
.expect(
|
|
138
|
+
annotations.find(
|
|
139
|
+
(item) =>
|
|
140
|
+
item.innerText.includes("reports") &&
|
|
141
|
+
item.href === "https://quizizz.com/admin/reports",
|
|
142
|
+
),
|
|
143
|
+
)
|
|
144
|
+
.toBeTruthy();
|
|
145
|
+
test
|
|
146
|
+
.expect(
|
|
147
|
+
annotations.find(
|
|
148
|
+
(item) =>
|
|
149
|
+
item.innerText.includes("classes") &&
|
|
150
|
+
item.href === "https://quizizz.com/admin/classes",
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
.toBeTruthy();
|
|
154
|
+
test
|
|
155
|
+
.expect(
|
|
156
|
+
annotations.find(
|
|
157
|
+
(item) =>
|
|
158
|
+
item.innerText.includes("accommodations") &&
|
|
159
|
+
item.href ===
|
|
160
|
+
"https://quizizz.com/admin/differentiation/accommodations",
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
.toBeTruthy();
|
|
164
|
+
test
|
|
165
|
+
.expect(
|
|
166
|
+
annotations.find(
|
|
167
|
+
(item) =>
|
|
168
|
+
item.innerText.includes("quizizz ai") &&
|
|
169
|
+
item.href === "https://quizizz.com/admin/quizizz-ai",
|
|
170
|
+
),
|
|
171
|
+
)
|
|
172
|
+
.toBeTruthy();
|
|
173
|
+
test
|
|
174
|
+
.expect(
|
|
175
|
+
annotations.find(
|
|
176
|
+
(item) =>
|
|
177
|
+
item.innerText.includes("start your free trial") &&
|
|
178
|
+
item.href === "https://quizizz.com/super-pricing",
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
.toBeTruthy();
|
|
182
|
+
test
|
|
183
|
+
.expect(
|
|
184
|
+
annotations.find(
|
|
185
|
+
(item) =>
|
|
186
|
+
item.innerText.includes("upgrade") &&
|
|
187
|
+
item.href === "https://quizizz.com/super-pricing?backto=/admin",
|
|
188
|
+
),
|
|
189
|
+
)
|
|
190
|
+
.toBeTruthy();
|
|
191
|
+
|
|
192
|
+
test
|
|
193
|
+
.expect(
|
|
194
|
+
annotations.find(
|
|
195
|
+
(item) =>
|
|
196
|
+
item.tagName === "INPUT" &&
|
|
197
|
+
item.testId === "emphasized-search-bar-input",
|
|
198
|
+
),
|
|
199
|
+
)
|
|
200
|
+
.toBeTruthy();
|
|
201
|
+
|
|
202
|
+
test
|
|
203
|
+
.expect(
|
|
204
|
+
annotations.find(
|
|
205
|
+
(item) =>
|
|
206
|
+
item.tagName === "BUTTON" &&
|
|
207
|
+
item.innerText.includes("verify details") &&
|
|
208
|
+
item.testId === "verify-profile-cta",
|
|
209
|
+
),
|
|
210
|
+
)
|
|
211
|
+
.toBeTruthy();
|
|
212
|
+
|
|
213
|
+
test
|
|
214
|
+
.expect(
|
|
215
|
+
annotations.find(
|
|
216
|
+
(item) =>
|
|
217
|
+
item.tagName === "BUTTON" && item.innerText.includes("for you"),
|
|
218
|
+
),
|
|
219
|
+
)
|
|
220
|
+
.toBeTruthy();
|
|
221
|
+
|
|
222
|
+
test
|
|
223
|
+
.expect(
|
|
224
|
+
annotations.find(
|
|
225
|
+
(item) =>
|
|
226
|
+
item.tagName === "BUTTON" && item.innerText.includes("assessments"),
|
|
227
|
+
),
|
|
228
|
+
)
|
|
229
|
+
.toBeTruthy();
|
|
230
|
+
|
|
231
|
+
test
|
|
232
|
+
.expect(
|
|
233
|
+
annotations.find(
|
|
234
|
+
(item) =>
|
|
235
|
+
item.tagName === "BUTTON" && item.innerText.includes("lessons"),
|
|
236
|
+
),
|
|
237
|
+
)
|
|
238
|
+
.toBeTruthy();
|
|
239
|
+
|
|
240
|
+
test
|
|
241
|
+
.expect(
|
|
242
|
+
annotations.find(
|
|
243
|
+
(item) =>
|
|
244
|
+
item.tagName === "BUTTON" &&
|
|
245
|
+
item.innerText.includes("interactive videos"),
|
|
246
|
+
),
|
|
247
|
+
)
|
|
248
|
+
.toBeTruthy();
|
|
249
|
+
|
|
250
|
+
test
|
|
251
|
+
.expect(
|
|
252
|
+
annotations.find(
|
|
253
|
+
(item) =>
|
|
254
|
+
item.tagName === "BUTTON" && item.innerText.includes("passages"),
|
|
255
|
+
),
|
|
256
|
+
)
|
|
257
|
+
.toBeTruthy();
|
|
258
|
+
});
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { TraceClient } from "@empiricalrun/llm";
|
|
2
2
|
import { TestCase, TestGenConfigOptions } from "../../types";
|
|
3
|
+
export declare function getAddScenarioCompletion({ testCase, testFiles, pageFiles, testFilePath, trace, options, }: {
|
|
4
|
+
testCase: TestCase;
|
|
5
|
+
testFiles: string;
|
|
6
|
+
pageFiles: string;
|
|
7
|
+
testFilePath: string;
|
|
8
|
+
trace?: TraceClient;
|
|
9
|
+
options?: TestGenConfigOptions;
|
|
10
|
+
}): Promise<string>;
|
|
3
11
|
export declare function generateTest(testCase: TestCase, file: string, options: TestGenConfigOptions, trace?: TraceClient): Promise<TestCase[]>;
|
|
4
12
|
//# sourceMappingURL=run.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAkBhF,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI7D,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,EAC7B,KAAK,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,QAAQ,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAkBhF,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI7D,wBAAsB,wBAAwB,CAAC,EAC7C,QAAQ,EACR,SAAS,EACT,SAAS,EACT,YAAY,EACZ,KAAK,EACL,OAAO,GACR,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC,mBA4BA;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,EAC7B,KAAK,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,QAAQ,EAAE,CAAC,CAgFrB"}
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.generateTest = void 0;
|
|
6
|
+
exports.generateTest = exports.getAddScenarioCompletion = void 0;
|
|
7
7
|
const llm_1 = require("@empiricalrun/llm");
|
|
8
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
9
|
const logger_1 = require("../../bin/logger");
|
|
@@ -12,6 +12,35 @@ const web_1 = require("../../bin/utils/platform/web");
|
|
|
12
12
|
const constants_1 = require("../../constants");
|
|
13
13
|
const fix_ts_errors_1 = require("./fix-ts-errors");
|
|
14
14
|
const update_flow_1 = require("./update-flow");
|
|
15
|
+
async function getAddScenarioCompletion({ testCase, testFiles, pageFiles, testFilePath, trace, options, }) {
|
|
16
|
+
const promptSpan = trace?.span({
|
|
17
|
+
name: "add-scenario-prompt",
|
|
18
|
+
});
|
|
19
|
+
const instruction = await (0, llm_1.getPrompt)("add-scenario", {
|
|
20
|
+
testFiles: testFiles,
|
|
21
|
+
pageFiles: pageFiles,
|
|
22
|
+
scenarioName: testCase.name,
|
|
23
|
+
scenarioSteps: testCase.steps.join("\n"),
|
|
24
|
+
scenarioFile: testFilePath,
|
|
25
|
+
});
|
|
26
|
+
promptSpan?.end({ output: { instruction } });
|
|
27
|
+
const llm = new llm_1.LLM({
|
|
28
|
+
trace,
|
|
29
|
+
provider: options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
|
|
30
|
+
defaultModel: options?.model || constants_1.DEFAULT_MODEL,
|
|
31
|
+
providerApiKey: constants_1.MODEL_API_KEYS[options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
|
|
32
|
+
});
|
|
33
|
+
const firstShotMessage = await llm.createChatCompletion({
|
|
34
|
+
messages: instruction,
|
|
35
|
+
modelParameters: {
|
|
36
|
+
...constants_1.DEFAULT_MODEL_PARAMETERS,
|
|
37
|
+
...options?.modelParameters,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
let response = firstShotMessage?.content || "";
|
|
41
|
+
return response;
|
|
42
|
+
}
|
|
43
|
+
exports.getAddScenarioCompletion = getAddScenarioCompletion;
|
|
15
44
|
async function generateTest(testCase, file, options, trace) {
|
|
16
45
|
const logger = new logger_1.CustomLogger();
|
|
17
46
|
if (!fs_extra_1.default.existsSync(file)) {
|
|
@@ -35,43 +64,19 @@ async function generateTest(testCase, file, options, trace) {
|
|
|
35
64
|
name: "create-test",
|
|
36
65
|
input: {
|
|
37
66
|
testCase,
|
|
38
|
-
|
|
39
|
-
|
|
67
|
+
testFiles: codePrompt,
|
|
68
|
+
pageFiles: pomPrompt,
|
|
69
|
+
testFilePath: file,
|
|
40
70
|
},
|
|
41
71
|
});
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
output: {
|
|
45
|
-
codePrompt,
|
|
46
|
-
pomPrompt,
|
|
47
|
-
testFileContent,
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
const promptSpan = createTestSpan?.span({
|
|
51
|
-
name: "add-scenario-prompt",
|
|
52
|
-
});
|
|
53
|
-
const instruction = await (0, llm_1.getPrompt)("add-scenario", {
|
|
72
|
+
const response = await getAddScenarioCompletion({
|
|
73
|
+
testCase,
|
|
54
74
|
testFiles: codePrompt,
|
|
55
75
|
pageFiles: pomPrompt,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
});
|
|
60
|
-
promptSpan?.end({ output: { instruction } });
|
|
61
|
-
const llm = new llm_1.LLM({
|
|
62
|
-
trace,
|
|
63
|
-
provider: options.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
|
|
64
|
-
defaultModel: options.model || constants_1.DEFAULT_MODEL,
|
|
65
|
-
providerApiKey: constants_1.MODEL_API_KEYS[options.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
|
|
66
|
-
});
|
|
67
|
-
const firstShotMessage = await llm.createChatCompletion({
|
|
68
|
-
messages: instruction,
|
|
69
|
-
modelParameters: {
|
|
70
|
-
...constants_1.DEFAULT_MODEL_PARAMETERS,
|
|
71
|
-
...options.modelParameters,
|
|
72
|
-
},
|
|
76
|
+
testFilePath: file,
|
|
77
|
+
trace: createTestSpan,
|
|
78
|
+
options,
|
|
73
79
|
});
|
|
74
|
-
let response = firstShotMessage?.content || "";
|
|
75
80
|
logger.success("Test generated successfully!");
|
|
76
81
|
const readWriteFileSpan = trace?.span({ name: "write-to-file" });
|
|
77
82
|
let contents = fs_extra_1.default.readFileSync(file, "utf-8");
|
|
@@ -3,6 +3,15 @@ import { TestCase, TestGenConfigOptions } from "../../types";
|
|
|
3
3
|
type UpdatedTestCase = TestCase & {
|
|
4
4
|
updatedFiles: string[];
|
|
5
5
|
};
|
|
6
|
+
export declare function getUpdateTestCodeCompletion({ testCase, testFileContent, testFiles, pageFiles, testFilePath, trace, options, }: {
|
|
7
|
+
testCase: TestCase;
|
|
8
|
+
testFiles: string;
|
|
9
|
+
pageFiles: string;
|
|
10
|
+
testFilePath: string;
|
|
11
|
+
testFileContent: string;
|
|
12
|
+
trace?: TraceClient;
|
|
13
|
+
options?: TestGenConfigOptions;
|
|
14
|
+
}): Promise<string>;
|
|
6
15
|
export declare function updateTest(testCase: TestCase, file: string, options: TestGenConfigOptions | undefined, logging?: boolean, validate?: boolean, trace?: TraceClient): Promise<UpdatedTestCase[]>;
|
|
7
16
|
export declare function appendCreateTestBlock({ testCase, file, options, trace, validateTypes, }: {
|
|
8
17
|
testCase: TestCase;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-flow.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/update-flow.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAsB3B,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI7D,KAAK,eAAe,GAAG,QAAQ,GAAG;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAqIF,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAAG,SAAS,EACzC,OAAO,GAAE,OAAc,EACvB,QAAQ,GAAE,OAAc,EACxB,KAAK,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,eAAe,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"update-flow.d.ts","sourceRoot":"","sources":["../../../src/agent/codegen/update-flow.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAsB3B,OAAO,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI7D,KAAK,eAAe,GAAG,QAAQ,GAAG;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAqIF,wBAAsB,2BAA2B,CAAC,EAChD,QAAQ,EACR,eAAe,EACf,SAAS,EACT,SAAS,EACT,YAAY,EACZ,KAAK,EACL,OAAO,GACR,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC,GAAG,OAAO,CAAC,MAAM,CAAC,CA6ClB;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAAG,SAAS,EACzC,OAAO,GAAE,OAAc,EACvB,QAAQ,GAAE,OAAc,EACxB,KAAK,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,eAAe,EAAE,CAAC,CA6D5B;AAED,wBAAsB,qBAAqB,CAAC,EAC1C,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,KAAK,EACL,aAAoB,GACrB,EAAE;IACD,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CA+E7B"}
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.appendCreateTestBlock = exports.updateTest = void 0;
|
|
6
|
+
exports.appendCreateTestBlock = exports.updateTest = exports.getUpdateTestCodeCompletion = void 0;
|
|
7
7
|
const llm_1 = require("@empiricalrun/llm");
|
|
8
8
|
const crypto_1 = __importDefault(require("crypto"));
|
|
9
9
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
@@ -103,41 +103,8 @@ async function applyFileChanges({ validateTypes = true, trace, testCase, fileCha
|
|
|
103
103
|
logger.success(`${fileChange.filePath} file formatted successfully!`);
|
|
104
104
|
}));
|
|
105
105
|
}
|
|
106
|
-
async function
|
|
107
|
-
const
|
|
108
|
-
const context = await (0, context_1.contextForGeneration)(file);
|
|
109
|
-
const { codePrompt, pomPrompt, testFileContent } = context;
|
|
110
|
-
const generatedTestCases = [];
|
|
111
|
-
logger.logEmptyLine();
|
|
112
|
-
const session = (0, session_1.getSessionDetails)();
|
|
113
|
-
trace =
|
|
114
|
-
trace ||
|
|
115
|
-
llm_1.langfuseInstance?.trace({
|
|
116
|
-
name: "update-test",
|
|
117
|
-
id: crypto_1.default.randomUUID(),
|
|
118
|
-
release: session.version,
|
|
119
|
-
tags: [
|
|
120
|
-
options?.metadata.projectName || "",
|
|
121
|
-
options?.metadata.environment || "",
|
|
122
|
-
].filter((s) => !!s),
|
|
123
|
-
});
|
|
124
|
-
const updateTestSpan = trace?.span({
|
|
125
|
-
name: "update-test",
|
|
126
|
-
input: {
|
|
127
|
-
testCase,
|
|
128
|
-
file,
|
|
129
|
-
options,
|
|
130
|
-
},
|
|
131
|
-
});
|
|
132
|
-
updateTestSpan?.event({
|
|
133
|
-
name: "collate-files-as-text",
|
|
134
|
-
output: {
|
|
135
|
-
codePrompt,
|
|
136
|
-
pomPrompt,
|
|
137
|
-
testFileContent,
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
const promptSpan = updateTestSpan?.span({
|
|
106
|
+
async function getUpdateTestCodeCompletion({ testCase, testFileContent, testFiles, pageFiles, testFilePath, trace, options, }) {
|
|
107
|
+
const promptSpan = trace?.span({
|
|
141
108
|
name: "update-scenario-prompt",
|
|
142
109
|
});
|
|
143
110
|
const promptName = "update-scenario";
|
|
@@ -154,16 +121,16 @@ async function updateTest(testCase, file, options, logging = true, validate = tr
|
|
|
154
121
|
suites: testCase?.suites || [],
|
|
155
122
|
});
|
|
156
123
|
const instruction = await (0, llm_1.getPrompt)(promptName, {
|
|
157
|
-
testFiles:
|
|
158
|
-
pageFiles:
|
|
124
|
+
testFiles: testFiles,
|
|
125
|
+
pageFiles: pageFiles,
|
|
159
126
|
scenarioName,
|
|
160
127
|
scenarioSteps: testCase.steps.join("\n"),
|
|
161
|
-
scenarioFile:
|
|
128
|
+
scenarioFile: testFilePath,
|
|
162
129
|
currentScenarioCodeBlock,
|
|
163
130
|
});
|
|
164
131
|
promptSpan?.end({ output: { instruction } });
|
|
165
132
|
const llm = new llm_1.LLM({
|
|
166
|
-
trace
|
|
133
|
+
trace,
|
|
167
134
|
provider: options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER,
|
|
168
135
|
defaultModel: options?.model || constants_1.DEFAULT_MODEL,
|
|
169
136
|
providerApiKey: constants_1.MODEL_API_KEYS[options?.modelProvider || constants_1.DEFAULT_MODEL_PROVIDER],
|
|
@@ -176,6 +143,43 @@ async function updateTest(testCase, file, options, logging = true, validate = tr
|
|
|
176
143
|
},
|
|
177
144
|
});
|
|
178
145
|
let response = firstShotMessage?.content || "";
|
|
146
|
+
return response;
|
|
147
|
+
}
|
|
148
|
+
exports.getUpdateTestCodeCompletion = getUpdateTestCodeCompletion;
|
|
149
|
+
async function updateTest(testCase, file, options, logging = true, validate = true, trace) {
|
|
150
|
+
const logger = new logger_1.CustomLogger({ useReporter: logging });
|
|
151
|
+
const context = await (0, context_1.contextForGeneration)(file);
|
|
152
|
+
const { codePrompt, pomPrompt, testFileContent } = context;
|
|
153
|
+
const generatedTestCases = [];
|
|
154
|
+
logger.logEmptyLine();
|
|
155
|
+
const session = (0, session_1.getSessionDetails)();
|
|
156
|
+
trace =
|
|
157
|
+
trace ||
|
|
158
|
+
llm_1.langfuseInstance?.trace({
|
|
159
|
+
name: "update-test",
|
|
160
|
+
id: crypto_1.default.randomUUID(),
|
|
161
|
+
release: session.version,
|
|
162
|
+
tags: [
|
|
163
|
+
options?.metadata.projectName || "",
|
|
164
|
+
options?.metadata.environment || "",
|
|
165
|
+
].filter((s) => !!s),
|
|
166
|
+
});
|
|
167
|
+
const request = {
|
|
168
|
+
testCase,
|
|
169
|
+
testFileContent,
|
|
170
|
+
testFiles: codePrompt,
|
|
171
|
+
pageFiles: pomPrompt,
|
|
172
|
+
testFilePath: file,
|
|
173
|
+
options,
|
|
174
|
+
};
|
|
175
|
+
const updateTestSpan = trace?.span({
|
|
176
|
+
name: "update-test",
|
|
177
|
+
input: request,
|
|
178
|
+
});
|
|
179
|
+
const response = await getUpdateTestCodeCompletion({
|
|
180
|
+
...request,
|
|
181
|
+
trace: updateTestSpan,
|
|
182
|
+
});
|
|
179
183
|
logger.success("Test generated successfully!");
|
|
180
184
|
const fileChanges = (0, utils_1.extractTestUpdates)(response);
|
|
181
185
|
await applyFileChanges({
|
package/dist/agent/master/run.js
CHANGED
|
@@ -40,7 +40,7 @@ async function getNextAction({ task, executedActions, failedActions, pageUrl, tr
|
|
|
40
40
|
failedActions: failedActions.map((a) => a).join("\n"),
|
|
41
41
|
executedActions: executedActions.map((a) => a).join("\n"),
|
|
42
42
|
pageUrl,
|
|
43
|
-
},
|
|
43
|
+
}, 18);
|
|
44
44
|
// assuming there is only one user message in the prompt. if there is a change in langfuse prompt format, this will need to be updated
|
|
45
45
|
const userMessage = promptMessages.filter((m) => m.role === "user")[0];
|
|
46
46
|
const systemMessage = promptMessages.filter((m) => m.role === "system")[0];
|
|
@@ -22,20 +22,56 @@ window.annotateClickableElements = function annotateClickableElements(
|
|
|
22
22
|
let annotationsContainer = null;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Checks if
|
|
26
|
-
*
|
|
25
|
+
* Checks if the provided DOM element is clickable by ensuring that
|
|
26
|
+
* no other elements are covering it, regardless of its current
|
|
27
|
+
* viewport visibility.
|
|
28
|
+
*
|
|
27
29
|
* @param {Element} element - The DOM element to check.
|
|
28
|
-
* @returns {boolean}
|
|
30
|
+
* @returns {boolean} - Returns true if the element is clickable; otherwise, false.
|
|
29
31
|
*/
|
|
30
|
-
|
|
32
|
+
|
|
33
|
+
function isElementClickNotBlocked(element) {
|
|
31
34
|
const rect = element.getBoundingClientRect();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
|
|
36
|
+
// Calculate the center point of the element
|
|
37
|
+
const centerX = rect.left + rect.width / 2;
|
|
38
|
+
const centerY = rect.top + rect.height / 2;
|
|
39
|
+
|
|
40
|
+
// check if element is within the viewport
|
|
41
|
+
if (
|
|
42
|
+
centerX < 0 ||
|
|
43
|
+
centerY < 0 ||
|
|
44
|
+
centerX > (window.innerWidth || document.documentElement.clientWidth) ||
|
|
45
|
+
centerY > (window.innerHeight || document.documentElement.clientHeight)
|
|
46
|
+
) {
|
|
47
|
+
// Determine the viewport dimensions
|
|
48
|
+
const viewportWidth =
|
|
49
|
+
window.innerWidth || document.documentElement.clientWidth;
|
|
50
|
+
const viewportHeight =
|
|
51
|
+
window.innerHeight || document.documentElement.clientHeight;
|
|
52
|
+
|
|
53
|
+
// Calculate the new scroll positions to bring the element into the center of the viewport
|
|
54
|
+
const newScrollX = centerX - viewportWidth / 2;
|
|
55
|
+
const newScrollY = centerY - viewportHeight / 2;
|
|
56
|
+
|
|
57
|
+
// Scroll the window to the new positions
|
|
58
|
+
window.scrollTo({
|
|
59
|
+
top: newScrollY,
|
|
60
|
+
left: newScrollX,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const newRect = element.getBoundingClientRect();
|
|
64
|
+
const newCenterX = newRect.left + newRect.width / 2;
|
|
65
|
+
const newCenterY = newRect.top + newRect.height / 2;
|
|
66
|
+
const topElement = document.elementFromPoint(newCenterX, newCenterY);
|
|
67
|
+
return element.contains(topElement);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Retrieve the topmost element at the center point
|
|
71
|
+
const topElement = document.elementFromPoint(centerX, centerY);
|
|
72
|
+
|
|
73
|
+
// Check if the topmost element is the target element or one of its descendants
|
|
74
|
+
return element.contains(topElement);
|
|
39
75
|
}
|
|
40
76
|
|
|
41
77
|
/**
|
|
@@ -408,7 +444,18 @@ window.annotateClickableElements = function annotateClickableElements(
|
|
|
408
444
|
document.body.appendChild(annotationsContainer);
|
|
409
445
|
|
|
410
446
|
const clickableElements = Array.from(document.querySelectorAll("*")).filter(
|
|
411
|
-
(el) =>
|
|
447
|
+
(el) => {
|
|
448
|
+
const isClickable = isElementClickable(el);
|
|
449
|
+
|
|
450
|
+
const originalScrollX = window.scrollX;
|
|
451
|
+
const originalScrollY = window.scrollY;
|
|
452
|
+
|
|
453
|
+
const isClickNotBlocked = isElementClickNotBlocked(el);
|
|
454
|
+
// Restore the original scroll positions
|
|
455
|
+
window.scrollTo(originalScrollX, originalScrollY);
|
|
456
|
+
|
|
457
|
+
return isClickable && isClickNotBlocked;
|
|
458
|
+
},
|
|
412
459
|
);
|
|
413
460
|
const hints = generateHintStrings(
|
|
414
461
|
hintCharacterSet,
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
import { test } from "@playwright/test";
|
|
4
|
+
import path from "path";
|
|
5
|
+
|
|
6
|
+
test("should annotate all links on empirical landing page", async ({
|
|
7
|
+
page,
|
|
8
|
+
}) => {
|
|
9
|
+
await page.goto(
|
|
10
|
+
"https://assets-test.empirical.run/selector-hints-testing/dom-1.html",
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
await page.addScriptTag({
|
|
14
|
+
path: path.resolve(__dirname, "./annotate-elements.js"),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const annotations = await page.evaluate(() => {
|
|
18
|
+
const { annotations } = window.annotateClickableElements();
|
|
19
|
+
|
|
20
|
+
return Object.entries(annotations).map(([hint, config]) => ({
|
|
21
|
+
hint,
|
|
22
|
+
innerText: config.node.innerText,
|
|
23
|
+
tagName: config.node.tagName,
|
|
24
|
+
href: config.node.href,
|
|
25
|
+
}));
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test.expect(annotations).toEqual([
|
|
29
|
+
{
|
|
30
|
+
hint: "A",
|
|
31
|
+
innerText: "Empirical",
|
|
32
|
+
tagName: "A",
|
|
33
|
+
href: "https://assets-test.empirical.run/",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
hint: "B",
|
|
37
|
+
innerText: "Blog",
|
|
38
|
+
tagName: "A",
|
|
39
|
+
href: "https://assets-test.empirical.run/blog",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
hint: "C",
|
|
43
|
+
innerText: "Contact us",
|
|
44
|
+
tagName: "A",
|
|
45
|
+
href: "https://assets-test.empirical.run/contact",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
hint: "D",
|
|
49
|
+
href: "https://dash.empirical.run/",
|
|
50
|
+
innerText: "Login ↗\n(opens in a new tab)",
|
|
51
|
+
tagName: "A",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
hint: "E",
|
|
55
|
+
innerText: "Get early access",
|
|
56
|
+
tagName: "A",
|
|
57
|
+
href: "https://assets-test.empirical.run/contact",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
hint: "F",
|
|
61
|
+
innerText: "Playwright\n(opens in a new tab)",
|
|
62
|
+
tagName: "A",
|
|
63
|
+
href: "https://github.com/microsoft/playwright",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
hint: "G",
|
|
67
|
+
innerText: "Meet with us",
|
|
68
|
+
tagName: "A",
|
|
69
|
+
href: "https://assets-test.empirical.run/contact",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
hint: "H",
|
|
73
|
+
innerText: "Privacy Policy",
|
|
74
|
+
tagName: "A",
|
|
75
|
+
href: "https://assets-test.empirical.run/privacy.html",
|
|
76
|
+
},
|
|
77
|
+
]);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("should annotate all important items on quizizz page", async ({
|
|
81
|
+
page,
|
|
82
|
+
}) => {
|
|
83
|
+
await page.goto(
|
|
84
|
+
"https://assets-test.empirical.run/selector-hints-testing/dom-2/index.html",
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
await page.addScriptTag({
|
|
88
|
+
path: path.resolve(__dirname, "./annotate-elements.js"),
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const annotations = await page.evaluate(() => {
|
|
92
|
+
const { annotations } = window.annotateClickableElements();
|
|
93
|
+
|
|
94
|
+
return Object.entries(annotations).map(([hint, config]) => ({
|
|
95
|
+
hint,
|
|
96
|
+
innerText: config.node.innerText.toLowerCase().trim(),
|
|
97
|
+
tagName: config.node.tagName,
|
|
98
|
+
testId: config.node.getAttribute("data-testid"),
|
|
99
|
+
href: config.node.href,
|
|
100
|
+
}));
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test
|
|
104
|
+
.expect(annotations.find((item) => item.innerText.includes("enter code")))
|
|
105
|
+
.toBeTruthy();
|
|
106
|
+
test
|
|
107
|
+
.expect(annotations.find((item) => item.innerText.includes("get help")))
|
|
108
|
+
.toBeTruthy();
|
|
109
|
+
test
|
|
110
|
+
.expect(
|
|
111
|
+
annotations.find(
|
|
112
|
+
(item) =>
|
|
113
|
+
item.innerText.includes("create") &&
|
|
114
|
+
item.testId === "create-content-button",
|
|
115
|
+
),
|
|
116
|
+
)
|
|
117
|
+
.toBeTruthy();
|
|
118
|
+
test
|
|
119
|
+
.expect(
|
|
120
|
+
annotations.find(
|
|
121
|
+
(item) =>
|
|
122
|
+
item.innerText.includes("explore") &&
|
|
123
|
+
item.href === "https://quizizz.com/admin",
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
.toBeTruthy();
|
|
127
|
+
test
|
|
128
|
+
.expect(
|
|
129
|
+
annotations.find(
|
|
130
|
+
(item) =>
|
|
131
|
+
item.innerText.includes("library") &&
|
|
132
|
+
item.href === "https://quizizz.com/admin/my-library/createdByMe",
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
.toBeTruthy();
|
|
136
|
+
test
|
|
137
|
+
.expect(
|
|
138
|
+
annotations.find(
|
|
139
|
+
(item) =>
|
|
140
|
+
item.innerText.includes("reports") &&
|
|
141
|
+
item.href === "https://quizizz.com/admin/reports",
|
|
142
|
+
),
|
|
143
|
+
)
|
|
144
|
+
.toBeTruthy();
|
|
145
|
+
test
|
|
146
|
+
.expect(
|
|
147
|
+
annotations.find(
|
|
148
|
+
(item) =>
|
|
149
|
+
item.innerText.includes("classes") &&
|
|
150
|
+
item.href === "https://quizizz.com/admin/classes",
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
.toBeTruthy();
|
|
154
|
+
test
|
|
155
|
+
.expect(
|
|
156
|
+
annotations.find(
|
|
157
|
+
(item) =>
|
|
158
|
+
item.innerText.includes("accommodations") &&
|
|
159
|
+
item.href ===
|
|
160
|
+
"https://quizizz.com/admin/differentiation/accommodations",
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
.toBeTruthy();
|
|
164
|
+
test
|
|
165
|
+
.expect(
|
|
166
|
+
annotations.find(
|
|
167
|
+
(item) =>
|
|
168
|
+
item.innerText.includes("quizizz ai") &&
|
|
169
|
+
item.href === "https://quizizz.com/admin/quizizz-ai",
|
|
170
|
+
),
|
|
171
|
+
)
|
|
172
|
+
.toBeTruthy();
|
|
173
|
+
test
|
|
174
|
+
.expect(
|
|
175
|
+
annotations.find(
|
|
176
|
+
(item) =>
|
|
177
|
+
item.innerText.includes("start your free trial") &&
|
|
178
|
+
item.href === "https://quizizz.com/super-pricing",
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
.toBeTruthy();
|
|
182
|
+
test
|
|
183
|
+
.expect(
|
|
184
|
+
annotations.find(
|
|
185
|
+
(item) =>
|
|
186
|
+
item.innerText.includes("upgrade") &&
|
|
187
|
+
item.href === "https://quizizz.com/super-pricing?backto=/admin",
|
|
188
|
+
),
|
|
189
|
+
)
|
|
190
|
+
.toBeTruthy();
|
|
191
|
+
|
|
192
|
+
test
|
|
193
|
+
.expect(
|
|
194
|
+
annotations.find(
|
|
195
|
+
(item) =>
|
|
196
|
+
item.tagName === "INPUT" &&
|
|
197
|
+
item.testId === "emphasized-search-bar-input",
|
|
198
|
+
),
|
|
199
|
+
)
|
|
200
|
+
.toBeTruthy();
|
|
201
|
+
|
|
202
|
+
test
|
|
203
|
+
.expect(
|
|
204
|
+
annotations.find(
|
|
205
|
+
(item) =>
|
|
206
|
+
item.tagName === "BUTTON" &&
|
|
207
|
+
item.innerText.includes("verify details") &&
|
|
208
|
+
item.testId === "verify-profile-cta",
|
|
209
|
+
),
|
|
210
|
+
)
|
|
211
|
+
.toBeTruthy();
|
|
212
|
+
|
|
213
|
+
test
|
|
214
|
+
.expect(
|
|
215
|
+
annotations.find(
|
|
216
|
+
(item) =>
|
|
217
|
+
item.tagName === "BUTTON" && item.innerText.includes("for you"),
|
|
218
|
+
),
|
|
219
|
+
)
|
|
220
|
+
.toBeTruthy();
|
|
221
|
+
|
|
222
|
+
test
|
|
223
|
+
.expect(
|
|
224
|
+
annotations.find(
|
|
225
|
+
(item) =>
|
|
226
|
+
item.tagName === "BUTTON" && item.innerText.includes("assessments"),
|
|
227
|
+
),
|
|
228
|
+
)
|
|
229
|
+
.toBeTruthy();
|
|
230
|
+
|
|
231
|
+
test
|
|
232
|
+
.expect(
|
|
233
|
+
annotations.find(
|
|
234
|
+
(item) =>
|
|
235
|
+
item.tagName === "BUTTON" && item.innerText.includes("lessons"),
|
|
236
|
+
),
|
|
237
|
+
)
|
|
238
|
+
.toBeTruthy();
|
|
239
|
+
|
|
240
|
+
test
|
|
241
|
+
.expect(
|
|
242
|
+
annotations.find(
|
|
243
|
+
(item) =>
|
|
244
|
+
item.tagName === "BUTTON" &&
|
|
245
|
+
item.innerText.includes("interactive videos"),
|
|
246
|
+
),
|
|
247
|
+
)
|
|
248
|
+
.toBeTruthy();
|
|
249
|
+
|
|
250
|
+
test
|
|
251
|
+
.expect(
|
|
252
|
+
annotations.find(
|
|
253
|
+
(item) =>
|
|
254
|
+
item.tagName === "BUTTON" && item.innerText.includes("passages"),
|
|
255
|
+
),
|
|
256
|
+
)
|
|
257
|
+
.toBeTruthy();
|
|
258
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-scenario-agent.evals.d.ts","sourceRoot":"","sources":["../../src/evals/add-scenario-agent.evals.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC,QAAA,MAAM,4BAA4B,EAAE,UAkBnC,CAAC;AAEF,eAAe,4BAA4B,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const run_1 = require("../agent/codegen/run");
|
|
4
|
+
const addScenarioCodeAgentEvaluate = async ({ item, trace }) => {
|
|
5
|
+
const { testCase, testFiles, pageFiles, testFilePath } = item.input;
|
|
6
|
+
const response = await (0, run_1.getAddScenarioCompletion)({
|
|
7
|
+
testCase,
|
|
8
|
+
testFiles,
|
|
9
|
+
pageFiles,
|
|
10
|
+
testFilePath,
|
|
11
|
+
trace,
|
|
12
|
+
});
|
|
13
|
+
return {
|
|
14
|
+
scores: [
|
|
15
|
+
{
|
|
16
|
+
name: "equality",
|
|
17
|
+
value: item.expectedOutput === response ? 1 : 0,
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
output: response,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
exports.default = addScenarioCodeAgentEvaluate;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-scenario-agent.evals.d.ts","sourceRoot":"","sources":["../../src/evals/update-scenario-agent.evals.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAUpC,QAAA,MAAM,+BAA+B,EAAE,UAiDtC,CAAC;AAEF,eAAe,+BAA+B,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
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 js_levenshtein_1 = __importDefault(require("js-levenshtein"));
|
|
7
|
+
const update_flow_1 = require("../agent/codegen/update-flow");
|
|
8
|
+
const utils_1 = require("../agent/codegen/utils");
|
|
9
|
+
const updateScenarioCodeAgentEvaluate = async ({ item, trace }) => {
|
|
10
|
+
const { testCase, testFiles, pageFiles, testFilePath, testFileContent } = item.input;
|
|
11
|
+
const response = await (0, update_flow_1.getUpdateTestCodeCompletion)({
|
|
12
|
+
testCase,
|
|
13
|
+
testFiles,
|
|
14
|
+
pageFiles,
|
|
15
|
+
testFilePath,
|
|
16
|
+
testFileContent,
|
|
17
|
+
trace,
|
|
18
|
+
});
|
|
19
|
+
const fileChanges = (0, utils_1.extractTestUpdates)(response);
|
|
20
|
+
const expectedFileChanges = (0, utils_1.extractTestUpdates)(item.expectedOutput);
|
|
21
|
+
const fileChangeCount = fileChanges.length;
|
|
22
|
+
const expectedFileChangeCount = expectedFileChanges.length;
|
|
23
|
+
const correctFilePathChanges = expectedFileChanges.every((ef) => fileChanges.some((f) => f.filePath === ef.filePath));
|
|
24
|
+
const distanceScores = [];
|
|
25
|
+
expectedFileChanges.forEach((ef) => fileChanges.forEach((f) => {
|
|
26
|
+
if (f.filePath === ef.filePath && f.newCode && ef.newCode) {
|
|
27
|
+
const maxLength = ef.newCode.length > f.newCode.length
|
|
28
|
+
? ef.newCode.length
|
|
29
|
+
: f.newCode.length;
|
|
30
|
+
distanceScores.push(1 - (0, js_levenshtein_1.default)(f.newCode || "", ef.newCode || "") / maxLength);
|
|
31
|
+
}
|
|
32
|
+
}));
|
|
33
|
+
let score = 0;
|
|
34
|
+
if (fileChangeCount === expectedFileChangeCount && correctFilePathChanges) {
|
|
35
|
+
score = distanceScores.length
|
|
36
|
+
? distanceScores.reduce((agg, s) => agg * s)
|
|
37
|
+
: 0;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
scores: [
|
|
41
|
+
{
|
|
42
|
+
name: "score",
|
|
43
|
+
value: score,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
output: response,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
exports.default = updateScenarioCodeAgentEvaluate;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/test-gen",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.5",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -52,8 +52,10 @@
|
|
|
52
52
|
"@types/detect-port": "^1.3.5",
|
|
53
53
|
"@types/express": "^4.17.21",
|
|
54
54
|
"@types/fs-extra": "^11.0.4",
|
|
55
|
+
"@types/js-levenshtein": "^1.1.3",
|
|
55
56
|
"@types/lodash.isequal": "^4.5.8",
|
|
56
|
-
"@types/md5": "^2.3.5"
|
|
57
|
+
"@types/md5": "^2.3.5",
|
|
58
|
+
"js-levenshtein": "^1.1.6"
|
|
57
59
|
},
|
|
58
60
|
"scripts": {
|
|
59
61
|
"dev": "tsc --build --watch",
|
|
@@ -61,6 +63,7 @@
|
|
|
61
63
|
"clean": "tsc --build --clean",
|
|
62
64
|
"lint": "eslint .",
|
|
63
65
|
"test": "vitest run",
|
|
66
|
+
"e2e-test": "npx playwright test",
|
|
64
67
|
"test:watch": "vitest",
|
|
65
68
|
"test:watch-files": "vitest $0 --watch"
|
|
66
69
|
}
|