import { afterEach, describe, expect, test } from 'bun:test'; import { copyMarkdownToClipboard } from './clipboard'; const originalNavigator = Object.getOwnPropertyDescriptor(globalThis, 'navigator'); const originalClipboardItem = Object.getOwnPropertyDescriptor(globalThis, 'ClipboardItem'); const restoreGlobal = (name: 'navigator' | 'ClipboardItem', descriptor?: PropertyDescriptor): void => { if (descriptor) { Object.defineProperty(globalThis, name, descriptor); } else { Reflect.deleteProperty(globalThis, name); } }; afterEach(() => { restoreGlobal('navigator', originalNavigator); restoreGlobal('ClipboardItem', originalClipboardItem); }); describe('copyMarkdownToClipboard', () => { test('offers rich HTML and Markdown source in one clipboard item', async () => { let writtenItem: { data: Record } | undefined; class FakeClipboardItem { static supports(type: string): boolean { return type === 'text/markdown'; } readonly data: Record; constructor(data: Record) { this.data = data; } } Object.defineProperty(globalThis, 'ClipboardItem', { configurable: true, value: FakeClipboardItem }); Object.defineProperty(globalThis, 'navigator', { configurable: true, value: { clipboard: { write: async (items: Array<{ data: Record }>) => { writtenItem = items[0]; }, }, }, }); const result = await copyMarkdownToClipboard('**bold**', '

bold

'); expect(result).toEqual({ ok: true, method: 'clipboard' }); expect(Object.keys(writtenItem?.data ?? {}).sort()).toEqual(['text/html', 'text/markdown', 'text/plain']); expect(await writtenItem?.data['text/plain']?.text()).toBe('**bold**'); expect(await writtenItem?.data['text/markdown']?.text()).toBe('**bold**'); expect(await writtenItem?.data['text/html']?.text()).toBe('

bold

'); }); test('falls back to plain Markdown when a rich clipboard write fails', async () => { let fallbackText = ''; class FakeClipboardItem { static supports(): boolean { return false; } constructor() {} } Object.defineProperty(globalThis, 'ClipboardItem', { configurable: true, value: FakeClipboardItem }); Object.defineProperty(globalThis, 'navigator', { configurable: true, value: { clipboard: { write: async () => { throw new Error('unsupported'); }, writeText: async (text: string) => { fallbackText = text; }, }, }, }); const result = await copyMarkdownToClipboard('# title', '

title

'); expect(result).toEqual({ ok: true, method: 'clipboard' }); expect(fallbackText).toBe('# title'); }); });