Files
accounted/components/settings/__tests__/members-payload.test.ts
T
admin e7f0070e84
masterplan-lock / check (push) Successful in 19s
ci / sonar (push) Failing after 21s
security / scan (push) Failing after 1m37s
fix(restore): återställ default branch-filer som raderades av docs-publiceringen (7e33543)
Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i
server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma
strängvärde byggs med konkatenering i stället för att matcha mönstret.
2026-11-10 09:37:22 +01:00

131 lines
4.6 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
parseCompanyMembersPayload,
parseTeamMembersPayload,
} from '@/components/settings/members-payload'
// Read-side guards for the member rosters. A 200 whose body does not carry
// the expected lists is a failed read, not an empty roster: the original bug
// was `setMembers(data.data.members)` straight off `res.json()`, so a
// wrong-shape 200 rendered an apparently member-less company, and a
// consultant looking at that list re-invites people who are already members.
describe('parseCompanyMembersPayload', () => {
const valid = {
data: {
members: [{ id: 'm1', email: 'anna@example.se' }],
invitations: [{ id: 'i1', email: 'bo@example.se' }],
canInvite: true,
},
}
it('passes a well-formed payload through', () => {
expect(parseCompanyMembersPayload(valid)).toEqual({
members: [{ id: 'm1', email: 'anna@example.se' }],
invitations: [{ id: 'i1', email: 'bo@example.se' }],
canInvite: true,
inviteRequiresUpgrade: false,
})
})
it('accepts a confirmed-empty roster (that is data, not a failure)', () => {
expect(
parseCompanyMembersPayload({ data: { members: [], invitations: [], canInvite: false } }),
).toEqual({ members: [], invitations: [], canInvite: false, inviteRequiresUpgrade: false })
})
it('rejects a 200 with no data envelope instead of fabricating an empty roster', () => {
expect(parseCompanyMembersPayload({})).toBeNull()
expect(parseCompanyMembersPayload(null)).toBeNull()
expect(parseCompanyMembersPayload('ok')).toBeNull()
})
it('rejects a 200 whose data lacks the lists', () => {
expect(parseCompanyMembersPayload({ data: {} })).toBeNull()
expect(parseCompanyMembersPayload({ data: { members: [] } })).toBeNull()
expect(
parseCompanyMembersPayload({ data: { members: 'oops', invitations: [] } }),
).toBeNull()
})
it('only grants canInvite on an explicit boolean true', () => {
// A truthy non-boolean must not unlock the invite form.
expect(
parseCompanyMembersPayload({ data: { members: [], invitations: [], canInvite: 'yes' } }),
).toEqual({ members: [], invitations: [], canInvite: false, inviteRequiresUpgrade: false })
})
it('only swaps the invite form for the upsell on an explicit boolean true', () => {
// Multi-user seat gate: an older server without the field (or junk) keeps
// the form; the invite POST's own 403 is the real enforcement.
expect(
parseCompanyMembersPayload({
data: { members: [], invitations: [], canInvite: true, inviteRequiresUpgrade: 'yes' },
}),
).toEqual({ members: [], invitations: [], canInvite: true, inviteRequiresUpgrade: false })
expect(
parseCompanyMembersPayload({
data: { members: [], invitations: [], canInvite: true, inviteRequiresUpgrade: true },
}),
).toEqual({ members: [], invitations: [], canInvite: true, inviteRequiresUpgrade: true })
})
})
describe('parseTeamMembersPayload', () => {
it('passes a well-formed payload through', () => {
expect(
parseTeamMembersPayload({
data: {
members: [{ id: 'm1' }],
invitations: [{ id: 'i1' }],
teamName: 'Byrån AB',
teamId: 'team-1',
teamKind: 'byra',
isOwner: true,
canInvite: true,
},
}),
).toEqual({
members: [{ id: 'm1' }],
invitations: [{ id: 'i1' }],
teamName: 'Byrån AB',
teamId: 'team-1',
teamKind: 'byra',
isOwner: true,
canInvite: true,
})
})
it('accepts a confirmed-empty roster and missing management fields', () => {
expect(parseTeamMembersPayload({ data: { members: [] } })).toEqual({
members: [],
invitations: [],
teamName: null,
teamId: null,
teamKind: null,
isOwner: false,
canInvite: false,
})
})
it('rejects wrong-shape 200s instead of fabricating an empty roster', () => {
expect(parseTeamMembersPayload({})).toBeNull()
expect(parseTeamMembersPayload(null)).toBeNull()
expect(parseTeamMembersPayload({ data: {} })).toBeNull()
expect(parseTeamMembersPayload({ data: { members: 'oops' } })).toBeNull()
})
it('drops a non-string team name instead of rendering it', () => {
expect(
parseTeamMembersPayload({ data: { members: [], teamName: 42 } }),
).toMatchObject({ members: [], teamName: null })
})
it('only grants management affordances on explicit boolean true', () => {
expect(
parseTeamMembersPayload({
data: { members: [], invitations: 'oops', isOwner: 'yes', canInvite: 1 },
}),
).toMatchObject({ invitations: [], isOwner: false, canInvite: false })
})
})