diff --git a/packages/playwright-core/src/server/browserType.ts b/packages/playwright-core/src/server/browserType.ts index d900b792cc..dd21bbb679 100644 --- a/packages/playwright-core/src/server/browserType.ts +++ b/packages/playwright-core/src/server/browserType.ts @@ -31,6 +31,9 @@ import { helper } from './helper'; import { RecentLogsCollector } from '../utils/debugLogger'; import { CallMetadata, SdkObject } from './instrumentation'; +export const kNoXServerRunningError = 'Looks like you launched a headed browser without having a XServer running.\n' + + 'Set either \'headless: false\' or use \'xvfb-run \' before running Playwright.\n\n<3 Playwright Team'; + export abstract class BrowserType extends SdkObject { private _name: BrowserName; readonly _playwrightOptions: PlaywrightOptions; @@ -76,7 +79,7 @@ export abstract class BrowserType extends SdkObject { async _innerLaunchWithRetries(progress: Progress, options: types.LaunchOptions, persistent: types.BrowserContextOptions | undefined, protocolLogger: types.ProtocolLogger, userDataDir?: string): Promise { try { - return this._innerLaunch(progress, options, persistent, protocolLogger, userDataDir); + return await this._innerLaunch(progress, options, persistent, protocolLogger, userDataDir); } catch (error) { // @see https://github.com/microsoft/playwright/issues/5214 const errorMessage = typeof error === 'object' && typeof error.message === 'string' ? error.message : ''; diff --git a/packages/playwright-core/src/server/chromium/chromium.ts b/packages/playwright-core/src/server/chromium/chromium.ts index 03f20c3659..1e52673268 100644 --- a/packages/playwright-core/src/server/chromium/chromium.ts +++ b/packages/playwright-core/src/server/chromium/chromium.ts @@ -22,12 +22,12 @@ import { CRBrowser } from './crBrowser'; import { Env, gracefullyCloseSet } from '../../utils/processLauncher'; import { kBrowserCloseMessageId } from './crConnection'; import { rewriteErrorMessage } from '../../utils/stackTrace'; -import { BrowserType } from '../browserType'; +import { BrowserType, kNoXServerRunningError } from '../browserType'; import { ConnectionTransport, ProtocolRequest, WebSocketTransport } from '../transport'; import { CRDevTools } from './crDevTools'; import { Browser, BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser'; import * as types from '../types'; -import { debugMode, fetchData, getUserAgent, headersArrayToObject, HTTPRequestParams, removeFolders, streamToString } from '../../utils/utils'; +import { debugMode, fetchData, getUserAgent, headersArrayToObject, HTTPRequestParams, removeFolders, streamToString, wrapInASCIIBox } from '../../utils/utils'; import { RecentLogsCollector } from '../../utils/debugLogger'; import { Progress, ProgressController } from '../progress'; import { TimeoutSettings } from '../../utils/timeoutSettings'; @@ -126,6 +126,8 @@ export class Chromium extends BrowserType { } _rewriteStartupError(error: Error): Error { + if (error.message.includes('Missing X server')) + return rewriteErrorMessage(error, '\n' + wrapInASCIIBox(kNoXServerRunningError, 1)); // These error messages are taken from Chromium source code as of July, 2020: // https://github.com/chromium/chromium/blob/70565f67e79f79e17663ad1337dc6e63ee207ce9/content/browser/zygote_host/zygote_host_impl_linux.cc if (!error.message.includes('crbug.com/357670') && !error.message.includes('No usable sandbox!') && !error.message.includes('crbug.com/638180')) diff --git a/packages/playwright-core/src/server/firefox/firefox.ts b/packages/playwright-core/src/server/firefox/firefox.ts index 9c6f45d56c..d5331f7d95 100644 --- a/packages/playwright-core/src/server/firefox/firefox.ts +++ b/packages/playwright-core/src/server/firefox/firefox.ts @@ -20,11 +20,13 @@ import fs from 'fs'; import path from 'path'; import { FFBrowser } from './ffBrowser'; import { kBrowserCloseMessageId } from './ffConnection'; -import { BrowserType } from '../browserType'; +import { BrowserType, kNoXServerRunningError } from '../browserType'; import { Env } from '../../utils/processLauncher'; import { ConnectionTransport } from '../transport'; import { BrowserOptions, PlaywrightOptions } from '../browser'; import * as types from '../types'; +import { rewriteErrorMessage } from '../../utils/stackTrace'; +import { wrapInASCIIBox } from '../../utils/utils'; export class Firefox extends BrowserType { constructor(playwrightOptions: PlaywrightOptions) { @@ -36,6 +38,8 @@ export class Firefox extends BrowserType { } _rewriteStartupError(error: Error): Error { + if (error.message.includes('no DISPLAY environment variable specified')) + return rewriteErrorMessage(error, '\n' + wrapInASCIIBox(kNoXServerRunningError, 1)); return error; } diff --git a/packages/playwright-core/src/server/webkit/webkit.ts b/packages/playwright-core/src/server/webkit/webkit.ts index 6b8e1b8efb..dd32a078ab 100644 --- a/packages/playwright-core/src/server/webkit/webkit.ts +++ b/packages/playwright-core/src/server/webkit/webkit.ts @@ -19,10 +19,12 @@ import { WKBrowser } from '../webkit/wkBrowser'; import { Env } from '../../utils/processLauncher'; import path from 'path'; import { kBrowserCloseMessageId } from './wkConnection'; -import { BrowserType } from '../browserType'; +import { BrowserType, kNoXServerRunningError } from '../browserType'; import { ConnectionTransport } from '../transport'; import { BrowserOptions, PlaywrightOptions } from '../browser'; import * as types from '../types'; +import { rewriteErrorMessage } from '../../utils/stackTrace'; +import { wrapInASCIIBox } from '../../utils/utils'; export class WebKit extends BrowserType { constructor(playwrightOptions: PlaywrightOptions) { @@ -38,6 +40,8 @@ export class WebKit extends BrowserType { } _rewriteStartupError(error: Error): Error { + if (error.message.includes('cannot open display')) + return rewriteErrorMessage(error, '\n' + wrapInASCIIBox(kNoXServerRunningError, 1)); return error; } diff --git a/tests/launcher.spec.ts b/tests/launcher.spec.ts index fea0854664..4be5982e06 100644 --- a/tests/launcher.spec.ts +++ b/tests/launcher.spec.ts @@ -40,3 +40,17 @@ it('should kill browser process on timeout after close', async ({ browserType, m await browser.close(); expect(stalled).toBeTruthy(); }); + +it('should throw a friendly error if its headed and there is no xserver on linux running', async ({ browserType, platform }) => { + it.skip(platform !== 'linux'); + const error: Error = await browserType.launch({ + headless: false, + env: { + ...process.env, + DISPLAY: undefined, + }, + }).catch(e => e); + expect(error).toBeInstanceOf(Error); + expect(error.message).toMatch(/Looks like you launched a headed browser without having a XServer running./); + expect(error.message).toMatch(/xvfb-run/); +});