<h3 dir="auto">System info</h3>
<ul dir="auto">
<li>Playwright Version: [v1.36.1]</li>
<li>Operating System: [Windows 10]</li>
<li>Browser: [Chromium, Firefox, WebKit]</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" checked=""> I provided exact source code that allows reproducing the issue locally.</li>
</ul>
<p dir="auto"><strong>Config file</strong></p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import { defineConfig, devices } from '@playwright/test';
import dotenv from 'dotenv';
dotenv.config();

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',
  use: {
    trace: 'on-first-retry',
  },

  projects: [
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
      },
    },
  ],
});"><pre class="notranslate"><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-k">import</span> <span class="pl-s1">dotenv</span> <span class="pl-k">from</span> <span class="pl-s">'dotenv'</span><span class="pl-kos">;</span>
<span class="pl-s1">dotenv</span><span class="pl-kos">.</span><span class="pl-en">config</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">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-c1">fullyParallel</span>: <span class="pl-c1">true</span><span class="pl-kos">,</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-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-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-c1">reporter</span>: <span class="pl-s">'html'</span><span class="pl-kos">,</span>
  <span class="pl-c1">use</span>: <span class="pl-kos">{</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-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-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"><strong>Test file (self-contained)</strong></p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import {test, expect} from '@playwright/test';

const check = async(page) =&gt; {
    await expect(page.getByText(String(null))).toHaveCount(0);
    await expect(page.getByText(String(undefined))).toHaveCount(0);
    await expect(page.getByText(String(0), {exact: true})).toHaveCount(0);
}

test('function', async ({ page }) =&gt; {
    await page.goto('https://example.com');
    await expect(page.locator(&quot;body &gt; div &gt; h1&quot;)).toHaveText(&quot;Example Domain&quot;);
    check(page);
});

test('direct call', async ({ page }) =&gt; {
    await page.goto('https://example.com');
    await expect(page.locator(&quot;body &gt; div &gt; h1&quot;)).toHaveText(&quot;Example Domain&quot;);

    await expect(page.getByText(String(null))).toHaveCount(0);
    await expect(page.getByText(String(undefined))).toHaveCount(0);
    await expect(page.getByText(String(0), {exact: true})).toHaveCount(0);
});"><pre class="notranslate"><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-s1">expect</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">const</span> <span class="pl-en">check</span> <span class="pl-c1">=</span> <span class="pl-k">async</span><span class="pl-kos">(</span><span class="pl-s1">page</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-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">getByText</span><span class="pl-kos">(</span><span class="pl-v">String</span><span class="pl-kos">(</span><span class="pl-c1">null</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">toHaveCount</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">await</span> <span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">getByText</span><span class="pl-kos">(</span><span class="pl-v">String</span><span class="pl-kos">(</span><span class="pl-c1">undefined</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">toHaveCount</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">await</span> <span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">getByText</span><span class="pl-kos">(</span><span class="pl-v">String</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-c1">exact</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-en">toHaveCount</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span>

<span class="pl-en">test</span><span class="pl-kos">(</span><span class="pl-s">'function'</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><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">goto</span><span class="pl-kos">(</span><span class="pl-s">'https://example.com'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">await</span> <span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">locator</span><span class="pl-kos">(</span><span class="pl-s">"body &gt; div &gt; h1"</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">toHaveText</span><span class="pl-kos">(</span><span class="pl-s">"Example Domain"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-en">check</span><span class="pl-kos">(</span><span class="pl-s1">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-en">test</span><span class="pl-kos">(</span><span class="pl-s">'direct call'</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><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">goto</span><span class="pl-kos">(</span><span class="pl-s">'https://example.com'</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">await</span> <span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">locator</span><span class="pl-kos">(</span><span class="pl-s">"body &gt; div &gt; h1"</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">toHaveText</span><span class="pl-kos">(</span><span class="pl-s">"Example Domain"</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

    <span class="pl-k">await</span> <span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">getByText</span><span class="pl-kos">(</span><span class="pl-v">String</span><span class="pl-kos">(</span><span class="pl-c1">null</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">toHaveCount</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">await</span> <span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">getByText</span><span class="pl-kos">(</span><span class="pl-v">String</span><span class="pl-kos">(</span><span class="pl-c1">undefined</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">toHaveCount</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">await</span> <span class="pl-en">expect</span><span class="pl-kos">(</span><span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">getByText</span><span class="pl-kos">(</span><span class="pl-v">String</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-kos">{</span><span class="pl-c1">exact</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-en">toHaveCount</span><span class="pl-kos">(</span><span class="pl-c1">0</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"><strong>Steps</strong></p>
<ul dir="auto">
<li>Run <code class="notranslate">playwright test --ui</code></li>
<li>Run <code class="notranslate">playwright test</code></li>
</ul>
<p dir="auto"><strong>Expected</strong></p>
<p dir="auto">The test results are consistent between UI and CI</p>
<p dir="auto"><strong>Actual</strong></p>
<p dir="auto">the <code class="notranslate">function</code> test fails in CI test.</p>
<p dir="auto"><a target="_blank" rel="noopener noreferrer" href="https://user-images.githubusercontent.com/16105352/254278433-55cc3f93-c80e-434d-8433-0a39fd5bb9f6.png"><img src="https://user-images.githubusercontent.com/16105352/254278433-55cc3f93-c80e-434d-8433-0a39fd5bb9f6.png" alt="image" style="max-width: 100%;"></a></p>
<h3 dir="auto">System info</h3>
<ul dir="auto">
<li>Playwright Version: v1.31.2</li>
<li>Operating System: Ubuntu 22.04 LTS</li>
<li>Browser: All</li>
<li>Other info:</li>
</ul>
<p dir="auto">This may simply need to be clarified in the docs but it would also be good to know if this is possible.</p>
<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" checked=""> I provided exact source code that allows reproducing the issue locally.</li>
</ul>
<p dir="auto"><strong>Test file (self-contained)</strong></p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import { Page, test } from '@playwright/test';

test('test intercept request ws', async ({ page }) =&gt; {
  // doesn't work, no wss traffic is ever intercepted
  await page.route('wss://**', async (route, request) =&gt; {
    console.log('Routing all wss traffic via page: ' + request.url());
    const response = await route.fetch();
    route.fulfill({ response });
  });

  // also doesn't work, no ws traffic is ever intercepted
  await page.context().route('ws://**', async (route, request) =&gt; {
    console.log('Routing all ws traffic via context: ' + request.url());
    const response = await route.fetch();
    route.fulfill({ response });
  });

  // await request.get('ws://127.0.0.1:3030/ws');
  // await request.get('ws://127.0.0.1:3030');
  // await request.get('wss://127.0.0.1:3030');
  await request.get('wss://127.0.0.1:3030/ws');
});"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-smi">Page</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">'@playwright/test'</span><span class="pl-kos">;</span>

<span class="pl-en">test</span><span class="pl-kos">(</span><span class="pl-s">'test intercept request ws'</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><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
  <span class="pl-c">// doesn't work, no wss traffic is ever intercepted</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-s">'wss://**'</span><span class="pl-kos">,</span> <span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-s1">route</span><span class="pl-kos">,</span> <span class="pl-s1">request</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s">'Routing all wss traffic via page: '</span> <span class="pl-c1">+</span> <span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-en">url</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">response</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-s1">route</span><span class="pl-kos">.</span><span class="pl-en">fetch</span><span class="pl-kos">(</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-en">fulfill</span><span class="pl-kos">(</span><span class="pl-kos">{</span> response <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-c">// also doesn't work, no ws traffic is ever intercepted</span>
  <span class="pl-k">await</span> <span class="pl-s1">page</span><span class="pl-kos">.</span><span class="pl-en">context</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-en">route</span><span class="pl-kos">(</span><span class="pl-s">'ws://**'</span><span class="pl-kos">,</span> <span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-s1">route</span><span class="pl-kos">,</span> <span class="pl-s1">request</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s">'Routing all ws traffic via context: '</span> <span class="pl-c1">+</span> <span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-en">url</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">response</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-s1">route</span><span class="pl-kos">.</span><span class="pl-en">fetch</span><span class="pl-kos">(</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-en">fulfill</span><span class="pl-kos">(</span><span class="pl-kos">{</span> response <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-c">// await request.get('ws://127.0.0.1:3030/ws');</span>
  <span class="pl-c">// await request.get('ws://127.0.0.1:3030');</span>
  <span class="pl-c">// await request.get('wss://127.0.0.1:3030');</span>
  <span class="pl-k">await</span> <span class="pl-s1">request</span><span class="pl-kos">.</span><span class="pl-en">get</span><span class="pl-kos">(</span><span class="pl-s">'wss://127.0.0.1:3030/ws'</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"><strong>Steps</strong></p>
<ul dir="auto">
<li>Run the test</li>
</ul>
<p dir="auto"><strong>Expected</strong></p>
<p dir="auto">The router should pickup requests made to the web socket server URL.</p>
<p dir="auto"><strong>Actual</strong></p>
<p dir="auto">The router never picks up the requests. I would like to route requests away from a remote web socket serve to a locally hosted one.</p>
<p dir="auto">I believe this bug is sort of related to <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="1632730271" data-permission-text="Title is private" data-url="https://github.com/microsoft/playwright/issues/21816" data-hovercard-type="issue" data-hovercard-url="/microsoft/playwright/issues/21816/hovercard" href="https://github.com/microsoft/playwright/issues/21816">#21816</a> as well as the full WebSocket routing open feature request <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="746657874" data-permission-text="Title is private" data-url="https://github.com/microsoft/playwright/issues/4488" data-hovercard-type="issue" data-hovercard-url="/microsoft/playwright/issues/4488/hovercard" href="https://github.com/microsoft/playwright/issues/4488">#4488</a> -- I was hoping for a way to simply redirect the GET request to the WebSocket server URL with <code class="notranslate">route()</code> -- low hanging fruit compared to the full message mocking and intercepting I think is required to delivery the feature request.</p>