playwright-core 1.58.0-alpha-2025-12-09 → 1.58.0-alpha-2025-12-11
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/ThirdPartyNotices.txt +3 -3
- package/browsers.json +6 -6
- package/lib/client/browser.js +3 -5
- package/lib/client/browserType.js +2 -2
- package/lib/client/fetch.js +2 -4
- package/lib/mcpBundleImpl/index.js +29 -29
- package/lib/protocol/serializers.js +5 -0
- package/lib/server/agent/actionRunner.js +33 -2
- package/lib/server/agent/agent.js +18 -12
- package/lib/server/agent/context.js +26 -11
- package/lib/server/agent/tools.js +67 -5
- package/lib/server/artifact.js +1 -1
- package/lib/server/bidi/bidiBrowser.js +13 -4
- package/lib/server/bidi/bidiPage.js +9 -1
- package/lib/server/bidi/third_party/bidiProtocolCore.js +1 -0
- package/lib/server/chromium/crPage.js +5 -5
- package/lib/server/dispatchers/dispatcher.js +5 -8
- package/lib/server/firefox/ffBrowser.js +1 -1
- package/lib/server/firefox/ffPage.js +1 -1
- package/lib/server/instrumentation.js +3 -0
- package/lib/server/page.js +2 -2
- package/lib/server/progress.js +2 -0
- package/lib/server/screencast.js +24 -25
- package/lib/server/utils/network.js +18 -26
- package/lib/server/videoRecorder.js +20 -11
- package/lib/server/webkit/wkBrowser.js +1 -1
- package/lib/server/webkit/wkPage.js +7 -7
- package/lib/vite/traceViewer/index.BVu7tZDe.css +1 -0
- package/lib/vite/traceViewer/index.html +2 -2
- package/lib/vite/traceViewer/index.zFV_GQE-.js +2 -0
- package/lib/vite/traceViewer/sw.bundle.js +3 -1
- package/lib/vite/traceViewer/uiMode.DiEbKRwa.js +5 -0
- package/lib/vite/traceViewer/uiMode.html +1 -1
- package/package.json +1 -1
- package/lib/vite/traceViewer/index.C4Y3Aw8n.css +0 -1
- package/lib/vite/traceViewer/index.YskCIlQ-.js +0 -2
- package/lib/vite/traceViewer/uiMode.CmFFBCQb.js +0 -5
|
@@ -41,16 +41,13 @@ module.exports = __toCommonJS(network_exports);
|
|
|
41
41
|
var import_http = __toESM(require("http"));
|
|
42
42
|
var import_http2 = __toESM(require("http2"));
|
|
43
43
|
var import_https = __toESM(require("https"));
|
|
44
|
-
var import_url = __toESM(require("url"));
|
|
45
44
|
var import_utilsBundle = require("../../utilsBundle");
|
|
46
45
|
var import_happyEyeballs = require("./happyEyeballs");
|
|
47
46
|
var import_manualPromise = require("../../utils/isomorphic/manualPromise");
|
|
48
47
|
const NET_DEFAULT_TIMEOUT = 3e4;
|
|
49
48
|
function httpRequest(params, onResponse, onError) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
...parsedUrl,
|
|
53
|
-
agent: parsedUrl.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent,
|
|
49
|
+
let url = new URL(params.url);
|
|
50
|
+
const options = {
|
|
54
51
|
method: params.method || "GET",
|
|
55
52
|
headers: params.headers
|
|
56
53
|
};
|
|
@@ -58,21 +55,16 @@ function httpRequest(params, onResponse, onError) {
|
|
|
58
55
|
options.rejectUnauthorized = params.rejectUnauthorized;
|
|
59
56
|
const proxyURL = (0, import_utilsBundle.getProxyForUrl)(params.url);
|
|
60
57
|
if (proxyURL) {
|
|
58
|
+
const parsedProxyURL = normalizeProxyURL(proxyURL);
|
|
61
59
|
if (params.url.startsWith("http:")) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
path: parsedUrl.href,
|
|
65
|
-
host: parsedProxyURL.hostname,
|
|
66
|
-
port: parsedProxyURL.port,
|
|
67
|
-
protocol: parsedProxyURL.protocol || "http:",
|
|
68
|
-
headers: options.headers,
|
|
69
|
-
method: options.method
|
|
70
|
-
};
|
|
60
|
+
parsedProxyURL.pathname = url.toString();
|
|
61
|
+
url = parsedProxyURL;
|
|
71
62
|
} else {
|
|
72
|
-
options.agent = new import_utilsBundle.HttpsProxyAgent(
|
|
63
|
+
options.agent = new import_utilsBundle.HttpsProxyAgent(parsedProxyURL);
|
|
73
64
|
options.rejectUnauthorized = false;
|
|
74
65
|
}
|
|
75
66
|
}
|
|
67
|
+
options.agent ??= url.protocol === "https:" ? import_happyEyeballs.httpsHappyEyeballsAgent : import_happyEyeballs.httpHappyEyeballsAgent;
|
|
76
68
|
let cancelRequest;
|
|
77
69
|
const requestCallback = (res) => {
|
|
78
70
|
const statusCode = res.statusCode || 0;
|
|
@@ -83,7 +75,7 @@ function httpRequest(params, onResponse, onError) {
|
|
|
83
75
|
onResponse(res);
|
|
84
76
|
}
|
|
85
77
|
};
|
|
86
|
-
const request =
|
|
78
|
+
const request = url.protocol === "https:" ? import_https.default.request(url, options, requestCallback) : import_http.default.request(url, options, requestCallback);
|
|
87
79
|
request.on("error", onError);
|
|
88
80
|
if (params.socketTimeout !== void 0) {
|
|
89
81
|
request.setTimeout(params.socketTimeout, () => {
|
|
@@ -122,7 +114,7 @@ async function fetchData(progress, params, onError) {
|
|
|
122
114
|
throw error;
|
|
123
115
|
}
|
|
124
116
|
}
|
|
125
|
-
function shouldBypassProxy(
|
|
117
|
+
function shouldBypassProxy(url, bypass) {
|
|
126
118
|
if (!bypass)
|
|
127
119
|
return false;
|
|
128
120
|
const domains = bypass.split(",").map((s) => {
|
|
@@ -131,7 +123,7 @@ function shouldBypassProxy(url2, bypass) {
|
|
|
131
123
|
s = "." + s;
|
|
132
124
|
return s;
|
|
133
125
|
});
|
|
134
|
-
const domain = "." +
|
|
126
|
+
const domain = "." + url.hostname;
|
|
135
127
|
return domains.some((d) => domain.endsWith(d));
|
|
136
128
|
}
|
|
137
129
|
function normalizeProxyURL(proxy) {
|
|
@@ -177,20 +169,20 @@ function createHttp2Server(...args) {
|
|
|
177
169
|
decorateServer(server);
|
|
178
170
|
return server;
|
|
179
171
|
}
|
|
180
|
-
async function isURLAvailable(
|
|
181
|
-
let statusCode = await httpStatusCode(
|
|
182
|
-
if (statusCode === 404 &&
|
|
183
|
-
const indexUrl = new URL(
|
|
172
|
+
async function isURLAvailable(url, ignoreHTTPSErrors, onLog, onStdErr) {
|
|
173
|
+
let statusCode = await httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr);
|
|
174
|
+
if (statusCode === 404 && url.pathname === "/") {
|
|
175
|
+
const indexUrl = new URL(url);
|
|
184
176
|
indexUrl.pathname = "/index.html";
|
|
185
177
|
statusCode = await httpStatusCode(indexUrl, ignoreHTTPSErrors, onLog, onStdErr);
|
|
186
178
|
}
|
|
187
179
|
return statusCode >= 200 && statusCode < 404;
|
|
188
180
|
}
|
|
189
|
-
async function httpStatusCode(
|
|
181
|
+
async function httpStatusCode(url, ignoreHTTPSErrors, onLog, onStdErr) {
|
|
190
182
|
return new Promise((resolve) => {
|
|
191
|
-
onLog?.(`HTTP GET: ${
|
|
183
|
+
onLog?.(`HTTP GET: ${url}`);
|
|
192
184
|
httpRequest({
|
|
193
|
-
url:
|
|
185
|
+
url: url.toString(),
|
|
194
186
|
headers: { Accept: "*/*" },
|
|
195
187
|
rejectUnauthorized: !ignoreHTTPSErrors
|
|
196
188
|
}, (res) => {
|
|
@@ -201,7 +193,7 @@ async function httpStatusCode(url2, ignoreHTTPSErrors, onLog, onStdErr) {
|
|
|
201
193
|
}, (error) => {
|
|
202
194
|
if (error.code === "DEPTH_ZERO_SELF_SIGNED_CERT")
|
|
203
195
|
onStdErr?.(`[WebServer] Self-signed certificate detected. Try adding ignoreHTTPSErrors: true to config.webServer.`);
|
|
204
|
-
onLog?.(`Error while checking if ${
|
|
196
|
+
onLog?.(`Error while checking if ${url} is available: ${error.message}`);
|
|
205
197
|
resolve(0);
|
|
206
198
|
});
|
|
207
199
|
});
|
|
@@ -22,11 +22,10 @@ __export(videoRecorder_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(videoRecorder_exports);
|
|
24
24
|
var import_utils = require("../utils");
|
|
25
|
-
var import_page = require("./page");
|
|
26
25
|
var import_processLauncher = require("./utils/processLauncher");
|
|
27
26
|
const fps = 25;
|
|
28
27
|
class VideoRecorder {
|
|
29
|
-
constructor(
|
|
28
|
+
constructor(ffmpegPath, options) {
|
|
30
29
|
this._process = null;
|
|
31
30
|
this._gracefullyClose = null;
|
|
32
31
|
this._lastWritePromise = Promise.resolve();
|
|
@@ -36,16 +35,12 @@ class VideoRecorder {
|
|
|
36
35
|
this._frameQueue = [];
|
|
37
36
|
this._isStopped = false;
|
|
38
37
|
this._ffmpegPath = ffmpegPath;
|
|
39
|
-
page.on(import_page.Page.Events.ScreencastFrame, (frame) => this.writeFrame(frame.buffer, frame.frameSwapWallTime / 1e3));
|
|
40
|
-
}
|
|
41
|
-
static async launch(page, ffmpegPath, options) {
|
|
42
38
|
if (!options.outputFile.endsWith(".webm"))
|
|
43
39
|
throw new Error("File must have .webm extension");
|
|
44
|
-
|
|
45
|
-
await recorder._launch(options);
|
|
46
|
-
return recorder;
|
|
40
|
+
this._launchPromise = this._launch(options).catch((e) => e);
|
|
47
41
|
}
|
|
48
42
|
async _launch(options) {
|
|
43
|
+
await (0, import_utils.mkdirIfNeeded)(options.outputFile);
|
|
49
44
|
const w = options.width;
|
|
50
45
|
const h = options.height;
|
|
51
46
|
const args = `-loglevel error -f image2pipe -avioflags direct -fpsprobesize 0 -probesize 32 -analyzeduration 0 -c:v mjpeg -i pipe:0 -y -an -r ${fps} -c:v vp8 -qmin 0 -qmax 50 -crf 8 -deadline realtime -speed 8 -b:v 1M -threads 1 -vf pad=${w}:${h}:0:0:gray,crop=${w}:${h}:0:0`.split(" ");
|
|
@@ -74,6 +69,13 @@ class VideoRecorder {
|
|
|
74
69
|
this._gracefullyClose = gracefullyClose;
|
|
75
70
|
}
|
|
76
71
|
writeFrame(frame, timestamp) {
|
|
72
|
+
this._launchPromise.then((error) => {
|
|
73
|
+
if (error)
|
|
74
|
+
return;
|
|
75
|
+
this._writeFrame(frame, timestamp);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
_writeFrame(frame, timestamp) {
|
|
77
79
|
(0, import_utils.assert)(this._process);
|
|
78
80
|
if (this._isStopped)
|
|
79
81
|
return;
|
|
@@ -100,13 +102,20 @@ class VideoRecorder {
|
|
|
100
102
|
});
|
|
101
103
|
}
|
|
102
104
|
async stop() {
|
|
105
|
+
const error = await this._launchPromise;
|
|
106
|
+
if (error)
|
|
107
|
+
throw error;
|
|
103
108
|
if (this._isStopped || !this._lastFrame)
|
|
104
109
|
return;
|
|
105
110
|
const addTime = Math.max(((0, import_utils.monotonicTime)() - this._lastWriteNodeTime) / 1e3, 1);
|
|
106
|
-
this.
|
|
111
|
+
this._writeFrame(Buffer.from([]), this._lastFrame.timestamp + addTime);
|
|
107
112
|
this._isStopped = true;
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
try {
|
|
114
|
+
await this._lastWritePromise;
|
|
115
|
+
await this._gracefullyClose();
|
|
116
|
+
} catch (e) {
|
|
117
|
+
import_utils.debugLogger.log("error", `ffmpeg failed to stop: ${String(e)}`);
|
|
118
|
+
}
|
|
110
119
|
}
|
|
111
120
|
}
|
|
112
121
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -79,7 +79,7 @@ class WKBrowser extends import_browser.Browser {
|
|
|
79
79
|
wkPage.didClose();
|
|
80
80
|
this._wkPages.clear();
|
|
81
81
|
for (const video of this._idToVideo.values())
|
|
82
|
-
video.artifact.reportFinished(new import_errors.TargetClosedError());
|
|
82
|
+
video.artifact.reportFinished(new import_errors.TargetClosedError(this.closeReason()));
|
|
83
83
|
this._idToVideo.clear();
|
|
84
84
|
this._didClose();
|
|
85
85
|
}
|
|
@@ -245,7 +245,7 @@ class WKPage {
|
|
|
245
245
|
this._provisionalPage.dispose();
|
|
246
246
|
this._provisionalPage = null;
|
|
247
247
|
}
|
|
248
|
-
this._firstNonInitialNavigationCommittedReject(new import_errors.TargetClosedError());
|
|
248
|
+
this._firstNonInitialNavigationCommittedReject(new import_errors.TargetClosedError(this._page.closeReason()));
|
|
249
249
|
this._page._didClose();
|
|
250
250
|
}
|
|
251
251
|
dispatchMessageToSession(message) {
|
|
@@ -441,7 +441,7 @@ class WKPage {
|
|
|
441
441
|
}
|
|
442
442
|
async navigateFrame(frame, url, referrer) {
|
|
443
443
|
if (this._pageProxySession.isDisposed())
|
|
444
|
-
throw new import_errors.TargetClosedError();
|
|
444
|
+
throw new import_errors.TargetClosedError(this._page.closeReason());
|
|
445
445
|
const pageProxyId = this._pageProxySession.sessionId;
|
|
446
446
|
const result = await this._pageProxySession.connection.browserSession.send("Playwright.navigate", { url, pageProxyId, frameId: frame._id, referrer });
|
|
447
447
|
return { newDocumentId: result.loaderId };
|
|
@@ -750,9 +750,10 @@ class WKPage {
|
|
|
750
750
|
return 0;
|
|
751
751
|
}
|
|
752
752
|
async _initializeVideoRecording() {
|
|
753
|
-
const
|
|
754
|
-
|
|
755
|
-
|
|
753
|
+
const screencast = this._page.screencast;
|
|
754
|
+
const videoOptions = screencast.launchVideoRecorder();
|
|
755
|
+
if (videoOptions)
|
|
756
|
+
await screencast.startVideoRecording(videoOptions);
|
|
756
757
|
}
|
|
757
758
|
validateScreenshotDimension(side, omitDeviceScaleFactor) {
|
|
758
759
|
if (process.platform === "darwin")
|
|
@@ -840,8 +841,7 @@ class WKPage {
|
|
|
840
841
|
const buffer = Buffer.from(event.data, "base64");
|
|
841
842
|
this._page.emit(import_page.Page.Events.ScreencastFrame, {
|
|
842
843
|
buffer,
|
|
843
|
-
frameSwapWallTime: event.timestamp * 1e3,
|
|
844
|
-
// timestamp is in seconds, we need to convert to milliseconds.
|
|
844
|
+
frameSwapWallTime: event.timestamp ? event.timestamp * 1e3 : Date.now(),
|
|
845
845
|
width: event.deviceWidth,
|
|
846
846
|
height: event.deviceHeight
|
|
847
847
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.drop-target{display:flex;align-items:center;justify-content:center;flex:auto;flex-direction:column;background-color:var(--vscode-editor-background);position:absolute;top:0;right:0;bottom:0;left:0;z-index:100;line-height:24px}body .drop-target{background:#fffc}:root.dark-mode .drop-target{background:#000c}.drop-target .title{font-size:24px;font-weight:700;margin-bottom:30px}.drop-target .info{max-width:400px;text-align:center}.drop-target .processing-error{font-size:24px;color:#e74c3c;font-weight:700;text-align:center;margin:30px;white-space:pre-line}.drop-target input{margin-top:50px}.drop-target button{color:#fff;background-color:#007acc;padding:8px 12px;border:none;margin:30px 0;cursor:pointer}.drop-target .version{color:var(--vscode-disabledForeground);margin-top:8px}.progress-dialog{width:400px;top:0;right:0;bottom:0;left:0;border:none;outline:none;background-color:var(--vscode-sideBar-background)}.progress-dialog::backdrop{background-color:#0006}.progress-content{padding:16px}.progress-content .title{background-color:unset;font-size:18px;font-weight:700;padding:0}.progress-wrapper{background-color:var(--vscode-commandCenter-activeBackground);width:100%;margin-top:16px;margin-bottom:8px}.inner-progress{background-color:var(--vscode-progressBar-background);height:4px}.header{display:flex;background-color:#000;flex:none;flex-basis:48px;line-height:48px;font-size:16px;color:#ccc}.workbench-loader{contain:size}.workbench-loader .header .toolbar-button{margin:12px;padding:8px 4px}.workbench-loader .logo{margin-left:16px;display:flex;align-items:center}.workbench-loader .logo img{height:32px;width:32px;pointer-events:none;flex:none}.workbench-loader .product{font-weight:600;margin-left:16px;flex:none}.workbench-loader .header .title{margin-left:16px;overflow:hidden;text-overflow:ellipsis;text-wrap:nowrap}html,body{min-width:550px;min-height:450px;overflow:auto}
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
<link rel="icon" href="./playwright-logo.svg" type="image/svg+xml">
|
|
8
8
|
<link rel="manifest" href="./manifest.webmanifest">
|
|
9
9
|
<title>Playwright Trace Viewer</title>
|
|
10
|
-
<script type="module" crossorigin src="./index.
|
|
10
|
+
<script type="module" crossorigin src="./index.zFV_GQE-.js"></script>
|
|
11
11
|
<link rel="modulepreload" crossorigin href="./assets/defaultSettingsView-V7hnXDpz.js">
|
|
12
12
|
<link rel="stylesheet" crossorigin href="./defaultSettingsView.CpI7RarT.css">
|
|
13
|
-
<link rel="stylesheet" crossorigin href="./index.
|
|
13
|
+
<link rel="stylesheet" crossorigin href="./index.BVu7tZDe.css">
|
|
14
14
|
</head>
|
|
15
15
|
<body>
|
|
16
16
|
<div id="root"></div>
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{T as x,r,a as F,W as M,j as e,D as I,b as z,c as P,d as A,e as O,f as V}from"./assets/defaultSettingsView-V7hnXDpz.js";const $=()=>{const[o,c]=r.useState(!1),[i,l]=r.useState(),[h,u]=r.useState(),[p,b]=r.useState(N),[f,j]=r.useState({done:0,total:0}),[k,v]=r.useState(!1),[y,m]=r.useState(null),[S,R]=r.useState(null),[U,E]=r.useState(!1),g=r.useCallback(t=>{const s=new URL(window.location.href);if(!t.length)return;const n=t.item(0),a=URL.createObjectURL(n);s.searchParams.append("trace",a);const d=s.toString();window.history.pushState({},"",d),l(a),u(n.name),v(!1),m(null)},[]);r.useEffect(()=>{const t=async s=>{var n;if((n=s.clipboardData)!=null&&n.files.length){for(const a of s.clipboardData.files)if(a.type!=="application/zip")return;s.preventDefault(),g(s.clipboardData.files)}};return document.addEventListener("paste",t),()=>document.removeEventListener("paste",t)}),r.useEffect(()=>{const t=s=>{const{method:n,params:a}=s.data;if(n!=="load"||!((a==null?void 0:a.trace)instanceof Blob))return;const d=new File([a.trace],"trace.zip",{type:"application/zip"}),w=new DataTransfer;w.items.add(d),g(w.files)};return window.addEventListener("message",t),()=>window.removeEventListener("message",t)});const W=r.useCallback(t=>{t.preventDefault(),g(t.dataTransfer.files)},[g]),C=r.useCallback(t=>{t.preventDefault(),t.target.files&&g(t.target.files)},[g]);r.useEffect(()=>{const t=new URL(window.location.href).searchParams,s=t.get("trace");if(c(t.has("isServer")),s!=null&&s.startsWith("file:")){R(s||null);return}if(t.has("isServer")){const n=new URLSearchParams(window.location.search).get("ws"),a=new URL(`../${n}`,window.location.toString());a.protocol=window.location.protocol==="https:"?"wss:":"ws:";const d=new F(new M(a));d.onLoadTraceRequested(async w=>{l(w.traceUrl),v(!1),m(null)}),d.initialize({}).catch(()=>{})}else s&&!s.startsWith("blob:")&&l(s)},[]);const T=r.useCallback(async t=>{const s=new URLSearchParams;s.set("trace",t);const n=await fetch(`contexts?${s.toString()}`);if(!n.ok){const{error:w}=await n.json();return m(w),w}const a=await n.json(),d=new x(t,a);j({done:0,total:0}),m(null),b(d)},[]);r.useEffect(()=>{(async()=>{if(!i){b(N);return}const t=s=>{s.data.method==="progress"&&j(s.data.params)};try{navigator.serviceWorker.addEventListener("message",t),j({done:0,total:1});let s=await T(i);s!=null&&s.includes("please grant permission for Local Network Access")&&(await fetch(i,{method:"HEAD",headers:{"x-pw-serviceworker":"skip"}}),s=await T(i)),s&&(o||l(void 0))}finally{navigator.serviceWorker.removeEventListener("message",t)}})()},[o,i,h,T]);const D=f.done!==f.total&&f.total!==0&&!y;r.useEffect(()=>{if(D){const t=setTimeout(()=>{E(!0)},200);return()=>clearTimeout(t)}else E(!1)},[D]);const L=!!(!o&&!k&&!S&&(!i||y));return e.jsxs("div",{className:"vbox workbench-loader",onDragOver:t=>{t.preventDefault(),t.dataTransfer.types.includes("Files")&&v(!0)},children:[e.jsxs("div",{className:"hbox header",...L?{inert:!0}:{},children:[e.jsx("div",{className:"logo",children:e.jsx("img",{src:"playwright-logo.svg",alt:"Playwright logo"})}),e.jsx("div",{className:"product",children:"Playwright"}),p.title&&e.jsx("div",{className:"title",children:p.title}),e.jsx("div",{className:"spacer"}),e.jsx(I,{icon:"settings-gear",title:"Settings",dialogDataTestId:"settings-toolbar-dialog",children:e.jsx(z,{location:"trace-viewer"})})]}),e.jsx(P,{model:p,inert:L}),S&&e.jsxs("div",{className:"drop-target",children:[e.jsx("div",{children:"Trace Viewer uses Service Workers to show traces. To view trace:"}),e.jsxs("div",{style:{paddingTop:20},children:[e.jsxs("div",{children:["1. Click ",e.jsx("a",{href:S,children:"here"})," to put your trace into the download shelf"]}),e.jsxs("div",{children:["2. Go to ",e.jsx("a",{href:"https://trace.playwright.dev",children:"trace.playwright.dev"})]}),e.jsx("div",{children:"3. Drop the trace from the download shelf into the page"})]})]}),e.jsx(A,{open:U,isModal:!0,className:"progress-dialog",children:e.jsxs("div",{className:"progress-content",children:[e.jsx("div",{className:"title",role:"heading","aria-level":1,children:"Loading Playwright Trace..."}),e.jsx("div",{className:"progress-wrapper",children:e.jsx("div",{className:"inner-progress",style:{width:f.total?100*f.done/f.total+"%":0}})})]})}),L&&e.jsxs("div",{className:"drop-target",children:[e.jsx("div",{className:"processing-error",role:"alert",children:y}),e.jsx("div",{className:"title",role:"heading","aria-level":1,children:"Drop Playwright Trace to load"}),e.jsx("div",{children:"or"}),e.jsx("button",{onClick:()=>{const t=document.createElement("input");t.type="file",t.click(),t.addEventListener("change",s=>C(s))},type:"button",children:"Select file"}),e.jsx("div",{className:"info",children:"Playwright Trace Viewer is a Progressive Web App, it does not send your trace anywhere, it opens it locally."}),e.jsxs("div",{className:"version",children:["Playwright v","1.58.0-next"]})]}),o&&!i&&e.jsx("div",{className:"drop-target",children:e.jsx("div",{className:"title",children:"Select test to see the trace"})}),k&&e.jsx("div",{className:"drop-target",onDragLeave:()=>{v(!1)},onDrop:t=>W(t),children:e.jsx("div",{className:"title",children:"Release to analyse the Playwright Trace"})})]})},N=new x("",[]),q=({traceJson:o})=>{const[c,i]=r.useState(void 0),[l,h]=r.useState(0),u=r.useRef(null);return r.useEffect(()=>(u.current&&clearTimeout(u.current),u.current=setTimeout(async()=>{try{const p=await B(o);i(p)}catch{const p=new x("",[]);i(p)}finally{h(l+1)}},500),()=>{u.current&&clearTimeout(u.current)}),[o,l]),e.jsx(P,{isLive:!0,model:c})};async function B(o){const c=new URLSearchParams;c.set("trace",o);const l=await(await fetch(`contexts?${c.toString()}`)).json();return new x(o,l)}(async()=>{const o=new URLSearchParams(window.location.search);if(O(),window.location.protocol!=="file:"){if(o.get("isUnderTest")==="true"&&await new Promise(h=>setTimeout(h,1e3)),!navigator.serviceWorker)throw new Error(`Service workers are not supported.
|
|
2
|
+
Make sure to serve the Trace Viewer (${window.location}) via HTTPS or localhost.`);navigator.serviceWorker.register("sw.bundle.js"),navigator.serviceWorker.controller||await new Promise(h=>{navigator.serviceWorker.oncontrollerchange=()=>h()}),setInterval(function(){fetch("ping")},1e4)}const c=o.get("trace"),l=(c==null?void 0:c.endsWith(".json"))?e.jsx(q,{traceJson:c}):e.jsx($,{});V.createRoot(document.querySelector("#root")).render(l)})();
|