feat: implement global loading indicators and refactor activity feed tests

This commit is contained in:
Abhimanyu Saharan
2026-02-12 00:55:04 +05:30
parent f2f4ee78a9
commit 10ea95678b
7 changed files with 70 additions and 7 deletions

View File

@@ -31,7 +31,8 @@ describe("/activity feed", () => {
}
function assertSignedInAndLanded() {
cy.contains(/live feed/i, { timeout: 30_000 }).should("be.visible");
cy.waitForAppLoaded();
cy.contains(/live feed/i).should("be.visible");
}
it("auth negative: signed-out user cannot access /activity", () => {
@@ -70,7 +71,6 @@ describe("/activity feed", () => {
cy.visit("/activity");
assertSignedInAndLanded();
cy.wait("@activityList");
cy.contains("CI hardening").should("be.visible");
cy.contains("Hello world").should("be.visible");
});
@@ -91,7 +91,6 @@ describe("/activity feed", () => {
cy.visit("/activity");
assertSignedInAndLanded();
cy.wait("@activityList");
cy.contains(/waiting for new comments/i).should("be.visible");
});
@@ -111,7 +110,6 @@ describe("/activity feed", () => {
cy.visit("/activity");
assertSignedInAndLanded();
cy.wait("@activityList");
cy.contains(/unable to load feed|boom/i).should("be.visible");
});
});

View File

@@ -10,6 +10,7 @@ describe("Clerk login", () => {
// After login, user should be able to access protected route.
cy.visit("/activity");
cy.contains(/live feed/i, { timeout: 30_000 }).should("be.visible");
cy.waitForAppLoaded();
cy.contains(/live feed/i).should("be.visible");
});
});

View File

@@ -14,7 +14,8 @@ describe("Organizations (PR #61)", () => {
cy.clerkSignIn({ strategy: "email_code", identifier: email });
cy.visit("/organization");
cy.contains(/members\s*&\s*invites/i, { timeout: 30_000 }).should("be.visible");
cy.waitForAppLoaded();
cy.contains(/members\s*&\s*invites/i).should("be.visible");
// Deterministic assertion across roles:
// - if user is admin: invite button enabled

View File

@@ -6,6 +6,8 @@ type ClerkOtpLoginOptions = {
otp: string;
};
const APP_LOAD_TIMEOUT_MS = 30_000;
function getEnv(name: string, fallback?: string): string {
const value = Cypress.env(name) as string | undefined;
if (value) return value;
@@ -40,6 +42,16 @@ function normalizeOrigin(value: string): string {
}
}
Cypress.Commands.add("waitForAppLoaded", () => {
cy.get("[data-cy='route-loader']", {
timeout: APP_LOAD_TIMEOUT_MS,
}).should("not.exist");
cy.get("[data-cy='global-loader']", {
timeout: APP_LOAD_TIMEOUT_MS,
}).should("have.attr", "aria-hidden", "true");
});
Cypress.Commands.add("loginWithClerkOtp", () => {
const clerkOrigin = normalizeOrigin(
getEnv("CLERK_ORIGIN", clerkOriginFromPublishableKey()),
@@ -194,6 +206,11 @@ declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
namespace Cypress {
interface Chainable {
/**
* Waits for route-level and global app loaders to disappear.
*/
waitForAppLoaded(): Chainable<void>;
/**
* Logs in via the real Clerk SignIn page using deterministic OTP credentials.
*