Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

ctrf-cli

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ctrf-cli - npm Package Compare versions

Comparing version
0.0.3
to
0.0.4
+2
dist/cli.d.ts
#!/usr/bin/env node
export {};
#!/usr/bin/env node
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const yargs_1 = __importDefault(require("yargs/yargs"));
const helpers_1 = require("yargs/helpers");
const merge_1 = require("./merge");
const flaky_1 = require("./flaky");
const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
.command('merge <directory>', 'Merge CTRF reports into a single report', yargs => {
return yargs
.positional('directory', {
describe: 'Directory of the CTRF reports',
type: 'string',
demandOption: true,
})
.option('output', {
alias: 'o',
describe: 'Output file path (default: ctrf-report.json)',
type: 'string',
default: 'ctrf-report.json',
})
.option('output-dir', {
alias: 'd',
describe: 'Output directory for merged report',
type: 'string',
hidden: true,
deprecated: 'Use --output with a path instead',
})
.option('keep-reports', {
alias: 'k',
describe: 'Keep existing reports after merging',
type: 'boolean',
default: false,
});
}, (argv) => __awaiter(void 0, void 0, void 0, function* () {
yield (0, merge_1.mergeReports)(argv.directory, argv.output, argv['keep-reports'], argv['output-dir']);
}))
.command('flaky <file>', 'Identify flaky tests from a CTRF report file', yargs => {
return yargs.positional('file', {
describe: 'CTRF report file',
type: 'string',
demandOption: true,
});
}, (argv) => __awaiter(void 0, void 0, void 0, function* () {
yield (0, flaky_1.identifyFlakyTests)(argv.file);
}))
.help()
.demandCommand(1, 'You need at least one command before moving on').argv;
export declare function identifyFlakyTests(filePath: string): Promise<void>;
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.identifyFlakyTests = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
function identifyFlakyTests(filePath) {
return __awaiter(this, void 0, void 0, function* () {
try {
const resolvedFilePath = path_1.default.resolve(filePath);
if (!fs_1.default.existsSync(resolvedFilePath)) {
console.error(`The file ${resolvedFilePath} does not exist.`);
return;
}
const fileContent = fs_1.default.readFileSync(resolvedFilePath, 'utf8');
const report = JSON.parse(fileContent);
const flakyTests = report.results.tests.filter((test) => test.flaky === true);
if (flakyTests.length > 0) {
console.log(`Found ${flakyTests.length} flaky test(s):`);
flakyTests.forEach((test) => {
console.log(`- Test Name: ${test.name}, Retries: ${test.retries}`);
});
}
else {
console.log(`No flaky tests found in ${resolvedFilePath}.`);
}
}
catch (error) {
console.error('Error identifying flaky tests:', error);
}
});
}
exports.identifyFlakyTests = identifyFlakyTests;
export { mergeReports } from './methods/merge-reports';
export { readReportsFromDirectory } from './methods/read-reports';
export { readReportsFromGlobPattern } from './methods/read-reports';
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.readReportsFromGlobPattern = exports.readReportsFromDirectory = exports.mergeReports = void 0;
var merge_reports_1 = require("./methods/merge-reports");
Object.defineProperty(exports, "mergeReports", { enumerable: true, get: function () { return merge_reports_1.mergeReports; } });
var read_reports_1 = require("./methods/read-reports");
Object.defineProperty(exports, "readReportsFromDirectory", { enumerable: true, get: function () { return read_reports_1.readReportsFromDirectory; } });
var read_reports_2 = require("./methods/read-reports");
Object.defineProperty(exports, "readReportsFromGlobPattern", { enumerable: true, get: function () { return read_reports_2.readReportsFromGlobPattern; } });
export declare function mergeReports(directory: string, output: string, keepReports: boolean, outputDir?: string): Promise<void>;
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeReports = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
function mergeReports(directory, output, keepReports, outputDir) {
return __awaiter(this, void 0, void 0, function* () {
try {
const directoryPath = path_1.default.resolve(directory);
let outputPath;
if (outputDir) {
console.warn('Warning: --output-dir is deprecated. Use --output with a path instead.');
const outputFileName = output;
const resolvedOutputDir = path_1.default.resolve(outputDir);
outputPath = path_1.default.join(resolvedOutputDir, outputFileName);
}
else if (output.includes('/') || output.includes('\\')) {
const resolvedPath = path_1.default.resolve(output);
const endsWithSeparator = output.endsWith('/') || output.endsWith('\\');
const isExistingDirectory = fs_1.default.existsSync(resolvedPath) && fs_1.default.statSync(resolvedPath).isDirectory();
const hasNoExtension = path_1.default.extname(output) === '';
if (endsWithSeparator || isExistingDirectory || hasNoExtension) {
outputPath = path_1.default.join(resolvedPath, 'ctrf-report.json');
}
else {
outputPath = resolvedPath;
}
}
else {
outputPath = path_1.default.join(directoryPath, output);
}
console.log('Merging CTRF reports...');
const files = fs_1.default.readdirSync(directoryPath);
files.forEach(file => {
console.log('Found file:', file);
});
const ctrfReportFiles = files.filter(file => {
try {
if (path_1.default.extname(file) !== '.json') {
console.log(`Skipping non-CTRF file: ${file}`);
return false;
}
const filePath = path_1.default.join(directoryPath, file);
const fileContent = fs_1.default.readFileSync(filePath, 'utf8');
const jsonData = JSON.parse(fileContent);
if (!Object.prototype.hasOwnProperty.call(jsonData, 'results')) {
console.log(`Skipping non-CTRF file: ${file}`);
return false;
}
return true;
}
catch (error) {
console.error(`Error reading JSON file '${file}':`, error);
return false;
}
});
if (ctrfReportFiles.length === 0) {
console.log('No CTRF reports found in the specified directory.');
return;
}
const outputDirPath = path_1.default.dirname(outputPath);
if (!fs_1.default.existsSync(outputDirPath)) {
fs_1.default.mkdirSync(outputDirPath, { recursive: true });
console.log(`Created output directory: ${outputDirPath}`);
}
const mergedReport = ctrfReportFiles
.map(file => {
console.log('Merging report:', file);
const filePath = path_1.default.join(directoryPath, file);
const fileContent = fs_1.default.readFileSync(filePath, 'utf8');
return JSON.parse(fileContent);
})
.reduce((acc, curr) => {
if (!acc.results) {
return curr;
}
acc.results.summary.tests += curr.results.summary.tests;
acc.results.summary.passed += curr.results.summary.passed;
acc.results.summary.failed += curr.results.summary.failed;
acc.results.summary.skipped += curr.results.summary.skipped;
acc.results.summary.pending += curr.results.summary.pending;
acc.results.summary.other += curr.results.summary.other;
acc.results.tests.push(...curr.results.tests);
acc.results.summary.start = Math.min(acc.results.summary.start, curr.results.summary.start);
acc.results.summary.stop = Math.max(acc.results.summary.stop, curr.results.summary.stop);
return acc;
}, { results: null });
fs_1.default.writeFileSync(outputPath, JSON.stringify(mergedReport, null, 2));
if (!keepReports) {
const outputFileName = path_1.default.basename(outputPath);
ctrfReportFiles.forEach(file => {
const filePath = path_1.default.join(directoryPath, file);
if (file !== outputFileName) {
fs_1.default.unlinkSync(filePath);
}
});
}
console.log('CTRF reports merged successfully.');
console.log(`Merged report saved to: ${outputPath}`);
}
catch (error) {
console.error('Error merging CTRF reports:', error);
}
});
}
exports.mergeReports = mergeReports;
import { CtrfReport } from '../../types/ctrf';
/**
* Merges multiple CTRF reports into a single report.
*
* @param reports Array of CTRF report objects to be merged.
* @returns The merged CTRF report object.
*/
export declare function mergeReports(reports: CtrfReport[]): CtrfReport;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeReports = void 0;
/**
* Merges multiple CTRF reports into a single report.
*
* @param reports Array of CTRF report objects to be merged.
* @returns The merged CTRF report object.
*/
function mergeReports(reports) {
if (!reports || reports.length === 0) {
throw new Error('No reports provided for merging.');
}
const mergedReport = {
results: {
tool: reports[0].results.tool,
summary: initializeEmptySummary(),
tests: [],
},
};
reports.forEach(report => {
const { summary, tests, environment, extra } = report.results;
mergedReport.results.summary.tests += summary.tests;
mergedReport.results.summary.passed += summary.passed;
mergedReport.results.summary.failed += summary.failed;
mergedReport.results.summary.skipped += summary.skipped;
mergedReport.results.summary.pending += summary.pending;
mergedReport.results.summary.other += summary.other;
if (summary.suites !== undefined) {
mergedReport.results.summary.suites =
(mergedReport.results.summary.suites || 0) + summary.suites;
}
mergedReport.results.summary.start = Math.min(mergedReport.results.summary.start, summary.start);
mergedReport.results.summary.stop = Math.max(mergedReport.results.summary.stop, summary.stop);
mergedReport.results.tests.push(...tests);
if (environment) {
mergedReport.results.environment = Object.assign(Object.assign({}, mergedReport.results.environment), environment);
}
if (extra) {
mergedReport.results.extra = Object.assign(Object.assign({}, mergedReport.results.extra), extra);
}
});
return mergedReport;
}
exports.mergeReports = mergeReports;
/**
* Initializes an empty summary object.
*
* @returns An empty Summary object.
*/
function initializeEmptySummary() {
return {
tests: 0,
passed: 0,
failed: 0,
skipped: 0,
pending: 0,
other: 0,
start: Number.MAX_SAFE_INTEGER,
stop: 0,
};
}
import { CtrfReport } from '../../types/ctrf';
/**
* Reads a single CTRF report file from a specified path.
*
* @param filePath Path to the JSON file containing the CTRF report.
* @returns The parsed `CtrfReport` object.
* @throws If the file does not exist, is not a valid JSON, or does not conform to the `CtrfReport` structure.
*/
export declare function readSingleReport(filePath: string): CtrfReport;
/**
* Reads all CTRF report files from a given directory.
*
* @param directory Path to the directory containing JSON files.
* @returns An array of parsed `CtrfReport` objects.
* @throws If the directory does not exist or no valid CTRF reports are found.
*/
export declare function readReportsFromDirectory(directoryPath: string): CtrfReport[];
/**
* Reads all CTRF report files matching a glob pattern.
*
* @param pattern The glob pattern to match files (e.g., ctrf/*.json).
* @returns An array of parsed `CtrfReport` objects.
* @throws If no valid CTRF reports are found.
*/
export declare function readReportsFromGlobPattern(pattern: string): CtrfReport[];
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readReportsFromGlobPattern = exports.readReportsFromDirectory = exports.readSingleReport = void 0;
// TO BE REMOVED, WILL USE THE CTRF LIBRARY INSTEAD
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const glob_1 = require("glob");
/**
* Reads a single CTRF report file from a specified path.
*
* @param filePath Path to the JSON file containing the CTRF report.
* @returns The parsed `CtrfReport` object.
* @throws If the file does not exist, is not a valid JSON, or does not conform to the `CtrfReport` structure.
*/
function readSingleReport(filePath) {
if (!fs_1.default.existsSync(filePath)) {
throw new Error(`JSON file not found: ${filePath}`);
}
const resolvedPath = path_1.default.resolve(filePath);
if (!fs_1.default.existsSync(resolvedPath)) {
throw new Error(`The file '${resolvedPath}' does not exist.`);
}
try {
const content = fs_1.default.readFileSync(resolvedPath, 'utf8');
const parsed = JSON.parse(content);
if (!isCtrfReport(parsed)) {
throw new Error(`The file '${resolvedPath}' is not a valid CTRF report.`);
}
return parsed;
}
catch (error) {
const errorMessage = error.message || 'Unknown error';
throw new Error(`Failed to read or parse the file '${resolvedPath}': ${errorMessage}`);
}
}
exports.readSingleReport = readSingleReport;
/**
* Reads all CTRF report files from a given directory.
*
* @param directory Path to the directory containing JSON files.
* @returns An array of parsed `CtrfReport` objects.
* @throws If the directory does not exist or no valid CTRF reports are found.
*/
function readReportsFromDirectory(directoryPath) {
directoryPath = path_1.default.resolve(directoryPath);
if (!fs_1.default.existsSync(directoryPath)) {
throw new Error(`The directory '${directoryPath}' does not exist.`);
}
const files = fs_1.default.readdirSync(directoryPath);
const reports = files
.filter(file => path_1.default.extname(file) === '.json')
.map(file => {
const filePath = path_1.default.join(directoryPath, file);
try {
const content = fs_1.default.readFileSync(filePath, 'utf8');
const parsed = JSON.parse(content);
if (!isCtrfReport(parsed)) {
console.warn(`Skipping invalid CTRF report file: ${file}`);
return null;
}
return parsed;
}
catch (error) {
console.warn(`Failed to read or parse file '${file}':`, error);
return null;
}
})
.filter((report) => report !== null);
if (reports.length === 0) {
throw new Error(`No valid CTRF reports found in the directory '${directoryPath}'.`);
}
return reports;
}
exports.readReportsFromDirectory = readReportsFromDirectory;
/**
* Reads all CTRF report files matching a glob pattern.
*
* @param pattern The glob pattern to match files (e.g., ctrf/*.json).
* @returns An array of parsed `CtrfReport` objects.
* @throws If no valid CTRF reports are found.
*/
function readReportsFromGlobPattern(pattern) {
const files = glob_1.glob.sync(pattern);
if (files.length === 0) {
throw new Error(`No files found matching the pattern '${pattern}'.`);
}
const reports = files
.map(file => {
try {
const content = fs_1.default.readFileSync(file, 'utf8');
const parsed = JSON.parse(content);
if (!isCtrfReport(parsed)) {
console.warn(`Skipping invalid CTRF report file: ${file}`);
return null;
}
return parsed;
}
catch (error) {
console.warn(`Failed to read or parse file '${file}':`, error);
return null;
}
})
.filter((report) => report !== null);
if (reports.length === 0) {
throw new Error(`No valid CTRF reports found matching the pattern '${pattern}'.`);
}
return reports;
}
exports.readReportsFromGlobPattern = readReportsFromGlobPattern;
/**
* Checks if an object conforms to the `CtrfReport` structure.
*
* @param obj The object to validate.
* @returns `true` if the object matches the `CtrfReport` type; otherwise, `false`.
*/
function isCtrfReport(obj) {
return (obj &&
typeof obj === 'object' &&
obj.results &&
Array.isArray(obj.results.tests) &&
typeof obj.results.summary === 'object' &&
typeof obj.results.tool === 'object');
}
+2
-2
{
"name": "ctrf-cli",
"version": "0.0.3",
"version": "0.0.4",
"description": "Various CTRF utilities available from the command line",
"main": "dist/index.js",
"bin": {
"ctrf": "dist/cli.js"
"ctrf-cli": "dist/cli.js"
},

@@ -9,0 +9,0 @@ "scripts": {