-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature] Set storage state when using persistent context #14949
Comments
@Mosorathia If you are launching the persistent context, you can set up the user data dir to your liking by using the browser with that user data dir first. |
@dgozman I want to set the local storage on launch of the browser at run time.. I don't want to set it prior to the execution.. |
There is not yet, marking it as a feature request by that. |
I also want to vote for this. I have following scenario:
Would be really helpful to have an ability to use/set storage state in persistent context. |
Another vote, I desperately need a way to deal with both MFA and extensions. |
This comment was marked as spam.
This comment was marked as spam.
Recently I've observed that even on launching a browser context using |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
A workaround to set cookies for the persistent context: // Get cookies from some other context, for example the one you were authenticating in.
const cookies = await someOtherContext.cookies();
// Alternatively, retrieve cookies from a saved storage state.
const cookies = require('path/to/storageState.json').cookies;
....
// Now launch persistent context and add cookies.
const context = await chromium.launchPersistentContext('path/to/user/data/dir');
await context.addCookies(cookies);
// Persistent context has cookies and is ready to use.
const page = context.pages()[0];
await page.goto('https://playwright.dev'); |
but bro loading cookies is only half of what loadStorage does, loadStorage or whatever is the function name, loads local storage as well under "Origins". How to do this programatically? |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
Are you making any improvement or work on this, please? |
Any news? |
Another vote. Extensions require persistent context. // test/tests/e2e/fixtures.ts
import { test as base, chromium, type BrowserContext } from '@playwright/test';
import path from 'path';
export const test = base.extend<{
context: BrowserContext;
extensionId: string;
}>({
context: async ({ }, use) => {
const pathToExtension = path.join(__dirname, '../../../src');
const context = await chromium.launchPersistentContext('', {
headless: false,
args: [
`--headless=new`,
`--disable-extensions-except=${pathToExtension}`,
`--load-extension=${pathToExtension}`,
],
});
await use(context);
await context.close();
},
extensionId: async ({ context }, use) => {
// for manifest v3:
let [background] = context.serviceWorkers();
if (!background)
background = await context.waitForEvent('serviceworker');
const extensionId = background.url().split('/')[2];
await use(extensionId);
},
});
export const expect = test.expect; // test/playwright.config.ts
import { defineConfig, devices } from '@playwright/test'
import dotenv from 'dotenv'
import path from 'path';
dotenv.config({ path: path.resolve(__dirname, 'environment', '.env.production') });
export default defineConfig({
// Folder for all tests files
testDir: 'tests/e2e',
// Folder for test artifacts such as screenshots, videos, traces, etc.
outputDir: 'test_results',
// // path to the global setup files.
// globalSetup: require.resolve('./global-setup'),
// // path to the global teardown files.
// globalTeardown: require.resolve('./global-teardown'),
// Each test timeout [msec]
timeout: 5000,
// Fail the build on CI if you accidentally left test.only in the source code.
forbidOnly: !!process.env.CI,
// Opt out of parallel tests on CI.
workers: process.env.CI ? 1 : undefined,
use: {
// Maximum time each action such as `click()` can take. Defaults to 0 (no limit).
actionTimeout: 0,
// Name of the browser that runs tests. For example `chromium`, `firefox`, `webkit`.
browserName: 'chromium',
// Toggles bypassing Content-Security-Policy.
bypassCSP: false,
// Channel to use, for example "chrome", "chrome-beta", "msedge", "msedge-beta".
channel: 'chrome',
// Run browser in headless mode.
headless: false,
},
projects: [
{
name: 'LoginSetup',
testMatch: 'login.setup.spec.ts',
testDir: 'tests/e2e/login_setup',
retries: 0
},
{
name: 'LoggedIn',
testDir: 'tests/e2e/logged_in',
dependencies: ['LoginSetup'],
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/user.json',
}
},
{
name: 'LoggedOut',
testDir: 'tests/e2e/logged_out'
}
]
},
); // test/tests/e2e/login_setup/login.setup.spec.ts
import { type Page, type BrowserContext } from '@playwright/test';
import { test, expect } from '../fixtures';
const authFile = 'playwright/.auth/user.json';
const popupUrl = (extensionId: string) => `chrome-extension:https://${extensionId}/popup/popup.html`
const server_base_url = process.env.SERVER_URL
// first call to server may take much longer before it warms up,
// so we set a longer timeout for this specific test
test.setTimeout(10000)
test('popup login', async ({ page, context, extensionId }) => {
const loginPage = await openLoginPage(page, context, extensionId)
expect(await loginPage.url()).toBe(`${server_base_url}/login`)
const dashboardPage = await login(loginPage)
expect(await dashboardPage.url()).toBe(`${server_base_url}/`)
await page.context().storageState({ path: authFile });
});
async function login(loginPage: Page): Promise<Page> {
const username = process.env.LOGIN_USERNAME
const password = process.env.LOGIN_PASS
await loginPage.locator('input[type=email]').fill(username)
await loginPage.locator('input[type=password]').fill(password)
const loginButtonOnLoginPage = await loginPage.getByRole('button').filter({hasText: 'Login'})
await loginButtonOnLoginPage.click()
await loginPage.waitForURL(server_base_url);
return loginPage
}
async function openLoginPage(page: Page, context: BrowserContext, extensionId: string): Promise<Page> {
await page.goto(popupUrl(extensionId))
const loginButton = page.getByRole('button').filter({hasText: 'Login'})
// Start waiting for new page before clicking. Note no await.
const pagePromise = context.waitForEvent('page');
await loginButton.click()
const loginPage = await pagePromise;
await loginPage.waitForLoadState();
return loginPage
} // test/tests/e2e/logged_in/popup.spec.ts
import { test, expect } from '../fixtures';
const popupUrl = (extensionId: string) => `chrome-extension:https://${extensionId}/popup/popup.html`
test('popup page', async ({ page, extensionId, context }) => {
await page.goto(popupUrl(extensionId))
const state = await context.storageState()
// Fails here because there is no cookies in the context storage state:
expect(state.cookies.length).toBeGreaterThan(0) // <---- Fails !!! |
Another up-vote for this feature request. |
Another vote for extensions + cookie authentication. @airbender-1 is there any workaround to inject a storageState to a running context? |
Another vote, we need this as well |
It is a very important feature. I need it to create an authorization setup for extension tests. Upvote! |
Another upvote. This is a particularly helpful feature for testing extensions |
@tg44 referencing your comment My workaround was injecting cookies from file (json) import { type BrowserContext } from '@playwright/test';
import path from 'path';
import fs from 'fs';
import { storageStateRelativePath } from './test_constants'
import { test as base } from './fixtures-incognito';
// see: https://github.com/microsoft/playwright/issues/26693
process.env.PW_CHROMIUM_ATTACH_TO_OTHER = "1";
export const test = base.extend<{
context: BrowserContext;
extensionId: string;
}>({
context: async ({ context }, use) => {
// Patch [BUG](https://github.com/microsoft/playwright/issues/14949)
// manually inject saved storageState if the state file is found
// 1. cookies
const cookies = loadCookies()
if (cookies) await context.addCookies(cookies);
// 2. localStorage - TODO
const state = await context.storageState()
expect(state.cookies.length).toBeGreaterThan(0)
await use(context);
await context.close();
},
});
type Cookies = Parameters<BrowserContext['addCookies']>[0]
function loadCookies(): Cookies | null {
const authFile = path.resolve(__dirname, '..', '..', storageStateRelativePath)
if (!fs.existsSync(authFile)) return null
const cookies: Cookies = require(authFile).cookies
return cookies
}
export const expect = test.expect; authFile is just a json with default hard-coded value for cookie of specific user. {
"cookies": [
{
"name": "mycookie-name",
"value": "ab123...",
"domain": ".app.mydomain",
"path": "/",
"expires": 1708554480.933069,
"httpOnly": true,
"secure": true,
"sameSite": "Lax"
}
],
"origins": []
} Here is the fixture-incognito that the above "test" fixture depends on: import { test as base, chromium, type BrowserContext } from '@playwright/test';
import path from 'path';
export const test = base.extend<{
context: BrowserContext;
extensionId: string;
}>({
context: async ({ }, use) => {
const pathToExtension = path.join(__dirname, '../../../src');
const context = await chromium.launchPersistentContext('', {
headless: false,
args: [
`--headless=new`,
`--disable-extensions-except=${pathToExtension}`,
`--load-extension=${pathToExtension}`,
],
// slowMo: 2000
});
await use(context);
await context.close();
},
extensionId: async ({ context }, use) => {
// for manifest v3:
let [background] = context.serviceWorkers();
if (!background)
background = await context.waitForEvent('serviceworker');
const extensionId = background.url().split('/')[2];
await use(extensionId);
},
});
export const expect = test.expect; |
Rather than mess around with loading and injecting the cookies (which was insufficient for us since we also needed to set up local storage and browser extension storage), what we did was to reuse the profile from an initial project that logs the test user in. We duplicate it for every new test context and it comes loaded with the needed auth cookies and local storage for our tests to do their thing. See: https://github.com/pixiebrix/pixiebrix-extension/blob/main/end-to-end-tests/fixtures/authSetup.ts#L78-L86 |
I want to set Local storage for a browser but open it in non-incognito mode..
Here i am able to set localstorage but only in incognito mode.. But i want to open browser in Non-Incognito mode so i am launching it with persistent context..
browserContext=chromium.launchPersistentContext(Paths.get("C:\\Users\\mohib.s\\AppData\\Local\\Google\\Chrome\\User Data"), new BrowserType.LaunchPersistentContextOptions().setChannel("chrome").setHeadless(false));
How can i set the session storage for browser opened with persistent context.. Can you please help me with this
The text was updated successfully, but these errors were encountered: