Skip to content

Commit

Permalink
Multiple windows support (#2988)
Browse files Browse the repository at this point in the history
* [wip] preparations for multiple windows

* multiple windows
  • Loading branch information
AlexKamaev authored Dec 18, 2023
1 parent ae88ef6 commit df9c497
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 15 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "testcafe-hammerhead",
"description": "A powerful web-proxy used as a core for the TestCafe testing framework (https://github.com/DevExpress/testcafe).",
"version": "31.6.4",
"version": "31.7.0",
"homepage": "https://github.com/DevExpress/testcafe-hammerhead",
"bugs": {
"url": "https://github.com/DevExpress/testcafe-hammerhead/issues"
Expand Down
25 changes: 15 additions & 10 deletions src/client/sandbox/child-window/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import getTopOpenerWindow from '../../utils/get-top-opener-window';
import nextTick from '../../utils/next-tick';
import { version, isSafari } from '../../utils/browser';
import getCorrectedTargetForSinglePageMode from '../../utils/get-corrected-target-for-single-page-mode';

import isTargetBlank from '../../../utils/is-target-blank';

const DEFAULT_WINDOW_PARAMETERS = 'width=500px, height=500px';
const STORE_CHILD_WINDOW_CMD = 'hammerhead|command|store-child-window';
Expand Down Expand Up @@ -48,18 +48,18 @@ export default class ChildWindowSandbox extends SandboxBase {
target = target.toLowerCase();

if (isKeywordTarget(target))
return target === '_blank';
return isTargetBlank(target);

return !windowsStorage.findByName(target);
}

private _openUrlInNewWindow (url: string, windowName?: string, windowParams?: string, window?: Window): OpenedWindowInfo | null {
private _openUrlInNewWindow (url: string, windowName?: string, windowParams?: string, window?: Window, form?: HTMLFormElement): OpenedWindowInfo | null {
const windowId = getRandomInt16Value().toString();

windowParams = windowParams || DEFAULT_WINDOW_PARAMETERS;
windowName = windowName || windowId;

const newPageUrl = settings.get().nativeAutomation ? url : urlUtils.getPageProxyUrl(url, windowId);
const newPageUrl = urlUtils.getPageProxyUrl(url, windowId, settings.get().nativeAutomation);
const targetWindow = window || this.window;
const beforeWindowOpenedEventArgs = { isPrevented: false };

Expand All @@ -68,11 +68,12 @@ export default class ChildWindowSandbox extends SandboxBase {
if (beforeWindowOpenedEventArgs.isPrevented)
return null;

const openedWindow = nativeMethods.windowOpen.call(targetWindow, newPageUrl, windowName, windowParams);
const startPageUrl = settings.get().nativeAutomation ? SPECIAL_BLANK_PAGE : newPageUrl;
const openedWindow = nativeMethods.windowOpen.call(targetWindow, startPageUrl, windowName, windowParams);

this._tryToStoreChildWindow(openedWindow, getTopOpenerWindow());

this.emit(this.WINDOW_OPENED_EVENT, { windowId, window: openedWindow });
this.emit(this.WINDOW_OPENED_EVENT, { windowId, window: openedWindow, windowName, pageUrl: newPageUrl, form });

return { windowId, wnd: openedWindow };
}
Expand Down Expand Up @@ -160,7 +161,7 @@ export default class ChildWindowSandbox extends SandboxBase {
const [url, target, parameters] = args;

if (settings.get().allowMultipleWindows && ChildWindowSandbox._shouldOpenInNewWindow(target, DefaultTarget.windowOpen)) {
const openedWindowInfo = this._openUrlInNewWindow(url, target, parameters, window);
const openedWindowInfo = this._openUrlInNewWindow(url, isTargetBlank(target) ? void 0 : target, parameters, window);

return openedWindowInfo?.wnd;
}
Expand Down Expand Up @@ -204,18 +205,22 @@ export default class ChildWindowSandbox extends SandboxBase {
!ChildWindowSandbox._shouldOpenInNewWindowOnElementAction(form, DefaultTarget.form))
return;

const aboutBlankUrl = urlUtils.getProxyUrl(SPECIAL_BLANK_PAGE);
const openedInfo = this._openUrlInNewWindow(aboutBlankUrl);
const isNativeAutomation = settings.get().nativeAutomation;
const aboutBlankUrl = urlUtils.getProxyUrl(SPECIAL_BLANK_PAGE, void 0, isNativeAutomation);
const openedInfo = this._openUrlInNewWindow(aboutBlankUrl, void 0, void 0, void 0, form);

if (!openedInfo)
return;

const formAction = nativeMethods.formActionGetter.call(form);
const newWindowUrl = urlUtils.getPageProxyUrl(formAction, openedInfo.windowId);
const newWindowUrl = urlUtils.getPageProxyUrl(formAction, openedInfo.windowId, isNativeAutomation);

nativeMethods.formActionSetter.call(form, newWindowUrl);
nativeMethods.formTargetSetter.call(form, openedInfo.windowId);

if (isNativeAutomation)
e.preventDefault();

// TODO: On hammerhead start we need to clean up the window.name
// It's necessary for form submit.
// Also we need clean up the form target to the original value.
Expand Down
4 changes: 2 additions & 2 deletions src/client/utils/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export function overrideGetCrossDomainIframeProxyUrl (func: typeof getCrossDomai
getCrossDomainIframeProxyUrl = func;
}

export function getPageProxyUrl (url: string, windowId: string): string {
export function getPageProxyUrl (url: string, windowId: string, nativeAutomation?: boolean): string {
const parsedProxyUrl = parseProxyUrl(url);
let resourceType = null;

Expand All @@ -228,7 +228,7 @@ export function getPageProxyUrl (url: string, windowId: string): string {
const isCrossDomainUrl = !destLocation.sameOriginCheck(destLocation.getLocation(), url);
const proxyPort = isCrossDomainUrl ? settings.get().crossDomainProxyPort : location.port.toString(); // eslint-disable-line no-restricted-properties

return getProxyUrl(url, { windowId, proxyPort, resourceType });
return getProxyUrl(url, { windowId, proxyPort, resourceType }, nativeAutomation);
}

export function getCrossDomainProxyPort (proxyPort: string) {
Expand Down
4 changes: 2 additions & 2 deletions src/session/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export default abstract class Session extends EventEmitter {
let payloadScript = '';

if (withPayload)
payloadScript = isIframe ? await this.getIframePayloadScript(false) : await this.getPayloadScript();
payloadScript = isIframe ? await this.getIframePayloadScript(false) : await this.getPayloadScript(windowId);

const taskScript = this._fillTaskScriptTemplate({
serverInfo,
Expand Down Expand Up @@ -329,7 +329,7 @@ export default abstract class Session extends EventEmitter {
}

abstract getIframePayloadScript (iframeWithoutSrc: boolean): Promise<string>;
abstract getPayloadScript (): Promise<string>;
abstract getPayloadScript (windowId?: string): Promise<string>;
abstract handleFileDownload (): void;
abstract handleAttachment ({ isOpenedInNewWindow }: { isOpenedInNewWindow: boolean }): void;
abstract handlePageError (ctx: RequestPipelineContext, err: string): void;
Expand Down
3 changes: 3 additions & 0 deletions src/utils/is-target-blank.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function (value = ''): boolean {
return value === '_blank';
}

0 comments on commit df9c497

Please sign in to comment.