Files
tm_panel/cypress/e2e/dashboard/dashboard.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

229 lines
6.8 KiB
TypeScript

/// <reference types="cypress" />
const apiResponse = (data: Record<string, unknown>) => ({
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");
});
});