Skip to content

Commit 203758f

Browse files
ryanthemanuellmiller1990mjhenkes
authored
fix: properly run multiple specs in run and headed mode on linux and windows in chrome (#22168)
* fix: properly run multiple specs in run and headed mode on linux and windows * fix: properly run multiple specs in run and headed mode on linux and windows * Update test * Update test * Fix issue with running headed in linux and windows * Improve test * Update packages/server/lib/browsers/browser-cri-client.ts * PR comments * PR comments * Fix test failure due to refactor Co-authored-by: Lachlan Miller <[email protected]> Co-authored-by: Matt Henkes <[email protected]>
1 parent 600daef commit 203758f

File tree

15 files changed

+102
-72
lines changed

15 files changed

+102
-72
lines changed

packages/extension/app/background.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ const connect = function (host, path, extraOpts) {
127127
return invoke('takeScreenshot', id)
128128
case 'reset:browser:state':
129129
return invoke('resetBrowserState', id)
130-
case 'close:browser:tabs':
131-
return invoke('closeBrowserTabs', id)
130+
case 'reset:browser:tabs:for:next:test':
131+
return invoke('resetBrowserTabsForNextTest', id)
132132
default:
133133
return fail(id, { message: `No handler registered for: '${msg}'` })
134134
}
@@ -264,8 +264,10 @@ const automation = {
264264
return browser.browsingData.remove({}, { cache: true, cookies: true, downloads: true, formData: true, history: true, indexedDB: true, localStorage: true, passwords: true, pluginData: true, serviceWorkers: true }).then(fn)
265265
},
266266

267-
closeBrowserTabs (fn) {
267+
resetBrowserTabsForNextTest (fn) {
268268
return Promise.try(() => {
269+
return browser.tabs.create({ url: 'about:blank' })
270+
}).then(() => {
269271
return browser.windows.getCurrent({ populate: true })
270272
}).then((windowInfo) => {
271273
return browser.tabs.remove(windowInfo.tabs.map((tab) => tab.id))

packages/extension/test/integration/background_spec.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const browser = {
3030
},
3131
runtime: {},
3232
tabs: {
33+
create () {},
3334
query () {},
3435
executeScript () {},
3536
captureVisibleTab () {},
@@ -48,6 +49,7 @@ const browser = {
4849
mockRequire('webextension-polyfill', browser)
4950

5051
const background = require('../../app/background')
52+
const { expect } = require('chai')
5153

5254
const PORT = 12345
5355

@@ -846,8 +848,9 @@ describe('app/background', () => {
846848
})
847849
})
848850

849-
describe('close:browser:tabs', () => {
851+
describe('reset:browser:tabs:for:next:test', () => {
850852
beforeEach(() => {
853+
sinon.stub(browser.tabs, 'create').withArgs({ url: 'about:blank' })
851854
sinon.stub(browser.windows, 'getCurrent').withArgs({ populate: true }).resolves({ id: '10', tabs: [{ id: '1' }, { id: '2' }, { id: '3' }] })
852855
sinon.stub(browser.tabs, 'remove').withArgs(['1', '2', '3']).resolves()
853856
})
@@ -857,13 +860,14 @@ describe('app/background', () => {
857860
expect(id).to.eq(123)
858861
expect(obj.response).to.be.undefined
859862

863+
expect(browser.tabs.create).to.be.called
860864
expect(browser.windows.getCurrent).to.be.called
861865
expect(browser.tabs.remove).to.be.called
862866

863867
done()
864868
})
865869

866-
return this.server.emit('automation:request', 123, 'close:browser:tabs')
870+
return this.server.emit('automation:request', 123, 'reset:browser:tabs:for:next:test')
867871
})
868872
})
869873
})

packages/server/lib/browsers/browser-cri-client.ts

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const retryWithIncreasingDelay = async <T>(retryable: () => Promise<T>, browserN
6767
}
6868

6969
export class BrowserCriClient {
70-
private currentlyAttachedTarget: CRIWrapper.Client | undefined
70+
currentlyAttachedTarget: CRIWrapper.Client | undefined
7171
private constructor (private browserClient: CRIWrapper.Client, private versionInfo, private port: number, private browserName: string, private onAsynchronousError: Function) {}
7272

7373
/**
@@ -132,28 +132,22 @@ export class BrowserCriClient {
132132
}
133133

134134
/**
135-
* Creates a new target with the given url and then attaches to it
135+
* Resets the browser's targets optionally keeping a tab open
136136
*
137-
* @param url the url to create and attach to
138-
* @returns the chrome remote interface wrapper for the target
139-
*/
140-
attachToNewUrl = async (url: string): Promise<CRIWrapper.Client> => {
141-
debug('Attaching to new url %s', url)
142-
const target = await this.browserClient.send('Target.createTarget', { url })
143-
144-
this.currentlyAttachedTarget = await create(target.targetId, this.onAsynchronousError, HOST, this.port)
145-
146-
return this.currentlyAttachedTarget
147-
}
148-
149-
/**
150-
* Closes the currently attached page target
137+
* @param shouldKeepTabOpen whether or not to keep the tab open
151138
*/
152-
closeCurrentTarget = async (): Promise<void> => {
139+
resetBrowserTargets = async (shouldKeepTabOpen: boolean): Promise<void> => {
153140
if (!this.currentlyAttachedTarget) {
154141
throw new Error('Cannot close target because no target is currently attached')
155142
}
156143

144+
let target
145+
146+
// If we are keeping a tab open, we need to first launch a new default tab prior to closing the existing one
147+
if (shouldKeepTabOpen) {
148+
target = await this.browserClient.send('Target.createTarget', { url: 'about:blank' })
149+
}
150+
157151
debug('Closing current target %s', this.currentlyAttachedTarget.targetId)
158152

159153
await Promise.all([
@@ -162,7 +156,9 @@ export class BrowserCriClient {
162156
this.browserClient.send('Target.closeTarget', { targetId: this.currentlyAttachedTarget.targetId }),
163157
])
164158

165-
this.currentlyAttachedTarget = undefined
159+
if (target) {
160+
this.currentlyAttachedTarget = await create(target.targetId, this.onAsynchronousError, HOST, this.port)
161+
}
166162
}
167163

168164
/**

packages/server/lib/browsers/cdp_automation.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ const normalizeResourceType = (resourceType: string | undefined): ResourceType =
164164
}
165165

166166
type SendDebuggerCommand = (message: string, data?: any) => Promise<any>
167-
type SendCloseCommand = () => Promise<any>
167+
type SendCloseCommand = (shouldKeepTabOpen: boolean) => Promise<any>
168168
type OnFn = (eventName: string, cb: Function) => void
169169

170170
// the intersection of what's valid in CDP and what's valid in FFCDP
@@ -355,8 +355,8 @@ export class CdpAutomation {
355355
this.sendDebuggerCommandFn('Storage.clearDataForOrigin', { origin: '*', storageTypes: 'all' }),
356356
this.sendDebuggerCommandFn('Network.clearBrowserCache'),
357357
])
358-
case 'close:browser:tabs':
359-
return this.sendCloseCommandFn()
358+
case 'reset:browser:tabs:for:next:test':
359+
return this.sendCloseCommandFn(data.shouldKeepTabOpen)
360360
case 'focus:browser:window':
361361
return this.sendDebuggerCommandFn('Page.bringToFront')
362362
default:

packages/server/lib/browsers/chrome.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,8 @@ const _handlePausedRequests = async (client) => {
433433
})
434434
}
435435

436-
const _setAutomation = async (client: CRIWrapper.Client, automation: Automation, closeCurrentTarget: () => Promise<void>, options: CypressConfiguration = {}) => {
437-
const cdpAutomation = await CdpAutomation.create(client.send, client.on, closeCurrentTarget, automation, options.experimentalSessionAndOrigin)
436+
const _setAutomation = async (client: CRIWrapper.Client, automation: Automation, resetBrowserTargets: (shouldKeepTabOpen: boolean) => Promise<void>, options: CypressConfiguration = {}) => {
437+
const cdpAutomation = await CdpAutomation.create(client.send, client.on, resetBrowserTargets, automation, options.experimentalSessionAndOrigin)
438438

439439
return automation.use(cdpAutomation)
440440
}
@@ -555,9 +555,9 @@ export = {
555555
debug('connecting to new chrome tab in existing instance with url and debugging port', { url: options.url })
556556

557557
const browserCriClient = this._getBrowserCriClient()
558-
const pageCriClient = await browserCriClient.attachToNewUrl('about:blank')
558+
const pageCriClient = browserCriClient.currentlyAttachedTarget
559559

560-
await this._setAutomation(pageCriClient, automation, browserCriClient.closeCurrentTarget, options)
560+
await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)
561561

562562
// make sure page events are re enabled or else frame tree updates will NOT work as well as other items listening for page events
563563
await pageCriClient.send('Page.enable')
@@ -584,7 +584,7 @@ export = {
584584
const browserCriClient = await BrowserCriClient.create(port, browser.displayName, options.onError, onReconnect)
585585
const pageCriClient = await browserCriClient.attachToTargetUrl(options.url)
586586

587-
await this._setAutomation(pageCriClient, automation, browserCriClient.closeCurrentTarget, options)
587+
await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)
588588
},
589589

590590
async open (browser: Browser, url, options: CypressConfiguration = {}, automation: Automation): Promise<LaunchedBrowser> {
@@ -679,7 +679,7 @@ export = {
679679

680680
const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank')
681681

682-
await this._setAutomation(pageCriClient, automation, browserCriClient.closeCurrentTarget, options)
682+
await this._setAutomation(pageCriClient, automation, browserCriClient.resetBrowserTargets, options)
683683

684684
await pageCriClient.send('Page.enable')
685685

packages/server/lib/browsers/firefox-util.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ async function connectToNewSpec (options, automation: Automation, browserCriClie
126126

127127
const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank')
128128

129-
await CdpAutomation.create(pageCriClient.send, pageCriClient.on, browserCriClient.closeCurrentTarget, automation, options.experimentalSessionAndOrigin)
129+
await CdpAutomation.create(pageCriClient.send, pageCriClient.on, browserCriClient.resetBrowserTargets, automation, options.experimentalSessionAndOrigin)
130130

131131
await options.onInitializeNewBrowserTab()
132132

@@ -138,7 +138,7 @@ async function setupRemote (remotePort, automation, onError, options): Promise<B
138138
const browserCriClient = await BrowserCriClient.create(remotePort, 'Firefox', onError)
139139
const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank')
140140

141-
await CdpAutomation.create(pageCriClient.send, pageCriClient.on, browserCriClient.closeCurrentTarget, automation, options.experimentalSessionAndOrigin)
141+
await CdpAutomation.create(pageCriClient.send, pageCriClient.on, browserCriClient.resetBrowserTargets, automation, options.experimentalSessionAndOrigin)
142142

143143
return browserCriClient
144144
}

packages/server/lib/modes/run.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,7 @@ module.exports = {
11691169
},
11701170

11711171
waitForTestsToFinishRunning (options = {}) {
1172-
const { project, screenshots, startedVideoCapture, endVideoCapture, videoName, compressedVideoName, videoCompression, videoUploadOnPasses, exit, spec, estimated, quiet, config } = options
1172+
const { project, screenshots, startedVideoCapture, endVideoCapture, videoName, compressedVideoName, videoCompression, videoUploadOnPasses, exit, spec, estimated, quiet, config, shouldKeepTabOpen } = options
11731173

11741174
// https://github.com/cypress-io/cypress/issues/2370
11751175
// delay 1 second if we're recording a video to give
@@ -1251,7 +1251,7 @@ module.exports = {
12511251
// await openProject.closeBrowser()
12521252
// } else {
12531253
debug('attempting to close the browser tab')
1254-
await openProject.closeBrowserTabs()
1254+
await openProject.resetBrowserTabsForNextTest(shouldKeepTabOpen)
12551255
// }
12561256

12571257
debug('resetting server state')
@@ -1330,16 +1330,16 @@ module.exports = {
13301330
})
13311331
}
13321332

1333-
let firstSpec = true
1333+
let isFirstSpec = true
13341334

13351335
const runEachSpec = (spec, index, length, estimated) => {
13361336
if (!options.quiet) {
13371337
displaySpecHeader(spec.baseName, index + 1, length, estimated)
13381338
}
13391339

1340-
return this.runSpec(config, spec, options, estimated, firstSpec)
1340+
return this.runSpec(config, spec, options, estimated, isFirstSpec, index === length - 1)
13411341
.tap(() => {
1342-
firstSpec = false
1342+
isFirstSpec = false
13431343
})
13441344
.get('results')
13451345
.tap((results) => {
@@ -1435,7 +1435,7 @@ module.exports = {
14351435
})
14361436
},
14371437

1438-
runSpec (config, spec = {}, options = {}, estimated, firstSpec) {
1438+
runSpec (config, spec = {}, options = {}, estimated, isFirstSpec, isLastSpec) {
14391439
const { project, browser, onError } = options
14401440

14411441
const { isHeadless } = browser
@@ -1484,6 +1484,7 @@ module.exports = {
14841484
videoUploadOnPasses: options.videoUploadOnPasses,
14851485
quiet: options.quiet,
14861486
browser,
1487+
shouldKeepTabOpen: !isLastSpec,
14871488
}),
14881489

14891490
connection: this.waitForBrowserToConnect({
@@ -1496,7 +1497,7 @@ module.exports = {
14961497
socketId: options.socketId,
14971498
webSecurity: options.webSecurity,
14981499
projectRoot: options.projectRoot,
1499-
shouldLaunchNewTab: !firstSpec, // !process.env.CYPRESS_INTERNAL_FORCE_BROWSER_RELAUNCH && !firstSpec,
1500+
shouldLaunchNewTab: !isFirstSpec, // !process.env.CYPRESS_INTERNAL_FORCE_BROWSER_RELAUNCH && !isFirstSpec,
15001501
// TODO(tim): investigate the socket disconnect
15011502
}),
15021503
})

packages/server/lib/open_project.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ export class OpenProject {
196196
return browsers.close()
197197
}
198198

199-
async closeBrowserTabs () {
200-
return this.projectBase?.closeBrowserTabs()
199+
async resetBrowserTabsForNextTest (shouldKeepTabOpen: boolean) {
200+
return this.projectBase?.resetBrowserTabsForNextTest(shouldKeepTabOpen)
201201
}
202202

203203
async resetBrowserState () {

packages/server/lib/project-base.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,8 +409,8 @@ export class ProjectBase<TServer extends Server> extends EE {
409409
this.ctx.setAppSocketServer(io)
410410
}
411411

412-
async closeBrowserTabs () {
413-
return this.server.socket.closeBrowserTabs()
412+
async resetBrowserTabsForNextTest (shouldKeepTabOpen: boolean) {
413+
return this.server.socket.resetBrowserTabsForNextTest(shouldKeepTabOpen)
414414
}
415415

416416
async resetBrowserState () {

packages/server/lib/socket-base.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ const retry = (fn: (res: any) => void) => {
7676
}
7777

7878
export class SocketBase {
79-
private _sendCloseBrowserTabsMessage
79+
private _sendResetBrowserTabsForNextTestMessage
8080
private _sendResetBrowserStateMessage
8181
private _isRunnerSocketConnected
8282
private _sendFocusBrowserMessage
@@ -294,8 +294,8 @@ export class SocketBase {
294294
})
295295
})
296296

297-
this._sendCloseBrowserTabsMessage = async () => {
298-
await automationRequest('close:browser:tabs', {})
297+
this._sendResetBrowserTabsForNextTestMessage = async (shouldKeepTabOpen: boolean) => {
298+
await automationRequest('reset:browser:tabs:for:next:test', { shouldKeepTabOpen })
299299
}
300300

301301
this._sendResetBrowserStateMessage = async () => {
@@ -583,9 +583,9 @@ export class SocketBase {
583583
return this._io?.emit('tests:finished')
584584
}
585585

586-
async closeBrowserTabs () {
587-
if (this._sendCloseBrowserTabsMessage) {
588-
await this._sendCloseBrowserTabsMessage()
586+
async resetBrowserTabsForNextTest (shouldKeepTabOpen: boolean) {
587+
if (this._sendResetBrowserTabsForNextTestMessage) {
588+
await this._sendResetBrowserTabsForNextTestMessage(shouldKeepTabOpen)
589589
}
590590
}
591591

0 commit comments

Comments
 (0)