-
-
Notifications
You must be signed in to change notification settings - Fork 1
feat: migrate to TypeScript #110
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,8 +2,20 @@ | |
| "name": "doc-detective-resolver", | ||
| "version": "3.6.2", | ||
| "description": "Detect and resolve docs into Doc Detective tests.", | ||
| "main": "src/index.js", | ||
| "main": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "require": "./dist/index.js", | ||
| "import": "./dist/index.mjs", | ||
| "types": "./dist/index.d.ts" | ||
| } | ||
| }, | ||
| "scripts": { | ||
| "compile": "tsc && node scripts/createEsmWrapper.js", | ||
| "build": "npm run compile", | ||
| "prebuild": "rm -rf dist", | ||
| "postbuild": "npm run test", | ||
|
Comment on lines
+15
to
+18
|
||
| "test": "mocha src/*.test.js --ignore src/*.integration.test.js", | ||
| "test:integration": "mocha src/*.integration.test.js --timeout 600000", | ||
| "test:all": "mocha src/*.test.js --timeout 600000", | ||
|
Comment on lines
+18
to
21
|
||
|
|
@@ -37,13 +49,16 @@ | |
| "posthog-node": "^5.18.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/adm-zip": "^0.5.7", | ||
| "@types/node": "^22.10.5", | ||
| "body-parser": "^2.2.1", | ||
| "chai": "^6.2.2", | ||
| "express": "^5.2.1", | ||
| "mocha": "^11.7.5", | ||
| "proxyquire": "^2.1.3", | ||
| "semver": "^7.7.3", | ||
| "sinon": "^21.0.1", | ||
| "typescript": "^5.7.3", | ||
| "yaml": "^2.8.2" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,21 @@ | ||||||||||
| const fs = require("fs").promises; | ||||||||||
| const path = require("path"); | ||||||||||
|
|
||||||||||
| async function createEsmWrapper() { | ||||||||||
| const distDir = path.join(__dirname, "..", "dist"); | ||||||||||
| await fs.mkdir(distDir, { recursive: true }); | ||||||||||
|
|
||||||||||
| const esmContent = `// ESM wrapper for CommonJS output | ||||||||||
| import cjsModule from './index.js'; | ||||||||||
| export const { detectTests, resolveTests, detectAndResolveTests } = cjsModule; | ||||||||||
|
||||||||||
| export const { detectTests, resolveTests, detectAndResolveTests } = cjsModule; | |
| // Re-export all named exports from the CommonJS bundle | |
| export * from './index.js'; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,24 +1,40 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const crypto = require("crypto"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import crypto from "crypto"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { ArazzoDescription, ArazzoWorkflowStep, DetectedTest, Step, OpenApiDefinition } from "./types"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Doc Detective test specification created from Arazzo workflow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface ArazzoTestSpec extends DetectedTest { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| steps: Step[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| openApi: OpenApiDefinition[]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Translates an Arazzo description into a Doc Detective test specification | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param {Object} arazzoDescription - The Arazzo description object | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @returns {Object} - The Doc Detective test specification object | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param arazzoDescription - The Arazzo description object | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param workflowId - The ID of the workflow to translate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @param _inputs - Optional inputs for the workflow (currently unused) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @returns The Doc Detective test specification object, or undefined if workflow not found | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function workflowToTest(arazzoDescription, workflowId, inputs) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export function workflowToTest( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| arazzoDescription: ArazzoDescription, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflowId: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| _inputs?: unknown | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): ArazzoTestSpec | undefined { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Initialize the Doc Detective test specification | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const test = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const test: ArazzoTestSpec = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: arazzoDescription.info.title || `${crypto.randomUUID()}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| arazzoDescription.info.description || arazzoDescription.info.summary, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: arazzoDescription.info.description || arazzoDescription.info.summary, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| steps: [], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| openApi: [], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| arazzoDescription.sourceDescriptions.forEach((source) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Translate OpenAPI definitions to Doc Detective format | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (source.type === "openapi") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const openApiDefinition = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const openApiDefinition: OpenApiDefinition = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: source.name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| descriptionPath: source.url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -28,17 +44,17 @@ function workflowToTest(arazzoDescription, workflowId, inputs) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Find workflow by ID | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const workflow = arazzoDescription.workflows.find( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (workflow) => workflow.workflowId === workflowId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (w) => w.workflowId === workflowId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!workflow) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn(`Workflow with ID ${workflowId} not found.`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Translate each step in the workflow to a Doc Detective step | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflow.steps.forEach((workflowStep) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const docDetectiveStep = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflow.steps.forEach((workflowStep: ArazzoWorkflowStep) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const docDetectiveStep: Step = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| action: "httpRequest", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -48,13 +64,13 @@ function workflowToTest(arazzoDescription, workflowId, inputs) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (workflowStep.operationPath) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Handle operation path references (not yet supported in Doc Detective) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `Operation path references arne't yet supported in Doc Detective: ${workflowStep.operationPath}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `Operation path references aren't yet supported in Doc Detective: ${workflowStep.operationPath}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (workflowStep.workflowId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Handle workflow references (not yet supported in Doc Detective) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `Workflow references arne't yet supported in Doc Detective: ${workflowStep.workflowId}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `Workflow references aren't yet supported in Doc Detective: ${workflowStep.workflowId}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -65,14 +81,15 @@ function workflowToTest(arazzoDescription, workflowId, inputs) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Add parameters | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (workflowStep.parameters) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docDetectiveStep.requestParams = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docDetectiveStep.requestParams = {} as Record<string, unknown>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflowStep.parameters.forEach((param) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (param.in === "query") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docDetectiveStep.requestParams[param.name] = param.value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (docDetectiveStep.requestParams as Record<string, unknown>)[param.name] = param.value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (param.in === "header") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!docDetectiveStep.requestHeaders) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docDetectiveStep.requestHeaders = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docDetectiveStep.requestHeaders[param.name] = param.value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!docDetectiveStep.requestHeaders) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docDetectiveStep.requestHeaders = {} as Record<string, unknown>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (docDetectiveStep.requestHeaders as Record<string, unknown>)[param.name] = param.value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Note: path parameters would require modifying the URL, which is not handled in this simple translation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -85,15 +102,15 @@ function workflowToTest(arazzoDescription, workflowId, inputs) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Translate success criteria to response validation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (workflowStep.successCriteria) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docDetectiveStep.responseData = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docDetectiveStep.responseData = {} as Record<string, unknown>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflowStep.successCriteria.forEach((criterion) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (criterion.condition.startsWith("$statusCode")) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docDetectiveStep.statusCodes = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parseInt(criterion.condition.split("==")[1].trim()), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (criterion.context === "$response.body") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This is a simplification; actual JSONPath translation would be more complex | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| docDetectiveStep.responseData[criterion.condition] = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (docDetectiveStep.responseData as Record<string, unknown>)[criterion.condition] = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
104
to
116
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fragile status code parsing could fail on malformed conditions. The parsing logic Suggested defensive fix if (criterion.condition.startsWith("$statusCode")) {
- docDetectiveStep.statusCodes = [
- parseInt(criterion.condition.split("==")[1].trim()),
- ];
+ const parts = criterion.condition.split("==");
+ if (parts.length >= 2) {
+ const statusCode = parseInt(parts[1].trim(), 10);
+ if (!isNaN(statusCode)) {
+ docDetectiveStep.statusCodes = [statusCode];
+ }
+ }
} else if (criterion.context === "$response.body") {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,7 +1,7 @@ | ||||||||||||||||||||||||||
| const assert = require("assert"); | ||||||||||||||||||||||||||
| const sinon = require("sinon"); | ||||||||||||||||||||||||||
| const proxyquire = require("proxyquire"); | ||||||||||||||||||||||||||
| const { setConfig } = require("./config"); | ||||||||||||||||||||||||||
| const { setConfig } = require("../dist/config"); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| before(async function () { | ||||||||||||||||||||||||||
| const { expect } = await import("chai"); | ||||||||||||||||||||||||||
|
|
@@ -24,10 +24,10 @@ describe("envMerge", function () { | |||||||||||||||||||||||||
| replaceEnvsStub = sinon.stub().returnsArg(0); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Setup proxyquire | ||||||||||||||||||||||||||
| setConfig = proxyquire("./config", { | ||||||||||||||||||||||||||
| setConfig = proxyquire("../dist/config", { | ||||||||||||||||||||||||||
| "doc-detective-common": { validate: validStub }, | ||||||||||||||||||||||||||
| "./utils": { log: logStub, loadEnvs: loadEnvsStub, replaceEnvs: replaceEnvsStub }, | ||||||||||||||||||||||||||
| "./openapi": { loadDescription: sinon.stub().resolves({}) } | ||||||||||||||||||||||||||
| "../dist/utils": { log: logStub, loadEnvs: loadEnvsStub, replaceEnvs: replaceEnvsStub }, | ||||||||||||||||||||||||||
| "../dist/openapi": { loadDescription: sinon.stub().resolves({}) } | ||||||||||||||||||||||||||
| }).setConfig; | ||||||||||||||||||||||||||
|
Comment on lines
+27
to
31
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incorrect proxyquire stub paths - same issue as index.test.js. The proxyquire paths should match how the compiled 🐛 Proposed fix setConfig = proxyquire("../dist/config", {
"doc-detective-common": { validate: validStub },
- "../dist/utils": { log: logStub, loadEnvs: loadEnvsStub, replaceEnvs: replaceEnvsStub },
- "../dist/openapi": { loadDescription: sinon.stub().resolves({}) }
+ "./utils": { log: logStub, loadEnvs: loadEnvsStub, replaceEnvs: replaceEnvsStub },
+ "./openapi": { loadDescription: sinon.stub().resolves({}) }
}).setConfig;📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
@@ -404,7 +404,7 @@ function deepObjectExpect(actual, expected) { | |||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| describe("resolveConcurrentRunners", function () { | ||||||||||||||||||||||||||
| const { resolveConcurrentRunners } = require("./config"); | ||||||||||||||||||||||||||
| const { resolveConcurrentRunners } = require("../dist/config"); | ||||||||||||||||||||||||||
| const os = require("os"); | ||||||||||||||||||||||||||
| let originalCpus; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The workflow runs
npm run buildand thennpm test, butnpm run buildcurrently triggerspostbuildwhich runs tests. This results in tests running twice on every CI job. Either remove thepostbuildtest hook or drop the explicitnpm teststep here.