mirror of
https://github.com/MadsLorentzen/ai-job-search.git
synced 2026-09-17 00:26:26 +00:00
fix(portal-clis): accept full posting URLs in jobbank, jobdanmark, and jobnet detail commands (#430)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { defineCommand, option } from "@bunli/core"
|
||||
import { z } from "zod"
|
||||
import { parse } from "node-html-parser"
|
||||
import { BASE_URL, writeError } from "../helpers.js"
|
||||
import { BASE_URL, normalizeSlug, writeError } from "../helpers.js"
|
||||
import { extractCity, toContractDate } from "./search.js"
|
||||
|
||||
interface JsonLdJobPosting {
|
||||
@@ -226,12 +226,18 @@ export const detail = defineCommand({
|
||||
handler: async ({ flags, positional, signal }) => {
|
||||
if (signal.aborted) return
|
||||
|
||||
const slug = positional[0]
|
||||
if (!slug) {
|
||||
const rawSlug = positional[0]
|
||||
if (!rawSlug) {
|
||||
writeError("slug argument is required", "MISSING_REQUIRED")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const slug = normalizeSlug(rawSlug)
|
||||
if (!slug) {
|
||||
writeError(`Could not extract slug from "${rawSlug}"`, "BAD_ID")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const url = `${BASE_URL}/job/${slug}`
|
||||
|
||||
try {
|
||||
|
||||
@@ -71,3 +71,13 @@ export function writeError(error: string, code: string): void {
|
||||
export function stripHtml(html: string): string {
|
||||
return html.replace(/<[^>]*>/g, "").replace(/\s+/g, " ").trim()
|
||||
}
|
||||
|
||||
export function normalizeSlug(input: string): string | null {
|
||||
const trimmed = input.trim()
|
||||
if (!trimmed) return null
|
||||
const match = trimmed.match(/\/job\/([^/?#]+)/)
|
||||
if (match) return match[1]
|
||||
if (/^[a-zA-Z0-9_-]+$/.test(trimmed)) return trimmed
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { normalizeSlug } from "../src/helpers.js"
|
||||
import { runCLI } from "./helpers.js"
|
||||
|
||||
describe("jobdanmark-search normalizeSlug", () => {
|
||||
test("accepts bare slug", () => {
|
||||
expect(normalizeSlug("software-udvikler-12345")).toBe("software-udvikler-12345")
|
||||
expect(normalizeSlug(" senior_dev_67890 ")).toBe("senior_dev_67890")
|
||||
})
|
||||
|
||||
test("extracts slug from full URL with trailing slash", () => {
|
||||
expect(normalizeSlug("https://jobdanmark.dk/job/software-udvikler-12345/")).toBe("software-udvikler-12345")
|
||||
})
|
||||
|
||||
test("extracts slug from full URL without trailing slash", () => {
|
||||
expect(normalizeSlug("https://jobdanmark.dk/job/software-udvikler-12345")).toBe("software-udvikler-12345")
|
||||
})
|
||||
|
||||
test("extracts slug from relative URL path", () => {
|
||||
expect(normalizeSlug("/job/software-udvikler-12345")).toBe("software-udvikler-12345")
|
||||
expect(normalizeSlug("/job/software-udvikler-12345/")).toBe("software-udvikler-12345")
|
||||
})
|
||||
|
||||
test("extracts slug from URL with query parameters and hash fragments", () => {
|
||||
expect(normalizeSlug("https://jobdanmark.dk/job/software-udvikler-12345?utm_source=test&ref=1")).toBe(
|
||||
"software-udvikler-12345",
|
||||
)
|
||||
expect(normalizeSlug("https://jobdanmark.dk/job/software-udvikler-12345#apply")).toBe(
|
||||
"software-udvikler-12345",
|
||||
)
|
||||
})
|
||||
|
||||
test("rejects empty string and invalid URLs", () => {
|
||||
expect(normalizeSlug("")).toBeNull()
|
||||
expect(normalizeSlug(" ")).toBeNull()
|
||||
expect(normalizeSlug("https://example.com/other/test")).toBeNull()
|
||||
})
|
||||
|
||||
test("CLI detail command rejects invalid slug format with BAD_ID", async () => {
|
||||
const result = await runCLI(["detail", "https://invalid.com/not-a-job"])
|
||||
expect(result.exitCode).toBe(1)
|
||||
const err = JSON.parse(result.stderr)
|
||||
expect(err.code).toBe("BAD_ID")
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user