feat: Focus browser from select browser screen and on dashboard login (#19842)

Co-authored-by: Zachary Williams <zachjw34@gmail.com>
This commit is contained in:
Ryan Manuel
2022-01-27 18:04:15 +00:00
committed by GitHub
parent 371b0031c1
commit 56626c62a8
27 changed files with 351 additions and 38 deletions
@@ -10,4 +10,8 @@ export class BrowserActions {
closeBrowser () {
return this.browserApi.close()
}
async focusActiveBrowserWindow () {
await this.browserApi.focusActiveBrowserWindow()
}
}
@@ -1,10 +1,24 @@
import type { FoundBrowser } from '@packages/types'
import os from 'os'
import { execSync } from 'child_process'
import type { DataContext } from '..'
let isPowerShellAvailable = false
try {
execSync(`[void] ''`, { shell: 'powershell' })
isPowerShellAvailable = true
} catch {
// Powershell is unavailable
}
const platform = os.platform()
export interface BrowserApiShape {
close(): Promise<any>
ensureAndGetByNameOrPath(nameOrPath: string): Promise<FoundBrowser | undefined>
getBrowsers(): Promise<FoundBrowser[]>
focusActiveBrowserWindow(): Promise<any>
}
export class BrowserDataSource {
@@ -48,4 +62,17 @@ export class BrowserDataSource {
return this.idForBrowser(this.ctx.coreData.chosenBrowser) === this.idForBrowser(obj)
}
isFocusSupported (obj: FoundBrowser) {
if (platform === 'darwin' || obj.family !== 'firefox') {
return true
}
// Only allow focusing if PowerShell is available on Windows, since that's what we use to do it
if (obj.family === 'firefox' && platform === 'win32') {
return isPowerShellAvailable
}
return false
}
}
@@ -18,7 +18,6 @@ import pDefer from 'p-defer'
interface InternalOpenProjectArgs {
argv: string[]
projectName: string
browser: string
}
interface InternalAddProjectOpts {
@@ -228,15 +227,11 @@ async function makeE2ETasks () {
e2eServerPort: ctx.appServerPort,
}
},
async __internal_openProject ({ argv, projectName, browser }: InternalOpenProjectArgs): Promise<ResetOptionsResult> {
async __internal_openProject ({ argv, projectName }: InternalOpenProjectArgs): Promise<ResetOptionsResult> {
if (!scaffoldedProjects.has(projectName)) {
throw new Error(`${projectName} has not been scaffolded. Be sure to call cy.scaffoldProject('${projectName}') in the test, a before, or beforeEach hook`)
}
if (browser !== 'chrome') {
throw new Error(`Cypress in cypress does not support running in the ${browser} browser`)
}
const openArgv = [...argv, '--project', Fixtures.projectPath(projectName), '--port', '4455']
// Runs the launchArgs through the whole pipeline for the CLI open process,
@@ -186,7 +186,7 @@ function openProject (projectName: ProjectFixture, argv: string[] = []) {
}
return logInternal({ name: 'openProject', message: argv.join(' ') }, () => {
return taskInternal('__internal_openProject', { projectName, argv, browser: Cypress.browser.name })
return taskInternal('__internal_openProject', { projectName, argv })
}).then((obj) => {
Cypress.env('e2e_serverPort', obj.e2eServerPort)
@@ -195,6 +195,12 @@ function openProject (projectName: ProjectFixture, argv: string[] = []) {
}
function startAppServer (mode: 'component' | 'e2e' = 'e2e') {
const browser = Cypress.browser.name
if (browser !== 'chrome') {
throw new Error(`Cypress in cypress does not support running in the ${browser} browser`)
}
return logInternal('startAppServer', (log) => {
return cy.window({ log: false }).then((win) => {
return cy.withCtx(async (ctx, o) => {
@@ -1,5 +1,6 @@
export const longBrowsersList = [
{
id: '1',
name: 'electron',
displayName: 'Electron',
family: 'chromium',
@@ -8,8 +9,10 @@ export const longBrowsersList = [
path: '',
majorVersion: '73',
info: 'Info about electron browser',
isFocusSupported: true,
},
{
id: '2',
name: 'chrome',
displayName: 'Chrome',
family: 'chromium',
@@ -17,8 +20,10 @@ export const longBrowsersList = [
version: '78.0.3904.108',
path: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
majorVersion: '78',
isFocusSupported: true,
},
{
id: '3',
name: 'chrome',
displayName: 'Chrome',
family: 'chromium',
@@ -26,8 +31,10 @@ export const longBrowsersList = [
version: '88.0.3904.00',
path: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
majorVersion: '88',
isFocusSupported: true,
},
{
id: '4',
name: 'chrome',
displayName: 'Canary',
family: 'chromium',
@@ -35,8 +42,10 @@ export const longBrowsersList = [
version: '80.0.3977.4',
path: '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary',
majorVersion: '80',
isFocusSupported: true,
},
{
id: '5',
name: 'chromium',
displayName: 'Chromium',
family: 'chromium',
@@ -44,8 +53,10 @@ export const longBrowsersList = [
version: '74.0.3729.0',
path: '/Applications/Chromium.app/Contents/MacOS/Chromium',
majorVersion: '74',
isFocusSupported: true,
},
{
id: '6',
name: 'chromium',
displayName: 'Chromium',
family: 'chromium',
@@ -53,8 +64,10 @@ export const longBrowsersList = [
version: '85.0.3729.0',
path: '/Applications/Chromium.app/Contents/MacOS/Chromium',
majorVersion: '85',
isFocusSupported: true,
},
{
id: '7',
name: 'edge',
displayName: 'Edge Beta',
family: 'chromium',
@@ -62,8 +75,10 @@ export const longBrowsersList = [
version: '79.0.309.71',
path: '/Applications/Microsoft Edge Beta.app/Contents/MacOS/Microsoft Edge Beta',
majorVersion: '79',
isFocusSupported: true,
},
{
id: '8',
name: 'edge',
displayName: 'Edge Canary',
family: 'chromium',
@@ -71,8 +86,10 @@ export const longBrowsersList = [
version: '79.0.309.71',
path: '/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary',
majorVersion: '79',
isFocusSupported: true,
},
{
id: '9',
name: 'edge',
displayName: 'Edge Dev',
family: 'chromium',
@@ -80,8 +97,10 @@ export const longBrowsersList = [
version: '80.0.309.71',
path: '/Applications/Microsoft Edge Dev.app/Contents/MacOS/Microsoft Edge Dev',
majorVersion: '79',
isFocusSupported: true,
},
{
id: '10',
name: 'firefox',
displayName: 'Firefox',
family: 'firefox',
@@ -90,8 +109,10 @@ export const longBrowsersList = [
path: '/Applications/Firefox/Contents/MacOS/Firefox',
majorVersion: '69',
unsupportedVersion: true,
isFocusSupported: true,
},
{
id: '11',
name: 'firefox',
displayName: 'Firefox',
family: 'firefox',
@@ -100,8 +121,10 @@ export const longBrowsersList = [
path: '/Applications/Firefox/Contents/MacOS/Firefox',
majorVersion: '75',
unsupportedVersion: true,
isFocusSupported: true,
},
{
id: '12',
name: 'firefox',
displayName: 'Firefox Developer Edition',
channel: 'dev',
@@ -109,8 +132,10 @@ export const longBrowsersList = [
version: '69.0.2',
path: '/Applications/Firefox Developer/Contents/MacOS/Firefox Developer',
majorVersion: '69',
isFocusSupported: true,
},
{
id: '13',
name: 'firefox',
displayName: 'Firefox Nightly',
channel: 'beta',
@@ -118,5 +143,6 @@ export const longBrowsersList = [
version: '69.0.3',
path: '/Applications/Firefox Nightly/Contents/MacOS/Firefox Nightly',
majorVersion: '69',
isFocusSupported: false,
},
] as const
@@ -32,6 +32,9 @@ export const stubMutation: MaybeResolver<Mutation> = {
return {}
},
focusActiveBrowserWindow (sourc, args, ctx) {
return true
},
hideBrowserWindow (source, args, ctx) {
return true
},
+4
View File
@@ -18,6 +18,7 @@ type Browser implements Node {
"""Relay style Node ID field for the Browser field"""
id: ID!
isFocusSupported: Boolean!
isSelected: Boolean!
majorVersion: String
name: String!
@@ -692,6 +693,9 @@ type Mutation {
"""user has finished migration component specs - move to next step"""
finishedRenamingComponentSpecs: Query
"""Sets focus to the active browser window"""
focusActiveBrowserWindow: Boolean!
"""Generate spec from source"""
generateSpecFromSource(codeGenCandidate: String!, type: CodeGenType!): ScaffoldedFile
@@ -24,6 +24,9 @@ export const Browser = objectType({
t.nonNull.string('name')
t.nonNull.string('path')
t.nonNull.string('version')
t.nonNull.boolean('isFocusSupported', {
resolve: (source, args, ctx) => ctx.browser.isFocusSupported(source),
})
},
sourceType: {
module: '@packages/types',
@@ -347,6 +347,16 @@ export const mutation = mutationType({
},
})
t.nonNull.field('focusActiveBrowserWindow', {
type: 'Boolean',
description: 'Sets focus to the active browser window',
resolve: async (_, args, ctx) => {
await ctx.actions.browser.focusActiveBrowserWindow()
return true
},
})
t.nonNull.field('reconfigureProject', {
type: 'Boolean',
description: 'show the launchpad windows',
@@ -182,6 +182,63 @@ describe('Choose a Browser Page', () => {
cy.contains('button', 'Close').click()
cy.wait('@closeBrowser')
})
it('performs mutation to focus open browser when focus button is pressed', () => {
cy.openProject('launchpad', ['--e2e'])
cy.visitLaunchpad()
cy.get('h1').should('contain', 'Choose a Browser')
cy.contains('button', 'Start E2E Testing in Chrome').as('launchButton')
// Stub out response to prevent browser launch but not break internals
cy.intercept('mutation-OpenBrowser_LaunchProject', {
body: {
data: {
launchOpenProject: true,
setProjectPreferences: {
currentProject: {
id: 'test-id',
title: 'launchpad',
__typename: 'CurrentProject',
},
__typename: 'Query',
},
},
},
delay: 500,
}).as('launchProject')
cy.get('@launchButton').click()
cy.contains('button', 'Opening E2E Testing in Chrome').should('be.visible')
cy.wait('@launchProject').then(({ request }) => {
expect(request?.body.variables.testingType).to.eq('e2e')
})
cy.intercept('query-OpenBrowser', (req) => {
req.on('before:response', (res) => {
res.body.data.currentProject.isBrowserOpen = true
})
})
cy.contains('button', 'Focus').as('focusButton')
cy.intercept('mutation-OpenBrowser_FocusActiveBrowserWindow').as('focusBrowser')
cy.withCtx((ctx) => {
sinon.spy(ctx.actions.browser, 'focusActiveBrowserWindow')
})
cy.get('@focusButton').click()
cy.wait('@focusBrowser').then(() => {
cy.withCtx((ctx) => {
expect(ctx.actions.browser.focusActiveBrowserWindow).to.be.called
})
})
})
})
describe('No System Browsers Detected', () => {
+15 -1
View File
@@ -15,6 +15,7 @@
@navigated-back="backFn"
@launch="launch"
@close-browser="closeBrowserFn"
@focus-browser="setFocusToActiveBrowserWindow"
/>
</template>
</template>
@@ -23,7 +24,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 } from '../generated/graphql'
import { OpenBrowserDocument, OpenBrowser_CloseBrowserDocument, OpenBrowser_ClearTestingTypeDocument, OpenBrowser_LaunchProjectDocument, OpenBrowser_FocusActiveBrowserWindowDocument } from '../generated/graphql'
import LaunchpadHeader from './LaunchpadHeader.vue'
import { useI18n } from '@cy/i18n'
import { computed, ref } from 'vue'
@@ -81,6 +82,12 @@ mutation OpenBrowser_CloseBrowser {
}
`
gql`
mutation OpenBrowser_FocusActiveBrowserWindow {
focusActiveBrowserWindow
}
`
const launchOpenProject = useMutation(OpenBrowser_LaunchProjectDocument)
const clearCurrentTestingType = useMutation(OpenBrowser_ClearTestingTypeDocument)
const closeBrowser = useMutation(OpenBrowser_CloseBrowserDocument)
@@ -114,4 +121,11 @@ const isBrowserOpening = computed(() => !!launchOpenProject.fetching.value || la
const headingDescription = computed(() => {
return t('setupWizard.chooseBrowser.description', { testingType: query.data.value?.currentProject?.currentTestingType === 'component' ? 'component' : 'E2E' })
})
const focusActiveBrowserWindow = useMutation(OpenBrowser_FocusActiveBrowserWindowDocument)
const setFocusToActiveBrowserWindow = () => {
focusActiveBrowserWindow.executeMutation({})
}
</script>
@@ -86,4 +86,28 @@ describe('<OpenBrowserList />', () => {
cy.percySnapshot()
})
it('hides focus button when unsupported', () => {
cy.mountFragment(OpenBrowserListFragmentDoc, {
onResult: (result) => {
result.currentBrowser = longBrowsersList.find((browser) => !browser.isFocusSupported) || null
},
render: (gqlVal) => (
<div class="border-current border-1 resize overflow-auto">
<OpenBrowserList
gql={gqlVal}
isBrowserOpen={true}
isBrowserOpening={false}
onClose-browser={cy.stub().as('closeBrowser')}/>
</div>),
})
cy.get('[data-cy-browser]').each((browser) => cy.wrap(browser).should('have.attr', 'aria-disabled', 'true'))
cy.contains('button', defaultMessages.openBrowser.running.replace('{browser}', 'Electron')).should('be.disabled')
cy.contains('button', defaultMessages.openBrowser.focus).should('not.exist')
cy.contains('button', defaultMessages.openBrowser.close).click()
cy.get('@closeBrowser').should('have.been.called')
cy.percySnapshot()
})
})
@@ -5,7 +5,7 @@
>
<RadioGroup
v-model="selectedBrowserId"
class="flex flex-wrap justify-center py-40px gap-24px"
class="flex flex-wrap py-40px gap-24px justify-center"
data-cy="open-browser-list"
>
<RadioGroupOption
@@ -18,7 +18,7 @@
>
<RadioGroupLabel
:for="browser.id"
class="relative block pt-6 pb-4 text-center rounded radio-label border-1 min-h-144px w-160px"
class="rounded border-1 text-center min-h-144px pt-6 pb-4 w-160px relative block radio-label"
:class="{
'border-jade-300 ring-2 ring-jade-100 focus:border-jade-400 focus:border-1 focus:outline-none': checked,
'border-gray-200 before:hocus:cursor-pointer': !checked && !(isBrowserOpening || isBrowserOpen) ,
@@ -31,7 +31,7 @@
<img
:src="allBrowsersIcons[browser.displayName]"
alt=""
class="inline h-40px w-40px"
class="h-40px w-40px inline"
>
</div>
<div
@@ -50,7 +50,7 @@
v-if="props.gql.currentTestingType"
class="mb-14"
>
<div class="flex items-center justify-center mb-4 gap-16px">
<div class="flex mb-4 gap-16px items-center justify-center">
<template v-if="!isBrowserOpen">
<Button
v-if="!isBrowserOpening"
@@ -91,12 +91,14 @@
{{ browserText.running }}
</Button>
<Button
v-if="props.gql.currentBrowser?.isFocusSupported"
size="lg"
type="button"
variant="outline"
:prefix-icon="ExportIcon"
prefix-icon-class="icon-dark-gray-500"
class="font-medium"
@click="emit('focus-browser')"
>
{{ browserText.focus }}
</Button>
@@ -119,7 +121,7 @@
variant="text"
:prefix-icon="ArrowLeftIcon"
prefix-icon-class="icon-dark-gray-500"
class="mx-auto font-medium text-gray-600 hover:text-indigo-500"
class="font-medium mx-auto text-gray-600 hover:text-indigo-500"
@click="emit('navigated-back')"
>
{{ browserText.switchTestingType }}
@@ -161,6 +163,7 @@ fragment OpenBrowserList on CurrentProject {
id
displayName
path
isFocusSupported
}
browsers {
id
@@ -188,9 +191,7 @@ const emit = defineEmits<{
(e: 'navigated-back'): void
(e: 'launch'): void
(e: 'close-browser'): void
// TODO: Add browser focus
// see: https://cypress-io.atlassian.net/browse/UNIFY-953
// (e: 'focus-browser'): void
(e: 'focus-browser'): void
}>()
const { t } = useI18n()
@@ -322,6 +322,8 @@ export class CdpAutomation {
.then(({ data }) => {
return `data:image/png;base64,${data}`
})
case 'focus:browser:window':
return this.sendDebuggerCommandFn('Page.bringToFront')
default:
throw new Error(`No automation handler registered for: '${message}'`)
}
+24 -15
View File
@@ -54,24 +54,33 @@ const _getAutomation = function (win, options, parent) {
const automation = new CdpAutomation(sendCommand, on, parent)
if (!options.onScreencastFrame) {
// after upgrading to Electron 8, CDP screenshots can hang if a screencast is not also running
// workaround: start and stop screencasts between screenshots
// @see https://github.com/cypress-io/cypress/pull/6555#issuecomment-596747134
automation.onRequest = _.wrap(automation.onRequest, async (fn, message, data) => {
if (message !== 'take:screenshot') {
automation.onRequest = _.wrap(automation.onRequest, async (fn, message, data) => {
switch (message) {
case 'take:screenshot': {
// after upgrading to Electron 8, CDP screenshots can hang if a screencast is not also running
// workaround: start and stop screencasts between screenshots
// @see https://github.com/cypress-io/cypress/pull/6555#issuecomment-596747134
if (!options.onScreencastFrame) {
await sendCommand('Page.startScreencast', screencastOpts)
const ret = await fn(message, data)
await sendCommand('Page.stopScreencast')
return ret
}
return fn(message, data)
}
case 'focus:browser:window': {
win.show()
await sendCommand('Page.startScreencast', screencastOpts)
const ret = await fn(message, data)
await sendCommand('Page.stopScreencast')
return ret
})
}
return
}
default: {
return fn(message, data)
}
}
})
return automation
}
+23
View File
@@ -3,6 +3,9 @@ const Promise = require('bluebird')
const debug = require('debug')('cypress:server:browsers')
const utils = require('./utils')
const check = require('check-more-types')
const { exec } = require('child_process')
const util = require('util')
const os = require('os')
// returns true if the passed string is a known browser family name
const isBrowserFamily = check.oneOf(['chromium', 'firefox'])
@@ -40,6 +43,25 @@ const kill = function (unbind, isProcessExit) {
})
}
const setFocus = async function () {
const platform = os.platform()
const execAsync = util.promisify(exec)
try {
switch (platform) {
case 'darwin':
return execAsync(`open -a "$(ps -p ${instance.pid} -o comm=)"`)
case 'win32': {
return execAsync(`(New-Object -ComObject WScript.Shell).AppActivate(((Get-WmiObject -Class win32_process -Filter "ParentProcessID = '${instance.pid}'") | Select -ExpandProperty ProcessId))`, { shell: 'powershell.exe' })
}
default:
debug(`Unexpected os platform ${platform}. Set focus is only functional on Windows and MacOS`)
}
} catch (error) {
debug(`Failure to set focus. ${error}`)
}
}
const getBrowserLauncher = function (browser) {
debug('getBrowserLauncher %o', { browser })
if (!isBrowserFamily(browser.family)) {
@@ -166,4 +188,5 @@ module.exports = {
})
})
},
setFocus,
}
+2 -2
View File
@@ -188,7 +188,7 @@ const _internal = {
/**
* @returns a promise that is resolved with a user when auth is complete or rejected when it fails
*/
const start = (onMessage, utmCode) => {
const start = (onMessage, utmCode, onLogin) => {
function sendMessage (type, name, arg1) {
onMessage({
type,
@@ -222,7 +222,7 @@ const start = (onMessage, utmCode) => {
})
.finally(() => {
_internal.stopServer()
require('./windows').focusMainWindow()
onLogin()
})
}
+4
View File
@@ -100,6 +100,10 @@ export function hideAllUnlessAnotherWindowIsFocused () {
return _.invoke(windows, 'hide')
}
export function isMainWindowFocused () {
return getByType('INDEX').isFocused()
}
export function focusMainWindow () {
return getByType('INDEX').show()
}
+14 -1
View File
@@ -51,6 +51,9 @@ export function makeDataContext (options: MakeDataContextOptions): DataContext {
return await ensureAndGetByNameOrPath(nameOrPath, false, browsers)
},
async focusActiveBrowserWindow () {
return openProject.projectBase?.sendFocusBrowserMessage()
},
},
errorApi: {
error: errors.get,
@@ -77,7 +80,17 @@ export function makeDataContext (options: MakeDataContextOptions): DataContext {
return user.get()
},
logIn (onMessage) {
return auth.start(onMessage, 'launchpad')
const windows = require('./gui/windows')
const originalIsMainWindowFocused = windows.isMainWindowFocused()
const onLogin = async () => {
if (originalIsMainWindowFocused || !ctx.browser.isFocusSupported(ctx.coreData.chosenBrowser)) {
windows.focusMainWindow()
} else {
await ctx.actions.browser.focusActiveBrowserWindow()
}
}
return auth.start(onMessage, 'launchpad', onLogin)
},
logOut () {
return user.logOut()
+8
View File
@@ -474,6 +474,14 @@ export class ProjectBase<TServer extends Server> extends EE {
this.server.changeToUrl(url)
}
async sendFocusBrowserMessage () {
if (this.browser.family === 'firefox') {
await browsers.setFocus()
} else {
await this.server.sendFocusBrowserMessage()
}
}
shouldCorrelatePreRequests = () => {
if (!this.browser) {
return false
+4
View File
@@ -577,6 +577,10 @@ export abstract class ServerBase<TSocket extends SocketE2E | SocketCt> {
return this._socket && this._socket.changeToUrl(url)
}
async sendFocusBrowserMessage () {
this._socket && await this._socket.sendFocusBrowserMessage()
}
onRequest (fn) {
this._middleware = fn
}
+10
View File
@@ -74,6 +74,8 @@ const retry = (fn: (res: any) => void) => {
}
export class SocketBase {
private _sendFocusBrowserMessage
protected ended: boolean
protected _io?: socketIo.SocketIOServer
@@ -273,6 +275,10 @@ export class SocketBase {
})
})
this._sendFocusBrowserMessage = async () => {
await automationRequest('focus:browser:window', {})
}
socket.on('reporter:connected', () => {
if (socket.inReporterRoom) {
return
@@ -538,6 +544,10 @@ export class SocketBase {
return this.toRunner('change:to:url', url)
}
async sendFocusBrowserMessage () {
await this._sendFocusBrowserMessage()
}
close () {
return this._io?.close()
}
@@ -1,9 +1,13 @@
require('../../spec_helper')
const os = require('os')
const browsers = require(`../../../lib/browsers`)
const utils = require(`../../../lib/browsers/utils`)
const snapshot = require('snap-shot-it')
const { EventEmitter } = require('events')
const { sinon } = require('../../spec_helper')
const { exec } = require('child_process')
const util = require('util')
const normalizeBrowsers = (message) => {
return message.replace(/(found on your system are:)(?:\n- .*)*/, '$1\n- chrome\n- firefox\n- electron')
@@ -160,6 +164,40 @@ describe('lib/browsers/index', () => {
expect(utils.getMajorVersion(vers)).to.eq(vers)
})
})
context('setFocus', () => {
it('calls open when running MacOS', () => {
const mockExec = sinon.stub()
sinon.stub(os, 'platform').returns('darwin')
sinon.stub(util, 'promisify').returns(mockExec)
browsers._setInstance({
pid: 3333,
})
browsers.setFocus()
expect(util.promisify).to.be.calledWith(exec)
expect(mockExec).to.be.calledWith(`open -a "$(ps -p 3333 -o comm=)"`)
})
it('calls WScript AppActivate to activate the window when running Windows', () => {
const mockExec = sinon.stub()
sinon.stub(os, 'platform').returns('win32')
sinon.stub(util, 'promisify').returns(mockExec)
browsers._setInstance({
pid: 3333,
})
browsers.setFocus()
expect(util.promisify).to.be.calledWith(exec)
expect(mockExec).to.be.calledWith(`(New-Object -ComObject WScript.Shell).AppActivate(((Get-WmiObject -Class win32_process -Filter "ParentProcessID = '3333'") | Select -ExpandProperty ProcessId))`, { shell: 'powershell.exe' })
})
})
})
// Ooo, browser clean up tests are disabled?!!
@@ -218,5 +218,13 @@ context('lib/browsers/cdp_automation', () => {
.to.be.rejectedWith('The browser responded with an error when Cypress attempted to take a screenshot.')
})
})
describe('focus:browser:window', function () {
it('sends Page.bringToFront when focus is requested', function () {
this.sendDebuggerCommand.withArgs('Page.bringToFront').resolves()
return this.onRequest('focus:browser:window').then((resp) => expect(resp).to.be.undefined)
})
})
})
})
@@ -289,6 +289,7 @@ describe('lib/browsers/electron', () => {
this.newWin = {
maximize: sinon.stub(),
setSize: sinon.stub(),
show: sinon.stub(),
webContents: this.win.webContents,
}
@@ -339,14 +340,19 @@ describe('lib/browsers/electron', () => {
})
})
it('registers onRequest automation middleware', function () {
it('registers onRequest automation middleware and calls show when requesting to be focused', function () {
sinon.spy(this.automation, 'use')
return electron._render(this.url, this.automation, this.preferences, this.options)
electron._render(this.url, this.automation, this.preferences, this.options)
.then(() => {
expect(Windows.create).to.be.calledWith(this.options.projectRoot, this.options)
expect(this.automation.use).to.be.called
expect(this.automation.use.lastCall.args[0].onRequest).to.be.a('function')
this.automation.use.lastCall.args[0].onRequest('focus:browser:window')
expect(this.newWin.show).to.be.called
})
})
})
+6 -2
View File
@@ -132,7 +132,9 @@ describe('lib/gui/auth', function () {
sinon.stub(auth._internal, 'stopServer')
sinon.stub(windows, 'focusMainWindow').callsFake(() => {})
await auth.start(() => {}, 'code')
await auth.start(() => {}, 'code', () => {
windows.focusMainWindow()
})
expect(auth._internal.stopServer).to.be.calledOnce
expect(windows.focusMainWindow).to.be.calledOnce
@@ -144,7 +146,9 @@ describe('lib/gui/auth', function () {
sinon.stub(windows, 'focusMainWindow').callsFake(() => {})
try {
await auth.start(() => {}, 'code')
await auth.start(() => {}, 'code', () => {
windows.focusMainWindow()
})
} catch (e) {
expect(e.message).to.eql('test error')
}
+10
View File
@@ -601,6 +601,16 @@ describe('lib/socket', () => {
})
})
context('#sendFocusBrowserMessage', function () {
it('sends an automation request of focus:browser:window', function () {
sinon.stub(this.automation, 'request')
this.socket.sendFocusBrowserMessage()
expect(this.automation.request).to.be.calledWith('focus:browser:window', {})
})
})
context('#close', () => {
it('calls close on #io', function () {
this.socket.close()