import { defaultCredentialName, readCredentialName, GENERIC_CREDENTIAL_NAME, MAX_CREDENTIAL_NAME_LENGTH } from '../../src/passkeys/credentialName'; /** * The management screen (#40) lists credentials and offers to revoke them, so a * default that says nothing produces a list of identical rows and a customer * removing a device at random. These are about that list being readable. */ describe('defaultCredentialName', () => { it('calls a cross-device authenticator a phone or tablet', () => { expect(defaultCredentialName(['hybrid'])).toBe('Phone or tablet'); }); it('prefers hybrid over internal when both are reported', () => { // A phone used as a cross-device passkey commonly reports both, and "Phone // or tablet" is the more useful reading — `internal` on its own means the // authenticator built into the machine in front of the customer. expect(defaultCredentialName(['internal', 'hybrid'])).toBe('Phone or tablet'); }); it('calls a platform authenticator this device', () => { expect(defaultCredentialName(['internal'])).toBe('This device'); }); it.each([['usb'], ['nfc'], ['ble']])('calls %s a security key', (transport) => { expect(defaultCredentialName([transport])).toBe('Security key'); }); it.each([[[]], [null], [undefined], [['something-new']]])( 'falls back to the generic name for %p', (transports) => { // Transports are a hint the authenticator supplies, so an unrecognised // one is expected rather than exceptional — the list grows. expect(defaultCredentialName(transports)).toBe(GENERIC_CREDENTIAL_NAME); } ); }); describe('readCredentialName', () => { it('takes a name the customer gave', () => { expect(readCredentialName('Work laptop')).toBe('Work laptop'); }); it('trims, because a padded name is not a different name', () => { expect(readCredentialName(' Work laptop ')).toBe('Work laptop'); }); it.each([[' '], [''], [null], [undefined], [42], [{}]])( 'returns null for %p so the caller falls back to a default', (value) => { // A name of spaces is a row the customer cannot read, and a non-string is // a client sending something unexpected. Both mean "no name given". expect(readCredentialName(value)).toBeNull(); } ); it('bounds the length, because this is rendered in a table cell', () => { const long = 'a'.repeat(MAX_CREDENTIAL_NAME_LENGTH + 50); expect(readCredentialName(long)).toHaveLength(MAX_CREDENTIAL_NAME_LENGTH); }); });