import { describe, it, expect, beforeAll, beforeEach, afterEach, vi } from 'vitest' import { clearFixture, getFixture } from '../helpers/fixture' import Clipboard from '../../src/clipboard' const MARKUP = `
` describe('Clipboard', () => { let fixtureEl: HTMLElement let written: string[] beforeAll(() => { fixtureEl = getFixture() }) beforeEach(() => { written = [] // The real clipboard needs a user gesture and a secure context. vi.stubGlobal('navigator', { clipboard: { writeText: (text: string) => { written.push(text) return Promise.resolve() }, }, }) fixtureEl.innerHTML = MARKUP }) afterEach(() => { vi.unstubAllGlobals() vi.useRealTimers() clearFixture() }) const trigger = (): HTMLElement => fixtureEl.querySelector('button')! const label = (): HTMLElement => fixtureEl.querySelector('.clipboard-label')! const hidden = (el: Element): boolean => el.hasAttribute('hidden') const feedback = (): HTMLElement => fixtureEl.querySelector('.clipboard-feedback')! describe('NAME', () => { it('should return plugin name', () => { expect(Clipboard.NAME).toBe('clipboard') }) }) describe('constructor', () => { it('should hide the feedback and mark it as a status', () => { new Clipboard(trigger()) expect(hidden(feedback())).toBe(true) expect(feedback().getAttribute('role')).toBe('status') expect(hidden(label())).toBe(false) }) }) describe('copy', () => { it('should copy the value of the target field and show the copied state', async () => { const spy = vi.fn() trigger().addEventListener('copied.bs.clipboard', spy) const instance = new Clipboard(trigger(), { delay: 0 }) await instance.copy() expect(written).toEqual(['sk_live_1234']) expect(trigger().classList.contains('copied')).toBe(true) expect(hidden(label())).toBe(true) expect(hidden(feedback())).toBe(false) expect(spy).toHaveBeenCalledTimes(1) }) it('should read the text of a target that is not a field', async () => { fixtureEl.innerHTML = '
npm install @tabler/core
' await new Clipboard(trigger()).copy() expect(written).toEqual(['npm install @tabler/core']) }) it('should prefer a literal text over the target', async () => { const instance = new Clipboard(trigger(), { text: 'literal' }) await instance.copy() expect(written).toEqual(['literal']) }) it('should copy on click', async () => { new Clipboard(trigger()) trigger().click() await vi.waitFor(() => expect(written).toEqual(['sk_live_1234'])) }) it('should go back to the label after the delay', async () => { const instance = new Clipboard(trigger(), { delay: 50 }) await instance.copy() expect(hidden(feedback())).toBe(false) await vi.waitFor(() => expect(hidden(feedback())).toBe(true)) expect(trigger().classList.contains('copied')).toBe(false) expect(hidden(label())).toBe(false) }) }) describe('failure', () => { it('should fire error.bs.clipboard when there is nothing to copy', async () => { fixtureEl.innerHTML = '
' const spy = vi.fn() trigger().addEventListener('error.bs.clipboard', spy) await new Clipboard(trigger()).copy() expect(written).toEqual([]) expect(spy).toHaveBeenCalledTimes(1) }) it('should fire error.bs.clipboard when the browser refuses', async () => { vi.stubGlobal('navigator', { clipboard: { writeText: () => Promise.reject(new Error('denied')) } }) const spy = vi.fn() trigger().addEventListener('error.bs.clipboard', spy) await new Clipboard(trigger()).copy() expect(spy).toHaveBeenCalledTimes(1) expect(trigger().classList.contains('copied')).toBe(false) }) }) describe('svg children', () => { it('should hide an icon with the attribute, not the DOM property', () => { fixtureEl.innerHTML = '
' new Clipboard(trigger()) const icons = fixtureEl.querySelectorAll('.clipboard-feedback') expect(icons.length).toBe(2) for (const icon of icons) { expect(icon.hasAttribute('hidden')).toBe(true) } }) }) describe('dispose', () => { it('should stop the timer and remove the instance', async () => { const instance = new Clipboard(trigger(), { delay: 50 }) await instance.copy() instance.dispose() expect(Clipboard.getInstance(trigger())).toBeNull() expect(hidden(feedback())).toBe(false) }) }) describe('text sources', () => { it('copies data-bs-text as written', async () => { fixtureEl.innerHTML = '' const instance = new Clipboard(trigger()) await instance.copy() expect(written).toEqual(['007']) }) it('copies data-bs-text that looks like JSON as written', async () => { fixtureEl.innerHTML = '' const instance = new Clipboard(trigger()) await instance.copy() expect(written).toEqual(['{"a":1}']) }) it('skips hidden text and keeps line breaks from an element target', async () => { fixtureEl.innerHTML = '
Line 1
Line 2 (copy)
' const instance = new Clipboard(trigger()) await instance.copy() expect(written).toEqual(['Line 1\nLine 2']) }) it('finds a target whose id needs escaping', async () => { fixtureEl.innerHTML = '' const instance = new Clipboard(trigger()) await instance.copy() expect(written).toEqual(['x']) }) }) describe('reset', () => { it('ends the copied state kept by delay 0', async () => { const instance = new Clipboard(trigger(), { delay: 0 }) await instance.copy() expect(hidden(feedback())).toBe(false) instance.reset() expect(hidden(feedback())).toBe(true) expect(hidden(label())).toBe(false) }) }) })