mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-19 21:51:16 -06:00
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
This commit is contained in:
@@ -760,6 +760,27 @@ describe('studio functionality', () => {
|
||||
cy.location().its('hash').and('not.contain', 'testId=').and('not.contain', 'studio=')
|
||||
})
|
||||
|
||||
it('does not prompt for a URL until studio is active', () => {
|
||||
launchStudio({ specName: 'spec-w-visit.cy.js', createNewTestFromSuite: true })
|
||||
cy.location().its('hash').should('contain', 'suiteId=r2').and('contain', 'studio=')
|
||||
cy.waitForSpecToFinish()
|
||||
|
||||
cy.findByTestId('aut-url-input').should('have.value', 'http://localhost:4455/cypress/e2e/index.html')
|
||||
})
|
||||
|
||||
it('does not reload the page if we didnt open a test in studio', () => {
|
||||
launchStudio({ specName: 'spec-w-visit.cy.js', createNewTestFromSuite: true })
|
||||
|
||||
// set a property on the window to see if the page reloads
|
||||
cy.window().then((w) => w['beforeReload'] = true)
|
||||
|
||||
// close new test mode
|
||||
cy.findByTestId('studio-header-studio-button').click()
|
||||
|
||||
// if this property is still set on the window, then the page didn't reload
|
||||
cy.window().then((w) => expect(w['beforeReload']).to.be.true)
|
||||
})
|
||||
|
||||
it('removes the studio url parameters when closing studio new test', () => {
|
||||
launchStudio({ specName: 'spec-w-visit.cy.js', createNewTestFromSuite: true })
|
||||
|
||||
@@ -770,6 +791,68 @@ describe('studio functionality', () => {
|
||||
cy.location().its('hash').and('not.contain', 'suiteId=').and('not.contain', 'studio=')
|
||||
})
|
||||
|
||||
it('stays in new test mode when studio panel is opened when the spec is running', () => {
|
||||
loadProjectAndRunSpec()
|
||||
|
||||
cy.waitForSpecToFinish()
|
||||
|
||||
cy.findByTestId('studio-button').click()
|
||||
cy.findByTestId('studio-panel').should('be.visible')
|
||||
cy.findByTestId('new-test-button').should('be.visible')
|
||||
|
||||
// Verify we're initially in new test mode
|
||||
cy.location().its('hash').should('contain', 'suiteId=r1').and('not.contain', 'testId=')
|
||||
|
||||
// Now restart the spec, which will call interceptTest with the running test
|
||||
// This is where the bug would manifest - it would incorrectly switch from
|
||||
// "new test" mode to "edit the running test" mode
|
||||
cy.get('button.restart').click()
|
||||
|
||||
cy.get('.test').should('have.length', 1)
|
||||
cy.get('.test').first().should('have.class', 'runnable-active')
|
||||
|
||||
// verify we're still in new test mode
|
||||
cy.findByTestId('studio-panel').should('be.visible')
|
||||
cy.findByTestId('new-test-button').should('be.visible')
|
||||
|
||||
// these should not exist if we stayed in new test mode
|
||||
cy.findByTestId('studio-single-test-title').should('not.exist')
|
||||
cy.findByTestId('record-button-recording').should('not.exist')
|
||||
|
||||
// verify URL still shows suite mode, not edit test mode
|
||||
cy.location().its('hash').should('contain', 'suiteId=r1').and('not.contain', 'testId=')
|
||||
})
|
||||
|
||||
it('shows test body sections correctly when studio panel is open and page is refreshed', () => {
|
||||
loadProjectAndRunSpec()
|
||||
|
||||
cy.waitForSpecToFinish()
|
||||
|
||||
cy.findByTestId('studio-button').click()
|
||||
cy.findByTestId('studio-panel').should('be.visible')
|
||||
cy.findByTestId('new-test-button').should('be.visible')
|
||||
|
||||
cy.reload()
|
||||
|
||||
cy.waitForSpecToFinish()
|
||||
|
||||
cy.findByTestId('studio-panel').should('be.visible')
|
||||
cy.findByTestId('new-test-button').should('be.visible')
|
||||
|
||||
// verify test body section is visible after refresh
|
||||
cy.get('.runnable-instruments').should('be.visible')
|
||||
cy.get('.runnable-commands-region').should('be.visible')
|
||||
|
||||
// verify the test body hook is present
|
||||
cy.get('.hook-item').contains('test body').should('be.visible')
|
||||
|
||||
// verify commands are visible within the test body
|
||||
cy.get('.command-name-visit').should('be.visible')
|
||||
|
||||
// Verify URL parameters show suite mode, not test mode
|
||||
cy.location().its('hash').should('contain', 'suiteId=r1').and('not.contain', 'testId=')
|
||||
})
|
||||
|
||||
describe('prompt for a new url', () => {
|
||||
const autUrl = 'http://localhost:4455/cypress/e2e/index.html'
|
||||
const visitUrl = 'cypress/e2e/index.html'
|
||||
|
||||
@@ -223,6 +223,7 @@ describe('SpecRunnerHeaderOpenMode', { viewportHeight: 500 }, () => {
|
||||
// This emulates the 'needsUrl' state in the studio store
|
||||
studioStore.setActive(true)
|
||||
studioStore.setUrl(undefined)
|
||||
studioStore._hasStarted = true
|
||||
|
||||
cy.mountFragment(SpecRunnerHeaderFragmentDoc, {
|
||||
render: (gqlVal) => {
|
||||
|
||||
@@ -307,6 +307,17 @@ export class EventManager {
|
||||
studioInitSuite({ suiteId })
|
||||
})
|
||||
|
||||
const maybeCleanUpProtocol = () => {
|
||||
const needsReload = this.studioStore.needsProtocolCleanup()
|
||||
|
||||
this.studioStore.cancel()
|
||||
|
||||
// only reload the page if Studio has actually been used for recording
|
||||
if (needsReload) {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
this.reporterBus.on('studio:cancel', () => {
|
||||
this.ws.emit('studio:destroy', ({ error }) => {
|
||||
if (error) {
|
||||
@@ -314,9 +325,7 @@ export class EventManager {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
this.studioStore.cancel()
|
||||
// Reloading for now. This is the easiest way to clear out the protocol code from the front end
|
||||
window.location.reload()
|
||||
maybeCleanUpProtocol()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -366,9 +375,7 @@ export class EventManager {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
this.studioStore.cancel()
|
||||
// Reloading for now. This is the easiest way to clear out the protocol code from the front end
|
||||
window.location.reload()
|
||||
maybeCleanUpProtocol()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -857,7 +864,8 @@ export class EventManager {
|
||||
performance.measure('run', 'run-s', 'run-e')
|
||||
})
|
||||
|
||||
const hasRunnableId = !!this.studioStore.testId || !!this.studioStore.suiteId
|
||||
const hasActiveStudio = !!this.studioStore.testId ||
|
||||
!!this.studioStore.newTestLineNumber
|
||||
|
||||
const studioSingleTestActive = this.studioStore.newTestLineNumber != null || !!this.studioStore.testId
|
||||
|
||||
@@ -869,7 +877,7 @@ export class EventManager {
|
||||
autoScrollingEnabled: runState.autoScrollingEnabled,
|
||||
isSpecsListOpen: runState.isSpecsListOpen,
|
||||
scrollTop: runState.scrollTop,
|
||||
studioActive: hasRunnableId,
|
||||
studioActive: hasActiveStudio,
|
||||
studioSingleTestActive,
|
||||
} as ReporterStartInfo)
|
||||
}
|
||||
@@ -928,7 +936,9 @@ export class EventManager {
|
||||
}
|
||||
|
||||
_interceptStudio (displayProps) {
|
||||
if (this.studioStore.isActive) {
|
||||
// Only intercept logs when Studio is actually recording a specific test
|
||||
// Don't intercept when Studio is just open in "new test" mode
|
||||
if (this.studioStore.isActive && this.studioStore.testId) {
|
||||
displayProps.hookId = this.studioStore.hookId
|
||||
|
||||
if (displayProps.name === 'visit' && displayProps.state === 'failed') {
|
||||
|
||||
@@ -175,6 +175,11 @@ export const useStudioStore = defineStore('studioRecorder', {
|
||||
this.newTestLineNumber = undefined
|
||||
},
|
||||
|
||||
needsProtocolCleanup () {
|
||||
// Protocol cleanup (page reload) is only needed if the user has actually entered single test mode in Studio
|
||||
return this._hasStarted || this.testId || this._isStudioCreatedTest
|
||||
},
|
||||
|
||||
openInstructionModal () {
|
||||
this.instructionModalIsOpen = true
|
||||
},
|
||||
@@ -246,7 +251,7 @@ export const useStudioStore = defineStore('studioRecorder', {
|
||||
|
||||
interceptTest (test) {
|
||||
// if this test is the one we created, we can just set the test id
|
||||
if ((this.newTestLineNumber && test.invocationDetails?.line === this.newTestLineNumber) || this.suiteId) {
|
||||
if ((this.newTestLineNumber && test.invocationDetails?.line === this.newTestLineNumber) || (this.suiteId && this._hasStarted)) {
|
||||
this._isStudioCreatedTest = true
|
||||
this.setTestId(test.id)
|
||||
getCypress().runner.setIsStudioCreatedTest(true)
|
||||
@@ -848,7 +853,7 @@ export const useStudioStore = defineStore('studioRecorder', {
|
||||
},
|
||||
|
||||
needsUrl: (state) => {
|
||||
return state.isActive && !state.url && !state.isFailed
|
||||
return state.isActive && !state.url && !state.isFailed && state._hasStarted
|
||||
},
|
||||
|
||||
testError: (state) => {
|
||||
|
||||
Reference in New Issue
Block a user