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