UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

383 lines 18.8 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; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const clack_1 = require("../../../src/utils/clack/"); const fs = __importStar(require("node:fs")); const ChildProcess = __importStar(require("node:child_process")); const PackageManagerUtils = __importStar(require("../../../src/utils/package-manager")); const package_manager_1 = require("../../../src/utils/package-manager"); const axios_1 = __importDefault(require("axios")); // @ts-expect-error - clack is ESM and TS complains about that. It works though const clack = __importStar(require("@clack/prompts")); const Sentry = __importStar(require("@sentry/node")); const vitest_1 = require("vitest"); vitest_1.vi.mock('node:child_process', async () => ({ __esModule: true, ...(await vitest_1.vi.importActual('node:child_process')), })); vitest_1.vi.mock('@clack/prompts', () => ({ log: { info: vitest_1.vi.fn(), success: vitest_1.vi.fn(), warn: vitest_1.vi.fn(), error: vitest_1.vi.fn(), }, outro: vitest_1.vi.fn(), text: vitest_1.vi.fn(), confirm: vitest_1.vi.fn(), cancel: vitest_1.vi.fn(), // passthrough for abortIfCancelled isCancel: vitest_1.vi.fn().mockReturnValue(false), spinner: vitest_1.vi .fn() .mockImplementation(() => ({ start: vitest_1.vi.fn(), stop: vitest_1.vi.fn() })), select: vitest_1.vi.fn(), })); const clackMock = clack; vitest_1.vi.mock('axios'); const mockedAxios = axios_1.default; vitest_1.vi.mock('opn', () => ({ default: vitest_1.vi.fn(() => Promise.resolve({ on: vitest_1.vi.fn() })), })); // Sentry mock functions defined at module level for the abort tests const mockRootSpan = { setStatus: vitest_1.vi.fn(), end: vitest_1.vi.fn(), }; const mockActiveSpan = {}; let mockSentrySession = { status: 999 }; vitest_1.vi.mock('@sentry/node', async () => { const actual = await vitest_1.vi.importActual('@sentry/node'); return { ...actual, getActiveSpan: vitest_1.vi.fn(() => mockActiveSpan), getRootSpan: vitest_1.vi.fn(() => mockRootSpan), getCurrentScope: vitest_1.vi.fn(() => ({ getSession: () => mockSentrySession, })), captureSession: vitest_1.vi.fn(), flush: vitest_1.vi.fn(() => Promise.resolve(true)), setTag: vitest_1.vi.fn(), captureException: vitest_1.vi.fn(() => 'id'), }; }); function mockUserResponse(fn, response) { fn.mockReturnValueOnce(response); } (0, vitest_1.describe)('askForToolConfigPath', () => { (0, vitest_1.afterEach)(() => { vitest_1.vi.clearAllMocks(); }); (0, vitest_1.it)('returns undefined if users have no config file', async () => { mockUserResponse(clack.confirm, false); const result = await (0, clack_1.askForToolConfigPath)('Webpack', 'webpack.config.js'); (0, vitest_1.expect)(clack.confirm).toHaveBeenCalledWith(vitest_1.expect.objectContaining({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment message: vitest_1.expect.stringContaining('have a Webpack config file'), })); (0, vitest_1.expect)(result).toBeUndefined(); }); (0, vitest_1.it)('returns the path if users have a config file and the entered path is valid', async () => { mockUserResponse(clack.confirm, true); mockUserResponse(clack.text, 'my.webpack.config.js'); const result = await (0, clack_1.askForToolConfigPath)('Webpack', 'webpack.config.js'); (0, vitest_1.expect)(clack.confirm).toHaveBeenCalledWith(vitest_1.expect.objectContaining({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment message: vitest_1.expect.stringContaining('have a Webpack config file'), })); (0, vitest_1.expect)(clack.text).toHaveBeenCalledWith(vitest_1.expect.objectContaining({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment message: vitest_1.expect.stringContaining('enter the path to your Webpack config file'), })); (0, vitest_1.expect)(result).toBe('my.webpack.config.js'); }); }); (0, vitest_1.describe)('createNewConfigFile', () => { (0, vitest_1.afterEach)(() => { vitest_1.vi.clearAllMocks(); }); (0, vitest_1.it)('writes the file to disk and returns true if the file was created successfully', async () => { const writeFileSpy = vitest_1.vi .spyOn(fs.promises, 'writeFile') .mockImplementation(vitest_1.vi.fn()); const filename = '/webpack.config.js'; const code = 'module.exports = {/*config...*/}'; const result = await (0, clack_1.createNewConfigFile)(filename, code); (0, vitest_1.expect)(result).toBe(true); (0, vitest_1.expect)(writeFileSpy).toHaveBeenCalledWith(filename, code); }); (0, vitest_1.it)('logs more information if provided as an argument', async () => { vitest_1.vi.spyOn(fs.promises, 'writeFile').mockImplementation(vitest_1.vi.fn()); const filename = '/webpack.config.js'; const code = 'module.exports = {/*config...*/}'; const moreInfo = 'More information...'; await (0, clack_1.createNewConfigFile)(filename, code, moreInfo); (0, vitest_1.expect)(clack.log.info).toHaveBeenCalledTimes(1); (0, vitest_1.expect)(clack.log.info).toHaveBeenCalledWith(vitest_1.expect.stringContaining(moreInfo)); }); (0, vitest_1.it)('returns false and logs a warning if the file could not be created', async () => { const writeFileSpy = vitest_1.vi .spyOn(fs.promises, 'writeFile') .mockImplementation(() => Promise.reject(new Error('Could not write'))); const filename = '/webpack.config.js'; const code = 'module.exports = {/*config...*/}'; const result = await (0, clack_1.createNewConfigFile)(filename, code); (0, vitest_1.expect)(result).toBe(false); (0, vitest_1.expect)(writeFileSpy).toHaveBeenCalledWith(filename, code); (0, vitest_1.expect)(clack.log.warn).toHaveBeenCalledTimes(1); }); (0, vitest_1.it)('returns false if the passed path is not absolute', async () => { const result = await (0, clack_1.createNewConfigFile)('./relative/webpack.config.js', ''); (0, vitest_1.expect)(result).toBe(false); }); }); (0, vitest_1.describe)('installPackage', () => { (0, vitest_1.afterEach)(() => { vitest_1.vi.clearAllMocks(); }); const spawnSpy = vitest_1.vi.spyOn(ChildProcess, 'spawn').mockImplementation(() => ({ // @ts-expect-error - not passing the full object but directly resolving // to simulate a successful install on: vitest_1.vi.fn((evt, cb) => { if (evt === 'close') { cb(0); } }), // @ts-expect-error - this is fine stderr: { on: vitest_1.vi.fn() }, })); (0, vitest_1.it)('force-installs a package if the forceInstall flag is set', async () => { const packageManagerMock = { name: 'npm', label: 'NPM', installCommand: 'install', buildCommand: 'npm run build', runScriptCommand: 'npm run', flags: '', forceInstallFlag: '--force', detect: vitest_1.vi.fn(), addOverride: vitest_1.vi.fn(), }; await (0, clack_1.installPackage)({ alreadyInstalled: false, packageName: '@some/package', packageNameDisplayLabel: '@some/package', forceInstall: true, askBeforeUpdating: false, packageManager: packageManagerMock, }); (0, vitest_1.expect)(spawnSpy).toHaveBeenCalledWith('npm', ['install', '@some/package', '--force'], { shell: true, stdio: ['pipe', 'ignore', 'pipe'] }); }); vitest_1.it.each([false, undefined])("doesn't force-install a package if the forceInstall flag is %s", async (flag) => { const packageManagerMock = { name: 'npm', label: 'NPM', installCommand: 'install', buildCommand: 'npm run build', runScriptCommand: 'npm run', flags: '', forceInstallFlag: '--force', detect: vitest_1.vi.fn(), addOverride: vitest_1.vi.fn(), }; await (0, clack_1.installPackage)({ alreadyInstalled: false, packageName: '@sentry/sveltekit', packageNameDisplayLabel: '@sentry/sveltekit', forceInstall: flag, askBeforeUpdating: false, packageManager: packageManagerMock, }); (0, vitest_1.expect)(spawnSpy).toHaveBeenCalledWith('npm', ['install', '@sentry/sveltekit'], { shell: true, stdio: ['pipe', 'ignore', 'pipe'] }); }); (0, vitest_1.it)('adds install flags if defined', async () => { const packageManagerMock = { name: 'npm', label: 'NPM', installCommand: 'install', buildCommand: 'npm run build', runScriptCommand: 'npm run', flags: '--ignore-workspace-root-check', forceInstallFlag: '--force', detect: vitest_1.vi.fn(), addOverride: vitest_1.vi.fn(), }; await (0, clack_1.installPackage)({ alreadyInstalled: false, packageName: '@some/package', packageNameDisplayLabel: '@some/package', forceInstall: true, askBeforeUpdating: false, packageManager: packageManagerMock, }); (0, vitest_1.expect)(spawnSpy).toHaveBeenCalledWith('npm', ['install', '@some/package', '--ignore-workspace-root-check', '--force'], { shell: true, stdio: ['pipe', 'ignore', 'pipe'] }); }); }); (0, vitest_1.describe)('askForWizardLogin', () => { (0, vitest_1.beforeEach)(() => { vitest_1.vi.clearAllMocks(); mockedAxios.get.mockClear(); clackMock.confirm.mockClear(); clackMock.confirm.mockReset(); mockUserResponse(clack.confirm, undefined); }); (0, vitest_1.it)('asks if a user already has a Sentry account by default', async () => { mockUserResponse(clack.confirm, Promise.resolve(true)); // Provide the data object to be returned mockedAxios.get.mockResolvedValue({ data: { hash: 'mockedHash', }, }); await (0, clack_1.askForWizardLogin)({ url: 'https://santry.io/' }); (0, vitest_1.expect)(clack.confirm).toHaveBeenCalledWith(vitest_1.expect.objectContaining({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment message: vitest_1.expect.stringContaining('already have a Sentry account'), })); }); (0, vitest_1.it)('skips asking for if a user already has a Sentry account if org and project are pre-selected', async () => { mockUserResponse(clackMock.confirm, Promise.resolve(true)); // Provide the data object to be returned mockedAxios.get.mockResolvedValue({ data: { hash: 'mockedHash', }, }); await (0, clack_1.askForWizardLogin)({ url: 'https://santry.io/', orgSlug: 'my-org', projectSlug: 'my-project', }); (0, vitest_1.expect)(clack.confirm).not.toHaveBeenCalled(); }); }); (0, vitest_1.describe)('abort', () => { (0, vitest_1.beforeEach)(() => { vitest_1.vi.clearAllMocks(); mockSentrySession = { status: 999 }; }); (0, vitest_1.it)('ends the process with an error exit code by default', async () => { // @ts-expect-error - vitest doesn't like the empty function // eslint-disable-next-line @typescript-eslint/no-empty-function const exitSpy = vitest_1.vi.spyOn(process, 'exit').mockImplementation(() => { }); await (0, clack_1.abort)(); (0, vitest_1.expect)(exitSpy).toHaveBeenCalledWith(1); (0, vitest_1.expect)(clackMock.outro).toHaveBeenCalledTimes(1); (0, vitest_1.expect)(clackMock.outro).toHaveBeenCalledWith('Wizard setup cancelled.'); (0, vitest_1.expect)(mockRootSpan.setStatus).toHaveBeenLastCalledWith({ code: 2, message: 'aborted', }); (0, vitest_1.expect)(mockRootSpan.end).toHaveBeenCalledTimes(1); (0, vitest_1.expect)(mockSentrySession.status).toBe('crashed'); (0, vitest_1.expect)(Sentry.flush).toHaveBeenLastCalledWith(3000); }); (0, vitest_1.it)('ends the process with a custom exit code and message if provided', async () => { // @ts-expect-error - vitest doesn't like the empty function // eslint-disable-next-line @typescript-eslint/no-empty-function const exitSpy = vitest_1.vi.spyOn(process, 'exit').mockImplementation(() => { }); await (0, clack_1.abort)('Bye', 0); (0, vitest_1.expect)(exitSpy).toHaveBeenCalledWith(0); (0, vitest_1.expect)(clackMock.outro).toHaveBeenCalledTimes(1); (0, vitest_1.expect)(clackMock.outro).toHaveBeenCalledWith('Bye'); (0, vitest_1.expect)(mockRootSpan.setStatus).toHaveBeenLastCalledWith({ code: 1, message: 'cancelled', }); (0, vitest_1.expect)(mockRootSpan.end).toHaveBeenCalledTimes(1); (0, vitest_1.expect)(mockSentrySession.status).toBe('abnormal'); (0, vitest_1.expect)(Sentry.flush).toHaveBeenLastCalledWith(3000); }); }); (0, vitest_1.describe)('getPackageManager', () => { (0, vitest_1.afterEach)(() => { vitest_1.vi.clearAllMocks(); // @ts-expect-error - this variable is set by the wizard delete global.__sentry_wizard_cached_package_manager; }); (0, vitest_1.it)('returns the auto-detected package manager', async () => { const detectPacManSpy = vitest_1.vi .spyOn(PackageManagerUtils, '_detectPackageManger') .mockReturnValueOnce(package_manager_1.YARN_V1); const packageManager = await (0, clack_1.getPackageManager)(); (0, vitest_1.expect)(detectPacManSpy).toHaveBeenCalledTimes(1); (0, vitest_1.expect)(packageManager).toBe(package_manager_1.YARN_V1); }); (0, vitest_1.it)('caches the auto-detected package manager', async () => { const detectPacManSpy = vitest_1.vi .spyOn(PackageManagerUtils, '_detectPackageManger') .mockReturnValueOnce(package_manager_1.YARN_V1); const packageManager1 = await (0, clack_1.getPackageManager)(); const packageManager2 = await (0, clack_1.getPackageManager)(); (0, vitest_1.expect)(detectPacManSpy).toHaveBeenCalledTimes(1); (0, vitest_1.expect)(packageManager1).toBe(package_manager_1.YARN_V1); (0, vitest_1.expect)(packageManager2).toBe(package_manager_1.YARN_V1); }); (0, vitest_1.describe)('when auto detection fails', () => { (0, vitest_1.it)('returns a fallback package manager if fallback is specified', async () => { const detectPacManSpy = vitest_1.vi .spyOn(PackageManagerUtils, '_detectPackageManger') .mockReturnValueOnce(null); const packageManager = await (0, clack_1.getPackageManager)(package_manager_1.YARN_V2); (0, vitest_1.expect)(detectPacManSpy).toHaveBeenCalledTimes(1); (0, vitest_1.expect)(packageManager).toBe(package_manager_1.YARN_V2); }); (0, vitest_1.it)("doesn't cache the fallback package manager", async () => { const detectPacManSpy = vitest_1.vi .spyOn(PackageManagerUtils, '_detectPackageManger') .mockReturnValue(null); const packageManager1 = await (0, clack_1.getPackageManager)(package_manager_1.YARN_V2); const packageManager2 = await (0, clack_1.getPackageManager)(package_manager_1.NPM); (0, vitest_1.expect)(detectPacManSpy).toHaveBeenCalledTimes(2); (0, vitest_1.expect)(packageManager1).toBe(package_manager_1.YARN_V2); (0, vitest_1.expect)(packageManager2).toBe(package_manager_1.NPM); }); (0, vitest_1.it)('returns the user-selected package manager if no fallback is provided', async () => { const detectPacManSpy = vitest_1.vi .spyOn(PackageManagerUtils, '_detectPackageManger') .mockReturnValueOnce(null); clackMock.select.mockReturnValueOnce(Promise.resolve(package_manager_1.PNPM)); const packageManager = await (0, clack_1.getPackageManager)(); (0, vitest_1.expect)(detectPacManSpy).toHaveBeenCalledTimes(1); (0, vitest_1.expect)(packageManager).toBe(package_manager_1.PNPM); }); (0, vitest_1.it)('caches the user-selected package manager', async () => { const detectPacManSpy = vitest_1.vi .spyOn(PackageManagerUtils, '_detectPackageManger') .mockReturnValueOnce(null); clackMock.select.mockReturnValueOnce(Promise.resolve(package_manager_1.PNPM)); const packageManager1 = await (0, clack_1.getPackageManager)(); const packageManager2 = await (0, clack_1.getPackageManager)(); (0, vitest_1.expect)(detectPacManSpy).toHaveBeenCalledTimes(1); (0, vitest_1.expect)(clackMock.select).toHaveBeenCalledTimes(1); (0, vitest_1.expect)(packageManager1).toBe(package_manager_1.PNPM); (0, vitest_1.expect)(packageManager2).toBe(package_manager_1.PNPM); }); }); }); //# sourceMappingURL=index.test.js.map