fix: repair tests and formatting that Gitea CI never checked
- mock the job-listing probe in discover-jobs tests (they hit the real network via example.com fixtures) - update JobDetailPanel skip test to the runJobAction API introduced by the undo feature - fix jobActionUndo test action type and apply Biome formatting that drifted while Gitea CI had no lint lane
This commit is contained in:
parent
285b094bce
commit
a16fa88a2d
@ -6,7 +6,10 @@ import type { Job } from "@shared/types.js";
|
||||
import type React from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { toastMoveToReadyUndo, toastWithUndo } from "@/client/lib/jobActionUndo";
|
||||
import {
|
||||
toastMoveToReadyUndo,
|
||||
toastWithUndo,
|
||||
} from "@/client/lib/jobActionUndo";
|
||||
import { trackProductEvent } from "@/lib/analytics";
|
||||
import { JobDetailsEditDrawer } from "../JobDetailsEditDrawer";
|
||||
import { DecideMode } from "./DecideMode";
|
||||
|
||||
@ -14,10 +14,7 @@ vi.mock("@client/api", () => ({
|
||||
|
||||
import * as api from "@client/api";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
restoresFromSkipResults,
|
||||
toastWithUndo,
|
||||
} from "./jobActionUndo";
|
||||
import { restoresFromSkipResults, toastWithUndo } from "./jobActionUndo";
|
||||
|
||||
describe("restoresFromSkipResults", () => {
|
||||
it("includes primary and also-skipped restores", () => {
|
||||
@ -49,7 +46,7 @@ describe("toastWithUndo", () => {
|
||||
expect(toast.message).toHaveBeenCalled();
|
||||
const call = vi.mocked(toast.message).mock.calls[0];
|
||||
const opts = call?.[1] as {
|
||||
action?: { onClick: () => void };
|
||||
action?: { label?: string; onClick: () => void };
|
||||
};
|
||||
expect(opts?.action?.label ?? opts?.action).toBeTruthy();
|
||||
opts?.action?.onClick();
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
import * as api from "@client/api";
|
||||
import type { JobActionResult, JobStatus, JobStatusRestore } from "@shared/types";
|
||||
import type {
|
||||
JobActionResult,
|
||||
JobStatus,
|
||||
JobStatusRestore,
|
||||
} from "@shared/types";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const DEFAULT_UNDO_MS = 8_000;
|
||||
|
||||
@ -27,7 +27,6 @@ import {
|
||||
import React from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { toast } from "sonner";
|
||||
import { toastAppliedUndo, toastWithUndo } from "@/client/lib/jobActionUndo";
|
||||
import { invalidateJobData } from "@/client/hooks/queries/invalidate";
|
||||
import {
|
||||
useCheckSponsorMutation,
|
||||
@ -38,6 +37,7 @@ import {
|
||||
useUpdateJobMutation,
|
||||
} from "@/client/hooks/queries/useJobMutations";
|
||||
import { useQueryErrorToast } from "@/client/hooks/useQueryErrorToast";
|
||||
import { toastAppliedUndo, toastWithUndo } from "@/client/lib/jobActionUndo";
|
||||
import { queryKeys } from "@/client/lib/queryKeys";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
@ -34,23 +34,25 @@ vi.mock("../api", () => ({
|
||||
}),
|
||||
getProfile: vi.fn().mockResolvedValue({ personName: "Test User" }),
|
||||
skipJob: vi.fn().mockResolvedValue({}),
|
||||
runJobAction: vi.fn().mockImplementation(async (input: { action: string; jobIds: string[] }) => {
|
||||
const jobId = input.jobIds[0] ?? "job-1";
|
||||
return {
|
||||
action: input.action,
|
||||
requested: 1,
|
||||
succeeded: 1,
|
||||
failed: 0,
|
||||
results: [
|
||||
{
|
||||
jobId,
|
||||
ok: true,
|
||||
job: { id: jobId, status: "skipped" },
|
||||
undo: { previousStatus: "ready", alsoSkipped: [] },
|
||||
},
|
||||
],
|
||||
};
|
||||
}),
|
||||
runJobAction: vi
|
||||
.fn()
|
||||
.mockImplementation(async (input: { action: string; jobIds: string[] }) => {
|
||||
const jobId = input.jobIds[0] ?? "job-1";
|
||||
return {
|
||||
action: input.action,
|
||||
requested: 1,
|
||||
succeeded: 1,
|
||||
failed: 0,
|
||||
results: [
|
||||
{
|
||||
jobId,
|
||||
ok: true,
|
||||
job: { id: jobId, status: "skipped" },
|
||||
undo: { previousStatus: "ready", alsoSkipped: [] },
|
||||
},
|
||||
],
|
||||
};
|
||||
}),
|
||||
restoreJobStatuses: vi.fn().mockResolvedValue({ restored: 1, jobs: [] }),
|
||||
markAsApplied: vi.fn().mockResolvedValue({}),
|
||||
processJob: vi.fn().mockResolvedValue({}),
|
||||
|
||||
@ -136,7 +136,7 @@ vi.mock("@client/api", () => ({
|
||||
processJob: vi.fn(),
|
||||
generateJobPdf: vi.fn(),
|
||||
markAsApplied: vi.fn(),
|
||||
skipJob: vi.fn(),
|
||||
runJobAction: vi.fn(),
|
||||
getProfile: vi.fn().mockResolvedValue({}),
|
||||
}));
|
||||
|
||||
@ -346,7 +346,16 @@ describe("JobDetailPanel", () => {
|
||||
|
||||
it("skips a job from the menu", async () => {
|
||||
const onJobUpdated = vi.fn().mockResolvedValue(undefined);
|
||||
vi.mocked(api.skipJob).mockResolvedValue(undefined as any);
|
||||
vi.mocked(api.runJobAction).mockResolvedValue({
|
||||
results: [
|
||||
{
|
||||
jobId: "job-1",
|
||||
ok: true,
|
||||
job: { id: "job-1", status: "skipped" },
|
||||
undo: { previousStatus: "ready" },
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
|
||||
await renderJobDetailPanel({
|
||||
activeTab: "all",
|
||||
@ -362,7 +371,12 @@ describe("JobDetailPanel", () => {
|
||||
const skipItem = await screen.findByRole("menuitem", { name: /skip job/i });
|
||||
fireEvent.click(skipItem);
|
||||
|
||||
await waitFor(() => expect(api.skipJob).toHaveBeenCalledWith("job-1"));
|
||||
await waitFor(() =>
|
||||
expect(api.runJobAction).toHaveBeenCalledWith({
|
||||
action: "skip",
|
||||
jobIds: ["job-1"],
|
||||
}),
|
||||
);
|
||||
expect(onJobUpdated).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@ -33,8 +33,8 @@ import {
|
||||
import type React from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { toastAppliedUndo, toastWithUndo } from "@/client/lib/jobActionUndo";
|
||||
import { JobDescriptionMarkdown } from "@/client/components/JobDescriptionMarkdown";
|
||||
import { toastAppliedUndo, toastWithUndo } from "@/client/lib/jobActionUndo";
|
||||
import { getRenderableJobDescription } from "@/client/lib/jobDescription";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
@ -324,7 +324,12 @@ export const JobDetailPanel: React.FC<JobDetailPanelProps> = ({
|
||||
});
|
||||
toastWithUndo({
|
||||
title: "Moved to in progress",
|
||||
restores: [{ jobId: selectedJob.id, status: fromStatus === "applied" ? "applied" : "applied" }],
|
||||
restores: [
|
||||
{
|
||||
jobId: selectedJob.id,
|
||||
status: fromStatus === "applied" ? "applied" : "applied",
|
||||
},
|
||||
],
|
||||
variant: "success",
|
||||
onRestored: onJobUpdated,
|
||||
successMessage: "Reverted to Applied",
|
||||
|
||||
@ -7,7 +7,10 @@ import {
|
||||
} from "@shared/types.js";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { restoresFromSkipResults, toastWithUndo } from "@/client/lib/jobActionUndo";
|
||||
import {
|
||||
restoresFromSkipResults,
|
||||
toastWithUndo,
|
||||
} from "@/client/lib/jobActionUndo";
|
||||
import { trackProductEvent } from "@/lib/analytics";
|
||||
import type { FilterTab } from "./constants";
|
||||
import { JobActionProgressToast } from "./JobActionProgressToast";
|
||||
|
||||
@ -9,7 +9,11 @@ import { SHORTCUTS } from "@client/lib/shortcut-map";
|
||||
import type { JobAction, JobListItem } from "@shared/types.js";
|
||||
import { useCallback, useRef } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { toastAppliedUndo, toastMoveToReadyUndo, toastWithUndo } from "@/client/lib/jobActionUndo";
|
||||
import {
|
||||
toastAppliedUndo,
|
||||
toastMoveToReadyUndo,
|
||||
toastWithUndo,
|
||||
} from "@/client/lib/jobActionUndo";
|
||||
import { safeFilenamePart } from "@/lib/utils";
|
||||
import type { FilterTab } from "./constants";
|
||||
import { tabs } from "./constants";
|
||||
|
||||
@ -1004,9 +1004,12 @@ jobsRouter.post("/restore-status", async (req: Request, res: Response) => {
|
||||
try {
|
||||
const parsed = restoreStatusSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
return fail(res, badRequest("Invalid restore payload", {
|
||||
issues: parsed.error.issues,
|
||||
}));
|
||||
return fail(
|
||||
res,
|
||||
badRequest("Invalid restore payload", {
|
||||
issues: parsed.error.issues,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const restored: Job[] = [];
|
||||
@ -1470,7 +1473,6 @@ jobsRouter.post("/:id/generate-pdf", async (req: Request, res: Response) => {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* POST /api/jobs/:id/apply - Mark a job as applied
|
||||
*/
|
||||
|
||||
@ -23,6 +23,14 @@ vi.mock("@server/extractors/registry", () => ({
|
||||
getExtractorRegistry: vi.fn(),
|
||||
}));
|
||||
|
||||
// Keep tests hermetic: the real probe fetches listing URLs over the network.
|
||||
vi.mock("@shared/job-listing-probe.js", () => ({
|
||||
filterExpiredJobListings: vi.fn(async <T>(jobs: readonly T[]) => ({
|
||||
kept: [...jobs],
|
||||
dropped: 0,
|
||||
})),
|
||||
}));
|
||||
|
||||
const baseConfig: PipelineConfig = {
|
||||
topN: 10,
|
||||
minSuitabilityScore: 50,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user