UNPKG

@shopify/cli-kit

Version:

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

197 lines • 9.77 kB
import { SingleTask } from './SingleTask.js'; import { render } from '../../testing/ui.js'; import { TokenizedString } from '../../../../public/node/output.js'; import React from 'react'; import { describe, expect, test } from 'vitest'; describe('SingleTask', () => { test('unmounts when promise resolves successfully', async () => { // Given const title = new TokenizedString('Uploading files'); let resolvePromise; const task = () => new Promise((resolve) => { resolvePromise = resolve; }); // When const renderInstance = render(React.createElement(SingleTask, { title: title, task: task })); // Wait for initial render await new Promise((resolve) => setTimeout(resolve, 10)); // Resolve the promise resolvePromise('success'); // Wait for component to update and unmount await renderInstance.waitUntilExit(); // Then - component should unmount cleanly expect(renderInstance.lastFrame()).toBeDefined(); }); test('unmounts when promise rejects', async () => { // Given const title = new TokenizedString('Failed task'); let rejectPromise; const task = () => new Promise((resolve, reject) => { rejectPromise = reject; }); // When const renderInstance = render(React.createElement(SingleTask, { title: title, task: task })); // Wait for initial render await new Promise((resolve) => setTimeout(resolve, 10)); // Reject the promise and expect waitUntilExit to reject rejectPromise(new Error('Task failed')); // The component should exit with the error await expect(renderInstance.waitUntilExit()).rejects.toThrow('Task failed'); }); test('handles promise that resolves immediately', async () => { // Given const title = new TokenizedString('Instant task'); const task = () => Promise.resolve('immediate success'); // When const renderInstance = render(React.createElement(SingleTask, { title: title, task: task })); await renderInstance.waitUntilExit(); // Then - component should complete successfully expect(renderInstance.lastFrame()).toBeDefined(); }); test('handles promise that rejects immediately', async () => { // Given const title = new TokenizedString('Instant failure'); const task = () => Promise.reject(new Error('Immediate error')); // When const renderInstance = render(React.createElement(SingleTask, { title: title, task: task })); // Then - should exit with error await expect(renderInstance.waitUntilExit()).rejects.toThrow('Immediate error'); }); test('handles different types of promise return values', async () => { // Test with string let stringResult; const stringTask = () => Promise.resolve('task completed'); const stringRender = render(React.createElement(SingleTask, { title: new TokenizedString('String task'), task: stringTask, onComplete: (result) => (stringResult = result) })); await stringRender.waitUntilExit(); expect(stringRender.lastFrame()).toBeDefined(); expect(stringResult).toBe('task completed'); // Test with object let objectResult; const objectTask = () => Promise.resolve({ id: 1, name: 'test' }); const objectRender = render(React.createElement(SingleTask, { title: new TokenizedString('Object task'), task: objectTask, onComplete: (result) => (objectResult = result) })); await objectRender.waitUntilExit(); expect(objectRender.lastFrame()).toBeDefined(); // Test with number let numberResult; const numberTask = () => Promise.resolve(42); const numberRender = render(React.createElement(SingleTask, { title: new TokenizedString('Number task'), task: numberTask, onComplete: (result) => (numberResult = result) })); await numberRender.waitUntilExit(); expect(numberRender.lastFrame()).toBeDefined(); expect(numberResult).toBe(42); // Test with boolean let booleanResult; const booleanTask = () => Promise.resolve(true); const booleanRender = render(React.createElement(SingleTask, { title: new TokenizedString('Boolean task'), task: booleanTask, onComplete: (result) => (booleanResult = result) })); await booleanRender.waitUntilExit(); expect(booleanRender.lastFrame()).toBeDefined(); expect(booleanResult).toBe(true); }); test('handles promise with delayed resolution', async () => { // Given const title = new TokenizedString('Delayed task'); const task = () => new Promise((resolve) => { setTimeout(() => resolve('completed'), 100); }); // When const renderInstance = render(React.createElement(SingleTask, { title: title, task: task })); // Wait for completion await renderInstance.waitUntilExit(); // Then expect(renderInstance.lastFrame()).toBeDefined(); }); test('handles promise with delayed rejection', async () => { // Given const title = new TokenizedString('Delayed failure'); const task = () => new Promise((resolve, reject) => { setTimeout(() => reject(new Error('delayed error')), 100); }); // When const renderInstance = render(React.createElement(SingleTask, { title: title, task: task })); // Wait for completion - should throw error await expect(renderInstance.waitUntilExit()).rejects.toThrow('delayed error'); }); test('preserves error types and messages', async () => { // Test with custom error class CustomError extends Error { constructor(message, code) { super(message); this.code = code; this.name = 'CustomError'; } } const customError = new CustomError('Custom error message', 'CUSTOM_CODE'); const task = () => Promise.reject(customError); // When const renderInstance = render(React.createElement(SingleTask, { title: new TokenizedString('Custom error task'), task: task })); // Then - should preserve the exact error await expect(renderInstance.waitUntilExit()).rejects.toThrow('Custom error message'); }); test('handles concurrent promise operations', async () => { // Given - Multiple SingleTask components with different promises const fastPromise = () => new Promise((resolve) => setTimeout(() => resolve('fast'), 50)); const slowPromise = () => new Promise((resolve) => setTimeout(() => resolve('slow'), 150)); // When const fastRender = render(React.createElement(SingleTask, { title: new TokenizedString('Fast task'), task: fastPromise })); const slowRender = render(React.createElement(SingleTask, { title: new TokenizedString('Slow task'), task: slowPromise })); // Then - Both should complete successfully await fastRender.waitUntilExit(); await slowRender.waitUntilExit(); expect(fastRender.lastFrame()).toBeDefined(); expect(slowRender.lastFrame()).toBeDefined(); }); test('passes noColor prop to LoadingBar component', async () => { // Given const title = new TokenizedString('No color task'); const task = () => Promise.resolve(); // When - Test that noColor prop doesn't break the component const renderInstance = render(React.createElement(SingleTask, { title: title, task: task, noColor: true })); await renderInstance.waitUntilExit(); // Then - Component should complete successfully with noColor prop expect(renderInstance.lastFrame()).toBeDefined(); }); test('updates status message during task execution', async () => { // Given const initialTitle = new TokenizedString('Starting task'); let step1Resolve; let step2Resolve; let step3Resolve; const step1Promise = new Promise((resolve) => { step1Resolve = resolve; }); const step2Promise = new Promise((resolve) => { step2Resolve = resolve; }); const step3Promise = new Promise((resolve) => { step3Resolve = resolve; }); const task = async (updateStatus) => { updateStatus(new TokenizedString('Running (1 complete)...')); await step1Promise; updateStatus(new TokenizedString('Running (2 complete)...')); await step2Promise; updateStatus(new TokenizedString('Running (3 complete)...')); await step3Promise; return 'completed'; }; // When const renderInstance = render(React.createElement(SingleTask, { title: initialTitle, task: task })); // Wait for component to render with first status await new Promise((resolve) => setTimeout(resolve, 10)); const frame1 = renderInstance.lastFrame(); expect(frame1).toContain('1 complete'); // Progress to step 2 step1Resolve(); await new Promise((resolve) => setTimeout(resolve, 10)); const frame2 = renderInstance.lastFrame(); expect(frame2).toContain('2 complete'); // Progress to step 3 step2Resolve(); await new Promise((resolve) => setTimeout(resolve, 10)); const frame3 = renderInstance.lastFrame(); expect(frame3).toContain('3 complete'); // Complete the task step3Resolve(); await renderInstance.waitUntilExit(); }); }); //# sourceMappingURL=SingleTask.test.js.map