cypress 3.3.2 → 3.6.0

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.
@@ -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
  };