/// 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 notificationHistoryPattern = /\/api\/proxy\/notifications(?:\?|$)/; const makeNotification = ( overrides: Partial = {}, ): 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 = (times = 1) => { cy.intercept( { method: "GET", url: "**/api/proxy/notifications/my**", times, }, { 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", notificationHistoryPattern, (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("exist"); }); 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", notificationHistoryPattern, (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("filters notification history by user when a user is selected", () => { const currentUserNotification = makeNotification({ id: "notification-current-user", title: "Current user notification", }); const selectedUserNotification = makeNotification({ id: "notification-selected-user", title: "Selected user notification", recipient: { full_name: "Notification Admin" }, }); cy.intercept("GET", notificationHistoryPattern, { statusCode: 200, body: historyResponse([currentUserNotification]), }).as("currentUserNotifications"); cy.intercept("GET", "**/api/proxy/users**", { statusCode: 200, body: { success: true, message: "ok", data: { users: [ { id: "64f9b24d2f1b2f0012345678", full_name: "Notification Admin", username: "notification-admin", email: "notification-admin@example.com", role: "admin", status: "active", }, ], }, meta: { offset: 0, limit: 20, total: 1, page: 1, pages: 1, has_more: false, }, }, }).as("users"); cy.intercept("GET", notificationHistoryPattern, (req) => { expect(req.query.user_id).to.eq("64f9b24d2f1b2f0012345678"); req.reply({ statusCode: 200, body: historyResponse([selectedUserNotification]), }); }).as("selectedUserNotifications"); cy.visit("/notification-history"); cy.wait("@currentUserNotifications"); cy.contains(currentUserNotification.title).should("be.visible"); cy.getByCy("notification-history-filter-panel-toggle").click(); cy.getByCy("notification-history-filter-user-select") .siblings("button") .click(); cy.getByCy("notification-history-filter-user-select-search").type( "Notification Admin", ); cy.wait("@users"); cy.getByCy("notification-history-filter-user-select-option") .filter('[data-value="64f9b24d2f1b2f0012345678"]') .click(); cy.getByCy("notification-history-filter-submit-button").click(); cy.wait("@selectedUserNotifications"); cy.location("search").should( "include", "user_id=64f9b24d2f1b2f0012345678", ); cy.contains(selectedUserNotification.title).should("be.visible"); cy.contains(currentUserNotification.title).should("not.exist"); }); it("shows the current user filter when notification history is opened for the current user", () => { const notification = makeNotification({ id: "notification-current-user-filter", title: "Current user filtered notification", }); cy.intercept("GET", notificationHistoryPattern, (req) => { expect(req.query.user_id).to.eq("507f1f77bcf86cd799439011"); req.reply({ statusCode: 200, body: historyResponse([notification]), }); }).as("currentUserFilteredNotifications"); cy.visit("/notification-history?user_id=507f1f77bcf86cd799439011"); cy.wait("@currentUserFilteredNotifications"); cy.getByCy("notification-history-filter-panel-toggle").should( "contain.text", "1 active", ); cy.getByCy("notification-history-filter-panel-toggle").click(); cy.getByCy("notification-history-filter-user-select") .siblings("button") .should("contain.text", "E2E Admin One"); cy.contains(notification.title).should("be.visible"); }); it("shows loading and empty states, and opens the create page", () => { cy.intercept("GET", notificationHistoryPattern, (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"); }); });