mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
test: simplify more tests (#6471)
This commit is contained in:
parent
a5143ebaa9
commit
76e409637a
@ -15,15 +15,8 @@
|
||||
*/
|
||||
|
||||
import { browserTest as it, expect } from './config/browserTest';
|
||||
import type { Browser } from '../index';
|
||||
|
||||
let browser: Browser;
|
||||
it.beforeAll(async ({ browserType, browserOptions }) => {
|
||||
browser = await browserType.launch({ ...browserOptions, proxy: { server: 'per-context' } });
|
||||
});
|
||||
it.afterAll(async () => {
|
||||
await browser.close();
|
||||
});
|
||||
it.useOptions({ launchOptions: { proxy: { server: 'per-context' } } });
|
||||
|
||||
it('should throw for missing global proxy on Chromium Windows', async ({ browserName, platform, browserType, browserOptions, server }) => {
|
||||
it.skip(browserName !== 'chromium' || platform !== 'win32');
|
||||
@ -44,8 +37,8 @@ it('should work when passing the proxy only on the context level', async ({brows
|
||||
res.end('<html><title>Served by the proxy</title></html>');
|
||||
});
|
||||
delete browserOptions.proxy;
|
||||
const browserWithoutProxyInLaunch = await browserType.launch(browserOptions);
|
||||
const context = await browserWithoutProxyInLaunch.newContext({
|
||||
const browser = await browserType.launch(browserOptions);
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
proxy: { server: `localhost:${server.PORT}` }
|
||||
});
|
||||
@ -53,24 +46,22 @@ it('should work when passing the proxy only on the context level', async ({brows
|
||||
const page = await context.newPage();
|
||||
await page.goto('http://non-existent.com/target.html');
|
||||
expect(await page.title()).toBe('Served by the proxy');
|
||||
await browserWithoutProxyInLaunch.close();
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('should throw for bad server value', async ({ contextOptions }) => {
|
||||
const error = await browser.newContext({
|
||||
...contextOptions,
|
||||
it('should throw for bad server value', async ({ contextFactory }) => {
|
||||
const error = await contextFactory({
|
||||
// @ts-expect-error server must be a string
|
||||
proxy: { server: 123 }
|
||||
}).catch(e => e);
|
||||
expect(error.message).toContain('proxy.server: expected string, got number');
|
||||
});
|
||||
|
||||
it('should use proxy', async ({ contextOptions, server }) => {
|
||||
it('should use proxy', async ({ contextFactory, server }) => {
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
res.end('<html><title>Served by the proxy</title></html>');
|
||||
});
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
const context = await contextFactory({
|
||||
proxy: { server: `localhost:${server.PORT}` }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
@ -79,12 +70,11 @@ it('should use proxy', async ({ contextOptions, server }) => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should use proxy twice', async ({ contextOptions, server }) => {
|
||||
it('should use proxy twice', async ({ contextFactory, server }) => {
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
res.end('<html><title>Served by the proxy</title></html>');
|
||||
});
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
const context = await contextFactory({
|
||||
proxy: { server: `localhost:${server.PORT}` }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
@ -94,12 +84,11 @@ it('should use proxy twice', async ({ contextOptions, server }) => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should use proxy for second page', async ({contextOptions, server}) => {
|
||||
it('should use proxy for second page', async ({contextFactory, server}) => {
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
res.end('<html><title>Served by the proxy</title></html>');
|
||||
});
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
const context = await contextFactory({
|
||||
proxy: { server: `localhost:${server.PORT}` }
|
||||
});
|
||||
|
||||
@ -114,12 +103,11 @@ it('should use proxy for second page', async ({contextOptions, server}) => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with IP:PORT notion', async ({contextOptions, server}) => {
|
||||
it('should work with IP:PORT notion', async ({contextFactory, server}) => {
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
res.end('<html><title>Served by the proxy</title></html>');
|
||||
});
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
const context = await contextFactory({
|
||||
proxy: { server: `127.0.0.1:${server.PORT}` }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
@ -128,23 +116,21 @@ it('should work with IP:PORT notion', async ({contextOptions, server}) => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should throw for socks5 authentication', async ({contextOptions}) => {
|
||||
const error = await browser.newContext({
|
||||
...contextOptions,
|
||||
it('should throw for socks5 authentication', async ({contextFactory}) => {
|
||||
const error = await contextFactory({
|
||||
proxy: { server: `socks5://localhost:1234`, username: 'user', password: 'secret' }
|
||||
}).catch(e => e);
|
||||
expect(error.message).toContain('Browser does not support socks5 proxy authentication');
|
||||
});
|
||||
|
||||
it('should throw for socks4 authentication', async ({contextOptions}) => {
|
||||
const error = await browser.newContext({
|
||||
...contextOptions,
|
||||
it('should throw for socks4 authentication', async ({contextFactory}) => {
|
||||
const error = await contextFactory({
|
||||
proxy: { server: `socks4://localhost:1234`, username: 'user', password: 'secret' }
|
||||
}).catch(e => e);
|
||||
expect(error.message).toContain('Socks4 proxy protocol does not support authentication');
|
||||
});
|
||||
|
||||
it('should authenticate', async ({contextOptions, server}) => {
|
||||
it('should authenticate', async ({contextFactory, server}) => {
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
const auth = req.headers['proxy-authorization'];
|
||||
if (!auth) {
|
||||
@ -156,8 +142,7 @@ it('should authenticate', async ({contextOptions, server}) => {
|
||||
res.end(`<html><title>${auth}</title></html>`);
|
||||
}
|
||||
});
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
const context = await contextFactory({
|
||||
proxy: { server: `localhost:${server.PORT}`, username: 'user', password: 'secret' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
@ -166,7 +151,7 @@ it('should authenticate', async ({contextOptions, server}) => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should authenticate with empty password', async ({contextOptions, server}) => {
|
||||
it('should authenticate with empty password', async ({contextFactory, server}) => {
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
const auth = req.headers['proxy-authorization'];
|
||||
if (!auth) {
|
||||
@ -178,8 +163,7 @@ it('should authenticate with empty password', async ({contextOptions, server}) =
|
||||
res.end(`<html><title>${auth}</title></html>`);
|
||||
}
|
||||
});
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
const context = await contextFactory({
|
||||
proxy: { server: `localhost:${server.PORT}`, username: 'user', password: '' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
@ -188,8 +172,7 @@ it('should authenticate with empty password', async ({contextOptions, server}) =
|
||||
await context.close();
|
||||
});
|
||||
|
||||
|
||||
it('should isolate proxy credentials between contexts', async ({contextOptions, server, browserName}) => {
|
||||
it('should isolate proxy credentials between contexts', async ({contextFactory, server, browserName}) => {
|
||||
it.fixme(browserName === 'firefox', 'Credentials from the first context stick around');
|
||||
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
@ -204,8 +187,7 @@ it('should isolate proxy credentials between contexts', async ({contextOptions,
|
||||
}
|
||||
});
|
||||
{
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
const context = await contextFactory({
|
||||
proxy: { server: `localhost:${server.PORT}`, username: 'user1', password: 'secret1' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
@ -214,8 +196,7 @@ it('should isolate proxy credentials between contexts', async ({contextOptions,
|
||||
await context.close();
|
||||
}
|
||||
{
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
const context = await contextFactory({
|
||||
proxy: { server: `localhost:${server.PORT}`, username: 'user2', password: 'secret2' }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
@ -225,7 +206,7 @@ it('should isolate proxy credentials between contexts', async ({contextOptions,
|
||||
}
|
||||
});
|
||||
|
||||
it('should exclude patterns', async ({contextOptions, server, browserName, headful}) => {
|
||||
it('should exclude patterns', async ({contextFactory, server, browserName, headful}) => {
|
||||
it.fixme(browserName === 'chromium' && headful, 'Chromium headful crashes with CHECK(!in_frame_tree_) in RenderFrameImpl::OnDeleteFrame.');
|
||||
|
||||
server.setRoute('/target.html', async (req, res) => {
|
||||
@ -235,8 +216,7 @@ it('should exclude patterns', async ({contextOptions, server, browserName, headf
|
||||
// that resolves everything to some weird search results page.
|
||||
//
|
||||
// @see https://gist.github.com/CollinChaffin/24f6c9652efb3d6d5ef2f5502720ef00
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
const context = await contextFactory({
|
||||
proxy: { server: `localhost:${server.PORT}`, bypass: '1.non.existent.domain.for.the.test, 2.non.existent.domain.for.the.test, .another.test' }
|
||||
});
|
||||
|
||||
@ -267,9 +247,8 @@ it('should exclude patterns', async ({contextOptions, server, browserName, headf
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should use socks proxy', async ({ contextOptions, socksPort }) => {
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
it('should use socks proxy', async ({ contextFactory, socksPort }) => {
|
||||
const context = await contextFactory({
|
||||
proxy: { server: `socks5://localhost:${socksPort}` }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
@ -278,9 +257,8 @@ it('should use socks proxy', async ({ contextOptions, socksPort }) => {
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should use socks proxy in second page', async ({ contextOptions, socksPort }) => {
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
it('should use socks proxy in second page', async ({ contextFactory, socksPort }) => {
|
||||
const context = await contextFactory({
|
||||
proxy: { server: `socks5://localhost:${socksPort}` }
|
||||
});
|
||||
|
||||
@ -295,9 +273,8 @@ it('should use socks proxy in second page', async ({ contextOptions, socksPort }
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('does launch without a port', async ({ contextOptions }) => {
|
||||
const context = await browser.newContext({
|
||||
...contextOptions,
|
||||
it('does launch without a port', async ({ contextFactory }) => {
|
||||
const context = await contextFactory({
|
||||
proxy: { server: 'http://localhost' }
|
||||
});
|
||||
await context.close();
|
||||
|
||||
@ -56,11 +56,11 @@ class PlaywrightEnv {
|
||||
async beforeAll(args: CommonArgs & PlaywrightEnvOptions, workerInfo: folio.WorkerInfo): Promise<PlaywrightWorkerArgs> {
|
||||
this._browserType = args.playwright[args.browserName];
|
||||
this._browserOptions = {
|
||||
...args.launchOptions,
|
||||
_traceDir: args.traceDir,
|
||||
channel: args.browserChannel,
|
||||
headless: !args.headful,
|
||||
handleSIGINT: false,
|
||||
...args.launchOptions,
|
||||
} as any;
|
||||
return {
|
||||
browserType: this._browserType,
|
||||
@ -126,21 +126,30 @@ type BrowserTestArgs = {
|
||||
contextFactory: (options?: BrowserContextOptions) => Promise<BrowserContext>;
|
||||
};
|
||||
|
||||
type BrowserTestOptions = {
|
||||
contextOptions?: BrowserContextOptions;
|
||||
};
|
||||
|
||||
class BrowserEnv {
|
||||
private _browser: Browser | undefined;
|
||||
private _contexts: BrowserContext[] = [];
|
||||
protected _browserVersion: string;
|
||||
|
||||
hasBeforeAllOptions(options: BrowserTestOptions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
async beforeAll(args: PlaywrightWorkerArgs, workerInfo: folio.WorkerInfo) {
|
||||
this._browser = await args.browserType.launch(args.browserOptions);
|
||||
this._browserVersion = this._browser.version();
|
||||
}
|
||||
|
||||
async beforeEach(options: CommonArgs, testInfo: folio.TestInfo): Promise<BrowserTestArgs> {
|
||||
async beforeEach(options: CommonArgs & BrowserTestOptions, testInfo: folio.TestInfo): Promise<BrowserTestArgs> {
|
||||
const debugName = path.relative(testInfo.project.outputDir, testInfo.outputDir).replace(/[\/\\]/g, '-');
|
||||
const contextOptions = {
|
||||
recordVideo: options.video ? { dir: testInfo.outputPath('') } : undefined,
|
||||
_debugName: debugName,
|
||||
...options.contextOptions,
|
||||
} as BrowserContextOptions;
|
||||
|
||||
testInfo.data.browserVersion = this._browserVersion;
|
||||
|
||||
@ -14,39 +14,41 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { contextTest as it, expect } from './config/browserTest';
|
||||
import { contextTest, expect } from './config/browserTest';
|
||||
import { InMemorySnapshotter } from '../lib/server/snapshot/inMemorySnapshotter';
|
||||
import { HttpServer } from '../lib/utils/httpServer';
|
||||
import { SnapshotServer } from '../lib/server/snapshot/snapshotServer';
|
||||
|
||||
it.describe('snapshots', () => {
|
||||
let snapshotter: any;
|
||||
let httpServer: any;
|
||||
let snapshotPort: number;
|
||||
|
||||
it.skip(({ mode }) => mode !== 'default');
|
||||
|
||||
it.beforeEach(async ({ toImpl, context }, testInfo) => {
|
||||
snapshotter = new InMemorySnapshotter(toImpl(context));
|
||||
const it = contextTest.extend({
|
||||
async beforeEach({ context, toImpl, mode }, testInfo) {
|
||||
testInfo.skip(mode !== 'default');
|
||||
const snapshotter = new InMemorySnapshotter(toImpl(context));
|
||||
await snapshotter.initialize();
|
||||
httpServer = new HttpServer();
|
||||
new SnapshotServer(httpServer, snapshotter);
|
||||
snapshotPort = 11000 + testInfo.workerIndex;
|
||||
httpServer.start(snapshotPort);
|
||||
});
|
||||
this.httpServer = new HttpServer();
|
||||
new SnapshotServer(this.httpServer, snapshotter);
|
||||
const snapshotPort = 11000 + testInfo.workerIndex;
|
||||
await this.httpServer.start(snapshotPort);
|
||||
this.snapshotter = snapshotter;
|
||||
return {
|
||||
snapshotter,
|
||||
snapshotPort,
|
||||
};
|
||||
},
|
||||
|
||||
it.afterEach(async () => {
|
||||
await snapshotter.dispose();
|
||||
httpServer.stop();
|
||||
});
|
||||
async afterEach() {
|
||||
await this.snapshotter.dispose();
|
||||
await this.httpServer.stop();
|
||||
},
|
||||
});
|
||||
|
||||
it('should collect snapshot', async ({ page, toImpl }) => {
|
||||
it.describe('snapshots', () => {
|
||||
it('should collect snapshot', async ({ page, toImpl, snapshotter }) => {
|
||||
await page.setContent('<button>Hello</button>');
|
||||
const snapshot = await snapshotter.captureSnapshot(toImpl(page), 'snapshot');
|
||||
expect(distillSnapshot(snapshot)).toBe('<BUTTON>Hello</BUTTON>');
|
||||
});
|
||||
|
||||
it('should capture resources', async ({ page, toImpl, server }) => {
|
||||
it('should capture resources', async ({ page, toImpl, server, snapshotter }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.route('**/style.css', route => {
|
||||
route.fulfill({ body: 'button { color: red; }', }).catch(() => {});
|
||||
@ -58,7 +60,7 @@ it.describe('snapshots', () => {
|
||||
expect(resources[cssHref]).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should collect multiple', async ({ page, toImpl }) => {
|
||||
it('should collect multiple', async ({ page, toImpl, snapshotter }) => {
|
||||
await page.setContent('<button>Hello</button>');
|
||||
const snapshots = [];
|
||||
snapshotter.on('snapshot', snapshot => snapshots.push(snapshot));
|
||||
@ -67,7 +69,7 @@ it.describe('snapshots', () => {
|
||||
expect(snapshots.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should respect inline CSSOM change', async ({ page, toImpl }) => {
|
||||
it('should respect inline CSSOM change', async ({ page, toImpl, snapshotter }) => {
|
||||
await page.setContent('<style>button { color: red; }</style><button>Hello</button>');
|
||||
const snapshot1 = await snapshotter.captureSnapshot(toImpl(page), 'snapshot1');
|
||||
expect(distillSnapshot(snapshot1)).toBe('<style>button { color: red; }</style><BUTTON>Hello</BUTTON>');
|
||||
@ -77,7 +79,7 @@ it.describe('snapshots', () => {
|
||||
expect(distillSnapshot(snapshot2)).toBe('<style>button { color: blue; }</style><BUTTON>Hello</BUTTON>');
|
||||
});
|
||||
|
||||
it('should respect subresource CSSOM change', async ({ page, server, toImpl }) => {
|
||||
it('should respect subresource CSSOM change', async ({ page, server, toImpl, snapshotter }) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
await page.route('**/style.css', route => {
|
||||
route.fulfill({ body: 'button { color: red; }', }).catch(() => {});
|
||||
@ -95,7 +97,7 @@ it.describe('snapshots', () => {
|
||||
expect(snapshotter.resourceContent(sha1).toString()).toBe('button { color: blue; }');
|
||||
});
|
||||
|
||||
it('should capture iframe', async ({ page, contextFactory, server, toImpl, browserName }) => {
|
||||
it('should capture iframe', async ({ page, contextFactory, server, toImpl, browserName, snapshotter, snapshotPort }) => {
|
||||
it.skip(browserName === 'firefox');
|
||||
|
||||
await page.route('**/empty.html', route => {
|
||||
@ -136,7 +138,7 @@ it.describe('snapshots', () => {
|
||||
expect(await button.textContent()).toBe('Hello iframe');
|
||||
});
|
||||
|
||||
it('should capture snapshot target', async ({ page, toImpl }) => {
|
||||
it('should capture snapshot target', async ({ page, toImpl, snapshotter }) => {
|
||||
await page.setContent('<button>Hello</button><button>World</button>');
|
||||
{
|
||||
const handle = await page.$('text=Hello');
|
||||
@ -150,7 +152,7 @@ it.describe('snapshots', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should collect on attribute change', async ({ page, toImpl }) => {
|
||||
it('should collect on attribute change', async ({ page, toImpl, snapshotter }) => {
|
||||
await page.setContent('<button>Hello</button>');
|
||||
{
|
||||
const snapshot = await snapshotter.captureSnapshot(toImpl(page), 'snapshot');
|
||||
|
||||
@ -14,19 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { browserTest, expect } from './config/browserTest';
|
||||
import { contextTest as it, expect } from './config/browserTest';
|
||||
import { ElementHandle } from '../index';
|
||||
import type { ServerResponse } from 'http';
|
||||
|
||||
const it = browserTest.extend({
|
||||
async beforeEach({ browser }) {
|
||||
this.page = await browser.newPage({ hasTouch: true });
|
||||
return { page: this.page };
|
||||
},
|
||||
async afterEach() {
|
||||
await this.page.close();
|
||||
}
|
||||
});
|
||||
it.useOptions({ contextOptions: { hasTouch: true } });
|
||||
|
||||
it('should send all of the correct events', async ({ page }) => {
|
||||
await page.setContent(`
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user