1 | var check = require('check-types');
|
2 | var verify = check.verify;
|
3 | var q = require('q');
|
4 | var _ = require('lodash');
|
5 | var semver = require('semver');
|
6 | var quote = require('quote');
|
7 | var installModule = require('./module-install');
|
8 | var reportSuccess = require('./report').reportSuccess;
|
9 | var reportFailure = require('./report').reportFailure;
|
10 | var stats = require('./stats');
|
11 |
|
12 | var cleanVersions = require('./registry').cleanVersions;
|
13 | check.verify.fn(cleanVersions, 'cleanVersions should be a function');
|
14 |
|
15 | var revertModules = require('./revert');
|
16 | check.verify.fn(revertModules, 'revert is not a function, but ' +
|
17 | JSON.stringify(revertModules));
|
18 |
|
19 | var npmTest = require('./npm-test').test;
|
20 | var execTest = require('./exec-test');
|
21 | var report = require('./report-available');
|
22 | var filterAllowed = require('./filter-allowed-updates');
|
23 |
|
24 |
|
25 |
|
26 | function testModulesVersions(options, available) {
|
27 | verify.object(options, 'missing options');
|
28 | verify.array(available, 'expected array of available modules');
|
29 |
|
30 | var cleaned = cleanVersions(options.modules);
|
31 |
|
32 |
|
33 | var names = _.pluck(cleaned, 'name');
|
34 | var listed = _.zipObject(names, cleaned);
|
35 |
|
36 | |
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 | var allowed = filterAllowed(listed, available, options);
|
44 | la(check.array(allowed), 'could not filter allowed updates', listed, available, options);
|
45 |
|
46 | if (available.length && !allowed.length) {
|
47 | console.log('No updates allowed using option', quote(options.allow || options.allowed));
|
48 | console.log(available.length + ' available updates filtered');
|
49 | return q(listed);
|
50 | }
|
51 |
|
52 |
|
53 | return q.when(report(allowed, listed, options))
|
54 | .then(function testInstalls() {
|
55 |
|
56 | if (options.all) {
|
57 | var install = installAll(allowed, options);
|
58 | console.assert(install, 'could not get install all promise');
|
59 | var test = testPromise(options, options.command);
|
60 | console.assert(test, 'could not get test promise for command', options.command);
|
61 |
|
62 |
|
63 |
|
64 | var installThenTest = install.then(test);
|
65 | if (options.keep) {
|
66 | return installThenTest;
|
67 | }
|
68 |
|
69 | var revert = revertModules.bind(null, listed);
|
70 | console.assert(revert, 'could not get revert promise');
|
71 | return installThenTest.then(revert);
|
72 | }
|
73 |
|
74 | return installEachTestRevert(listed, allowed,
|
75 | options.command, options.color, options.keep, options.tldr);
|
76 | });
|
77 | }
|
78 |
|
79 |
|
80 | function installAll(available, options) {
|
81 | verify.array(available, 'expected array');
|
82 |
|
83 | var installFunctions = available.map(function (nameVersions) {
|
84 | var name = nameVersions.name;
|
85 | var version = nameVersions.versions[0];
|
86 | verify.string(name, 'missing module name from ' +
|
87 | JSON.stringify(nameVersions));
|
88 | verify.string(version, 'missing module version from ' +
|
89 | JSON.stringify(nameVersions));
|
90 |
|
91 | var installOptions = {
|
92 | name: name,
|
93 | version: version,
|
94 | keep: keep,
|
95 | tldr: tldr
|
96 | };
|
97 | var installFunction = installModule.bind(null, installOptions);
|
98 | return installFunction;
|
99 | });
|
100 | var installAllPromise = installFunctions.reduce(q.when, q());
|
101 | return installAllPromise;
|
102 | }
|
103 |
|
104 | function installEachTestRevert(listed, available, command, color, keep, tldr) {
|
105 | verify.object(listed, 'expected listed object');
|
106 | verify.array(available, 'expected array');
|
107 |
|
108 | var checkModulesFunctions = available.map(function (nameVersion) {
|
109 | var name = nameVersion.name;
|
110 | var currentVersion = listed[name].version;
|
111 | la(check.string(currentVersion), 'cannot find current version for', name,
|
112 | 'among current dependencies', listed);
|
113 |
|
114 | var installOptions = {
|
115 | name: name,
|
116 | version: currentVersion,
|
117 | keep: keep,
|
118 | tldr: tldr
|
119 | };
|
120 | var revertFunction = installModule.bind(null, installOptions);
|
121 |
|
122 | var checkModuleFunction = testModuleVersions.bind(null, {
|
123 | moduleVersions: nameVersion,
|
124 | revertFunction: revertFunction,
|
125 | command: command,
|
126 | color: color,
|
127 | currentVersion: currentVersion,
|
128 | keep: keep,
|
129 | tldr: tldr
|
130 | });
|
131 | return checkModuleFunction;
|
132 | });
|
133 | var checkAllPromise = checkModulesFunctions.reduce(q.when, q());
|
134 | return checkAllPromise;
|
135 | }
|
136 |
|
137 |
|
138 |
|
139 | function testModuleVersions(options, results) {
|
140 | verify.object(options, 'missing options');
|
141 | var nameVersions = options.moduleVersions;
|
142 | var restoreVersionFunc = options.revertFunction;
|
143 |
|
144 | var name = nameVersions.name;
|
145 | var versions = nameVersions.versions;
|
146 | verify.string(name, 'expected name string');
|
147 | verify.array(versions, 'expected versions array');
|
148 | results = results || [];
|
149 | verify.array(results, 'expected results array');
|
150 | if (!semver.valid(options.currentVersion)) {
|
151 | throw new Error('do not have current version for ' + name);
|
152 | }
|
153 |
|
154 | var deferred = q.defer();
|
155 | var checkPromises = versions.map(function (version) {
|
156 | return testModuleVersion.bind(null, {
|
157 | name: name,
|
158 | version: version,
|
159 | command: options.command,
|
160 | color: options.color,
|
161 | currentVersion: options.currentVersion,
|
162 | tldr: options.tldr
|
163 | });
|
164 | });
|
165 | var checkAllPromise = checkPromises.reduce(q.when, q());
|
166 | if (options.keep) {
|
167 | checkAllPromise = checkAllPromise.then(function (result) {
|
168 | verify.array(result, 'expected array of results', result);
|
169 | var lastSuccess = _.last(_.filter(result, { works: true }));
|
170 | if (lastSuccess) {
|
171 | console.log('keeping last working version', lastSuccess.name + '@' + lastSuccess.version);
|
172 | return installModule({
|
173 | name: lastSuccess.name,
|
174 | version: lastSuccess.version,
|
175 | keep: true,
|
176 | tldr: options.tldr
|
177 | }, result);
|
178 | } else {
|
179 | return restoreVersionFunc().then(function () {
|
180 |
|
181 | return q(result);
|
182 | });
|
183 | }
|
184 | });
|
185 | } else {
|
186 | checkAllPromise = checkAllPromise
|
187 | .then(restoreVersionFunc);
|
188 | }
|
189 | checkAllPromise
|
190 | .then(function (result) {
|
191 | check.verify.array(result, 'could not get result array');
|
192 | results.push(result);
|
193 | deferred.resolve(results);
|
194 | }, function (error) {
|
195 | console.error('could not check', nameVersions, error);
|
196 | deferred.reject(error);
|
197 | });
|
198 |
|
199 | return deferred.promise;
|
200 | }
|
201 |
|
202 | var logLine = (function formLine() {
|
203 | var n = process.stdout.isTTY ? process.stdout.columns : 40;
|
204 | verify.positiveNumber(n, 'expected to get terminal width, got ' + n);
|
205 | var k;
|
206 | var str = '';
|
207 | for(k = 0; k < n; k += 1) {
|
208 | str += '-'
|
209 | }
|
210 | return function () {
|
211 | console.log(str);
|
212 | };
|
213 | }());
|
214 |
|
215 |
|
216 |
|
217 | function testModuleVersion(options, results) {
|
218 | verify.object(options, 'missing test module options');
|
219 | verify.string(options.name, 'missing module name');
|
220 | verify.string(options.version, 'missing version string');
|
221 | verify.unemptyString(options.currentVersion, 'missing current version');
|
222 |
|
223 | if (options.command) {
|
224 | verify.string(options.command, 'expected command string');
|
225 | }
|
226 |
|
227 |
|
228 | results = results || [];
|
229 | verify.array(results, 'missing previous results array');
|
230 |
|
231 | var nameVersion = options.name + '@' + options.version;
|
232 |
|
233 | if (!options.tldr) {
|
234 | console.log('\ntesting', nameVersion);
|
235 | }
|
236 |
|
237 | var result = {
|
238 | name: options.name,
|
239 | version: options.version,
|
240 | from: options.currentVersion,
|
241 | works: true
|
242 | };
|
243 |
|
244 | var test = testPromise(options, options.command);
|
245 | console.assert(test, 'could not get test promise for command', options.command);
|
246 |
|
247 | var deferred = q.defer();
|
248 |
|
249 | var getSuccess = stats.getSuccessStats({
|
250 | name: options.name,
|
251 | from: options.currentVersion,
|
252 | to: options.version
|
253 | });
|
254 |
|
255 | getSuccess
|
256 | .then(stats.printStats.bind(null, options), function () {
|
257 | console.log('could not get update stats', options.name);
|
258 | return;
|
259 | })
|
260 | .then(function () {
|
261 | return installModule({
|
262 | name: options.name,
|
263 | version: options.version,
|
264 | keep: false,
|
265 | tldr: options.tldr
|
266 | });
|
267 | })
|
268 | .then(test)
|
269 | .then(function () {
|
270 | reportSuccess(nameVersion + ' works', options.color);
|
271 |
|
272 | stats.sendUpdateResult({
|
273 | name: options.name,
|
274 | from: options.currentVersion,
|
275 | to: options.version,
|
276 | success: true
|
277 | });
|
278 | results.push(result);
|
279 | deferred.resolve(results);
|
280 | }, function (error) {
|
281 | reportFailure(nameVersion + ' tests failed :(', options.color);
|
282 |
|
283 | stats.sendUpdateResult({
|
284 | name: options.name,
|
285 | from: options.currentVersion,
|
286 | to: options.version,
|
287 | success: false
|
288 | });
|
289 |
|
290 | verify.number(error.code, 'expected code in error ' +
|
291 | JSON.stringify(error, null, 2));
|
292 |
|
293 | var horizontalLine = options.tldr ? _.noop : logLine;
|
294 |
|
295 | horizontalLine();
|
296 | if (!options.tldr) {
|
297 | console.error('test finished with exit code', error.code);
|
298 | verify.string(error.errors, 'expected errors string in error ' +
|
299 | JSON.stringify(error, null, 2));
|
300 | console.error(error.errors);
|
301 | }
|
302 |
|
303 | horizontalLine();
|
304 |
|
305 | result.works = false;
|
306 | results.push(result);
|
307 | deferred.resolve(results);
|
308 | });
|
309 | return deferred.promise;
|
310 | }
|
311 |
|
312 | function testPromise(options, command) {
|
313 | var testFunction = npmTest.bind(null, options);
|
314 | if (command) {
|
315 | verify.unemptyString(command, 'expected string command, not ' + command);
|
316 | testFunction = execTest.bind(null, options, command);
|
317 | }
|
318 | return testFunction;
|
319 | }
|
320 |
|
321 | module.exports = {
|
322 | testModulesVersions: testModulesVersions,
|
323 | testModuleVersion: testModuleVersion,
|
324 | testPromise: testPromise
|
325 | };
|