Skip to content

Commit 7c9a0c6

Browse files
fix: Only save session if validation succeeds (#24565)
1 parent c9db39f commit 7c9a0c6

File tree

7 files changed

+74
-16
lines changed

7 files changed

+74
-16
lines changed

packages/app/cypress/e2e/runner/sessions.ui.cy.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,46 @@ describe('runner/cypress sessions.ui.spec', {
132132
// cy.percySnapshot() // TODO: restore when Percy CSS is fixed. See https://github.com/cypress-io/cypress/issues/23435
133133
})
134134

135+
// https://github.com/cypress-io/cypress/issues/24208
136+
it('does not save session when validation fails', () => {
137+
loadSpec({
138+
projectName: 'session-and-origin-e2e-specs',
139+
filePath: 'session/new_session_and_fails_validation_retries.cy.js',
140+
failCount: 1,
141+
})
142+
143+
validateSessionsInstrumentPanel(['blank_session'])
144+
145+
cy.contains('Attempt 1').click()
146+
cy.get('.attempt-item').eq(0).within(() => {
147+
cy.contains('validation error')
148+
})
149+
150+
cy.get('.attempt-item').eq(1).within(() => {
151+
cy.contains('validation error')
152+
// when we stored sessions pre-validation, the 2nd attempt would fail
153+
// with this error instead of the validation failing again
154+
cy.contains('session already exists').should('not.exist')
155+
})
156+
})
157+
158+
it('creates, not recreates, session when validation fails then succeeds', () => {
159+
loadSpec({
160+
projectName: 'session-and-origin-e2e-specs',
161+
filePath: 'session/new_session_and_fails_then_succeeds_validation_retries.cy.js',
162+
failCount: 1,
163+
})
164+
165+
validateSessionsInstrumentPanel(['blank_session'])
166+
167+
cy.get('.attempt-item').eq(1).within(() => {
168+
cy.contains('Create new session')
169+
// when we stored sessions pre-validation, the 2nd attempt would
170+
// say "Recreate session"
171+
cy.contains('Recreate session').should('not.exist')
172+
})
173+
})
174+
135175
it('restores session as expected', () => {
136176
loadSpec({
137177
projectName: 'session-and-origin-e2e-specs',

packages/app/cypress/e2e/runner/support/spec-loader.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export const shouldHaveTestResults = ({ passCount, failCount, pendingCount }) =>
22
passCount = passCount || '--'
33
failCount = failCount || '--'
44

5+
cy.get('button.restart', { timeout: 30000 }).should('be.visible') // ensure tests are finished running
56
cy.findByLabelText('Stats', { timeout: 10000 }).within(() => {
67
cy.get('.passed .num', { timeout: 30000 }).should('have.text', `${passCount}`)
78
cy.get('.failed .num', { timeout: 30000 }).should('have.text', `${failCount}`)

packages/driver/cypress/e2e/commands/sessions/manager.cy.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,6 @@ describe('src/cy/commands/sessions/manager.ts', () => {
235235
describe('.sessions', () => {
236236
it('sessions.defineSession()', () => {
237237
const sessionsManager = new SessionsManager(CypressInstance, cy)
238-
const sessionsSpy = cy.stub(sessionsManager, 'setActiveSession')
239238
const setup = cy.stub()
240239
const sess = sessionsManager.sessions.defineSession({ id: '1', setup })
241240

@@ -249,9 +248,6 @@ describe('src/cy/commands/sessions/manager.ts', () => {
249248
sessionStorage: null,
250249
hydrated: false,
251250
})
252-
253-
expect(sessionsSpy).to.be.calledOnce
254-
expect(sessionsSpy.getCall(0).args[0]).to.deep.eq({ 1: sess })
255251
})
256252

257253
it('sessions.clearAllSavedSessions()', async () => {

packages/driver/src/cy/commands/sessions/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,6 @@ export default function (Commands, Cypress, cy) {
145145
validate: options.validate,
146146
cacheAcrossSpecs: options.cacheAcrossSpecs,
147147
})
148-
149-
sessionsManager.registeredSessions.set(id, true)
150148
}
151149

152150
function setSessionLogStatus (status: string) {
@@ -159,7 +157,7 @@ export default function (Commands, Cypress, cy) {
159157
})
160158
}
161159

162-
function createSession (existingSession, recreateSession = false) {
160+
function createSession (existingSession: SessionData, recreateSession = false) {
163161
logGroup(Cypress, {
164162
name: 'session',
165163
displayName: recreateSession ? 'Recreate session' : 'Create new session',
@@ -187,7 +185,6 @@ export default function (Commands, Cypress, cy) {
187185

188186
_.extend(existingSession, data)
189187
existingSession.hydrated = true
190-
await sessions.saveSessionData(existingSession)
191188

192189
_log.set({ consoleProps: () => getConsoleProps(existingSession) })
193190

@@ -348,7 +345,7 @@ export default function (Commands, Cypress, cy) {
348345
* 1. create session
349346
* 2. validate session
350347
*/
351-
const createSessionWorkflow = (existingSession, recreateSession = false) => {
348+
const createSessionWorkflow = (existingSession: SessionData, recreateSession = false) => {
352349
return cy.then(async () => {
353350
setSessionLogStatus(recreateSession ? 'recreating' : 'creating')
354351

@@ -358,11 +355,14 @@ export default function (Commands, Cypress, cy) {
358355
return createSession(existingSession, recreateSession)
359356
})
360357
.then(() => validateSession(existingSession))
361-
.then((isValidSession: boolean) => {
358+
.then(async (isValidSession: boolean) => {
362359
if (!isValidSession) {
363360
return
364361
}
365362

363+
sessionsManager.registeredSessions.set(existingSession.id, true)
364+
await sessions.saveSessionData(existingSession)
365+
366366
setSessionLogStatus(recreateSession ? 'recreated' : 'created')
367367
})
368368
}
@@ -373,7 +373,7 @@ export default function (Commands, Cypress, cy) {
373373
* 2. validate session
374374
* 3. if validation fails, catch error and recreate session
375375
*/
376-
const restoreSessionWorkflow = (existingSession) => {
376+
const restoreSessionWorkflow = (existingSession: SessionData) => {
377377
return cy.then(async () => {
378378
setSessionLogStatus('restoring')
379379
await navigateAboutBlank()

packages/driver/src/cy/commands/sessions/manager.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export default class SessionsManager {
116116
// this the public api exposed to consumers as Cypress.session
117117
sessions = {
118118
defineSession: (options = {} as any): SessionData => {
119-
const sess_state: SessionData = {
119+
return {
120120
id: options.id,
121121
cookies: null,
122122
localStorage: null,
@@ -126,10 +126,6 @@ export default class SessionsManager {
126126
validate: options.validate,
127127
cacheAcrossSpecs: !!options.cacheAcrossSpecs,
128128
}
129-
130-
this.setActiveSession({ [sess_state.id]: sess_state })
131-
132-
return sess_state
133129
},
134130

135131
clearAllSavedSessions: async () => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Used in cy-in-cy tests in @packages/app.
3+
*/
4+
it('t1', { retries: 1 }, () => {
5+
const setupFn = cy.stub()
6+
const validateFn = cy.stub()
7+
8+
validateFn.onCall(0).throws(new Error('validation error'))
9+
validateFn.onCall(1).returns(true)
10+
11+
cy.session('blank_session', setupFn, {
12+
validate: validateFn,
13+
})
14+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Used in cy-in-cy tests in @packages/app.
3+
*/
4+
it('t1', { retries: 1 }, () => {
5+
const setupFn = cy.stub()
6+
const validateFn = cy.stub().throws(new Error('validation error'))
7+
8+
cy.session('blank_session', setupFn, {
9+
validate: validateFn,
10+
})
11+
})

0 commit comments

Comments
 (0)