test(portals): cover linkedin card date/location and jobindex parseSearchPage

The linkedin fixture was purpose-built for entity decoding and had no
<time> or location element, so removing the date extraction - a /scrape
contract field on a default-ON portal - survived the suite. jobindex's
parseSearchPage (the Stash parser behind every search) had zero tests,
so meta.total silently dropping hitcount survived too. Both mutations
now fail exactly the new tests. The ASAP deadline branch is deliberately
left to the F12 fix, which changes its behaviour to null. Review finding
F35 (2026-08-19).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mads Lorentzen
2026-08-19 20:03:00 +02:00
co-authored by Claude Opus 5
parent 65fbe8b8a4
commit 2edf8c41f1
3 changed files with 101 additions and 0 deletions
@@ -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 `<html><head><script>var Stash = ${JSON.stringify(stash)};</script></head></html>`;
}
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");
});
});