slicejs-cli 3.5.0 → 3.6.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 +34 -15
- package/client.js +67 -20
- package/commands/createComponent/createComponent.js +6 -2
- package/commands/deleteComponent/deleteComponent.js +4 -0
- package/commands/doctor/doctor.js +78 -3
- package/commands/getComponent/getComponent.js +33 -25
- package/commands/init/init.js +106 -28
- package/commands/utils/PackageManager.js +148 -0
- package/commands/utils/VersionChecker.js +6 -4
- package/commands/utils/bundling/BundleGenerator.js +271 -38
- package/commands/utils/sliceScripts.js +21 -0
- package/commands/utils/updateManager.js +54 -35
- package/package.json +15 -1
- package/post.js +8 -16
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -29
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -25
- package/.github/pull_request_template.md +0 -22
- package/AGENTS.md +0 -247
- package/CODE_OF_CONDUCT.md +0 -126
- package/ECOSYSTEM.md +0 -9
- package/docs/superpowers/specs/2026-05-10-pwa-generate-design.md +0 -182
- package/tests/bundle-generator.test.js +0 -691
- package/tests/bundle-v2-register-output.test.js +0 -470
- package/tests/client-launcher-contract.test.js +0 -211
- package/tests/client-update-flow-contract.test.js +0 -272
- package/tests/component-registry-parse.test.js +0 -34
- package/tests/dependency-analyzer.test.js +0 -24
- package/tests/fixtures/components.js +0 -8
- package/tests/fixtures/sliceConfig.json +0 -74
- package/tests/getcomponent.test.js +0 -407
- package/tests/helpers/setup.js +0 -97
- package/tests/init-command-contract.test.js +0 -46
- package/tests/local-cli-delegation.test.js +0 -81
- package/tests/path-helper.test.js +0 -206
- package/tests/postinstall-command.test.js +0 -72
- package/tests/types-breakage.test.js +0 -491
- package/tests/types-generator-errors.test.js +0 -361
- package/tests/types-generator.test.js +0 -344
- package/tests/update-manager-notifications.test.js +0 -88
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
import { test } from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { createTestProject, cleanupTestProject } from './helpers/setup.js';
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
ensureEditorConfigForTypes,
|
|
9
|
-
ensureNoCheckInPublicVendorFiles,
|
|
10
|
-
extractStaticPropsFromSource,
|
|
11
|
-
generateDeclarationContent,
|
|
12
|
-
generateTypesFile
|
|
13
|
-
} from '../commands/types/types.js';
|
|
14
|
-
|
|
15
|
-
test('extractStaticPropsFromSource reads static props definitions', () => {
|
|
16
|
-
const source = `
|
|
17
|
-
export default class Button extends HTMLElement {
|
|
18
|
-
static props = {
|
|
19
|
-
value: { type: 'string', default: 'Button', allowedValues: ['Button', 'Submit'] },
|
|
20
|
-
disabled: { type: 'boolean', default: false },
|
|
21
|
-
size: { type: 'number', allowedValues: [12, 16, 20] },
|
|
22
|
-
options: {
|
|
23
|
-
type: 'object',
|
|
24
|
-
schema: {
|
|
25
|
-
theme: {
|
|
26
|
-
type: 'object',
|
|
27
|
-
schema: {
|
|
28
|
-
mode: { type: 'string', allowedValues: ['light', 'dark'] }
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
steps: {
|
|
34
|
-
type: 'array',
|
|
35
|
-
items: {
|
|
36
|
-
type: 'object',
|
|
37
|
-
schema: {
|
|
38
|
-
id: { type: 'string', required: true },
|
|
39
|
-
enabled: { type: 'boolean', default: true }
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
onClickCallback: { type: 'function', required: true }
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
`;
|
|
47
|
-
|
|
48
|
-
const result = extractStaticPropsFromSource(source);
|
|
49
|
-
|
|
50
|
-
assert.deepEqual(result, {
|
|
51
|
-
value: { type: 'string', required: false, allowedValues: ['Button', 'Submit'] },
|
|
52
|
-
disabled: { type: 'boolean', required: false },
|
|
53
|
-
size: { type: 'number', required: false, allowedValues: [12, 16, 20] },
|
|
54
|
-
options: {
|
|
55
|
-
type: 'object',
|
|
56
|
-
required: false,
|
|
57
|
-
schema: {
|
|
58
|
-
theme: {
|
|
59
|
-
type: 'object',
|
|
60
|
-
required: false,
|
|
61
|
-
schema: {
|
|
62
|
-
mode: {
|
|
63
|
-
type: 'string',
|
|
64
|
-
required: false,
|
|
65
|
-
allowedValues: ['light', 'dark']
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
steps: {
|
|
72
|
-
type: 'array',
|
|
73
|
-
required: false,
|
|
74
|
-
items: {
|
|
75
|
-
type: 'object',
|
|
76
|
-
required: false,
|
|
77
|
-
schema: {
|
|
78
|
-
id: { type: 'string', required: true },
|
|
79
|
-
enabled: { type: 'boolean', required: false }
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
},
|
|
83
|
-
onClickCallback: { type: 'function', required: true }
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('generateDeclarationContent creates build typing map', () => {
|
|
88
|
-
const content = generateDeclarationContent({
|
|
89
|
-
Button: {
|
|
90
|
-
value: { type: 'string', required: false, allowedValues: ['primary', 'secondary', 'danger'] },
|
|
91
|
-
size: { type: 'number', required: false, allowedValues: [12, 16] },
|
|
92
|
-
disabled: { type: 'boolean', required: false },
|
|
93
|
-
status: { type: 'string', required: false, allowedValues: ['ok', 1] },
|
|
94
|
-
options: {
|
|
95
|
-
type: 'object',
|
|
96
|
-
required: false,
|
|
97
|
-
schema: {
|
|
98
|
-
mode: { type: 'string', required: false, allowedValues: ['light', 'dark'] }
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
steps: {
|
|
102
|
-
type: 'array',
|
|
103
|
-
required: false,
|
|
104
|
-
items: {
|
|
105
|
-
type: 'object',
|
|
106
|
-
required: false,
|
|
107
|
-
schema: {
|
|
108
|
-
id: { type: 'string', required: true }
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
FetchManager: {
|
|
114
|
-
baseUrl: { type: 'string', required: true }
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
assert.match(content, /export interface ButtonProps/);
|
|
119
|
-
assert.match(content, /\[key: string\]: unknown;/);
|
|
120
|
-
assert.match(content, /value\?: 'primary' \| 'secondary' \| 'danger';/);
|
|
121
|
-
assert.match(content, /size\?: 12 \| 16;/);
|
|
122
|
-
assert.match(content, /disabled\?: boolean;/);
|
|
123
|
-
assert.match(content, /status\?: string;/);
|
|
124
|
-
assert.match(content, /export interface FetchManagerProps/);
|
|
125
|
-
assert.match(content, /baseUrl: string;/);
|
|
126
|
-
assert.match(content, /export interface SliceComponentPropsMap/);
|
|
127
|
-
assert.match(content, /Button: ButtonProps;/);
|
|
128
|
-
assert.match(content, /FetchManager: FetchManagerProps;/);
|
|
129
|
-
assert.match(content, /options\?: \{/);
|
|
130
|
-
assert.match(content, /mode\?: 'light' \| 'dark';/);
|
|
131
|
-
assert.match(content, /steps\?: \{/);
|
|
132
|
-
assert.match(content, /id: string;/);
|
|
133
|
-
assert.match(content, /}\[\];/);
|
|
134
|
-
assert.match(content, /declare module 'slicejs-web-framework'/);
|
|
135
|
-
assert.match(content, /interface SliceApi \{/);
|
|
136
|
-
assert.match(content, /export type SliceDynamicElement = HTMLElement & Record<string, any>;/);
|
|
137
|
-
assert.match(content, /build<K extends SliceComponentName>/);
|
|
138
|
-
assert.match(content, /getComponent<T extends SliceDynamicElement = SliceDynamicElement>\(/);
|
|
139
|
-
assert.match(content, /componentSliceId: string/);
|
|
140
|
-
assert.match(content, /\): T \| undefined;/);
|
|
141
|
-
assert.match(content, /interface Element \{/);
|
|
142
|
-
assert.match(content, /querySelector<E extends Element = HTMLElement>\(selectors: string\): E \| null;/);
|
|
143
|
-
assert.match(content, /querySelectorAll<E extends Element = HTMLElement>\(selectors: string\): NodeListOf<E>;/);
|
|
144
|
-
assert.match(content, /interface HTMLElement \{/);
|
|
145
|
-
assert.match(content, /\[key: string\]: any;/);
|
|
146
|
-
assert.match(content, /const slice: SliceBuildApi & Record<string, any>;/);
|
|
147
|
-
assert.match(content, /interface EventTarget \{/);
|
|
148
|
-
assert.match(content, /interface Event \{/);
|
|
149
|
-
assert.match(content, /detail: any;/);
|
|
150
|
-
assert.match(content, /currentTarget: any;/);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test('generateTypesFile creates declaration file from local components', async () => {
|
|
154
|
-
const tmpRoot = await createTestProject();
|
|
155
|
-
const srcDir = path.join(tmpRoot, 'src');
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
const visualDir = path.join(srcDir, 'Components', 'Visual', 'Button');
|
|
159
|
-
const noStaticDir = path.join(srcDir, 'Components', 'Visual', 'Tabs');
|
|
160
|
-
const serviceDir = path.join(srcDir, 'Components', 'Service', 'FetchManager');
|
|
161
|
-
const outputFile = path.join(srcDir, 'slice-build.generated.d.ts');
|
|
162
|
-
|
|
163
|
-
fs.mkdirSync(noStaticDir, { recursive: true });
|
|
164
|
-
|
|
165
|
-
fs.writeFileSync(
|
|
166
|
-
path.join(visualDir, 'Button.js'),
|
|
167
|
-
`
|
|
168
|
-
export default class Button extends HTMLElement {
|
|
169
|
-
static props = {
|
|
170
|
-
value: { type: 'string', default: 'Button', allowedValues: ['Button', 'Submit'] },
|
|
171
|
-
size: { type: 'number', allowedValues: [12, 16] },
|
|
172
|
-
disabled: { type: 'boolean', default: false }
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
`,
|
|
176
|
-
'utf8'
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
fs.writeFileSync(
|
|
180
|
-
path.join(noStaticDir, 'Tabs.js'),
|
|
181
|
-
`
|
|
182
|
-
export default class Tabs extends HTMLElement {
|
|
183
|
-
constructor() {
|
|
184
|
-
super();
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
`,
|
|
188
|
-
'utf8'
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
fs.writeFileSync(
|
|
192
|
-
path.join(serviceDir, 'FetchManager.js'),
|
|
193
|
-
`
|
|
194
|
-
export default class FetchManager extends HTMLElement {
|
|
195
|
-
static props = {
|
|
196
|
-
baseUrl: { type: 'string', required: true }
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
`,
|
|
200
|
-
'utf8'
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
fs.writeFileSync(
|
|
204
|
-
path.join(srcDir, 'Components', 'components.js'),
|
|
205
|
-
'const components = {"Button": "Visual", "Tabs": "Visual", "FetchManager": "Service"};\n\nexport default components;\n',
|
|
206
|
-
'utf8'
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
const result = await generateTypesFile({
|
|
210
|
-
projectRoot: tmpRoot,
|
|
211
|
-
outputPath: outputFile
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
assert.equal(result.componentsProcessed, 3);
|
|
215
|
-
assert.equal(fs.existsSync(result.outputPath), true);
|
|
216
|
-
|
|
217
|
-
const declaration = fs.readFileSync(result.outputPath, 'utf8');
|
|
218
|
-
assert.match(declaration, /export interface ButtonProps/);
|
|
219
|
-
assert.match(declaration, /value\?: 'Button' \| 'Submit';/);
|
|
220
|
-
assert.match(declaration, /size\?: 12 \| 16;/);
|
|
221
|
-
assert.match(declaration, /export interface TabsProps/);
|
|
222
|
-
assert.match(declaration, /\[key: string\]: unknown;/);
|
|
223
|
-
assert.match(declaration, /export interface FetchManagerProps/);
|
|
224
|
-
assert.match(declaration, /Tabs: TabsProps;/);
|
|
225
|
-
assert.match(declaration, /build<K extends SliceComponentName>/);
|
|
226
|
-
} finally {
|
|
227
|
-
await cleanupTestProject(tmpRoot);
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
test('ensureEditorConfigForTypes creates jsconfig when missing', async () => {
|
|
232
|
-
const tmpRoot = await createTestProject();
|
|
233
|
-
const outputFile = path.join(tmpRoot, 'src', 'slice-build.generated.d.ts');
|
|
234
|
-
|
|
235
|
-
try {
|
|
236
|
-
fs.writeFileSync(outputFile, 'export {};\n', 'utf8');
|
|
237
|
-
|
|
238
|
-
const result = await ensureEditorConfigForTypes({
|
|
239
|
-
projectRoot: tmpRoot,
|
|
240
|
-
outputPath: outputFile
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
assert.equal(result.mode, 'created_jsconfig');
|
|
244
|
-
|
|
245
|
-
const jsconfigPath = path.join(tmpRoot, 'jsconfig.json');
|
|
246
|
-
assert.equal(fs.existsSync(jsconfigPath), true);
|
|
247
|
-
|
|
248
|
-
const jsconfig = JSON.parse(fs.readFileSync(jsconfigPath, 'utf8'));
|
|
249
|
-
assert.equal(Array.isArray(jsconfig.include), true);
|
|
250
|
-
assert.equal(jsconfig.include.includes('src/Components/**/*.js'), true);
|
|
251
|
-
assert.equal(jsconfig.include.includes('src/**/*.d.ts'), true);
|
|
252
|
-
assert.equal(jsconfig.include.includes('api/**/*.js'), false);
|
|
253
|
-
assert.equal(jsconfig.include.includes('tests/**/*.js'), false);
|
|
254
|
-
assert.equal(jsconfig.include.includes('src/**/*.js'), false);
|
|
255
|
-
assert.equal(jsconfig.compilerOptions.checkJs, true);
|
|
256
|
-
assert.equal(jsconfig.compilerOptions.strictNullChecks, false);
|
|
257
|
-
assert.equal(jsconfig.compilerOptions.noImplicitAny, false);
|
|
258
|
-
assert.equal(jsconfig.compilerOptions.strict, false);
|
|
259
|
-
assert.equal(Array.isArray(jsconfig.exclude), true);
|
|
260
|
-
assert.equal(jsconfig.exclude.includes('src/libs/**'), true);
|
|
261
|
-
assert.equal(jsconfig.exclude.includes('tests/**'), true);
|
|
262
|
-
} finally {
|
|
263
|
-
await cleanupTestProject(tmpRoot);
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
test('types functions use PathHelper with explicit projectRoot', async () => {
|
|
268
|
-
const tmpRoot = await createTestProject({ visualComponents: ['Button'] });
|
|
269
|
-
const srcDir = path.join(tmpRoot, 'src');
|
|
270
|
-
const outputFile = path.join(srcDir, 'slice-build.generated.d.ts');
|
|
271
|
-
|
|
272
|
-
try {
|
|
273
|
-
const visualDir = path.join(srcDir, 'Components', 'Visual', 'Button');
|
|
274
|
-
fs.writeFileSync(
|
|
275
|
-
path.join(visualDir, 'Button.js'),
|
|
276
|
-
`export default class Button extends HTMLElement {
|
|
277
|
-
static props = { value: { type: 'string' } };
|
|
278
|
-
}`,
|
|
279
|
-
'utf8'
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
const result = await generateTypesFile({
|
|
283
|
-
projectRoot: tmpRoot,
|
|
284
|
-
outputPath: outputFile
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
assert.equal(result.componentsProcessed, 1);
|
|
288
|
-
assert.equal(fs.existsSync(result.outputPath), true);
|
|
289
|
-
|
|
290
|
-
const declaration = fs.readFileSync(result.outputPath, 'utf8');
|
|
291
|
-
assert.match(declaration, /export interface ButtonProps/);
|
|
292
|
-
assert.match(declaration, /build<K extends SliceComponentName>/);
|
|
293
|
-
} finally {
|
|
294
|
-
await cleanupTestProject(tmpRoot);
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
test('ensureEditorConfigForTypes augments existing jsconfig include list', async () => {
|
|
299
|
-
const tmpRoot = await createTestProject();
|
|
300
|
-
const outputFile = path.join(tmpRoot, 'src', 'slice-build.generated.d.ts');
|
|
301
|
-
const jsconfigPath = path.join(tmpRoot, 'jsconfig.json');
|
|
302
|
-
|
|
303
|
-
try {
|
|
304
|
-
fs.writeFileSync(outputFile, 'export {};\n', 'utf8');
|
|
305
|
-
fs.writeFileSync(
|
|
306
|
-
jsconfigPath,
|
|
307
|
-
JSON.stringify(
|
|
308
|
-
{
|
|
309
|
-
compilerOptions: {
|
|
310
|
-
allowJs: true
|
|
311
|
-
},
|
|
312
|
-
include: ['src/Components/**/*.js', 'src/**/*.js', 'api/**/*.js', 'tests/**/*.js']
|
|
313
|
-
},
|
|
314
|
-
null,
|
|
315
|
-
2
|
|
316
|
-
),
|
|
317
|
-
'utf8'
|
|
318
|
-
);
|
|
319
|
-
|
|
320
|
-
const result = await ensureEditorConfigForTypes({
|
|
321
|
-
projectRoot: tmpRoot,
|
|
322
|
-
outputPath: outputFile
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
assert.equal(result.mode, 'updated_jsconfig');
|
|
326
|
-
|
|
327
|
-
const jsconfig = JSON.parse(fs.readFileSync(jsconfigPath, 'utf8'));
|
|
328
|
-
assert.equal(jsconfig.include.includes('src/Components/**/*.js'), true);
|
|
329
|
-
assert.equal(jsconfig.include.includes('src/**/*.d.ts'), true);
|
|
330
|
-
assert.equal(jsconfig.include.includes('src/**/*.js'), false);
|
|
331
|
-
assert.equal(jsconfig.include.includes('api/**/*.js'), false);
|
|
332
|
-
assert.equal(jsconfig.include.includes('tests/**/*.js'), false);
|
|
333
|
-
assert.equal(jsconfig.compilerOptions.allowJs, true);
|
|
334
|
-
assert.equal(jsconfig.compilerOptions.checkJs, true);
|
|
335
|
-
assert.equal(jsconfig.compilerOptions.noImplicitAny, false);
|
|
336
|
-
assert.equal(jsconfig.compilerOptions.strictNullChecks, false);
|
|
337
|
-
assert.equal(jsconfig.compilerOptions.strict, false);
|
|
338
|
-
assert.equal(Array.isArray(jsconfig.exclude), true);
|
|
339
|
-
assert.equal(jsconfig.exclude.includes('src/libs/**'), true);
|
|
340
|
-
assert.equal(jsconfig.exclude.includes('tests/**'), true);
|
|
341
|
-
} finally {
|
|
342
|
-
await cleanupTestProject(tmpRoot);
|
|
343
|
-
}
|
|
344
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { test } from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import Print from '../commands/Print.js';
|
|
4
|
-
import { UpdateManager } from '../commands/utils/updateManager.js';
|
|
5
|
-
|
|
6
|
-
test('notifyAvailableUpdates shows advisory output and never invokes interactive update flow', async () => {
|
|
7
|
-
const manager = new UpdateManager();
|
|
8
|
-
const calls = {
|
|
9
|
-
display: 0,
|
|
10
|
-
prompted: 0,
|
|
11
|
-
info: [],
|
|
12
|
-
error: []
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
manager.checkForUpdates = async () => ({
|
|
16
|
-
hasUpdates: true,
|
|
17
|
-
updates: [
|
|
18
|
-
{
|
|
19
|
-
name: 'slicejs-web-framework',
|
|
20
|
-
displayName: 'Slice.js Framework',
|
|
21
|
-
current: '2.4.3',
|
|
22
|
-
latest: '2.5.0',
|
|
23
|
-
type: 'framework'
|
|
24
|
-
}
|
|
25
|
-
],
|
|
26
|
-
allCurrent: false
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
manager.displayUpdates = () => {
|
|
30
|
-
calls.display += 1;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
manager.checkAndPromptUpdates = async () => {
|
|
34
|
-
calls.prompted += 1;
|
|
35
|
-
return true;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const originalInfo = Print.info;
|
|
39
|
-
const originalError = Print.error;
|
|
40
|
-
|
|
41
|
-
Print.info = (message) => calls.info.push(message);
|
|
42
|
-
Print.error = (message) => calls.error.push(message);
|
|
43
|
-
|
|
44
|
-
try {
|
|
45
|
-
const result = await manager.notifyAvailableUpdates();
|
|
46
|
-
|
|
47
|
-
assert.equal(result, true);
|
|
48
|
-
assert.equal(calls.display, 1);
|
|
49
|
-
assert.equal(calls.prompted, 0);
|
|
50
|
-
assert.equal(calls.error.length, 0);
|
|
51
|
-
assert.equal(calls.info.length, 1);
|
|
52
|
-
assert.match(calls.info[0], /slice update/);
|
|
53
|
-
} finally {
|
|
54
|
-
Print.info = originalInfo;
|
|
55
|
-
Print.error = originalError;
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('notifyAvailableUpdates returns false and prints nothing when no updates are available', async () => {
|
|
60
|
-
const manager = new UpdateManager();
|
|
61
|
-
const calls = {
|
|
62
|
-
display: 0,
|
|
63
|
-
info: []
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
manager.checkForUpdates = async () => ({
|
|
67
|
-
hasUpdates: false,
|
|
68
|
-
updates: [],
|
|
69
|
-
allCurrent: true
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
manager.displayUpdates = () => {
|
|
73
|
-
calls.display += 1;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const originalInfo = Print.info;
|
|
77
|
-
Print.info = (message) => calls.info.push(message);
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const result = await manager.notifyAvailableUpdates();
|
|
81
|
-
|
|
82
|
-
assert.equal(result, false);
|
|
83
|
-
assert.equal(calls.display, 0);
|
|
84
|
-
assert.equal(calls.info.length, 0);
|
|
85
|
-
} finally {
|
|
86
|
-
Print.info = originalInfo;
|
|
87
|
-
}
|
|
88
|
-
});
|