snapshot of https://github.com/marmelab/atomic-crm.git (main, 2026-09-18; blobs > 131072B omitted)
Some checks failed
✅ Check / 🔬 ESLint (push) Failing after 21s
✅ Check / 🏷️ Typecheck (push) Failing after 11s
✅ Check / 🔎 Test (push) Failing after 11s
✅ Check / e2e-test (push) Failing after 11s
✅ Check / 🔨 Build (push) Failing after 11s
🚀 Deploy / 🚀 Deploy (doc) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (demo) (push) Failing after 12s
🚀 Deploy / 🚀 Deploy (supabase) (push) Failing after 18s

This commit is contained in:
upstream-mirror
2026-09-18 01:48:50 +05:00
commit 93ecc1a6f8
787 changed files with 75240 additions and 0 deletions

264
e2e/fixtures.ts Normal file
View File

@@ -0,0 +1,264 @@
import { test as base, expect, type Page } from "@playwright/test";
import { createClient } from "@supabase/supabase-js";
const adminSupabase = createClient(
process.env.VITE_SUPABASE_URL ?? "http://127.0.0.1:54341",
process.env.SERVICE_ROLE_KEY!,
{ auth: { autoRefreshToken: false, persistSession: false } },
);
// Tables in FK-safe deletion order (children before parents)
const TABLES = [
"tasks",
"contact_notes",
"deal_notes",
"deals",
"contacts",
"companies",
"tags",
"favicons_excluded_domains",
"configuration",
"sales",
];
async function resetDb() {
for (const table of TABLES) {
// Supabase client delete need a where clause to get executed, so we use one that will match on all rows (id is not null)
await adminSupabase.from(table).delete().not("id", "is", null);
}
// Delete all auth users (cascades to sales via DB trigger)
const { data } = await adminSupabase.auth.admin.listUsers();
await Promise.all(
data.users.map((user) => adminSupabase.auth.admin.deleteUser(user.id)),
);
}
async function createUser({
email,
password,
}: {
email: string;
password: string;
}) {
const { data, error } = await adminSupabase.auth.admin.createUser({
email,
password,
email_confirm: true,
});
if (error) {
throw new Error(`Failed to create user: ${error.message}`);
}
return data.user;
}
async function createSales({
first_name,
last_name,
email,
password,
administrator = false,
}: {
first_name: string;
last_name: string;
email: string;
password: string;
administrator?: boolean;
}) {
const { data: userData, error: userError } =
await adminSupabase.auth.admin.createUser({
email,
password,
email_confirm: true,
});
if (userError) {
throw new Error(`Failed to create sales: ${userError.message}`);
}
const { data, error } = await adminSupabase
.from("sales")
.update({ first_name, last_name, administrator })
.eq("user_id", userData.user?.id)
.select()
.single();
if (error) {
throw new Error(`Failed to create sales: ${error.message}`);
}
return data;
}
async function createNotes({
contactId,
salesId,
notes,
}: {
contactId: string | number;
salesId: string | number;
notes: {
text: string;
date?: string;
status?: "cold" | "warm" | "hot";
}[];
}) {
if (notes.length === 0) return;
const { error } = await adminSupabase.from("contact_notes").insert(
notes.map(({ text, date, status = "cold" }) => ({
contact_id: contactId,
sales_id: salesId,
text,
date,
status,
})),
);
if (error) {
throw new Error(`Failed to create notes: ${error.message}`);
}
}
async function createCompany({
name,
salesId,
}: {
name: string;
salesId: string | number;
}) {
const { data, error } = await adminSupabase
.from("companies")
.insert({ name, sales_id: salesId })
.select("id")
.single();
if (error) {
throw new Error(`Failed to create company: ${error.message}`);
}
return data;
}
async function createContact({
first_name,
last_name,
title = "",
company_id = null,
sales_id,
notes = [],
}: {
first_name: string;
last_name: string;
title?: string;
company_id?: string | number | null;
sales_id: string | number;
notes?: {
text: string;
date?: string;
status?: "cold" | "warm" | "hot";
}[];
}) {
const { data, error } = await adminSupabase
.from("contacts")
.insert({
first_name,
last_name,
title,
company_id,
sales_id,
first_seen: new Date().toISOString(),
last_seen: new Date().toISOString(),
has_newsletter: false,
tags: [],
gender: "unknown",
status: "cold",
background: "",
email_jsonb: [],
phone_jsonb: [],
})
.select("id")
.single();
if (error) {
throw new Error(`Failed to create contact: ${error.message}`);
}
await createNotes({
contactId: data.id,
salesId: sales_id,
notes,
});
return data;
}
const getMenuMethod = ({ page }: { page: Page; isMobile: boolean }) => ({
goToDashboard: async () => {
await page.getByRole("link", { name: "Dashboard" }).click();
await page.waitForLoadState("networkidle");
},
goToContacts: async () => {
await page.getByRole("link", { name: "Contacts" }).click();
await page.waitForLoadState("networkidle");
},
});
const dismissToast = async (page: Page, content: string) => {
await expect(page.getByText(content)).toBeVisible();
await page.getByLabel("Close toast").first().click();
// Since we are in optimistic UI, dismissing the toast trigger the request to the api linked to the toast message
await page.waitForLoadState("networkidle");
};
export const test = base.extend<{
resetDb: void;
createUser: typeof createUser;
createSales: typeof createSales;
createCompany: typeof createCompany;
createContact: typeof createContact;
createNotes: typeof createNotes;
menu: ReturnType<typeof getMenuMethod>;
dismissToast: (content: string) => Promise<void>;
}>({
resetDb: [
// The first argument to a Playwright fixture function must use object destructuring ({}) — _ is not allowed.
// Playwright uses this to statically analyze which fixtures are requested.
// eslint-disable-next-line no-empty-pattern
async ({}, use) => {
await resetDb();
await use();
},
{ auto: true },
],
// eslint-disable-next-line no-empty-pattern
createUser: async ({}, cb) => {
await cb(createUser);
},
// eslint-disable-next-line no-empty-pattern
createSales: async ({}, cb) => {
await cb(createSales);
},
// eslint-disable-next-line no-empty-pattern
createCompany: async ({}, cb) => {
await cb(createCompany);
},
// eslint-disable-next-line no-empty-pattern
createContact: async ({}, cb) => {
await cb(createContact);
},
// eslint-disable-next-line no-empty-pattern
createNotes: async ({}, cb) => {
await cb(createNotes);
},
menu: async ({ page, isMobile }, cb) => {
await cb(getMenuMethod({ page, isMobile }));
},
dismissToast: async ({ page }, cb) => {
await cb((content: string) => dismissToast(page, content));
},
});
export { expect };