UNPKG

@shopify/cli-kit

Version:

A set of utilities, interfaces, and models that are common across all the platform features

166 lines 10.4 kB
import { TextInput } from './TextInput.js'; import { sendInputAndWait, sendInputAndWaitForChange, waitForChange, waitForInputsToBeReady, render, } from '../../testing/ui.js'; import React, { useState } from 'react'; import { describe, test, expect, vi } from 'vitest'; const ARROW_LEFT = '\u001B[D'; const ARROW_RIGHT = '\u001B[C'; const DELETE = '\u007F'; const TAB = '\u0009'; describe('TextInput', () => { test('default state', () => { const { lastFrame } = render(React.createElement(TextInput, { value: "", onChange: () => { } })); // inverted space escape sequence expect(lastFrame()).toMatchInlineSnapshot('"█"'); }); test('displays value with cursor', () => { const { lastFrame } = render(React.createElement(TextInput, { value: "Hello", onChange: () => { } })); // inverted space escape sequence after Hello expect(lastFrame()).toMatchInlineSnapshot('"Hello█"'); }); test('displays placeholder', () => { const { lastFrame } = render(React.createElement(TextInput, { value: "", placeholder: "Placeholder", onChange: () => { } })); // inverted escape sequence around "P", laceholder after that expect(lastFrame()).toMatchInlineSnapshot('"Placeholder"'); }); test('moves the cursor with arrows', async () => { const renderInstance = render(React.createElement(TextInput, { value: "Hello", onChange: () => { } })); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello█"'); await waitForInputsToBeReady(); await sendInputAndWaitForChange(renderInstance, ARROW_LEFT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"'); await sendInputAndWaitForChange(renderInstance, ARROW_LEFT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"'); await sendInputAndWaitForChange(renderInstance, ARROW_LEFT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"'); await sendInputAndWaitForChange(renderInstance, ARROW_LEFT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"'); await sendInputAndWaitForChange(renderInstance, ARROW_LEFT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"'); // cursor can't go before the first character await sendInputAndWait(renderInstance, 100, ARROW_LEFT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"'); await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"'); await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"'); await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"'); await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello"'); await sendInputAndWaitForChange(renderInstance, ARROW_RIGHT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello█"'); // cursor can't go after the last character await sendInputAndWait(renderInstance, 100, ARROW_RIGHT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello█"'); }); test('in noColor mode replaces the current character with █', async () => { const renderInstance = render(React.createElement(TextInput, { noColor: true, value: "Hello", onChange: () => { } })); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello█"'); await waitForInputsToBeReady(); await sendInputAndWaitForChange(renderInstance, ARROW_LEFT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hell█"'); await sendInputAndWaitForChange(renderInstance, ARROW_LEFT); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hel█o"'); }); test('moves the cursor when deleting', async () => { const StatefulTextInput = () => { const [value, setValue] = useState('Hello'); return React.createElement(TextInput, { value: value, onChange: setValue }); }; const renderInstance = render(React.createElement(StatefulTextInput, null)); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello█"'); await waitForInputsToBeReady(); await sendInputAndWaitForChange(renderInstance, DELETE); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hell█"'); await sendInputAndWaitForChange(renderInstance, DELETE); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hel█"'); await sendInputAndWaitForChange(renderInstance, DELETE); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"He█"'); await sendInputAndWaitForChange(renderInstance, DELETE); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"H█"'); await sendInputAndWaitForChange(renderInstance, DELETE); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"█"'); // cannot delete after the value has been cleared await sendInputAndWait(renderInstance, 100, DELETE); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"█"'); }); test('accepts input', async () => { const StatefulTextInput = () => { const [value, setValue] = useState(''); return React.createElement(TextInput, { value: value, onChange: setValue }); }; const renderInstance = render(React.createElement(StatefulTextInput, null)); await waitForInputsToBeReady(); await sendInputAndWaitForChange(renderInstance, 'H'); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"H█"'); await sendInputAndWaitForChange(renderInstance, 'ello'); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello█"'); }); test('onChange', async () => { const onChange = vi.fn(); const StatefulTextInput = () => { const [value, setValue] = useState(''); return (React.createElement(TextInput, { value: value, onChange: (value) => { setValue(value); onChange(value); } })); }; const renderInstance = render(React.createElement(StatefulTextInput, null)); await waitForInputsToBeReady(); await sendInputAndWaitForChange(renderInstance, 'X'); expect(onChange).toHaveBeenCalledWith('X'); }); test('deletes at the beginning and in the middle of text', async () => { const StatefulTextInput = () => { const [value, setValue] = useState(''); return React.createElement(TextInput, { value: value, onChange: setValue }); }; const renderInstance = render(React.createElement(StatefulTextInput, null)); await waitForInputsToBeReady(); await sendInputAndWaitForChange(renderInstance, 'T'); await sendInputAndWaitForChange(renderInstance, 'e'); await sendInputAndWaitForChange(renderInstance, 's'); await sendInputAndWaitForChange(renderInstance, 't'); await sendInputAndWaitForChange(renderInstance, ARROW_LEFT); await sendInputAndWaitForChange(renderInstance, DELETE); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Tet"'); await sendInputAndWaitForChange(renderInstance, ARROW_LEFT); await sendInputAndWaitForChange(renderInstance, ARROW_LEFT); await sendInputAndWait(renderInstance, 100, DELETE); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Tet"'); }); test('adjusts cursor when text is shorter than last value', async () => { let resetValue = () => { }; const StatefulTextInput = () => { const [value, setValue] = useState(''); resetValue = () => setValue(''); return React.createElement(TextInput, { value: value, onChange: setValue }); }; const renderInstance = render(React.createElement(StatefulTextInput, null)); await waitForInputsToBeReady(); await sendInputAndWaitForChange(renderInstance, 'A'); await sendInputAndWaitForChange(renderInstance, 'B'); await waitForChange(resetValue, renderInstance.lastFrame); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"█"'); await sendInputAndWaitForChange(renderInstance, 'A'); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"A█"'); await sendInputAndWaitForChange(renderInstance, 'B'); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"AB█"'); }); test("masking the input if it's a password", async () => { const renderInstance = render(React.createElement(TextInput, { onChange: () => { }, value: "ABC", password: true })); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"***█"'); }); test('tab completes with placeholder content', async () => { const StatefulTextInput = () => { const [value, setValue] = useState(''); const placeholder = 'Hello'; return React.createElement(TextInput, { value: value, onChange: setValue, placeholder: placeholder }); }; const renderInstance = render(React.createElement(StatefulTextInput, null)); await waitForInputsToBeReady(); await sendInputAndWaitForChange(renderInstance, TAB); expect(renderInstance.lastFrame()).toMatchInlineSnapshot('"Hello█"'); }); }); //# sourceMappingURL=TextInput.test.js.map