From 913bd5babe806b76eb2c56ec0dafeec812e866be Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Mon, 2 Mar 2026 15:07:34 +0900 Subject: [PATCH] test(cookies): add Playwright e2e test for sign-out cookie clearing (#8275) --- e2e/integration/vanilla-node/e2e/app.ts | 3 + .../e2e/cookie-cache-signout.spec.ts | 68 +++++++++++++++++++ e2e/integration/vanilla-node/e2e/utils.ts | 5 +- 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 e2e/integration/vanilla-node/e2e/cookie-cache-signout.spec.ts diff --git a/e2e/integration/vanilla-node/e2e/app.ts b/e2e/integration/vanilla-node/e2e/app.ts index 826c66f158..b8410504c7 100644 --- a/e2e/integration/vanilla-node/e2e/app.ts +++ b/e2e/integration/vanilla-node/e2e/app.ts @@ -1,11 +1,13 @@ import { createServer } from "node:http"; import { DatabaseSync } from "node:sqlite"; +import type { BetterAuthOptions } from "better-auth"; import { betterAuth } from "better-auth"; import { getMigrations } from "better-auth/db/migration"; import { toNodeHandler } from "better-auth/node"; export async function createAuthServer( baseURL: string = "http://localhost:3000", + overrides?: Partial, ) { const database = new DatabaseSync(":memory:"); @@ -20,6 +22,7 @@ export async function createAuthServer( "http://localhost:*", // Dynamic frontend port "http://test.com:*", // Cross-domain test ], + ...overrides, }); const { runMigrations } = await getMigrations(auth.options); diff --git a/e2e/integration/vanilla-node/e2e/cookie-cache-signout.spec.ts b/e2e/integration/vanilla-node/e2e/cookie-cache-signout.spec.ts new file mode 100644 index 0000000000..501cad70ee --- /dev/null +++ b/e2e/integration/vanilla-node/e2e/cookie-cache-signout.spec.ts @@ -0,0 +1,68 @@ +import { expect, test } from "@playwright/test"; +import { runClient, setup } from "./utils"; + +/** + * @see https://github.com/better-auth/better-auth/issues/8273 + */ +const { ref, start, clean } = setup({ + session: { + cookieCache: { + enabled: true, + maxAge: 60, + strategy: "compact", + }, + }, +}); + +test.describe("sign-out with cookieCache", () => { + test.beforeEach(async () => start()); + test.afterEach(async () => clean()); + + test("should clear both session_token and session_data cookies on sign-out", async ({ + page, + }) => { + await page.goto( + `http://localhost:${ref.clientPort}/?port=${ref.serverPort}`, + ); + await page.locator("text=Ready").waitFor(); + + // Sign in + await runClient(page, ({ client }) => + client.signIn.email({ + email: "test@test.com", + password: "password123", + }), + ); + + // Verify both cookies are set + let cookies = await page.context().cookies(); + expect( + cookies.find((c) => c.name === "better-auth.session_token"), + ).toBeDefined(); + expect( + cookies.find((c) => c.name === "better-auth.session_data"), + ).toBeDefined(); + + // Verify session is valid + const session = await runClient(page, ({ client }) => client.getSession()); + expect(session.data?.user.email).toBe("test@test.com"); + + // Sign out + await runClient(page, ({ client }) => client.signOut()); + + // Verify both cookies are cleared by the browser + cookies = await page.context().cookies(); + expect( + cookies.find((c) => c.name === "better-auth.session_token"), + ).toBeUndefined(); + expect( + cookies.find((c) => c.name === "better-auth.session_data"), + ).toBeUndefined(); + + // Verify session is null + const sessionAfter = await runClient(page, ({ client }) => + client.getSession(), + ); + expect(sessionAfter.data).toBeNull(); + }); +}); diff --git a/e2e/integration/vanilla-node/e2e/utils.ts b/e2e/integration/vanilla-node/e2e/utils.ts index 6b5b8c113b..9e593dd9f9 100644 --- a/e2e/integration/vanilla-node/e2e/utils.ts +++ b/e2e/integration/vanilla-node/e2e/utils.ts @@ -3,6 +3,7 @@ import { spawn } from "node:child_process"; import { fileURLToPath } from "node:url"; import { terminate } from "@better-auth-test/test-utils/playwright"; import type { Page } from "@playwright/test"; +import type { BetterAuthOptions } from "better-auth"; import { createAuthServer } from "./app"; const root = fileURLToPath(new URL("../", import.meta.url)); @@ -15,7 +16,7 @@ export async function runClient( return page.evaluate(fn, { client }); } -export function setup() { +export function setup(overrides?: Partial) { let server: Awaited>; let clientChild: ChildProcessWithoutNullStreams; const ref: { @@ -28,7 +29,7 @@ export function setup() { return { ref, start: async () => { - server = await createAuthServer(); + server = await createAuthServer(undefined, overrides); clientChild = spawn("pnpm", ["run", "start:client"], { cwd: root, stdio: "pipe",