Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import type { Factory } from "~/utils/types";
export const createPopulatedOrganization: Factory<Organization> = ({
id = createId(),
name = faker.company.name(),
slug = slugify(name),
slug = slugify(`${name}-${createId()}`),
updatedAt = faker.date.recent({ days: 10 }),
createdAt = faker.date.past({ refDate: updatedAt, years: 1 }),
imageUrl = faker.image.url(),
Expand Down
26 changes: 16 additions & 10 deletions app/routes/_authenticated-routes+/onboarding+/organization.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/** biome-ignore-all lint/style/noNonNullAssertion: test code */
import { createId } from "@paralleldrive/cuid2";
import { describe, expect, onTestFinished, test } from "vitest";

import { action } from "./organization";
Expand Down Expand Up @@ -122,10 +123,10 @@ describe("/onboarding/organization route action", () => {

test("given: a valid name for an organization, should: create organization and redirect to organization page", async () => {
const { userAccount } = await setup();
const organization = createPopulatedOrganization();
const name = `${createPopulatedOrganization().name} ${createId()}`;
const formData = toFormData({
intent,
name: organization.name,
name,
});

const response = (await sendAuthenticatedRequest({
Expand All @@ -134,7 +135,7 @@ describe("/onboarding/organization route action", () => {
})) as Response;

expect(response.status).toEqual(302);
const slug = slugify(organization.name);
const slug = slugify(name);
expect(response.headers.get("Location")).toEqual(
`/organizations/${slug}`,
);
Expand All @@ -143,7 +144,7 @@ describe("/onboarding/organization route action", () => {
const createdOrganization =
await retrieveOrganizationWithMembershipsFromDatabaseBySlug(slug);
expect(createdOrganization).toMatchObject({
name: organization.name,
name,
});
expect(createdOrganization?.memberships[0]?.member.id).toEqual(
userAccount.id,
Expand All @@ -156,8 +157,13 @@ describe("/onboarding/organization route action", () => {
test("given: an organization name that already exists, should: create organization with unique slug", async () => {
const { userAccount } = await setup();

// Create first organization
const firstOrg = createPopulatedOrganization();
// Create first organization with a slug matching what the route would
// produce from the name, so the second org triggers the slug extension.
const { name } = createPopulatedOrganization();
const firstOrg = createPopulatedOrganization({
name,
slug: slugify(name),
});
await saveOrganizationToDatabase(firstOrg);
onTestFinished(async () => {
await deleteOrganizationFromDatabaseById(firstOrg.id);
Expand Down Expand Up @@ -320,11 +326,11 @@ describe("/onboarding/organization route action", () => {

test("given: a valid name, should: create organization", async () => {
const { userAccount } = await setup();
const organization = createPopulatedOrganization();
const name = `${createPopulatedOrganization().name} ${createId()}`;

const formData = toFormData({
intent,
name: organization.name,
name,
});

const response = (await sendAuthenticatedRequest({
Expand All @@ -334,7 +340,7 @@ describe("/onboarding/organization route action", () => {

// Assert redirect
expect(response.status).toEqual(302);
const slug = slugify(organization.name);
const slug = slugify(name);
expect(response.headers.get("Location")).toEqual(
`/organizations/${slug}`,
);
Expand All @@ -346,7 +352,7 @@ describe("/onboarding/organization route action", () => {
expect(createdOrganization).toBeTruthy();
expect(createdOrganization).toMatchObject({
imageUrl: "", // No logo uploaded
name: organization.name,
name,
slug: slug,
});
expect(createdOrganization!.memberships).toHaveLength(1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createId } from "@paralleldrive/cuid2";
import { describe, expect, onTestFinished, test } from "vitest";

import { action } from "./general";
Expand All @@ -20,6 +21,7 @@ import {
teardownOrganizationAndMember,
} from "~/test/test-utils";
import { badRequest, forbidden, notFound } from "~/utils/http-responses.server";
import { slugify } from "~/utils/slugify.server";
import { toFormData } from "~/utils/to-form-data";
import { getToast } from "~/utils/toast.server";

Expand Down Expand Up @@ -145,7 +147,8 @@ describe("/organizations/:organizationSlug/settings/general route action", () =>
const { user, organization } = await setupUserWithOrgAndAddAsMember({
role: OrganizationMembershipRole.owner,
});
const { name, slug } = createPopulatedOrganization();
const name = `${createPopulatedOrganization().name} ${createId()}`;
const slug = slugify(name);
const formData = toFormData({ intent, name });

const response = (await sendAuthenticatedRequest({
Expand Down
26 changes: 16 additions & 10 deletions app/routes/_authenticated-routes+/organizations_+/new.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/** biome-ignore-all lint/style/noNonNullAssertion: test code */
import { parseSubmission, report } from "@conform-to/react/future";
import { createId } from "@paralleldrive/cuid2";
import { describe, expect, onTestFinished, test } from "vitest";

import { action } from "./new";
Expand Down Expand Up @@ -101,10 +102,10 @@ describe("/organizations/new route action", () => {

test("given: a valid name for an organization, should: create organization and redirect to organization page", async () => {
const { userAccount } = await setup();
const organization = createPopulatedOrganization();
const name = `${createPopulatedOrganization().name} ${createId()}`;
const formData = toFormData({
intent,
name: organization.name,
name,
});

const response = (await sendAuthenticatedRequest({
Expand All @@ -113,7 +114,7 @@ describe("/organizations/new route action", () => {
})) as Response;

expect(response.status).toEqual(302);
const slug = slugify(organization.name);
const slug = slugify(name);
expect(response.headers.get("Location")).toEqual(
`/organizations/${slug}`,
);
Expand All @@ -122,7 +123,7 @@ describe("/organizations/new route action", () => {
const createdOrganization =
await retrieveOrganizationWithMembershipsFromDatabaseBySlug(slug);
expect(createdOrganization).toMatchObject({
name: organization.name,
name,
});
expect(createdOrganization?.memberships[0]?.member.id).toEqual(
userAccount.id,
Expand All @@ -135,8 +136,13 @@ describe("/organizations/new route action", () => {
test("given: an organization name that already exists, should: create organization with unique slug", async () => {
const { userAccount } = await setup();

// Create first organization
const firstOrg = createPopulatedOrganization();
// Create first organization with a slug matching what the route would
// produce from the name, so the second org triggers the slug extension.
const { name } = createPopulatedOrganization();
const firstOrg = createPopulatedOrganization({
name,
slug: slugify(name),
});
await saveOrganizationToDatabase(firstOrg);
onTestFinished(async () => {
await deleteOrganizationFromDatabaseById(firstOrg.id);
Expand Down Expand Up @@ -206,7 +212,7 @@ describe("/organizations/new route action", () => {

test("given: a valid name and a logo file, should: create organization with logo", async () => {
const { userAccount } = await setup();
const organization = createPopulatedOrganization();
const name = `${createPopulatedOrganization().name} ${createId()}`;

// Create a mock File object for the logo
const logoFile = new File(["logo content"], "logo.png", {
Expand All @@ -216,7 +222,7 @@ describe("/organizations/new route action", () => {
const formData = toFormData({
intent,
logo: logoFile,
name: organization.name,
name,
});

const response = (await sendAuthenticatedRequest({
Expand All @@ -226,7 +232,7 @@ describe("/organizations/new route action", () => {

// Assert redirect
expect(response.status).toEqual(302);
const slug = slugify(organization.name);
const slug = slugify(name);
expect(response.headers.get("Location")).toEqual(
`/organizations/${slug}`,
);
Expand All @@ -237,7 +243,7 @@ describe("/organizations/new route action", () => {

expect(createdOrganization).toBeTruthy();
expect(createdOrganization).toMatchObject({
name: organization.name,
name,
slug: slug,
});
// Logo URL should be set (uploaded to storage)
Expand Down
8 changes: 5 additions & 3 deletions app/test/vitest.global-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ config();

let teardownHappened = false;

export default function setupVitest() {
void ensureStripeProductsAndPricesExist().catch((error) => {
export default async function setupVitest() {
try {
await ensureStripeProductsAndPricesExist();
} catch (error) {
console.error("✨ Failed to seed Stripe pricing:", error);
process.exit(1);
});
}

// Clear mock sessions after all tests are run.
return async function teardownVitest() {
Expand Down
7 changes: 5 additions & 2 deletions playwright/e2e/onboarding/onboarding-organization.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { createPopulatedUserAccount } from "~/features/user-accounts/user-accoun
import { deleteUserAccountFromDatabaseById } from "~/features/user-accounts/user-accounts-model.server";
import { OrganizationMembershipRole } from "~/generated/client";
import { teardownOrganizationAndMember } from "~/test/test-utils";
import { slugify } from "~/utils/slugify.server";

const path = "/onboarding/organization";

Expand Down Expand Up @@ -83,7 +84,8 @@ test.describe("onboarding organization page", () => {
).toBeVisible();

// Enter organization name
const { name: newName, slug: newSlug } = createPopulatedOrganization();
const { name: newName } = createPopulatedOrganization();
const newSlug = slugify(newName);
await page
.getByRole("textbox", { name: /organization name/i })
.fill(newName);
Expand Down Expand Up @@ -175,7 +177,8 @@ test.describe("onboarding organization page", () => {
).toBeVisible();

// Enter organization name
const { name: newName, slug: newSlug } = createPopulatedOrganization();
const { name: newName } = createPopulatedOrganization();
const newSlug = slugify(newName);
await page
.getByRole("textbox", { name: /organization name/i })
.fill(newName);
Expand Down
13 changes: 9 additions & 4 deletions playwright/e2e/organizations/new-organization.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { createPopulatedUserAccount } from "~/features/user-accounts/user-accoun
import { deleteUserAccountFromDatabaseById } from "~/features/user-accounts/user-accounts-model.server";
import { OrganizationMembershipRole } from "~/generated/client";
import { teardownOrganizationAndMember } from "~/test/test-utils";
import { slugify } from "~/utils/slugify.server";

const path = "/organizations/new";

Expand Down Expand Up @@ -80,7 +81,8 @@ test.describe("new organization page", () => {
await expect(privacyLink).toHaveAttribute("href", "/privacy-policy");

// Enter organization name
const { name: newName, slug: newSlug } = createPopulatedOrganization();
const { name: newName } = createPopulatedOrganization();
const newSlug = slugify(newName);
await page
.getByRole("textbox", { name: /organization name/i })
.fill(newName);
Expand Down Expand Up @@ -176,7 +178,8 @@ test.describe("new organization page", () => {
).toBeVisible();

// Enter organization name
const { name: newName, slug: newSlug } = createPopulatedOrganization();
const { name: newName } = createPopulatedOrganization();
const newSlug = slugify(newName);
await page
.getByRole("textbox", { name: /organization name/i })
.fill(newName);
Expand Down Expand Up @@ -244,7 +247,8 @@ test.describe("new organization page", () => {
).toBeVisible();

// Create organization
const { name: newName, slug: newSlug } = createPopulatedOrganization();
const { name: newName } = createPopulatedOrganization();
const newSlug = slugify(newName);
await page
.getByRole("textbox", { name: /organization name/i })
.fill(newName);
Expand Down Expand Up @@ -304,7 +308,8 @@ test.describe("new organization page", () => {
).toBeVisible();

// Create organization
const { name: newName, slug: newSlug } = createPopulatedOrganization();
const { name: newName } = createPopulatedOrganization();
const newSlug = slugify(newName);
await page
.getByRole("textbox", { name: /organization name/i })
.fill(newName);
Expand Down