mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 08:36:25 +00:00
fix(linkedin-search): accept LinkedIn job URLs with trailing slashes in detail command (#411) (#412)
* fix(linkedin-search): accept LinkedIn job URLs with trailing slashes in detail command (#411) * docs(changelog): record linkedin-search trailing-slash fix (#411)
This commit is contained in:
@@ -6,10 +6,10 @@ export interface DetailOpts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Accept a raw job ID, a job-view URL, or a job URN. */
|
/** Accept a raw job ID, a job-view URL, or a job URN. */
|
||||||
function normalizeId(input: string): string | null {
|
export function normalizeId(input: string): string | null {
|
||||||
const urn = input.match(/urn:li:jobPosting:(\d+)/)
|
const urn = input.match(/urn:li:jobPosting:(\d+)/)
|
||||||
if (urn) return urn[1]
|
if (urn) return urn[1]
|
||||||
const url = input.match(/-(\d{6,})(?:\?|$)/) || input.match(/\/(\d{6,})(?:\?|$)/)
|
const url = input.match(/-(\d{6,})(?:[\/?]|$)/) || input.match(/\/(\d{6,})(?:[\/?]|$)/)
|
||||||
if (url) return url[1]
|
if (url) return url[1]
|
||||||
const bare = input.match(/^\d{6,}$/)
|
const bare = input.match(/^\d{6,}$/)
|
||||||
if (bare) return input
|
if (bare) return input
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe, test, expect } from "bun:test";
|
import { describe, test, expect } from "bun:test";
|
||||||
import { parseJobCards, parseJobDetail, extractDivContent, minutesToTPR } from "../src/helpers";
|
import { parseJobCards, parseJobDetail, extractDivContent, minutesToTPR } from "../src/helpers";
|
||||||
|
import { normalizeId } from "../src/commands/detail";
|
||||||
|
|
||||||
// Minimal search-card markup: parseJobCards splits on the job-posting URN and
|
// Minimal search-card markup: parseJobCards splits on the job-posting URN and
|
||||||
// needs an id, a base-search-card__title, and a full-link. Everything else is
|
// needs an id, a base-search-card__title, and a full-link. Everything else is
|
||||||
@@ -222,3 +223,55 @@ describe("minutesToTPR", () => {
|
|||||||
expect(minutesToTPR(-5)).toBeNull();
|
expect(minutesToTPR(-5)).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("normalizeId", () => {
|
||||||
|
test("extracts ID from raw numeric string", () => {
|
||||||
|
expect(normalizeId("1234567890")).toBe("1234567890");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("extracts ID from URN", () => {
|
||||||
|
expect(normalizeId("urn:li:jobPosting:1234567890")).toBe("1234567890");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("extracts ID from simple job view URL without trailing slash", () => {
|
||||||
|
expect(normalizeId("https://www.linkedin.com/jobs/view/1234567890")).toBe("1234567890");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("extracts ID from simple job view URL with trailing slash", () => {
|
||||||
|
expect(normalizeId("https://www.linkedin.com/jobs/view/1234567890/")).toBe("1234567890");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("extracts ID from simple job view URL with query parameter", () => {
|
||||||
|
expect(normalizeId("https://www.linkedin.com/jobs/view/1234567890?refId=abc")).toBe("1234567890");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("extracts ID from simple job view URL with trailing slash and query parameter", () => {
|
||||||
|
expect(normalizeId("https://www.linkedin.com/jobs/view/1234567890/?refId=abc")).toBe("1234567890");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("extracts ID from slug URL without trailing slash", () => {
|
||||||
|
expect(normalizeId("https://www.linkedin.com/jobs/view/software-engineer-1234567890")).toBe("1234567890");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("extracts ID from slug URL with trailing slash", () => {
|
||||||
|
expect(normalizeId("https://www.linkedin.com/jobs/view/software-engineer-1234567890/")).toBe("1234567890");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("extracts ID from slug URL with trailing slash and tracking query params", () => {
|
||||||
|
expect(
|
||||||
|
normalizeId("https://www.linkedin.com/jobs/view/software-engineer-at-company-1234567890/?trackingId=xyz&refId=123"),
|
||||||
|
).toBe("1234567890");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("extracts ID from regional subdomain LinkedIn URL with trailing slash", () => {
|
||||||
|
expect(normalizeId("https://dk.linkedin.com/jobs/view/data-scientist-9876543210/")).toBe("9876543210");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("returns null for non-job URLs and invalid strings", () => {
|
||||||
|
expect(normalizeId("https://www.linkedin.com/feed/")).toBeNull();
|
||||||
|
expect(normalizeId("not-a-url")).toBeNull();
|
||||||
|
expect(normalizeId("12345")).toBeNull(); // fewer than 6 digits
|
||||||
|
expect(normalizeId("")).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,14 @@ per-file diff commands.
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- **`linkedin-search detail` accepts LinkedIn job URLs with trailing slashes** (#411) -
|
||||||
|
passing a job URL with a trailing slash (e.g., `https://www.linkedin.com/jobs/view/<id>/`
|
||||||
|
or a slugged variant with or without query strings) failed validation and exited 1 with
|
||||||
|
`BAD_ID` before any network request because the regex delimiter strictly expected `?`
|
||||||
|
or end-of-string immediately after the numeric ID. The boundary check now matches
|
||||||
|
`[\/?]`, correctly extracting IDs from browser-copied URLs, regional subdomains, and
|
||||||
|
links with tracking parameters. Pinned by eleven new cases in `parsing.test.ts`.
|
||||||
|
|
||||||
- **The `documents/interview/**` ignore rule no longer claims interview prep is written there**
|
- **The `documents/interview/**` ignore rule no longer claims interview prep is written there**
|
||||||
(#336). `/interview` saves its pack to
|
(#336). `/interview` saves its pack to
|
||||||
`documents/applications/<company>_<role>/interview_prep_<stage>.md`, already ignored by
|
`documents/applications/<company>_<role>/interview_prep_<stage>.md`, already ignored by
|
||||||
|
|||||||
Reference in New Issue
Block a user