"use strict"; const _ = require('lodash'); const os = require('os'); const path = require('path'); const untildify = require('untildify'); const debug = require('debug')('cypress:cli'); const fs = require('../fs'); const util = require('../util'); const getPlatformExecutable = () => { const platform = os.platform(); switch (platform) { case 'darwin': return 'Contents/MacOS/Cypress'; case 'linux': return 'Cypress'; case 'win32': return 'Cypress.exe'; // TODO handle this error using our standard default: throw new Error(`Platform: "${platform}" is not supported.`); } }; const getPlatFormBinaryFolder = () => { const platform = os.platform(); switch (platform) { case 'darwin': return 'Cypress.app'; case 'linux': return 'Cypress'; case 'win32': return 'Cypress'; // TODO handle this error using our standard default: throw new Error(`Platform: "${platform}" is not supported.`); } }; const getBinaryPkgPath = binaryDir => { const platform = os.platform(); switch (platform) { case 'darwin': return path.join(binaryDir, 'Contents', 'Resources', 'app', 'package.json'); case 'linux': return path.join(binaryDir, 'resources', 'app', 'package.json'); case 'win32': return path.join(binaryDir, 'resources', 'app', 'package.json'); // TODO handle this error using our standard default: throw new Error(`Platform: "${platform}" is not supported.`); } }; /** * Get path to binary directory */ const getBinaryDir = (version = util.pkgVersion()) => { return path.join(getVersionDir(version), getPlatFormBinaryFolder()); }; const getVersionDir = (version = util.pkgVersion(), buildInfo = util.pkgBuildInfo()) => { if (buildInfo && !buildInfo.stable) { version = ['beta', version, buildInfo.commitBranch, buildInfo.commitSha.slice(0, 8)].join('-'); } return path.join(getCacheDir(), version); }; /** * When executing "npm postinstall" hook, the working directory is set to * "/node_modules/cypress", which can be surprising when using relative paths. */ const isInstallingFromPostinstallHook = () => { // individual folders const cwdFolders = process.cwd().split(path.sep); const length = cwdFolders.length; return cwdFolders[length - 2] === 'node_modules' && cwdFolders[length - 1] === 'cypress'; }; const getCacheDir = () => { let cache_directory = util.getCacheDir(); if (util.getEnv('CYPRESS_CACHE_FOLDER')) { const envVarCacheDir = untildify(util.getEnv('CYPRESS_CACHE_FOLDER')); debug('using environment variable CYPRESS_CACHE_FOLDER %s', envVarCacheDir); if (!path.isAbsolute(envVarCacheDir) && isInstallingFromPostinstallHook()) { const packageRootFolder = path.join('..', '..', envVarCacheDir); cache_directory = path.resolve(packageRootFolder); debug('installing from postinstall hook, original root folder is %s', packageRootFolder); debug('and resolved cache directory is %s', cache_directory); } else { cache_directory = path.resolve(envVarCacheDir); } } return cache_directory; }; const parseRealPlatformBinaryFolderAsync = binaryPath => { return fs.realpathAsync(binaryPath).then(realPath => { debug('CYPRESS_RUN_BINARY has realpath:', realPath); if (!realPath.toString().endsWith(getPlatformExecutable())) { return false; } if (os.platform() === 'darwin') { return path.resolve(realPath, '..', '..', '..'); } return path.resolve(realPath, '..'); }); }; const getDistDir = () => { return path.join(__dirname, '..', '..', 'dist'); }; /** * Returns full filename to the file that keeps the Test Runner verification state as JSON text. * Note: the binary state file will be stored one level up from the given binary folder. * @param {string} binaryDir - full path to the folder holding the binary. */ const getBinaryStatePath = binaryDir => { return path.join(binaryDir, '..', 'binary_state.json'); }; const getBinaryStateContentsAsync = binaryDir => { const fullPath = getBinaryStatePath(binaryDir); return fs.readJsonAsync(fullPath).catch({ code: 'ENOENT' }, SyntaxError, () => { debug('could not read binary_state.json file at "%s"', fullPath); return {}; }); }; const getBinaryVerifiedAsync = binaryDir => { return getBinaryStateContentsAsync(binaryDir).tap(debug).get('verified'); }; const clearBinaryStateAsync = binaryDir => { return fs.removeAsync(getBinaryStatePath(binaryDir)); }; /** * Writes the new binary status. * @param {boolean} verified The new test runner state after smoke test * @param {string} binaryDir Folder holding the binary * @returns {Promise} returns a promise */ const writeBinaryVerifiedAsync = (verified, binaryDir) => { return getBinaryStateContentsAsync(binaryDir).then(contents => { return fs.outputJsonAsync(getBinaryStatePath(binaryDir), _.extend(contents, { verified }), { spaces: 2 }); }); }; const getPathToExecutable = binaryDir => { return path.join(binaryDir, getPlatformExecutable()); }; /** * Resolves with an object read from the binary app package.json file. * If the file does not exist resolves with null */ const getBinaryPkgAsync = binaryDir => { const pathToPackageJson = getBinaryPkgPath(binaryDir); debug('Reading binary package.json from:', pathToPackageJson); return fs.pathExistsAsync(pathToPackageJson).then(exists => { if (!exists) { return null; } return fs.readJsonAsync(pathToPackageJson); }); }; const getBinaryPkgVersion = o => _.get(o, 'version', null); const getBinaryElectronVersion = o => _.get(o, 'electronVersion', null); const getBinaryElectronNodeVersion = o => _.get(o, 'electronNodeVersion', null); module.exports = { getPathToExecutable, getPlatformExecutable, // those names start to sound like Java getBinaryElectronNodeVersion, getBinaryElectronVersion, getBinaryPkgVersion, getBinaryVerifiedAsync, getBinaryPkgAsync, getBinaryPkgPath, getBinaryDir, getCacheDir, clearBinaryStateAsync, writeBinaryVerifiedAsync, parseRealPlatformBinaryFolderAsync, getDistDir, getVersionDir };