From c205145419a00c1ef1f6b2ddc2c48da9bb77ed51 Mon Sep 17 00:00:00 2001 From: Adam Stone-Lord Date: Thu, 8 May 2025 09:59:00 -0400 Subject: [PATCH 01/13] internal: only capture anonymous studio started analytics when cloud studio is disabled (#31665) --- packages/server/lib/project-base.ts | 35 ++++++++------ packages/server/test/unit/project_spec.js | 57 +++++++++++++++++++++++ 2 files changed, 77 insertions(+), 15 deletions(-) diff --git a/packages/server/lib/project-base.ts b/packages/server/lib/project-base.ts index 1e317299f9..e847be38bb 100644 --- a/packages/server/lib/project-base.ts +++ b/packages/server/lib/project-base.ts @@ -431,21 +431,26 @@ export class ProjectBase extends EE { const studio = await this.ctx.coreData.studioLifecycleManager?.getStudio() - try { - studio?.captureStudioEvent({ - type: StudioMetricsTypes.STUDIO_STARTED, - machineId: await this.ctx.coreData.machineId, - projectId: this.cfg.projectId, - browser: this.browser ? { - name: this.browser.name, - family: this.browser.family, - channel: this.browser.channel, - version: this.browser.version, - } : undefined, - cypressVersion: pkg.version, - }) - } catch (error) { - debug('Error capturing studio event:', error) + const isCloudStudio = !!(process.env.CYPRESS_ENABLE_CLOUD_STUDIO || process.env.CYPRESS_LOCAL_STUDIO_PATH) + + // only capture studio started event if the user is accessing legacy studio + if (!isCloudStudio) { + try { + studio?.captureStudioEvent({ + type: StudioMetricsTypes.STUDIO_STARTED, + machineId: await this.ctx.coreData.machineId, + projectId: this.cfg.projectId, + browser: this.browser ? { + name: this.browser.name, + family: this.browser.family, + channel: this.browser.channel, + version: this.browser.version, + } : undefined, + cypressVersion: pkg.version, + }) + } catch (error) { + debug('Error capturing studio event:', error) + } } if (this.spec && studio?.protocolManager) { diff --git a/packages/server/test/unit/project_spec.js b/packages/server/test/unit/project_spec.js index 490c4297e7..569e025b91 100644 --- a/packages/server/test/unit/project_spec.js +++ b/packages/server/test/unit/project_spec.js @@ -896,6 +896,63 @@ This option will not have an effect in Some-other-name. Tests that rely on web s expect(this.project['_protocolManager']).to.be.undefined }) + it('does not capture studio started event if the user is accessing cloud studio', async function () { + process.env.CYPRESS_ENABLE_CLOUD_STUDIO = 'true' + process.env.CYPRESS_LOCAL_STUDIO_PATH = 'false' + + const mockAccessStudioAI = sinon.stub().resolves(true) + const mockCaptureStudioEvent = sinon.stub().resolves() + + this.project.spec = {} + + this.project._cfg = this.project._cfg || {} + this.project._cfg.projectId = 'test-project-id' + this.project.ctx.coreData.user = { email: 'test@example.com' } + this.project.ctx.coreData.machineId = Promise.resolve('test-machine-id') + + const studioManager = new StudioManager() + + studioManager.canAccessStudioAI = mockAccessStudioAI + studioManager.captureStudioEvent = mockCaptureStudioEvent + const studioLifecycleManager = new StudioLifecycleManager() + + this.project.ctx.coreData.studioLifecycleManager = studioLifecycleManager + + studioLifecycleManager.studioManagerPromise = Promise.resolve(studioManager) + + studioLifecycleManager.isStudioReady = sinon.stub().returns(true) + + // Create a browser object + this.project.browser = { + name: 'chrome', + family: 'chromium', + } + + this.project.options = { browsers: [this.project.browser] } + + sinon.stub(browsers, 'closeProtocolConnection').resolves() + + sinon.stub(browsers, 'connectProtocolToBrowser').resolves() + sinon.stub(this.project, 'protocolManager').get(() => { + return this.project['_protocolManager'] + }).set((protocolManager) => { + this.project['_protocolManager'] = protocolManager + }) + + let studioInitPromise + + this.project.server.startWebsockets.callsFake(async (automation, config, callbacks) => { + studioInitPromise = callbacks.onStudioInit() + }) + + this.project.startWebsockets({}, {}) + + const { canAccessStudioAI } = await studioInitPromise + + expect(canAccessStudioAI).to.be.false + expect(mockCaptureStudioEvent).not.to.be.called + }) + it('passes onStudioDestroy callback', async function () { // Set up minimal required properties this.project.ctx = this.project.ctx || {} From a63693ace60d3919b7c72109392ccca64fa1828c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 10:40:11 -0400 Subject: [PATCH 02/13] chore: Update Chrome (beta) to 137.0.7151.15 (#31664) * chore: Update Chrome (beta) to 137.0.7151.15 * empty commit --------- Co-authored-by: cypress-bot[bot] <41898282+cypress-bot[bot]@users.noreply.github.com> Co-authored-by: Jennifer Shehane --- .circleci/workflows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/workflows.yml b/.circleci/workflows.yml index c34ab5a74e..f3a88c8678 100644 --- a/.circleci/workflows.yml +++ b/.circleci/workflows.yml @@ -1,7 +1,7 @@ version: 2.1 chrome-stable-version: &chrome-stable-version "136.0.7103.92" -chrome-beta-version: &chrome-beta-version "137.0.7151.6" +chrome-beta-version: &chrome-beta-version "137.0.7151.15" firefox-stable-version: &firefox-stable-version "137.0" orbs: From ed304a024ef3570401aecd1709e3800e492ad51f Mon Sep 17 00:00:00 2001 From: Jennifer Shehane Date: Thu, 8 May 2025 12:59:55 -0400 Subject: [PATCH 03/13] internal: hide selector playground and studio toolbar when studio beta enabled (#31666) * hide selector playground and studio toolbar when studio beta enabled * internal: empty commit * fix indentation --- packages/app/cypress/e2e/studio/helper.ts | 28 +++ .../app/cypress/e2e/studio/studio-cloud.cy.ts | 191 +++++++++++++++ packages/app/cypress/e2e/studio/studio.cy.ts | 218 +----------------- .../runner/SpecRunnerHeaderOpenMode.cy.tsx | 65 +++--- .../src/runner/SpecRunnerHeaderOpenMode.vue | 4 +- .../app/src/runner/SpecRunnerOpenMode.vue | 7 +- 6 files changed, 271 insertions(+), 242 deletions(-) create mode 100644 packages/app/cypress/e2e/studio/studio-cloud.cy.ts diff --git a/packages/app/cypress/e2e/studio/helper.ts b/packages/app/cypress/e2e/studio/helper.ts index 1addc507d7..6c66f9dbf4 100644 --- a/packages/app/cypress/e2e/studio/helper.ts +++ b/packages/app/cypress/e2e/studio/helper.ts @@ -43,3 +43,31 @@ export function launchStudio ({ specName = 'spec.cy.js', createNewTest = false, cy.get('[data-cy="hook-name-studio commands"]').should('exist') } } + +export function assertClosingPanelWithoutChanges () { + // Cypress re-runs after you cancel Studio. + // Original spec should pass + cy.waitForSpecToFinish({ passCount: 1 }) + + cy.get('.command').should('have.length', 1) + + // Assert the spec was executed without any new commands. + cy.get('.command-name-visit').within(() => { + cy.contains('visit') + cy.contains('cypress/e2e/index.html') + }) + + cy.findByTestId('hook-name-studio commands').should('not.exist') + + cy.withCtx(async (ctx) => { + const spec = await ctx.actions.file.readFileInProject('cypress/e2e/spec.cy.js') + + // No change, since we closed studio + expect(spec.trim().replace(/\r/g, '')).to.eq(` +describe('studio functionality', () => { + it('visits a basic html page', () => { + cy.visit('cypress/e2e/index.html') + }) +})`.trim()) + }) +} diff --git a/packages/app/cypress/e2e/studio/studio-cloud.cy.ts b/packages/app/cypress/e2e/studio/studio-cloud.cy.ts new file mode 100644 index 0000000000..ea3286fb49 --- /dev/null +++ b/packages/app/cypress/e2e/studio/studio-cloud.cy.ts @@ -0,0 +1,191 @@ +import { launchStudio, loadProjectAndRunSpec, assertClosingPanelWithoutChanges } from './helper' +import pDefer from 'p-defer' + +describe('Studio Cloud', () => { + it('enables protocol for cloud studio', () => { + launchStudio({ enableCloudStudio: true }) + + cy.window().then((win) => { + expect(win.Cypress.config('isDefaultProtocolEnabled')).to.be.false + expect(win.Cypress.state('isProtocolEnabled')).to.be.true + }) + }) + + it('loads the studio UI correctly when studio bundle is taking too long to load', () => { + loadProjectAndRunSpec({ enableCloudStudio: false }) + + cy.window().then(() => { + cy.withCtx((ctx) => { + // Mock the studioLifecycleManager.getStudio method to return a hanging promise + if (ctx.coreData.studioLifecycleManager) { + const neverResolvingPromise = new Promise(() => {}) + + ctx.coreData.studioLifecycleManager.getStudio = () => neverResolvingPromise + ctx.coreData.studioLifecycleManager.isStudioReady = () => false + } + }) + }) + + cy.contains('visits a basic html page') + .closest('.runnable-wrapper') + .findByTestId('launch-studio') + .click() + + cy.waitForSpecToFinish() + + // Verify the cloud studio panel is not present + cy.findByTestId('studio-panel').should('not.exist') + + cy.get('[data-cy="loading-studio-panel"]').should('not.exist') + + cy.get('[data-cy="hook-name-studio commands"]').should('exist') + + cy.getAutIframe().within(() => { + cy.get('#increment').realClick() + }) + + cy.findByTestId('hook-name-studio commands').closest('.hook-studio').within(() => { + cy.get('.command').should('have.length', 2) + cy.get('.command-name-get').should('contain.text', '#increment') + cy.get('.command-name-click').should('contain.text', 'click') + }) + + cy.get('button').contains('Save Commands').should('not.be.disabled') + }) + + it('immediately loads the studio panel', () => { + const deferred = pDefer() + + loadProjectAndRunSpec({ enableCloudStudio: true }) + + cy.findByTestId('studio-panel').should('not.exist') + + cy.intercept('/cypress/e2e/index.html', () => { + // wait for the promise to resolve before responding + // this will ensure the studio panel is loaded before the test finishes + return deferred.promise + }).as('indexHtml') + + cy.contains('visits a basic html page') + .closest('.runnable-wrapper') + .findByTestId('launch-studio') + .click() + + // regular studio is not loaded until after the test finishes + cy.get('[data-cy="hook-name-studio commands"]').should('not.exist') + // cloud studio is loaded immediately + cy.findByTestId('studio-panel').then(() => { + // check for the loading panel from the app first + cy.get('[data-cy="loading-studio-panel"]').should('be.visible') + // we've verified the studio panel is loaded, now resolve the promise so the test can finish + deferred.resolve() + }) + + cy.wait('@indexHtml') + + // Studio re-executes spec before waiting for commands - wait for the spec to finish executing. + cy.waitForSpecToFinish() + + // Verify the studio panel is still open + cy.findByTestId('studio-panel') + cy.get('[data-cy="hook-name-studio commands"]') + }) + + it('hides selector playground and studio controls when studio beta is available', () => { + launchStudio({ enableCloudStudio: true }) + cy.get('[data-cy="studio-header-studio-button"]').click() + + cy.get('[data-cy="playground-activator"]').should('not.exist') + cy.get('[data-cy="studio-toolbar"]').should('not.exist') + }) + + it('closes studio panel when clicking studio button (from the cloud)', () => { + launchStudio({ enableCloudStudio: true }) + + cy.get('[data-cy="studio-header-studio-button"]').click() + + assertClosingPanelWithoutChanges() + }) + + it('opens studio panel to new test when clicking on studio button (from the app) next to url', () => { + cy.viewport(1500, 1000) + loadProjectAndRunSpec({ enableCloudStudio: true }) + // studio button should be visible when using cloud studio + cy.get('[data-cy="studio-button"]').should('be.visible').click() + cy.get('[data-cy="studio-panel"]').should('be.visible') + + cy.contains('New Test') + + cy.get('[data-cy="studio-url-prompt"]').should('not.exist') + + cy.percySnapshot() + }) + + it('opens a cloud studio session with AI enabled', () => { + cy.mockNodeCloudRequest({ + url: '/studio/testgen/n69px6/enabled', + method: 'get', + body: { enabled: true }, + }) + + const aiOutput = 'cy.get(\'button\').should(\'have.text\', \'Increment\')' + + cy.mockNodeCloudStreamingRequest({ + url: '/studio/testgen/n69px6/generate', + method: 'post', + body: { recommendations: [{ content: aiOutput }] }, + }) + + cy.mockStudioFullSnapshot({ + id: 1, + nodeType: 1, + nodeName: 'div', + localName: 'div', + nodeValue: 'div', + children: [], + shadowRoots: [], + }) + + const deferred = pDefer() + + loadProjectAndRunSpec({ enableCloudStudio: true }) + + cy.findByTestId('studio-panel').should('not.exist') + + cy.intercept('/cypress/e2e/index.html', () => { + // wait for the promise to resolve before responding + // this will ensure the studio panel is loaded before the test finishes + return deferred.promise + }).as('indexHtml') + + cy.contains('visits a basic html page') + .closest('.runnable-wrapper') + .findByTestId('launch-studio') + .click() + + // regular studio is not loaded until after the test finishes + cy.get('[data-cy="hook-name-studio commands"]').should('not.exist') + // cloud studio is loaded immediately + cy.findByTestId('studio-panel').then(() => { + // check for the loading panel from the app first + cy.get('[data-cy="loading-studio-panel"]').should('be.visible') + // we've verified the studio panel is loaded, now resolve the promise so the test can finish + deferred.resolve() + }) + + cy.wait('@indexHtml') + + // Studio re-executes spec before waiting for commands - wait for the spec to finish executing. + cy.waitForSpecToFinish() + + // Verify the studio panel is still open + cy.findByTestId('studio-panel') + cy.get('[data-cy="hook-name-studio commands"]') + + // Verify that AI is enabled + cy.get('[data-cy="ai-status-text"]').should('contain.text', 'Enabled') + + // Verify that the AI output is correct + cy.get('[data-cy="recommendation-editor"]').should('contain', aiOutput) + }) +}) diff --git a/packages/app/cypress/e2e/studio/studio.cy.ts b/packages/app/cypress/e2e/studio/studio.cy.ts index c78d758d7b..54d89070c0 100644 --- a/packages/app/cypress/e2e/studio/studio.cy.ts +++ b/packages/app/cypress/e2e/studio/studio.cy.ts @@ -1,5 +1,4 @@ -import { launchStudio, loadProjectAndRunSpec } from './helper' -import pDefer from 'p-defer' +import { launchStudio, loadProjectAndRunSpec, assertClosingPanelWithoutChanges } from './helper' describe('Cypress Studio', () => { function incrementCounter (initialCount: number) { @@ -22,219 +21,10 @@ describe('Cypress Studio', () => { }) } - function assertClosingPanelWithoutChanges () { - // Cypress re-runs after you cancel Studio. - // Original spec should pass - cy.waitForSpecToFinish({ passCount: 1 }) + it('does not display Studio button when not using cloud studio', () => { + loadProjectAndRunSpec({ }) - cy.get('.command').should('have.length', 1) - - // Assert the spec was executed without any new commands. - cy.get('.command-name-visit').within(() => { - cy.contains('visit') - cy.contains('cypress/e2e/index.html') - }) - - cy.findByTestId('hook-name-studio commands').should('not.exist') - - cy.withCtx(async (ctx) => { - const spec = await ctx.actions.file.readFileInProject('cypress/e2e/spec.cy.js') - - // No change, since we closed studio - expect(spec.trim().replace(/\r/g, '')).to.eq(` -describe('studio functionality', () => { - it('visits a basic html page', () => { - cy.visit('cypress/e2e/index.html') - }) -})`.trim()) - }) - } - - context('cloud studio', () => { - it('loads the studio page', () => { - launchStudio({ enableCloudStudio: true }) - - cy.window().then((win) => { - expect(win.Cypress.config('isDefaultProtocolEnabled')).to.be.false - expect(win.Cypress.state('isProtocolEnabled')).to.be.true - }) - }) - - it('loads the studio UI correctly when studio bundle is taking too long to load', () => { - loadProjectAndRunSpec({ enableCloudStudio: false }) - - cy.window().then(() => { - cy.withCtx((ctx) => { - // Mock the studioLifecycleManager.getStudio method to return a hanging promise - if (ctx.coreData.studioLifecycleManager) { - const neverResolvingPromise = new Promise(() => {}) - - ctx.coreData.studioLifecycleManager.getStudio = () => neverResolvingPromise - ctx.coreData.studioLifecycleManager.isStudioReady = () => false - } - }) - }) - - cy.contains('visits a basic html page') - .closest('.runnable-wrapper') - .findByTestId('launch-studio') - .click() - - cy.waitForSpecToFinish() - - // Verify the cloud studio panel is not present - cy.findByTestId('studio-panel').should('not.exist') - - cy.get('[data-cy="loading-studio-panel"]').should('not.exist') - - cy.get('[data-cy="hook-name-studio commands"]').should('exist') - - cy.getAutIframe().within(() => { - cy.get('#increment').realClick() - }) - - cy.findByTestId('hook-name-studio commands').closest('.hook-studio').within(() => { - cy.get('.command').should('have.length', 2) - cy.get('.command-name-get').should('contain.text', '#increment') - cy.get('.command-name-click').should('contain.text', 'click') - }) - - cy.get('button').contains('Save Commands').should('not.be.disabled') - }) - - it('does not display Studio button when not using cloud studio', () => { - loadProjectAndRunSpec({ }) - - cy.get('[data-cy="studio-button"]').should('not.exist') - }) - - it('immediately loads the studio panel', () => { - const deferred = pDefer() - - loadProjectAndRunSpec({ enableCloudStudio: true }) - - cy.findByTestId('studio-panel').should('not.exist') - - cy.intercept('/cypress/e2e/index.html', () => { - // wait for the promise to resolve before responding - // this will ensure the studio panel is loaded before the test finishes - return deferred.promise - }).as('indexHtml') - - cy.contains('visits a basic html page') - .closest('.runnable-wrapper') - .findByTestId('launch-studio') - .click() - - // regular studio is not loaded until after the test finishes - cy.get('[data-cy="hook-name-studio commands"]').should('not.exist') - // cloud studio is loaded immediately - cy.findByTestId('studio-panel').then(() => { - // check for the loading panel from the app first - cy.get('[data-cy="loading-studio-panel"]').should('be.visible') - // we've verified the studio panel is loaded, now resolve the promise so the test can finish - deferred.resolve() - }) - - cy.wait('@indexHtml') - - // Studio re-executes spec before waiting for commands - wait for the spec to finish executing. - cy.waitForSpecToFinish() - - // Verify the studio panel is still open - cy.findByTestId('studio-panel') - cy.get('[data-cy="hook-name-studio commands"]') - }) - - it('closes studio panel when clicking studio button (from the cloud)', () => { - launchStudio({ enableCloudStudio: true }) - - cy.get('[data-cy="studio-header-studio-button"]').click() - - assertClosingPanelWithoutChanges() - }) - - it('opens studio panel to new test when clicking on studio button (from the app) next to url', () => { - cy.viewport(1500, 1000) - loadProjectAndRunSpec({ enableCloudStudio: true }) - // studio button should be visible when using cloud studio - cy.get('[data-cy="studio-button"]').should('be.visible').click() - cy.get('[data-cy="studio-panel"]').should('be.visible') - - cy.contains('New Test') - - cy.get('[data-cy="studio-url-prompt"]').should('not.exist') - - cy.percySnapshot() - }) - - it('opens a cloud studio session with AI enabled', () => { - cy.mockNodeCloudRequest({ - url: '/studio/testgen/n69px6/enabled', - method: 'get', - body: { enabled: true }, - }) - - const aiOutput = 'cy.get(\'button\').should(\'have.text\', \'Increment\')' - - cy.mockNodeCloudStreamingRequest({ - url: '/studio/testgen/n69px6/generate', - method: 'post', - body: { recommendations: [{ content: aiOutput }] }, - }) - - cy.mockStudioFullSnapshot({ - id: 1, - nodeType: 1, - nodeName: 'div', - localName: 'div', - nodeValue: 'div', - children: [], - shadowRoots: [], - }) - - const deferred = pDefer() - - loadProjectAndRunSpec({ enableCloudStudio: true }) - - cy.findByTestId('studio-panel').should('not.exist') - - cy.intercept('/cypress/e2e/index.html', () => { - // wait for the promise to resolve before responding - // this will ensure the studio panel is loaded before the test finishes - return deferred.promise - }).as('indexHtml') - - cy.contains('visits a basic html page') - .closest('.runnable-wrapper') - .findByTestId('launch-studio') - .click() - - // regular studio is not loaded until after the test finishes - cy.get('[data-cy="hook-name-studio commands"]').should('not.exist') - // cloud studio is loaded immediately - cy.findByTestId('studio-panel').then(() => { - // check for the loading panel from the app first - cy.get('[data-cy="loading-studio-panel"]').should('be.visible') - // we've verified the studio panel is loaded, now resolve the promise so the test can finish - deferred.resolve() - }) - - cy.wait('@indexHtml') - - // Studio re-executes spec before waiting for commands - wait for the spec to finish executing. - cy.waitForSpecToFinish() - - // Verify the studio panel is still open - cy.findByTestId('studio-panel') - cy.get('[data-cy="hook-name-studio commands"]') - - // Verify that AI is enabled - cy.get('[data-cy="ai-status-text"]').should('contain.text', 'Enabled') - - // Verify that the AI output is correct - cy.get('[data-cy="recommendation-editor"]').should('contain', aiOutput) - }) + cy.get('[data-cy="studio-button"]').should('not.exist') }) it('updates an existing test with an action', () => { diff --git a/packages/app/src/runner/SpecRunnerHeaderOpenMode.cy.tsx b/packages/app/src/runner/SpecRunnerHeaderOpenMode.cy.tsx index 959dbc5ea2..3a57142da4 100644 --- a/packages/app/src/runner/SpecRunnerHeaderOpenMode.cy.tsx +++ b/packages/app/src/runner/SpecRunnerHeaderOpenMode.cy.tsx @@ -5,7 +5,7 @@ import { createEventManager, createTestAutIframe } from '../../cypress/component import { ExternalLink_OpenExternalDocument } from '@packages/frontend-shared/src/generated/graphql' import { cyGeneralGlobeX16 } from '@cypress-design/icon-registry' -function renderWithGql (gqlVal: SpecRunnerHeaderFragment, shouldShowStudioButton = false) { +function renderWithGql (gqlVal: SpecRunnerHeaderFragment, shouldShowStudioButton = false, studioBetaAvailable = false) { const eventManager = createEventManager() const autIframe = createTestAutIframe() @@ -17,6 +17,7 @@ function renderWithGql (gqlVal: SpecRunnerHeaderFragment, shouldShowStudioButton eventManager={eventManager} getAutIframe={() => autIframe} shouldShowStudioButton={shouldShowStudioButton} + studioBetaAvailable={studioBetaAvailable} />) } @@ -37,42 +38,54 @@ describe('SpecRunnerHeaderOpenMode', { viewportHeight: 500 }, () => { cy.findByTestId('viewport-size').should('be.visible').contains('500x500') }) - it('disabled selector playground button when isRunning is true', () => { - const autStore = useAutStore() + describe('selector playground button', () => { + it('is enabled by default', () => { + cy.mountFragment(SpecRunnerHeaderFragmentDoc, { + render: (gqlVal) => { + return renderWithGql(gqlVal) + }, + }) - autStore.setIsRunning(true) - - cy.mountFragment(SpecRunnerHeaderFragmentDoc, { - render: (gqlVal) => { - return renderWithGql(gqlVal) - }, + cy.get('[data-cy="playground-activator"]').should('not.be.disabled') }) - cy.get('[data-cy="playground-activator"]').should('be.disabled') - }) + it('is disabled when isRunning is true', () => { + const autStore = useAutStore() - it('disabled selector playground button when isLoading is true', () => { - const autStore = useAutStore() + autStore.setIsRunning(true) - autStore.setIsLoading(true) + cy.mountFragment(SpecRunnerHeaderFragmentDoc, { + render: (gqlVal) => { + return renderWithGql(gqlVal) + }, + }) - cy.mountFragment(SpecRunnerHeaderFragmentDoc, { - render: (gqlVal) => { - return renderWithGql(gqlVal) - }, + cy.get('[data-cy="playground-activator"]').should('be.disabled') }) - cy.get('[data-cy="playground-activator"]').should('be.disabled') - }) + it('is disabled when isLoading is true', () => { + const autStore = useAutStore() - it('enables selector playground button by default', () => { - cy.mountFragment(SpecRunnerHeaderFragmentDoc, { - render: (gqlVal) => { - return renderWithGql(gqlVal) - }, + autStore.setIsLoading(true) + + cy.mountFragment(SpecRunnerHeaderFragmentDoc, { + render: (gqlVal) => { + return renderWithGql(gqlVal) + }, + }) + + cy.get('[data-cy="playground-activator"]').should('be.disabled') }) - cy.get('[data-cy="playground-activator"]').should('not.be.disabled') + it('is hidden when studio beta is available', () => { + cy.mountFragment(SpecRunnerHeaderFragmentDoc, { + render: (gqlVal) => { + return renderWithGql(gqlVal, true, true) + }, + }) + + cy.get('[data-cy="playground-activator"]').should('not.exist') + }) }) it('shows url section if currentTestingType is e2e', () => { diff --git a/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue b/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue index 0fce79fd65..861e103560 100644 --- a/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue +++ b/packages/app/src/runner/SpecRunnerHeaderOpenMode.vue @@ -6,6 +6,7 @@ >