@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,114 @@
|
|
|
1
|
+
const lernaExec = require('@lerna/exec');
|
|
2
|
+
import { Lint } from '../lint';
|
|
3
|
+
import { eslint } from '../../utils';
|
|
4
|
+
import { getPackages } from '../../../utils';
|
|
5
|
+
|
|
6
|
+
jest.mock('@lerna/exec', () => jest.fn());
|
|
7
|
+
jest.mock('execa', () => jest.fn());
|
|
8
|
+
jest.mock('stylelint', () => ({ lint: jest.fn(() => ({ output: '' })) }));
|
|
9
|
+
|
|
10
|
+
jest.mock('../../utils', () => ({ eslint: jest.fn() }));
|
|
11
|
+
jest.mock('../../../utils', () => ({
|
|
12
|
+
log: { info: jest.fn() },
|
|
13
|
+
getConfiguration: jest.fn(() => ({})),
|
|
14
|
+
getDestinationFolders: jest.fn(() => []),
|
|
15
|
+
getPackages: jest.fn(() => []),
|
|
16
|
+
getStylelintConfiguration: jest.fn(() => ({})),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
describe(`[startup] ${Lint.name}`, () => {
|
|
20
|
+
const packages = ['package-a', 'package-b'];
|
|
21
|
+
let args: ConstructorParameters<typeof Lint>[0];
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
jest.clearAllMocks();
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
26
|
+
args = { _: [] };
|
|
27
|
+
(getPackages as jest.Mock).mockReturnValue(packages.map(name => ({ name })));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const subject = async () => new Lint(args).execute();
|
|
31
|
+
|
|
32
|
+
test('runs styles-check', async () => {
|
|
33
|
+
await subject();
|
|
34
|
+
|
|
35
|
+
expect(lernaExec).toHaveBeenCalledWith(
|
|
36
|
+
expect.objectContaining({
|
|
37
|
+
cmd: 'startup styles-check',
|
|
38
|
+
scope: packages,
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('runs eslint directly', async () => {
|
|
44
|
+
await subject();
|
|
45
|
+
|
|
46
|
+
expect(eslint).toHaveBeenCalledWith({ paths: [] });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('when instructed to fix issues', () => {
|
|
50
|
+
beforeEach(() => (args.fix = true));
|
|
51
|
+
|
|
52
|
+
test('instructs eslint to fix issues', async () => {
|
|
53
|
+
await subject();
|
|
54
|
+
|
|
55
|
+
expect(eslint).toHaveBeenCalledWith({ fix: true, paths: [] });
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
function itRunsLernaExec() {
|
|
60
|
+
test('runs @lerna/exec instead of eslint', async () => {
|
|
61
|
+
await subject();
|
|
62
|
+
|
|
63
|
+
expect(eslint).not.toHaveBeenCalled();
|
|
64
|
+
expect(lernaExec).toHaveBeenCalledWith(
|
|
65
|
+
expect.objectContaining({
|
|
66
|
+
cmd: 'startup eslint',
|
|
67
|
+
scope: packages,
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
describe('when instructed to isolate packages', () => {
|
|
74
|
+
beforeEach(() => (args.isolated = true));
|
|
75
|
+
|
|
76
|
+
itRunsLernaExec();
|
|
77
|
+
|
|
78
|
+
describe('when instructed to fix errors', () => {
|
|
79
|
+
beforeEach(() => (args.fix = true));
|
|
80
|
+
|
|
81
|
+
test('adds instruction to fix errors', async () => {
|
|
82
|
+
await subject();
|
|
83
|
+
|
|
84
|
+
expect(lernaExec).toHaveBeenCalledWith(
|
|
85
|
+
expect.objectContaining({ args: ['--fix'] })
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('with a list of paths to lint', () => {
|
|
91
|
+
beforeEach(() => (args._ = ['path/a', 'path/b']));
|
|
92
|
+
|
|
93
|
+
test('runs eslint directly', async () => {
|
|
94
|
+
await subject();
|
|
95
|
+
|
|
96
|
+
expect(eslint).toHaveBeenCalledWith({
|
|
97
|
+
paths: args._,
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('with a list of packages to lint', () => {
|
|
104
|
+
beforeEach(() => (args.scope = ['package-a']));
|
|
105
|
+
|
|
106
|
+
itRunsLernaExec();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('with a list of packages to ignore', () => {
|
|
110
|
+
beforeEach(() => (args.ignore = ['package-a']));
|
|
111
|
+
|
|
112
|
+
itRunsLernaExec();
|
|
113
|
+
});
|
|
114
|
+
});
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import mockFS from 'mock-fs';
|
|
2
|
+
import { isWebComponent, log } from '../../../utils';
|
|
3
|
+
import { npmGetPackageVersionDates, npmUnpublish } from '../../utils/cli-npm';
|
|
4
|
+
|
|
5
|
+
import { MFEPackageClean } from '../mfe-publish';
|
|
6
|
+
|
|
7
|
+
jest.mock('../../../utils', () => ({
|
|
8
|
+
...jest.requireActual('../../../utils'),
|
|
9
|
+
isWebComponent: jest.fn(),
|
|
10
|
+
log: { error: jest.fn(), info: jest.fn() },
|
|
11
|
+
}));
|
|
12
|
+
jest.mock('../../utils/cli-npm', () => ({
|
|
13
|
+
npmGetPackageVersionDates: jest.fn(),
|
|
14
|
+
npmUnpublish: jest.fn(),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const DEFAULT_VERSIONS_TO_KEEP = 5;
|
|
18
|
+
|
|
19
|
+
describe(`[startup] ${MFEPackageClean.name}`, () => {
|
|
20
|
+
const registry = 'https://verdaccio.servicetitan.com';
|
|
21
|
+
const packageName = '@servicetitan/foo';
|
|
22
|
+
let args: ConstructorParameters<typeof MFEPackageClean>[0];
|
|
23
|
+
let versions: Record<string, Date>;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
args = {};
|
|
27
|
+
versions = Object.fromEntries(
|
|
28
|
+
Array.from({ length: 10 }).map((_, index) => [
|
|
29
|
+
`0.0.0-master.${(0xff000000 + index).toString(16)}`,
|
|
30
|
+
dayAgo(index + 1), // tests assume versions are in reverse chronological order
|
|
31
|
+
])
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
jest.resetAllMocks();
|
|
35
|
+
jest.mocked(isWebComponent).mockReturnValue(true);
|
|
36
|
+
jest.mocked(npmGetPackageVersionDates).mockImplementation(() => Object.entries(versions));
|
|
37
|
+
mockFS({ 'package.json': JSON.stringify({ name: packageName }) });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(() => mockFS.restore());
|
|
41
|
+
|
|
42
|
+
const subject = async () => new MFEPackageClean(args).execute();
|
|
43
|
+
|
|
44
|
+
test('fetches package info from registry', async () => {
|
|
45
|
+
await subject();
|
|
46
|
+
|
|
47
|
+
expect(npmGetPackageVersionDates).toHaveBeenCalledWith(registry, packageName);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
function itLogsAndUnpublishedOldestPackages({ branch = 'master' }: { branch?: string } = {}) {
|
|
51
|
+
test('logs and unpublishes oldest packages', async () => {
|
|
52
|
+
const oldest = Object.entries(versions).slice(DEFAULT_VERSIONS_TO_KEEP);
|
|
53
|
+
|
|
54
|
+
await subject();
|
|
55
|
+
|
|
56
|
+
expect(log.info).toHaveBeenCalledWith(
|
|
57
|
+
'found versions for unpublish:',
|
|
58
|
+
JSON.stringify({ [branch]: oldest }, null, 4)
|
|
59
|
+
);
|
|
60
|
+
oldest.forEach(([version]) => {
|
|
61
|
+
expect(npmUnpublish).toHaveBeenCalledWith(registry, packageName, version);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
itLogsAndUnpublishedOldestPackages();
|
|
67
|
+
|
|
68
|
+
describe.each(['dev', 'develop', 'next'])('when branch is %s', branch => {
|
|
69
|
+
beforeEach(() => {
|
|
70
|
+
versions = transform(versions, (v: string) => v.replace('master', branch));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
itLogsAndUnpublishedOldestPackages({ branch });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('with master versions generated by nerdbank versioning', () => {
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
versions = transform(versions, (v: string, index: number) =>
|
|
79
|
+
v.replace(/master.*/, `${index}.0.0`)
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
itLogsAndUnpublishedOldestPackages();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('with branch versions generated by nerdbank versioning', () => {
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
versions = transform(versions, (v: string, index: number) =>
|
|
89
|
+
v.replace(/master.*/, `${index}.0.0-next-${index}`)
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
itLogsAndUnpublishedOldestPackages({ branch: 'next' });
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('with "count" argument', () => {
|
|
97
|
+
beforeEach(() => (args.count = Object.keys(versions).length - 1));
|
|
98
|
+
|
|
99
|
+
test('keeps the specified number of versions', async () => {
|
|
100
|
+
await subject();
|
|
101
|
+
|
|
102
|
+
expect(log.info).toHaveBeenCalledWith(
|
|
103
|
+
'found versions for unpublish:',
|
|
104
|
+
JSON.stringify({ master: Object.entries(versions).slice(-1) }, null, 4)
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
expect(npmUnpublish).toHaveBeenCalledTimes(1);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('when publish fails', () => {
|
|
112
|
+
beforeEach(() =>
|
|
113
|
+
jest.mocked(npmUnpublish).mockImplementationOnce(() => {
|
|
114
|
+
throw new Error('Oops!');
|
|
115
|
+
})
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
test('logs error', async () => {
|
|
119
|
+
await subject();
|
|
120
|
+
|
|
121
|
+
const firstUnpublishVersion = Object.entries(versions)[DEFAULT_VERSIONS_TO_KEEP][0];
|
|
122
|
+
expect(log.error).toHaveBeenCalledWith(
|
|
123
|
+
`error while removing ${packageName} version ${firstUnpublishVersion}`
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
function itDoesNotUnpublish() {
|
|
129
|
+
test('does not unpublish', async () => {
|
|
130
|
+
await subject();
|
|
131
|
+
|
|
132
|
+
expect(log.info).toHaveBeenCalledWith('found versions for unpublish:', '{}');
|
|
133
|
+
expect(npmUnpublish).not.toHaveBeenCalled();
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
describe('when version does not start with "0.0.0-"', () => {
|
|
138
|
+
beforeEach(() => {
|
|
139
|
+
versions = transform(versions, (v: string) => v.replace('0.0.0-', ''));
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
itDoesNotUnpublish();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('when branch is not recognized', () => {
|
|
146
|
+
beforeEach(() => {
|
|
147
|
+
versions = transform(versions, (v: string) => v.replace('master', 'foo'));
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
itDoesNotUnpublish();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('when version is not recognized', () => {
|
|
154
|
+
const unknownVersions = ['0.0.0-master-1^', '0.0.0-master-2^'];
|
|
155
|
+
|
|
156
|
+
beforeEach(() => unknownVersions.forEach(version => (versions[version] = new Date())));
|
|
157
|
+
|
|
158
|
+
test('logs unrecognized versions', async () => {
|
|
159
|
+
await subject();
|
|
160
|
+
|
|
161
|
+
expect(log.info).toHaveBeenCalledWith('unknown versions:', unknownVersions.join());
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('when package is not a web component', () => {
|
|
166
|
+
beforeEach(() => jest.mocked(isWebComponent).mockReturnValue(false));
|
|
167
|
+
|
|
168
|
+
test('throws error', async () => {
|
|
169
|
+
jest.spyOn(process.stdout, 'write').mockImplementation(jest.fn()); // suppress error output
|
|
170
|
+
expect.assertions(1);
|
|
171
|
+
try {
|
|
172
|
+
await subject();
|
|
173
|
+
} catch (error: any) {
|
|
174
|
+
expect(error.message).toBe('only web-components can be cleaned');
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
function dayAgo(count: number) {
|
|
181
|
+
const date = new Date();
|
|
182
|
+
date.setDate(-count);
|
|
183
|
+
return date;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function transform(
|
|
187
|
+
versions: Record<string, Date>,
|
|
188
|
+
callback: (version: string, index: number) => string
|
|
189
|
+
) {
|
|
190
|
+
return Object.fromEntries(
|
|
191
|
+
Object.entries(versions).map(([version, date], index) => [callback(version, index), date])
|
|
192
|
+
);
|
|
193
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import mockFS from 'mock-fs';
|
|
2
|
+
import { isWebComponent, log } from '../../../utils';
|
|
3
|
+
import { gitGetBranch, gitGetCommitHash } from '../../utils/cli-git';
|
|
4
|
+
import {
|
|
5
|
+
npmGetPackageVersions,
|
|
6
|
+
npmPackageSet,
|
|
7
|
+
npmPublish,
|
|
8
|
+
npmPublishDry,
|
|
9
|
+
} from '../../utils/cli-npm';
|
|
10
|
+
import { MFEPackagePublish } from '../mfe-publish';
|
|
11
|
+
|
|
12
|
+
jest.mock('../../../utils', () => ({
|
|
13
|
+
...jest.requireActual('../../../utils'),
|
|
14
|
+
isWebComponent: jest.fn(),
|
|
15
|
+
log: { info: jest.fn() },
|
|
16
|
+
}));
|
|
17
|
+
jest.mock('../../utils/cli-git', () => ({
|
|
18
|
+
gitGetBranch: jest.fn(),
|
|
19
|
+
gitGetCommitHash: jest.fn(),
|
|
20
|
+
}));
|
|
21
|
+
jest.mock('../../utils/cli-npm', () => ({
|
|
22
|
+
npmGetPackageVersions: jest.fn(),
|
|
23
|
+
npmPackageSet: jest.fn(),
|
|
24
|
+
npmPublish: jest.fn(),
|
|
25
|
+
npmPublishDry: jest.fn(),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
describe(`[startup] ${MFEPackagePublish.name}`, () => {
|
|
29
|
+
const branch = 'master';
|
|
30
|
+
const commitHash = '6c461f1a';
|
|
31
|
+
const packageName = '@servicetitan/foo';
|
|
32
|
+
let args: ConstructorParameters<typeof MFEPackagePublish>[0];
|
|
33
|
+
|
|
34
|
+
const defaultPackageVersion = () => `0.0.0-${branch}.${commitHash}`;
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
args = {};
|
|
38
|
+
jest.resetAllMocks();
|
|
39
|
+
jest.mocked(isWebComponent).mockReturnValue(true);
|
|
40
|
+
jest.mocked(gitGetBranch).mockReturnValue(branch);
|
|
41
|
+
jest.mocked(gitGetCommitHash).mockReturnValue(commitHash);
|
|
42
|
+
jest.mocked(npmGetPackageVersions).mockReturnValue([]);
|
|
43
|
+
mockFS({ 'package.json': JSON.stringify({ name: packageName, files: [] }) });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterEach(() => mockFS.restore());
|
|
47
|
+
|
|
48
|
+
const subject = async () => new MFEPackagePublish(args).execute();
|
|
49
|
+
|
|
50
|
+
test('sets package version', async () => {
|
|
51
|
+
await subject();
|
|
52
|
+
|
|
53
|
+
expect(npmPackageSet).toHaveBeenCalledWith('version', defaultPackageVersion());
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('sets package registry', async () => {
|
|
57
|
+
await subject();
|
|
58
|
+
|
|
59
|
+
expect(npmPackageSet).toHaveBeenCalledWith(
|
|
60
|
+
'publishConfig.registry',
|
|
61
|
+
'https://verdaccio.servicetitan.com'
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('publishes package and logs message', async () => {
|
|
66
|
+
await subject();
|
|
67
|
+
|
|
68
|
+
expect(npmPublish).toHaveBeenCalledWith('prod');
|
|
69
|
+
expect(log.info).toHaveBeenCalledWith(
|
|
70
|
+
expect.stringContaining(
|
|
71
|
+
`packages ${packageName} is published with version ${defaultPackageVersion()}`
|
|
72
|
+
)
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
Object.entries({ dev: ['dev', 'develop'], next: ['next'], prod: ['master'] }).forEach(
|
|
77
|
+
([tag, branches]) => {
|
|
78
|
+
describe.each(branches)('when branch is %s', branch => {
|
|
79
|
+
beforeEach(() => (args.branch = branch));
|
|
80
|
+
|
|
81
|
+
test(`uses tag ${tag}`, async () => {
|
|
82
|
+
await subject();
|
|
83
|
+
|
|
84
|
+
expect(npmPublish).toHaveBeenCalledWith(tag);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
describe('with "tag" argument', () => {
|
|
91
|
+
beforeEach(() => (args.tag = 'foo'));
|
|
92
|
+
|
|
93
|
+
test('uses specified tag', async () => {
|
|
94
|
+
await subject();
|
|
95
|
+
|
|
96
|
+
expect(npmPublish).toHaveBeenCalledWith(args.tag);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe('when "tag" is false', () => {
|
|
101
|
+
beforeEach(() => (args.tag = false));
|
|
102
|
+
|
|
103
|
+
test('omits the tag', async () => {
|
|
104
|
+
await subject();
|
|
105
|
+
|
|
106
|
+
expect(npmPublish).toHaveBeenCalledWith('');
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('with "build" argument', () => {
|
|
111
|
+
beforeEach(() => (args.build = '42'));
|
|
112
|
+
|
|
113
|
+
test('uses build in package version', async () => {
|
|
114
|
+
await subject();
|
|
115
|
+
|
|
116
|
+
expect(npmPackageSet).toHaveBeenCalledWith('version', `0.0.0-${args.build}`);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('with "dry" argument', () => {
|
|
121
|
+
beforeEach(() => (args.dry = true));
|
|
122
|
+
|
|
123
|
+
test('does dry run and logs message', async () => {
|
|
124
|
+
await subject();
|
|
125
|
+
|
|
126
|
+
expect(npmPublishDry).toHaveBeenCalled();
|
|
127
|
+
expect(log.info).toHaveBeenCalledWith(
|
|
128
|
+
expect.stringContaining(
|
|
129
|
+
`packages ${packageName} is published (dry) with version ${defaultPackageVersion()}`
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('when package has no files', () => {
|
|
136
|
+
beforeEach(() => mockFS({ 'package.json': JSON.stringify({ name: packageName }) }));
|
|
137
|
+
|
|
138
|
+
test('sets package files to "package.json"', async () => {
|
|
139
|
+
await subject();
|
|
140
|
+
|
|
141
|
+
expect(npmPackageSet).toHaveBeenCalledWith('files[0]', 'dist');
|
|
142
|
+
expect(npmPackageSet).toHaveBeenCalledWith('files[1]', 'package.json');
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
function itLogsMessageAndSkipsPublishing(message: RegExp) {
|
|
147
|
+
test('logs message and skips publishing', async () => {
|
|
148
|
+
await subject();
|
|
149
|
+
|
|
150
|
+
expect(log.info).toHaveBeenCalledWith(expect.stringMatching(message));
|
|
151
|
+
expect(npmPublish).not.toHaveBeenCalled();
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
describe('when version is already published', () => {
|
|
156
|
+
beforeEach(() =>
|
|
157
|
+
jest.mocked(npmGetPackageVersions).mockReturnValue([defaultPackageVersion()])
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
itLogsMessageAndSkipsPublishing(/is already published/);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('with unrecognized branch', () => {
|
|
164
|
+
beforeEach(() => jest.mocked(gitGetBranch).mockReturnValue('foo'));
|
|
165
|
+
|
|
166
|
+
itLogsMessageAndSkipsPublishing(/branch is not configured for publishing/);
|
|
167
|
+
|
|
168
|
+
describe('with "force" argument"', () => {
|
|
169
|
+
beforeEach(() => (args.force = true));
|
|
170
|
+
|
|
171
|
+
test('publishes the package', async () => {
|
|
172
|
+
await subject();
|
|
173
|
+
|
|
174
|
+
expect(npmPublish).toHaveBeenCalledWith('');
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe('when package is not web component', () => {
|
|
180
|
+
beforeEach(() => jest.mocked(isWebComponent).mockReturnValue(false));
|
|
181
|
+
|
|
182
|
+
test('throws error', async () => {
|
|
183
|
+
jest.spyOn(process.stdout, 'write').mockImplementation(jest.fn()); // suppress error output
|
|
184
|
+
expect.assertions(1);
|
|
185
|
+
try {
|
|
186
|
+
await subject();
|
|
187
|
+
} catch (error: any) {
|
|
188
|
+
expect(error.message).toMatch(/only web-components can be published/);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
const exec = require('@lerna/exec');
|
|
2
|
+
import mockFS from 'mock-fs';
|
|
3
|
+
import { Package, PackageType, getPackages } from '../../../utils';
|
|
4
|
+
import { createPackage } from '../../__mocks__';
|
|
5
|
+
|
|
6
|
+
import { MFEPublish } from '../mfe-publish';
|
|
7
|
+
|
|
8
|
+
jest.mock('@lerna/exec', () => jest.fn());
|
|
9
|
+
jest.mock('../../../utils', () => ({
|
|
10
|
+
...jest.requireActual('../../../utils'),
|
|
11
|
+
getPackages: jest.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
describe(`[startup] ${MFEPublish.name}`, () => {
|
|
15
|
+
let args: ConstructorParameters<typeof MFEPublish>[0];
|
|
16
|
+
let packages: Package[];
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
args = {};
|
|
20
|
+
packages = [];
|
|
21
|
+
jest.resetAllMocks();
|
|
22
|
+
jest.mocked(getPackages).mockReturnValue(packages);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => mockFS.restore());
|
|
26
|
+
|
|
27
|
+
const subject = async () => new MFEPublish(args).execute();
|
|
28
|
+
|
|
29
|
+
function itReportsError() {
|
|
30
|
+
test('reports there are no packages to publish', async () => {
|
|
31
|
+
jest.spyOn(process.stdout, 'write').mockImplementation(jest.fn()); // suppress error output
|
|
32
|
+
expect.assertions(1);
|
|
33
|
+
try {
|
|
34
|
+
await subject();
|
|
35
|
+
} catch (error: any) {
|
|
36
|
+
expect(error.message).toMatch(/no packages found/);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('with TSC package', () => {
|
|
42
|
+
beforeEach(() => packages.push(createPackage({ type: PackageType.TSC })));
|
|
43
|
+
|
|
44
|
+
itReportsError();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('with Webpack package', () => {
|
|
48
|
+
function mockPackageJson(
|
|
49
|
+
packages: Package[],
|
|
50
|
+
json: Record<string, any> = { cli: { 'web-component': true } }
|
|
51
|
+
) {
|
|
52
|
+
mockFS(
|
|
53
|
+
packages.reduce(
|
|
54
|
+
(result, { location }) => ({
|
|
55
|
+
...result,
|
|
56
|
+
[`${location}/package.json`]: JSON.stringify(json),
|
|
57
|
+
}),
|
|
58
|
+
{}
|
|
59
|
+
)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
packages.push(createPackage({ type: PackageType.Webpack }));
|
|
65
|
+
mockPackageJson(packages);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('publishes the package', async () => {
|
|
69
|
+
await subject();
|
|
70
|
+
|
|
71
|
+
expect(exec).toHaveBeenCalledWith({
|
|
72
|
+
'cmd': 'startup mfe-package-publish',
|
|
73
|
+
'scope': packages.map(({ name }) => name),
|
|
74
|
+
'stream': true,
|
|
75
|
+
'--': [],
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('when package is not a web component', () => {
|
|
80
|
+
beforeEach(() => mockPackageJson(packages, {}));
|
|
81
|
+
|
|
82
|
+
itReportsError();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe('with "{clean: true}"', () => {
|
|
86
|
+
beforeEach(() => (args.clean = true));
|
|
87
|
+
|
|
88
|
+
test('cleans the package', async () => {
|
|
89
|
+
await subject();
|
|
90
|
+
|
|
91
|
+
expect(exec).toHaveBeenCalledWith(
|
|
92
|
+
expect.objectContaining({ cmd: 'startup mfe-package-clean' })
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
type ArgumentName = keyof typeof args;
|
|
98
|
+
const testArgs: { name: ArgumentName; value: any; expected?: string }[] = [
|
|
99
|
+
{ name: 'build', value: true },
|
|
100
|
+
{ name: 'branch', value: 'foo-123' },
|
|
101
|
+
{ name: 'tag', value: 'foo' },
|
|
102
|
+
{ name: 'tag', value: false, expected: '--no-tag' },
|
|
103
|
+
{ name: 'dry', value: true, expected: '--dry' },
|
|
104
|
+
{ name: 'force', value: true, expected: '--force' },
|
|
105
|
+
{ name: 'count', value: 42 },
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
describe.each(testArgs)('with "{$name: $value}"', ({ name, value, expected }) => {
|
|
109
|
+
beforeEach(() => (args[name] = value));
|
|
110
|
+
|
|
111
|
+
test(`runs publish with "${expected ?? `--${name} ${value}`}"`, async () => {
|
|
112
|
+
await subject();
|
|
113
|
+
|
|
114
|
+
expect(exec).toHaveBeenCalledWith(
|
|
115
|
+
expect.objectContaining({ '--': [expected ?? `--${name} ${value}`] })
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('with multiple packages', () => {
|
|
121
|
+
beforeEach(() => {
|
|
122
|
+
packages.push(
|
|
123
|
+
createPackage({
|
|
124
|
+
name: '@servicetitan/bar',
|
|
125
|
+
type: PackageType.Webpack,
|
|
126
|
+
location: 'packages/bar',
|
|
127
|
+
}),
|
|
128
|
+
createPackage({
|
|
129
|
+
name: '@servicetitan/baz',
|
|
130
|
+
type: PackageType.Webpack,
|
|
131
|
+
location: 'packages/baz',
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
mockPackageJson(packages);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('publishes the packages', async () => {
|
|
138
|
+
await subject();
|
|
139
|
+
|
|
140
|
+
expect(exec).toHaveBeenCalledWith(
|
|
141
|
+
expect.objectContaining({ scope: packages.map(({ name }) => name) })
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('with "{scope: string}"', () => {
|
|
146
|
+
beforeEach(() => (args.scope = packages[0].name));
|
|
147
|
+
|
|
148
|
+
test('publishes specified packages', async () => {
|
|
149
|
+
await subject();
|
|
150
|
+
|
|
151
|
+
expect(exec).toHaveBeenCalledWith(
|
|
152
|
+
expect.objectContaining({ scope: [packages[0].name] })
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('with "{scope: Array<string>}"', () => {
|
|
158
|
+
beforeEach(() => (args.scope = packages.slice(1).map(({ name }) => name)));
|
|
159
|
+
|
|
160
|
+
test('publishes the specified packages', async () => {
|
|
161
|
+
await subject();
|
|
162
|
+
|
|
163
|
+
expect(exec).toHaveBeenCalledWith(
|
|
164
|
+
expect.objectContaining({
|
|
165
|
+
scope: packages.slice(1).map(({ name }) => name),
|
|
166
|
+
})
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
});
|