Automerge backed Markdown editor for the Atmosphere
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151// src/plugin/__tests__/events.test.tsimport { describe, it, expect, vi } from 'vitest'import { Events } from '../events'
describe('Events', () => { it('isolates async observers and ignores worker rejection only after its observer detaches', async () => { class HostEvents extends Events { publish() { this.triggerIsolated('resolve') } } const events = new HostEvents() let reject!: (reason: Error) => void const worker = new Promise<void>((_, fail) => { reject = fail }) const ref = events.on('resolve', () => worker) const other = vi.fn() events.on('resolve', other) const report = vi.spyOn(console, 'error').mockImplementation(() => {}) events.publish() events.offref(ref) reject(new Error('Terminated')) await Promise.resolve() expect(other).toHaveBeenCalledOnce() expect(report).not.toHaveBeenCalled() events.on('resolve', async () => { throw new Error('Actual live observer failure') }) events.publish() await Promise.resolve() expect(report).toHaveBeenCalledWith("Event 'resolve' observer rejected:", expect.objectContaining({ message: 'Actual live observer failure' })) }) describe('on/trigger', () => { it('calls registered callback when event is triggered', () => { const events = new Events() const callback = vi.fn() events.on('test', callback) events.trigger('test', 'arg1', 'arg2') expect(callback).toHaveBeenCalledWith('arg1', 'arg2') })
it('returns an EventRef that can be used to unsubscribe', () => { const events = new Events() const callback = vi.fn() const ref = events.on('test', callback) events.offref(ref) events.trigger('test') expect(callback).not.toHaveBeenCalled() })
it('supports multiple callbacks for the same event', () => { const events = new Events() const cb1 = vi.fn() const cb2 = vi.fn() events.on('test', cb1) events.on('test', cb2) events.trigger('test', 'data') expect(cb1).toHaveBeenCalledWith('data') expect(cb2).toHaveBeenCalledWith('data') }) })
describe('off', () => { it('removes callback by reference', () => { const events = new Events() const callback = vi.fn() events.on('test', callback) events.off('test', callback) events.trigger('test') expect(callback).not.toHaveBeenCalled() }) })
describe('tryTrigger', () => { it('triggers event via EventRef', () => { const events = new Events() const callback = vi.fn() const ref = events.on('test', callback) events.tryTrigger(ref, ['arg']) expect(callback).toHaveBeenCalledWith('arg') }) })
describe('context binding', () => { it('binds callback to context when provided', () => { const events = new Events() const context = { value: 42 } const callback = vi.fn(function(this: { value: number }) { return this.value }) events.on('test', callback, context) events.trigger('test') expect(callback).toHaveBeenCalled() expect(callback.mock.results[0].value).toBe(42) }) })
describe('subscribe/unsubscribe during dispatch', () => { it('completes dispatch safely when callback unsubscribes another callback', () => { const events = new Events() const results: string[] = [] let ref2: ReturnType<typeof events.on> events.on('test', () => { results.push('first') // Unsubscribe second callback during dispatch events.offref(ref2) }) ref2 = events.on('test', () => { results.push('second') }) events.on('test', () => { results.push('third') }) events.trigger('test') // All three callbacks were in the snapshot when trigger started // so all three should run (iterate over copy behavior) expect(results).toEqual(['first', 'second', 'third']) // On second trigger, second is unsubscribed results.length = 0 events.trigger('test') expect(results).toEqual(['first', 'third']) })
it('does not call callbacks added during dispatch in same dispatch', () => { const events = new Events() const results: string[] = [] events.on('test', () => { results.push('first') // Subscribe new callback during dispatch events.on('test', () => { results.push('dynamic') }) }) events.trigger('test') // Only 'first' runs - dynamic was added after snapshot expect(results).toEqual(['first']) // On next trigger, dynamic is included results.length = 0 events.trigger('test') expect(results).toContain('first') expect(results).toContain('dynamic') }) })})