Skip to content
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

Merged
merged 46 commits into from
Aug 12, 2022

Conversation

emilyrohrbough
Copy link
Member

@emilyrohrbough emilyrohrbough commented Aug 1, 2022

  • Closes Add new testIsolation configuration option #22279

  • add testIsolation option - when experimentalSessionAndOrigin=false, the default value is legacy. When experimentalSessionAndOrigin=true, this the default value is strict.

    • legacy - this is the current test isolation behavior in cypress - clock, viewport, cookies, local storage, etc continue to be reset before each tests
    • strict - all rests from legacy plus clearing the page between each test by visiting 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 with Cypress.config()
    • test:before:run - config altered during event handler with Cypress.config()
    • test:before:run:async - config altered during event handler with Cypress.config()
    • suite - configuration provided as suite override describe('...', { ...configOverides }, ()=>{})
    • test- configuration provided as test override it('...', { ...configOverides }, ()=>{})
    • runtime - configuration altered from runnable (hook or test execution) using Cypress.config()
  • suite - configuration can only be set from the suite level

  • never - configuration is read-only and cannot be altered without restarting Cypress

User facing changelog

  • Ability new configuration option, called testIsolation, has been added to allow you to configured the test isolation mode. This configuration option can be enabled by setting the experimentalSessionAndOrigin flag to true in the Cypress config and defaults to strict mode. Read more about test isolation in Cypress to learn more.

Additional details

How has the user experience changed?

  • Improved error messaging
  • Ability to set the testIsolation intensity when experiementalSessionAndOrigin=true. this preps for when this goes GA and users don't want to update tests.

PR Tasks

@emilyrohrbough emilyrohrbough requested review from a team as code owners August 1, 2022 13:44
@cypress-bot
Copy link
Contributor

cypress-bot bot commented Aug 1, 2022

Thanks for taking the time to open a PR!

@emilyrohrbough emilyrohrbough requested review from AtofStryker, chrisbreiding, mjhenkes and mschile and removed request for a team August 1, 2022 13:45
@emilyrohrbough emilyrohrbough self-assigned this Aug 1, 2022
@cypress
Copy link

cypress bot commented Aug 1, 2022



Test summary

4961 0 59 0Flakiness 1


Run details

Project cypress
Status Passed
Commit 2291a29
Started Aug 12, 2022 4:46 PM
Ended Aug 12, 2022 4:59 PM
Duration 12:29 💡
OS Linux Debian - 11.3
Browser Electron 102

View run in Cypress Dashboard ➡️


Flakiness

cypress/e2e/cypress/proxy-logging.cy.ts Flakiness
1 Proxy Logging > request logging > xhr log has response body/status code when xhr response is logged second

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,
Copy link
Member Author

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?

Copy link
Contributor

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.

Copy link
Member

@mjhenkes mjhenkes Aug 12, 2022

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.

}, {
name: 'numTestsKeptInMemory',
defaultValue: 50,
validation: validate.isNumber,
canUpdateDuringTestTime: true,
overrideLevels: ALL_OVERRIDE_LEVELS,
Copy link
Member Author

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. 😕

Copy link
Contributor

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

Copy link
Contributor

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.

}, {
name: 'port',
defaultValue: null,
validation: validate.isNumber,
canUpdateDuringTestTime: true,
overrideLevels: ALL_OVERRIDE_LEVELS,
Copy link
Member Author

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. 😕

}, {
name: 'projectId',
defaultValue: null,
validation: validate.isString,
canUpdateDuringTestTime: true,
overrideLevels: ALL_OVERRIDE_LEVELS,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here??

Copy link
Contributor

@lmiller1990 lmiller1990 left a 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.

cli/types/cypress.d.ts Outdated Show resolved Hide resolved
packages/config/src/browser.ts Outdated Show resolved Hide resolved
@@ -215,6 +214,19 @@ export function mergeDefaults (
throw makeConfigError(errors.get(err, ...args))
}, testingType)

// TODO: testIsolation should equal 'strict' by default when experimentalSessionAndOrigin=true
Copy link
Contributor

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?

Copy link
Member Author

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.

Copy link
Contributor

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
Copy link
Contributor

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 💯

Copy link
Contributor

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?

Copy link
Contributor

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

packages/config/test/index.spec.ts Outdated Show resolved Hide resolved
emilyrohrbough and others added 2 commits August 2, 2022 09:05
Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>
@@ -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
Copy link
Contributor

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

Copy link
Contributor

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.

Copy link
Member Author

@emilyrohrbough emilyrohrbough Aug 11, 2022

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?

Copy link
Contributor

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.

@chrisbreiding chrisbreiding self-requested a review August 11, 2022 17:28
@@ -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) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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?

Copy link
Member Author

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

@@ -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
Copy link
Contributor

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.

cli/types/cypress.d.ts Outdated Show resolved Hide resolved
}, {
name: 'arch',
defaultValue: () => os.arch(),
validation: validate.isString,
canUpdateDuringTestTime: false,
Copy link
Member

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

Comment on lines +89 to +95
// 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
Copy link
Contributor

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

Copy link
Member Author

@emilyrohrbough emilyrohrbough Aug 12, 2022

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>
@@ -61,7 +61,7 @@ describe('testConfigOverrides', () => {
spec: 'testConfigOverrides/invalid.js',
snapshot: true,
browser: browserList,
expectedExitCode: 8,
expectedExitCode: 14,
Copy link
Contributor

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?

Copy link
Member Author

@emilyrohrbough emilyrohrbough Aug 12, 2022

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

@emilyrohrbough emilyrohrbough changed the title feat: add testIsolation option and support config override levels feat: add testIsolation option and support describe-only overrides Aug 12, 2022
@emilyrohrbough emilyrohrbough merged commit 0729a68 into develop Aug 12, 2022
@emilyrohrbough emilyrohrbough deleted the test-isolation-config-opt-2 branch August 12, 2022 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add new testIsolation configuration option
5 participants