diff --git a/docs/articles/blog/2020-12-3-v1-10-0-new-compiler-options-api-resize-and-screenshot-support-for-child-windows.md b/docs/articles/blog/2020-12-3-v1-10-0-new-compiler-options-api-resize-and-screenshot-support-for-child-windows.md new file mode 100644 index 00000000000..835e5fe85f1 --- /dev/null +++ b/docs/articles/blog/2020-12-3-v1-10-0-new-compiler-options-api-resize-and-screenshot-support-for-child-windows.md @@ -0,0 +1,128 @@ +--- +layout: post +title: "v1.10.0: New Compiler Options API, Resize and Screenshot Support for Child Windows" +permalink: /blog/:title.html +--- +# v1.10.0: New Compiler Options API, Resize and Screenshot Support for Child Windows + +...and the selector API for shadow DOM access, plus multiple bugfixes. + + + +## Enhancements + +### Window Resize and Screenshot Support for Child Windows in Chrome ([PR #5661](https://github.com/DevExpress/testcafe/pull/5661), [PR #5567](https://github.com/DevExpress/testcafe/pull/5567)) + +You can now use the following actions in Google Chrome when you switch the test context to a [child window](../documentation/guides/advanced-guides/multiple-browser-windows.md): + +* [t.maximizeWindow](../documentation/reference/test-api/testcontroller/maximizewindow.md) +* [t.resizeWindow](../documentation/reference/test-api/testcontroller/resizewindow.md) +* [t.resizeWindowToFitDevice](../documentation/reference/test-api/testcontroller/resizewindowtofitdevice.md) +* [t.takeElementScreenshot](../documentation/reference/test-api/testcontroller/takeelementscreenshot.md) +* [t.takeScreenshot](../documentation/reference/test-api/testcontroller/takescreenshot.md) + +### New API to Specify Compiler Options ([#5519](https://github.com/DevExpress/testcafe/issues/5519)) + +In previous versions, you used the following methods to specify TypeScript compiler options: + +* the [--ts-config-path](../documentation/reference/command-line-interface.md#--ts-config-path-path) command line flag + + ```sh + testcafe chrome my-tests --ts-config-path path/to/config.json + ``` + +* the [runner.tsConfigPath](../documentation/reference/testcafe-api/runner/tsconfigpath.md) method + + ```js + runner.tsConfigPath('path/to/config.json'); + ``` + +* the [tsConfigPath](../documentation/reference/configuration-file.md#tsconfigpath) configuration file property + + ```json + { + "tsConfigPath": "path/to/config.json" + } + ``` + +In v1.10.0, we introduced a new easy-to-use API that allows you to specify the compiler options in the command line, API or TestCafe configuration file, without creating a separate JSON file. The new API is also designed to accept options for more compilers (for instance, Babel) in future releases. + +The API consists of the following members: + +* the [--compiler-options](../documentation/reference/command-line-interface.md#--compiler-options-options) command line flag + + ```sh + testcafe chrome my-tests --compiler-options typescript.experimentalDecorators=true + ``` + +* the [runner.compilerOptions](../documentation/reference/testcafe-api/runner/compileroptions.md) method + + ```js + runner.compilerOptions({ + typescript: { + experimentalDecorators: true + } + }); + ``` + +* the [compilerOptions](../documentation/reference/configuration-file.md#compileroptions) configuration file property + + ```json + { + "compilerOptions": { + "typescript": { + "experimentalDecorators": true + } + } + } + ``` + +If you prefer to keep compiler settings in a configuration file, you can use the new API to specify the path to this file: + +```sh +testcafe chrome my-tests --compiler-options typescript.configPath='path/to/config.json' +``` + +In v1.10.0, you can customize TypeScript compiler options only. + +For more information, see [TypeScript and CoffeeScript](../documentation/guides/concepts/typescript-and-coffeescript.md). + +### Added a Selector Method to Access Shadow DOM ([PR #5560](https://github.com/DevExpress/testcafe/pull/5560) by [@mostlyfabulous](https://github.com/mostlyfabulous)) + +This release introduces the [selector.shadowRoot](../documentation/reference/test-api/selector/shadowroot.md) method that allows you to access and interact with the shadow DOM elements. This method returns a shadow DOM root hosted in the selector's matched element. + +```js +import { Selector } from 'testcafe' + +fixture `Target Shadow DOM elements` + .page('https://devexpress.github.io/testcafe/example') + +test('Get text within shadow tree', async t => { + const shadowRoot = Selector('div').withAttribute('id', 'shadow-host').shadowRoot(); + const paragraph = shadowRoot.child('p'); + + await t.expect(paragraph.textContent).eql('This paragraph is in the shadow tree'); +}); +``` + +Note that you should chain other [selector methods](../documentation/guides/basic-guides/select-page-elements.md#member-tables) to [selector.shadowRoot](../documentation/reference/test-api/selector/shadowroot.md) to access elements in the shadow DOM. You cannot interact with the root element (an error occurs if you specify `selector.shadowRoot` as an action's target element). + +## Bug Fixes + +* Browsers now restart correctly on BrowserStack when the connection is lost ([#5238](https://github.com/DevExpress/testcafe/issues/5238)) +* Fixed an error that occurs if a child window is opened in an `iframe` ([#5033](https://github.com/DevExpress/testcafe/issues/5033)) +* TestCafe can now switch between the child and parent windows after the parent window is reloaded ([#5463](https://github.com/DevExpress/testcafe/issues/5463), [#5597](https://github.com/DevExpress/testcafe/issues/5597)) +* Fixed an issue when touch and mouse events fired on mobile devices even though the mouse event was prevented in page code ([#5380](https://github.com/DevExpress/testcafe/issues/5380)) +* Cross-domain `iframes` are now focused correctly in Safari ([#4793](https://github.com/DevExpress/testcafe/issues/4793)) +* Fixed an excessive warning displayed when an assertion is executed in a loop or against an element returned by a `selector.xxxSibling` method ([#5449](https://github.com/DevExpress/testcafe/issues/5449), [#5389](https://github.com/DevExpress/testcafe/issues/5389)) +* Cross-domain `iframe` source links now have the correct protocol when SSL is used ([PR testcafe-hammerhead/#2478](https://github.com/DevExpress/testcafe-hammerhead/pull/2478)) +* A page error is no longer emitted if the destination server responded with the `304` status ([#5025](https://github.com/DevExpress/testcafe/issues/5025)) +* Fixed an issue when TestCafe could not authenticate websites that use MSAL ([#4834](https://github.com/DevExpress/testcafe/issues/4834)) +* The `srcdoc` attributes for `iframes` are now processed ([testcafe-hammerhead/#1237](https://github.com/DevExpress/testcafe-hammerhead/issues/1237)) +* The `authorization` header is now preserved in response headers of fetch requests ([testcafe-hammerhead/#2334](https://github.com/DevExpress/testcafe-hammerhead/issues/2334)) +* The `document.title` for an `iframe` without `src` can now be correctly obtained in Firefox ([PR testcafe-hammerhead/#2466](https://github.com/DevExpress/testcafe-hammerhead/pull/2466)) +* TestCafe UI is now displayed correctly if the tested page's body content is added dynamically ([PR testcafe-hammerhead/#2454](https://github.com/DevExpress/testcafe-hammerhead/pull/2454)) +* Service Workers now receive `fetch` events ([testcafe-hammerhead/#2412](https://github.com/DevExpress/testcafe-hammerhead/issues/2412)) +* Fixed the case of headers sent to the web app server ([testcafe-hammerhead/#2344](https://github.com/DevExpress/testcafe-hammerhead/issues/2344)) +* `Location` objects in `iframes` without `src` now contain the correct data ([PR testcafe-hammerhead/#2448](https://github.com/DevExpress/testcafe-hammerhead/pull/2448)) +* Native function wrappers are now converted to strings correctly ([testcafe-hammerhead/#2394](https://github.com/DevExpress/testcafe-hammerhead/issues/2394)) \ No newline at end of file diff --git a/docs/articles/documentation/guides/basic-guides/select-page-elements.md b/docs/articles/documentation/guides/basic-guides/select-page-elements.md index aaa90256668..04925893168 100644 --- a/docs/articles/documentation/guides/basic-guides/select-page-elements.md +++ b/docs/articles/documentation/guides/basic-guides/select-page-elements.md @@ -376,7 +376,32 @@ To set the timeout for all selectors, pass it to the [runner.run](../../referenc During the timeout, the selector is re-executed until it returns a DOM node or the timeout is exceeded. If TestCafe cannot find the corresponding node in the DOM, the test fails. -> Note that you can specify that the node returned by the selector should also be visible. To do this, use the [visibilityCheck](../../reference/test-api/selector/constructor.md#optionsvisibilitycheck) option. +> You can specify that the selector should only return visible nodes. To do this, use the [visibilityCheck](../../reference/test-api/selector/constructor.md#optionsvisibilitycheck) option. + +Selector timeouts have no effect on [Selector.exists](../../reference/test-api/selector/exists.md) and [Selector.count](../../reference/test-api/selector/count.md) properties. These properties are evaluated immediately regardless of a timeout. To apply a timeout to `exists` and `count` assertions, pass the timeout to the assertion method (`expect.ok`, `expect.eql`, etc.). + +```js +import { Selector } from 'testcafe'; + +fixture `My fixture` + .page `http://www.example.com/`; + +const timedOutSelector = Selector('#element-id', {timeout: 10000}); +const immediateSelector = Selector('#element-id'); + +test('Test timeouts', async t => { + await t.expect(timedOutSelector.exists).ok(); + await t.expect(immediateSelector.exists).ok(); + await t.expect(timedOutSelector.count).eql(1); + await t.expect(immediateSelector.count).eql(1); + //these assertions execute immediately regardless of the selector timeout + + await t.expect(immediateSelector.exists).ok({timeout: 10000}); + await t.expect(timedOutSelector.exists).ok({timeout: 10000}); + await t.expect(immediateSelector.count).eql(1, 'count elements', {timeout: 10000}); + await t.expect(timedOutSelector.count).eql(1, 'count elements', {timeout: 10000}); + //these assertions retry within the assertion timeout specified +``` ## Debug Selectors diff --git a/docs/articles/documentation/guides/continuous-integration/circleci-and-lambdatest.md b/docs/articles/documentation/guides/continuous-integration/circleci-and-lambdatest.md index ada2372bfcc..a6c184c861a 100644 --- a/docs/articles/documentation/guides/continuous-integration/circleci-and-lambdatest.md +++ b/docs/articles/documentation/guides/continuous-integration/circleci-and-lambdatest.md @@ -73,7 +73,7 @@ Property | Description `executor` | An [executor](https://circleci.com/docs/2.0/configuration-reference/#executors-requires-version-21) that defines the environment in which this job runs. This example uses the default executor from the `node` orb. The `tag` property specifies that the LTS version of Node.js should be used. `steps` | The job's [steps](https://circleci.com/docs/2.0/jobs-steps/#steps-overview). `checkout` | Checks out code from the `testcafe-ci-demo` repository. This is a pre-defined step (see [checkout](https://circleci.com/docs/2.0/configuration-reference/#checkout)). -`node/install-packages` | A step from the `node` orb that installs the project's dependencies (see [install-packages](https://circleci.com/developer/orbs/orb/circleci/node#commands-install-packages)). +`node/install-packages` | A step from the `node` orb that installs the project's dependencies (see [install-packages](https://circleci.com/developer/orbs/orb/circleci/node)). `run` | The [run](https://circleci.com/docs/2.0/configuration-reference/#run) step invokes shell commands. In this example, `run` executes the `npm test` script. `store_test_results` | Uploads test results to the specified directory (see [store_test_results](https://circleci.com/docs/2.0/configuration-reference/#store_test_results)). `workflows` | A [workflow](https://circleci.com/docs/2.0/workflows/) that runs the `test` job. diff --git a/package.json b/package.json index 0e8f48ce7c1..7b238fd3833 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "testcafe", "description": "Automated browser testing for the modern web development stack.", "license": "MIT", - "version": "1.10.0-rc.4", + "version": "1.10.0-rc.5", "author": { "name": "Developer Express Inc.", "url": "https://www.devexpress.com/" diff --git a/src/compiler/test-file/test-file-parser-base.js b/src/compiler/test-file/test-file-parser-base.js index 827f95c2eac..b67ee6ed311 100644 --- a/src/compiler/test-file/test-file-parser-base.js +++ b/src/compiler/test-file/test-file-parser-base.js @@ -6,19 +6,23 @@ import { RUNTIME_ERRORS } from '../../errors/types'; const METHODS_SPECIFYING_NAME = ['only', 'skip']; const COMPUTED_NAME_TEXT_TMP = '(line: %s)'; -function cleanLoc (loc) { +function getLoc (loc) { + // NOTE: Don't modify the Babel's parser data structure + const locCopy = Object.assign({}, loc); + // NOTE: 'fileName' and 'identifierName' fields with 'undefined' values added in the SourceLocation class constructor. // https://github.com/babel/babel/blob/d51aa6d76177b544590cdfe3868f9f4d33d8813d/packages/babel-parser/src/util/location.js#L22 // Since this is useless information, we remove it. + delete locCopy.filename; + delete locCopy.identifierName; - delete loc.filename; - delete loc.identifierName; + return locCopy; } export class Fixture { constructor (name, start, end, loc, meta) { this.name = name; - this.loc = cleanLoc(loc); + this.loc = getLoc(loc); this.start = start; this.end = end; this.meta = meta; @@ -29,7 +33,7 @@ export class Fixture { export class Test { constructor (name, start, end, loc, meta) { this.name = name; - this.loc = cleanLoc(loc); + this.loc = getLoc(loc); this.start = start; this.end = end; this.meta = meta; diff --git a/test/server/data/test-suites/typescript-defs/test-controller.ts b/test/server/data/test-suites/typescript-defs/test-controller.ts index 73b92290753..000d9ccb7e7 100644 --- a/test/server/data/test-suites/typescript-defs/test-controller.ts +++ b/test/server/data/test-suites/typescript-defs/test-controller.ts @@ -794,3 +794,11 @@ test('messages formatting', async t => { .expect(messages.warn.length).eql(0) .expect(messages.error.length).eql(0); }); + +test('action result coercion', async t => { + function test (): Promise { + return t.expect(1).ok(); + } + + test(); +}); diff --git a/test/server/data/test-suites/typescript-testcafe-scripts-defs/test-controller.ts b/test/server/data/test-suites/typescript-testcafe-scripts-defs/test-controller.ts index f0b21670be5..24139f3eda1 100644 --- a/test/server/data/test-suites/typescript-testcafe-scripts-defs/test-controller.ts +++ b/test/server/data/test-suites/typescript-testcafe-scripts-defs/test-controller.ts @@ -789,3 +789,11 @@ const getInputValue = ClientFunction(() => (document.getElemen .expect(messages.warn.length).eql(0) .expect(messages.error.length).eql(0); })(); + +(async () => { + function test (): Promise { + return t.expect(1).ok(); + } + + test(); +})(); diff --git a/test/server/parse-fixture-test.js b/test/server/parse-fixture-test.js index bd44f953999..d951b39eecf 100644 --- a/test/server/parse-fixture-test.js +++ b/test/server/parse-fixture-test.js @@ -40,6 +40,14 @@ function testFixtureParser (dir, expectedStructure, fileParser, codeParser) { .then(function (structure) { expect(structure).eql(expected); expect(codeParser(fileContent)).eql(expected); + + Object.values(structure).forEach(fixture => { + expect(fixture.loc).not.undefined; + + fixture.tests.forEach(test => { + expect(test.loc).not.undefined; + }); + }); }); }); diff --git a/ts-defs-src/test-api/test-controller.d.ts b/ts-defs-src/test-api/test-controller.d.ts index 5e1db0d8b68..ff7a35ec767 100644 --- a/ts-defs-src/test-api/test-controller.d.ts +++ b/ts-defs-src/test-api/test-controller.d.ts @@ -414,9 +414,9 @@ interface TestController { removeRequestHooks(...hooks: object[]): TestControllerPromise; } -interface TestControllerPromise extends TestController, Promise { +interface TestControllerPromise extends TestController, Promise { } -interface WindowDescriptorPromise extends TestControllerPromise, Promise { +interface WindowDescriptorPromise extends TestControllerPromise { }