From 199c9831d7ae156e80f6730501e258e337a1c52b Mon Sep 17 00:00:00 2001 From: Matt Schile Date: Mon, 17 Jan 2022 22:42:26 -0700 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit d8fa85d3dc9888c3fe65af9b4fb8291939f5675d Author: Chris Breiding Date: Fri Jan 14 09:48:43 2022 -0500 chore: Fix a couple multi-domain bugs (#19698) commit 2e5fbad2c050b81ce75575802578c307808d61b5 Author: Chris Breiding Date: Thu Jan 13 11:44:35 2022 -0500 fix types issue commit cc08d12c771d722c4e79a058b65ea09d4d737117 Author: Chris Breiding Date: Thu Jan 13 09:56:31 2022 -0500 fix issues after merge commit 8e0770f23524e31f3d84f88d1e95d900e35c7999 Merge: 2ee98938cd d87711e21b Author: Chris Breiding Date: Thu Jan 13 09:31:25 2022 -0500 Merge branch 'develop' into feature-multidomain commit d87711e21bf1665294ff1b6c2a1d15e3edb9c932 Merge: 576519e465 f22e3ca79c Author: Brian Barrow Date: Wed Jan 12 16:41:35 2022 +0000 Merge branch 'master' into develop commit f22e3ca79cf626b0fdf33e52e5471f09f85dcc47 Author: Brian Barrow Date: Wed Jan 12 09:40:48 2022 -0700 Fixed Vue README links in Global Components section (#19550) commit 576519e465af28e2ed3f0b2c181fbe8f17e348a2 Author: Pascal Gafner Date: Wed Jan 12 15:52:26 2022 +0100 fix: use util.getEnv to handle environment variables set with npm (#19560) Co-authored-by: Matt Henkes Co-authored-by: Emily Rohrbough commit 0382768981bd68695ca498cfb52107c4748c9489 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Tue Jan 11 21:35:43 2022 +0000 chore(deps): update dependency electron to v15.3.4 🌟 (#19657) Co-authored-by: Renovate Bot commit 1305cca9f19a1dd15851a3830209ba7597404777 Author: Lachlan Miller Date: Wed Jan 12 07:10:14 2022 +1000 fix: rename specs to correctly match convention (#19641) * fix: rename specs to correctly match convention * Remove underscore from TESTFILES glob pattern Co-authored-by: Zach Bloomquist commit c45a24044ce1b0d3e012cc6fd0a0e5b5c8423295 Author: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Date: Tue Jan 11 12:59:14 2022 -0800 fix(deps): update dependency node-forge to v1 [security] (#19635) Co-authored-by: Renovate Bot commit ea531b7840171a211905bad530875e2af15c11f2 Author: Kukhyeon Heo Date: Wed Jan 12 00:37:05 2022 +0900 chore: remove pkg/driver //@ts-nocheck part 2 (#19483) * listeners.ts * chainer.ts * command.ts * actionability.ts * inspect.ts * agents.ts * aliasing.ts * angular.ts * asserting.ts * clock.ts files * commands.ts * debugging.ts * fix comment. * roll back change. * Fix. * fix * Casted to cast. * Feedback changes. * fix any. commit 513074e4c3b043c39db3b0ed3e49ff29520674ec Author: Josh Wooding <12938082+joshwooding@users.noreply.github.com> Date: Tue Jan 11 15:34:01 2022 +0000 fix: overflow clip to prevent selector header from disapearing (#18649) (#19646) Co-authored-by: Tim Griesser commit b8ccf1283b033bacabe9fcc2ac5f5dd50f7f4cf0 Merge: 20715755ef d22742031e Author: Ryan Manuel Date: Mon Jan 10 15:38:23 2022 -0600 Merge branch 'develop' commit d22742031e4d999a97cc39c8c8a90b48e2785a8f Author: Ryan Manuel Date: Mon Jan 10 15:34:34 2022 -0600 release 9.2.1 [skip ci] commit 5d1dce61e0641ceaaf1fd0b8153182869459715f Author: Ryan Manuel Date: Mon Jan 10 13:01:12 2022 -0600 Merge master to dev commit 4818a214658707151cd34b6f6495b0d259e0adc0 Author: Juan Julián Merelo Guervós Date: Mon Jan 10 19:52:32 2022 +0100 fix: update cli-table dependency to fix broken colors.js (#19622) Co-authored-by: Emily Rohrbough Co-authored-by: Ryan Manuel commit 20715755ef27e2d6c32ed8ffa97976329b573b57 Author: semantic-release-bot Date: Mon Jan 10 11:23:17 2022 -0500 chore: release @cypress/react-v5.12.1 [skip ci] commit 3f85a049ef482251e85631cc45c2fd8a8bd08fb7 Merge: 642ec41406 6304fd7548 Author: Zachary Williams Date: Mon Jan 10 16:02:22 2022 +0000 Merge branch 'master' into develop commit 6304fd7548a0a3fee90fc8a9ba449ab81e7a7a0c Author: Zachary Williams Date: Mon Jan 10 10:01:27 2022 -0600 fix: check resolvedNodePath for Next.js 12 guard (#19604) commit 10e3e0a31750708abd2431231d3928a8fd8597f0 Author: semantic-release-bot Date: Tue Dec 21 14:35:12 2021 -0500 chore: release @cypress/react-v5.12.0 [skip ci] --- circle.yml | 2 +- cli/lib/tasks/verify.js | 2 +- cli/package.json | 6 +- cli/test/lib/tasks/verify_spec.js | 8 + npm/react/CHANGELOG.md | 14 ++ npm/react/plugins/next/checkSWC.ts | 8 +- npm/vue/README.md | 8 +- package.json | 2 +- ...-spec.ts => static-response-utils_spec.ts} | 0 ...-logging-spec.ts => proxy-logging_spec.ts} | 0 .../integration/dom/coordinates_spec.ts | 45 +++++ ...{dom_hitbox.spec.js => dom_hitbox_spec.js} | 0 .../integration/e2e/multi_domain_spec.ts | 27 +-- ...nd.spec.js => selector_playground_spec.js} | 0 ...es.spec.js => testConfigOverrides_spec.js} | 0 packages/driver/package.json | 3 +- packages/driver/src/cy/actionability.ts | 20 ++- packages/driver/src/cy/chai/inspect.ts | 4 +- packages/driver/src/cy/commands/agents.ts | 31 ++-- packages/driver/src/cy/commands/aliasing.ts | 5 +- packages/driver/src/cy/commands/angular.ts | 9 +- packages/driver/src/cy/commands/asserting.ts | 14 +- packages/driver/src/cy/commands/clock.ts | 34 ++-- packages/driver/src/cy/commands/commands.ts | 8 +- packages/driver/src/cy/commands/debugging.ts | 8 +- packages/driver/src/cy/listeners.ts | 169 ++++++++++-------- packages/driver/src/cy/top_attr_guards.ts | 8 +- packages/driver/src/cypress/chainer.ts | 8 +- packages/driver/src/cypress/clock.ts | 4 +- packages/driver/src/cypress/command.ts | 17 +- packages/driver/src/cypress/cy.ts | 6 +- packages/driver/src/dom/coordinates.ts | 66 ++++--- .../src/dom/elements/complexElements.ts | 2 +- packages/driver/src/multi-domain/index.ts | 8 +- packages/electron/package.json | 2 +- packages/https-proxy/package.json | 2 +- packages/network/package.json | 2 +- packages/runner/src/app/app.scss | 2 +- yarn.lock | 54 ++++-- 39 files changed, 383 insertions(+), 225 deletions(-) rename packages/driver/cypress/integration/cy/net-stubbing/{static-response-utils-spec.ts => static-response-utils_spec.ts} (100%) rename packages/driver/cypress/integration/cypress/{proxy-logging-spec.ts => proxy-logging_spec.ts} (100%) rename packages/driver/cypress/integration/e2e/{dom_hitbox.spec.js => dom_hitbox_spec.js} (100%) rename packages/driver/cypress/integration/e2e/{selector_playground.spec.js => selector_playground_spec.js} (100%) rename packages/driver/cypress/integration/e2e/{testConfigOverrides.spec.js => testConfigOverrides_spec.js} (100%) diff --git a/circle.yml b/circle.yml index 2a115cfa0fef..68136c1c0ce5 100644 --- a/circle.yml +++ b/circle.yml @@ -407,7 +407,7 @@ commands: yarn cypress:run --record --parallel --group 5x-driver-<> --browser <> else # external PR - TESTFILES=$(circleci tests glob "cypress/integration/**/*_spec.*" | circleci tests split --total=$CIRCLE_NODE_TOTAL) + TESTFILES=$(circleci tests glob "cypress/integration/**/*spec.*" | circleci tests split --total=$CIRCLE_NODE_TOTAL) echo "Test files for this machine are $TESTFILES" if [[ -z "$TESTFILES" ]]; then diff --git a/cli/lib/tasks/verify.js b/cli/lib/tasks/verify.js index 619ce4a9b6eb..826c35e1654f 100644 --- a/cli/lib/tasks/verify.js +++ b/cli/lib/tasks/verify.js @@ -15,7 +15,7 @@ const logger = require('../logger') const xvfb = require('../exec/xvfb') const state = require('./state') -const VERIFY_TEST_RUNNER_TIMEOUT_MS = +process.env.CYPRESS_VERIFY_TIMEOUT || 30000 +const VERIFY_TEST_RUNNER_TIMEOUT_MS = +util.getEnv('CYPRESS_VERIFY_TIMEOUT') || 30000 const checkExecutable = (binaryDir) => { const executable = state.getPathToExecutable(binaryDir) diff --git a/cli/package.json b/cli/package.json index 8034eaf928c3..643f4227e8cb 100644 --- a/cli/package.json +++ b/cli/package.json @@ -24,7 +24,7 @@ "@cypress/request": "^2.88.10", "@cypress/xvfb": "^1.2.4", "@types/node": "^14.14.31", - "@types/sinonjs__fake-timers": "^6.0.2", + "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", @@ -33,7 +33,7 @@ "chalk": "^4.1.0", "check-more-types": "^2.24.0", "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.0", + "cli-table3": "~0.6.1", "commander": "^5.1.0", "common-tags": "^1.8.0", "dayjs": "^1.10.4", @@ -75,7 +75,7 @@ "@types/lodash": "4.14.168", "@types/minimatch": "3.0.3", "@types/mocha": "8.0.3", - "@types/sinon": "7.5.1", + "@types/sinon": "9.0.9", "@types/sinon-chai": "3.2.5", "chai": "3.5.0", "chai-as-promised": "7.1.1", diff --git a/cli/test/lib/tasks/verify_spec.js b/cli/test/lib/tasks/verify_spec.js index f1b314ff1de7..3865b44463c2 100644 --- a/cli/test/lib/tasks/verify_spec.js +++ b/cli/test/lib/tasks/verify_spec.js @@ -81,6 +81,14 @@ context('lib/tasks/verify', () => { expect(newVerifyInstance.VERIFY_TEST_RUNNER_TIMEOUT_MS).to.eql(500000) }) + it('accepts custom verify task timeout from npm', () => { + process.env.npm_config_CYPRESS_VERIFY_TIMEOUT = '500000' + delete require.cache[require.resolve(`${lib}/tasks/verify`)] + const newVerifyInstance = require(`${lib}/tasks/verify`) + + expect(newVerifyInstance.VERIFY_TEST_RUNNER_TIMEOUT_MS).to.eql(500000) + }) + it('falls back to default verify task timeout if custom value is invalid', () => { process.env.CYPRESS_VERIFY_TIMEOUT = 'foobar' delete require.cache[require.resolve(`${lib}/tasks/verify`)] diff --git a/npm/react/CHANGELOG.md b/npm/react/CHANGELOG.md index 5d1dbfce27b2..ca7216c2251d 100644 --- a/npm/react/CHANGELOG.md +++ b/npm/react/CHANGELOG.md @@ -1,3 +1,17 @@ +# [@cypress/react-v5.12.1](https://github.com/cypress-io/cypress/compare/@cypress/react-v5.12.0...@cypress/react-v5.12.1) (2022-01-10) + + +### Bug Fixes + +* check resolvedNodePath for Next.js 12 guard ([#19604](https://github.com/cypress-io/cypress/issues/19604)) ([6304fd7](https://github.com/cypress-io/cypress/commit/6304fd7548a0a3fee90fc8a9ba449ab81e7a7a0c)) + +# [@cypress/react-v5.12.0](https://github.com/cypress-io/cypress/compare/@cypress/react-v5.11.0...@cypress/react-v5.12.0) (2021-12-21) + + +### Features + +* support create-react-app v5 ([#19434](https://github.com/cypress-io/cypress/issues/19434)) ([415a7b1](https://github.com/cypress-io/cypress/commit/415a7b149aaac37ae605dc1a11007bad29187dc5)) + # [@cypress/react-v5.11.0](https://github.com/cypress-io/cypress/compare/@cypress/react-v5.10.3...@cypress/react-v5.11.0) (2021-12-16) diff --git a/npm/react/plugins/next/checkSWC.ts b/npm/react/plugins/next/checkSWC.ts index 6ba4fb5a1cc3..3db6b281a059 100644 --- a/npm/react/plugins/next/checkSWC.ts +++ b/npm/react/plugins/next/checkSWC.ts @@ -10,9 +10,11 @@ export function checkSWC ( ) }) - if (hasSWCLoader && cypressConfig.nodeVersion !== 'system') { - throw new Error(`Cypress requires "nodeVersion" to be set to "system" in order to run Next.js with SWC optimizations. -Please add "nodeVersion": "system" to your Cypress configuration and try again.`) + // "resolvedNodePath" is only set when using the user's Node.js, which is required to compile Next.js with SWC optimizations + // If it is not set, they have either explicitly set "nodeVersion" to "bundled" or are are using Cypress < 9.0.0 where it was set to "bundled" by default + if (hasSWCLoader && !cypressConfig.resolvedNodePath) { + throw new Error(`Cannot compile Next.js application with configured Node.js. +If you are on Cypress version >= \`9.0.0\`, remove the "nodeVersion" property from your Cypress config. Otherwise, please add "nodeVersion": "system" to your Cypress config and try again.`) } return false diff --git a/npm/vue/README.md b/npm/vue/README.md index edc0c2844ddb..55d72c02ef8b 100644 --- a/npm/vue/README.md +++ b/npm/vue/README.md @@ -122,10 +122,10 @@ You can pass extensions (global components, mixins, modules to use) when mounting Vue component. Use `{ extensions: { ... }}` object inside the `options`. -- `components` - object of 'id' and components to register globally, see [Components](cypress/component/basic/components) example -- `use` (alias `plugins`) - list of plugins, see [Plugins](cypress/component/basic/plugins) -- `mixin` (alias `mixins`) - list of global mixins, see [Mixins](cypress/component/basic/mixins) example -- `filters` - hash of global filters, see [Filters](cypress/component/basic/filters) example +- `components` - object of 'id' and components to register globally, see [Components](npm/vue/cypress/component/basic/components) example +- `use` (alias `plugins`) - list of plugins, see [Plugins](npm/vue/cypress/component/basic/plugins) +- `mixin` (alias `mixins`) - list of global mixins, see [Mixins](npm/vue/cypress/component/basic/mixins) example +- `filters` - hash of global filters, see [Filters](npm/vue/cypress/component/basic/filters) example ### intro example diff --git a/package.json b/package.json index e7b04655fdaf..f7a8cfb899ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cypress", - "version": "9.2.0", + "version": "9.2.1", "description": "Cypress.io end to end testing tool", "private": true, "scripts": { diff --git a/packages/driver/cypress/integration/cy/net-stubbing/static-response-utils-spec.ts b/packages/driver/cypress/integration/cy/net-stubbing/static-response-utils_spec.ts similarity index 100% rename from packages/driver/cypress/integration/cy/net-stubbing/static-response-utils-spec.ts rename to packages/driver/cypress/integration/cy/net-stubbing/static-response-utils_spec.ts diff --git a/packages/driver/cypress/integration/cypress/proxy-logging-spec.ts b/packages/driver/cypress/integration/cypress/proxy-logging_spec.ts similarity index 100% rename from packages/driver/cypress/integration/cypress/proxy-logging-spec.ts rename to packages/driver/cypress/integration/cypress/proxy-logging_spec.ts diff --git a/packages/driver/cypress/integration/dom/coordinates_spec.ts b/packages/driver/cypress/integration/dom/coordinates_spec.ts index f7d272ac1d06..333b8c2fcde9 100644 --- a/packages/driver/cypress/integration/dom/coordinates_spec.ts +++ b/packages/driver/cypress/integration/dom/coordinates_spec.ts @@ -1,3 +1,6 @@ +import $coordinates from '../../../src/dom/coordinates' +import $elements from '../../../src/dom/elements' + const { $ } = Cypress export {} @@ -233,6 +236,48 @@ describe('src/dom/coordinates', () => { }) }) + context('isAUTFrame', () => { + const { isAUTFrame } = $coordinates + + // our test for a window is that it has a `window` that refers + // to itself + const getWindowLikeObject = () => { + const win = { parent: {} as any } + + win.parent.window = win.parent + + return win + } + + it('returns true if parent is a window and not an iframe', () => { + const win = getWindowLikeObject() + + expect(isAUTFrame(win)).to.be.true + }) + + it('returns true if parent is a window and getting its frameElement property throws an error', () => { + const win = getWindowLikeObject() + + cy.stub($elements, 'getNativeProp').throws('cross-origin error') + + expect(isAUTFrame(win)).to.be.true + }) + + it('returns false if parent is not a window', () => { + const win = { parent: {} } + + expect(isAUTFrame(win)).to.be.false + }) + + it('returns false if parent is an iframe', () => { + const win = getWindowLikeObject() + + cy.stub($elements, 'getNativeProp').returns(true) + + expect(isAUTFrame(win)).to.be.false + }) + }) + context('span spanning multiple lines', () => { it('gets first dom rect in multiline text', () => { $(`\ diff --git a/packages/driver/cypress/integration/e2e/dom_hitbox.spec.js b/packages/driver/cypress/integration/e2e/dom_hitbox_spec.js similarity index 100% rename from packages/driver/cypress/integration/e2e/dom_hitbox.spec.js rename to packages/driver/cypress/integration/e2e/dom_hitbox_spec.js diff --git a/packages/driver/cypress/integration/e2e/multi_domain_spec.ts b/packages/driver/cypress/integration/e2e/multi_domain_spec.ts index eec66e2a20a4..483551fd4bf2 100644 --- a/packages/driver/cypress/integration/e2e/multi_domain_spec.ts +++ b/packages/driver/cypress/integration/e2e/multi_domain_spec.ts @@ -18,6 +18,17 @@ describe('multi-domain', { experimentalSessionSupport: true, experimentalMultiDo cy.log('after switchToDomain') }) + it('handles querying nested elements', () => { + cy.switchToDomain('foobar.com', () => { + cy + .get('form button') + .invoke('text') + .should('equal', 'Submit') + }) + + cy.log('after switchToDomain') + }) + it('sets up window.Cypress in secondary domain', () => { cy.switchToDomain('foobar.com', () => { cy @@ -138,9 +149,7 @@ describe('multi-domain', { experimentalSessionSupport: true, experimentalMultiDo done() }) - cy.get('[data-cy="alert"]').then(($el) => { - $el.trigger('click') - }) + cy.get('[data-cy="alert"]').click() }) }) @@ -152,9 +161,7 @@ describe('multi-domain', { experimentalSessionSupport: true, experimentalMultiDo done() }) - cy.get('[data-cy="confirm"]').then(($el) => { - $el.trigger('click') - }) + cy.get('[data-cy="confirm"]').click() }) }) @@ -171,9 +178,7 @@ describe('multi-domain', { experimentalSessionSupport: true, experimentalMultiDo return true }) - cy.get('[data-cy="confirm"]').then(($el) => { - $el.trigger('click') - }) + cy.get('[data-cy="confirm"]').click() }) }) @@ -191,9 +196,7 @@ describe('multi-domain', { experimentalSessionSupport: true, experimentalMultiDo Cypress.on('window:confirm', () => {}) - cy.get('[data-cy="confirm"]').then(($el) => { - $el.trigger('click') - }) + cy.get('[data-cy="confirm"]').click() }) }) }) diff --git a/packages/driver/cypress/integration/e2e/selector_playground.spec.js b/packages/driver/cypress/integration/e2e/selector_playground_spec.js similarity index 100% rename from packages/driver/cypress/integration/e2e/selector_playground.spec.js rename to packages/driver/cypress/integration/e2e/selector_playground_spec.js diff --git a/packages/driver/cypress/integration/e2e/testConfigOverrides.spec.js b/packages/driver/cypress/integration/e2e/testConfigOverrides_spec.js similarity index 100% rename from packages/driver/cypress/integration/e2e/testConfigOverrides.spec.js rename to packages/driver/cypress/integration/e2e/testConfigOverrides_spec.js diff --git a/packages/driver/package.json b/packages/driver/package.json index 99c9099e5153..53243d4680b8 100644 --- a/packages/driver/package.json +++ b/packages/driver/package.json @@ -24,11 +24,12 @@ "@packages/server": "0.0.0-development", "@packages/ts": "0.0.0-development", "@rollup/plugin-node-resolve": "^13.0.4", - "@sinonjs/fake-timers": "7.0.2", + "@sinonjs/fake-timers": "8.1.0", "@types/chalk": "^2.2.0", "@types/common-tags": "^1.8.0", "@types/jquery.scrollto": "1.4.29", "@types/mocha": "^8.0.3", + "@types/sinonjs__fake-timers": "8.1.1", "@types/underscore.string": "0.0.38", "angular": "1.8.0", "basic-auth": "2.0.1", diff --git a/packages/driver/src/cy/actionability.ts b/packages/driver/src/cy/actionability.ts index ee9b86d0396c..279316d1dfdf 100644 --- a/packages/driver/src/cy/actionability.ts +++ b/packages/driver/src/cy/actionability.ts @@ -1,10 +1,10 @@ -// @ts-nocheck import _ from 'lodash' import $ from 'jquery' import Promise from 'bluebird' import debugFn from 'debug' import $dom from '../dom' +import type { ElWindowPostion, ElViewportPostion } from '../dom/coordinates' import $elements from '../dom/elements' import $errUtils from '../cypress/error_utils' const debug = debugFn('cypress:driver:actionability') @@ -54,7 +54,7 @@ const getPositionFromArguments = function (positionOrX, y, options) { } const ensureElIsNotCovered = function (cy, win, $el, fromElViewport, options, log, onScroll) { - let $elAtCoords = null + let $elAtCoords: JQuery | null = null const getElementAtPointFromViewport = function (fromElViewport) { // get the element at point from the viewport based @@ -64,8 +64,12 @@ const ensureElIsNotCovered = function (cy, win, $el, fromElViewport, options, lo elAtCoords = $dom.getElementAtPointFromViewport(win.document, fromElViewport.x, fromElViewport.y) if (elAtCoords) { - return $elAtCoords = $dom.wrap(elAtCoords) + $elAtCoords = $dom.wrap(elAtCoords) + + return $elAtCoords } + + return null } const ensureDescendents = function (fromElViewport) { @@ -164,7 +168,7 @@ const ensureElIsNotCovered = function (cy, win, $el, fromElViewport, options, lo const scrollContainers = function (scrollables) { // hold onto all the elements we've scrolled // past in this cycle - const elementsScrolledPast = [] + const elementsScrolledPast: JQuery[] = [] // pull off scrollables starting with the most outer // container which is window @@ -178,9 +182,7 @@ const ensureElIsNotCovered = function (cy, win, $el, fromElViewport, options, lo const possiblyScrollMultipleTimes = function ($fixed) { // if we got something AND - let needle - - if ($fixed && ((needle = $fixed.get(0), !elementsScrolledPast.includes(needle)))) { + if ($fixed && !elementsScrolledPast.includes($fixed.get(0))) { elementsScrolledPast.push($fixed.get(0)) scrollContainerPastElement($scrollableContainer, $fixed) @@ -229,7 +231,7 @@ const ensureElIsNotCovered = function (cy, win, $el, fromElViewport, options, lo try { ensureDescendentsAndScroll() - } catch (error) { + } catch (error: any) { const err = error if (log) { @@ -352,7 +354,7 @@ const verify = function (cy, $el, config, options, callbacks) { } return Promise.try(() => { - const coordsHistory = [] + const coordsHistory: (ElViewportPostion | ElWindowPostion)[] = [] const runAllChecks = function () { let $elAtCoords diff --git a/packages/driver/src/cy/chai/inspect.ts b/packages/driver/src/cy/chai/inspect.ts index 2508a31e395c..512336709ff4 100644 --- a/packages/driver/src/cy/chai/inspect.ts +++ b/packages/driver/src/cy/chai/inspect.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - // Changes made: added 'formatValueHook' to process value before being formatted. // For example the hook can be used to turn `window` objects into the string '[window]' // to avoid deep recursion. @@ -272,7 +270,7 @@ export function create (chai) { } function formatArray (ctx, value, recurseTimes, visibleKeys, keys) { - let output = [] + let output: string[] = [] for (let i = 0, l = value.length; i < l; ++i) { if (Object.prototype.hasOwnProperty.call(value, String(i))) { diff --git a/packages/driver/src/cy/commands/agents.ts b/packages/driver/src/cy/commands/agents.ts index baccb0320ca5..e1e6578b4200 100644 --- a/packages/driver/src/cy/commands/agents.ts +++ b/packages/driver/src/cy/commands/agents.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import _ from 'lodash' import sinon from 'sinon' @@ -8,7 +6,13 @@ import Promise from 'bluebird' import $utils from '../../cypress/utils' import $errUtils from '../../cypress/error_utils' -let counts = null +type Counts = { + spy: number + stub: number + children: Record +} + +let counts: Counts | null = null sinon.setFormatter($utils.stringifyArg.bind($utils)) @@ -24,6 +28,8 @@ const display = (name) => { if (name === 'stub') { return 'Stubbed Obj' } + + return '' } const formatArgs = (args) => { @@ -59,7 +65,7 @@ const onInvoke = function (Cypress, obj, args) { return } - const logProps = { + const logProps: Record = { name: agentName, message: obj.message, error: obj.error, @@ -68,7 +74,7 @@ const onInvoke = function (Cypress, obj, args) { snapshot: !agent._noSnapshot, event: true, consoleProps () { - const consoleObj = {} + const consoleObj: Record = {} consoleObj.Command = null consoleObj.Error = null @@ -147,15 +153,15 @@ export default function (Commands, Cypress, cy, state) { // to reset the counts + the sandbox Cypress.on('test:before:run', resetAndSetSandbox) - const wrap = function (ctx, type, agent, obj, method, count) { + const wrap = function (ctx, type, agent, obj, method, count?) { if (!count) { - count = (counts[type] += 1) + count = (counts![type] += 1) } const name = `${type}-${count}` if (!agent.parent) { - counts.children[name] = 0 + counts!.children[name] = 0 } const log = Cypress.log({ @@ -184,7 +190,7 @@ export default function (Commands, Cypress, cy, state) { // and the user can easily find the error try { returned = invoke.call(this, func, thisValue, args) - } catch (e) { + } catch (e: any) { error = e } @@ -249,7 +255,7 @@ export default function (Commands, Cypress, cy, state) { const { withArgs } = agent agent.withArgs = function (...args) { - const childCount = (counts.children[name] += 1) + const childCount = (counts!.children[name] += 1) return wrap(ctx, type, withArgs.apply(this, args), obj, method, `${count}.${childCount}`) } @@ -263,8 +269,9 @@ export default function (Commands, Cypress, cy, state) { return wrap(this, 'spy', theSpy, obj, method) } - const stub = function (obj, method, replacerFnOrValue) { - let theStub = sandbox.stub.call(sandbox, obj, method) + const stub = function (obj, method: string, replacerFnOrValue) { + // TODO: make the code below work with `packages/runner` type check without casting to `never`. + let theStub = sandbox.stub.call(sandbox, obj, method as never) // sinon 2 changed the stub signature // this maintains the 3-argument signature so it's not breaking diff --git a/packages/driver/src/cy/commands/aliasing.ts b/packages/driver/src/cy/commands/aliasing.ts index 717ee5226a08..9ba767057b20 100644 --- a/packages/driver/src/cy/commands/aliasing.ts +++ b/packages/driver/src/cy/commands/aliasing.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import _ from 'lodash' import $dom from '../../dom' @@ -22,9 +20,10 @@ export default function (Commands, Cypress, cy, state) { }) } + // TODO: change the log type from `any` to `Log`. // we also need to set the alias on the last command log // that matches our chainerId - const log = _.last(cy.queue.logs({ + const log: any = _.last(cy.queue.logs({ instrument: 'command', event: false, chainerId: state('chainerId'), diff --git a/packages/driver/src/cy/commands/angular.ts b/packages/driver/src/cy/commands/angular.ts index 4a677bd1df1c..48661b05d8a2 100644 --- a/packages/driver/src/cy/commands/angular.ts +++ b/packages/driver/src/cy/commands/angular.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import _ from 'lodash' import $ from 'jquery' import Promise from 'bluebird' @@ -34,7 +32,7 @@ export default (Commands, Cypress, cy, state) => { } // else return null element - return $(null) + return $(null as any) // cast to any to satisfy typescript } const resolveElements = () => { @@ -52,7 +50,7 @@ export default (Commands, Cypress, cy, state) => { } const findByNgAttr = (name, attr, el, options) => { - const selectors = [] + const selectors: string[] = [] let error = `Could not find element for ${name}: '${el}'. Searched ` _.extend(options, { verify: false, log: false }) @@ -91,7 +89,8 @@ export default (Commands, Cypress, cy, state) => { } Commands.addAll({ - ng (type, selector, options = {}) { + // TODO: Change the options type from `any` to `Partial`. + ng (type, selector, options: any = {}) { const userOptions = options // what about requirejs / browserify? diff --git a/packages/driver/src/cy/commands/asserting.ts b/packages/driver/src/cy/commands/asserting.ts index 098ef04227d7..4f4bc2dfbf5d 100644 --- a/packages/driver/src/cy/commands/asserting.ts +++ b/packages/driver/src/cy/commands/asserting.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import _ from 'lodash' import Promise from 'bluebird' @@ -27,7 +25,7 @@ export default function (Commands, Cypress, cy, state) { const shouldFn = function (subject, chainers, ...args) { if (_.isFunction(chainers)) { - return shouldFnWithCallback.apply(this, [subject, chainers, ...args]) // eslint-disable-line prefer-rest-params + return shouldFnWithCallback.apply(this, [subject, chainers]) } let exp = cy.expect(subject).to @@ -40,7 +38,7 @@ export default function (Commands, Cypress, cy, state) { const log = Cypress.log({ name: 'should', type: 'child', - message: [].concat(originalChainers, args), + message: ([] as any[]).concat(originalChainers, args), end: true, snapshot: true, error: err, @@ -69,7 +67,7 @@ export default function (Commands, Cypress, cy, state) { if (_.isFunction(cmd)) { try { return cmd.apply(memo, args) - } catch (err) { + } catch (err: any) { // if we made it all the way to the actual // assertion but its set to retry false then // we need to log out this .should since there @@ -134,13 +132,15 @@ export default function (Commands, Cypress, cy, state) { Commands.addAll({ type: 'assertion', prevSubject: true }, { should () { + // Cast to `any` to pass all arguments // eslint-disable-next-line prefer-rest-params - return shouldFn.apply(this, arguments) + return shouldFn.apply(this, arguments as any) }, and () { + // Cast to `any` to pass all arguments // eslint-disable-next-line prefer-rest-params - return shouldFn.apply(this, arguments) + return shouldFn.apply(this, arguments as any) }, }) } diff --git a/packages/driver/src/cy/commands/clock.ts b/packages/driver/src/cy/commands/clock.ts index 128634c50b95..aa202fbeff46 100644 --- a/packages/driver/src/cy/commands/clock.ts +++ b/packages/driver/src/cy/commands/clock.ts @@ -1,12 +1,15 @@ -// @ts-nocheck - import _ from 'lodash' -import * as $Clock from '../../cypress/clock' +import { create as createClock, Clock } from '../../cypress/clock' import $errUtils from '../../cypress/error_utils' +type CyClock = Clock & { + tick(ms, options?: any): number + restore(options?: any): void +} + // create a global clock -let clock = null +let clock: CyClock | null = null export default function (Commands, Cypress, cy, state) { const reset = () => { @@ -34,10 +37,13 @@ export default function (Commands, Cypress, cy, state) { if (clock) { return clock.bind(contentWindow) } + + return }) return Commands.addAll({ type: 'utility' }, { - clock (subject, now, methods, options = {}) { + // TODO: change the options type from `any` to Partial. + clock (subject, now, methods, options: any = {}) { let userOptions = options const ctx = state('ctx') @@ -71,12 +77,12 @@ export default function (Commands, Cypress, cy, state) { log: true, }) - const log = (name, message, snapshot = true, consoleProps = {}) => { + const log = (name, message = '', snapshot = true, consoleProps = {}) => { if (!options.log) { return } - const details = clock.details() + const details = clock!.details() const logNow = details.now const logMethods = details.methods.slice() @@ -95,11 +101,11 @@ export default function (Commands, Cypress, cy, state) { }) } - clock = $Clock.create(state('window'), now, methods) + clock = createClock(state('window'), now, methods) const { tick } = clock - clock.tick = function (ms, options = {}) { + clock.tick = function (ms, options: Partial = {}) { if ((ms != null) && !_.isNumber(ms)) { $errUtils.throwErrByPath('tick.invalid_argument', { args: { arg: JSON.stringify(ms) } }) } @@ -112,7 +118,7 @@ export default function (Commands, Cypress, cy, state) { if (options.log !== false) { theLog = log('tick', `${ms}ms`, false, { - 'Now': clock.details().now + ms, + 'Now': clock!.details().now + ms, 'Ticked': `${ms} milliseconds`, }) } @@ -132,8 +138,8 @@ export default function (Commands, Cypress, cy, state) { const { restore } = clock - clock.restore = function (options = {}) { - const ret = restore.apply(this, [options]) + clock.restore = function (options: Partial = {}) { + const ret = restore.apply(this) if (options.log !== false) { log('restore') @@ -157,12 +163,12 @@ export default function (Commands, Cypress, cy, state) { return clock }, - tick (subject, ms, options = {}) { + tick (subject, ms, options: Partial = {}) { if (!clock) { $errUtils.throwErrByPath('tick.no_clock') } - clock.tick(ms, options) + clock!.tick(ms, options) return clock }, diff --git a/packages/driver/src/cy/commands/commands.ts b/packages/driver/src/cy/commands/commands.ts index 54cdc512dae7..285dc0dda7e2 100644 --- a/packages/driver/src/cy/commands/commands.ts +++ b/packages/driver/src/cy/commands/commands.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import _ from 'lodash' import { $Chainer } from '../../cypress/chainer' @@ -21,6 +19,9 @@ export default function (Commands, Cypress, cy) { Commands.addChainer({ // userInvocationStack has to be passed in here, but can be ignored command (chainer, userInvocationStack, args) { + // `...args` below is the shorthand of `args[0], ...args.slice(1)` + // TypeScript doesn't allow this. + // @ts-ignore return command(chainer, ...args) }, }) @@ -29,7 +30,8 @@ export default function (Commands, Cypress, cy) { command (...args) { args.unshift(cy) - return command.apply(window, args) + // cast to `any` to ignore ts error. + return command.apply(window, args as any) }, }) } diff --git a/packages/driver/src/cy/commands/debugging.ts b/packages/driver/src/cy/commands/debugging.ts index fa9e71b24633..6318e904b9ef 100644 --- a/packages/driver/src/cy/commands/debugging.ts +++ b/packages/driver/src/cy/commands/debugging.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import _ from 'lodash' import $utils from '../../cypress/utils' @@ -44,9 +42,10 @@ export default (Commands, Cypress, cy, state, config) => { }) Commands.addAll({ type: 'utility', prevSubject: 'optional' }, { + // TODO: change the options type from `any` to `Loggable`. // pause should indefinitely pause until the user // presses a key or clicks in the UI to continue - pause (subject, options = {}) { + pause (subject, options: any = {}) { // bail if we're in run mode, unless --headed and --no-exit flags are passed if (!config('isInteractive') && (!config('browser').isHeaded || config('exit'))) { return subject @@ -104,7 +103,8 @@ export default (Commands, Cypress, cy, state, config) => { return subject }, - debug (subject, options = {}) { + // TODO: change `any` to Loggable + debug (subject, options: any = {}) { const userOptions = options options = _.defaults({}, userOptions, { diff --git a/packages/driver/src/cy/listeners.ts b/packages/driver/src/cy/listeners.ts index a48cc3354476..14fad0a49bf6 100644 --- a/packages/driver/src/cy/listeners.ts +++ b/packages/driver/src/cy/listeners.ts @@ -1,13 +1,23 @@ -// @ts-nocheck - import _ from 'lodash' -import { handleInvalidEventTarget, handleInvalidAnchorTarget } from './top_attr_guards' +import { handleInvalidEventTarget, handleInvalidAnchorTarget, GuardedEvent, GuardedAnchorEvent } from './top_attr_guards' const HISTORY_ATTRS = 'pushState replaceState'.split(' ') const HISTORY_NAV_ATTRS = 'go back forward'.split(' ') -let events = [] -let listenersAdded = null +type BoundEventHandler = + K extends 'click' ? (this: Window, ev: GuardedAnchorEvent) => any + : K extends 'submit' ? (this: Window, ev: GuardedEvent) => any + : (this: Window, ev: WindowEventMap[K]) => any + +type BoundEvent = [ + win: Window, + event: keyof WindowEventMap, + fn: BoundEventHandler, + capture?: boolean +] + +let events: BoundEvent[] = [] +let listenersAdded: boolean | null = null const removeAllListeners = () => { listenersAdded = false @@ -15,7 +25,8 @@ const removeAllListeners = () => { for (let e of events) { const [win, event, cb, capture] = e - win.removeEventListener(event, cb, capture) + // Cast to `any` to ignore `GuardedEvent`/`GuardedAnchorEvent`. + win.removeEventListener(event, cb as any, capture) } // reset all the events @@ -24,10 +35,11 @@ const removeAllListeners = () => { return null } -const addListener = (win, event, fn, capture) => { +const addListener = (win: Window, event: K, fn: BoundEventHandler, capture?: boolean) => { events.push([win, event, fn, capture]) - win.addEventListener(event, fn, capture) + // Cast to `any` to ignore `GuardedEvent`/`GuardedAnchorEvent`. + win.addEventListener(event, fn as any, capture) } const eventHasReturnValue = (e) => { @@ -43,93 +55,102 @@ const eventHasReturnValue = (e) => { return true } -export default { - bindTo (contentWindow, callbacks = {}) { - if (listenersAdded) { - return - } - - removeAllListeners() +type BoundCallbacks = { + onError: (handlerType) => (event) => undefined + onHistoryNav: (delta) => void + onSubmit: (e) => any + onLoad: (e) => any + onBeforeUnload: (e) => undefined + onUnload: (e) => any + onNavigation: (...args) => any + onAlert: (str) => any + onConfirm: (str) => boolean +} - listenersAdded = true +export const bindToListeners = (contentWindow, callbacks: BoundCallbacks) => { + if (listenersAdded) { + return + } - addListener(contentWindow, 'error', callbacks.onError('error')) - addListener(contentWindow, 'unhandledrejection', callbacks.onError('unhandledrejection')) + removeAllListeners() - addListener(contentWindow, 'beforeunload', (e) => { - // bail if we've canceled this event (from another source) - // or we've set a returnValue on the original event - if (e.defaultPrevented || eventHasReturnValue(e)) { - return - } + listenersAdded = true - callbacks.onBeforeUnload(e) - }) + addListener(contentWindow, 'error', callbacks.onError('error')) + addListener(contentWindow, 'unhandledrejection', callbacks.onError('unhandledrejection')) - addListener(contentWindow, 'load', (e) => { - callbacks.onLoad(e) - }) + addListener(contentWindow, 'load', (e) => { + callbacks.onLoad(e) + }) - addListener(contentWindow, 'unload', (e) => { - // when we unload we need to remove all of the event listeners - removeAllListeners() + addListener(contentWindow, 'beforeunload', (e) => { + // bail if we've canceled this event (from another source) + // or we've set a returnValue on the original event + if (e.defaultPrevented || eventHasReturnValue(e)) { + return + } - // else we know to proceed onwards! - callbacks.onUnload(e) - }) + callbacks.onBeforeUnload(e) + }) - addListener(contentWindow, 'hashchange', (e) => { - callbacks.onNavigation('hashchange', e) - }) + addListener(contentWindow, 'unload', (e) => { + // when we unload we need to remove all of the event listeners + removeAllListeners() - for (let attr of HISTORY_NAV_ATTRS) { - const orig = contentWindow.history?.[attr] + // else we know to proceed onwards! + callbacks.onUnload(e) + }) - if (!orig) { - continue - } + addListener(contentWindow, 'hashchange', (e) => { + callbacks.onNavigation('hashchange', e) + }) - contentWindow.history[attr] = function (delta) { - callbacks.onHistoryNav(attr === 'back' ? -1 : (attr === 'forward' ? 1 : delta)) + for (let attr of HISTORY_NAV_ATTRS) { + const orig = contentWindow.history?.[attr] - orig.apply(this, [delta]) - } + if (!orig) { + continue } - for (let attr of HISTORY_ATTRS) { - const orig = contentWindow.history?.[attr] + contentWindow.history[attr] = function (delta) { + callbacks.onHistoryNav(attr === 'back' ? -1 : (attr === 'forward' ? 1 : delta)) - if (!orig) { - continue - } + orig.apply(this, [delta]) + } + } - contentWindow.history[attr] = function (...args) { - orig.apply(this, args) + for (let attr of HISTORY_ATTRS) { + const orig = contentWindow.history?.[attr] - return callbacks.onNavigation(attr, args) - } + if (!orig) { + continue } - addListener(contentWindow, 'submit', (e) => { - // if we've prevented the default submit action - // without stopping propagation, we will still - // receive this event even though the form - // did not submit - if (e.defaultPrevented) { - return - } + contentWindow.history[attr] = function (...args) { + orig.apply(this, args) - // else we know to proceed onwards! - return callbacks.onSubmit(e) - }) + return callbacks.onNavigation(attr, args) + } + } + + addListener(contentWindow, 'submit', (e) => { + // if we've prevented the default submit action + // without stopping propagation, we will still + // receive this event even though the form + // did not submit + if (e.defaultPrevented) { + return + } - // Handling the situation where "_top" is set on the
/ element, either in - // html or dynamically, by tapping in at the capture phase of the events - addListener(contentWindow, 'submit', handleInvalidEventTarget, true) - addListener(contentWindow, 'click', handleInvalidAnchorTarget, true) + // else we know to proceed onwards! + return callbacks.onSubmit(e) + }) - contentWindow.alert = callbacks.onAlert - contentWindow.confirm = callbacks.onConfirm - }, + // Handling the situation where "_top" is set on the / element, either in + // html or dynamically, by tapping in at the capture phase of the events + addListener(contentWindow, 'submit', handleInvalidEventTarget, true) + addListener(contentWindow, 'click', handleInvalidAnchorTarget, true) + contentWindow.alert = callbacks.onAlert + contentWindow.confirm = callbacks.onConfirm } diff --git a/packages/driver/src/cy/top_attr_guards.ts b/packages/driver/src/cy/top_attr_guards.ts index 0471757febf0..730bbbb8bd7d 100644 --- a/packages/driver/src/cy/top_attr_guards.ts +++ b/packages/driver/src/cy/top_attr_guards.ts @@ -2,11 +2,13 @@ import $elements from '../dom/elements' const invalidTargets = new Set(['_parent', '_top']) +export type GuardedEvent = Event & {target: HTMLFormElement | HTMLAnchorElement} + /** * Guard against target beting set to something other than blank or self, while trying * to preserve the appearance of having the correct target value. */ -export function handleInvalidEventTarget (e: Event & {target: HTMLFormElement | HTMLAnchorElement}) { +export function handleInvalidEventTarget (e: GuardedEvent) { let targetValue = e.target.target let targetSet = e.target.hasAttribute('target') @@ -66,6 +68,8 @@ export function handleInvalidEventTarget (e: Event & {target: HTMLFormElement | } } +export type GuardedAnchorEvent = Event & {target: HTMLAnchorElement} + /** * We need to listen to all click events on the window, but only handle anchor elements, * as those might be the ones where we have an incorrect "target" attr, or could have one @@ -73,7 +77,7 @@ export function handleInvalidEventTarget (e: Event & {target: HTMLFormElement | * * @param e */ -export function handleInvalidAnchorTarget (e: Event & {target: HTMLAnchorElement}) { +export function handleInvalidAnchorTarget (e: GuardedAnchorEvent) { if (e.target.tagName === 'A') { handleInvalidEventTarget(e) } diff --git a/packages/driver/src/cypress/chainer.ts b/packages/driver/src/cypress/chainer.ts index e4445e4755bc..09d749b4bcd7 100644 --- a/packages/driver/src/cypress/chainer.ts +++ b/packages/driver/src/cypress/chainer.ts @@ -1,8 +1,13 @@ -// @ts-nocheck import _ from 'lodash' import $stackUtils from './stack_utils' export class $Chainer { + userInvocationStack: any + specWindow: Window + chainerId: string + firstCall: boolean + useInitialStack: boolean | null + constructor (userInvocationStack, specWindow) { this.userInvocationStack = userInvocationStack this.specWindow = specWindow @@ -11,6 +16,7 @@ export class $Chainer { // to the primary domain for the command log, etc. this.chainerId = _.uniqueId(`ch-${window.location.origin}-`) this.firstCall = true + this.useInitialStack = null } static remove (key) { diff --git a/packages/driver/src/cypress/clock.ts b/packages/driver/src/cypress/clock.ts index 4182c3434396..9d15b8e6982d 100644 --- a/packages/driver/src/cypress/clock.ts +++ b/packages/driver/src/cypress/clock.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import _ from 'lodash' import fakeTimers from '@sinonjs/fake-timers' @@ -59,3 +57,5 @@ export const create = (win, now, methods) => { } } + +export type Clock = ReturnType diff --git a/packages/driver/src/cypress/command.ts b/packages/driver/src/cypress/command.ts index a342fc213acf..6e9e8544f818 100644 --- a/packages/driver/src/cypress/command.ts +++ b/packages/driver/src/cypress/command.ts @@ -1,23 +1,26 @@ -// @ts-nocheck import _ from 'lodash' import utils from './utils' export class $Command { - constructor (obj = {}) { + // `attributes` is initiated at reset(), but ts cannot detect it. + // @ts-ignore + attributes: Record + + constructor (attrs: any = {}) { this.reset() // if the command came from a secondary domain, it already has an id - if (!obj.id) { + if (!attrs.id) { // the id prefix needs to be unique per domain, so there are not // collisions when commands created in a secondary domain are passed // to the primary domain for the command log, etc. - obj.id = _.uniqueId(`cmd-${window.location.origin}-`) + attrs.id = _.uniqueId(`cmd-${window.location.origin}-`) } - this.set(obj) + this.set(attrs) } - set (key, val) { + set (key, val?) { let obj if (_.isString(key)) { @@ -108,7 +111,7 @@ export class $Command { stringify () { let { name, args } = this.attributes - args = _.reduce(args, (memo, arg) => { + args = _.reduce(args, (memo: string[], arg) => { arg = _.isString(arg) ? _.truncate(arg, { length: 20 }) : '...' memo.push(arg) diff --git a/packages/driver/src/cypress/cy.ts b/packages/driver/src/cypress/cy.ts index f69aff76854c..878269d9200c 100644 --- a/packages/driver/src/cypress/cy.ts +++ b/packages/driver/src/cypress/cy.ts @@ -19,7 +19,7 @@ import { create as createMouse, Mouse } from '../cy/mouse' import { Keyboard } from '../cy/keyboard' import { create as createLocation, ILocation } from '../cy/location' import { create as createAssertions, IAssertions } from '../cy/assertions' -import $Listeners from '../cy/listeners' +import { bindToListeners } from '../cy/listeners' import { $Chainer } from './chainer' import { create as createTimer, ITimer } from '../cy/timers' import { create as createTimeouts, ITimeouts } from '../cy/timeouts' @@ -1072,7 +1072,7 @@ export class $Cy extends EventEmitter2 implements ITimeouts, IStability, IAssert private contentWindowListeners (contentWindow) { const cy = this - $Listeners.bindTo(contentWindow, { + bindToListeners(contentWindow, { // eslint-disable-next-line @cypress/dev/arrow-body-multiline-braces onError: (handlerType) => (event) => { const { originalErr, err, promise } = $errUtils.errorFromUncaughtEvent(handlerType, event) as ErrorFromProjectRejectionEvent @@ -1097,6 +1097,7 @@ export class $Cy extends EventEmitter2 implements ITimeouts, IStability, IAssert onSubmit (e) { return cy.Cypress.action('app:form:submitted', e) }, + onLoad () {}, onBeforeUnload (e) { cy.isStable(false, 'beforeunload') @@ -1110,7 +1111,6 @@ export class $Cy extends EventEmitter2 implements ITimeouts, IStability, IAssert // doesn't trigger a confirmation dialog return undefined }, - onLoad () {}, onUnload (e) { return cy.Cypress.action('app:window:unload', e) }, diff --git a/packages/driver/src/dom/coordinates.ts b/packages/driver/src/dom/coordinates.ts index 4f2733046ec4..d425e7a34562 100644 --- a/packages/driver/src/dom/coordinates.ts +++ b/packages/driver/src/dom/coordinates.ts @@ -7,12 +7,26 @@ const getElementAtPointFromViewport = (doc, x, y) => { return $elements.elementFromPoint(doc, x, y) } -const isAutIframe = (win) => { +const isAUTFrame = (win) => { const parent = win.parent // https://github.com/cypress-io/cypress/issues/6412 // ensure the parent is a Window before checking prop - return $window.isWindow(parent) && !$elements.getNativeProp(parent, 'frameElement') + if (!$window.isWindow(parent)) { + return false + } + + try { + // window.frameElement only exists on iframe windows, so if it doesn't + // exist on parent, it must be the top frame, and `win` is the AUT + return !$elements.getNativeProp(parent, 'frameElement') + } catch (err) { + // if the AUT is cross-domain, accessing parent.frameElement will throw + // a cross-origin error, meaning this is the AUT + // NOTE: this will need to be updated once we add support for + // cross-domain iframes + return true + } } const getFirstValidSizedRect = (el) => { @@ -22,30 +36,34 @@ const getFirstValidSizedRect = (el) => { }) || el.getBoundingClientRect() // otherwise fall back to the parent client rect } -type ElementPositioning = { +export type ElViewportPostion = { + doc: Document + x?: number + y?: number + top: number + left: number + right: number + bottom: number + topCenter: number + leftCenter: number +} + +export type ElWindowPostion = { + x?: number + y?: number + top: number + left: number + topCenter: number + leftCenter: number +} + +export type ElementPositioning = { scrollTop: number scrollLeft: number width: number height: number - fromElViewport: { - doc: Document - x?: number - y?: number - top: number - left: number - right: number - bottom: number - topCenter: number - leftCenter: number - } - fromElWindow: { - x?: number - y?: number - top: number - left: number - topCenter: number - leftCenter: number - } + fromElViewport: ElViewportPostion + fromElWindow: ElWindowPostion fromAutWindow: { x?: number y?: number @@ -94,7 +112,7 @@ const getElementPositioning = ($el: JQuery | HTMLElement): ElementP // https://github.com/cypress-io/cypress/issues/6412 // ensure the parent is a Window before checking prop // walk up from a nested iframe so we continually add the x + y values - while ($window.isWindow(curWindow) && !isAutIframe(curWindow) && curWindow.parent !== curWindow) { + while ($window.isWindow(curWindow) && !isAUTFrame(curWindow) && curWindow.parent !== curWindow) { frame = $elements.getNativeProp(curWindow, 'frameElement') if (curWindow && frame) { @@ -371,4 +389,6 @@ export default { getElementCoordinatesByPosition, getElementCoordinatesByPositionRelativeToXY, + + isAUTFrame, } diff --git a/packages/driver/src/dom/elements/complexElements.ts b/packages/driver/src/dom/elements/complexElements.ts index 9cbbf5c5d547..51b735485a3f 100644 --- a/packages/driver/src/dom/elements/complexElements.ts +++ b/packages/driver/src/dom/elements/complexElements.ts @@ -314,7 +314,7 @@ export const isScrollable = ($el) => { /** * Getters where DOM state like focus, styling, and actionability affect the return value */ -export const getFirstFixedOrStickyPositionParent = ($el) => { +export const getFirstFixedOrStickyPositionParent = ($el): JQuery | null => { if (isUndefinedOrHTMLBodyDoc($el)) { return null } diff --git a/packages/driver/src/multi-domain/index.ts b/packages/driver/src/multi-domain/index.ts index 9811a69ab811..d692bb8d68b8 100644 --- a/packages/driver/src/multi-domain/index.ts +++ b/packages/driver/src/multi-domain/index.ts @@ -8,7 +8,7 @@ import $Cypress from '../cypress' import { $Cy } from '../cypress/cy' import $Commands from '../cypress/commands' import $Log from '../cypress/log' -import $Listeners from '../cy/listeners' +import { bindToListeners } from '../cy/listeners' import { SpecBridgeDomainCommunicator } from './communicator' const specBridgeCommunicator = new SpecBridgeDomainCommunicator() @@ -106,6 +106,7 @@ const setup = () => { clearTimeout () {}, resetTimeout () {}, timeout () {}, + isPending () {}, }) let fnWrapper = `(${fn})` @@ -191,10 +192,11 @@ const onBeforeAppWindowLoad = (cy: $Cy, Cypress: Cypress.Cypress) => (autWindow: cy.overrides.wrapNativeMethods(autWindow) // TODO: DRY this up with the mostly-the-same code in src/cypress/cy.js - $Listeners.bindTo(autWindow, { + bindToListeners(autWindow, { // TODO: implement this once there's a better way to forward // messages to the top frame - onError () {}, + onError: () => () => undefined, + onHistoryNav () {}, onSubmit (e) { return Cypress.action('app:form:submitted', e) }, diff --git a/packages/electron/package.json b/packages/electron/package.json index 33fb3e2251e4..67af3d3faa8f 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -23,7 +23,7 @@ "minimist": "1.2.5" }, "devDependencies": { - "electron": "15.2.0", + "electron": "15.3.4", "electron-packager": "15.1.0", "execa": "4.1.0", "mocha": "3.5.3" diff --git a/packages/https-proxy/package.json b/packages/https-proxy/package.json index f6d72562617d..4a898947e106 100644 --- a/packages/https-proxy/package.json +++ b/packages/https-proxy/package.json @@ -18,7 +18,7 @@ "debug": "^4.3.2", "fs-extra": "8.1.0", "lodash": "^4.17.21", - "node-forge": "0.10.0", + "node-forge": "1.0.0", "semaphore": "1.1.0" }, "devDependencies": { diff --git a/packages/network/package.json b/packages/network/package.json index 171897501bf5..96fde1583b3b 100644 --- a/packages/network/package.json +++ b/packages/network/package.json @@ -19,7 +19,7 @@ "debug": "^4.3.2", "fs-extra": "8.1.0", "lodash": "^4.17.21", - "node-forge": "0.10.0", + "node-forge": "1.0.0", "proxy-from-env": "1.0.0" }, "devDependencies": { diff --git a/packages/runner/src/app/app.scss b/packages/runner/src/app/app.scss index 44988f229b8f..a814de540dc6 100644 --- a/packages/runner/src/app/app.scss +++ b/packages/runner/src/app/app.scss @@ -49,7 +49,7 @@ $spec-list-width: 250px; bottom: 0; box-shadow: inset 0 0 10px #555; left: 33%; - overflow: hidden; + overflow: clip; position: absolute; right: 0; top: 0; diff --git a/yarn.lock b/yarn.lock index baab08a6f15e..b45b3f8cb78e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6530,10 +6530,10 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.0.2.tgz#a53e71d4154ee704ea9b36a6d0b0780e246fadd1" - integrity sha512-dF84L5YC90gIOegPDCYymPIsDmwMWWSh7BwfDXQYePi8lVIEp7IZ1UVGkME8FjXOsDPxan12x4aaK+Lo6wVh9A== +"@sinonjs/fake-timers@8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" + integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== dependencies: "@sinonjs/commons" "^1.7.0" @@ -8422,15 +8422,17 @@ "@types/chai" "*" "@types/sinon" "*" -"@types/sinon@*", "@types/sinon@7.5.1": - version "7.5.1" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-7.5.1.tgz#d27b81af0d1cfe1f9b24eebe7a24f74ae40f5b7c" - integrity sha512-EZQUP3hSZQyTQRfiLqelC9NMWd1kqLcmQE0dMiklxBkgi84T+cHOhnKpgk4NnOWpGX863yE6+IaGnOXUNFqDnQ== +"@types/sinon@*", "@types/sinon@9.0.9": + version "9.0.9" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.9.tgz#115843b491583f924080f684b6d0d7438344f73c" + integrity sha512-z/y8maYOQyYLyqaOB+dYQ6i0pxKLOsfwCmHmn4T7jS/SDHicIslr37oE3Dg8SCqKrKeBy6Lemu7do2yy+unLrw== + dependencies: + "@types/sinonjs__fake-timers" "*" -"@types/sinonjs__fake-timers@^6.0.2": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae" - integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg== +"@types/sinonjs__fake-timers@*", "@types/sinonjs__fake-timers@8.1.1": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" + integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== "@types/sizzle@*", "@types/sizzle@^2.3.2": version "2.3.2" @@ -14067,7 +14069,7 @@ cli-table3@0.5.1, cli-table3@^0.5.0, cli-table3@^0.5.1: optionalDependencies: colors "^1.1.2" -cli-table3@0.6.0, cli-table3@~0.6.0: +cli-table3@0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.0.tgz#b7b1bc65ca8e7b5cef9124e13dc2b21e2ce4faee" integrity sha512-gnB85c3MGC7Nm9I/FkiasNBOKjOiO1RNuXXarQms37q4QMpWdlbBgD/VnOStA2faG1dpXMv31RFApjX1/QdgWQ== @@ -14077,6 +14079,15 @@ cli-table3@0.6.0, cli-table3@~0.6.0: optionalDependencies: colors "^1.1.2" +cli-table3@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.1.tgz#36ce9b7af4847f288d3cdd081fbd09bf7bd237b8" + integrity sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA== + dependencies: + string-width "^4.2.0" + optionalDependencies: + colors "1.4.0" + cli-table@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" @@ -14420,7 +14431,7 @@ colors@1.0.3, colors@1.0.x: resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= -colors@^1.1.2, colors@^1.3.3, colors@^1.4.0: +colors@1.4.0, colors@^1.1.2, colors@^1.3.3, colors@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== @@ -17428,10 +17439,10 @@ electron-to-chromium@^1.3.247, electron-to-chromium@^1.3.378, electron-to-chromi resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz#857e310ca00f0b75da4e1db6ff0e073cc4a91ddf" integrity sha512-Mfz4FIB4FSvEwBpDfdipRIrwd6uo8gUDoRDF4QEYb4h4tSuI3ov594OrjU6on042UlFHouIJpClDODGkPcBSbg== -electron@15.2.0: - version "15.2.0" - resolved "https://registry.npmjs.org/electron/-/electron-15.2.0.tgz#3068099d1e5c625d1708487de519c59d7c0a8e6e" - integrity sha512-kg0JdlsVbJgD/hO/A7o9VH8U44pQWkIsyt/sALxH6g8CiHQxMujLn2JfB2gyUfHXPT7m8vD4Z+CurS2KodEsWw== +electron@15.3.4: + version "15.3.4" + resolved "https://registry.yarnpkg.com/electron/-/electron-15.3.4.tgz#811e8872f4500b88ad49e005cbe8f93e10676f6d" + integrity sha512-GLTE+UEKw1pJehkgpLgXtsHhYqSPp6skSNY1bxnY3dDYBrsPlP3nTEO9YQY2p4eHk+uxFVTXOVy5afcu9fIZ9A== dependencies: "@electron/get" "^1.13.0" "@types/node" "^14.6.2" @@ -28267,7 +28278,12 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-forge@0.10.0, node-forge@^0.10.0: +node-forge@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.0.0.tgz#a025e3beeeb90d9cee37dae34d25b968ec3e6f15" + integrity sha512-ShkiiAlzSsgH1IwGlA0jybk9vQTIOLyJ9nBd0JTuP+nzADJFLY0NoDijM2zvD/JaezooGu3G2p2FNxOAK6459g== + +node-forge@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==