Skip to content

Commit c4adc8c

Browse files
authored
fix: Ensuring current browser is synchronized between app and launchpad (#20830)
* fix: Ensuring current browser is synchronized between app and launchpad * Adding e2e test * Fixing types * Using slot prop for radio group to determine selection styles
1 parent 5b4ceed commit c4adc8c

File tree

5 files changed

+145
-119
lines changed

5 files changed

+145
-119
lines changed

packages/app/src/runner/SpecRunnerHeaderOpenMode.vue

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -171,26 +171,6 @@ fragment SpecRunnerHeader_Browser on Browser {
171171
}
172172
`
173173
174-
gql`
175-
mutation SpecRunnerHeader_SetBrowser($browserId: ID!, $specPath: String!) {
176-
launchpadSetBrowser(id: $browserId) {
177-
id
178-
currentBrowser {
179-
id
180-
displayName
181-
majorVersion
182-
}
183-
browsers {
184-
id
185-
isSelected
186-
}
187-
}
188-
launchOpenProject(specPath: $specPath) {
189-
id
190-
}
191-
}
192-
`
193-
194174
const { t } = useI18n()
195175
196176
const autStore = useAutStore()

packages/data-context/src/actions/DataEmitterActions.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ abstract class DataEmitterEvents {
2828
this._emit('cloudViewerChange')
2929
}
3030

31+
/**
32+
* Emitted when the browserStatus field has changed due to the browser
33+
* having opened or closed.
34+
*/
3135
browserStatusChange () {
3236
this._emit('browserStatusChange')
3337
}

packages/launchpad/cypress/e2e/choose-a-browser.cy.ts

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { FoundBrowser } from '@packages/types/src'
2+
13
describe('Choose a Browser Page', () => {
24
beforeEach(() => {
35
cy.scaffoldProject('launchpad')
@@ -174,8 +176,8 @@ describe('Choose a Browser Page', () => {
174176

175177
cy.get('h1').should('contain', 'Choose a Browser')
176178

177-
cy.withCtx((ctx) => {
178-
ctx.actions.project.launchProject = sinon.spy()
179+
cy.withCtx((ctx, o) => {
180+
ctx.actions.project.launchProject = o.sinon.spy()
179181
})
180182

181183
cy.intercept('mutation-OpenBrowser_LaunchProject', cy.stub().as('launchProject'))
@@ -217,8 +219,8 @@ describe('Choose a Browser Page', () => {
217219

218220
cy.intercept('mutation-OpenBrowser_FocusActiveBrowserWindow').as('focusBrowser')
219221

220-
cy.withCtx((ctx) => {
221-
sinon.spy(ctx.actions.browser, 'focusActiveBrowserWindow')
222+
cy.withCtx((ctx, o) => {
223+
o.sinon.spy(ctx.actions.browser, 'focusActiveBrowserWindow')
222224
})
223225

224226
cy.get('@focusButton').click()
@@ -238,9 +240,9 @@ describe('Choose a Browser Page', () => {
238240
})
239241

240242
cy.openProject('launchpad', ['--e2e'])
241-
cy.withCtx((ctx) => {
243+
cy.withCtx((ctx, o) => {
242244
ctx.project.setRelaunchBrowser(true)
243-
ctx.actions.project.launchProject = sinon.stub()
245+
ctx.actions.project.launchProject = o.sinon.stub()
244246
})
245247

246248
cy.visitLaunchpad()
@@ -251,6 +253,34 @@ describe('Choose a Browser Page', () => {
251253
expect(ctx.actions.project.launchProject).to.have.been.called
252254
})
253255
})
256+
257+
it('subscribes to changes to browserStatus/currentBrowser through the browserStatusUpdated subscription', () => {
258+
cy.openProject('launchpad', ['--e2e'])
259+
260+
cy.visitLaunchpad()
261+
262+
cy.get('h1').should('contain', 'Choose a Browser')
263+
264+
cy.findByRole('radio', { name: 'Chrome v1', checked: true }).as('chromeItem')
265+
266+
cy.contains('button', 'Start E2E Testing in Chrome').should('be.visible').click()
267+
268+
cy.withCtx((ctx) => {
269+
ctx.browser.setBrowserStatus('open')
270+
})
271+
272+
cy.contains('button', 'Running Chrome')
273+
274+
// Updating active browser in conjunction with the browser status to ensure that changes to
275+
// both are reflected in the UI.
276+
cy.withCtx((ctx) => {
277+
ctx.actions.app.setActiveBrowser(ctx.lifecycleManager.browsers!.find((browser) => browser.name === 'firefox') as FoundBrowser)
278+
ctx.browser.setBrowserStatus('closed')
279+
})
280+
281+
cy.contains('button', 'Start E2E Testing in Firefox').should('be.visible')
282+
cy.findByRole('radio', { name: 'Firefox v5', checked: true }).should('be.visible')
283+
})
254284
})
255285

256286
describe('No System Browsers Detected', () => {

packages/launchpad/src/setup/OpenBrowserList.cy.tsx

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@ describe('<OpenBrowserList />', () => {
1414

1515
it('renders a long list of found browsers correctly', () => {
1616
cy.mountFragment(OpenBrowserListFragmentDoc, {
17-
onResult: (result) => {
18-
result.currentBrowser = null
19-
},
2017
render: (gqlVal) =>
2118
(<div class="border-current border-1 resize overflow-auto">
2219
<OpenBrowserList gql={gqlVal}/>
@@ -36,19 +33,11 @@ describe('<OpenBrowserList />', () => {
3633
cy.get('[data-cy-browser="fake"]').should('have.attr', 'aria-disabled', 'true')
3734
cy.get('[data-cy-browser="fake"] img').should('have.attr', 'src').should('include', 'generic-browser')
3835

39-
// If no default value, should choose electron
40-
cy.get('[data-cy-browser="electron"]').should('have.attr', 'aria-checked', 'true')
41-
cy.get('[data-cy="launch-button"]').contains(defaultMessages.openBrowser.startE2E.replace('{browser}', 'Electron'))
42-
cy.get('[data-cy="launch-button"]').get('[data-cy="icon-testing-type-e2e"]')
43-
4436
cy.percySnapshot()
4537
})
4638

4739
it('displays a tooltip for an unsupported browser', () => {
4840
cy.mountFragment(OpenBrowserListFragmentDoc, {
49-
onResult: (result) => {
50-
result.currentBrowser = null
51-
},
5241
render: (gqlVal) =>
5342
(<div class="border-current border-1 resize overflow-auto">
5443
<div class="h-40" />
@@ -122,21 +111,42 @@ describe('<OpenBrowserList />', () => {
122111
cy.mountFragment(OpenBrowserListFragmentDoc, {
123112
onResult: (res) => {
124113
res.browserStatus = 'open'
125-
res.currentBrowser = longBrowsersList.find((browser) => !browser.isFocusSupported) || null
114+
res.currentBrowser!.isFocusSupported = false
115+
},
116+
render: (gqlVal) => {
117+
return (
118+
<div class="border-current border-1 resize overflow-auto">
119+
<OpenBrowserList
120+
gql={gqlVal}
121+
onCloseBrowser={cy.stub().as('closeBrowser')}/>
122+
</div>)
126123
},
127-
render: (gqlVal) => (
128-
<div class="border-current border-1 resize overflow-auto">
129-
<OpenBrowserList
130-
gql={gqlVal}
131-
onCloseBrowser={cy.stub().as('closeBrowser')}/>
132-
</div>),
133124
})
134125

135-
cy.get('[data-cy-browser]').each((browser) => cy.wrap(browser).should('have.attr', 'aria-disabled', 'true'))
136126
cy.contains('button', defaultMessages.openBrowser.running.replace('{browser}', 'Electron')).should('be.disabled')
137127
cy.contains('button', defaultMessages.openBrowser.focus).should('not.exist')
138-
cy.contains('button', defaultMessages.openBrowser.close).click()
139-
cy.get('@closeBrowser').should('have.been.called')
128+
129+
cy.percySnapshot()
130+
})
131+
132+
it('hides action buttons when currentBrowser is null', () => {
133+
cy.mountFragment(OpenBrowserListFragmentDoc, {
134+
onResult: (res) => {
135+
res.currentBrowser = null
136+
},
137+
render: (gqlVal) => {
138+
return (
139+
<div class="border-current border-1 resize overflow-auto">
140+
<OpenBrowserList
141+
gql={gqlVal}
142+
onCloseBrowser={cy.stub().as('closeBrowser')}/>
143+
</div>)
144+
},
145+
})
146+
147+
cy.get('button[data-cy="launch-button"]').should('not.exist')
148+
cy.contains('button', defaultMessages.openBrowser.focus).should('not.exist')
149+
cy.contains('button', defaultMessages.openBrowser.close).should('not.exist')
140150

141151
cy.percySnapshot()
142152
})

packages/launchpad/src/setup/OpenBrowserList.vue

Lines changed: 74 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
</div>
5656
<div
5757
class="font-medium pt-2 text-indigo-600 text-18px leading-28px"
58-
:class="{ 'text-jade-600': browser.isSelected, 'text-gray-500': browser.disabled || !browser.isVersionSupported }"
58+
:class="{ 'text-jade-600': checked, 'text-gray-500': browser.disabled || !browser.isVersionSupported }"
5959
>
6060
{{ browser.displayName }}
6161
</div>
@@ -70,68 +70,69 @@
7070
class="mb-14"
7171
>
7272
<div class="flex mb-4 gap-16px items-center justify-center">
73-
<template v-if="browserStatus.closed || browserStatus.opening">
74-
<Button
75-
v-if="browserStatus.closed"
76-
size="lg"
77-
type="submit"
78-
:prefix-icon="testingTypeIcon"
79-
prefix-icon-class="icon-dark-white"
80-
variant="secondary"
81-
data-cy="launch-button"
82-
class="font-medium"
83-
>
84-
{{ browserText[props.gql.currentTestingType].start }}
85-
</Button>
86-
<Button
87-
v-else
88-
size="lg"
89-
type="button"
90-
disabled
91-
variant="pending"
92-
class="font-medium disabled:cursor-default"
93-
:prefix-icon="StatusRunningIcon"
94-
prefix-icon-class="icon-light-gray-300 icon-dark-white animate-spin"
95-
>
96-
{{ browserText[props.gql.currentTestingType].opening }}
97-
</Button>
98-
</template>
99-
100-
<template v-else>
101-
<Button
102-
size="lg"
103-
type="button"
104-
disabled
105-
variant="pending"
106-
:prefix-icon="testingTypeIcon"
107-
prefix-icon-class="icon-dark-white"
108-
class="font-medium disabled:cursor-default"
109-
>
110-
{{ browserText.running }}
111-
</Button>
112-
<Button
113-
v-if="props.gql.currentBrowser?.isFocusSupported"
114-
size="lg"
115-
type="button"
116-
variant="outline"
117-
:prefix-icon="ExportIcon"
118-
prefix-icon-class="icon-dark-gray-500"
119-
class="font-medium"
120-
@click="emit('focusBrowser')"
121-
>
122-
{{ browserText.focus }}
123-
</Button>
124-
<Button
125-
size="lg"
126-
type="button"
127-
variant="outline"
128-
:prefix-icon="PowerStandbyIcon"
129-
prefix-icon-class="icon-dark-gray-500"
130-
class="font-medium"
131-
@click="emit('closeBrowser')"
132-
>
133-
{{ browserText.close }}
134-
</Button>
73+
<template v-if="selectedBrowserId">
74+
<template v-if="browserStatus.closed || browserStatus.opening">
75+
<Button
76+
v-if="browserStatus.closed"
77+
size="lg"
78+
type="submit"
79+
:prefix-icon="testingTypeIcon"
80+
prefix-icon-class="icon-dark-white"
81+
variant="secondary"
82+
data-cy="launch-button"
83+
class="font-medium"
84+
>
85+
{{ browserText[props.gql.currentTestingType].start }}
86+
</Button>
87+
<Button
88+
v-else
89+
size="lg"
90+
type="button"
91+
disabled
92+
variant="pending"
93+
class="font-medium disabled:cursor-default"
94+
:prefix-icon="StatusRunningIcon"
95+
prefix-icon-class="icon-light-gray-300 icon-dark-white animate-spin"
96+
>
97+
{{ browserText[props.gql.currentTestingType].opening }}
98+
</Button>
99+
</template>
100+
<template v-else>
101+
<Button
102+
size="lg"
103+
type="button"
104+
disabled
105+
variant="pending"
106+
:prefix-icon="testingTypeIcon"
107+
prefix-icon-class="icon-dark-white"
108+
class="font-medium disabled:cursor-default"
109+
>
110+
{{ browserText.running }}
111+
</Button>
112+
<Button
113+
v-if="props.gql.currentBrowser?.isFocusSupported"
114+
size="lg"
115+
type="button"
116+
variant="outline"
117+
:prefix-icon="ExportIcon"
118+
prefix-icon-class="icon-dark-gray-500"
119+
class="font-medium"
120+
@click="emit('focusBrowser')"
121+
>
122+
{{ browserText.focus }}
123+
</Button>
124+
<Button
125+
size="lg"
126+
type="button"
127+
variant="outline"
128+
:prefix-icon="PowerStandbyIcon"
129+
prefix-icon-class="icon-dark-gray-500"
130+
class="font-medium"
131+
@click="emit('closeBrowser')"
132+
>
133+
{{ browserText.close }}
134+
</Button>
135+
</template>
135136
</template>
136137
</div>
137138

@@ -182,21 +183,14 @@ fragment OpenBrowserList on CurrentProject {
182183
id
183184
currentBrowser {
184185
id
185-
displayName
186-
path
187186
isFocusSupported
188187
}
189188
browsers {
190189
id
191-
name
192-
family
193190
disabled
194-
isSelected
195191
isVersionSupported
196-
channel
192+
name
197193
displayName
198-
path
199-
version
200194
warning
201195
majorVersion
202196
}
@@ -208,6 +202,10 @@ subscription OpenBrowserList_browserStatusChange {
208202
browserStatusChange {
209203
id
210204
browserStatus
205+
currentBrowser {
206+
id
207+
isFocusSupported
208+
}
211209
}
212210
}
213211
`
@@ -238,7 +236,11 @@ const browsers = computed(() => {
238236
const setBrowser = useMutation(OpenBrowserList_SetBrowserDocument)
239237
240238
const selectedBrowserId = computed({
241-
get: () => props.gql.currentBrowser?.id || props.gql.browsers?.find((browser) => browser.displayName === 'Electron')?.id,
239+
get: () => {
240+
// NOTE: The currentBrowser is set to the first detected browser
241+
// found during project initialization. It should always be defined.
242+
return props.gql.currentBrowser?.id
243+
},
242244
set (browserId) {
243245
if (browserId) {
244246
setBrowser.executeMutation({ id: browserId })

0 commit comments

Comments
 (0)