Issue 1:
System info

Playwright Version: ^1.34.3

Operating System: macOs 13.2.1

Browser: runner doesn't even start

Other info:

Source code

I provided exact source code that allows reproducing the issue locally.

Link to the GitHub repository with the repro

internal repo, cannot provide link

or

Config file

Just the one provided with install steps, no tinkering involved

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// require('dotenv').config();

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: [['html', { open: 'never' }]],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://127.0.0.1:3000',

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},

/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},

{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},

{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},

/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },

/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ..devices['Desktop Chrome'], channel: 'chrome' },
// },
],

/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});

Test file (self-contained)

So, I've got an application server that can be started with async function. This function always starts server on a random free port (and I'd rather keep it that way) and returns an application object, that I can use to get exact application url.

I've made a fixture for this that looks like that

// tests/fixtures/application.ts
import { startCli } from '@corporate/test-integration';
import { WorkerFixture } from '@playwright/test';

export type App = ReturnType<typeof startCli> extends Promise<infer U>
? U
: never;

export type AppArg = {
application: App;
};

export const application: [
WorkerFixture<App, {}>,
{ scope: 'worker'; timeout: number }
] = [
async (_: {}, use: (arg: App) => Promise<void>) => {
const app = await startCli('appname');
await use(app);
await app.close();
},
{ scope: 'worker' as const, timeout: 120000 },
];

Then, I'd like to block all unmocked requests, which is done by the next fixture

// tests/fixtures/endpoints/disable-network.ts
import { TestFixture, PlaywrightTestArgs } from '@playwright/test';
import { AppArg } from '../application';
// those are just (url: Url) => boolean to make matching routes less verbose
import { matchAll, originIs } from '../../utils/route-matchers';

export type DisableNetworkArg = { disableNetwork: void };

export const disableNetwork: [
TestFixture<void, PlaywrightTestArgs & AppArg>,
{ scope: 'test'; auto: true }
] = [
async ({ page, application }, use) => {
const isAppOrigin = originIs(application.serverUrl);
await page.route(matchAll, (route) => route.abort());
await page.route(isAppOrigin, (route) => route.continue());
await use();
await page.unroute(isAppOrigin);
await page.unroute(matchAll);
},
{
scope: 'test',
auto: true,
},
];

Next, I'd like to have base url for api application defined as option, that's where next fixture option comes

// tests/fixtures/endpoints/motherapi-url.ts
export type MotherapiUrlOptionArg = { motherapiUrl: string };

export const motherapiUrl: [string, { scope: 'test'; option: true }] = [
'https://corporate/api',
{ scope: 'test', option: true },
];

Finally, I'd like to reduce test verbosity by adding specific ways to mock some urls using next fixture

// tests/fixtures/endpoints/motherapi-session-authorize.ts
import { TestFixture, PlaywrightTestArgs } from '@playwright/test';
import { combineMatchers, pathIs, qsHas } from '../../utils/route-matchers';
import { MotherapiUrlOptionArg } from './motherapi-url';

type MotherapiSessionAuthorizeMock = {
iframe: {
success: () => Promise<void>;
fail: () => Promise<void>;
};
fullscreen: {
unmock: () => Promise<void>;
};
};

export type MotherapiSessionAuthorizeArg = {
motherapiSessionAuthorize: MotherapiSessionAuthorizeMock;
};

export const motherapiSessionAuthorize: [
TestFixture<
MotherapiSessionAuthorizeMock,
PlaywrightTestArgs & MotherapiUrlOptionArg
>,
{ scope: 'test' }
] = [
async ({ page, motherapiUrl }, use) => {
const iframeUrlMatcher = combineMatchers(
pathIs(`${motherapiUrl}/session/authorize`),
qsHas({ prompt: 'none' })
);
const fullscreenUrlMatcher = combineMatchers(
pathIs(`${motherapiUrl}/session/authorize`),
qsHas({ prompt: 'none' })
);
const mock = {
iframe: {
success: async () => {
await page.route(iframeUrlMatcher, (route) => {
route.fulfill({ /** authorized response */});
});
},
fail: async () => {
await page.route(iframeUrlMatcher, (route) => {
route.fulfill({ /** unauthorized response */});
});
},
},
fullscreen: {
unmock: async () => {
await page.route(fullscreenUrlMatcher, (route) => {
route.continue();
});
},
},
};
await use(mock);
},
{ scope: 'test' },
];

So, those 4 fixtures have something that looks like a dependency graph

application does not depend on anything

disableNetwork depends on application

motherapiUrl does not depend on anything

motherapiSessionAuthorize depends on both application and motherapiUrl

I'm trying to extend base test function with the following way:

// tests/fixtures/index.ts
import { test as base } from '@playwright/test';
import { application, AppArg } from './application';
import { disableNetwork, DisableNetworkArg } from './endpoints/disable-network';
import { motherapiUrl, MotherapiUrlOptionArg } from './endpoints/motherapi-url';
import {
motherapiSessionAuthorize,
MotherapiSessionAuthorizeArg,
} from './endpoints/motherapi-session-authorize';

export const test = base
.extend<MotherapiUrlOptionArg, AppArg>({
application,
motherapiUrl,
})
.extend<DisableNetworkArg>({
disableNetwork,
})
.extend<MotherapiSessionAuthorizeArg>({
motherapiSessionAuthorize,
});

and, finally, using it in a single test like that

// tests/example.spec.ts
import { test } from './fixtures';

test.describe('kek', () => {
test('lol', async ({ page, application, motherapiSessionAuthorize }) => {
await motherapiSessionAuthorize.iframe.fail();
await motherapiSessionAuthorize.fullscreen.unmock();
await page.goto(`${application.serverUrl}/auth/login`);
await page.waitForRequest(`${application.serverUrl}/some-page/`);
});
});

and it comes to:

Steps

npx playwright test

Expected

test running

Actual

First argument must use the object destructuring pattern: _

at fixtures/index.ts:11

9 |
10 | export const test = base
> 11 | .extend<MotherapiUrlOptionArg, AppArg>({
| ^
12 | application,
13 | motherapiUrl,
14 | })

To open last HTML report run:

npx playwright show-report

Commentary

I've tried a few ways to separate fixture declaration in different files, tried to come up with builders that overcome extend's poor type declarations (sorry for the rant, but I've had too much bad time trying to normalize fixture declaration and make it modular - can't provide a way to help with that, yet, but I'll skim source files if I'd have time and will come back with propositions to improve typing if I'll ever find one) - everything fails. And when I've come to the last resort of what's in the issue and it didn't work - I've came here.

Right now I'd like to get an idea of what I'm doing wrong and how to get a running test environment with described fixtures and dependencies between them

Issue 2:
Hello,

I noticed that playwright does not have the simulation for mouse wheel user action. It is sometimes required, for example when you want to test the mouse wheel behavior in certain inputs (which are usually numeric and have increase/decrease buttons).

We are currently using Puppeteer, which provides this method: https://pptr.dev/#?product=Puppeteer&version=v9.1.1&show=api-mousewheeloptions

We are considering switching to Playwright, so would be great it will provide feature parity.

Thank you, Nickolay