rwsdk
Version:
Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime
128 lines (127 loc) • 5.67 kB
JavaScript
import { computeExecutablePath, detectBrowserPlatform, install, Browser as PuppeteerBrowser, resolveBuildId, } from "@puppeteer/browsers";
import debug from "debug";
import { mkdirp, pathExists } from "fs-extra";
import { join } from "path";
import puppeteer from "puppeteer-core";
import { ensureTmpDir } from "./utils.mjs";
const log = debug("rwsdk:e2e:browser");
/**
* Launch a browser instance
*/
export async function launchBrowser(browserPath, headless = true) {
// Define a consistent cache directory path in system temp folder
const rwCacheDir = join(await ensureTmpDir(), "redwoodjs-smoke-test-cache");
await mkdirp(rwCacheDir);
log("Using cache directory: %s", rwCacheDir);
// Get browser path if not provided
if (!browserPath) {
log("Getting browser executable path");
browserPath = await getBrowserPath({ headless });
}
console.log(`🚀 Launching browser from ${browserPath} (headless: ${headless})`);
log("Starting browser with puppeteer");
return await puppeteer.launch({
executablePath: browserPath,
headless,
args: ["--no-sandbox", "--disable-setuid-sandbox"],
});
}
/**
* Get the browser executable path
*/
export async function getBrowserPath(testOptions) {
console.log("Finding Chrome executable...");
// First try using environment variable if set
if (process.env.CHROME_PATH) {
console.log(`Using Chrome from environment variable: ${process.env.CHROME_PATH}`);
return process.env.CHROME_PATH;
}
// Detect platform
log("Detecting platform");
const platform = detectBrowserPlatform();
if (!platform) {
log("ERROR: Failed to detect browser platform");
throw new Error("Failed to detect browser platform");
}
log("Detected platform: %s", platform);
// Define a consistent cache directory path in system temp folder
const rwCacheDir = join(await ensureTmpDir(), "redwoodjs-smoke-test-cache");
await mkdirp(rwCacheDir);
log("Using cache directory: %s", rwCacheDir);
// Determine browser type based on headless option
const browser = testOptions?.headless === false
? PuppeteerBrowser.CHROME
: PuppeteerBrowser.CHROMEHEADLESSSHELL;
log(`Using browser type: ${browser}`);
// Resolve the buildId for the stable Chrome version - do this once
log("Resolving Chrome buildId for stable channel");
const buildId = await resolveBuildId(browser, platform, "stable");
log("Resolved buildId: %s", buildId);
// Create installation options - use them consistently
const installOptions = {
browser,
platform,
cacheDir: rwCacheDir,
buildId,
unpack: true,
};
try {
// Try to compute the path first (this will check if it's installed)
log("Attempting to find existing Chrome installation");
const path = computeExecutablePath(installOptions);
if (await pathExists(path)) {
console.log(`Found existing Chrome at: ${path}`);
return path;
}
else {
throw new Error("Chrome not found at: " + path);
}
}
catch (error) {
// If path computation fails, install Chrome
console.log("No Chrome installation found. Installing Chrome...");
// Add better error handling for the install step
try {
console.log("Downloading Chrome (this may take a few minutes)...");
const attempts = 10;
let installError;
for (let i = 0; i < attempts; i++) {
try {
await install(installOptions);
installError = null; // Reset error on success
break; // Exit loop on success
}
catch (e) {
installError = e;
console.log(`Attempt ${i + 1}/${attempts} failed. Retrying in 1 second...`);
// Wait for 1 second before retrying
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
if (installError) {
throw installError;
}
console.log("✅ Chrome installation completed successfully");
// Now compute the path for the installed browser
const path = computeExecutablePath(installOptions);
console.log(`Installed and using Chrome at: ${path}`);
return path;
}
catch (installError) {
// Provide more detailed error about the browser download failure
log("ERROR: Failed to download/install Chrome: %O", installError);
console.error(`❌ Failed to download/install Chrome browser.`);
console.error(`This is likely a network issue or the browser download URL is unavailable.`);
console.error(`Error details: ${installError instanceof Error ? installError.message : String(installError)}`);
// For debug builds, show the full error stack if available
if (installError instanceof Error && installError.stack) {
log("Error stack: %s", installError.stack);
}
console.log("\nPossible solutions:");
console.log("1. Check your internet connection");
console.log("2. Set CHROME_PATH environment variable to an existing Chrome installation");
console.log("3. Install Chrome manually and run the tests again");
throw new Error(`Failed to install Chrome browser: ${installError instanceof Error ? installError.message : String(installError)}`);
}
}
}