fix(playwright): improve chrome installation and fix spinner duplication on narrow terminals (#2347)

* headless: false will now install binaries for headless mode too

* fix spinner message duplication on narrow terminals

* add changeset
This commit is contained in:
nicktrn
2025-08-06 11:08:33 +01:00
committed by GitHub
parent 1f4434035e
commit f82a4e9651
3 changed files with 90 additions and 3 deletions
+7
View File
@@ -0,0 +1,7 @@
---
"trigger.dev": patch
"@trigger.dev/build": patch
---
- Improve playwright non-headless chrome installation
- Prevent spinner message duplication in narrow terminals
+21 -2
View File
@@ -295,10 +295,29 @@ class PlaywrightExtension implements BuildExtension {
* We save this output to a file and then parse it to get the download urls for the browsers.
*/
instructions.push(`RUN npx playwright install --dry-run > /tmp/browser-info.txt`);
// Determine which browsers to actually install
const browsersToInstall = new Set<PlaywrightBrowser | "chromium-headless-shell">();
this.options.browsers.forEach((browser) => {
const browserType = browser === "chromium" ? "chromium-headless-shell" : browser;
if (browser === "chromium") {
if (this.options.headless) {
// For headless mode, only install chromium-headless-shell
browsersToInstall.add("chromium-headless-shell");
} else {
// For non-headless mode, install both chromium and chromium-headless-shell
// This allows users to easily switch between headless and non-headless mode
browsersToInstall.add("chromium");
browsersToInstall.add("chromium-headless-shell");
}
} else {
browsersToInstall.add(browser);
}
});
Array.from(browsersToInstall).forEach((browser) => {
instructions.push(
`RUN grep -A5 "browser: ${browserType}" /tmp/browser-info.txt > /tmp/${browser}-info.txt`,
`RUN grep -A5 -m1 "browser: ${browser}" /tmp/browser-info.txt > /tmp/${browser}-info.txt`,
`RUN INSTALL_DIR=$(grep "Install location:" /tmp/${browser}-info.txt | cut -d':' -f2- | xargs) && \
DIR_NAME=$(basename "$INSTALL_DIR") && \
+62 -1
View File
@@ -7,6 +7,67 @@ export function escapeImportPath(path: string) {
return isWindows ? path.replaceAll("\\", "\\\\") : path;
}
// Removes ANSI escape sequences to get actual visible length
function getVisibleLength(str: string): number {
return (
str
// Remove terminal hyperlinks: \u001b]8;;URL\u0007TEXT\u001b]8;;\u0007
.replace(/\u001b]8;;[^\u0007]*\u0007/g, "")
// Remove standard ANSI escape sequences (colors, cursor movement, etc.)
.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "").length
);
}
function truncateMessage(msg: string, maxLength?: number): string {
const terminalWidth = maxLength ?? process.stdout.columns ?? 80;
const availableWidth = terminalWidth - 5; // Reserve some space for the spinner and padding
const visibleLength = getVisibleLength(msg);
if (visibleLength <= availableWidth) {
return msg;
}
// We need to truncate based on visible characters, but preserve ANSI sequences
// Simple approach: truncate character by character until we fit
let truncated = msg;
while (getVisibleLength(truncated) > availableWidth - 3) {
truncated = truncated.slice(0, -1);
}
return truncated + "...";
}
const wrappedClackSpinner = () => {
let currentMessage = "";
let isActive = false;
const handleResize = () => {
if (isActive && currentMessage) {
spinner.message(truncateMessage(currentMessage));
}
};
const spinner = clackSpinner();
return {
start: (msg?: string): void => {
currentMessage = msg ?? "";
isActive = true;
process.stdout.on("resize", handleResize);
spinner.start(truncateMessage(currentMessage));
},
stop: (msg?: string, code?: number): void => {
isActive = false;
process.stdout.off("resize", handleResize);
spinner.stop(truncateMessage(msg ?? ""), code);
},
message: (msg?: string): void => {
currentMessage = msg ?? "";
spinner.message(truncateMessage(currentMessage));
},
};
};
const ballmerSpinner = () => ({
start: (msg?: string): void => {
log.step(msg ?? "");
@@ -21,4 +82,4 @@ const ballmerSpinner = () => ({
// This will become unecessary with the next clack release, the bug was fixed here:
// https://github.com/natemoo-re/clack/pull/182
export const spinner = () => (isWindows ? ballmerSpinner() : clackSpinner());
export const spinner = () => (isWindows ? ballmerSpinner() : wrappedClackSpinner());