mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: make trace server work over http (#23561)
This commit is contained in:
parent
2e327c9d0c
commit
0b30f2017c
@ -94,9 +94,9 @@ async function startTraceViewerServer(traceUrls: string[], options?: Options): P
|
||||
});
|
||||
|
||||
const params = traceUrls.map(t => `trace=${t}`);
|
||||
const transport = options?.transport || (options?.isServer ? new StdinServer() : undefined);
|
||||
|
||||
if (options?.transport) {
|
||||
const transport = options?.transport;
|
||||
if (transport) {
|
||||
const guid = createGuid();
|
||||
params.push('ws=' + guid);
|
||||
const wss = new wsServer({ server: server.server(), path: '/' + guid });
|
||||
@ -135,7 +135,6 @@ async function startTraceViewerServer(traceUrls: string[], options?: Options): P
|
||||
}
|
||||
|
||||
export async function openTraceViewerApp(traceUrls: string[], browserName: string, options?: Options): Promise<Page> {
|
||||
const stdinServer = options?.isServer ? new StdinServer() : undefined;
|
||||
const { url } = await startTraceViewerServer(traceUrls, options);
|
||||
const traceViewerPlaywright = createPlaywright({ sdkLanguage: 'javascript', isInternalPlaywright: true });
|
||||
const traceViewerBrowser = isUnderTest() ? 'chromium' : browserName;
|
||||
@ -176,7 +175,6 @@ export async function openTraceViewerApp(traceUrls: string[], browserName: strin
|
||||
page.on('close', () => process.exit());
|
||||
|
||||
await page.mainFrame().goto(serverSideCallMetadata(), url);
|
||||
stdinServer?.setPage(page);
|
||||
return page;
|
||||
}
|
||||
|
||||
@ -187,7 +185,7 @@ async function openTraceInBrowser(traceUrls: string[], options?: Options) {
|
||||
await open(url, { wait: true }).catch(() => {});
|
||||
}
|
||||
|
||||
class StdinServer {
|
||||
class StdinServer implements Transport {
|
||||
private _pollTimer: NodeJS.Timeout | undefined;
|
||||
private _traceUrl: string | undefined;
|
||||
private _page: Page | undefined;
|
||||
@ -197,7 +195,6 @@ class StdinServer {
|
||||
const url = data.toString().trim();
|
||||
if (url === this._traceUrl)
|
||||
return;
|
||||
this._traceUrl = url;
|
||||
if (url.endsWith('.json'))
|
||||
this._pollLoadTrace(url);
|
||||
else
|
||||
@ -206,15 +203,24 @@ class StdinServer {
|
||||
process.stdin.on('close', () => this._selfDestruct());
|
||||
}
|
||||
|
||||
setPage(page: Page) {
|
||||
this._page = page;
|
||||
if (this._traceUrl)
|
||||
this._loadTrace(this._traceUrl);
|
||||
async dispatch(method: string, params: any) {
|
||||
if (method === 'ready') {
|
||||
if (this._traceUrl)
|
||||
this._loadTrace(this._traceUrl);
|
||||
}
|
||||
}
|
||||
|
||||
onclose() {
|
||||
this._selfDestruct();
|
||||
}
|
||||
|
||||
sendEvent?: (method: string, params: any) => void;
|
||||
close?: () => void;
|
||||
|
||||
private _loadTrace(url: string) {
|
||||
this._traceUrl = url;
|
||||
clearTimeout(this._pollTimer);
|
||||
this._page?.mainFrame().evaluateExpression(`window.setTraceURL(${JSON.stringify(url)})`).catch(() => {});
|
||||
this.sendEvent?.('loadTrace', { url });
|
||||
}
|
||||
|
||||
private _pollLoadTrace(url: string) {
|
||||
|
@ -87,6 +87,9 @@ class UIMode {
|
||||
|
||||
this._transport = {
|
||||
dispatch: async (method, params) => {
|
||||
if (method === 'ping')
|
||||
return;
|
||||
|
||||
if (method === 'exit') {
|
||||
exitPromise.resolve();
|
||||
return;
|
||||
|
@ -37,11 +37,14 @@ import { toggleTheme } from '@web/theme';
|
||||
import { artifactsFolderName } from '@testIsomorphic/folders';
|
||||
import { msToString, settings, useSetting } from '@web/uiUtils';
|
||||
import type { ActionTraceEvent } from '@trace/trace';
|
||||
import { connect } from './wsPort';
|
||||
|
||||
let updateRootSuite: (config: FullConfig, rootSuite: Suite, loadErrors: TestError[], progress: Progress | undefined) => void = () => {};
|
||||
let runWatchedTests = (fileNames: string[]) => {};
|
||||
let xtermSize = { cols: 80, rows: 24 };
|
||||
|
||||
let sendMessage: (method: string, params?: any) => Promise<any> = async () => {};
|
||||
|
||||
const xtermDataSource: XtermDataSource = {
|
||||
pending: [],
|
||||
clear: () => {},
|
||||
@ -96,7 +99,10 @@ export const UIModeView: React.FC<{}> = ({
|
||||
React.useEffect(() => {
|
||||
inputRef.current?.focus();
|
||||
setIsLoading(true);
|
||||
initWebSocket(() => setIsDisconnected(true)).then(() => reloadTests());
|
||||
connect({ onEvent: dispatchEvent, onClose: () => setIsDisconnected(true) }).then(send => {
|
||||
sendMessage = send;
|
||||
reloadTests();
|
||||
});
|
||||
}, [reloadTests]);
|
||||
|
||||
updateRootSuite = React.useCallback((config: FullConfig, rootSuite: Suite, loadErrors: TestError[], newProgress: Progress | undefined) => {
|
||||
@ -639,43 +645,6 @@ const refreshRootSuite = (eraseResults: boolean): Promise<void> => {
|
||||
return sendMessage('list', {});
|
||||
};
|
||||
|
||||
let lastId = 0;
|
||||
let _ws: WebSocket;
|
||||
const callbacks = new Map<number, { resolve: (arg: any) => void, reject: (arg: Error) => void }>();
|
||||
|
||||
const initWebSocket = async (onClose: () => void) => {
|
||||
const guid = new URLSearchParams(window.location.search).get('ws');
|
||||
const ws = new WebSocket(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.hostname}:${window.location.port}/${guid}`);
|
||||
await new Promise(f => ws.addEventListener('open', f));
|
||||
ws.addEventListener('close', onClose);
|
||||
ws.addEventListener('message', event => {
|
||||
const message = JSON.parse(event.data);
|
||||
const { id, result, error, method, params } = message;
|
||||
if (id) {
|
||||
const callback = callbacks.get(id);
|
||||
if (!callback)
|
||||
return;
|
||||
callbacks.delete(id);
|
||||
if (error)
|
||||
callback.reject(new Error(error));
|
||||
else
|
||||
callback.resolve(result);
|
||||
} else {
|
||||
dispatchMessage(method, params);
|
||||
}
|
||||
});
|
||||
_ws = ws;
|
||||
};
|
||||
|
||||
const sendMessage = async (method: string, params: any): Promise<any> => {
|
||||
const id = ++lastId;
|
||||
const message = { id, method, params };
|
||||
_ws.send(JSON.stringify(message));
|
||||
return new Promise((resolve, reject) => {
|
||||
callbacks.set(id, { resolve, reject });
|
||||
});
|
||||
};
|
||||
|
||||
const sendMessageNoReply = (method: string, params?: any) => {
|
||||
if ((window as any)._overrideProtocolForTest) {
|
||||
(window as any)._overrideProtocolForTest({ method, params }).catch(() => {});
|
||||
@ -687,7 +656,7 @@ const sendMessageNoReply = (method: string, params?: any) => {
|
||||
});
|
||||
};
|
||||
|
||||
const dispatchMessage = (method: string, params?: any) => {
|
||||
const dispatchEvent = (method: string, params?: any) => {
|
||||
if (method === 'listChanged') {
|
||||
refreshRootSuite(false).catch(() => {});
|
||||
return;
|
||||
|
@ -21,6 +21,7 @@ import { MultiTraceModel } from './modelUtil';
|
||||
import './workbench.css';
|
||||
import { toggleTheme } from '@web/theme';
|
||||
import { Workbench } from './workbench';
|
||||
import { connect } from './wsPort';
|
||||
|
||||
export const WorkbenchLoader: React.FunctionComponent<{
|
||||
}> = () => {
|
||||
@ -82,13 +83,19 @@ export const WorkbenchLoader: React.FunctionComponent<{
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).setTraceURL = (url: string) => {
|
||||
setTraceURLs([url]);
|
||||
setDragOver(false);
|
||||
setProcessingErrorMessage(null);
|
||||
};
|
||||
if (earlyTraceURL) {
|
||||
(window as any).setTraceURL(earlyTraceURL);
|
||||
if (params.has('isServer')) {
|
||||
connect({
|
||||
onEvent(method: string, params?: any) {
|
||||
if (method === 'loadTrace') {
|
||||
setTraceURLs([params!.url]);
|
||||
setDragOver(false);
|
||||
setProcessingErrorMessage(null);
|
||||
}
|
||||
},
|
||||
onClose() {}
|
||||
}).then(sendMessage => {
|
||||
sendMessage('ready');
|
||||
});
|
||||
} else if (!newTraceURLs.some(url => url.startsWith('blob:'))) {
|
||||
// Don't re-use blob file URLs on page load (results in Fetch error)
|
||||
setTraceURLs(newTraceURLs);
|
||||
@ -176,9 +183,3 @@ export const WorkbenchLoader: React.FunctionComponent<{
|
||||
};
|
||||
|
||||
export const emptyModel = new MultiTraceModel([]);
|
||||
|
||||
let earlyTraceURL: string | undefined = undefined;
|
||||
|
||||
(window as any).setTraceURL = (url: string) => {
|
||||
earlyTraceURL = url;
|
||||
};
|
||||
|
54
packages/trace-viewer/src/ui/wsPort.ts
Normal file
54
packages/trace-viewer/src/ui/wsPort.ts
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
let lastId = 0;
|
||||
let _ws: WebSocket;
|
||||
const callbacks = new Map<number, { resolve: (arg: any) => void, reject: (arg: Error) => void }>();
|
||||
|
||||
export async function connect(options: { onEvent: (method: string, params?: any) => void, onClose: () => void }): Promise<(method: string, params?: any) => Promise<any>> {
|
||||
const guid = new URLSearchParams(window.location.search).get('ws');
|
||||
const ws = new WebSocket(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.hostname}:${window.location.port}/${guid}`);
|
||||
await new Promise(f => ws.addEventListener('open', f));
|
||||
ws.addEventListener('close', options.onClose);
|
||||
ws.addEventListener('message', event => {
|
||||
const message = JSON.parse(event.data);
|
||||
const { id, result, error, method, params } = message;
|
||||
if (id) {
|
||||
const callback = callbacks.get(id);
|
||||
if (!callback)
|
||||
return;
|
||||
callbacks.delete(id);
|
||||
if (error)
|
||||
callback.reject(new Error(error));
|
||||
else
|
||||
callback.resolve(result);
|
||||
} else {
|
||||
options.onEvent(method, params);
|
||||
}
|
||||
});
|
||||
_ws = ws;
|
||||
setInterval(() => sendMessage('ping').catch(() => {}), 30000);
|
||||
return sendMessage;
|
||||
}
|
||||
|
||||
const sendMessage = async (method: string, params?: any): Promise<any> => {
|
||||
const id = ++lastId;
|
||||
const message = { id, method, params };
|
||||
_ws.send(JSON.stringify(message));
|
||||
return new Promise((resolve, reject) => {
|
||||
callbacks.set(id, { resolve, reject });
|
||||
});
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user