jobhunt-platform/apps/web/src/views/Applications.badges.test.ts

112 lines
No EOL
3.8 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import Applications from '@/views/Applications.vue'
import type { Application } from '@/types'
vi.mock('@/api', () => ({
getApplications: vi.fn(),
transitionApplication: vi.fn(),
batchScore: vi.fn(),
getToday: vi.fn(),
HttpError: class HttpError extends Error {
status: number
body: unknown
constructor(status: number, body: unknown, msg?: string) {
super(msg ?? `HTTP ${status}`)
this.status = status
this.body = body
}
}
}))
function makeApp(id: string, state: string, company: string): Application {
return {
id,
job_posting_id: 'j-' + id,
state: state as Application['state'],
score: 80,
score_rationale: null,
notes: '',
state_changed_at: '2026-01-01T00:00:00Z',
created_at: '2026-01-01T00:00:00Z',
posting: {
id: 'j-' + id, source: 'manual_url', external_id: null, url: 'http://x',
company, title: 'Engineer', location: 'Remote', description: '',
raw: {}, fetched_at: '2026-01-01T00:00:00Z'
}
}
}
describe('Applications kanban badges', () => {
it('renders nudge dot on cards that have a follow-up nudge', async () => {
setActivePinia(createPinia())
const api = await import('@/api')
const apps = [makeApp('app-1', 'sent', 'NudgeCorp'), makeApp('app-2', 'discovered', 'NoNudge')]
;(api.getApplications as ReturnType<typeof vi.fn>).mockResolvedValue(apps)
;(api.batchScore as ReturnType<typeof vi.fn>).mockResolvedValue({ results: [] })
;(api.getToday as ReturnType<typeof vi.fn>).mockResolvedValue({
digest: [],
nudges: [
{ application_id: 'app-1', days_since_sent: 9, suggestion: 'Send a follow-up email' }
],
pending_approvals: 0
})
const wrapper = mount(Applications)
await vi.waitFor(() => {
expect(wrapper.text()).toContain('NudgeCorp')
})
// The nudge dot should be rendered as an orange dot (span with bg-orange-500)
const dots = wrapper.findAll('.bg-orange-500')
expect(dots.length).toBeGreaterThanOrEqual(1)
// Verify the dot is in the card for NudgeCorp (the sent column)
const sentCol = wrapper.findAll('.font-semibold').find((el) => el.text() === 'sent')
expect(sentCol).toBeTruthy()
const sentColumn = sentCol!.element.parentElement!
expect(sentColumn.textContent).toContain('NudgeCorp')
// The dot should be inside this column
expect(sentColumn.querySelector('.bg-orange-500')).toBeTruthy()
})
it('renders red flag badge with tooltip text from batch scoring results', async () => {
setActivePinia(createPinia())
const api = await import('@/api')
const apps = [makeApp('app-1', 'discovered', 'ScamCorp')]
;(api.getApplications as ReturnType<typeof vi.fn>).mockResolvedValue(apps)
;(api.batchScore as ReturnType<typeof vi.fn>).mockResolvedValue({
results: [
{
application_id: 'app-1',
score: 30,
rationale: {},
red_flags: ['Unpaid trial period mentioned', 'Asks for bank details upfront']
}
]
})
;(api.getToday as ReturnType<typeof vi.fn>).mockResolvedValue({
digest: [],
nudges: [],
pending_approvals: 0
})
const wrapper = mount(Applications)
await vi.waitFor(() => {
expect(wrapper.text()).toContain('ScamCorp')
})
// The warning symbol should be rendered in the card
const warningEl = wrapper.find('.text-red-600.font-bold')
expect(warningEl.exists()).toBe(true)
// The title attribute should contain the red flag text
const title = warningEl.attributes('title')
expect(title).toBeTruthy()
expect(title).toContain('Unpaid trial period mentioned')
expect(title).toContain('Asks for bank details upfront')
})
})