@sentry/wizard
Version:
Sentry wizard helping you to configure your project
289 lines • 12.3 kB
JavaScript
;
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_utils_1 = require("../../src/utils/clack-utils");
const fs = __importStar(require("node:fs"));
const ChildProcess = __importStar(require("node:child_process"));
const axios_1 = __importDefault(require("axios"));
// @ts-ignore - 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() })),
}));
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_utils_1.askForToolConfigPath)('Webpack', 'webpack.config.js');
expect(clack.confirm).toHaveBeenCalledWith(expect.objectContaining({
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_utils_1.askForToolConfigPath)('Webpack', 'webpack.config.js');
expect(clack.confirm).toHaveBeenCalledWith(expect.objectContaining({
message: expect.stringContaining('have a Webpack config file'),
}));
expect(clack.text).toHaveBeenCalledWith(expect.objectContaining({
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_utils_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_utils_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_utils_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_utils_1.createNewConfigFile)('./relative/webpack.config.js', '');
expect(result).toBe(false);
});
});
describe('installPackage', () => {
afterEach(() => {
jest.clearAllMocks();
});
it('force-installs a package if the forceInstall flag is set', async () => {
const packageManagerMock = {
name: 'npm',
label: 'NPM',
installCommand: 'npm install',
buildCommand: 'npm run build',
runScriptCommand: 'npm run',
flags: '',
forceInstallFlag: '--force',
detect: jest.fn(),
addOverride: jest.fn(),
};
const execSpy = jest
.spyOn(ChildProcess, 'exec')
// @ts-expect-error - don't care about the return value
.mockImplementationOnce((cmd, cb) => {
if (cb) {
// @ts-expect-error - don't care about the options value
cb(null, '', '');
}
});
await (0, clack_utils_1.installPackage)({
alreadyInstalled: false,
packageName: '@sentry/sveltekit',
packageNameDisplayLabel: '@sentry/sveltekit',
forceInstall: true,
askBeforeUpdating: false,
packageManager: packageManagerMock,
});
expect(execSpy).toHaveBeenCalledWith('npm install @sentry/sveltekit --force', expect.any(Function));
});
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: 'npm install',
buildCommand: 'npm run build',
runScriptCommand: 'npm run',
flags: '',
forceInstallFlag: '--force',
detect: jest.fn(),
addOverride: jest.fn(),
};
const execSpy = jest
.spyOn(ChildProcess, 'exec')
// @ts-expect-error - don't care about the return value
.mockImplementationOnce((cmd, cb) => {
if (cb) {
// @ts-expect-error - don't care about the options value
cb(null, '', '');
}
});
await (0, clack_utils_1.installPackage)({
alreadyInstalled: false,
packageName: '@sentry/sveltekit',
packageNameDisplayLabel: '@sentry/sveltekit',
forceInstall: flag,
askBeforeUpdating: false,
packageManager: packageManagerMock,
});
expect(execSpy).toHaveBeenCalledWith('npm install @sentry/sveltekit ', expect.any(Function));
});
});
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_utils_1.askForWizardLogin)({ url: 'https://santry.io/' });
expect(clack.confirm).toHaveBeenCalledWith(expect.objectContaining({
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_utils_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-ignore - 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_utils_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-ignore - 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_utils_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);
});
});
//# sourceMappingURL=clack-utils.test.js.map