diff --git a/browser-versions.json b/browser-versions.json index c633c59f3519..eef229329ff3 100644 --- a/browser-versions.json +++ b/browser-versions.json @@ -1,4 +1,4 @@ { - "chrome:beta": "98.0.4758.66", + "chrome:beta": "98.0.4758.74", "chrome:stable": "97.0.4692.99" } diff --git a/circle.yml b/circle.yml index 809c60669f93..a019323f61f2 100644 --- a/circle.yml +++ b/circle.yml @@ -29,7 +29,6 @@ mainBuildFilters: &mainBuildFilters only: - develop - 10.0-release - - test-binary-downstream-windows # usually we don't build Mac app - it takes a long time # but sometimes we want to really confirm we are doing the right thing diff --git a/cli/lib/util.js b/cli/lib/util.js index da089ee003f5..b05572df3c99 100644 --- a/cli/lib/util.js +++ b/cli/lib/util.js @@ -18,10 +18,12 @@ const executable = require('executable') const { stripIndent } = require('common-tags') const supportsColor = require('supports-color') const isInstalledGlobally = require('is-installed-globally') -const pkg = require(path.join(__dirname, '..', 'package.json')) const logger = require('./logger') const debug = require('debug')('cypress:cli') const fs = require('./fs') +const semver = require('semver') + +const pkg = require(path.join(__dirname, '..', 'package.json')) const issuesUrl = 'https://github.com/cypress-io/cypress/issues' @@ -291,18 +293,33 @@ const util = { .mapValues((value) => { // stringify to 1 or 0 return value ? '1' : '0' }) - .extend(util.getOriginalNodeOptions(options)) + .extend(util.getOriginalNodeOptions()) .value() }, - getOriginalNodeOptions (options) { + getOriginalNodeOptions () { + const opts = {} + if (process.env.NODE_OPTIONS) { - return { - ORIGINAL_NODE_OPTIONS: process.env.NODE_OPTIONS, - } + opts.ORIGINAL_NODE_OPTIONS = process.env.NODE_OPTIONS + } + + // https://github.com/cypress-io/cypress/issues/18914 + // Node 17+ ships with OpenSSL 3 by default, so we may need the option + // --openssl-legacy-provider so that webpack@4 can use the legacy MD4 hash + // function. This option doesn't exist on Node <17 or when it is built + // against OpenSSL 1, so we have to detect Node's major version and check + // which version of OpenSSL it was built against before spawning the plugins + // process. + + // To be removed when the Cypress binary pulls in the @cypress/webpack-batteries-included-preprocessor + // version that has been updated to webpack >= 5.61, which no longer relies on + // Node's builtin crypto.hash function. + if (process.versions && semver.satisfies(process.versions.node, '>=17.0.0') && process.versions.openssl.startsWith('3.')) { + opts.ORIGINAL_NODE_OPTIONS = `${opts.ORIGINAL_NODE_OPTIONS || ''} --openssl-legacy-provider` } - return {} + return opts }, getForceTty () { diff --git a/cli/package.json b/cli/package.json index 1cbf5623efdf..2a9089f6c572 100644 --- a/cli/package.json +++ b/cli/package.json @@ -58,6 +58,7 @@ "pretty-bytes": "^5.6.0", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", + "semver": "^7.3.2", "supports-color": "^8.1.1", "tmp": "~0.2.1", "untildify": "^4.0.0", diff --git a/cli/test/lib/util_spec.js b/cli/test/lib/util_spec.js index b325ccab31a0..8412933170f6 100644 --- a/cli/test/lib/util_spec.js +++ b/cli/test/lib/util_spec.js @@ -257,6 +257,7 @@ describe('util', () => { context('.getOriginalNodeOptions', () => { let restoreEnv + const sandbox = sinon.createSandbox() afterEach(() => { if (restoreEnv) { @@ -266,6 +267,9 @@ describe('util', () => { }) it('copy NODE_OPTIONS to ORIGINAL_NODE_OPTIONS', () => { + sandbox.stub(process.versions, 'node').value('v16.5.0') + sandbox.stub(process.versions, 'openssl').value('1.0.0') + restoreEnv = mockedEnv({ NODE_OPTIONS: '--require foo.js', }) @@ -274,6 +278,68 @@ describe('util', () => { ORIGINAL_NODE_OPTIONS: '--require foo.js', }) }) + + // https://github.com/cypress-io/cypress/issues/18914 + it('includes --openssl-legacy-provider in Node 17+ w/ OpenSSL 3', () => { + sandbox.stub(process.versions, 'node').value('v17.1.0') + sandbox.stub(process.versions, 'openssl').value('3.0.0-quic') + + restoreEnv = mockedEnv({ + NODE_OPTIONS: '--require foo.js', + }) + + let childOptions = util.getOriginalNodeOptions() + + expect(childOptions.ORIGINAL_NODE_OPTIONS).to.eq('--require foo.js --openssl-legacy-provider') + + restoreEnv() + restoreEnv = mockedEnv({}) + childOptions = util.getOriginalNodeOptions() + + expect(childOptions.ORIGINAL_NODE_OPTIONS).to.eq(' --openssl-legacy-provider') + }) + + // https://github.com/cypress-io/cypress/issues/19320 + it('does not include --openssl-legacy-provider in Node 17+ w/ OpenSSL 1', () => { + sandbox.stub(process.versions, 'node').value('v17.1.0') + sandbox.stub(process.versions, 'openssl').value('1.0.0') + + restoreEnv = mockedEnv({ + NODE_OPTIONS: '--require foo.js', + }) + + let childOptions = util.getOriginalNodeOptions() + + expect(childOptions.ORIGINAL_NODE_OPTIONS).to.eq('--require foo.js') + expect(childOptions.ORIGINAL_NODE_OPTIONS).not.to.contain('--openssl-legacy-provider') + + restoreEnv() + restoreEnv = mockedEnv({}) + childOptions = util.getOriginalNodeOptions() + + expect(childOptions.ORIGINAL_NODE_OPTIONS).to.be.undefined + }) + + // https://github.com/cypress-io/cypress/issues/18914 + it('does not include --openssl-legacy-provider in Node <=16', () => { + sandbox.stub(process.versions, 'node').value('v16.5.0') + sandbox.stub(process.versions, 'openssl').value('1.0.0') + + restoreEnv = mockedEnv({}) + + let childOptions = util.getOriginalNodeOptions() + + expect(childOptions.ORIGINAL_NODE_OPTIONS).to.be.undefined + + restoreEnv = mockedEnv({ + NODE_OPTIONS: '--require foo.js', + }) + + childOptions = util.getOriginalNodeOptions() + + expect(childOptions.ORIGINAL_NODE_OPTIONS).to.eq('--require foo.js') + expect(childOptions.ORIGINAL_NODE_OPTIONS).not.to.contain('--openssl-legacy-provider') + }) }) context('.exit', () => { diff --git a/npm/cypress-schematic/sandbox/.gitignore b/npm/cypress-schematic/sandbox/.gitignore index 105c00f22e08..de51f68a2cda 100644 --- a/npm/cypress-schematic/sandbox/.gitignore +++ b/npm/cypress-schematic/sandbox/.gitignore @@ -31,7 +31,6 @@ chrome-profiler-events*.json .history/* # misc -/.angular/cache /.sass-cache /connect.lock /coverage diff --git a/npm/cypress-schematic/sandbox/package.json b/npm/cypress-schematic/sandbox/package.json index 2182e1115f3c..6d4647bd8147 100644 --- a/npm/cypress-schematic/sandbox/package.json +++ b/npm/cypress-schematic/sandbox/package.json @@ -36,4 +36,4 @@ "karma-jasmine-html-reporter": "^1.5.0", "typescript": "~4.5.5" } -} \ No newline at end of file +} diff --git a/npm/cypress-schematic/sandbox/src/polyfills.ts b/npm/cypress-schematic/sandbox/src/polyfills.ts index 2a8e5973100d..4660fa5793e3 100644 --- a/npm/cypress-schematic/sandbox/src/polyfills.ts +++ b/npm/cypress-schematic/sandbox/src/polyfills.ts @@ -18,6 +18,18 @@ * BROWSER POLYFILLS */ +/** + * IE11 requires the following for NgClass support on SVG elements + */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + */ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + /** * By default, zone.js will patch all possible macroTask and DomEvents * user can disable parts of macroTask/DomEvents patch by setting following flags diff --git a/npm/cypress-schematic/sandbox/src/test.ts b/npm/cypress-schematic/sandbox/src/test.ts index b3b713de643e..2123582e4131 100644 --- a/npm/cypress-schematic/sandbox/src/test.ts +++ b/npm/cypress-schematic/sandbox/src/test.ts @@ -17,9 +17,7 @@ declare const require: { // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( BrowserDynamicTestingModule, - platformBrowserDynamicTesting(), { - teardown: { destroyAfterEach: false }, - }, + platformBrowserDynamicTesting(), ) // Then we find all the tests. diff --git a/packages/driver/cypress/e2e/commands/actions/type.cy.js b/packages/driver/cypress/e2e/commands/actions/type.cy.js index 14e2abde5992..d9cfa5a70727 100644 --- a/packages/driver/cypress/e2e/commands/actions/type.cy.js +++ b/packages/driver/cypress/e2e/commands/actions/type.cy.js @@ -593,6 +593,53 @@ describe('src/cy/commands/actions/type - #type', () => { }) }) + // https://github.com/cypress-io/cypress/issues/19541 + describe(`type('{enter}') and click event on button-like elements`, () => { + beforeEach(() => { + cy.visit('fixtures/type-enter.html') + }) + + describe('triggers', () => { + const targets = [ + 'button-tag', + 'input-button', + 'input-image', + 'input-reset', + 'input-submit', + ] + + targets.forEach((targetId) => { + it(`${targetId}`, () => { + cy.get(`#target-${targetId}`).focus() + cy.get(`#target-${targetId}`).type('{enter}') + + cy.get('li').eq(0).should('have.text', 'keydown') + cy.get('li').eq(1).should('have.text', 'keypress') + cy.get('li').eq(2).should('have.text', 'click') + cy.get('li').eq(3).should('have.text', 'keyup') + }) + }) + }) + + describe('does not trigger', () => { + const targets = [ + 'input-checkbox', + 'input-radio', + ] + + targets.forEach((targetId) => { + it(`${targetId}`, () => { + cy.get(`#target-${targetId}`).focus() + cy.get(`#target-${targetId}`).type('{enter}') + + cy.get('li').eq(0).should('have.text', 'keydown') + cy.get('li').eq(1).should('have.text', 'keypress') + cy.get('li').eq(2).should('have.text', 'keyup') + }) + }) + }) + }) + describe('tabindex', () => { beforeEach(function () { this.$div = cy.$$('#tabindex') diff --git a/packages/driver/cypress/fixtures/type-enter.html b/packages/driver/cypress/fixtures/type-enter.html new file mode 100644 index 000000000000..421e2683d19d --- /dev/null +++ b/packages/driver/cypress/fixtures/type-enter.html @@ -0,0 +1,75 @@ + + + + type('{enter}') + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/packages/driver/src/cy/commands/actions/type.ts b/packages/driver/src/cy/commands/actions/type.ts index d6d18c9445de..4e3e8a3a0322 100644 --- a/packages/driver/src/cy/commands/actions/type.ts +++ b/packages/driver/src/cy/commands/actions/type.ts @@ -272,6 +272,11 @@ export default function (Commands, Cypress, cy, state, config) { const isContentEditable = $elements.isContentEditable(options.$el.get(0)) const isTextarea = $elements.isTextarea(options.$el.get(0)) + // click event is only fired on button, image, submit, reset elements. + // That's why we cannot use $elements.isButtonLike() here. + const type = (type) => $elements.isInputType(options.$el.get(0), type) + const sendClickEvent = type('button') || type('image') || type('submit') || type('reset') + return keyboard.type({ $el: options.$el, chars, @@ -347,22 +352,33 @@ export default function (Commands, Cypress, cy, state, config) { }) }, - onEnterPressed (id) { + onEnterPressed (el) { // dont dispatch change events or handle // submit event if we've pressed enter into // a textarea or contenteditable - if (isTextarea || isContentEditable) { return } + // https://github.com/cypress-io/cypress/issues/19541 + // Send click event on type('{enter}') + if (sendClickEvent) { + // Firefox sends a click event automatically. + if (!Cypress.isBrowser('firefox')) { + const ctor = $dom.getDocumentFromElement(el).defaultView?.PointerEvent + const event = new ctor('click') + + el.dispatchEvent(event) + } + } + // if our value has changed since our // element was activated we need to // fire a change event immediately const changeEvent = state('changeEvent') if (changeEvent) { - changeEvent(id) + changeEvent() } // handle submit event handler here diff --git a/packages/driver/src/cy/keyboard.ts b/packages/driver/src/cy/keyboard.ts index 5e28c5329f56..f03306b6fa21 100644 --- a/packages/driver/src/cy/keyboard.ts +++ b/packages/driver/src/cy/keyboard.ts @@ -582,7 +582,7 @@ const simulatedDefaultKeyMap: { [key: string]: SimulatedDefault } = { $selection.replaceSelectionContents(el, '\n') } - options.onEnterPressed && options.onEnterPressed() + options.onEnterPressed && options.onEnterPressed(el) }, Delete: (el, key) => { key.events.input = $selection.deleteRightOfCursor(el) diff --git a/scripts/binary/bump.js b/scripts/binary/bump.js index d8a28866040b..b7cc4dd8dd40 100644 --- a/scripts/binary/bump.js +++ b/scripts/binary/bump.js @@ -268,15 +268,7 @@ Testing new Cypress version ${version} } // first try to commit to branch for next upcoming version - const specificBranchOptions = { - owner, - repo, - token: creds.githubToken, - message, - branch: version, - } - - return makeEmptyGithubCommit(specificBranchOptions) + return makeEmptyGithubCommit({ ...defaultOptions, branch: version }) .catch(() => { // maybe there is no branch for next version // try default branch