import { afterEach, describe, expect, it, vi } from 'vitest'; import { flushPromises, mount } from '@vue/test-utils'; import RenderPreview from '../../../resources/js/Components/RenderPreview.vue'; const BODY = 'Hi {{ name }}, order {{ order_id }} shipped.'; const VARIABLES = ['name', 'order_id']; /** Replace `navigator.clipboard` for the duration of a test. */ const installClipboard = (fail = false) => { const writeText = vi.fn( fail ? () => Promise.reject(new Error('Permission denied')) : () => Promise.resolve() ); vi.stubGlobal('navigator', { clipboard: { writeText } }); return writeText; }; const buttonWithText = (wrapper, text) => wrapper.findAll('button').find((button) => button.text() === text); describe('RenderPreview', () => { afterEach(() => { vi.unstubAllGlobals(); vi.useRealTimers(); }); it('shows a hint and no inputs when the prompt has no variables', () => { const wrapper = mount(RenderPreview, { props: { body: 'Static text only.', variables: [] }, }); expect(wrapper.text()).toContain('This prompt has no {{variables}}'); expect(wrapper.find('textarea').exists()).toBe(false); expect(wrapper.find('pre').text()).toBe('Static text only.'); }); it('renders one labelled input per variable', () => { const wrapper = mount(RenderPreview, { props: { body: BODY, variables: VARIABLES } }); const inputs = wrapper.findAll('textarea'); expect(inputs).toHaveLength(2); expect(inputs[0].attributes('id')).toBe('var-name'); expect(inputs[1].attributes('id')).toBe('var-order_id'); expect(wrapper.text()).toContain('{{name}}'); expect(wrapper.text()).toContain('{{order_id}}'); }); it('keeps placeholders until values are entered, then substitutes live', async () => { const wrapper = mount(RenderPreview, { props: { body: BODY, variables: VARIABLES } }); expect(wrapper.find('pre').text()).toBe(BODY); await wrapper.findAll('textarea')[0].setValue('Ada'); expect(wrapper.find('pre').text()).toBe('Hi Ada, order {{ order_id }} shipped.'); await wrapper.findAll('textarea')[1].setValue('42'); expect(wrapper.find('pre').text()).toBe('Hi Ada, order 42 shipped.'); }); it('emits "use" with the current values', async () => { const wrapper = mount(RenderPreview, { props: { body: BODY, variables: VARIABLES, useLabel: 'Use prompt (counts usage)' }, }); await wrapper.findAll('textarea')[0].setValue('Ada'); await buttonWithText(wrapper, 'Use prompt (counts usage)').trigger('click'); expect(wrapper.emitted('use')).toHaveLength(1); expect(wrapper.emitted('use')[0]).toEqual([{ name: 'Ada', order_id: '' }]); }); it('disables the use button while busy', () => { const wrapper = mount(RenderPreview, { props: { body: BODY, variables: VARIABLES, busy: true }, }); expect(buttonWithText(wrapper, 'Use prompt').attributes('disabled')).toBeDefined(); }); it('copies the live preview and shows a transient confirmation', async () => { vi.useFakeTimers(); const writeText = installClipboard(); const wrapper = mount(RenderPreview, { props: { body: BODY, variables: VARIABLES } }); await wrapper.findAll('textarea')[0].setValue('Ada'); const copyButton = buttonWithText(wrapper, 'Copy preview'); await copyButton.trigger('click'); await vi.advanceTimersByTimeAsync(0); expect(writeText).toHaveBeenCalledWith('Hi Ada, order {{ order_id }} shipped.'); expect(copyButton.text()).toBe('✓ Copied'); await vi.advanceTimersByTimeAsync(1500); expect(copyButton.text()).toBe('Copy preview'); }); it('ignores clipboard failures', async () => { installClipboard(true); const wrapper = mount(RenderPreview, { props: { body: BODY, variables: VARIABLES } }); await wrapper.findAll('textarea')[0].setValue('Ada'); const copyButton = buttonWithText(wrapper, 'Copy preview'); await copyButton.trigger('click'); await flushPromises(); expect(copyButton.text()).toBe('Copy preview'); }); });