Skip to content

Commit e59949f

Browse files
authored
internal: (studio) fix studio and runner states during opening, closing, and refreshing (#32153)
* internal: (studio) fix studio states during opening, closing, and refreshing * test title * fix tests * feedback * add test coverage * Update packages/app/cypress/e2e/studio/studio.cy.ts * Update packages/app/src/store/studio-store.ts
1 parent b2992fe commit e59949f

File tree

4 files changed

+110
-11
lines changed

4 files changed

+110
-11
lines changed

packages/app/cypress/e2e/studio/studio.cy.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,27 @@ describe('studio functionality', () => {
760760
cy.location().its('hash').and('not.contain', 'testId=').and('not.contain', 'studio=')
761761
})
762762

763+
it('does not prompt for a URL until studio is active', () => {
764+
launchStudio({ specName: 'spec-w-visit.cy.js', createNewTestFromSuite: true })
765+
cy.location().its('hash').should('contain', 'suiteId=r2').and('contain', 'studio=')
766+
cy.waitForSpecToFinish()
767+
768+
cy.findByTestId('aut-url-input').should('have.value', 'http://localhost:4455/cypress/e2e/index.html')
769+
})
770+
771+
it('does not reload the page if we didnt open a test in studio', () => {
772+
launchStudio({ specName: 'spec-w-visit.cy.js', createNewTestFromSuite: true })
773+
774+
// set a property on the window to see if the page reloads
775+
cy.window().then((w) => w['beforeReload'] = true)
776+
777+
// close new test mode
778+
cy.findByTestId('studio-header-studio-button').click()
779+
780+
// if this property is still set on the window, then the page didn't reload
781+
cy.window().then((w) => expect(w['beforeReload']).to.be.true)
782+
})
783+
763784
it('removes the studio url parameters when closing studio new test', () => {
764785
launchStudio({ specName: 'spec-w-visit.cy.js', createNewTestFromSuite: true })
765786

@@ -770,6 +791,68 @@ describe('studio functionality', () => {
770791
cy.location().its('hash').and('not.contain', 'suiteId=').and('not.contain', 'studio=')
771792
})
772793

794+
it('stays in new test mode when studio panel is opened when the spec is running', () => {
795+
loadProjectAndRunSpec()
796+
797+
cy.waitForSpecToFinish()
798+
799+
cy.findByTestId('studio-button').click()
800+
cy.findByTestId('studio-panel').should('be.visible')
801+
cy.findByTestId('new-test-button').should('be.visible')
802+
803+
// Verify we're initially in new test mode
804+
cy.location().its('hash').should('contain', 'suiteId=r1').and('not.contain', 'testId=')
805+
806+
// Now restart the spec, which will call interceptTest with the running test
807+
// This is where the bug would manifest - it would incorrectly switch from
808+
// "new test" mode to "edit the running test" mode
809+
cy.get('button.restart').click()
810+
811+
cy.get('.test').should('have.length', 1)
812+
cy.get('.test').first().should('have.class', 'runnable-active')
813+
814+
// verify we're still in new test mode
815+
cy.findByTestId('studio-panel').should('be.visible')
816+
cy.findByTestId('new-test-button').should('be.visible')
817+
818+
// these should not exist if we stayed in new test mode
819+
cy.findByTestId('studio-single-test-title').should('not.exist')
820+
cy.findByTestId('record-button-recording').should('not.exist')
821+
822+
// verify URL still shows suite mode, not edit test mode
823+
cy.location().its('hash').should('contain', 'suiteId=r1').and('not.contain', 'testId=')
824+
})
825+
826+
it('shows test body sections correctly when studio panel is open and page is refreshed', () => {
827+
loadProjectAndRunSpec()
828+
829+
cy.waitForSpecToFinish()
830+
831+
cy.findByTestId('studio-button').click()
832+
cy.findByTestId('studio-panel').should('be.visible')
833+
cy.findByTestId('new-test-button').should('be.visible')
834+
835+
cy.reload()
836+
837+
cy.waitForSpecToFinish()
838+
839+
cy.findByTestId('studio-panel').should('be.visible')
840+
cy.findByTestId('new-test-button').should('be.visible')
841+
842+
// verify test body section is visible after refresh
843+
cy.get('.runnable-instruments').should('be.visible')
844+
cy.get('.runnable-commands-region').should('be.visible')
845+
846+
// verify the test body hook is present
847+
cy.get('.hook-item').contains('test body').should('be.visible')
848+
849+
// verify commands are visible within the test body
850+
cy.get('.command-name-visit').should('be.visible')
851+
852+
// Verify URL parameters show suite mode, not test mode
853+
cy.location().its('hash').should('contain', 'suiteId=r1').and('not.contain', 'testId=')
854+
})
855+
773856
describe('prompt for a new url', () => {
774857
const autUrl = 'http://localhost:4455/cypress/e2e/index.html'
775858
const visitUrl = 'cypress/e2e/index.html'

packages/app/src/runner/SpecRunnerHeaderOpenMode.cy.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ describe('SpecRunnerHeaderOpenMode', { viewportHeight: 500 }, () => {
223223
// This emulates the 'needsUrl' state in the studio store
224224
studioStore.setActive(true)
225225
studioStore.setUrl(undefined)
226+
studioStore._hasStarted = true
226227

227228
cy.mountFragment(SpecRunnerHeaderFragmentDoc, {
228229
render: (gqlVal) => {

packages/app/src/runner/event-manager.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -307,16 +307,25 @@ export class EventManager {
307307
studioInitSuite({ suiteId })
308308
})
309309

310+
const maybeCleanUpProtocol = () => {
311+
const needsReload = this.studioStore.needsProtocolCleanup()
312+
313+
this.studioStore.cancel()
314+
315+
// only reload the page if Studio has actually been used for recording
316+
if (needsReload) {
317+
window.location.reload()
318+
}
319+
}
320+
310321
this.reporterBus.on('studio:cancel', () => {
311322
this.ws.emit('studio:destroy', ({ error }) => {
312323
if (error) {
313324
// eslint-disable-next-line no-console
314325
console.error(error)
315326
}
316327

317-
this.studioStore.cancel()
318-
// Reloading for now. This is the easiest way to clear out the protocol code from the front end
319-
window.location.reload()
328+
maybeCleanUpProtocol()
320329
})
321330
})
322331

@@ -366,9 +375,7 @@ export class EventManager {
366375
console.error(error)
367376
}
368377

369-
this.studioStore.cancel()
370-
// Reloading for now. This is the easiest way to clear out the protocol code from the front end
371-
window.location.reload()
378+
maybeCleanUpProtocol()
372379
})
373380
})
374381

@@ -857,7 +864,8 @@ export class EventManager {
857864
performance.measure('run', 'run-s', 'run-e')
858865
})
859866

860-
const hasRunnableId = !!this.studioStore.testId || !!this.studioStore.suiteId
867+
const hasActiveStudio = !!this.studioStore.testId ||
868+
!!this.studioStore.newTestLineNumber
861869

862870
const studioSingleTestActive = this.studioStore.newTestLineNumber != null || !!this.studioStore.testId
863871

@@ -869,7 +877,7 @@ export class EventManager {
869877
autoScrollingEnabled: runState.autoScrollingEnabled,
870878
isSpecsListOpen: runState.isSpecsListOpen,
871879
scrollTop: runState.scrollTop,
872-
studioActive: hasRunnableId,
880+
studioActive: hasActiveStudio,
873881
studioSingleTestActive,
874882
} as ReporterStartInfo)
875883
}
@@ -928,7 +936,9 @@ export class EventManager {
928936
}
929937

930938
_interceptStudio (displayProps) {
931-
if (this.studioStore.isActive) {
939+
// Only intercept logs when Studio is actually recording a specific test
940+
// Don't intercept when Studio is just open in "new test" mode
941+
if (this.studioStore.isActive && this.studioStore.testId) {
932942
displayProps.hookId = this.studioStore.hookId
933943

934944
if (displayProps.name === 'visit' && displayProps.state === 'failed') {

packages/app/src/store/studio-store.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ export const useStudioStore = defineStore('studioRecorder', {
175175
this.newTestLineNumber = undefined
176176
},
177177

178+
needsProtocolCleanup () {
179+
// Protocol cleanup (page reload) is only needed if the user has actually entered single test mode in Studio
180+
return this._hasStarted || this.testId || this._isStudioCreatedTest
181+
},
182+
178183
openInstructionModal () {
179184
this.instructionModalIsOpen = true
180185
},
@@ -246,7 +251,7 @@ export const useStudioStore = defineStore('studioRecorder', {
246251

247252
interceptTest (test) {
248253
// if this test is the one we created, we can just set the test id
249-
if ((this.newTestLineNumber && test.invocationDetails?.line === this.newTestLineNumber) || this.suiteId) {
254+
if ((this.newTestLineNumber && test.invocationDetails?.line === this.newTestLineNumber) || (this.suiteId && this._hasStarted)) {
250255
this._isStudioCreatedTest = true
251256
this.setTestId(test.id)
252257
getCypress().runner.setIsStudioCreatedTest(true)
@@ -848,7 +853,7 @@ export const useStudioStore = defineStore('studioRecorder', {
848853
},
849854

850855
needsUrl: (state) => {
851-
return state.isActive && !state.url && !state.isFailed
856+
return state.isActive && !state.url && !state.isFailed && state._hasStarted
852857
},
853858

854859
testError: (state) => {

0 commit comments

Comments
 (0)