diff --git a/cli/CHANGELOG.md b/cli/CHANGELOG.md index 2ddf860e64de..bd1cb5a065bd 100644 --- a/cli/CHANGELOG.md +++ b/cli/CHANGELOG.md @@ -15,6 +15,7 @@ _Released 8/27/2024 (PENDING)_ - `.type({upArrow})` and `.type({downArrow})` now also works for date, month, week, time, datetime-local and range input types. Addresses [#29665](https://github.com/cypress-io/cypress/issues/29665). - Added a `CYPRESS_SKIP_VERIFY` flag to enable suppressing Cypress verification checks. Addresses [#22243](https://github.com/cypress-io/cypress/issues/22243). - Updated the protocol to allow making Cloud API requests. Addressed in [#30066](https://github.com/cypress-io/cypress/pull/30066). +- Passing the browser without the testing type (i.e. `cypress open --browser `) will now directly launch the browser after the testing type is selected. Addresses [#22003](https://github.com/cypress-io/cypress/issues/22003). Addressed in [#28538](https://github.com/cypress-io/cypress/pull/28538). **Bugfixes:** diff --git a/packages/data-context/src/actions/ProjectActions.ts b/packages/data-context/src/actions/ProjectActions.ts index 744e1f4a132c..fb9a8fa2acb0 100644 --- a/packages/data-context/src/actions/ProjectActions.ts +++ b/packages/data-context/src/actions/ProjectActions.ts @@ -89,6 +89,13 @@ type SetForceReconfigureProjectByTestingType = { const debug = debugLib('cypress:data-context:ProjectActions') export class ProjectActions { + /** + * @var globalLaunchCount + * Used as a read-only in the launchpad to ensure + * that launchProject is only called once if + * the --browser flag is passed alone. + */ + private globalLaunchCount = 0 constructor (private ctx: DataContext) {} private get api () { @@ -127,6 +134,14 @@ export class ProjectActions { }) } + get launchCount () { + return this.globalLaunchCount + } + + set launchCount (count) { + this.globalLaunchCount = count + } + openDirectoryInIDE (projectPath: string) { this.ctx.debug(`opening ${projectPath} in ${this.ctx.coreData.localSettings.preferences.preferredEditorBinary}`) @@ -284,6 +299,7 @@ export class ProjectActions { } await this.api.launchProject(browser, activeSpec ?? emptySpec, options) + this.globalLaunchCount++ return } diff --git a/packages/graphql/schemas/schema.graphql b/packages/graphql/schemas/schema.graphql index 5eff23a8dd54..903804eef0fb 100644 --- a/packages/graphql/schemas/schema.graphql +++ b/packages/graphql/schemas/schema.graphql @@ -1444,6 +1444,11 @@ type LocalSettingsPreferences { proxyBypass: String proxyServer: String reporterWidth: Int + + """ + Determine if the browser should launch when the browser flag is passed alone + """ + shouldLaunchBrowserFromOpenBrowser: Boolean specListWidth: Int wasBrowserSetInCLI: Boolean } diff --git a/packages/graphql/src/schemaTypes/objectTypes/gql-LocalSettings.ts b/packages/graphql/src/schemaTypes/objectTypes/gql-LocalSettings.ts index 1fd269fb5270..3c6182dddfa0 100644 --- a/packages/graphql/src/schemaTypes/objectTypes/gql-LocalSettings.ts +++ b/packages/graphql/src/schemaTypes/objectTypes/gql-LocalSettings.ts @@ -33,6 +33,27 @@ export const LocalSettingsPreferences = objectType({ }, }) + t.boolean('shouldLaunchBrowserFromOpenBrowser', { + description: 'Determine if the browser should launch when the browser flag is passed alone', + resolve: async (_source, _args, ctx) => { + try { + const cliBrowser = ctx.coreData.cliBrowser + + if (!cliBrowser) { + return false + } + + const browser = await ctx._apis.browserApi.ensureAndGetByNameOrPath(cliBrowser) + const shouldLaunch = Boolean(browser) && (ctx.actions.project.launchCount === 0) + + return shouldLaunch + } catch (e) { + // if error is thrown, browser doesn't exist + return false + } + }, + }) + t.boolean('debugSlideshowComplete') t.boolean('desktopNotificationsEnabled') t.dateTime('dismissNotificationBannerUntil') diff --git a/packages/launchpad/cypress/e2e/choose-a-browser.cy.ts b/packages/launchpad/cypress/e2e/choose-a-browser.cy.ts index abf043246ac8..4faee86b56a6 100644 --- a/packages/launchpad/cypress/e2e/choose-a-browser.cy.ts +++ b/packages/launchpad/cypress/e2e/choose-a-browser.cy.ts @@ -3,6 +3,9 @@ import type { FoundBrowser } from '@packages/types' describe('Choose a browser page', () => { beforeEach(() => { cy.scaffoldProject('launchpad') + cy.withCtx((ctx, _) => { + ctx.actions.project.launchCount = 0 + }) }) describe('System Browsers Detected', () => { @@ -14,6 +17,24 @@ describe('Choose a browser page', () => { }) }) + it('launches when --browser is passed alone through the command line', () => { + cy.withCtx((ctx, o) => { + o.sinon.stub(ctx._apis.projectApi, 'launchProject').resolves() + }) + + cy.openProject('launchpad', ['--browser', 'edge']) + cy.visitLaunchpad() + + cy.skipWelcome() + cy.get('[data-cy=card]').then(($buttons) => { + $buttons[0].click() + }) + + cy.withRetryableCtx((ctx, o) => { + expect(ctx._apis.projectApi.launchProject).to.be.calledOnce + }) + }) + it('preselects browser that is provided through the command line', () => { cy.withCtx((ctx, o) => { // stub launching project since we have `--browser --testingType --project` here @@ -37,6 +58,10 @@ describe('Choose a browser page', () => { }) it('shows warning when launched with --browser name that cannot be matched to found browsers', () => { + cy.withCtx((ctx, o) => { + o.sinon.stub(ctx._apis.projectApi, 'launchProject').resolves() + }) + cy.openProject('launchpad', ['--e2e', '--browser', 'doesNotExist']) cy.visitLaunchpad() cy.skipWelcome() @@ -55,6 +80,9 @@ describe('Choose a browser page', () => { // Ensure warning can be dismissed cy.get('[data-cy="alert-suffix-icon"]').click() cy.get('[data-cy="alert-header"]').should('not.exist') + cy.withRetryableCtx((ctx, o) => { + expect(ctx._apis.projectApi.launchProject).not.to.be.called + }) }) it('shows warning when launched with --browser path option that cannot be matched to found browsers', () => { diff --git a/packages/launchpad/src/Main.vue b/packages/launchpad/src/Main.vue index a624ffbb16f4..fdba61598ce4 100644 --- a/packages/launchpad/src/Main.vue +++ b/packages/launchpad/src/Main.vue @@ -132,6 +132,7 @@ fragment MainLaunchpadQueryData on Query { preferences { majorVersionWelcomeDismissed wasBrowserSetInCLI + shouldLaunchBrowserFromOpenBrowser } } currentProject { @@ -246,11 +247,11 @@ watch( function handleClearLandingPage () { setMajorVersionWelcomeDismissed(MAJOR_VERSION_FOR_CONTENT) - const wasBrowserSetInCLI = query.data?.value?.localSettings.preferences?.wasBrowserSetInCLI + const shouldLaunchBrowser = query.data?.value?.localSettings?.preferences?.shouldLaunchBrowserFromOpenBrowser const currentTestingType = currentProject.value?.currentTestingType - if (wasBrowserSetInCLI && currentTestingType) { + if (shouldLaunchBrowser && currentTestingType) { launchProject.executeMutation({ testingType: currentTestingType }) } } diff --git a/packages/launchpad/src/setup/OpenBrowser.vue b/packages/launchpad/src/setup/OpenBrowser.vue index f9d5ea9e0406..9e85ef00c936 100644 --- a/packages/launchpad/src/setup/OpenBrowser.vue +++ b/packages/launchpad/src/setup/OpenBrowser.vue @@ -22,7 +22,7 @@ import { useMutation, gql, useQuery } from '@urql/vue' import OpenBrowserList from './OpenBrowserList.vue' import WarningList from '../warning/WarningList.vue' -import { OpenBrowserDocument, OpenBrowser_CloseBrowserDocument, OpenBrowser_ClearTestingTypeDocument, OpenBrowser_LaunchProjectDocument, OpenBrowser_FocusActiveBrowserWindowDocument, OpenBrowser_ResetLatestVersionTelemetryDocument } from '../generated/graphql' +import { OpenBrowserDocument, OpenBrowser_CloseBrowserDocument, OpenBrowser_ClearTestingTypeDocument, OpenBrowser_LaunchProjectDocument, OpenBrowser_FocusActiveBrowserWindowDocument, OpenBrowser_ResetLatestVersionTelemetryDocument, OpenBrowser_LocalSettingsDocument } from '../generated/graphql' import LaunchpadHeader from './LaunchpadHeader.vue' import { useI18n } from '@cy/i18n' import { computed, ref, onMounted } from 'vue' @@ -42,7 +42,18 @@ query OpenBrowser { } ` +gql` +query OpenBrowser_LocalSettings { + localSettings { + preferences { + shouldLaunchBrowserFromOpenBrowser + } + } +} +` + const query = useQuery({ query: OpenBrowserDocument }) +const lsQuery = useQuery({ query: OpenBrowser_LocalSettingsDocument, requestPolicy: 'network-only' }) gql` mutation OpenBrowser_ClearTestingType { @@ -106,6 +117,16 @@ const launch = async () => { } } +const launchIfBrowserSetInCli = async () => { + const shouldLaunchBrowser = (await lsQuery).data.value?.localSettings?.preferences?.shouldLaunchBrowserFromOpenBrowser + + if (shouldLaunchBrowser) { + await launch() + } + + return +} + const backFn = () => { clearCurrentTestingType.executeMutation({}) } @@ -126,6 +147,7 @@ const setFocusToActiveBrowserWindow = () => { onMounted(() => { resetLatestVersionTelemetry.executeMutation({}) + launchIfBrowserSetInCli() })