<p dir="auto">It would be great if we could load a chrome extension only once and reuse the same persistent context across multiple tests.  Right now, if I load the chrome extension in <code class="notranslate">global-setup.js</code>, I am unable to get the chromium instance that contains the extension.  I need to test communication between a dApp and a browser extension, so the dApp and the extension should share the same browser instance.</p>
<p dir="auto">Currently, if follow the <a href="https://playwright.dev/docs/chrome-extensions" rel="nofollow">docs</a>, each time I run a new test, a new chromium instance launches with the persistent context.</p>
<p dir="auto">I'm trying to test a relatively heavy chrome extension, so I would prefer to load the chrome extension globally and then re-use the context in each of my subsequent tests.</p>
<p dir="auto">As a hacky work around, I tried to defer a promise like so</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// global-setup.js
import { chromium } from '@playwright/test'
import testConfig from './config'

let context

let contextIsResolved
const contextIsLoaded = new Promise(
  (resolve) =&gt; (contextIsResolved = resolve)
)

export default async (config) =&gt; {
  const pathToExtension = testConfig.browserExtensionPath
  context = await chromium.launchPersistentContext('', {
    headless: false,
    args: [
      `--disable-extensions-except=${pathToExtension}`,
      `--load-extension=${pathToExtension}`,
    ],
  })
  contextIsResolved(context)
  await context.waitForEvent('page')
  // extension is after the blank ghost page
  // see issue https://github.com/microsoft/playwright-python/issues/689
  const extensionPage = context.pages()[1]
}

export const getGlobalContext = async () =&gt; {
  await contextIsLoaded
  return context
}

// some-test.spec.js
import { getGlobalContext } from '../global-setup'

test.beforeAll(async ({ context }) =&gt; {
  const context = await getGlobalContext() // this just hangs forever
})"><pre class="notranslate"><span class="pl-c">// global-setup.js</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">chromium</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'@playwright/test'</span>
<span class="pl-k">import</span> <span class="pl-s1">testConfig</span> <span class="pl-k">from</span> <span class="pl-s">'./config'</span>

<span class="pl-k">let</span> <span class="pl-s1">context</span>

<span class="pl-k">let</span> <span class="pl-s1">contextIsResolved</span>
<span class="pl-k">const</span> <span class="pl-s1">contextIsLoaded</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-v">Promise</span><span class="pl-kos">(</span>
  <span class="pl-kos">(</span><span class="pl-s1">resolve</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">(</span><span class="pl-s1">contextIsResolved</span> <span class="pl-c1">=</span> <span class="pl-s1">resolve</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-k">async</span> <span class="pl-kos">(</span><span class="pl-s1">config</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">pathToExtension</span> <span class="pl-c1">=</span> <span class="pl-s1">testConfig</span><span class="pl-kos">.</span><span class="pl-c1">browserExtensionPath</span>
  <span class="pl-s1">context</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-s1">chromium</span><span class="pl-kos">.</span><span class="pl-en">launchPersistentContext</span><span class="pl-kos">(</span><span class="pl-s">''</span><span class="pl-kos">,</span> <span class="pl-kos">{</span>
    <span class="pl-c1">headless</span>: <span class="pl-c1">false</span><span class="pl-kos">,</span>
    <span class="pl-c1">args</span>: <span class="pl-kos">[</span>
      <span class="pl-s">`--disable-extensions-except=<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">pathToExtension</span><span class="pl-kos">}</span></span>`</span><span class="pl-kos">,</span>
      <span class="pl-s">`--load-extension=<span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">pathToExtension</span><span class="pl-kos">}</span></span>`</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-s1">contextIsResolved</span><span class="pl-kos">(</span><span class="pl-s1">context</span><span class="pl-kos">)</span>
  <span class="pl-k">await</span> <span class="pl-s1">context</span><span class="pl-kos">.</span><span class="pl-en">waitForEvent</span><span class="pl-kos">(</span><span class="pl-s">'page'</span><span class="pl-kos">)</span>
  <span class="pl-c">// extension is after the blank ghost page</span>
  <span class="pl-c">// see issue https://github.com/microsoft/playwright-python/issues/689</span>
  <span class="pl-k">const</span> <span class="pl-s1">extensionPage</span> <span class="pl-c1">=</span> <span class="pl-s1">context</span><span class="pl-kos">.</span><span class="pl-en">pages</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">[</span><span class="pl-c1">1</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-en">getGlobalContext</span> <span class="pl-c1">=</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">contextIsLoaded</span>
  <span class="pl-k">return</span> <span class="pl-s1">context</span>
<span class="pl-kos">}</span>

<span class="pl-c">// some-test.spec.js</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-en">getGlobalContext</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'../global-setup'</span>

<span class="pl-s1">test</span><span class="pl-kos">.</span><span class="pl-en">beforeAll</span><span class="pl-kos">(</span><span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-kos">{</span> context <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">const</span> <span class="pl-s1">context</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-en">getGlobalContext</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c">// this just hangs forever</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span></pre></div>
<p dir="auto">The issue with the above (which I thought might work) is that the promise never resolves and it hangs indefinitely. Thus, the test suite fails.</p>
<p dir="auto">I tried all tests in a single file like below</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// main.spec.js
import { test, expect } from '../fixtures/browser-extension'
import { createPageModelObject } from '../page-object-models/base-page-object-model';

let extensionPage // reference for all the tests

test.describe.configure({ mode: 'serial' });

test.beforeAll(async ({ context }) =&gt; {
  extensionPage = context.pages()[1]
})

test.describe('Test 1', () =&gt; {
  let page
  test.beforeAll(async ({ context }) =&gt; {
    page = createPageModelObject()
  })

  test('should do stuff', async () =&gt; {

  })
})

test.describe('Test 2', () =&gt; {
  let page
  test.beforeAll(async ({ context }) =&gt; {
    page = createPageModelObject()
  })

  test('should do stuff', async () =&gt; {
    
  })
})"><pre class="notranslate"><span class="pl-c">// main.spec.js</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-s1">expect</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'../fixtures/browser-extension'</span>
<span class="pl-k">import</span> <span class="pl-kos">{</span> <span class="pl-s1">createPageModelObject</span> <span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'../page-object-models/base-page-object-model'</span><span class="pl-kos">;</span>

<span class="pl-k">let</span> <span class="pl-s1">extensionPage</span> <span class="pl-c">// reference for all the tests</span>

<span class="pl-s1">test</span><span class="pl-kos">.</span><span class="pl-c1">describe</span><span class="pl-kos">.</span><span class="pl-en">configure</span><span class="pl-kos">(</span><span class="pl-kos">{</span> <span class="pl-c1">mode</span>: <span class="pl-s">'serial'</span> <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

<span class="pl-s1">test</span><span class="pl-kos">.</span><span class="pl-en">beforeAll</span><span class="pl-kos">(</span><span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-kos">{</span> context <span class="pl-kos">}</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
  <span class="pl-s1">extensionPage</span> <span class="pl-c1">=</span> <span class="pl-s1">context</span><span class="pl-kos">.</span><span class="pl-en">pages</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">[</span><span class="pl-c1">1</span><span class="pl-kos">]</span>
<span class="pl-kos">}</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">'Test 1'</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-k">let</span> <span class="pl-s1">page</span>
  <span class="pl-s1">test</span><span class="pl-kos">.</span><span class="pl-en">beforeAll</span><span class="pl-kos">(</span><span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-kos">{</span> context <span class="pl-kos">}</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-s1">page</span> <span class="pl-c1">=</span> <span class="pl-en">createPageModelObject</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">'should do stuff'</span><span class="pl-kos">,</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-kos">}</span><span class="pl-kos">)</span>
<span class="pl-kos">}</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">'Test 2'</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-k">let</span> <span class="pl-s1">page</span>
  <span class="pl-s1">test</span><span class="pl-kos">.</span><span class="pl-en">beforeAll</span><span class="pl-kos">(</span><span class="pl-k">async</span> <span class="pl-kos">(</span><span class="pl-kos">{</span> context <span class="pl-kos">}</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-kos">{</span>
    <span class="pl-s1">page</span> <span class="pl-c1">=</span> <span class="pl-en">createPageModelObject</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">'should do stuff'</span><span class="pl-kos">,</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-kos">}</span><span class="pl-kos">)</span>
<span class="pl-kos">}</span><span class="pl-kos">)</span></pre></div>
<p dir="auto">The problem with the above is that my browser tears down and setups up in each new <code class="notranslate">describe</code> + <code class="notranslate">beforeAll</code> block.  Ideally this wouldn't be the case so that I could reuse a single extension window.</p>
<p dir="auto"><strong>Context:</strong></p>
<ul dir="auto">
<li>Playwright Version: 1.18.1</li>
<li>Operating System: Windows 10</li>
<li>Node.js version: 16.13.0</li>
<li>Browser: Chromium</li>
<li>Extra: Chromium is run with <code class="notranslate">--no-sandbox</code></li>
</ul>
<p dir="auto"><strong>Describe the bug</strong></p>
<p dir="auto">When run normally, the browser is accepting the <code class="notranslate">Set-Cookie</code> header. However, when run with <code class="notranslate">show</code> set to false (i.e. headless mode), the browser ignores the <code class="notranslate">Set-Cookie</code> header.</p>
<p dir="auto">This bug differs from its tens of near-duplicates because:</p>
<ul dir="auto">
<li>It affects first-party as well as third-party cookies.</li>
<li>It's verified to only happen in headless mode.</li>
<li>It's not because of headless detection. Packet interception shows that the <code class="notranslate">Set-Cookie</code> header is being sent by the server.</li>
</ul>
<p dir="auto"><strong>Workaround</strong><br>
The magical incantation <code class="notranslate">--disable-features=ImprovedCookieControls,ImprovedCookieControlsForThirdPartyCookieBlocking,SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure</code> makes the symptoms go away, at least for right now.</p>