fetch chrome using which

This commit is contained in:
Christian Bromann 2022-09-06 10:53:00 +02:00
parent 2cbaf0450c
commit 59605be103
No known key found for this signature in database
GPG key ID: 355B5D0EA83B4D19
6 changed files with 77 additions and 35 deletions

View file

@ -29,7 +29,9 @@ jobs:
- name: Lint / Typings / Unit / Smoke Tests
run: npm run test
- name: Interop Tests
run: npm run test:e2e:cjs
run:
npm i chromedriver@104
npm run test:e2e:cjs
e2e-windows:
runs-on: windows-latest
steps:

6
__mocks__/which.ts Normal file
View file

@ -0,0 +1,6 @@
import { vi } from 'vitest'
const which = vi.fn()
// @ts-expect-error
which.sync = vi.fn()
export default which

View file

@ -45,7 +45,8 @@
"puppeteer-core": "^17.0.0",
"query-selector-shadow-dom": "^1.0.0",
"ua-parser-js": "^1.0.1",
"uuid": "^9.0.0"
"uuid": "^9.0.0",
"which": "^2.0.2"
},
"devDependencies": {
"@types/node": "^18.0.0",

View file

@ -1,4 +1,4 @@
import { launch as launchChromeBrowser } from 'chrome-launcher'
import { launch as launchChromeBrowser, Options } from 'chrome-launcher'
import puppeteer, { PuppeteerLaunchOptions } from 'puppeteer-core'
import logger from '@wdio/logger'
import type { Browser } from 'puppeteer-core/lib/cjs/puppeteer/common/Browser'
@ -6,7 +6,7 @@ import type { Capabilities } from '@wdio/types'
import { QueryHandler } from 'query-selector-shadow-dom/plugins/puppeteer/index.js'
import browserFinder from './finder/index.js'
import { getPages } from './utils.js'
import { getPages, launchChromeUsingWhich } from './utils.js'
import {
CHROME_NAMES,
FIREFOX_NAMES,
@ -106,7 +106,7 @@ async function launchChrome (capabilities: ExtendedCapabilities) {
}
log.info(`Launch Google Chrome with flags: ${chromeFlags.join(' ')}`)
const chrome = await launchChromeBrowser({
const launchOptions: Options = {
prefs: chromeOptions.prefs,
chromePath: chromeOptions.binary,
ignoreDefaultFlags: true,
@ -114,7 +114,9 @@ async function launchChrome (capabilities: ExtendedCapabilities) {
userDataDir,
envVars: devtoolsOptions.env,
...(devtoolsOptions.customPort ? { port: devtoolsOptions.customPort } : {})
})
}
const chrome = await launchChromeBrowser(launchOptions).catch(
(err: Error) => launchChromeUsingWhich(err, launchOptions))
log.info(`Connect Puppeteer with browser on port ${chrome.port}`)
const browser = await puppeteer.connect({

View file

@ -1,10 +1,11 @@
import fs from 'node:fs'
import { createRequire } from 'node:module'
import { execFileSync } from 'node:child_process'
import which from 'which'
import logger from '@wdio/logger'
import { commandCallStructure, isValidParameter, getArgumentType, canAccess } from '@wdio/utils'
import { WebDriverProtocol, CommandParameters, CommandPathVariables, ElementReference } from '@wdio/protocols'
import { launch as launchChromeBrowser, Options } from 'chrome-launcher'
import type { Logger } from '@wdio/logger/build/node'
import type { ElementHandle } from 'puppeteer-core/lib/cjs/puppeteer/common/ElementHandle'
import type { Browser } from 'puppeteer-core/lib/cjs/puppeteer/common/Browser'
@ -302,12 +303,7 @@ export function findByWhich (executables: string[], priorities: Priorities[]) {
const installations: string[] = []
executables.forEach((executable) => {
try {
const browserPath = execFileSync(
'which',
[executable],
{ stdio: 'pipe' }
).toString().split(/\r?\n/)[0]
const browserPath = which.sync(executable)
if (canAccess(browserPath)) {
installations.push(browserPath)
}
@ -374,3 +370,29 @@ export async function patchDebug (scoppedLogger: Logger) {
export const sleep = (time = 0) => new Promise(
(resolve) => setTimeout(resolve, time))
export const launchChromeUsingWhich = async (err: Error, launchOptions: Options, ) => {
/**
* we only handle the error if
*/
if (
/**
* the installation could not be found
*/
!err.message.includes('No Chrome installations found.') ||
/**
* the user specified a binary path that could not be found
*/
typeof launchOptions.chromePath === 'string'
) {
throw err
}
/**
* try to use node-which to resolve a Chrome path
*/
const chromePath = await which('chrome')
.catch(() => which('chromium')
.catch(() => which('google-chrome')))
return launchChromeBrowser({ ...launchOptions, chromePath })
}

View file

@ -1,17 +1,20 @@
import { describe, it, expect, afterEach, vi, test } from 'vitest'
import path from 'node:path'
import childProcess from 'node:child_process'
// @ts-ignore no types needed
import debug from 'debug'
import which from 'which'
import { launch } from 'chrome-launcher'
import { canAccess } from '@wdio/utils'
import {
validate, getPrototype, findElement, findElements, getStaleElementError,
sanitizeError, transformExecuteArgs, transformExecuteResult, getPages,
uniq, findByWhich, patchDebug, sleep
uniq, findByWhich, patchDebug, sleep, launchChromeUsingWhich
} from '../src/utils.js'
vi.mock('which')
vi.mock('chrome-launcher')
vi.mock('@wdio/logger', async () => {
const pathModule = await vi.importActual('node:path') as typeof path
return import(pathModule.join(process.cwd(), '__mocks__', '@wdio/logger'))
@ -85,24 +88,6 @@ vi.mock('fs', () => {
}
})
vi.mock('node:child_process', () => {
let returnValue = false
const execFileSync = vi.fn().mockImplementation(() => {
if (!returnValue) {
throw new Error('foo not found')
}
return returnValue
})
return {
execFileSync,
default: {
execFileSync,
shouldReturn: (value: any) => (returnValue = value)
}
}
})
vi.mock('debug', () => ({
default: { log: vi.fn() }
}))
@ -400,8 +385,7 @@ test('findByWhich', () => {
expect(findByWhich(['firefox'], [{ regex: /firefox/, weight: 51 }]))
.toEqual([])
// @ts-expect-error mock feature
vi.mocked(childProcess).shouldReturn('/path/to/other/firefox\n')
vi.mocked(which.sync).mockReturnValue('/path/to/other/firefox')
expect(findByWhich(['firefox'], [{ regex: /firefox/, weight: 51 }]))
.toEqual(['/path/to/other/firefox'])
@ -430,3 +414,28 @@ test('sleep', async () => {
await sleep(100)
expect(Date.now() - start).toBeGreaterThanOrEqual(90)
})
describe('launchChromeUsingWhich', () => {
it('should throw if error is not related to chrome binary not found', async () => {
await expect(() => launchChromeUsingWhich(new Error('ups'), {}))
.rejects.toThrow('ups')
})
it('should throw if user has specified path explicitly', async () => {
await expect(() => launchChromeUsingWhich(
new Error('No Chrome installations found.'),
{ chromePath: '/foo/bar' }
)).rejects.toThrow('No Chrome installations found.')
})
it('should use which properly to find path', async () => {
vi.mocked(which).mockImplementation(async (binary: string) => {
if (binary === 'google-chrome') {
return '/foo/bar/google-chrome'
}
throw new Error('not found')
})
await launchChromeUsingWhich(new Error('No Chrome installations found.'), {})
expect(launch).toBeCalledWith({ chromePath: '/foo/bar/google-chrome' })
})
})