This commit is contained in:
DaKheera47 2026-01-19 19:26:21 +00:00
parent 90e4feecf5
commit a14814291e
2 changed files with 242 additions and 0 deletions

View File

@ -0,0 +1,168 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { ManualImportSheet } from "./ManualImportSheet";
import * as api from "../api";
import { toast } from "sonner";
vi.mock("../api", () => ({
inferManualJob: vi.fn(),
importManualJob: vi.fn(),
}));
vi.mock("sonner", () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
},
}));
describe("ManualImportSheet", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("runs analyze -> review -> import on the happy path", async () => {
const rawDescription = " Backend Engineer role in London. ";
const onOpenChange = vi.fn();
const onImported = vi.fn().mockResolvedValue(undefined);
vi.mocked(api.inferManualJob).mockResolvedValue({
job: {
title: "Backend Engineer",
employer: "Acme Labs",
location: "London, UK",
},
});
vi.mocked(api.importManualJob).mockResolvedValue({ id: "job-1" } as any);
render(
<ManualImportSheet open onOpenChange={onOpenChange} onImported={onImported} />
);
fireEvent.change(
screen.getByPlaceholderText("Paste the full job description here..."),
{ target: { value: rawDescription } }
);
fireEvent.click(screen.getByRole("button", { name: /analyze jd/i }));
const titleInput = await screen.findByPlaceholderText("e.g. Junior Backend Engineer");
expect(titleInput).toHaveValue("Backend Engineer");
const jdTextarea = screen.getByPlaceholderText("Paste the job description...") as HTMLTextAreaElement;
expect(jdTextarea.value).toBe(rawDescription.trim());
fireEvent.change(screen.getByPlaceholderText("e.g. GBP 45k-55k"), {
target: { value: " 120k " },
});
fireEvent.click(screen.getByRole("button", { name: /import job/i }));
await waitFor(() => expect(api.importManualJob).toHaveBeenCalled());
expect(api.importManualJob).toHaveBeenCalledWith({
job: expect.objectContaining({
title: "Backend Engineer",
employer: "Acme Labs",
location: "London, UK",
salary: "120k",
jobDescription: rawDescription.trim(),
}),
});
await waitFor(() => expect(onImported).toHaveBeenCalledWith("job-1"));
expect(onOpenChange).toHaveBeenCalledWith(false);
expect(toast.success).toHaveBeenCalledWith(
"Job imported",
expect.objectContaining({
description: expect.any(String),
})
);
});
it("shows warnings and requires required fields before import", async () => {
const rawDescription = "Manual QA Engineer role.";
vi.mocked(api.inferManualJob).mockResolvedValue({
job: {},
warning: "AI inference failed. Fill details manually.",
});
render(
<ManualImportSheet open onOpenChange={vi.fn()} onImported={vi.fn()} />
);
fireEvent.change(
screen.getByPlaceholderText("Paste the full job description here..."),
{ target: { value: rawDescription } }
);
fireEvent.click(screen.getByRole("button", { name: /analyze jd/i }));
await screen.findByText("AI inference failed. Fill details manually.");
const importButton = screen.getByRole("button", { name: /import job/i });
expect(importButton).toBeDisabled();
fireEvent.change(screen.getByPlaceholderText("e.g. Junior Backend Engineer"), {
target: { value: "QA Engineer" },
});
fireEvent.change(screen.getByPlaceholderText("e.g. Acme Labs"), {
target: { value: "Acme Labs" },
});
await waitFor(() => expect(importButton).toBeEnabled());
});
it("returns to the paste step when inference fails", async () => {
const rawDescription = "Backend role description.";
vi.mocked(api.inferManualJob).mockRejectedValue(new Error("Inference failed"));
render(
<ManualImportSheet open onOpenChange={vi.fn()} onImported={vi.fn()} />
);
fireEvent.change(
screen.getByPlaceholderText("Paste the full job description here..."),
{ target: { value: rawDescription } }
);
fireEvent.click(screen.getByRole("button", { name: /analyze jd/i }));
await screen.findByText("Inference failed");
expect(screen.getByRole("button", { name: /analyze jd/i })).toBeInTheDocument();
expect(
screen.queryByPlaceholderText("e.g. Junior Backend Engineer")
).not.toBeInTheDocument();
});
it("shows a toast error and keeps the sheet open when import fails", async () => {
vi.mocked(api.inferManualJob).mockResolvedValue({
job: {
title: "Backend Engineer",
employer: "Acme Labs",
},
});
vi.mocked(api.importManualJob).mockRejectedValue(new Error("Import failed"));
const onOpenChange = vi.fn();
render(
<ManualImportSheet open onOpenChange={onOpenChange} onImported={vi.fn()} />
);
fireEvent.change(
screen.getByPlaceholderText("Paste the full job description here..."),
{ target: { value: "Backend Engineer role." } }
);
fireEvent.click(screen.getByRole("button", { name: /analyze jd/i }));
await screen.findByPlaceholderText("e.g. Junior Backend Engineer");
fireEvent.click(screen.getByRole("button", { name: /import job/i }));
await waitFor(() =>
expect(toast.error).toHaveBeenCalledWith("Import failed")
);
expect(onOpenChange).not.toHaveBeenCalled();
expect(screen.getByRole("button", { name: /import job/i })).toBeEnabled();
});
});

View File

@ -0,0 +1,74 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import * as settingsRepo from "../repositories/settings.js";
import { inferManualJobDetails } from "./manualJob.js";
vi.mock("../repositories/settings.js", () => ({
getSetting: vi.fn(),
}));
const originalEnv = process.env;
const originalFetch = global.fetch;
describe("manual job inference", () => {
beforeEach(() => {
vi.resetAllMocks();
process.env = { ...originalEnv, OPENROUTER_API_KEY: "test-key" };
global.fetch = vi.fn();
vi.mocked(settingsRepo.getSetting).mockResolvedValue(null);
});
afterEach(() => {
process.env = originalEnv;
global.fetch = originalFetch;
vi.restoreAllMocks();
});
it("returns a warning when the API key is missing", async () => {
delete process.env.OPENROUTER_API_KEY;
const result = await inferManualJobDetails("JD text");
expect(result.job).toEqual({});
expect(result.warning).toContain("OPENROUTER_API_KEY not set");
expect(global.fetch).not.toHaveBeenCalled();
});
it("parses JSON even when wrapped in markdown fences", async () => {
vi.mocked(global.fetch).mockResolvedValue({
ok: true,
json: async () => ({
choices: [
{
message: {
content:
"Here is the data: ```json\n{ \"title\": \"Backend Engineer\", \"employer\": \"Acme\", \"salary\": \" 100k \" }\n```",
},
},
],
}),
} as any);
const result = await inferManualJobDetails("JD text");
expect(result.warning).toBeUndefined();
expect(result.job).toMatchObject({
title: "Backend Engineer",
employer: "Acme",
salary: "100k",
});
});
it("returns a warning when the API response fails", async () => {
vi.mocked(global.fetch).mockResolvedValue({
ok: false,
status: 500,
} as any);
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
const result = await inferManualJobDetails("JD text");
expect(result.job).toEqual({});
expect(result.warning).toContain("AI inference failed");
warnSpy.mockRestore();
});
});