diff --git a/src/api/test-page-url.ts b/src/api/test-page-url.ts index 0cd64c05943..6370134a5a8 100644 --- a/src/api/test-page-url.ts +++ b/src/api/test-page-url.ts @@ -44,7 +44,7 @@ export function getUrl (url: string, base?: URL): string { return resolveRelativeUrl(url, base); } - else if (isAbsolute(url)) + if (isAbsolute(url)) return pathToFileURL(url).toString(); return ensureProtocol(url); diff --git a/src/errors/runtime/templates.js b/src/errors/runtime/templates.js index 5bda568039b..e9ab2b3769e 100644 --- a/src/errors/runtime/templates.js +++ b/src/errors/runtime/templates.js @@ -128,7 +128,7 @@ export default { `For more information, see ${DOCUMENTATION_LINKS.HEADLESS_MODE}`, [RUNTIME_ERRORS.uncaughtErrorInReporter]: 'The "{methodName}" method of the "{reporterName}" reporter produced an uncaught error. Error details:\n{originalError}', - [RUNTIME_ERRORS.roleInitializedWithRelativeUrl]: 'You cannot specify relative login page URLs in the Role constructor. Use an absolute URL.', + [RUNTIME_ERRORS.roleInitializedWithRelativeUrl]: 'Your Role includes a relative login page URL, but the "baseUrl" option is not set.\nUse an absolute URL or add the baseUrl option to your configuration file or CLI launch string.', [RUNTIME_ERRORS.typeScriptCompilerLoadingError]: 'Cannot load the TypeScript compiler.\n{originErrorMessage}.', [RUNTIME_ERRORS.cannotCustomizeSpecifiedCompilers]: 'You cannot specify options for the {noncustomizableCompilerList} compiler{suffix}.', diff --git a/src/role/index.ts b/src/role/index.ts index 68e0971405c..2c501754dca 100644 --- a/src/role/index.ts +++ b/src/role/index.ts @@ -1,6 +1,5 @@ import { assertType, is } from '../errors/runtime/type-assertions'; import wrapTestFunction from '../api/wrap-test-function'; -import { getUrl, assertRoleUrl } from '../api/test-page-url'; import Role from './role'; interface RoleOptions { @@ -15,9 +14,6 @@ export function createRole (loginUrl: string, initFn: Function, options: RoleOpt if (options.preserveUrl !== void 0) assertType(is.boolean, 'Role', 'The "preserveUrl" option', options.preserveUrl); - assertRoleUrl(loginUrl, 'Role'); - - loginUrl = getUrl(loginUrl); initFn = wrapTestFunction(initFn); return new Role(loginUrl, initFn, options); diff --git a/src/role/role.ts b/src/role/role.ts index 55b6bfad0db..14109e1081c 100644 --- a/src/role/role.ts +++ b/src/role/role.ts @@ -5,6 +5,7 @@ import roleMarker from './marker-symbol'; import { nanoid } from 'nanoid'; import TestRun from '../test-run'; import TestCafeErrorList from '../errors/error-list'; +import { getUrl, assertRoleUrl } from '../api/test-page-url'; export interface RedirectUrl { [testId: string]: string; @@ -63,8 +64,18 @@ export default class Role extends EventEmitter { } } + private _prepareLoginUrl (loginUrl: string, baseUrl: string): string { + if (!baseUrl) + assertRoleUrl(loginUrl, 'role'); + + return getUrl(loginUrl, baseUrl ? new URL(baseUrl) : void 0); + } + private async _switchToCleanRun (testRun: TestRun): Promise { try { + if (this.loginUrl) + this.loginUrl = this._prepareLoginUrl(this.loginUrl, testRun.baseUrl); + await testRun.switchToCleanRun(this.loginUrl as string); } catch (err: any) { diff --git a/src/test-run/index.ts b/src/test-run/index.ts index 5cf615a7596..a228fa975fb 100644 --- a/src/test-run/index.ts +++ b/src/test-run/index.ts @@ -839,6 +839,10 @@ export default class TestRun extends AsyncEventEmitter { return this.driverTaskQueue[0]; } + public get baseUrl (): string { + return this.opts.baseUrl as string || ''; + } + private _resolveCurrentDriverTask (result?: unknown): void { this.currentDriverTask.resolve(result); this.driverTaskQueue.shift(); diff --git a/test/functional/fixtures/api/es-next/generic-errors/test.js b/test/functional/fixtures/api/es-next/generic-errors/test.js index a769bda00e3..fc2531ddd1e 100644 --- a/test/functional/fixtures/api/es-next/generic-errors/test.js +++ b/test/functional/fixtures/api/es-next/generic-errors/test.js @@ -16,8 +16,8 @@ describe('[API] Generic errors', function () { it('Should handle the relative path in the Role constructor', () => { return runTests('testcafe-fixtures/role-initialized-with-relative-url.js', null, { shouldFail: true, only: 'chrome' }) - .catch(err => { - expect(err.message).contains('You cannot specify relative login page URLs in the Role constructor. Use an absolute URL.'); + .catch(errs => { + expect(errs[0]).contains('Your Role includes a relative login page URL, but the "baseUrl" option is not set.'); }); }); diff --git a/test/functional/fixtures/api/es-next/roles/test.js b/test/functional/fixtures/api/es-next/roles/test.js index 459c9292770..54415a394dc 100644 --- a/test/functional/fixtures/api/es-next/roles/test.js +++ b/test/functional/fixtures/api/es-next/roles/test.js @@ -112,4 +112,30 @@ const isRemoteTask = config.currentEnvironmentName === config.testingEnvironment }); }); }); + + describe('URL is Role constructor', function () { + it('Should throw "error in role initializer" without baseUrl and with relative path Role', () => { + return runTests( + './testcafe-fixtures/role-with-baseurl-test.js', + 'Should throw error in role initializer without baseUrl and with relative path Role', + { shouldFail: true }) + .catch(errs => { + expect(errs[0]).contains('Your Role includes a relative login page URL, but the "baseUrl" option is not set.'); + }); + }); + + it('Should pass if `baseUrl` is set with relative path Role', () => { + return runTests( + './testcafe-fixtures/role-with-baseurl-test.js', + 'Use role with relative path and baseUrl', + { baseUrl: 'http://localhost:3000/' }); + }); + + it('Should pass if `baseUrl` is set with absolute path Role', () => { + return runTests( + './testcafe-fixtures/role-with-baseurl-test.js', + 'Use role with absolute path and baseUrl', + { baseUrl: 'http://localhost:3000/' }); + }); + }); }); diff --git a/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/role-with-baseurl-test.js b/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/role-with-baseurl-test.js new file mode 100644 index 00000000000..7154ae9c0a5 --- /dev/null +++ b/test/functional/fixtures/api/es-next/roles/testcafe-fixtures/role-with-baseurl-test.js @@ -0,0 +1,25 @@ +import { Role, ClientFunction } from 'testcafe'; + +fixture `Roles with absolute/relative "loginUrl"`; + +const getLocation = ClientFunction(() => document.location.toString()); + +const roleAbs = Role('http://localhost:3000/fixtures/api/es-next/roles/pages/index.html', async t => { + await t.expect(getLocation()).eql('http://localhost:3000/fixtures/api/es-next/roles/pages/index.html'); +}, { preserveUrl: true }); + +const roleRel = Role('./fixtures/api/es-next/roles/pages/index.html', async t => { + await t.expect(getLocation()).eql('http://localhost:3000/fixtures/api/es-next/roles/pages/index.html'); +}, { preserveUrl: true }); + +test('Should throw error in role initializer without baseUrl and with relative path Role', async t => { + await t.useRole(roleRel); +}); + +test('Use role with relative path and baseUrl', async t => { + await t.useRole(roleRel); +}); + +test('Use role with absolute path and baseUrl', async t => { + await t.useRole(roleAbs); +});