Skip to content

Commit

Permalink
Add action menu to LinkCard
Browse files Browse the repository at this point in the history
  • Loading branch information
brendanv committed Aug 21, 2024
1 parent 77a1d1d commit 535c01c
Show file tree
Hide file tree
Showing 10 changed files with 773 additions and 139 deletions.
48 changes: 48 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@tanstack/react-table": "^8.20.1",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
Expand All @@ -35,6 +36,7 @@
"@tailwindcss/typography": "^0.5.14",
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
"@types/node": "^22.1.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
Expand Down
125 changes: 125 additions & 0 deletions frontend/src/components/LinkCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import React from "react";
import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { vi, describe, it, expect, beforeEach } from "vitest";
import { BrowserRouter } from "react-router-dom";
import LinkCard from "./LinkCard";
import { PocketBaseProvider } from "@/hooks/usePocketBase";
import { toast, ToastProvider } from "@/components/ui/use-toast";

// Mock PocketBase
vi.mock("pocketbase", () => {
return {
default: vi.fn(() => ({
authStore: {
model: { id: "user123" },
onChange: vi.fn(),
},
collection: vi.fn(() => ({
update: vi.fn(),
delete: vi.fn(),
})),
})),
};
});

const { mockedToast } = vi.hoisted(() => ({
mockedToast: vi.fn(),
}));
vi.mock("@/components/ui/use-toast", () => {
return {
useToast: () => ({
toast: mockedToast,
}),
toast: vi.fn(),
ToastProvider: vi.fn(({ children }) => children),
};
});


describe("LinkCard", () => {
const mockLink = {
id: "1",
title: "Test Link",
hostname: "test.com",
excerpt: "This is a test link",
header_image_url: "https://test.com/image.jpg",
last_viewed_at: null,
added_to_library: new Date(),
article_date: null,
author: null,
tags: [],
};

const mockOnUpdate = vi.fn();

beforeEach(() => {
vi.clearAllMocks();
});

const renderLinkCard = () => {
return render(
<BrowserRouter>
<PocketBaseProvider>
<ToastProvider>
<LinkCard link={mockLink} onUpdate={mockOnUpdate} />
</ToastProvider>
</PocketBaseProvider>
</BrowserRouter>,
);
};

it("renders link information correctly", () => {
renderLinkCard();
expect(screen.getByText("Test Link")).toBeInTheDocument();
expect(screen.getByText("test.com")).toBeInTheDocument();
expect(screen.getByText("This is a test link")).toBeInTheDocument();
});

it("shows unread indicator for unread links", () => {
renderLinkCard();
const unreadIndicator = screen.getByTestId("unread-indicator");
expect(unreadIndicator).toBeInTheDocument();
});

it('toggles read status when "Mark as Read" is clicked', async () => {
renderLinkCard();
const moreButton = screen.getByRole("button", { name: /more/i });
await userEvent.click(moreButton);

const dropdownMenu = screen.getByRole("menu");
const markAsReadButton = within(dropdownMenu).getByText("Mark as Read");
await userEvent.click(markAsReadButton);

expect(mockedToast).toHaveBeenCalled();
expect(mockOnUpdate).toHaveBeenCalled();
});

it("shows delete confirmation dialog when delete is clicked", async () => {
renderLinkCard();
const moreButton = screen.getByRole("button", { name: /more/i });
await userEvent.click(moreButton);

const dropdownMenu = screen.getByRole("menu");
const deleteButton = within(dropdownMenu).getByText("Delete");
await userEvent.click(deleteButton);

expect(await screen.findByText("Are you sure?")).toBeInTheDocument();
});

it("deletes link when confirmed in delete dialog", async () => {
renderLinkCard();
const moreButton = screen.getByRole("button", { name: /more/i });
await userEvent.click(moreButton);

const dropdownMenu = screen.getByRole("menu");
const deleteButton = within(dropdownMenu).getByText("Delete");
await userEvent.click(deleteButton);

const confirmDeleteButton = screen.getByTestId("delete-confirm-button");
await userEvent.click(confirmDeleteButton);

expect(mockedToast).toHaveBeenCalled();
expect(mockOnUpdate).toHaveBeenCalled();
});
});
Loading

0 comments on commit 535c01c

Please sign in to comment.