Ship a Camoufox-backed Google Jobs source, restore Glassdoor results discarded by a python-jobspy GraphQL quirk, and fix Himalayas/Gradcracker zero-job failures. Also harden prior-skip dismiss matching and multi-profile basic-auth switching for CA/US runs.
157 lines
4.6 KiB
TypeScript
157 lines
4.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import {
|
||
buildJobContentFingerprint,
|
||
buildJobDescriptionFingerprint,
|
||
collectJobDedupKeys,
|
||
employersMatchForDismiss,
|
||
jobsMatchEmployerAndTitleDismiss,
|
||
normalizeEmployerForFingerprint,
|
||
normalizeTitleForFingerprint,
|
||
titlesMatchForDismiss,
|
||
} from "./job-fingerprint";
|
||
|
||
describe("buildJobContentFingerprint", () => {
|
||
it("collapses the same role across sources", () => {
|
||
const a = buildJobContentFingerprint({
|
||
employer: "Stripe, Inc.",
|
||
title: "Senior Software Engineer (Backend) - Toronto, ON",
|
||
});
|
||
const b = buildJobContentFingerprint({
|
||
employer: "stripe inc",
|
||
title: "Senior Software Engineer (Backend)",
|
||
});
|
||
expect(a).toBe(b);
|
||
expect(a).not.toBeNull();
|
||
});
|
||
|
||
it("ignores trailing location decorations on titles", () => {
|
||
const a = buildJobContentFingerprint({
|
||
employer: "Acme",
|
||
title: "Software Engineer — Remote",
|
||
});
|
||
const b = buildJobContentFingerprint({
|
||
employer: "Acme",
|
||
title: "Software Engineer",
|
||
});
|
||
expect(a).toBe(b);
|
||
});
|
||
|
||
it("strips diacritics and punctuation", () => {
|
||
const a = buildJobContentFingerprint({
|
||
employer: "Café Münchën",
|
||
title: "Étudiant – Stage",
|
||
});
|
||
const b = buildJobContentFingerprint({
|
||
employer: "cafe munchen",
|
||
title: "Etudiant Stage",
|
||
});
|
||
expect(a).toBe(b);
|
||
});
|
||
|
||
it("returns null when employer or title is empty", () => {
|
||
expect(
|
||
buildJobContentFingerprint({ employer: "", title: "Engineer" }),
|
||
).toBeNull();
|
||
expect(
|
||
buildJobContentFingerprint({ employer: "Acme", title: "" }),
|
||
).toBeNull();
|
||
});
|
||
|
||
it("does not collapse different roles at the same employer", () => {
|
||
const a = buildJobContentFingerprint({
|
||
employer: "Acme",
|
||
title: "Software Engineer",
|
||
});
|
||
const b = buildJobContentFingerprint({
|
||
employer: "Acme",
|
||
title: "Product Designer",
|
||
});
|
||
expect(a).not.toBe(b);
|
||
});
|
||
|
||
it("matches reposts with the same employer and description body", () => {
|
||
const description =
|
||
"We are hiring an Automation Test Engineer to build scalable test frameworks. ".repeat(
|
||
4,
|
||
);
|
||
const a = buildJobDescriptionFingerprint({
|
||
employer: "Joveo",
|
||
jobDescription: description,
|
||
});
|
||
const b = buildJobDescriptionFingerprint({
|
||
employer: "Joveo",
|
||
jobDescription: description,
|
||
});
|
||
expect(a).toBe(b);
|
||
expect(
|
||
collectJobDedupKeys({
|
||
employer: "Joveo",
|
||
title: "SDET",
|
||
jobDescription: description,
|
||
}),
|
||
).toContain(a);
|
||
});
|
||
|
||
describe("normalizers", () => {
|
||
it("normalizeEmployerForFingerprint strips legal suffixes", () => {
|
||
expect(normalizeEmployerForFingerprint("Acme Corporation")).toBe("acme");
|
||
expect(normalizeEmployerForFingerprint("Acme, LLC")).toBe("acme");
|
||
expect(normalizeEmployerForFingerprint("Acme GmbH")).toBe("acme");
|
||
});
|
||
|
||
it("normalizeEmployerForFingerprint strips leading punctuation", () => {
|
||
expect(normalizeEmployerForFingerprint(". CGI IT UK Limited")).toBe(
|
||
"cgiituklimited",
|
||
);
|
||
});
|
||
|
||
it("normalizeTitleForFingerprint drops leading repost markers", () => {
|
||
expect(normalizeTitleForFingerprint("[Reposted] Software Engineer")).toBe(
|
||
"softwareengineer",
|
||
);
|
||
});
|
||
});
|
||
|
||
describe("prior skip dismiss matching", () => {
|
||
it("employersMatchForDismiss allows prefix matches", () => {
|
||
expect(
|
||
employersMatchForDismiss("cgi", normalizeEmployerForFingerprint("CGI")),
|
||
).toBe(true);
|
||
expect(
|
||
employersMatchForDismiss(
|
||
normalizeEmployerForFingerprint("CGI IT UK Limited"),
|
||
normalizeEmployerForFingerprint("CGI"),
|
||
),
|
||
).toBe(true);
|
||
});
|
||
|
||
it("titlesMatchForDismiss allows plural variants", () => {
|
||
expect(
|
||
titlesMatchForDismiss(
|
||
normalizeTitleForFingerprint("Automation Test Engineer"),
|
||
normalizeTitleForFingerprint("Automation Test Engineers"),
|
||
),
|
||
).toBe(true);
|
||
});
|
||
|
||
it("jobsMatchEmployerAndTitleDismiss requires same title family", () => {
|
||
expect(
|
||
jobsMatchEmployerAndTitleDismiss({
|
||
employerA: "CGI IT UK Limited",
|
||
titleA: "Automation Test Engineers",
|
||
employerB: "CGI",
|
||
titleB: "Automation Test Engineer",
|
||
}),
|
||
).toBe(true);
|
||
expect(
|
||
jobsMatchEmployerAndTitleDismiss({
|
||
employerA: "Cognizant",
|
||
titleA: "AI Automation Test Engineer",
|
||
employerB: "Cognizant",
|
||
titleB: "Automation Test Engineer",
|
||
}),
|
||
).toBe(false);
|
||
});
|
||
});
|
||
});
|