feat(capture): add minimal browser extension
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# Jobjakt Quick Capture
|
||||
|
||||
The Manifest V3 extension opens the current page or selected link in Jobjakt's existing reviewed import flow. It does not scrape page content, store credentials, or submit an application automatically.
|
||||
|
||||
## Install locally
|
||||
|
||||
1. Open `chrome://extensions` (or the equivalent extensions page in Edge).
|
||||
2. Enable developer mode.
|
||||
3. Choose **Load unpacked** and select this `browser-extension` folder.
|
||||
4. Open a vacancy and click the Jobjakt extension, or use **Save vacancy to Jobjakt** from the page/link context menu.
|
||||
|
||||
The Jobjakt add-job dialog opens with the vacancy URL and runs the same server-side preview, validation, duplicate checking, and user review used by normal URL imports.
|
||||
|
||||
## Test
|
||||
|
||||
```sh
|
||||
node --test browser-extension/tests/*.test.mjs
|
||||
```
|
||||
|
||||
No build or package installation is required. Zip the folder contents for store submission after adding store artwork and completing the browser-store privacy declarations.
|
||||
@@ -0,0 +1,23 @@
|
||||
import { buildCaptureUrl } from "./capture-url.mjs";
|
||||
|
||||
const menuId = "save-vacancy-to-jobjakt";
|
||||
|
||||
function openCapture(pageUrl) {
|
||||
const captureUrl = buildCaptureUrl(pageUrl);
|
||||
if (captureUrl) chrome.tabs.create({ url: captureUrl });
|
||||
}
|
||||
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
chrome.contextMenus.removeAll(() => {
|
||||
chrome.contextMenus.create({
|
||||
id: menuId,
|
||||
title: "Save vacancy to Jobjakt",
|
||||
contexts: ["page", "link"]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
chrome.action.onClicked.addListener((tab) => openCapture(tab.url));
|
||||
chrome.contextMenus.onClicked.addListener((info, tab) => {
|
||||
if (info.menuItemId === menuId) openCapture(info.linkUrl || info.pageUrl || tab?.url);
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
export const JOBBJAKT_CAPTURE_ORIGIN = "https://jobs.cesnimda.uk";
|
||||
|
||||
export function buildCaptureUrl(pageUrl, origin = JOBBJAKT_CAPTURE_ORIGIN) {
|
||||
let source;
|
||||
try {
|
||||
source = new URL(pageUrl);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
if (source.protocol !== "http:" && source.protocol !== "https:") return null;
|
||||
|
||||
const capture = new URL("/", origin);
|
||||
capture.searchParams.set("add", source.href);
|
||||
return capture.href;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Jobjakt Quick Capture",
|
||||
"version": "1.0.0",
|
||||
"description": "Send the current vacancy to Jobjakt's reviewed import flow.",
|
||||
"permissions": ["activeTab", "contextMenus"],
|
||||
"action": {
|
||||
"default_title": "Save vacancy to Jobjakt"
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "background.mjs",
|
||||
"type": "module"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { buildCaptureUrl } from "../capture-url.mjs";
|
||||
|
||||
test("builds the existing reviewed capture route", () => {
|
||||
const result = new URL(buildCaptureUrl("https://example.test/jobs/42?from=search"));
|
||||
assert.equal(result.origin, "https://jobs.cesnimda.uk");
|
||||
assert.equal(result.pathname, "/");
|
||||
assert.equal(result.searchParams.get("add"), "https://example.test/jobs/42?from=search");
|
||||
});
|
||||
|
||||
test("rejects privileged and malformed URLs", () => {
|
||||
assert.equal(buildCaptureUrl("chrome://settings"), null);
|
||||
assert.equal(buildCaptureUrl("javascript:alert(1)"), null);
|
||||
assert.equal(buildCaptureUrl("not a url"), null);
|
||||
});
|
||||
|
||||
test("supports a local application origin during development", () => {
|
||||
assert.equal(
|
||||
buildCaptureUrl("https://arbeidsplassen.nav.no/stillinger/stilling/abc", "http://localhost:3300"),
|
||||
"http://localhost:3300/?add=https%3A%2F%2Farbeidsplassen.nav.no%2Fstillinger%2Fstilling%2Fabc"
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user