From 11bb30eed4597917d4c4a6ec98bffbdd8b4ac4fd Mon Sep 17 00:00:00 2001 From: miherlosev Date: Wed, 20 Jul 2022 15:36:49 +0400 Subject: [PATCH 1/9] proxyless: inject resources using cdp --- package.json | 2 +- .../connection/browser-connection-tracker.ts | 19 +++++ src/browser/connection/gateway.ts | 7 +- src/browser/connection/index.ts | 73 +++++++++-------- .../dedicated/chrome/cdp-client/index.ts | 4 +- .../built-in/dedicated/chrome/index.js | 10 ++- .../dedicated/chrome/requests-interceptor.ts | 80 +++++++++++++++++++ src/client/test-run/index.js.mustache | 20 ++--- src/configuration/testcafe-configuration.ts | 2 + src/runner/browser-job.ts | 7 ++ src/test-run/index.ts | 37 ++++----- 11 files changed, 187 insertions(+), 74 deletions(-) create mode 100644 src/browser/connection/browser-connection-tracker.ts create mode 100644 src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts diff --git a/package.json b/package.json index 756e578fd28..c59af9408b5 100644 --- a/package.json +++ b/package.json @@ -137,7 +137,7 @@ "source-map-support": "^0.5.16", "strip-bom": "^2.0.0", "testcafe-browser-tools": "2.0.23", - "testcafe-hammerhead": "24.5.24", + "testcafe-hammerhead": "https://github.com/miherlosev/tmp/files/9149185/testcafe-hammerhead-24.6.0.zip", "testcafe-legacy-api": "5.1.4", "testcafe-reporter-dashboard": "1.0.0-rc.3", "testcafe-reporter-json": "^2.1.0", diff --git a/src/browser/connection/browser-connection-tracker.ts b/src/browser/connection/browser-connection-tracker.ts new file mode 100644 index 00000000000..3d649d4848c --- /dev/null +++ b/src/browser/connection/browser-connection-tracker.ts @@ -0,0 +1,19 @@ +import BrowserConnection from './index'; + +class BrowserConnectionTracker { + public activeBrowserConnections: { [id: string]: BrowserConnection }; + + public constructor () { + this.activeBrowserConnections = {}; + } + + public add (connection: BrowserConnection): void { + this.activeBrowserConnections[connection.id] = connection; + } + + public remove (connection: BrowserConnection): void { + delete this.activeBrowserConnections[connection.id]; + } +} + +export default new BrowserConnectionTracker(); diff --git a/src/browser/connection/gateway.ts b/src/browser/connection/gateway.ts index f83653032be..da87eda5c85 100644 --- a/src/browser/connection/gateway.ts +++ b/src/browser/connection/gateway.ts @@ -18,16 +18,15 @@ import SERVICE_ROUTES from './service-routes'; export default class BrowserConnectionGateway { private _connections: Dictionary = {}; private _remotesQueue: RemotesQueue; - public readonly domain: string; public readonly connectUrl: string; public retryTestPages: boolean; + public readonly proxy: Proxy; public constructor (proxy: Proxy, options: { retryTestPages: boolean }) { this._remotesQueue = new RemotesQueue(); - // @ts-ignore Need to improve typings of the 'testcafe-hammerhead' module - this.domain = (proxy as any).server1Info.domain; - this.connectUrl = `${this.domain}/browser/connect`; + this.connectUrl = proxy.resolveRelativeServiceUrl('/browser/connect'); this.retryTestPages = options.retryTestPages; + this.proxy = proxy; this._registerRoutes(proxy); } diff --git a/src/browser/connection/index.ts b/src/browser/connection/index.ts index ded7588558e..832b99a4983 100644 --- a/src/browser/connection/index.ts +++ b/src/browser/connection/index.ts @@ -17,7 +17,6 @@ import BrowserConnectionStatus from './status'; import HeartbeatStatus from './heartbeat-status'; import { GeneralError, TimeoutError } from '../../errors/runtime'; import { RUNTIME_ERRORS } from '../../errors/types'; -import { Dictionary } from '../../configuration/interfaces'; import BrowserConnectionGateway from './gateway'; import BrowserJob from '../../runner/browser-job'; import WarningLog from '../../notifications/warning-log'; @@ -32,11 +31,15 @@ import { REMOTE_BROWSER_INIT_TIMEOUT, } from '../../utils/browser-connection-timeouts'; import MessageBus from '../../utils/message-bus'; +import BrowserConnectionTracker from './browser-connection-tracker'; +import TestRun from '../../test-run'; +// @ts-ignore +import { TestRun as LegacyTestRun } from 'testcafe-legacy-api'; const getBrowserConnectionDebugScope = (id: string): string => `testcafe:browser:connection:${id}`; -const IDLE_PAGE_TEMPLATE = read('../../client/browser/idle-page/index.html.mustache'); -const connections: Dictionary = {}; +const IDLE_PAGE_TEMPLATE = read('../../client/browser/idle-page/index.html.mustache'); + interface DisconnectionPromise extends Promise { resolve: Function; @@ -83,31 +86,28 @@ export default class BrowserConnection extends EventEmitter { public permanent: boolean; public previousActiveWindowId: string | null; private readonly disableMultipleWindows: boolean; - private readonly proxyless: boolean; + public readonly proxyless: boolean; private readonly HEARTBEAT_TIMEOUT: number; private readonly BROWSER_CLOSE_TIMEOUT: number; private readonly BROWSER_RESTART_TIMEOUT: number; public readonly id: string; private readonly jobQueue: BrowserJob[]; private readonly initScriptsQueue: InitScriptTask[]; - private browserConnectionGateway: BrowserConnectionGateway; + public browserConnectionGateway: BrowserConnectionGateway; private disconnectionPromise: DisconnectionPromise | null; private testRunAborted: boolean; public status: BrowserConnectionStatus; private heartbeatTimeout: NodeJS.Timeout | null; private pendingTestRunUrl: string | null; - public readonly url: string; - public readonly idleUrl: string; - private forcedIdleUrl: string; - private readonly initScriptUrl: string; - public readonly heartbeatRelativeUrl: string; - public readonly statusRelativeUrl: string; - public readonly statusDoneRelativeUrl: string; - private readonly heartbeatUrl: string; - private readonly statusUrl: string; - public readonly activeWindowIdUrl: string; - public readonly closeWindowUrl: string; - private statusDoneUrl: string; + public url = ''; + public idleUrl = ''; + private forcedIdleUrl = ''; + private initScriptUrl = ''; + public heartbeatUrl = ''; + public statusUrl = ''; + public activeWindowIdUrl = ''; + public closeWindowUrl = ''; + public statusDoneUrl = ''; private readonly debugLogger: debug.Debugger; private osInfo: OSInfo | null = null; @@ -155,24 +155,10 @@ export default class BrowserConnection extends EventEmitter { this.disableMultipleWindows = disableMultipleWindows; this.proxyless = proxyless; - this.url = `${gateway.domain}/browser/connect/${this.id}`; - this.idleUrl = `${gateway.domain}/browser/idle/${this.id}`; - this.forcedIdleUrl = `${gateway.domain}/browser/idle-forced/${this.id}`; - this.initScriptUrl = `${gateway.domain}/browser/init-script/${this.id}`; - - this.heartbeatRelativeUrl = `/browser/heartbeat/${this.id}`; - this.statusRelativeUrl = `/browser/status/${this.id}`; - this.statusDoneRelativeUrl = `/browser/status-done/${this.id}`; - this.activeWindowIdUrl = `/browser/active-window-id/${this.id}`; - this.closeWindowUrl = `/browser/close-window/${this.id}`; - - this.heartbeatUrl = `${gateway.domain}${this.heartbeatRelativeUrl}`; - this.statusUrl = `${gateway.domain}${this.statusRelativeUrl}`; - this.statusDoneUrl = `${gateway.domain}${this.statusDoneRelativeUrl}`; - + this._buildCommunicationUrls(gateway); this._setEventHandlers(); - connections[this.id] = this; + BrowserConnectionTracker.add(this); this.previousActiveWindowId = null; @@ -182,6 +168,19 @@ export default class BrowserConnection extends EventEmitter { process.nextTick(() => this._runBrowser()); } + private _buildCommunicationUrls (gateway: BrowserConnectionGateway): void { + this.url = gateway.proxy.resolveRelativeServiceUrl(`/browser/connect/${this.id}`); + this.idleUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/idle/${this.id}`); + this.forcedIdleUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/idle-forced/${this.id}`); + this.initScriptUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/init-script/${this.id}`); + this.activeWindowIdUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/active-window-id/${this.id}`); + this.closeWindowUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/close-window/${this.id}`); + this.heartbeatUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/heartbeat/${this.id}`); + this.statusUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/status/${this.id}`); + this.statusDoneUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/status-done/${this.id}`); + + } + public set messageBus (messageBus: MessageBus) { this._messageBus = messageBus; this.warningLog.callback = WarningLog.createAddWarningCallback(this._messageBus); @@ -278,8 +277,12 @@ export default class BrowserConnection extends EventEmitter { return this.hasQueuedJobs ? await this.currentJob.popNextTestRunUrl(this) : null; } + public getCurrentTestRun (): LegacyTestRun | TestRun | null { + return this.currentJob ? this.currentJob.currentTestRun : null; + } + public static getById (id: string): BrowserConnection | null { - return connections[id] || null; + return BrowserConnectionTracker.activeBrowserConnections[id] || null; } private async _restartBrowser (): Promise { @@ -454,7 +457,7 @@ export default class BrowserConnection extends EventEmitter { if (this.heartbeatTimeout) clearTimeout(this.heartbeatTimeout); - delete connections[this.id]; + BrowserConnectionTracker.remove(this); this.status = BrowserConnectionStatus.closed; this.emit(BrowserConnectionStatus.closed); diff --git a/src/browser/provider/built-in/dedicated/chrome/cdp-client/index.ts b/src/browser/provider/built-in/dedicated/chrome/cdp-client/index.ts index 85163a5582f..2cf7edb6f63 100644 --- a/src/browser/provider/built-in/dedicated/chrome/cdp-client/index.ts +++ b/src/browser/provider/built-in/dedicated/chrome/cdp-client/index.ts @@ -49,16 +49,14 @@ interface VideoFrameData { export class BrowserClient { private _clients: Dictionary = {}; private readonly _runtimeInfo: RuntimeInfo; - private readonly _proxyless: boolean; private _parentTarget?: remoteChrome.TargetInfo; private readonly debugLogger: debug.Debugger; private readonly _videoFramesBuffer: VideoFrameData[]; private _lastFrame: VideoFrameData | null; - public constructor (runtimeInfo: RuntimeInfo, proxyless: boolean) { + public constructor (runtimeInfo: RuntimeInfo) { this._runtimeInfo = runtimeInfo; this.debugLogger = debug(DEBUG_SCOPE(runtimeInfo.browserId)); - this._proxyless = proxyless; runtimeInfo.browserClient = this; diff --git a/src/browser/provider/built-in/dedicated/chrome/index.js b/src/browser/provider/built-in/dedicated/chrome/index.js index 4ef89c1bfea..56c3a7be1e1 100644 --- a/src/browser/provider/built-in/dedicated/chrome/index.js +++ b/src/browser/provider/built-in/dedicated/chrome/index.js @@ -10,6 +10,7 @@ import { } from './local-chrome'; import { GET_WINDOW_DIMENSIONS_INFO_SCRIPT } from '../../../utils/client-functions'; import { BrowserClient } from './cdp-client'; +import RequestsInterceptor from './requests-interceptor'; const MIN_AVAILABLE_DIMENSION = 50; @@ -70,7 +71,7 @@ export default { if (!disableMultipleWindows) runtimeInfo.activeWindowId = this.calculateWindowId(); - const browserClient = new BrowserClient(runtimeInfo, proxyless); + const browserClient = new BrowserClient(runtimeInfo); this.openedBrowsers[browserId] = runtimeInfo; @@ -79,6 +80,13 @@ export default { await this._ensureWindowIsExpanded(browserId, runtimeInfo.viewportSize); this._setUserAgentMetaInfoForEmulatingDevice(browserId, runtimeInfo.config); + + if (proxyless) { + const requestsInterceptor = new RequestsInterceptor(browserId); + const cdpClient = await browserClient.getActiveClient(); + + await requestsInterceptor.setup(cdpClient); + } }, async closeBrowser (browserId, closingInfo = {}) { diff --git a/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts b/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts new file mode 100644 index 00000000000..f7bb6e92c56 --- /dev/null +++ b/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts @@ -0,0 +1,80 @@ +import { ProtocolApi } from 'chrome-remote-interface'; +import Protocol from 'devtools-protocol'; +import RequestPausedEvent = Protocol.Fetch.RequestPausedEvent; +import RequestPattern = Protocol.Fetch.RequestPattern; +import GetResponseBodyResponse = Protocol.Fetch.GetResponseBodyResponse; +import { injectResources, PageInjectableResources } from 'testcafe-hammerhead'; +import BrowserConnection from '../../../../connection'; +import { SCRIPTS } from '../../../../../assets/injectables'; + +const HTTP_STATUS_OK = 200; + +export default class RequestsInterceptor { + private readonly _browserId: string; + + public constructor (browserId: string) { + this._browserId = browserId; + } + + private _getResponseAsString (response: GetResponseBodyResponse): string { + return response.base64Encoded + ? Buffer.from(response.body, 'base64').toString() + : response.body; + } + + private async _prepareInjectableResources (): Promise { + const browserConnection = BrowserConnection.getById(this._browserId) as BrowserConnection; + const proxy = browserConnection.browserConnectionGateway.proxy; + + const payloadScript = await browserConnection.currentJob.currentTestRun.getPayloadScript(); + + const injectableResources = { + stylesheets: [ + '/testcafe-ui-styles.css', + ], + scripts: [ + '/hammerhead.js', + ...SCRIPTS, + ], + embeddedScripts: [payloadScript], + }; + + for (let i = 0; i < injectableResources.scripts.length; i++) + injectableResources.scripts[i] = proxy.resolveRelativeServiceUrl(injectableResources.scripts[i]); + + for (let j = 0; j < injectableResources.stylesheets.length; j++) + injectableResources.stylesheets[j] = proxy.resolveRelativeServiceUrl(injectableResources.stylesheets[j]); + + return injectableResources; + } + + public async setup (client: ProtocolApi): Promise { + const fetchAllDocumentsPattern = { + urlPattern: '*', + resourceType: 'Document', + requestStage: 'Response', + } as RequestPattern; + + await client.Fetch.enable({ patterns: [fetchAllDocumentsPattern] }); + + client.Fetch.on('requestPaused', async (params: RequestPausedEvent) => { + const { + requestId, + responseHeaders, + responseStatusCode, + } = params; + + const responseObj = await client.Fetch.getResponseBody({ requestId }); + const responseStr = this._getResponseAsString(responseObj); + const injectableResources = await this._prepareInjectableResources(); + const updatedResponseStr = injectResources(responseStr, injectableResources); + + await client.Fetch.fulfillRequest({ + requestId, + responseCode: responseStatusCode || HTTP_STATUS_OK, + responseHeaders: responseHeaders || [], + body: Buffer.from(updatedResponseStr).toString('base64'), + }); + }); + } +} diff --git a/src/client/test-run/index.js.mustache b/src/client/test-run/index.js.mustache index 13bc1e307af..bbcc624bb63 100644 --- a/src/client/test-run/index.js.mustache +++ b/src/client/test-run/index.js.mustache @@ -2,12 +2,6 @@ if (window !== window.top) return; - var origin = location.origin; - - // NOTE: location.origin doesn't exist in IE11 on Windows 10.10240 LTSB - if (!origin) - origin = location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : ''); - var testRunId = {{{testRunId}}}; var browserId = {{{browserId}}}; var selectorTimeout = {{{selectorTimeout}}}; @@ -15,17 +9,18 @@ var childWindowReadyTimeout = {{{childWindowReadyTimeout}}}; var retryTestPages = {{{retryTestPages}}}; var speed = {{{speed}}}; - var browserHeartbeatUrl = origin + {{{browserHeartbeatRelativeUrl}}}; - var browserStatusUrl = origin + {{{browserStatusRelativeUrl}}}; - var browserStatusDoneUrl = origin + {{{browserStatusDoneRelativeUrl}}}; - var browserActiveWindowIdUrl = origin + {{{browserActiveWindowIdUrl}}}; - var browserCloseWindowUrl = origin + {{{browserCloseWindowUrl}}}; + var browserHeartbeatUrl = {{{browserHeartbeatUrl}}}; + var browserStatusUrl = {{{browserStatusUrl}}}; + var browserStatusDoneUrl = {{{browserStatusDoneUrl}}}; + var browserActiveWindowIdUrl = {{{browserActiveWindowIdUrl}}}; + var browserCloseWindowUrl = {{{browserCloseWindowUrl}}}; var skipJsErrors = {{{skipJsErrors}}}; var dialogHandler = {{{dialogHandler}}}; var userAgent = {{{userAgent}}}; var fixtureName = {{{fixtureName}}}; var testName = {{{testName}}}; var canUseDefaultWindowActions = {{{canUseDefaultWindowActions}}}; + var proxyless = {{{proxyless}}}; var ClientDriver = window['%testCafeDriver%']; var driver = new ClientDriver(testRunId, @@ -39,7 +34,8 @@ dialogHandler: dialogHandler, retryTestPages: retryTestPages, speed: speed, - canUseDefaultWindowActions: canUseDefaultWindowActions + canUseDefaultWindowActions: canUseDefaultWindowActions, + proxyless: proxyless } ); diff --git a/src/configuration/testcafe-configuration.ts b/src/configuration/testcafe-configuration.ts index 1c1560503e0..f5fe1330d35 100644 --- a/src/configuration/testcafe-configuration.ts +++ b/src/configuration/testcafe-configuration.ts @@ -74,6 +74,7 @@ interface TestCafeAdditionalStartOptions { developmentMode: boolean; cache: boolean; disableHttp2: boolean; + proxyless: boolean; } interface TestCafeStartOptions { @@ -160,6 +161,7 @@ export default class TestCafeConfiguration extends Configuration { retryTestPages: this.getOption(OPTION_NAMES.retryTestPages) as boolean, cache: this.getOption(OPTION_NAMES.cache) as boolean, disableHttp2: this.getOption(OPTION_NAMES.disableHttp2) as boolean, + proxyless: this.getOption(OPTION_NAMES.proxyless) as boolean, }, }; diff --git a/src/runner/browser-job.ts b/src/runner/browser-job.ts index 41739a5faa3..af09d31c259 100644 --- a/src/runner/browser-job.ts +++ b/src/runner/browser-job.ts @@ -14,6 +14,9 @@ import CompilerService from '../services/compiler/host'; import { BrowserJobInit } from './interfaces'; import MessageBus from '../utils/message-bus'; import TestRunHookController from './test-run-hook-controller'; +import TestRun from '../test-run'; +// @ts-ignore +import { TestRun as LegacyTestRun } from 'testcafe-legacy-api'; interface BrowserJobResultInfo { status: BrowserJobResult; @@ -207,6 +210,10 @@ export default class BrowserJob extends AsyncEventEmitter { return !!this._testRunControllerQueue.length; } + public get currentTestRun (): LegacyTestRun | TestRun | null { + return this._completionQueue.length ? this._completionQueue[0].testRun : null; + } + public async popNextTestRunUrl (connection: BrowserConnection): Promise { while (this._testRunControllerQueue.length) { const testRunController = this._testRunControllerQueue[0]; diff --git a/src/test-run/index.ts b/src/test-run/index.ts index 102fee7a0c4..17ca288f35c 100644 --- a/src/test-run/index.ts +++ b/src/test-run/index.ts @@ -535,24 +535,25 @@ export default class TestRun extends AsyncEventEmitter { this.resolveWaitForFileDownloadingPromise = null; return Mustache.render(TEST_RUN_TEMPLATE, { - testRunId: JSON.stringify(this.session.id), - browserId: JSON.stringify(this.browserConnection.id), - browserHeartbeatRelativeUrl: JSON.stringify(this.browserConnection.heartbeatRelativeUrl), - browserStatusRelativeUrl: JSON.stringify(this.browserConnection.statusRelativeUrl), - browserStatusDoneRelativeUrl: JSON.stringify(this.browserConnection.statusDoneRelativeUrl), - browserActiveWindowIdUrl: JSON.stringify(this.browserConnection.activeWindowIdUrl), - browserCloseWindowUrl: JSON.stringify(this.browserConnection.closeWindowUrl), - userAgent: JSON.stringify(this.browserConnection.userAgent), - testName: JSON.stringify(this.test.name), - fixtureName: JSON.stringify((this.test.fixture as Fixture).name), - selectorTimeout: this.opts.selectorTimeout, - pageLoadTimeout: this.pageLoadTimeout, - childWindowReadyTimeout: CHILD_WINDOW_READY_TIMEOUT, - skipJsErrors: this.opts.skipJsErrors, - retryTestPages: this.opts.retryTestPages, - speed: this.speed, - dialogHandler: JSON.stringify(this.activeDialogHandler), - canUseDefaultWindowActions: JSON.stringify(await this.browserConnection.canUseDefaultWindowActions()), + testRunId: JSON.stringify(this.session.id), + browserId: JSON.stringify(this.browserConnection.id), + browserHeartbeatUrl: JSON.stringify(this.browserConnection.heartbeatUrl), + browserStatusUrl: JSON.stringify(this.browserConnection.statusUrl), + browserStatusDoneUrl: JSON.stringify(this.browserConnection.statusDoneUrl), + browserActiveWindowIdUrl: JSON.stringify(this.browserConnection.activeWindowIdUrl), + browserCloseWindowUrl: JSON.stringify(this.browserConnection.closeWindowUrl), + userAgent: JSON.stringify(this.browserConnection.userAgent), + testName: JSON.stringify(this.test.name), + fixtureName: JSON.stringify((this.test.fixture as Fixture).name), + selectorTimeout: this.opts.selectorTimeout, + pageLoadTimeout: this.pageLoadTimeout, + childWindowReadyTimeout: CHILD_WINDOW_READY_TIMEOUT, + skipJsErrors: this.opts.skipJsErrors, + retryTestPages: this.opts.retryTestPages, + speed: this.speed, + dialogHandler: JSON.stringify(this.activeDialogHandler), + canUseDefaultWindowActions: JSON.stringify(await this.browserConnection.canUseDefaultWindowActions()), + proxyless: JSON.stringify(this.opts.proxyless), }); } From eb2b1cbf287e86e66b2fdf5a22b523f922a0ac99 Mon Sep 17 00:00:00 2001 From: miherlosev Date: Wed, 20 Jul 2022 18:00:57 +0400 Subject: [PATCH 2/9] fix tests --- src/browser/connection/index.ts | 23 ++++++++++++----------- test/server/bootstrapper-test.js | 10 ++++------ test/server/browser-provider-test.js | 7 ++----- test/server/helpers/mocks.js | 14 ++++++++++++++ test/server/runner-test.js | 18 +++++++++--------- 5 files changed, 41 insertions(+), 31 deletions(-) create mode 100644 test/server/helpers/mocks.js diff --git a/src/browser/connection/index.ts b/src/browser/connection/index.ts index 832b99a4983..20dbaebf87c 100644 --- a/src/browser/connection/index.ts +++ b/src/browser/connection/index.ts @@ -35,6 +35,7 @@ import BrowserConnectionTracker from './browser-connection-tracker'; import TestRun from '../../test-run'; // @ts-ignore import { TestRun as LegacyTestRun } from 'testcafe-legacy-api'; +import { Proxy } from 'testcafe-hammerhead'; const getBrowserConnectionDebugScope = (id: string): string => `testcafe:browser:connection:${id}`; @@ -155,7 +156,7 @@ export default class BrowserConnection extends EventEmitter { this.disableMultipleWindows = disableMultipleWindows; this.proxyless = proxyless; - this._buildCommunicationUrls(gateway); + this._buildCommunicationUrls(gateway.proxy); this._setEventHandlers(); BrowserConnectionTracker.add(this); @@ -168,16 +169,16 @@ export default class BrowserConnection extends EventEmitter { process.nextTick(() => this._runBrowser()); } - private _buildCommunicationUrls (gateway: BrowserConnectionGateway): void { - this.url = gateway.proxy.resolveRelativeServiceUrl(`/browser/connect/${this.id}`); - this.idleUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/idle/${this.id}`); - this.forcedIdleUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/idle-forced/${this.id}`); - this.initScriptUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/init-script/${this.id}`); - this.activeWindowIdUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/active-window-id/${this.id}`); - this.closeWindowUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/close-window/${this.id}`); - this.heartbeatUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/heartbeat/${this.id}`); - this.statusUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/status/${this.id}`); - this.statusDoneUrl = gateway.proxy.resolveRelativeServiceUrl(`/browser/status-done/${this.id}`); + private _buildCommunicationUrls (proxy: Proxy): void { + this.url = proxy.resolveRelativeServiceUrl(`/browser/connect/${this.id}`); + this.idleUrl = proxy.resolveRelativeServiceUrl(`/browser/idle/${this.id}`); + this.forcedIdleUrl = proxy.resolveRelativeServiceUrl(`/browser/idle-forced/${this.id}`); + this.initScriptUrl = proxy.resolveRelativeServiceUrl(`/browser/init-script/${this.id}`); + this.activeWindowIdUrl = proxy.resolveRelativeServiceUrl(`/browser/active-window-id/${this.id}`); + this.closeWindowUrl = proxy.resolveRelativeServiceUrl(`/browser/close-window/${this.id}`); + this.heartbeatUrl = proxy.resolveRelativeServiceUrl(`/browser/heartbeat/${this.id}`); + this.statusUrl = proxy.resolveRelativeServiceUrl(`/browser/status/${this.id}`); + this.statusDoneUrl = proxy.resolveRelativeServiceUrl(`/browser/status-done/${this.id}`); } diff --git a/test/server/bootstrapper-test.js b/test/server/bootstrapper-test.js index da4eac1dce8..cbab402d066 100644 --- a/test/server/bootstrapper-test.js +++ b/test/server/bootstrapper-test.js @@ -5,12 +5,10 @@ const Test = require('../../lib/api/structure/test'); const Bootstrapper = require('../../lib/runner/bootstrapper'); const delay = require('../../lib/utils/delay'); +const { browserConnectionGatewayMock } = require('./helpers/mocks'); + describe('Bootstrapper', () => { describe('.createRunnableConfiguration()', () => { - const browserConnectionGateway = { - startServingConnection: noop, - stopServingConnection: noop, - }; const compilerService = { init: noop, getTests: async () => { @@ -26,7 +24,7 @@ describe('Bootstrapper', () => { let bootstrapper = null; beforeEach(() => { - bootstrapper = new Bootstrapper({ browserConnectionGateway, compilerService, configuration }); + bootstrapper = new Bootstrapper({ browserConnectionGatewayMock, compilerService, configuration }); bootstrapper.browserInitTimeout = 100; bootstrapper.TESTS_COMPILATION_UPPERBOUND = 0; @@ -38,7 +36,7 @@ describe('Bootstrapper', () => { closeBrowser: noop, }; - bootstrapper.browsers = [ new BrowserConnection(browserConnectionGateway, { provider }) ]; + bootstrapper.browsers = [ new BrowserConnection(browserConnectionGatewayMock, { provider }) ]; }); it('Browser connection error message should include hint that tests compilation takes too long', async function () { diff --git a/test/server/browser-provider-test.js b/test/server/browser-provider-test.js index 98841ad3c41..cb6364bd53e 100644 --- a/test/server/browser-provider-test.js +++ b/test/server/browser-provider-test.js @@ -16,13 +16,10 @@ const WARNING_MESSAGE = require('../../lib/notifications/warning const BrowserProviderPluginHost = require('../../lib/browser/provider/plugin-host'); const WarningLog = require('../../lib/notifications/warning-log'); +const { browserConnectionGatewayMock } = require('./helpers/mocks'); + class BrowserConnectionMock extends BrowserConnection { constructor () { - const browserConnectionGatewayMock = { - startServingConnection: noop, - stopServingConnection: noop, - }; - const providerMock = { openBrowser: noop, isLocalBrowser: noop, diff --git a/test/server/helpers/mocks.js b/test/server/helpers/mocks.js new file mode 100644 index 00000000000..f7ac2a39719 --- /dev/null +++ b/test/server/helpers/mocks.js @@ -0,0 +1,14 @@ +const { noop } = require('lodash'); + +const browserConnectionGatewayMock = { + startServingConnection: noop, + stopServingConnection: noop, + + proxy: { + resolveRelativeServiceUrl: noop, + }, +}; + +module.exports = { + browserConnectionGatewayMock, +}; diff --git a/test/server/runner-test.js b/test/server/runner-test.js index a2b2ab0c078..15b11f3071d 100644 --- a/test/server/runner-test.js +++ b/test/server/runner-test.js @@ -23,6 +23,8 @@ const { noop } = require('lodash'); const Test = require('../../lib/api/structure/test'); const TestCafeConfiguration = require('../../lib/configuration/testcafe-configuration'); +const { browserConnectionGatewayMock } = require('./helpers/mocks'); + const createConfigFile = (configPath, options) => { options = options || {}; fs.writeFileSync(configPath, JSON.stringify(options)); @@ -1067,11 +1069,6 @@ describe('Runner', () => { }); describe('On Linux without a graphics subsystem', () => { - - const browserConnectionGateway = { - startServingConnection: noop, - stopServingConnection: noop, - }; const compilerService = { init: noop, getTests: () => [new Test({ currentFixture: void 0 })], @@ -1082,6 +1079,9 @@ describe('Runner', () => { class BrowserConnectionMock extends BrowserConnection { constructor (...args) { + if (!args[0]) + args[0] = browserConnectionGatewayMock; + super(...args); this.status = BrowserConnectionStatus.opened; @@ -1095,7 +1095,7 @@ describe('Runner', () => { '../browser/connection': BrowserConnectionMock, }); - return new BootstrapperMock({ browserConnectionGateway, compilerService }); + return new BootstrapperMock({ browserConnectionGatewayMock, compilerService }); } function createMockRunner () { @@ -1106,7 +1106,7 @@ describe('Runner', () => { const runnerLocal = new RunnerMock({ proxy: testCafe.proxy, - browserConnectionGateway: browserConnectionGateway, + browserConnectionGateway: browserConnectionGatewayMock, configuration: testCafe.configuration.clone(), compilerService: compilerService, }); @@ -1172,7 +1172,7 @@ describe('Runner', () => { await runnerLinux._validateRunOptions(); await runnerLinux._createRunnableConfiguration(); } - catch { + catch (e) { isErrorThrown = true; } @@ -1184,7 +1184,7 @@ describe('Runner', () => { let isErrorThrown = false; try { - await runnerLinux.browsers([new BrowserConnection(browserConnectionGateway, browserInfo)]); + await runnerLinux.browsers([new BrowserConnection(browserConnectionGatewayMock, browserInfo)]); await runnerLinux._setConfigurationOptions(); await runnerLinux._setBootstrapperOptions(); await runnerLinux._validateRunOptions(); From 188e88f884c2baaac9589a8f30c5fe16194a55b7 Mon Sep 17 00:00:00 2001 From: miherlosev Date: Wed, 20 Jul 2022 18:13:37 +0400 Subject: [PATCH 3/9] small refactoring --- .../provider/built-in/dedicated/chrome/index.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/browser/provider/built-in/dedicated/chrome/index.js b/src/browser/provider/built-in/dedicated/chrome/index.js index 56c3a7be1e1..811a8e70384 100644 --- a/src/browser/provider/built-in/dedicated/chrome/index.js +++ b/src/browser/provider/built-in/dedicated/chrome/index.js @@ -44,6 +44,13 @@ export default { this.setUserAgentMetaInfo(browserId, metaInfo, options); }, + async _setupProxyless (browserId, browserClient) { + const requestsInterceptor = new RequestsInterceptor(browserId); + const cdpClient = await browserClient.getActiveClient(); + + await requestsInterceptor.setup(cdpClient); + }, + async openBrowser (browserId, pageUrl, config, disableMultipleWindows, proxyless) { const parsedPageUrl = parseUrl(pageUrl); const runtimeInfo = await this._createRunTimeInfo(parsedPageUrl.hostname, config, disableMultipleWindows); @@ -81,12 +88,8 @@ export default { this._setUserAgentMetaInfoForEmulatingDevice(browserId, runtimeInfo.config); - if (proxyless) { - const requestsInterceptor = new RequestsInterceptor(browserId); - const cdpClient = await browserClient.getActiveClient(); - - await requestsInterceptor.setup(cdpClient); - } + if (proxyless) + await this._setupProxyless(browserId, browserClient); }, async closeBrowser (browserId, closingInfo = {}) { From 2a0923557820e2b2d4f2534b570e1cd63593ad36 Mon Sep 17 00:00:00 2001 From: miherlosev Date: Wed, 20 Jul 2022 23:32:44 +0400 Subject: [PATCH 4/9] small refactoring --- .../dedicated/chrome/requests-interceptor.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts b/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts index f7bb6e92c56..a9689a64f53 100644 --- a/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts +++ b/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts @@ -3,9 +3,13 @@ import Protocol from 'devtools-protocol'; import RequestPausedEvent = Protocol.Fetch.RequestPausedEvent; import RequestPattern = Protocol.Fetch.RequestPattern; import GetResponseBodyResponse = Protocol.Fetch.GetResponseBodyResponse; -import { injectResources, PageInjectableResources } from 'testcafe-hammerhead'; +import { + injectResources, + PageInjectableResources, + INJECTABLE_SCRIPTS as HAMMERHEAD_INJECTABLE_SCRIPTS, +} from 'testcafe-hammerhead'; import BrowserConnection from '../../../../connection'; -import { SCRIPTS } from '../../../../../assets/injectables'; +import { SCRIPTS, TESTCAFE_UI_STYLES } from '../../../../../assets/injectables'; const HTTP_STATUS_OK = 200; @@ -25,25 +29,21 @@ export default class RequestsInterceptor { private async _prepareInjectableResources (): Promise { const browserConnection = BrowserConnection.getById(this._browserId) as BrowserConnection; const proxy = browserConnection.browserConnectionGateway.proxy; - - const payloadScript = await browserConnection.currentJob.currentTestRun.getPayloadScript(); + const payloadScript = await browserConnection.currentJob.currentTestRun.getPayloadScript(); const injectableResources = { stylesheets: [ - '/testcafe-ui-styles.css', + TESTCAFE_UI_STYLES, ], scripts: [ - '/hammerhead.js', + ...HAMMERHEAD_INJECTABLE_SCRIPTS, ...SCRIPTS, ], embeddedScripts: [payloadScript], }; - for (let i = 0; i < injectableResources.scripts.length; i++) - injectableResources.scripts[i] = proxy.resolveRelativeServiceUrl(injectableResources.scripts[i]); - - for (let j = 0; j < injectableResources.stylesheets.length; j++) - injectableResources.stylesheets[j] = proxy.resolveRelativeServiceUrl(injectableResources.stylesheets[j]); + injectableResources.scripts = injectableResources.scripts.map(proxy.resolveRelativeServiceUrl); + injectableResources.stylesheets = injectableResources.stylesheets.map(proxy.resolveRelativeServiceUrl); return injectableResources; } From 36f6bcabcc79b52bce1a3185902aa7fddc0c54e3 Mon Sep 17 00:00:00 2001 From: miherlosev Date: Thu, 21 Jul 2022 10:43:29 +0400 Subject: [PATCH 5/9] update hammerhead --- package.json | 2 +- .../built-in/dedicated/chrome/requests-interceptor.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c59af9408b5..b1185a21674 100644 --- a/package.json +++ b/package.json @@ -137,7 +137,7 @@ "source-map-support": "^0.5.16", "strip-bom": "^2.0.0", "testcafe-browser-tools": "2.0.23", - "testcafe-hammerhead": "https://github.com/miherlosev/tmp/files/9149185/testcafe-hammerhead-24.6.0.zip", + "testcafe-hammerhead": "24.6.0", "testcafe-legacy-api": "5.1.4", "testcafe-reporter-dashboard": "1.0.0-rc.3", "testcafe-reporter-json": "^2.1.0", diff --git a/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts b/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts index a9689a64f53..656c9fd6413 100644 --- a/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts +++ b/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts @@ -42,8 +42,8 @@ export default class RequestsInterceptor { embeddedScripts: [payloadScript], }; - injectableResources.scripts = injectableResources.scripts.map(proxy.resolveRelativeServiceUrl); - injectableResources.stylesheets = injectableResources.stylesheets.map(proxy.resolveRelativeServiceUrl); + injectableResources.scripts = injectableResources.scripts.map(script => proxy.resolveRelativeServiceUrl(script)); + injectableResources.stylesheets = injectableResources.stylesheets.map(style => proxy.resolveRelativeServiceUrl(style)); return injectableResources; } From 47f5c97220870f1d8a560dad4dd92b8caf942838 Mon Sep 17 00:00:00 2001 From: miherlosev Date: Thu, 21 Jul 2022 11:59:59 +0400 Subject: [PATCH 6/9] stub proxyless tests --- gulp/constants/functional-test-globs.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gulp/constants/functional-test-globs.js b/gulp/constants/functional-test-globs.js index 5fea177ef30..c21a317ed7e 100644 --- a/gulp/constants/functional-test-globs.js +++ b/gulp/constants/functional-test-globs.js @@ -27,6 +27,8 @@ const DEBUG_GLOB_2 = [ ...SCREENSHOT_TESTS_GLOB.map(glob => `!${glob}`), ]; +const PROXYLESS_TESTS_GLOB = []; + module.exports = { TESTS_GLOB, LEGACY_TESTS_GLOB, @@ -36,4 +38,5 @@ module.exports = { DEBUG_GLOB_1, DEBUG_GLOB_2, SCREENSHOT_TESTS_GLOB, + PROXYLESS_TESTS_GLOB, }; From 9d75bf81c7d5bfc03292baf91a685895f77bce92 Mon Sep 17 00:00:00 2001 From: miherlosev Date: Thu, 21 Jul 2022 14:06:42 +0400 Subject: [PATCH 7/9] fix task script creation --- .../dedicated/chrome/requests-interceptor.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts b/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts index 656c9fd6413..76546df2a1b 100644 --- a/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts +++ b/src/browser/provider/built-in/dedicated/chrome/requests-interceptor.ts @@ -29,7 +29,16 @@ export default class RequestsInterceptor { private async _prepareInjectableResources (): Promise { const browserConnection = BrowserConnection.getById(this._browserId) as BrowserConnection; const proxy = browserConnection.browserConnectionGateway.proxy; - const payloadScript = await browserConnection.currentJob.currentTestRun.getPayloadScript(); + const windowId = browserConnection.activeWindowId; + + const taskScript = await browserConnection.currentJob.currentTestRun.session.getTaskScript({ + referer: '', + cookieUrl: '', + isIframe: false, + withPayload: true, + serverInfo: proxy.server1Info, + windowId, + }); const injectableResources = { stylesheets: [ @@ -39,7 +48,7 @@ export default class RequestsInterceptor { ...HAMMERHEAD_INJECTABLE_SCRIPTS, ...SCRIPTS, ], - embeddedScripts: [payloadScript], + embeddedScripts: [taskScript], }; injectableResources.scripts = injectableResources.scripts.map(script => proxy.resolveRelativeServiceUrl(script)); From 20cc0dc2a2d7fce1c689ee8f678f45c6c49e20af Mon Sep 17 00:00:00 2001 From: miherlosev Date: Thu, 21 Jul 2022 14:07:18 +0400 Subject: [PATCH 8/9] fix proxyless run --- Gulpfile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gulpfile.js b/Gulpfile.js index 95e3ccb09eb..1bfe09be70a 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -36,6 +36,7 @@ const { MULTIPLE_WINDOWS_TESTS_GLOB, DEBUG_GLOB_1, DEBUG_GLOB_2, + PROXYLESS_TESTS_GLOB, } = require('./gulp/constants/functional-test-globs'); const { @@ -424,7 +425,7 @@ gulp.task('test-functional-local-debug-1', gulp.series('prepare-tests', 'test-fu gulp.task('test-functional-local-debug-2', gulp.series('prepare-tests', 'test-functional-local-debug-run-2')); gulp.step('test-functional-local-proxyless-run', () => { - return testFunctional(TESTS_GLOB, functionalTestConfig.testingEnvironmentNames.localHeadlessChrome, { isProxyless: true }); + return testFunctional(PROXYLESS_TESTS_GLOB, functionalTestConfig.testingEnvironmentNames.localHeadlessChrome, { isProxyless: true }); }); gulp.task('test-functional-local-proxyless', gulp.series('prepare-tests', 'test-functional-local-proxyless-run')); From 2e12dd75def9757b3d020609101c99806318eb4c Mon Sep 17 00:00:00 2001 From: miherlosev Date: Thu, 21 Jul 2022 15:29:44 +0400 Subject: [PATCH 9/9] revert refactoring --- src/browser/connection/index.ts | 18 +++++++++---- src/client/test-run/index.js.mustache | 16 +++++++---- src/test-run/index.ts | 38 +++++++++++++-------------- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/browser/connection/index.ts b/src/browser/connection/index.ts index 20dbaebf87c..64fa40b0fb2 100644 --- a/src/browser/connection/index.ts +++ b/src/browser/connection/index.ts @@ -109,6 +109,9 @@ export default class BrowserConnection extends EventEmitter { public activeWindowIdUrl = ''; public closeWindowUrl = ''; public statusDoneUrl = ''; + public heartbeatRelativeUrl = ''; + public statusRelativeUrl = ''; + public statusDoneRelativeUrl = ''; private readonly debugLogger: debug.Debugger; private osInfo: OSInfo | null = null; @@ -174,11 +177,16 @@ export default class BrowserConnection extends EventEmitter { this.idleUrl = proxy.resolveRelativeServiceUrl(`/browser/idle/${this.id}`); this.forcedIdleUrl = proxy.resolveRelativeServiceUrl(`/browser/idle-forced/${this.id}`); this.initScriptUrl = proxy.resolveRelativeServiceUrl(`/browser/init-script/${this.id}`); - this.activeWindowIdUrl = proxy.resolveRelativeServiceUrl(`/browser/active-window-id/${this.id}`); - this.closeWindowUrl = proxy.resolveRelativeServiceUrl(`/browser/close-window/${this.id}`); - this.heartbeatUrl = proxy.resolveRelativeServiceUrl(`/browser/heartbeat/${this.id}`); - this.statusUrl = proxy.resolveRelativeServiceUrl(`/browser/status/${this.id}`); - this.statusDoneUrl = proxy.resolveRelativeServiceUrl(`/browser/status-done/${this.id}`); + + this.heartbeatRelativeUrl = `/browser/heartbeat/${this.id}`; + this.statusRelativeUrl = `/browser/status/${this.id}`; + this.statusDoneRelativeUrl = `/browser/status-done/${this.id}`; + this.activeWindowIdUrl = `/browser/active-window-id/${this.id}`; + this.closeWindowUrl = `/browser/close-window/${this.id}`; + + this.heartbeatUrl = proxy.resolveRelativeServiceUrl(this.heartbeatRelativeUrl); + this.statusUrl = proxy.resolveRelativeServiceUrl(this.statusRelativeUrl); + this.statusDoneUrl = proxy.resolveRelativeServiceUrl(this.statusDoneRelativeUrl); } diff --git a/src/client/test-run/index.js.mustache b/src/client/test-run/index.js.mustache index bbcc624bb63..30ea959ce0c 100644 --- a/src/client/test-run/index.js.mustache +++ b/src/client/test-run/index.js.mustache @@ -2,6 +2,12 @@ if (window !== window.top) return; + var origin = location.origin; + + // NOTE: location.origin doesn't exist in IE11 on Windows 10.10240 LTSB + if (!origin) + origin = location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : ''); + var testRunId = {{{testRunId}}}; var browserId = {{{browserId}}}; var selectorTimeout = {{{selectorTimeout}}}; @@ -9,11 +15,11 @@ var childWindowReadyTimeout = {{{childWindowReadyTimeout}}}; var retryTestPages = {{{retryTestPages}}}; var speed = {{{speed}}}; - var browserHeartbeatUrl = {{{browserHeartbeatUrl}}}; - var browserStatusUrl = {{{browserStatusUrl}}}; - var browserStatusDoneUrl = {{{browserStatusDoneUrl}}}; - var browserActiveWindowIdUrl = {{{browserActiveWindowIdUrl}}}; - var browserCloseWindowUrl = {{{browserCloseWindowUrl}}}; + var browserHeartbeatUrl = origin + {{{browserHeartbeatRelativeUrl}}}; + var browserStatusUrl = origin + {{{browserStatusRelativeUrl}}}; + var browserStatusDoneUrl = origin + {{{browserStatusDoneRelativeUrl}}}; + var browserActiveWindowIdUrl = origin + {{{browserActiveWindowIdUrl}}}; + var browserCloseWindowUrl = origin + {{{browserCloseWindowUrl}}}; var skipJsErrors = {{{skipJsErrors}}}; var dialogHandler = {{{dialogHandler}}}; var userAgent = {{{userAgent}}}; diff --git a/src/test-run/index.ts b/src/test-run/index.ts index 17ca288f35c..ded7e28419f 100644 --- a/src/test-run/index.ts +++ b/src/test-run/index.ts @@ -535,25 +535,25 @@ export default class TestRun extends AsyncEventEmitter { this.resolveWaitForFileDownloadingPromise = null; return Mustache.render(TEST_RUN_TEMPLATE, { - testRunId: JSON.stringify(this.session.id), - browserId: JSON.stringify(this.browserConnection.id), - browserHeartbeatUrl: JSON.stringify(this.browserConnection.heartbeatUrl), - browserStatusUrl: JSON.stringify(this.browserConnection.statusUrl), - browserStatusDoneUrl: JSON.stringify(this.browserConnection.statusDoneUrl), - browserActiveWindowIdUrl: JSON.stringify(this.browserConnection.activeWindowIdUrl), - browserCloseWindowUrl: JSON.stringify(this.browserConnection.closeWindowUrl), - userAgent: JSON.stringify(this.browserConnection.userAgent), - testName: JSON.stringify(this.test.name), - fixtureName: JSON.stringify((this.test.fixture as Fixture).name), - selectorTimeout: this.opts.selectorTimeout, - pageLoadTimeout: this.pageLoadTimeout, - childWindowReadyTimeout: CHILD_WINDOW_READY_TIMEOUT, - skipJsErrors: this.opts.skipJsErrors, - retryTestPages: this.opts.retryTestPages, - speed: this.speed, - dialogHandler: JSON.stringify(this.activeDialogHandler), - canUseDefaultWindowActions: JSON.stringify(await this.browserConnection.canUseDefaultWindowActions()), - proxyless: JSON.stringify(this.opts.proxyless), + testRunId: JSON.stringify(this.session.id), + browserId: JSON.stringify(this.browserConnection.id), + browserHeartbeatRelativeUrl: JSON.stringify(this.browserConnection.heartbeatRelativeUrl), + browserStatusRelativeUrl: JSON.stringify(this.browserConnection.statusRelativeUrl), + browserStatusDoneRelativeUrl: JSON.stringify(this.browserConnection.statusDoneRelativeUrl), + browserActiveWindowIdUrl: JSON.stringify(this.browserConnection.activeWindowIdUrl), + browserCloseWindowUrl: JSON.stringify(this.browserConnection.closeWindowUrl), + userAgent: JSON.stringify(this.browserConnection.userAgent), + testName: JSON.stringify(this.test.name), + fixtureName: JSON.stringify((this.test.fixture as Fixture).name), + selectorTimeout: this.opts.selectorTimeout, + pageLoadTimeout: this.pageLoadTimeout, + childWindowReadyTimeout: CHILD_WINDOW_READY_TIMEOUT, + skipJsErrors: this.opts.skipJsErrors, + retryTestPages: this.opts.retryTestPages, + speed: this.speed, + dialogHandler: JSON.stringify(this.activeDialogHandler), + canUseDefaultWindowActions: JSON.stringify(await this.browserConnection.canUseDefaultWindowActions()), + proxyless: JSON.stringify(this.opts.proxyless), }); }