verify.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. 'use strict';
  2. var _templateObject = _taggedTemplateLiteral(['\n Cypress executable not found at: ', '\n '], ['\n Cypress executable not found at: ', '\n ']),
  3. _templateObject2 = _taggedTemplateLiteral(['\n It looks like this is your first time using Cypress: ', '\n '], ['\n It looks like this is your first time using Cypress: ', '\n ']),
  4. _templateObject3 = _taggedTemplateLiteral(['\n ', ' You have set the environment variable:\n\n ', '', '\n\n This overrides the default Cypress binary path used.\n '], ['\n ', ' You have set the environment variable:\n\n ', '', '\n\n This overrides the default Cypress binary path used.\n ']),
  5. _templateObject4 = _taggedTemplateLiteral(['\n The supplied binary path is not executable\n '], ['\n The supplied binary path is not executable\n ']),
  6. _templateObject5 = _taggedTemplateLiteral(['\n\n\n ', ' Warning: Binary version ', ' does not match the expected package version ', '\n\n These versions may not work properly together.\n '], ['\n\n\n ', ' Warning: Binary version ', ' does not match the expected package version ', '\n\n These versions may not work properly together.\n ']);
  7. function _taggedTemplateLiteral(strings, raw) { return Object.freeze(Object.defineProperties(strings, { raw: { value: Object.freeze(raw) } })); }
  8. var _ = require('lodash');
  9. var chalk = require('chalk');
  10. var Listr = require('listr');
  11. var debug = require('debug')('cypress:cli');
  12. var verbose = require('@cypress/listr-verbose-renderer');
  13. var _require = require('common-tags'),
  14. stripIndent = _require.stripIndent;
  15. var Promise = require('bluebird');
  16. var logSymbols = require('log-symbols');
  17. var path = require('path');
  18. var os = require('os');
  19. var _require2 = require('../errors'),
  20. throwFormErrorText = _require2.throwFormErrorText,
  21. errors = _require2.errors;
  22. var util = require('../util');
  23. var logger = require('../logger');
  24. var xvfb = require('../exec/xvfb');
  25. var state = require('./state');
  26. var VERIFY_TEST_RUNNER_TIMEOUT_MS = 30000;
  27. var checkExecutable = function checkExecutable(binaryDir) {
  28. var executable = state.getPathToExecutable(binaryDir);
  29. debug('checking if executable exists', executable);
  30. return util.isExecutableAsync(executable).then(function (isExecutable) {
  31. debug('Binary is executable? :', isExecutable);
  32. if (!isExecutable) {
  33. return throwFormErrorText(errors.binaryNotExecutable(executable))();
  34. }
  35. }).catch({ code: 'ENOENT' }, function () {
  36. if (util.isCi()) {
  37. return throwFormErrorText(errors.notInstalledCI(executable))();
  38. }
  39. return throwFormErrorText(errors.missingApp(binaryDir))(stripIndent(_templateObject, chalk.cyan(executable)));
  40. });
  41. };
  42. var runSmokeTest = function runSmokeTest(binaryDir, options) {
  43. var executable = state.getPathToExecutable(binaryDir);
  44. var onSmokeTestError = function onSmokeTestError(smokeTestCommand, linuxWithDisplayEnv) {
  45. return function (err) {
  46. debug('Smoke test failed:', err);
  47. var errMessage = err.stderr || err.message;
  48. debug('error message:', errMessage);
  49. if (err.timedOut) {
  50. debug('error timedOut is true');
  51. return throwFormErrorText(errors.smokeTestFailure(smokeTestCommand, true))(errMessage);
  52. }
  53. if (linuxWithDisplayEnv && util.isBrokenGtkDisplay(errMessage)) {
  54. util.logBrokenGtkDisplayWarning();
  55. return throwFormErrorText(errors.invalidSmokeTestDisplayError)(errMessage);
  56. }
  57. return throwFormErrorText(errors.missingDependency)(errMessage);
  58. };
  59. };
  60. var needsXvfb = xvfb.isNeeded();
  61. debug('needs Xvfb?', needsXvfb);
  62. /**
  63. * Spawn Cypress running smoke test to check if all operating system
  64. * dependencies are good.
  65. */
  66. var spawn = function spawn(linuxWithDisplayEnv) {
  67. var random = _.random(0, 1000);
  68. var args = ['--smoke-test', '--ping=' + random];
  69. if (needsSandbox()) {
  70. // electron requires --no-sandbox to run as root
  71. debug('disabling Electron sandbox');
  72. args.unshift('--no-sandbox');
  73. }
  74. if (options.dev) {
  75. executable = 'node';
  76. args.unshift(path.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js'));
  77. }
  78. var smokeTestCommand = executable + ' ' + args.join(' ');
  79. debug('running smoke test');
  80. debug('using Cypress executable %s', executable);
  81. debug('smoke test command:', smokeTestCommand);
  82. debug('smoke test timeout %d ms', options.smokeTestTimeout);
  83. var env = _.extend({}, process.env, {
  84. ELECTRON_ENABLE_LOGGING: true
  85. });
  86. var stdioOptions = _.extend({}, {
  87. env: env,
  88. timeout: options.smokeTestTimeout
  89. });
  90. return Promise.resolve(util.exec(executable, args, stdioOptions)).catch(onSmokeTestError(smokeTestCommand, linuxWithDisplayEnv)).then(function (result) {
  91. // TODO: when execa > 1.1 is released
  92. // change this to `result.all` for both stderr and stdout
  93. // use lodash to be robust during tests against null result or missing stdout
  94. var smokeTestStdout = _.get(result, 'stdout', '');
  95. debug('smoke test stdout "%s"', smokeTestStdout);
  96. if (!util.stdoutLineMatches(String(random), smokeTestStdout)) {
  97. debug('Smoke test failed because could not find %d in:', random, result);
  98. var smokeTestStderr = _.get(result, 'stderr', '');
  99. var errorText = smokeTestStderr || smokeTestStdout;
  100. return throwFormErrorText(errors.smokeTestFailure(smokeTestCommand, false))(errorText);
  101. }
  102. });
  103. };
  104. var spawnInXvfb = function spawnInXvfb(linuxWithDisplayEnv) {
  105. return xvfb.start().then(function () {
  106. return spawn(linuxWithDisplayEnv);
  107. }).finally(xvfb.stop);
  108. };
  109. var userFriendlySpawn = function userFriendlySpawn(linuxWithDisplayEnv) {
  110. debug('spawning, should retry on display problem?', Boolean(linuxWithDisplayEnv));
  111. return spawn(linuxWithDisplayEnv).catch({ code: 'INVALID_SMOKE_TEST_DISPLAY_ERROR' }, function () {
  112. return spawnInXvfb(linuxWithDisplayEnv);
  113. });
  114. };
  115. if (needsXvfb) {
  116. return spawnInXvfb();
  117. }
  118. // if we are on linux and there's already a DISPLAY
  119. // set, then we may need to rerun cypress after
  120. // spawning our own Xvfb server
  121. var linuxWithDisplayEnv = util.isPossibleLinuxWithIncorrectDisplay();
  122. return userFriendlySpawn(linuxWithDisplayEnv);
  123. };
  124. function testBinary(version, binaryDir, options) {
  125. debug('running binary verification check', version);
  126. logger.log(stripIndent(_templateObject2, chalk.cyan(version)));
  127. logger.log();
  128. // if we are running in CI then use
  129. // the verbose renderer else use
  130. // the default
  131. var renderer = util.isCi() ? verbose : 'default';
  132. if (logger.logLevel() === 'silent') renderer = 'silent';
  133. var rendererOptions = {
  134. renderer: renderer
  135. };
  136. var tasks = new Listr([{
  137. title: util.titleize('Verifying Cypress can run', chalk.gray(binaryDir)),
  138. task: function task(ctx, _task) {
  139. debug('clearing out the verified version');
  140. return state.clearBinaryStateAsync(binaryDir).then(function () {
  141. return Promise.all([runSmokeTest(binaryDir, options), Promise.resolve().delay(1500)] // good user experience
  142. );
  143. }).then(function () {
  144. debug('write verified: true');
  145. return state.writeBinaryVerifiedAsync(true, binaryDir);
  146. }).then(function () {
  147. util.setTaskTitle(_task, util.titleize(chalk.green('Verified Cypress!'), chalk.gray(binaryDir)), rendererOptions.renderer);
  148. });
  149. }
  150. }], rendererOptions);
  151. return tasks.run();
  152. }
  153. var maybeVerify = function maybeVerify(installedVersion, binaryDir, options) {
  154. return state.getBinaryVerifiedAsync(binaryDir).then(function (isVerified) {
  155. debug('is Verified ?', isVerified);
  156. var shouldVerify = !isVerified;
  157. // force verify if options.force
  158. if (options.force) {
  159. debug('force verify');
  160. shouldVerify = true;
  161. }
  162. if (shouldVerify) {
  163. return testBinary(installedVersion, binaryDir, options).then(function () {
  164. if (options.welcomeMessage) {
  165. logger.log();
  166. logger.log('Opening Cypress...');
  167. }
  168. });
  169. }
  170. });
  171. };
  172. var start = function start() {
  173. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  174. debug('verifying Cypress app');
  175. var packageVersion = util.pkgVersion();
  176. var binaryDir = state.getBinaryDir(packageVersion);
  177. _.defaults(options, {
  178. dev: false,
  179. force: false,
  180. welcomeMessage: true,
  181. smokeTestTimeout: VERIFY_TEST_RUNNER_TIMEOUT_MS
  182. });
  183. if (options.dev) {
  184. return runSmokeTest('', options);
  185. }
  186. var parseBinaryEnvVar = function parseBinaryEnvVar() {
  187. var envBinaryPath = util.getEnv('CYPRESS_RUN_BINARY');
  188. debug('CYPRESS_RUN_BINARY exists, =', envBinaryPath);
  189. logger.log(stripIndent(_templateObject3, chalk.yellow('Note:'), chalk.white('CYPRESS_RUN_BINARY='), chalk.cyan(envBinaryPath)));
  190. logger.log();
  191. return util.isExecutableAsync(envBinaryPath).then(function (isExecutable) {
  192. debug('CYPRESS_RUN_BINARY is executable? :', isExecutable);
  193. if (!isExecutable) {
  194. return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))(stripIndent(_templateObject4));
  195. }
  196. }).then(function () {
  197. return state.parseRealPlatformBinaryFolderAsync(envBinaryPath);
  198. }).then(function (envBinaryDir) {
  199. if (!envBinaryDir) {
  200. return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))();
  201. }
  202. debug('CYPRESS_RUN_BINARY has binaryDir:', envBinaryDir);
  203. binaryDir = envBinaryDir;
  204. }).catch({ code: 'ENOENT' }, function (err) {
  205. return throwFormErrorText(errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))(err.message);
  206. });
  207. };
  208. return Promise.try(function () {
  209. debug('checking environment variables');
  210. if (util.getEnv('CYPRESS_RUN_BINARY')) {
  211. return parseBinaryEnvVar();
  212. }
  213. }).then(function () {
  214. return checkExecutable(binaryDir);
  215. }).tap(function () {
  216. return debug('binaryDir is ', binaryDir);
  217. }).then(function () {
  218. return state.getBinaryPkgVersionAsync(binaryDir);
  219. }).then(function (binaryVersion) {
  220. if (!binaryVersion) {
  221. debug('no Cypress binary found for cli version ', packageVersion);
  222. return throwFormErrorText(errors.missingApp(binaryDir))('\n Cannot read binary version from: ' + chalk.cyan(state.getBinaryPkgPath(binaryDir)) + '\n ');
  223. }
  224. debug('Found binary version ' + chalk.green(binaryVersion) + ' installed in: ' + chalk.cyan(binaryDir));
  225. if (binaryVersion !== packageVersion) {
  226. // warn if we installed with CYPRESS_INSTALL_BINARY or changed version
  227. // in the package.json
  228. logger.log('Found binary version ' + chalk.green(binaryVersion) + ' installed in: ' + chalk.cyan(binaryDir));
  229. logger.log();
  230. logger.warn(stripIndent(_templateObject5, logSymbols.warning, chalk.green(binaryVersion), chalk.green(packageVersion)));
  231. logger.log();
  232. }
  233. return maybeVerify(binaryVersion, binaryDir, options);
  234. }).catch(function (err) {
  235. if (err.known) {
  236. throw err;
  237. }
  238. return throwFormErrorText(errors.unexpected)(err.stack);
  239. });
  240. };
  241. var isLinuxLike = function isLinuxLike() {
  242. return os.platform() !== 'win32';
  243. };
  244. /**
  245. * Returns true if running on a system where Electron needs "--no-sandbox" flag.
  246. * @see https://crbug.com/638180
  247. *
  248. * On Debian we had problems running in sandbox even for non-root users.
  249. * @see https://github.com/cypress-io/cypress/issues/5434
  250. * Seems there is a lot of discussion around this issue among Electron users
  251. * @see https://github.com/electron/electron/issues/17972
  252. */
  253. var needsSandbox = function needsSandbox() {
  254. return isLinuxLike();
  255. };
  256. module.exports = {
  257. start: start,
  258. VERIFY_TEST_RUNNER_TIMEOUT_MS: VERIFY_TEST_RUNNER_TIMEOUT_MS,
  259. needsSandbox: needsSandbox
  260. };