UNPKG

rebrowser-playwright-core

Version:

A drop-in replacement for playwright-core patched with rebrowser-patches. It allows to pass modern automation detection tests.

214 lines (211 loc) 9.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.installRootRedirect = installRootRedirect; exports.openTraceInBrowser = openTraceInBrowser; exports.openTraceViewerApp = openTraceViewerApp; exports.runTraceInBrowser = runTraceInBrowser; exports.runTraceViewerApp = runTraceViewerApp; exports.startTraceViewerServer = startTraceViewerServer; var _path = _interopRequireDefault(require("path")); var _fs = _interopRequireDefault(require("fs")); var _httpServer = require("../../../utils/httpServer"); var _utils = require("../../../utils"); var _launchApp = require("../../launchApp"); var _instrumentation = require("../../instrumentation"); var _playwright = require("../../playwright"); var _progress = require("../../progress"); var _utilsBundle = require("../../../utilsBundle"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * 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. */ function validateTraceUrls(traceUrls) { for (const traceUrl of traceUrls) { let traceFile = traceUrl; // If .json is requested, we'll synthesize it. if (traceUrl.endsWith('.json')) traceFile = traceUrl.substring(0, traceUrl.length - '.json'.length); if (!traceUrl.startsWith('http://') && !traceUrl.startsWith('https://') && !_fs.default.existsSync(traceFile) && !_fs.default.existsSync(traceFile + '.trace')) throw new Error(`Trace file ${traceUrl} does not exist!`); } } async function startTraceViewerServer(options) { const server = new _httpServer.HttpServer(); server.routePrefix('/trace', (request, response) => { const url = new URL('http://localhost' + request.url); const relativePath = url.pathname.slice('/trace'.length); if (relativePath.endsWith('/stall.js')) return true; if (relativePath.startsWith('/file')) { try { const filePath = url.searchParams.get('path'); if (_fs.default.existsSync(filePath)) return server.serveFile(request, response, url.searchParams.get('path')); // If .json is requested, we'll synthesize it for zip-less operation. if (filePath.endsWith('.json')) { const traceName = filePath.substring(0, filePath.length - '.json'.length); response.statusCode = 200; response.setHeader('Content-Type', 'application/json'); response.end(JSON.stringify(traceDescriptor(traceName))); return true; } } catch (e) {} response.statusCode = 404; response.end(); return true; } const absolutePath = _path.default.join(__dirname, '..', '..', '..', 'vite', 'traceViewer', ...relativePath.split('/')); return server.serveFile(request, response, absolutePath); }); const transport = (options === null || options === void 0 ? void 0 : options.transport) || (options !== null && options !== void 0 && options.isServer ? new StdinServer() : undefined); if (transport) server.createWebSocket(transport); const { host, port } = options || {}; await server.start({ preferredPort: port, host }); return server; } async function installRootRedirect(server, traceUrls, options) { const params = new URLSearchParams(); if (_path.default.sep !== _path.default.posix.sep) params.set('pathSeparator', _path.default.sep); for (const traceUrl of traceUrls) params.append('trace', traceUrl); if (server.wsGuid()) params.append('ws', server.wsGuid()); if (options !== null && options !== void 0 && options.isServer) params.append('isServer', ''); if ((0, _utils.isUnderTest)()) params.append('isUnderTest', 'true'); for (const arg of options.args || []) params.append('arg', arg); if (options.grep) params.append('grep', options.grep); if (options.grepInvert) params.append('grepInvert', options.grepInvert); for (const project of options.project || []) params.append('project', project); for (const reporter of options.reporter || []) params.append('reporter', reporter); const urlPath = `./trace/${options.webApp || 'index.html'}?${params.toString()}`; server.routePath('/', (_, response) => { response.statusCode = 302; response.setHeader('Location', urlPath); response.end(); return true; }); } async function runTraceViewerApp(traceUrls, browserName, options, exitOnClose) { validateTraceUrls(traceUrls); const server = await startTraceViewerServer(options); await installRootRedirect(server, traceUrls, options); const page = await openTraceViewerApp(server.urlPrefix('precise'), browserName, options); if (exitOnClose) page.on('close', () => (0, _utils.gracefullyProcessExitDoNotHang)(0)); return page; } async function runTraceInBrowser(traceUrls, options) { validateTraceUrls(traceUrls); const server = await startTraceViewerServer(options); await installRootRedirect(server, traceUrls, options); await openTraceInBrowser(server.urlPrefix('human-readable')); } async function openTraceViewerApp(url, browserName, options) { const traceViewerPlaywright = (0, _playwright.createPlaywright)({ sdkLanguage: 'javascript', isInternalPlaywright: true }); const traceViewerBrowser = (0, _utils.isUnderTest)() ? 'chromium' : browserName; const { context, page } = await (0, _launchApp.launchApp)(traceViewerPlaywright[traceViewerBrowser], { // TODO: store language in the trace. sdkLanguage: traceViewerPlaywright.options.sdkLanguage, windowSize: { width: 1280, height: 800 }, persistentContextOptions: { ...(options === null || options === void 0 ? void 0 : options.persistentContextOptions), useWebSocket: (0, _utils.isUnderTest)(), headless: !!(options !== null && options !== void 0 && options.headless), colorScheme: (0, _utils.isUnderTest)() ? 'light' : undefined } }); const controller = new _progress.ProgressController((0, _instrumentation.serverSideCallMetadata)(), context._browser); await controller.run(async progress => { await context._browser._defaultContext._loadDefaultContextAsIs(progress); }); if (process.env.PWTEST_PRINT_WS_ENDPOINT) process.stderr.write('DevTools listening on: ' + context._browser.options.wsEndpoint + '\n'); if (!(0, _utils.isUnderTest)()) await (0, _launchApp.syncLocalStorageWithSettings)(page, 'traceviewer'); if ((0, _utils.isUnderTest)()) page.on('close', () => context.close({ reason: 'Trace viewer closed' }).catch(() => {})); await page.mainFrame().goto((0, _instrumentation.serverSideCallMetadata)(), url); return page; } async function openTraceInBrowser(url) { // eslint-disable-next-line no-console console.log('\nListening on ' + url); if (!(0, _utils.isUnderTest)()) await (0, _utilsBundle.open)(url.replace('0.0.0.0', 'localhost')).catch(() => {}); } class StdinServer { constructor() { this._pollTimer = void 0; this._traceUrl = void 0; this.sendEvent = void 0; this.close = void 0; process.stdin.on('data', data => { const url = data.toString().trim(); if (url === this._traceUrl) return; if (url.endsWith('.json')) this._pollLoadTrace(url);else this._loadTrace(url); }); process.stdin.on('close', () => (0, _utils.gracefullyProcessExitDoNotHang)(0)); } onconnect() {} async dispatch(method, params) { if (method === 'initialize') { if (this._traceUrl) this._loadTrace(this._traceUrl); } } onclose() {} _loadTrace(traceUrl) { var _this$sendEvent; this._traceUrl = traceUrl; clearTimeout(this._pollTimer); (_this$sendEvent = this.sendEvent) === null || _this$sendEvent === void 0 || _this$sendEvent.call(this, 'loadTraceRequested', { traceUrl }); } _pollLoadTrace(url) { this._loadTrace(url); this._pollTimer = setTimeout(() => { this._pollLoadTrace(url); }, 500); } } function traceDescriptor(traceName) { const result = { entries: [] }; const traceDir = _path.default.dirname(traceName); const traceFile = _path.default.basename(traceName); for (const name of _fs.default.readdirSync(traceDir)) { if (name.startsWith(traceFile)) result.entries.push({ name, path: _path.default.join(traceDir, name) }); } const resourcesDir = _path.default.join(traceDir, 'resources'); if (_fs.default.existsSync(resourcesDir)) { for (const name of _fs.default.readdirSync(resourcesDir)) result.entries.push({ name: 'resources/' + name, path: _path.default.join(resourcesDir, name) }); } return result; }