Files
shopdb-flask/frontend/src/composables/employeeResolve.spec.js

82 lines
3.2 KiB
JavaScript

// Typing a name in full must reach the same employee as picking it off the list.
//
// Both pickers turned a typed name straight into `NAME:<text>` - a free-text
// label linked to nothing. On screen the chip is identical to a real
// selection, so the difference only shows up later: no photo, no directory
// record, and a recognition that cannot be found by the person it is about.
import { describe, it, expect, vi, beforeEach } from 'vitest'
vi.mock('../api', () => ({
employeesApi: { search: vi.fn() }
}))
import { employeesApi } from '../api'
import { resolveEmployeeName } from './employeeResolve'
const DANA = { SSO: 270015376, First_Name: 'Dana', Last_Name: 'Reyes' }
const DANA_HYPHEN = { SSO: 270099999, First_Name: 'Dana', Last_Name: 'Reyes-Smith' }
const SAM = { SSO: 270000001, First_Name: 'Sam', Last_Name: 'Reyes' }
function found(rows) {
employeesApi.search.mockResolvedValue({ data: { data: rows } })
}
describe('resolveEmployeeName', () => {
beforeEach(() => employeesApi.search.mockReset())
it('resolves a full name to the employee', async () => {
found([DANA])
const { match } = await resolveEmployeeName('Dana Reyes')
expect(match.SSO).toBe(270015376)
})
it('prefers an exact full-name match over a longer partial one', async () => {
// Searching "Dana Reyes" also returns "Dana Reyes-Smith"; the person who
// typed the shorter name meant the shorter name.
found([DANA, DANA_HYPHEN])
const { match, matches } = await resolveEmployeeName('Dana Reyes')
expect(matches).toBeUndefined()
expect(match.SSO).toBe(270015376)
})
it('refuses to guess between genuinely ambiguous matches', async () => {
found([DANA, SAM])
const { match, matches } = await resolveEmployeeName('Reyes')
expect(match).toBeUndefined()
expect(matches).toHaveLength(2)
})
it('reports nothing found, so the caller may fall back to a label', async () => {
found([])
const result = await resolveEmployeeName('Somebody External')
expect(result.match).toBeUndefined()
expect(result.matches).toBeUndefined()
expect(result.failed).toBeUndefined()
})
it('says the lookup FAILED rather than reporting nobody', async () => {
// The difference matters: "not in the directory" may become a custom
// label, "could not ask" must not.
// A THENABLE that calls its reject callback, rather than a mock returning
// a rejected promise. The await sees the same failure, but no rejected
// promise object exists for the runner to collect as an unhandled error -
// which it does even when the code under test catches it, failing the very
// test that proves the catch works.
employeesApi.search.mockImplementationOnce(() => {
const failure = new Error('network down')
return { then: (_ok, fail) => fail(failure) }
})
// resolves.toEqual, so the runner sees the rejection consumed by an
// assertion rather than as an unhandled error of its own.
await expect(resolveEmployeeName('Dana Reyes')).resolves.toEqual({
failed: true
})
})
it('does not call the directory for an empty box', async () => {
const result = await resolveEmployeeName(' ')
expect(employeesApi.search).not.toHaveBeenCalled()
expect(result).toEqual({})
})
})