cypress 3.3.2 → 3.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var _arguments = arguments;
4
-
5
3
  var _templateObject = _taggedTemplateLiteral(['\n URL: ', '\n ', '\n '], ['\n URL: ', '\n ', '\n ']),
6
- _templateObject2 = _taggedTemplateLiteral(['\n Failed downloading the Cypress binary.\n Response code: ', '\n Response message: ', '\n '], ['\n Failed downloading the Cypress binary.\n Response code: ', '\n Response message: ', '\n ']);
4
+ _templateObject2 = _taggedTemplateLiteral(['\n Corrupted download\n\n Expected downloaded file to have checksum: ', '\n Computed checksum: ', '\n\n Expected downloaded file to have size: ', '\n Computed size: ', '\n '], ['\n Corrupted download\n\n Expected downloaded file to have checksum: ', '\n Computed checksum: ', '\n\n Expected downloaded file to have size: ', '\n Computed size: ', '\n ']),
5
+ _templateObject3 = _taggedTemplateLiteral(['\n Corrupted download\n\n Expected downloaded file to have checksum: ', '\n Computed checksum: ', '\n '], ['\n Corrupted download\n\n Expected downloaded file to have checksum: ', '\n Computed checksum: ', '\n ']),
6
+ _templateObject4 = _taggedTemplateLiteral(['\n Corrupted download\n\n Expected downloaded file to have size: ', '\n Computed size: ', '\n '], ['\n Corrupted download\n\n Expected downloaded file to have size: ', '\n Computed size: ', '\n ']),
7
+ _templateObject5 = _taggedTemplateLiteral(['\n Failed downloading the Cypress binary.\n Response code: ', '\n Response message: ', '\n '], ['\n Failed downloading the Cypress binary.\n Response code: ', '\n Response message: ', '\n ']);
7
8
 
8
9
  function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
9
10
 
@@ -30,6 +31,10 @@ var util = require('../util');
30
31
 
31
32
  var defaultBaseUrl = 'https://download.cypress.io/';
32
33
 
34
+ var getProxyUrl = function getProxyUrl() {
35
+ return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.npm_config_https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || process.env.npm_config_proxy || null;
36
+ };
37
+
33
38
  var getRealOsArch = function getRealOsArch() {
34
39
  // os.arch() returns the arch for which this node was compiled
35
40
  // we want the operating system's arch instead: x64 or x86
@@ -88,6 +93,71 @@ var prettyDownloadErr = function prettyDownloadErr(err, version) {
88
93
  return throwFormErrorText(errors.failedDownload)(msg);
89
94
  };
90
95
 
96
+ /**
97
+ * Checks checksum and file size for the given file. Allows both
98
+ * values or just one of them to be checked.
99
+ */
100
+ var verifyDownloadedFile = function verifyDownloadedFile(filename, expectedSize, expectedChecksum) {
101
+ if (expectedSize && expectedChecksum) {
102
+ debug('verifying checksum and file size');
103
+
104
+ return Promise.join(util.getFileChecksum(filename), util.getFileSize(filename), function (checksum, filesize) {
105
+ if (checksum === expectedChecksum && filesize === expectedSize) {
106
+ debug('downloaded file has the expected checksum and size ✅');
107
+
108
+ return;
109
+ }
110
+
111
+ debug('raising error: checksum or file size mismatch');
112
+ var text = stripIndent(_templateObject2, expectedChecksum, checksum, expectedSize, filesize);
113
+
114
+ debug(text);
115
+
116
+ throw new Error(text);
117
+ });
118
+ }
119
+
120
+ if (expectedChecksum) {
121
+ debug('only checking expected file checksum %d', expectedChecksum);
122
+
123
+ return util.getFileChecksum(filename).then(function (checksum) {
124
+ if (checksum === expectedChecksum) {
125
+ debug('downloaded file has the expected checksum ✅');
126
+
127
+ return;
128
+ }
129
+
130
+ debug('raising error: file checksum mismatch');
131
+ var text = stripIndent(_templateObject3, expectedChecksum, checksum);
132
+
133
+ throw new Error(text);
134
+ });
135
+ }
136
+
137
+ if (expectedSize) {
138
+ // maybe we don't have a checksum, but at least CDN returns content length
139
+ // which we can check against the file size
140
+ debug('only checking expected file size %d', expectedSize);
141
+
142
+ return util.getFileSize(filename).then(function (filesize) {
143
+ if (filesize === expectedSize) {
144
+ debug('downloaded file has the expected size ✅');
145
+
146
+ return;
147
+ }
148
+
149
+ debug('raising error: file size mismatch');
150
+ var text = stripIndent(_templateObject4, expectedSize, filesize);
151
+
152
+ throw new Error(text);
153
+ });
154
+ }
155
+
156
+ debug('downloaded file lacks checksum or size to verify');
157
+
158
+ return Promise.resolve();
159
+ };
160
+
91
161
  // downloads from given url
92
162
  // return an object with
93
163
  // {filename: ..., downloaded: true}
@@ -97,13 +167,19 @@ var downloadFromUrl = function downloadFromUrl(_ref) {
97
167
  progress = _ref.progress;
98
168
 
99
169
  return new Promise(function (resolve, reject) {
100
- debug('Downloading from', url);
101
- debug('Saving file to', downloadDestination);
170
+ var proxy = getProxyUrl();
171
+
172
+ debug('Downloading package', {
173
+ url: url,
174
+ proxy: proxy,
175
+ downloadDestination: downloadDestination
176
+ });
102
177
 
103
178
  var redirectVersion = void 0;
104
179
 
105
180
  var req = request({
106
181
  url: url,
182
+ proxy: proxy,
107
183
  followRedirect: function followRedirect(response) {
108
184
  var version = response.headers['x-version'];
109
185
 
@@ -122,10 +198,30 @@ var downloadFromUrl = function downloadFromUrl(_ref) {
122
198
 
123
199
  // closure
124
200
  var started = null;
201
+ var expectedSize = void 0;
202
+ var expectedChecksum = void 0;
125
203
 
126
204
  requestProgress(req, {
127
205
  throttle: progress.throttle
128
206
  }).on('response', function (response) {
207
+ // we have computed checksum and filesize during test runner binary build
208
+ // and have set it on the S3 object as user meta data, available via
209
+ // these custom headers "x-amz-meta-..."
210
+ // see https://github.com/cypress-io/cypress/pull/4092
211
+ expectedSize = response.headers['x-amz-meta-size'] || response.headers['content-length'];
212
+
213
+ expectedChecksum = response.headers['x-amz-meta-checksum'];
214
+
215
+ if (expectedChecksum) {
216
+ debug('expected checksum %s', expectedChecksum);
217
+ }
218
+
219
+ if (expectedSize) {
220
+ // convert from string (all Amazon custom headers are strings)
221
+ expectedSize = Number(expectedSize);
222
+ debug('expected file size %d', expectedSize);
223
+ }
224
+
129
225
  // start counting now once we've gotten
130
226
  // response headers
131
227
  started = new Date();
@@ -134,7 +230,7 @@ var downloadFromUrl = function downloadFromUrl(_ref) {
134
230
  if (!/^2/.test(response.statusCode)) {
135
231
  debug('response code %d', response.statusCode);
136
232
 
137
- var err = new Error(stripIndent(_templateObject2, response.statusCode, response.statusMessage));
233
+ var err = new Error(stripIndent(_templateObject5, response.statusCode, response.statusMessage));
138
234
 
139
235
  reject(err);
140
236
  }
@@ -143,27 +239,38 @@ var downloadFromUrl = function downloadFromUrl(_ref) {
143
239
  // starting on our first progress notification
144
240
  var elapsed = new Date() - started;
145
241
 
146
- var eta = util.calculateEta(state.percent, elapsed);
242
+ // request-progress sends a value between 0 and 1
243
+ var percentage = util.convertPercentToPercentage(state.percent);
244
+
245
+ var eta = util.calculateEta(percentage, elapsed);
147
246
 
148
247
  // send up our percent and seconds remaining
149
- progress.onProgress(state.percent, util.secsRemaining(eta));
248
+ progress.onProgress(percentage, util.secsRemaining(eta));
150
249
  })
151
250
  // save this download here
152
251
  .pipe(fs.createWriteStream(downloadDestination)).on('finish', function () {
153
252
  debug('downloading finished');
154
253
 
155
- resolve(redirectVersion);
254
+ verifyDownloadedFile(downloadDestination, expectedSize, expectedChecksum).then(function () {
255
+ return resolve(redirectVersion);
256
+ }, reject);
156
257
  });
157
258
  });
158
259
  };
159
260
 
160
- var start = function start(_ref2) {
161
- var version = _ref2.version,
162
- downloadDestination = _ref2.downloadDestination,
163
- progress = _ref2.progress;
261
+ /**
262
+ * Download Cypress.zip from external url to local file.
263
+ * @param [string] version Could be "3.3.0" or full URL
264
+ * @param [string] downloadDestination Local filename to save as
265
+ */
266
+ var start = function start(opts) {
267
+ var version = opts.version,
268
+ downloadDestination = opts.downloadDestination,
269
+ progress = opts.progress;
270
+
164
271
 
165
272
  if (!downloadDestination) {
166
- la(is.unemptyString(downloadDestination), 'missing download dir', _arguments);
273
+ la(is.unemptyString(downloadDestination), 'missing download dir', opts);
167
274
  }
168
275
 
169
276
  if (!progress) {
@@ -177,6 +284,7 @@ var start = function start(_ref2) {
177
284
  progress.throttle = 100;
178
285
 
179
286
  debug('needed Cypress version: %s', version);
287
+ debug('source url %s', url);
180
288
  debug('downloading cypress.zip to "' + downloadDestination + '"');
181
289
 
182
290
  // ensure download dir exists
@@ -189,5 +297,6 @@ var start = function start(_ref2) {
189
297
 
190
298
  module.exports = {
191
299
  start: start,
192
- getUrl: getUrl
300
+ getUrl: getUrl,
301
+ getProxyUrl: getProxyUrl
193
302
  };
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var _templateObject = _taggedTemplateLiteral([' \n Skipping installation:\n \n Pass the ', ' option if you\'d like to reinstall anyway.\n '], [' \n Skipping installation:\n \n Pass the ', ' option if you\'d like to reinstall anyway.\n ']),
3
+ var _templateObject = _taggedTemplateLiteral(['\n Skipping installation:\n\n Pass the ', ' option if you\'d like to reinstall anyway.\n '], ['\n Skipping installation:\n\n Pass the ', ' option if you\'d like to reinstall anyway.\n ']),
4
4
  _templateObject2 = _taggedTemplateLiteral(['\n ', ' Warning: It looks like you\'ve installed Cypress globally.\n\n This will work, but it\'s not recommended.\n\n The recommended way to install Cypress is as a devDependency per project.\n\n You should probably run these commands:\n\n - ', '\n - ', '\n '], ['\n ', ' Warning: It looks like you\\\'ve installed Cypress globally.\n\n This will work, but it\'\\s not recommended.\n\n The recommended way to install Cypress is as a devDependency per project.\n\n You should probably run these commands:\n\n - ', '\n - ', '\n ']),
5
5
  _templateObject3 = _taggedTemplateLiteral(['\n ', ' Skipping binary installation: Environment variable CYPRESS_INSTALL_BINARY = 0.'], ['\n ', ' Skipping binary installation: Environment variable CYPRESS_INSTALL_BINARY = 0.']),
6
6
  _templateObject4 = _taggedTemplateLiteral(['\n ', ' Overriding Cypress cache directory to: ', '\n\n Previous installs of Cypress may not be found.\n '], ['\n ', ' Overriding Cypress cache directory to: ', '\n\n Previous installs of Cypress may not be found.\n ']),
@@ -41,7 +41,6 @@ var alreadyInstalledMsg = function alreadyInstalledMsg() {
41
41
  };
42
42
 
43
43
  var displayCompletionMsg = function displayCompletionMsg() {
44
-
45
44
  // check here to see if we are globally installed
46
45
  if (util.isInstalledGlobally()) {
47
46
  // if we are display a warning
@@ -98,7 +97,6 @@ var downloadAndUnzip = function downloadAndUnzip(_ref) {
98
97
  }), {
99
98
  title: util.titleize('Finishing Installation'),
100
99
  task: function task(ctx, _task2) {
101
-
102
100
  var cleanup = function cleanup() {
103
101
  debug('removing zip file %s', downloadDestination);
104
102
 
@@ -120,7 +118,6 @@ var downloadAndUnzip = function downloadAndUnzip(_ref) {
120
118
  var start = function start() {
121
119
  var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
122
120
 
123
-
124
121
  // handle deprecated / removed
125
122
  if (util.getEnv('CYPRESS_BINARY_VERSION')) {
126
123
  return throwFormErrorText(errors.removed.CYPRESS_BINARY_VERSION)();
@@ -143,14 +140,17 @@ var start = function start() {
143
140
 
144
141
  // let this environment variable reset the binary version we need
145
142
  if (util.getEnv('CYPRESS_INSTALL_BINARY')) {
143
+ // because passed file paths are often double quoted
144
+ // and might have extra whitespace around, be robust and trim the string
145
+ var trimAndRemoveDoubleQuotes = true;
146
+ var envVarVersion = util.getEnv('CYPRESS_INSTALL_BINARY', trimAndRemoveDoubleQuotes);
146
147
 
147
- var envVarVersion = util.getEnv('CYPRESS_INSTALL_BINARY');
148
-
149
- debug('using environment variable CYPRESS_INSTALL_BINARY %s', envVarVersion);
148
+ debug('using environment variable CYPRESS_INSTALL_BINARY "%s"', envVarVersion);
150
149
 
151
150
  if (envVarVersion === '0') {
152
151
  debug('environment variable CYPRESS_INSTALL_BINARY = 0, skipping install');
153
152
  logger.log(stripIndent(_templateObject3, chalk.yellow('Note:')));
153
+
154
154
  logger.log();
155
155
 
156
156
  return Promise.resolve();
@@ -159,7 +159,6 @@ var start = function start() {
159
159
  // if this doesn't match the expected version
160
160
  // then print warning to the user
161
161
  if (envVarVersion !== needVersion) {
162
-
163
162
  // reset the version to the env var version
164
163
  needVersion = envVarVersion;
165
164
  }
@@ -169,6 +168,7 @@ var start = function start() {
169
168
  var envCache = util.getEnv('CYPRESS_CACHE_FOLDER');
170
169
 
171
170
  logger.log(stripIndent(_templateObject4, chalk.yellow('Note:'), chalk.cyan(envCache)));
171
+
172
172
  logger.log();
173
173
  }
174
174
 
@@ -181,7 +181,6 @@ var start = function start() {
181
181
  }).then(function () {
182
182
  return state.getBinaryPkgVersionAsync(binaryDir);
183
183
  }).then(function (binaryVersion) {
184
-
185
184
  if (!binaryVersion) {
186
185
  debug('no binary installed under cli version');
187
186
 
@@ -192,6 +191,7 @@ var start = function start() {
192
191
 
193
192
  logger.log();
194
193
  logger.log(stripIndent(_templateObject6, chalk.green(binaryVersion), chalk.cyan(installDir)));
194
+
195
195
  logger.log();
196
196
 
197
197
  if (options.force) {
@@ -218,6 +218,7 @@ var start = function start() {
218
218
 
219
219
  if (needVersion !== pkgVersion) {
220
220
  logger.log(chalk.yellow(stripIndent(_templateObject7, logSymbols.warning, chalk.green(pkgVersion), chalk.green(needVersion))));
221
+
221
222
  logger.log();
222
223
  }
223
224
 
@@ -3,6 +3,7 @@
3
3
  var _ = require('lodash');
4
4
  var os = require('os');
5
5
  var path = require('path');
6
+ var untildify = require('untildify');
6
7
  var debug = require('debug')('cypress:cli');
7
8
 
8
9
  var fs = require('../fs');
@@ -71,14 +72,35 @@ var getVersionDir = function getVersionDir() {
71
72
  return path.join(getCacheDir(), version);
72
73
  };
73
74
 
75
+ /**
76
+ * When executing "npm postinstall" hook, the working directory is set to
77
+ * "<current folder>/node_modules/cypress", which can be surprising when using relative paths.
78
+ */
79
+ var isInstallingFromPostinstallHook = function isInstallingFromPostinstallHook() {
80
+ // individual folders
81
+ var cwdFolders = process.cwd().split(path.sep);
82
+ var length = cwdFolders.length;
83
+
84
+ return cwdFolders[length - 2] === 'node_modules' && cwdFolders[length - 1] === 'cypress';
85
+ };
86
+
74
87
  var getCacheDir = function getCacheDir() {
75
88
  var cache_directory = util.getCacheDir();
76
89
 
77
90
  if (util.getEnv('CYPRESS_CACHE_FOLDER')) {
78
- var envVarCacheDir = util.getEnv('CYPRESS_CACHE_FOLDER');
91
+ var envVarCacheDir = untildify(util.getEnv('CYPRESS_CACHE_FOLDER'));
79
92
 
80
93
  debug('using environment variable CYPRESS_CACHE_FOLDER %s', envVarCacheDir);
81
- cache_directory = path.resolve(envVarCacheDir);
94
+
95
+ if (!path.isAbsolute(envVarCacheDir) && isInstallingFromPostinstallHook()) {
96
+ var packageRootFolder = path.join('..', '..', envVarCacheDir);
97
+
98
+ cache_directory = path.resolve(packageRootFolder);
99
+ debug('installing from postinstall hook, original root folder is %s', packageRootFolder);
100
+ debug('and resolved cache directory is %s', cache_directory);
101
+ } else {
102
+ cache_directory = path.resolve(envVarCacheDir);
103
+ }
82
104
  }
83
105
 
84
106
  return cache_directory;
@@ -23,7 +23,6 @@ var unzip = function unzip(_ref) {
23
23
  installDir = _ref.installDir,
24
24
  progress = _ref.progress;
25
25
 
26
-
27
26
  debug('unzipping from %s', zipFilePath);
28
27
  debug('into', installDir);
29
28
 
@@ -20,6 +20,7 @@ var _require = require('common-tags'),
20
20
  var Promise = require('bluebird');
21
21
  var logSymbols = require('log-symbols');
22
22
  var path = require('path');
23
+ var os = require('os');
23
24
 
24
25
  var _require2 = require('../errors'),
25
26
  throwFormErrorText = _require2.throwFormErrorText,
@@ -30,6 +31,8 @@ var logger = require('../logger');
30
31
  var xvfb = require('../exec/xvfb');
31
32
  var state = require('./state');
32
33
 
34
+ var VERIFY_TEST_RUNNER_TIMEOUT_MS = 30000;
35
+
33
36
  var checkExecutable = function checkExecutable(binaryDir) {
34
37
  var executable = state.getPathToExecutable(binaryDir);
35
38
 
@@ -88,6 +91,12 @@ var runSmokeTest = function runSmokeTest(binaryDir, options) {
88
91
  var random = _.random(0, 1000);
89
92
  var args = ['--smoke-test', '--ping=' + random];
90
93
 
94
+ if (needsSandbox()) {
95
+ // electron requires --no-sandbox to run as root
96
+ debug('disabling Electron sandbox');
97
+ args.unshift('--no-sandbox');
98
+ }
99
+
91
100
  if (options.dev) {
92
101
  executable = 'node';
93
102
  args.unshift(path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js'));
@@ -98,6 +107,7 @@ var runSmokeTest = function runSmokeTest(binaryDir, options) {
98
107
  debug('running smoke test');
99
108
  debug('using Cypress executable %s', executable);
100
109
  debug('smoke test command:', smokeTestCommand);
110
+ debug('smoke test timeout %d ms', options.smokeTestTimeout);
101
111
 
102
112
  var env = _.extend({}, process.env, {
103
113
  ELECTRON_ENABLE_LOGGING: true
@@ -111,14 +121,18 @@ var runSmokeTest = function runSmokeTest(binaryDir, options) {
111
121
  return Promise.resolve(util.exec(executable, args, stdioOptions)).catch(onSmokeTestError(smokeTestCommand, linuxWithDisplayEnv)).then(function (result) {
112
122
  // TODO: when execa > 1.1 is released
113
123
  // change this to `result.all` for both stderr and stdout
114
- var smokeTestReturned = result.stdout;
124
+ // use lodash to be robust during tests against null result or missing stdout
125
+ var smokeTestStdout = _.get(result, 'stdout', '');
115
126
 
116
- debug('smoke test stdout "%s"', smokeTestReturned);
127
+ debug('smoke test stdout "%s"', smokeTestStdout);
117
128
 
118
- if (!util.stdoutLineMatches(String(random), smokeTestReturned)) {
129
+ if (!util.stdoutLineMatches(String(random), smokeTestStdout)) {
119
130
  debug('Smoke test failed because could not find %d in:', random, result);
120
131
 
121
- return throwFormErrorText(errors.smokeTestFailure(smokeTestCommand, false))(result.stderr || result.stdout);
132
+ var smokeTestStderr = _.get(result, 'stderr', '');
133
+ var errorText = smokeTestStderr || smokeTestStdout;
134
+
135
+ return throwFormErrorText(errors.smokeTestFailure(smokeTestCommand, false))(errorText);
122
136
  }
123
137
  });
124
138
  };
@@ -190,7 +204,6 @@ function testBinary(version, binaryDir, options) {
190
204
 
191
205
  var maybeVerify = function maybeVerify(installedVersion, binaryDir, options) {
192
206
  return state.getBinaryVerifiedAsync(binaryDir).then(function (isVerified) {
193
-
194
207
  debug('is Verified ?', isVerified);
195
208
 
196
209
  var shouldVerify = !isVerified;
@@ -224,7 +237,7 @@ var start = function start() {
224
237
  dev: false,
225
238
  force: false,
226
239
  welcomeMessage: true,
227
- smokeTestTimeout: 10000
240
+ smokeTestTimeout: VERIFY_TEST_RUNNER_TIMEOUT_MS
228
241
  });
229
242
 
230
243
  if (options.dev) {
@@ -236,6 +249,7 @@ var start = function start() {
236
249
 
237
250
  debug('CYPRESS_RUN_BINARY exists, =', envBinaryPath);
238
251
  logger.log(stripIndent(_templateObject3, chalk.yellow('Note:'), chalk.white('CYPRESS_RUN_BINARY='), chalk.cyan(envBinaryPath)));
252
+
239
253
  logger.log();
240
254
 
241
255
  return util.isExecutableAsync(envBinaryPath).then(function (isExecutable) {
@@ -270,7 +284,6 @@ var start = function start() {
270
284
  }).then(function () {
271
285
  return state.getBinaryPkgVersionAsync(binaryDir);
272
286
  }).then(function (binaryVersion) {
273
-
274
287
  if (!binaryVersion) {
275
288
  debug('no Cypress binary found for cli version ', packageVersion);
276
289
 
@@ -299,6 +312,25 @@ var start = function start() {
299
312
  });
300
313
  };
301
314
 
315
+ var isLinuxLike = function isLinuxLike() {
316
+ return os.platform() !== 'win32';
317
+ };
318
+
319
+ /**
320
+ * Returns true if running on a system where Electron needs "--no-sandbox" flag.
321
+ * @see https://crbug.com/638180
322
+ *
323
+ * On Debian we had problems running in sandbox even for non-root users.
324
+ * @see https://github.com/cypress-io/cypress/issues/5434
325
+ * Seems there is a lot of discussion around this issue among Electron users
326
+ * @see https://github.com/electron/electron/issues/17972
327
+ */
328
+ var needsSandbox = function needsSandbox() {
329
+ return isLinuxLike();
330
+ };
331
+
302
332
  module.exports = {
303
- start: start
333
+ start: start,
334
+ VERIFY_TEST_RUNNER_TIMEOUT_MS: VERIFY_TEST_RUNNER_TIMEOUT_MS,
335
+ needsSandbox: needsSandbox
304
336
  };