From 386f4e7f16b23f6803eadac6f3aef97e6ee58048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Antonio=20Fern=C3=A1ndez=20de=20Alba?= Date: Mon, 3 Feb 2025 14:24:34 +0100 Subject: [PATCH] =?UTF-8?q?[test=20optimization]=C2=A0Fix=20`cy.window`=20?= =?UTF-8?q?for=20multi=20origin=20tests=20(#5185)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- integration-tests/cypress-esm-config.mjs | 2 +- integration-tests/cypress.config.js | 2 +- integration-tests/cypress/cypress.spec.js | 57 +++++++++++++++- integration-tests/cypress/e2e/multi-origin.js | 14 ++++ .../datadog-plugin-cypress/src/support.js | 65 ++++++++++--------- 5 files changed, 108 insertions(+), 32 deletions(-) create mode 100644 integration-tests/cypress/e2e/multi-origin.js diff --git a/integration-tests/cypress-esm-config.mjs b/integration-tests/cypress-esm-config.mjs index 92888de62e7..d5881f41af0 100644 --- a/integration-tests/cypress-esm-config.mjs +++ b/integration-tests/cypress-esm-config.mjs @@ -4,7 +4,7 @@ import cypress from 'cypress' async function runCypress () { await cypress.run({ config: { - defaultCommandTimeout: 100, + defaultCommandTimeout: 1000, e2e: { setupNodeEvents (on, config) { if (process.env.CYPRESS_ENABLE_INCOMPATIBLE_PLUGIN) { diff --git a/integration-tests/cypress.config.js b/integration-tests/cypress.config.js index 799ca06df8c..529980e298c 100644 --- a/integration-tests/cypress.config.js +++ b/integration-tests/cypress.config.js @@ -4,7 +4,7 @@ const cypressFailFast = require('cypress-fail-fast/plugin') const ddTracePlugin = require('dd-trace/ci/cypress/plugin') module.exports = { - defaultCommandTimeout: 100, + defaultCommandTimeout: 1000, e2e: { setupNodeEvents (on, config) { if (process.env.CYPRESS_ENABLE_INCOMPATIBLE_PLUGIN) { diff --git a/integration-tests/cypress/cypress.spec.js b/integration-tests/cypress/cypress.spec.js index d1fda8baa23..7bec90d898b 100644 --- a/integration-tests/cypress/cypress.spec.js +++ b/integration-tests/cypress/cypress.spec.js @@ -1,5 +1,6 @@ 'use strict' +const http = require('http') const { exec } = require('child_process') const getPort = require('get-port') @@ -74,7 +75,7 @@ moduleTypes.forEach(({ describe(`cypress@${version} ${type}`, function () { this.retries(2) this.timeout(60000) - let sandbox, cwd, receiver, childProcess, webAppPort + let sandbox, cwd, receiver, childProcess, webAppPort, secondWebAppServer if (type === 'commonJS') { testCommand = testCommand(version) @@ -91,6 +92,9 @@ moduleTypes.forEach(({ after(async () => { await sandbox.remove() await new Promise(resolve => webAppServer.close(resolve)) + if (secondWebAppServer) { + await new Promise(resolve => secondWebAppServer.close(resolve)) + } }) beforeEach(async function () { @@ -1636,5 +1640,56 @@ moduleTypes.forEach(({ }) }) }) + + // cy.origin is not available in old versions of Cypress + if (version === 'latest') { + it('does not crash for multi origin tests', async () => { + const { + NODE_OPTIONS, // NODE_OPTIONS dd-trace config does not work with cypress + ...restEnvVars + } = getCiVisEvpProxyConfig(receiver.port) + + const secondWebAppPort = await getPort() + + secondWebAppServer = http.createServer((req, res) => { + res.setHeader('Content-Type', 'text/html') + res.writeHead(200) + res.end(` + + +
Hella World
+ + `) + }) + + secondWebAppServer.listen(secondWebAppPort) + + const specToRun = 'cypress/e2e/multi-origin.js' + + childProcess = exec( + version === 'latest' ? testCommand : `${testCommand} --spec ${specToRun}`, + { + cwd, + env: { + ...restEnvVars, + CYPRESS_BASE_URL: `http://localhost:${webAppPort}`, + CYPRESS_BASE_URL_SECOND: `http://localhost:${secondWebAppPort}`, + SPEC_PATTERN: specToRun + }, + stdio: 'pipe' + } + ) + + await receiver + .gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => { + const events = payloads.flatMap(({ payload }) => payload.events) + assert.equal(events.length, 4) + + const test = events.find(event => event.type === 'test').content + assert.equal(test.resource, 'cypress/e2e/multi-origin.js.tests multiple origins') + assert.equal(test.meta[TEST_STATUS], 'pass') + }) + }) + } }) }) diff --git a/integration-tests/cypress/e2e/multi-origin.js b/integration-tests/cypress/e2e/multi-origin.js new file mode 100644 index 00000000000..d59ed2b70a1 --- /dev/null +++ b/integration-tests/cypress/e2e/multi-origin.js @@ -0,0 +1,14 @@ +/* eslint-disable */ + +it('tests multiple origins', () => { + // Visit first site + cy.visit('/'); + cy.get('.hello-world') + .should('have.text', 'Hello World') + + // Visit second site + cy.origin(Cypress.env('BASE_URL_SECOND'), () => { + cy.visit('/') + cy.get('.hella-world').should('have.text', 'Hella World') + }); +}); diff --git a/packages/datadog-plugin-cypress/src/support.js b/packages/datadog-plugin-cypress/src/support.js index 6e31e9e45a1..749a25d7f66 100644 --- a/packages/datadog-plugin-cypress/src/support.js +++ b/packages/datadog-plugin-cypress/src/support.js @@ -4,6 +4,10 @@ let isKnownTestsEnabled = false let knownTestsForSuite = [] let suiteTests = [] let earlyFlakeDetectionNumRetries = 0 +// We need to grab the original window as soon as possible, +// in case the test changes the origin. If the test does change the origin, +// any call to `cy.window()` will result in a cross origin error. +let originalWindow // If the test is using multi domain with cy.origin, trying to access // window properties will result in a cross origin error. @@ -61,6 +65,9 @@ beforeEach(function () { this.skip() } }) + cy.window().then(win => { + originalWindow = win + }) }) before(function () { @@ -78,39 +85,39 @@ before(function () { }) after(() => { - cy.window().then(win => { - if (safeGetRum(win)) { - win.dispatchEvent(new Event('beforeunload')) + try { + if (safeGetRum(originalWindow)) { + originalWindow.dispatchEvent(new Event('beforeunload')) } - }) + } catch (e) { + // ignore error. It's usually a multi origin issue. + } }) afterEach(function () { - cy.window().then(win => { - const currentTest = Cypress.mocha.getRunner().suite.ctx.currentTest - const testInfo = { - testName: currentTest.fullTitle(), - testSuite: Cypress.mocha.getRootSuite().file, - testSuiteAbsolutePath: Cypress.spec && Cypress.spec.absolute, - state: currentTest.state, - error: currentTest.err, - isNew: currentTest._ddIsNew, - isEfdRetry: currentTest._ddIsEfdRetry - } - try { - testInfo.testSourceLine = Cypress.mocha.getRunner().currentRunnable.invocationDetails.line - } catch (e) {} + const currentTest = Cypress.mocha.getRunner().suite.ctx.currentTest + const testInfo = { + testName: currentTest.fullTitle(), + testSuite: Cypress.mocha.getRootSuite().file, + testSuiteAbsolutePath: Cypress.spec && Cypress.spec.absolute, + state: currentTest.state, + error: currentTest.err, + isNew: currentTest._ddIsNew, + isEfdRetry: currentTest._ddIsEfdRetry + } + try { + testInfo.testSourceLine = Cypress.mocha.getRunner().currentRunnable.invocationDetails.line + } catch (e) {} - if (safeGetRum(win)) { - testInfo.isRUMActive = true - } - let coverage - try { - coverage = win.__coverage__ - } catch (e) { - // ignore error and continue - } - cy.task('dd:afterEach', { test: testInfo, coverage }) - }) + if (safeGetRum(originalWindow)) { + testInfo.isRUMActive = true + } + let coverage + try { + coverage = originalWindow.__coverage__ + } catch (e) { + // ignore error and continue + } + cy.task('dd:afterEach', { test: testInfo, coverage }) })