@shopify/cli-kit 3.84.1 → 3.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/private/node/analytics/bounded-collections.js.map +1 -0
- package/dist/{public/node/themes → private/node}/analytics/error-categorizer.d.ts +7 -1
- package/dist/private/node/analytics/error-categorizer.js +106 -0
- package/dist/private/node/analytics/error-categorizer.js.map +1 -0
- package/dist/{public/node/themes → private/node}/analytics/storage.js +8 -3
- package/dist/private/node/analytics/storage.js.map +1 -0
- package/dist/private/node/api/graphql/business-platform-destinations/user-email.d.ts +6 -0
- package/dist/private/node/api/graphql/business-platform-destinations/user-email.js +8 -0
- package/dist/private/node/api/graphql/business-platform-destinations/user-email.js.map +1 -0
- package/dist/private/node/api/headers.d.ts +3 -6
- package/dist/private/node/api/headers.js +8 -24
- package/dist/private/node/api/headers.js.map +1 -1
- package/dist/private/node/conf-store.d.ts +20 -3
- package/dist/private/node/conf-store.js +32 -7
- package/dist/private/node/conf-store.js.map +1 -1
- package/dist/private/node/constants.d.ts +0 -1
- package/dist/private/node/constants.js +0 -1
- package/dist/private/node/constants.js.map +1 -1
- package/dist/private/node/context/service.d.ts +8 -2
- package/dist/private/node/context/service.js +9 -5
- package/dist/private/node/context/service.js.map +1 -1
- package/dist/private/node/otel-metrics.js +2 -3
- package/dist/private/node/otel-metrics.js.map +1 -1
- package/dist/private/node/session/schema.d.ts +796 -41
- package/dist/private/node/session/schema.js +24 -25
- package/dist/private/node/session/schema.js.map +1 -1
- package/dist/private/node/session/store.d.ts +21 -11
- package/dist/private/node/session/store.js +52 -18
- package/dist/private/node/session/store.js.map +1 -1
- package/dist/private/node/session/validate.d.ts +2 -7
- package/dist/private/node/session/validate.js.map +1 -1
- package/dist/private/node/session.d.ts +8 -6
- package/dist/private/node/session.js +99 -71
- package/dist/private/node/session.js.map +1 -1
- package/dist/private/node/ui/components/LoadingBar.d.ts +8 -0
- package/dist/private/node/ui/components/LoadingBar.js +21 -0
- package/dist/private/node/ui/components/LoadingBar.js.map +1 -0
- package/dist/private/node/ui/components/LoadingBar.test.d.ts +1 -0
- package/dist/private/node/ui/components/LoadingBar.test.js +182 -0
- package/dist/private/node/ui/components/LoadingBar.test.js.map +1 -0
- package/dist/private/node/ui/components/SingleTask.d.ts +8 -0
- package/dist/private/node/ui/components/SingleTask.js +27 -0
- package/dist/private/node/ui/components/SingleTask.js.map +1 -0
- package/dist/private/node/ui/components/SingleTask.test.d.ts +1 -0
- package/dist/private/node/ui/components/SingleTask.test.js +145 -0
- package/dist/private/node/ui/components/SingleTask.test.js.map +1 -0
- package/dist/private/node/ui/components/Tasks.d.ts +2 -1
- package/dist/private/node/ui/components/Tasks.js +5 -25
- package/dist/private/node/ui/components/Tasks.js.map +1 -1
- package/dist/private/node/ui/components/Tasks.test.js +19 -103
- package/dist/private/node/ui/components/Tasks.test.js.map +1 -1
- package/dist/private/node/ui/hooks/use-exit-on-ctrl-c.d.ts +4 -0
- package/dist/private/node/ui/hooks/use-exit-on-ctrl-c.js +15 -0
- package/dist/private/node/ui/hooks/use-exit-on-ctrl-c.js.map +1 -0
- package/dist/public/common/version.d.ts +1 -1
- package/dist/public/common/version.js +1 -1
- package/dist/public/common/version.js.map +1 -1
- package/dist/public/node/analytics.d.ts +77 -0
- package/dist/public/node/analytics.js +88 -0
- package/dist/public/node/analytics.js.map +1 -1
- package/dist/public/node/api/admin.js +2 -3
- package/dist/public/node/api/admin.js.map +1 -1
- package/dist/public/node/api/app-dev.d.ts +2 -0
- package/dist/public/node/api/app-dev.js +1 -0
- package/dist/public/node/api/app-dev.js.map +1 -1
- package/dist/public/node/base-command.d.ts +22 -0
- package/dist/public/node/base-command.js +1 -1
- package/dist/public/node/base-command.js.map +1 -1
- package/dist/public/node/context/fqdn.d.ts +0 -4
- package/dist/public/node/context/fqdn.js +1 -23
- package/dist/public/node/context/fqdn.js.map +1 -1
- package/dist/public/node/context/local.d.ts +2 -2
- package/dist/public/node/context/local.js +2 -6
- package/dist/public/node/context/local.js.map +1 -1
- package/dist/public/node/error-handler.js +2 -1
- package/dist/public/node/error-handler.js.map +1 -1
- package/dist/public/node/http.d.ts +1 -1
- package/dist/public/node/http.js +1 -1
- package/dist/public/node/http.js.map +1 -1
- package/dist/public/node/metadata.d.ts +31 -4
- package/dist/public/node/metadata.js.map +1 -1
- package/dist/public/node/session-prompt.d.ts +10 -0
- package/dist/public/node/session-prompt.js +86 -0
- package/dist/public/node/session-prompt.js.map +1 -0
- package/dist/public/node/session.d.ts +11 -6
- package/dist/public/node/session.js +15 -4
- package/dist/public/node/session.js.map +1 -1
- package/dist/public/node/themes/api.js +28 -8
- package/dist/public/node/themes/api.js.map +1 -1
- package/dist/public/node/ui.d.ts +17 -1
- package/dist/public/node/ui.js +26 -2
- package/dist/public/node/ui.js.map +1 -1
- package/dist/public/node/vendor/dev_server/dev-server.js +1 -5
- package/dist/public/node/vendor/dev_server/dev-server.js.map +1 -1
- package/dist/public/node/vendor/dev_server/env.js +2 -2
- package/dist/public/node/vendor/dev_server/env.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/dist/private/node/context/spin-cache.d.ts +0 -2
- package/dist/private/node/context/spin-cache.js +0 -8
- package/dist/private/node/context/spin-cache.js.map +0 -1
- package/dist/public/node/context/spin.d.ts +0 -69
- package/dist/public/node/context/spin.js +0 -152
- package/dist/public/node/context/spin.js.map +0 -1
- package/dist/public/node/themes/analytics/bounded-collections.js.map +0 -1
- package/dist/public/node/themes/analytics/error-categorizer.js +0 -49
- package/dist/public/node/themes/analytics/error-categorizer.js.map +0 -1
- package/dist/public/node/themes/analytics/storage.js.map +0 -1
- package/dist/public/node/themes/analytics.d.ts +0 -60
- package/dist/public/node/themes/analytics.js +0 -71
- package/dist/public/node/themes/analytics.js.map +0 -1
- package/dist/public/node/vendor/dev_server/dev-server-spin.d.ts +0 -5
- package/dist/public/node/vendor/dev_server/dev-server-spin.js +0 -28
- package/dist/public/node/vendor/dev_server/dev-server-spin.js.map +0 -1
- /package/dist/{public/node/themes → private/node}/analytics/bounded-collections.d.ts +0 -0
- /package/dist/{public/node/themes → private/node}/analytics/bounded-collections.js +0 -0
- /package/dist/{public/node/themes → private/node}/analytics/storage.d.ts +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { LoadingBar } from './LoadingBar.js';
|
|
2
|
+
import { render } from '../../testing/ui.js';
|
|
3
|
+
import { shouldDisplayColors, unstyled } from '../../../../public/node/output.js';
|
|
4
|
+
import useLayout from '../hooks/use-layout.js';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { beforeEach, describe, expect, test, vi } from 'vitest';
|
|
7
|
+
vi.mock('../hooks/use-layout.js');
|
|
8
|
+
vi.mock('../../../../public/node/output.js', async () => {
|
|
9
|
+
const original = await vi.importActual('../../../../public/node/output.js');
|
|
10
|
+
return {
|
|
11
|
+
...original,
|
|
12
|
+
shouldDisplayColors: vi.fn(),
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
// Default terminal width
|
|
17
|
+
vi.mocked(useLayout).mockReturnValue({
|
|
18
|
+
twoThirds: 53,
|
|
19
|
+
oneThird: 27,
|
|
20
|
+
fullWidth: 80,
|
|
21
|
+
});
|
|
22
|
+
vi.mocked(shouldDisplayColors).mockReturnValue(true);
|
|
23
|
+
});
|
|
24
|
+
describe('LoadingBar', () => {
|
|
25
|
+
test('renders loading bar with default colored characters', async () => {
|
|
26
|
+
// Given
|
|
27
|
+
const title = 'Loading content';
|
|
28
|
+
// When
|
|
29
|
+
const { lastFrame } = render(React.createElement(LoadingBar, { title: title }));
|
|
30
|
+
// Then
|
|
31
|
+
expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
|
|
32
|
+
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
|
33
|
+
Loading content ..."
|
|
34
|
+
`);
|
|
35
|
+
});
|
|
36
|
+
test('renders loading bar with hill pattern when noColor prop is true', async () => {
|
|
37
|
+
// Given
|
|
38
|
+
const title = 'Processing files';
|
|
39
|
+
// When
|
|
40
|
+
const { lastFrame } = render(React.createElement(LoadingBar, { title: title, noColor: true }));
|
|
41
|
+
// Then
|
|
42
|
+
expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
|
|
43
|
+
"▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅
|
|
44
|
+
Processing files ..."
|
|
45
|
+
`);
|
|
46
|
+
});
|
|
47
|
+
test('renders loading bar with hill pattern when shouldDisplayColors returns false', async () => {
|
|
48
|
+
// Given
|
|
49
|
+
vi.mocked(shouldDisplayColors).mockReturnValue(false);
|
|
50
|
+
const title = 'Downloading packages';
|
|
51
|
+
// When
|
|
52
|
+
const { lastFrame } = render(React.createElement(LoadingBar, { title: title }));
|
|
53
|
+
// Then
|
|
54
|
+
expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
|
|
55
|
+
"▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅
|
|
56
|
+
Downloading packages ..."
|
|
57
|
+
`);
|
|
58
|
+
});
|
|
59
|
+
test('handles narrow terminal width correctly', async () => {
|
|
60
|
+
// Given
|
|
61
|
+
vi.mocked(useLayout).mockReturnValue({
|
|
62
|
+
twoThirds: 20,
|
|
63
|
+
oneThird: 10,
|
|
64
|
+
fullWidth: 30,
|
|
65
|
+
});
|
|
66
|
+
const title = 'Building app';
|
|
67
|
+
// When
|
|
68
|
+
const { lastFrame } = render(React.createElement(LoadingBar, { title: title }));
|
|
69
|
+
// Then
|
|
70
|
+
expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
|
|
71
|
+
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
|
72
|
+
Building app ..."
|
|
73
|
+
`);
|
|
74
|
+
});
|
|
75
|
+
test('handles narrow terminal width correctly in no-color mode', async () => {
|
|
76
|
+
// Given
|
|
77
|
+
vi.mocked(useLayout).mockReturnValue({
|
|
78
|
+
twoThirds: 15,
|
|
79
|
+
oneThird: 8,
|
|
80
|
+
fullWidth: 23,
|
|
81
|
+
});
|
|
82
|
+
const title = 'Installing';
|
|
83
|
+
// When
|
|
84
|
+
const { lastFrame } = render(React.createElement(LoadingBar, { title: title, noColor: true }));
|
|
85
|
+
// Then
|
|
86
|
+
expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
|
|
87
|
+
"▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇
|
|
88
|
+
Installing ..."
|
|
89
|
+
`);
|
|
90
|
+
});
|
|
91
|
+
test('handles very narrow terminal width in no-color mode', async () => {
|
|
92
|
+
// Given
|
|
93
|
+
vi.mocked(useLayout).mockReturnValue({
|
|
94
|
+
twoThirds: 5,
|
|
95
|
+
oneThird: 3,
|
|
96
|
+
fullWidth: 8,
|
|
97
|
+
});
|
|
98
|
+
const title = 'Wait';
|
|
99
|
+
// When
|
|
100
|
+
const { lastFrame } = render(React.createElement(LoadingBar, { title: title, noColor: true }));
|
|
101
|
+
// Then
|
|
102
|
+
expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
|
|
103
|
+
"▁▁▁▂▂
|
|
104
|
+
Wait ..."
|
|
105
|
+
`);
|
|
106
|
+
});
|
|
107
|
+
test('handles wide terminal width correctly', async () => {
|
|
108
|
+
// Given
|
|
109
|
+
vi.mocked(useLayout).mockReturnValue({
|
|
110
|
+
twoThirds: 100,
|
|
111
|
+
oneThird: 50,
|
|
112
|
+
fullWidth: 150,
|
|
113
|
+
});
|
|
114
|
+
const title = 'Synchronizing data';
|
|
115
|
+
// When
|
|
116
|
+
const { lastFrame } = render(React.createElement(LoadingBar, { title: title }));
|
|
117
|
+
// Then
|
|
118
|
+
expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
|
|
119
|
+
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
|
120
|
+
Synchronizing data ..."
|
|
121
|
+
`);
|
|
122
|
+
});
|
|
123
|
+
test('handles wide terminal width correctly in no-color mode with pattern repetition', async () => {
|
|
124
|
+
// Given
|
|
125
|
+
vi.mocked(useLayout).mockReturnValue({
|
|
126
|
+
twoThirds: 90,
|
|
127
|
+
oneThird: 45,
|
|
128
|
+
fullWidth: 135,
|
|
129
|
+
});
|
|
130
|
+
const title = 'Analyzing dependencies';
|
|
131
|
+
// When
|
|
132
|
+
const { lastFrame } = render(React.createElement(LoadingBar, { title: title, noColor: true }));
|
|
133
|
+
// Then
|
|
134
|
+
expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
|
|
135
|
+
"▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁
|
|
136
|
+
Analyzing dependencies ..."
|
|
137
|
+
`);
|
|
138
|
+
});
|
|
139
|
+
test('renders correctly with empty title', async () => {
|
|
140
|
+
// Given
|
|
141
|
+
const title = '';
|
|
142
|
+
// When
|
|
143
|
+
const { lastFrame } = render(React.createElement(LoadingBar, { title: title }));
|
|
144
|
+
// Then
|
|
145
|
+
expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
|
|
146
|
+
"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
|
147
|
+
..."
|
|
148
|
+
`);
|
|
149
|
+
});
|
|
150
|
+
test('noColor prop overrides shouldDisplayColors when both would show colors', async () => {
|
|
151
|
+
// Given
|
|
152
|
+
vi.mocked(shouldDisplayColors).mockReturnValue(true);
|
|
153
|
+
const title = 'Testing override';
|
|
154
|
+
// When
|
|
155
|
+
const { lastFrame } = render(React.createElement(LoadingBar, { title: title, noColor: true }));
|
|
156
|
+
// Then
|
|
157
|
+
expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
|
|
158
|
+
"▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅
|
|
159
|
+
Testing override ..."
|
|
160
|
+
`);
|
|
161
|
+
});
|
|
162
|
+
test('renders consistently with same props', async () => {
|
|
163
|
+
// Given
|
|
164
|
+
const title = 'Consistent test';
|
|
165
|
+
const props = { title, noColor: false };
|
|
166
|
+
// When
|
|
167
|
+
const { lastFrame: frame1 } = render(React.createElement(LoadingBar, { ...props }));
|
|
168
|
+
const { lastFrame: frame2 } = render(React.createElement(LoadingBar, { ...props }));
|
|
169
|
+
// Then
|
|
170
|
+
expect(frame1()).toBe(frame2());
|
|
171
|
+
});
|
|
172
|
+
test('hides progress bar when noProgressBar is true', async () => {
|
|
173
|
+
// Given
|
|
174
|
+
vi.mocked(shouldDisplayColors).mockReturnValue(true);
|
|
175
|
+
const title = 'task 1';
|
|
176
|
+
// When
|
|
177
|
+
const { lastFrame } = render(React.createElement(LoadingBar, { title: title, noProgressBar: true }));
|
|
178
|
+
// Then
|
|
179
|
+
expect(unstyled(lastFrame())).toMatchInlineSnapshot(`"task 1 ..."`);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
//# sourceMappingURL=LoadingBar.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoadingBar.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/LoadingBar.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {LoadingBar} from './LoadingBar.js'\nimport {render} from '../../testing/ui.js'\nimport {shouldDisplayColors, unstyled} from '../../../../public/node/output.js'\nimport useLayout from '../hooks/use-layout.js'\nimport React from 'react'\nimport {beforeEach, describe, expect, test, vi} from 'vitest'\n\nvi.mock('../hooks/use-layout.js')\nvi.mock('../../../../public/node/output.js', async () => {\n const original: any = await vi.importActual('../../../../public/node/output.js')\n return {\n ...original,\n shouldDisplayColors: vi.fn(),\n }\n})\n\nbeforeEach(() => {\n // Default terminal width\n vi.mocked(useLayout).mockReturnValue({\n twoThirds: 53,\n oneThird: 27,\n fullWidth: 80,\n })\n vi.mocked(shouldDisplayColors).mockReturnValue(true)\n})\n\ndescribe('LoadingBar', () => {\n test('renders loading bar with default colored characters', async () => {\n // Given\n const title = 'Loading content'\n\n // When\n const {lastFrame} = render(<LoadingBar title={title} />)\n\n // Then\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀\n Loading content ...\"\n `)\n })\n\n test('renders loading bar with hill pattern when noColor prop is true', async () => {\n // Given\n const title = 'Processing files'\n\n // When\n const {lastFrame} = render(<LoadingBar title={title} noColor />)\n\n // Then\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅\n Processing files ...\"\n `)\n })\n\n test('renders loading bar with hill pattern when shouldDisplayColors returns false', async () => {\n // Given\n vi.mocked(shouldDisplayColors).mockReturnValue(false)\n const title = 'Downloading packages'\n\n // When\n const {lastFrame} = render(<LoadingBar title={title} />)\n\n // Then\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅\n Downloading packages ...\"\n `)\n })\n\n test('handles narrow terminal width correctly', async () => {\n // Given\n vi.mocked(useLayout).mockReturnValue({\n twoThirds: 20,\n oneThird: 10,\n fullWidth: 30,\n })\n const title = 'Building app'\n\n // When\n const {lastFrame} = render(<LoadingBar title={title} />)\n\n // Then\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀\n Building app ...\"\n `)\n })\n\n test('handles narrow terminal width correctly in no-color mode', async () => {\n // Given\n vi.mocked(useLayout).mockReturnValue({\n twoThirds: 15,\n oneThird: 8,\n fullWidth: 23,\n })\n const title = 'Installing'\n\n // When\n const {lastFrame} = render(<LoadingBar title={title} noColor />)\n\n // Then\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇\n Installing ...\"\n `)\n })\n\n test('handles very narrow terminal width in no-color mode', async () => {\n // Given\n vi.mocked(useLayout).mockReturnValue({\n twoThirds: 5,\n oneThird: 3,\n fullWidth: 8,\n })\n const title = 'Wait'\n\n // When\n const {lastFrame} = render(<LoadingBar title={title} noColor />)\n\n // Then\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"▁▁▁▂▂\n Wait ...\"\n `)\n })\n\n test('handles wide terminal width correctly', async () => {\n // Given\n vi.mocked(useLayout).mockReturnValue({\n twoThirds: 100,\n oneThird: 50,\n fullWidth: 150,\n })\n const title = 'Synchronizing data'\n\n // When\n const {lastFrame} = render(<LoadingBar title={title} />)\n\n // Then\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀\n Synchronizing data ...\"\n `)\n })\n\n test('handles wide terminal width correctly in no-color mode with pattern repetition', async () => {\n // Given\n vi.mocked(useLayout).mockReturnValue({\n twoThirds: 90,\n oneThird: 45,\n fullWidth: 135,\n })\n const title = 'Analyzing dependencies'\n\n // When\n const {lastFrame} = render(<LoadingBar title={title} noColor />)\n\n // Then\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁\n Analyzing dependencies ...\"\n `)\n })\n\n test('renders correctly with empty title', async () => {\n // Given\n const title = ''\n\n // When\n const {lastFrame} = render(<LoadingBar title={title} />)\n\n // Then\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀\n ...\"\n `)\n })\n\n test('noColor prop overrides shouldDisplayColors when both would show colors', async () => {\n // Given\n vi.mocked(shouldDisplayColors).mockReturnValue(true)\n const title = 'Testing override'\n\n // When\n const {lastFrame} = render(<LoadingBar title={title} noColor />)\n\n // Then\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁▁▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅\n Testing override ...\"\n `)\n })\n\n test('renders consistently with same props', async () => {\n // Given\n const title = 'Consistent test'\n const props = {title, noColor: false}\n\n // When\n const {lastFrame: frame1} = render(<LoadingBar {...props} />)\n const {lastFrame: frame2} = render(<LoadingBar {...props} />)\n\n // Then\n expect(frame1()).toBe(frame2())\n })\n\n test('hides progress bar when noProgressBar is true', async () => {\n // Given\n vi.mocked(shouldDisplayColors).mockReturnValue(true)\n const title = 'task 1'\n\n // When\n const {lastFrame} = render(<LoadingBar title={title} noProgressBar />)\n\n // Then\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\"task 1 ...\"`)\n })\n})\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface SingleTaskProps {
|
|
3
|
+
title: string;
|
|
4
|
+
taskPromise: Promise<unknown>;
|
|
5
|
+
noColor?: boolean;
|
|
6
|
+
}
|
|
7
|
+
declare const SingleTask: ({ taskPromise, title, noColor }: React.PropsWithChildren<SingleTaskProps>) => JSX.Element | null;
|
|
8
|
+
export { SingleTask };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { LoadingBar } from './LoadingBar.js';
|
|
2
|
+
import { useExitOnCtrlC } from '../hooks/use-exit-on-ctrl-c.js';
|
|
3
|
+
import React, { useEffect, useState } from 'react';
|
|
4
|
+
import { useApp } from 'ink';
|
|
5
|
+
const SingleTask = ({ taskPromise, title, noColor }) => {
|
|
6
|
+
const [isDone, setIsDone] = useState(false);
|
|
7
|
+
const { exit: unmountInk } = useApp();
|
|
8
|
+
useExitOnCtrlC();
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
taskPromise
|
|
11
|
+
.then(() => {
|
|
12
|
+
setIsDone(true);
|
|
13
|
+
unmountInk();
|
|
14
|
+
})
|
|
15
|
+
.catch((error) => {
|
|
16
|
+
setIsDone(true);
|
|
17
|
+
unmountInk(error);
|
|
18
|
+
});
|
|
19
|
+
}, [taskPromise, unmountInk]);
|
|
20
|
+
if (isDone) {
|
|
21
|
+
// clear things once done
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return React.createElement(LoadingBar, { title: title, noColor: noColor });
|
|
25
|
+
};
|
|
26
|
+
export { SingleTask };
|
|
27
|
+
//# sourceMappingURL=SingleTask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SingleTask.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/SingleTask.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAA;AAC1C,OAAO,EAAC,cAAc,EAAC,MAAM,gCAAgC,CAAA;AAC7D,OAAO,KAAK,EAAE,EAAC,SAAS,EAAE,QAAQ,EAAC,MAAM,OAAO,CAAA;AAChD,OAAO,EAAC,MAAM,EAAC,MAAM,KAAK,CAAA;AAQ1B,MAAM,UAAU,GAAG,CAAC,EAAC,WAAW,EAAE,KAAK,EAAE,OAAO,EAA2C,EAAE,EAAE;IAC7F,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3C,MAAM,EAAC,IAAI,EAAE,UAAU,EAAC,GAAG,MAAM,EAAE,CAAA;IACnC,cAAc,EAAE,CAAA;IAEhB,SAAS,CAAC,GAAG,EAAE;QACb,WAAW;aACR,IAAI,CAAC,GAAG,EAAE;YACT,SAAS,CAAC,IAAI,CAAC,CAAA;YACf,UAAU,EAAE,CAAA;QACd,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,SAAS,CAAC,IAAI,CAAC,CAAA;YACf,UAAU,CAAC,KAAK,CAAC,CAAA;QACnB,CAAC,CAAC,CAAA;IACN,CAAC,EAAE,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAA;IAE7B,IAAI,MAAM,EAAE,CAAC;QACX,yBAAyB;QACzB,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,oBAAC,UAAU,IAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,GAAI,CAAA;AACvD,CAAC,CAAA;AAED,OAAO,EAAC,UAAU,EAAC,CAAA","sourcesContent":["import {LoadingBar} from './LoadingBar.js'\nimport {useExitOnCtrlC} from '../hooks/use-exit-on-ctrl-c.js'\nimport React, {useEffect, useState} from 'react'\nimport {useApp} from 'ink'\n\ninterface SingleTaskProps {\n title: string\n taskPromise: Promise<unknown>\n noColor?: boolean\n}\n\nconst SingleTask = ({taskPromise, title, noColor}: React.PropsWithChildren<SingleTaskProps>) => {\n const [isDone, setIsDone] = useState(false)\n const {exit: unmountInk} = useApp()\n useExitOnCtrlC()\n\n useEffect(() => {\n taskPromise\n .then(() => {\n setIsDone(true)\n unmountInk()\n })\n .catch((error) => {\n setIsDone(true)\n unmountInk(error)\n })\n }, [taskPromise, unmountInk])\n\n if (isDone) {\n // clear things once done\n return null\n }\n\n return <LoadingBar title={title} noColor={noColor} />\n}\n\nexport {SingleTask}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { SingleTask } from './SingleTask.js';
|
|
2
|
+
import { render } from '../../testing/ui.js';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { describe, expect, test } from 'vitest';
|
|
5
|
+
describe('SingleTask', () => {
|
|
6
|
+
test('unmounts when promise resolves successfully', async () => {
|
|
7
|
+
// Given
|
|
8
|
+
const title = 'Uploading files';
|
|
9
|
+
let resolvePromise;
|
|
10
|
+
const taskPromise = new Promise((resolve) => {
|
|
11
|
+
resolvePromise = resolve;
|
|
12
|
+
});
|
|
13
|
+
// When
|
|
14
|
+
const renderInstance = render(React.createElement(SingleTask, { title: title, taskPromise: taskPromise }));
|
|
15
|
+
// Wait for initial render
|
|
16
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
17
|
+
// Resolve the promise
|
|
18
|
+
resolvePromise('success');
|
|
19
|
+
// Wait for component to update and unmount
|
|
20
|
+
await renderInstance.waitUntilExit();
|
|
21
|
+
// Then - component should unmount cleanly
|
|
22
|
+
expect(renderInstance.lastFrame()).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
test('unmounts when promise rejects', async () => {
|
|
25
|
+
// Given
|
|
26
|
+
const title = 'Failed task';
|
|
27
|
+
let rejectPromise;
|
|
28
|
+
const taskPromise = new Promise((resolve, reject) => {
|
|
29
|
+
rejectPromise = reject;
|
|
30
|
+
});
|
|
31
|
+
// When
|
|
32
|
+
const renderInstance = render(React.createElement(SingleTask, { title: title, taskPromise: taskPromise }));
|
|
33
|
+
// Wait for initial render
|
|
34
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
35
|
+
// Reject the promise and expect waitUntilExit to reject
|
|
36
|
+
rejectPromise(new Error('Task failed'));
|
|
37
|
+
// The component should exit with the error
|
|
38
|
+
await expect(renderInstance.waitUntilExit()).rejects.toThrow('Task failed');
|
|
39
|
+
});
|
|
40
|
+
test('handles promise that resolves immediately', async () => {
|
|
41
|
+
// Given
|
|
42
|
+
const title = 'Instant task';
|
|
43
|
+
const taskPromise = Promise.resolve('immediate success');
|
|
44
|
+
// When
|
|
45
|
+
const renderInstance = render(React.createElement(SingleTask, { title: title, taskPromise: taskPromise }));
|
|
46
|
+
await renderInstance.waitUntilExit();
|
|
47
|
+
// Then - component should complete successfully
|
|
48
|
+
expect(renderInstance.lastFrame()).toBeDefined();
|
|
49
|
+
});
|
|
50
|
+
test('handles promise that rejects immediately', async () => {
|
|
51
|
+
// Given
|
|
52
|
+
const title = 'Instant failure';
|
|
53
|
+
const taskPromise = Promise.reject(new Error('Immediate error'));
|
|
54
|
+
// When
|
|
55
|
+
const renderInstance = render(React.createElement(SingleTask, { title: title, taskPromise: taskPromise }));
|
|
56
|
+
// Then - should exit with error
|
|
57
|
+
await expect(renderInstance.waitUntilExit()).rejects.toThrow('Immediate error');
|
|
58
|
+
});
|
|
59
|
+
test('handles different types of promise return values', async () => {
|
|
60
|
+
// Test with string
|
|
61
|
+
const stringTask = Promise.resolve('task completed');
|
|
62
|
+
const stringRender = render(React.createElement(SingleTask, { title: "String task", taskPromise: stringTask }));
|
|
63
|
+
await stringRender.waitUntilExit();
|
|
64
|
+
expect(stringRender.lastFrame()).toBeDefined();
|
|
65
|
+
// Test with object
|
|
66
|
+
const objectTask = Promise.resolve({ id: 1, name: 'test' });
|
|
67
|
+
const objectRender = render(React.createElement(SingleTask, { title: "Object task", taskPromise: objectTask }));
|
|
68
|
+
await objectRender.waitUntilExit();
|
|
69
|
+
expect(objectRender.lastFrame()).toBeDefined();
|
|
70
|
+
// Test with number
|
|
71
|
+
const numberTask = Promise.resolve(42);
|
|
72
|
+
const numberRender = render(React.createElement(SingleTask, { title: "Number task", taskPromise: numberTask }));
|
|
73
|
+
await numberRender.waitUntilExit();
|
|
74
|
+
expect(numberRender.lastFrame()).toBeDefined();
|
|
75
|
+
// Test with boolean
|
|
76
|
+
const booleanTask = Promise.resolve(true);
|
|
77
|
+
const booleanRender = render(React.createElement(SingleTask, { title: "Boolean task", taskPromise: booleanTask }));
|
|
78
|
+
await booleanRender.waitUntilExit();
|
|
79
|
+
expect(booleanRender.lastFrame()).toBeDefined();
|
|
80
|
+
});
|
|
81
|
+
test('handles promise with delayed resolution', async () => {
|
|
82
|
+
// Given
|
|
83
|
+
const title = 'Delayed task';
|
|
84
|
+
const taskPromise = new Promise((resolve) => {
|
|
85
|
+
setTimeout(() => resolve('completed'), 100);
|
|
86
|
+
});
|
|
87
|
+
// When
|
|
88
|
+
const renderInstance = render(React.createElement(SingleTask, { title: title, taskPromise: taskPromise }));
|
|
89
|
+
// Wait for completion
|
|
90
|
+
await renderInstance.waitUntilExit();
|
|
91
|
+
// Then
|
|
92
|
+
expect(renderInstance.lastFrame()).toBeDefined();
|
|
93
|
+
});
|
|
94
|
+
test('handles promise with delayed rejection', async () => {
|
|
95
|
+
// Given
|
|
96
|
+
const title = 'Delayed failure';
|
|
97
|
+
const taskPromise = new Promise((resolve, reject) => {
|
|
98
|
+
setTimeout(() => reject(new Error('delayed error')), 100);
|
|
99
|
+
});
|
|
100
|
+
// When
|
|
101
|
+
const renderInstance = render(React.createElement(SingleTask, { title: title, taskPromise: taskPromise }));
|
|
102
|
+
// Wait for completion - should throw error
|
|
103
|
+
await expect(renderInstance.waitUntilExit()).rejects.toThrow('delayed error');
|
|
104
|
+
});
|
|
105
|
+
test('preserves error types and messages', async () => {
|
|
106
|
+
// Test with custom error
|
|
107
|
+
class CustomError extends Error {
|
|
108
|
+
constructor(message, code) {
|
|
109
|
+
super(message);
|
|
110
|
+
this.code = code;
|
|
111
|
+
this.name = 'CustomError';
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const customError = new CustomError('Custom error message', 'CUSTOM_CODE');
|
|
115
|
+
const taskPromise = Promise.reject(customError);
|
|
116
|
+
// When
|
|
117
|
+
const renderInstance = render(React.createElement(SingleTask, { title: "Custom error task", taskPromise: taskPromise }));
|
|
118
|
+
// Then - should preserve the exact error
|
|
119
|
+
await expect(renderInstance.waitUntilExit()).rejects.toThrow('Custom error message');
|
|
120
|
+
});
|
|
121
|
+
test('handles concurrent promise operations', async () => {
|
|
122
|
+
// Given - Multiple SingleTask components with different promises
|
|
123
|
+
const fastPromise = new Promise((resolve) => setTimeout(() => resolve('fast'), 50));
|
|
124
|
+
const slowPromise = new Promise((resolve) => setTimeout(() => resolve('slow'), 150));
|
|
125
|
+
// When
|
|
126
|
+
const fastRender = render(React.createElement(SingleTask, { title: "Fast task", taskPromise: fastPromise }));
|
|
127
|
+
const slowRender = render(React.createElement(SingleTask, { title: "Slow task", taskPromise: slowPromise }));
|
|
128
|
+
// Then - Both should complete successfully
|
|
129
|
+
await fastRender.waitUntilExit();
|
|
130
|
+
await slowRender.waitUntilExit();
|
|
131
|
+
expect(fastRender.lastFrame()).toBeDefined();
|
|
132
|
+
expect(slowRender.lastFrame()).toBeDefined();
|
|
133
|
+
});
|
|
134
|
+
test('passes noColor prop to LoadingBar component', async () => {
|
|
135
|
+
// Given
|
|
136
|
+
const title = 'No color task';
|
|
137
|
+
const taskPromise = Promise.resolve();
|
|
138
|
+
// When - Test that noColor prop doesn't break the component
|
|
139
|
+
const renderInstance = render(React.createElement(SingleTask, { title: title, taskPromise: taskPromise, noColor: true }));
|
|
140
|
+
await renderInstance.waitUntilExit();
|
|
141
|
+
// Then - Component should complete successfully with noColor prop
|
|
142
|
+
expect(renderInstance.lastFrame()).toBeDefined();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
//# sourceMappingURL=SingleTask.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SingleTask.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/SingleTask.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {SingleTask} from './SingleTask.js'\nimport {render} from '../../testing/ui.js'\nimport React from 'react'\nimport {describe, expect, test} from 'vitest'\n\ndescribe('SingleTask', () => {\n test('unmounts when promise resolves successfully', async () => {\n // Given\n const title = 'Uploading files'\n let resolvePromise: (value: string) => void\n const taskPromise = new Promise<string>((resolve) => {\n resolvePromise = resolve\n })\n\n // When\n const renderInstance = render(<SingleTask title={title} taskPromise={taskPromise} />)\n\n // Wait for initial render\n await new Promise((resolve) => setTimeout(resolve, 10))\n\n // Resolve the promise\n resolvePromise!('success')\n\n // Wait for component to update and unmount\n await renderInstance.waitUntilExit()\n\n // Then - component should unmount cleanly\n expect(renderInstance.lastFrame()).toBeDefined()\n })\n\n test('unmounts when promise rejects', async () => {\n // Given\n const title = 'Failed task'\n let rejectPromise: (error: Error) => void\n const taskPromise = new Promise<string>((resolve, reject) => {\n rejectPromise = reject\n })\n\n // When\n const renderInstance = render(<SingleTask title={title} taskPromise={taskPromise} />)\n\n // Wait for initial render\n await new Promise((resolve) => setTimeout(resolve, 10))\n\n // Reject the promise and expect waitUntilExit to reject\n rejectPromise!(new Error('Task failed'))\n\n // The component should exit with the error\n await expect(renderInstance.waitUntilExit()).rejects.toThrow('Task failed')\n })\n\n test('handles promise that resolves immediately', async () => {\n // Given\n const title = 'Instant task'\n const taskPromise = Promise.resolve('immediate success')\n\n // When\n const renderInstance = render(<SingleTask title={title} taskPromise={taskPromise} />)\n await renderInstance.waitUntilExit()\n\n // Then - component should complete successfully\n expect(renderInstance.lastFrame()).toBeDefined()\n })\n\n test('handles promise that rejects immediately', async () => {\n // Given\n const title = 'Instant failure'\n const taskPromise = Promise.reject(new Error('Immediate error'))\n\n // When\n const renderInstance = render(<SingleTask title={title} taskPromise={taskPromise} />)\n\n // Then - should exit with error\n await expect(renderInstance.waitUntilExit()).rejects.toThrow('Immediate error')\n })\n\n test('handles different types of promise return values', async () => {\n // Test with string\n const stringTask = Promise.resolve('task completed')\n const stringRender = render(<SingleTask title=\"String task\" taskPromise={stringTask} />)\n await stringRender.waitUntilExit()\n expect(stringRender.lastFrame()).toBeDefined()\n\n // Test with object\n const objectTask = Promise.resolve({id: 1, name: 'test'})\n const objectRender = render(<SingleTask title=\"Object task\" taskPromise={objectTask} />)\n await objectRender.waitUntilExit()\n expect(objectRender.lastFrame()).toBeDefined()\n\n // Test with number\n const numberTask = Promise.resolve(42)\n const numberRender = render(<SingleTask title=\"Number task\" taskPromise={numberTask} />)\n await numberRender.waitUntilExit()\n expect(numberRender.lastFrame()).toBeDefined()\n\n // Test with boolean\n const booleanTask = Promise.resolve(true)\n const booleanRender = render(<SingleTask title=\"Boolean task\" taskPromise={booleanTask} />)\n await booleanRender.waitUntilExit()\n expect(booleanRender.lastFrame()).toBeDefined()\n })\n\n test('handles promise with delayed resolution', async () => {\n // Given\n const title = 'Delayed task'\n const taskPromise = new Promise<string>((resolve) => {\n setTimeout(() => resolve('completed'), 100)\n })\n\n // When\n const renderInstance = render(<SingleTask title={title} taskPromise={taskPromise} />)\n\n // Wait for completion\n await renderInstance.waitUntilExit()\n\n // Then\n expect(renderInstance.lastFrame()).toBeDefined()\n })\n\n test('handles promise with delayed rejection', async () => {\n // Given\n const title = 'Delayed failure'\n const taskPromise = new Promise<string>((resolve, reject) => {\n setTimeout(() => reject(new Error('delayed error')), 100)\n })\n\n // When\n const renderInstance = render(<SingleTask title={title} taskPromise={taskPromise} />)\n\n // Wait for completion - should throw error\n await expect(renderInstance.waitUntilExit()).rejects.toThrow('delayed error')\n })\n\n test('preserves error types and messages', async () => {\n // Test with custom error\n class CustomError extends Error {\n constructor(message: string, public code: string) {\n super(message)\n this.name = 'CustomError'\n }\n }\n\n const customError = new CustomError('Custom error message', 'CUSTOM_CODE')\n const taskPromise = Promise.reject(customError)\n\n // When\n const renderInstance = render(<SingleTask title=\"Custom error task\" taskPromise={taskPromise} />)\n\n // Then - should preserve the exact error\n await expect(renderInstance.waitUntilExit()).rejects.toThrow('Custom error message')\n })\n\n test('handles concurrent promise operations', async () => {\n // Given - Multiple SingleTask components with different promises\n const fastPromise = new Promise((resolve) => setTimeout(() => resolve('fast'), 50))\n const slowPromise = new Promise((resolve) => setTimeout(() => resolve('slow'), 150))\n\n // When\n const fastRender = render(<SingleTask title=\"Fast task\" taskPromise={fastPromise} />)\n const slowRender = render(<SingleTask title=\"Slow task\" taskPromise={slowPromise} />)\n\n // Then - Both should complete successfully\n await fastRender.waitUntilExit()\n await slowRender.waitUntilExit()\n\n expect(fastRender.lastFrame()).toBeDefined()\n expect(slowRender.lastFrame()).toBeDefined()\n })\n\n test('passes noColor prop to LoadingBar component', async () => {\n // Given\n const title = 'No color task'\n const taskPromise = Promise.resolve()\n\n // When - Test that noColor prop doesn't break the component\n const renderInstance = render(<SingleTask title={title} taskPromise={taskPromise} noColor />)\n await renderInstance.waitUntilExit()\n\n // Then - Component should complete successfully with noColor prop\n expect(renderInstance.lastFrame()).toBeDefined()\n })\n})\n"]}
|
|
@@ -14,6 +14,7 @@ interface TasksProps<TContext> {
|
|
|
14
14
|
onComplete?: (ctx: TContext) => void;
|
|
15
15
|
abortSignal?: AbortSignal;
|
|
16
16
|
noColor?: boolean;
|
|
17
|
+
noProgressBar?: boolean;
|
|
17
18
|
}
|
|
18
|
-
declare function Tasks<TContext>({ tasks, silent, onComplete, abortSignal, noColor, }: React.PropsWithChildren<TasksProps<TContext>>): JSX.Element | null;
|
|
19
|
+
declare function Tasks<TContext>({ tasks, silent, onComplete, abortSignal, noColor, noProgressBar, }: React.PropsWithChildren<TasksProps<TContext>>): JSX.Element | null;
|
|
19
20
|
export { Tasks };
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import useLayout from '../hooks/use-layout.js';
|
|
1
|
+
import { LoadingBar } from './LoadingBar.js';
|
|
3
2
|
import useAsyncAndUnmount from '../hooks/use-async-and-unmount.js';
|
|
4
3
|
import { isUnitTest } from '../../../../public/node/context/local.js';
|
|
5
|
-
import { shouldDisplayColors } from '../../../../public/node/output.js';
|
|
6
4
|
import useAbortSignal from '../hooks/use-abort-signal.js';
|
|
7
|
-
import {
|
|
8
|
-
import { Box, Text, useStdin, useInput } from 'ink';
|
|
5
|
+
import { useExitOnCtrlC } from '../hooks/use-exit-on-ctrl-c.js';
|
|
9
6
|
import React, { useRef, useState } from 'react';
|
|
10
|
-
const loadingBarChar = '▀';
|
|
11
|
-
const hillString = '▁▁▂▂▃▃▄▄▅▅▆▆▇▇██▇▇▆▆▅▅▄▄▃▃▂▂▁▁';
|
|
12
7
|
var TasksState;
|
|
13
8
|
(function (TasksState) {
|
|
14
9
|
TasksState["Loading"] = "loading";
|
|
@@ -41,17 +36,11 @@ async function runTask(task, ctx) {
|
|
|
41
36
|
}
|
|
42
37
|
const noop = () => { };
|
|
43
38
|
// eslint-disable-next-line react/function-component-definition
|
|
44
|
-
function Tasks({ tasks, silent = isUnitTest(), onComplete = noop, abortSignal, noColor, }) {
|
|
45
|
-
const { twoThirds } = useLayout();
|
|
46
|
-
let loadingBar = new Array(twoThirds).fill(loadingBarChar).join('');
|
|
47
|
-
if (noColor ?? !shouldDisplayColors()) {
|
|
48
|
-
loadingBar = hillString.repeat(Math.ceil(twoThirds / hillString.length));
|
|
49
|
-
}
|
|
39
|
+
function Tasks({ tasks, silent = isUnitTest(), onComplete = noop, abortSignal, noColor, noProgressBar = false, }) {
|
|
50
40
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
51
41
|
const [currentTask, setCurrentTask] = useState(tasks[0]);
|
|
52
42
|
const [state, setState] = useState(TasksState.Loading);
|
|
53
43
|
const ctx = useRef({});
|
|
54
|
-
const { isRawModeSupported } = useStdin();
|
|
55
44
|
const runTasks = async () => {
|
|
56
45
|
for (const task of tasks) {
|
|
57
46
|
setCurrentTask(task);
|
|
@@ -76,21 +65,12 @@ function Tasks({ tasks, silent = isUnitTest(), onComplete = noop, abortSignal, n
|
|
|
76
65
|
setState(TasksState.Failure);
|
|
77
66
|
},
|
|
78
67
|
});
|
|
79
|
-
|
|
80
|
-
handleCtrlC(input, key);
|
|
81
|
-
if (key.return) {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
}, { isActive: Boolean(isRawModeSupported) });
|
|
68
|
+
useExitOnCtrlC();
|
|
85
69
|
const { isAborted } = useAbortSignal(abortSignal);
|
|
86
70
|
if (silent) {
|
|
87
71
|
return null;
|
|
88
72
|
}
|
|
89
|
-
return state === TasksState.Loading && !isAborted ? (React.createElement(
|
|
90
|
-
React.createElement(TextAnimation, { text: loadingBar, maxWidth: twoThirds }),
|
|
91
|
-
React.createElement(Text, null,
|
|
92
|
-
currentTask.title,
|
|
93
|
-
" ..."))) : null;
|
|
73
|
+
return state === TasksState.Loading && !isAborted ? (React.createElement(LoadingBar, { title: currentTask.title, noColor: noColor, noProgressBar: noProgressBar })) : null;
|
|
94
74
|
}
|
|
95
75
|
export { Tasks };
|
|
96
76
|
//# sourceMappingURL=Tasks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tasks.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/Tasks.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"Tasks.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/Tasks.tsx"],"names":[],"mappings":"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","sourcesContent":["import {LoadingBar} from './LoadingBar.js'\nimport useAsyncAndUnmount from '../hooks/use-async-and-unmount.js'\nimport {isUnitTest} from '../../../../public/node/context/local.js'\nimport {AbortSignal} from '../../../../public/node/abort.js'\nimport useAbortSignal from '../hooks/use-abort-signal.js'\nimport {useExitOnCtrlC} from '../hooks/use-exit-on-ctrl-c.js'\nimport React, {useRef, useState} from 'react'\n\nexport interface Task<TContext = unknown> {\n title: string\n // eslint-disable-next-line @typescript-eslint/no-invalid-void-type\n task: (ctx: TContext, task: Task<TContext>) => Promise<void | Task<TContext>[]>\n retry?: number\n retryCount?: number\n errors?: Error[]\n skip?: (ctx: TContext) => boolean\n}\n\ninterface TasksProps<TContext> {\n tasks: Task<TContext>[]\n silent?: boolean\n onComplete?: (ctx: TContext) => void\n abortSignal?: AbortSignal\n noColor?: boolean\n noProgressBar?: boolean\n}\n\nenum TasksState {\n Loading = 'loading',\n Success = 'success',\n Failure = 'failure',\n}\n\nasync function runTask<TContext>(task: Task<TContext>, ctx: TContext) {\n task.retryCount = 0\n task.errors = []\n const retry = task.retry && task.retry > 0 ? task.retry + 1 : 1\n\n for (let retries = 1; retries <= retry; retries++) {\n try {\n if (task.skip?.(ctx)) {\n return\n }\n // eslint-disable-next-line no-await-in-loop\n return await task.task(ctx, task)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (retries === retry) {\n throw error\n } else {\n task.errors.push(error)\n task.retryCount = retries\n }\n }\n }\n}\n\nconst noop = () => {}\n\n// eslint-disable-next-line react/function-component-definition\nfunction Tasks<TContext>({\n tasks,\n silent = isUnitTest(),\n onComplete = noop,\n abortSignal,\n noColor,\n noProgressBar = false,\n}: React.PropsWithChildren<TasksProps<TContext>>) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const [currentTask, setCurrentTask] = useState<Task<TContext>>(tasks[0]!)\n const [state, setState] = useState<TasksState>(TasksState.Loading)\n const ctx = useRef<TContext>({} as TContext)\n\n const runTasks = async () => {\n for (const task of tasks) {\n setCurrentTask(task)\n\n // eslint-disable-next-line no-await-in-loop\n const subTasks = await runTask(task, ctx.current)\n\n // subtasks\n if (Array.isArray(subTasks) && subTasks.length > 0 && subTasks.every((task) => 'task' in task)) {\n for (const subTask of subTasks) {\n setCurrentTask(subTask)\n // eslint-disable-next-line no-await-in-loop\n await runTask(subTask, ctx.current)\n }\n }\n }\n }\n\n useAsyncAndUnmount(runTasks, {\n onFulfilled: () => {\n setState(TasksState.Success)\n onComplete(ctx.current)\n },\n onRejected: () => {\n setState(TasksState.Failure)\n },\n })\n\n useExitOnCtrlC()\n\n const {isAborted} = useAbortSignal(abortSignal)\n\n if (silent) {\n return null\n }\n\n return state === TasksState.Loading && !isAborted ? (\n <LoadingBar title={currentTask.title} noColor={noColor} noProgressBar={noProgressBar} />\n ) : null\n}\n\nexport {Tasks}\n"]}
|