cypress 15.1.0 → 15.3.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.
Files changed (49) hide show
  1. package/bin/cypress +3 -1
  2. package/dist/VerboseRenderer.js +61 -0
  3. package/dist/cli.js +544 -0
  4. package/dist/cypress.js +104 -0
  5. package/dist/errors.js +391 -0
  6. package/dist/exec/info.js +103 -0
  7. package/dist/exec/open.js +103 -0
  8. package/dist/exec/run.js +177 -0
  9. package/dist/exec/shared.js +55 -0
  10. package/dist/exec/spawn.js +301 -0
  11. package/dist/exec/versions.js +67 -0
  12. package/dist/exec/xvfb.js +118 -0
  13. package/dist/index.js +52 -0
  14. package/dist/index.mjs +9 -0
  15. package/dist/logger.js +55 -0
  16. package/dist/tasks/cache.js +144 -0
  17. package/dist/tasks/download.js +304 -0
  18. package/dist/tasks/get-folder-size.js +44 -0
  19. package/dist/tasks/install.js +326 -0
  20. package/dist/tasks/state.js +184 -0
  21. package/dist/tasks/unzip.js +192 -0
  22. package/dist/tasks/verify.js +303 -0
  23. package/dist/util.js +452 -0
  24. package/package.json +10 -13
  25. package/types/cypress-automation.d.ts +2 -1
  26. package/types/cypress.d.ts +1 -0
  27. package/index.js +0 -27
  28. package/index.mjs +0 -17
  29. package/lib/VerboseRenderer.js +0 -58
  30. package/lib/cli.js +0 -411
  31. package/lib/cypress.js +0 -98
  32. package/lib/errors.js +0 -392
  33. package/lib/exec/info.js +0 -92
  34. package/lib/exec/open.js +0 -90
  35. package/lib/exec/run.js +0 -176
  36. package/lib/exec/shared.js +0 -62
  37. package/lib/exec/spawn.js +0 -247
  38. package/lib/exec/versions.js +0 -53
  39. package/lib/exec/xvfb.js +0 -93
  40. package/lib/fs.js +0 -4
  41. package/lib/logger.js +0 -50
  42. package/lib/tasks/cache.js +0 -132
  43. package/lib/tasks/download.js +0 -324
  44. package/lib/tasks/get-folder-size.js +0 -33
  45. package/lib/tasks/install.js +0 -368
  46. package/lib/tasks/state.js +0 -185
  47. package/lib/tasks/unzip.js +0 -200
  48. package/lib/tasks/verify.js +0 -300
  49. package/lib/util.js +0 -448
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const lodash_1 = __importDefault(require("lodash"));
16
+ const os_1 = __importDefault(require("os"));
17
+ const path_1 = __importDefault(require("path"));
18
+ const untildify_1 = __importDefault(require("untildify"));
19
+ const debug_1 = __importDefault(require("debug"));
20
+ const process_1 = require("process");
21
+ const fs_extra_1 = __importDefault(require("fs-extra"));
22
+ const util_1 = __importDefault(require("../util"));
23
+ const debug = (0, debug_1.default)('cypress:cli');
24
+ const getPlatformExecutable = () => {
25
+ const platform = os_1.default.platform();
26
+ switch (platform) {
27
+ case 'darwin': return 'Contents/MacOS/Cypress';
28
+ case 'linux': return 'Cypress';
29
+ case 'win32': return 'Cypress.exe';
30
+ // TODO handle this error using our standard
31
+ default: throw new Error(`Platform: "${platform}" is not supported.`);
32
+ }
33
+ };
34
+ const getPlatFormBinaryFolder = () => {
35
+ const platform = os_1.default.platform();
36
+ switch (platform) {
37
+ case 'darwin': return 'Cypress.app';
38
+ case 'linux': return 'Cypress';
39
+ case 'win32': return 'Cypress';
40
+ // TODO handle this error using our standard
41
+ default: throw new Error(`Platform: "${platform}" is not supported.`);
42
+ }
43
+ };
44
+ const getBinaryPkgPath = (binaryDir) => {
45
+ const platform = os_1.default.platform();
46
+ switch (platform) {
47
+ case 'darwin': return path_1.default.join(binaryDir, 'Contents', 'Resources', 'app', 'package.json');
48
+ case 'linux': return path_1.default.join(binaryDir, 'resources', 'app', 'package.json');
49
+ case 'win32': return path_1.default.join(binaryDir, 'resources', 'app', 'package.json');
50
+ // TODO handle this error using our standard
51
+ default: throw new Error(`Platform: "${platform}" is not supported.`);
52
+ }
53
+ };
54
+ /**
55
+ * Get path to binary directory
56
+ */
57
+ const getBinaryDir = (version = util_1.default.pkgVersion()) => {
58
+ return path_1.default.join(getVersionDir(version), getPlatFormBinaryFolder());
59
+ };
60
+ const getVersionDir = (version = util_1.default.pkgVersion(), buildInfo = util_1.default.pkgBuildInfo()) => {
61
+ if (buildInfo && !buildInfo.stable) {
62
+ version = ['beta', version, buildInfo.commitBranch, buildInfo.commitSha.slice(0, 8)].join('-');
63
+ }
64
+ return path_1.default.join(getCacheDir(), version);
65
+ };
66
+ /**
67
+ * When executing "npm postinstall" hook, the working directory is set to
68
+ * "<current folder>/node_modules/cypress", which can be surprising when using relative paths.
69
+ */
70
+ const isInstallingFromPostinstallHook = () => {
71
+ // individual folders
72
+ const cwdFolders = (0, process_1.cwd)().split(path_1.default.sep);
73
+ const length = cwdFolders.length;
74
+ return cwdFolders[length - 2] === 'node_modules' && cwdFolders[length - 1] === 'cypress';
75
+ };
76
+ const getCacheDir = () => {
77
+ let cache_directory = util_1.default.getCacheDir();
78
+ if (util_1.default.getEnv('CYPRESS_CACHE_FOLDER')) {
79
+ const envVarCacheDir = (0, untildify_1.default)(util_1.default.getEnv('CYPRESS_CACHE_FOLDER'));
80
+ debug('using environment variable CYPRESS_CACHE_FOLDER %s', envVarCacheDir);
81
+ if (!path_1.default.isAbsolute(envVarCacheDir) && isInstallingFromPostinstallHook()) {
82
+ const packageRootFolder = path_1.default.join('..', '..', envVarCacheDir);
83
+ cache_directory = path_1.default.resolve(packageRootFolder);
84
+ debug('installing from postinstall hook, original root folder is %s', packageRootFolder);
85
+ debug('and resolved cache directory is %s', cache_directory);
86
+ }
87
+ else {
88
+ cache_directory = path_1.default.resolve(envVarCacheDir);
89
+ }
90
+ }
91
+ return cache_directory;
92
+ };
93
+ const parseRealPlatformBinaryFolderAsync = (binaryPath) => __awaiter(void 0, void 0, void 0, function* () {
94
+ const realPath = yield fs_extra_1.default.realpath(binaryPath);
95
+ debug('CYPRESS_RUN_BINARY has realpath:', realPath);
96
+ if (!realPath.toString().endsWith(getPlatformExecutable())) {
97
+ return false;
98
+ }
99
+ if (os_1.default.platform() === 'darwin') {
100
+ return path_1.default.resolve(realPath, '..', '..', '..');
101
+ }
102
+ return path_1.default.resolve(realPath, '..');
103
+ });
104
+ const getDistDir = () => {
105
+ return path_1.default.join(__dirname, '..', '..', 'dist');
106
+ };
107
+ /**
108
+ * Returns full filename to the file that keeps the Test Runner verification state as JSON text.
109
+ * Note: the binary state file will be stored one level up from the given binary folder.
110
+ * @param {string} binaryDir - full path to the folder holding the binary.
111
+ */
112
+ const getBinaryStatePath = (binaryDir) => {
113
+ return path_1.default.join(binaryDir, '..', 'binary_state.json');
114
+ };
115
+ const getBinaryStateContentsAsync = (binaryDir) => __awaiter(void 0, void 0, void 0, function* () {
116
+ const fullPath = getBinaryStatePath(binaryDir);
117
+ try {
118
+ const contents = yield fs_extra_1.default.readJson(fullPath);
119
+ debug('binary_state.json contents:', contents);
120
+ return contents;
121
+ }
122
+ catch (error) {
123
+ if (error.code === 'ENOENT' || error instanceof SyntaxError) {
124
+ debug('could not read binary_state.json file at "%s"', fullPath);
125
+ return {};
126
+ }
127
+ throw error;
128
+ }
129
+ });
130
+ const getBinaryVerifiedAsync = (binaryDir) => __awaiter(void 0, void 0, void 0, function* () {
131
+ const contents = yield getBinaryStateContentsAsync(binaryDir);
132
+ return contents.verified;
133
+ });
134
+ const clearBinaryStateAsync = (binaryDir) => __awaiter(void 0, void 0, void 0, function* () {
135
+ yield fs_extra_1.default.remove(getBinaryStatePath(binaryDir));
136
+ });
137
+ /**
138
+ * Writes the new binary status.
139
+ * @param {boolean} verified The new test runner state after smoke test
140
+ * @param {string} binaryDir Folder holding the binary
141
+ * @returns {Promise<void>} returns a promise
142
+ */
143
+ const writeBinaryVerifiedAsync = (verified, binaryDir) => __awaiter(void 0, void 0, void 0, function* () {
144
+ const contents = yield getBinaryStateContentsAsync(binaryDir);
145
+ yield fs_extra_1.default.outputJson(getBinaryStatePath(binaryDir), lodash_1.default.extend(contents, { verified }), { spaces: 2 });
146
+ });
147
+ const getPathToExecutable = (binaryDir) => {
148
+ return path_1.default.join(binaryDir, getPlatformExecutable());
149
+ };
150
+ /**
151
+ * Resolves with an object read from the binary app package.json file.
152
+ * If the file does not exist resolves with null
153
+ */
154
+ const getBinaryPkgAsync = (binaryDir) => __awaiter(void 0, void 0, void 0, function* () {
155
+ const pathToPackageJson = getBinaryPkgPath(binaryDir);
156
+ debug('Reading binary package.json from:', pathToPackageJson);
157
+ const exists = yield fs_extra_1.default.pathExists(pathToPackageJson);
158
+ if (!exists) {
159
+ return null;
160
+ }
161
+ return fs_extra_1.default.readJson(pathToPackageJson);
162
+ });
163
+ const getBinaryPkgVersion = (o) => lodash_1.default.get(o, 'version', null);
164
+ const getBinaryElectronVersion = (o) => lodash_1.default.get(o, 'electronVersion', null);
165
+ const getBinaryElectronNodeVersion = (o) => lodash_1.default.get(o, 'electronNodeVersion', null);
166
+ const stateModule = {
167
+ getPathToExecutable,
168
+ getPlatformExecutable,
169
+ // those names start to sound like Java
170
+ getBinaryElectronNodeVersion,
171
+ getBinaryElectronVersion,
172
+ getBinaryPkgVersion,
173
+ getBinaryVerifiedAsync,
174
+ getBinaryPkgAsync,
175
+ getBinaryPkgPath,
176
+ getBinaryDir,
177
+ getCacheDir,
178
+ clearBinaryStateAsync,
179
+ writeBinaryVerifiedAsync,
180
+ parseRealPlatformBinaryFolderAsync,
181
+ getDistDir,
182
+ getVersionDir,
183
+ };
184
+ exports.default = stateModule;
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const lodash_1 = __importDefault(require("lodash"));
16
+ const child_process_1 = __importDefault(require("child_process"));
17
+ const os_1 = __importDefault(require("os"));
18
+ const yauzl_1 = __importDefault(require("yauzl"));
19
+ const debug_1 = __importDefault(require("debug"));
20
+ const extract_zip_1 = __importDefault(require("extract-zip"));
21
+ const readline_1 = __importDefault(require("readline"));
22
+ const fs_extra_1 = __importDefault(require("fs-extra"));
23
+ const errors_1 = require("../errors");
24
+ const util_1 = __importDefault(require("../util"));
25
+ const assert_1 = __importDefault(require("assert"));
26
+ const debug = (0, debug_1.default)('cypress:cli:unzip');
27
+ const unzipTools = {
28
+ extract: extract_zip_1.default,
29
+ };
30
+ // expose this function for simple testing
31
+ const unzip = (_a) => __awaiter(void 0, [_a], void 0, function* ({ zipFilePath, installDir, progress }) {
32
+ debug('unzipping from %s', zipFilePath);
33
+ debug('into', installDir);
34
+ if (!zipFilePath) {
35
+ throw new Error('Missing zip filename');
36
+ }
37
+ const startTime = Date.now();
38
+ let yauzlDoneTime = 0;
39
+ yield fs_extra_1.default.ensureDir(installDir);
40
+ yield new Promise((resolve, reject) => {
41
+ return yauzl_1.default.open(zipFilePath, (err, zipFile) => {
42
+ yauzlDoneTime = Date.now();
43
+ if (err) {
44
+ debug('error using yauzl %s', err.message);
45
+ return reject(err);
46
+ }
47
+ const total = zipFile.entryCount;
48
+ debug('zipFile entries count', total);
49
+ const started = new Date();
50
+ let percent = 0;
51
+ let count = 0;
52
+ const notify = (percent) => {
53
+ const elapsed = +new Date() - +started;
54
+ const eta = util_1.default.calculateEta(percent, elapsed);
55
+ progress.onProgress(percent, util_1.default.secsRemaining(eta));
56
+ };
57
+ const tick = () => {
58
+ count += 1;
59
+ percent = ((count / total) * 100);
60
+ const displayPercent = percent.toFixed(0);
61
+ return notify(Number(displayPercent));
62
+ };
63
+ const unzipWithNode = () => __awaiter(void 0, void 0, void 0, function* () {
64
+ debug('unzipping with node.js (slow)');
65
+ const opts = {
66
+ dir: installDir,
67
+ onEntry: tick,
68
+ };
69
+ debug('calling Node extract tool %s %o', zipFilePath, opts);
70
+ try {
71
+ yield unzipTools.extract(zipFilePath, opts);
72
+ debug('node unzip finished');
73
+ return resolve();
74
+ }
75
+ catch (err) {
76
+ const error = err || new Error('Unknown error with Node extract tool');
77
+ debug('error %s', error.message);
78
+ return reject(error);
79
+ }
80
+ });
81
+ const unzipFallback = lodash_1.default.once(unzipWithNode);
82
+ const unzipWithUnzipTool = () => {
83
+ debug('unzipping via `unzip`');
84
+ const inflatingRe = /inflating:/;
85
+ const sp = child_process_1.default.spawn('unzip', ['-o', zipFilePath, '-d', installDir]);
86
+ sp.on('error', (err) => {
87
+ debug('unzip tool error: %s', err.message);
88
+ unzipFallback();
89
+ });
90
+ sp.on('close', (code) => {
91
+ debug('unzip tool close with code %d', code);
92
+ if (code === 0) {
93
+ percent = 100;
94
+ notify(percent);
95
+ return resolve();
96
+ }
97
+ debug('`unzip` failed %o', { code });
98
+ return unzipFallback();
99
+ });
100
+ sp.stdout.on('data', (data) => {
101
+ if (inflatingRe.test(data)) {
102
+ return tick();
103
+ }
104
+ });
105
+ sp.stderr.on('data', (data) => {
106
+ debug('`unzip` stderr %s', data);
107
+ });
108
+ };
109
+ // we attempt to first unzip with the native osx
110
+ // ditto because its less likely to have problems
111
+ // with corruption, symlinks, or icons causing failures
112
+ // and can handle resource forks
113
+ // http://automatica.com.au/2011/02/unzip-mac-os-x-zip-in-terminal/
114
+ const unzipWithOsx = () => {
115
+ debug('unzipping via `ditto`');
116
+ const copyingFileRe = /^copying file/;
117
+ const sp = child_process_1.default.spawn('ditto', ['-xkV', zipFilePath, installDir]);
118
+ // f-it just unzip with node
119
+ sp.on('error', (err) => {
120
+ debug(err.message);
121
+ unzipFallback();
122
+ });
123
+ sp.on('close', (code) => {
124
+ if (code === 0) {
125
+ // make sure we get to 100% on the progress bar
126
+ // because reading in lines is not really accurate
127
+ percent = 100;
128
+ notify(percent);
129
+ return resolve();
130
+ }
131
+ debug('`ditto` failed %o', { code });
132
+ return unzipFallback();
133
+ });
134
+ return readline_1.default.createInterface({
135
+ input: sp.stderr,
136
+ })
137
+ .on('line', (line) => {
138
+ if (copyingFileRe.test(line)) {
139
+ return tick();
140
+ }
141
+ });
142
+ };
143
+ switch (os_1.default.platform()) {
144
+ case 'darwin':
145
+ return unzipWithOsx();
146
+ case 'linux':
147
+ return unzipWithUnzipTool();
148
+ case 'win32':
149
+ return unzipWithNode();
150
+ default:
151
+ return;
152
+ }
153
+ });
154
+ });
155
+ debug('unzip completed %o', {
156
+ yauzlMs: yauzlDoneTime - startTime,
157
+ unzipMs: Date.now() - yauzlDoneTime,
158
+ });
159
+ });
160
+ function isMaybeWindowsMaxPathLengthError(err) {
161
+ return os_1.default.platform() === 'win32' && err.code === 'ENOENT' && err.syscall === 'realpath';
162
+ }
163
+ const start = (_a) => __awaiter(void 0, [_a], void 0, function* ({ zipFilePath, installDir, progress }) {
164
+ assert_1.default.ok(lodash_1.default.isString(installDir) && !lodash_1.default.isEmpty(installDir), 'missing installDir');
165
+ if (!progress) {
166
+ progress = { onProgress: () => {
167
+ return {};
168
+ } };
169
+ }
170
+ try {
171
+ const installDirExists = yield fs_extra_1.default.pathExists(installDir);
172
+ if (installDirExists) {
173
+ debug('removing existing unzipped binary', installDir);
174
+ yield fs_extra_1.default.remove(installDir);
175
+ }
176
+ yield unzip({ zipFilePath, installDir, progress });
177
+ }
178
+ catch (err) {
179
+ const errorTemplate = isMaybeWindowsMaxPathLengthError(err) ?
180
+ errors_1.errors.failedUnzipWindowsMaxPathLength
181
+ : errors_1.errors.failedUnzip;
182
+ yield (0, errors_1.throwFormErrorText)(errorTemplate)(err);
183
+ }
184
+ });
185
+ const unzipModule = {
186
+ start,
187
+ utils: {
188
+ unzip,
189
+ unzipTools,
190
+ },
191
+ };
192
+ exports.default = unzipModule;
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.needsSandbox = exports.start = exports.verifyTestRunnerTimeoutMs = void 0;
16
+ const lodash_1 = __importDefault(require("lodash"));
17
+ const chalk_1 = __importDefault(require("chalk"));
18
+ const listr2_1 = require("listr2");
19
+ const debug_1 = __importDefault(require("debug"));
20
+ const common_tags_1 = require("common-tags");
21
+ const bluebird_1 = __importDefault(require("bluebird"));
22
+ const log_symbols_1 = __importDefault(require("log-symbols"));
23
+ const path_1 = __importDefault(require("path"));
24
+ const os_1 = __importDefault(require("os"));
25
+ const VerboseRenderer_1 = __importDefault(require("../VerboseRenderer"));
26
+ const errors_1 = require("../errors");
27
+ const util_1 = __importDefault(require("../util"));
28
+ const logger_1 = __importDefault(require("../logger"));
29
+ const xvfb_1 = __importDefault(require("../exec/xvfb"));
30
+ const state_1 = __importDefault(require("./state"));
31
+ const debug = (0, debug_1.default)('cypress:cli');
32
+ const verifyTestRunnerTimeoutMs = () => {
33
+ const verifyTimeout = +((util_1.default === null || util_1.default === void 0 ? void 0 : util_1.default.getEnv('CYPRESS_VERIFY_TIMEOUT')) || 'NaN');
34
+ if (lodash_1.default.isNumber(verifyTimeout) && !lodash_1.default.isNaN(verifyTimeout)) {
35
+ return verifyTimeout;
36
+ }
37
+ return 30000;
38
+ };
39
+ exports.verifyTestRunnerTimeoutMs = verifyTestRunnerTimeoutMs;
40
+ const checkExecutable = (binaryDir) => __awaiter(void 0, void 0, void 0, function* () {
41
+ const executable = state_1.default.getPathToExecutable(binaryDir);
42
+ debug('checking if executable exists', executable);
43
+ try {
44
+ const isExecutable = yield util_1.default.isExecutableAsync(executable);
45
+ debug('Binary is executable? :', isExecutable);
46
+ if (!isExecutable) {
47
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.binaryNotExecutable(executable))();
48
+ }
49
+ }
50
+ catch (err) {
51
+ if (err.code === 'ENOENT') {
52
+ if (util_1.default.isCi()) {
53
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.notInstalledCI(executable))();
54
+ }
55
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.missingApp(binaryDir))((0, common_tags_1.stripIndent) `
56
+ Cypress executable not found at: ${chalk_1.default.cyan(executable)}
57
+ `);
58
+ }
59
+ throw err;
60
+ }
61
+ });
62
+ const runSmokeTest = (binaryDir, options) => {
63
+ let executable = state_1.default.getPathToExecutable(binaryDir);
64
+ const needsXvfb = xvfb_1.default.isNeeded();
65
+ debug('needs Xvfb?', needsXvfb);
66
+ /**
67
+ * Spawn Cypress running smoke test to check if all operating system
68
+ * dependencies are good.
69
+ */
70
+ const spawn = (linuxWithDisplayEnv) => __awaiter(void 0, void 0, void 0, function* () {
71
+ const random = lodash_1.default.random(0, 1000);
72
+ const args = ['--smoke-test', `--ping=${random}`];
73
+ if ((0, exports.needsSandbox)()) {
74
+ // electron requires --no-sandbox to run as root
75
+ debug('disabling Electron sandbox');
76
+ args.unshift('--no-sandbox');
77
+ }
78
+ if (options.dev) {
79
+ executable = 'node';
80
+ args.unshift(path_1.default.resolve(__dirname, '..', '..', '..', 'scripts', 'start.js'));
81
+ }
82
+ const smokeTestCommand = `${executable} ${args.join(' ')}`;
83
+ debug('running smoke test');
84
+ debug('using Cypress executable %s', executable);
85
+ debug('smoke test command:', smokeTestCommand);
86
+ debug('smoke test timeout %d ms', options.smokeTestTimeout);
87
+ const stdioOptions = lodash_1.default.extend({}, {
88
+ env: Object.assign(Object.assign({}, process.env), { FORCE_COLOR: '0' }),
89
+ timeout: options.smokeTestTimeout,
90
+ });
91
+ try {
92
+ const result = yield util_1.default.exec(executable, args, stdioOptions);
93
+ // TODO: when execa > 1.1 is released
94
+ // change this to `result.all` for both stderr and stdout
95
+ // use lodash to be robust during tests against null result or missing stdout
96
+ const smokeTestStdout = lodash_1.default.get(result, 'stdout', '');
97
+ debug('smoke test stdout "%s"', smokeTestStdout);
98
+ if (!util_1.default.stdoutLineMatches(String(random), smokeTestStdout)) {
99
+ debug('Smoke test failed because could not find %d in:', random, result);
100
+ const smokeTestStderr = lodash_1.default.get(result, 'stderr', '');
101
+ const errorText = smokeTestStderr || smokeTestStdout;
102
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.smokeTestFailure(smokeTestCommand, false))(errorText);
103
+ }
104
+ }
105
+ catch (err) {
106
+ debug('Smoke test failed:', err);
107
+ let errMessage = err.stderr || err.message;
108
+ debug('error message:', errMessage);
109
+ if (err.timedOut) {
110
+ debug('error timedOut is true');
111
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.smokeTestFailure(smokeTestCommand, true))(errMessage);
112
+ }
113
+ if (linuxWithDisplayEnv && util_1.default.isBrokenGtkDisplay(errMessage)) {
114
+ util_1.default.logBrokenGtkDisplayWarning();
115
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.invalidSmokeTestDisplayError)(errMessage);
116
+ }
117
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.missingDependency)(errMessage);
118
+ }
119
+ });
120
+ const spawnInXvfb = (linuxWithDisplayEnv) => __awaiter(void 0, void 0, void 0, function* () {
121
+ yield xvfb_1.default.start();
122
+ return spawn(linuxWithDisplayEnv || false).finally(() => __awaiter(void 0, void 0, void 0, function* () {
123
+ yield xvfb_1.default.stop();
124
+ }));
125
+ });
126
+ const userFriendlySpawn = (linuxWithDisplayEnv) => __awaiter(void 0, void 0, void 0, function* () {
127
+ debug('spawning, should retry on display problem?', Boolean(linuxWithDisplayEnv));
128
+ try {
129
+ yield spawn(linuxWithDisplayEnv);
130
+ }
131
+ catch (err) {
132
+ if (err.code === 'INVALID_SMOKE_TEST_DISPLAY_ERROR') {
133
+ return spawnInXvfb(linuxWithDisplayEnv);
134
+ }
135
+ throw err;
136
+ }
137
+ });
138
+ if (needsXvfb) {
139
+ return spawnInXvfb();
140
+ }
141
+ // if we are on linux and there's already a DISPLAY
142
+ // set, then we may need to rerun cypress after
143
+ // spawning our own Xvfb server
144
+ const linuxWithDisplayEnv = util_1.default.isPossibleLinuxWithIncorrectDisplay();
145
+ return userFriendlySpawn(linuxWithDisplayEnv);
146
+ };
147
+ function testBinary(version, binaryDir, options) {
148
+ debug('running binary verification check', version);
149
+ // if running from 'cypress verify', don't print this message
150
+ if (!options.force) {
151
+ logger_1.default.log((0, common_tags_1.stripIndent) `
152
+ It looks like this is your first time using Cypress: ${chalk_1.default.cyan(version)}
153
+ `);
154
+ }
155
+ logger_1.default.log();
156
+ // if we are running in CI then use
157
+ // the verbose renderer else use
158
+ // the default
159
+ let renderer = util_1.default.isCi() ? VerboseRenderer_1.default : 'default';
160
+ // NOTE: under test we set the listr renderer to 'silent' in order to get deterministic snapshots
161
+ if (logger_1.default.logLevel() === 'silent' || options.listrRenderer)
162
+ renderer = 'silent';
163
+ const rendererOptions = {
164
+ renderer,
165
+ };
166
+ const tasks = new listr2_1.Listr([
167
+ {
168
+ title: util_1.default.titleize('Verifying Cypress can run', chalk_1.default.gray(binaryDir)),
169
+ task: (ctx, task) => __awaiter(this, void 0, void 0, function* () {
170
+ debug('clearing out the verified version');
171
+ yield state_1.default.clearBinaryStateAsync(binaryDir);
172
+ yield Promise.all([
173
+ runSmokeTest(binaryDir, options),
174
+ bluebird_1.default.delay(1500), // good user experience
175
+ ]);
176
+ debug('write verified: true');
177
+ yield state_1.default.writeBinaryVerifiedAsync(true, binaryDir);
178
+ util_1.default.setTaskTitle(task, util_1.default.titleize(chalk_1.default.green('Verified Cypress!'), chalk_1.default.gray(binaryDir)), rendererOptions.renderer);
179
+ }),
180
+ },
181
+ ], rendererOptions);
182
+ return tasks.run();
183
+ }
184
+ const maybeVerify = (installedVersion, binaryDir, options) => __awaiter(void 0, void 0, void 0, function* () {
185
+ const isVerified = yield state_1.default.getBinaryVerifiedAsync(binaryDir);
186
+ debug('is Verified ?', isVerified);
187
+ let shouldVerify = !isVerified;
188
+ // force verify if options.force
189
+ if (options.force) {
190
+ debug('force verify');
191
+ shouldVerify = true;
192
+ }
193
+ if (shouldVerify) {
194
+ yield testBinary(installedVersion, binaryDir, options);
195
+ if (options.welcomeMessage) {
196
+ logger_1.default.log();
197
+ logger_1.default.log('Opening Cypress...');
198
+ }
199
+ }
200
+ });
201
+ const start = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (options = {}) {
202
+ debug('verifying Cypress app');
203
+ const packageVersion = util_1.default.pkgVersion();
204
+ let binaryDir = state_1.default.getBinaryDir(packageVersion);
205
+ lodash_1.default.defaults(options, {
206
+ dev: false,
207
+ force: false,
208
+ welcomeMessage: true,
209
+ smokeTestTimeout: (0, exports.verifyTestRunnerTimeoutMs)(),
210
+ skipVerify: util_1.default.getEnv('CYPRESS_SKIP_VERIFY') === 'true',
211
+ });
212
+ if (options.skipVerify) {
213
+ debug('skipping verification of the Cypress app');
214
+ return Promise.resolve();
215
+ }
216
+ if (options.dev) {
217
+ return runSmokeTest('', options);
218
+ }
219
+ const parseBinaryEnvVar = () => __awaiter(void 0, void 0, void 0, function* () {
220
+ const envBinaryPath = util_1.default.getEnv('CYPRESS_RUN_BINARY');
221
+ debug('CYPRESS_RUN_BINARY exists, =', envBinaryPath);
222
+ logger_1.default.log((0, common_tags_1.stripIndent) `
223
+ ${chalk_1.default.yellow('Note:')} You have set the environment variable:
224
+
225
+ ${chalk_1.default.white('CYPRESS_RUN_BINARY=')}${chalk_1.default.cyan(envBinaryPath)}
226
+
227
+ This overrides the default Cypress binary path used.
228
+ `);
229
+ logger_1.default.log();
230
+ try {
231
+ const isExecutable = yield util_1.default.isExecutableAsync(envBinaryPath);
232
+ debug('CYPRESS_RUN_BINARY is executable? :', isExecutable);
233
+ if (!isExecutable) {
234
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))((0, common_tags_1.stripIndent) `
235
+ The supplied binary path is not executable
236
+ `);
237
+ }
238
+ const envBinaryDir = yield state_1.default.parseRealPlatformBinaryFolderAsync(envBinaryPath);
239
+ if (!envBinaryDir) {
240
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))();
241
+ }
242
+ debug('CYPRESS_RUN_BINARY has binaryDir:', envBinaryDir);
243
+ binaryDir = envBinaryDir;
244
+ }
245
+ catch (err) {
246
+ if (err.code === 'ENOENT') {
247
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.CYPRESS_RUN_BINARY.notValid(envBinaryPath))(err.message);
248
+ }
249
+ throw err;
250
+ }
251
+ });
252
+ try {
253
+ debug('checking environment variables');
254
+ if (util_1.default.getEnv('CYPRESS_RUN_BINARY')) {
255
+ yield parseBinaryEnvVar();
256
+ }
257
+ yield checkExecutable(binaryDir);
258
+ debug('binaryDir is ', binaryDir);
259
+ const pkg = yield state_1.default.getBinaryPkgAsync(binaryDir);
260
+ const binaryVersion = state_1.default.getBinaryPkgVersion(pkg);
261
+ if (!binaryVersion) {
262
+ debug('no Cypress binary found for cli version ', packageVersion);
263
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.missingApp(binaryDir))(`
264
+ Cannot read binary version from: ${chalk_1.default.cyan(state_1.default.getBinaryPkgPath(binaryDir))}
265
+ `);
266
+ }
267
+ debug(`Found binary version ${chalk_1.default.green(binaryVersion)} installed in: ${chalk_1.default.cyan(binaryDir)}`);
268
+ if (binaryVersion !== packageVersion) {
269
+ // warn if we installed with CYPRESS_INSTALL_BINARY or changed version
270
+ // in the package.json
271
+ logger_1.default.log(`Found binary version ${chalk_1.default.green(binaryVersion)} installed in: ${chalk_1.default.cyan(binaryDir)}`);
272
+ logger_1.default.log();
273
+ logger_1.default.warn((0, common_tags_1.stripIndent) `
274
+
275
+
276
+ ${log_symbols_1.default.warning} Warning: Binary version ${chalk_1.default.green(binaryVersion)} does not match the expected package version ${chalk_1.default.green(packageVersion)}
277
+
278
+ These versions may not work properly together.
279
+ `);
280
+ logger_1.default.log();
281
+ }
282
+ yield maybeVerify(binaryVersion, binaryDir, options);
283
+ }
284
+ catch (err) {
285
+ if (err.known) {
286
+ throw err;
287
+ }
288
+ return (0, errors_1.throwFormErrorText)(errors_1.errors.unexpected)(err.stack);
289
+ }
290
+ });
291
+ exports.start = start;
292
+ const isLinuxLike = () => os_1.default.platform() !== 'win32';
293
+ /**
294
+ * Returns true if running on a system where Electron needs "--no-sandbox" flag.
295
+ * @see https://crbug.com/638180
296
+ *
297
+ * On Debian we had problems running in sandbox even for non-root users.
298
+ * @see https://github.com/cypress-io/cypress/issues/5434
299
+ * Seems there is a lot of discussion around this issue among Electron users
300
+ * @see https://github.com/electron/electron/issues/17972
301
+ */
302
+ const needsSandbox = () => isLinuxLike();
303
+ exports.needsSandbox = needsSandbox;