Files
tm_panel/cypress/e2e/notifications/notification-history.cy.ts
T
AmirReza Jamali 169708e49d
continuous-integration/drone/push Build is passing
fix(companies): align optional numeric fields with API validation
Make employee_count, annual_revenue, and founded_year truly optional in
the create company form and payload. Add field validation docs and
Cypress e2e coverage for dashboard and notifications.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 11:47:45 +03:30

191 lines
5.6 KiB
TypeScript

/// <reference types="cypress" />
type NotificationRecord = {
id: string;
title: string;
event_type: string;
created_at: number;
message: string;
priority: string;
type: string;
seen: boolean;
status: string;
recipient?: { full_name: string };
};
const makeNotification = (
overrides: Partial<NotificationRecord> = {},
): NotificationRecord => ({
id: "notification-1",
title: "Tender deadline reminder",
event_type: "push",
created_at: 1_717_934_400,
message: "A watched tender is closing soon and needs your attention.",
priority: "important",
type: "warning",
seen: false,
status: "sent",
recipient: { full_name: "E2E Admin One" },
...overrides,
});
const historyResponse = (
notifications: NotificationRecord[],
options: { offset?: number; total?: number; pages?: number } = {},
) => ({
success: true,
message: "ok",
data: notifications,
meta: {
offset: options.offset ?? 0,
limit: 10,
total: options.total ?? notifications.length,
page: Math.floor((options.offset ?? 0) / 10) + 1,
pages: options.pages ?? 1,
has_more: false,
},
});
const stubHeaderNotifications = () => {
cy.intercept("GET", "**/api/proxy/notifications/my**", {
statusCode: 200,
body: historyResponse([]),
}).as("myNotifications");
};
describe("notification history", () => {
beforeEach(() => {
cy.setAuthCookies();
stubHeaderNotifications();
});
it("renders breadcrumbs, actions, columns, API rows, and details navigation", () => {
const notification = makeNotification();
cy.intercept("GET", /\/api\/proxy\/notifications(?:\?|$)/, (req) => {
expect(req.query).to.include({
sort_by: "created_at",
sort_order: "desc",
offset: "0",
limit: "10",
});
req.reply({
statusCode: 200,
body: historyResponse([notification]),
});
}).as("notificationHistory");
cy.visit("/notification-history");
cy.wait("@notificationHistory");
cy.getByCy("breadcrumb-link-dashboard").should("be.visible");
cy.getByCy("breadcrumb-current-notification-history")
.should("be.visible")
.and("contain.text", "Notification history");
cy.getByCy("list-header-create-button").should(
"contain.text",
"Create notification",
);
[
"row",
"title",
"channel",
"created at",
"receiver",
"message",
"priority",
"type",
"seen",
"status",
"actions",
].forEach((column) => {
cy.contains("th", new RegExp(`^${column}$`, "i")).should("be.visible");
});
cy.contains("tr", notification.title)
.should("contain.text", notification.event_type)
.and("contain.text", notification.recipient?.full_name)
.and("contain.text", notification.priority)
.and("contain.text", notification.type)
.and("contain.text", notification.status)
.within(() => {
cy.get('button[data-tooltip-id="details"]').click();
});
cy.location("pathname").should(
"eq",
`/notification-history/${notification.id}`,
);
});
it("filters by seen state and search, then resets to the default list", () => {
const defaultNotification = makeNotification({
id: "notification-default",
title: "Default notification",
});
const filteredNotification = makeNotification({
id: "notification-filtered",
title: "Filtered urgent notice",
seen: true,
});
cy.intercept("GET", /\/api\/proxy\/notifications(?:\?|$)/, (req) => {
const isFiltered =
req.query.seen === "true" && req.query.search === "urgent";
req.alias = isFiltered ? "filteredNotifications" : "defaultNotifications";
req.reply({
statusCode: 200,
body: historyResponse([
isFiltered ? filteredNotification : defaultNotification,
]),
});
});
cy.visit("/notification-history");
cy.wait("@defaultNotifications");
cy.contains(defaultNotification.title).should("be.visible");
cy.getByCy("notification-history-filter-panel-toggle").click();
cy.get('select[name="seen"]').select("true", { force: true });
cy.getByCy("notification-history-filter-search-input").type("urgent");
cy.getByCy("notification-history-filter-submit-button").click();
cy.wait("@filteredNotifications");
cy.location("search")
.should("include", "seen=true")
.and("include", "search=urgent");
cy.contains(filteredNotification.title).should("be.visible");
cy.contains(defaultNotification.title).should("not.exist");
cy.contains("2 active").should("be.visible");
cy.getByCy("notification-history-filter-reset-button").click();
cy.wait("@defaultNotifications");
cy.location("search").should("eq", "");
cy.contains(defaultNotification.title).should("be.visible");
});
it("shows loading and empty states, and opens the create page", () => {
cy.intercept("GET", /\/api\/proxy\/notifications(?:\?|$)/, (req) => {
req.reply({
delay: 900,
statusCode: 200,
body: historyResponse([]),
});
}).as("emptyNotificationHistory");
cy.visit("/notification-history");
cy.getByCy("table-skeleton").should("be.visible");
cy.wait("@emptyNotificationHistory");
cy.getByCy("table-skeleton").should("not.exist");
cy.getByCy("empty-list-message").should(
"contain.text",
"No notifications were found",
);
cy.getByCy("list-header-create-button").click();
cy.location("pathname").should("eq", "/notification-history/create");
cy.getByCy("breadcrumb-current-create-notification").should("be.visible");
});
});