chore: Update Chrome (stable) to 96.0.4664.110 and Chrome (beta) to 97.0.4692.45 (#18996)

Co-authored-by: Chris Breiding <chrisbreiding@users.noreply.github.com>
Co-authored-by: cypress-bot[bot] <2f0651858c6e38e0+cypress-bot[bot]@users.noreply.github.com>
Co-authored-by: Emily Rohrbough <emilyrohrbough@users.noreply.github.com>
Co-authored-by: Emily Rohrbough <emilyrohrbough@yahoo.com>
This commit is contained in:
github-actions[bot]
2021-12-15 13:56:29 -06:00
committed by GitHub
parent 52dfe1ef91
commit ec84f660be
7 changed files with 439 additions and 54 deletions

View File

@@ -1,4 +1,4 @@
{
"chrome:beta": "96.0.4664.45",
"chrome:stable": "96.0.4664.45"
"chrome:beta": "97.0.4692.45",
"chrome:stable": "96.0.4664.110"
}

View File

@@ -315,6 +315,59 @@ describe('src/cy/commands/navigation', () => {
})
})
it('handles hashchange events', () => {
const emit = cy.spy(Cypress, 'emit').log(false).withArgs('url:changed')
cy
.visit('/fixtures/generic.html')
.get('#hashchange').click()
.then(() => {
cy.go('back')
cy.go('forward')
cy.get('#dimensions').click()
cy.go('back')
cy.go('back')
})
.then(function () {
expect(emit.firstCall).to.be.calledWith(
'url:changed',
'http://localhost:3500/fixtures/generic.html',
)
expect(emit.secondCall).to.be.calledWith(
'url:changed',
'http://localhost:3500/fixtures/generic.html#hashchange',
)
expect(emit.thirdCall).to.be.calledWith(
'url:changed',
'http://localhost:3500/fixtures/generic.html',
)
expect(emit.getCall(3)).to.be.calledWith(
'url:changed',
'http://localhost:3500/fixtures/generic.html#hashchange',
)
expect(emit.getCall(4)).to.be.calledWith(
'url:changed',
'http://localhost:3500/fixtures/dimensions.html',
)
expect(emit.getCall(5)).to.be.calledWith(
'url:changed',
'http://localhost:3500/fixtures/generic.html#hashchange',
)
expect(emit.getCall(6)).to.be.calledWith(
'url:changed',
'http://localhost:3500/fixtures/generic.html',
)
expect(emit.callCount).to.eq(7)
})
})
it('removes listeners', () => {
cy
.visit('/fixtures/generic.html')
@@ -1440,30 +1493,6 @@ describe('src/cy/commands/navigation', () => {
})
it('throws attempting to visit 2 unique ip addresses', function (done) {
const $autIframe = cy.state('$autIframe')
const load = () => {
return $autIframe.trigger('load')
}
cy.stub(Cypress, 'backend')
.withArgs('resolve:url')
.resolves({
isOkStatusCode: true,
isHtml: true,
url: 'http://127.0.0.1:3500',
})
// whenever we're told to change the src
// just fire the load event directly on the $autIframe
cy.stub(Cypress.utils, 'iframeSrc').callsFake(load)
// make it seem like we're already on http://127.0.0.1:3500
const one = Cypress.Location.create('http://127.0.0.1:3500/fixtures/generic.html')
cy.stub(Cypress.utils, 'locExisting')
.returns(one)
cy.on('fail', (err) => {
const { lastLog } = this
@@ -2136,8 +2165,7 @@ describe('src/cy/commands/navigation', () => {
})
})
// this tests isLoading spinner
// and page load event
// this tests isLoading spinner and page load event
context('#page loading', () => {
beforeEach(function () {
this.logs = []
@@ -2453,6 +2481,105 @@ describe('src/cy/commands/navigation', () => {
})
})
// https://github.com/cypress-io/cypress/issues/19230
it('filters page load events when going back with window navigation', () => {
const emit = cy.spy(Cypress, 'emit').log(false).withArgs('navigation:changed')
cy
.visit('/fixtures/generic.html')
.get('#hashchange').click()
.window().then((win) => {
return new Promise((resolve) => {
cy.once('navigation:changed', resolve)
win.history.back()
}).then(() => {
return new Promise((resolve) => {
cy.once('navigation:changed', resolve)
win.history.forward()
})
})
})
cy.get('#dimensions').click()
.window().then((win) => {
return new Promise((resolve) => {
cy.on('navigation:changed', (event) => {
if (event.includes('(load)')) {
resolve()
}
})
win.history.back()
})
.then(() => {
return new Promise((resolve) => {
cy.on('navigation:changed', resolve)
win.history.back()
})
})
.then(() => {
expect(emit.firstCall).to.be.calledWith(
'navigation:changed',
'page navigation event (load)',
)
expect(emit.secondCall).to.be.calledWith(
'navigation:changed',
'page navigation event (before:load)',
)
expect(emit.thirdCall).to.be.calledWith(
'navigation:changed',
'page navigation event (load)',
)
expect(emit.getCall(3)).to.be.calledWithMatch(
'navigation:changed',
'hashchange',
)
expect(emit.getCall(4)).to.be.calledWithMatch(
'navigation:changed',
'hashchange',
)
expect(emit.getCall(5)).to.be.calledWithMatch(
'navigation:changed',
'hashchange',
)
expect(emit.getCall(6)).to.be.calledWith(
'navigation:changed',
'page navigation event (before:load)',
)
expect(emit.getCall(7)).to.be.calledWith(
'navigation:changed',
'page navigation event (load)',
)
expect(emit.getCall(8)).to.be.calledWith(
'navigation:changed',
'page navigation event (before:load)',
)
expect(emit.getCall(9)).to.be.calledWith(
'navigation:changed',
'page navigation event (load)',
)
expect(emit.getCall(10)).to.be.calledWithMatch(
'navigation:changed',
'hashchange',
)
expect(emit.callCount).to.eq(11)
})
})
})
it('logs url changed event', () => {
cy
.visit('/fixtures/generic.html')

View File

@@ -0,0 +1,190 @@
import { $Location } from '../../../src/cypress/location'
import $SetterGetter from '../../../src/cypress/setter_getter'
import { bothUrlsMatchAndOneHasHash, historyNavigationTriggeredHashChange } from '../../../src/cy/navigation'
describe('cy/navigation', () => {
describe('.bothUrlsMatchAndOneHasHash', () => {
describe('matches remote url', () => {
it('when remote url has hash', () => {
const current = $Location.create('https://my_url.com')
const remote = $Location.create('https://my_url.com#hash')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote)
expect(isMatch).to.be.true
})
})
describe('does not match remote url', () => {
it('when only current url has hash', () => {
const current = $Location.create('https://my_url.com#hash')
const remote = $Location.create('https://my_url.com')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote)
expect(isMatch).to.be.false
})
it('when remote url is missing hash', () => {
const current = $Location.create('https://my_url.com')
const remote = $Location.create('https://my_url.com')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote)
expect(isMatch).to.be.false
})
it('when origins are different', () => {
const current = $Location.create('https://my_url.com')
const remote = $Location.create('https://my_other_url.com#hash')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote)
expect(isMatch).to.be.false
})
it('when pathnames are different', () => {
const current = $Location.create('https://my_url.com/home')
const remote = $Location.create('https://my_url.com/random_page#hash')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote)
expect(isMatch).to.be.false
})
it('when search is different', () => {
const current = $Location.create('https://my_url.com/home?user=1')
const remote = $Location.create('https://my_url.com/home#hash?user=1')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote)
expect(isMatch).to.be.false
})
})
describe('matches least one url', () => {
it('when current url has hash', () => {
const current = $Location.create('https://my_url.com#title')
const remote = $Location.create('https://my_url.com')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote, true)
expect(isMatch).to.be.true
})
it('when remote url has hash', () => {
const current = $Location.create('https://my_url.com')
const remote = $Location.create('https://my_url.com#title')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote, true)
expect(isMatch).to.be.true
})
it('when both urls have a hash', () => {
const current = $Location.create('https://my_url.com#home')
const remote = $Location.create('https://my_url.com#title')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote, true)
expect(isMatch).to.be.true
})
})
describe('does not match either url', () => {
it('when neither url has hash', () => {
const current = $Location.create('https://my_url.com')
const remote = $Location.create('https://my_url.com')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote, true)
expect(isMatch).to.be.false
})
it('when origins are different', () => {
const current = $Location.create('https://my_url.com')
const remote = $Location.create('https://my_other_url.com#hash')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote, true)
expect(isMatch).to.be.false
})
it('when pathnames are different', () => {
const current = $Location.create('https://my_url.com/home')
const remote = $Location.create('https://my_url.com/random_page#hash')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote, true)
expect(isMatch).to.be.false
})
it('when search is different', () => {
const current = $Location.create('https://my_url.com/home?user=1')
const remote = $Location.create('https://my_url.com/home#hash?user=1')
const isMatch = bothUrlsMatchAndOneHasHash(current, remote, true)
expect(isMatch).to.be.false
})
})
})
describe('.historyNavigationTriggeredHashChange', () => {
it('when no navHistoryDelta in state', () => {
const state = $SetterGetter.create({ })
const triggeredHashChange = historyNavigationTriggeredHashChange(state)
expect(triggeredHashChange).to.be.false
})
it('when navHistoryDelta is 0', () => {
const state = $SetterGetter.create({
navHistoryDelta: 0,
})
const triggeredHashChange = historyNavigationTriggeredHashChange(state)
expect(triggeredHashChange).to.be.false
})
it('when navHistoryDelta is null', () => {
const state = $SetterGetter.create({
navHistoryDelta: null,
})
const triggeredHashChange = historyNavigationTriggeredHashChange(state)
expect(triggeredHashChange).to.be.false
})
it('when neither url has a hash', () => {
const state = $SetterGetter.create({
navHistoryDelta: 1,
urlPosition: 0,
url: 'https://my_url.com/',
urls: ['https://my_url.com/', 'https://my_url.com/home'],
})
const triggeredHashChange = historyNavigationTriggeredHashChange(state)
expect(triggeredHashChange).to.be.false
})
it('when one url has a hash and navigation moves forward in history', () => {
const state = $SetterGetter.create({
navHistoryDelta: 1,
urlPosition: 0,
url: 'https://my_url.com/home',
urls: ['https://my_url.com/home', 'https://my_url.com/home#hash'],
})
const triggeredHashChange = historyNavigationTriggeredHashChange(state)
expect(triggeredHashChange).to.be.true
expect(state('navHistoryDelta')).to.eq(1)
})
it('when one url has a hash and navigation moves back in history', () => {
const state = $SetterGetter.create({
navHistoryDelta: -1,
urlPosition: 1,
url: 'https://my_url.com/home#hash',
urls: ['https://my_url.com/home', 'https://my_url.com/home#hash'],
})
const triggeredHashChange = historyNavigationTriggeredHashChange(state)
expect(triggeredHashChange).to.be.true
expect(state('navHistoryDelta')).to.eq(-1)
})
})
})

View File

@@ -8,6 +8,7 @@ import Promise from 'bluebird'
import $utils from '../../cypress/utils'
import $errUtils from '../../cypress/error_utils'
import $Log from '../../cypress/log'
import { bothUrlsMatchAndOneHasHash } from '../navigation'
import { $Location } from '../../cypress/location'
import debugFn from 'debug'
@@ -60,19 +61,6 @@ const timedOutWaitingForPageLoad = (ms, log) => {
})
}
const bothUrlsMatchAndRemoteHasHash = (current, remote) => {
// the remote has a hash
// or the last char of href
// is a hash
return (remote.hash || remote.href.slice(-1) === '#') &&
// both must have the same origin
current.origin === remote.origin &&
// both must have the same pathname
current.pathname === remote.pathname &&
// both must have the same query params
current.search === remote.search
}
const cannotVisitDifferentOrigin = (origin, previousUrlVisited, remoteUrl, existingUrl, log) => {
const differences = []
@@ -129,13 +117,18 @@ const navigationChanged = (Cypress, cy, state, source, arg) => {
}
// start storing the history entries
const urls = state('urls') || []
let urls = state('urls') || []
let urlPosition = state('urlPosition')
const previousUrl = _.last(urls)
if (urlPosition === undefined) {
urlPosition = -1
}
// ensure our new url doesnt match whatever
const previousUrl = urls[urlPosition]
// ensure our new url doesn't match whatever
// the previous was. this prevents logging
// additionally when the url didnt actually change
// additionally when the url didn't actually change
if (url === previousUrl) {
return
}
@@ -143,11 +136,23 @@ const navigationChanged = (Cypress, cy, state, source, arg) => {
// else notify the world and log this event
Cypress.action('cy:url:changed', url)
urls.push(url)
const navHistoryDelta = state('navHistoryDelta')
// if navigation was changed via a manipulation of the browser session we
// need to update the urlPosition to match the position of the history stack
// and we do not need to push a new url onto the urls state
if (navHistoryDelta) {
urlPosition = urlPosition + navHistoryDelta
state('navHistoryDelta', undefined)
} else {
urls = urls.slice(0, urlPosition + 1)
urls.push(url)
urlPosition = urlPosition + 1
}
state('urls', urls)
state('url', url)
state('urlPosition', urlPosition)
// don't output a command log for 'load' or 'before:load' events
// return if source in command
@@ -573,10 +578,8 @@ export default (Commands, Cypress, cy, state, config) => {
})
},
go (numberOrString, options = {}) {
const userOptions = options
options = _.defaults({}, userOptions, {
go (numberOrString, userOptions = {}) {
const options = _.defaults({}, userOptions, {
log: true,
timeout: config('pageLoadTimeout'),
})
@@ -918,7 +921,7 @@ export default (Commands, Cypress, cy, state, config) => {
// the browser won't actually make a new http request
// for this, and so we need to resolve onLoad immediately
// and bypass the actual visit resolution stuff
if (bothUrlsMatchAndRemoteHasHash(current, remote)) {
if (bothUrlsMatchAndOneHasHash(current, remote)) {
// https://github.com/cypress-io/cypress/issues/1311
if (current.hash === remote.hash) {
consoleProps['Note'] = 'Because this visit was to the same hash, the page did not reload and the onBeforeLoad and onLoad callbacks did not fire.'

View File

@@ -4,6 +4,7 @@ import _ from 'lodash'
import { handleInvalidEventTarget, handleInvalidAnchorTarget } from './top_attr_guards'
const HISTORY_ATTRS = 'pushState replaceState'.split(' ')
const HISTORY_NAV_ATTRS = 'go back forward'.split(' ')
let events = []
let listenersAdded = null
@@ -77,6 +78,20 @@ export default {
callbacks.onNavigation('hashchange', e)
})
for (let attr of HISTORY_NAV_ATTRS) {
const orig = contentWindow.history?.[attr]
if (!orig) {
continue
}
contentWindow.history[attr] = function (delta) {
callbacks.onHistoryNav(attr === 'back' ? -1 : (attr === 'forward' ? 1 : delta))
orig.apply(this, [delta])
}
}
for (let attr of HISTORY_ATTRS) {
const orig = contentWindow.history?.[attr]

View File

@@ -0,0 +1,35 @@
import { $Location } from '../cypress/location'
export const bothUrlsMatchAndOneHasHash = (current, remote, eitherShouldHaveHash: boolean = false): boolean => {
// the current has a hash or the last char of href is a hash
const currentHasHash = current.hash || current.href.slice(-1) === '#'
// the remote has a hash or the last char of href is a hash
const remoteHasHash = remote.hash || remote.href.slice(-1) === '#'
const urlHasHash = eitherShouldHaveHash ? (remoteHasHash || currentHasHash) : remoteHasHash
return urlHasHash &&
// both must have the same origin
current.origin === remote.origin &&
// both must have the same pathname
current.pathname === remote.pathname &&
// both must have the same query params
current.search === remote.search
}
export const historyNavigationTriggeredHashChange = (state): boolean => {
const delta = state('navHistoryDelta') || 0
if (delta === 0 || delta == null) { // page refresh
return false
}
const urls = state('urls')
const urlPosition = state('urlPosition')
const currentUrl = $Location.create(urls[urlPosition])
const nextPosition = urlPosition + delta
const nextUrl = $Location.create(urls[nextPosition])
return bothUrlsMatchAndOneHasHash(currentUrl, nextUrl, true)
}

View File

@@ -34,6 +34,7 @@ import { $Command } from './command'
import { CommandQueue } from './command_queue'
import { initVideoRecorder } from '../cy/video-recorder'
import { TestConfigOverride } from '../cy/testConfigOverrides'
import { historyNavigationTriggeredHashChange } from '../cy/navigation'
const debugErrors = debugFn('cypress:driver:errors')
@@ -480,8 +481,19 @@ export class $Cy implements ITimeouts, IStability, IAssertions, IRetries, IJQuer
// proxy has not injected Cypress.action('window:before:load')
// so Cypress.onBeforeAppWindowLoad() was never called
return $autIframe.on('load', () => {
// if setting these props failed
// then we know we're in a cross origin failure
if (historyNavigationTriggeredHashChange(this.state)) {
// Skip load event.
// Chromium 97+ triggers fires iframe onload for cross-origin-initiated same-document
// navigations to make it appear to be a cross-document navigation, even when it wasn't
// to alleviate security risk where a cross-origin initiator can check whether
// or not onload fired to guess the url of a target frame.
// When the onload is fired, neither the before:unload or unload event is fired to remove
// the attached listeners or to clean up the current page state.
// https://github.com/cypress-io/cypress/issues/19230
return
}
// if setting these props failed then we know we're in a cross origin failure
try {
setWindowDocumentProps(getContentWindow($autIframe), this.state)
@@ -1080,6 +1092,9 @@ export class $Cy implements ITimeouts, IStability, IAssertions, IRetries, IJQuer
// uncaught exception behavior (logging to console)
return undefined
},
onHistoryNav (delta) {
cy.state('navHistoryDelta', delta)
},
onSubmit (e) {
return cy.Cypress.action('app:form:submitted', e)
},