Files
tm_panel/cypress/e2e/customers/customers-management.cy.ts
T
AmirReza Jamali 4fee2c4944 chore: update CI configuration and Dockerfile for pnpm compatibility
- Adjusted CI configuration to ensure compatibility with pnpm.
- Made necessary updates to the Dockerfile for seamless integration with pnpm.
- Refined installation instructions in README.md to align with the latest changes.
2026-04-26 10:13:02 +03:30

374 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// <reference types="cypress" />
const makeCustomer = (overrides: Partial<Record<string, any>> = {}, index = 1) => ({
id: `507f1f77bcf86cd7994390${String(index).padStart(2, "0")}`,
full_name: `Customer ${index}`,
email: `customer${index}@example.com`,
username: `customer${index}`,
phone: "1234567890",
type: "individual",
status: "active",
role: "admin",
companies: [],
created_at: 1710000000 + index,
updated_at: 1710000500 + index,
...overrides,
});
const makeCompany = (overrides: Partial<Record<string, any>> = {}, index = 1) => ({
id: `company-${index}`,
name: `Company ${index}`,
email: `company${index}@example.com`,
phone: "1234567890",
website: "https://example.com",
type: "vendor",
description: "Test company",
founded_year: 2020,
employee_count: 25,
annual_revenue: 1000000,
currency: "USD",
industry: "Technology",
language: "en",
tax_id: `TAX-${index}`,
registration_number: `REG-${index}`,
timezone: "UTC",
address: {
street: "Main St",
city: "Tehran",
state: "Tehran",
postal_code: "12345",
country: "IR",
},
contact_person: {
full_name: "Jane Doe",
first_name: "Jane",
last_name: "Doe",
email: "jane.doe@example.com",
phone: "1234567890",
mobile: "1234567890",
position: "Manager",
is_primary: true,
},
tags: {
keywords: [],
categories: [],
specializations: [],
certifications: [],
cpv_codes: [],
},
...overrides,
});
const customersResponse = ({
customers,
offset = 0,
limit = 10,
total = customers.length,
pages = Math.max(1, Math.ceil(total / limit)),
}: {
customers: Record<string, any>[];
offset?: number;
limit?: number;
total?: number;
pages?: number;
}) => ({
success: true,
message: "ok",
data: { customers },
meta: { offset, limit, total, pages, page: Math.floor(offset / limit) + 1 },
});
describe("customers management - list, actions, filters, feedback", () => {
beforeEach(() => {
cy.setAuthCookies();
});
it("renders table columns and row data from API", () => {
const customers = [
makeCustomer(
{
full_name: "John Carter",
email: "john.carter@example.com",
companies: [{ id: "c1", name: "Acme" }],
},
1,
),
makeCustomer({ status: "inactive", role: "analyst" }, 2),
];
cy.intercept("GET", "**/api/proxy/customers**", {
statusCode: 200,
body: customersResponse({ customers }),
}).as("getCustomers");
cy.visit("/customers");
cy.wait("@getCustomers");
[
"row",
"full name",
"email",
"created at",
"type",
"status",
"company",
"role",
"actions",
].forEach((header) => cy.contains("th", new RegExp(`^${header}$`, "i")).should("be.visible"));
cy.contains("John Carter").should("be.visible");
cy.contains("john.carter@example.com").should("be.visible");
cy.contains("Acme").should("be.visible");
});
it("shows empty state when API returns no customers", () => {
cy.intercept("GET", "**/api/proxy/customers**", {
statusCode: 200,
body: customersResponse({ customers: [], total: 0 }),
}).as("getCustomersEmpty");
cy.visit("/customers");
cy.wait("@getCustomersEmpty");
cy.getByCy("empty-list-message").should("contain", "No Customers were found");
});
it("shows loading skeleton while list request is in flight", () => {
cy.intercept("GET", "**/api/proxy/customers**", (req) => {
req.reply({
delay: 1200,
statusCode: 200,
body: customersResponse({ customers: [makeCustomer()] }),
});
}).as("getCustomersSlow");
cy.visit("/customers");
cy.getByCy("table-skeleton").should("be.visible");
cy.wait("@getCustomersSlow");
cy.getByCy("table-skeleton").should("not.exist");
});
it("paginates with correct offset when moving to next page", () => {
const pageOne = Array.from({ length: 10 }).map((_, i) => makeCustomer({}, i + 1));
const pageTwo = [makeCustomer({ id: "507f1f77bcf86cd799439099", full_name: "Page 2 Customer" }, 99)];
cy.intercept("GET", "**/api/proxy/customers**", (req) => {
const offset = Number(req.query.offset ?? 0);
if (offset === 10) {
req.alias = "getCustomersPage2";
req.reply({
statusCode: 200,
body: customersResponse({
customers: pageTwo,
offset: 10,
limit: 10,
total: 11,
pages: 2,
}),
});
return;
}
req.alias = "getCustomersPage1";
req.reply({
statusCode: 200,
body: customersResponse({
customers: pageOne,
offset: 0,
limit: 10,
total: 11,
pages: 2,
}),
});
});
cy.visit("/customers");
cy.wait("@getCustomersPage1");
cy.contains("Page 2 Customer").should("not.exist");
cy.contains("").click();
cy.wait("@getCustomersPage2")
.its("request.query.offset")
.should("eq", "10");
cy.contains("Page 2 Customer").should("be.visible");
});
it("deletes customer with confirm modal and sends correct request", () => {
const customer = makeCustomer({ id: "507f1f77bcf86cd799439066", full_name: "Delete Me" }, 66);
cy.intercept("GET", "**/api/proxy/customers**", {
statusCode: 200,
body: customersResponse({ customers: [customer] }),
}).as("getCustomers");
cy.intercept("DELETE", `**/api/proxy/customers/${customer.id}`, {
statusCode: 200,
body: { success: true, data: { message: "Customer deleted successfully" } },
}).as("deleteCustomer");
cy.visit("/customers");
cy.wait("@getCustomers");
cy.contains("Delete Me").should("be.visible");
cy.get('button[data-tooltip-id="delete"]').first().click();
cy.getByCy("confirmation-modal").should("be.visible");
cy.getByCy("confirmation-modal-cancel").click();
cy.getByCy("confirmation-modal").should("not.exist");
cy.intercept("GET", "**/api/proxy/customers**", {
statusCode: 200,
body: customersResponse({ customers: [], total: 0 }),
}).as("getCustomersAfterDelete");
cy.get('button[data-tooltip-id="delete"]').first().click();
cy.getByCy("confirmation-modal-confirm").click();
cy.wait("@deleteCustomer");
cy.wait("@getCustomersAfterDelete");
cy.getByCy("empty-list-message").should("contain", "No Customers were found");
});
it("assigns customer to company with correct payload", () => {
const customer = makeCustomer(
{ id: "507f1f77bcf86cd799439033", companies: [{ id: "comp-2", name: "Global LLC" }] },
33,
);
const companies = [
makeCompany({ id: "comp-1", name: "Acme Corp" }, 1),
makeCompany({ id: "comp-2", name: "Global LLC" }, 2),
];
cy.intercept("GET", "**/api/proxy/customers**", {
statusCode: 200,
body: customersResponse({ customers: [customer] }),
}).as("getCustomers");
cy.intercept("GET", "**/api/proxy/companies**", {
statusCode: 200,
body: {
success: true,
message: "ok",
data: { companies },
meta: { offset: 0, limit: 10, total: companies.length },
},
}).as("getCompanies");
cy.intercept("POST", `**/api/proxy/customers/${customer.id}/companies`, (req) => {
expect(req.body).to.deep.equal({ company_ids: ["comp-2"] });
req.reply({
statusCode: 200,
body: { success: true, message: "Selected Companies were assigned", data: {} },
});
}).as("assignCompany");
cy.visit("/customers");
cy.wait("@getCustomers");
cy.get('button[data-tooltip-id="assign-company"]').first().click();
cy.wait("@getCompanies");
cy.getByCy("modal-confirm-button").click();
cy.wait("@assignCompany");
});
it("surfaces assign-company API failures (404)", () => {
const customer = makeCustomer(
{ id: "507f1f77bcf86cd799439034", companies: [{ id: "comp-1", name: "Acme Corp" }] },
34,
);
cy.intercept("GET", "**/api/proxy/customers**", {
statusCode: 200,
body: customersResponse({ customers: [customer] }),
}).as("getCustomers");
cy.intercept("GET", "**/api/proxy/companies**", {
statusCode: 200,
body: {
success: true,
message: "ok",
data: { companies: [makeCompany({ id: "comp-1", name: "Acme Corp" }, 1)] },
},
}).as("getCompanies");
cy.intercept("POST", `**/api/proxy/customers/${customer.id}/companies`, {
statusCode: 404,
body: { error: { message: "Route not found" } },
}).as("assignCompany404");
cy.visit("/customers");
cy.wait("@getCustomers");
cy.get('button[data-tooltip-id="assign-company"]').first().click();
cy.wait("@getCompanies");
cy.getByCy("modal-confirm-button").click();
cy.wait("@assignCompany404").its("response.statusCode").should("eq", 404);
cy.getByCy("modal-content").should("be.visible");
});
it("applies status/role/type filters and keeps close action side-effect free", () => {
const unfiltered = [makeCustomer({ full_name: "Unfiltered Customer" }, 11)];
const filtered = [makeCustomer({ full_name: "Filtered Customer", status: "inactive" }, 12)];
cy.intercept("GET", "**/api/proxy/customers**", (req) => {
const isFiltered =
req.query.status === "inactive" &&
req.query.role === "analyst" &&
req.query.type === "company";
req.alias = isFiltered ? "getCustomersFiltered" : "getCustomersDefault";
req.reply({
statusCode: 200,
body: customersResponse({
customers: isFiltered ? filtered : unfiltered,
total: 1,
}),
});
});
cy.visit("/customers");
cy.wait("@getCustomersDefault");
cy.contains("button", "Filter").click();
cy.get('select[name="status"]').select("inactive");
cy.get('select[name="role"]').select("analyst");
cy.get('select[name="type"]').select("company");
cy.contains("button", "Search").click();
cy.wait("@getCustomersFiltered");
cy.contains("Filtered Customer").should("be.visible");
cy.contains("button", "Filter").click();
cy.contains("button", "Close").click();
cy.get("@getCustomersFiltered.all").should("have.length", 1);
});
it("navigates to feedback page and loads customer feedback list", () => {
const customer = makeCustomer({ id: "507f1f77bcf86cd799439044", full_name: "Feedback Target" }, 44);
cy.intercept("GET", "**/api/proxy/customers**", {
statusCode: 200,
body: customersResponse({ customers: [customer] }),
}).as("getCustomers");
cy.intercept("GET", "**/api/proxy/feedback**", (req) => {
expect(req.query.customer_id).to.eq(customer.id);
req.reply({
statusCode: 200,
body: {
success: true,
message: "ok",
data: [
{
id: "fb-1",
feedback_type: "bug",
created_at: 1710001000,
updated_at: 1710001200,
},
],
},
});
}).as("getCustomerFeedback");
cy.visit("/customers");
cy.wait("@getCustomers");
cy.get('button[data-tooltip-id="feedback"]').first().click();
cy.location("pathname").should("eq", `/customers/feedback/${customer.id}`);
cy.wait("@getCustomerFeedback");
cy.contains("feedback type").should("be.visible");
});
});