import { describe, it, expect, vi } from 'vitest' import { createRoot } from '../vdom.ts' import { on } from './on-mixin.ts' import { invariant } from '../invariant.ts' import type { Assert, Equal } from '../../test/utils.ts' import type { Dispatched } from './on-mixin.ts' describe('on mixin', () => { it('updates listeners in place without rebinding when capture is unchanged', () => { let calls: string[] = [] let container = document.createElement('div') let root = createRoot(container) root.render( , ) root.flush() let button = container.querySelector('button') invariant(button) let addSpy = vi.spyOn(button, 'addEventListener') let removeSpy = vi.spyOn(button, 'removeEventListener') root.render( , ) root.flush() button.click() root.flush() expect(calls).toEqual(['second']) expect(addSpy).toHaveBeenCalledTimes(0) expect(removeSpy).toHaveBeenCalledTimes(0) }) it('rebinds when capture option changes', () => { let container = document.createElement('div') let root = createRoot(container) root.render() root.flush() let button = container.querySelector('button') invariant(button) let addSpy = vi.spyOn(button, 'addEventListener') let removeSpy = vi.spyOn(button, 'removeEventListener') root.render() root.flush() expect(addSpy).toHaveBeenCalledTimes(1) expect(removeSpy).toHaveBeenCalledTimes(1) }) it('passes abort signal as the second handler argument', () => { let receivedSignal = AbortSignal.abort() let container = document.createElement('div') let root = createRoot(container) root.render( , ) root.flush() let button = container.querySelector('button') invariant(button) button.click() root.flush() expect(receivedSignal).toBeInstanceOf(AbortSignal) expect(receivedSignal.aborted).toBe(false) }) it('supports multiple event types on the same element', () => { let calls: string[] = [] let container = document.createElement('div') let root = createRoot(container) root.render( , ) root.flush() let button = container.querySelector('button') invariant(button) button.dispatchEvent(new FocusEvent('focus', { bubbles: true })) button.click() root.flush() expect(calls).toEqual(['focus', 'click']) }) it('removes listeners when on() mixin is removed', () => { let calls = 0 let container = document.createElement('div') let root = createRoot(container) root.render( , ) root.flush() let button = container.querySelector('button') invariant(button) button.click() root.flush() expect(calls).toBe(1) root.render() root.flush() button.click() root.flush() expect(calls).toBe(1) }) it('aborts previous handler signal on reentry', async () => { let signals: AbortSignal[] = [] let pendingResolvers: Array<() => void> = [] let container = document.createElement('div') let root = createRoot(container) root.render( , ) root.flush() let button = container.querySelector('button') invariant(button) button.click() button.click() root.flush() expect(signals).toHaveLength(2) expect(signals[0]!.aborted).toBe(true) expect(signals[1]!.aborted).toBe(false) for (let resolve of pendingResolvers) resolve() await Promise.resolve() }) }) const _infersNodeType = (