77 lines
4.0 KiB
JavaScript
77 lines
4.0 KiB
JavaScript
// Playwright (and the image helpers pngjs / pixelmatch) for the SW browser tests. Playwright is not a repository
|
|
// dependency, so the first location that has it is used:
|
|
// 1. SW_PLAYWRIGHT_PATH the playwright package directory (or its entry file)
|
|
// 2. SW_RUNTIME_MODULES a node_modules directory that contains playwright
|
|
// 3. HM_PLAYWRIGHT_DIR a project directory whose node_modules contains playwright (scripts/verify-quality-runtime.mjs)
|
|
// 4. this repository node_modules/playwright (npm i -D playwright, not committed)
|
|
// 5. the Codex runtime <home>/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules
|
|
// 6. global npm `npm root -g`
|
|
// A location set by an environment variable must exist: a wrong value fails instead of falling back silently.
|
|
import { createRequire } from 'node:module'
|
|
import { execFileSync } from 'node:child_process'
|
|
import { existsSync } from 'node:fs'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
|
|
const nodeModulesOf = file => {
|
|
const parts = path.resolve(file).split(path.sep)
|
|
const index = parts.lastIndexOf('node_modules')
|
|
return index < 0 ? null : parts.slice(0, index + 1).join(path.sep)
|
|
}
|
|
const fromNodeModules = dir => createRequire(path.join(dir, 'index.js'))
|
|
const globalRoot = () => {
|
|
try { return execFileSync('npm', ['root', '-g'], { encoding: 'utf8', shell: process.platform === 'win32', stdio: ['ignore', 'pipe', 'ignore'] }).trim() } catch { return null }
|
|
}
|
|
|
|
function candidates() {
|
|
const list = []
|
|
const { SW_PLAYWRIGHT_PATH: entry, SW_RUNTIME_MODULES: modules, HM_PLAYWRIGHT_DIR: project } = process.env
|
|
if (entry) {
|
|
const modulesDir = nodeModulesOf(entry)
|
|
list.push({ source: `SW_PLAYWRIGHT_PATH=${entry}`, explicit: true, entry: path.resolve(entry), require: modulesDir ? fromNodeModules(modulesDir) : createRequire(import.meta.url) })
|
|
}
|
|
if (modules) list.push({ source: `SW_RUNTIME_MODULES=${modules}`, explicit: true, require: fromNodeModules(path.resolve(modules)) })
|
|
if (project) list.push({ source: `HM_PLAYWRIGHT_DIR=${project}`, explicit: true, require: createRequire(path.join(path.resolve(project), 'package.json')) })
|
|
list.push({ source: 'repository node_modules', require: createRequire(import.meta.url) })
|
|
const codex = path.join(os.homedir(), '.cache', 'codex-runtimes', 'codex-primary-runtime', 'dependencies', 'node', 'node_modules')
|
|
if (existsSync(codex)) list.push({ source: `Codex runtime ${codex}`, require: fromNodeModules(codex) })
|
|
const global = globalRoot()
|
|
if (global && existsSync(global)) list.push({ source: `global npm ${global}`, require: fromNodeModules(global) })
|
|
return list
|
|
}
|
|
|
|
let resolved
|
|
function resolve() {
|
|
if (resolved) return resolved
|
|
const tried = []
|
|
for (const candidate of candidates()) {
|
|
try {
|
|
const entry = candidate.entry ?? candidate.require.resolve('playwright')
|
|
if (candidate.entry && !existsSync(candidate.entry)) throw new Error('path does not exist')
|
|
resolved = { ...candidate, entry }
|
|
return resolved
|
|
} catch (error) {
|
|
tried.push(`${candidate.source}: ${error.message.split('\n')[0]}`)
|
|
if (candidate.explicit) break
|
|
}
|
|
}
|
|
throw new Error(`Playwright not found for the SW browser tests. Set SW_RUNTIME_MODULES to a node_modules directory that contains playwright (or SW_PLAYWRIGHT_PATH to the playwright package).\n tried:\n - ${tried.join('\n - ')}`)
|
|
}
|
|
|
|
/** The playwright module (`{ chromium, firefox, ... }`). */
|
|
export const loadPlaywright = () => resolve().require(resolve().entry)
|
|
|
|
/**
|
|
* A `require` rooted where Playwright was found, for the other runtime modules the tests use (pngjs, pixelmatch).
|
|
* `require('playwright')` through it returns the same module as `loadPlaywright()`.
|
|
*/
|
|
export function runtimeRequire() {
|
|
const { require, entry } = resolve()
|
|
const wrapped = id => (id === 'playwright' ? require(entry) : require(id))
|
|
wrapped.resolve = (id, options) => (id === 'playwright' ? entry : require.resolve(id, options))
|
|
return wrapped
|
|
}
|
|
|
|
/** Where Playwright came from (for reports). */
|
|
export const playwrightSource = () => resolve().source
|