Add tests 🧪🚢
This commit is contained in:
parent
ba8615e062
commit
8e9b2266ac
52 changed files with 3537 additions and 1 deletions
31
apps/main/src/components/AnimatedBackground.test.tsx
Normal file
31
apps/main/src/components/AnimatedBackground.test.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { AnimatedBackground } from "./AnimatedBackground";
|
||||
|
||||
describe("AnimatedBackground", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = render(<AnimatedBackground />);
|
||||
expect(container.firstChild).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders multiple animated logo images", () => {
|
||||
render(<AnimatedBackground />);
|
||||
const images = screen.getAllByAltText("Xtablo");
|
||||
expect(images.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("has pointer-events-none class to prevent interaction", () => {
|
||||
const { container } = render(<AnimatedBackground />);
|
||||
const wrapper = container.firstChild as HTMLElement;
|
||||
expect(wrapper).toHaveClass("pointer-events-none");
|
||||
});
|
||||
|
||||
it("has absolute positioning", () => {
|
||||
const { container } = render(<AnimatedBackground />);
|
||||
const wrapper = container.firstChild as HTMLElement;
|
||||
expect(wrapper).toHaveClass("absolute");
|
||||
expect(wrapper).toHaveClass("inset-0");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
124
apps/main/src/components/AvailabilityCard.test.tsx
Normal file
124
apps/main/src/components/AvailabilityCard.test.tsx
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { AvailabilityCard } from "./AvailabilityCard";
|
||||
|
||||
describe("AvailabilityCard", () => {
|
||||
const defaultProps = {
|
||||
day: 0, // Monday
|
||||
enabled: true,
|
||||
onEnabledChange: vi.fn(),
|
||||
timeRanges: [{ start: "09:00", end: "17:00" }],
|
||||
onTimeRangesChange: vi.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders without crashing", () => {
|
||||
render(<AvailabilityCard {...defaultProps} />);
|
||||
expect(screen.getByText("Lundi")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays the correct day name", () => {
|
||||
render(<AvailabilityCard {...defaultProps} day={2} />);
|
||||
expect(screen.getByText("Mercredi")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows enabled state correctly", () => {
|
||||
render(<AvailabilityCard {...defaultProps} />);
|
||||
expect(screen.getByText("Disponible")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows disabled state correctly", () => {
|
||||
render(<AvailabilityCard {...defaultProps} enabled={false} />);
|
||||
expect(screen.getByText("Indisponible")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls onEnabledChange when switch is toggled", () => {
|
||||
const onEnabledChange = vi.fn();
|
||||
render(<AvailabilityCard {...defaultProps} onEnabledChange={onEnabledChange} />);
|
||||
const switchElement = screen.getByRole("switch");
|
||||
fireEvent.click(switchElement);
|
||||
expect(onEnabledChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("displays time ranges", () => {
|
||||
render(<AvailabilityCard {...defaultProps} />);
|
||||
expect(screen.getByDisplayValue("09:00")).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue("17:00")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays multiple time ranges", () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
timeRanges: [
|
||||
{ start: "09:00", end: "12:00" },
|
||||
{ start: "14:00", end: "17:00" },
|
||||
],
|
||||
};
|
||||
render(<AvailabilityCard {...props} />);
|
||||
expect(screen.getByDisplayValue("09:00")).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue("12:00")).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue("14:00")).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue("17:00")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows add button when less than 3 time ranges", () => {
|
||||
render(<AvailabilityCard {...defaultProps} />);
|
||||
expect(screen.getByText("Ajouter une plage horaire")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("adds time range when add button is clicked", () => {
|
||||
const onTimeRangesChange = vi.fn();
|
||||
render(<AvailabilityCard {...defaultProps} onTimeRangesChange={onTimeRangesChange} />);
|
||||
fireEvent.click(screen.getByText("Ajouter une plage horaire"));
|
||||
expect(onTimeRangesChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows delete button when multiple time ranges", () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
timeRanges: [
|
||||
{ start: "09:00", end: "12:00" },
|
||||
{ start: "14:00", end: "17:00" },
|
||||
],
|
||||
};
|
||||
const { container } = render(<AvailabilityCard {...props} />);
|
||||
const deleteButtons = container.querySelectorAll("button svg");
|
||||
// Should have delete buttons for time ranges
|
||||
expect(deleteButtons.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("shows copy button when onCopyToOtherDays is provided", () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
onCopyToOtherDays: vi.fn(),
|
||||
};
|
||||
render(<AvailabilityCard {...props} />);
|
||||
expect(screen.getByText("Copier")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls onCopyToOtherDays when copy button is clicked", () => {
|
||||
const onCopyToOtherDays = vi.fn();
|
||||
const props = {
|
||||
...defaultProps,
|
||||
onCopyToOtherDays,
|
||||
};
|
||||
render(<AvailabilityCard {...props} />);
|
||||
fireEvent.click(screen.getByText("Copier"));
|
||||
expect(onCopyToOtherDays).toHaveBeenCalledWith(0, true, defaultProps.timeRanges);
|
||||
});
|
||||
|
||||
it("disables inputs when not enabled", () => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
enabled: false,
|
||||
};
|
||||
render(<AvailabilityCard {...props} />);
|
||||
const startInput = screen.getByDisplayValue("09:00");
|
||||
expect(startInput).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
69
apps/main/src/components/AvailabilityVisualization.test.tsx
Normal file
69
apps/main/src/components/AvailabilityVisualization.test.tsx
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { WeeklyAvailability } from "../hooks/availabilities";
|
||||
import { AvailabilityVisualization } from "./AvailabilityVisualization";
|
||||
|
||||
describe("AvailabilityVisualization", () => {
|
||||
const mockAvailabilities: WeeklyAvailability = {
|
||||
0: { enabled: true, timeRanges: [{ start: "09:00", end: "17:00" }] },
|
||||
1: { enabled: true, timeRanges: [{ start: "09:00", end: "17:00" }] },
|
||||
2: { enabled: false, timeRanges: [] },
|
||||
3: { enabled: true, timeRanges: [{ start: "10:00", end: "16:00" }] },
|
||||
4: { enabled: true, timeRanges: [{ start: "09:00", end: "17:00" }] },
|
||||
5: { enabled: false, timeRanges: [] },
|
||||
6: { enabled: false, timeRanges: [] },
|
||||
};
|
||||
|
||||
it("renders without crashing", () => {
|
||||
render(<AvailabilityVisualization draftAvailabilities={mockAvailabilities} />);
|
||||
expect(screen.getByText("Heure")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays all days of the week", () => {
|
||||
render(<AvailabilityVisualization draftAvailabilities={mockAvailabilities} />);
|
||||
expect(screen.getByText("Lundi")).toBeInTheDocument();
|
||||
expect(screen.getByText("Mardi")).toBeInTheDocument();
|
||||
expect(screen.getByText("Mercredi")).toBeInTheDocument();
|
||||
expect(screen.getByText("Jeudi")).toBeInTheDocument();
|
||||
expect(screen.getByText("Vendredi")).toBeInTheDocument();
|
||||
expect(screen.getByText("Samedi")).toBeInTheDocument();
|
||||
expect(screen.getByText("Dimanche")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays time slots", () => {
|
||||
render(<AvailabilityVisualization draftAvailabilities={mockAvailabilities} />);
|
||||
// Should show time slots from 6 AM to 11 PM
|
||||
expect(screen.getByText("09:00")).toBeInTheDocument();
|
||||
expect(screen.getByText("12:00")).toBeInTheDocument();
|
||||
expect(screen.getByText("17:00")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows availability grid", () => {
|
||||
const { container } = render(
|
||||
<AvailabilityVisualization draftAvailabilities={mockAvailabilities} />
|
||||
);
|
||||
// Check for grid structure
|
||||
expect(container.querySelector(".grid")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("accepts custom slot duration", () => {
|
||||
render(
|
||||
<AvailabilityVisualization
|
||||
draftAvailabilities={mockAvailabilities}
|
||||
slotDurationMinutes={60}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("Heure")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders calendar structure", () => {
|
||||
const { container } = render(
|
||||
<AvailabilityVisualization draftAvailabilities={mockAvailabilities} />
|
||||
);
|
||||
// Check that the calendar has proper structure
|
||||
const headers = container.querySelectorAll(".grid-cols-8");
|
||||
expect(headers.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
59
apps/main/src/components/ChannelBadge.test.tsx
Normal file
59
apps/main/src/components/ChannelBadge.test.tsx
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { ChannelBadge } from "./ChannelBadge";
|
||||
|
||||
describe("ChannelBadge", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = render(
|
||||
<ChannelBadge tablo={null} displayTitle="Test" isOnline={false} />
|
||||
);
|
||||
expect(container.firstChild).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays initials from tablo name", () => {
|
||||
const tablo = { name: "Project Alpha", color: "bg-blue-500" } as any;
|
||||
const { container } = render(
|
||||
<ChannelBadge tablo={tablo} displayTitle="Test" isOnline={false} />
|
||||
);
|
||||
expect(container).toHaveTextContent("P");
|
||||
});
|
||||
|
||||
it("displays initials from displayTitle when tablo is null", () => {
|
||||
const { container } = render(
|
||||
<ChannelBadge tablo={null} displayTitle="MyChannel" isOnline={false} />
|
||||
);
|
||||
expect(container).toHaveTextContent("M");
|
||||
});
|
||||
|
||||
it("displays default initial 'C' when no names provided", () => {
|
||||
const { container } = render(
|
||||
<ChannelBadge tablo={null} displayTitle={undefined} isOnline={false} />
|
||||
);
|
||||
expect(container).toHaveTextContent("C");
|
||||
});
|
||||
|
||||
it("shows online indicator when isOnline is true", () => {
|
||||
const { container } = render(<ChannelBadge tablo={null} displayTitle="Test" isOnline={true} />);
|
||||
const onlineIndicator = container.querySelector(".bg-green-500");
|
||||
expect(onlineIndicator).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show online indicator when isOnline is false", () => {
|
||||
const { container } = render(
|
||||
<ChannelBadge tablo={null} displayTitle="Test" isOnline={false} />
|
||||
);
|
||||
const onlineIndicator = container.querySelector(".bg-green-500");
|
||||
expect(onlineIndicator).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("applies tablo color class when provided", () => {
|
||||
const tablo = { name: "Test", color: "bg-purple-500" } as any;
|
||||
const { container } = render(
|
||||
<ChannelBadge tablo={tablo} displayTitle="Test" isOnline={false} />
|
||||
);
|
||||
const badge = container.querySelector(".bg-purple-500");
|
||||
expect(badge).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
99
apps/main/src/components/ChannelPreview.test.tsx
Normal file
99
apps/main/src/components/ChannelPreview.test.tsx
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { Channel } from "stream-chat";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ChannelPreview } from "./ChannelPreview";
|
||||
|
||||
// Mock ChannelBadge
|
||||
vi.mock("./ChannelBadge", () => ({
|
||||
ChannelBadge: ({ tablo, displayTitle, isOnline }: any) => (
|
||||
<div data-testid="channel-badge">
|
||||
{displayTitle}-{isOnline ? "online" : "offline"}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
describe("ChannelPreview", () => {
|
||||
const mockChannel = {
|
||||
id: "channel-1",
|
||||
data: {
|
||||
created_at: new Date("2024-01-01").toISOString(),
|
||||
config: {
|
||||
name: "Test Channel",
|
||||
},
|
||||
},
|
||||
state: {
|
||||
members: {},
|
||||
},
|
||||
} as unknown as Channel;
|
||||
|
||||
const mockTablo = {
|
||||
id: "tablo-1",
|
||||
name: "Test Tablo",
|
||||
color: "bg-blue-500",
|
||||
} as any;
|
||||
|
||||
const defaultProps = {
|
||||
channel: mockChannel,
|
||||
tablo: mockTablo,
|
||||
displayTitle: "Test Channel",
|
||||
};
|
||||
|
||||
it("renders without crashing", () => {
|
||||
render(<ChannelPreview {...defaultProps} />);
|
||||
expect(screen.getByText("Test Channel")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays channel title", () => {
|
||||
render(<ChannelPreview {...defaultProps} />);
|
||||
expect(screen.getByText("Test Channel")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders ChannelBadge component", () => {
|
||||
render(<ChannelPreview {...defaultProps} />);
|
||||
expect(screen.getByTestId("channel-badge")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows unread count badge when unreadCount > 0", () => {
|
||||
render(<ChannelPreview {...defaultProps} unreadCount={5} />);
|
||||
expect(screen.getByText("5")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows 99+ for unread counts over 99", () => {
|
||||
render(<ChannelPreview {...defaultProps} unreadCount={150} />);
|
||||
expect(screen.getByText("99+")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show unread badge when count is 0", () => {
|
||||
const { container } = render(<ChannelPreview {...defaultProps} unreadCount={0} />);
|
||||
expect(container.querySelector(".min-w-\\[20px\\]")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls setActiveChannel when clicked", () => {
|
||||
const setActiveChannel = vi.fn();
|
||||
render(<ChannelPreview {...defaultProps} setActiveChannel={setActiveChannel} />);
|
||||
fireEvent.click(screen.getByText("Test Channel"));
|
||||
expect(setActiveChannel).toHaveBeenCalledWith(mockChannel);
|
||||
});
|
||||
|
||||
it("highlights active channel", () => {
|
||||
const { container } = render(<ChannelPreview {...defaultProps} activeChannel={mockChannel} />);
|
||||
expect(container.querySelector(".bg-blue-50")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays latest message preview", () => {
|
||||
render(<ChannelPreview {...defaultProps} latestMessagePreview="Hello world" />);
|
||||
expect(screen.getByText("Hello world")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("applies custom className", () => {
|
||||
const { container } = render(<ChannelPreview {...defaultProps} className="custom-class" />);
|
||||
expect(container.querySelector(".custom-class")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows active indicator for active channel", () => {
|
||||
const { container } = render(<ChannelPreview {...defaultProps} activeChannel={mockChannel} />);
|
||||
expect(container.querySelector(".bg-blue-500")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
57
apps/main/src/components/ClickOutside.test.tsx
Normal file
57
apps/main/src/components/ClickOutside.test.tsx
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ClickOutside } from "./ClickOutside";
|
||||
|
||||
// Mock the useClickOutside hook
|
||||
vi.mock("@xtablo/shared/hooks/useClickOutside", () => ({
|
||||
useClickOutside: (callback: () => void) => {
|
||||
const ref = { current: null };
|
||||
// Store callback for testing
|
||||
(ref as any).callback = callback;
|
||||
return ref;
|
||||
},
|
||||
}));
|
||||
|
||||
describe("ClickOutside", () => {
|
||||
it("renders without crashing", () => {
|
||||
const onClickOutside = vi.fn();
|
||||
render(
|
||||
<ClickOutside onClickOutside={onClickOutside}>
|
||||
<div>Test Content</div>
|
||||
</ClickOutside>
|
||||
);
|
||||
expect(screen.getByText("Test Content")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders children correctly", () => {
|
||||
const onClickOutside = vi.fn();
|
||||
render(
|
||||
<ClickOutside onClickOutside={onClickOutside}>
|
||||
<button>Click Me</button>
|
||||
</ClickOutside>
|
||||
);
|
||||
expect(screen.getByText("Click Me")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("applies custom className", () => {
|
||||
const onClickOutside = vi.fn();
|
||||
const { container } = render(
|
||||
<ClickOutside onClickOutside={onClickOutside} className="custom-class">
|
||||
<div>Test Content</div>
|
||||
</ClickOutside>
|
||||
);
|
||||
expect(container.firstChild).toHaveClass("custom-class");
|
||||
});
|
||||
|
||||
it("renders with disabled prop", () => {
|
||||
const onClickOutside = vi.fn();
|
||||
render(
|
||||
<ClickOutside onClickOutside={onClickOutside} disabled={true}>
|
||||
<div>Test Content</div>
|
||||
</ClickOutside>
|
||||
);
|
||||
expect(screen.getByText("Test Content")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
121
apps/main/src/components/CreateTabloModal.test.tsx
Normal file
121
apps/main/src/components/CreateTabloModal.test.tsx
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { CreateTabloModal } from "./CreateTabloModal";
|
||||
|
||||
// Mock ClickOutside
|
||||
vi.mock("./ClickOutside", () => ({
|
||||
ClickOutside: ({ children, onClickOutside }: any) => (
|
||||
<div data-testid="click-outside" onClick={onClickOutside}>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock translations
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("CreateTabloModal", () => {
|
||||
const mockOnClose = vi.fn();
|
||||
const mockOnCreate = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders without crashing", () => {
|
||||
render(<CreateTabloModal onClose={mockOnClose} onCreate={mockOnCreate} />);
|
||||
expect(screen.getByText("modals:createTablo.title")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays name input field", () => {
|
||||
render(<CreateTabloModal onClose={mockOnClose} onCreate={mockOnCreate} />);
|
||||
expect(screen.getByPlaceholderText("modals:createTablo.namePlaceholder")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("allows typing in name input", () => {
|
||||
render(<CreateTabloModal onClose={mockOnClose} onCreate={mockOnCreate} />);
|
||||
const input = screen.getByPlaceholderText(
|
||||
"modals:createTablo.namePlaceholder"
|
||||
) as HTMLInputElement;
|
||||
fireEvent.change(input, { target: { value: "New Tablo" } });
|
||||
expect(input.value).toBe("New Tablo");
|
||||
});
|
||||
|
||||
it("calls onCreate when create button is clicked with valid name", () => {
|
||||
render(<CreateTabloModal onClose={mockOnClose} onCreate={mockOnCreate} />);
|
||||
const input = screen.getByPlaceholderText("modals:createTablo.namePlaceholder");
|
||||
fireEvent.change(input, { target: { value: "New Tablo" } });
|
||||
|
||||
const createButton = screen.getByText("common:buttons.create");
|
||||
fireEvent.click(createButton);
|
||||
|
||||
expect(mockOnCreate).toHaveBeenCalledWith({
|
||||
name: "New Tablo",
|
||||
status: "todo",
|
||||
image: null,
|
||||
color: "bg-blue-500",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not call onCreate when name is empty", () => {
|
||||
render(<CreateTabloModal onClose={mockOnClose} onCreate={mockOnCreate} />);
|
||||
const createButton = screen.getByText("common:buttons.create");
|
||||
fireEvent.click(createButton);
|
||||
|
||||
expect(mockOnCreate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("disables create button when name is empty", () => {
|
||||
render(<CreateTabloModal onClose={mockOnClose} onCreate={mockOnCreate} />);
|
||||
const createButton = screen.getByText("common:buttons.create");
|
||||
expect(createButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("calls onClose when cancel button is clicked", () => {
|
||||
render(<CreateTabloModal onClose={mockOnClose} onCreate={mockOnCreate} />);
|
||||
const cancelButton = screen.getByText("common:buttons.cancel");
|
||||
fireEvent.click(cancelButton);
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("renders StatusPicker component", () => {
|
||||
render(<CreateTabloModal onClose={mockOnClose} onCreate={mockOnCreate} />);
|
||||
expect(screen.getByText("À faire")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders ImageColorPicker component", () => {
|
||||
render(<CreateTabloModal onClose={mockOnClose} onCreate={mockOnCreate} />);
|
||||
expect(screen.getByText("Style")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("resets form after successful creation", () => {
|
||||
render(<CreateTabloModal onClose={mockOnClose} onCreate={mockOnCreate} />);
|
||||
const input = screen.getByPlaceholderText(
|
||||
"modals:createTablo.namePlaceholder"
|
||||
) as HTMLInputElement;
|
||||
fireEvent.change(input, { target: { value: "New Tablo" } });
|
||||
|
||||
const createButton = screen.getByText("common:buttons.create");
|
||||
fireEvent.click(createButton);
|
||||
|
||||
expect(input.value).toBe("");
|
||||
});
|
||||
|
||||
it("disables create button when in image mode", () => {
|
||||
render(<CreateTabloModal onClose={mockOnClose} onCreate={mockOnCreate} />);
|
||||
const input = screen.getByPlaceholderText("modals:createTablo.namePlaceholder");
|
||||
fireEvent.change(input, { target: { value: "New Tablo" } });
|
||||
|
||||
// Switch to image mode
|
||||
fireEvent.click(screen.getByText("Image (Bientôt disponible)"));
|
||||
|
||||
const createButton = screen.getByText("common:buttons.create");
|
||||
expect(createButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
111
apps/main/src/components/CustomChannelHeader.test.tsx
Normal file
111
apps/main/src/components/CustomChannelHeader.test.tsx
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { CustomChannelHeader } from "./CustomChannelHeader";
|
||||
|
||||
// Mock stream-chat-react
|
||||
vi.mock("stream-chat-react", () => ({
|
||||
ChannelHeader: ({ Avatar }: any) => (
|
||||
<div data-testid="channel-header">{Avatar && <Avatar />}</div>
|
||||
),
|
||||
useChannelStateContext: () => ({
|
||||
channel: {
|
||||
id: "test-channel",
|
||||
data: {
|
||||
config: {
|
||||
name: "Test Channel",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock ChannelBadge
|
||||
vi.mock("./ChannelBadge", () => ({
|
||||
ChannelBadge: ({ tablo, displayTitle }: any) => (
|
||||
<div data-testid="channel-badge">{displayTitle}</div>
|
||||
),
|
||||
}));
|
||||
|
||||
describe("CustomChannelHeader", () => {
|
||||
const mockTablos = [
|
||||
{
|
||||
id: "test-channel",
|
||||
name: "Test Tablo",
|
||||
color: "bg-blue-500",
|
||||
},
|
||||
] as any[];
|
||||
|
||||
it("renders without crashing", () => {
|
||||
render(<CustomChannelHeader tablos={mockTablos} />);
|
||||
expect(screen.getByTestId("channel-header")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders ChannelHeader component", () => {
|
||||
render(<CustomChannelHeader tablos={mockTablos} />);
|
||||
expect(screen.getByTestId("channel-header")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows toggle button when showToggleButton is true", () => {
|
||||
render(
|
||||
<CustomChannelHeader
|
||||
tablos={mockTablos}
|
||||
showToggleButton={true}
|
||||
onToggleChannelList={vi.fn()}
|
||||
/>
|
||||
);
|
||||
const toggleButton = screen.getByLabelText("Toggle channel list");
|
||||
expect(toggleButton).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("hides toggle button when showToggleButton is false", () => {
|
||||
render(
|
||||
<CustomChannelHeader
|
||||
tablos={mockTablos}
|
||||
showToggleButton={false}
|
||||
onToggleChannelList={vi.fn()}
|
||||
/>
|
||||
);
|
||||
const toggleButton = screen.queryByLabelText("Toggle channel list");
|
||||
expect(toggleButton).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls onToggleChannelList when toggle button is clicked", () => {
|
||||
const onToggleChannelList = vi.fn();
|
||||
render(
|
||||
<CustomChannelHeader
|
||||
tablos={mockTablos}
|
||||
onToggleChannelList={onToggleChannelList}
|
||||
showToggleButton={true}
|
||||
/>
|
||||
);
|
||||
const toggleButton = screen.getByLabelText("Toggle channel list");
|
||||
fireEvent.click(toggleButton);
|
||||
expect(onToggleChannelList).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("applies rotation class when isChannelListExpanded is true", () => {
|
||||
const { container } = render(
|
||||
<CustomChannelHeader
|
||||
tablos={mockTablos}
|
||||
onToggleChannelList={vi.fn()}
|
||||
isChannelListExpanded={true}
|
||||
showToggleButton={true}
|
||||
/>
|
||||
);
|
||||
const svg = container.querySelector(".rotate-180");
|
||||
expect(svg).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders without toggle button when onToggleChannelList is not provided", () => {
|
||||
render(<CustomChannelHeader tablos={mockTablos} />);
|
||||
const toggleButton = screen.queryByLabelText("Toggle channel list");
|
||||
expect(toggleButton).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders ChannelBadge with correct props", () => {
|
||||
render(<CustomChannelHeader tablos={mockTablos} />);
|
||||
expect(screen.getByTestId("channel-badge")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
35
apps/main/src/components/CustomLoadingOverlay.test.tsx
Normal file
35
apps/main/src/components/CustomLoadingOverlay.test.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { CustomLoadingOverlay } from "./CustomLoadingOverlay";
|
||||
|
||||
describe("CustomLoadingOverlay", () => {
|
||||
it("renders without crashing", () => {
|
||||
render(<CustomLoadingOverlay />);
|
||||
expect(screen.getByRole("presentation")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays default loading message", () => {
|
||||
render(<CustomLoadingOverlay />);
|
||||
expect(screen.getByText("Loading...")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays custom loading message", () => {
|
||||
render(<CustomLoadingOverlay loadingMessage="Please wait..." />);
|
||||
expect(screen.getByText("Please wait...")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays loading icon", () => {
|
||||
render(<CustomLoadingOverlay />);
|
||||
const icon = screen.getByAltText("Loading icon");
|
||||
expect(icon).toBeInTheDocument();
|
||||
expect(icon).toHaveAttribute("src", "/icon.jpg");
|
||||
});
|
||||
|
||||
it("has spinning animation on icon", () => {
|
||||
render(<CustomLoadingOverlay />);
|
||||
const icon = screen.getByAltText("Loading icon");
|
||||
expect(icon).toHaveClass("animate-spin");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
131
apps/main/src/components/CustomModal.test.tsx
Normal file
131
apps/main/src/components/CustomModal.test.tsx
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { CustomModal } from "./CustomModal";
|
||||
|
||||
// Mock Dialog components from shadcn/ui
|
||||
vi.mock("@xtablo/ui/components/dialog", () => ({
|
||||
Dialog: ({ open, children }: any) => (open ? <div data-testid="dialog">{children}</div> : null),
|
||||
DialogContent: ({ children, className }: any) => (
|
||||
<div data-testid="dialog-content" className={className}>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
DialogHeader: ({ children }: any) => <div data-testid="dialog-header">{children}</div>,
|
||||
DialogTitle: ({ children }: any) => <div data-testid="dialog-title">{children}</div>,
|
||||
}));
|
||||
|
||||
describe("CustomModal", () => {
|
||||
const mockOnClose = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders without crashing when open", () => {
|
||||
render(
|
||||
<CustomModal isOpen={true} onClose={mockOnClose} title="Test Modal">
|
||||
<div>Test Content</div>
|
||||
</CustomModal>
|
||||
);
|
||||
expect(screen.getByTestId("dialog")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render when closed", () => {
|
||||
render(
|
||||
<CustomModal isOpen={false} onClose={mockOnClose} title="Test Modal">
|
||||
<div>Test Content</div>
|
||||
</CustomModal>
|
||||
);
|
||||
expect(screen.queryByTestId("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays the title", () => {
|
||||
render(
|
||||
<CustomModal isOpen={true} onClose={mockOnClose} title="Test Modal Title">
|
||||
<div>Test Content</div>
|
||||
</CustomModal>
|
||||
);
|
||||
expect(screen.getByText("Test Modal Title")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders children content", () => {
|
||||
render(
|
||||
<CustomModal isOpen={true} onClose={mockOnClose} title="Test Modal">
|
||||
<div>Custom Modal Content</div>
|
||||
</CustomModal>
|
||||
);
|
||||
expect(screen.getByText("Custom Modal Content")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("applies sm width class", () => {
|
||||
render(
|
||||
<CustomModal isOpen={true} onClose={mockOnClose} title="Test Modal" width="sm">
|
||||
<div>Test Content</div>
|
||||
</CustomModal>
|
||||
);
|
||||
const content = screen.getByTestId("dialog-content");
|
||||
expect(content).toHaveClass("max-w-sm");
|
||||
});
|
||||
|
||||
it("applies md width class by default", () => {
|
||||
render(
|
||||
<CustomModal isOpen={true} onClose={mockOnClose} title="Test Modal">
|
||||
<div>Test Content</div>
|
||||
</CustomModal>
|
||||
);
|
||||
const content = screen.getByTestId("dialog-content");
|
||||
expect(content).toHaveClass("max-w-md");
|
||||
});
|
||||
|
||||
it("applies lg width class", () => {
|
||||
render(
|
||||
<CustomModal isOpen={true} onClose={mockOnClose} title="Test Modal" width="lg">
|
||||
<div>Test Content</div>
|
||||
</CustomModal>
|
||||
);
|
||||
const content = screen.getByTestId("dialog-content");
|
||||
expect(content).toHaveClass("max-w-lg");
|
||||
});
|
||||
|
||||
it("applies xl width class", () => {
|
||||
render(
|
||||
<CustomModal isOpen={true} onClose={mockOnClose} title="Test Modal" width="xl">
|
||||
<div>Test Content</div>
|
||||
</CustomModal>
|
||||
);
|
||||
const content = screen.getByTestId("dialog-content");
|
||||
expect(content).toHaveClass("max-w-xl");
|
||||
});
|
||||
|
||||
it("applies 2xl width class", () => {
|
||||
render(
|
||||
<CustomModal isOpen={true} onClose={mockOnClose} title="Test Modal" width="2xl">
|
||||
<div>Test Content</div>
|
||||
</CustomModal>
|
||||
);
|
||||
const content = screen.getByTestId("dialog-content");
|
||||
expect(content).toHaveClass("max-w-2xl");
|
||||
});
|
||||
|
||||
it("applies full width class", () => {
|
||||
render(
|
||||
<CustomModal isOpen={true} onClose={mockOnClose} title="Test Modal" width="full">
|
||||
<div>Test Content</div>
|
||||
</CustomModal>
|
||||
);
|
||||
const content = screen.getByTestId("dialog-content");
|
||||
expect(content).toHaveClass("max-w-full");
|
||||
});
|
||||
|
||||
it("applies auto width class", () => {
|
||||
render(
|
||||
<CustomModal isOpen={true} onClose={mockOnClose} title="Test Modal" width="auto">
|
||||
<div>Test Content</div>
|
||||
</CustomModal>
|
||||
);
|
||||
const content = screen.getByTestId("dialog-content");
|
||||
expect(content).toHaveClass("w-auto");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
145
apps/main/src/components/DeleteTabloModal.test.tsx
Normal file
145
apps/main/src/components/DeleteTabloModal.test.tsx
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { DeleteTabloModal } from "./DeleteTabloModal";
|
||||
|
||||
// Mock ClickOutside
|
||||
vi.mock("./ClickOutside", () => ({
|
||||
ClickOutside: ({ children }: any) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
// Mock translations
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("DeleteTabloModal", () => {
|
||||
const mockTablo = {
|
||||
id: "tablo-1",
|
||||
name: "Test Tablo",
|
||||
color: "bg-blue-500",
|
||||
} as any;
|
||||
|
||||
const mockOnClose = vi.fn();
|
||||
const mockOnConfirm = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders without crashing when tablo is provided", () => {
|
||||
render(
|
||||
<DeleteTabloModal
|
||||
tablo={mockTablo}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
isDeleting={false}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("deleteTabloModal.title")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("returns null when tablo is null", () => {
|
||||
const { container } = render(
|
||||
<DeleteTabloModal
|
||||
tablo={null}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
isDeleting={false}
|
||||
/>
|
||||
);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it("displays tablo name in confirmation message", () => {
|
||||
render(
|
||||
<DeleteTabloModal
|
||||
tablo={mockTablo}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
isDeleting={false}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText(/Test Tablo/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls onConfirm when delete button is clicked", () => {
|
||||
render(
|
||||
<DeleteTabloModal
|
||||
tablo={mockTablo}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
isDeleting={false}
|
||||
/>
|
||||
);
|
||||
const deleteButton = screen.getByText("deleteTabloModal.buttons.delete");
|
||||
fireEvent.click(deleteButton);
|
||||
expect(mockOnConfirm).toHaveBeenCalledWith("tablo-1");
|
||||
});
|
||||
|
||||
it("calls onClose when cancel button is clicked", () => {
|
||||
render(
|
||||
<DeleteTabloModal
|
||||
tablo={mockTablo}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
isDeleting={false}
|
||||
/>
|
||||
);
|
||||
const cancelButton = screen.getByText("deleteTabloModal.buttons.cancel");
|
||||
fireEvent.click(cancelButton);
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("disables buttons when isDeleting is true", () => {
|
||||
render(
|
||||
<DeleteTabloModal
|
||||
tablo={mockTablo}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
isDeleting={true}
|
||||
/>
|
||||
);
|
||||
const deleteButton = screen.getByText("deleteTabloModal.buttons.deleting");
|
||||
const cancelButton = screen.getByText("deleteTabloModal.buttons.cancel");
|
||||
expect(deleteButton).toBeDisabled();
|
||||
expect(cancelButton).toBeDisabled();
|
||||
});
|
||||
|
||||
it("shows deleting text when isDeleting is true", () => {
|
||||
render(
|
||||
<DeleteTabloModal
|
||||
tablo={mockTablo}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
isDeleting={true}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("deleteTabloModal.buttons.deleting")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows spinner when deleting", () => {
|
||||
const { container } = render(
|
||||
<DeleteTabloModal
|
||||
tablo={mockTablo}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
isDeleting={true}
|
||||
/>
|
||||
);
|
||||
expect(container.querySelector(".animate-spin")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays warning message", () => {
|
||||
render(
|
||||
<DeleteTabloModal
|
||||
tablo={mockTablo}
|
||||
onClose={mockOnClose}
|
||||
onConfirm={mockOnConfirm}
|
||||
isDeleting={false}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("deleteTabloModal.warning")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
151
apps/main/src/components/EmbedConfigModal.test.tsx
Normal file
151
apps/main/src/components/EmbedConfigModal.test.tsx
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { EmbedConfigModal } from "./EmbedConfigModal";
|
||||
|
||||
// Mock Dialog components
|
||||
vi.mock("@xtablo/ui/components/dialog", () => ({
|
||||
Dialog: ({ open, children }: any) => (open ? <div data-testid="dialog">{children}</div> : null),
|
||||
DialogContent: ({ children }: any) => <div data-testid="dialog-content">{children}</div>,
|
||||
DialogHeader: ({ children }: any) => <div>{children}</div>,
|
||||
DialogTitle: ({ children }: any) => <h2>{children}</h2>,
|
||||
DialogFooter: ({ children }: any) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
// Mock other UI components
|
||||
vi.mock("@xtablo/ui/components/button", () => ({
|
||||
Button: ({ children, onClick, variant }: any) => (
|
||||
<button onClick={onClick} data-variant={variant}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/clipboard", () => ({
|
||||
CopyButton: ({ label }: any) => <button>{label}</button>,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/label", () => ({
|
||||
Label: ({ children }: any) => <label>{children}</label>,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/select", () => ({
|
||||
Select: ({ children, onValueChange, value }: any) => (
|
||||
<div
|
||||
data-testid="select"
|
||||
data-value={value}
|
||||
onClick={() => onValueChange && onValueChange("embed")}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
SelectTrigger: ({ children }: any) => <div>{children}</div>,
|
||||
SelectValue: () => <div>Selected</div>,
|
||||
SelectContent: ({ children }: any) => <div>{children}</div>,
|
||||
SelectItem: ({ children, value }: any) => <div data-value={value}>{children}</div>,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/typography", () => ({
|
||||
TypographyMuted: ({ children }: any) => <div>{children}</div>,
|
||||
TypographyP: ({ children }: any) => <p>{children}</p>,
|
||||
}));
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("EmbedConfigModal", () => {
|
||||
const mockBuildPublicLink = vi.fn((type) => `https://example.com/${type}`);
|
||||
const mockOnClose = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders when open", () => {
|
||||
render(
|
||||
<EmbedConfigModal isOpen={true} onClose={mockOnClose} buildPublicLink={mockBuildPublicLink} />
|
||||
);
|
||||
expect(screen.getByTestId("dialog")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render when closed", () => {
|
||||
render(
|
||||
<EmbedConfigModal
|
||||
isOpen={false}
|
||||
onClose={mockOnClose}
|
||||
buildPublicLink={mockBuildPublicLink}
|
||||
/>
|
||||
);
|
||||
expect(screen.queryByTestId("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays title", () => {
|
||||
render(
|
||||
<EmbedConfigModal isOpen={true} onClose={mockOnClose} buildPublicLink={mockBuildPublicLink} />
|
||||
);
|
||||
expect(screen.getByText("embedConfigModal.title")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays configuration labels", () => {
|
||||
render(
|
||||
<EmbedConfigModal isOpen={true} onClose={mockOnClose} buildPublicLink={mockBuildPublicLink} />
|
||||
);
|
||||
expect(screen.getByText("embedConfigModal.labels.integrationType")).toBeInTheDocument();
|
||||
expect(screen.getByText("embedConfigModal.labels.buttonColor")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays preview link section", () => {
|
||||
render(
|
||||
<EmbedConfigModal isOpen={true} onClose={mockOnClose} buildPublicLink={mockBuildPublicLink} />
|
||||
);
|
||||
expect(screen.getByText("embedConfigModal.labels.previewLink")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays embed code section", () => {
|
||||
render(
|
||||
<EmbedConfigModal isOpen={true} onClose={mockOnClose} buildPublicLink={mockBuildPublicLink} />
|
||||
);
|
||||
expect(screen.getByText("embedConfigModal.labels.embedCode")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays close button", () => {
|
||||
render(
|
||||
<EmbedConfigModal isOpen={true} onClose={mockOnClose} buildPublicLink={mockBuildPublicLink} />
|
||||
);
|
||||
expect(screen.getByText("embedConfigModal.buttons.close")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays preview button", () => {
|
||||
render(
|
||||
<EmbedConfigModal isOpen={true} onClose={mockOnClose} buildPublicLink={mockBuildPublicLink} />
|
||||
);
|
||||
expect(screen.getByText("embedConfigModal.buttons.preview")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays copy button", () => {
|
||||
render(
|
||||
<EmbedConfigModal isOpen={true} onClose={mockOnClose} buildPublicLink={mockBuildPublicLink} />
|
||||
);
|
||||
expect(screen.getByText("embedConfigModal.buttons.copy")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls onClose when close button is clicked", () => {
|
||||
render(
|
||||
<EmbedConfigModal isOpen={true} onClose={mockOnClose} buildPublicLink={mockBuildPublicLink} />
|
||||
);
|
||||
fireEvent.click(screen.getByText("embedConfigModal.buttons.close"));
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls buildPublicLink to generate URL", () => {
|
||||
render(
|
||||
<EmbedConfigModal isOpen={true} onClose={mockOnClose} buildPublicLink={mockBuildPublicLink} />
|
||||
);
|
||||
// buildPublicLink should be called to generate the embed URL
|
||||
expect(mockBuildPublicLink).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
144
apps/main/src/components/EventDetailsModal.test.tsx
Normal file
144
apps/main/src/components/EventDetailsModal.test.tsx
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { EventDetailsModal } from "./EventDetailsModal";
|
||||
|
||||
// Mock CustomModal
|
||||
vi.mock("./CustomModal", () => ({
|
||||
CustomModal: ({ isOpen, children, title }: any) =>
|
||||
isOpen ? (
|
||||
<div data-testid="custom-modal">
|
||||
<div>{title}</div>
|
||||
<div>{children}</div>
|
||||
</div>
|
||||
) : null,
|
||||
}));
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
i18n: { language: "en" },
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("EventDetailsModal", () => {
|
||||
const mockEvent = {
|
||||
id: "event-1",
|
||||
title: "Test Event",
|
||||
start_date: "2024-01-15",
|
||||
start_time: "10:00:00",
|
||||
end_time: "11:00:00",
|
||||
description: "Test description",
|
||||
tablo_name: "Test Tablo",
|
||||
tablo_color: "bg-blue-500",
|
||||
} as any;
|
||||
|
||||
const mockOnClose = vi.fn();
|
||||
const mockOnEdit = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders without crashing when open", () => {
|
||||
render(<EventDetailsModal event={mockEvent} isOpen={true} onClose={mockOnClose} />);
|
||||
expect(screen.getByTestId("custom-modal")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render when closed", () => {
|
||||
render(<EventDetailsModal event={mockEvent} isOpen={false} onClose={mockOnClose} />);
|
||||
expect(screen.queryByTestId("custom-modal")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("returns null when event is null", () => {
|
||||
const { container } = render(
|
||||
<EventDetailsModal event={null} isOpen={true} onClose={mockOnClose} />
|
||||
);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it("displays event title", () => {
|
||||
render(<EventDetailsModal event={mockEvent} isOpen={true} onClose={mockOnClose} />);
|
||||
expect(screen.getByText("Test Event")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays event date and time labels", () => {
|
||||
render(<EventDetailsModal event={mockEvent} isOpen={true} onClose={mockOnClose} />);
|
||||
expect(screen.getByText("eventDetailsModal.labels.dateTime")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays tablo information", () => {
|
||||
render(<EventDetailsModal event={mockEvent} isOpen={true} onClose={mockOnClose} />);
|
||||
expect(screen.getByText("Test Tablo")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays description when present", () => {
|
||||
render(<EventDetailsModal event={mockEvent} isOpen={true} onClose={mockOnClose} />);
|
||||
expect(screen.getByText("Test description")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows close button", () => {
|
||||
render(<EventDetailsModal event={mockEvent} isOpen={true} onClose={mockOnClose} />);
|
||||
expect(screen.getByText("eventDetailsModal.buttons.close")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls onClose when close button is clicked", () => {
|
||||
render(<EventDetailsModal event={mockEvent} isOpen={true} onClose={mockOnClose} />);
|
||||
fireEvent.click(screen.getByText("eventDetailsModal.buttons.close"));
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows edit button when canEdit is true", () => {
|
||||
render(
|
||||
<EventDetailsModal
|
||||
event={mockEvent}
|
||||
isOpen={true}
|
||||
onClose={mockOnClose}
|
||||
onEdit={mockOnEdit}
|
||||
canEdit={true}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("eventDetailsModal.buttons.edit")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show edit button when canEdit is false", () => {
|
||||
render(
|
||||
<EventDetailsModal
|
||||
event={mockEvent}
|
||||
isOpen={true}
|
||||
onClose={mockOnClose}
|
||||
onEdit={mockOnEdit}
|
||||
canEdit={false}
|
||||
/>
|
||||
);
|
||||
expect(screen.queryByText("eventDetailsModal.buttons.edit")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls onEdit when edit button is clicked", () => {
|
||||
render(
|
||||
<EventDetailsModal
|
||||
event={mockEvent}
|
||||
isOpen={true}
|
||||
onClose={mockOnClose}
|
||||
onEdit={mockOnEdit}
|
||||
canEdit={true}
|
||||
/>
|
||||
);
|
||||
fireEvent.click(screen.getByText("eventDetailsModal.buttons.edit"));
|
||||
expect(mockOnEdit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("displays status badge", () => {
|
||||
render(<EventDetailsModal event={mockEvent} isOpen={true} onClose={mockOnClose} />);
|
||||
// Status badge should be rendered (upcoming, today, or past)
|
||||
const modal = screen.getByTestId("custom-modal");
|
||||
expect(modal).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("handles event without description", () => {
|
||||
const eventWithoutDesc = { ...mockEvent, description: null };
|
||||
render(<EventDetailsModal event={eventWithoutDesc} isOpen={true} onClose={mockOnClose} />);
|
||||
expect(screen.queryByText("eventDetailsModal.labels.description")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
74
apps/main/src/components/EventModal.test.tsx
Normal file
74
apps/main/src/components/EventModal.test.tsx
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { EventModal } from "./EventModal";
|
||||
|
||||
// Mock hooks and dependencies
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useParams: () => ({ event_id: undefined }),
|
||||
useSearchParams: () => [new URLSearchParams(), vi.fn()],
|
||||
useNavigate: () => vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../hooks/events", () => ({
|
||||
useEvent: () => ({ data: null }),
|
||||
useCreateEvents: () => vi.fn(),
|
||||
useUpdateEvent: () => ({ mutate: vi.fn() }),
|
||||
}));
|
||||
|
||||
vi.mock("../hooks/tablos", () => ({
|
||||
useTablosList: () => ({
|
||||
data: [{ id: "tablo-1", name: "Test Tablo" }],
|
||||
isLoading: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../providers/UserStoreProvider", () => ({
|
||||
useUser: () => ({ id: "user-1", name: "Test User" }),
|
||||
useIsReadOnlyUser: () => false,
|
||||
TestUserStoreProvider: ({ children }: any) => children,
|
||||
}));
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
i18n: { language: "en" },
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("EventModal", () => {
|
||||
it("renders in create mode", () => {
|
||||
renderWithProviders(<EventModal mode="create" />);
|
||||
expect(screen.getByText("eventModal.title.create")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders in edit mode", () => {
|
||||
renderWithProviders(<EventModal mode="edit" />);
|
||||
expect(screen.getByText("eventModal.title.edit")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays form fields", () => {
|
||||
renderWithProviders(<EventModal mode="create" />);
|
||||
expect(screen.getByText("eventModal.labels.title")).toBeInTheDocument();
|
||||
expect(screen.getByText("eventModal.labels.tablo")).toBeInTheDocument();
|
||||
expect(screen.getByText("eventModal.labels.date")).toBeInTheDocument();
|
||||
expect(screen.getByText("eventModal.labels.startTime")).toBeInTheDocument();
|
||||
expect(screen.getByText("eventModal.labels.endTime")).toBeInTheDocument();
|
||||
expect(screen.getByText("eventModal.labels.description")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays action buttons", () => {
|
||||
renderWithProviders(<EventModal mode="create" />);
|
||||
expect(screen.getByText("eventModal.buttons.cancel")).toBeInTheDocument();
|
||||
expect(screen.getByText("eventModal.buttons.save")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows edit button text in edit mode", () => {
|
||||
renderWithProviders(<EventModal mode="edit" />);
|
||||
expect(screen.getByText("eventModal.buttons.edit")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
138
apps/main/src/components/EventTypeCard.test.tsx
Normal file
138
apps/main/src/components/EventTypeCard.test.tsx
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { EventTypeCard } from "./EventTypeCard";
|
||||
|
||||
// Mock hooks
|
||||
vi.mock("../hooks/event-types", () => ({
|
||||
useEventTypes: () => ({
|
||||
toggleEventType: vi.fn(),
|
||||
deleteEventType: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../providers/UserStoreProvider", () => ({
|
||||
useUser: () => ({
|
||||
id: "test-user-id-123",
|
||||
name: "Test User",
|
||||
}),
|
||||
TestUserStoreProvider: ({ children }: any) => children,
|
||||
}));
|
||||
|
||||
vi.mock("../lib/env", () => ({
|
||||
isDev: false,
|
||||
}));
|
||||
|
||||
// Mock translations
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("EventTypeCard", () => {
|
||||
const mockEventType = {
|
||||
id: "1",
|
||||
name: "30 Min Meeting",
|
||||
duration: 30,
|
||||
isActive: true,
|
||||
standardName: "30-min-meeting",
|
||||
bufferTime: 10,
|
||||
maxBookingsPerDay: 5,
|
||||
minAdvanceBooking: { value: 1, unit: "hours" as const },
|
||||
};
|
||||
|
||||
const handleEditEventType = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders without crashing", () => {
|
||||
renderWithProviders(
|
||||
<EventTypeCard eventType={mockEventType} handleEditEventType={handleEditEventType} />
|
||||
);
|
||||
expect(screen.getByText("30 Min Meeting")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays event type name", () => {
|
||||
renderWithProviders(
|
||||
<EventTypeCard eventType={mockEventType} handleEditEventType={handleEditEventType} />
|
||||
);
|
||||
expect(screen.getByText("30 Min Meeting")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays duration information", () => {
|
||||
renderWithProviders(
|
||||
<EventTypeCard eventType={mockEventType} handleEditEventType={handleEditEventType} />
|
||||
);
|
||||
expect(screen.getByText("eventTypeCard.duration")).toBeInTheDocument();
|
||||
// Duration is displayed as "30 eventTypeCard.minutes"
|
||||
const durationElements = screen.getAllByText((content, element) => {
|
||||
return (
|
||||
(element?.textContent?.includes("30") &&
|
||||
element?.textContent?.includes("eventTypeCard.minutes")) ||
|
||||
false
|
||||
);
|
||||
});
|
||||
expect(durationElements.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("displays buffer time when present", () => {
|
||||
renderWithProviders(
|
||||
<EventTypeCard eventType={mockEventType} handleEditEventType={handleEditEventType} />
|
||||
);
|
||||
expect(screen.getByText("eventTypeCard.bufferTime")).toBeInTheDocument();
|
||||
expect(screen.getByText(/10/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays max bookings per day when present", () => {
|
||||
renderWithProviders(
|
||||
<EventTypeCard eventType={mockEventType} handleEditEventType={handleEditEventType} />
|
||||
);
|
||||
expect(screen.getByText("5")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows active status when isActive is true", () => {
|
||||
renderWithProviders(
|
||||
<EventTypeCard eventType={mockEventType} handleEditEventType={handleEditEventType} />
|
||||
);
|
||||
expect(screen.getByText("eventTypeCard.active")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows inactive status when isActive is false", () => {
|
||||
const inactiveEventType = { ...mockEventType, isActive: false };
|
||||
renderWithProviders(
|
||||
<EventTypeCard eventType={inactiveEventType} handleEditEventType={handleEditEventType} />
|
||||
);
|
||||
expect(screen.getByText("eventTypeCard.inactive")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls handleEditEventType when edit button is clicked", () => {
|
||||
renderWithProviders(
|
||||
<EventTypeCard eventType={mockEventType} handleEditEventType={handleEditEventType} />
|
||||
);
|
||||
const editButton = screen.getByLabelText("eventTypeCard.aria.edit");
|
||||
fireEvent.click(editButton);
|
||||
expect(handleEditEventType).toHaveBeenCalledWith(mockEventType.id, mockEventType);
|
||||
});
|
||||
|
||||
it("has settings, preview, edit, and delete buttons", () => {
|
||||
renderWithProviders(
|
||||
<EventTypeCard eventType={mockEventType} handleEditEventType={handleEditEventType} />
|
||||
);
|
||||
expect(screen.getByLabelText("eventTypeCard.aria.settings")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("eventTypeCard.aria.preview")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("eventTypeCard.aria.edit")).toBeInTheDocument();
|
||||
expect(screen.getByLabelText("eventTypeCard.aria.delete")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("applies opacity when inactive", () => {
|
||||
const inactiveEventType = { ...mockEventType, isActive: false };
|
||||
const { container } = renderWithProviders(
|
||||
<EventTypeCard eventType={inactiveEventType} handleEditEventType={handleEditEventType} />
|
||||
);
|
||||
const card = container.querySelector(".opacity-60");
|
||||
expect(card).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
176
apps/main/src/components/EventTypeModal.test.tsx
Normal file
176
apps/main/src/components/EventTypeModal.test.tsx
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { EventTypeConfig } from "../hooks/event-types";
|
||||
import { EventTypeModal } from "./EventTypeModal";
|
||||
|
||||
// Mock Dialog components
|
||||
vi.mock("@xtablo/ui/components/dialog", () => ({
|
||||
Dialog: ({ open, children }: any) => (open ? <div data-testid="dialog">{children}</div> : null),
|
||||
DialogContent: ({ children }: any) => <div>{children}</div>,
|
||||
DialogHeader: ({ children }: any) => <div>{children}</div>,
|
||||
DialogTitle: ({ children }: any) => <h2>{children}</h2>,
|
||||
DialogFooter: ({ children }: any) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
// Mock other components
|
||||
vi.mock("@xtablo/ui/components/button", () => ({
|
||||
Button: ({ children, onClick }: any) => <button onClick={onClick}>{children}</button>,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/input", () => ({
|
||||
Input: ({ value, onChange, type }: any) => (
|
||||
<input value={value} onChange={onChange} type={type} />
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/label", () => ({
|
||||
Label: ({ children }: any) => <label>{children}</label>,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/textarea", () => ({
|
||||
Textarea: ({ value, onChange }: any) => <textarea value={value} onChange={onChange} />,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/select", () => ({
|
||||
Select: ({ children, onValueChange }: any) => (
|
||||
<div data-testid="select" onClick={() => onValueChange && onValueChange("hours")}>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
SelectTrigger: ({ children }: any) => <div>{children}</div>,
|
||||
SelectValue: () => <div>Selected</div>,
|
||||
SelectContent: ({ children }: any) => <div>{children}</div>,
|
||||
SelectItem: ({ children, value }: any) => <div data-value={value}>{children}</div>,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/field", () => ({
|
||||
FieldDescription: ({ children }: any) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("EventTypeModal", () => {
|
||||
const mockFormData: EventTypeConfig = {
|
||||
name: "30 Min Meeting",
|
||||
description: "Test description",
|
||||
duration: 30,
|
||||
bufferTime: 0,
|
||||
};
|
||||
|
||||
const mockSetIsModalOpen = vi.fn();
|
||||
const mockSetFormData = vi.fn();
|
||||
const mockHandleSaveEventType = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders when open", () => {
|
||||
render(
|
||||
<EventTypeModal
|
||||
isModalOpen={true}
|
||||
setIsModalOpen={mockSetIsModalOpen}
|
||||
editingEventType={null}
|
||||
formData={mockFormData}
|
||||
setFormData={mockSetFormData}
|
||||
handleSaveEventType={mockHandleSaveEventType}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByTestId("dialog")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render when closed", () => {
|
||||
render(
|
||||
<EventTypeModal
|
||||
isModalOpen={false}
|
||||
setIsModalOpen={mockSetIsModalOpen}
|
||||
editingEventType={null}
|
||||
formData={mockFormData}
|
||||
setFormData={mockSetFormData}
|
||||
handleSaveEventType={mockHandleSaveEventType}
|
||||
/>
|
||||
);
|
||||
expect(screen.queryByTestId("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows create title when editingEventType is null", () => {
|
||||
render(
|
||||
<EventTypeModal
|
||||
isModalOpen={true}
|
||||
setIsModalOpen={mockSetIsModalOpen}
|
||||
editingEventType={null}
|
||||
formData={mockFormData}
|
||||
setFormData={mockSetFormData}
|
||||
handleSaveEventType={mockHandleSaveEventType}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("eventTypeModal.title.create")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows edit title when editingEventType is provided", () => {
|
||||
render(
|
||||
<EventTypeModal
|
||||
isModalOpen={true}
|
||||
setIsModalOpen={mockSetIsModalOpen}
|
||||
editingEventType={mockFormData}
|
||||
formData={mockFormData}
|
||||
setFormData={mockSetFormData}
|
||||
handleSaveEventType={mockHandleSaveEventType}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("eventTypeModal.title.edit")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays form fields", () => {
|
||||
render(
|
||||
<EventTypeModal
|
||||
isModalOpen={true}
|
||||
setIsModalOpen={mockSetIsModalOpen}
|
||||
editingEventType={null}
|
||||
formData={mockFormData}
|
||||
setFormData={mockSetFormData}
|
||||
handleSaveEventType={mockHandleSaveEventType}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("eventTypeModal.labels.name")).toBeInTheDocument();
|
||||
expect(screen.getByText("eventTypeModal.labels.description")).toBeInTheDocument();
|
||||
expect(screen.getByText("eventTypeModal.sections.timing")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays name input with correct value", () => {
|
||||
render(
|
||||
<EventTypeModal
|
||||
isModalOpen={true}
|
||||
setIsModalOpen={mockSetIsModalOpen}
|
||||
editingEventType={null}
|
||||
formData={mockFormData}
|
||||
setFormData={mockSetFormData}
|
||||
handleSaveEventType={mockHandleSaveEventType}
|
||||
/>
|
||||
);
|
||||
const inputs = screen.getAllByDisplayValue("30 Min Meeting");
|
||||
expect(inputs.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("calls setFormData when name is changed", () => {
|
||||
render(
|
||||
<EventTypeModal
|
||||
isModalOpen={true}
|
||||
setIsModalOpen={mockSetIsModalOpen}
|
||||
editingEventType={null}
|
||||
formData={mockFormData}
|
||||
setFormData={mockSetFormData}
|
||||
handleSaveEventType={mockHandleSaveEventType}
|
||||
/>
|
||||
);
|
||||
const inputs = screen.getAllByDisplayValue("30 Min Meeting");
|
||||
fireEvent.change(inputs[0], { target: { value: "New Name" } });
|
||||
expect(mockSetFormData).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
118
apps/main/src/components/ExceptionModal.test.tsx
Normal file
118
apps/main/src/components/ExceptionModal.test.tsx
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ExceptionModal } from "./ExceptionModal";
|
||||
|
||||
// Mock Dialog components
|
||||
vi.mock("@xtablo/ui/components/dialog", () => ({
|
||||
Dialog: ({ open, children }: any) => (open ? <div data-testid="dialog">{children}</div> : null),
|
||||
DialogContent: ({ children }: any) => <div>{children}</div>,
|
||||
DialogHeader: ({ children }: any) => <div>{children}</div>,
|
||||
DialogTitle: ({ children }: any) => <h2>{children}</h2>,
|
||||
DialogDescription: ({ children }: any) => <p>{children}</p>,
|
||||
DialogFooter: ({ children }: any) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
// Mock other components
|
||||
vi.mock("@xtablo/ui/components/button", () => ({
|
||||
Button: ({ children, onClick, type }: any) => (
|
||||
<button onClick={onClick} type={type}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/button-group", () => ({
|
||||
ButtonGroup: ({ children }: any) => <div data-testid="button-group">{children}</div>,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/label", () => ({
|
||||
Label: ({ children }: any) => <label>{children}</label>,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/date-picker", () => ({
|
||||
DatePickerV1: ({ value, onChange }: any) => (
|
||||
<input
|
||||
type="date"
|
||||
value={value?.toISOString().split("T")[0]}
|
||||
onChange={(e) => onChange && onChange(new Date(e.target.value))}
|
||||
data-testid="date-picker"
|
||||
/>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/time-input", () => ({
|
||||
TimeInput: ({ value, onChange }: any) => (
|
||||
<input
|
||||
type="time"
|
||||
value={value}
|
||||
onChange={(e) => onChange && onChange(e.target.value)}
|
||||
data-testid="time-input"
|
||||
/>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("ExceptionModal", () => {
|
||||
const mockOnClose = vi.fn();
|
||||
const mockOnSubmit = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders when open", () => {
|
||||
render(<ExceptionModal isOpen={true} onClose={mockOnClose} onSubmit={mockOnSubmit} />);
|
||||
expect(screen.getByTestId("dialog")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render when closed", () => {
|
||||
render(<ExceptionModal isOpen={false} onClose={mockOnClose} onSubmit={mockOnSubmit} />);
|
||||
expect(screen.queryByTestId("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays title", () => {
|
||||
render(<ExceptionModal isOpen={true} onClose={mockOnClose} onSubmit={mockOnSubmit} />);
|
||||
expect(screen.getByText("exceptionModal.title")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays description", () => {
|
||||
render(<ExceptionModal isOpen={true} onClose={mockOnClose} onSubmit={mockOnSubmit} />);
|
||||
expect(screen.getByText("exceptionModal.description")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays exception type label", () => {
|
||||
render(<ExceptionModal isOpen={true} onClose={mockOnClose} onSubmit={mockOnSubmit} />);
|
||||
expect(screen.getByText("exceptionModal.labels.exceptionType")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays exception type buttons", () => {
|
||||
render(<ExceptionModal isOpen={true} onClose={mockOnClose} onSubmit={mockOnSubmit} />);
|
||||
expect(screen.getByText("exceptionModal.types.allDay")).toBeInTheDocument();
|
||||
expect(screen.getByText("exceptionModal.types.customHours")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays date picker", () => {
|
||||
render(<ExceptionModal isOpen={true} onClose={mockOnClose} onSubmit={mockOnSubmit} />);
|
||||
expect(screen.getByTestId("date-picker")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders button group for exception types", () => {
|
||||
render(<ExceptionModal isOpen={true} onClose={mockOnClose} onSubmit={mockOnSubmit} />);
|
||||
expect(screen.getByTestId("button-group")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays cancel button", () => {
|
||||
render(<ExceptionModal isOpen={true} onClose={mockOnClose} onSubmit={mockOnSubmit} />);
|
||||
expect(screen.getByText("exceptionModal.buttons.cancel")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays add button", () => {
|
||||
render(<ExceptionModal isOpen={true} onClose={mockOnClose} onSubmit={mockOnSubmit} />);
|
||||
expect(screen.getByText("exceptionModal.buttons.add")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
112
apps/main/src/components/ImageColorPicker.test.tsx
Normal file
112
apps/main/src/components/ImageColorPicker.test.tsx
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ImageColorPicker } from "./ImageColorPicker";
|
||||
|
||||
describe("ImageColorPicker", () => {
|
||||
it("renders without crashing", () => {
|
||||
const props = {
|
||||
creationMode: "color" as const,
|
||||
setCreationMode: vi.fn(),
|
||||
selectedColor: "bg-blue-500",
|
||||
setSelectedColor: vi.fn(),
|
||||
};
|
||||
render(<ImageColorPicker {...props} />);
|
||||
expect(screen.getByText("Style")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders mode toggle buttons", () => {
|
||||
const props = {
|
||||
creationMode: "color" as const,
|
||||
setCreationMode: vi.fn(),
|
||||
selectedColor: "bg-blue-500",
|
||||
setSelectedColor: vi.fn(),
|
||||
};
|
||||
render(<ImageColorPicker {...props} />);
|
||||
expect(screen.getByText("Image (Bientôt disponible)")).toBeInTheDocument();
|
||||
expect(screen.getAllByText("Couleur").length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("calls setCreationMode when image button is clicked", () => {
|
||||
const setCreationMode = vi.fn();
|
||||
const props = {
|
||||
creationMode: "color" as const,
|
||||
setCreationMode,
|
||||
selectedColor: "bg-blue-500",
|
||||
setSelectedColor: vi.fn(),
|
||||
};
|
||||
render(<ImageColorPicker {...props} />);
|
||||
fireEvent.click(screen.getByText("Image (Bientôt disponible)"));
|
||||
expect(setCreationMode).toHaveBeenCalledWith("image");
|
||||
});
|
||||
|
||||
it("calls setCreationMode when color button is clicked", () => {
|
||||
const setCreationMode = vi.fn();
|
||||
const props = {
|
||||
creationMode: "image" as const,
|
||||
setCreationMode,
|
||||
selectedColor: "bg-blue-500",
|
||||
setSelectedColor: vi.fn(),
|
||||
};
|
||||
render(<ImageColorPicker {...props} />);
|
||||
fireEvent.click(screen.getByText("Couleur"));
|
||||
expect(setCreationMode).toHaveBeenCalledWith("color");
|
||||
});
|
||||
|
||||
it("shows color picker when in color mode", () => {
|
||||
const props = {
|
||||
creationMode: "color" as const,
|
||||
setCreationMode: vi.fn(),
|
||||
selectedColor: "bg-blue-500",
|
||||
setSelectedColor: vi.fn(),
|
||||
};
|
||||
render(<ImageColorPicker {...props} />);
|
||||
expect(screen.getAllByText("Couleur").length).toBeGreaterThan(0);
|
||||
// Check for color buttons - there should be 10 available colors
|
||||
const colorButtons = screen
|
||||
.getAllByRole("button")
|
||||
.filter((btn) => btn.className.includes("bg-"));
|
||||
expect(colorButtons.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("shows image upload placeholder when in image mode", () => {
|
||||
const props = {
|
||||
creationMode: "image" as const,
|
||||
setCreationMode: vi.fn(),
|
||||
selectedColor: "bg-blue-500",
|
||||
setSelectedColor: vi.fn(),
|
||||
};
|
||||
render(<ImageColorPicker {...props} />);
|
||||
expect(screen.getByText("Import d'images")).toBeInTheDocument();
|
||||
expect(screen.getByText("Bientôt disponible")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls setSelectedColor when a color is clicked", () => {
|
||||
const setSelectedColor = vi.fn();
|
||||
const props = {
|
||||
creationMode: "color" as const,
|
||||
setCreationMode: vi.fn(),
|
||||
selectedColor: "bg-blue-500",
|
||||
setSelectedColor,
|
||||
};
|
||||
const { container } = render(<ImageColorPicker {...props} />);
|
||||
|
||||
// Find a color button that's not the selected one
|
||||
const greenButton = container.querySelector(".bg-green-500");
|
||||
if (greenButton) {
|
||||
fireEvent.click(greenButton);
|
||||
expect(setSelectedColor).toHaveBeenCalledWith("bg-green-500");
|
||||
}
|
||||
});
|
||||
|
||||
it("highlights the selected color", () => {
|
||||
const props = {
|
||||
creationMode: "color" as const,
|
||||
setCreationMode: vi.fn(),
|
||||
selectedColor: "bg-blue-500",
|
||||
setSelectedColor: vi.fn(),
|
||||
};
|
||||
const { container } = render(<ImageColorPicker {...props} />);
|
||||
const selectedButton = container.querySelector(".bg-blue-500");
|
||||
expect(selectedButton).toHaveTextContent("✓");
|
||||
});
|
||||
});
|
||||
183
apps/main/src/components/ImageCropDialog.test.tsx
Normal file
183
apps/main/src/components/ImageCropDialog.test.tsx
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ImageCropDialog } from "./ImageCropDialog";
|
||||
|
||||
// Mock react-easy-crop
|
||||
vi.mock("react-easy-crop", () => ({
|
||||
default: ({ onCropChange, onZoomChange }: any) => (
|
||||
<div data-testid="cropper">
|
||||
<button onClick={() => onCropChange({ x: 10, y: 10 })}>Change Crop</button>
|
||||
<button onClick={() => onZoomChange(2)}>Change Zoom</button>
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
// Mock Dialog components
|
||||
vi.mock("@xtablo/ui/components/dialog", () => ({
|
||||
Dialog: ({ open, children }: any) => (open ? <div data-testid="dialog">{children}</div> : null),
|
||||
DialogContent: ({ children }: any) => <div data-testid="dialog-content">{children}</div>,
|
||||
DialogHeader: ({ children }: any) => <div>{children}</div>,
|
||||
DialogTitle: ({ children }: any) => <h2>{children}</h2>,
|
||||
DialogDescription: ({ children }: any) => <p>{children}</p>,
|
||||
DialogFooter: ({ children }: any) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
// Mock other UI components
|
||||
vi.mock("@xtablo/ui/components/button", () => ({
|
||||
Button: ({ children, onClick, disabled }: any) => (
|
||||
<button onClick={onClick} disabled={disabled}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/label", () => ({
|
||||
Label: ({ children, htmlFor }: any) => <label htmlFor={htmlFor}>{children}</label>,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/slider", () => ({
|
||||
Slider: ({ value, onValueChange }: any) => (
|
||||
<input
|
||||
type="range"
|
||||
value={value[0]}
|
||||
onChange={(e) => onValueChange([Number.parseFloat(e.target.value)])}
|
||||
data-testid="zoom-slider"
|
||||
/>
|
||||
),
|
||||
}));
|
||||
|
||||
describe("ImageCropDialog", () => {
|
||||
const mockOnOpenChange = vi.fn();
|
||||
const mockOnCropComplete = vi.fn();
|
||||
const mockImageSrc = "data:image/png;base64,test";
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders without crashing when open", () => {
|
||||
render(
|
||||
<ImageCropDialog
|
||||
open={true}
|
||||
onOpenChange={mockOnOpenChange}
|
||||
imageSrc={mockImageSrc}
|
||||
onCropComplete={mockOnCropComplete}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByTestId("dialog")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render when closed", () => {
|
||||
render(
|
||||
<ImageCropDialog
|
||||
open={false}
|
||||
onOpenChange={mockOnOpenChange}
|
||||
imageSrc={mockImageSrc}
|
||||
onCropComplete={mockOnCropComplete}
|
||||
/>
|
||||
);
|
||||
expect(screen.queryByTestId("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays title", () => {
|
||||
render(
|
||||
<ImageCropDialog
|
||||
open={true}
|
||||
onOpenChange={mockOnOpenChange}
|
||||
imageSrc={mockImageSrc}
|
||||
onCropComplete={mockOnCropComplete}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("Recadrer l'image")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays description", () => {
|
||||
render(
|
||||
<ImageCropDialog
|
||||
open={true}
|
||||
onOpenChange={mockOnOpenChange}
|
||||
imageSrc={mockImageSrc}
|
||||
onCropComplete={mockOnCropComplete}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText(/Ajustez la position et le zoom/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders cropper component", () => {
|
||||
render(
|
||||
<ImageCropDialog
|
||||
open={true}
|
||||
onOpenChange={mockOnOpenChange}
|
||||
imageSrc={mockImageSrc}
|
||||
onCropComplete={mockOnCropComplete}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByTestId("cropper")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders zoom slider", () => {
|
||||
render(
|
||||
<ImageCropDialog
|
||||
open={true}
|
||||
onOpenChange={mockOnOpenChange}
|
||||
imageSrc={mockImageSrc}
|
||||
onCropComplete={mockOnCropComplete}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("Zoom")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("zoom-slider")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders cancel button", () => {
|
||||
render(
|
||||
<ImageCropDialog
|
||||
open={true}
|
||||
onOpenChange={mockOnOpenChange}
|
||||
imageSrc={mockImageSrc}
|
||||
onCropComplete={mockOnCropComplete}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("Annuler")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders confirm button", () => {
|
||||
render(
|
||||
<ImageCropDialog
|
||||
open={true}
|
||||
onOpenChange={mockOnOpenChange}
|
||||
imageSrc={mockImageSrc}
|
||||
onCropComplete={mockOnCropComplete}
|
||||
/>
|
||||
);
|
||||
expect(screen.getByText("Confirmer")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls onOpenChange when cancel button is clicked", () => {
|
||||
render(
|
||||
<ImageCropDialog
|
||||
open={true}
|
||||
onOpenChange={mockOnOpenChange}
|
||||
imageSrc={mockImageSrc}
|
||||
onCropComplete={mockOnCropComplete}
|
||||
/>
|
||||
);
|
||||
fireEvent.click(screen.getByText("Annuler"));
|
||||
expect(mockOnOpenChange).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("allows zoom adjustment", () => {
|
||||
render(
|
||||
<ImageCropDialog
|
||||
open={true}
|
||||
onOpenChange={mockOnOpenChange}
|
||||
imageSrc={mockImageSrc}
|
||||
onCropComplete={mockOnCropComplete}
|
||||
/>
|
||||
);
|
||||
const slider = screen.getByTestId("zoom-slider");
|
||||
fireEvent.change(slider, { target: { value: "2" } });
|
||||
expect(slider).toHaveValue("2");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
102
apps/main/src/components/ImportICSModal.test.tsx
Normal file
102
apps/main/src/components/ImportICSModal.test.tsx
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ImportICSModal } from "./ImportICSModal";
|
||||
|
||||
// Mock hooks
|
||||
vi.mock("../hooks/tablos", () => ({
|
||||
useTablosList: () => ({
|
||||
data: [{ id: "tablo-1", name: "Tablo 1" }],
|
||||
isLoading: false,
|
||||
}),
|
||||
useCreateTablo: () => ({ mutate: vi.fn() }),
|
||||
}));
|
||||
|
||||
vi.mock("../hooks/events", () => ({
|
||||
useCreateEvents: () => vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("../providers/UserStoreProvider", () => ({
|
||||
useUser: () => ({ id: "user-1", name: "Test User" }),
|
||||
}));
|
||||
|
||||
// Mock Select component
|
||||
vi.mock("@xtablo/ui/components/select", () => ({
|
||||
Select: ({ children, onValueChange, disabled }: any) => (
|
||||
<div
|
||||
data-testid="select"
|
||||
onClick={() => onValueChange && onValueChange("tablo-1")}
|
||||
data-disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
SelectTrigger: ({ children }: any) => <div>{children}</div>,
|
||||
SelectValue: ({ placeholder }: any) => <div>{placeholder}</div>,
|
||||
SelectContent: ({ children }: any) => <div>{children}</div>,
|
||||
SelectItem: ({ children, value }: any) => <div data-value={value}>{children}</div>,
|
||||
}));
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("ImportICSModal", () => {
|
||||
const mockOnClose = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const { container } = render(<ImportICSModal onClose={mockOnClose} />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays title", () => {
|
||||
render(<ImportICSModal onClose={mockOnClose} />);
|
||||
expect(screen.getByText("importICSModal.title")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays file label", () => {
|
||||
render(<ImportICSModal onClose={mockOnClose} />);
|
||||
expect(screen.getByText("importICSModal.labels.file")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays destination label", () => {
|
||||
render(<ImportICSModal onClose={mockOnClose} />);
|
||||
expect(screen.getByText("importICSModal.labels.destination")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays choose file button", () => {
|
||||
render(<ImportICSModal onClose={mockOnClose} />);
|
||||
expect(screen.getByText("importICSModal.buttons.chooseFile")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays cancel button", () => {
|
||||
render(<ImportICSModal onClose={mockOnClose} />);
|
||||
expect(screen.getByText("importICSModal.buttons.cancel")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays import button", () => {
|
||||
render(<ImportICSModal onClose={mockOnClose} />);
|
||||
expect(screen.getByText("importICSModal.buttons.import")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders select component for tablo selection", () => {
|
||||
render(<ImportICSModal onClose={mockOnClose} />);
|
||||
expect(screen.getByTestId("select")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays create new tablo checkbox", () => {
|
||||
render(<ImportICSModal onClose={mockOnClose} />);
|
||||
expect(screen.getByText("importICSModal.checkbox.createNewTablo")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("disables import button initially", () => {
|
||||
render(<ImportICSModal onClose={mockOnClose} />);
|
||||
const importButton = screen.getByText("importICSModal.buttons.import");
|
||||
expect(importButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
29
apps/main/src/components/LanguageSelector.test.tsx
Normal file
29
apps/main/src/components/LanguageSelector.test.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { LanguageSelector } from "./LanguageSelector";
|
||||
|
||||
// Mock react-i18next
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
i18n: {
|
||||
language: "en",
|
||||
changeLanguage: vi.fn(),
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("LanguageSelector", () => {
|
||||
it("renders without crashing", () => {
|
||||
render(<LanguageSelector />);
|
||||
// The SelectTrigger should be present
|
||||
const trigger = screen.getByRole("combobox");
|
||||
expect(trigger).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays the select component", () => {
|
||||
const { container } = render(<LanguageSelector />);
|
||||
expect(container.querySelector('[role="combobox"]')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
46
apps/main/src/components/LanguageToggle.test.tsx
Normal file
46
apps/main/src/components/LanguageToggle.test.tsx
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { LanguageToggle } from "./LanguageToggle";
|
||||
|
||||
// Mock react-i18next
|
||||
const changeLanguageMock = vi.fn();
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
i18n: {
|
||||
language: "en",
|
||||
changeLanguage: changeLanguageMock,
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("LanguageToggle", () => {
|
||||
beforeEach(() => {
|
||||
changeLanguageMock.mockClear();
|
||||
});
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const { container } = render(<LanguageToggle />);
|
||||
expect(container.firstChild).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays both language flags", () => {
|
||||
const { container } = render(<LanguageToggle />);
|
||||
expect(container).toHaveTextContent("🇬🇧");
|
||||
expect(container).toHaveTextContent("🇫🇷");
|
||||
});
|
||||
|
||||
it("renders a switch component", () => {
|
||||
render(<LanguageToggle />);
|
||||
const switchElement = screen.getByRole("switch");
|
||||
expect(switchElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls changeLanguage when switch is toggled", () => {
|
||||
render(<LanguageToggle />);
|
||||
const switchElement = screen.getByRole("switch");
|
||||
fireEvent.click(switchElement);
|
||||
expect(changeLanguageMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
25
apps/main/src/components/LoadingSpinner.test.tsx
Normal file
25
apps/main/src/components/LoadingSpinner.test.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { LoadingSpinner } from "./LoadingSpinner";
|
||||
|
||||
describe("LoadingSpinner", () => {
|
||||
it("renders without crashing", () => {
|
||||
render(<LoadingSpinner />);
|
||||
expect(screen.getByRole("status")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays loading image", () => {
|
||||
render(<LoadingSpinner />);
|
||||
const img = screen.getByAltText("Loading...");
|
||||
expect(img).toBeInTheDocument();
|
||||
expect(img).toHaveAttribute("src", "/icon.jpg");
|
||||
});
|
||||
|
||||
it("has spinning animation class", () => {
|
||||
render(<LoadingSpinner />);
|
||||
const img = screen.getByAltText("Loading...");
|
||||
expect(img).toHaveClass("animate-spin");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
70
apps/main/src/components/NotesEditor.test.tsx
Normal file
70
apps/main/src/components/NotesEditor.test.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { NotesEditor } from "./NotesEditor";
|
||||
|
||||
// Mock BlockNote components
|
||||
vi.mock("@blocknote/react", () => ({
|
||||
useCreateBlockNote: () => ({
|
||||
document: [],
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@blocknote/mantine", () => ({
|
||||
BlockNoteView: ({ editor, theme, editable }: any) => (
|
||||
<div data-testid="blocknote-view" data-theme={theme} data-editable={editable}>
|
||||
BlockNote Editor
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/shared/contexts/ThemeContext", () => ({
|
||||
useTheme: () => ({ theme: "light" }),
|
||||
}));
|
||||
|
||||
describe("NotesEditor", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = render(<NotesEditor initialContent="" />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders BlockNoteView", () => {
|
||||
const { getByTestId } = render(<NotesEditor initialContent="" />);
|
||||
expect(getByTestId("blocknote-view")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("applies light theme by default", () => {
|
||||
const { getByTestId } = render(<NotesEditor initialContent="" />);
|
||||
expect(getByTestId("blocknote-view")).toHaveAttribute("data-theme", "light");
|
||||
});
|
||||
|
||||
it("is editable by default", () => {
|
||||
const { getByTestId } = render(<NotesEditor initialContent="" />);
|
||||
expect(getByTestId("blocknote-view")).toHaveAttribute("data-editable", "true");
|
||||
});
|
||||
|
||||
it("is not editable when readOnly is true", () => {
|
||||
const { getByTestId } = render(<NotesEditor initialContent="" readOnly={true} />);
|
||||
expect(getByTestId("blocknote-view")).toHaveAttribute("data-editable", "false");
|
||||
});
|
||||
|
||||
it("accepts onChange callback", () => {
|
||||
const onChange = vi.fn();
|
||||
render(<NotesEditor initialContent="" onChange={onChange} />);
|
||||
// The component is rendered successfully with onChange
|
||||
expect(onChange).not.toHaveBeenCalled(); // Not called on initial render
|
||||
});
|
||||
|
||||
it("accepts initialContent", () => {
|
||||
const initialContent = JSON.stringify([{ type: "paragraph", content: "Test" }]);
|
||||
render(<NotesEditor initialContent={initialContent} />);
|
||||
// Component renders without error
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
it("renders with empty initial content", () => {
|
||||
render(<NotesEditor initialContent="" />);
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
56
apps/main/src/components/StatusPicker.test.tsx
Normal file
56
apps/main/src/components/StatusPicker.test.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { StatusPicker } from "./StatusPicker";
|
||||
|
||||
describe("StatusPicker", () => {
|
||||
it("renders without crashing", () => {
|
||||
const setSelectedStatus = vi.fn();
|
||||
render(<StatusPicker selectedStatus="todo" setSelectedStatus={setSelectedStatus} />);
|
||||
expect(screen.getByText("Statut")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders all three status buttons", () => {
|
||||
const setSelectedStatus = vi.fn();
|
||||
render(<StatusPicker selectedStatus="todo" setSelectedStatus={setSelectedStatus} />);
|
||||
expect(screen.getByText("À faire")).toBeInTheDocument();
|
||||
expect(screen.getByText("En cours")).toBeInTheDocument();
|
||||
expect(screen.getByText("Terminé")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("highlights the selected status", () => {
|
||||
const setSelectedStatus = vi.fn();
|
||||
render(<StatusPicker selectedStatus="in_progress" setSelectedStatus={setSelectedStatus} />);
|
||||
const inProgressButton = screen.getByText("En cours");
|
||||
expect(inProgressButton).toHaveClass("bg-blue-100");
|
||||
});
|
||||
|
||||
it("calls setSelectedStatus when todo button is clicked", () => {
|
||||
const setSelectedStatus = vi.fn();
|
||||
render(<StatusPicker selectedStatus="in_progress" setSelectedStatus={setSelectedStatus} />);
|
||||
fireEvent.click(screen.getByText("À faire"));
|
||||
expect(setSelectedStatus).toHaveBeenCalledWith("todo");
|
||||
});
|
||||
|
||||
it("calls setSelectedStatus when in_progress button is clicked", () => {
|
||||
const setSelectedStatus = vi.fn();
|
||||
render(<StatusPicker selectedStatus="todo" setSelectedStatus={setSelectedStatus} />);
|
||||
fireEvent.click(screen.getByText("En cours"));
|
||||
expect(setSelectedStatus).toHaveBeenCalledWith("in_progress");
|
||||
});
|
||||
|
||||
it("calls setSelectedStatus when done button is clicked", () => {
|
||||
const setSelectedStatus = vi.fn();
|
||||
render(<StatusPicker selectedStatus="todo" setSelectedStatus={setSelectedStatus} />);
|
||||
fireEvent.click(screen.getByText("Terminé"));
|
||||
expect(setSelectedStatus).toHaveBeenCalledWith("done");
|
||||
});
|
||||
|
||||
it("applies correct styling for done status", () => {
|
||||
const setSelectedStatus = vi.fn();
|
||||
render(<StatusPicker selectedStatus="done" setSelectedStatus={setSelectedStatus} />);
|
||||
const doneButton = screen.getByText("Terminé");
|
||||
expect(doneButton).toHaveClass("bg-green-100");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
47
apps/main/src/components/TabloDiscussionSection.test.tsx
Normal file
47
apps/main/src/components/TabloDiscussionSection.test.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { TabloDiscussionSection } from "./TabloDiscussionSection";
|
||||
|
||||
// Mock Stream Chat
|
||||
vi.mock("stream-chat-react", () => ({
|
||||
Chat: ({ children }: any) => <div data-testid="chat">{children}</div>,
|
||||
Channel: ({ children }: any) => <div data-testid="channel">{children}</div>,
|
||||
Window: ({ children }: any) => <div data-testid="window">{children}</div>,
|
||||
MessageList: () => <div data-testid="message-list">Messages</div>,
|
||||
MessageInput: () => <div data-testid="message-input">Input</div>,
|
||||
useChannelStateContext: () => ({ channel: null }),
|
||||
useCreateChatClient: () => null,
|
||||
useChatContext: () => ({
|
||||
client: null,
|
||||
setActiveChannel: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../providers/ChatProvider", () => ({
|
||||
useChatContext: () => ({
|
||||
client: null,
|
||||
setActiveChannel: vi.fn(),
|
||||
}),
|
||||
default: ({ children }: any) => <>{children}</>,
|
||||
}));
|
||||
|
||||
describe("TabloDiscussionSection", () => {
|
||||
const mockTablo = {
|
||||
id: "test-tablo-id",
|
||||
name: "Test Tablo",
|
||||
color: "bg-blue-500",
|
||||
user_id: "test-user-id",
|
||||
};
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<TabloDiscussionSection tablo={mockTablo} />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
50
apps/main/src/components/TabloEventsSection.test.tsx
Normal file
50
apps/main/src/components/TabloEventsSection.test.tsx
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { TabloEventsSection } from "./TabloEventsSection";
|
||||
|
||||
// Mock hooks
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useParams: () => ({ tablo_id: "test-tablo-id" }),
|
||||
useNavigate: () => vi.fn(),
|
||||
Link: ({ children, to }: any) => <a href={to}>{children}</a>,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../hooks/events", () => ({
|
||||
useEventsByTablo: () => ({
|
||||
data: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../providers/UserStoreProvider", () => ({
|
||||
useIsReadOnlyUser: () => false,
|
||||
TestUserStoreProvider: ({ children }: any) => children,
|
||||
}));
|
||||
|
||||
describe("TabloEventsSection", () => {
|
||||
const mockTablo = {
|
||||
id: "test-tablo-id",
|
||||
name: "Test Tablo",
|
||||
color: "bg-blue-500",
|
||||
user_id: "test-user-id",
|
||||
};
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(
|
||||
<TabloEventsSection tablo={mockTablo} isAdmin={true} />
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
45
apps/main/src/components/TabloFilesSection.test.tsx
Normal file
45
apps/main/src/components/TabloFilesSection.test.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { TabloFilesSection } from "./TabloFilesSection";
|
||||
|
||||
// Mock hooks
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useParams: () => ({ tablo_id: "test-tablo-id" }),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../hooks/files", () => ({
|
||||
useTabloFileNames: () => ({
|
||||
data: [],
|
||||
isLoading: false,
|
||||
error: null,
|
||||
}),
|
||||
useUploadFile: () => vi.fn(),
|
||||
useDeleteFile: () => vi.fn(),
|
||||
}));
|
||||
|
||||
describe("TabloFilesSection", () => {
|
||||
const mockTablo = {
|
||||
id: "test-tablo-id",
|
||||
name: "Test Tablo",
|
||||
color: "bg-blue-500",
|
||||
user_id: "test-user-id",
|
||||
};
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(
|
||||
<TabloFilesSection tablo={mockTablo} isAdmin={true} />
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
41
apps/main/src/components/TabloNotesSection.test.tsx
Normal file
41
apps/main/src/components/TabloNotesSection.test.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { TabloNotesSection } from "./TabloNotesSection";
|
||||
|
||||
// Mock hooks
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useParams: () => ({ tablo_id: "test-tablo-id" }),
|
||||
useNavigate: () => vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../hooks/notes", () => ({
|
||||
useTabloNotes: () => ({
|
||||
notes: [],
|
||||
isLoading: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("TabloNotesSection", () => {
|
||||
const mockTablo = {
|
||||
id: "test-tablo-id",
|
||||
name: "Test Tablo",
|
||||
color: "bg-blue-500",
|
||||
user_id: "test-user-id",
|
||||
};
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<TabloNotesSection tablo={mockTablo} />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
58
apps/main/src/components/TabloSettingsSection.test.tsx
Normal file
58
apps/main/src/components/TabloSettingsSection.test.tsx
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { TabloSettingsSection } from "./TabloSettingsSection";
|
||||
|
||||
// Mock hooks
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useParams: () => ({ tablo_id: "test-tablo-id" }),
|
||||
useNavigate: () => vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../hooks/tablos", () => ({
|
||||
useUpdateTablo: () => ({
|
||||
mutate: vi.fn(),
|
||||
}),
|
||||
useDeleteTablo: () => ({
|
||||
mutate: vi.fn(),
|
||||
}),
|
||||
useTabloMembers: () => ({
|
||||
data: [],
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../providers/UserStoreProvider", () => ({
|
||||
useUser: () => ({
|
||||
id: "test-user-id",
|
||||
name: "Test User",
|
||||
}),
|
||||
TestUserStoreProvider: ({ children }: any) => children,
|
||||
}));
|
||||
|
||||
describe("TabloSettingsSection", () => {
|
||||
const mockTablo = {
|
||||
id: "test-tablo-id",
|
||||
name: "Test Tablo",
|
||||
color: "bg-blue-500",
|
||||
user_id: "test-user-id",
|
||||
};
|
||||
|
||||
const mockOnEdit = vi.fn();
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(
|
||||
<TabloSettingsSection tablo={mockTablo} isAdmin={true} onEdit={mockOnEdit} />
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
105
apps/main/src/components/TabloTutorial.test.tsx
Normal file
105
apps/main/src/components/TabloTutorial.test.tsx
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { TabloTutorial } from "./TabloTutorial";
|
||||
|
||||
// Mock UI components
|
||||
vi.mock("@xtablo/ui/components/button", () => ({
|
||||
Button: ({ children, onClick, className }: any) => (
|
||||
<button onClick={onClick} className={className}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
describe("TabloTutorial", () => {
|
||||
const mockOnClose = vi.fn();
|
||||
const mockOnCreateTablo = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
it("renders when open", () => {
|
||||
render(<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />);
|
||||
expect(screen.getByText("Guide de démarrage")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render when closed", () => {
|
||||
const { container } = render(
|
||||
<TabloTutorial isOpen={false} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />
|
||||
);
|
||||
expect(container.firstChild).toBeNull();
|
||||
});
|
||||
|
||||
it("displays first step by default", () => {
|
||||
render(<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />);
|
||||
expect(screen.getByText(/Bienvenue sur XTablo/)).toBeInTheDocument();
|
||||
expect(screen.getByText("Étape 1 sur 7")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows next button", () => {
|
||||
render(<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />);
|
||||
expect(screen.getByText("Suivant")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("navigates to next step when next button is clicked", () => {
|
||||
render(<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />);
|
||||
fireEvent.click(screen.getByText("Suivant"));
|
||||
expect(screen.getByText("Étape 2 sur 7")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows previous button after first step", () => {
|
||||
render(<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />);
|
||||
fireEvent.click(screen.getByText("Suivant"));
|
||||
expect(screen.getByText("Précédent")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("navigates to previous step when previous button is clicked", () => {
|
||||
render(<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />);
|
||||
fireEvent.click(screen.getByText("Suivant"));
|
||||
fireEvent.click(screen.getByText("Précédent"));
|
||||
expect(screen.getByText("Étape 1 sur 7")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows skip button", () => {
|
||||
render(<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />);
|
||||
expect(screen.getByText("Passer")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("closes tutorial when close button is clicked", () => {
|
||||
render(<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />);
|
||||
const closeButton = screen.getByRole("button", { name: "" });
|
||||
fireEvent.click(closeButton);
|
||||
expect(mockOnClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("sets localStorage when tutorial is completed", () => {
|
||||
render(<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />);
|
||||
const closeButton = screen.getByRole("button", { name: "" });
|
||||
fireEvent.click(closeButton);
|
||||
expect(localStorage.getItem("xtablo-tutorial-completed")).toBe("true");
|
||||
});
|
||||
|
||||
it("displays progress bar", () => {
|
||||
const { container } = render(
|
||||
<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />
|
||||
);
|
||||
expect(container.querySelector(".bg-blue-600")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows create tablo button on last step", () => {
|
||||
render(<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />);
|
||||
// Skip to last step
|
||||
fireEvent.click(screen.getByText("Passer"));
|
||||
expect(screen.getByText("Créer mon premier Tablo")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows completion message on last step", () => {
|
||||
render(<TabloTutorial isOpen={true} onClose={mockOnClose} onCreateTablo={mockOnCreateTablo} />);
|
||||
fireEvent.click(screen.getByText("Passer"));
|
||||
expect(screen.getByText(/Félicitations/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
122
apps/main/src/components/WebcalModal.test.tsx
Normal file
122
apps/main/src/components/WebcalModal.test.tsx
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { WebcalModal } from "./WebcalModal";
|
||||
|
||||
// Mock hooks
|
||||
vi.mock("../hooks/tablos", () => ({
|
||||
useTablosList: () => ({
|
||||
data: [
|
||||
{ id: "tablo-1", name: "Tablo 1" },
|
||||
{ id: "tablo-2", name: "Tablo 2" },
|
||||
],
|
||||
isLoading: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../hooks/webcal", () => ({
|
||||
useGenerateWebcalToken: () => ({
|
||||
generateWebcalUrl: vi.fn(),
|
||||
isPending: false,
|
||||
data: null,
|
||||
}),
|
||||
}));
|
||||
|
||||
// Mock Dialog components
|
||||
vi.mock("@xtablo/ui/components/dialog", () => ({
|
||||
Dialog: ({ open, children }: any) => (open ? <div data-testid="dialog">{children}</div> : null),
|
||||
DialogContent: ({ children }: any) => <div>{children}</div>,
|
||||
DialogHeader: ({ children }: any) => <div>{children}</div>,
|
||||
DialogTitle: ({ children }: any) => <h2>{children}</h2>,
|
||||
DialogDescription: ({ children }: any) => <p>{children}</p>,
|
||||
}));
|
||||
|
||||
// Mock other UI components
|
||||
vi.mock("@xtablo/ui/components/button", () => ({
|
||||
Button: ({ children, onClick, disabled }: any) => (
|
||||
<button onClick={onClick} disabled={disabled}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/label", () => ({
|
||||
Label: ({ children }: any) => <label>{children}</label>,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/select", () => ({
|
||||
Select: ({ children, onValueChange, disabled }: any) => (
|
||||
<div
|
||||
data-testid="select"
|
||||
onClick={() => onValueChange && onValueChange("tablo-1")}
|
||||
data-disabled={disabled}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
SelectTrigger: ({ children }: any) => <div>{children}</div>,
|
||||
SelectValue: ({ placeholder }: any) => <div>{placeholder}</div>,
|
||||
SelectContent: ({ children }: any) => <div>{children}</div>,
|
||||
SelectItem: ({ children, value }: any) => <div data-value={value}>{children}</div>,
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/ui/components/input", () => ({
|
||||
Input: ({ value, readOnly }: any) => <input value={value} readOnly={readOnly} />,
|
||||
}));
|
||||
|
||||
describe("WebcalModal", () => {
|
||||
const mockOnOpenChange = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders when open", () => {
|
||||
render(<WebcalModal open={true} onOpenChange={mockOnOpenChange} />);
|
||||
expect(screen.getByTestId("dialog")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not render when closed", () => {
|
||||
render(<WebcalModal open={false} onOpenChange={mockOnOpenChange} />);
|
||||
expect(screen.queryByTestId("dialog")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays title", () => {
|
||||
render(<WebcalModal open={true} onOpenChange={mockOnOpenChange} />);
|
||||
expect(screen.getByText("Synchronisation de calendrier")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays description", () => {
|
||||
render(<WebcalModal open={true} onOpenChange={mockOnOpenChange} />);
|
||||
expect(
|
||||
screen.getByText(/Synchronisez vos événements avec votre application de calendrier préférée/)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays calendar selection label", () => {
|
||||
render(<WebcalModal open={true} onOpenChange={mockOnOpenChange} />);
|
||||
expect(screen.getByText("Calendrier à synchroniser")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays generate button", () => {
|
||||
render(<WebcalModal open={true} onOpenChange={mockOnOpenChange} />);
|
||||
expect(screen.getByText("Générer l'URL de synchronisation")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("disables generate button when no tablo selected", () => {
|
||||
render(<WebcalModal open={true} onOpenChange={mockOnOpenChange} />);
|
||||
const button = screen.getByText("Générer l'URL de synchronisation");
|
||||
expect(button).toBeDisabled();
|
||||
});
|
||||
|
||||
it.skip("shows loading state in generate button", () => {
|
||||
// This test is skipped because mocking the hook dynamically is complex
|
||||
// The hook is already mocked at the module level with isPending: false
|
||||
render(<WebcalModal open={true} onOpenChange={mockOnOpenChange} />);
|
||||
expect(screen.getByText("Générer l'URL de synchronisation")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays select placeholder", () => {
|
||||
render(<WebcalModal open={true} onOpenChange={mockOnOpenChange} />);
|
||||
expect(screen.getByText("Sélectionner un calendrier")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
75
apps/main/src/components/header.test.tsx
Normal file
75
apps/main/src/components/header.test.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { Header } from "./header";
|
||||
|
||||
// Mock the iconHelpers
|
||||
vi.mock("../utils/iconHelpers", () => ({
|
||||
getXtabloIcon: () => "/icon.jpg",
|
||||
}));
|
||||
|
||||
describe("Header", () => {
|
||||
it("renders without crashing", () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<Header />
|
||||
</BrowserRouter>
|
||||
);
|
||||
expect(screen.getByAltText("Logo XTablo")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays the XTablo logo and title", () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<Header />
|
||||
</BrowserRouter>
|
||||
);
|
||||
expect(screen.getByAltText("Logo XTablo")).toBeInTheDocument();
|
||||
expect(screen.getByText("XTablo")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders navigation links", () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<Header />
|
||||
</BrowserRouter>
|
||||
);
|
||||
expect(screen.getByText("Fonctionnalités")).toBeInTheDocument();
|
||||
expect(screen.getByText("Tarifs")).toBeInTheDocument();
|
||||
expect(screen.getByText("Contact")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders login and signup buttons", () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<Header />
|
||||
</BrowserRouter>
|
||||
);
|
||||
expect(screen.getByText("Connexion")).toBeInTheDocument();
|
||||
expect(screen.getByText("S'inscrire")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("has correct links for login and signup", () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<Header />
|
||||
</BrowserRouter>
|
||||
);
|
||||
const loginLink = screen.getByText("Connexion").closest("a");
|
||||
const signupLink = screen.getByText("S'inscrire").closest("a");
|
||||
expect(loginLink).toHaveAttribute("href", "/login");
|
||||
expect(signupLink).toHaveAttribute("href", "/signup");
|
||||
});
|
||||
|
||||
it("has sticky positioning", () => {
|
||||
const { container } = render(
|
||||
<BrowserRouter>
|
||||
<Header />
|
||||
</BrowserRouter>
|
||||
);
|
||||
const header = container.querySelector("header");
|
||||
expect(header).toHaveClass("sticky");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
30
apps/main/src/pages/NotFoundPage.test.tsx
Normal file
30
apps/main/src/pages/NotFoundPage.test.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { NotFoundPage } from "./NotFoundPage";
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("NotFoundPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = render(
|
||||
<BrowserRouter>
|
||||
<NotFoundPage />
|
||||
</BrowserRouter>
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays 404 message", () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<NotFoundPage />
|
||||
</BrowserRouter>
|
||||
);
|
||||
expect(screen.getByText("404")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
26
apps/main/src/pages/PublicBookingPage.test.tsx
Normal file
26
apps/main/src/pages/PublicBookingPage.test.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { PublicBookingPage } from "./PublicBookingPage";
|
||||
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useParams: () => ({ username_id: "test-user", event_type: "test-event" }),
|
||||
useSearchParams: () => [new URLSearchParams(), vi.fn()],
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("PublicBookingPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<PublicBookingPage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
25
apps/main/src/pages/PublicNotePage.test.tsx
Normal file
25
apps/main/src/pages/PublicNotePage.test.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { PublicNotePage } from "./PublicNotePage";
|
||||
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useParams: () => ({ note_id: "test-note-id" }),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("PublicNotePage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<PublicNotePage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
58
apps/main/src/pages/chat.test.tsx
Normal file
58
apps/main/src/pages/chat.test.tsx
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { ChatPage } from "./chat";
|
||||
|
||||
vi.mock("../hooks/channel", () => ({
|
||||
useChannelFromUrl: () => ({
|
||||
channel: null,
|
||||
isChannelInUrl: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../hooks/tablos", () => ({
|
||||
useTablosList: () => ({
|
||||
data: [],
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../providers/UserStoreProvider", () => ({
|
||||
useUser: () => ({
|
||||
id: "test-user-id",
|
||||
name: "Test User",
|
||||
}),
|
||||
TestUserStoreProvider: ({ children }: any) => children,
|
||||
}));
|
||||
|
||||
vi.mock("../providers/ChatProvider", () => ({
|
||||
useChatClient: () => null,
|
||||
useChatContext: () => ({
|
||||
client: null,
|
||||
channel: null,
|
||||
setActiveChannel: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("stream-chat-react", () => ({
|
||||
Chat: ({ children }: any) => <div data-testid="chat">{children}</div>,
|
||||
ChannelList: ({ children }: any) => <div data-testid="channel-list">{children}</div>,
|
||||
Channel: ({ children }: any) => <div data-testid="channel">{children}</div>,
|
||||
ChannelHeader: () => <div data-testid="channel-header">Header</div>,
|
||||
MessageList: () => <div data-testid="message-list">Messages</div>,
|
||||
MessageInput: () => <div data-testid="message-input">Input</div>,
|
||||
Window: ({ children }: any) => <div data-testid="window">{children}</div>,
|
||||
useChannelStateContext: () => ({ channel: null }),
|
||||
useCreateChatClient: () => null,
|
||||
useChatContext: () => ({
|
||||
client: null,
|
||||
channel: null,
|
||||
setActiveChannel: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("ChatPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<ChatPage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
11
apps/main/src/pages/factures.test.tsx
Normal file
11
apps/main/src/pages/factures.test.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { FacturesPage } from "./factures";
|
||||
|
||||
describe("FacturesPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<FacturesPage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
17
apps/main/src/pages/feedback.test.tsx
Normal file
17
apps/main/src/pages/feedback.test.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { FeedbackPage } from "./feedback";
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("FeedbackPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<FeedbackPage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
26
apps/main/src/pages/join.test.tsx
Normal file
26
apps/main/src/pages/join.test.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { JoinPage } from "./join";
|
||||
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useParams: () => ({ invite_code: "test-invite" }),
|
||||
useNavigate: () => vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("JoinPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<JoinPage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
33
apps/main/src/pages/landing.test.tsx
Normal file
33
apps/main/src/pages/landing.test.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { LandingPage } from "./landing";
|
||||
|
||||
// Mock Header component
|
||||
vi.mock("../components/header", () => ({
|
||||
Header: () => <div data-testid="header">Header</div>,
|
||||
}));
|
||||
|
||||
// Mock AnimatedBackground
|
||||
vi.mock("../components/AnimatedBackground", () => ({
|
||||
AnimatedBackground: () => <div data-testid="animated-background">Background</div>,
|
||||
}));
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("LandingPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = render(
|
||||
<BrowserRouter>
|
||||
<LandingPage />
|
||||
</BrowserRouter>
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Note: LandingPage returns null and redirects immediately, so we can't test much
|
||||
});
|
||||
26
apps/main/src/pages/login.test.tsx
Normal file
26
apps/main/src/pages/login.test.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { LoginPage } from "./login";
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useNavigate: () => vi.fn(),
|
||||
Link: ({ children, to }: any) => <a href={to}>{children}</a>,
|
||||
};
|
||||
});
|
||||
|
||||
describe("LoginPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<LoginPage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
25
apps/main/src/pages/notes.test.tsx
Normal file
25
apps/main/src/pages/notes.test.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import NotesPage from "./notes";
|
||||
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useNavigate: () => vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("NotesPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<NotesPage mode="create" />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
20
apps/main/src/pages/oauth-signin.test.tsx
Normal file
20
apps/main/src/pages/oauth-signin.test.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { OAuthSigninPage } from "./oauth-signin";
|
||||
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useNavigate: () => vi.fn(),
|
||||
useSearchParams: () => [new URLSearchParams(), vi.fn()],
|
||||
};
|
||||
});
|
||||
|
||||
describe("OAuthSigninPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<OAuthSigninPage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
17
apps/main/src/pages/reset-password.test.tsx
Normal file
17
apps/main/src/pages/reset-password.test.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { ResetPasswordPage } from "./reset-password";
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("ResetPasswordPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<ResetPasswordPage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
36
apps/main/src/pages/settings.test.tsx
Normal file
36
apps/main/src/pages/settings.test.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import SettingsPage from "./settings";
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
i18n: {
|
||||
language: "en",
|
||||
changeLanguage: vi.fn(),
|
||||
},
|
||||
}),
|
||||
useTranslationWithOptions: () => ({
|
||||
t: (key: string) => key,
|
||||
i18n: {
|
||||
language: "en",
|
||||
changeLanguage: vi.fn(),
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useNavigate: () => vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
describe("SettingsPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<SettingsPage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
26
apps/main/src/pages/signup.test.tsx
Normal file
26
apps/main/src/pages/signup.test.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { SignUpPage } from "./signup";
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("react-router-dom", async () => {
|
||||
const actual = await vi.importActual("react-router-dom");
|
||||
return {
|
||||
...actual,
|
||||
useNavigate: () => vi.fn(),
|
||||
Link: ({ children, to }: any) => <a href={to}>{children}</a>,
|
||||
};
|
||||
});
|
||||
|
||||
describe("SignUpPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<SignUpPage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
17
apps/main/src/pages/tablo.test.tsx
Normal file
17
apps/main/src/pages/tablo.test.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { render } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders } from "../utils/testHelpers";
|
||||
import { TabloPage } from "./tablo";
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("TabloPage", () => {
|
||||
it("renders without crashing", () => {
|
||||
const { container } = renderWithProviders(<TabloPage />);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
56
apps/main/src/providers/ChatProvider.test.tsx
Normal file
56
apps/main/src/providers/ChatProvider.test.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import ChatProvider from "./ChatProvider";
|
||||
|
||||
// Mock Stream Chat
|
||||
vi.mock("stream-chat", () => ({
|
||||
StreamChat: {
|
||||
getInstance: vi.fn(() => ({
|
||||
connectUser: vi.fn(),
|
||||
disconnectUser: vi.fn(),
|
||||
})),
|
||||
},
|
||||
StateStore: vi.fn(),
|
||||
FixedSizeQueueCache: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("stream-chat-react", () => ({
|
||||
Chat: ({ children }: any) => <div data-testid="stream-chat">{children}</div>,
|
||||
useCreateChatClient: () => ({ id: "test-client" }),
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/shared/contexts/SessionContext", () => ({
|
||||
useSession: () => ({
|
||||
session: {
|
||||
access_token: "test-token",
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("./UserStoreProvider", () => ({
|
||||
useUser: () => ({
|
||||
id: "test-user-id",
|
||||
name: "Test User",
|
||||
streamToken: "test-stream-token",
|
||||
}),
|
||||
}));
|
||||
|
||||
describe("ChatProvider", () => {
|
||||
it("renders children", () => {
|
||||
render(
|
||||
<ChatProvider>
|
||||
<div>Test Child</div>
|
||||
</ChatProvider>
|
||||
);
|
||||
expect(screen.getByText("Test Child")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const { container } = render(
|
||||
<ChatProvider>
|
||||
<div>Content</div>
|
||||
</ChatProvider>
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
44
apps/main/src/providers/DatadogRumProvider.test.tsx
Normal file
44
apps/main/src/providers/DatadogRumProvider.test.tsx
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { DatadogRumProvider } from "./DatadogRumProvider";
|
||||
|
||||
// Mock Datadog RUM
|
||||
vi.mock("@datadog/browser-rum-react", () => ({
|
||||
DdRumReactIntegration: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@datadog/browser-rum", () => ({
|
||||
datadogRum: {
|
||||
init: vi.fn(),
|
||||
startView: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("../lib/rum", () => ({
|
||||
initRum: vi.fn(),
|
||||
}));
|
||||
|
||||
describe("DatadogRumProvider", () => {
|
||||
it("renders children", () => {
|
||||
render(
|
||||
<BrowserRouter>
|
||||
<DatadogRumProvider>
|
||||
<div>Test Child</div>
|
||||
</DatadogRumProvider>
|
||||
</BrowserRouter>
|
||||
);
|
||||
expect(screen.getByText("Test Child")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const { container } = render(
|
||||
<BrowserRouter>
|
||||
<DatadogRumProvider>
|
||||
<div>Content</div>
|
||||
</DatadogRumProvider>
|
||||
</BrowserRouter>
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
94
apps/main/src/providers/UserStoreProvider.test.tsx
Normal file
94
apps/main/src/providers/UserStoreProvider.test.tsx
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { TestUserStoreProvider, UserStoreProvider } from "./UserStoreProvider";
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock("@tanstack/react-query", () => ({
|
||||
useQuery: () => ({
|
||||
data: {
|
||||
id: "test-user-id",
|
||||
name: "Test User",
|
||||
streamToken: null,
|
||||
},
|
||||
isPending: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@xtablo/shared/contexts/SessionContext", () => ({
|
||||
useSession: () => ({
|
||||
session: {
|
||||
access_token: "test-token",
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("../lib/api", () => ({
|
||||
api: {
|
||||
get: vi.fn(() =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
id: "test-user-id",
|
||||
name: "Test User",
|
||||
streamToken: null,
|
||||
},
|
||||
})
|
||||
),
|
||||
},
|
||||
}));
|
||||
|
||||
describe("UserStoreProvider", () => {
|
||||
it("renders children", () => {
|
||||
render(
|
||||
<UserStoreProvider>
|
||||
<div>Test Child</div>
|
||||
</UserStoreProvider>
|
||||
);
|
||||
expect(screen.getByText("Test Child")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const { container } = render(
|
||||
<UserStoreProvider>
|
||||
<div>Content</div>
|
||||
</UserStoreProvider>
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("TestUserStoreProvider", () => {
|
||||
const mockUser = {
|
||||
id: "test-user-id",
|
||||
name: "Test User",
|
||||
streamToken: null,
|
||||
} as any;
|
||||
|
||||
it("renders children with user", () => {
|
||||
render(
|
||||
<TestUserStoreProvider user={mockUser}>
|
||||
<div>Test Child</div>
|
||||
</TestUserStoreProvider>
|
||||
);
|
||||
expect(screen.getByText("Test Child")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders children without user", () => {
|
||||
render(
|
||||
<TestUserStoreProvider user={null}>
|
||||
<div>Test Child</div>
|
||||
</TestUserStoreProvider>
|
||||
);
|
||||
expect(screen.getByText("Test Child")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const { container } = render(
|
||||
<TestUserStoreProvider user={mockUser}>
|
||||
<div>Content</div>
|
||||
</TestUserStoreProvider>
|
||||
);
|
||||
expect(container).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue