diff --git a/.github/workflows/tests_secondary.yml b/.github/workflows/tests_secondary.yml index d5844b1ca3..7d2847a7ff 100644 --- a/.github/workflows/tests_secondary.yml +++ b/.github/workflows/tests_secondary.yml @@ -119,6 +119,7 @@ jobs: - "^12.0.0" - "^14.1.0" # pre 14.1, zip extraction was broken (https://github.com/microsoft/playwright/issues/1988) - "^16.0.0" + - "^18.0.0" timeout-minutes: 20 steps: - uses: actions/checkout@v2 diff --git a/packages/playwright-core/src/common/socksProxy.ts b/packages/playwright-core/src/common/socksProxy.ts index 504afdfc6a..2c8c5a8270 100644 --- a/packages/playwright-core/src/common/socksProxy.ts +++ b/packages/playwright-core/src/common/socksProxy.ts @@ -375,7 +375,10 @@ export class SocksProxyHandler extends EventEmitter { async socketRequested({ uid, host, port }: SocksSocketRequestedPayload): Promise { if (host === 'local.playwright') - host = 'localhost'; + host = '127.0.0.1'; + // Node.js 17 does resolve localhost to ipv6 + if (host === 'localhost') + host = '127.0.0.1'; try { if (this._redirectPortForTest) port = this._redirectPortForTest; diff --git a/packages/playwright-core/src/server/chromium/chromium.ts b/packages/playwright-core/src/server/chromium/chromium.ts index 019e552c5f..19bb9f32f6 100644 --- a/packages/playwright-core/src/server/chromium/chromium.ts +++ b/packages/playwright-core/src/server/chromium/chromium.ts @@ -225,7 +225,8 @@ export class Chromium extends BrowserType { const chromeOptions = maybeChromeOptions && typeof maybeChromeOptions === 'object' ? maybeChromeOptions : undefined; const debuggerAddress = chromeOptions && typeof chromeOptions.debuggerAddress === 'string' ? chromeOptions.debuggerAddress : undefined; const chromeOptionsURL = typeof maybeChromeOptions === 'string' ? maybeChromeOptions : undefined; - const endpointURLString = addProtocol(debuggerAddress || chromeOptionsURL); + // TODO(dgozman): figure out if we can make ChromeDriver to return 127.0.0.1 instead of localhost. + const endpointURLString = addProtocol(debuggerAddress || chromeOptionsURL).replace('localhost', '127.0.0.1'); progress.log(` retrieved endpoint ${endpointURLString} for sessionId=${sessionId}`); endpointURL = new URL(endpointURLString); if (endpointURL.hostname === 'localhost' || endpointURL.hostname === '127.0.0.1') { diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index f2fbef7a71..e17ea3484c 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -364,11 +364,12 @@ export abstract class APIRequestContext extends SdkObject { if (e) reject(new Error(`failed to decompress '${encoding}' encoding: ${e}`)); }); + } else { + body.on('error', reject); } body.on('data', chunk => chunks.push(chunk)); body.on('end', notifyBodyFinished); - body.on('error', reject); }); request.on('error', reject); diff --git a/packages/playwright-webkit/package.json b/packages/playwright-webkit/package.json index 0399185270..10de80842c 100644 --- a/packages/playwright-webkit/package.json +++ b/packages/playwright-webkit/package.json @@ -16,7 +16,8 @@ "import": "./index.mjs", "require": "./index.js" }, - "./": "./" + "./cli": "./cli.js", + "./package.json": "./package.json" }, "bin": { "playwright": "./cli.js" diff --git a/tests/config/commonFixtures.ts b/tests/config/commonFixtures.ts index 90828a6aa2..85da894b71 100644 --- a/tests/config/commonFixtures.ts +++ b/tests/config/commonFixtures.ts @@ -141,7 +141,7 @@ export const commonFixtures: Fixtures = { await use(async port => { while (!token.canceled) { const promise = new Promise(resolve => { - const conn = net.connect(port) + const conn = net.connect(port, '127.0.0.1') .on('error', () => resolve(false)) .on('connect', () => { conn.end(); diff --git a/tests/library/browsercontext-fetch.spec.ts b/tests/library/browsercontext-fetch.spec.ts index 0221260ac3..97d24c50ac 100644 --- a/tests/library/browsercontext-fetch.spec.ts +++ b/tests/library/browsercontext-fetch.spec.ts @@ -963,7 +963,7 @@ it('should work with connectOverCDP', async ({ browserName, browserType, server args: ['--remote-debugging-port=' + port] }); try { - const cdpBrowser = await browserType.connectOverCDP(`http://localhost:${port}/`); + const cdpBrowser = await browserType.connectOverCDP(`http://127.0.0.1:${port}/`); const [context] = cdpBrowser.contexts(); const response = await context.request.get(server.PREFIX + '/simple.json'); expect(response.url()).toBe(server.PREFIX + '/simple.json'); diff --git a/tests/library/browsertype-launch-selenium.spec.ts b/tests/library/browsertype-launch-selenium.spec.ts index 345c8dee5f..db5495c91b 100644 --- a/tests/library/browsertype-launch-selenium.spec.ts +++ b/tests/library/browsertype-launch-selenium.spec.ts @@ -47,7 +47,7 @@ test('selenium grid 3.141.59 standalone chromium', async ({ browserName, childPr }); await waitForPort(port); - const __testHookSeleniumRemoteURL = `http://localhost:${port}/wd/hub`; + const __testHookSeleniumRemoteURL = `http://127.0.0.1:${port}/wd/hub`; const browser = await browserType.launch({ __testHookSeleniumRemoteURL } as any); const page = await browser.newPage(); await page.setContent('Hello world
Get Started
'); @@ -71,7 +71,7 @@ test('selenium grid 3.141.59 hub + node chromium', async ({ browserName, childPr await waitForPort(port); const node = childProcess({ - command: ['java', `-Dwebdriver.chrome.driver=${chromeDriver}`, '-jar', standalone_3_141_59, '-role', 'node', '-host', '127.0.0.1', '-hub', `http://localhost:${port}/grid/register`], + command: ['java', `-Dwebdriver.chrome.driver=${chromeDriver}`, '-jar', standalone_3_141_59, '-role', 'node', '-host', '127.0.0.1', '-hub', `http://127.0.0.1:${port}/grid/register`], cwd: __dirname, }); await Promise.all([ @@ -79,7 +79,7 @@ test('selenium grid 3.141.59 hub + node chromium', async ({ browserName, childPr hub.waitForOutput('Registered a node'), ]); - const __testHookSeleniumRemoteURL = `http://localhost:${port}/wd/hub`; + const __testHookSeleniumRemoteURL = `http://127.0.0.1:${port}/wd/hub`; const browser = await browserType.launch({ __testHookSeleniumRemoteURL } as any); const page = await browser.newPage(); await page.setContent('Hello world
Get Started
'); @@ -103,7 +103,7 @@ test('selenium grid 4.0.0-rc-1 standalone chromium', async ({ browserName, child }); await waitForPort(port); - const __testHookSeleniumRemoteURL = `http://localhost:${port}/wd/hub`; + const __testHookSeleniumRemoteURL = `http://127.0.0.1:${port}/wd/hub`; const browser = await browserType.launch({ __testHookSeleniumRemoteURL } as any); const page = await browser.newPage(); await page.setContent('Hello world
Get Started
'); @@ -125,10 +125,10 @@ test('selenium grid 4.0.0-rc-1 hub + node chromium', async ({ browserName, child cwd: __dirname, }); await waitForPort(port); - const __testHookSeleniumRemoteURL = `http://localhost:${port}/wd/hub`; + const __testHookSeleniumRemoteURL = `http://127.0.0.1:${port}/wd/hub`; const node = childProcess({ - command: ['java', `-Dwebdriver.chrome.driver=${chromeDriver}`, '-jar', selenium_4_0_0_rc1, 'node', '--grid-url', `http://localhost:${port}`, '--port', String(port + 1)], + command: ['java', `-Dwebdriver.chrome.driver=${chromeDriver}`, '-jar', selenium_4_0_0_rc1, 'node', '--grid-url', `http://127.0.0.1:${port}`, '--port', String(port + 1)], cwd: __dirname, }); await Promise.all([ @@ -158,9 +158,9 @@ test('selenium grid 4.0.0-rc-1 standalone chromium broken driver', async ({ brow }); await waitForPort(port); - const __testHookSeleniumRemoteURL = `http://localhost:${port}/wd/hub`; + const __testHookSeleniumRemoteURL = `http://127.0.0.1:${port}/wd/hub`; const error = await browserType.launch({ __testHookSeleniumRemoteURL } as any).catch(e => e); - expect(error.message).toContain(`Error connecting to Selenium at http://localhost:${port}/wd/hub/session: Could not start a new session`); + expect(error.message).toContain(`Error connecting to Selenium at http://127.0.0.1:${port}/wd/hub/session: Could not start a new session`); expect(grid.output).not.toContain('Starting ChromeDriver'); }); @@ -168,7 +168,7 @@ test('selenium grid 4.0.0-rc-1 standalone chromium broken driver', async ({ brow test('selenium grid 3.141.59 standalone non-chromium', async ({ browserName, browserType }, testInfo) => { test.skip(browserName === 'chromium'); - const __testHookSeleniumRemoteURL = `http://localhost:4444/wd/hub`; + const __testHookSeleniumRemoteURL = `http://127.0.0.1:4444/wd/hub`; const error = await browserType.launch({ __testHookSeleniumRemoteURL } as any).catch(e => e); expect(error.message).toContain('Connecting to SELENIUM_REMOTE_URL is only supported by Chromium'); }); @@ -184,7 +184,7 @@ test('selenium grid 3.141.59 standalone chromium through run-driver', async ({ b await waitForPort(port); const { playwright: pw, stop } = await start({ - SELENIUM_REMOTE_URL: `http://localhost:${port}/wd/hub`, + SELENIUM_REMOTE_URL: `http://127.0.0.1:${port}/wd/hub`, }); const browser = await pw.chromium.launch(); const page = await browser.newPage(); diff --git a/tests/library/chromium/chromium.spec.ts b/tests/library/chromium/chromium.spec.ts index e1456a06a3..bb6f08bfe0 100644 --- a/tests/library/chromium/chromium.spec.ts +++ b/tests/library/chromium/chromium.spec.ts @@ -102,7 +102,7 @@ playwrightTest('should connect to an existing cdp session', async ({ browserType }); try { const cdpBrowser = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}/`, + endpointURL: `http://127.0.0.1:${port}/`, }); const contexts = cdpBrowser.contexts(); expect(contexts.length).toBe(1); @@ -120,7 +120,7 @@ playwrightTest('should cleanup artifacts dir after connectOverCDP disconnects du args: ['--remote-debugging-port=' + port] }); const cdpBrowser = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}/`, + endpointURL: `http://127.0.0.1:${port}/`, }); const dir = toImpl(cdpBrowser).options.artifactsDir; const exists1 = fs.existsSync(dir); @@ -140,10 +140,10 @@ playwrightTest('should connect to an existing cdp session twice', async ({ brows }); try { const cdpBrowser1 = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}/`, + endpointURL: `http://127.0.0.1:${port}/`, }); const cdpBrowser2 = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}/`, + endpointURL: `http://127.0.0.1:${port}/`, }); const contexts1 = cdpBrowser1.contexts(); expect(contexts1.length).toBe(1); @@ -178,7 +178,7 @@ playwrightTest('should connect to existing page with iframe and navigate', async const page = await context1.newPage(); await page.goto(server.PREFIX + '/frames/one-frame.html'); } - const cdpBrowser = await browserType.connectOverCDP(`http://localhost:${port}/`); + const cdpBrowser = await browserType.connectOverCDP(`http://127.0.0.1:${port}/`); const contexts = cdpBrowser.contexts(); expect(contexts.length).toBe(1); await contexts[0].pages()[0].goto(server.EMPTY_PAGE); @@ -195,7 +195,7 @@ playwrightTest('should connect to existing service workers', async ({ browserTyp }); try { const cdpBrowser1 = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}`, + endpointURL: `http://127.0.0.1:${port}`, }); const context = cdpBrowser1.contexts()[0]; const page = await cdpBrowser1.contexts()[0].newPage(); @@ -207,7 +207,7 @@ playwrightTest('should connect to existing service workers', async ({ browserTyp await cdpBrowser1.close(); const cdpBrowser2 = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}`, + endpointURL: `http://127.0.0.1:${port}`, }); const context2 = cdpBrowser2.contexts()[0]; expect(context2.serviceWorkers().length).toBe(1); @@ -224,7 +224,7 @@ playwrightTest('should connect over a ws endpoint', async ({ browserType, server }); try { const json = await new Promise((resolve, reject) => { - http.get(`http://localhost:${port}/json/version/`, resp => { + http.get(`http://127.0.0.1:${port}/json/version/`, resp => { let data = ''; resp.on('data', chunk => data += chunk); resp.on('end', () => resolve(data)); @@ -306,7 +306,7 @@ playwrightTest('should report all pages in an existing browser', async ({ browse }); try { const cdpBrowser = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}/`, + endpointURL: `http://127.0.0.1:${port}/`, }); const contexts = cdpBrowser.contexts(); expect(contexts.length).toBe(1); @@ -315,7 +315,7 @@ playwrightTest('should report all pages in an existing browser', async ({ browse await cdpBrowser.close(); const cdpBrowser2 = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}/`, + endpointURL: `http://127.0.0.1:${port}/`, }); expect(cdpBrowser2.contexts()[0].pages().length).toBe(3); @@ -332,7 +332,7 @@ playwrightTest('should connect via https', async ({ browserType, httpsServer, mo args: ['--remote-debugging-port=' + port] }); const json = await new Promise((resolve, reject) => { - http.get(`http://localhost:${port}/json/version/`, resp => { + http.get(`http://127.0.0.1:${port}/json/version/`, resp => { let data = ''; resp.on('data', chunk => data += chunk); resp.on('end', () => resolve(data)); @@ -366,7 +366,7 @@ playwrightTest('should return valid browser from context.browser()', async ({ br }); try { const cdpBrowser = await browserType.connectOverCDP({ - endpointURL: `http://localhost:${port}/`, + endpointURL: `http://127.0.0.1:${port}/`, }); const contexts = cdpBrowser.contexts(); expect(contexts.length).toBe(1); @@ -410,7 +410,7 @@ playwrightTest('should connect to an existing cdp session when passed as a first args: ['--remote-debugging-port=' + port] }); try { - const cdpBrowser = await browserType.connectOverCDP(`http://localhost:${port}/`); + const cdpBrowser = await browserType.connectOverCDP(`http://127.0.0.1:${port}/`); const contexts = cdpBrowser.contexts(); expect(contexts.length).toBe(1); await cdpBrowser.close(); @@ -428,7 +428,7 @@ playwrightTest('should use proxy with connectOverCDP', async ({ browserType, ser args: ['--remote-debugging-port=' + port, ...(process.platform === 'win32' ? ['--proxy-server=some-value'] : [])] }); try { - const cdpBrowser = await browserType.connectOverCDP(`http://localhost:${port}/`); + const cdpBrowser = await browserType.connectOverCDP(`http://127.0.0.1:${port}/`); const context = await cdpBrowser.newContext({ proxy: { server: `localhost:${server.PORT}` } }); diff --git a/tests/library/permissions.spec.ts b/tests/library/permissions.spec.ts index 13b8c354f9..473f8c88fe 100644 --- a/tests/library/permissions.spec.ts +++ b/tests/library/permissions.spec.ts @@ -51,7 +51,7 @@ it.describe('permissions', () => { it('should prompt for geolocation permission when origin is not listed', async ({ page, context, server }) => { await page.goto(server.EMPTY_PAGE); await context.grantPermissions(['geolocation'], { origin: server.EMPTY_PAGE }); - await page.goto(server.EMPTY_PAGE.replace('localhost', '127.0.0.1')); + await page.goto(server.CROSS_PROCESS_PREFIX + '/empty.html'); expect(await getPermission(page, 'geolocation')).toBe('prompt'); }); diff --git a/tests/library/port-forwarding-server.spec.ts b/tests/library/port-forwarding-server.spec.ts index eaebc2f5c4..9ae7fcc503 100644 --- a/tests/library/port-forwarding-server.spec.ts +++ b/tests/library/port-forwarding-server.spec.ts @@ -117,7 +117,7 @@ it('should proxy localhost requests @smoke', async ({ pageFactory, server, brows }); const examplePort = 20_000 + workerInfo.workerIndex * 3; const page = await pageFactory(testServerPort); - await page.goto(`http://localhost:${examplePort}/foo.html`); + await page.goto(`http://127.0.0.1:${examplePort}/foo.html`); expect(await page.content()).toContain('from-retargeted-server'); expect(reachedOriginalTarget).toBe(false); stopTestServer(); @@ -134,7 +134,7 @@ it('should proxy localhost requests from fetch api', async ({ pageFactory, serve }); const examplePort = 20_000 + workerInfo.workerIndex * 3; const page = await pageFactory(testServerPort); - const response = await page.request.get(`http://localhost:${examplePort}/foo.html`); + const response = await page.request.get(`http://127.0.0.1:${examplePort}/foo.html`); expect(response.status()).toBe(200); expect(await response.text()).toContain('from-retargeted-server'); expect(reachedOriginalTarget).toBe(false); @@ -159,9 +159,9 @@ it('should proxy local.playwright requests', async ({ pageFactory, server, brows it('should lead to the error page for forwarded requests when the connection is refused', async ({ pageFactory, browserName }, workerInfo) => { const examplePort = 20_000 + workerInfo.workerIndex * 3; const page = await pageFactory(); - const error = await page.goto(`http://localhost:${examplePort}`).catch(e => e); + const error = await page.goto(`http://127.0.0.1:${examplePort}`).catch(e => e); if (browserName === 'chromium') - expect(error.message).toContain('net::ERR_SOCKS_CONNECTION_FAILED at http://localhost:20'); + expect(error.message).toContain('net::ERR_SOCKS_CONNECTION_FAILED at http://127.0.0.1:20'); else if (browserName === 'webkit') expect(error.message).toBeTruthy(); else if (browserName === 'firefox')