release-it 19.0.0-next.1 → 19.0.0-next.3

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/test/npm.js CHANGED
@@ -1,418 +1,360 @@
1
- import path from 'node:path';
2
- import { patchFs } from 'fs-monkey';
3
- import test from 'ava';
4
- import sinon from 'sinon';
5
- import { vol } from 'memfs';
1
+ import { join } from 'node:path';
2
+ import test, { describe } from 'node:test';
3
+ import assert from 'node:assert/strict';
4
+ import { writeFileSync } from 'node:fs';
6
5
  import npm from '../lib/plugin/npm/npm.js';
7
6
  import { factory, runTasks } from './util/index.js';
8
- import { getArgs } from './util/helpers.js';
9
-
10
- const mockFs = volume => {
11
- vol.fromJSON(volume);
12
- const unpatch = patchFs(vol);
13
- return () => {
14
- vol.reset();
15
- unpatch();
16
- };
17
- };
18
-
19
- test('should return npm package url', t => {
20
- const options = { npm: { name: 'my-cool-package' } };
21
- const npmClient = factory(npm, { options });
22
- t.is(npmClient.getPackageUrl(), 'https://www.npmjs.com/package/my-cool-package');
23
- });
24
-
25
- test('should return npm package url (custom registry)', t => {
26
- const options = { npm: { name: 'my-cool-package', publishConfig: { registry: 'https://registry.example.org/' } } };
27
- const npmClient = factory(npm, { options });
28
- t.is(npmClient.getPackageUrl(), 'https://registry.example.org/package/my-cool-package');
29
- });
7
+ import { mkTmpDir, getArgs } from './util/helpers.js';
30
8
 
31
- test('should return npm package url (custom publicPath)', t => {
32
- const options = { npm: { name: 'my-cool-package', publishConfig: { publicPath: '/custom/public-path' } } };
33
- const npmClient = factory(npm, { options });
34
- t.is(npmClient.getPackageUrl(), 'https://www.npmjs.com/custom/public-path/my-cool-package');
35
- });
9
+ describe('npm', async () => {
10
+ test('should return npm package url', async () => {
11
+ const options = { npm: { name: 'my-cool-package' } };
12
+ const npmClient = factory(npm, { options });
13
+ assert.equal(npmClient.getPackageUrl(), 'https://www.npmjs.com/package/my-cool-package');
14
+ });
36
15
 
37
- test('should return npm package url (custom registry and publicPath)', t => {
38
- const options = {
39
- npm: {
40
- name: 'my-cool-package',
41
- publishConfig: { registry: 'https://registry.example.org/', publicPath: '/custom/public-path' }
42
- }
43
- };
44
- const npmClient = factory(npm, { options });
45
- t.is(npmClient.getPackageUrl(), 'https://registry.example.org/custom/public-path/my-cool-package');
46
- });
16
+ test('should return npm package url (custom registry)', async () => {
17
+ const options = { npm: { name: 'my-cool-package', publishConfig: { registry: 'https://registry.example.org/' } } };
18
+ const npmClient = factory(npm, { options });
19
+ assert.equal(npmClient.getPackageUrl(), 'https://registry.example.org/package/my-cool-package');
20
+ });
47
21
 
48
- test('should return default tag', async t => {
49
- const npmClient = factory(npm);
50
- const tag = await npmClient.resolveTag();
51
- t.is(tag, 'latest');
52
- });
22
+ test('should return npm package url (custom publicPath)', async () => {
23
+ const options = { npm: { name: 'my-cool-package', publishConfig: { publicPath: '/custom/public-path' } } };
24
+ const npmClient = factory(npm, { options });
25
+ assert.equal(npmClient.getPackageUrl(), 'https://www.npmjs.com/custom/public-path/my-cool-package');
26
+ });
53
27
 
54
- test('should resolve default tag for pre-release', async t => {
55
- const npmClient = factory(npm);
56
- const stub = sinon.stub(npmClient, 'getRegistryPreReleaseTags').resolves([]);
57
- const tag = await npmClient.resolveTag('1.0.0-0');
58
- t.is(tag, 'next');
59
- stub.restore();
60
- });
28
+ test('should return npm package url (custom registry and publicPath)', async () => {
29
+ const options = {
30
+ npm: {
31
+ name: 'my-cool-package',
32
+ publishConfig: { registry: 'https://registry.example.org/', publicPath: '/custom/public-path' }
33
+ }
34
+ };
35
+ const npmClient = factory(npm, { options });
36
+ assert.equal(npmClient.getPackageUrl(), 'https://registry.example.org/custom/public-path/my-cool-package');
37
+ });
61
38
 
62
- test('should guess tag from registry for pre-release', async t => {
63
- const npmClient = factory(npm);
64
- const stub = sinon.stub(npmClient, 'getRegistryPreReleaseTags').resolves(['alpha']);
65
- const tag = await npmClient.resolveTag('1.0.0-0');
66
- t.is(tag, 'alpha');
67
- stub.restore();
68
- });
39
+ test('should return default tag', async () => {
40
+ const npmClient = factory(npm);
41
+ const tag = await npmClient.resolveTag();
42
+ assert.equal(tag, 'latest');
43
+ });
69
44
 
70
- test('should derive tag from pre-release version', async t => {
71
- const npmClient = factory(npm);
72
- const tag = await npmClient.resolveTag('1.0.2-alpha.3');
73
- t.is(tag, 'alpha');
74
- });
45
+ test('should resolve default tag for pre-release', async t => {
46
+ const npmClient = factory(npm);
47
+ t.mock.method(npmClient, 'getRegistryPreReleaseTags', () => []);
48
+ const tag = await npmClient.resolveTag('1.0.0-0');
49
+ assert.equal(tag, 'next');
50
+ });
75
51
 
76
- test('should use provided (default) tag even for pre-release', async t => {
77
- const options = { npm: { tag: 'latest' } };
78
- const npmClient = factory(npm, { options });
79
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
80
- await npmClient.bump('1.0.0-next.0');
81
- t.is(npmClient.getContext('tag'), 'latest');
82
- exec.restore();
83
- });
52
+ test('should guess tag from registry for pre-release', async t => {
53
+ const npmClient = factory(npm);
54
+ t.mock.method(npmClient, 'getRegistryPreReleaseTags', () => ['alpha']);
55
+ const tag = await npmClient.resolveTag('1.0.0-0');
56
+ assert.equal(tag, 'alpha');
57
+ });
84
58
 
85
- test('should throw when `npm version` fails', async t => {
86
- const npmClient = factory(npm);
87
- const exec = sinon
88
- .stub(npmClient.shell, 'exec')
89
- .rejects(new Error('npm ERR! Version not changed, might want --allow-same-version'));
59
+ test('should derive tag from pre-release version', async () => {
60
+ const npmClient = factory(npm);
61
+ const tag = await npmClient.resolveTag('1.0.2-alpha.3');
62
+ assert.equal(tag, 'alpha');
63
+ });
90
64
 
91
- await t.throwsAsync(npmClient.bump('1.0.0-next.0'), { message: /Version not changed/ });
65
+ test('should use provided (default) tag even for pre-release', async t => {
66
+ const options = { npm: { tag: 'latest' } };
67
+ const npmClient = factory(npm, { options });
68
+ t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
69
+ await npmClient.bump('1.0.0-next.0');
70
+ assert.equal(npmClient.getContext('tag'), 'latest');
71
+ });
92
72
 
93
- exec.restore();
94
- });
73
+ test('should throw when `npm version` fails', async t => {
74
+ const npmClient = factory(npm);
75
+ t.mock.method(npmClient.shell, 'exec', () =>
76
+ Promise.reject(new Error('npm ERR! Version not changed, might want --allow-same-version'))
77
+ );
78
+ await assert.rejects(npmClient.bump('1.0.0-next.0'), { message: /Version not changed/ });
79
+ });
95
80
 
96
- test('should return first pre-release tag from package in registry when resolving tag without pre-id', async t => {
97
- const npmClient = factory(npm);
98
- const response = {
99
- latest: '1.4.1',
100
- alpha: '2.0.0-alpha.1',
101
- beta: '2.0.0-beta.3'
102
- };
103
- const exec = sinon.stub(npmClient.shell, 'exec').resolves(JSON.stringify(response));
104
- t.is(await npmClient.resolveTag('2.0.0-5'), 'alpha');
105
- exec.restore();
106
- });
81
+ test('should return first pre-release tag from package in registry when resolving tag without pre-id', async t => {
82
+ const npmClient = factory(npm);
83
+ const response = { latest: '1.4.1', alpha: '2.0.0-alpha.1', beta: '2.0.0-beta.3' };
84
+ t.mock.method(npmClient.shell, 'exec', () => Promise.resolve(JSON.stringify(response)));
85
+ assert.equal(await npmClient.resolveTag('2.0.0-5'), 'alpha');
86
+ });
107
87
 
108
- test('should return default pre-release tag when resolving tag without pre-id', async t => {
109
- const npmClient = factory(npm);
110
- const response = {
111
- latest: '1.4.1'
112
- };
113
- const exec = sinon.stub(npmClient.shell, 'exec').resolves(JSON.stringify(response));
114
- t.is(await npmClient.resolveTag('2.0.0-0'), 'next');
115
- exec.restore();
116
- });
88
+ test('should return default pre-release tag when resolving tag without pre-id', async t => {
89
+ const npmClient = factory(npm);
90
+ const response = {
91
+ latest: '1.4.1'
92
+ };
93
+ t.mock.method(npmClient.shell, 'exec', () => Promise.resolve(JSON.stringify(response)));
94
+ assert.equal(await npmClient.resolveTag('2.0.0-0'), 'next');
95
+ });
117
96
 
118
- test('should handle erroneous output when resolving tag without pre-id', async t => {
119
- const npmClient = factory(npm);
120
- const exec = sinon.stub(npmClient.shell, 'exec').resolves('');
121
- t.is(await npmClient.resolveTag('2.0.0-0'), 'next');
122
- exec.restore();
123
- });
97
+ test('should handle erroneous output when resolving tag without pre-id', async t => {
98
+ const npmClient = factory(npm);
99
+ t.mock.method(npmClient.shell, 'exec', () => Promise.resolve(''));
100
+ assert.equal(await npmClient.resolveTag('2.0.0-0'), 'next');
101
+ });
124
102
 
125
- test('should handle errored request when resolving tag without pre-id', async t => {
126
- const npmClient = factory(npm);
127
- const exec = sinon.stub(npmClient.shell, 'exec').rejects();
128
- t.is(await npmClient.resolveTag('2.0.0-0'), 'next');
129
- exec.restore();
130
- });
103
+ test('should handle errored request when resolving tag without pre-id', async t => {
104
+ const npmClient = factory(npm);
105
+ t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
106
+ assert.equal(await npmClient.resolveTag('2.0.0-0'), 'next');
107
+ });
131
108
 
132
- test('should add registry to commands when specified', async t => {
133
- const npmClient = factory(npm);
134
- npmClient.setContext({ publishConfig: { registry: 'registry.example.org' } });
135
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
136
- exec.withArgs('npm whoami --registry registry.example.org').resolves('john');
137
- exec
138
- .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it --registry registry.example.org/)
139
- .resolves(JSON.stringify({ john: ['write'] }));
140
- await runTasks(npmClient);
141
- t.is(exec.args[0][0], 'npm ping --registry registry.example.org');
142
- t.is(exec.args[1][0], 'npm whoami --registry registry.example.org');
143
- t.regex(exec.args[2][0], /npm show release-it@[a-z]+ version --registry registry\.example\.org/);
144
- exec.restore();
145
- });
109
+ test('should add registry to commands when specified', async t => {
110
+ const npmClient = factory(npm);
111
+ npmClient.setContext({ publishConfig: { registry: 'registry.example.org' } });
112
+ const exec = t.mock.method(npmClient.shell, 'exec', command => {
113
+ if (command === 'npm whoami --registry registry.example.org') return Promise.resolve('john');
114
+ const re = /npm access (list collaborators --json|ls-collaborators) release-it --registry registry.example.org/;
115
+ if (re.test.command) return Promise.resolve(JSON.stringify({ john: ['write'] }));
116
+ return Promise.resolve();
117
+ });
146
118
 
147
- test('should not throw when executing tasks', async t => {
148
- const npmClient = factory(npm);
149
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
150
- exec.withArgs('npm whoami').resolves('john');
151
- exec
152
- .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
153
- .resolves(JSON.stringify({ john: ['write'] }));
154
- await t.notThrowsAsync(runTasks(npmClient));
155
- exec.restore();
156
- });
119
+ await runTasks(npmClient);
120
+ assert.equal(exec.mock.calls[0].arguments[0], 'npm ping --registry registry.example.org');
121
+ assert.equal(exec.mock.calls[1].arguments[0], 'npm whoami --registry registry.example.org');
122
+ assert.match(
123
+ exec.mock.calls[2].arguments[0],
124
+ /npm show release-it@[a-z]+ version --registry registry\.example\.org/
125
+ );
126
+ });
157
127
 
158
- test('should throw if npm is down', async t => {
159
- const npmClient = factory(npm);
160
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
161
- exec.withArgs('npm ping').rejects();
162
- await t.throwsAsync(runTasks(npmClient), { message: /^Unable to reach npm registry/ });
163
- exec.restore();
164
- });
128
+ test('should not throw when executing tasks', async t => {
129
+ const npmClient = factory(npm);
130
+ t.mock.method(npmClient.shell, 'exec', command => {
131
+ if (command === 'npm whoami') return Promise.resolve('john');
132
+ const re = /npm access (list collaborators --json|ls-collaborators) release-it/;
133
+ if (re.test.command) return Promise.resolve(JSON.stringify({ john: ['write'] }));
134
+ return Promise.resolve();
135
+ });
136
+ await assert.doesNotReject(runTasks(npmClient));
137
+ });
165
138
 
166
- test('should not throw if npm returns 400/404 for unsupported ping/whoami/access', async t => {
167
- const npmClient = factory(npm);
168
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
169
- const pingError = "npm ERR! code E404\nnpm ERR! 404 Package '--ping' not found : ping";
170
- const whoamiError = "npm ERR! code E404\nnpm ERR! 404 Package '--whoami' not found : whoami";
171
- const accessError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/collaborators';
172
- exec.withArgs('npm ping').rejects(new Error(pingError));
173
- exec.withArgs('npm whoami').rejects(new Error(whoamiError));
174
- exec.withArgs('npm access').rejects(new Error(accessError));
175
- await runTasks(npmClient);
176
- t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag latest');
177
- exec.restore();
178
- });
139
+ test('should throw if npm is down', async t => {
140
+ const npmClient = factory(npm);
141
+ t.mock.method(npmClient.shell, 'exec', command => {
142
+ if (command === 'npm ping') return Promise.reject();
143
+ return Promise.resolve();
144
+ });
145
+ await assert.rejects(runTasks(npmClient), { message: /^Unable to reach npm registry/ });
146
+ });
179
147
 
180
- test('should not throw if npm returns 400 for unsupported ping/whoami/access', async t => {
181
- const npmClient = factory(npm);
182
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
183
- const pingError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/ping?write=true';
184
- const whoamiError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/whoami';
185
- const accessError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/collaborators';
186
- exec.withArgs('npm ping').rejects(new Error(pingError));
187
- exec.withArgs('npm whoami').rejects(new Error(whoamiError));
188
- exec.withArgs('npm access').rejects(new Error(accessError));
189
- await runTasks(npmClient);
190
- t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag latest');
191
- exec.restore();
192
- });
148
+ test('should not throw if npm returns 400/404 for unsupported ping/whoami/access', async t => {
149
+ const npmClient = factory(npm);
150
+ const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
151
+ const pingError = "npm ERR! code E404\nnpm ERR! 404 Package '--ping' not found : ping";
152
+ const whoamiError = "npm ERR! code E404\nnpm ERR! 404 Package '--whoami' not found : whoami";
153
+ const accessError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/collaborators';
154
+ exec.mock.mockImplementationOnce(() => Promise.reject(new Error(pingError)), 0);
155
+ exec.mock.mockImplementationOnce(() => Promise.reject(new Error(whoamiError)), 1);
156
+ exec.mock.mockImplementationOnce(() => Promise.reject(new Error(accessError)), 2);
157
+ await runTasks(npmClient);
158
+ assert.equal(exec.mock.calls.at(-1).arguments[0].trim(), 'npm publish . --tag latest');
159
+ });
193
160
 
194
- test('should not throw if npm returns 404 for unsupported ping', async t => {
195
- const npmClient = factory(npm);
196
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
197
- const pingError = 'npm ERR! <title>404 - No content for path /-/ping</title>';
198
- exec.withArgs('npm ping').rejects(new Error(pingError));
199
- exec.withArgs('npm whoami').resolves('john');
200
- exec
201
- .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
202
- .resolves(JSON.stringify({ john: ['write'] }));
203
- await runTasks(npmClient);
204
- t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag latest');
205
- exec.restore();
206
- });
161
+ test('should not throw if npm returns 400 for unsupported ping/whoami/access', async t => {
162
+ const npmClient = factory(npm);
163
+ const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
164
+ const pingError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/ping?write=true';
165
+ const whoamiError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/whoami';
166
+ const accessError = 'npm ERR! code E400\nnpm ERR! 400 Bad Request - GET https://npm.example.org/-/collaborators';
167
+ exec.mock.mockImplementationOnce(() => Promise.reject(new Error(pingError)), 0);
168
+ exec.mock.mockImplementationOnce(() => Promise.reject(new Error(whoamiError)), 1);
169
+ exec.mock.mockImplementationOnce(() => Promise.reject(new Error(accessError)), 2);
170
+ await runTasks(npmClient);
171
+ assert.equal(exec.mock.calls.at(-1).arguments[0].trim(), 'npm publish . --tag latest');
172
+ });
207
173
 
208
- test('should throw if user is not authenticated', async t => {
209
- const npmClient = factory(npm);
210
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
211
- exec.withArgs('npm whoami').rejects();
212
- await t.throwsAsync(runTasks(npmClient), { message: /^Not authenticated with npm/ });
213
- exec.restore();
214
- });
174
+ test('should throw if user is not authenticated', async t => {
175
+ const npmClient = factory(npm);
176
+ const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
177
+ exec.mock.mockImplementationOnce(() => Promise.reject(), 1);
178
+ await assert.rejects(runTasks(npmClient), { message: /^Not authenticated with npm/ });
179
+ });
215
180
 
216
- test('should throw if user is not a collaborator (v9)', async t => {
217
- const npmClient = factory(npm);
218
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
219
- exec.withArgs('npm whoami').resolves('ada');
220
- exec.withArgs('npm --version').resolves('9.2.0');
221
- exec.withArgs('npm access list collaborators --json release-it').resolves(JSON.stringify({ john: ['write'] }));
222
- await t.throwsAsync(runTasks(npmClient), { message: /^User ada is not a collaborator for release-it/ });
223
- exec.restore();
224
- });
181
+ test('should throw if user is not a collaborator (v9)', async t => {
182
+ const npmClient = factory(npm);
183
+ t.mock.method(npmClient.shell, 'exec', command => {
184
+ if (command === 'npm whoami') return Promise.resolve('ada');
185
+ if (command === 'npm --version') return Promise.resolve('9.2.0');
186
+ if (command === 'npm access list collaborators --json release-it')
187
+ return Promise.resolve(JSON.stringify({ john: ['write'] }));
188
+ return Promise.resolve();
189
+ });
190
+ await assert.rejects(runTasks(npmClient), { message: /^User ada is not a collaborator for release-it/ });
191
+ });
225
192
 
226
- test('should throw if user is not a collaborator (v8)', async t => {
227
- const npmClient = factory(npm);
228
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
229
- exec.withArgs('npm whoami').resolves('ada');
230
- exec.withArgs('npm --version').resolves('8.2.0');
231
- exec
232
- .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
233
- .resolves(JSON.stringify({ john: ['write'] }));
234
- await t.throwsAsync(runTasks(npmClient), { message: /^User ada is not a collaborator for release-it/ });
235
- exec.restore();
236
- });
193
+ test('should throw if user is not a collaborator (v8)', async t => {
194
+ const npmClient = factory(npm);
237
195
 
238
- test('should not throw if user is not a collaborator on a new package', async t => {
239
- const npmClient = factory(npm);
240
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
241
- exec.withArgs('npm whoami').resolves('ada');
242
- exec
243
- .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
244
- .rejects(
245
- new Error(
246
- 'npm ERR! code E404\nnpm ERR! 404 Not Found - GET https://registry.npmjs.org/-/package/release-it/collaborators?format=cli - File not found'
247
- )
248
- );
249
- await t.notThrowsAsync(runTasks(npmClient));
250
- exec.restore();
251
- });
196
+ t.mock.method(npmClient.shell, 'exec', command => {
197
+ if (command === 'npm whoami') return Promise.resolve('ada');
198
+ if (command === 'npm --version') return Promise.resolve('8.2.0');
199
+ const re = /npm access (list collaborators --json|ls-collaborators) release-it/;
200
+ if (re.test(command)) return Promise.resolve(JSON.stringify({ john: ['write'] }));
201
+ return Promise.resolve();
202
+ });
252
203
 
253
- test('should publish a new private scoped package as npm would', async t => {
254
- const options = { npm: { tag: 'beta' } };
255
- const npmClient = factory(npm, { options });
256
- npmClient.setContext({ name: '@scoped/pkg' });
257
- const exec = sinon.spy(npmClient.shell, 'exec');
258
- await npmClient.publish();
259
- t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag beta');
260
- exec.restore();
261
- });
204
+ await assert.rejects(runTasks(npmClient), { message: /^User ada is not a collaborator for release-it/ });
205
+ });
262
206
 
263
- test('should not publish private package', async t => {
264
- const npmClient = factory(npm);
265
- npmClient.setContext({ name: 'pkg', private: true });
266
- const exec = sinon.spy(npmClient.shell, 'exec');
267
- await npmClient.publish();
268
- const publish = exec.args.filter(arg => arg[0].startsWith('npm publish'));
269
- t.is(publish.length, 0);
270
- t.regex(npmClient.log.warn.lastCall.args[0], /package is private/);
271
- });
207
+ test('should not throw if user is not a collaborator on a new package', async t => {
208
+ const npmClient = factory(npm);
272
209
 
273
- test('should handle 2FA and publish with OTP', async t => {
274
- const npmClient = factory(npm);
275
- npmClient.setContext({ name: 'pkg' });
210
+ t.mock.method(npmClient.shell, 'exec', command => {
211
+ if (command === 'npm whoami') return Promise.resolve('ada');
212
+ const re = /npm access (list collaborators --json|ls-collaborators) release-it/;
213
+ const message =
214
+ 'npm ERR! code E404\nnpm ERR! 404 Not Found - GET https://registry.npmjs.org/-/package/release-it/collaborators?format=cli - File not found';
215
+ if (re.test(command)) return Promise.reject(new Error(message));
216
+ return Promise.resolve();
217
+ });
276
218
 
277
- const exec = sinon.stub(npmClient.shell, 'exec');
219
+ await assert.doesNotReject(runTasks(npmClient));
220
+ });
278
221
 
279
- exec.onFirstCall().rejects(new Error('Initial error with one-time pass.'));
280
- exec.onSecondCall().rejects(new Error('The provided one-time pass is incorrect.'));
281
- exec.onThirdCall().resolves();
222
+ test('should handle 2FA and publish with OTP', async t => {
223
+ const npmClient = factory(npm);
224
+ npmClient.setContext({ name: 'pkg' });
282
225
 
283
- await npmClient.publish({
284
- otpCallback: () =>
285
- npmClient.publish({
286
- otp: '123',
287
- otpCallback: () => npmClient.publish({ otp: '123456' })
288
- })
289
- });
226
+ const exec = t.mock.method(npmClient.shell, 'exec');
290
227
 
291
- t.is(exec.callCount, 3);
292
- t.is(exec.firstCall.args[0].trim(), 'npm publish . --tag latest');
293
- t.is(exec.secondCall.args[0].trim(), 'npm publish . --tag latest --otp 123');
294
- t.is(exec.thirdCall.args[0].trim(), 'npm publish . --tag latest --otp 123456');
228
+ exec.mock.mockImplementationOnce(() => Promise.reject(new Error('Initial error with one-time pass.')), 0);
229
+ exec.mock.mockImplementationOnce(() => Promise.reject(new Error('The provided one-time pass is incorrect.')), 1);
230
+ exec.mock.mockImplementationOnce(() => Promise.resolve(), 2);
295
231
 
296
- t.is(npmClient.log.warn.callCount, 1);
297
- t.is(npmClient.log.warn.firstCall.args[0], 'The provided OTP is incorrect or has expired.');
298
- });
232
+ await npmClient.publish({
233
+ otpCallback: () =>
234
+ npmClient.publish({
235
+ otp: '123',
236
+ otpCallback: () => npmClient.publish({ otp: '123456' })
237
+ })
238
+ });
299
239
 
300
- test('should publish', async t => {
301
- const npmClient = factory(npm);
302
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
303
- exec.withArgs('npm whoami').resolves('john');
304
- exec
305
- .withArgs(/npm access (list collaborators --json|ls-collaborators) release-it/)
306
- .resolves(JSON.stringify({ john: ['write'] }));
307
- await runTasks(npmClient);
308
- t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag latest');
309
- exec.restore();
310
- });
240
+ assert.equal(exec.mock.callCount(), 3);
241
+ assert.equal(exec.mock.calls[0].arguments[0].trim(), 'npm publish . --tag latest');
242
+ assert.equal(exec.mock.calls[1].arguments[0].trim(), 'npm publish . --tag latest --otp 123');
243
+ assert.equal(exec.mock.calls[2].arguments[0].trim(), 'npm publish . --tag latest --otp 123456');
311
244
 
312
- test('should use extra publish arguments ', async t => {
313
- const options = { npm: { skipChecks: true, publishArgs: '--registry=http://my-internal-registry.local' } };
314
- const npmClient = factory(npm, { options });
315
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
316
- await runTasks(npmClient);
317
- t.is(exec.lastCall.args[0].trim(), 'npm publish . --tag latest --registry=http://my-internal-registry.local');
318
- exec.restore();
319
- });
245
+ assert.equal(npmClient.log.warn.mock.callCount(), 1);
246
+ assert.equal(npmClient.log.warn.mock.calls[0].arguments[0], 'The provided OTP is incorrect or has expired.');
247
+ });
320
248
 
321
- test('should skip checks', async t => {
322
- const options = { npm: { skipChecks: true } };
323
- const npmClient = factory(npm, { options });
324
- await t.notThrowsAsync(npmClient.init());
325
- });
249
+ test('should publish', async t => {
250
+ const npmClient = factory(npm);
251
+ const exec = t.mock.method(npmClient.shell, 'exec', command => {
252
+ if (command === 'npm whoami') return Promise.resolve('john');
253
+ const re = /npm access (list collaborators --json|ls-collaborators) release-it/;
254
+ if (re.test(command)) return Promise.resolve(JSON.stringify({ john: ['write'] }));
255
+ return Promise.resolve();
256
+ });
257
+ await runTasks(npmClient);
258
+ assert.equal(exec.mock.calls.at(-1).arguments[0].trim(), 'npm publish . --tag latest');
259
+ });
326
260
 
327
- test('should publish to a different/scoped registry', async t => {
328
- const resetFs = mockFs({
329
- [path.resolve('package.json')]: JSON.stringify({
330
- name: '@my-scope/my-pkg',
331
- version: '1.0.0',
332
- publishConfig: {
333
- access: 'public',
334
- '@my-scope:registry': 'https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/'
335
- }
336
- })
261
+ test('should use extra publish arguments', async t => {
262
+ const options = { npm: { skipChecks: true, publishArgs: '--registry=http://my-internal-registry.local' } };
263
+ const npmClient = factory(npm, { options });
264
+ const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
265
+ await runTasks(npmClient);
266
+ assert.equal(
267
+ exec.mock.calls.at(-1).arguments[0].trim(),
268
+ 'npm publish . --tag latest --registry=http://my-internal-registry.local'
269
+ );
337
270
  });
338
- const options = { npm };
339
- const npmClient = factory(npm, { options });
340
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
341
- exec
342
- .withArgs('npm whoami --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/')
343
- .resolves('john');
344
- exec
345
- .withArgs(
346
- /npm access (list collaborators --json|ls-collaborators) @my-scope\/my-pkg --registry https:\/\/gitlab\.com\/api\/v4\/projects\/my-scope%2Fmy-pkg\/packages\/npm\//
347
- )
348
- .resolves(JSON.stringify({ john: ['write'] }));
349
-
350
- await runTasks(npmClient);
351
-
352
- t.deepEqual(getArgs(exec.args, 'npm'), [
353
- 'npm ping --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
354
- 'npm whoami --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
355
- 'npm show @my-scope/my-pkg@latest version --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
356
- 'npm --version',
357
- 'npm version 1.0.1 --no-git-tag-version',
358
- 'npm publish . --tag latest --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/'
359
- ]);
360
-
361
- exec.restore();
362
- resetFs();
363
- });
364
271
 
365
- test('should not publish when `npm version` fails', async t => {
366
- const resetFs = mockFs({
367
- [path.resolve('package.json')]: JSON.stringify({
368
- name: '@my-scope/my-pkg',
369
- version: '1.0.0'
370
- })
272
+ test('should skip checks', async () => {
273
+ const options = { npm: { skipChecks: true } };
274
+ const npmClient = factory(npm, { options });
275
+ await assert.doesNotReject(npmClient.init());
371
276
  });
372
- const options = { npm };
373
- const npmClient = factory(npm, { options });
374
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
375
-
376
- exec.withArgs('npm whoami').resolves('john');
377
- exec
378
- .withArgs(/npm access (list collaborators --json|ls-collaborators) @my-scope\/my-pkg/)
379
- .resolves(JSON.stringify({ john: ['write'] }));
380
- exec
381
- .withArgs('npm version 1.0.1 --no-git-tag-version')
382
- .rejects('npm ERR! Version not changed, might want --allow-same-version');
383
-
384
- try {
277
+
278
+ test('should publish to a different/scoped registry', async t => {
279
+ const tmp = mkTmpDir();
280
+ process.chdir(tmp);
281
+ writeFileSync(
282
+ join(tmp, 'package.json'),
283
+ JSON.stringify({
284
+ name: '@my-scope/my-pkg',
285
+ version: '1.0.0',
286
+ publishConfig: {
287
+ access: 'public',
288
+ '@my-scope:registry': 'https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/'
289
+ }
290
+ })
291
+ );
292
+ const options = { npm };
293
+ const npmClient = factory(npm, { options });
294
+ const exec = t.mock.method(npmClient.shell, 'exec', command => {
295
+ const cmd = 'npm whoami --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/';
296
+ if (command === cmd) return Promise.resolve('john');
297
+ const re =
298
+ /npm access (list collaborators --json|ls-collaborators) @my-scope\/my-pkg --registry https:\/\/gitlab\.com\/api\/v4\/projects\/my-scope%2Fmy-pkg\/packages\/npm\//;
299
+ if (re.test(command)) return Promise.resolve(JSON.stringify({ john: ['write'] }));
300
+ return Promise.resolve();
301
+ });
385
302
  await runTasks(npmClient);
386
- } catch (error) {
387
- t.regex(error.toString(), /Version not changed/);
388
- }
389
-
390
- t.deepEqual(getArgs(exec.args, 'npm'), [
391
- 'npm ping',
392
- 'npm whoami',
393
- 'npm show @my-scope/my-pkg@latest version',
394
- 'npm --version',
395
- 'npm version 1.0.1 --no-git-tag-version'
396
- ]);
397
-
398
- exec.restore();
399
- resetFs();
400
- });
401
303
 
402
- test('should add allow-same-version argument', async t => {
403
- const options = { npm: { skipChecks: true, allowSameVersion: true } };
404
- const npmClient = factory(npm, { options });
405
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
406
- await runTasks(npmClient);
407
- const version = exec.args.filter(arg => arg[0].startsWith('npm version'));
408
- t.regex(version[0][0], / --allow-same-version/);
409
- });
304
+ assert.deepEqual(getArgs(exec, 'npm'), [
305
+ 'npm ping --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
306
+ 'npm whoami --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
307
+ 'npm show @my-scope/my-pkg@latest version --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/',
308
+ 'npm --version',
309
+ 'npm version 1.0.1 --no-git-tag-version',
310
+ 'npm publish . --tag latest --registry https://gitlab.com/api/v4/projects/my-scope%2Fmy-pkg/packages/npm/'
311
+ ]);
312
+ });
313
+
314
+ test('should not publish when `npm version` fails', async t => {
315
+ const tmp = mkTmpDir();
316
+ process.chdir(tmp);
317
+ writeFileSync(join(tmp, 'package.json'), JSON.stringify({ name: '@my-scope/my-pkg', version: '1.0.0' }));
318
+ const options = { npm };
319
+ const npmClient = factory(npm, { options });
320
+
321
+ const exec = t.mock.method(npmClient.shell, 'exec', command => {
322
+ if (command === 'npm whoami') return Promise.resolve('john');
323
+ const re = /npm access (list collaborators --json|ls-collaborators) @my-scope\/my-pkg/;
324
+ if (re.test(command)) return Promise.resolve(JSON.stringify({ john: ['write'] }));
325
+ if (command === 'npm version 1.0.1 --no-git-tag-version')
326
+ return Promise.reject('npm ERR! Version not changed, might want --allow-same-version');
327
+ return Promise.resolve();
328
+ });
329
+
330
+ await assert.rejects(runTasks(npmClient), /Version not changed/);
331
+
332
+ assert.deepEqual(getArgs(exec, 'npm'), [
333
+ 'npm ping',
334
+ 'npm whoami',
335
+ 'npm show @my-scope/my-pkg@latest version',
336
+ 'npm --version',
337
+ 'npm version 1.0.1 --no-git-tag-version'
338
+ ]);
339
+ });
340
+
341
+ test('should add allow-same-version argument', async t => {
342
+ const options = { npm: { skipChecks: true, allowSameVersion: true } };
343
+ const npmClient = factory(npm, { options });
344
+
345
+ const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
410
346
 
411
- test('should add version arguments', async t => {
412
- const options = { npm: { skipChecks: true, versionArgs: ['--workspaces-update=false', '--allow-same-version'] } };
413
- const npmClient = factory(npm, { options });
414
- const exec = sinon.stub(npmClient.shell, 'exec').resolves();
415
- await runTasks(npmClient);
416
- const version = exec.args.filter(arg => arg[0].startsWith('npm version'));
417
- t.regex(version[0][0], / --workspaces-update=false --allow-same-version/);
347
+ await runTasks(npmClient);
348
+ const versionArgs = getArgs(exec, 'npm version');
349
+ assert.match(versionArgs[0], / --allow-same-version/);
350
+ });
351
+
352
+ test('should add version arguments', async t => {
353
+ const options = { npm: { skipChecks: true, versionArgs: ['--workspaces-update=false', '--allow-same-version'] } };
354
+ const npmClient = factory(npm, { options });
355
+ const exec = t.mock.method(npmClient.shell, 'exec', () => Promise.resolve());
356
+ await runTasks(npmClient);
357
+ const versionArgs = getArgs(exec, 'npm version');
358
+ assert.match(versionArgs[0], / --workspaces-update=false --allow-same-version/);
359
+ });
418
360
  });