PromptVault/tests/js/Components/RenderPreview.test.js

112 lines
4.2 KiB
JavaScript
Raw Normal View History

2026-10-26 20:12:16 +00:00
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');
});
});