mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-22 15:12:27 -05:00
fix(testIsolation): improve the behavior, clarify config options and sync with session command (#24316)
Co-authored-by: Matt Henkes <mjhenkes@gmail.com> Co-authored-by: Bill Glesias <bglesias@gmail.com>
This commit is contained in:
Vendored
+25
-5
@@ -2808,12 +2808,32 @@ declare namespace Cypress {
|
||||
*/
|
||||
supportFile: string | false
|
||||
/**
|
||||
* The test isolation level applied to ensure a clean slate between tests.
|
||||
* - legacy - resets/clears aliases, intercepts, clock, viewport, cookies, and local storage before each test.
|
||||
* - strict - applies all resets/clears from legacy, plus clears the page by visiting 'about:blank' to ensure clean app state before each test.
|
||||
* @default "legacy", however, when experimentalSessionAndOrigin=true, the default is "strict"
|
||||
* The test isolation ensures a clean browser context between tests. This option is only available when
|
||||
* `experimentalSessionAndOrigin=true`.
|
||||
*
|
||||
* Cypress will always reset/clear aliases, intercepts, clock, and viewport before each test
|
||||
* to ensure a clean test slate; i.e. this configuration only impacts the browser context.
|
||||
*
|
||||
* Note: the [`cy.session()`](https://on.cypress.io/session) command will inherent this value to determine whether
|
||||
* or not the page is cleared when the command executes. This command is only available in end-to-end testing.
|
||||
*
|
||||
* - on - The page is cleared before each test. Cookies, local storage and session storage in all domains are cleared
|
||||
* before each test. The `cy.session()` command will also clear the page and current browser context when creating
|
||||
* or restoring the browser session.
|
||||
* - off - The current browser state will persist between tests. The page does not clear before the test and cookies, local
|
||||
* storage and session storage will be available in the next test. The `cy.session()` command will only clear the
|
||||
* current browser context when creating or restoring the browser session - the current page will not clear.
|
||||
*
|
||||
* Tradeoffs:
|
||||
* Turning test isolation off may improve performance of end-to-end tests, however, previous tests could impact the
|
||||
* browser state of the next test and cause inconsistency when using .only(). Be mindful to write isolated tests when
|
||||
* test isolation is off. If a test in the suite impacts the state of other tests and it were to fail, you could see
|
||||
* misleading errors in later tests which makes debugging clunky. See the [documentation](https://on.cypress.io/test-isolation)
|
||||
* for more information.
|
||||
*
|
||||
* @default null, when experimentalSessionAndOrigin=false. The default is 'on' when experimentalSessionAndOrigin=true.
|
||||
*/
|
||||
testIsolation: 'legacy' | 'strict'
|
||||
testIsolation: null | 'on' | 'off'
|
||||
/**
|
||||
* Path to folder where videos will be saved after a headless or CI run
|
||||
* @default "cypress/videos"
|
||||
|
||||
@@ -70,7 +70,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys 1
|
||||
"supportFile": "cypress/support/e2e.{js,jsx,ts,tsx}",
|
||||
"supportFolder": false,
|
||||
"taskTimeout": 60000,
|
||||
"testIsolation": "legacy",
|
||||
"testIsolation": null,
|
||||
"trashAssetsBeforeRuns": true,
|
||||
"userAgent": null,
|
||||
"video": true,
|
||||
@@ -155,7 +155,7 @@ exports['config/src/index .getDefaultValues returns list of public config keys f
|
||||
"supportFile": "cypress/support/e2e.{js,jsx,ts,tsx}",
|
||||
"supportFolder": false,
|
||||
"taskTimeout": 60000,
|
||||
"testIsolation": "legacy",
|
||||
"testIsolation": null,
|
||||
"trashAssetsBeforeRuns": true,
|
||||
"userAgent": null,
|
||||
"video": true,
|
||||
|
||||
@@ -155,7 +155,7 @@ export const matchesConfigKey = (key: string) => {
|
||||
return
|
||||
}
|
||||
|
||||
export const validate = (cfg: any, onErr: (property: ErrResult | string) => void) => {
|
||||
export const validate = (cfg: any, onErr: (property: ErrResult | string) => void, testingType: TestingType | null) => {
|
||||
debug('validating configuration')
|
||||
|
||||
return _.each(cfg, (value, key) => {
|
||||
@@ -163,7 +163,11 @@ export const validate = (cfg: any, onErr: (property: ErrResult | string) => void
|
||||
|
||||
// key has a validation rule & value different from the default
|
||||
if (validationFn && value !== defaultValues[key]) {
|
||||
const result = validationFn(key, value)
|
||||
const result = validationFn(key, value, {
|
||||
testingType,
|
||||
// TODO: remove with experimentalSessionAndOrigin. Fixed with: https://github.com/cypress-io/cypress/issues/21471
|
||||
experimentalSessionAndOrigin: cfg.experimentalSessionAndOrigin,
|
||||
})
|
||||
|
||||
if (result !== true) {
|
||||
return onErr(result)
|
||||
@@ -199,6 +203,9 @@ export const validateOverridableAtRunTime = (config: any, isSuiteLevelOverride:
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: add a hook to ensure valid testing-type configuration is being set at runtime for all configuration values.
|
||||
// https://github.com/cypress-io/cypress/issues/24365
|
||||
|
||||
if (overrideLevel === 'never' || (overrideLevel === 'suite' && !isSuiteLevelOverride)) {
|
||||
onErr({
|
||||
invalidConfigKey: configKey,
|
||||
|
||||
@@ -31,6 +31,11 @@ const BREAKING_OPTION_ERROR_KEY: Readonly<AllCypressErrorNames[]> = [
|
||||
'TEST_FILES_RENAMED',
|
||||
] as const
|
||||
|
||||
type ValidationOptions = {
|
||||
testingType: TestingType | null
|
||||
experimentalSessionAndOrigin: boolean
|
||||
}
|
||||
|
||||
export type BreakingOptionErrorKey = typeof BREAKING_OPTION_ERROR_KEY[number]
|
||||
|
||||
export type OverrideLevel = 'any' | 'suite' | 'never'
|
||||
@@ -90,7 +95,7 @@ export interface BreakingOption {
|
||||
showInLaunchpad?: boolean
|
||||
}
|
||||
|
||||
const isValidConfig = (testingType: string, config: any) => {
|
||||
const isValidConfig = (testingType: string, config: any, opts: ValidationOptions) => {
|
||||
const status = validate.isPlainObject(testingType, config)
|
||||
|
||||
if (status !== true) {
|
||||
@@ -99,7 +104,7 @@ const isValidConfig = (testingType: string, config: any) => {
|
||||
|
||||
for (const rule of options) {
|
||||
if (rule.name in config && rule.validation) {
|
||||
const status = rule.validation(`${testingType}.${rule.name}`, config[rule.name])
|
||||
const status = rule.validation(`${testingType}.${rule.name}`, config[rule.name], opts)
|
||||
|
||||
if (status !== true) {
|
||||
return status
|
||||
@@ -200,6 +205,7 @@ const driverConfigOptions: Array<DriverConfigOption> = [
|
||||
isExperimental: true,
|
||||
requireRestartOnChange: 'server',
|
||||
}, {
|
||||
// TODO: remove with experimentalSessionAndOrigin. Fixed with: https://github.com/cypress-io/cypress/issues/21471
|
||||
name: 'experimentalSessionAndOrigin',
|
||||
defaultValue: false,
|
||||
validation: validate.isBoolean,
|
||||
@@ -373,11 +379,37 @@ const driverConfigOptions: Array<DriverConfigOption> = [
|
||||
name: 'testIsolation',
|
||||
// TODO: https://github.com/cypress-io/cypress/issues/23093
|
||||
// When experimentalSessionAndOrigin is removed and released as GA,
|
||||
// update the defaultValue from 'legacy' to 'strict' and
|
||||
// update the defaultValue from undefined to 'on' and
|
||||
// update this code to remove the check/override specific to enable
|
||||
// strict by default when experimentalSessionAndOrigin=true
|
||||
defaultValue: 'legacy',
|
||||
validation: validate.isOneOf('legacy', 'strict'),
|
||||
// 'on' by default when experimentalSessionAndOrigin=true
|
||||
defaultValue: (options: Record<string, any> = {}) => {
|
||||
if (options.testingType === 'component') {
|
||||
return null
|
||||
}
|
||||
|
||||
return options?.experimentalSessionAndOrigin || options?.config?.e2e?.experimentalSessionAndOrigin ? 'on' : null
|
||||
},
|
||||
validation: (key: string, value: any, opts: ValidationOptions) => {
|
||||
const { testingType, experimentalSessionAndOrigin } = opts
|
||||
|
||||
if (testingType == null || testingType === 'component') {
|
||||
return true
|
||||
}
|
||||
|
||||
if (experimentalSessionAndOrigin && testingType === 'e2e') {
|
||||
return validate.isOneOf('on', 'off')(key, value)
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
return true
|
||||
}
|
||||
|
||||
return {
|
||||
key,
|
||||
value,
|
||||
type: 'not set unless the experimentalSessionAndOrigin flag is turned on',
|
||||
}
|
||||
},
|
||||
overrideLevel: 'suite',
|
||||
}, {
|
||||
name: 'trashAssetsBeforeRuns',
|
||||
|
||||
@@ -60,7 +60,7 @@ export function updateWithPluginValues (cfg: FullConfig, modifiedConfig: any, te
|
||||
}
|
||||
|
||||
return errors.throwErr('CONFIG_VALIDATION_ERROR', 'configFile', configFile, validationResult)
|
||||
})
|
||||
}, testingType)
|
||||
|
||||
debug('validate that there is no breaking config options added by setupNodeEvents')
|
||||
|
||||
|
||||
@@ -401,7 +401,11 @@ export function mergeDefaults (
|
||||
config.baseUrl = url.replace(/\/\/+$/, '/')
|
||||
}
|
||||
|
||||
const defaultsForRuntime = getDefaultValues(options)
|
||||
const defaultsForRuntime = getDefaultValues({
|
||||
...options,
|
||||
// TODO: clean this up. Fixed with: https://github.com/cypress-io/cypress/issues/21471
|
||||
experimentalSessionAndOrigin: config.experimentalSessionAndOrigin,
|
||||
})
|
||||
|
||||
_.defaultsDeep(config, defaultsForRuntime)
|
||||
|
||||
@@ -454,7 +458,7 @@ export function mergeDefaults (
|
||||
}
|
||||
|
||||
return errors.throwErr('CONFIG_VALIDATION_ERROR', null, null, validationResult)
|
||||
})
|
||||
}, testingType)
|
||||
|
||||
config = setAbsolutePaths(config)
|
||||
|
||||
@@ -477,15 +481,15 @@ export function mergeDefaults (
|
||||
}, testingType)
|
||||
|
||||
// TODO: https://github.com/cypress-io/cypress/issues/23093
|
||||
// testIsolation should equal 'strict' by default when experimentalSessionAndOrigin=true
|
||||
// testIsolation should equal 'on' by default when experimentalSessionAndOrigin=true
|
||||
// Once experimentalSessionAndOrigin is made GA, remove this logic and update the defaultValue
|
||||
// to be be 'strict'
|
||||
// to be be 'on'
|
||||
if (testingType === 'e2e' && config.experimentalSessionAndOrigin) {
|
||||
if (config.rawJson.testIsolation) {
|
||||
config.resolved.testIsolation.from = 'config'
|
||||
} else {
|
||||
config.testIsolation = 'strict'
|
||||
config.resolved.testIsolation.value = 'strict'
|
||||
config.testIsolation = 'on'
|
||||
config.resolved.testIsolation.value = 'on'
|
||||
config.resolved.testIsolation.from === 'default'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ describe('config/src/index', () => {
|
||||
|
||||
configUtil.validate({
|
||||
'baseUrl': 'https://',
|
||||
}, errorFn)
|
||||
}, errorFn, 'e2e')
|
||||
|
||||
expect(errorFn).to.have.callCount(0)
|
||||
})
|
||||
@@ -128,7 +128,7 @@ describe('config/src/index', () => {
|
||||
|
||||
configUtil.validate({
|
||||
'baseUrl': ' ',
|
||||
}, errorFn)
|
||||
}, errorFn, 'e2e')
|
||||
|
||||
expect(errorFn).to.have.been.calledWithMatch({ key: 'baseUrl' })
|
||||
expect(errorFn).to.have.been.calledWithMatch({ type: 'a fully qualified URL (starting with `http://` or `https://`)' })
|
||||
@@ -195,7 +195,7 @@ describe('config/src/index', () => {
|
||||
|
||||
const isSuiteOverride = true
|
||||
|
||||
configUtil.validateOverridableAtRunTime({ testIsolation: 'strict' }, isSuiteOverride, errorFn)
|
||||
configUtil.validateOverridableAtRunTime({ testIsolation: 'on' }, isSuiteOverride, errorFn)
|
||||
|
||||
expect(errorFn).to.have.callCount(0)
|
||||
})
|
||||
|
||||
@@ -1074,7 +1074,7 @@ describe('config/src/project/utils', () => {
|
||||
supportFile: { value: false, from: 'config' },
|
||||
supportFolder: { value: false, from: 'default' },
|
||||
taskTimeout: { value: 60000, from: 'default' },
|
||||
testIsolation: { value: 'legacy', from: 'default' },
|
||||
testIsolation: { value: null, from: 'default' },
|
||||
trashAssetsBeforeRuns: { value: true, from: 'default' },
|
||||
userAgent: { value: null, from: 'default' },
|
||||
video: { value: true, from: 'default' },
|
||||
@@ -1190,7 +1190,7 @@ describe('config/src/project/utils', () => {
|
||||
supportFile: { value: false, from: 'config' },
|
||||
supportFolder: { value: false, from: 'default' },
|
||||
taskTimeout: { value: 60000, from: 'default' },
|
||||
testIsolation: { value: 'legacy', from: 'default' },
|
||||
testIsolation: { value: null, from: 'default' },
|
||||
trashAssetsBeforeRuns: { value: true, from: 'default' },
|
||||
userAgent: { value: null, from: 'default' },
|
||||
video: { value: true, from: 'default' },
|
||||
@@ -1206,7 +1206,7 @@ describe('config/src/project/utils', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('sets testIsolation=strict by default when experimentalSessionAndOrigin=true and e2e testing', () => {
|
||||
it('sets testIsolation=on by default when experimentalSessionAndOrigin=true and e2e testing', () => {
|
||||
sinon.stub(utils, 'getProcessEnvVars').returns({})
|
||||
|
||||
const obj = {
|
||||
@@ -1227,7 +1227,7 @@ describe('config/src/project/utils', () => {
|
||||
expect(cfg.resolved).to.have.property('experimentalSessionAndOrigin')
|
||||
expect(cfg.resolved.experimentalSessionAndOrigin).to.deep.eq({ value: true, from: 'config' })
|
||||
expect(cfg.resolved).to.have.property('testIsolation')
|
||||
expect(cfg.resolved.testIsolation).to.deep.eq({ value: 'strict', from: 'default' })
|
||||
expect(cfg.resolved.testIsolation).to.deep.eq({ value: 'on', from: 'default' })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1239,7 +1239,7 @@ describe('config/src/project/utils', () => {
|
||||
supportFile: false,
|
||||
baseUrl: 'http://localhost:8080',
|
||||
experimentalSessionAndOrigin: true,
|
||||
testIsolation: 'legacy',
|
||||
testIsolation: 'on',
|
||||
}
|
||||
|
||||
const options = {
|
||||
@@ -1253,7 +1253,7 @@ describe('config/src/project/utils', () => {
|
||||
expect(cfg.resolved).to.have.property('experimentalSessionAndOrigin')
|
||||
expect(cfg.resolved.experimentalSessionAndOrigin).to.deep.eq({ value: true, from: 'config' })
|
||||
expect(cfg.resolved).to.have.property('testIsolation')
|
||||
expect(cfg.resolved.testIsolation).to.deep.eq({ value: 'legacy', from: 'config' })
|
||||
expect(cfg.resolved.testIsolation).to.deep.eq({ value: 'on', from: 'config' })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -367,7 +367,7 @@ export class ProjectConfigManager {
|
||||
}
|
||||
|
||||
throw getError('CONFIG_VALIDATION_ERROR', 'configFile', file || null, errMsg)
|
||||
})
|
||||
}, this._testingType)
|
||||
|
||||
return validateNoBreakingConfigLaunchpad(
|
||||
config,
|
||||
|
||||
@@ -581,8 +581,8 @@ describe('src/cy/commands/navigation', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('removes window:load listeners when testIsolation=legacy', { testIsolation: 'legacy' }, () => {
|
||||
it('removes 2x for about:blank and first url visit', () => {
|
||||
if (!Cypress.config('experimentalSessionAndOrigin')) {
|
||||
it('removes window:load listeners 2x for about:blank and first url visit when experimentalSessionAndOrigin=false', () => {
|
||||
const listeners = cy.listeners('window:load')
|
||||
|
||||
const winLoad = cy.spy(cy, 'once').withArgs('window:load')
|
||||
@@ -593,17 +593,17 @@ describe('src/cy/commands/navigation', () => {
|
||||
expect(cy.listeners('window:load')).to.deep.eq(listeners)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
describe('removes window:load listeners when testIsolation=strict', () => {
|
||||
describe('removes window:load listeners for first url visit when experimentalSessionAndOrigin=true', () => {
|
||||
it('removes for first url visit', () => {
|
||||
const listeners = cy.listeners('window:load')
|
||||
|
||||
const winLoad = cy.spy(cy, 'once').withArgs('window:load')
|
||||
|
||||
cy.visit('/fixtures/generic.html').then(() => {
|
||||
expect(winLoad).to.be.calledOnce
|
||||
expect(winLoad).to.be.calledOnce // once for $iframe src
|
||||
expect(cy.listeners('window:load')).to.deep.eq(listeners)
|
||||
})
|
||||
})
|
||||
@@ -768,46 +768,48 @@ describe('src/cy/commands/navigation', () => {
|
||||
cy.get('div').should('contain', 'this should fail?')
|
||||
})
|
||||
|
||||
describe('when only hashes are changing when testIsolation=legacy', { testIsolation: 'legacy' }, () => {
|
||||
it('short circuits the visit if the page will not refresh', () => {
|
||||
let count = 0
|
||||
const urls = []
|
||||
if (!Cypress.config('experimentalSessionAndOrigin')) {
|
||||
describe('when only hashes are changing when experimentalSessionAndOrigin=false', () => {
|
||||
it('short circuits the visit if the page will not refresh', () => {
|
||||
let count = 0
|
||||
const urls = []
|
||||
|
||||
cy.on('window:load', () => {
|
||||
urls.push(cy.state('window').location.href)
|
||||
cy.on('window:load', () => {
|
||||
urls.push(cy.state('window').location.href)
|
||||
|
||||
count += 1
|
||||
})
|
||||
count += 1
|
||||
})
|
||||
|
||||
cy
|
||||
// about:blank yes (1)
|
||||
.visit('/fixtures/generic.html?foo#bar') // yes (2)
|
||||
.visit('/fixtures/generic.html?foo#foo') // no (2)
|
||||
.visit('/fixtures/generic.html?bar#bar') // yes (3)
|
||||
.visit('/fixtures/dimensions.html?bar#bar') // yes (4)
|
||||
.visit('/fixtures/dimensions.html?baz#bar') // yes (5)
|
||||
.visit('/fixtures/dimensions.html#bar') // yes (6)
|
||||
.visit('/fixtures/dimensions.html') // yes (7)
|
||||
.visit('/fixtures/dimensions.html#baz') // no (7)
|
||||
.visit('/fixtures/dimensions.html#') // no (7)
|
||||
.then(() => {
|
||||
expect(count).to.eq(7)
|
||||
cy
|
||||
// about:blank yes (1)
|
||||
.visit('/fixtures/generic.html?foo#bar') // yes (2)
|
||||
.visit('/fixtures/generic.html?foo#foo') // no (2)
|
||||
.visit('/fixtures/generic.html?bar#bar') // yes (3)
|
||||
.visit('/fixtures/dimensions.html?bar#bar') // yes (4)
|
||||
.visit('/fixtures/dimensions.html?baz#bar') // yes (5)
|
||||
.visit('/fixtures/dimensions.html#bar') // yes (6)
|
||||
.visit('/fixtures/dimensions.html') // yes (7)
|
||||
.visit('/fixtures/dimensions.html#baz') // no (7)
|
||||
.visit('/fixtures/dimensions.html#') // no (7)
|
||||
.then(() => {
|
||||
expect(count).to.eq(7)
|
||||
|
||||
expect(urls).to.deep.eq([
|
||||
'about:blank',
|
||||
'http://localhost:3500/fixtures/generic.html?foo#bar',
|
||||
'http://localhost:3500/fixtures/generic.html?bar#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html?bar#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html?baz#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html',
|
||||
])
|
||||
expect(urls).to.deep.eq([
|
||||
'about:blank',
|
||||
'http://localhost:3500/fixtures/generic.html?foo#bar',
|
||||
'http://localhost:3500/fixtures/generic.html?bar#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html?bar#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html?baz#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html#bar',
|
||||
'http://localhost:3500/fixtures/dimensions.html',
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
describe('when only hashes are changing when testIsolation=strict', () => {
|
||||
describe('when only hashes are changing when experimentalSessionAndOrigin=true', () => {
|
||||
it('short circuits the visit if the page will not refresh', () => {
|
||||
let count = 0
|
||||
const urls = []
|
||||
@@ -2588,109 +2590,111 @@ describe('src/cy/commands/navigation', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('filters page load events when going back with window navigation when testIsolation=legacy', { testIsolation: 'legacy' }, () => {
|
||||
if (!Cypress.config('experimentalSessionAndOrigin')) {
|
||||
describe('filters page load events when going back with window navigation when experimentalSessionAndOrigin=false', () => {
|
||||
// https://github.com/cypress-io/cypress/issues/19230
|
||||
it('when going back with window navigation', () => {
|
||||
const emit = cy.spy(Cypress, 'emit').log(false).withArgs('navigation:changed')
|
||||
it('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(() => {
|
||||
cy
|
||||
.visit('/fixtures/generic.html')
|
||||
.get('#hashchange').click()
|
||||
.window().then((win) => {
|
||||
return new Promise((resolve) => {
|
||||
cy.once('navigation:changed', resolve)
|
||||
|
||||
win.history.forward()
|
||||
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(() => {
|
||||
cy.get('#dimensions').click()
|
||||
.window().then((win) => {
|
||||
return new Promise((resolve) => {
|
||||
cy.on('navigation:changed', resolve)
|
||||
cy.on('navigation:changed', (event) => {
|
||||
if (event.includes('(load)')) {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
|
||||
win.history.back()
|
||||
})
|
||||
})
|
||||
.then(() => {
|
||||
expect(emit.getCall(0)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
.then(() => {
|
||||
return new Promise((resolve) => {
|
||||
cy.on('navigation:changed', resolve)
|
||||
win.history.back()
|
||||
})
|
||||
})
|
||||
.then(() => {
|
||||
expect(emit.getCall(0)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(1)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
expect(emit.getCall(1)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(2)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
expect(emit.getCall(2)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(3)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
expect(emit.getCall(3)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.getCall(4)).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(5)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.getCall(6)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (before:load)',
|
||||
)
|
||||
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(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(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(9)).to.be.calledWith(
|
||||
'navigation:changed',
|
||||
'page navigation event (load)',
|
||||
)
|
||||
|
||||
expect(emit.getCall(10)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
expect(emit.getCall(10)).to.be.calledWithMatch(
|
||||
'navigation:changed',
|
||||
'hashchange',
|
||||
)
|
||||
|
||||
expect(emit.callCount).to.eq(11)
|
||||
expect(emit.callCount).to.eq(11)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
describe('filters page load events when going back with window navigation when testIsolation=strict', () => {
|
||||
describe('filters page load events when going back with window navigation when experimentalSessionAndOrigin=true', () => {
|
||||
// https://github.com/cypress-io/cypress/issues/19230
|
||||
it('when going back with window navigation', () => {
|
||||
const emit = cy.spy(Cypress, 'emit').log(false).withArgs('navigation:changed')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -110,7 +110,8 @@ describe('driver/src/cypress/validate_config', () => {
|
||||
expect(overrideLevel).to.eq('suite')
|
||||
|
||||
expect(() => {
|
||||
validateConfig(state, { testIsolation: 'strict' })
|
||||
// TODO: remove with experimentalSessionAndOrigin. Fixed with: https://github.com/cypress-io/cypress/issues/21471
|
||||
validateConfig(state, { testIsolation: Cypress.config('experimentalSessionAndOrigin') ? 'on' : null })
|
||||
}).not.to.throw()
|
||||
})
|
||||
|
||||
@@ -127,7 +128,7 @@ describe('driver/src/cypress/validate_config', () => {
|
||||
expect(overrideLevel).to.eq('test')
|
||||
|
||||
expect(() => {
|
||||
validateConfig(state, { testIsolation: 'strict' })
|
||||
validateConfig(state, { testIsolation: 'on' })
|
||||
}).to.throw(`The \`testIsolation\` configuration can only be overridden from a suite-level override.`)
|
||||
})
|
||||
})
|
||||
@@ -192,7 +193,8 @@ describe('driver/src/cypress/validate_config', () => {
|
||||
})
|
||||
|
||||
expect(() => {
|
||||
validateConfig(state, { testIsolation: 'legacy' })
|
||||
// TODO: remove with experimentalSessionAndOrigin. Fixed with: https://github.com/cypress-io/cypress/issues/21471
|
||||
validateConfig(state, { testIsolation: Cypress.config('experimentalSessionAndOrigin') ? 'off' : null })
|
||||
}).not.to.throw()
|
||||
})
|
||||
|
||||
|
||||
@@ -35,11 +35,12 @@ const reset = (test: any = {}) => {
|
||||
// before each test run!
|
||||
previouslyVisitedLocation = undefined
|
||||
|
||||
const { experimentalSessionAndOrigin, testIsolation } = Cypress.config()
|
||||
// TODO: remove with experimentalSessionAndOrigin. Fixed with: https://github.com/cypress-io/cypress/issues/21471
|
||||
const { experimentalSessionAndOrigin } = Cypress.config()
|
||||
|
||||
// make sure we reset that we haven't visited about blank again
|
||||
// strict test isolation resets the navigation history for us.
|
||||
hasVisitedAboutBlank = experimentalSessionAndOrigin && testIsolation === 'strict'
|
||||
hasVisitedAboutBlank = experimentalSessionAndOrigin
|
||||
|
||||
currentlyVisitingAboutBlank = false
|
||||
|
||||
@@ -1220,6 +1221,10 @@ export default (Commands, Cypress, cy, state, config) => {
|
||||
}
|
||||
|
||||
const visit = () => {
|
||||
// REMOVE THIS ONCE GA HITS. Sessions will handle visiting
|
||||
// about blank.
|
||||
// Fixed with: https://github.com/cypress-io/cypress/issues/21471
|
||||
//
|
||||
// if we've visiting for the first time during
|
||||
// a test then we want to first visit about:blank
|
||||
// so that we nuke the previous state. subsequent
|
||||
|
||||
@@ -49,7 +49,12 @@ export default function (Commands, Cypress, cy) {
|
||||
|
||||
Cypress.on('test:before:run:async', () => {
|
||||
if (Cypress.config('experimentalSessionAndOrigin')) {
|
||||
const clearPage = Cypress.config('testIsolation') === 'strict' ? navigateAboutBlank(false) : new Cypress.Promise.resolve()
|
||||
if (Cypress.config('testIsolation') === 'off') {
|
||||
return
|
||||
}
|
||||
|
||||
// Component testing does not support navigation and handles clearing the page via mount utils
|
||||
const clearPage = Cypress.testingType === 'e2e' ? navigateAboutBlank(false) : new Cypress.Promise.resolve()
|
||||
|
||||
return clearPage
|
||||
.then(() => sessions.clearCurrentSessionData())
|
||||
|
||||
@@ -184,6 +184,10 @@ const getPostMessageLocalStorage = (specWindow, origins): Promise<any[]> => {
|
||||
}
|
||||
|
||||
function navigateAboutBlank (session: boolean = true) {
|
||||
if (Cypress.config('testIsolation') === 'off') {
|
||||
return
|
||||
}
|
||||
|
||||
Cypress.action('cy:url:changed', '')
|
||||
|
||||
return Cypress.action('cy:visit:blank', { type: session ? 'session' : 'session-lifecycle' }) as unknown as Promise<void>
|
||||
|
||||
@@ -113,6 +113,12 @@ export const validateConfig = (state: State, config: Record<string, any>, skipCo
|
||||
})
|
||||
}
|
||||
|
||||
config = {
|
||||
// TODO: remove with experimentalSessionAndOrigin. Fixed with: https://github.com/cypress-io/cypress/issues/21471
|
||||
experimentalSessionAndOrigin: Cypress.originalConfig.experimentalSessionAndOrigin,
|
||||
...config,
|
||||
}
|
||||
|
||||
validateConfigValues(config, (errResult: ErrResult | string) => {
|
||||
const stringify = (str) => format(JSON.stringify(str))
|
||||
|
||||
@@ -126,5 +132,5 @@ export const validateConfig = (state: State, config: Record<string, any>, skipCo
|
||||
: `Expected ${format(errResult.key)} to be ${errResult.type}.\n\nInstead the value was: ${stringify(errResult.value)}`
|
||||
|
||||
throw new (state('specWindow').Error)(errMsg)
|
||||
})
|
||||
}, Cypress.testingType)
|
||||
}
|
||||
|
||||
@@ -1195,10 +1195,11 @@ exports['testConfigOverrides / successfully runs valid suite-level-only override
|
||||
(Run Starting)
|
||||
|
||||
┌────────────────────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Cypress: 1.2.3 │
|
||||
│ Browser: FooBrowser 88 │
|
||||
│ Specs: 1 found (valid-suite-only.js) │
|
||||
│ Searched: cypress/e2e/testConfigOverrides/valid-suite-only.js │
|
||||
│ Cypress: 1.2.3 │
|
||||
│ Browser: FooBrowser 88 │
|
||||
│ Specs: 1 found (valid-suite-only.js) │
|
||||
│ Searched: cypress/e2e/testConfigOverrides/valid-suite-only.js │
|
||||
│ Experiments: experimentalSessionAndOrigin=true │
|
||||
└────────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
|
||||
|
||||
@@ -54,18 +54,18 @@ describe('throws error correctly when beforeEach hook', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('throws error when invalid test-level override', { testIsolation: 'legacy' }, () => {
|
||||
it('throws error when invalid test-level override', { testIsolation: 'off' }, () => {
|
||||
shouldNotExecute()
|
||||
})
|
||||
|
||||
it('throws error when invalid config opt in Cypress.config() in test', () => {
|
||||
Cypress.config({ testIsolation: 'legacy' })
|
||||
Cypress.config({ testIsolation: 'off' })
|
||||
shouldNotExecute()
|
||||
})
|
||||
|
||||
describe('throws error when invalid config opt in Cypress.config() in before hook', () => {
|
||||
before(() => {
|
||||
Cypress.config({ testIsolation: 'legacy' })
|
||||
Cypress.config({ testIsolation: 'off' })
|
||||
})
|
||||
|
||||
it('4', () => {
|
||||
@@ -75,7 +75,7 @@ describe('throws error when invalid config opt in Cypress.config() in before hoo
|
||||
|
||||
describe('throws error when invalid config opt in Cypress.config() in beforeEach hook', () => {
|
||||
beforeEach(() => {
|
||||
Cypress.config({ testIsolation: 'legacy' })
|
||||
Cypress.config({ testIsolation: 'off' })
|
||||
})
|
||||
|
||||
it('5', () => {
|
||||
@@ -85,7 +85,7 @@ describe('throws error when invalid config opt in Cypress.config() in beforeEach
|
||||
|
||||
describe('throws error when invalid config opt in Cypress.config() in after hook', () => {
|
||||
after(() => {
|
||||
Cypress.config({ testIsolation: 'legacy' })
|
||||
Cypress.config({ testIsolation: 'off' })
|
||||
})
|
||||
|
||||
it('5', () => {
|
||||
@@ -95,7 +95,7 @@ describe('throws error when invalid config opt in Cypress.config() in after hook
|
||||
|
||||
describe('throws error when invalid config opt in Cypress.config() in afterEach hook', () => {
|
||||
afterEach(() => {
|
||||
Cypress.config({ testIsolation: 'legacy' })
|
||||
Cypress.config({ testIsolation: 'off' })
|
||||
})
|
||||
|
||||
it('5', () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
describe('suite-level-only overrides run as expected', { testIsolation: 'legacy' }, () => {
|
||||
describe('suite-level-only overrides run as expected', { testIsolation: 'off' }, () => {
|
||||
it('1st test passes', () => {
|
||||
cy.visit('https://example.cypress.io')
|
||||
})
|
||||
@@ -13,7 +13,7 @@ describe('suite-level-only overrides run as expected', { testIsolation: 'legacy'
|
||||
})
|
||||
|
||||
describe('nested contexts ', () => {
|
||||
describe('nested suite-level-only overrides run as expected', { testIsolation: 'legacy' }, () => {
|
||||
describe('nested suite-level-only overrides run as expected', { testIsolation: 'off' }, () => {
|
||||
it('1st test passes', () => {
|
||||
cy.visit('https://example.cypress.io')
|
||||
})
|
||||
|
||||
@@ -16,6 +16,7 @@ describe('testConfigOverrides', () => {
|
||||
expectedExitCode: 0,
|
||||
browser: 'electron',
|
||||
config: {
|
||||
experimentalSessionAndOrigin: true,
|
||||
video: false,
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user