diff --git a/tests/browsercontext-viewport-mobile.spec.ts b/tests/browsercontext-viewport-mobile.spec.ts index 178503244d..fb55986a27 100644 --- a/tests/browsercontext-viewport-mobile.spec.ts +++ b/tests/browsercontext-viewport-mobile.spec.ts @@ -173,4 +173,20 @@ it.describe('mobile viewport', () => { expect(await page.evaluate('result')).toEqual({ x: 30, y: 40 }); await context.close(); }); + + it('should scroll when emulating a mobile viewport', async ({ browser, server, browserName }) => { + const context = await browser.newContext({ + viewport: { 'width': 1000, 'height': 600 }, + isMobile: true, + }); + const page = await context.newPage(); + await page.goto(server.PREFIX + '/input/scrollable.html'); + await page.mouse.move(50, 60); + const error = await page.mouse.wheel(0, 100).catch(e => e); + if (browserName === 'webkit') + expect(error.message).toContain('Mouse wheel is not supported in mobile WebKit'); + else + await page.waitForFunction('window.scrollY === 100'); + await context.close(); + }); }); diff --git a/tests/page/page-network-response.spec.ts b/tests/page/page-network-response.spec.ts index 458c99485c..3ae59fde43 100644 --- a/tests/page/page-network-response.spec.ts +++ b/tests/page/page-network-response.spec.ts @@ -235,9 +235,9 @@ it('should report multiple set-cookie headers', async ({ page, server }) => { expect(await response.headerValues('set-cookie')).toEqual(['a=b', 'c=d']); }); -it('should behave the same way for headers and allHeaders', async ({ page, server, browserName, channel, platform }) => { +it('should behave the same way for headers and allHeaders', async ({ page, server, browserName, channel, platform, isAndroid }) => { it.fixme(browserName === 'webkit' && platform === 'win32', 'libcurl does not support non-set-cookie multivalue headers'); - it.skip(!!channel, 'Stable chrome uses \n as a header separator in non-raw headers'); + it.fixme(isAndroid, 'Android uses \n as a header separator in non-raw headers'); server.setRoute('/headers', (req, res) => { const headers = { 'Set-Cookie': ['a=b', 'c=d'], diff --git a/tests/page/page-request-fulfill.spec.ts b/tests/page/page-request-fulfill.spec.ts index 078c038ebe..cc93da6010 100644 --- a/tests/page/page-request-fulfill.spec.ts +++ b/tests/page/page-request-fulfill.spec.ts @@ -15,9 +15,24 @@ * limitations under the License. */ -import { test as it, expect } from './pageTest'; +import { test as base, expect } from './pageTest'; import fs from 'fs'; +const it = base.extend<{ + // We access test servers at 10.0.2.2 from inside the browser on Android, + // which is actually forwarded to the desktop localhost. + // To use request such an url with apiRequestContext on the desktop, we need to change it back to localhost. + rewriteAndroidLoopbackURL(url: string): string + }>({ + rewriteAndroidLoopbackURL: ({ isAndroid }, use) => use(givenURL => { + if (!isAndroid) + return givenURL; + const requestURL = new URL(givenURL); + requestURL.hostname = 'localhost'; + return requestURL.toString(); + }) + }); + it('should work', async ({ page, server }) => { await page.route('**/*', route => { route.fulfill({ @@ -194,11 +209,11 @@ it('should include the origin header', async ({ page, server, isAndroid }) => { expect(interceptedRequest.headers()['origin']).toEqual(server.PREFIX); }); -it('should fulfill with global fetch result', async ({ playwright, page, server, isElectron }) => { +it('should fulfill with global fetch result', async ({ playwright, page, server, isElectron, rewriteAndroidLoopbackURL }) => { it.fixme(isElectron, 'error: Browser context management is not supported.'); await page.route('**/*', async route => { const request = await playwright.request.newContext(); - const response = await request.get(server.PREFIX + '/simple.json'); + const response = await request.get(rewriteAndroidLoopbackURL(server.PREFIX + '/simple.json')); route.fulfill({ response }); }); const response = await page.goto(server.EMPTY_PAGE); @@ -206,10 +221,10 @@ it('should fulfill with global fetch result', async ({ playwright, page, server, expect(await response.json()).toEqual({ 'foo': 'bar' }); }); -it('should fulfill with fetch result', async ({ page, server, isElectron }) => { +it('should fulfill with fetch result', async ({ page, server, isElectron, rewriteAndroidLoopbackURL }) => { it.fixme(isElectron, 'error: Browser context management is not supported.'); await page.route('**/*', async route => { - const response = await page.request.get(server.PREFIX + '/simple.json'); + const response = await page.request.get(rewriteAndroidLoopbackURL(server.PREFIX + '/simple.json')); route.fulfill({ response }); }); const response = await page.goto(server.EMPTY_PAGE); @@ -217,10 +232,10 @@ it('should fulfill with fetch result', async ({ page, server, isElectron }) => { expect(await response.json()).toEqual({ 'foo': 'bar' }); }); -it('should fulfill with fetch result and overrides', async ({ page, server, isElectron }) => { +it('should fulfill with fetch result and overrides', async ({ page, server, isElectron, rewriteAndroidLoopbackURL }) => { it.fixme(isElectron, 'error: Browser context management is not supported.'); await page.route('**/*', async route => { - const response = await page.request.get(server.PREFIX + '/simple.json'); + const response = await page.request.get(rewriteAndroidLoopbackURL(server.PREFIX + '/simple.json')); route.fulfill({ response, status: 201, @@ -236,8 +251,9 @@ it('should fulfill with fetch result and overrides', async ({ page, server, isEl expect(await response.json()).toEqual({ 'foo': 'bar' }); }); -it('should fetch original request and fulfill', async ({ page, server, isElectron }) => { +it('should fetch original request and fulfill', async ({ page, server, isElectron, isAndroid }) => { it.fixme(isElectron, 'error: Browser context management is not supported.'); + it.skip(isAndroid, 'The internal Android localhost (10.0.0.2) != the localhost on the host'); await page.route('**/*', async route => { const response = await page.request.fetch(route.request()); route.fulfill({ @@ -249,8 +265,9 @@ it('should fetch original request and fulfill', async ({ page, server, isElectro expect(await page.title()).toEqual('Woof-Woof'); }); -it('should fulfill with multiple set-cookie', async ({ page, server, browserName, isElectron }) => { +it('should fulfill with multiple set-cookie', async ({ page, server, isAndroid, isElectron }) => { it.fixme(isElectron, 'Electron 14+ is required'); + it.fixme(isAndroid); const cookies = ['a=b', 'c=d']; await page.route('**/empty.html', async route => { route.fulfill({ @@ -269,7 +286,8 @@ it('should fulfill with multiple set-cookie', async ({ page, server, browserName expect(await response.headerValue('X-Header-2')).toBe('v2'); }); -it('should fulfill with fetch response that has multiple set-cookie', async ({ playwright, page, server, browserName }) => { +it('should fulfill with fetch response that has multiple set-cookie', async ({ playwright, page, server, isAndroid }) => { + it.fixme(isAndroid); server.setRoute('/empty.html', (req, res) => { res.setHeader('Set-Cookie', ['a=b', 'c=d']); res.setHeader('Content-Type', 'text/html'); diff --git a/tests/page/page-request-intercept.spec.ts b/tests/page/page-request-intercept.spec.ts index e32af3e927..bf5db2dfa8 100644 --- a/tests/page/page-request-intercept.spec.ts +++ b/tests/page/page-request-intercept.spec.ts @@ -16,12 +16,28 @@ */ import type { Route } from 'playwright-core'; -import { expect, test as it } from './pageTest'; +import { expect, test as base } from './pageTest'; import fs from 'fs'; import path from 'path'; -it('should fulfill intercepted response', async ({ page, server, isElectron }) => { +const it = base.extend<{ + // We access test servers at 10.0.2.2 from inside the browser on Android, + // which is actually forwarded to the desktop localhost. + // To use request such an url with apiRequestContext on the desktop, we need to change it back to localhost. + rewriteAndroidLoopbackURL(url: string): string + }>({ + rewriteAndroidLoopbackURL: ({ isAndroid }, use) => use(givenURL => { + if (!isAndroid) + return givenURL; + const requestURL = new URL(givenURL); + requestURL.hostname = 'localhost'; + return requestURL.toString(); + }) + }); + +it('should fulfill intercepted response', async ({ page, server, isElectron, isAndroid }) => { it.fixme(isElectron, 'error: Browser context management is not supported.'); + it.skip(isAndroid, 'The internal Android localhost (10.0.0.2) != the localhost on the host'); await page.route('**/*', async route => { const response = await page.request.fetch(route.request()); await route.fulfill({ @@ -41,8 +57,8 @@ it('should fulfill intercepted response', async ({ page, server, isElectron }) = expect(await page.evaluate(() => document.body.textContent)).toBe('Yo, page!'); }); -it('should fulfill response with empty body', async ({ page, server, browserName, browserMajorVersion }) => { - it.skip(browserName === 'chromium' && browserMajorVersion <= 91, 'Fails in Electron that uses old Chromium'); +it('should fulfill response with empty body', async ({ page, server, isAndroid }) => { + it.skip(isAndroid, 'The internal Android localhost (10.0.0.2) != the localhost on the host'); await page.route('**/*', async route => { const response = await page.request.fetch(route.request()); await route.fulfill({ @@ -56,8 +72,9 @@ it('should fulfill response with empty body', async ({ page, server, browserName expect(await response.text()).toBe(''); }); -it('should override with defaults when intercepted response not provided', async ({ page, server, browserName, isElectron }) => { +it('should override with defaults when intercepted response not provided', async ({ page, server, browserName, isElectron, isAndroid }) => { it.fixme(isElectron, 'error: Browser context management is not supported.'); + it.skip(isAndroid, 'The internal Android localhost (10.0.0.2) != the localhost on the host'); server.setRoute('/empty.html', (req, res) => { res.setHeader('foo', 'bar'); res.end('my content'); @@ -77,14 +94,14 @@ it('should override with defaults when intercepted response not provided', async expect(response.headers()).toEqual({ }); }); -it('should fulfill with any response', async ({ page, server, isElectron }) => { +it('should fulfill with any response', async ({ page, server, isElectron, rewriteAndroidLoopbackURL }) => { it.fixme(isElectron, 'error: Browser context management is not supported.'); server.setRoute('/sample', (req, res) => { res.setHeader('foo', 'bar'); res.end('Woo-hoo'); }); - const sampleResponse = await page.request.get(`${server.PREFIX}/sample`); + const sampleResponse = await page.request.get(rewriteAndroidLoopbackURL(`${server.PREFIX}/sample`)); await page.route('**/*', async route => { await route.fulfill({ @@ -99,8 +116,9 @@ it('should fulfill with any response', async ({ page, server, isElectron }) => { expect(response.headers()['foo']).toBe('bar'); }); -it('should support fulfill after intercept', async ({ page, server, isElectron }) => { +it('should support fulfill after intercept', async ({ page, server, isElectron, isAndroid }) => { it.fixme(isElectron, 'error: Browser context management is not supported.'); + it.skip(isAndroid, 'The internal Android localhost (10.0.0.2) != the localhost on the host'); const requestPromise = server.waitForRequest('/title.html'); await page.route('**', async route => { const response = await page.request.fetch(route.request()); @@ -113,8 +131,9 @@ it('should support fulfill after intercept', async ({ page, server, isElectron } expect(await response.text()).toBe(original); }); -it('should give access to the intercepted response', async ({ page, server, isElectron }) => { +it('should give access to the intercepted response', async ({ page, server, isElectron, isAndroid }) => { it.fixme(isElectron, 'error: Browser context management is not supported.'); + it.skip(isAndroid, 'The internal Android localhost (10.0.0.2) != the localhost on the host'); await page.goto(server.EMPTY_PAGE); let routeCallback; @@ -129,15 +148,16 @@ it('should give access to the intercepted response', async ({ page, server, isEl expect(response.status()).toBe(200); expect(response.statusText()).toBe('OK'); expect(response.ok()).toBeTruthy(); - expect(response.url()).toBe(server.PREFIX + '/title.html'); + expect(response.url().endsWith('/title.html')).toBe(true); expect(response.headers()['content-type']).toBe('text/html; charset=utf-8'); - expect(await (await response.headersArray()).filter(({ name }) => name.toLowerCase() === 'content-type')).toEqual([{ name: 'Content-Type', value: 'text/html; charset=utf-8' }]); + expect(response.headersArray().filter(({ name }) => name.toLowerCase() === 'content-type')).toEqual([{ name: 'Content-Type', value: 'text/html; charset=utf-8' }]); await Promise.all([route.fulfill({ response }), evalPromise]); }); -it('should give access to the intercepted response body', async ({ page, server, isElectron }) => { +it('should give access to the intercepted response body', async ({ page, server, isElectron, isAndroid }) => { it.fixme(isElectron, 'error: Browser context management is not supported.'); + it.skip(isAndroid, 'The internal Android localhost (10.0.0.2) != the localhost on the host'); await page.goto(server.EMPTY_PAGE); let routeCallback; @@ -149,7 +169,7 @@ it('should give access to the intercepted response body', async ({ page, server, const route = await routePromise; const response = await page.request.fetch(route.request()); - expect((await response.text())).toBe('{"foo": "bar"}\n'); + expect(await response.text()).toBe('{"foo": "bar"}\n'); await Promise.all([route.fulfill({ response }), evalPromise]); }); diff --git a/tests/page/wheel.spec.ts b/tests/page/wheel.spec.ts index d18743179d..b4252bca2c 100644 --- a/tests/page/wheel.spec.ts +++ b/tests/page/wheel.spec.ts @@ -15,11 +15,10 @@ */ import type { Page } from 'playwright-core'; import { test as it, expect } from './pageTest'; -import { contextTest } from '../config/browserTest'; -it.skip(({ isElectron, browserMajorVersion }) => { +it.skip(({ isElectron, browserMajorVersion, isAndroid }) => { // Old Electron has flaky wheel events. - return isElectron && browserMajorVersion <= 11; + return (isElectron && browserMajorVersion <= 11) || isAndroid; }); it('should dispatch wheel events #smoke', async ({ page, server }) => { await page.setContent(`
`); @@ -110,22 +109,6 @@ it('should work when the event is canceled', async ({ page }) => { expect(await page.evaluate('window.scrollY')).toBe(0); }); -contextTest('should scroll when emulating a mobile viewport', async ({ contextFactory, server, browserName }) => { - contextTest.skip(browserName === 'firefox'); - const context = await contextFactory({ - viewport: { 'width': 1000, 'height': 600 }, - isMobile: true, - }); - const page = await context.newPage(); - await page.goto(server.PREFIX + '/input/scrollable.html'); - await page.mouse.move(50, 60); - const error = await page.mouse.wheel(0, 100).catch(e => e); - if (browserName === 'webkit') - expect(error.message).toContain('Mouse wheel is not supported in mobile WebKit'); - else - await page.waitForFunction('window.scrollY === 100'); -}); - async function listenForWheelEvents(page: Page, selector: string) { await page.evaluate(selector => { document.querySelector(selector).addEventListener('wheel', (e: WheelEvent) => {