From 458786b1fe087215d6c6c752b4334e35f311ef52 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 3 Nov 2022 00:26:32 -0400 Subject: [PATCH 1/8] fix: only take snapshots if the AUT document is in state --- packages/driver/src/cy/snapshots.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/driver/src/cy/snapshots.ts b/packages/driver/src/cy/snapshots.ts index 707a83fd0b39..297758848ec1 100644 --- a/packages/driver/src/cy/snapshots.ts +++ b/packages/driver/src/cy/snapshots.ts @@ -229,6 +229,11 @@ export const create = ($$: $Cy['$$'], state: StateFunc) => { const createSnapshot = (name, $elToHighlight, preprocessedSnapshot) => { Cypress.action('cy:snapshot', name) + // only take a snapshot if the AUT document is in state to prevent cypress-in-cypress like snapshots + // or if the snapshot has been taken in a spec bridge and has already been preprocessed + if (!preprocessedSnapshot && !state('document')) { + return null + } try { const { From 97da4aa9e9ff9233720c50f0908e0bf892189a36 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Thu, 3 Nov 2022 15:53:14 -0400 Subject: [PATCH 2/8] test: add tests for snapshot behavior --- .../cypress/e2e/e2e/origin/snapshots.cy.ts | 172 +++++++++++------- .../cache/dev-darwin/snapshot-meta.cache.json | 4 +- 2 files changed, 111 insertions(+), 65 deletions(-) diff --git a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts index 49da844ba0f6..24460d82871b 100644 --- a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts @@ -2,107 +2,153 @@ import '../../../support/utils' describe('cy.origin - snapshots', { browser: '!webkit' }, () => { - const findLog = (logMap: Map, displayName: string, url: string) => { - return Array.from(logMap.values()).find((log: any) => { - const props = log.get() + it('does not create snapshots after the document has unloaded and the AUT has navigated cross-origin', () => { + cy.visit('/fixtures/generic.html') + cy.visit('http://www.foobar.com:3500/fixtures/generic.html') + cy.then(() => { + const snapshot = cy.createSnapshot() - return props.displayName === displayName && (props?.consoleProps?.URL === url || props?.consoleProps()?.URL === url) + expect(snapshot).to.be.null }) - } - let logs: Map + }) + + it('takes snapshots from the secondary origin even after the primary AUT has been unloaded from state', () => { + const findLog = (logMap: Map, selector) => { + return Array.from(logMap.values()).find((log: any) => { + const props = log.get() - beforeEach(() => { - logs = new Map() + return (props?.consoleProps?.Selector === selector) + }) + } + let logs: Map = new Map() cy.on('log:changed', (attrs, log) => { logs.set(attrs.id, log) }) - cy.fixture('foo.bar.baz.json').then((fooBarBaz) => { - cy.intercept('GET', '/foo.bar.baz.json', { body: fooBarBaz }).as('fooBarBaz') - }) - cy.visit('/fixtures/primary-origin.html') cy.get('a[data-cy="xhr-fetch-requests-onload"]').click() - }) - // TODO: the xhr event is showing up twice in the log, which is wrong and causing flake. skipping until: https://github.com/cypress-io/cypress/issues/23840 is addressed. - it.skip('verifies XHR requests made while a secondary origin is active eventually update with snapshots of the secondary origin', () => { cy.origin('http://www.foobar.com:3500', () => { - // need to set isInteractive in the spec bridge in order to take xhr snapshots in run mode, similar to how isInteractive is set within support/defaults.js - // @ts-ignore - Cypress.config('isInteractive', true) - cy.visit('http://www.foobar.com:3500/fixtures/xhr-fetch-requests.html') - cy.get(`[data-cy="assertion-header"]`).should('exist') - cy.wait('@fooBarBaz') + cy.get(`[data-cy="assertion-header"]`) }) cy.shouldWithTimeout(() => { - const xhrLogFromSecondaryOrigin = findLog(logs, 'xhr', 'http://localhost:3500/foo.bar.baz.json')?.get() + const getLogFromSecondaryOrigin = findLog(logs, '[data-cy="assertion-header"]')?.get() - expect(xhrLogFromSecondaryOrigin).to.not.be.undefined + expect(getLogFromSecondaryOrigin).to.not.be.undefined - const snapshots = xhrLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot.body.get()[0]) + const snapshots = getLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot.body.get()[0]) - expect(snapshots.length).to.equal(2) + expect(snapshots.length).to.equal(1) - // TODO: Since we have two events, one of them does not have a request snapshot - - expect(snapshots[1].querySelector(`[data-cy="assertion-header"]`)).to.have.property('innerText').that.equals('Making XHR and Fetch Requests behind the scenes if fireOnload is true!') + expect(snapshots[0].querySelector(`[data-cy="assertion-header"]`)).to.have.property('innerText').that.equals('Making XHR and Fetch Requests behind the scenes if fireOnload is true!') }) }) - // TODO: fix flaky test https://github.com/cypress-io/cypress/issues/23437 - it.skip('verifies fetch requests made while a secondary origin is active eventually update with snapshots of the secondary origin', () => { - cy.origin('http://www.foobar.com:3500', () => { - // need to set isInteractive in the spec bridge in order to take xhr snapshots in run mode, similar to how isInteractive is set within support/defaults.js - // @ts-ignore - Cypress.config('isInteractive', true) - cy.visit('http://www.foobar.com:3500/fixtures/xhr-fetch-requests.html') - cy.get(`[data-cy="assertion-header"]`).should('exist') - cy.wait('@fooBarBaz') - }) + describe('e2e log verification', () => { + const findLog = (logMap: Map, displayName: string, url: string) => { + return Array.from(logMap.values()).find((log: any) => { + const props = log.get() - cy.shouldWithTimeout(() => { - const xhrLogFromSecondaryOrigin = findLog(logs, 'fetch', 'http://localhost:3500/foo.bar.baz.json')?.get() + return props.displayName === displayName && (props?.consoleProps?.URL === url || props?.consoleProps()?.URL === url) + }) + } + let logs: Map - expect(xhrLogFromSecondaryOrigin).to.not.be.undefined + beforeEach(() => { + logs = new Map() - const snapshots = xhrLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot.body.get()[0]) + cy.on('log:changed', (attrs, log) => { + logs.set(attrs.id, log) + }) - snapshots.forEach((snapshot) => { - expect(snapshot.querySelector(`[data-cy="assertion-header"]`)).to.have.property('innerText').that.equals('Making XHR and Fetch Requests behind the scenes if fireOnload is true!') + cy.fixture('foo.bar.baz.json').then((fooBarBaz) => { + cy.intercept('GET', '/foo.bar.baz.json', { body: fooBarBaz }).as('fooBarBaz') }) + + cy.visit('/fixtures/primary-origin.html') + cy.get('a[data-cy="xhr-fetch-requests-onload"]').click() }) - }) - it('Does not take snapshots of XHR/fetch requests from secondary origin if the wrong origin is / origin mismatch, but instead the primary origin (existing behavior)', { - defaultCommandTimeout: 50, - }, - (done) => { - cy.on('fail', () => { - const xhrLogFromSecondaryOrigin = findLog(logs, 'fetch', 'http://localhost:3500/foo.bar.baz.json')?.get() + // TODO: the xhr event is showing up twice in the log, which is wrong and causing flake. skipping until: https://github.com/cypress-io/cypress/issues/23840 is addressed. + it.skip('verifies XHR requests made while a secondary origin is active eventually update with snapshots of the secondary origin', () => { + cy.origin('http://www.foobar.com:3500', () => { + // need to set isInteractive in the spec bridge in order to take xhr snapshots in run mode, similar to how isInteractive is set within support/defaults.js + // @ts-ignore + Cypress.config('isInteractive', true) + cy.visit('http://www.foobar.com:3500/fixtures/xhr-fetch-requests.html') + cy.get(`[data-cy="assertion-header"]`).should('exist') + cy.wait('@fooBarBaz') + }) + + cy.shouldWithTimeout(() => { + const xhrLogFromSecondaryOrigin = findLog(logs, 'xhr', 'http://localhost:3500/foo.bar.baz.json')?.get() - expect(xhrLogFromSecondaryOrigin).to.not.be.undefined + expect(xhrLogFromSecondaryOrigin).to.not.be.undefined - const snapshots = xhrLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot.body.get()[0]) + const snapshots = xhrLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot.body.get()[0]) - snapshots.forEach((snapshot) => { - expect(snapshot.querySelector(`[data-cy="assertion-header"]`)).to.be.null + expect(snapshots.length).to.equal(2) + + // TODO: Since we have two events, one of them does not have a request snapshot + + expect(snapshots[1].querySelector(`[data-cy="assertion-header"]`)).to.have.property('innerText').that.equals('Making XHR and Fetch Requests behind the scenes if fireOnload is true!') + }) + }) + + // TODO: fix flaky test https://github.com/cypress-io/cypress/issues/23437 + it.skip('verifies fetch requests made while a secondary origin is active eventually update with snapshots of the secondary origin', () => { + cy.origin('http://www.foobar.com:3500', () => { + // need to set isInteractive in the spec bridge in order to take xhr snapshots in run mode, similar to how isInteractive is set within support/defaults.js + // @ts-ignore + Cypress.config('isInteractive', true) + cy.visit('http://www.foobar.com:3500/fixtures/xhr-fetch-requests.html') + cy.get(`[data-cy="assertion-header"]`).should('exist') + cy.wait('@fooBarBaz') }) - done() + cy.shouldWithTimeout(() => { + const xhrLogFromSecondaryOrigin = findLog(logs, 'fetch', 'http://localhost:3500/foo.bar.baz.json')?.get() + + expect(xhrLogFromSecondaryOrigin).to.not.be.undefined + + const snapshots = xhrLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot.body.get()[0]) + + snapshots.forEach((snapshot) => { + expect(snapshot.querySelector(`[data-cy="assertion-header"]`)).to.have.property('innerText').that.equals('Making XHR and Fetch Requests behind the scenes if fireOnload is true!') + }) + }) }) - cy.visit('http://www.foobar.com:3500/fixtures/xhr-fetch-requests.html') + it('Does not take snapshots of XHR/fetch requests from secondary origin if the wrong origin is / origin mismatch, but instead the primary origin (existing behavior)', { + defaultCommandTimeout: 50, + }, + (done) => { + cy.on('fail', () => { + const xhrLogFromSecondaryOrigin = findLog(logs, 'fetch', 'http://localhost:3500/foo.bar.baz.json')?.get() - cy.origin('http://www.barbaz.com:3500', () => { - // need to set isInteractive in the spec bridge in order to take xhr snapshots in run mode, similar to how isInteractive is set within support/defaults.js - // @ts-ignore - Cypress.config('isInteractive', true) + expect(xhrLogFromSecondaryOrigin).to.not.be.undefined - cy.get(`[data-cy="assertion-header"]`).should('exist') - cy.wait('@fooBarBaz') + const snapshots = xhrLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot.body.get()[0]) + + snapshots.forEach((snapshot) => { + expect(snapshot.querySelector(`[data-cy="assertion-header"]`)).to.be.null + }) + + done() + }) + + cy.visit('http://www.foobar.com:3500/fixtures/xhr-fetch-requests.html') + + cy.origin('http://www.barbaz.com:3500', () => { + // need to set isInteractive in the spec bridge in order to take xhr snapshots in run mode, similar to how isInteractive is set within support/defaults.js + // @ts-ignore + Cypress.config('isInteractive', true) + + cy.get(`[data-cy="assertion-header"]`).should('exist') + cy.wait('@fooBarBaz') + }) }) }) }) diff --git a/tooling/v8-snapshot/cache/dev-darwin/snapshot-meta.cache.json b/tooling/v8-snapshot/cache/dev-darwin/snapshot-meta.cache.json index 1545a365fdea..4fd1c92a9dc1 100644 --- a/tooling/v8-snapshot/cache/dev-darwin/snapshot-meta.cache.json +++ b/tooling/v8-snapshot/cache/dev-darwin/snapshot-meta.cache.json @@ -1101,7 +1101,6 @@ "./node_modules/ansi_up/ansi_up.js", "./node_modules/any-base/index.js", "./node_modules/any-base/src/converter.js", - "./node_modules/anymatch/index.js", "./node_modules/archiver-utils/file.js", "./node_modules/archiver-utils/index.js", "./node_modules/archiver-utils/node_modules/glob/common.js", @@ -3285,6 +3284,7 @@ "./node_modules/yn/lenient.js", "./packages/data-context/node_modules/@babel/code-frame/lib/index.js", "./packages/data-context/node_modules/@babel/parser/lib/index.js", + "./packages/data-context/node_modules/anymatch/index.js", "./packages/data-context/node_modules/cross-spawn/index.js", "./packages/data-context/node_modules/cross-spawn/lib/enoent.js", "./packages/data-context/node_modules/cross-spawn/lib/parse.js", @@ -3528,5 +3528,5 @@ "./tooling/v8-snapshot/cache/dev-darwin/snapshot-entry.js" ], "deferredHashFile": "yarn.lock", - "deferredHash": "66156a5424fbd0d70c750ef2b16c22b0084a30220c67b632cff9212a7de7a226" + "deferredHash": "9fe6e763921f20d9fa851fee03e5b5e7c334c2bd85a88bcdf0ed49cde252e095" } \ No newline at end of file From d904eebdbc4ba27cfbc5c58b662a0a01149ac6b8 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 4 Nov 2022 09:54:53 -0400 Subject: [PATCH 3/8] Update packages/driver/src/cy/snapshots.ts Co-authored-by: Chris Breiding --- packages/driver/src/cy/snapshots.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/driver/src/cy/snapshots.ts b/packages/driver/src/cy/snapshots.ts index 297758848ec1..daf0eeace651 100644 --- a/packages/driver/src/cy/snapshots.ts +++ b/packages/driver/src/cy/snapshots.ts @@ -229,8 +229,12 @@ export const create = ($$: $Cy['$$'], state: StateFunc) => { const createSnapshot = (name, $elToHighlight, preprocessedSnapshot) => { Cypress.action('cy:snapshot', name) - // only take a snapshot if the AUT document is in state to prevent cypress-in-cypress like snapshots - // or if the snapshot has been taken in a spec bridge and has already been preprocessed + // when using cy.origin() and in a transitionary state, state('document') + // can be undefined, resulting in a bizarre snapshot of the entire Cypress + // UI. better not to take the snapshot in that case. + // https://github.com/cypress-io/cypress/issues/24506 + // also, do not take the snapshot here if it has already been taken and + // preprocessed in a spec bridge. if (!preprocessedSnapshot && !state('document')) { return null } From 228cef0524dbcc9f53bdd5e7fbd8d8b6939893e5 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 4 Nov 2022 11:21:39 -0400 Subject: [PATCH 4/8] chore: add comment to explain fallback document when cloning snapshot body --- packages/driver/src/cy/snapshots.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/driver/src/cy/snapshots.ts b/packages/driver/src/cy/snapshots.ts index 297758848ec1..2cf87f971213 100644 --- a/packages/driver/src/cy/snapshots.ts +++ b/packages/driver/src/cy/snapshots.ts @@ -246,7 +246,16 @@ export const create = ($$: $Cy['$$'], state: StateFunc) => { const body = { get: () => { if (!attachedBody) { - // If we don't have an AUT document, use the spec bridge document + // logs streaming in from the secondary need to be cloned off a document, + // which means state("document") will be undefined in the primary + // if a cy.origin block is active + + // this could also be possible, but unlikely, if the spec bridge is taking + // snapshots before the document has loaded into state, as could be the case with logs + // generated from before:load event handlers in a spec bridge + + // in any of these cases, fall back to the root document as we only + // need the document to clone the node. const doc = state('document') || window.document attachedBody = $$(doc.adoptNode($body[0])) From 1d4323475b7b891589263f49096df78a858f12ad Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 4 Nov 2022 11:21:57 -0400 Subject: [PATCH 5/8] chore: add more context to xhr test name --- packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts index 24460d82871b..7fc66edc1492 100644 --- a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts @@ -38,7 +38,7 @@ describe('cy.origin - snapshots', { browser: '!webkit' }, () => { expect(getLogFromSecondaryOrigin).to.not.be.undefined - const snapshots = getLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot.body.get()[0]) + const snapshots = getLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot?.body.get()[0]) expect(snapshots.length).to.equal(1) @@ -121,7 +121,7 @@ describe('cy.origin - snapshots', { browser: '!webkit' }, () => { }) }) - it('Does not take snapshots of XHR/fetch requests from secondary origin if the wrong origin is / origin mismatch, but instead the primary origin (existing behavior)', { + it('Does not take snapshots of XHR/fetch requests from secondary origin if the wrong origin is visited / origin mismatch, but instead the primary origin (existing behavior)', { defaultCommandTimeout: 50, }, (done) => { From 17d37f56ecbd0b139e48ca6b3936f1ff06a09035 Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 4 Nov 2022 11:29:38 -0400 Subject: [PATCH 6/8] fix: update not.to.be.undefined to to exist in snapshots.cy.ts --- packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts index 7fc66edc1492..71c3c00538d3 100644 --- a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts @@ -36,7 +36,7 @@ describe('cy.origin - snapshots', { browser: '!webkit' }, () => { cy.shouldWithTimeout(() => { const getLogFromSecondaryOrigin = findLog(logs, '[data-cy="assertion-header"]')?.get() - expect(getLogFromSecondaryOrigin).to.not.be.undefined + expect(getLogFromSecondaryOrigin).to.exist const snapshots = getLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot?.body.get()[0]) @@ -85,7 +85,7 @@ describe('cy.origin - snapshots', { browser: '!webkit' }, () => { cy.shouldWithTimeout(() => { const xhrLogFromSecondaryOrigin = findLog(logs, 'xhr', 'http://localhost:3500/foo.bar.baz.json')?.get() - expect(xhrLogFromSecondaryOrigin).to.not.be.undefined + expect(xhrLogFromSecondaryOrigin).to.exist const snapshots = xhrLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot.body.get()[0]) @@ -111,7 +111,7 @@ describe('cy.origin - snapshots', { browser: '!webkit' }, () => { cy.shouldWithTimeout(() => { const xhrLogFromSecondaryOrigin = findLog(logs, 'fetch', 'http://localhost:3500/foo.bar.baz.json')?.get() - expect(xhrLogFromSecondaryOrigin).to.not.be.undefined + expect(xhrLogFromSecondaryOrigin).to.exist const snapshots = xhrLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot.body.get()[0]) @@ -128,7 +128,7 @@ describe('cy.origin - snapshots', { browser: '!webkit' }, () => { cy.on('fail', () => { const xhrLogFromSecondaryOrigin = findLog(logs, 'fetch', 'http://localhost:3500/foo.bar.baz.json')?.get() - expect(xhrLogFromSecondaryOrigin).to.not.be.undefined + expect(xhrLogFromSecondaryOrigin).to.exist const snapshots = xhrLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot.body.get()[0]) From 6d74227e6acd0823ffe270ba4f9b78673f14597d Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 4 Nov 2022 13:37:49 -0400 Subject: [PATCH 7/8] fix: attempt to patch test to prevent nullish failures --- packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts index 71c3c00538d3..dfa920be5544 100644 --- a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts @@ -38,7 +38,7 @@ describe('cy.origin - snapshots', { browser: '!webkit' }, () => { expect(getLogFromSecondaryOrigin).to.exist - const snapshots = getLogFromSecondaryOrigin.snapshots.map((snapshot) => snapshot?.body.get()[0]) + const snapshots = getLogFromSecondaryOrigin?.snapshots?.map((snapshot) => snapshot?.body.get()[0]) || [] expect(snapshots.length).to.equal(1) From ae2d5cde0a831f6e71fb70535a79155e6686adcd Mon Sep 17 00:00:00 2001 From: Bill Glesias Date: Fri, 4 Nov 2022 13:49:09 -0400 Subject: [PATCH 8/8] fix: turn on interactive flag in spec bridge to get snapshots --- packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts index dfa920be5544..1a7aa7070f32 100644 --- a/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts +++ b/packages/driver/cypress/e2e/e2e/origin/snapshots.cy.ts @@ -30,6 +30,9 @@ describe('cy.origin - snapshots', { browser: '!webkit' }, () => { cy.get('a[data-cy="xhr-fetch-requests-onload"]').click() cy.origin('http://www.foobar.com:3500', () => { + // need to set isInteractive in the spec bridge in order to take snapshots in run mode, similar to how isInteractive is set within support/defaults.js + // @ts-ignore + Cypress.config('isInteractive', true) cy.get(`[data-cy="assertion-header"]`) })