Closed Meemaw closed 2 years ago
@Meemaw if I understand correctly, substituting default context
fixture with the one from the persistent context will do the trick for you. Since page
fixture depends on context
fixture, the default page
will be created in the persistent context.
Here's an example:
import { test as base, expect, chromium, firefox, webkit } from '@playwright/test';
import path from 'path';
export const test = base.extend({
context: async ({ browserName }, use) => {
const browserTypes = {chromium, firefox, webkit};
const context = await browserTypes[browserName].launchPersistentContext(path.join(__dirname, 'ppp'), {});
await use(context);
await context.close();
},
});
test.only('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');
});
Does it help?
@aslushnikov found this shortly after I posted the question. It works, but I have a follow up question. What is the scope of context
? Is this being executed for each test, or can it be changed to per worker scope?
@Meemaw context
fixture is a "test" fixture, meaning that it'll be recreated for every test. If you want to re-use the context you have a few options:
globalContext
worker fixture and override page
fixture to depend on it.The second approach looks like this:
import { test as base, expect, chromium, firefox, webkit } from '@playwright/test';
import path from 'path';
export const test = base.extend({
globalContext: [async ({ browserName }, use) => {
const browserTypes = {chromium, firefox, webkit};
const context = await browserTypes[browserName].launchPersistentContext(path.join(__dirname, 'ppp'), {});
await use(context);
await context.close();
}, { scope: 'worker'}],
page: async ({ globalContext }, use) => {
const page = await globalContext.newPage();
await use(page);
await page.close();
},
});
test.only('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');
});
Do note though that profile directory has to be unique per worker, so don't hardcode it's name and rather generate one randomly.
@aslushnikov thanks for the feedback. I'm having some issues with the "globalContext":
There seems to be some Typescript issues with scope: "worker"
:
Is "globalContext" somehow special global fixture? I tried something similar with my own fixture and was expecting it to have a same value across tests on a single worker:
export const test = base.extend<{ workerId: string }>({
workerId: [
async ({}, use) => {
const workerId = Math.random().toString(36).substring(2, 7)
await use(workerId)
},
{ scope: "worker" },
],
context: async ({ browserName, workerId }, use, { title }) => {
console.log(workerId) // this is different between tests even on same worker
...someCode
await use(context)
},
})
I would like something like parallelIndex
that is stable across tests, but is random every time we invoke tests.
- There seems to be some Typescript issues with scope: "worker":
@Meemaw for the first, the following works for me with typescript:
import { test as base, expect, chromium, firefox, webkit, BrowserContext } from '@playwright/test';
import path from 'path';
type WorkerContextFixture = {
globalContext: BrowserContext,
}
export const test = base.extend<{}, WorkerContextFixture>({
globalContext: [async ({ browserName, }, use, info) => {
const browserTypes = {chromium, firefox, webkit};
const userDataDir = path.join(__dirname, 'profile-' + info.workerIndex);
const context = await browserTypes[browserName].launchPersistentContext(userDataDir, {});
await use(context);
await context.close();
}, { scope: 'worker'}],
page: async ({ globalContext }, use) => {
const page = await globalContext.newPage();
await use(page);
await page.close();
},
});
test('basic test', async ({ page }) => {
await page.goto('https://playwright.dev/');
});
test('basic test 2 ', async ({ page }) => {
await page.goto('https://playwright.dev/');
});
- Is "globalContext" somehow special global fixture? I tried something similar with my own fixture and was expecting it to have a same value across tests on a single worker:
This should work. Two things though:
base.extend
should look like this - base.extend<{}, { workerId: string }>
, this way it'll compiletest
in all subfiles.If it still doesn't work, would you mind sharing some code?
@aslushnikov that works. Thanks for the help, things make sense now 👍
I have 1 final question that is somehow related to Chrome Extensions:
I saw some issues related to this (e.g https://github.com/microsoft/playwright/issues/5586), so wondering how hard would it be to add support for something like that?
Is there a way/workaround to click on the extension icon in the tabbar? I have 1 extension (Kaikas) that doesn't play well if not triggered like this. They even have a screenshoot
@Meemaw this is tracked separately at https://github.com/microsoft/playwright/issues/5593. Please upvote!
Given how popular the issue is, it's likely to appear sometime in future!
@aslushnikov I'm doing this exact same thing, with the addition of needing to set storage state on that persistent context. How would you recommend I do it?
I tried this but this isn't working though –
export const test = base.extend({
context: async ({ browserName, }, use) => {
const browserTypes = { chromium, firefox, webkit }
const extensionPath = '/some/path/'
const context = await browserTypes[browserName].launchPersistentContext('/some/dir', {
headless: false,
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
]
})
await use({
...context,
storageState: () => Promise.resolve({/* storage state */}),
})
await context.close()
},
})
@aslushnikov This doesn't work either (after having read https://playwright.dev/docs/test-fixtures#overriding-fixtures) –
export const test = base.extend({
storageState: async ({}, use) => {
console.log('[INFO] Extending storage state')
const customStorageState = { /* storage state */ }
await use(customStorageState)
},
context: async ({ browserName, }, use) => {
console.log('[INFO] Extending browser context')
const browserTypes = { chromium, firefox, webkit }
const extensionPath = '/some/path/'
const context = await browserTypes[browserName].launchPersistentContext('/some/dir', {
headless: false,
args: [
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`,
]
})
await use(context)
await context.close()
},
})
Curiously, when I use this custom test
method, I do see the [INFO] Extending browser context
line being printed, but not [INFO] Extending storage state
.
Hi.
We're exploring Playwright as our E2E test runner. One of our main requirements is ability to communicate with chrome extensions (e.g. Metamask). I'm working on a proof of concept with
@playwright/test
where I successfully setupMetaMask
usingchromium.launchPersistentContext
, but facing some issues.After the MetaMask setup, we would want that
window.ethereum
to get populated/injected by the extension in the default test page/context (one passed in by thetest
fixture). I assume this doesn't work because@playwright/test
creates an isolated context for each test, and that context is completely unrelated to the one created viachromium.launchPersistentContext
.If my understanding is correct, we would either need to somehow link those 2 contexts (probably not possible/doesn't make sense), or have an option to launch extensions in the text context. To make this performant, we would probably only want to do this once for all tests.
Any help/pointers would be much appreciated.