Skip to content

Commit

Permalink
native automation: disable multiple windows if native automation is e…
Browse files Browse the repository at this point in the history
…nabled (#7618)
  • Loading branch information
AlexKamaev authored Apr 13, 2023
1 parent ab760e0 commit 3a6ff5d
Show file tree
Hide file tree
Showing 18 changed files with 227 additions and 110 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
"source-map-support": "^0.5.16",
"strip-bom": "^2.0.0",
"testcafe-browser-tools": "2.0.23",
"testcafe-hammerhead": "31.0.0",
"testcafe-hammerhead": "31.1.0",
"testcafe-legacy-api": "5.1.6",
"testcafe-reporter-dashboard": "^0.2.10",
"testcafe-reporter-json": "^2.1.0",
Expand Down
4 changes: 4 additions & 0 deletions src/api/test-controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import TestRunProxy from '../../services/compiler/test-run-proxy';
import {
MultipleWindowsModeIsDisabledError,
MultipleWindowsModeIsNotAvailableInRemoteBrowserError,
MultipleWindowsModeIsNotSupportedInNativeAutomationModeError,
} from '../../errors/test-run';

import { AssertionCommand } from '../../test-run/commands/assertion';
Expand Down Expand Up @@ -187,6 +188,9 @@ export default class TestController {
_validateMultipleWindowCommand (apiMethodName) {
const { disableMultipleWindows, activeWindowId } = this.testRun;

if (this.testRun.isNativeAutomation())
throw new MultipleWindowsModeIsNotSupportedInNativeAutomationModeError(apiMethodName);

if (disableMultipleWindows)
throw new MultipleWindowsModeIsDisabledError(apiMethodName);

Expand Down
14 changes: 14 additions & 0 deletions src/client/driver/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
CannotCloseWindowWithoutParentError,
WindowNotFoundError,
CannotRestoreChildWindowError,
MultipleWindowsModeIsNotSupportedInNativeAutomationModeError,
} from '../../shared/errors';


Expand Down Expand Up @@ -216,6 +217,7 @@ export default class Driver extends serviceUtils.EventEmitter {
hammerhead.on(hammerhead.EVENTS.unhandledRejection, err => this._onJsError(err));
hammerhead.on(hammerhead.EVENTS.consoleMethCalled, e => this._onConsoleMessage(e));
hammerhead.on(hammerhead.EVENTS.beforeFormSubmit, e => this._onFormSubmit(e));
hammerhead.on(hammerhead.EVENTS.beforeWindowOpened, e => this._onBeforeChildWindowOpened(e));
hammerhead.on(hammerhead.EVENTS.windowOpened, e => this._onChildWindowOpened(e));

this.setCustomCommandHandlers(COMMAND_TYPE.unlockPage, () => this._unlockPageAfterTestIsDone());
Expand Down Expand Up @@ -411,6 +413,18 @@ export default class Driver extends serviceUtils.EventEmitter {
});
}

_onBeforeChildWindowOpened (e) {
if (!this.options.nativeAutomation)
return;

this._onReady(new DriverStatus({
isCommandResult: true,
executionError: new MultipleWindowsModeIsNotSupportedInNativeAutomationModeError(),
}));

e.isPrevented = true;
}

_onChildWindowOpened (e) {
this._addChildWindowDriverLink(e);
this._switchToChildWindow(e.windowId);
Expand Down
4 changes: 4 additions & 0 deletions src/errors/test-run/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,10 @@ export default {
Multi-window mode is supported only in locally-installed Chrome, Chromium, Edge 84+ and Firefox. Run tests in these browsers to use the "${err.methodName}" method.
`,

[TEST_RUN_ERRORS.multipleWindowsModeIsNotSupportedInNativeAutomationError]: () => `
The Native Automation mode does not support the use of multiple browser windows. Remove the "native automation" option to continue.
`,

[TEST_RUN_ERRORS.cannotCloseWindowWithoutParent]: () => `
Cannot close the window because it does not have a parent. The parent window was closed or you are attempting to close the root browser window where tests were launched.
`,
Expand Down
197 changes: 99 additions & 98 deletions src/errors/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,104 +4,105 @@
// -------------------------------------------------------------

export const TEST_RUN_ERRORS = {
uncaughtErrorOnPage: 'E1',
uncaughtErrorInTestCode: 'E2',
uncaughtNonErrorObjectInTestCode: 'E3',
uncaughtErrorInClientFunctionCode: 'E4',
uncaughtErrorInCustomDOMPropertyCode: 'E5',
unhandledPromiseRejection: 'E6',
uncaughtException: 'E7',
missingAwaitError: 'E8',
actionIntegerOptionError: 'E9',
actionPositiveIntegerOptionError: 'E10',
actionBooleanOptionError: 'E11',
actionSpeedOptionError: 'E12',
actionOptionsTypeError: 'E14',
actionBooleanArgumentError: 'E15',
actionStringArgumentError: 'E16',
actionNullableStringArgumentError: 'E17',
actionStringOrStringArrayArgumentError: 'E18',
actionStringArrayElementError: 'E19',
actionIntegerArgumentError: 'E20',
actionRoleArgumentError: 'E21',
actionPositiveIntegerArgumentError: 'E22',
actionSelectorError: 'E23',
actionElementNotFoundError: 'E24',
actionElementIsInvisibleError: 'E26',
actionSelectorMatchesWrongNodeTypeError: 'E27',
actionAdditionalElementNotFoundError: 'E28',
actionAdditionalElementIsInvisibleError: 'E29',
actionAdditionalSelectorMatchesWrongNodeTypeError: 'E30',
actionElementNonEditableError: 'E31',
actionElementNotTextAreaError: 'E32',
actionElementNonContentEditableError: 'E33',
actionElementIsNotFileInputError: 'E34',
actionRootContainerNotFoundError: 'E35',
actionIncorrectKeysError: 'E36',
actionCannotFindFileToUploadError: 'E37',
actionUnsupportedDeviceTypeError: 'E38',
actionIframeIsNotLoadedError: 'E39',
actionElementNotIframeError: 'E40',
actionInvalidScrollTargetError: 'E41',
currentIframeIsNotLoadedError: 'E42',
currentIframeNotFoundError: 'E43',
currentIframeIsInvisibleError: 'E44',
nativeDialogNotHandledError: 'E45',
uncaughtErrorInNativeDialogHandler: 'E46',
setTestSpeedArgumentError: 'E47',
setNativeDialogHandlerCodeWrongTypeError: 'E48',
clientFunctionExecutionInterruptionError: 'E49',
domNodeClientFunctionResultError: 'E50',
invalidSelectorResultError: 'E51',
cannotObtainInfoForElementSpecifiedBySelectorError: 'E52',
externalAssertionLibraryError: 'E53',
pageLoadError: 'E54',
windowDimensionsOverflowError: 'E55',
forbiddenCharactersInScreenshotPathError: 'E56',
invalidElementScreenshotDimensionsError: 'E57',
roleSwitchInRoleInitializerError: 'E58',
assertionExecutableArgumentError: 'E59',
assertionWithoutMethodCallError: 'E60',
assertionUnawaitedPromiseError: 'E61',
requestHookNotImplementedError: 'E62',
requestHookUnhandledError: 'E63',
uncaughtErrorInCustomClientScriptCode: 'E64',
uncaughtErrorInCustomClientScriptCodeLoadedFromModule: 'E65',
uncaughtErrorInCustomScript: 'E66',
uncaughtTestCafeErrorInCustomScript: 'E67',
childWindowIsNotLoadedError: 'E68',
childWindowNotFoundError: 'E69',
cannotSwitchToWindowError: 'E70',
closeChildWindowError: 'E71',
childWindowClosedBeforeSwitchingError: 'E72',
cannotCloseWindowWithChildrenError: 'E73',
targetWindowNotFoundError: 'E74',
parentWindowNotFoundError: 'E76',
previousWindowNotFoundError: 'E77',
switchToWindowPredicateError: 'E78',
actionFunctionArgumentError: 'E79',
multipleWindowsModeIsDisabledError: 'E80',
multipleWindowsModeIsNotSupportedInRemoteBrowserError: 'E81',
cannotCloseWindowWithoutParent: 'E82',
cannotRestoreChildWindowError: 'E83',
executionTimeoutExceeded: 'E84',
actionRequiredCookieArguments: 'E85',
actionCookieArgumentError: 'E86',
actionCookieArgumentsError: 'E87',
actionUrlCookieArgumentError: 'E88',
actionUrlsCookieArgumentError: 'E89',
actionStringOptionError: 'E90',
actionDateOptionError: 'E91',
actionNumberOptionError: 'E92',
actionUrlOptionError: 'E93',
actionUrlSearchParamsOptionError: 'E94',
actionObjectOptionError: 'E95',
actionUrlArgumentError: 'E96',
actionStringOrRegexOptionError: 'E97',
actionSkipJsErrorsArgumentError: 'E98',
actionFunctionOptionError: 'E99',
actionInvalidObjectPropertyError: 'E100',
actionElementIsNotTargetError: 'E101',
uncaughtErrorOnPage: 'E1',
uncaughtErrorInTestCode: 'E2',
uncaughtNonErrorObjectInTestCode: 'E3',
uncaughtErrorInClientFunctionCode: 'E4',
uncaughtErrorInCustomDOMPropertyCode: 'E5',
unhandledPromiseRejection: 'E6',
uncaughtException: 'E7',
missingAwaitError: 'E8',
actionIntegerOptionError: 'E9',
actionPositiveIntegerOptionError: 'E10',
actionBooleanOptionError: 'E11',
actionSpeedOptionError: 'E12',
actionOptionsTypeError: 'E14',
actionBooleanArgumentError: 'E15',
actionStringArgumentError: 'E16',
actionNullableStringArgumentError: 'E17',
actionStringOrStringArrayArgumentError: 'E18',
actionStringArrayElementError: 'E19',
actionIntegerArgumentError: 'E20',
actionRoleArgumentError: 'E21',
actionPositiveIntegerArgumentError: 'E22',
actionSelectorError: 'E23',
actionElementNotFoundError: 'E24',
actionElementIsInvisibleError: 'E26',
actionSelectorMatchesWrongNodeTypeError: 'E27',
actionAdditionalElementNotFoundError: 'E28',
actionAdditionalElementIsInvisibleError: 'E29',
actionAdditionalSelectorMatchesWrongNodeTypeError: 'E30',
actionElementNonEditableError: 'E31',
actionElementNotTextAreaError: 'E32',
actionElementNonContentEditableError: 'E33',
actionElementIsNotFileInputError: 'E34',
actionRootContainerNotFoundError: 'E35',
actionIncorrectKeysError: 'E36',
actionCannotFindFileToUploadError: 'E37',
actionUnsupportedDeviceTypeError: 'E38',
actionIframeIsNotLoadedError: 'E39',
actionElementNotIframeError: 'E40',
actionInvalidScrollTargetError: 'E41',
currentIframeIsNotLoadedError: 'E42',
currentIframeNotFoundError: 'E43',
currentIframeIsInvisibleError: 'E44',
nativeDialogNotHandledError: 'E45',
uncaughtErrorInNativeDialogHandler: 'E46',
setTestSpeedArgumentError: 'E47',
setNativeDialogHandlerCodeWrongTypeError: 'E48',
clientFunctionExecutionInterruptionError: 'E49',
domNodeClientFunctionResultError: 'E50',
invalidSelectorResultError: 'E51',
cannotObtainInfoForElementSpecifiedBySelectorError: 'E52',
externalAssertionLibraryError: 'E53',
pageLoadError: 'E54',
windowDimensionsOverflowError: 'E55',
forbiddenCharactersInScreenshotPathError: 'E56',
invalidElementScreenshotDimensionsError: 'E57',
roleSwitchInRoleInitializerError: 'E58',
assertionExecutableArgumentError: 'E59',
assertionWithoutMethodCallError: 'E60',
assertionUnawaitedPromiseError: 'E61',
requestHookNotImplementedError: 'E62',
requestHookUnhandledError: 'E63',
uncaughtErrorInCustomClientScriptCode: 'E64',
uncaughtErrorInCustomClientScriptCodeLoadedFromModule: 'E65',
uncaughtErrorInCustomScript: 'E66',
uncaughtTestCafeErrorInCustomScript: 'E67',
childWindowIsNotLoadedError: 'E68',
childWindowNotFoundError: 'E69',
cannotSwitchToWindowError: 'E70',
closeChildWindowError: 'E71',
childWindowClosedBeforeSwitchingError: 'E72',
cannotCloseWindowWithChildrenError: 'E73',
targetWindowNotFoundError: 'E74',
parentWindowNotFoundError: 'E76',
previousWindowNotFoundError: 'E77',
switchToWindowPredicateError: 'E78',
actionFunctionArgumentError: 'E79',
multipleWindowsModeIsDisabledError: 'E80',
multipleWindowsModeIsNotSupportedInRemoteBrowserError: 'E81',
cannotCloseWindowWithoutParent: 'E82',
cannotRestoreChildWindowError: 'E83',
executionTimeoutExceeded: 'E84',
actionRequiredCookieArguments: 'E85',
actionCookieArgumentError: 'E86',
actionCookieArgumentsError: 'E87',
actionUrlCookieArgumentError: 'E88',
actionUrlsCookieArgumentError: 'E89',
actionStringOptionError: 'E90',
actionDateOptionError: 'E91',
actionNumberOptionError: 'E92',
actionUrlOptionError: 'E93',
actionUrlSearchParamsOptionError: 'E94',
actionObjectOptionError: 'E95',
actionUrlArgumentError: 'E96',
actionStringOrRegexOptionError: 'E97',
actionSkipJsErrorsArgumentError: 'E98',
actionFunctionOptionError: 'E99',
actionInvalidObjectPropertyError: 'E100',
actionElementIsNotTargetError: 'E101',
multipleWindowsModeIsNotSupportedInNativeAutomationError: 'E102',
};

export const RUNTIME_ERRORS = {
Expand Down
4 changes: 2 additions & 2 deletions src/services/compiler/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,10 +467,10 @@ export default class CompilerHost extends AsyncEventEmitter implements CompilerP
await this.emit('removeRequestEventListeners', { rules });
}

public async initializeTestRunData ({ testRunId, testId, browser, activeWindowId, messageBus }: InitializeTestRunDataArguments): Promise<void> {
public async initializeTestRunData ({ testRunId, testId, browser, activeWindowId, messageBus, isNativeAutomation }: InitializeTestRunDataArguments): Promise<void> {
const { proxy } = await this._getRuntime();

return proxy.call(this.initializeTestRunData, { testRunId, testId, browser, activeWindowId, messageBus });
return proxy.call(this.initializeTestRunData, { testRunId, testId, browser, activeWindowId, messageBus, isNativeAutomation });
}

public async getAssertionActualValue ({ testRunId, commandId }: CommandLocator): Promise<unknown> {
Expand Down
1 change: 1 addition & 0 deletions src/services/compiler/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export interface InitializeTestRunDataArguments extends TestRunLocator {
browser: Browser;
activeWindowId: string | null;
messageBus?: MessageBus;
isNativeAutomation: boolean;
}

export interface RoleLocator {
Expand Down
8 changes: 5 additions & 3 deletions src/services/compiler/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ interface InitTestRunProxyData {
browser: Browser;
activeWindowId: string | null;
messageBus?: MessageBus;
isNativeAutomation: boolean;
}

class CompilerService implements CompilerProtocol {
Expand Down Expand Up @@ -266,7 +267,7 @@ class CompilerService implements CompilerProtocol {
};
}

private _initializeTestRunProxy ({ testRunId, test, browser, activeWindowId, messageBus }: InitTestRunProxyData): void {
private _initializeTestRunProxy ({ testRunId, test, browser, activeWindowId, messageBus, isNativeAutomation }: InitTestRunProxyData): void {
const testRunProxy = new TestRunProxy({
dispatcher: this,
id: testRunId,
Expand All @@ -275,6 +276,7 @@ class CompilerService implements CompilerProtocol {
browser,
activeWindowId,
messageBus,
isNativeAutomation,
});

this.state.testRuns[testRunId] = testRunProxy;
Expand Down Expand Up @@ -427,15 +429,15 @@ class CompilerService implements CompilerProtocol {
return await this.proxy.call(this.removeRequestEventListeners, { rules });
}

public async initializeTestRunData ({ testRunId, testId, browser, activeWindowId, messageBus }: InitializeTestRunDataArguments): Promise<void> {
public async initializeTestRunData ({ testRunId, testId, browser, activeWindowId, messageBus, isNativeAutomation }: InitializeTestRunDataArguments): Promise<void> {
// NOTE: In case of raising an error into ReporterPluginHost methods,
// TestRun has time to start.
const test = this.state.units[testId] as Test;

if (!test)
return;

this._initializeTestRunProxy({ testRunId, test, browser, activeWindowId, messageBus });
this._initializeTestRunProxy({ testRunId, test, browser, activeWindowId, messageBus, isNativeAutomation });
this._initializeFixtureCtx(test);
}

Expand Down
Loading

0 comments on commit 3a6ff5d

Please sign in to comment.