Skip to content

Commit cbe6390

Browse files
committed
fix(deps): update rxjs to v6
no issue - update rxjs version to 6 - use rxjs correctly in yarn utils (we weren't using it correctly before) - refactor tests to work better with observable usage
1 parent 0427456 commit cbe6390

File tree

6 files changed

+124
-142
lines changed

6 files changed

+124
-142
lines changed

lib/tasks/yarn-install.js

+14-9
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,20 @@ module.exports = function yarnInstall(ui, zipFile) {
6161

6262
tasks.push({
6363
title: 'Installing dependencies',
64-
task: ctx => yarn(['install', '--no-emoji', '--no-progress'], {
65-
cwd: ctx.installPath,
66-
env: {NODE_ENV: 'production'},
67-
observe: true
68-
}).catch((error) => {
69-
// Add error catcher so we can cleanup the install path if an error occurs
70-
fs.removeSync(ctx.installPath);
71-
return Promise.reject(error);
72-
})
64+
task: (ctx) => {
65+
const observable = yarn(['install', '--no-emoji', '--no-progress'], {
66+
cwd: ctx.installPath,
67+
env: {NODE_ENV: 'production'},
68+
observe: true
69+
});
70+
71+
observable.subscribe({
72+
// Add error catcher so we can cleanup the install path if an error occurs
73+
error: () => fs.removeSync(ctx.installPath)
74+
});
75+
76+
return observable;
77+
}
7378
});
7479

7580
return ui.listr(tasks, false);

lib/utils/yarn.js

+9-11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use strict';
22
const execa = require('execa');
3-
const Observable = require('rxjs').Observable;
4-
5-
const errors = require('../errors');
3+
const {Observable} = require('rxjs');
4+
const {ProcessError} = require('../errors');
65

76
/**
87
* Runs a yarn command. Can return an Observer which allows
@@ -28,7 +27,7 @@ module.exports = function yarn(yarnArgs, options) {
2827
// some other properties, so we just pass
2928
// the entire error object in as options to
3029
// the ProcessError
31-
return cp.catch(error => Promise.reject(new errors.ProcessError(error)));
30+
return cp.catch(error => Promise.reject(new ProcessError(error)));
3231
}
3332

3433
return new Observable((observer) => {
@@ -41,12 +40,11 @@ module.exports = function yarn(yarnArgs, options) {
4140
cp.then(() => {
4241
observer.complete();
4342
}).catch((error) => {
44-
observer.error(error);
43+
// execa augments the error object with
44+
// some other properties, so we just pass
45+
// the entire error object in as options to
46+
// the ProcessError
47+
observer.error(new ProcessError(error));
4548
});
46-
47-
// execa augments the error object with
48-
// some other properties, so we just pass
49-
// the entire error object in as options to
50-
// the ProcessError
51-
}).catch(error => Promise.reject(new errors.ProcessError(error)));
49+
});
5250
};

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
"prettyjson": "1.2.1",
7777
"read-last-lines": "1.6.0",
7878
"replace-in-file": "3.4.3",
79-
"rxjs": "5.5.11",
79+
"rxjs": "6.4.0",
8080
"semver": "5.6.0",
8181
"shasum": "1.0.2",
8282
"stat-mode": "0.3.0",

test/unit/tasks/yarn-install-spec.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const Promise = require('bluebird');
66
const {setupTestFolder, cleanupTestFolders} = require('../../utils/test-folder');
77
const path = require('path');
88
const fs = require('fs');
9+
const {Observable, isObservable} = require('rxjs');
910

1011
const modulePath = '../../../lib/tasks/yarn-install';
1112
const errors = require('../../../lib/errors');
@@ -16,7 +17,7 @@ describe('Unit: Tasks > yarn-install', function () {
1617
});
1718

1819
it('base function calls subtasks and yarn util', function () {
19-
const yarnStub = sinon.stub().resolves();
20+
const yarnStub = sinon.stub().returns(new Observable(o => o.complete()));
2021
const yarnInstall = proxyquire(modulePath, {
2122
'../utils/yarn': yarnStub
2223
});
@@ -25,7 +26,10 @@ describe('Unit: Tasks > yarn-install', function () {
2526
const listrStub = sinon.stub().callsFake((tasks) => {
2627
expect(tasks).to.have.length(3);
2728

28-
return Promise.each(tasks, task => task.task(ctx));
29+
return Promise.each(tasks, (task) => {
30+
const result = task.task(ctx);
31+
return isObservable(result) ? result.toPromise() : result;
32+
});
2933
});
3034

3135
const distTaskStub = sinon.stub(subTasks, 'dist').resolves();
@@ -67,7 +71,7 @@ describe('Unit: Tasks > yarn-install', function () {
6771
});
6872

6973
it('catches errors from yarn and cleans up install folder', function () {
70-
const yarnStub = sinon.stub().rejects(new Error('an error occurred'));
74+
const yarnStub = sinon.stub().returns(new Observable(o => o.error(new Error('an error occurred'))));
7175
const yarnInstall = proxyquire(modulePath, {
7276
'../utils/yarn': yarnStub
7377
});
@@ -77,7 +81,10 @@ describe('Unit: Tasks > yarn-install', function () {
7781
const listrStub = sinon.stub().callsFake((tasks) => {
7882
expect(tasks).to.have.length(3);
7983

80-
return Promise.each(tasks, task => task.task(ctx));
84+
return Promise.each(tasks, (task) => {
85+
const result = task.task(ctx);
86+
return isObservable(result) ? result.toPromise() : result;
87+
});
8188
});
8289

8390
const distTaskStub = sinon.stub(subTasks, 'dist').resolves();

test/unit/utils/yarn-spec.js

+88-104
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,31 @@
22
const expect = require('chai').expect;
33
const proxyquire = require('proxyquire');
44
const sinon = require('sinon');
5-
const ProcessError = require('../../../lib/errors').ProcessError;
5+
const {isObservable} = require('rxjs');
6+
const {getReadableStream} = require('../../utils/stream');
7+
const {ProcessError} = require('../../../lib/errors');
68

79
const modulePath = '../../../lib/utils/yarn';
810

9-
let yarn;
11+
const setup = proxies => proxyquire(modulePath, proxies);
1012

1113
describe('Unit: yarn', function () {
1214
let currentEnv;
13-
let execa;
1415

1516
beforeEach(function () {
1617
currentEnv = process.env;
1718
process.env = {};
18-
19-
execa = sinon.stub().resolves();
20-
yarn = proxyquire(modulePath, {
21-
execa: execa
22-
});
2319
});
2420

2521
afterEach(function () {
2622
process.env = currentEnv;
2723
});
2824

2925
it('spawns yarn process with no arguments correctly', function () {
30-
const promise = yarn();
26+
const execa = sinon.stub().resolves();
27+
const yarn = setup({execa});
3128

32-
return promise.then(function () {
29+
return yarn().then(function () {
3330
expect(execa.calledOnce).to.be.true;
3431
expect(execa.args[0]).to.be.ok;
3532
expect(execa.args[0]).to.have.lengthOf(3);
@@ -38,9 +35,10 @@ describe('Unit: yarn', function () {
3835
});
3936

4037
it('spawns yarn process with correct arguments', function () {
41-
const promise = yarn(['cache', 'clear']);
38+
const execa = sinon.stub().resolves();
39+
const yarn = setup({execa});
4240

43-
return promise.then(function () {
41+
return yarn(['cache', 'clear']).then(function () {
4442
expect(execa.calledOnce).to.be.true;
4543
expect(execa.args[0]).to.be.ok;
4644
expect(execa.args[0]).to.have.lengthOf(3);
@@ -49,9 +47,10 @@ describe('Unit: yarn', function () {
4947
});
5048

5149
it('correctly passes through options', function () {
52-
const promise = yarn([], {cwd: 'test'});
50+
const execa = sinon.stub().resolves();
51+
const yarn = setup({execa});
5352

54-
return promise.then(function () {
53+
return yarn([], {cwd: 'test'}).then(function () {
5554
expect(execa.calledOnce).to.be.true;
5655
expect(execa.args[0]).to.be.ok;
5756
expect(execa.args[0]).to.have.lengthOf(3);
@@ -61,11 +60,11 @@ describe('Unit: yarn', function () {
6160
});
6261

6362
it('respects process.env overrides but doesn\'t mutate process.env', function () {
64-
process.env.TESTENV = 'test';
65-
66-
const promise = yarn([], {env: {TESTENV: 'override'}});
63+
const execa = sinon.stub().resolves();
64+
const yarn = setup({execa});
6765

68-
return promise.then(() => {
66+
process.env.TESTENV = 'test';
67+
return yarn([], {env: {TESTENV: 'override'}}).then(() => {
6968
expect(execa.calledOnce).to.be.true;
7069
expect(execa.args[0][2]).to.be.an('object');
7170
expect(execa.args[0][2].env).to.be.an('object');
@@ -75,8 +74,8 @@ describe('Unit: yarn', function () {
7574
});
7675

7776
it('fails gracefully when yarn fails', function () {
78-
execa.rejects(new Error('YARN_TO_FAST'));
79-
yarn = proxyquire(modulePath, {execa: execa});
77+
const execa = sinon.stub().rejects(new Error('YARN_TO_FAST'));
78+
const yarn = setup({execa});
8079

8180
return yarn().then(() => {
8281
expect(false, 'Promise should have rejected').to.be.true;
@@ -88,105 +87,90 @@ describe('Unit: yarn', function () {
8887
});
8988

9089
describe('can return observables', function () {
91-
let stubs;
92-
beforeEach(function () {
93-
stubs = {
94-
stdout: sinon.stub(),
95-
observer: {
96-
next: sinon.stub(),
97-
complete: sinon.stub(),
98-
error: sinon.stub()
99-
}
100-
};
101-
102-
class TestClass {
103-
constructor(fn) {
104-
stubs.proxy = {fn: fn};
105-
return (new Promise((resolve,reject) => {
106-
stubs.proxy.resolve = resolve;
107-
stubs.proxy.reject = reject;
108-
}));
109-
}
110-
}
111-
112-
const execaPromise = new Promise((resolve, reject) => {
113-
stubs._execa = {
114-
resolve: resolve,
115-
reject: reject
116-
};
90+
it('ends properly', function () {
91+
const execa = sinon.stub().callsFake(() => {
92+
const promise = Promise.resolve();
93+
promise.stdout = getReadableStream();
94+
return promise;
11795
});
118-
execaPromise.stdout = {
119-
setEncoding: () => true,
120-
on: stubs.stdout
96+
const yarn = setup({execa});
97+
98+
const res = yarn([], {observe: true});
99+
expect(isObservable(res)).to.be.true;
100+
101+
const subscriber = {
102+
next: sinon.stub(),
103+
error: sinon.stub(),
104+
complete: sinon.stub()
121105
};
122-
execa.returns(execaPromise);
123106

124-
yarn = proxyquire(modulePath, {
125-
execa: execa,
126-
rxjs: {Observable: TestClass}
107+
res.subscribe(subscriber);
108+
109+
return res.toPromise().then(() => {
110+
expect(execa.calledOnce).to.be.true;
111+
expect(subscriber.next.called).to.be.false;
112+
expect(subscriber.error.called).to.be.false;
113+
expect(subscriber.complete.calledOnce).to.be.true;
127114
});
128115
});
129116

130-
it('ends properly', function () {
117+
it('ends properly (error)', function () {
118+
const execa = sinon.stub().callsFake(() => {
119+
const promise = Promise.reject(new Error('test error'));
120+
promise.stdout = getReadableStream();
121+
return promise;
122+
});
123+
const yarn = setup({execa});
124+
131125
const res = yarn([], {observe: true});
132-
expect(stubs.proxy).to.be.an('Object');
126+
expect(isObservable(res)).to.be.true;
133127

134-
stubs.proxy.fn(stubs.observer);
135-
stubs._execa.resolve();
136-
res.then(() => {
137-
expect(stubs.observer.complete.calledOnce).to.be.true;
138-
});
128+
const subscriber = {
129+
next: sinon.stub(),
130+
error: sinon.stub(),
131+
complete: sinon.stub()
132+
};
139133

140-
stubs.proxy.resolve();
141-
return res;
142-
});
134+
res.subscribe(subscriber);
143135

144-
it('ends properly (error)', function () {
145-
const res = yarn([], {observe: true});
146-
expect(stubs.proxy).to.be.an('Object');
147-
stubs.proxy.fn(stubs.observer);
148-
stubs._execa.reject('test');
149-
150-
res.then(() => {
151-
expect(stubs.observer.complete.called).to.be.false;
152-
expect(stubs.observer.error.calledOnce).to.be.true;
153-
expect(stubs.observer.error.args[0][0]).to.equal('test');
136+
return res.toPromise().catch((error) => {
137+
expect(error.message).to.equal('test error');
138+
expect(execa.calledOnce).to.be.true;
139+
expect(subscriber.next.called).to.be.false;
140+
expect(subscriber.error.calledOnce).to.be.true;
141+
expect(subscriber.error.args[0][0]).to.be.an.instanceOf(ProcessError);
142+
expect(subscriber.complete.called).to.be.false;
154143
});
155-
156-
stubs.proxy.resolve();
157-
return res;
158144
});
159145

160-
it('proxies data through', function () {
161-
yarn([], {observe: true});
162-
expect(stubs.proxy).to.be.an('Object');
163-
stubs.proxy.fn(stubs.observer);
164-
expect(stubs.stdout.calledOnce).to.be.true;
165-
expect(stubs.stdout.args[0][0]).to.equal('data');
166-
167-
const onFn = stubs.stdout.args[0][1];
168-
onFn('\n\n\n');
169-
onFn('test\n');
170-
onFn('\nbest\n');
171-
onFn('run');
172-
173-
const next = stubs.observer.next;
174-
expect(next.callCount).to.equal(4);
175-
expect(next.args[0][0]).to.equal('\n\n');
176-
expect(next.args[1][0]).to.equal('test');
177-
expect(next.args[2][0]).to.equal('\nbest');
178-
expect(next.args[3][0]).to.equal('run');
179-
});
146+
it('passes data through', function () {
147+
const execa = sinon.stub().callsFake(() => {
148+
const promise = Promise.resolve();
149+
promise.stdout = getReadableStream(function () {
150+
this.push('test message\n');
151+
this.push(null);
152+
});
153+
return promise;
154+
});
155+
const yarn = setup({execa});
180156

181-
it('cleans up observer error', function () {
182157
const res = yarn([], {observe: true});
183-
expect(stubs.proxy).to.be.an('Object');
184-
stubs.proxy.reject();
185-
return res.then(() => {
186-
expect(false, 'Promise should have rejected').to.be.true;
187-
}).catch((e) => {
188-
expect(e).to.be.ok;
189-
expect(e).to.be.instanceOf(ProcessError);
158+
expect(isObservable(res)).to.be.true;
159+
160+
const subscriber = {
161+
next: sinon.stub(),
162+
error: sinon.stub(),
163+
complete: sinon.stub()
164+
};
165+
166+
res.subscribe(subscriber);
167+
168+
return res.toPromise().then(() => {
169+
expect(execa.calledOnce).to.be.true;
170+
expect(subscriber.next.calledOnce).to.be.true;
171+
expect(subscriber.next.calledWithExactly('test message')).to.be.true;
172+
expect(subscriber.error.called).to.be.false;
173+
expect(subscriber.complete.calledOnce).to.be.true;
190174
});
191175
});
192176
});

0 commit comments

Comments
 (0)