diff --git a/.agents/skills/jobindex-search/cli/tests/search-page.test.ts b/.agents/skills/jobindex-search/cli/tests/search-page.test.ts new file mode 100644 index 0000000..b9a2414 --- /dev/null +++ b/.agents/skills/jobindex-search/cli/tests/search-page.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, test } from "bun:test"; +import { parseSearchPage } from "../src/helpers"; + +// parseSearchPage had no tests at all: mutating total to stop using +// sr.hitcount survived the whole suite (review finding F35, 2026-08-19). +// The fixture mirrors the real Stash nesting documented in helpers.ts: +// jobsearch/result_app -> storeData -> searchResponse -> { hitcount, results[] }. +function stashPage(searchResponse: object): string { + const stash = { jobsearch: { result_app: { storeData: { searchResponse } } } }; + return `
`; +} + +const RESULT = { + tid: "h1689961", + headline: "Softwareudvikler", + company: { name: "Acme A/S", homeurl: "https://acme.example" }, + area: "Aarhus", + firstdate: "2026-08-10", + apply_deadline: "2026-09-11T00:00:00", +}; + +describe("parseSearchPage", () => { + test("total comes from hitcount, not the page's result count", () => { + const page = stashPage({ hitcount: 435, results: [RESULT] }); + const parsed = parseSearchPage(page); + expect(parsed.total).toBe(435); + expect(parsed.results).toHaveLength(1); + }); + + test("total falls back to the result count when hitcount is absent", () => { + const page = stashPage({ results: [RESULT, { ...RESULT, tid: "h2" }] }); + expect(parseSearchPage(page).total).toBe(2); + }); + + test("maps the contract fields from a Stash result", () => { + const [job] = parseSearchPage(stashPage({ hitcount: 1, results: [RESULT] })).results; + expect(job).toMatchObject({ + id: "h1689961", + title: "Softwareudvikler", + company: "Acme A/S", + location: "Aarhus", + date: "2026-08-10", + deadline: "2026-09-11", + url: "https://www.jobindex.dk/jobannonce/h1689961", + }); + }); + + test("falls back to lastdate when apply_deadline is absent", () => { + const [job] = parseSearchPage( + stashPage({ hitcount: 1, results: [{ ...RESULT, apply_deadline: undefined, lastdate: "2026-09-30" }] }), + ).results; + expect(job.deadline).toBe("2026-09-30"); + }); +}); diff --git a/.agents/skills/linkedin-search/cli/tests/parsing.test.ts b/.agents/skills/linkedin-search/cli/tests/parsing.test.ts index 93b44d1..4cfd98d 100644 --- a/.agents/skills/linkedin-search/cli/tests/parsing.test.ts +++ b/.agents/skills/linkedin-search/cli/tests/parsing.test.ts @@ -14,6 +14,46 @@ function searchCard(id: string, title: string, company = "Acme"): string { `; } +// The /scrape contract fields beyond title/company. The original fixture had +// no