@empiricalrun/playwright-utils 0.62.0 → 0.63.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.
- package/CHANGELOG.md +25 -0
- package/dist/api-worker-client.d.ts +0 -13
- package/dist/api-worker-client.d.ts.map +1 -1
- package/dist/api-worker-client.js +0 -15
- package/dist/auth/google.d.ts.map +1 -1
- package/dist/auth/google.js +0 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +0 -9
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +0 -8
- package/dist/generated/api-worker-openapi.d.ts +2773 -1298
- package/dist/generated/api-worker-openapi.d.ts.map +1 -1
- package/dist/overlay-tests/cache.spec.js +0 -10
- package/dist/overlay-tests/click.spec.js +0 -18
- package/dist/playwright-extensions.d.ts.map +1 -1
- package/dist/playwright-extensions.js +0 -1
- package/dist/reporter/attachment-cleanup-test-reporter.d.ts +0 -6
- package/dist/reporter/attachment-cleanup-test-reporter.d.ts.map +1 -1
- package/dist/reporter/attachment-cleanup-test-reporter.js +0 -6
- package/dist/reporter/attachment-cleanup.d.ts +0 -33
- package/dist/reporter/attachment-cleanup.d.ts.map +1 -1
- package/dist/reporter/attachment-cleanup.js +2 -51
- package/dist/reporter/empirical-reporter.d.ts.map +1 -1
- package/dist/reporter/empirical-reporter.js +0 -9
- package/dist/reporter/harness.d.ts +0 -62
- package/dist/reporter/harness.d.ts.map +1 -1
- package/dist/reporter/harness.js +1 -53
- package/dist/reporter/ibr-utils.d.ts +0 -5
- package/dist/reporter/ibr-utils.d.ts.map +1 -1
- package/dist/reporter/ibr-utils.js +0 -5
- package/dist/reporter/incremental-blob-reporter.d.ts +0 -17
- package/dist/reporter/incremental-blob-reporter.d.ts.map +1 -1
- package/dist/reporter/incremental-blob-reporter.js +5 -35
- package/dist/reporter/lifecycle-events.d.ts.map +1 -1
- package/dist/reporter/lifecycle-events.js +0 -9
- package/dist/reporter/parity-utils.d.ts +0 -30
- package/dist/reporter/parity-utils.d.ts.map +1 -1
- package/dist/reporter/parity-utils.js +4 -28
- package/dist/reporter/reporter-state.d.ts +0 -12
- package/dist/reporter/reporter-state.d.ts.map +1 -1
- package/dist/reporter/reporter-state.js +0 -12
- package/dist/reporter/test-case-sync.d.ts.map +1 -1
- package/dist/reporter/test-case-sync.js +0 -12
- package/dist/reporter/uploader.d.ts.map +1 -1
- package/dist/reporter/uploader.js +0 -6
- package/dist/reporter/util.d.ts +0 -33
- package/dist/reporter/util.d.ts.map +1 -1
- package/dist/reporter/util.js +0 -37
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +1 -10
- package/dist/test/coverage.d.ts.map +1 -1
- package/dist/test/coverage.js +1 -3
- package/dist/test/expect/visual.d.ts.map +1 -1
- package/dist/test/expect/visual.js +1 -9
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +4 -21
- package/dist/test/scripts/agent-capabilities.js +2 -2
- package/dist/test/scripts/index.d.ts +2 -2
- package/dist/test/scripts/index.d.ts.map +1 -1
- package/dist/test/scripts/index.js +3 -16
- package/dist/test/scripts/pw-locator-patch/actions/click.d.ts.map +1 -0
- package/dist/test/scripts/pw-locator-patch/{highlight → actions}/click.js +0 -6
- package/dist/test/scripts/pw-locator-patch/actions/hover.d.ts.map +1 -0
- package/dist/test/scripts/pw-locator-patch/{highlight → actions}/hover.js +0 -3
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/cache.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/cache.js +0 -7
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/index.js +1 -9
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/prompt.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/prompt.js +0 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/utils.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/dismiss-overlays/utils.js +2 -5
- package/dist/test/scripts/pw-locator-patch/utils/index.d.ts +0 -1
- package/dist/test/scripts/pw-locator-patch/utils/index.d.ts.map +1 -1
- package/dist/test/scripts/pw-locator-patch/utils/index.js +0 -18
- package/dist/test/types.d.ts +2 -5
- package/dist/test/types.d.ts.map +1 -1
- package/dist/test/video-labels.d.ts.map +1 -1
- package/dist/test/video-labels.js +0 -1
- package/dist/webhook.d.ts.map +1 -1
- package/dist/webhook.js +0 -3
- package/docs/fixtures.md +4 -5
- package/package.json +6 -6
- package/playwright.config.ts +0 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/upstream-oracle/ibr-adapter.js +0 -23
- package/upstream-oracle/run.mjs +1 -69
- package/dist/test/scripts/locator-highlights.d.ts +0 -4
- package/dist/test/scripts/locator-highlights.d.ts.map +0 -1
- package/dist/test/scripts/locator-highlights.js +0 -28
- package/dist/test/scripts/pw-locator-patch/highlight/click.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/expect.d.ts +0 -5
- package/dist/test/scripts/pw-locator-patch/highlight/expect.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/expect.js +0 -23
- package/dist/test/scripts/pw-locator-patch/highlight/hover.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/inner-text.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/inner-text.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/inner-text.js +0 -30
- package/dist/test/scripts/pw-locator-patch/highlight/input-value.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/input-value.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/input-value.js +0 -30
- package/dist/test/scripts/pw-locator-patch/highlight/is-checked.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/is-checked.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/is-checked.js +0 -30
- package/dist/test/scripts/pw-locator-patch/highlight/is-disabled.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/is-disabled.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/is-disabled.js +0 -30
- package/dist/test/scripts/pw-locator-patch/highlight/is-editable.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/is-editable.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/is-editable.js +0 -29
- package/dist/test/scripts/pw-locator-patch/highlight/text-content.d.ts +0 -6
- package/dist/test/scripts/pw-locator-patch/highlight/text-content.d.ts.map +0 -1
- package/dist/test/scripts/pw-locator-patch/highlight/text-content.js +0 -30
- /package/dist/test/scripts/pw-locator-patch/{highlight → actions}/click.d.ts +0 -0
- /package/dist/test/scripts/pw-locator-patch/{highlight → actions}/hover.d.ts +0 -0
|
@@ -13,7 +13,6 @@ const useCache = (data) => {
|
|
|
13
13
|
version: "2025-03-06",
|
|
14
14
|
data,
|
|
15
15
|
};
|
|
16
|
-
// Ensure the directory exists
|
|
17
16
|
const dir = path_1.default.dirname(cache_1.CACHE_FILE);
|
|
18
17
|
if (!fs_1.default.existsSync(dir)) {
|
|
19
18
|
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
@@ -43,13 +42,11 @@ fixtures_1.test.beforeEach(async ({ page }) => {
|
|
|
43
42
|
(0, test_1.injectLocatorHighlightScripts)(page, fixtures_1.test);
|
|
44
43
|
});
|
|
45
44
|
fixtures_1.test.afterEach(() => {
|
|
46
|
-
// Delete overlay cache
|
|
47
45
|
if (fs_1.default.existsSync(cache_1.CACHE_FILE)) {
|
|
48
46
|
fs_1.default.unlinkSync(cache_1.CACHE_FILE);
|
|
49
47
|
}
|
|
50
48
|
});
|
|
51
49
|
(0, fixtures_1.test)("overlay dismiss should not trigger recursively when executing from cache", async ({ page, server, }) => {
|
|
52
|
-
// Set up cache with a bad value: click on "Target" to dimiss the glass pane
|
|
53
50
|
useCache([
|
|
54
51
|
{
|
|
55
52
|
element: { dom: '<div class="glass-pane">', textContent: "" },
|
|
@@ -58,15 +55,8 @@ fixtures_1.test.afterEach(() => {
|
|
|
58
55
|
]);
|
|
59
56
|
await page.goto(`${server.baseURL}/glass-pane.html`);
|
|
60
57
|
let error;
|
|
61
|
-
// The only two routes the browser agent has to a model: the project API key
|
|
62
|
-
// (api-worker's proxy) and a sandbox's OPENAI_BASE_URL. Clearing both makes
|
|
63
|
-
// llmProxyOpenAIConfig throw before any request — which matters here more
|
|
64
|
-
// than elsewhere, because this is the recursive case and every extra
|
|
65
|
-
// dismissal would spawn another agent and another set of screenshots.
|
|
66
58
|
const restoreEnv = clearEnv(["EMPIRICALRUN_API_KEY", "OPENAI_BASE_URL"]);
|
|
67
59
|
try {
|
|
68
|
-
// Target element click will trigger the cache - which will invoke another
|
|
69
|
-
// click to the same element (recursive overlay dismissal)
|
|
70
60
|
await page.getByRole("button", { name: "Target" }).click();
|
|
71
61
|
}
|
|
72
62
|
catch (e) {
|
|
@@ -11,7 +11,6 @@ fixtures_1.test.beforeEach(async ({ page }) => {
|
|
|
11
11
|
(0, test_1.injectLocatorHighlightScripts)(page, fixtures_1.test);
|
|
12
12
|
});
|
|
13
13
|
fixtures_1.test.afterAll(async () => {
|
|
14
|
-
// Delete overlay cache
|
|
15
14
|
if (fs_1.default.existsSync(cache_1.CACHE_FILE)) {
|
|
16
15
|
fs_1.default.unlinkSync(cache_1.CACHE_FILE);
|
|
17
16
|
}
|
|
@@ -36,28 +35,20 @@ function getCacheDataWithTextContent({ textContent }) {
|
|
|
36
35
|
}
|
|
37
36
|
(0, fixtures_1.test)("should dismiss survicate for hover", async ({ page, server }) => {
|
|
38
37
|
await page.goto(`${server.baseURL}/survey.html`);
|
|
39
|
-
// Assert that Survicate and button loads
|
|
40
38
|
await (0, fixtures_1.expect)(page.getByText("Start survey")).toBeVisible();
|
|
41
39
|
const targetButton = page.getByRole("button", { name: "Target" });
|
|
42
40
|
await (0, fixtures_1.expect)(targetButton).toBeVisible();
|
|
43
|
-
// Get initial z-index
|
|
44
41
|
const initialZIndex = await targetButton.evaluate((el) => window.getComputedStyle(el).getPropertyValue("z-index"));
|
|
45
|
-
// Do the hover, which should pass after survey overlay is dismissed
|
|
46
42
|
await targetButton.hover();
|
|
47
|
-
// Assert z-index hasn't changed
|
|
48
43
|
const finalZIndex = await targetButton.evaluate((el) => window.getComputedStyle(el).getPropertyValue("z-index"));
|
|
49
44
|
(0, fixtures_1.expect)(finalZIndex).toBe(initialZIndex);
|
|
50
45
|
});
|
|
51
|
-
// Skipped since it's flaky
|
|
52
46
|
fixtures_1.test.skip("should dismiss two-step overlay for click", async ({ page, server, }) => {
|
|
53
47
|
await page.goto(`${server.baseURL}/tos.html`);
|
|
54
|
-
// Assert that button and overlay load
|
|
55
48
|
await (0, fixtures_1.expect)(page.getByRole("button", { name: "Target" })).toBeVisible();
|
|
56
49
|
await (0, fixtures_1.expect)(page.getByRole("heading", { name: "Terms of Service" })).toBeVisible();
|
|
57
|
-
// Do the click, which should pass
|
|
58
50
|
await page.getByRole("button", { name: "Target" }).click();
|
|
59
51
|
await (0, fixtures_1.expect)(page.getByRole("heading", { name: "Terms of Service" })).not.toBeVisible();
|
|
60
|
-
// Assert cache is correct
|
|
61
52
|
const withDom = getCacheDataWithDom({
|
|
62
53
|
dom: '<div class="terms-text">',
|
|
63
54
|
});
|
|
@@ -75,7 +66,6 @@ fixtures_1.test.skip("should dismiss two-step overlay for click", async ({ page,
|
|
|
75
66
|
await page.goto(`${server.baseURL}/overlay-form.html`);
|
|
76
67
|
await page.getByRole("button", { name: "Target" }).click();
|
|
77
68
|
await (0, fixtures_1.expect)(page.getByText("Target was clicked")).toBeVisible();
|
|
78
|
-
// Assert cache is correct
|
|
79
69
|
const withDom = getCacheDataWithDom({
|
|
80
70
|
dom: 'id="hearAboutUs"',
|
|
81
71
|
});
|
|
@@ -92,16 +82,12 @@ fixtures_1.test.skip("should dismiss two-step overlay for click", async ({ page,
|
|
|
92
82
|
});
|
|
93
83
|
(0, fixtures_1.test)("should choose and dismiss the correct overlay", async ({ page, server, }) => {
|
|
94
84
|
await page.goto(`${server.baseURL}/choose-an-overlay.html`);
|
|
95
|
-
// Assert that button and overlays load
|
|
96
85
|
await (0, fixtures_1.expect)(page.getByRole("button", { name: "Target" })).toBeVisible();
|
|
97
86
|
await (0, fixtures_1.expect)(page.getByText("This is a toast message")).toBeVisible();
|
|
98
87
|
await (0, fixtures_1.expect)(page.getByRole("button", { name: "Close" })).toBeVisible();
|
|
99
|
-
// Do the click, which should pass
|
|
100
88
|
await page.getByRole("button", { name: "Target" }).click();
|
|
101
|
-
// Assert correct overlay was dismissed
|
|
102
89
|
await (0, fixtures_1.expect)(page.getByText("This is a toast message")).not.toBeVisible();
|
|
103
90
|
await (0, fixtures_1.expect)(page.getByRole("button", { name: "Close" })).toBeVisible();
|
|
104
|
-
// Assert cache is correct
|
|
105
91
|
const withDom = getCacheDataWithDom({
|
|
106
92
|
dom: '<div id="toast" class="z-[100]">',
|
|
107
93
|
});
|
|
@@ -115,19 +101,15 @@ fixtures_1.test.skip("should dismiss two-step overlay for click", async ({ page,
|
|
|
115
101
|
(0, fixtures_1.expect)(code).toBeDefined();
|
|
116
102
|
(0, fixtures_1.expect)(code).toContain("await page.getByRole('button', { name: '×' }).click()");
|
|
117
103
|
});
|
|
118
|
-
// Test is flaky - skipping it
|
|
119
104
|
fixtures_1.test.skip("should fallback when the overlay cannot be dismissed", async ({ page, server, }) => {
|
|
120
105
|
await page.goto(`${server.baseURL}/no-overlay.html`);
|
|
121
106
|
let error;
|
|
122
107
|
try {
|
|
123
|
-
// Click on element that is hidden below another button, which is not
|
|
124
|
-
// dismissible
|
|
125
108
|
await page.getByRole("button", { name: "Bottom Button" }).click();
|
|
126
109
|
}
|
|
127
110
|
catch (e) {
|
|
128
111
|
error = e;
|
|
129
112
|
}
|
|
130
|
-
// TODO: add better assertions with result from master agent
|
|
131
113
|
console.log(fixtures_1.test.info().annotations);
|
|
132
114
|
(0, fixtures_1.expect)(error).toBeDefined();
|
|
133
115
|
(0, fixtures_1.expect)(error.message).toContain('<button type="button" class="top-button">Top Button</button> intercepts pointer events');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-extensions.d.ts","sourceRoot":"","sources":["../src/playwright-extensions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"playwright-extensions.d.ts","sourceRoot":"","sources":["../src/playwright-extensions.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7C,CAAC;AAEF,OAAO,QAAQ,iBAAiB,CAAC;IAC/B,UAAU,OAAQ,SAAQ,eAAe;KAAG;CAC7C;AAED,OAAO,QAAQ,kBAAkB,CAAC;IAChC,UAAU,OAAQ,SAAQ,eAAe;KAAG;CAC7C"}
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Test-only reporter used by attachment-cleanup integration tests.
|
|
3
|
-
*
|
|
4
|
-
* Records a timestamped event log of register/markUploaded/eviction events
|
|
5
|
-
* so tests can make precise assertions about ordering and timing.
|
|
6
|
-
*/
|
|
7
1
|
import type { FullResult, Reporter, TestCase, TestResult } from "@playwright/test/reporter";
|
|
8
2
|
declare class AttachmentCleanupTestReporter implements Reporter {
|
|
9
3
|
private _timeline;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment-cleanup-test-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/attachment-cleanup-test-reporter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"attachment-cleanup-test-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/attachment-cleanup-test-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AA0CnC,cAAM,6BAA8B,YAAW,QAAQ;IACrD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,MAAM,CAAkC;IAEhD,cAQC;IAED,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,QAyB5C;IAEK,KAAK,CAAC,OAAO,EAAE,UAAU,iBAyB9B;CACF;eAEc,6BAA6B"}
|
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Test-only reporter used by attachment-cleanup integration tests.
|
|
4
|
-
*
|
|
5
|
-
* Records a timestamped event log of register/markUploaded/eviction events
|
|
6
|
-
* so tests can make precise assertions about ordering and timing.
|
|
7
|
-
*/
|
|
8
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
4
|
};
|
|
@@ -1,28 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tracks test attachments and evicts the oldest uploaded ones from disk
|
|
3
|
-
* when total size exceeds the configured limit.
|
|
4
|
-
*
|
|
5
|
-
* Two-phase tracking:
|
|
6
|
-
* 1. register() — called immediately when an attachment is created.
|
|
7
|
-
* Adds the file to the total size accounting so the LRU knows about
|
|
8
|
-
* all disk usage, even while uploads are still queued.
|
|
9
|
-
* 2. markUploaded() — called after the file is uploaded to R2.
|
|
10
|
-
* Marks the file as safe to evict (delete from disk).
|
|
11
|
-
*
|
|
12
|
-
* Eviction only deletes files that have been uploaded. This ensures we
|
|
13
|
-
* never delete a file before its upload completes.
|
|
14
|
-
*
|
|
15
|
-
* Why this exists:
|
|
16
|
-
* Test attachments (videos, traces, screenshots) accumulate on disk during a
|
|
17
|
-
* test run. On Fargate workers with 50 GB ephemeral storage, large test suites
|
|
18
|
-
* can exhaust disk space. Since attachments are already uploaded to R2, we can
|
|
19
|
-
* safely delete them from disk — but we can't delete them immediately because
|
|
20
|
-
* Playwright's blob reporter reads them during onEnd to embed as resources/.
|
|
21
|
-
*
|
|
22
|
-
* The blob reporter's statSync guard (Playwright 1.57+) gracefully skips
|
|
23
|
-
* missing files, and patchBlobZip replaces resources/ with
|
|
24
|
-
* _empirical_urls.json pointing to R2 URLs anyway.
|
|
25
|
-
*/
|
|
26
1
|
export declare class AttachmentCleanup {
|
|
27
2
|
private _pending;
|
|
28
3
|
private _uploaded;
|
|
@@ -36,17 +11,9 @@ export declare class AttachmentCleanup {
|
|
|
36
11
|
private _diskLogTick;
|
|
37
12
|
private _onEvict;
|
|
38
13
|
constructor(limitBytes?: number);
|
|
39
|
-
/**
|
|
40
|
-
* Phase 1: Register an attachment immediately when it's created.
|
|
41
|
-
* Adds to total size accounting and attempts eviction of already-uploaded files.
|
|
42
|
-
*/
|
|
43
14
|
register(attachment: {
|
|
44
15
|
path?: string | undefined;
|
|
45
16
|
}): void;
|
|
46
|
-
/**
|
|
47
|
-
* Phase 2: Mark an attachment as uploaded (safe to evict).
|
|
48
|
-
* Moves from pending to the uploaded queue and attempts eviction.
|
|
49
|
-
*/
|
|
50
17
|
markUploaded(attachmentPath: string): void;
|
|
51
18
|
private _evict;
|
|
52
19
|
private _logDiskStatus;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachment-cleanup.d.ts","sourceRoot":"","sources":["../../src/reporter/attachment-cleanup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"attachment-cleanup.d.ts","sourceRoot":"","sources":["../../src/reporter/attachment-cleanup.ts"],"names":[],"mappings":"AA6DA,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,SAAS,CAAwC;IACzD,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAA+C;IACpE,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAyC;IAEzD,YAAY,UAAU,GAAE,MAAmC,EAM1D;IAED,QAAQ,CAAC,UAAU,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,QAejD;IAED,YAAY,CAAC,cAAc,EAAE,MAAM,QASlC;IAED,OAAO,CAAC,MAAM;IA4Bd,OAAO,CAAC,cAAc;IAItB,UAAU,SAWT;IAED,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAE3C;IAED,IAAI,KAAK;QAEL,eAAe;QACf,aAAa;QACb,YAAY;QACZ,YAAY;QACZ,YAAY;QACZ,WAAW;QACX,SAAS;QACT,UAAU;MAEb;CACF"}
|
|
@@ -16,7 +16,7 @@ function getDefaultDiskLimitBytes() {
|
|
|
16
16
|
}
|
|
17
17
|
logger_1.logger.warn(`[AttachmentCleanup] Invalid ATTACHMENT_CLEANUP_LIMIT_BYTES=${raw}, using default`);
|
|
18
18
|
}
|
|
19
|
-
return 10 * 1024 * 1024 * 1024;
|
|
19
|
+
return 10 * 1024 * 1024 * 1024;
|
|
20
20
|
}
|
|
21
21
|
const DISK_LOG_INTERVAL_MS = 30_000;
|
|
22
22
|
function formatBytes(bytes) {
|
|
@@ -39,12 +39,9 @@ function getDiskUsage() {
|
|
|
39
39
|
return "unavailable";
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
// Where the test-run worker checks out the test repo.
|
|
43
42
|
const WORKER_REPO_DIR = "/app/source-repo";
|
|
44
43
|
function getDiskBreakdown() {
|
|
45
44
|
try {
|
|
46
|
-
// /tmp also holds browser profiles and download dirs that no attachment
|
|
47
|
-
// accounts for, so it is listed whole alongside its artifact dirs.
|
|
48
45
|
const output = (0, child_process_1.execSync)("du -sh test-results/.playwright-artifacts-* test-results/ blob-report/ blob-report-incremental/ playwright-report/ /tmp/playwright-artifacts-* /tmp 2>/dev/null | sort -rh", { encoding: "utf8", timeout: 8000, cwd: WORKER_REPO_DIR }).trim();
|
|
49
46
|
if (!output) {
|
|
50
47
|
return "none";
|
|
@@ -58,31 +55,6 @@ function getDiskBreakdown() {
|
|
|
58
55
|
return "none";
|
|
59
56
|
}
|
|
60
57
|
}
|
|
61
|
-
/**
|
|
62
|
-
* Tracks test attachments and evicts the oldest uploaded ones from disk
|
|
63
|
-
* when total size exceeds the configured limit.
|
|
64
|
-
*
|
|
65
|
-
* Two-phase tracking:
|
|
66
|
-
* 1. register() — called immediately when an attachment is created.
|
|
67
|
-
* Adds the file to the total size accounting so the LRU knows about
|
|
68
|
-
* all disk usage, even while uploads are still queued.
|
|
69
|
-
* 2. markUploaded() — called after the file is uploaded to R2.
|
|
70
|
-
* Marks the file as safe to evict (delete from disk).
|
|
71
|
-
*
|
|
72
|
-
* Eviction only deletes files that have been uploaded. This ensures we
|
|
73
|
-
* never delete a file before its upload completes.
|
|
74
|
-
*
|
|
75
|
-
* Why this exists:
|
|
76
|
-
* Test attachments (videos, traces, screenshots) accumulate on disk during a
|
|
77
|
-
* test run. On Fargate workers with 50 GB ephemeral storage, large test suites
|
|
78
|
-
* can exhaust disk space. Since attachments are already uploaded to R2, we can
|
|
79
|
-
* safely delete them from disk — but we can't delete them immediately because
|
|
80
|
-
* Playwright's blob reporter reads them during onEnd to embed as resources/.
|
|
81
|
-
*
|
|
82
|
-
* The blob reporter's statSync guard (Playwright 1.57+) gracefully skips
|
|
83
|
-
* missing files, and patchBlobZip replaces resources/ with
|
|
84
|
-
* _empirical_urls.json pointing to R2 URLs anyway.
|
|
85
|
-
*/
|
|
86
58
|
class AttachmentCleanup {
|
|
87
59
|
_pending = new Map();
|
|
88
60
|
_uploaded = [];
|
|
@@ -97,19 +69,11 @@ class AttachmentCleanup {
|
|
|
97
69
|
_onEvict = null;
|
|
98
70
|
constructor(limitBytes = getDefaultDiskLimitBytes()) {
|
|
99
71
|
this._limitBytes = limitBytes;
|
|
100
|
-
// logger.debug(
|
|
101
|
-
// `[AttachmentCleanup] Initialized with limit: ${formatBytes(limitBytes)}`,
|
|
102
|
-
// );
|
|
103
72
|
this._diskLogTimer = setInterval(() => {
|
|
104
73
|
this._logDiskStatus();
|
|
105
74
|
}, DISK_LOG_INTERVAL_MS);
|
|
106
|
-
// Don't keep the process alive just for this timer
|
|
107
75
|
this._diskLogTimer.unref();
|
|
108
76
|
}
|
|
109
|
-
/**
|
|
110
|
-
* Phase 1: Register an attachment immediately when it's created.
|
|
111
|
-
* Adds to total size accounting and attempts eviction of already-uploaded files.
|
|
112
|
-
*/
|
|
113
77
|
register(attachment) {
|
|
114
78
|
try {
|
|
115
79
|
if (!attachment.path) {
|
|
@@ -125,14 +89,8 @@ class AttachmentCleanup {
|
|
|
125
89
|
this._registeredCount++;
|
|
126
90
|
this._evict();
|
|
127
91
|
}
|
|
128
|
-
catch {
|
|
129
|
-
// Ignore errors (file may not exist)
|
|
130
|
-
}
|
|
92
|
+
catch { }
|
|
131
93
|
}
|
|
132
|
-
/**
|
|
133
|
-
* Phase 2: Mark an attachment as uploaded (safe to evict).
|
|
134
|
-
* Moves from pending to the uploaded queue and attempts eviction.
|
|
135
|
-
*/
|
|
136
94
|
markUploaded(attachmentPath) {
|
|
137
95
|
const size = this._pending.get(attachmentPath);
|
|
138
96
|
if (size === undefined) {
|
|
@@ -170,19 +128,12 @@ class AttachmentCleanup {
|
|
|
170
128
|
}
|
|
171
129
|
_logDiskStatus() {
|
|
172
130
|
this._diskLogTick++;
|
|
173
|
-
// const artifactsSuffix =
|
|
174
|
-
// this._diskLogTick % 15 === 0 ? `, breakdown=${getDiskBreakdown()}` : "";
|
|
175
|
-
// logger.info(
|
|
176
|
-
// `[AttachmentCleanup] Periodic: registered=${this._registeredCount}, pending=${this._pending.size}, uploaded=${this._uploadedCount}, evicted=${this._evictedCount} (${formatBytes(this._evictedBytes)}), totalSize=${formatBytes(this._totalSize)}, limit=${formatBytes(this._limitBytes)}, disk=${getDiskUsage()}${artifactsSuffix}`,
|
|
177
|
-
// );
|
|
178
131
|
}
|
|
179
132
|
logSummary() {
|
|
180
133
|
if (this._diskLogTimer) {
|
|
181
134
|
clearInterval(this._diskLogTimer);
|
|
182
135
|
this._diskLogTimer = null;
|
|
183
136
|
}
|
|
184
|
-
// One line per run, only on the worker: the only record of what filled the
|
|
185
|
-
// disk when a run hits ENOSPC. Local runs stay quiet.
|
|
186
137
|
if (!fs_1.default.existsSync(WORKER_REPO_DIR)) {
|
|
187
138
|
return;
|
|
188
139
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAuBnC,cAAM,iBAAkB,YAAW,QAAQ;IACzC,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,oBAAoB,CAG1B;IACF,OAAO,CAAC,sBAAsB,CAAuB;IACrD,OAAO,CAAC,YAAY,CAAgD;IACpE,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,kBAAkB,CAA2B;
|
|
1
|
+
{"version":3,"file":"empirical-reporter.d.ts","sourceRoot":"","sources":["../../src/reporter/empirical-reporter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAuBnC,cAAM,iBAAkB,YAAW,QAAQ;IACzC,OAAO,CAAC,kBAAkB,CAAyB;IACnD,OAAO,CAAC,oBAAoB,CAG1B;IACF,OAAO,CAAC,sBAAsB,CAAuB;IACrD,OAAO,CAAC,YAAY,CAAgD;IACpE,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,gBAAgB,CAAkC;IAC1D,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,kBAAkB,CAA2B;IACrD,OAAO,CAAC,YAAY,CAAqB;IAEzC,cAEC;IAED,OAAO,CAAC,MAAM,EAAE,UAAU,QAEzB;IAED,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,6BAA6B,CA2CnC;IAEF,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,QA+I3C;IAEK,KAAK,CAAC,MAAM,EAAE,UAAU,iBAkH7B;IAED,OAAO,CAAC,qBAAqB;YAkBf,gBAAgB;YAOhB,iBAAiB;CAqChC;eAEc,iBAAiB"}
|
|
@@ -23,8 +23,6 @@ class EmpiricalReporter {
|
|
|
23
23
|
_testRetryCounts = new Map();
|
|
24
24
|
_uploader = null;
|
|
25
25
|
_attachmentCleanup = new attachment_cleanup_1.AttachmentCleanup();
|
|
26
|
-
// Resolved Playwright maxFailures (0 = no cap), sent on every run-end event
|
|
27
|
-
// so the server-side early-end enforces the client's configured cap.
|
|
28
26
|
_maxFailures;
|
|
29
27
|
constructor() {
|
|
30
28
|
this._uploader = (0, uploader_1.getUploader)();
|
|
@@ -62,11 +60,6 @@ class EmpiricalReporter {
|
|
|
62
60
|
}
|
|
63
61
|
};
|
|
64
62
|
onTestEnd(test, result) {
|
|
65
|
-
// After the SIGINT salvage, an attempt that really finished (its body
|
|
66
|
-
// completed, only its teardown overlapped the signal) is still reported:
|
|
67
|
-
// the blob keeps it, and a ledger that misses it calls the test unfinished,
|
|
68
|
-
// so the resume shard re-runs it and the merged report holds it twice.
|
|
69
|
-
// Shutdown artifacts stay out, as they do in the blob.
|
|
70
63
|
if ((0, reporter_state_1.isFinalized)() && (0, reporter_state_1.isShutdownArtifact)(result.status, true)) {
|
|
71
64
|
return;
|
|
72
65
|
}
|
|
@@ -186,8 +179,6 @@ class EmpiricalReporter {
|
|
|
186
179
|
}
|
|
187
180
|
async onEnd(result) {
|
|
188
181
|
if ((0, reporter_state_1.isFinalized)()) {
|
|
189
|
-
// The salvage owns the report upload. Only flush the test-case events
|
|
190
|
-
// sent during shutdown, so they land before the process exits.
|
|
191
182
|
await Promise.allSettled(this._pendingTestCaseEvents);
|
|
192
183
|
return;
|
|
193
184
|
}
|
|
@@ -8,10 +8,6 @@ export interface TestCaseSyncTestContext {
|
|
|
8
8
|
outputPath: string;
|
|
9
9
|
}
|
|
10
10
|
export declare function sleep(ms: number): Promise<void>;
|
|
11
|
-
/**
|
|
12
|
-
* Sets up a temp directory with the IBR fixture files.
|
|
13
|
-
* Returns a context object and a cleanup function.
|
|
14
|
-
*/
|
|
15
11
|
export declare function setupFixture(suite: string): Promise<{
|
|
16
12
|
ctx: IBRTestContext;
|
|
17
13
|
cleanup: () => Promise<void>;
|
|
@@ -20,16 +16,12 @@ export declare function setupTestCaseSyncFixture(suite: string): Promise<{
|
|
|
20
16
|
ctx: TestCaseSyncTestContext;
|
|
21
17
|
cleanup: () => Promise<void>;
|
|
22
18
|
}>;
|
|
23
|
-
/**
|
|
24
|
-
* Cleans blob artifacts from a previous run inside the same temp dir.
|
|
25
|
-
*/
|
|
26
19
|
export declare function cleanBlobArtifacts(ctx: IBRTestContext): Promise<void>;
|
|
27
20
|
export interface RunResult {
|
|
28
21
|
stdout: string;
|
|
29
22
|
stderr: string;
|
|
30
23
|
}
|
|
31
24
|
export type WaitFor = "first-passed" | "first-failed";
|
|
32
|
-
/** A `@@ NAME @@` line printed by a fixture suite, with its offset from run start. */
|
|
33
25
|
export interface Marker {
|
|
34
26
|
name: string;
|
|
35
27
|
atMs: number;
|
|
@@ -38,79 +30,40 @@ export interface SignalRunResult extends RunResult {
|
|
|
38
30
|
exitCode: number | null;
|
|
39
31
|
exitSignal: NodeJS.Signals | null;
|
|
40
32
|
markers: Marker[];
|
|
41
|
-
/** Offset from run start at which the first signal was delivered. */
|
|
42
33
|
signalSentAtMs: number;
|
|
43
|
-
/** Wall clock at run start, so `atMs` offsets can be compared to file mtimes. */
|
|
44
34
|
startedAtEpochMs: number;
|
|
45
35
|
}
|
|
46
36
|
export interface SignalRunOptions {
|
|
47
37
|
workers: number;
|
|
48
|
-
/**
|
|
49
|
-
* Signal as soon as this `@@ MARKER @@` is printed. Preferred over `waitFor`:
|
|
50
|
-
* it pins the signal to a known point in the run instead of a sleep.
|
|
51
|
-
*/
|
|
52
38
|
waitForMarker?: string;
|
|
53
|
-
/** Legacy trigger: wait for the first pass/fail line in stdout. */
|
|
54
39
|
waitFor?: WaitFor;
|
|
55
|
-
/** Settling delay between the trigger and the signal. */
|
|
56
40
|
delayBeforeSignalMs?: number;
|
|
57
|
-
/** Defaults to SIGINT, which is what production sends. */
|
|
58
41
|
signal?: NodeJS.Signals;
|
|
59
|
-
/** Send the same signal again this long after the first one. */
|
|
60
42
|
secondSignalAfterMs?: number;
|
|
61
|
-
/** SIGKILL the group this long after the first signal, like the cmd.ts grace cap. */
|
|
62
43
|
killAfterMs?: number;
|
|
63
|
-
/** How long to wait for the trigger before giving up and signalling anyway. */
|
|
64
44
|
triggerTimeoutMs?: number;
|
|
65
|
-
/** Extra env for the runner, e.g. from `startDashboardStub`. */
|
|
66
45
|
env?: NodeJS.ProcessEnv;
|
|
67
46
|
}
|
|
68
|
-
/**
|
|
69
|
-
* Spawns `playwright test` with the IBR reporter in its own process group,
|
|
70
|
-
* signals the group mid-run, and waits for exit.
|
|
71
|
-
*
|
|
72
|
-
* The process group and the signal both match production: `cmd.ts` spawns the
|
|
73
|
-
* runner detached and does `process.kill(-pid, signal)`.
|
|
74
|
-
*/
|
|
75
47
|
export declare function runPlaywrightWithSignal(ctx: IBRTestContext, opts: SignalRunOptions): Promise<SignalRunResult>;
|
|
76
|
-
/**
|
|
77
|
-
* Spawns `playwright test` with the IBR reporter, waits for a test outcome,
|
|
78
|
-
* sends SIGINT, and waits for the process to exit.
|
|
79
|
-
*
|
|
80
|
-
* Returns captured stdout/stderr.
|
|
81
|
-
*/
|
|
82
48
|
export declare function runPlaywrightWithSigint(ctx: IBRTestContext, opts: {
|
|
83
49
|
workers: number;
|
|
84
50
|
waitFor: WaitFor;
|
|
85
51
|
}): Promise<RunResult>;
|
|
86
|
-
/** Marker names in the order they were printed. */
|
|
87
52
|
export declare function markerNames(result: SignalRunResult): string[];
|
|
88
53
|
export declare function runPlaywrightList(ctx: TestCaseSyncTestContext): Promise<RunResult>;
|
|
89
54
|
export interface BlobEvent {
|
|
90
55
|
method: string;
|
|
91
56
|
params?: Record<string, unknown>;
|
|
92
57
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Reads `report.jsonl` from the blob zip and returns parsed events.
|
|
95
|
-
*/
|
|
96
58
|
export declare function readBlobEvents(ctx: IBRTestContext): Promise<BlobEvent[]>;
|
|
97
|
-
/**
|
|
98
|
-
* Reads `report.jsonl` from any blob-format zip (standard or incremental).
|
|
99
|
-
*/
|
|
100
59
|
export declare function readEventsFromZip(zipPath: string): Promise<BlobEvent[]>;
|
|
101
60
|
export interface ParityTestContext {
|
|
102
61
|
tempDir: string;
|
|
103
|
-
/** Playwright's built-in blob reporter output: blob-report/report-1.zip */
|
|
104
62
|
standardZipPath: string;
|
|
105
63
|
standardBlobDir: string;
|
|
106
|
-
/** IBR output: blob-report-incremental/incremental-report-1.zip */
|
|
107
64
|
incrementalZipPath: string;
|
|
108
65
|
incrementalBlobDir: string;
|
|
109
66
|
}
|
|
110
|
-
/**
|
|
111
|
-
* Sets up a fixture whose config runs BOTH the built-in blob reporter and the
|
|
112
|
-
* IBR on the same invocation, so one event stream feeds both zips.
|
|
113
|
-
*/
|
|
114
67
|
export declare function setupParityFixture(suite: string): Promise<{
|
|
115
68
|
ctx: ParityTestContext;
|
|
116
69
|
cleanup: () => Promise<void>;
|
|
@@ -118,10 +71,6 @@ export declare function setupParityFixture(suite: string): Promise<{
|
|
|
118
71
|
export interface CleanRunResult extends RunResult {
|
|
119
72
|
exitCode: number | null;
|
|
120
73
|
}
|
|
121
|
-
/**
|
|
122
|
-
* Spawns `playwright test` and lets it run to completion — the clean-finish
|
|
123
|
-
* half of the matrix (the SIGINT helpers above cover interruption).
|
|
124
|
-
*/
|
|
125
74
|
export declare function runPlaywrightClean(ctx: ParityTestContext, opts: {
|
|
126
75
|
workers: number;
|
|
127
76
|
shard?: {
|
|
@@ -129,22 +78,11 @@ export declare function runPlaywrightClean(ctx: ParityTestContext, opts: {
|
|
|
129
78
|
total: number;
|
|
130
79
|
};
|
|
131
80
|
}): Promise<CleanRunResult>;
|
|
132
|
-
/**
|
|
133
|
-
* Runs `playwright merge-reports <blobDir> --reporter json` and returns the
|
|
134
|
-
* parsed summary. Distinct output names let one temp dir hold both merges.
|
|
135
|
-
*/
|
|
136
81
|
export declare function mergeBlobDirToSummary(cwd: string, blobDir: string, outputName: string): Record<string, unknown>;
|
|
137
82
|
export interface DashboardStub {
|
|
138
|
-
/** `test_case_run_end` payloads EmpiricalReporter sent, in arrival order. */
|
|
139
83
|
testCaseEvents: () => Array<Record<string, any>>;
|
|
140
|
-
/** Runner env that points EmpiricalReporter's API calls and uploads here. */
|
|
141
84
|
env: NodeJS.ProcessEnv;
|
|
142
85
|
close: () => Promise<void>;
|
|
143
86
|
}
|
|
144
|
-
/**
|
|
145
|
-
* A local stand-in for api-worker and the report bucket, so EmpiricalReporter
|
|
146
|
-
* runs unmodified inside a real Playwright run. Records test-case events and
|
|
147
|
-
* accepts every upload.
|
|
148
|
-
*/
|
|
149
87
|
export declare function startDashboardStub(): Promise<DashboardStub>;
|
|
150
88
|
//# sourceMappingURL=harness.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/reporter/harness.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;AAED
|
|
1
|
+
{"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/reporter/harness.ts"],"names":[],"mappings":"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"}
|