UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

387 lines 13.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.checkIfRunsOnProdMode = exports.checkIfRunsOnDevMode = exports.checkIfFlutterBuilds = exports.checkIfBuilds = exports.checkSentryProperties = exports.checkEnvBuildPlugin = exports.checkSentryCliRc = exports.checkPackageJson = exports.checkFileExists = exports.checkFileContents = exports.modifyFile = exports.createFile = exports.startWizardInstance = exports.revertLocalChanges = exports.cleanupGit = exports.initGit = exports.WizardTestEnv = exports.log = exports.TEST_ARGS = exports.KEYS = void 0; const fs = __importStar(require("node:fs")); const path = __importStar(require("node:path")); const node_child_process_1 = require("node:child_process"); const Logging_1 = require("../../lib/Helper/Logging"); exports.KEYS = { UP: '\u001b[A', DOWN: '\u001b[B', LEFT: '\u001b[D', RIGHT: '\u001b[C', ENTER: '\r', SPACE: ' ', }; exports.TEST_ARGS = { AUTH_TOKEN: process.env.SENTRY_TEST_AUTH_TOKEN || 'TEST_AUTH_TOKEN', PROJECT_DSN: process.env.SENTRY_TEST_DSN || 'https://public@dsn.ingest.sentry.io/1337', ORG_SLUG: process.env.SENTRY_TEST_ORG || 'TEST_ORG_SLUG', PROJECT_SLUG: process.env.SENTRY_TEST_PROJECT || 'TEST_PROJECT_SLUG', }; exports.log = { success: (message) => { (0, Logging_1.green)(`[SUCCESS] ${message}`); }, info: (message) => { (0, Logging_1.dim)(`[INFO] ${message}`); }, error: (message) => { (0, Logging_1.red)(`[ERROR] ${message}`); }, }; class WizardTestEnv { taskHandle; constructor(cmd, args, opts) { this.taskHandle = (0, node_child_process_1.spawn)(cmd, args, { cwd: opts?.cwd, stdio: 'pipe' }); if (opts?.debug) { this.taskHandle.stdout?.pipe(process.stdout); this.taskHandle.stderr?.pipe(process.stderr); } } sendStdin(input) { this.taskHandle.stdin?.write(input); } /** * Sends the input and waits for the output. * @returns a promise that resolves when the output was found * @throws an error when the output was not found within the timeout */ sendStdinAndWaitForOutput(input, output, options) { const outputPromise = this.waitForOutput(output, options); if (Array.isArray(input)) { for (const i of input) { this.sendStdin(i); } } else { this.sendStdin(input); } return outputPromise; } /** * Waits for the task to exit with a given `statusCode`. * * @returns a promise that resolves to `true` if the run ends with the status * code, or it rejects when the `timeout` was reached. */ waitForStatusCode(statusCode, options = {}) { const { timeout } = { timeout: 60000, ...options, }; return new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { this.kill(); reject(new Error(`Timeout waiting for status code: ${statusCode}`)); }, timeout); this.taskHandle.on('error', (err) => { clearTimeout(timeoutId); reject(err); }); this.taskHandle.on('exit', (code) => { clearTimeout(timeoutId); resolve(code === statusCode); }); }); } /** * Waits for the provided output with `.includes()` logic. * * @returns a promise that resolves to `true` if the output was found, `false` if the output was not found within the * timeout and `optional: true` is set, or it rejects when the timeout was reached with `optional: false` */ waitForOutput(output, options = {}) { const { timeout, optional } = { timeout: 60000, optional: false, ...options, }; return new Promise((resolve, reject) => { let outputBuffer = ''; const timeoutId = setTimeout(() => { this.kill(); if (optional) { // The output is not found but it's optional so we can resolve the promise with false resolve(false); } else { reject(new Error(`Timeout waiting for output: ${output}. Got the following instead: ${outputBuffer}`)); } }, timeout); this.taskHandle.on('error', (err) => { clearTimeout(timeoutId); reject(err); }); this.taskHandle.stdout?.on('data', (data) => { outputBuffer += data; if (outputBuffer.includes(output)) { clearTimeout(timeoutId); // The output is found so we can resolve the promise with true resolve(true); } }); }); } kill() { this.taskHandle.stdin?.destroy(); this.taskHandle.stderr?.destroy(); this.taskHandle.stdout?.destroy(); this.taskHandle.kill('SIGINT'); this.taskHandle.unref(); } } exports.WizardTestEnv = WizardTestEnv; /** * Initialize a git repository in the given directory * @param projectDir */ function initGit(projectDir) { try { (0, node_child_process_1.execSync)('git init', { cwd: projectDir }); // Add all files to the git repo (0, node_child_process_1.execSync)('git add -A', { cwd: projectDir }); // Add author info to avoid git commit error (0, node_child_process_1.execSync)('git config user.email test@test.sentry.io', { cwd: projectDir }); (0, node_child_process_1.execSync)('git config user.name Test', { cwd: projectDir }); (0, node_child_process_1.execSync)('git commit -m init', { cwd: projectDir }); } catch (e) { exports.log.error('Error initializing git'); exports.log.error(e); } } exports.initGit = initGit; /** * Cleanup the git repository in the given directory * * Caution! Make sure `projectDir` is a test project directory, * if in doubt, please commit your local non-test changes first! * @param projectDir */ function cleanupGit(projectDir) { try { // Remove the .git directory (0, node_child_process_1.execSync)(`rm -rf ${projectDir}/.git`); } catch (e) { exports.log.error('Error cleaning up git'); exports.log.error(e); } } exports.cleanupGit = cleanupGit; /** * Revert local changes in the given directory * * Caution! Make sure `projectDir` is a test project directory, * if in doubt, please commit your local non-test changes first! * * @param projectDir */ function revertLocalChanges(projectDir) { try { // Revert tracked files (0, node_child_process_1.execSync)('git checkout .', { cwd: projectDir }); // Revert untracked files (0, node_child_process_1.execSync)('git clean -fd .', { cwd: projectDir }); } catch (e) { exports.log.error('Error reverting local changes'); exports.log.error(e); } } exports.revertLocalChanges = revertLocalChanges; /** * Start the wizard instance with the given integration and project directory * @param integration * @param projectDir * * @returns WizardTestEnv */ function startWizardInstance(integration, projectDir, debug = false) { const binName = process.env.SENTRY_WIZARD_E2E_TEST_BIN ? ['dist-bin', `sentry-wizard-${process.platform}-${process.arch}`] : ['dist', 'bin.js']; const binPath = path.join(__dirname, '..', '..', ...binName); revertLocalChanges(projectDir); cleanupGit(projectDir); initGit(projectDir); return new WizardTestEnv(binPath, [ '--debug', '-i', integration, '--preSelectedProject.authToken', exports.TEST_ARGS.AUTH_TOKEN, '--preSelectedProject.dsn', exports.TEST_ARGS.PROJECT_DSN, '--preSelectedProject.orgSlug', exports.TEST_ARGS.ORG_SLUG, '--preSelectedProject.projectSlug', exports.TEST_ARGS.PROJECT_SLUG, ], { cwd: projectDir, debug }); } exports.startWizardInstance = startWizardInstance; /** * Create a file with the given content * * @param filePath * @param content */ function createFile(filePath, content) { return fs.writeFileSync(filePath, content || ''); } exports.createFile = createFile; /** * Modify the file with the new content * * @param filePath * @param oldContent * @param newContent */ function modifyFile(filePath, replaceMap) { const fileContent = fs.readFileSync(filePath, 'utf-8'); let newFileContent = fileContent; for (const [oldContent, newContent] of Object.entries(replaceMap)) { newFileContent = newFileContent.replace(oldContent, newContent); } fs.writeFileSync(filePath, newFileContent); } exports.modifyFile = modifyFile; /** * Read the file contents and check if it contains the given content * * @param {string} filePath * @param {(string | string[])} content */ function checkFileContents(filePath, content) { const fileContent = fs.readFileSync(filePath, 'utf-8'); const contentArray = Array.isArray(content) ? content : [content]; for (const c of contentArray) { expect(fileContent).toContain(c); } } exports.checkFileContents = checkFileContents; /** * Check if the file exists * * @param filePath */ function checkFileExists(filePath) { expect(fs.existsSync(filePath)).toBe(true); } exports.checkFileExists = checkFileExists; /** * Check if the package.json contains the given integration * * @param projectDir * @param integration */ function checkPackageJson(projectDir, integration) { checkFileContents(`${projectDir}/package.json`, `@sentry/${integration}`); } exports.checkPackageJson = checkPackageJson; /** * Check if the .sentryclirc contains the auth token * * @param projectDir */ function checkSentryCliRc(projectDir) { checkFileContents(`${projectDir}/.sentryclirc`, `token=${exports.TEST_ARGS.AUTH_TOKEN}`); } exports.checkSentryCliRc = checkSentryCliRc; /** * Check if the .env.sentry-build-plugin contains the auth token * @param projectDir */ function checkEnvBuildPlugin(projectDir) { checkFileContents(`${projectDir}/.env.sentry-build-plugin`, `SENTRY_AUTH_TOKEN=${exports.TEST_ARGS.AUTH_TOKEN}`); } exports.checkEnvBuildPlugin = checkEnvBuildPlugin; /** * Check if the sentry.properties contains the auth token * @param projectDir */ function checkSentryProperties(projectDir) { checkFileContents(`${projectDir}/sentry.properties`, `auth_token=${exports.TEST_ARGS.AUTH_TOKEN}`); } exports.checkSentryProperties = checkSentryProperties; /** * Check if the project builds * Check if the project builds and ends with status code 0. * @param projectDir */ async function checkIfBuilds(projectDir) { const testEnv = new WizardTestEnv('npm', ['run', 'build'], { cwd: projectDir, }); await expect(testEnv.waitForStatusCode(0, { timeout: 120000, })).resolves.toBe(true); } exports.checkIfBuilds = checkIfBuilds; /** * Check if the flutter project builds * @param projectDir */ async function checkIfFlutterBuilds(projectDir, expectedOutput, debug = false) { const testEnv = new WizardTestEnv('flutter', ['build', 'web'], { cwd: projectDir, debug: debug, }); await expect(testEnv.waitForOutput(expectedOutput, { timeout: 120000, })).resolves.toBe(true); } exports.checkIfFlutterBuilds = checkIfFlutterBuilds; /** * Check if the project runs on dev mode * @param projectDir * @param expectedOutput */ async function checkIfRunsOnDevMode(projectDir, expectedOutput) { const testEnv = new WizardTestEnv('npm', ['run', 'dev'], { cwd: projectDir }); await expect(testEnv.waitForOutput(expectedOutput, { timeout: 120000, })).resolves.toBe(true); testEnv.kill(); } exports.checkIfRunsOnDevMode = checkIfRunsOnDevMode; /** * Check if the project runs on prod mode * @param projectDir * @param expectedOutput */ async function checkIfRunsOnProdMode(projectDir, expectedOutput, startCommand = 'start') { const testEnv = new WizardTestEnv('npm', ['run', startCommand], { cwd: projectDir, }); await expect(testEnv.waitForOutput(expectedOutput, { timeout: 120000, })).resolves.toBe(true); testEnv.kill(); } exports.checkIfRunsOnProdMode = checkIfRunsOnProdMode; //# sourceMappingURL=index.js.map