<h3 dir="auto">System info</h3>
<ul dir="auto">
<li>Playwright Version: ^1.34.3</li>
<li>Operating System: macOs 13.2.1</li>
<li>Browser: runner doesn't even start</li>
<li>Other info:</li>
</ul>
<h3 dir="auto">Source code</h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> I provided exact source code that allows reproducing the issue locally.</li>
</ul>




<p dir="auto"><strong>Link to the GitHub repository with the repro</strong></p>
<p dir="auto">internal repo, cannot provide link</p>
<p dir="auto">or</p>
<p dir="auto"><strong>Config file</strong></p>
<p dir="auto">Just the one provided with install steps, no tinkering involved</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// 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,
    // },
});"><pre class="notranslate"><span class="pl-c">// playwright.config.ts</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">defineConfig</span><span class="pl-kos">,</span> <span class="pl-s1">devices</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'@playwright/test'</span><span class="pl-kos">;</span>

<span class="pl-c">/**</span>
<span class="pl-c"> * Read environment variables from file.</span>
<span class="pl-c"> * https://github.com/motdotla/dotenv</span>
<span class="pl-c"> */</span>
<span class="pl-c">// require('dotenv').config();</span>

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

        <span class="pl-c">/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */</span>
        <span class="pl-c1">trace</span>: <span class="pl-s">'on-first-retry'</span><span class="pl-kos">,</span>
    <span class="pl-kos">}</span><span class="pl-kos">,</span>

    <span class="pl-c">/* Configure projects for major browsers */</span>
    <span class="pl-c1">projects</span>: <span class="pl-kos">[</span>
        <span class="pl-kos">{</span>
            <span class="pl-c1">name</span>: <span class="pl-s">'chromium'</span><span class="pl-kos">,</span>
            <span class="pl-c1">use</span>: <span class="pl-kos">{</span> ...<span class="pl-s1">devices</span><span class="pl-kos">[</span><span class="pl-s">'Desktop Chrome'</span><span class="pl-kos">]</span> <span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-kos">}</span><span class="pl-kos">,</span>

        <span class="pl-kos">{</span>
            <span class="pl-c1">name</span>: <span class="pl-s">'firefox'</span><span class="pl-kos">,</span>
            <span class="pl-c1">use</span>: <span class="pl-kos">{</span> ...<span class="pl-s1">devices</span><span class="pl-kos">[</span><span class="pl-s">'Desktop Firefox'</span><span class="pl-kos">]</span> <span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-kos">}</span><span class="pl-kos">,</span>

        <span class="pl-kos">{</span>
            <span class="pl-c1">name</span>: <span class="pl-s">'webkit'</span><span class="pl-kos">,</span>
            <span class="pl-c1">use</span>: <span class="pl-kos">{</span> ...<span class="pl-s1">devices</span><span class="pl-kos">[</span><span class="pl-s">'Desktop Safari'</span><span class="pl-kos">]</span> <span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-kos">}</span><span class="pl-kos">,</span>

        <span class="pl-c">/* Test against mobile viewports. */</span>
        <span class="pl-c">// {</span>
        <span class="pl-c">//   name: 'Mobile Chrome',</span>
        <span class="pl-c">//   use: { ...devices['Pixel 5'] },</span>
        <span class="pl-c">// },</span>
        <span class="pl-c">// {</span>
        <span class="pl-c">//   name: 'Mobile Safari',</span>
        <span class="pl-c">//   use: { ...devices['iPhone 12'] },</span>
        <span class="pl-c">// },</span>

        <span class="pl-c">/* Test against branded browsers. */</span>
        <span class="pl-c">// {</span>
        <span class="pl-c">//   name: 'Microsoft Edge',</span>
        <span class="pl-c">//   use: { ...devices['Desktop Edge'], channel: 'msedge' },</span>
        <span class="pl-c">// },</span>
        <span class="pl-c">// {</span>
        <span class="pl-c">//   name: 'Google Chrome',</span>
        <span class="pl-c">//   use: { ..devices['Desktop Chrome'], channel: 'chrome' },</span>
        <span class="pl-c">// },</span>
    <span class="pl-kos">]</span><span class="pl-kos">,</span>

    <span class="pl-c">/* Run your local dev server before starting the tests */</span>
    <span class="pl-c">// webServer: {</span>
    <span class="pl-c">//   command: 'npm run start',</span>
    <span class="pl-c">//   url: 'http://127.0.0.1:3000',</span>
    <span class="pl-c">//   reuseExistingServer: !process.env.CI,</span>
    <span class="pl-c">// },</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto"><strong>Test file (self-contained)</strong></p>
<p dir="auto">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.</p>
<p dir="auto">I've made a fixture for this that looks like that</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// tests/fixtures/application.ts
import { startCli } from '@corporate/test-integration';
import { WorkerFixture } from '@playwright/test';

export type App = ReturnType&lt;typeof startCli&gt; extends Promise&lt;infer U&gt;
    ? U
    : never;

export type AppArg = {
    application: App;
};

export const application: [
    WorkerFixture&lt;App, {}&gt;,
    { scope: 'worker'; timeout: number }
] = [
    async (_: {}, use: (arg: App) =&gt; Promise&lt;void&gt;) =&gt; {
        const app = await startCli('appname');
        await use(app);
        await app.close();
    },
    { scope: 'worker' as const, timeout: 120000 },
];"><pre class="notranslate"><span class="pl-c">// tests/fixtures/application.ts</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">startCli</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'@corporate/test-integration'</span><span class="pl-kos">;</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-smi">WorkerFixture</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'@playwright/test'</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">type</span> <span class="pl-smi">App</span> <span class="pl-c1">=</span> <span class="pl-smi">ReturnType</span><span class="pl-kos">&lt;</span><span class="pl-k">typeof</span> <span class="pl-s1">startCli</span><span class="pl-kos">&gt;</span> <span class="pl-k">extends</span> <span class="pl-smi">Promise</span><span class="pl-kos">&lt;</span>infer <span class="pl-smi">U</span><span class="pl-kos">&gt;</span>
    ? <span class="pl-smi">U</span>
    : <span class="pl-smi">never</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">type</span> <span class="pl-smi">AppArg</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span>
    <span class="pl-c1">application</span>: <span class="pl-smi">App</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-s1">application</span>: <span class="pl-kos">[</span>
    <span class="pl-smi">WorkerFixture</span><span class="pl-kos">&lt;</span><span class="pl-smi">App</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">&gt;</span><span class="pl-kos">,</span>
    <span class="pl-kos">{</span> <span class="pl-c1">scope</span>: <span class="pl-s">'worker'</span><span class="pl-kos">;</span> <span class="pl-c1">timeout</span>: <span class="pl-smi">number</span> <span class="pl-kos">}</span>
<span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-kos">[</span>
    <span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-s1">_</span>: <span class="pl-kos">{</span><span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-s1">use</span>: <span class="pl-kos">(</span><span class="pl-s1">arg</span>: <span class="pl-smi">App</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi">Promise</span><span class="pl-kos">&lt;</span><span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">&gt;</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
        <span class="pl-k">const</span> <span class="pl-s1">app</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-en">startCli</span><span class="pl-kos">(</span><span class="pl-s">'appname'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">await</span> <span class="pl-en">use</span><span class="pl-kos">(</span><span class="pl-s1">app</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">await</span> <span class="pl-s1">app</span><span class="pl-kos">.</span><span class="pl-en">close</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span><span class="pl-kos">,</span>
    <span class="pl-kos">{</span> <span class="pl-c1">scope</span>: <span class="pl-s">'worker'</span> <span class="pl-k">as</span> <span class="pl-smi">const</span><span class="pl-kos">,</span> <span class="pl-c1">timeout</span>: <span class="pl-c1">120000</span> <span class="pl-kos">}</span><span class="pl-kos">,</span>
<span class="pl-kos">]</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">Then, I'd like to block all unmocked requests, which is done by the next fixture</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// tests/fixtures/endpoints/disable-network.ts
import { TestFixture, PlaywrightTestArgs } from '@playwright/test';
import { AppArg } from '../application';
// those are just (url: Url) =&gt; boolean to make matching routes less verbose
import { matchAll, originIs } from '../../utils/route-matchers';

export type DisableNetworkArg = { disableNetwork: void };

export const disableNetwork: [
    TestFixture&lt;void, PlaywrightTestArgs &amp; AppArg&gt;,
    { scope: 'test'; auto: true }
] = [
    async ({ page, application }, use) =&gt; {
        const isAppOrigin = originIs(application.serverUrl);
        await page.route(matchAll, (route) =&gt; route.abort());
        await page.route(isAppOrigin, (route) =&gt; route.continue());
        await use();
        await page.unroute(isAppOrigin);
        await page.unroute(matchAll);
    },
    {
        scope: 'test',
        auto: true,
    },
];"><pre class="notranslate"><span class="pl-c">// tests/fixtures/endpoints/disable-network.ts</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-smi">TestFixture</span><span class="pl-kos">,</span> <span class="pl-smi">PlaywrightTestArgs</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'@playwright/test'</span><span class="pl-kos">;</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-smi">AppArg</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'../application'</span><span class="pl-kos">;</span>
<span class="pl-c">// those are just (url: Url) =&gt; boolean to make matching routes less verbose</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">matchAll</span><span class="pl-kos">,</span> <span class="pl-s1">originIs</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'../../utils/route-matchers'</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">type</span> <span class="pl-smi">DisableNetworkArg</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-c1">disableNetwork</span>: <span class="pl-smi"><span class="pl-k">void</span></span> <span class="pl-kos">}</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-s1">disableNetwork</span>: <span class="pl-kos">[</span>
    <span class="pl-smi">TestFixture</span><span class="pl-kos">&lt;</span><span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">,</span> <span class="pl-smi">PlaywrightTestArgs</span> <span class="pl-c1">&amp;</span> <span class="pl-smi">AppArg</span><span class="pl-kos">&gt;</span><span class="pl-kos">,</span>
    <span class="pl-kos">{</span> <span class="pl-c1">scope</span>: <span class="pl-s">'test'</span><span class="pl-kos">;</span> <span class="pl-c1">auto</span>: <span class="pl-c1">true</span> <span class="pl-kos">}</span>
<span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-kos">[</span>
    <span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-kos">{</span> page<span class="pl-kos">,</span> application <span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-s1">use</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
        <span class="pl-k">const</span> <span class="pl-s1">isAppOrigin</span> <span class="pl-c1">=</span> <span class="pl-en">originIs</span><span class="pl-kos">(</span><span class="pl-s1">application</span><span class="pl-kos">.</span><span class="pl-c1">serverUrl</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">await</span> <span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">route</span><span class="pl-kos">(</span><span class="pl-s1">matchAll</span><span class="pl-kos">,</span> <span class="pl-kos">(</span><span class="pl-s1">route</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-s1">route</span><span class="pl-kos">.</span><span class="pl-en">abort</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">await</span> <span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">route</span><span class="pl-kos">(</span><span class="pl-s1">isAppOrigin</span><span class="pl-kos">,</span> <span class="pl-kos">(</span><span class="pl-s1">route</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-s1">route</span><span class="pl-kos">.</span><span class="pl-en">continue</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">await</span> <span class="pl-en">use</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">await</span> <span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">unroute</span><span class="pl-kos">(</span><span class="pl-s1">isAppOrigin</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">await</span> <span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">unroute</span><span class="pl-kos">(</span><span class="pl-s1">matchAll</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span><span class="pl-kos">,</span>
    <span class="pl-kos">{</span>
        <span class="pl-c1">scope</span>: <span class="pl-s">'test'</span><span class="pl-kos">,</span>
        <span class="pl-c1">auto</span>: <span class="pl-c1">true</span><span class="pl-kos">,</span>
    <span class="pl-kos">}</span><span class="pl-kos">,</span>
<span class="pl-kos">]</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">Next, I'd like to have base url for api application defined as option, that's where next fixture option comes</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// 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 },
];"><pre class="notranslate"><span class="pl-c">// tests/fixtures/endpoints/motherapi-url.ts</span>
<span class="pl-k">export</span> <span class="pl-k">type</span> <span class="pl-smi">MotherapiUrlOptionArg</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-c1">motherapiUrl</span>: <span class="pl-smi">string</span> <span class="pl-kos">}</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-s1">motherapiUrl</span>: <span class="pl-kos">[</span><span class="pl-smi">string</span><span class="pl-kos">,</span> <span class="pl-kos">{</span> <span class="pl-c1">scope</span>: <span class="pl-s">'test'</span><span class="pl-kos">;</span> <span class="pl-c1">option</span>: <span class="pl-c1">true</span> <span class="pl-kos">}</span><span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-kos">[</span>
    <span class="pl-s">'https://corporate/api'</span><span class="pl-kos">,</span>
    <span class="pl-kos">{</span> <span class="pl-c1">scope</span>: <span class="pl-s">'test'</span><span class="pl-kos">,</span> <span class="pl-c1">option</span>: <span class="pl-c1">true</span> <span class="pl-kos">}</span><span class="pl-kos">,</span>
<span class="pl-kos">]</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">Finally, I'd like to reduce test verbosity by adding specific ways to mock some urls using next fixture</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// 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: () =&gt; Promise&lt;void&gt;;
        fail: () =&gt; Promise&lt;void&gt;;
    };
    fullscreen: {
        unmock: () =&gt; Promise&lt;void&gt;;
    };
};

export type MotherapiSessionAuthorizeArg = {
    motherapiSessionAuthorize: MotherapiSessionAuthorizeMock;
};

export const motherapiSessionAuthorize: [
    TestFixture&lt;
        MotherapiSessionAuthorizeMock,
        PlaywrightTestArgs &amp; MotherapiUrlOptionArg
    &gt;,
    { scope: 'test' }
] = [
    async ({ page, motherapiUrl }, use) =&gt; {
        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 () =&gt; {
                    await page.route(iframeUrlMatcher, (route) =&gt; {
                        route.fulfill({ /** authorized response */});
                    });
                },
                fail: async () =&gt; {
                    await page.route(iframeUrlMatcher, (route) =&gt; {
                        route.fulfill({ /** unauthorized response */});
                    });
                },
            },
            fullscreen: {
                unmock: async () =&gt; {
                    await page.route(fullscreenUrlMatcher, (route) =&gt; {
                        route.continue();
                    });
                },
            },
        };
        await use(mock);
    },
    { scope: 'test' },
];"><pre class="notranslate"><span class="pl-c">// tests/fixtures/endpoints/motherapi-session-authorize.ts</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-smi">TestFixture</span><span class="pl-kos">,</span> <span class="pl-smi">PlaywrightTestArgs</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'@playwright/test'</span><span class="pl-kos">;</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">combineMatchers</span><span class="pl-kos">,</span> <span class="pl-s1">pathIs</span><span class="pl-kos">,</span> <span class="pl-s1">qsHas</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'../../utils/route-matchers'</span><span class="pl-kos">;</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-smi">MotherapiUrlOptionArg</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'./motherapi-url'</span><span class="pl-kos">;</span>

<span class="pl-k">type</span> <span class="pl-smi">MotherapiSessionAuthorizeMock</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span>
    <span class="pl-c1">iframe</span>: <span class="pl-kos">{</span>
        <span class="pl-c1">success</span>: <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi">Promise</span><span class="pl-kos">&lt;</span><span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">&gt;</span><span class="pl-kos">;</span>
        <span class="pl-c1">fail</span>: <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi">Promise</span><span class="pl-kos">&lt;</span><span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">&gt;</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span><span class="pl-kos">;</span>
    <span class="pl-c1">fullscreen</span>: <span class="pl-kos">{</span>
        <span class="pl-c1">unmock</span>: <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-smi">Promise</span><span class="pl-kos">&lt;</span><span class="pl-smi"><span class="pl-k">void</span></span><span class="pl-kos">&gt;</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">type</span> <span class="pl-smi">MotherapiSessionAuthorizeArg</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span>
    <span class="pl-c1">motherapiSessionAuthorize</span>: <span class="pl-smi">MotherapiSessionAuthorizeMock</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-s1">motherapiSessionAuthorize</span>: <span class="pl-kos">[</span>
    <span class="pl-smi">TestFixture</span><span class="pl-kos">&lt;</span>
        <span class="pl-smi">MotherapiSessionAuthorizeMock</span><span class="pl-kos">,</span>
        <span class="pl-smi">PlaywrightTestArgs</span> <span class="pl-c1">&amp;</span> <span class="pl-smi">MotherapiUrlOptionArg</span>
    <span class="pl-kos">&gt;</span><span class="pl-kos">,</span>
    <span class="pl-kos">{</span> <span class="pl-c1">scope</span>: <span class="pl-s">'test'</span> <span class="pl-kos">}</span>
<span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-kos">[</span>
    <span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-kos">{</span> page<span class="pl-kos">,</span> motherapiUrl <span class="pl-kos">}</span><span class="pl-kos">,</span> <span class="pl-s1">use</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
        <span class="pl-k">const</span> <span class="pl-s1">iframeUrlMatcher</span> <span class="pl-c1">=</span> <span class="pl-en">combineMatchers</span><span class="pl-kos">(</span>
            <span class="pl-en">pathIs</span><span class="pl-kos">(</span><span class="pl-s">`<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">motherapiUrl</span><span class="pl-kos">}</span></span>/session/authorize`</span><span class="pl-kos">)</span><span class="pl-kos">,</span>
            <span class="pl-en">qsHas</span><span class="pl-kos">(</span><span class="pl-kos">{</span> <span class="pl-c1">prompt</span>: <span class="pl-s">'none'</span> <span class="pl-kos">}</span><span class="pl-kos">)</span>
        <span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">const</span> <span class="pl-s1">fullscreenUrlMatcher</span> <span class="pl-c1">=</span> <span class="pl-en">combineMatchers</span><span class="pl-kos">(</span>
            <span class="pl-en">pathIs</span><span class="pl-kos">(</span><span class="pl-s">`<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">motherapiUrl</span><span class="pl-kos">}</span></span>/session/authorize`</span><span class="pl-kos">)</span><span class="pl-kos">,</span>
            <span class="pl-en">qsHas</span><span class="pl-kos">(</span><span class="pl-kos">{</span> <span class="pl-c1">prompt</span>: <span class="pl-s">'none'</span> <span class="pl-kos">}</span><span class="pl-kos">)</span>
        <span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">const</span> <span class="pl-s1">mock</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span>
            <span class="pl-c1">iframe</span>: <span class="pl-kos">{</span>
                <span class="pl-en">success</span>: <span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
                    <span class="pl-k">await</span> <span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">route</span><span class="pl-kos">(</span><span class="pl-s1">iframeUrlMatcher</span><span class="pl-kos">,</span> <span class="pl-kos">(</span><span class="pl-s1">route</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
                        <span class="pl-s1">route</span><span class="pl-kos">.</span><span class="pl-en">fulfill</span><span class="pl-kos">(</span><span class="pl-kos">{</span> <span class="pl-c">/** authorized response */</span><span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
                    <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
                <span class="pl-kos">}</span><span class="pl-kos">,</span>
                <span class="pl-en">fail</span>: <span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
                    <span class="pl-k">await</span> <span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">route</span><span class="pl-kos">(</span><span class="pl-s1">iframeUrlMatcher</span><span class="pl-kos">,</span> <span class="pl-kos">(</span><span class="pl-s1">route</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
                        <span class="pl-s1">route</span><span class="pl-kos">.</span><span class="pl-en">fulfill</span><span class="pl-kos">(</span><span class="pl-kos">{</span> <span class="pl-c">/** unauthorized response */</span><span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
                    <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
                <span class="pl-kos">}</span><span class="pl-kos">,</span>
            <span class="pl-kos">}</span><span class="pl-kos">,</span>
            <span class="pl-c1">fullscreen</span>: <span class="pl-kos">{</span>
                <span class="pl-en">unmock</span>: <span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
                    <span class="pl-k">await</span> <span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">route</span><span class="pl-kos">(</span><span class="pl-s1">fullscreenUrlMatcher</span><span class="pl-kos">,</span> <span class="pl-kos">(</span><span class="pl-s1">route</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
                        <span class="pl-s1">route</span><span class="pl-kos">.</span><span class="pl-en">continue</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
                    <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
                <span class="pl-kos">}</span><span class="pl-kos">,</span>
            <span class="pl-kos">}</span><span class="pl-kos">,</span>
        <span class="pl-kos">}</span><span class="pl-kos">;</span>
        <span class="pl-k">await</span> <span class="pl-en">use</span><span class="pl-kos">(</span><span class="pl-s1">mock</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span><span class="pl-kos">,</span>
    <span class="pl-kos">{</span> <span class="pl-c1">scope</span>: <span class="pl-s">'test'</span> <span class="pl-kos">}</span><span class="pl-kos">,</span>
<span class="pl-kos">]</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">So, those 4 fixtures have something that looks like a dependency graph</p>
<ul dir="auto">
<li><code class="notranslate">application</code> does not depend on anything</li>
<li><code class="notranslate">disableNetwork</code> depends on <code class="notranslate">application</code></li>
<li><code class="notranslate">motherapiUrl</code> does not depend on anything</li>
<li><code class="notranslate">motherapiSessionAuthorize</code> depends on both <code class="notranslate">application</code> and <code class="notranslate">motherapiUrl</code></li>
</ul>
<p dir="auto">I'm trying to extend base test function with the following way:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// 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&lt;MotherapiUrlOptionArg, AppArg&gt;({
        application,
        motherapiUrl,
    })
    .extend&lt;DisableNetworkArg&gt;({
        disableNetwork,
    })
    .extend&lt;MotherapiSessionAuthorizeArg&gt;({
        motherapiSessionAuthorize,
    });"><pre class="notranslate"><span class="pl-c">// tests/fixtures/index.ts</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">test</span> <span class="pl-k">as</span> <span class="pl-s1">base</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'@playwright/test'</span><span class="pl-kos">;</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">application</span><span class="pl-kos">,</span> <span class="pl-smi">AppArg</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'./application'</span><span class="pl-kos">;</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">disableNetwork</span><span class="pl-kos">,</span> <span class="pl-smi">DisableNetworkArg</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'./endpoints/disable-network'</span><span class="pl-kos">;</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">motherapiUrl</span><span class="pl-kos">,</span> <span class="pl-smi">MotherapiUrlOptionArg</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'./endpoints/motherapi-url'</span><span class="pl-kos">;</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span>
    <span class="pl-s1">motherapiSessionAuthorize</span><span class="pl-kos">,</span>
    <span class="pl-smi">MotherapiSessionAuthorizeArg</span><span class="pl-kos">,</span>
<span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'./endpoints/motherapi-session-authorize'</span><span class="pl-kos">;</span>

<span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-s1">test</span> <span class="pl-c1">=</span> <span class="pl-s1">base</span>
    <span class="pl-kos">.</span><span class="pl-en">extend</span><span class="pl-kos">&lt;</span><span class="pl-smi">MotherapiUrlOptionArg</span><span class="pl-kos">,</span> <span class="pl-smi">AppArg</span><span class="pl-kos">&gt;</span><span class="pl-kos">(</span><span class="pl-kos">{</span>
        application<span class="pl-kos">,</span>
        motherapiUrl<span class="pl-kos">,</span>
    <span class="pl-kos">}</span><span class="pl-kos">)</span>
    <span class="pl-kos">.</span><span class="pl-en">extend</span><span class="pl-kos">&lt;</span><span class="pl-smi">DisableNetworkArg</span><span class="pl-kos">&gt;</span><span class="pl-kos">(</span><span class="pl-kos">{</span>
        disableNetwork<span class="pl-kos">,</span>
    <span class="pl-kos">}</span><span class="pl-kos">)</span>
    <span class="pl-kos">.</span><span class="pl-en">extend</span><span class="pl-kos">&lt;</span><span class="pl-smi">MotherapiSessionAuthorizeArg</span><span class="pl-kos">&gt;</span><span class="pl-kos">(</span><span class="pl-kos">{</span>
        motherapiSessionAuthorize<span class="pl-kos">,</span>
    <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">and, finally, using it in a single test like that</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// tests/example.spec.ts
import { test } from './fixtures';

test.describe('kek', () =&gt; {
    test('lol', async ({ page, application, motherapiSessionAuthorize }) =&gt; {
        await motherapiSessionAuthorize.iframe.fail();
        await motherapiSessionAuthorize.fullscreen.unmock();
        await page.goto(`${application.serverUrl}/auth/login`);
        await page.waitForRequest(`${application.serverUrl}/some-page/`);
    });
});"><pre class="notranslate"><span class="pl-c">// tests/example.spec.ts</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">test</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'./fixtures'</span><span class="pl-kos">;</span>

<span class="pl-s1">test</span><span class="pl-kos">.</span><span class="pl-en">describe</span><span class="pl-kos">(</span><span class="pl-s">'kek'</span><span class="pl-kos">,</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-en">test</span><span class="pl-kos">(</span><span class="pl-s">'lol'</span><span class="pl-kos">,</span> <span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-kos">{</span> page<span class="pl-kos">,</span> application<span class="pl-kos">,</span> motherapiSessionAuthorize <span class="pl-kos">}</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
        <span class="pl-k">await</span> <span class="pl-s1">motherapiSessionAuthorize</span><span class="pl-kos">.</span><span class="pl-c1">iframe</span><span class="pl-kos">.</span><span class="pl-en">fail</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">await</span> <span class="pl-s1">motherapiSessionAuthorize</span><span class="pl-kos">.</span><span class="pl-c1">fullscreen</span><span class="pl-kos">.</span><span class="pl-en">unmock</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">await</span> <span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">goto</span><span class="pl-kos">(</span><span class="pl-s">`<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">application</span><span class="pl-kos">.</span><span class="pl-c1">serverUrl</span><span class="pl-kos">}</span></span>/auth/login`</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">await</span> <span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">waitForRequest</span><span class="pl-kos">(</span><span class="pl-s">`<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">application</span><span class="pl-kos">.</span><span class="pl-c1">serverUrl</span><span class="pl-kos">}</span></span>/some-page/`</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">and it comes to:</p>
<p dir="auto"><strong>Steps</strong></p>
<ul dir="auto">
<li><code class="notranslate">npx playwright test</code></li>
</ul>
<p dir="auto"><strong>Expected</strong></p>
<p dir="auto">test running</p>
<p dir="auto"><strong>Actual</strong></p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="First argument must use the object destructuring pattern: _

   at fixtures/index.ts:11

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


To open last HTML report run:

  npx playwright show-report"><pre class="notranslate"><code class="notranslate">First argument must use the object destructuring pattern: _

   at fixtures/index.ts:11

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


To open last HTML report run:

  npx playwright show-report
</code></pre></div>
<p dir="auto"><strong>Commentary</strong></p>
<p dir="auto">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.</p>
<p dir="auto">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</p>
<p dir="auto">Hello,</p>
<p dir="auto">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).</p>
<p dir="auto">We are currently using Puppeteer, which provides this method: <a href="https://pptr.dev/#?product=Puppeteer&amp;version=v9.1.1&amp;show=api-mousewheeloptions" rel="nofollow">https://pptr.dev/#?product=Puppeteer&amp;version=v9.1.1&amp;show=api-mousewheeloptions</a></p>
<p dir="auto">We are considering switching to Playwright, so would be great it will provide feature parity.</p>
<p dir="auto">Thank you, Nickolay</p>