@servicetitan/startup 22.14.0 → 22.16.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/dist/cli/__mocks__/create-package.d.ts +3 -0
- package/dist/cli/__mocks__/create-package.d.ts.map +1 -0
- package/dist/cli/__mocks__/create-package.js +9 -0
- package/dist/cli/__mocks__/create-package.js.map +1 -0
- package/dist/cli/__mocks__/index.d.ts +2 -0
- package/dist/cli/__mocks__/index.d.ts.map +1 -0
- package/dist/cli/__mocks__/index.js +18 -0
- package/dist/cli/__mocks__/index.js.map +1 -0
- package/dist/cli/commands/eslint.d.ts +12 -0
- package/dist/cli/commands/eslint.d.ts.map +1 -0
- package/dist/cli/commands/eslint.js +47 -0
- package/dist/cli/commands/eslint.js.map +1 -0
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/lint.d.ts +5 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +22 -30
- package/dist/cli/commands/lint.js.map +1 -1
- package/dist/cli/index.js +20 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/eslint.d.ts +7 -0
- package/dist/cli/utils/eslint.d.ts.map +1 -0
- package/dist/cli/utils/eslint.js +57 -0
- package/dist/cli/utils/eslint.js.map +1 -0
- package/dist/cli/utils/index.d.ts +2 -0
- package/dist/cli/utils/index.d.ts.map +1 -1
- package/dist/cli/utils/index.js +2 -0
- package/dist/cli/utils/index.js.map +1 -1
- package/dist/cli/utils/set-node-options.d.ts +7 -0
- package/dist/cli/utils/set-node-options.d.ts.map +1 -0
- package/dist/cli/utils/set-node-options.js +46 -0
- package/dist/cli/utils/set-node-options.js.map +1 -0
- package/dist/cli/utils/tcm.d.ts.map +1 -1
- package/dist/cli/utils/tcm.js +3 -1
- package/dist/cli/utils/tcm.js.map +1 -1
- package/dist/utils/get-configuration.d.ts +5 -1
- package/dist/utils/get-configuration.d.ts.map +1 -1
- package/dist/utils/get-configuration.js +2 -2
- package/dist/utils/get-configuration.js.map +1 -1
- package/dist/utils/get-jest-config.d.ts +1 -1
- package/dist/utils/get-jest-config.d.ts.map +1 -1
- package/dist/utils/get-jest-config.js +8 -6
- package/dist/utils/get-jest-config.js.map +1 -1
- package/dist/utils/get-package-data.js +3 -3
- package/dist/utils/get-package-data.js.map +1 -1
- package/dist/utils/get-package-name.d.ts.map +1 -1
- package/dist/utils/get-package-name.js +2 -2
- package/dist/utils/get-package-name.js.map +1 -1
- package/dist/utils/get-packages.js +8 -7
- package/dist/utils/get-packages.js.map +1 -1
- package/dist/webpack/shared.config.d.ts.map +1 -1
- package/dist/webpack/shared.config.js +1 -0
- package/dist/webpack/shared.config.js.map +1 -1
- package/package.json +4 -4
- package/src/cli/__mocks__/create-package.ts +12 -0
- package/src/cli/__mocks__/index.ts +1 -0
- package/src/cli/commands/__tests__/build.test.ts +135 -0
- package/src/cli/commands/__tests__/bundle-package.test.ts +72 -0
- package/src/cli/commands/__tests__/eslint.test.ts +19 -0
- package/src/cli/commands/__tests__/init.test.ts +42 -0
- package/src/cli/commands/__tests__/lint.test.ts +114 -0
- package/src/cli/commands/__tests__/mfe-package-clean.test.ts +193 -0
- package/src/cli/commands/__tests__/mfe-package-publish.test.ts +192 -0
- package/src/cli/commands/__tests__/mfe-publish.test.ts +172 -0
- package/src/cli/commands/__tests__/prepare-package.test.ts +75 -0
- package/src/cli/commands/__tests__/start.test.ts +111 -0
- package/src/cli/commands/__tests__/styles-check.test.ts +119 -0
- package/src/cli/commands/__tests__/tests.test.ts +43 -0
- package/src/cli/commands/eslint.ts +19 -0
- package/src/cli/commands/index.ts +1 -0
- package/src/cli/commands/lint.ts +29 -42
- package/src/cli/index.ts +17 -5
- package/src/cli/utils/__tests__/assets-copy.test.ts +48 -52
- package/src/cli/utils/__tests__/bundle.test.ts +361 -0
- package/src/cli/utils/__tests__/cli-git.test.ts +28 -0
- package/src/cli/utils/__tests__/cli-npm.test.ts +166 -0
- package/src/cli/utils/__tests__/cli-os.test.ts +103 -0
- package/src/cli/utils/__tests__/eslint.test.ts +91 -0
- package/src/cli/utils/__tests__/get-module-type.test.ts +56 -0
- package/src/cli/utils/__tests__/is-module-installed.test.ts +24 -0
- package/src/cli/utils/__tests__/set-node-options.test.ts +131 -0
- package/src/cli/utils/__tests__/styles-copy.test.ts +48 -44
- package/src/cli/utils/__tests__/tcm.test.ts +101 -192
- package/src/cli/utils/__tests__/tsc.test.ts +85 -0
- package/src/cli/utils/eslint.ts +50 -0
- package/src/cli/utils/index.ts +2 -0
- package/src/cli/utils/set-node-options.ts +45 -0
- package/src/cli/utils/tcm.ts +3 -1
- package/src/utils/__tests__/get-configuration.test.ts +215 -16
- package/src/utils/__tests__/get-destination-folders.test.ts +65 -0
- package/src/utils/__tests__/get-jest-config.test.ts +134 -0
- package/src/utils/__tests__/get-package-data.test.ts +90 -0
- package/src/utils/__tests__/get-packages.test.ts +165 -0
- package/src/utils/__tests__/get-tsconfig.test.ts +48 -0
- package/src/utils/__tests__/log.test.ts +123 -0
- package/src/utils/__tests__/to-array.test.ts +19 -0
- package/src/utils/get-configuration.ts +8 -3
- package/src/utils/get-jest-config.ts +3 -1
- package/src/utils/get-package-data.ts +1 -1
- package/src/utils/get-package-name.ts +1 -2
- package/src/utils/get-packages.ts +2 -2
- package/src/webpack/shared.config.ts +1 -0
- package/template/package.json +8 -1
- package/template/packages/application/package.json +5 -0
- package/template/packages/application/src/__tests__/app.test.tsx +33 -0
- package/template/packages/application/src/app.tsx +9 -6
- package/template/packages/application/src/main-page.tsx +5 -0
- package/template/packages/application/src/second-page.tsx +5 -0
- package/template/setupTests.ts +27 -0
- package/template/tsconfig.test.json +1 -1
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import mockFS from 'mock-fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import webpack from 'webpack';
|
|
5
|
+
import WebpackDevServer from 'webpack-dev-server';
|
|
6
|
+
import { getFolders, getPackageData, loadSharedDependencies } from '../../../utils';
|
|
7
|
+
import { createWebpackConfig } from '../../../webpack';
|
|
8
|
+
import { createPackage } from '../../__mocks__';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
bundle,
|
|
12
|
+
bundleWatch,
|
|
13
|
+
webpackDevConfigFileName,
|
|
14
|
+
webpackProdConfigFileName,
|
|
15
|
+
} from '../bundle';
|
|
16
|
+
|
|
17
|
+
jest.mock('webpack', () => jest.fn());
|
|
18
|
+
jest.mock('webpack-dev-server');
|
|
19
|
+
jest.mock('../../../utils', () => ({
|
|
20
|
+
...jest.requireActual('../../../utils'),
|
|
21
|
+
getFolders: jest.fn(),
|
|
22
|
+
getPackageData: jest.fn(),
|
|
23
|
+
loadSharedDependencies: jest.fn(),
|
|
24
|
+
log: { info: jest.fn() }, // suppress test output
|
|
25
|
+
}));
|
|
26
|
+
jest.mock('../../../webpack', () => ({ createWebpackConfig: jest.fn() }));
|
|
27
|
+
|
|
28
|
+
describe('[startup] Cli Utils', () => {
|
|
29
|
+
const pkg = createPackage({});
|
|
30
|
+
const createWebpackResult = { stats: { all: true } };
|
|
31
|
+
let compiler: Partial<ReturnType<typeof webpack>>;
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
jest.resetAllMocks();
|
|
35
|
+
jest.mocked(createWebpackConfig).mockReturnValue(createWebpackResult);
|
|
36
|
+
jest.mocked(webpack).mockImplementation((): any => compiler);
|
|
37
|
+
compiler = { close: jest.fn(callback => callback()) };
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(() => mockFS.restore());
|
|
41
|
+
|
|
42
|
+
function expectPackageName() {
|
|
43
|
+
const name = pkg.name.replace(/\//g, '-').replace(/[^\w-]/g, '');
|
|
44
|
+
return expect.stringContaining(name);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function generatedMetadata(destination: string) {
|
|
48
|
+
return JSON.parse(fs.readFileSync(`${destination}/metadata.json`, 'utf8'));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function packageFS() {
|
|
52
|
+
return { 'package.json': JSON.stringify(pkg) };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function webComponentFS(startupVersion: string) {
|
|
56
|
+
return {
|
|
57
|
+
'package.json': JSON.stringify({ ...pkg, cli: { 'web-component': true } }),
|
|
58
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
59
|
+
'node_modules': {
|
|
60
|
+
'@servicetitan': {
|
|
61
|
+
'package.json': JSON.stringify({ version: startupVersion }),
|
|
62
|
+
'startup': { 'index.js': '' },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
describe(`${bundle.name}`, () => {
|
|
69
|
+
const customProdConfig = { configuration: { foo: 'bar' }, plugins: { baz: 'qux' } };
|
|
70
|
+
const stats: Partial<webpack.Stats> = { hasErrors: () => false, toString: () => 'foo' };
|
|
71
|
+
let buildStat: boolean;
|
|
72
|
+
let stdoutSpy: jest.SpyInstance;
|
|
73
|
+
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
mockFS(packageFS());
|
|
76
|
+
// Allows config file to be loaded with require(...)
|
|
77
|
+
jest.doMock(path.resolve(webpackProdConfigFileName), () => customProdConfig, {
|
|
78
|
+
virtual: true,
|
|
79
|
+
});
|
|
80
|
+
buildStat = false;
|
|
81
|
+
stdoutSpy = jest.spyOn(process.stdout, 'write').mockImplementation(jest.fn());
|
|
82
|
+
compiler.run = jest.fn(callback => callback(null, stats as any));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const subject = async () => bundle(buildStat);
|
|
86
|
+
|
|
87
|
+
test('runs webpack and outputs stats', async () => {
|
|
88
|
+
const toStringSpy = jest.spyOn(stats, 'toString');
|
|
89
|
+
|
|
90
|
+
await subject();
|
|
91
|
+
|
|
92
|
+
expect(createWebpackConfig).toHaveBeenCalledWith(
|
|
93
|
+
{ configuration: { mode: 'production' } },
|
|
94
|
+
{ buildStat, name: expectPackageName() }
|
|
95
|
+
);
|
|
96
|
+
expect(webpack).toHaveBeenCalledWith(createWebpackResult);
|
|
97
|
+
expect(compiler.run).toHaveBeenCalled();
|
|
98
|
+
expect(toStringSpy).toHaveBeenCalledWith(createWebpackResult.stats);
|
|
99
|
+
expect(stdoutSpy).toHaveBeenCalledWith(`${stats.toString!()}\n`);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('when webpack completes with errors', () => {
|
|
103
|
+
const stats = { hasErrors: () => true, toString: () => 'Oops!' };
|
|
104
|
+
|
|
105
|
+
beforeEach(() => {
|
|
106
|
+
jest.mocked(compiler.run)!.mockImplementation(callback =>
|
|
107
|
+
callback(null, stats as any)
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('returns errors', () => {
|
|
112
|
+
const toStringSpy = jest.spyOn(stats, 'toString');
|
|
113
|
+
expect(subject).rejects.toBe(stats.toString());
|
|
114
|
+
expect(toStringSpy).toHaveBeenCalledWith('errors-only');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('when webpack fails', () => {
|
|
119
|
+
const error = new Error('Oops!');
|
|
120
|
+
|
|
121
|
+
beforeEach(() => {
|
|
122
|
+
jest.mocked(compiler.run)!.mockImplementation(callback => callback(error));
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('returns error', () => {
|
|
126
|
+
expect(subject).rejects.toBe(error);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('when webpack reports unexpected results', () => {
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
jest.mocked(compiler.run)!.mockImplementation(callback => callback());
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('returns error', () => {
|
|
136
|
+
expect(subject).rejects.toThrowError(/something went wrong/i);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe(`with ${webpackProdConfigFileName} config`, () => {
|
|
141
|
+
beforeEach(() => mockFS({ ...packageFS(), [webpackProdConfigFileName]: '' }));
|
|
142
|
+
|
|
143
|
+
test('uses config', async () => {
|
|
144
|
+
await subject();
|
|
145
|
+
|
|
146
|
+
expect(webpack).toHaveBeenCalledWith(customProdConfig);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('when package is a web component', () => {
|
|
151
|
+
const source = 'src';
|
|
152
|
+
const destination = 'dist';
|
|
153
|
+
const startupVersion = '1.0.0';
|
|
154
|
+
const dependencies = { foo: '1.0.1' };
|
|
155
|
+
const sharedDependencies = { react: 'SharedDependencies.React' };
|
|
156
|
+
|
|
157
|
+
beforeEach(() => {
|
|
158
|
+
mockFS(webComponentFS(startupVersion));
|
|
159
|
+
jest.mocked(getFolders).mockReturnValue({ source, destination });
|
|
160
|
+
jest.mocked(getPackageData).mockReturnValue({ dependencies });
|
|
161
|
+
jest.mocked(loadSharedDependencies).mockReturnValue(sharedDependencies);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('builds full and "embed" packages', async () => {
|
|
165
|
+
const options = { buildStat, name: expectPackageName() };
|
|
166
|
+
const overrides = { configuration: { mode: 'production' } };
|
|
167
|
+
|
|
168
|
+
await subject();
|
|
169
|
+
|
|
170
|
+
expect(createWebpackConfig).toHaveBeenCalledWith(overrides, options);
|
|
171
|
+
expect(createWebpackConfig).toHaveBeenCalledWith(overrides, {
|
|
172
|
+
...options,
|
|
173
|
+
embed: true,
|
|
174
|
+
});
|
|
175
|
+
expect(compiler.run).toHaveBeenCalledTimes(2);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('generates package metadata', async () => {
|
|
179
|
+
await subject();
|
|
180
|
+
|
|
181
|
+
expect(generatedMetadata(destination)).toEqual({
|
|
182
|
+
name: expectPackageName(),
|
|
183
|
+
bundledWith: { '@servicetitan/startup': startupVersion },
|
|
184
|
+
dependencies,
|
|
185
|
+
sharedDependencies,
|
|
186
|
+
entrypoints: {},
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('when package has entrypoints', () => {
|
|
191
|
+
const full = { css: 'foo', js: 'bar ' };
|
|
192
|
+
const light = { css: 'baz', js: 'qux' };
|
|
193
|
+
|
|
194
|
+
beforeEach(() => {
|
|
195
|
+
mockFS({
|
|
196
|
+
...webComponentFS(startupVersion),
|
|
197
|
+
dist: {
|
|
198
|
+
bundle: {
|
|
199
|
+
full: { 'entrypoints.json': JSON.stringify(full) },
|
|
200
|
+
light: { 'entrypoints.json': JSON.stringify(light) },
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('adds entrypoints to package metadata', async () => {
|
|
207
|
+
await subject();
|
|
208
|
+
|
|
209
|
+
expect(generatedMetadata(destination)).toEqual(
|
|
210
|
+
expect.objectContaining({ entrypoints: { full, light } })
|
|
211
|
+
);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
describe(`with ${webpackProdConfigFileName} config`, () => {
|
|
216
|
+
beforeEach(() => {
|
|
217
|
+
mockFS({ ...webComponentFS(startupVersion), [webpackProdConfigFileName]: '' });
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test('uses config', async () => {
|
|
221
|
+
const options = { buildStat, name: expectPackageName() };
|
|
222
|
+
const overrides = {
|
|
223
|
+
configuration: { mode: 'production', ...customProdConfig.configuration },
|
|
224
|
+
plugins: customProdConfig.plugins,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
await subject();
|
|
228
|
+
|
|
229
|
+
expect(createWebpackConfig).toHaveBeenCalledWith(overrides, options);
|
|
230
|
+
expect(createWebpackConfig).toHaveBeenCalledWith(overrides, {
|
|
231
|
+
...options,
|
|
232
|
+
embed: true,
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe(`${bundleWatch.name}`, () => {
|
|
240
|
+
const customDevConfig = {
|
|
241
|
+
devServer: { host: 'http://127.0.0.0', port: '9000' },
|
|
242
|
+
configuration: { foo: 'bar' },
|
|
243
|
+
plugins: { baz: 'qux' },
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
function stopWatching(callback?: Function) {
|
|
247
|
+
setTimeout(() => callback?.(new Error('stopped')), 0);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
beforeEach(() => {
|
|
251
|
+
mockFS(packageFS());
|
|
252
|
+
// Allows config file to be loaded with require(...)
|
|
253
|
+
jest.doMock(path.resolve(webpackDevConfigFileName), () => customDevConfig, {
|
|
254
|
+
virtual: true,
|
|
255
|
+
});
|
|
256
|
+
jest.mocked(WebpackDevServer).mockImplementation((): any => ({
|
|
257
|
+
listen: jest.fn((_0: number, _1: string, callback?: (error?: Error) => void) =>
|
|
258
|
+
stopWatching(callback)
|
|
259
|
+
),
|
|
260
|
+
close: compiler.close,
|
|
261
|
+
}));
|
|
262
|
+
compiler.watch = jest.fn((_, callback): any => {
|
|
263
|
+
stopWatching(callback);
|
|
264
|
+
return { compiler, close: compiler.close };
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const subject = async () => {
|
|
269
|
+
await expect(bundleWatch()).rejects.toThrowError('stopped');
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
test('runs webpack development server', async () => {
|
|
273
|
+
await subject();
|
|
274
|
+
|
|
275
|
+
expect(createWebpackConfig).toHaveBeenCalledWith(
|
|
276
|
+
{ configuration: { mode: 'development' } },
|
|
277
|
+
{ name: expectPackageName() }
|
|
278
|
+
);
|
|
279
|
+
expect(webpack).toHaveBeenCalledWith(createWebpackResult);
|
|
280
|
+
expect(WebpackDevServer).toHaveBeenCalledWith(compiler, {});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
describe(`with ${webpackDevConfigFileName} config`, () => {
|
|
284
|
+
beforeEach(() => mockFS({ ...packageFS(), [webpackDevConfigFileName]: '' }));
|
|
285
|
+
|
|
286
|
+
test('uses config', async () => {
|
|
287
|
+
const { devServer, ...config } = customDevConfig;
|
|
288
|
+
|
|
289
|
+
await subject();
|
|
290
|
+
|
|
291
|
+
expect(webpack).toHaveBeenCalledWith(config);
|
|
292
|
+
expect(WebpackDevServer).toHaveBeenCalledWith(compiler, devServer);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
describe('when package is a web component', () => {
|
|
297
|
+
const source = 'src';
|
|
298
|
+
const destination = 'dist';
|
|
299
|
+
const startupVersion = '1.0.0';
|
|
300
|
+
const dependencies = { foo: '1.0.1' };
|
|
301
|
+
const sharedDependencies = { react: 'SharedDependencies.React' };
|
|
302
|
+
|
|
303
|
+
beforeEach(() => {
|
|
304
|
+
mockFS(webComponentFS(startupVersion));
|
|
305
|
+
jest.mocked(getFolders).mockReturnValue({ source, destination });
|
|
306
|
+
jest.mocked(getPackageData).mockReturnValue({ dependencies });
|
|
307
|
+
jest.mocked(loadSharedDependencies).mockReturnValue(sharedDependencies);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test('generates package metadata', async () => {
|
|
311
|
+
await subject();
|
|
312
|
+
|
|
313
|
+
expect(generatedMetadata(destination)).toEqual({
|
|
314
|
+
name: expectPackageName(),
|
|
315
|
+
bundledWith: { '@servicetitan/startup': startupVersion },
|
|
316
|
+
dependencies,
|
|
317
|
+
sharedDependencies,
|
|
318
|
+
entrypoints: {},
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test('runs webpack and webpack development server', async () => {
|
|
323
|
+
const options = { name: expectPackageName() };
|
|
324
|
+
const overrides = { configuration: { mode: 'development' } };
|
|
325
|
+
|
|
326
|
+
await subject();
|
|
327
|
+
|
|
328
|
+
expect(createWebpackConfig).toHaveBeenCalledWith(overrides, options);
|
|
329
|
+
expect(createWebpackConfig).toHaveBeenCalledWith(overrides, {
|
|
330
|
+
...options,
|
|
331
|
+
embed: true,
|
|
332
|
+
});
|
|
333
|
+
expect(webpack).toHaveBeenCalledWith(createWebpackResult);
|
|
334
|
+
expect(compiler.watch).toHaveBeenCalled();
|
|
335
|
+
expect(WebpackDevServer).toHaveBeenCalledWith(compiler, {});
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
describe(`with ${webpackDevConfigFileName} config`, () => {
|
|
339
|
+
beforeEach(() => {
|
|
340
|
+
mockFS({ ...webComponentFS(startupVersion), [webpackDevConfigFileName]: '' });
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test('uses config', async () => {
|
|
344
|
+
const options = { name: expectPackageName() };
|
|
345
|
+
const overrides = {
|
|
346
|
+
configuration: { mode: 'development', ...customDevConfig.configuration },
|
|
347
|
+
plugins: customDevConfig.plugins,
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
await subject();
|
|
351
|
+
|
|
352
|
+
expect(createWebpackConfig).toHaveBeenCalledWith(overrides, options);
|
|
353
|
+
expect(createWebpackConfig).toHaveBeenCalledWith(overrides, {
|
|
354
|
+
...options,
|
|
355
|
+
embed: true,
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { runCommandOutput } from '../cli-os';
|
|
2
|
+
|
|
3
|
+
import { gitGetBranch, gitGetCommitHash } from '../cli-git';
|
|
4
|
+
|
|
5
|
+
jest.mock('../cli-os', () => ({ runCommandOutput: jest.fn() }));
|
|
6
|
+
|
|
7
|
+
describe('[startup] Cli Utils (Git)', () => {
|
|
8
|
+
beforeEach(() => jest.resetAllMocks());
|
|
9
|
+
|
|
10
|
+
function itRunsCommand(subject: Function, command: string) {
|
|
11
|
+
test(`runs "${command}"`, () => {
|
|
12
|
+
const result = ` ${command} result `;
|
|
13
|
+
jest.mocked(runCommandOutput).mockReturnValue(result);
|
|
14
|
+
|
|
15
|
+
expect(subject()).toBe(result.trim());
|
|
16
|
+
|
|
17
|
+
expect(runCommandOutput).toHaveBeenCalledWith(command);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe(`${gitGetBranch.name}`, () => {
|
|
22
|
+
itRunsCommand(gitGetBranch, 'git rev-parse --abbrev-ref HEAD');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe(`${gitGetCommitHash.name}`, () => {
|
|
26
|
+
itRunsCommand(gitGetCommitHash, 'git rev-parse --short HEAD');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { runCommand, runCommandOutput } from '../cli-os';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
npmGetPackageVersionDates,
|
|
5
|
+
npmGetPackageVersions,
|
|
6
|
+
npmPackageSet,
|
|
7
|
+
npmPublish,
|
|
8
|
+
npmPublishDry,
|
|
9
|
+
npmUnpublish,
|
|
10
|
+
} from '../cli-npm';
|
|
11
|
+
|
|
12
|
+
jest.mock('../cli-os', () => ({
|
|
13
|
+
runCommand: jest.fn().mockResolvedValue(undefined),
|
|
14
|
+
runCommandOutput: jest.fn(),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
describe('[startup] Cli Utils (Npm)', () => {
|
|
18
|
+
beforeEach(() => jest.resetAllMocks());
|
|
19
|
+
|
|
20
|
+
function getRunCommandArgs(fn: typeof runCommand | typeof runCommandOutput) {
|
|
21
|
+
const [command, options] = jest.mocked(fn).mock.calls[0];
|
|
22
|
+
return [
|
|
23
|
+
(Array.isArray(command) ? command : [command]).filter(str => !!str).join(' '),
|
|
24
|
+
options,
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function itRunsCommand(subject: Function, command: string, options?: any) {
|
|
29
|
+
test(`runs "${command}"`, async () => {
|
|
30
|
+
await subject();
|
|
31
|
+
|
|
32
|
+
const [actualCommand, actualOptions] = getRunCommandArgs(runCommand);
|
|
33
|
+
|
|
34
|
+
expect(actualCommand).toBe(command);
|
|
35
|
+
expect(actualOptions).toEqual(options);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function itRunsCommandOutput(subject: Function, command: string, options?: any) {
|
|
40
|
+
test(`runs "${command}"`, () => {
|
|
41
|
+
subject();
|
|
42
|
+
|
|
43
|
+
const [actualCommand, actualOptions] = getRunCommandArgs(runCommandOutput);
|
|
44
|
+
|
|
45
|
+
expect(actualCommand).toBe(command);
|
|
46
|
+
expect(actualOptions).toEqual(options);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe(`${npmGetPackageVersions.name}`, () => {
|
|
51
|
+
let args: Parameters<typeof npmGetPackageVersions>;
|
|
52
|
+
|
|
53
|
+
beforeEach(() => (args = ['{registry}', '{packageName}']));
|
|
54
|
+
|
|
55
|
+
const subject = () => npmGetPackageVersions(...args);
|
|
56
|
+
|
|
57
|
+
itRunsCommandOutput(
|
|
58
|
+
subject,
|
|
59
|
+
'npm show {packageName} versions --registry {registry} --json',
|
|
60
|
+
{ timeout: 10000 }
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
test('returns parsed result', () => {
|
|
64
|
+
const result = [{ foo: 'bar' }];
|
|
65
|
+
|
|
66
|
+
jest.mocked(runCommandOutput).mockReturnValue(JSON.stringify(result));
|
|
67
|
+
|
|
68
|
+
expect(subject()).toEqual(result);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('when an error occurs', () => {
|
|
72
|
+
beforeEach(() => {
|
|
73
|
+
jest.mocked(runCommandOutput).mockImplementation(() => {
|
|
74
|
+
throw new Error('Oops!');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('returns []', () => {
|
|
79
|
+
expect(subject()).toEqual([]);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe(`${npmGetPackageVersionDates.name}`, () => {
|
|
85
|
+
let args: Parameters<typeof npmGetPackageVersionDates>;
|
|
86
|
+
|
|
87
|
+
beforeEach(() => (args = ['{registry}', '{packageName}']));
|
|
88
|
+
|
|
89
|
+
const subject = () => npmGetPackageVersionDates(...args);
|
|
90
|
+
|
|
91
|
+
itRunsCommandOutput(subject, 'npm view {packageName} time --registry {registry} --json', {
|
|
92
|
+
timeout: 10000,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('converts results to Array<[string, Date]>', () => {
|
|
96
|
+
function dayAgo(count: number) {
|
|
97
|
+
const date = new Date();
|
|
98
|
+
date.setDate(-count);
|
|
99
|
+
return date;
|
|
100
|
+
}
|
|
101
|
+
const result = { '1.0.1': dayAgo(3), '2.1.0': dayAgo(2), '3.0.0': dayAgo(1) };
|
|
102
|
+
|
|
103
|
+
jest.mocked(runCommandOutput).mockReturnValue(JSON.stringify(result));
|
|
104
|
+
|
|
105
|
+
expect(subject()).toEqual(
|
|
106
|
+
Object.entries(result).map(([version, date]) => [version, date])
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('when an error occurs', () => {
|
|
111
|
+
beforeEach(() => {
|
|
112
|
+
jest.mocked(runCommandOutput).mockImplementation(() => {
|
|
113
|
+
throw new Error('Oops!');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('returns []', () => {
|
|
118
|
+
expect(subject()).toEqual([]);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe(`${npmPackageSet.name}`, () => {
|
|
124
|
+
let args: Parameters<typeof npmPackageSet>;
|
|
125
|
+
|
|
126
|
+
beforeEach(() => (args = ['{key}', '{value}']));
|
|
127
|
+
|
|
128
|
+
const subject = () => npmPackageSet(...args);
|
|
129
|
+
|
|
130
|
+
itRunsCommand(subject, 'npm pkg set {key}={value}');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe(`${npmPublish.name}`, () => {
|
|
134
|
+
let args: Parameters<typeof npmPublish>;
|
|
135
|
+
|
|
136
|
+
beforeEach(() => (args = []));
|
|
137
|
+
|
|
138
|
+
const subject = async () => npmPublish(...args);
|
|
139
|
+
|
|
140
|
+
itRunsCommand(subject, 'npm publish');
|
|
141
|
+
|
|
142
|
+
describe('with a tag', () => {
|
|
143
|
+
beforeEach(() => args.push('{tag}'));
|
|
144
|
+
|
|
145
|
+
itRunsCommand(subject, 'npm publish --tag {tag}');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe(`${npmPublishDry.name}`, () => {
|
|
150
|
+
itRunsCommand(npmPublishDry, 'npm publish --dry-run');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe(`${npmUnpublish.name}`, () => {
|
|
154
|
+
let args: Parameters<typeof npmUnpublish>;
|
|
155
|
+
|
|
156
|
+
beforeEach(() => (args = ['{registry}', '{packageName}', '{packageVersion}']));
|
|
157
|
+
|
|
158
|
+
const subject = () => npmUnpublish(...args);
|
|
159
|
+
|
|
160
|
+
itRunsCommand(
|
|
161
|
+
subject,
|
|
162
|
+
'npm unpublish {packageName}@{packageVersion} --registry {registry}',
|
|
163
|
+
{ timeout: 10000 }
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { execSync, spawn } from 'child_process';
|
|
2
|
+
|
|
3
|
+
import { runCommand, runCommandOutput } from '../cli-os';
|
|
4
|
+
|
|
5
|
+
jest.mock('child_process', () => ({ execSync: jest.fn(), spawn: jest.fn() }));
|
|
6
|
+
jest.mock('../../../utils', () => ({ log: { info: jest.fn() } })); // suppress log output
|
|
7
|
+
|
|
8
|
+
describe('[startup] Cli Utils (OS)', () => {
|
|
9
|
+
describe(`${runCommand.name}`, () => {
|
|
10
|
+
let exitCode: number;
|
|
11
|
+
let childProcess: ReturnType<typeof spawn>;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
exitCode = 0;
|
|
15
|
+
childProcess = {
|
|
16
|
+
stderr: { pipe: jest.fn() },
|
|
17
|
+
stdout: { pipe: jest.fn() },
|
|
18
|
+
on: jest.fn((_event: string, callback: Function) => callback(exitCode)),
|
|
19
|
+
} as any;
|
|
20
|
+
jest.mocked(spawn).mockReturnValue(childProcess);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const subject = async (...args: Parameters<typeof runCommand>) => runCommand(...args);
|
|
24
|
+
|
|
25
|
+
test('runs command', () => {
|
|
26
|
+
expect(subject('foo bar')).resolves.toBe(undefined);
|
|
27
|
+
|
|
28
|
+
expect(spawn).toHaveBeenCalledWith('foo', ['bar'], undefined);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('runs command with options', async () => {
|
|
32
|
+
await subject('foo', { timeout: 10000 });
|
|
33
|
+
|
|
34
|
+
expect(spawn).toHaveBeenCalledWith('foo', [], { timeout: 10000 });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('runs command array', async () => {
|
|
38
|
+
await subject(['foo', 'bar', '--baz']);
|
|
39
|
+
|
|
40
|
+
expect(spawn).toHaveBeenCalledWith('foo', ['bar', '--baz'], undefined);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("pipes stdout and stderr to parent's streams", async () => {
|
|
44
|
+
await subject('foo');
|
|
45
|
+
|
|
46
|
+
expect(childProcess.stdout?.pipe).toHaveBeenCalledWith(process.stdout);
|
|
47
|
+
expect(childProcess.stderr?.pipe).toHaveBeenCalledWith(process.stderr);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('when child process exits with non-zero code', () => {
|
|
51
|
+
beforeEach(() => (exitCode = 2));
|
|
52
|
+
|
|
53
|
+
test('rejects promise', () => {
|
|
54
|
+
expect(subject('foo')).rejects.toBeUndefined();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
['', [], ['']].forEach(command =>
|
|
59
|
+
test(`rejects promise when called with ${JSON.stringify(command)}`, () => {
|
|
60
|
+
expect(subject(command)).rejects.toBeUndefined();
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe(`${runCommandOutput.name}`, () => {
|
|
66
|
+
beforeEach(() => jest.mocked(execSync).mockReturnValue(''));
|
|
67
|
+
|
|
68
|
+
const subject = (...args: Parameters<typeof runCommandOutput>) => runCommandOutput(...args);
|
|
69
|
+
|
|
70
|
+
test('runs command and returns result', () => {
|
|
71
|
+
const result = 'baz qux';
|
|
72
|
+
jest.mocked(execSync).mockReturnValue(result);
|
|
73
|
+
|
|
74
|
+
expect(subject('foo bar')).toBe(result);
|
|
75
|
+
|
|
76
|
+
expect(execSync).toHaveBeenCalledWith('foo bar', undefined);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('runs command with options', () => {
|
|
80
|
+
subject('foo bar', { timeout: 10000 });
|
|
81
|
+
|
|
82
|
+
expect(execSync).toHaveBeenCalledWith('foo bar', { timeout: 10000 });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('runs command array', () => {
|
|
86
|
+
subject(['foo', 'bar']);
|
|
87
|
+
|
|
88
|
+
expect(execSync).toHaveBeenCalledWith('foo bar', undefined);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('ignores false and "" array values', () => {
|
|
92
|
+
subject(['', 'foo', false, '', 'bar', false]);
|
|
93
|
+
|
|
94
|
+
expect(execSync).toHaveBeenCalledWith('foo bar', undefined);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
['', [], ['']].forEach(command =>
|
|
98
|
+
test(`throws error when called with ${JSON.stringify(command)}`, () => {
|
|
99
|
+
expect(() => subject(command)).toThrowError();
|
|
100
|
+
})
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
});
|