fix: preserve navigation functionality when unload event is deprecated (#29525)

* chore: Force deprecation of unload event

* empty commit

* use pagehide instead of unload event

* tslint

* bump cache

* some debugging

* rm debug

* comment out forced deprecation of unload - tbd how to enable

* rm more unnecessary debug

* changelog

* rename event callback from onUnload -> onPageHide

* comment on discrepency of event naming for end of page lifecycle events

* use pagehide in chrome, unload elsewhere

* comment on chromium-specific pagehide behavior

* changelog

---------

Co-authored-by: Jennifer Shehane <jennifer@cypress.io>
This commit is contained in:
Cacie Prins
2024-05-22 14:06:40 -04:00
committed by GitHub
parent 374ceb20f9
commit c8ec30ceab
10 changed files with 51 additions and 11 deletions

View File

@@ -1,3 +1,3 @@
# Bump this version to force CI to re-create the cache from scratch.
05-10-24
05-09-24

View File

@@ -1,4 +1,12 @@
<!-- See the ../guides/writing-the-cypress-changelog.md for details on writing the changelog. -->
## 13.10.1
_Released 5/28/2024 (PENDING)_
**Bugfixes:**
- Pre-emptively fix behavior with Chrome for when `unload` events are forcefully deprecated by using `pagehide` as a proxy. Fixes [#29241](https://github.com/cypress-io/cypress/issues/29241).
## 13.10.0
_Released 5/21/2024_

View File

@@ -344,7 +344,12 @@ export class EventManager {
// when we actually unload then
// nuke all of the cookies again
// so we clear out unload
$window.on('unload', () => {
// While we must move to pagehide for Chromium, it does not work for our
// needs in Firefox. Until that is addressed, only Chromium uses the pagehide
// event as a proxy for AUT unloads.
const unloadEvent = this.isBrowser({ family: 'chromium' }) ? 'pagehide' : 'unload'
$window.on(unloadEvent, (e) => {
this._clearAllCookies()
})

View File

@@ -97,13 +97,15 @@ describe('src/cy/commands/navigation', () => {
})
it('removes listeners', () => {
cy.log(Cypress.browser)
const unloadEvent = Cypress.browser.family === 'chromium' ? 'pagehide' : 'unload'
const win = cy.state('window')
const rel = cy.stub(win, 'removeEventListener')
cy.reload().then(() => {
expect(rel).to.be.calledWith('beforeunload')
expect(rel).to.be.calledWith('unload')
expect(rel).to.be.calledWith(unloadEvent)
})
})
@@ -411,8 +413,10 @@ describe('src/cy/commands/navigation', () => {
const rel = cy.stub(win, 'removeEventListener')
cy.go('back').then(() => {
const unloadEvent = cy.browser.family === 'chromium' ? 'pagehide' : 'unload'
expect(rel).to.be.calledWith('beforeunload')
expect(rel).to.be.calledWith('unload')
expect(rel).to.be.calledWith(unloadEvent)
})
})
})

View File

@@ -218,10 +218,13 @@ const attachToWindow = (autWindow: Window) => {
Cypress.specBridgeCommunicator.toPrimary('window:load', { url: remoteLocation.href })
cy.isStable(true, 'load')
},
onUnload (e) {
onPageHide (e) {
cy.state('window', undefined)
cy.state('document', undefined)
// unload is being actively deprecated/removed by chrome, so for
// compatibility, we are using `window`'s `pagehide` event as a proxy
// for the `window:unload` event that we emit. See: https://github.com/cypress-io/cypress/pull/29525
return Cypress.action('app:window:unload', e)
},
onNavigation (...args) {

View File

@@ -2,16 +2,20 @@ import type { $Cy } from '../../cypress/cy'
import { handleErrorEvent } from './errors'
export const handleSpecWindowEvents = (cy: $Cy) => {
// While we must move to pagehide for Chromium, it does not work for our
// needs in Firefox. Until that is addressed, only Chromium uses the pagehide
// event as a proxy for AUT unloads.
const unloadEvent = cy.Cypress.browser.family === 'chromium' ? 'pagehide' : 'unload'
const handleWindowErrorEvent = handleErrorEvent(cy, 'spec')('error')
const handleWindowUnhandledRejectionEvent = handleErrorEvent(cy, 'spec')('unhandledrejection')
const handleUnload = () => {
window.removeEventListener('unload', handleUnload)
window.removeEventListener(unloadEvent, handleUnload)
window.removeEventListener('error', handleWindowErrorEvent)
window.removeEventListener('unhandledrejection', handleWindowUnhandledRejectionEvent)
}
window.addEventListener('unload', handleUnload)
window.addEventListener(unloadEvent, handleUnload)
window.addEventListener('error', handleWindowErrorEvent)
window.addEventListener('unhandledrejection', handleWindowUnhandledRejectionEvent)
}

View File

@@ -61,7 +61,7 @@ type BoundCallbacks = {
onSubmit: (e) => any
onLoad: (e) => any
onBeforeUnload: (e) => undefined | Promise<undefined>
onUnload: (e) => any
onPageHide: (e: PageTransitionEvent) => any
onNavigation: (...args) => any
onAlert: (str) => any
onConfirm: (str) => boolean
@@ -93,12 +93,18 @@ export const bindToListeners = (contentWindow, callbacks: BoundCallbacks) => {
callbacks.onBeforeUnload(e)
})
addListener(contentWindow, 'unload', (e) => {
// While we must move to pagehide for Chromium, it does not work for our
// needs in Firefox. Until that is addressed, only Chromium uses the pagehide
// event as a proxy for AUT unloads.
const unloadEvent = Cypress.browser.family === 'chromium' ? 'pagehide' : 'unload'
addListener(contentWindow, unloadEvent, (e) => {
// when we unload we need to remove all of the event listeners
removeAllListeners()
// else we know to proceed onwards!
callbacks.onUnload(e)
callbacks.onPageHide(e)
})
addListener(contentWindow, 'hashchange', (e) => {

View File

@@ -117,6 +117,8 @@ class $Cypress {
log: any
isBrowser: any
browserMajorVersion: any
// This is NodeEventEmitter['emit'], but typescript cannot determine that it is
// definitively initialized due to being initialized with $Events.extend(this)
emit: any
emitThen: any
emitMap: any

View File

@@ -1125,7 +1125,10 @@ export class $Cy extends EventEmitter2 implements ITimeouts, IStability, IAssert
// doesn't trigger a confirmation dialog
return undefined
},
onUnload (e) {
onPageHide (e) {
// unload is being actively deprecated/removed by chrome, so for
// compatibility, we are using `window`'s `pagehide` event as a proxy
// for the `window:unload` event that we emit. See: https://github.com/cypress-io/cypress/pull/29525
return cy.Cypress.action('app:window:unload', e)
},
onNavigation (...args) {

View File

@@ -5,6 +5,8 @@ const disabledFeatures = [
// Disables "Enhanced ad privacy in Chrome" dialog
// https://github.com/cypress-io/cypress/issues/29199
'PrivacySandboxSettings4',
// Uncomment to force the deprecation of unload events
// 'DeprecateUnloadByUserAndOrigin',
]
// Common Chrome Flags for Automation
@@ -91,6 +93,9 @@ const DEFAULT_FLAGS = [
// enable precise memory info so performance.memory returns more accurate values
'enable-precise-memory-info',
// Uncomment to force the deprecation of upload events
//`--enable-features=PermissionsPolicyUnload,DeprecateUnload`,
]
// prepend -- to each flag and concatenate them together