Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117// @vitest-environment jsdomimport { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'import { computed, effectScope, nextTick, onScopeDispose, reactive, ref, shallowRef, watch } from 'vue'import { atprotoService } from '../src/services/atproto'
vi.hoisted(() => { vi.stubGlobal('createAppComposable', (factory: () => unknown) => factory) })vi.mock('../src/services/atproto', () => ({ atprotoService: { resumeSession: vi.fn(), listCalendarSpaces: vi.fn(), listSpaceMembers: vi.fn(), listSpaceEvents: vi.fn(), getCurrentDid: vi.fn(() => 'me'), logout: vi.fn(),} }))import { useCalendat } from '../app/composables/useCalendat'const spaces = ['A', 'B'].map(name => ({ uri: name, name, color: '#0284c7', isOwner: true, skey: name, authorityDid: 'me', type: 'calendar' }))const event = (spaceUri: string, name = spaceUri) => ({ uri: `${spaceUri}/event`, repo: 'me', spaceUri, cid: 'cid', rkey: 'event', value: { $type: 'event', name, startsAt: '2026-09-11T10:00:00Z', createdAt: '2026-09-01T00:00:00Z' } })let scope: ReturnType<typeof effectScope>function create() { scope = effectScope(); return scope.run(() => useCalendat())! }beforeEach(() => { vi.clearAllMocks() localStorage.clear() vi.mocked(atprotoService.getCurrentDid).mockReturnValue('me') Object.entries({ computed, ref, shallowRef, reactive, watch, onScopeDispose }).forEach(([name, value]) => vi.stubGlobal(name, value)) vi.stubGlobal('useToast', () => ({ add: vi.fn() })) vi.mocked(atprotoService.resumeSession).mockResolvedValue({ did: 'me' }) vi.mocked(atprotoService.listCalendarSpaces).mockResolvedValue(spaces) vi.mocked(atprotoService.listSpaceMembers).mockResolvedValue([{ did: 'me' }]) vi.mocked(atprotoService.listSpaceEvents).mockImplementation(async uri => [event(uri)])})afterEach(() => { scope?.stop() })const settle = async () => { await Promise.resolve(); await Promise.resolve(); await nextTick() }
describe('Nuxt calendar state', () => { it('changes the new-event calendar without closing the editor or moving an existing event', async () => { const state = create() await state.initialize(); await settle() const date = new Date('2026-09-11T09:00:00Z') state.openNewEvent(date) state.selectEventCalendar('B') expect(state.activeSpace.value?.uri).toBe('B') expect(state.eventOpen.value).toBe(true) expect(state.initialDate.value).toEqual(date) state.selectEventCalendar('missing') expect(state.activeSpace.value?.uri).toBe('B') state.openEvent(event('A')) state.selectEventCalendar('A') expect(state.activeSpace.value?.uri).toBe('B') expect(state.selectedEvent.value?.spaceUri).toBe('A') }) it('restores calendar visibility across reloads, including all calendars hidden', async () => { const first = create() await first.initialize(); await settle() first.toggleVisibleSpace('B') first.toggleVisibleSpace('A') scope.stop() const restored = create() await restored.initialize(); await settle() expect(restored.visibleSpaceUris.value).toEqual(['B']) restored.toggleVisibleSpace('B') restored.logout() scope.stop() const hidden = create() await hidden.initialize(); await settle() expect(hidden.visibleSpaceUris.value).toEqual([]) expect(hidden.events.value).toHaveLength(2) }) it('isolates preferences by account and removes unavailable calendars', async () => { localStorage.setItem('calendat-visible-calendars:me', JSON.stringify(['B', 'removed'])) const state = create() await state.initialize(); await settle() expect(state.visibleSpaceUris.value).toEqual(['B']) state.logout(); scope.stop() vi.mocked(atprotoService.getCurrentDid).mockReturnValue('other') const other = create() await other.initialize(); await settle() expect(other.visibleSpaceUris.value).toEqual(['A']) expect(JSON.parse(localStorage.getItem('calendat-visible-calendars:me')!)).toEqual(['B']) }) it('preloads hidden calendars and refreshes only the toggled calendar', async () => { const state = create() await state.initialize(); await settle() expect(state.events.value.map(e => e.spaceUri).sort()).toEqual(['A', 'B']) expect(state.visibleSpaceUris.value).toEqual(['A']) vi.mocked(atprotoService.listSpaceEvents).mockClear() state.toggleVisibleSpace('B'); await settle() expect(state.visibleSpaceUris.value).toEqual(['A', 'B']) expect(atprotoService.listSpaceEvents).toHaveBeenCalledTimes(1) expect(atprotoService.listSpaceEvents).toHaveBeenCalledWith('B', ['me']) }) it('ignores stale refreshes and clears cached private data on logout', async () => { const state = create() await state.initialize(); await settle() let resolve!: (events: ReturnType<typeof event>[]) => void vi.mocked(atprotoService.listSpaceEvents).mockImplementationOnce(() => new Promise(done => { resolve = done })) const pending = state.refresh('A'); await settle() state.logout() resolve([event('A', 'Late')]); await pending expect(state.events.value).toEqual([]) expect(state.membersBySpace.value).toEqual({}) expect(state.authenticated.value).toBe(false) }) it('keeps cached events when a refresh fails and offers an error', async () => { const state = create() await state.initialize(); await settle() vi.mocked(atprotoService.listSpaceEvents).mockRejectedValueOnce(new Error('offline')) expect(await state.refresh('A')).toBe(false) expect(state.events.value.some(e => e.spaceUri === 'A')).toBe(true) expect(state.contentError.value).toMatch(/Could not load/) expect(state.loadingEvents.value).toBe(false) }) it('enforces author ownership and archive restrictions', async () => { const state = create() await state.initialize(); await settle() expect(state.canEditEvent(event('A'))).toBe(true) expect(state.canEditEvent({ ...event('A'), repo: 'someone-else' })).toBe(false) state.updateSpace({ ...spaces[0]!, archived: true }) expect(state.canEditEvent(event('A'))).toBe(false) })})