playwright icon indicating copy to clipboard operation
playwright copied to clipboard

[Feature] Allow to set launchPersistentContext in playwright.config file

Open serembon opened this issue 3 years ago • 26 comments

Note from maintainers

If you are here for Chrome Extensions testing, take a look at this guide.


On one of our projects, we need to run tests with extensions. To launch extensions, we have to set browserType.launchPersistentContext in the beforeAll section of each test.

It would be convenient to set launchPersistentContext in a playwright.config file, for instance:

    use: {
        launchPersistentContext: {
            userDataDir: '/tmp/test-user-data-dir',
            args: [
                `--disable-extensions-except=${path.join(__dirname, './my-extension')}`,
                `--load-extension=${path.join(__dirname, './my-extension')}`
            ]
        }
     }

serembon avatar Feb 03 '22 12:02 serembon

Yesterday, I also started an attempt to use Playwright Test for writing E2E tests for a Chrome Extension. But because the browser (by default) is launched in incognito, extensions don't work.

It would be very helpful if it was made possible to define the browser(s) to launch with a persistent context. And/or in a more generic way, if it was possible to add a way to use a (factory) function that returns a browser instance upon invoking. Something like:

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

const config: PlaywrightTestConfig = {
  projects: [
    {
      name: 'chromium-from-a-factory-fn',
      use: () => { // returns a browser instance when invoked },
    },
  ],
};
export default config;

@serembon I would be interested in how you set the browserType.launchPersistentContext in the beforeAll section of each test. Could you show an example?

schmkr avatar Jun 02 '22 12:06 schmkr

@schmkr Extension should be unpacked. Here is an example:

test.describe('Open Chrome with loaded extension', () => {
    let context: BrowserContext;
    let page: Page;

    test.beforeAll(async () => {
        context = await chromium.launchPersistentContext('/tmp/test-user-data', {
            headless: false,
            channel: 'chrome',
            args: [
                `--disable-extensions-except=${path.join(__dirname, '../files/my-extenstion')}`,
                `--load-extension=${path.join(__dirname, '../files/my-extenstion')}`,
            ],
        });
        page = await context.newPage();
    });

    test('Open extensions page', async () => {
        await page.goto('chrome://extensions/');
        await page.pause();
    });
});

serembon avatar Jun 02 '22 12:06 serembon

This would be a major improvement for us, we've been watching the numerous requests on this topic as it would unlock the opportunity to use Playwright for our needs, where we can't at the moment.

adamjlow avatar Jun 02 '22 15:06 adamjlow

Have a similar issue but a different problem) I want to implement playwright component testing in my team, and playwright gives us excellent developer experience. But there is one missing point for me. When I debug my test, the browser opens in incognito mode, and I can not open vue devtools to debug my component.

AlexanderMykulych avatar Jun 06 '22 15:06 AlexanderMykulych

This would be a huge feature for the project I am working on as well. The ability to globally not be in incognito mode is mission critical.

Bparsons0904 avatar Jun 23 '22 19:06 Bparsons0904

I would like to:

--

Here's the solution I've found that should help.

We can add an option to the Playwright configuration file using the "option" fixtures. This allows us to declare what extensions to load in the configuration file once.

The page, context, and browser provided to each test are called test fixtures. Using test.extend we can create a new test object that modifies the context, calling launchPersistentContext and loading the extensions. Any tests that want to use the Chrome extensions must import test from a test fixture, otherwise the extensions are not loaded.

--

Let's start with the configuration file. The chromeExtensions "option" fixture allows us to set the optional userDataDir and paths to the extensions to load:

playwright.config.ts

import { join } from 'path';
import type { PlaywrightTestConfig } from '@playwright/test';
import { ChromeExtensionOptions } from './tests/fixtures/chrome-extension';

const config: PlaywrightTestConfig<ChromeExtensionOptions> = {
//                                 ^
//                                 Provide types for the "option" fixture
  projects: [
    {
      use: {
        browserName: 'chromium',
        chromeExtensions: {
          paths: [join(__dirname, 'example-extension')],
        },
        // ^
        // Provide the paths to one or more extensions. On MacOS, spaces in paths are handled OK.
      },
    },
  ],
};

export default config;

--

The test fixture extends the test object to add the chromeExtensions option and modifies the context to start the browser with the expected extensions in headed mode:

tests/fixtures/chrome-extension.ts

import { test as base, chromium } from "@playwright/test";

export type ChromeExtensionOptions = {
  chromeExtensions: {
    paths: Array<string>;
    userDataDir?: string;
  };
};

export const test = base.extend<ChromeExtensionOptions>({
  chromeExtensions: [{ paths: [] }, { option: true }],
  context: [
    async ({ chromeExtensions }, use) => {
      const { paths, userDataDir = "" } = chromeExtensions;
      const launchOptions = {
        headless: false,
        args:
          paths.length === 0
            ? []
            : [
                `--disable-extensions-except=${paths.join(",")}`,
                ...paths.map((path) => `--load-extension=${path}`),
              ],
      };
      const context = await chromium.launchPersistentContext(
        userDataDir,
        launchOptions
      );
      await use(context);
      await context.close();
    },
    { scope: "test", timeout: 2_500 },
    //                        ^
    //                        This is my preference. The default 30s timeout is too long to wait if there is a manifest issue.
  ],
});

--

To write a test using the Chrome extensions, import test from the test fixture:

tests/example.spec.ts

- import { test, expect } from '@playwright/test';
+ import { expect } from '@playwright/test';
+ import { test } from './fixtures/chrome-extension';

  test('The Chrome extension should be visible on the Extensions page', async ({ page }) => {

seanpoulter avatar Jul 11 '22 02:07 seanpoulter

Feature request reached 65 likes. Any chance of being included in upcoming releases?

serembon avatar Jul 29 '22 08:07 serembon

Everybody: there's now a new doc for chrome extension testing fixture: https://playwright.dev/docs/next/chrome-extensions#testing

Does it solve all your issues? Let us know if not!

aslushnikov avatar Aug 19 '22 16:08 aslushnikov

I need this feature not only for extensions testing, but mainly to run browser in non-incognito mode (without browser setup in tests). I really hope it'll be implemented soon.

bormando avatar Oct 06 '22 15:10 bormando

@schmkr Extension should be unpacked. Here is an example:

test.describe('Open Chrome with loaded extension', () => {
    let context: BrowserContext;
    let page: Page;

    test.beforeAll(async () => {
        context = await chromium.launchPersistentContext('/tmp/test-user-data', {
            headless: false,
            channel: 'chrome',
            args: [
                `--disable-extensions-except=${path.join(__dirname, '../files/my-extenstion')}`,
                `--load-extension=${path.join(__dirname, '../files/my-extenstion')}`,
            ],
        });
        page = await context.newPage();
    });

    test('Open extensions page', async () => {
        await page.goto('chrome://extensions/');
        await page.pause();
    });
});

In my case I'm able to launch and open the extension in chrome tab, but when I try to hit my website (google.com), the google.com page loads first and then the extension page loads, therefore I'm not able to switch back to the google.com page and work on it. Somehow the extension page blocks. How did you swicth back to the previous page after loading extension? Here;s my code :
import { test as base, expect, chromium } from '@playwright/test'; import path from 'path';

const extensionPath = path.join(__dirname, '../Myextensions/nkbihfbeogaeaoehlefnkodbefgpgknn/10.23.2_0') // make sure this is correct

const test = base.extend({ context: async ({ browserName }, use) => { const browserTypes = {chromium} const launchOptions = { headless: false, args: [ --disable-extensions-except=${extensionPath} ], viewport: { width: 1920, height: 1080 } } const context = await browserTypes[browserName].launchPersistentContext( '', launchOptions ) await use(context)

} })

test('Google page loads', async ({ page }) => { await page.waitForLoadState(); await page.goto( 'https://google.com/' ) // Click the button

await page.click('.btn-primary');
await page.waitForTimeout(3000) // this is here so that it won't automatically close the browser window

})

Smriti4A avatar Feb 08 '23 10:02 Smriti4A

It would be really helpful having the possibility to set launchPersistentContext in the playwright.config file.

NikkTod avatar Apr 24 '23 08:04 NikkTod

It would be really helpful having the possibility to set launchPersistentContext in the playwright.config file.

Yes this would be a big improvement. I also think it should be configurable on project level. Since projects are often used to test different user agents they might need different treatment.

slebs avatar Jun 20 '23 13:06 slebs

Agree with the comments above on incorporating a flag into the playwright.config file. We use web workers to load specific data across the application. The web worker requests are not intercepted correctly in incognito mode. Ideally a flag is made available to turn off incognito for a whole playwright project. Seems like this has been a request for over a year. Can we see some progress?

nikolas-garza-ai avatar Jul 13 '23 00:07 nikolas-garza-ai

To better help you with configuring the persistent context, we would like to know how and why you use it. Please upvote a respective comment below, or reply with your own version.

  • Using persistent context to test a Chrome extension.
  • Using persistent context to test a page with some extension installed.
  • Using persistent context for some other reason (please specify).
  • Need a separate persistent context for each test - slower but isolated.
  • Need a single persistent context for each file/project - faster but tests are not isolated.

dgozman avatar Jul 14 '23 00:07 dgozman

Please upvote this comment if you are using persistent context to test a Chrome extension.

dgozman avatar Jul 14 '23 00:07 dgozman

Please upvote this comment if you are using persistent context to test a page with some extension installed.

dgozman avatar Jul 14 '23 00:07 dgozman

Please upvote this comment if you are using persistent context for some other reason. Reply explaining your specific use case if possible.

dgozman avatar Jul 14 '23 00:07 dgozman

Please upvote this comment if you need a separate persistent context for each test - slower but isolated.

dgozman avatar Jul 14 '23 00:07 dgozman

Please upvote this comment if you need a single persistent context for each file/project - faster but tests are not isolated.

dgozman avatar Jul 14 '23 00:07 dgozman

Thank you @dgozman for looking into this. I have upvoted both "Using persistent context to test a Chrome extension." and "Using persistent context to test a page with some extension installed.", although I was not completely sure how they are different?

When you want to test a Chrome Extension, that testing happens on a page anyway, right? Or maybe I don't understand what the difference is between them?

For context, our use case is we develop a Chrome Extension that interacts with Gmail. So we want to test that extension within Gmail.

schmkr avatar Jul 14 '23 07:07 schmkr

Everybody: please vote in this poll so that we can react accordingly!

aslushnikov avatar Jul 31 '23 17:07 aslushnikov

Please upvote this comment if you are using persistent context for some other reason. Reply explaining your specific use case if possible.

I am using persistent context to download pdf files. In non persistent context if i click on the button to download the pdf then in that case the pdf is not download and instead its is opened in chrome's internal pdf viewer.

with persistent context i am able to pass the arguments which doesn't let chrome open the pdf and forces it to download it

vipinphogat91 avatar Aug 10 '23 19:08 vipinphogat91

We could use a persistent context to be able to download a pdf and save the file, which apparently isn't working in incognito mode, and then open the pdf using pdf2json to validate the contents. I'll continue trying to see why incognito mode isn't always saving the file but I suspect this is why.

England112578 avatar Aug 18 '23 18:08 England112578

Everybody: please vote in https://github.com/microsoft/playwright/issues/11833#issuecomment-1635088534 so that we can react accordingly!

aslushnikov avatar Aug 30 '23 18:08 aslushnikov

Please upvote this comment if you are using persistent context for some other reason. Reply explaining your specific use case if possible.

@dgozman Testing chrome extensions for multiple contexts (=> incognito mode). Requires changing settings at "chrome://extensions" automated. That url however can only be accessed outside of incognito:)

kaliiiiiiiiii avatar Oct 10 '23 18:10 kaliiiiiiiiii

2 years passed. Are there any plans to solve this?

serembon avatar Jan 28 '24 12:01 serembon