mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-24 16:08:33 -05:00
fix: cache last-used browser by project, name, and channel (#20760)
Co-authored-by: Barthélémy Ledoux <bart@cypress.io>
This commit is contained in:
@@ -80,7 +80,7 @@ describe('Cypress in Cypress', { viewportWidth: 1500, defaultCommandTimeout: 100
|
||||
cy.contains('li', 'Electron').click()
|
||||
|
||||
cy.withCtx((ctx) => {
|
||||
expect(ctx.coreData.chosenBrowser?.displayName).eq('Electron')
|
||||
expect(ctx.coreData.activeBrowser?.displayName).eq('Electron')
|
||||
expect(ctx.actions.project.launchProject).to.have.been.called
|
||||
})
|
||||
})
|
||||
|
||||
@@ -127,7 +127,7 @@ describe('SpecRunnerHeaderOpenMode', { viewportHeight: 500 }, () => {
|
||||
it('shows current browser and possible browsers', () => {
|
||||
cy.mountFragment(SpecRunnerHeaderFragmentDoc, {
|
||||
onResult: (ctx) => {
|
||||
ctx.currentBrowser = ctx.browsers?.find((x) => x.displayName === 'Chrome') ?? null
|
||||
ctx.activeBrowser = ctx.browsers?.find((x) => x.displayName === 'Chrome') ?? null
|
||||
},
|
||||
render: (gqlVal) => {
|
||||
return renderWithGql(gqlVal)
|
||||
|
||||
@@ -153,7 +153,7 @@ fragment SpecRunnerHeader on CurrentProject {
|
||||
id
|
||||
currentTestingType
|
||||
|
||||
currentBrowser {
|
||||
activeBrowser {
|
||||
id
|
||||
displayName
|
||||
majorVersion
|
||||
@@ -163,14 +163,6 @@ fragment SpecRunnerHeader on CurrentProject {
|
||||
}
|
||||
`
|
||||
|
||||
gql`
|
||||
fragment SpecRunnerHeader_Browser on Browser {
|
||||
id
|
||||
name
|
||||
displayName
|
||||
}
|
||||
`
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const autStore = useAutStore()
|
||||
@@ -203,7 +195,7 @@ const selectorPlaygroundStore = useSelectorPlaygroundStore()
|
||||
const togglePlayground = () => _togglePlayground(autIframe)
|
||||
|
||||
// Have to spread gql props since binding it to v-model causes error when testing
|
||||
const selectedBrowser = ref({ ...props.gql.currentBrowser })
|
||||
const selectedBrowser = ref({ ...props.gql.activeBrowser })
|
||||
|
||||
const activeSpecPath = specStore.activeSpec?.absolute
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ gql`
|
||||
fragment AutomationMissing on CurrentProject {
|
||||
id
|
||||
...VerticalBrowserListItems
|
||||
currentBrowser {
|
||||
activeBrowser {
|
||||
id
|
||||
displayName
|
||||
majorVersion
|
||||
@@ -76,6 +76,6 @@ fragment AutomationMissing on CurrentProject {
|
||||
const props = withDefaults(defineProps<{ gql: AutomationMissingFragment | null }>(), { gql: null })
|
||||
|
||||
// Have to spread gql props since binding it to v-model causes error when testing
|
||||
const selectedBrowser = ref({ ...props.gql?.currentBrowser })
|
||||
const selectedBrowser = ref({ ...props.gql?.activeBrowser })
|
||||
|
||||
</script>
|
||||
|
||||
@@ -26,7 +26,6 @@ exports['@packages/data-context initializeData initializes 1'] = {
|
||||
},
|
||||
"app": {
|
||||
"currentTestingType": null,
|
||||
"refreshingBrowsers": {},
|
||||
"browsers": [
|
||||
{
|
||||
"path": "/dev/chrome"
|
||||
@@ -57,7 +56,7 @@ exports['@packages/data-context initializeData initializes 1'] = {
|
||||
"history": [
|
||||
"welcome"
|
||||
],
|
||||
"chosenBrowser": {
|
||||
"activeBrowser": {
|
||||
"path": "/dev/chrome"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -17,19 +17,24 @@ export interface ApplicationDataApiShape {
|
||||
export class AppActions {
|
||||
constructor (private ctx: DataContext) {}
|
||||
|
||||
setActiveBrowser (browser: FoundBrowser) {
|
||||
this.ctx.coreData.chosenBrowser = browser
|
||||
async setActiveBrowser (browser: FoundBrowser) {
|
||||
this.ctx.coreData.activeBrowser = browser
|
||||
|
||||
await this.ctx._apis.projectApi.insertProjectPreferencesToCache(this.ctx.lifecycleManager.projectTitle, {
|
||||
lastBrowser: {
|
||||
name: browser.name,
|
||||
channel: browser.channel,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
setActiveBrowserById (id: string) {
|
||||
async setActiveBrowserById (id: string) {
|
||||
const browserId = this.ctx.fromId(id, 'Browser')
|
||||
|
||||
// Ensure that this is a valid ID to set
|
||||
const browser = this.ctx.lifecycleManager.browsers?.find((b) => this.idForBrowser(b as FoundBrowser) === browserId)
|
||||
|
||||
if (browser) {
|
||||
this.setActiveBrowser(browser)
|
||||
}
|
||||
if (!browser) throw new Error('no browser in setActiveBrowserById')
|
||||
|
||||
await this.setActiveBrowser(browser)
|
||||
}
|
||||
|
||||
async removeAppDataDir () {
|
||||
@@ -43,18 +48,4 @@ export class AppActions {
|
||||
private idForBrowser (obj: FoundBrowser) {
|
||||
return this.ctx.browser.idForBrowser(obj)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we have a current chosen browser, and it matches up to one of the
|
||||
* ones we have selected
|
||||
*/
|
||||
private hasValidChosenBrowser (browsers: FoundBrowser[]) {
|
||||
const chosenBrowser = this.ctx.coreData.chosenBrowser
|
||||
|
||||
if (!chosenBrowser) {
|
||||
return false
|
||||
}
|
||||
|
||||
return browsers.some((b) => this.idForBrowser(b) === this.idForBrowser(chosenBrowser))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,9 +209,16 @@ export class ProjectActions {
|
||||
|
||||
testingType = testingType || this.ctx.coreData.currentTestingType
|
||||
|
||||
if (!testingType) {
|
||||
return null
|
||||
}
|
||||
// It's strange to have no testingType here, but `launchProject` is called when switching testing types,
|
||||
// so it needs to short-circuit and return here.
|
||||
// TODO: Untangle this. https://cypress-io.atlassian.net/browse/UNIFY-1528
|
||||
if (!testingType) return
|
||||
|
||||
this.ctx.coreData.currentTestingType = testingType
|
||||
|
||||
const browser = this.ctx.coreData.activeBrowser
|
||||
|
||||
if (!browser) throw new Error('Missing browser in launchProject')
|
||||
|
||||
let activeSpec: FoundSpec | undefined
|
||||
|
||||
@@ -219,17 +226,6 @@ export class ProjectActions {
|
||||
activeSpec = this.ctx.project.getCurrentSpecByAbsolute(specPath)
|
||||
}
|
||||
|
||||
// Ensure that we have loaded browsers to choose from
|
||||
if (this.ctx.appData.refreshingBrowsers) {
|
||||
await this.ctx.appData.refreshingBrowsers
|
||||
}
|
||||
|
||||
const browser = this.ctx.coreData.chosenBrowser ?? this.ctx.appData.browsers?.[0]
|
||||
|
||||
if (!browser) {
|
||||
return null
|
||||
}
|
||||
|
||||
// launchProject expects a spec when opening browser for url navigation.
|
||||
// We give it an empty spec if none is passed so as to land on home page
|
||||
const emptySpec: Cypress.Spec = {
|
||||
@@ -239,8 +235,6 @@ export class ProjectActions {
|
||||
specType: testingType === 'e2e' ? 'integration' : 'component',
|
||||
}
|
||||
|
||||
this.ctx.coreData.currentTestingType = testingType
|
||||
|
||||
await this.api.launchProject(browser, activeSpec ?? emptySpec, options)
|
||||
|
||||
return
|
||||
@@ -258,10 +252,6 @@ export class ProjectActions {
|
||||
return this.api.removeProjectFromCache(projectRoot)
|
||||
}
|
||||
|
||||
syncProjects () {
|
||||
//
|
||||
}
|
||||
|
||||
async createConfigFile (type?: 'component' | 'e2e' | null) {
|
||||
const project = this.ctx.currentProject
|
||||
|
||||
|
||||
@@ -252,9 +252,7 @@ export class ProjectLifecycleManager {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.ctx.coreData.cliBrowser) {
|
||||
await this.setActiveBrowser(this.ctx.coreData.cliBrowser)
|
||||
}
|
||||
await this.setInitialActiveBrowser()
|
||||
|
||||
if (this._currentTestingType && finalConfig.specPattern) {
|
||||
await this.ctx.actions.project.setSpecsFoundBySpecPattern({
|
||||
@@ -272,6 +270,43 @@ export class ProjectLifecycleManager {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial `activeBrowser` depending on these criteria, in order of preference:
|
||||
* 1. The value of `--browser` passed via CLI.
|
||||
* 2. The last browser selected in `open` mode (by name and channel) for this project.
|
||||
* 3. The first browser found.
|
||||
*/
|
||||
async setInitialActiveBrowser () {
|
||||
if (this.ctx.coreData.cliBrowser) {
|
||||
await this.setActiveBrowserByNameOrPath(this.ctx.coreData.cliBrowser)
|
||||
|
||||
// only finish if `cliBrowser` was successfully set - we must have an activeBrowser once this function resolves
|
||||
if (this.ctx.coreData.activeBrowser) return
|
||||
}
|
||||
|
||||
// lastBrowser is cached per-project.
|
||||
const prefs = await this.ctx.project.getProjectPreferences(path.basename(this.projectRoot))
|
||||
const browsers = await this.ctx.browser.machineBrowsers()
|
||||
|
||||
if (!browsers[0]) throw new Error('No browsers available in setInitialActiveBrowser, cannot set initial active browser')
|
||||
|
||||
this.ctx.coreData.activeBrowser = (prefs?.lastBrowser && browsers.find((b) => {
|
||||
return b.name === prefs.lastBrowser!.name && b.channel === prefs.lastBrowser!.channel
|
||||
})) || browsers[0]
|
||||
}
|
||||
|
||||
private async setActiveBrowserByNameOrPath (nameOrPath: string) {
|
||||
try {
|
||||
const browser = await this.ctx._apis.browserApi.ensureAndGetByNameOrPath(nameOrPath)
|
||||
|
||||
this.ctx.coreData.activeBrowser = browser
|
||||
} catch (e) {
|
||||
const error = e as CypressError
|
||||
|
||||
this.ctx.onWarning(error)
|
||||
}
|
||||
}
|
||||
|
||||
async refreshLifecycle () {
|
||||
assert(this._projectRoot, 'Cannot reload config without a project root')
|
||||
assert(this._configManager, 'Cannot reload config without a config manager')
|
||||
@@ -312,21 +347,6 @@ export class ProjectLifecycleManager {
|
||||
return this._configManager.initializeConfig()
|
||||
}
|
||||
|
||||
private async setActiveBrowser (cliBrowser: string) {
|
||||
// When we're starting up, if we've chosen a browser to run with, check if it exists
|
||||
this.ctx.coreData.cliBrowser = null
|
||||
|
||||
try {
|
||||
const browser = await this.ctx._apis.browserApi.ensureAndGetByNameOrPath(cliBrowser)
|
||||
|
||||
this.ctx.coreData.chosenBrowser = browser ?? null
|
||||
} catch (e) {
|
||||
const error = e as CypressError
|
||||
|
||||
this.ctx.onWarning(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When we set the current project, we need to cleanup the
|
||||
* previous project that might have existed. We use this as the
|
||||
|
||||
@@ -56,8 +56,6 @@ export interface AppDataShape {
|
||||
isInGlobalMode: boolean
|
||||
browsers: ReadonlyArray<FoundBrowser> | null
|
||||
projects: ProjectShape[]
|
||||
refreshingBrowsers: Promise<FoundBrowser[]> | null
|
||||
refreshingNodePath: Promise<string> | null
|
||||
nodePath: Maybe<string>
|
||||
browserStatus: BrowserStatus
|
||||
relaunchBrowser: boolean
|
||||
@@ -111,7 +109,7 @@ export interface ForceReconfigureProjectDataShape {
|
||||
export interface CoreDataShape {
|
||||
cliBrowser: string | null
|
||||
cliTestingType: string | null
|
||||
chosenBrowser: FoundBrowser | null
|
||||
activeBrowser: FoundBrowser | null
|
||||
machineBrowsers: Promise<FoundBrowser[]> | null
|
||||
servers: {
|
||||
appServer?: Maybe<Server>
|
||||
@@ -158,10 +156,8 @@ export function makeCoreData (modeOptions: Partial<AllModeOptions> = {}): CoreDa
|
||||
},
|
||||
app: {
|
||||
isInGlobalMode: Boolean(modeOptions.global),
|
||||
refreshingBrowsers: null,
|
||||
browsers: null,
|
||||
projects: [],
|
||||
refreshingNodePath: null,
|
||||
nodePath: modeOptions.userNodePath,
|
||||
browserStatus: 'closed',
|
||||
relaunchBrowser: false,
|
||||
@@ -201,7 +197,7 @@ export function makeCoreData (modeOptions: Partial<AllModeOptions> = {}): CoreDa
|
||||
},
|
||||
},
|
||||
warnings: [],
|
||||
chosenBrowser: null,
|
||||
activeBrowser: null,
|
||||
user: null,
|
||||
electron: {
|
||||
app: null,
|
||||
|
||||
@@ -16,7 +16,7 @@ const platform = os.platform()
|
||||
|
||||
export interface BrowserApiShape {
|
||||
close(): Promise<any>
|
||||
ensureAndGetByNameOrPath(nameOrPath: string): Promise<FoundBrowser | undefined>
|
||||
ensureAndGetByNameOrPath(nameOrPath: string): Promise<FoundBrowser>
|
||||
getBrowsers(): Promise<FoundBrowser[]>
|
||||
focusActiveBrowserWindow(): Promise<any>
|
||||
}
|
||||
@@ -32,10 +32,8 @@ export class BrowserDataSource {
|
||||
if (!this.ctx.coreData.machineBrowsers) {
|
||||
const p = this.ctx._apis.browserApi.getBrowsers()
|
||||
|
||||
this.ctx.coreData.machineBrowsers = p.then((browsers) => {
|
||||
if (browsers[0]) {
|
||||
this.ctx.coreData.chosenBrowser = browsers[0]
|
||||
}
|
||||
this.ctx.coreData.machineBrowsers = p.then(async (browsers) => {
|
||||
if (!browsers[0]) throw new Error('no browsers found in machineBrowsers')
|
||||
|
||||
return browsers
|
||||
}).catch((e) => {
|
||||
@@ -56,11 +54,11 @@ export class BrowserDataSource {
|
||||
}
|
||||
|
||||
isSelected (obj: FoundBrowser) {
|
||||
if (!this.ctx.coreData.chosenBrowser) {
|
||||
if (!this.ctx.coreData.activeBrowser) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.idForBrowser(this.ctx.coreData.chosenBrowser) === this.idForBrowser(obj)
|
||||
return this.idForBrowser(this.ctx.coreData.activeBrowser) === this.idForBrowser(obj)
|
||||
}
|
||||
|
||||
isFocusSupported (obj: FoundBrowser) {
|
||||
|
||||
@@ -112,7 +112,7 @@ export class HtmlDataSource {
|
||||
window.__CYPRESS_MODE__ = ${JSON.stringify(this.ctx.isRunMode ? 'run' : 'open')};
|
||||
window.__CYPRESS_CONFIG__ = ${JSON.stringify(serveConfig)};
|
||||
window.__CYPRESS_TESTING_TYPE__ = '${this.ctx.coreData.currentTestingType}'
|
||||
window.__CYPRESS_BROWSER__ = ${JSON.stringify(this.ctx.coreData.chosenBrowser)}
|
||||
window.__CYPRESS_BROWSER__ = ${JSON.stringify(this.ctx.coreData.activeBrowser)}
|
||||
${process.env.CYPRESS_INTERNAL_GQL_NO_SOCKET
|
||||
? `window.__CYPRESS_GQL_NO_SOCKET__ = 'true';`
|
||||
: ''
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { expect } from 'chai'
|
||||
import type { DataContext } from '../../../src'
|
||||
import { createTestDataContext } from '../helper'
|
||||
import sinon from 'sinon'
|
||||
|
||||
describe('ProjectLifecycleManager', () => {
|
||||
let ctx: DataContext
|
||||
|
||||
beforeEach(() => {
|
||||
ctx = createTestDataContext('open')
|
||||
})
|
||||
|
||||
context('#setInitialActiveBrowser', () => {
|
||||
const browsers = [
|
||||
{ name: 'electron', family: 'chromium', channel: 'stable', displayName: 'Electron' },
|
||||
{ name: 'chrome', family: 'chromium', channel: 'stable', displayName: 'Chrome' },
|
||||
{ name: 'chrome', family: 'chromium', channel: 'beta', displayName: 'Chrome Beta' },
|
||||
]
|
||||
|
||||
beforeEach(() => {
|
||||
ctx.coreData.activeBrowser = undefined
|
||||
ctx.coreData.cliBrowser = undefined
|
||||
|
||||
ctx._apis.browserApi.getBrowsers = sinon.stub().resolves(browsers)
|
||||
ctx.project.getProjectPreferences = sinon.stub().resolves(null)
|
||||
// @ts-expect-error
|
||||
ctx.lifecycleManager._projectRoot = 'foo'
|
||||
})
|
||||
|
||||
it('falls back to browsers[0] if preferences and cliBrowser do not exist', async () => {
|
||||
await ctx.lifecycleManager.setInitialActiveBrowser()
|
||||
|
||||
expect(ctx.coreData.activeBrowser).to.include({ name: 'electron' })
|
||||
})
|
||||
|
||||
it('uses cli --browser option if one is set', async () => {
|
||||
ctx._apis.browserApi.ensureAndGetByNameOrPath = sinon.stub().withArgs('electron').resolves(browsers[0])
|
||||
|
||||
ctx.coreData.cliBrowser = 'electron'
|
||||
|
||||
await ctx.lifecycleManager.setInitialActiveBrowser()
|
||||
|
||||
expect(ctx.coreData.cliBrowser).to.eq('electron')
|
||||
expect(ctx.coreData.activeBrowser).to.include({ name: 'electron' })
|
||||
})
|
||||
|
||||
it('uses lastBrowser if available', async () => {
|
||||
ctx.project.getProjectPreferences = sinon.stub().resolves({ lastBrowser: { name: 'chrome', channel: 'beta' } })
|
||||
|
||||
await ctx.lifecycleManager.setInitialActiveBrowser()
|
||||
|
||||
expect(ctx.coreData.activeBrowser).to.include({ name: 'chrome', displayName: 'Chrome Beta' })
|
||||
})
|
||||
|
||||
it('falls back to browsers[0] if lastBrowser does not exist', async () => {
|
||||
ctx.project.getProjectPreferences = sinon.stub().resolves({ lastBrowser: { name: 'chrome', channel: 'dev' } })
|
||||
|
||||
await ctx.lifecycleManager.setInitialActiveBrowser()
|
||||
|
||||
expect(ctx.coreData.activeBrowser).to.include({ name: 'electron' })
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -271,6 +271,9 @@ function startAppServer (mode: 'component' | 'e2e' = 'e2e') {
|
||||
|
||||
await isInitialized.promise
|
||||
|
||||
if (!ctx.lifecycleManager.browsers?.length) throw new Error('No browsers available in startAppServer')
|
||||
|
||||
await ctx.actions.app.setActiveBrowser(ctx.lifecycleManager.browsers[0])
|
||||
await ctx.actions.project.launchProject(o.mode, { url: o.url })
|
||||
|
||||
return ctx.appServerPort
|
||||
|
||||
@@ -18,7 +18,7 @@ export interface ClientTestContext {
|
||||
currentProject: CurrentProject | null
|
||||
projects: GlobalProject[]
|
||||
app: {
|
||||
currentBrowser: Browser | null
|
||||
activeBrowser: Browser | null
|
||||
browsers: Browser[] | null
|
||||
}
|
||||
versions: VersionData
|
||||
@@ -29,7 +29,6 @@ export interface ClientTestContext {
|
||||
chosenFramework: WizardFrontendFramework | null
|
||||
chosenManualInstall: boolean
|
||||
allBundlers: WizardBundler[]
|
||||
chosenBrowser: null
|
||||
warnings: []
|
||||
}
|
||||
migration: {}
|
||||
@@ -52,7 +51,7 @@ export function makeClientTestContext (): ClientTestContext {
|
||||
projects: [stubGlobalProject, createTestGlobalProject('another-test-project')],
|
||||
app: {
|
||||
browsers: stubBrowsers,
|
||||
currentBrowser: stubBrowsers[0],
|
||||
activeBrowser: stubBrowsers[0],
|
||||
},
|
||||
versions: {
|
||||
__typename: 'VersionData',
|
||||
@@ -77,7 +76,6 @@ export function makeClientTestContext (): ClientTestContext {
|
||||
chosenFramework: null,
|
||||
chosenManualInstall: false,
|
||||
allBundlers,
|
||||
chosenBrowser: null,
|
||||
warnings: [],
|
||||
},
|
||||
user: null,
|
||||
|
||||
@@ -47,7 +47,7 @@ export const createTestCurrentProject = (title: string, currentProject: Partial<
|
||||
__typename: 'CodeGenGlobs',
|
||||
component: '**/*.vue',
|
||||
},
|
||||
currentBrowser: stubBrowsers[0],
|
||||
activeBrowser: stubBrowsers[0],
|
||||
browsers: stubBrowsers,
|
||||
isDefaultSpecPattern: true,
|
||||
browserStatus: 'closed',
|
||||
|
||||
@@ -102,19 +102,19 @@
|
||||
</ExternalLink>
|
||||
|
||||
<TopNavList
|
||||
v-if="props.gql?.currentProject?.currentBrowser && showBrowsers"
|
||||
v-if="props.gql?.currentProject?.activeBrowser && showBrowsers"
|
||||
>
|
||||
<template #heading="{ open }">
|
||||
<img
|
||||
class="w-16px filter group-hocus:grayscale-0"
|
||||
data-cy="top-nav-active-browser-icon"
|
||||
:class="open ? 'grayscale-0' : 'grayscale'"
|
||||
:src="allBrowsersIcons[props.gql?.currentProject?.currentBrowser?.displayName] || allBrowsersIcons.generic"
|
||||
:src="allBrowsersIcons[props.gql?.currentProject?.activeBrowser?.displayName] || allBrowsersIcons.generic"
|
||||
>
|
||||
<span
|
||||
data-cy="top-nav-active-browser"
|
||||
class="font-medium whitespace-nowrap"
|
||||
>{{ props.gql.currentProject?.currentBrowser?.displayName }} {{ props.gql.currentProject?.currentBrowser?.majorVersion }}</span>
|
||||
>{{ props.gql.currentProject?.activeBrowser?.displayName }} {{ props.gql.currentProject?.activeBrowser?.majorVersion }}</span>
|
||||
</template>
|
||||
<VerticalBrowserListItems
|
||||
:gql="props.gql.currentProject"
|
||||
@@ -230,7 +230,7 @@ fragment TopNav on Query {
|
||||
id
|
||||
title
|
||||
packageManager
|
||||
currentBrowser {
|
||||
activeBrowser {
|
||||
id
|
||||
displayName
|
||||
majorVersion
|
||||
|
||||
@@ -355,6 +355,9 @@ enum CodeLanguageEnum {
|
||||
The currently opened Cypress project, represented by a cypress.config.{ts|js} file
|
||||
"""
|
||||
type CurrentProject implements Node & ProjectLike {
|
||||
"""The currently selected browser for the project"""
|
||||
activeBrowser: Browser
|
||||
|
||||
"""The current branch of the project"""
|
||||
branch: String
|
||||
|
||||
@@ -380,9 +383,6 @@ type CurrentProject implements Node & ProjectLike {
|
||||
"""Config File Absolute Path"""
|
||||
configFileAbsolutePath: String
|
||||
|
||||
"""The currently selected browser for the application"""
|
||||
currentBrowser: Browser
|
||||
|
||||
"""The mode the interactive runner was launched in"""
|
||||
currentTestingType: TestingTypeEnum
|
||||
|
||||
@@ -1129,9 +1129,6 @@ interface ProjectLike {
|
||||
|
||||
"""Preferences specific to a project"""
|
||||
type ProjectPreferences {
|
||||
"""The preferred browser to launch"""
|
||||
browserPath: String
|
||||
|
||||
"""The preferred testing type to start in"""
|
||||
testingType: String
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ export const CurrentProject = objectType({
|
||||
resolve: (_, args, ctx) => ctx.coreData.currentTestingType,
|
||||
})
|
||||
|
||||
t.field('currentBrowser', {
|
||||
t.field('activeBrowser', {
|
||||
type: Browser,
|
||||
description: 'The currently selected browser for the application',
|
||||
description: 'The currently selected browser for the project',
|
||||
resolve: (source, args, ctx) => {
|
||||
return ctx.coreData.chosenBrowser
|
||||
return ctx.coreData.activeBrowser
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -229,8 +229,8 @@ export const mutation = mutationType({
|
||||
description: 'ID of the browser that we want to set',
|
||||
})),
|
||||
},
|
||||
resolve (_, args, ctx) {
|
||||
ctx.actions.app.setActiveBrowserById(args.id)
|
||||
async resolve (_, args, ctx) {
|
||||
await ctx.actions.app.setActiveBrowserById(args.id)
|
||||
|
||||
return ctx.lifecycleManager
|
||||
},
|
||||
|
||||
@@ -7,9 +7,5 @@ export const ProjectPreferences = objectType({
|
||||
t.string('testingType', {
|
||||
description: 'The preferred testing type to start in',
|
||||
})
|
||||
|
||||
t.string('browserPath', {
|
||||
description: 'The preferred browser to launch',
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
@@ -254,7 +254,7 @@ describe('Choose a Browser Page', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('subscribes to changes to browserStatus/currentBrowser through the browserStatusUpdated subscription', () => {
|
||||
it('subscribes to changes to browserStatus/activeBrowser through the browserStatusUpdated subscription', () => {
|
||||
cy.openProject('launchpad', ['--e2e'])
|
||||
|
||||
cy.visitLaunchpad()
|
||||
|
||||
@@ -111,7 +111,7 @@ describe('<OpenBrowserList />', () => {
|
||||
cy.mountFragment(OpenBrowserListFragmentDoc, {
|
||||
onResult: (res) => {
|
||||
res.browserStatus = 'open'
|
||||
res.currentBrowser!.isFocusSupported = false
|
||||
res.activeBrowser!.isFocusSupported = false
|
||||
},
|
||||
render: (gqlVal) => {
|
||||
return (
|
||||
@@ -129,10 +129,15 @@ describe('<OpenBrowserList />', () => {
|
||||
cy.percySnapshot()
|
||||
})
|
||||
|
||||
it('hides action buttons when currentBrowser is null', () => {
|
||||
it('throws when activeBrowser is null', (done) => {
|
||||
cy.once('uncaught:exception', (err) => {
|
||||
expect(err.message).to.include('Missing activeBrowser in selectedBrowserId')
|
||||
done()
|
||||
})
|
||||
|
||||
cy.mountFragment(OpenBrowserListFragmentDoc, {
|
||||
onResult: (res) => {
|
||||
res.currentBrowser = null
|
||||
res.activeBrowser = null
|
||||
},
|
||||
render: (gqlVal) => {
|
||||
return (
|
||||
@@ -143,11 +148,5 @@ describe('<OpenBrowserList />', () => {
|
||||
</div>)
|
||||
},
|
||||
})
|
||||
|
||||
cy.get('button[data-cy="launch-button"]').should('not.exist')
|
||||
cy.contains('button', defaultMessages.openBrowser.focus).should('not.exist')
|
||||
cy.contains('button', defaultMessages.openBrowser.close).should('not.exist')
|
||||
|
||||
cy.percySnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
{{ browserText.running }}
|
||||
</Button>
|
||||
<Button
|
||||
v-if="props.gql.currentBrowser?.isFocusSupported"
|
||||
v-if="props.gql.activeBrowser?.isFocusSupported"
|
||||
size="lg"
|
||||
type="button"
|
||||
variant="outline"
|
||||
@@ -181,7 +181,7 @@ mutation OpenBrowserList_SetBrowser($id: ID!) {
|
||||
gql`
|
||||
fragment OpenBrowserList on CurrentProject {
|
||||
id
|
||||
currentBrowser {
|
||||
activeBrowser {
|
||||
id
|
||||
isFocusSupported
|
||||
}
|
||||
@@ -202,7 +202,7 @@ subscription OpenBrowserList_browserStatusChange {
|
||||
browserStatusChange {
|
||||
id
|
||||
browserStatus
|
||||
currentBrowser {
|
||||
activeBrowser {
|
||||
id
|
||||
isFocusSupported
|
||||
}
|
||||
@@ -237,9 +237,10 @@ const setBrowser = useMutation(OpenBrowserList_SetBrowserDocument)
|
||||
|
||||
const selectedBrowserId = computed({
|
||||
get: () => {
|
||||
// NOTE: The currentBrowser is set to the first detected browser
|
||||
// found during project initialization. It should always be defined.
|
||||
return props.gql.currentBrowser?.id
|
||||
// NOTE: The activeBrowser is set during project initialization. It should always be defined.
|
||||
if (!props.gql.activeBrowser) throw new Error('Missing activeBrowser in selectedBrowserId')
|
||||
|
||||
return props.gql.activeBrowser.id
|
||||
},
|
||||
set (browserId) {
|
||||
if (browserId) {
|
||||
|
||||
@@ -245,8 +245,8 @@ const parseBrowserOption = (opt) => {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureAndGetByNameOrPath(nameOrPath: string, returnAll: false, browsers: FoundBrowser[]): Bluebird<FoundBrowser | undefined>
|
||||
function ensureAndGetByNameOrPath(nameOrPath: string, returnAll: true, browsers: FoundBrowser[]): Bluebird<FoundBrowser[] | undefined>
|
||||
function ensureAndGetByNameOrPath(nameOrPath: string, returnAll: false, browsers: FoundBrowser[]): Bluebird<FoundBrowser>
|
||||
function ensureAndGetByNameOrPath(nameOrPath: string, returnAll: true, browsers: FoundBrowser[]): Bluebird<FoundBrowser[]>
|
||||
|
||||
function ensureAndGetByNameOrPath (nameOrPath: string, returnAll = false, browsers: FoundBrowser[] = []) {
|
||||
const findBrowsers = browsers.length ? Bluebird.resolve(browsers) : getBrowsers()
|
||||
|
||||
@@ -75,7 +75,7 @@ export function makeDataContext (options: MakeDataContextOptions): DataContext {
|
||||
const windows = require('./gui/windows')
|
||||
const originalIsMainWindowFocused = windows.isMainWindowFocused()
|
||||
const onLoginFlowComplete = async () => {
|
||||
if (originalIsMainWindowFocused || !ctx.browser.isFocusSupported(ctx.coreData.chosenBrowser)) {
|
||||
if (originalIsMainWindowFocused || !ctx.browser.isFocusSupported(ctx.coreData.activeBrowser)) {
|
||||
windows.focusMainWindow()
|
||||
} else {
|
||||
await ctx.actions.browser.focusActiveBrowserWindow()
|
||||
|
||||
@@ -4,9 +4,9 @@ export interface Cache {
|
||||
USER: CachedUser
|
||||
}
|
||||
|
||||
export interface Preferences {
|
||||
testingType: 'e2e' | 'component' | null
|
||||
}
|
||||
import type { AllowedState } from './preferences'
|
||||
|
||||
export type Preferences = AllowedState
|
||||
|
||||
export interface CachedUser {
|
||||
authToken: string
|
||||
|
||||
@@ -33,6 +33,7 @@ export const allowedKeys: Readonly<Array<keyof AllowedState>> = [
|
||||
'promptsShown',
|
||||
'preferredEditorBinary',
|
||||
'isSideNavigationOpen',
|
||||
'lastBrowser',
|
||||
] as const
|
||||
|
||||
type Maybe<T> = T | null | undefined
|
||||
@@ -64,4 +65,6 @@ export type AllowedState = Partial<{
|
||||
promptsShown: Maybe<object>
|
||||
preferredEditorBinary: Maybe<string>
|
||||
isSideNavigationOpen: Maybe<boolean>
|
||||
testingType: 'e2e' | 'component'
|
||||
lastBrowser: { name: string, channel: string }
|
||||
}>
|
||||
|
||||
Reference in New Issue
Block a user