fix: encode launch options correctly when reusing browser (#24174)

Fixes https://github.com/microsoft/playwright/issues/24157
This commit is contained in:
Max Schmitt 2023-07-12 16:40:55 +02:00 committed by GitHub
parent 6e02fda2a0
commit a0b0752662
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 4 deletions

View File

@ -23,7 +23,7 @@ import { Connection } from './connection';
import { Events } from './events';
import type { ChildProcess } from 'child_process';
import { envObjectToArray } from './clientHelper';
import { assert, headersObjectToArray, monotonicTime } from '../utils';
import { jsonStringifyForceASCII, assert, headersObjectToArray, monotonicTime } from '../utils';
import type * as api from '../../types/types';
import { kBrowserClosedError } from '../common/errors';
import { raceAgainstTimeout } from '../utils/timeoutRunner';
@ -93,7 +93,8 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel> imple
return this._connect({
wsEndpoint: connectOptions.wsEndpoint,
headers: {
'x-playwright-launch-options': JSON.stringify({ ...this._defaultLaunchOptions, ...launchOptions }),
// HTTP headers are ASCII only (not UTF-8).
'x-playwright-launch-options': jsonStringifyForceASCII({ ...this._defaultLaunchOptions, ...launchOptions }),
...connectOptions.headers,
},
_exposeNetwork: connectOptions._exposeNetwork,

View File

@ -23,3 +23,10 @@ export function wrapInASCIIBox(text: string, padding = 0): string {
'╚' + '═'.repeat(maxLength + padding * 2) + '╝',
].join('\n');
}
export function jsonStringifyForceASCII(object: any): string {
return JSON.stringify(object).replace(
/[\u007f-\uffff]/g,
c => '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4)
);
}

View File

@ -18,7 +18,7 @@ import * as fs from 'fs';
import * as path from 'path';
import type { APIRequestContext, BrowserContext, Browser, BrowserContextOptions, LaunchOptions, Page, Tracing, Video } from 'playwright-core';
import * as playwrightLibrary from 'playwright-core';
import { createGuid, debugMode, addInternalStackPrefix, mergeTraceFiles, saveTraceFile, removeFolders, isString, asLocator } from 'playwright-core/lib/utils';
import { createGuid, debugMode, addInternalStackPrefix, mergeTraceFiles, saveTraceFile, removeFolders, isString, asLocator, jsonStringifyForceASCII } from 'playwright-core/lib/utils';
import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, ScreenshotMode, TestInfo, TestType, TraceMode, VideoMode } from '../types/test';
import type { TestInfoImpl } from './worker/testInfo';
import { rootTestType } from './common/testType';
@ -125,7 +125,8 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
...connectOptions,
headers: {
'x-playwright-reuse-context': '1',
'x-playwright-launch-options': JSON.stringify(_browserOptions),
// HTTP headers are ASCII only (not UTF-8).
'x-playwright-launch-options': jsonStringifyForceASCII(_browserOptions),
...connectOptions.headers,
},
});

View File

@ -21,10 +21,19 @@ test('should work with connectOptions', async ({ runInlineTest }) => {
'playwright.config.js': `
module.exports = {
globalSetup: './global-setup',
// outputDir is relative to the config file. Customers can have special characters in the path:
// See: https://github.com/microsoft/playwright/issues/24157
outputDir: 'Привет',
use: {
connectOptions: {
wsEndpoint: process.env.CONNECT_WS_ENDPOINT,
},
launchOptions: {
env: {
// Customers can have special characters: https://github.com/microsoft/playwright/issues/24157
RANDOM_TEST_SPECIAL: 'Привет',
}
}
},
};
`,

View File

@ -61,3 +61,40 @@ test('should reuse browser', async ({ runInlineTest, runServer }) => {
expect(guid2).toBe(guid1);
expect(workerIndex2).not.toBe(workerIndex1);
});
test('should reuse browser with special characters in the launch options', async ({ runInlineTest, runServer }) => {
const server = await runServer();
const result = await runInlineTest({
'playwright.config.js': `
module.exports = {
use: {
launchOptions: {
env: {
RANDOM_TEST_SPECIAL: 'Привет',
}
}
}
}
`,
'src/a.test.ts': `
import { test, expect } from '@playwright/test';
test('a', async ({ browser }) => {
console.log('%%' + process.env.TEST_WORKER_INDEX + ':' + browser._guid);
});
`,
'src/b.test.ts': `
import { test, expect } from '@playwright/test';
test('b', async ({ browser }) => {
console.log('%%' + process.env.TEST_WORKER_INDEX + ':' + browser._guid);
});
`,
}, { workers: 2 }, { PW_TEST_REUSE_CONTEXT: '1', PW_TEST_CONNECT_WS_ENDPOINT: server.wsEndpoint() });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(2);
expect(result.outputLines).toHaveLength(2);
const [workerIndex1, guid1] = result.outputLines[0].split(':');
const [workerIndex2, guid2] = result.outputLines[1].split(':');
expect(guid2).toBe(guid1);
expect(workerIndex2).not.toBe(workerIndex1);
});