// @vitest-environment happy-dom import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import MembershipRosterCard from '../../src/components/settings/MembershipRosterCard.vue' import { useMembershipSyncStore } from '../../src/stores/membershipSync.js' import { mountWithStore } from '../support/mountComponent.js' // #387 C3. The one thing this card must never do is render "never synced" as a // number. Three situations produce zero memberships — the account subscribes to // nothing, the sweep never ran, the sweep failed — and only the first means // "you are tracking sources you do not pay for". Conflating them is how C4 ends // up telling the operator to cancel things they are actively paying for. const mountWith = (platforms) => mountWithStore(MembershipRosterCard, () => { useMembershipSyncStore().platforms = platforms }) describe('MembershipRosterCard', () => { beforeEach(() => { // The card loads on mount; stub it so assertions are about seeded state. globalThis.fetch = vi.fn(async () => { throw new Error('offline') }) }) afterEach(() => vi.restoreAllMocks()) it('says "never synced" in words, and states no count', () => { const w = mountWith([ { platform: 'patreon', last_success_at: null, last_attempt_at: null, last_count: null, fresh: false }, ]) expect(w.text()).toContain('never synced') // The count line must be absent entirely — not "0 memberships found". expect(w.text()).not.toContain('memberships found') }) it('does not claim a count when a sync has been tried but never succeeded', () => { const w = mountWith([ { platform: 'patreon', last_success_at: null, last_attempt_at: new Date(Date.now() - 3600e3).toISOString(), last_count: null, fresh: false, last_error_type: 'PatreonAuthError', last_error_message: 'cookies expired' }, ]) expect(w.text()).toContain('no successful sync yet') expect(w.text()).not.toContain('memberships found') // The error is surfaced, because "rotate your credential" is actionable. expect(w.text()).toContain('PatreonAuthError') }) it('marks a stale roster as too old to rely on', () => { const w = mountWith([ { platform: 'patreon', last_success_at: new Date(Date.now() - 9 * 86400e3).toISOString(), last_count: 12, fresh: false }, ]) expect(w.text()).toContain('too old to rely on') }) it('states the count only when a successful sync stands behind it', () => { const w = mountWith([ { platform: 'patreon', last_success_at: new Date(Date.now() - 3600e3).toISOString(), last_count: 12, fresh: true }, ]) expect(w.text()).toContain('12 memberships found') expect(w.text()).not.toContain('never synced') expect(w.text()).not.toContain('too old') }) it('a real zero is reported as zero, because that one IS an answer', () => { const w = mountWith([ { platform: 'patreon', last_success_at: new Date(Date.now() - 3600e3).toISOString(), last_count: 0, fresh: true }, ]) expect(w.text()).toContain('0 memberships found') expect(w.text()).not.toContain('never synced') }) })