playwright-persona
Version:
Authentication in Playwright using personas.
82 lines (80 loc) • 3 kB
JavaScript
import * as fs from "node:fs";
import * as path from "node:path";
import { invariant } from "outvariant";
//#region src/index.ts
const STORAGE_STATE_DIRECTORY = path.join(process.cwd(), "./playwright/.auth/");
function definePersona(name, options) {
return {
name,
createSession: options.createSession,
verifySession: options.verifySession
};
}
function combinePersonas(...personas) {
return async ({ browser, context, page }, use, testInfo) => {
const createSessionFilePath = (persona) => {
return path.join(STORAGE_STATE_DIRECTORY, `./${testInfo.testId}-${persona.name}.json`);
};
const createSession = async (persona) => {
const session = await persona.createSession({ page }, testInfo);
const sessionFilePath = createSessionFilePath(persona);
await context.storageState({ path: sessionFilePath });
const sessionFile = await readSessionFile(sessionFilePath);
sessionFile.session = session;
await fs.promises.writeFile(sessionFilePath, JSON.stringify(sessionFile, null, 2));
return session;
};
await use(async (options) => {
const persona = personas.find((persona$1) => {
return persona$1.name === options.as;
});
invariant(persona, "Failed to authenticate: cannot find persona by name \"%s\" (known personas: %s)", options.as, personas.join(", "));
const sessionFilePath = createSessionFilePath(persona);
if (fs.existsSync(sessionFilePath)) {
const sessionFile = await readSessionFile(sessionFilePath);
const newContext = await browser.newContext({ storageState: sessionFile });
const newPage = await newContext.newPage();
await restoreSessionState(sessionFile, newPage);
return persona.verifySession({
page: newPage,
session: sessionFile.session
}, testInfo).then(async () => {
await restoreSessionState(sessionFile, page);
return sessionFile.session;
}).catch(async () => {
await persona.destroySession?.({
page,
session: sessionFile.session
}, testInfo);
return createSession(persona);
}).finally(async () => {
await newContext.close();
});
}
return createSession(persona);
});
};
}
async function readSessionFile(filePath) {
const textContent = await fs.promises.readFile(filePath, "utf8");
return JSON.parse(textContent);
}
async function restoreSessionState(sesionFile, page) {
await page.context().addCookies(sesionFile.cookies);
if (sesionFile.origins.length > 0) {
const newPage = await page.context().newPage();
await newPage.route(/.+/, async (route) => {
await route.fulfill({ body: `<html></html>` }).catch(() => {});
});
await Promise.allSettled(sesionFile.origins.map(async (state) => {
const frame = newPage.mainFrame();
await frame.goto(state.origin);
for (const item of state.localStorage) await frame.evaluate(([key, value]) => {
localStorage.setItem(key, value);
}, [item.name, item.value]);
}));
await newPage.close();
}
}
//#endregion
export { combinePersonas, definePersona };