feat(interview): integrate contextual AI prep
This commit is contained in:
@@ -1,9 +1,13 @@
|
||||
import React from "react";
|
||||
import "@testing-library/jest-dom";
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { fireEvent, render as rtlRender, screen, waitFor } from "@testing-library/react";
|
||||
|
||||
import { ApplicationFollowUp, ApplicationInterviewPrep } from "./components/InterviewPrep";
|
||||
import { api } from "./api";
|
||||
import { I18nProvider } from "./i18n/I18nProvider";
|
||||
import { ConfirmProvider } from "./confirm";
|
||||
|
||||
const render = (ui: React.ReactElement) => rtlRender(<I18nProvider><ConfirmProvider>{ui}</ConfirmProvider></I18nProvider>);
|
||||
|
||||
jest.mock("./api", () => ({
|
||||
api: {
|
||||
@@ -48,6 +52,7 @@ const followUp = { followUpAt: "2026-07-26T00:00:00", nextAction: "Chase recruit
|
||||
function routeGet(overrides: Record<string, any> = {}) {
|
||||
mockedApi.get.mockImplementation((url: string) => {
|
||||
if (url.endsWith("/follow-up")) return Promise.resolve({ data: overrides.followUp ?? followUp } as any);
|
||||
if (url.endsWith("/cv")) return Promise.resolve({ data: overrides.cv ?? { attachedVariantId: 3, attachedVariantName: "Backend CV" } } as any);
|
||||
return Promise.resolve({ data: overrides.board ?? board } as any);
|
||||
});
|
||||
}
|
||||
@@ -116,6 +121,7 @@ test("deleting a prep item calls delete", async () => {
|
||||
|
||||
render(<ApplicationInterviewPrep jobId={7} />);
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Delete: Funding history" }));
|
||||
fireEvent.click(await screen.findByRole("button", { name: "Delete" }));
|
||||
|
||||
await waitFor(() => expect(mockedApi.delete).toHaveBeenCalledWith("/jobapplications/7/interview-prep/1"));
|
||||
});
|
||||
@@ -129,6 +135,36 @@ test("empty prep shows a useful empty state", async () => {
|
||||
expect(screen.getByText(/has not reached an interview stage/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("AI preparation uses the linked CV and only saves after explicit acceptance", async () => {
|
||||
routeGet();
|
||||
mockedApi.post.mockImplementation((url: string) => {
|
||||
if (url.endsWith("/ai/generate")) {
|
||||
return Promise.resolve({ data: { result: { text: "## Technical\nExplain dependency injection." } } } as any);
|
||||
}
|
||||
return Promise.resolve({ data: board.groups[0].items[0] } as any);
|
||||
});
|
||||
|
||||
render(<ApplicationInterviewPrep jobId={7} />);
|
||||
expect(await screen.findByText("Backend CV")).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByRole("button", { name: "Generate preparation" }));
|
||||
|
||||
expect(await screen.findByText(/Explain dependency injection/i)).toBeInTheDocument();
|
||||
expect(mockedApi.post).toHaveBeenCalledWith(
|
||||
"/jobapplications/7/ai/generate",
|
||||
expect.objectContaining({ module: "interview", targetLanguage: "en" }),
|
||||
);
|
||||
expect(mockedApi.post).not.toHaveBeenCalledWith(
|
||||
"/jobapplications/7/interview-prep",
|
||||
expect.anything(),
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: "Add to preparation" }));
|
||||
await waitFor(() => expect(mockedApi.post).toHaveBeenCalledWith(
|
||||
"/jobapplications/7/interview-prep",
|
||||
expect.objectContaining({ source: "ai", content: expect.stringContaining("dependency injection") }),
|
||||
));
|
||||
});
|
||||
|
||||
test("prep surfaces a load error", async () => {
|
||||
mockedApi.get.mockRejectedValue(new Error("boom"));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user