/// const apiResponse = (data: Record) => ({ success: true, message: "ok", data, }); const visitDashboard = () => { cy.visit("/", { onBeforeLoad(win) { cy.stub(win, "matchMedia").callsFake((query: string) => ({ matches: query === "(prefers-reduced-motion: reduce)", media: query, onchange: null, addEventListener: () => undefined, removeEventListener: () => undefined, addListener: () => undefined, removeListener: () => undefined, dispatchEvent: () => false, })); }, }); }; const stubDashboardRequests = () => { const now = Math.floor(Date.now() / 1000); cy.intercept("GET", "**/api/proxy/dashboard/summary**", (req) => { expect(req.query.closing_window).to.be.undefined; req.reply({ statusCode: 200, body: apiResponse({ total_tenders: 120, active_tenders: 72, awarded_tenders: 18, expired_tenders: 20, cancelled_tenders: 10, closing_soon: 12, total_estimated_value: 2500000, value_currency: "EUR", generated_at: now, }), }); }).as("dashboardSummary"); cy.intercept("GET", "**/api/proxy/dashboard/trend**", (req) => { expect(req.query).to.include({ days: "14", metric: "created" }); req.reply({ statusCode: 200, body: apiResponse({ metric: "created", days: 14, series: [ { date: "2026-06-07", count: 4 }, { date: "2026-06-08", count: 7 }, { date: "2026-06-09", count: 9 }, ], }), }); }).as("dashboardTrend"); cy.intercept("GET", "**/api/proxy/dashboard/countries**", (req) => { expect(req.query.limit).to.eq("6"); req.reply({ statusCode: 200, body: apiResponse({ total: 120, items: [ { country_code: "DE", count: 60 }, { country_code: "FR", count: 30 }, { country_code: "NL", count: 10 }, ], other_count: 20, }), }); }).as("dashboardCountries"); cy.intercept("GET", "**/api/proxy/dashboard/notice-types**", { statusCode: 200, body: apiResponse({ total: 100, items: [ { type: "CONTRACT_NOTICE", count: 70 }, { type: "AWARD_NOTICE", count: 30 }, ], }), }).as("dashboardNoticeTypes"); cy.intercept("GET", "**/api/proxy/dashboard/closing-soon**", (req) => { expect(req.query.limit).to.eq("5"); req.reply({ statusCode: 200, body: apiResponse({ items: [ { id: "closing-tender-1", title: "Railway signaling upgrade", country_code: "DE", buyer_name: "Federal Rail Agency", submission_deadline: now + 36 * 60 * 60, status: "active", }, ], }), }); }).as("dashboardClosingSoon"); cy.intercept("GET", "**/api/proxy/tenders**", (req) => { expect(req.query).to.include({ sort_by: "created_at", sort_order: "desc", offset: "0", limit: "5", }); req.reply({ statusCode: 200, body: apiResponse({ tenders: [ { id: "recent-tender-1", title: "Hospital equipment framework", country_code: "FR", status: "active", created_at: now - 60 * 60, buyer_organization: { name: "Central Health Buyer" }, }, ], }), }); }).as("recentTenders"); cy.intercept("GET", "**/api/proxy/dashboard/statistics**", (req) => { const days = Number(req.query.days); req.alias = days === 30 ? "statistics30Days" : "statistics14Days"; req.reply({ statusCode: 200, body: apiResponse({ days, generated_at: now, daily: { scraped_ted: [ { date: "2026-06-08", count: days === 30 ? 12 : 5 }, { date: "2026-06-09", count: days === 30 ? 18 : 8 }, ], scraped_documents: [ { date: "2026-06-08", count: days === 30 ? 40 : 20 }, { date: "2026-06-09", count: days === 30 ? 60 : 30 }, ], translated_notices: [ { date: "2026-06-08", count: days === 30 ? 7 : 3 }, { date: "2026-06-09", count: days === 30 ? 11 : 6 }, ], }, totals: { scraped_documents: days === 30 ? 9876 : 4321, translated_tenders: days === 30 ? 654 : 321, }, }), }); }); }; describe("tender dashboard", () => { beforeEach(() => { cy.setAuthCookies(); stubDashboardRequests(); }); it("renders the new dashboard sections with API data and detail links", () => { visitDashboard(); cy.wait([ "@dashboardSummary", "@dashboardTrend", "@dashboardCountries", "@dashboardNoticeTypes", "@dashboardClosingSoon", "@recentTenders", "@statistics14Days", ]); cy.get('h1[aria-label*="welcome to OppLens"]') .should("be.visible") .and("have.attr", "aria-label") .and("include", "welcome to OppLens"); cy.contains("Total Tenders").parent().should("contain.text", "120"); cy.contains("Active Now").parent().should("contain.text", "72"); cy.contains("Closing Soon").parent().should("contain.text", "12"); cy.contains("Estimated Value").parent().should("contain.text", "€2.5M"); cy.contains("Tender Flow — Last 14 days").should("be.visible"); cy.contains("By Country").should("be.visible"); cy.contains(/^DE$/).should("be.visible"); cy.contains("Contract Notice").should("be.visible"); cy.contains("+70 · 70%").should("be.visible"); cy.contains("Railway signaling upgrade") .closest("a") .should("have.attr", "href", "/tenders/closing-tender-1"); cy.contains("Hospital equipment framework") .closest("a") .should("have.attr", "href", "/tenders/recent-tender-1"); cy.contains("Scraping & Translation").should("be.visible"); cy.contains("Total Scraped Documents").parent().should("contain.text", "4,321"); cy.contains("Total Translated Tenders").parent().should("contain.text", "321"); cy.contains("Scraped TED per day").should("be.visible"); cy.contains("Scraped documents per day").should("be.visible"); cy.contains("Translated notices per day").should("be.visible"); }); it("reloads scraping and translation statistics for a selected range", () => { visitDashboard(); cy.wait("@statistics14Days"); cy.contains("button", "14d").should("have.class", "bg-primary"); cy.contains("button", "30d").click(); cy.wait("@statistics30Days") .its("request.query.days") .should("eq", "30"); cy.contains("button", "30d").should("have.class", "bg-primary"); cy.contains("Total Scraped Documents").parent().should("contain.text", "9,876"); cy.contains("Total Translated Tenders").parent().should("contain.text", "654"); }); });