diff --git a/frontend/cypress/e2e/boards_list.cy.ts b/frontend/cypress/e2e/boards_list.cy.ts
index 126cbd4f..ee075937 100644
--- a/frontend/cypress/e2e/boards_list.cy.ts
+++ b/frontend/cypress/e2e/boards_list.cy.ts
@@ -1,18 +1,12 @@
///
+import { setupCommonPageTestHooks } from "../support/testHooks";
+
describe("/boards", () => {
const apiBase = "**/api/v1";
const email = "local-auth-user@example.com";
- const originalDefaultCommandTimeout = Cypress.config("defaultCommandTimeout");
-
- beforeEach(() => {
- Cypress.config("defaultCommandTimeout", 20_000);
- });
-
- afterEach(() => {
- Cypress.config("defaultCommandTimeout", originalDefaultCommandTimeout);
- });
+ setupCommonPageTestHooks(apiBase);
it("auth negative: signed-out user is shown local auth login", () => {
cy.visit("/boards");
diff --git a/frontend/cypress/e2e/global_approvals.cy.ts b/frontend/cypress/e2e/global_approvals.cy.ts
index 577e75f5..f63afea8 100644
--- a/frontend/cypress/e2e/global_approvals.cy.ts
+++ b/frontend/cypress/e2e/global_approvals.cy.ts
@@ -1,5 +1,7 @@
///
+import { setupCommonPageTestHooks } from "../support/testHooks";
+
// Clerk/Next.js occasionally triggers a hydration mismatch on auth routes in CI.
// This is non-deterministic UI noise for these tests; ignore it so assertions can proceed.
Cypress.on("uncaught:exception", (err) => {
@@ -13,25 +15,7 @@ describe("Global approvals", () => {
const apiBase = "**/api/v1";
const email = Cypress.env("CLERK_TEST_EMAIL") || "jane+clerk_test@example.com";
- const originalDefaultCommandTimeout = Cypress.config("defaultCommandTimeout");
-
- beforeEach(() => {
- Cypress.config("defaultCommandTimeout", 20_000);
-
- cy.intercept("GET", "**/healthz", {
- statusCode: 200,
- body: { ok: true },
- }).as("healthz");
-
- cy.intercept("GET", `${apiBase}/organizations/me/member*`, {
- statusCode: 200,
- body: { organization_id: "org1", role: "owner" },
- }).as("orgMeMember");
- });
-
- afterEach(() => {
- Cypress.config("defaultCommandTimeout", originalDefaultCommandTimeout);
- });
+ setupCommonPageTestHooks(apiBase);
it("can render a pending approval and approve it", () => {
const approval = {
diff --git a/frontend/cypress/e2e/skill_packs_sync.cy.ts b/frontend/cypress/e2e/skill_packs_sync.cy.ts
index 1a3c52f7..cc3590fb 100644
--- a/frontend/cypress/e2e/skill_packs_sync.cy.ts
+++ b/frontend/cypress/e2e/skill_packs_sync.cy.ts
@@ -1,5 +1,7 @@
///
+import { setupCommonPageTestHooks } from "../support/testHooks";
+
// Clerk/Next.js occasionally triggers a hydration mismatch on auth routes in CI.
// This is non-deterministic UI noise for these tests; ignore it so assertions can proceed.
Cypress.on("uncaught:exception", (err) => {
@@ -13,25 +15,7 @@ describe("Skill packs", () => {
const apiBase = "**/api/v1";
const email = Cypress.env("CLERK_TEST_EMAIL") || "jane+clerk_test@example.com";
- const originalDefaultCommandTimeout = Cypress.config("defaultCommandTimeout");
-
- beforeEach(() => {
- Cypress.config("defaultCommandTimeout", 20_000);
-
- cy.intercept("GET", "**/healthz", {
- statusCode: 200,
- body: { ok: true },
- }).as("healthz");
-
- cy.intercept("GET", `${apiBase}/organizations/me/member*`, {
- statusCode: 200,
- body: { organization_id: "org1", role: "owner" },
- }).as("orgMeMember");
- });
-
- afterEach(() => {
- Cypress.config("defaultCommandTimeout", originalDefaultCommandTimeout);
- });
+ setupCommonPageTestHooks(apiBase);
it("can sync a pack and surface warnings", () => {
cy.intercept("GET", `${apiBase}/skills/packs*`, {
diff --git a/frontend/cypress/support/testHooks.ts b/frontend/cypress/support/testHooks.ts
new file mode 100644
index 00000000..be07765b
--- /dev/null
+++ b/frontend/cypress/support/testHooks.ts
@@ -0,0 +1,32 @@
+///
+
+type CommonPageTestHooksOptions = {
+ timeoutMs?: number;
+ orgMemberRole?: string;
+};
+
+export function setupCommonPageTestHooks(
+ apiBase: string,
+ options: CommonPageTestHooksOptions = {},
+): void {
+ const { timeoutMs = 20_000, orgMemberRole = "owner" } = options;
+ const originalDefaultCommandTimeout = Cypress.config("defaultCommandTimeout");
+
+ beforeEach(() => {
+ Cypress.config("defaultCommandTimeout", timeoutMs);
+
+ cy.intercept("GET", "**/healthz", {
+ statusCode: 200,
+ body: { ok: true },
+ }).as("healthz");
+
+ cy.intercept("GET", `${apiBase}/organizations/me/member*`, {
+ statusCode: 200,
+ body: { organization_id: "org1", role: orgMemberRole },
+ }).as("orgMeMember");
+ });
+
+ afterEach(() => {
+ Cypress.config("defaultCommandTimeout", originalDefaultCommandTimeout);
+ });
+}