From 7be88a50cf42366452a9c42494d1036339c33d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathis=20Dr=C3=B6ge?= Date: Sun, 5 Nov 2023 23:32:09 +0100 Subject: [PATCH] Simplify our E2E tests --- .eslintignore | 1 + .github/workflows/test-e2e-dev.yml | 22 --------- .github/workflows/test-e2e-packaged.yml | 25 ---------- .github/workflows/test.yml | 12 ++++- .gitignore | 2 + .prettierignore | 1 + e2e/Dockerfile.test | 29 ----------- e2e/__specs__/api.spec.ts | 54 -------------------- e2e/__specs__/common-setup.ts | 65 ------------------------- e2e/api.spec.ts | 49 +++++++++++++++++++ e2e/docker-compose.yml | 10 ---- e2e/entrypoint.sh | 53 -------------------- e2e/helpers.ts | 28 +++++++++++ package.json | 10 ++-- playwright.config.ts | 11 +++++ src/backend/constants.ts | 14 +++++- src/backend/main.ts | 8 ++- yarn.lock | 57 ++++++++++++---------- 18 files changed, 156 insertions(+), 295 deletions(-) delete mode 100644 .github/workflows/test-e2e-dev.yml delete mode 100644 .github/workflows/test-e2e-packaged.yml delete mode 100644 e2e/Dockerfile.test delete mode 100644 e2e/__specs__/api.spec.ts delete mode 100644 e2e/__specs__/common-setup.ts create mode 100644 e2e/api.spec.ts delete mode 100644 e2e/docker-compose.yml delete mode 100755 e2e/entrypoint.sh create mode 100644 e2e/helpers.ts create mode 100644 playwright.config.ts diff --git a/.eslintignore b/.eslintignore index b5491e51094..359335e5f17 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,2 +1,3 @@ sign/** flatpak-build/** +playwright-report/** diff --git a/.github/workflows/test-e2e-dev.yml b/.github/workflows/test-e2e-dev.yml deleted file mode 100644 index a50d455df67..00000000000 --- a/.github/workflows/test-e2e-dev.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Test E2E Dev - -on: - pull_request: - branches: [main, stable] - workflow_dispatch: - -jobs: - test_dev: - strategy: - matrix: - os: [windows-2022, ubuntu-22.04, macos-12] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/install-deps - - name: Build and Test - run: yarn test:e2e-ci - env: - GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} - GH_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} - CSC_IDENTITY_AUTO_DISCOVERY: false diff --git a/.github/workflows/test-e2e-packaged.yml b/.github/workflows/test-e2e-packaged.yml deleted file mode 100644 index 9f19a093b02..00000000000 --- a/.github/workflows/test-e2e-packaged.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Test E2E Packaged - -on: - pull_request: - branches: [main, stable] - workflow_dispatch: - -env: - TEST_PACKAGED: true - -jobs: - test_packaged: - strategy: - matrix: - os: [windows-2022, ubuntu-22.04, macos-12] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v3 - - uses: ./.github/actions/install-deps - - name: Build and Test - run: yarn test:e2ePackaged-ci - env: - GITHUB_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} - GH_TOKEN: ${{ secrets.WORKFLOW_TOKEN }} - CSC_IDENTITY_AUTO_DISCOVERY: false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c96cea751ea..66c00752f15 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,11 +6,19 @@ on: workflow_dispatch: jobs: - test: + ci: runs-on: ubuntu-latest steps: - name: Checkout repository. uses: actions/checkout@v3 - uses: ./.github/actions/install-deps - - name: Test + - name: Test CI run: yarn test:ci + e2e: + runs-on: ubuntu-latest + steps: + - name: Checkout repository. + uses: actions/checkout@v3 + - uses: ./.github/actions/install-deps + - name: Test E2E + run: yarn test:e2e diff --git a/.gitignore b/.gitignore index 5575e8520c1..17a34248228 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ vite-plugin-electron.log #flatpak flatpak-build .env + +playwright-report diff --git a/.prettierignore b/.prettierignore index 8f3985e0801..18004298fbc 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,3 +8,4 @@ flatpak-build sign/ .github/workflows/build-base.yml signatures/version1/cla.json +playwright-report diff --git a/e2e/Dockerfile.test b/e2e/Dockerfile.test deleted file mode 100644 index 38686f9e3d3..00000000000 --- a/e2e/Dockerfile.test +++ /dev/null @@ -1,29 +0,0 @@ -FROM node:18 AS node - -FROM node AS headless - -RUN apt-get update && \ - apt-get install -y xvfb \ - libgbm1 \ - libxss1 \ - libnss3 \ - libgtk-3-dev \ - libasound2-dev \ - unzip \ - dos2unix \ - && rm -rf /var/lib/apt/lists/* - -FROM headless AS final - -EXPOSE 9515 -ENV ELECTRON_ENABLE_STACK_DUMPING=true -ENV ELECTRON_ENABLE_LOGGING=true - -# Create app directory -WORKDIR /app - -# Install app dependencies -COPY package.json ./ - -# Bundle app source -COPY . . diff --git a/e2e/__specs__/api.spec.ts b/e2e/__specs__/api.spec.ts deleted file mode 100644 index 0bd78dee628..00000000000 --- a/e2e/__specs__/api.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { DiskSpaceData } from '../../src/common/types' -// import '../../common/types/proxy-types' -import { expect, test } from '@playwright/test' -import { ipcMainInvokeHandler } from 'electron-playwright-helpers' -import { Page } from 'playwright' -import { compareVersions } from 'compare-versions' -import { platform as platformOS } from 'os' -import commonSetup, { electronApp } from './common-setup' - -test.describe('api e2e test', function () { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore: this is the correct usage - commonSetup.call(this) - - let page: Page - test('renders the first page', async () => { - page = await electronApp.firstWindow() - const title = await page.title() - expect(title).toBe('Heroic Games Launcher') - }) - - test('gets heroic, legendary, and gog versions', async () => { - const heroicVersion = await page.evaluate(async () => { - return window.api.getHeroicVersion() - }) - console.log('Heroic Version: ', heroicVersion) - // check that heroic version is newer or equal to 2.6.3 - expect(compareVersions(heroicVersion, '2.6.3')).toBeGreaterThanOrEqual(0) - - let legendaryVersion = await page.evaluate(async () => { - return window.api.getLegendaryVersion() - }) - legendaryVersion = legendaryVersion.trim().split(' ')[0] - console.log('Legendary Version: ', legendaryVersion) - expect(compareVersions(legendaryVersion, '0.20.32')).toBeGreaterThanOrEqual( - 0 - ) - - const gogdlVersion = await page.evaluate(async () => { - return window.api.getGogdlVersion() - }) - console.log('Gogdl Version: ', gogdlVersion) - expect(compareVersions(gogdlVersion, '0.7.1')).toBeGreaterThanOrEqual(0) - }) - - test('test ipcMainInvokeHandler', async () => { - const platform: DiskSpaceData = (await ipcMainInvokeHandler( - electronApp, - 'getPlatform' - )) as DiskSpaceData - console.log('Platform: ', platform) - expect(platform).toEqual(platformOS()) - }) -}) diff --git a/e2e/__specs__/common-setup.ts b/e2e/__specs__/common-setup.ts deleted file mode 100644 index df7df469c4c..00000000000 --- a/e2e/__specs__/common-setup.ts +++ /dev/null @@ -1,65 +0,0 @@ -import '../../src/common/types/proxy-types' -import { test } from '@playwright/test' -import { findLatestBuild, parseElectronApp } from 'electron-playwright-helpers' -import { ElectronApplication, _electron as electron } from 'playwright' - -export let electronApp: ElectronApplication - -export default function setup(): void { - test.beforeAll(async () => { - test.setTimeout(120000) - process.env.CI = 'e2e' - if (process.env.TEST_PACKAGED === 'true') { - console.log('Testing packaged build') - // must run yarn dist: prior to test - const latestBuild = findLatestBuild('dist') - const appInfo = parseElectronApp(latestBuild) - console.log( - 'app info main = ', - appInfo.main, - '\napp info exe = ', - appInfo.executable - ) - - electronApp = await electron.launch({ - args: [appInfo.main], - executablePath: appInfo.executable - }) - } else { - console.log('Testing unpackaged build') - electronApp = await electron.launch({ - args: ['.', '--no-sandbox'] - }) - } - - // this pipes the main process std out to test std out - electronApp - .process() - .stdout?.on('data', (data) => console.log(`main process stdout: ${data}`)) - electronApp - .process() - .stderr?.on('data', (error) => - console.log(`main process stderr: ${error}`) - ) - - electronApp.on('window', async (page) => { - const title = await page.title() - console.log('Window loaded: ', title) - const filename = page.url()?.split('/').pop() - console.log(`Window opened: ${filename}`) - - // capture errors - page.on('pageerror', (error) => { - console.error(error) - }) - // capture console messages - page.on('console', (msg) => { - console.log(msg.text()) - }) - }) - }) - - test.afterAll(async () => { - await electronApp.close() - }) -} diff --git a/e2e/api.spec.ts b/e2e/api.spec.ts new file mode 100644 index 00000000000..378db1370ad --- /dev/null +++ b/e2e/api.spec.ts @@ -0,0 +1,49 @@ +import { expect, test } from '@playwright/test' +import { compareVersions } from 'compare-versions' +import { electronTest } from './helpers' + +declare const window: { api: typeof import('../src/backend/api').default } + +electronTest('renders the first page', async (app) => { + const page = await app.firstWindow() + await expect(page).toHaveTitle('Heroic Games Launcher') +}) + +electronTest('gets heroic, legendary, and gog versions', async (app) => { + const page = await app.firstWindow() + + await test.step('get heroic version', async () => { + const heroicVersion = await page.evaluate(async () => + window.api.getHeroicVersion() + ) + console.log('Heroic Version: ', heroicVersion) + // check that heroic version is newer or equal to 2.6.3 + expect(compareVersions(heroicVersion, '2.6.3')).toBeGreaterThanOrEqual(0) + }) + + await test.step('get legendary version', async () => { + let legendaryVersion = await page.evaluate(async () => + window.api.getLegendaryVersion() + ) + legendaryVersion = legendaryVersion.trim().split(' ')[0] + console.log('Legendary Version: ', legendaryVersion) + expect(compareVersions(legendaryVersion, '0.20.32')).toBeGreaterThanOrEqual( + 0 + ) + }) + + await test.step('get gogdl version', async () => { + const gogdlVersion = await page.evaluate(async () => + window.api.getGogdlVersion() + ) + console.log('Gogdl Version: ', gogdlVersion) + expect(compareVersions(gogdlVersion, '0.7.1')).toBeGreaterThanOrEqual(0) + }) +}) + +electronTest('test ipcMainInvokeHandler', async (app) => { + const page = await app.firstWindow() + const platform = await page.evaluate(async () => window.api.getPlatform()) + console.log('Platform: ', platform) + expect(platform).toEqual(process.platform) +}) diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml deleted file mode 100644 index 579d0d7ac3c..00000000000 --- a/e2e/docker-compose.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: '3.8' -services: - process_all: - build: - context: ../ - dockerfile: e2e/Dockerfile.test - volumes: - - .:/src - environment: - - TEST_PACKAGED=${TEST_PACKAGED} diff --git a/e2e/entrypoint.sh b/e2e/entrypoint.sh deleted file mode 100755 index 4aa75f8f470..00000000000 --- a/e2e/entrypoint.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -echo "Clearing display lock for Xvfb" -rm -rf /tmp/.X99-lock - -echo "Starting Xvfb" -Xvfb :99 -ac & -sleep 2 - -export DISPLAY=:99 -echo "Executing command $@" - -exec "$@" - -echo -echo "#########" -echo "# Build #" -echo "#########" -echo - -# get os -os="win" -if [[ "$OSTYPE" == *"linux"* ]] -then - os="linux" - -elif [[ "$OSTYPE" == *"darwin"* ]] -then - os="mac" -fi - -yarn - -# build -if [[ "$TEST_PACKAGED" == "true" ]] -then - yarn dist:$os -else - yarn vite build -fi - -echo -echo "########" -echo "# Test #" -echo "########" -echo - -if [[ "$os" == "linux" ]] -then - xvfb-run -a -e /dev/stdout -s "-screen 0 1280x960x24" yarn playwright test .*.spec.ts -else - yarn playwright test .*.spec.ts -fi diff --git a/e2e/helpers.ts b/e2e/helpers.ts new file mode 100644 index 00000000000..0019edd32ca --- /dev/null +++ b/e2e/helpers.ts @@ -0,0 +1,28 @@ +import { join } from 'path' +import { + test, + _electron as electron, + type ElectronApplication +} from '@playwright/test' + +const main_js = join(__dirname, '../build/electron/main.js') + +/** + * Helper function to define a test requiring Heroic to be running + * @param name The name of the test + * @param func The test callback + */ +function electronTest( + name: string, + func: (app: ElectronApplication) => void | Promise +) { + test(name, async () => { + const app = await electron.launch({ + args: [main_js] + }) + await func(app) + await app.close() + }) +} + +export { electronTest } diff --git a/package.json b/package.json index e69e941ccde..2ab42f7e737 100644 --- a/package.json +++ b/package.json @@ -188,6 +188,7 @@ "simple-keyboard": "3.5.33", "steam-shortcut-editor": "3.1.1", "tslib": "2.5.0", + "xvfb-maybe": "^0.2.1", "zod": "3.22.3" }, "scripts": { @@ -197,10 +198,7 @@ "test": "jest", "test-watch": "jest --watch --maxWorkers=25%", "test:ci": "jest --runInBand --silent", - "test:e2e-ci": "cross-env-shell bash e2e/entrypoint.sh", - "test:e2ePackaged-ci": "cross-env TEST_PACKAGED=true yarn test:e2e-ci", - "test:e2e": "cross-env-shell docker compose -f e2e/docker-compose.yml run --rm --build process_all bash e2e/entrypoint.sh", - "test:e2ePackaged": "cross-env TEST_PACKAGED=true yarn test:e2e", + "test:e2e": "vite build && cross-env CI=e2e xvfb-maybe -- playwright test", "release:linux": "vite build && electron-builder -p always --linux deb AppImage rpm pacman tar.xz snap", "release:mac": "vite build && electron-builder -p always --mac --x64 --arm64", "release:win": "vite build && electron-builder -p always --win portable --x64", @@ -228,7 +226,7 @@ }, "devDependencies": { "@electron/notarize": "^2.1.0", - "@playwright/test": "1.32.1", + "@playwright/test": "1.39.0", "@testing-library/dom": "9.0.1", "@testing-library/jest-dom": "5.16.4", "@testing-library/react": "14.0.0", @@ -249,7 +247,6 @@ "electron": "castlabs/electron-releases#27.0.0+wvcus", "electron-builder": "24.6.4", "electron-devtools-installer": "3.2.0", - "electron-playwright-helpers": "1.5.5", "eslint": "8.36.0", "eslint-config-prettier": "8.7.0", "eslint-plugin-import": "2.27.5", @@ -258,7 +255,6 @@ "i18next-parser": "7.7.0", "jest": "29.5.0", "node-gyp": "^10.0.1", - "playwright": "1.32.1", "prettier": "3.0.3", "pretty-quick": "3.1.3", "sass": "1.59.2", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000000..e3b081d5212 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,11 @@ +import type { PlaywrightTestConfig } from '@playwright/test' + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config: PlaywrightTestConfig = { + testDir: './e2e', + workers: 1 +} + +export default config diff --git a/src/backend/constants.ts b/src/backend/constants.ts index cf272eb1057..d85697f3193 100644 --- a/src/backend/constants.ts +++ b/src/backend/constants.ts @@ -11,6 +11,7 @@ import { app } from 'electron' import { existsSync, mkdirSync, readFileSync } from 'graceful-fs' import { GlobalConfig } from './config' import { TypeCheckedStoreBackend } from './electron_store' +import { dirSync } from 'tmp' const configStore = new TypeCheckedStoreBackend('configStore', { cwd: 'store' @@ -39,7 +40,18 @@ const currentGlobalConfigVersion: GlobalConfigVersion = 'v0' const flatPakHome = env.XDG_DATA_HOME?.replace('/data', '') || homedir() const userHome = isSnap ? env.SNAP_REAL_HOME! : homedir() -const configFolder = app.getPath('appData') +let configFolder = app.getPath('appData') +// If we're running tests, we want a config folder independent of the normal +// user configuration +if (process.env.CI === 'e2e') { + const temp_dir = dirSync({ unsafeCleanup: true }) + logDebug( + `CI is set to "e2e", storing Heroic config files in ${temp_dir.name}` + ) + configFolder = temp_dir.name + mkdirSync(join(configFolder, 'heroic')) +} + const appFolder = join(configFolder, 'heroic') const legendaryConfigPath = isSnap ? join(env.XDG_CONFIG_HOME!, 'legendary') diff --git a/src/backend/main.ts b/src/backend/main.ts index 90736032963..fda68ab41be 100644 --- a/src/backend/main.ts +++ b/src/backend/main.ts @@ -233,7 +233,7 @@ async function initializeWindow(): Promise { detectVCRedist(mainWindow) } - if (!app.isPackaged && process.env.CI !== 'e2e') { + if (process.env.VITE_DEV_SERVER_URL) { if (!process.env.HEROIC_NO_REACT_DEVTOOLS) { import('electron-devtools-installer').then((devtools) => { const { default: installExtension, REACT_DEVELOPER_TOOLS } = devtools @@ -243,7 +243,7 @@ async function initializeWindow(): Promise { }) }) } - mainWindow.loadURL('http://localhost:5173') + mainWindow.loadURL(process.env.VITE_DEV_SERVER_URL) // Open the DevTools. mainWindow.webContents.openDevTools() } else { @@ -516,6 +516,10 @@ ipcMain.once('frontendReady', () => { // Maybe this can help with white screens process.on('uncaughtException', async (err) => { logError(`${err.name}: ${err.message}`, LogPrefix.Backend) + // We might get "object has been destroyed" exceptions in CI, since we start + // and close Heroic quickly there. Displaying an error box would lock up + // the test (until the timeout is reached), so let's not do that + if (process.env.CI === 'e2e') return showDialogBoxModalAuto({ title: i18next.t( 'box.error.uncaught-exception.title', diff --git a/yarn.lock b/yarn.lock index bb538133fa0..332ec65a164 100644 --- a/yarn.lock +++ b/yarn.lock @@ -322,7 +322,7 @@ ajv "^6.12.0" ajv-keywords "^3.4.1" -"@electron/asar@^3.2.1", "@electron/asar@^3.2.4": +"@electron/asar@^3.2.1": version "3.2.7" resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.7.tgz#bb8117dc6fd0c06a922ae7fb1c0e2d433e35a6e5" integrity sha512-8FaSCAIiZGYFWyjeevPQt+0e9xCK9YmJ2Rjg5SXgdsXon6cRnU0Yxnbe6CvJbQn26baifur2Y2G5EBayRIsjyg== @@ -1145,15 +1145,12 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@playwright/test@1.32.1": - version "1.32.1" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.32.1.tgz#749c9791adb048c266277a39ba0f7e33fe593ffe" - integrity sha512-FTwjCuhlm1qHUGf4hWjfr64UMJD/z0hXYbk+O387Ioe6WdyZQ+0TBDAc6P+pHjx2xCv1VYNgrKbYrNixFWy4Dg== +"@playwright/test@1.39.0": + version "1.39.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.39.0.tgz#d10ba8e38e44104499e25001945f07faa9fa91cd" + integrity sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ== dependencies: - "@types/node" "*" - playwright-core "1.32.1" - optionalDependencies: - fsevents "2.3.2" + playwright "1.39.0" "@popperjs/core@^2.11.6": version "2.11.8" @@ -3583,13 +3580,6 @@ electron-devtools-installer@3.2.0: tslib "^2.1.0" unzip-crx-3 "^0.2.0" -electron-playwright-helpers@1.5.5: - version "1.5.5" - resolved "https://registry.yarnpkg.com/electron-playwright-helpers/-/electron-playwright-helpers-1.5.5.tgz#56e8bc709d5685b2953c53b8c8e984a9fd841454" - integrity sha512-4WAHzIU+f8xFvmqbeB5O2syyRfhpPbA52/GGt5p7y8l04+CsZt4fUkCbvsph1xyn4itdsE2snS/fIoE33t+GfA== - dependencies: - "@electron/asar" "^3.2.4" - electron-publish@24.5.0: version "24.5.0" resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-24.5.0.tgz#492a4d7caa232e88ee3c18f5c3b4dc637e5e1b3a" @@ -7093,17 +7083,19 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -playwright-core@1.32.1: - version "1.32.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.32.1.tgz#5a10c32403323b07d75ea428ebeed866a80b76a1" - integrity sha512-KZYUQC10mXD2Am1rGlidaalNGYk3LU1vZqqNk0gT4XPty1jOqgup8KDP8l2CUlqoNKhXM5IfGjWgW37xvGllBA== +playwright-core@1.39.0: + version "1.39.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.39.0.tgz#efeaea754af4fb170d11845b8da30b2323287c63" + integrity sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw== -playwright@1.32.1: - version "1.32.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.32.1.tgz#c48195850740fbdbd7702f37e5a891b13259f689" - integrity sha512-GnEizysWMvoqHC3I9l8+4/ZxeLwLNdJJG76xdKGxzOcIZDcw5RSk/FKrFb5CuA+zcLpjIM2p9eR9Z4CuUDkWXg== +playwright@1.39.0: + version "1.39.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.39.0.tgz#184c81cd6478f8da28bcd9e60e94fcebf566e077" + integrity sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw== dependencies: - playwright-core "1.32.1" + playwright-core "1.39.0" + optionalDependencies: + fsevents "2.3.2" plist@3.0.5: version "3.0.5" @@ -9094,6 +9086,13 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: gopd "^1.0.1" has-tostringtag "^1.0.0" +which@^1.2.4: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -9166,6 +9165,14 @@ xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +xvfb-maybe@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/xvfb-maybe/-/xvfb-maybe-0.2.1.tgz#ed8cb132957b7848b439984c66f010ea7f24361b" + integrity sha512-9IyRz3l6Qyhl6LvnGRF5jMPB4oBEepQnuzvVAFTynP6ACLLSevqigICJ9d/+ofl29m2daeaVBChnPYUnaeJ7yA== + dependencies: + debug "^2.2.0" + which "^1.2.4" + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"