playwright-persona
Version:
Authentication in Playwright using personas.
77 lines (76 loc) • 3.66 kB
text/typescript
import { Page, TestFixture, TestInfo } from "@playwright/test";
//#region src/index.d.ts
type SessionValueType = string | number | Date | null | undefined | Array<SessionValueType> | {
[key: string]: SessionValueType;
};
type SessionType = Record<string, SessionValueType>;
interface PersonaOptions<Session extends SessionType> {
/**
* Create a new session for this persona.
* Returns the context object to share state across other methods.
*
* @example
* async createSession({ page }) {
* const user = await createMockUser()
* await page.goto('/login')
* await page.getByLabel('Username').fill(user.name)
* await page.getByLagbel('Password').fill(user.password)
* await page.getByRole('button', { name: 'Log in' }).click()
*
* return { user }
* }
*/
createSession: CreateSessionFunction<Session>;
/**
* Verify that the given session belongs to this persona.
* Used to determine if the authentication state stored on disk
* should still be used or a new session should be created.
*
* @example
* async verifySession({ page, session }) {
* await page.goto('/dashboard')
* await expect(page.getByText(session.user.name)).toBeVisible()
* }
*/
verifySession: VerifySessionFunction<Session>;
/**
* Destroy the given session.
* Used to properly dispose of test resources related to a stored session
* (e.g. test records in a database).
*
* @example
* async destroySession({ session }) {
* await deleteUser({ where: { id: session.user.id } })
* }
*/
destroySession?: DestroySessionFunction<Session>;
}
interface CreateSessionContext {
page: Page;
}
interface VerifySessionContext<Session extends SessionType> {
session: Session;
page: Page;
}
interface DestroySessionContext<Session extends SessionType> {
session: Session;
page: Page;
}
interface Persona<Name extends string, Session extends SessionType> {
name: Name;
createSession: CreateSessionFunction<Session>;
verifySession: VerifySessionFunction<Session>;
destroySession?: DestroySessionFunction<Session>;
}
type CreateSessionFunction<Session extends SessionType> = (context: CreateSessionContext, testInfo: TestInfo) => Promise<Session>;
type VerifySessionFunction<Session extends SessionType> = (context: VerifySessionContext<Session>, testInfo: TestInfo) => Promise<void>;
type DestroySessionFunction<Session extends SessionType> = (context: DestroySessionContext<Session>, testInfo: TestInfo) => Promise<void>;
declare function definePersona<Name extends string, Session extends SessionType>(name: Name, options: PersonaOptions<Session>): Persona<Name, Session>;
type AuthenticateFunction<Personas extends Array<Persona<any, any>>> = <Name extends ExtractPersonaNames<Personas>>(options: {
as: Name;
}) => Promise<MapPersonasToRecord<Personas>[Name]>;
type MapPersonasToRecord<Personas extends Array<Persona<any, any>>> = Personas extends Array<infer P> ? { [Name in P extends Persona<infer Name, any> ? Name : never]: P extends Persona<Name, infer Session> ? Session : never } : never;
type ExtractPersonaNames<Personas extends Array<Persona<any, any>>> = Personas extends Array<infer P> ? P extends Persona<infer Name, any> ? Name : never : never;
declare function combinePersonas<Personas extends Array<Persona<any, any>>>(...personas: Personas): TestFixture<AuthenticateFunction<Personas>, any>;
//#endregion
export { AuthenticateFunction, CreateSessionContext, CreateSessionFunction, DestroySessionContext, DestroySessionFunction, Persona, PersonaOptions, SessionType, VerifySessionContext, VerifySessionFunction, combinePersonas, definePersona };