-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add testIsolation option and support describe-only overrides #23040
Conversation
…fig override levels during run-time
Thanks for taking the time to open a PR!
|
Test summaryRun details
View run in Cypress Dashboard ➡️ Flakiness
This comment has been generated by cypress-bot as a result of this project's GitHub integration settings. You can manage this integration in this project's settings in the Cypress Dashboard |
requireRestartOnChange: 'server', | ||
}, { | ||
name: 'excludeSpecPattern', | ||
defaultValue: (options: Record<string, any> = {}) => options.testingType === 'component' ? ['**/__snapshots__/*', '**/__image_snapshots__/*'] : '*.hot-update.js', | ||
validation: validate.isStringOrArrayOfStrings, | ||
canUpdateDuringTestTime: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking at this....why can this update at test time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it should be able to - probably incorrect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thats funny, what would overriding this even do at test time? Probably nothing.
packages/config/src/options.ts
Outdated
}, { | ||
name: 'numTestsKeptInMemory', | ||
defaultValue: 50, | ||
validation: validate.isNumber, | ||
canUpdateDuringTestTime: true, | ||
overrideLevels: ALL_OVERRIDE_LEVELS, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same with this opt...this shouldn't be able to update. 😕
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we do something in our support file to set this dynamically if in run mode to test snapshots. We probably could configure it differently
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe there's not a great reason to change this during a test, but it's still technically possible, right? I think that's the main consideration, whether you can instead of whether you should.
packages/config/src/options.ts
Outdated
}, { | ||
name: 'port', | ||
defaultValue: null, | ||
validation: validate.isNumber, | ||
canUpdateDuringTestTime: true, | ||
overrideLevels: ALL_OVERRIDE_LEVELS, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same with this opt...this shouldn't be able to update. 😕
packages/config/src/options.ts
Outdated
}, { | ||
name: 'projectId', | ||
defaultValue: null, | ||
validation: validate.isString, | ||
canUpdateDuringTestTime: true, | ||
overrideLevels: ALL_OVERRIDE_LEVELS, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and here??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some misc comments/questions.
packages/server/lib/config.ts
Outdated
@@ -215,6 +214,19 @@ export function mergeDefaults ( | |||
throw makeConfigError(errors.get(err, ...args)) | |||
}, testingType) | |||
|
|||
// TODO: testIsolation should equal 'strict' by default when experimentalSessionAndOrigin=true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will we eventually apply strict
to everything? My understanding is we have strict
for cy.origin
to make it more reliable (clean slate). Since this seems ideal for all tests, is the intention to make this the default in Cypress 11?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great question! When Origin & Sessions go GA, we will default to strict
. Long-term this would be the only "mode", however we've received some concerns/pushback in user-feedback outreach that enforcing strict
mode without an option to remove the "clear page" before each test would cause some large re-writes for some and is might be a show-stopper for future upgrades. Overall I think strict
is what users should want to leverage, but there are some scenarios where this would be nice to turn off to allow more modular tests if the tests aren't altering page/app state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cy.origin
is tied to and dependent on the sessions feature because it enables clearing cookies/localstorage/etc for all origins, whereas historically it's only been cleared for the primary origin. Test isolation is a separate feature that, as you intuited, should benefit all e2e tests regardless of whether or not they use cy.origin
.
@@ -33,6 +33,23 @@ describe('testConfigOverrides', () => { | |||
}, | |||
}) | |||
|
|||
// FIXME: bug in runner causes browser to hang in run mode when test:before:run throws an exception |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, the tests throughout this PR look very thorough 💯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an issue for following up on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this one #23039
Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>
packages/config/src/options.ts
Outdated
@@ -27,31 +29,35 @@ export type BreakingOptionErrorKey = | |||
| 'RENAMED_CONFIG_OPTION' | |||
| 'TEST_FILES_RENAMED' | |||
|
|||
type TestingType = 'e2e' | 'component' | |||
// The test-time override levels | |||
export const ALL_OVERRIDE_LEVELS = ['code', 'event', 'suite', 'test', 'runtime'] as const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be useful to have the comment above this explain what each level means
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having trouble discerning the practical difference between 'code' and 'runtime'. It seems that 'code' is when setting config at the top/global level and 'runtime' is when it's inside a test, but it seems like the result is the same either way, since they're handled the same way during validation. The only difference seems to be when crafting the error message, but it seems like that's just a nicety that could apply to the 'code' error as well. Is there a case where a config value would only be overrideable at 'code' level and not 'runtime' or vice versa? If not, it seems like they could be merged into the same value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the code level is either when the support file or spec file is loaded before mocha has processed the spec to determine the suites/tests/runnable in the code. Originally I had named it "supportFile" but found it could be when either support file or spec file was loaded into the AUT.
I do think from a messaging standpoint the delineation is helpful since runTime is when a mocha runnable itself is executing, but maybe it's not? 🤷🏻♀️ are you confused on the difference between the two and/or are you wanting "slimmer" override levels and messaging?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the difference now - thanks for the clarification. If there's a use-case for having them separate, that's fine, but I would suggest renaming them to something like runtime-global
and runtime-test
. However, if there's not a use-case, I'd say YAGNI 😅 It's an internal API, so we can always change it later if a use-case emerges.
packages/config/src/browser.ts
Outdated
@@ -172,16 +193,24 @@ export const validateNoBreakingTestingTypeConfig = (cfg: any, testingType: keyof | |||
return validateNoBreakingOptions(options, cfg, onWarning, onErr, testingType) | |||
} | |||
|
|||
export const validateNoReadOnlyConfig = (config: any, onErr: (property: string) => void) => { | |||
let errProperty | |||
export const validateOverridableAtTestTest = (config: any, overrideLevel: Exclude<OverrideLevel, 'never'>, onErr: (result: InvalidTestOverrideResult) => void) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
export const validateOverridableAtTestTest = (config: any, overrideLevel: Exclude<OverrideLevel, 'never'>, onErr: (result: InvalidTestOverrideResult) => void) => { | |
export const validateOverridableAtTestTime = (config: any, overrideLevel: Exclude<OverrideLevel, 'never'>, onErr: (result: InvalidTestOverrideResult) => void) => { |
Or validateOverridableAtRuntime
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yes. good call out. I wanted to call this testTime, since that's what it in in my head, but I went with runtime to align with the resolve level of 'runtime' we use to pass to the cloud
packages/config/src/options.ts
Outdated
@@ -27,31 +29,35 @@ export type BreakingOptionErrorKey = | |||
| 'RENAMED_CONFIG_OPTION' | |||
| 'TEST_FILES_RENAMED' | |||
|
|||
type TestingType = 'e2e' | 'component' | |||
// The test-time override levels | |||
export const ALL_OVERRIDE_LEVELS = ['code', 'event', 'suite', 'test', 'runtime'] as const |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having trouble discerning the practical difference between 'code' and 'runtime'. It seems that 'code' is when setting config at the top/global level and 'runtime' is when it's inside a test, but it seems like the result is the same either way, since they're handled the same way during validation. The only difference seems to be when crafting the error message, but it seems like that's just a nicety that could apply to the 'code' error as well. Is there a case where a config value would only be overrideable at 'code' level and not 'runtime' or vice versa? If not, it seems like they could be merged into the same value.
}, { | ||
name: 'arch', | ||
defaultValue: () => os.arch(), | ||
validation: validate.isString, | ||
canUpdateDuringTestTime: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i like that this defaults to false now, much cleaner
// The run-time override levels (listed in order applied): | ||
// fileLoad - config override via Cypress.config() when either loading the supportFile or specFile in the | ||
// browser (this is before mocha as process the spec | ||
// suite - config override via describe('', {...}, () => {}) | ||
// test - config override via it('', {...}, () => {}) | ||
// event - config override via Cypress.config() in test:before:runner or test:before:runner:async event | ||
// runtime - config override via Cypress.config() when the test callback is executed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment should be updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left it intentionally to document the order these overrides are applied since the aren't documented and not easy to find in the code base without stepping through the runner code.
Co-authored-by: Matt Henkes <mjhenkes@gmail.com>
Co-authored-by: Matt Henkes <mjhenkes@gmail.com>
@@ -61,7 +61,7 @@ describe('testConfigOverrides', () => { | |||
spec: 'testConfigOverrides/invalid.js', | |||
snapshot: true, | |||
browser: browserList, | |||
expectedExitCode: 8, | |||
expectedExitCode: 14, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dare I ask what exit code 14
is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup! there are 14 failing tests in the invalid.js file
Closes Add new testIsolation configuration option #22279
add testIsolation option - when
experimentalSessionAndOrigin=false
, the default value islegacy
. WhenexperimentalSessionAndOrigin=true
, this the default value isstrict
.about:blank
. This ensures app state is reset/clean for each test.add support for config override levels - this allows each option to determine what override levels are supported. The override levels are:
any
- can be set from any level which includes:code
- when either the support file or spec file is loaded and config is altered withCypress.config()
test:before:run
- config altered during event handler withCypress.config()
test:before:run:async
- config altered during event handler withCypress.config()
suite
- configuration provided as suite overridedescribe('...', { ...configOverides }, ()=>{})
test
- configuration provided as test overrideit('...', { ...configOverides }, ()=>{})
runtime
- configuration altered from runnable (hook or test execution) using Cypress.config()suite
- configuration can only be set from the suite levelnever
- configuration is read-only and cannot be altered without restarting CypressUser facing changelog
experimentalSessionAndOrigin
flag totrue
in the Cypress config and defaults tostrict
mode. Read more about test isolation in Cypress to learn more.Additional details
test:before:run
does not handle thrown errors correctly ([driver] errors thrown from test:before:run are incorrectly handled #23039) so config validation is currently skipped for this hook.How has the user experience changed?
testIsolation
intensity whenexperiementalSessionAndOrigin=true
. this preps for when this goes GA and users don't want to update tests.PR Tasks
cypress-documentation
? add docs on new testIsolation configuration option. cypress-documentation#4634type definitions
?