Files
calendar/apps/web/playwright/insights-charts.e2e.ts
T
Keith WilliamsGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
486817a688 fix: add timeout: 0 to E2E assertions that check for absence of elements (#26353)
This fixes hanging E2E tests where assertions like toHaveCount(0) and
not.toBeVisible() would wait for the full timeout (10s in CI, 120s locally)
before failing, instead of failing immediately.

These assertions are 'state check' assertions that verify an element is
already absent or hidden, rather than waiting for it to become so.
Adding { timeout: 0 } makes them fail immediately if the condition is not met.

Files updated:
- locale.e2e.ts: 16 instances of toHaveCount(0)
- booking-seats.e2e.ts: 8 instances of toHaveCount(0)
- organization-redirection.e2e.ts: 3 instances of toHaveCount(0)
- organization-creation-flows.e2e.ts: 5 instances of not.toBeVisible()
- insights-charts.e2e.ts: 1 instance of toHaveCount(0)
- bookings-list.e2e.ts: 1 instance of toHaveCount(0)
- availability.e2e.ts: 1 instance of toHaveCount(0)
- managed-event-types.e2e.ts: 1 instance of toHaveCount(0)
- team-invitation.e2e.ts: 1 instance of toHaveCount(0)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-12-31 14:58:20 -03:00

289 lines
9.0 KiB
TypeScript

import { expect } from "@playwright/test";
import { v4 as uuidv4 } from "uuid";
import { BookingStatus } from "@calcom/prisma/enums";
import { expectAllChartsToLoad, getAllChartIds } from "./lib/chart-helpers";
import { test } from "./lib/fixtures";
test.afterEach(({ users }) => users.deleteAll());
test.describe("Insights > Charts Loading", () => {
test.describe("Routing Insights", () => {
test("all charts should load successfully with data", async ({ page, users, routingForms, prisma }) => {
// Setup: Create routing form with complete data
const owner = await users.create(
{ name: "Chart Test Owner" },
{
hasTeam: true,
isUnpublished: true,
isOrg: true,
hasSubteam: true,
}
);
await owner.apiLogin();
const membership = await owner.getFirstTeamMembership();
// Create a routing form with fields
const formName = "Chart Loading Test Form";
const form = await routingForms.create({
name: formName,
userId: owner.id,
teamId: membership.teamId,
fields: [
{
type: "text",
label: "Name",
identifier: "name",
required: true,
},
{
type: "email",
label: "Email",
identifier: "email",
required: true,
},
{
type: "select",
label: "Department",
identifier: "department",
required: true,
options: [
{
id: uuidv4(),
label: "Sales",
},
{
id: uuidv4(),
label: "Support",
},
],
},
],
});
const fieldIds = (form.fields as Array<{ id: string }>).map((f) => f.id);
// Create users for routing
const user1 = await users.create({ name: "Sales User" });
const user2 = await users.create({ name: "Support User" });
// Add users to the team
await prisma.membership.createMany({
data: [
{
teamId: membership.teamId,
userId: user1.id,
role: "MEMBER",
accepted: true,
},
{
teamId: membership.teamId,
userId: user2.id,
role: "MEMBER",
accepted: true,
},
],
});
// Create bookings for both users
const booking1 = await prisma.booking.create({
data: {
uid: `booking-${uuidv4()}`,
title: "Sales Booking",
startTime: new Date(),
endTime: new Date(Date.now() + 60 * 60 * 1000),
userId: user1.id,
status: BookingStatus.ACCEPTED,
},
});
const booking2 = await prisma.booking.create({
data: {
uid: `booking-${uuidv4()}`,
title: "Support Booking",
startTime: new Date(),
endTime: new Date(Date.now() + 60 * 60 * 1000),
userId: user2.id,
status: BookingStatus.ACCEPTED,
},
});
// Create form responses with bookings
await prisma.app_RoutingForms_FormResponse.create({
data: {
formFillerId: "filler-1",
formId: form.id,
response: {
[fieldIds[0]]: { label: "Name", value: "John Doe" },
[fieldIds[1]]: { label: "Email", value: "john@example.com" },
[fieldIds[2]]: { label: "Department", value: "Sales" },
},
routedToBookingUid: booking1.uid,
},
});
await prisma.app_RoutingForms_FormResponse.create({
data: {
formFillerId: "filler-2",
formId: form.id,
response: {
[fieldIds[0]]: { label: "Name", value: "Jane Smith" },
[fieldIds[1]]: { label: "Email", value: "jane@example.com" },
[fieldIds[2]]: { label: "Department", value: "Support" },
},
routedToBookingUid: booking2.uid,
},
});
// Create a response without booking (for "Failed Bookings" chart)
await prisma.app_RoutingForms_FormResponse.create({
data: {
formFillerId: "filler-3",
formId: form.id,
response: {
[fieldIds[0]]: { label: "Name", value: "Bob Wilson" },
[fieldIds[1]]: { label: "Email", value: "bob@example.com" },
[fieldIds[2]]: { label: "Department", value: "Sales" },
},
routedToBookingUid: null,
},
});
// Navigate to insights routing page
await page.goto("/insights/routing");
await expect(page).toHaveURL(/\/insights\/routing/);
// Wait for the page to be fully loaded
await page.waitForLoadState("networkidle");
// Get all chart IDs before checking
const chartIds = await getAllChartIds(page);
console.log(`[Routing] Found ${chartIds.length} charts: ${chartIds.join(", ")}`);
// Assert all charts load successfully
await expectAllChartsToLoad(page, 20000); // 20 second timeout for all charts
// Verify specific expected charts are present and loaded
const expectedCharts = ["stats", "routing-funnel", "routed-to-per-period", "failed-bookings-by-field"];
for (const chartId of expectedCharts) {
const chart = page.locator(`[data-testid="chart-card"][data-chart-id="${chartId}"]`);
await expect(chart).toBeVisible();
await expect(chart).toHaveAttribute("data-loading-state", "loaded");
}
// Cleanup
await prisma.booking.delete({ where: { id: booking1.id } });
await prisma.booking.delete({ where: { id: booking2.id } });
});
});
test.describe("Bookings Insights", () => {
test("all charts should load successfully with booking data", async ({
page,
users,
bookings,
prisma,
}) => {
// Setup: Create users with team membership
const owner = await users.create({ name: "Chart Test Owner" });
const member = await users.create({ name: "Chart Test Member" });
// Create team and add both users
const team = await prisma.team.create({
data: {
name: "test-insights-bookings",
slug: `test-insights-bookings-${Date.now()}`,
},
});
await prisma.membership.createMany({
data: [
{
userId: owner.id,
teamId: team.id,
accepted: true,
role: "ADMIN",
},
{
userId: member.id,
teamId: team.id,
accepted: true,
role: "MEMBER",
},
],
});
await owner.apiLogin();
// Create some bookings for the user
const now = new Date();
const tomorrow = new Date(now);
tomorrow.setDate(tomorrow.getDate() + 1);
const dayAfterTomorrow = new Date(tomorrow);
dayAfterTomorrow.setDate(dayAfterTomorrow.getDate() + 1);
const threeDaysFromNow = new Date(dayAfterTomorrow);
threeDaysFromNow.setDate(threeDaysFromNow.getDate() + 1);
// Create bookings at different times to ensure unique idempotency keys
await bookings.create(owner.id, owner.username, owner.eventTypes[0].id, {
status: BookingStatus.ACCEPTED,
startTime: now,
endTime: tomorrow,
});
await bookings.create(owner.id, owner.username, owner.eventTypes[0].id, {
status: BookingStatus.ACCEPTED,
startTime: tomorrow,
endTime: dayAfterTomorrow,
});
await bookings.create(owner.id, owner.username, owner.eventTypes[0].id, {
status: BookingStatus.CANCELLED,
startTime: dayAfterTomorrow,
endTime: threeDaysFromNow,
});
// Navigate to insights bookings page
await page.goto("/insights");
await expect(page).toHaveURL(/\/insights/);
// Wait for the page to be fully loaded
await page.waitForLoadState("networkidle");
// Get all chart IDs before checking
const chartIds = await getAllChartIds(page);
console.log(`[Bookings] Found ${chartIds.length} charts: ${chartIds.join(", ")}`);
// Assert all charts load successfully
await expectAllChartsToLoad(page, 20000); // 20 second timeout for all charts
// Verify specific expected charts are present and loaded
const expectedCharts = [
"events",
"performance",
"event-trends",
"no-show-hosts-over-time",
"csat-over-time",
"bookings-by-hour",
"average-event-duration",
];
for (const chartId of expectedCharts) {
const chart = page.locator(`[data-testid="chart-card"][data-chart-id="${chartId}"]`);
// Chart should exist and be loaded
await expect(chart).toHaveCount(1);
await expect(chart).toHaveAttribute("data-loading-state", "loaded");
}
// Verify no charts are in error state
const errorCharts = page.locator('[data-testid="chart-card"][data-loading-state="error"]');
await expect(errorCharts).toHaveCount(0, { timeout: 0 });
});
});
});