Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions src/app/__tests__/main-content.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { test, expect, vi, afterEach } from "vitest";
import { render, screen, cleanup } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { MainContent } from "../main-content";

vi.mock("@/lib/contexts/file-system-context", () => ({
FileSystemProvider: ({ children }: any) => <>{children}</>,
useFileSystem: () => ({
getAllFiles: () => new Map(),
refreshTrigger: 0,
selectedFile: null,
setSelectedFile: vi.fn(),
}),
}));

vi.mock("@/lib/contexts/chat-context", () => ({
ChatProvider: ({ children }: any) => <>{children}</>,
useChat: () => ({
messages: [],
input: "",
handleInputChange: vi.fn(),
handleSubmit: vi.fn(),
status: "idle",
}),
}));

vi.mock("@/components/chat/ChatInterface", () => ({
ChatInterface: () => <div data-testid="chat-interface" />,
}));

vi.mock("@/components/preview/PreviewFrame", () => ({
PreviewFrame: () => <div data-testid="preview-frame" />,
}));

vi.mock("@/components/editor/CodeEditor", () => ({
CodeEditor: () => <div data-testid="code-editor" />,
}));

vi.mock("@/components/editor/FileTree", () => ({
FileTree: () => <div data-testid="file-tree" />,
}));

vi.mock("@/components/HeaderActions", () => ({
HeaderActions: () => <div data-testid="header-actions" />,
}));

vi.mock("@/components/ui/resizable", () => ({
ResizablePanelGroup: ({ children }: any) => <div>{children}</div>,
ResizablePanel: ({ children }: any) => <div>{children}</div>,
ResizableHandle: () => <div />,
}));

afterEach(() => {
cleanup();
vi.clearAllMocks();
});

test("shows preview view by default", () => {
render(<MainContent />);

expect(screen.getByTestId("preview-frame")).toBeDefined();
expect(screen.queryByTestId("code-editor")).toBeNull();
expect(screen.queryByTestId("file-tree")).toBeNull();
});

test("Preview tab is active by default", () => {
render(<MainContent />);

const previewTab = screen.getByRole("tab", { name: "Preview" });
const codeTab = screen.getByRole("tab", { name: "Code" });

expect(previewTab.getAttribute("data-state")).toBe("active");
expect(codeTab.getAttribute("data-state")).toBe("inactive");
});

test("switches to code view when Code tab is clicked", async () => {
const user = userEvent.setup();
render(<MainContent />);

expect(screen.getByTestId("preview-frame")).toBeDefined();

await user.click(screen.getByRole("tab", { name: "Code" }));

expect(screen.queryByTestId("preview-frame")).toBeNull();
expect(screen.getByTestId("code-editor")).toBeDefined();
expect(screen.getByTestId("file-tree")).toBeDefined();
});

test("Code tab becomes active after clicking it", async () => {
const user = userEvent.setup();
render(<MainContent />);

await user.click(screen.getByRole("tab", { name: "Code" }));

const previewTab = screen.getByRole("tab", { name: "Preview" });
const codeTab = screen.getByRole("tab", { name: "Code" });

expect(codeTab.getAttribute("data-state")).toBe("active");
expect(previewTab.getAttribute("data-state")).toBe("inactive");
});

test("switches back to preview view when Preview tab is clicked", async () => {
const user = userEvent.setup();
render(<MainContent />);

await user.click(screen.getByRole("tab", { name: "Code" }));
expect(screen.getByTestId("code-editor")).toBeDefined();

await user.click(screen.getByRole("tab", { name: "Preview" }));

expect(screen.getByTestId("preview-frame")).toBeDefined();
expect(screen.queryByTestId("code-editor")).toBeNull();
});

test("can toggle between views multiple times", async () => {
const user = userEvent.setup();
render(<MainContent />);

// Start: preview
expect(screen.getByTestId("preview-frame")).toBeDefined();

// Switch to code
await user.click(screen.getByRole("tab", { name: "Code" }));
expect(screen.getByTestId("code-editor")).toBeDefined();

// Switch back to preview
await user.click(screen.getByRole("tab", { name: "Preview" }));
expect(screen.getByTestId("preview-frame")).toBeDefined();

// Switch to code again
await user.click(screen.getByRole("tab", { name: "Code" }));
expect(screen.getByTestId("code-editor")).toBeDefined();
});
15 changes: 4 additions & 11 deletions src/components/preview/PreviewFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,13 @@ export function PreviewFrame() {
const { getAllFiles, refreshTrigger } = useFileSystem();
const [error, setError] = useState<string | null>(null);
const [entryPoint, setEntryPoint] = useState<string>("/App.jsx");
const [isFirstLoad, setIsFirstLoad] = useState(true);
const isFirstLoadRef = useRef(true);

useEffect(() => {
const updatePreview = () => {
try {
const files = getAllFiles();

// Clear error first when we have files
if (files.size > 0 && error) {
setError(null);
}

// Find the entry point - look for App.jsx, App.tsx, index.jsx, or index.tsx
let foundEntryPoint = entryPoint;
const possibleEntries = [
Expand Down Expand Up @@ -54,7 +49,7 @@ export function PreviewFrame() {
}

if (files.size === 0) {
if (isFirstLoad) {
if (isFirstLoadRef.current) {
setError("firstLoad");
} else {
setError("No files to preview");
Expand All @@ -63,9 +58,7 @@ export function PreviewFrame() {
}

// We have files, so it's no longer the first load
if (isFirstLoad) {
setIsFirstLoad(false);
}
isFirstLoadRef.current = false;

if (!foundEntryPoint || !files.has(foundEntryPoint)) {
setError(
Expand Down Expand Up @@ -96,7 +89,7 @@ export function PreviewFrame() {
};

updatePreview();
}, [refreshTrigger, getAllFiles, entryPoint, error, isFirstLoad]);
}, [refreshTrigger, getAllFiles, entryPoint]);

if (error) {
if (error === "firstLoad") {
Expand Down