Skip to content

Commit

Permalink
Merge pull request #1 from dreamquality/alex/xlsx-feature
Browse files Browse the repository at this point in the history
added CSV support as an API spec
  • Loading branch information
dreamquality authored Jan 20, 2025
2 parents 7bc35b0 + d240d53 commit 41842cf
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 34 deletions.
56 changes: 41 additions & 15 deletions cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const { loadAndParseSpec, extractOperationsFromSpec } = require("./lib/swagger")
const { loadPostmanCollection, extractRequestsFromPostman } = require("./lib/postman");
const { matchOperationsDetailed } = require("./lib/match");
const { generateHtmlReport } = require("./lib/report");
const { loadExcelSpec } = require("./lib/excel");

const program = new Command();

Expand All @@ -28,21 +29,46 @@ program
try {
const { verbose, strictQuery, strictBody, output } = options;

// 1. Load & parse spec
const spec = await loadAndParseSpec(swaggerFile);
if (verbose) {
console.log(
"Specification loaded successfully:",
spec.info.title,
spec.info.version
);
const ext = path.extname(swaggerFile).toLowerCase();
const excelExtensions = [".xlsx", ".xls", ".csv"];
let specOperations;
let specName; // Add this variable

if (excelExtensions.includes(ext)) {
// Parse Excel
specOperations = loadExcelSpec(swaggerFile);
specName = path.basename(swaggerFile); // Set name for Excel files
} else {
// Original Swagger flow
const spec = await loadAndParseSpec(swaggerFile);
specName = spec.info.title; // Set name for Swagger files
if (verbose) {
console.log(
"Specification loaded successfully:",
specName,
spec.info.version
);
}
specOperations = extractOperationsFromSpec(spec, verbose);
}

// 2. Extract spec operations
const specOperations = extractOperationsFromSpec(spec, verbose);
// Ensure Postman file exists
if (!fs.existsSync(postmanFile)) {
throw new Error(`Postman file not found: ${postmanFile}`);
}

// Safely parse Postman JSON
let postmanData;
try {
const rawPostman = fs.readFileSync(postmanFile, "utf8");
if (!rawPostman.trim()) {
throw new Error("Postman file is empty.");
}
postmanData = JSON.parse(rawPostman);
} catch (err) {
throw new Error(`Unable to parse Postman JSON: ${err.message}`);
}

// 3. Load Postman collection
const postmanData = loadPostmanCollection(postmanFile);
if (verbose) {
console.log(
`Postman collection loaded successfully: "${postmanData.info.name}"`
Expand Down Expand Up @@ -96,13 +122,13 @@ program
});
}

// 7. Generate HTML report (enhanced with nested tables, charts, etc.)
// 7. Generate HTML report with specName instead of spec.info.title
const html = generateHtmlReport({
coverage,
coverageItems, // used by the expand/collapse table
coverageItems,
meta: {
timestamp: new Date().toLocaleString(),
specName: spec.info.title,
specName, // Use specName here
postmanCollectionName: postmanData.info.name,
},
});
Expand Down
59 changes: 59 additions & 0 deletions lib/excel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const XLSX = require('xlsx');

/**
* loadExcelSpec - Reads an Excel file and returns an array of operations:
* [
* {
* method: "GET",
* path: "/v2/artist/elements",
* name: "listElements",
* tags: ["Artists","Collections"],
* expectedStatusCodes: ["200"],
* statusCode: "200",
* requestBodyContent: [],
* ...
* },
* ...
* ]
*/
function loadExcelSpec(filePath) {
try {
const workbook = XLSX.readFile(filePath);
if (!workbook.SheetNames || workbook.SheetNames.length === 0) {
throw new Error("No sheets found in Excel file.");
}

const operations = [];
workbook.SheetNames.forEach(sheetName => {
const sheet = workbook.Sheets[sheetName];
if (!sheet) return;
const rows = XLSX.utils.sheet_to_json(sheet, { defval: "" });
rows.forEach(row => {
const method = (row.METHOD || "GET").toLowerCase();
const path = row.URI || "";
const name = row.NAME || "";
const tags = row.TAGS ? row.TAGS.split(",").map(t => t.trim()) : [];
const statusCode = row["STATUS CODE"] || "";
const expectedStatusCodes = statusCode ? [statusCode] : [];
const requestBodyContent = row.BODY ? ["application/json"] : [];

operations.push({
method,
path,
operationId: name,
summary: name,
tags,
statusCode,
expectedStatusCodes,
requestBodyContent
});
});
});

return operations;
} catch (err) {
throw new Error(`Error parsing Excel file: ${err.message}`);
}
}

module.exports = { loadExcelSpec };
9 changes: 3 additions & 6 deletions lib/match.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ function doesMatch(specOp, pmReq, { strictQuery, strictBody }) {
}

// 3. Status code
// If specOp has a 'statusCode' property, and it's numeric, ensure pmReq tested that code
if (specOp.statusCode && /^\d{3}$/.test(specOp.statusCode)) {
if (!pmReq.testedStatusCodes.includes(specOp.statusCode)) {
if (specOp.statusCode) {
const specStatusCode = specOp.statusCode.toString();
if (!pmReq.testedStatusCodes.includes(specStatusCode)) {
return false;
}
}
Expand Down Expand Up @@ -250,12 +250,9 @@ function validateParamWithSchema(value, paramSchema) {
* - Replaces {param} segments with [^/]+ in a regex, ignoring query part
*/
function urlMatchesSwaggerPath(postmanUrl, swaggerPath) {
// Remove protocol/host or {{baseUrl}}
let cleaned = postmanUrl.replace(/^(https?:\/\/)?\{\{.*?\}\}/, "");
cleaned = cleaned.replace(/^https?:\/\/[^/]+/, "");
// Remove query
cleaned = cleaned.split("?")[0];
// Trim trailing slashes
cleaned = cleaned.replace(/\/+$/, "");
if (!cleaned) cleaned = "/";

Expand Down
107 changes: 104 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "swagger-coverage-cli",
"version": "1.0.1",
"version": "1.1.0",
"description": "A Node.js CLI tool to measure test coverage of Swagger/OpenAPI specs using Postman collections.",
"main": "cli.js",
"bin": {
Expand Down Expand Up @@ -31,7 +31,8 @@
"@apidevtools/swagger-parser": "^10.1.1",
"ajv": "^8.17.1",
"commander": "^13.0.0",
"js-yaml": "^4.1.0"
"js-yaml": "^4.1.0",
"xlsx": "^0.18.5"
},
"devDependencies": {
"jest": "^29.7.0"
Expand Down
Loading

0 comments on commit 41842cf

Please sign in to comment.