UNPKG

@applitools/eyes-storybook

Version:
280 lines (247 loc) 8.56 kB
#!/usr/bin/env node const fs = require('fs').promises; const {input, confirm} = require('@inquirer/prompts'); const yargs = require('yargs/yargs'); const chalk = require('chalk'); const {pathToFileURL} = require('url'); const utils = require('@applitools/utils'); const {hideBin} = require('yargs/helpers'); yargs(hideBin(process.argv)) .command({ command: '*', builder: yargs => { const args = yargs.options({ apiKey: { describe: "Your Applitools Team's API key (here's how to obtain it: https://applitools.com/docs/topics/overview/obtain-api-key.html)", type: 'string', }, serverUrl: {describe: 'Your Eyes dedicated cloud server URL', type: 'string'}, }); return args; }, handler: handleError(init), }) .help() .parse(); function handleError(asyncFunc) { return async (...args) => asyncFunc(...args).catch(err => { if (err.name === 'ExitPromptError') { log('See you next time!'); } else { error(`Error while setting up Eyes: ${err.message}`); } process.exit(1); }); } async function init(args) { log(chalk.yellow('Setting up Eyes-Storybook for your project:')); let {config, fileName, isCorrupted, isESM} = await loadExistingConfig(); if (isCorrupted) { log(chalk.yellow(`\nWarning: Your configuration file '${fileName}' appears to be corrupted.`)); const shouldOverwrite = await confirm({ message: 'Would you like to create a new one? This will replace the corrupted file (Default Yes):', default: true, }); if (!shouldOverwrite) { log('Aborting setup. Please fix your configuration file manually or remove it.'); process.exit(0); } // By letting this block finish, the script proceeds as if no config was found } // If no config exists or is corrupted, run the interactive setup let apiKey, serverUrl, warnForApiKey = false; let shouldUpdate = !config; if (shouldUpdate) { let shouldUseEnvApiKey = false; if (utils.general.getEnvValue('API_KEY')) { shouldUseEnvApiKey = await confirm({ message: 'Detected APPLITOOLS_API_KEY environment variable. Would you like it to be used? (default Yes):', default: true, }); if (shouldUseEnvApiKey) { apiKey = 'process.env.APPLITOOLS_API_KEY'; } warnForApiKey = !shouldUseEnvApiKey; // Warn if the user has an env var but doesn't use it } let shouldUseEnvServerUrl = false; for (const url of ['EYES_SERVER_URL', 'SERVER_URL']) { if (utils.general.getEnvValue(url)) { shouldUseEnvServerUrl = await confirm({ message: `Detected APPLITOOLS_${url} environment variable. Would you like it to be used? (default Yes):`, default: true, }); if (shouldUseEnvServerUrl) { serverUrl = `process.env.APPLITOOLS_${url}`; break; // Use the first one found } } } if (!shouldUseEnvApiKey) { apiKey = args.apiKey ?? (await input({ message: 'Enter your API key (https://applitools.com/docs/topics/overview/obtain-api-key.html):', })); warnForApiKey = apiKey !== ''; // Warn if the user provided any API key directly } if (!shouldUseEnvServerUrl) { serverUrl = args.serverUrl ?? (await input({ message: 'Enter your Eyes server URL (default is https://eyes.applitools.com):', })); } config = { ...(config || {}), appName: 'My Storybook App', batchName: 'My Storybook', showLogs: false, }; const fileContent = generateConfigContent(isESM); await fs.writeFile(fileName, fileContent, 'utf8'); } printPostlude(); // --- Helper Functions --- async function loadExistingConfig() { const configFiles = [ 'applitools.config.js', 'applitools.config.mjs', 'applitools.config.cjs', 'eyes.config.js', ]; for (const file of configFiles) { try { const fileUrl = pathToFileURL(file).href; const module = await import(`${fileUrl}?t=${Date.now()}`); // Append timestamp to bypass cache const isESM = file.endsWith('.mjs'); return {config: module.default || module, fileName: file, isCorrupted: false, isESM}; } catch (err) { if (err.code === 'ERR_MODULE_NOT_FOUND') { continue; } return {config: null, fileName: file, isCorrupted: true}; } } // No file was found in any of the possible locations. // Check if type: module is set in package.json in cwd let fileName = 'applitools.config.js'; let isESM = false; try { const pkgRaw = await fs.readFile('./package.json', 'utf8'); const pkg = JSON.parse(pkgRaw); if (pkg.type === 'module') { isESM = true; fileName = 'applitools.config.mjs'; } } catch (e) { // ignore if package.json not found or invalid } return {config: null, fileName, isCorrupted: false, isESM}; } function generateConfigContent(isESM) { const envConfigMap = { batchName: 'APPLITOOLS_BATCH_NAME', batchId: 'APPLITOOLS_BATCH_ID', showLogs: 'APPLITOOLS_SHOW_LOGS', batchSequenceName: 'APPLITOOLS_BATCH_SEQUENCE_NAME', proxy: 'APPLITOOLS_PROXY', notifyOnCompletion: 'APPLITOOLS_NOTIFY_ON_COMPLETION', concurrentTabs: 'APPLITOOLS_CONCURRENT_TABS', appName: 'APPLITOOLS_APP_NAME', }; const configLines = []; // apiKey if (apiKey === 'process.env.APPLITOOLS_API_KEY') { configLines.push('apiKey: process.env.APPLITOOLS_API_KEY,'); } else if (apiKey) { configLines.push( `apiKey: '${apiKey}', // Warning: 'apiKey' is not obscured, consider setting it via environment variable APPLITOOLS_API_KEY`, ); } else { configLines.push("// apiKey: '',"); } // serverUrl if (serverUrl && serverUrl.startsWith('process.env.')) { configLines.push(`serverUrl: ${serverUrl},`); } else if (serverUrl) { configLines.push(`serverUrl: '${serverUrl}',`); } // other config const otherKeys = [ 'appName', 'batchName', 'batchId', 'showLogs', 'batchSequenceName', 'proxy', 'notifyOnCompletion', 'concurrentTabs', 'navigationWaitUntil', 'waitBeforeCapture', ]; for (const key of otherKeys) { const envVar = envConfigMap[key]; if (envVar && utils.general.getEnvValue(envVar.replace('APPLITOOLS_', ''))) { configLines.push(`${key}: process.env.${envVar},`); } else if (Object.prototype.hasOwnProperty.call(config, key)) { const value = config[key]; if (value !== undefined) { configLines.push(typeof value === 'string' ? `${key}: '${value}',` : `${key}: ${value},`); } } } configLines.push(`// browsersInfo: [ // {width: 1024, height: 768, name: 'firefox'}, // {width: 1024, height: 768, name: 'chrome'}, // {iosDeviceInfo: {deviceName: 'iPhone 16'}}, // {chromeEmulationInfo: {deviceName: 'Galaxy S20'}}, // ]`); const configObject = `{ ${configLines.join('\n ')} }`; if (isESM) { return `/**\n * @type {import('@applitools/eyes-storybook').ApplitoolsConfig}\n */\nconst config = ${configObject};\nexport default config;\n`; } else { return `/**\n * @type {import('@applitools/eyes-storybook').ApplitoolsConfig}\n **/\nmodule.exports = ${configObject};\n`; } } function printPostlude() { log(''); if (shouldUpdate) { log( chalk.green('✔ Success!'), chalk.bold('\n'), chalk.bold('Eyes Storybook is now set up!'), chalk.bold('Please visit our Storybook documentation to learn more.'), chalk.bold('https://applitools.com/tutorials/sdks/storybook/quickstart'), ); } else { log( chalk.green('✔ Good news! Eyes Storybook is already properly configured!'), chalk.bold('\n'), chalk.bold('Please visit our Storybook documentation to learn more.'), chalk.bold('https://applitools.com/tutorials/sdks/storybook/quickstart'), ); } if (warnForApiKey) { log( chalk.yellow( '\nWarning: Consider setting your API key via the APPLITOOLS_API_KEY environment variable.', ), ); } } } function log(...args) { console.log(...args); } function error(...args) { console.error(...args); }