From 93edf166d0a84bdb690cc219f586c73b37294c2b Mon Sep 17 00:00:00 2001 From: Alexander Brown Date: Tue, 12 May 2026 16:32:15 -0700 Subject: [PATCH] fix(website): link careers page to Ashby job description, not application form (#12200) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *PR Created by the Glary-Bot Agent* --- ## Summary The careers page at comfy.org/careers was linking every role to its Ashby application form (`.../{id}/application`) instead of the job description page (`.../{id}/`). Users expect to first read the role description, not land on the submit-resume page. Ashby's job board API returns both `jobUrl` (description) and `applyUrl` (application form). `toDomainRole` was preferring `applyUrl`; this PR switches to `jobUrl` and renames the `Role` field accordingly so the field name matches its meaning. ## Changes - `apps/website/src/utils/ashby.ts`: use `job.jobUrl` directly instead of `job.applyUrl ?? job.jobUrl`. - `apps/website/src/data/roles.ts`: rename `Role.applyUrl` → `Role.jobUrl`. - `apps/website/src/components/careers/RolesSection.vue`: update the `` binding. - `apps/website/src/data/ashby-roles.snapshot.json`: regenerated fallback snapshot — URLs stripped of `/application`, `id`s recomputed from the new URLs. - Unit + E2E tests updated; new E2E assertion that links do not end in `/application` prevents regressions. The Ashby schema (`ashby.schema.ts`) still accepts `applyUrl` since the API returns it — we just no longer consume it. ## Verification - `pnpm test:unit` — 70/70 pass - `pnpm typecheck` — 0 errors - `pnpm build` — succeeds; inspected `dist/careers/index.html`, all 19 Ashby links now point to description URLs and zero contain `/application` - Oracle code review — 0 issues Fixes user report in #hiring-ideas (Slack). ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12200-fix-website-link-careers-page-to-Ashby-job-description-not-application-form-35e6d73d3650815cbedadf974f7d3364) by [Unito](https://www.unito.io) --------- Co-authored-by: Glary-Bot --- apps/website/e2e/careers.spec.ts | 24 +++--- .../src/components/careers/RolesSection.vue | 2 +- .../src/data/ashby-roles.snapshot.json | 76 +++++++++---------- apps/website/src/data/roles.ts | 2 +- apps/website/src/utils/ashby.ci.test.ts | 4 +- apps/website/src/utils/ashby.test.ts | 23 +----- apps/website/src/utils/ashby.ts | 6 +- 7 files changed, 62 insertions(+), 75 deletions(-) diff --git a/apps/website/e2e/careers.spec.ts b/apps/website/e2e/careers.spec.ts index ae98056dde..22263c1bfc 100644 --- a/apps/website/e2e/careers.spec.ts +++ b/apps/website/e2e/careers.spec.ts @@ -23,15 +23,6 @@ test.describe('Careers page @smoke', () => { expect(await roles.count()).toBeGreaterThan(0) }) - test('each role links to jobs.ashbyhq.com', async ({ page }) => { - const roles = page.getByTestId('careers-role-link') - const count = await roles.count() - for (let i = 0; i < count; i++) { - const href = await roles.nth(i).getAttribute('href') - expect(href).toMatch(/^https:\/\/jobs\.ashbyhq\.com\//) - } - }) - test('clicking a department button scrolls to and activates that section', async ({ page }) => { @@ -63,6 +54,21 @@ test.describe('Careers page @smoke', () => { }) }) +test.describe('Careers page role links', () => { + test('each role links to the Ashby job description page, not the application form', async ({ + page + }) => { + await page.goto('/careers') + const roles = page.getByTestId('careers-role-link') + const count = await roles.count() + for (let i = 0; i < count; i++) { + const href = await roles.nth(i).getAttribute('href') + expect(href).toMatch(/^https:\/\/jobs\.ashbyhq\.com\//) + expect(href).not.toMatch(/\/application\/?$/) + } + }) +}) + test.describe('Careers page (zh-CN) @smoke', () => { test('renders localized heading and roles', async ({ page }) => { await page.goto('/zh-CN/careers') diff --git a/apps/website/src/components/careers/RolesSection.vue b/apps/website/src/components/careers/RolesSection.vue index eee25b81fb..f57a400f02 100644 --- a/apps/website/src/components/careers/RolesSection.vue +++ b/apps/website/src/components/careers/RolesSection.vue @@ -130,7 +130,7 @@ function scrollToDepartment(deptKey: string) { { if (outcome.status !== 'fresh') return expect(outcome.droppedCount).toBe(0) expect(outcome.snapshot.departments).toHaveLength(1) - expect(outcome.snapshot.departments[0]!.roles[0]!.applyUrl).toMatch( - /design-engineer\/apply$/ - ) - }) - - it('falls back to jobUrl when applyUrl is missing and keeps the role', async () => { - const job = validJob() - delete (job as Record).applyUrl - const fetchImpl = vi.fn(async () => - response({ apiVersion: '1', jobs: [job] }) - ) - const outcome = await fetchRolesForBuild({ - apiKey: KEY, - boardName: BOARD, - baseUrl: BASE_URL, - fetchImpl: fetchImpl as unknown as typeof fetch - }) - expect(outcome.status).toBe('fresh') - if (outcome.status !== 'fresh') return - expect(outcome.snapshot.departments[0]!.roles[0]!.applyUrl).toBe( + expect(outcome.snapshot.departments[0]!.roles[0]!.jobUrl).toBe( 'https://jobs.ashbyhq.com/comfy-org/design-engineer' ) }) diff --git a/apps/website/src/utils/ashby.ts b/apps/website/src/utils/ashby.ts index c863172db2..6402a80bec 100644 --- a/apps/website/src/utils/ashby.ts +++ b/apps/website/src/utils/ashby.ts @@ -243,13 +243,13 @@ function groupByDepartment(jobs: readonly AshbyJobPosting[]): Department[] { } function toDomainRole(job: AshbyJobPosting, department: string): Role { - const applyUrl = job.applyUrl ?? job.jobUrl + const jobUrl = job.jobUrl return { - id: createHash('sha1').update(applyUrl).digest('hex').slice(0, 16), + id: createHash('sha1').update(jobUrl).digest('hex').slice(0, 16), title: job.title, department: capitalize(department), location: (job.location ?? '').trim() || DEFAULT_LOCATION, - applyUrl + jobUrl } }