UNPKG

@sentry/wizard

Version:

Sentry wizard helping you to configure your project

379 lines 17.1 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")); jest.mock('node:child_process', () => ({ __esModule: true, ...jest.requireActual('node:child_process'), })); jest.mock('@clack/prompts', () => ({ log: { info: jest.fn(), success: jest.fn(), warn: jest.fn(), error: jest.fn(), }, outro: jest.fn(), text: jest.fn(), confirm: jest.fn(), cancel: jest.fn(), // passthrough for abortIfCancelled isCancel: jest.fn().mockReturnValue(false), spinner: jest .fn() .mockImplementation(() => ({ start: jest.fn(), stop: jest.fn() })), select: jest.fn(), })); const clackMock = clack; jest.mock('axios'); const mockedAxios = axios_1.default; jest.mock('opn', () => jest.fn(() => Promise.resolve({ on: jest.fn() }))); function mockUserResponse(fn, response) { fn.mockReturnValueOnce(response); } describe('askForToolConfigPath', () => { afterEach(() => { jest.clearAllMocks(); }); 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'); expect(clack.confirm).toHaveBeenCalledWith(expect.objectContaining({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment message: expect.stringContaining('have a Webpack config file'), })); expect(result).toBeUndefined(); }); 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'); expect(clack.confirm).toHaveBeenCalledWith(expect.objectContaining({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment message: expect.stringContaining('have a Webpack config file'), })); expect(clack.text).toHaveBeenCalledWith(expect.objectContaining({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment message: expect.stringContaining('enter the path to your Webpack config file'), })); expect(result).toBe('my.webpack.config.js'); }); }); describe('createNewConfigFile', () => { afterEach(() => { jest.clearAllMocks(); }); it('writes the file to disk and returns true if the file was created successfully', async () => { const writeFileSpy = jest .spyOn(fs.promises, 'writeFile') .mockImplementation(jest.fn()); const filename = '/webpack.config.js'; const code = 'module.exports = {/*config...*/}'; const result = await (0, clack_1.createNewConfigFile)(filename, code); expect(result).toBe(true); expect(writeFileSpy).toHaveBeenCalledWith(filename, code); }); it('logs more information if provided as an argument', async () => { jest.spyOn(fs.promises, 'writeFile').mockImplementation(jest.fn()); const filename = '/webpack.config.js'; const code = 'module.exports = {/*config...*/}'; const moreInfo = 'More information...'; await (0, clack_1.createNewConfigFile)(filename, code, moreInfo); expect(clack.log.info).toHaveBeenCalledTimes(1); expect(clack.log.info).toHaveBeenCalledWith(expect.stringContaining(moreInfo)); }); it('returns false and logs a warning if the file could not be created', async () => { const writeFileSpy = jest .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); expect(result).toBe(false); expect(writeFileSpy).toHaveBeenCalledWith(filename, code); expect(clack.log.warn).toHaveBeenCalledTimes(1); }); it('returns false if the passed path is not absolute', async () => { const result = await (0, clack_1.createNewConfigFile)('./relative/webpack.config.js', ''); expect(result).toBe(false); }); }); describe('installPackage', () => { afterEach(() => { jest.clearAllMocks(); }); const spawnSpy = jest .spyOn(ChildProcess, 'spawn') // @ts-expect-error - ignoring complete typing .mockImplementation(() => ({ // @ts-expect-error - not passing the full object but directly resolving // to simulate a successful install on: jest.fn((evt, cb) => { if (evt === 'close') { cb(0); } }), stderr: { on: jest.fn() }, })); 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: jest.fn(), addOverride: jest.fn(), }; await (0, clack_1.installPackage)({ alreadyInstalled: false, packageName: '@some/package', packageNameDisplayLabel: '@some/package', forceInstall: true, askBeforeUpdating: false, packageManager: packageManagerMock, }); expect(spawnSpy).toHaveBeenCalledWith('npm', ['install', '@some/package', '--force'], { shell: true, stdio: ['pipe', 'ignore', 'pipe'] }); }); 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: jest.fn(), addOverride: jest.fn(), }; await (0, clack_1.installPackage)({ alreadyInstalled: false, packageName: '@sentry/sveltekit', packageNameDisplayLabel: '@sentry/sveltekit', forceInstall: flag, askBeforeUpdating: false, packageManager: packageManagerMock, }); expect(spawnSpy).toHaveBeenCalledWith('npm', ['install', '@sentry/sveltekit'], { shell: true, stdio: ['pipe', 'ignore', 'pipe'] }); }); 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: jest.fn(), addOverride: jest.fn(), }; await (0, clack_1.installPackage)({ alreadyInstalled: false, packageName: '@some/package', packageNameDisplayLabel: '@some/package', forceInstall: true, askBeforeUpdating: false, packageManager: packageManagerMock, }); expect(spawnSpy).toHaveBeenCalledWith('npm', ['install', '@some/package', '--ignore-workspace-root-check', '--force'], { shell: true, stdio: ['pipe', 'ignore', 'pipe'] }); }); }); describe('askForWizardLogin', () => { // mock axios afterEach(() => { // clackMock.confirm.mockClear(); // mockUserResponse(clack.confirm as jest.Mock, undefined); }); beforeEach(() => { jest.clearAllMocks(); mockedAxios.get.mockClear(); clackMock.confirm.mockClear(); clackMock.confirm.mockReset(); mockUserResponse(clack.confirm, undefined); }); 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/' }); expect(clack.confirm).toHaveBeenCalledWith(expect.objectContaining({ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment message: expect.stringContaining('already have a Sentry account'), })); }); 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', }); expect(clack.confirm).not.toHaveBeenCalled(); }); }); describe('abort', () => { const sentryTxn = { setStatus: jest.fn(), finish: jest.fn(), }; let sentrySession = { status: 999, }; beforeEach(() => { jest.clearAllMocks(); sentrySession = { status: 999, }; }); jest.spyOn(Sentry, 'getCurrentHub').mockReturnValue({ getScope: () => ({ // @ts-expect-error - don't care about the rest of the required props value getTransaction: () => sentryTxn, // @ts-expect-error - don't care about the rest of the required props value getSession: () => sentrySession, }), captureSession: jest.fn(), }); const flushSpy = jest.fn(); jest.spyOn(Sentry, 'flush').mockImplementation(flushSpy); it('ends the process with an error exit code by default', async () => { // @ts-expect-error - jest doesn't like the empty function // eslint-disable-next-line @typescript-eslint/no-empty-function const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => { }); await (0, clack_1.abort)(); expect(exitSpy).toHaveBeenCalledWith(1); expect(clackMock.outro).toHaveBeenCalledTimes(1); expect(clackMock.outro).toHaveBeenCalledWith('Wizard setup cancelled.'); expect(sentryTxn.setStatus).toHaveBeenLastCalledWith('aborted'); expect(sentryTxn.finish).toHaveBeenCalledTimes(1); expect(sentrySession.status).toBe('crashed'); expect(flushSpy).toHaveBeenLastCalledWith(3000); }); it('ends the process with a custom exit code and message if provided', async () => { // @ts-expect-error - jest doesn't like the empty function // eslint-disable-next-line @typescript-eslint/no-empty-function const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => { }); await (0, clack_1.abort)('Bye', 0); expect(exitSpy).toHaveBeenCalledWith(0); expect(clackMock.outro).toHaveBeenCalledTimes(1); expect(clackMock.outro).toHaveBeenCalledWith('Bye'); expect(sentryTxn.setStatus).toHaveBeenLastCalledWith('cancelled'); expect(sentryTxn.finish).toHaveBeenCalledTimes(1); expect(sentrySession.status).toBe('abnormal'); expect(flushSpy).toHaveBeenLastCalledWith(3000); }); }); describe('getPackageManager', () => { afterEach(() => { jest.clearAllMocks(); // @ts-expect-error - this variable is set by the wizard delete global.__sentry_wizard_cached_package_manager; }); it('returns the auto-detected package manager', async () => { const detectPacManSpy = jest .spyOn(PackageManagerUtils, '_detectPackageManger') .mockReturnValueOnce(package_manager_1.YARN_V1); const packageManager = await (0, clack_1.getPackageManager)(); expect(detectPacManSpy).toHaveBeenCalledTimes(1); expect(packageManager).toBe(package_manager_1.YARN_V1); }); it('caches the auto-detected package manager', async () => { const detectPacManSpy = jest .spyOn(PackageManagerUtils, '_detectPackageManger') .mockReturnValueOnce(package_manager_1.YARN_V1); const packageManager1 = await (0, clack_1.getPackageManager)(); const packageManager2 = await (0, clack_1.getPackageManager)(); expect(detectPacManSpy).toHaveBeenCalledTimes(1); expect(packageManager1).toBe(package_manager_1.YARN_V1); expect(packageManager2).toBe(package_manager_1.YARN_V1); }); describe('when auto detection fails', () => { it('returns a fallback package manager if fallback is specified', async () => { const detectPacManSpy = jest .spyOn(PackageManagerUtils, '_detectPackageManger') .mockReturnValueOnce(null); const packageManager = await (0, clack_1.getPackageManager)(package_manager_1.YARN_V2); expect(detectPacManSpy).toHaveBeenCalledTimes(1); expect(packageManager).toBe(package_manager_1.YARN_V2); }); it("doesn't cache the fallback package manager", async () => { const detectPacManSpy = jest .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); expect(detectPacManSpy).toHaveBeenCalledTimes(2); expect(packageManager1).toBe(package_manager_1.YARN_V2); expect(packageManager2).toBe(package_manager_1.NPM); }); it('returns the user-selected package manager if no fallback is provided', async () => { const detectPacManSpy = jest .spyOn(PackageManagerUtils, '_detectPackageManger') .mockReturnValueOnce(null); clackMock.select.mockReturnValueOnce(Promise.resolve(package_manager_1.PNPM)); const packageManager = await (0, clack_1.getPackageManager)(); expect(detectPacManSpy).toHaveBeenCalledTimes(1); expect(packageManager).toBe(package_manager_1.PNPM); }); it('caches the user-selected package manager', async () => { const detectPacManSpy = jest .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)(); expect(detectPacManSpy).toHaveBeenCalledTimes(1); expect(clackMock.select).toHaveBeenCalledTimes(1); expect(packageManager1).toBe(package_manager_1.PNPM); expect(packageManager2).toBe(package_manager_1.PNPM); }); }); }); //# sourceMappingURL=index.test.js.map