assessment-model/cypress/e2e/projects/projects-shell.cy.js
Daniel Roth c482f2d6e8 fix(ara-projects): require user onboarding before /projects
Code review: /onboarding is *user* onboarding, not Portfolio onboarding.
It captures the user's details and their required privacy-policy
acceptance (plus marketing opt-in) and touches no Portfolio at all —
confirmed by the form schema in src/app/onboarding/page.tsx, which has
no portfolio coupling and makes acceptedPrivacy mandatory.

The exemption added in e34f166f was therefore wrong: it let contractor
users into the app without the consent we are required to capture. A
contractor can complete onboarding perfectly well, so nobody needs the
bypass.

Removes the /projects exemption and isProjectsPath(). /projects stays in
the matcher, so the tree is still auth-guarded. Unit and Cypress tests
inverted to assert the corrected behaviour, plus a new case covering an
onboarded contractor reaching /projects.

Refs #406

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 16:08:47 +00:00

96 lines
3 KiB
JavaScript

/**
* Ara Projects — route shell smoke test (issue #406)
*
* Covers the two acceptance criteria for the shell:
* 1. an unauthenticated visit to /projects is redirected to /,
* 2. an authenticated user reaches /projects and the layout + nav render.
*
* Uses the `cy.login` command from cypress/support/commands.ts, which mints a
* real next-auth JWE cookie so the *server* middleware sees a session (the
* /api/auth/session stub alone would not be enough — middleware reads the
* cookie). Requires NEXTAUTH_JWT_SECRET in the Cypress env, same as the
* live-tracking specs.
*/
const JWT_SECRET = Cypress.env("NEXTAUTH_JWT_SECRET");
describe("Ara Projects route shell", function () {
before(function () {
if (!JWT_SECRET) {
cy.log("NEXTAUTH_JWT_SECRET not set — skipping projects shell spec");
this.skip();
}
});
it("redirects an unauthenticated visitor to the sign-in page", function () {
cy.clearCookies();
cy.visit("/projects", { failOnStatusCode: false });
cy.location("pathname").should("eq", "/");
});
it("renders the shell and nav for an authenticated internal user", function () {
cy.login({
email: "dev@domna.homes",
name: "Internal User",
onboarded: true,
sub: "cypress-internal",
});
cy.visit("/projects");
cy.location("pathname").should("eq", "/projects");
cy.get("[data-testid=projects-shell]").should("exist");
cy.get("[data-testid=projects-nav-projects]")
.should("be.visible")
.and("have.attr", "aria-current", "page");
cy.contains("h1", "Projects").should("be.visible");
});
it("sends a non-onboarded user to onboarding", function () {
// /onboarding captures user details and the required privacy-policy
// acceptance — it is not Portfolio onboarding, so contractors complete it
// too and nobody reaches /projects without it.
cy.login({
email: "site@contractor.example",
name: "Contractor User",
onboarded: false,
sub: "cypress-contractor",
});
cy.visit("/projects");
cy.location("pathname").should("eq", "/onboarding");
});
it("lets an onboarded contractor into /projects", function () {
cy.login({
email: "site@contractor.example",
name: "Contractor User",
onboarded: true,
sub: "cypress-contractor",
});
cy.visit("/projects");
cy.location("pathname").should("eq", "/projects");
cy.get("[data-testid=projects-shell]").should("exist");
});
it("renders the per-project dashboard shell and its nav", function () {
cy.login({
email: "dev@domna.homes",
name: "Internal User",
onboarded: true,
sub: "cypress-internal",
});
cy.visit("/projects/1");
cy.get("[data-testid=project-shell]").should("exist");
cy.get("[data-testid=project-dashboard-heading]").should("be.visible");
["dashboard", "workOrders", "import", "settings"].forEach((item) => {
cy.get(`[data-testid=projects-nav-${item}]`).should("be.visible");
});
});
});