Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-lib-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
run: pnpm --filter @common-grants/cli run checks

- name: Run tests
run: pnpm --filter @common-grants/cli test
run: pnpm --filter @common-grants/cli test:coverage

- name: Build library
run: pnpm --filter @common-grants/cli run build
Expand Down
2 changes: 1 addition & 1 deletion lib/changelog-emitter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"prettier": "^3.3.3",
"typescript": "^5.3.3",
"typescript-eslint": "^8.15.0",
"vitest": "^3.2.4"
"vitest": "catalog:"
},
"scripts": {
"build": "tsc",
Expand Down
8 changes: 4 additions & 4 deletions lib/cli/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @ts-check
const eslint = require("@eslint/js");
const tsEslint = require("typescript-eslint");
const jest = require("eslint-plugin-jest");
const vitest = require("eslint-plugin-vitest");
const prettier = require("eslint-plugin-prettier");

module.exports = [
Expand All @@ -11,18 +11,18 @@ module.exports = [
"node_modules/**/*",
"coverage/**/*",
"eslint.config.js",
"jest.config.ts",
"vitest.config.ts",
],
},
eslint.configs.recommended,
...tsEslint.configs.recommended,
{
files: ["**/*.test.ts", "**/*.spec.ts"],
plugins: {
jest,
vitest,
},
rules: {
...jest.configs.recommended.rules,
...vitest.configs.recommended.rules,
},
},
{
Expand Down
12 changes: 0 additions & 12 deletions lib/cli/jest.config.ts

This file was deleted.

32 changes: 16 additions & 16 deletions lib/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,21 @@
"opportunities"
],
"scripts": {
"build": "npm run typespec && tsc",
"build": "pnpm run typespec && tsc",
"typespec:clean": "rm -rf dist tsp-output && rm -rf lib/openapi/*",
"typespec:openapi": "tsp compile lib/main.tsp --emit @typespec/openapi3 && cp tsp-output/@typespec/openapi3/** lib/openapi",
"typespec": "npm run typespec:clean && npm run typespec:openapi",
"typespec": "pnpm run typespec:clean && pnpm run typespec:openapi",
"start": "node dist/index.js",
"dev": "ts-node src/index.ts",
"prepare": "tsc",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint . --ext .ts --fix",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"lint": "eslint . --fix",
"format": "prettier --write .",
"check:lint": "eslint . --ext .ts",
"check:lint": "eslint .",
"check:format": "prettier --check .",
"checks": "npm run check:lint && npm run check:format"
"checks": "pnpm run check:lint && pnpm run check:format"
},
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.1",
Expand All @@ -65,31 +66,30 @@
},
"devDependencies": {
"@common-grants/core": "workspace:*",
"@typespec/json-schema": "catalog:",
"@typespec/openapi3": "catalog:",
"@typespec/versioning": "catalog:",
"@eslint/js": "^9.38.0",
"@jest/globals": "^29.7.0",
"@types/express": "^4.17.2",
"@types/inquirer": "^9.0.9",
"@types/jest": "^29.5.14",
"@types/js-yaml": "^4.0.9",
"@types/json-schema-merge-allof": "^0.6.5",
"@types/node": "catalog:",
"@types/supertest": "^6.0.3",
"@types/swagger-ui-express": "^4.1.8",
"@typescript-eslint/eslint-plugin": "^8.46.2",
"@typescript-eslint/parser": "^8.46.2",
"@typespec/json-schema": "catalog:",
"@typespec/openapi3": "catalog:",
"@typespec/versioning": "catalog:",
"@vitest/coverage-v8": "catalog:",
"eslint": "^9.38.0",
"eslint-config-prettier": "^9.1.2",
"eslint-plugin-jest": "^29.0.1",
"eslint-plugin-prettier": "^5.5.4",
"jest": "^29.7.0",
"eslint-plugin-vitest": "catalog:",
"prettier": "^3.6.2",
"prettier-linter-helpers": "^1.0.1",
"supertest": "^7.1.4",
"ts-jest": "^29.4.5",
"ts-node": "^10.9.2",
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.2"
"typescript-eslint": "^8.46.2",
"vitest": "catalog:"
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { vi } from "vitest";
import { DefaultCheckService } from "../../../commands/check/check-service";
import * as path from "path";

// No mocks - we want to test the real behavior
describe("DefaultCheckService - Circular References", () => {
let service: DefaultCheckService;
const mockConsoleLog = jest.spyOn(console, "log").mockImplementation(() => {});
const mockConsoleWarn = jest.spyOn(console, "warn").mockImplementation(() => {});
const mockConsoleError = jest.spyOn(console, "error").mockImplementation(() => {});
const mockConsoleLog = vi.spyOn(console, "log").mockImplementation(() => {});
const mockConsoleWarn = vi.spyOn(console, "warn").mockImplementation(() => {});
const mockConsoleError = vi.spyOn(console, "error").mockImplementation(() => {});

beforeEach(() => {
service = new DefaultCheckService();
jest.clearAllMocks();
vi.clearAllMocks();
});

afterAll(() => {
Expand Down
48 changes: 25 additions & 23 deletions lib/cli/src/__tests__/commands/check/check-service-mock.test.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
import { vi } from "vitest";
import { Mock } from "vitest";
import { OpenAPIV3 } from "openapi-types";
import { DefaultCheckService } from "../../../commands/check/check-service";
import * as fs from "fs";
import * as yaml from "js-yaml";

// Mock dependencies
jest.mock("fs", () => ({
readFileSync: jest.fn(),
existsSync: jest.fn(),
readdirSync: jest.fn(),
vi.mock("fs", () => ({
readFileSync: vi.fn(),
existsSync: vi.fn(),
readdirSync: vi.fn(),
}));

jest.mock("js-yaml", () => ({
load: jest.fn(),
vi.mock("js-yaml", () => ({
load: vi.fn(),
}));

// Mock the availableVersions import
jest.mock("../../../commands/check/check-args", () => ({
vi.mock("../../../commands/check/check-args", () => ({
availableVersions: ["1.0.0", "2.0.0"],
}));

describe("DefaultCheckService", () => {
let service: DefaultCheckService;
const mockConsoleLog = jest.spyOn(console, "log").mockImplementation(() => {});
const mockConsoleLog = vi.spyOn(console, "log").mockImplementation(() => {});

beforeEach(() => {
service = new DefaultCheckService();
jest.clearAllMocks();
vi.clearAllMocks();
});

afterAll(() => {
Expand Down Expand Up @@ -102,7 +104,7 @@ describe("DefaultCheckService", () => {
const implDoc = { ...baseDoc, info: { title: "Impl", version: "1.0.0" } };

// Mock file system operations to simulate the new pattern
(fs.existsSync as jest.Mock).mockImplementation((filePath: string) => {
(fs.existsSync as Mock).mockImplementation((filePath: string) => {
// Mock the OpenAPI directory structure
if (filePath.includes("lib/openapi")) {
return true; // Directory exists
Expand All @@ -116,9 +118,9 @@ describe("DefaultCheckService", () => {
return false;
});

(fs.readdirSync as jest.Mock).mockReturnValue(["openapi.1.0.0.yaml", "openapi.2.0.0.yaml"]);
(fs.readdirSync as Mock).mockReturnValue(["openapi.1.0.0.yaml", "openapi.2.0.0.yaml"]);

(fs.readFileSync as jest.Mock).mockImplementation((filePath: string) => {
(fs.readFileSync as Mock).mockImplementation((filePath: string) => {
if (filePath === "spec.yaml") {
return "impl yaml content";
} else if (filePath.includes("openapi.1.0.0.yaml")) {
Expand All @@ -128,7 +130,7 @@ describe("DefaultCheckService", () => {
}
});

(yaml.load as jest.Mock).mockImplementation((content: string) => {
(yaml.load as Mock).mockImplementation((content: string) => {
if (content === "impl yaml content") {
return implDoc;
} else {
Expand All @@ -151,7 +153,7 @@ describe("DefaultCheckService", () => {
it("should throw error when OpenAPI directory not found", async () => {
// Arrange
// Mock file system operations - directory doesn't exist
(fs.existsSync as jest.Mock).mockReturnValue(false);
(fs.existsSync as Mock).mockReturnValue(false);

// Act & Assert
await expect(service.checkSpec("spec.yaml", {})).rejects.toThrow(
Expand All @@ -175,16 +177,16 @@ describe("DefaultCheckService", () => {
const implDoc = { ...baseDoc, info: { title: "Impl", version: "1.0.0" } };

// Mock file system operations
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readFileSync as jest.Mock).mockImplementation((filePath: string) => {
(fs.existsSync as Mock).mockReturnValue(true);
(fs.readFileSync as Mock).mockImplementation((filePath: string) => {
if (filePath === "spec.yaml") {
return "impl yaml content";
} else if (filePath === providedBasePath) {
return "base yaml content";
}
return "default content";
});
(yaml.load as jest.Mock).mockImplementation((content: string) => {
(yaml.load as Mock).mockImplementation((content: string) => {
if (content === "impl yaml content") {
return implDoc;
} else {
Expand Down Expand Up @@ -240,8 +242,8 @@ describe("DefaultCheckService", () => {
};

// Mock file system operations
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readFileSync as jest.Mock).mockImplementation((filePath: string) => {
(fs.existsSync as Mock).mockReturnValue(true);
(fs.readFileSync as Mock).mockImplementation((filePath: string) => {
if (filePath === "spec.yaml") {
return "impl yaml content";
} else if (filePath.includes("openapi.1.0.0.yaml")) {
Expand All @@ -250,7 +252,7 @@ describe("DefaultCheckService", () => {
return "default content";
}
});
(yaml.load as jest.Mock).mockImplementation((content: string) => {
(yaml.load as Mock).mockImplementation((content: string) => {
if (content === "impl yaml content") {
return implDoc;
} else {
Expand Down Expand Up @@ -296,8 +298,8 @@ describe("DefaultCheckService", () => {
const implDoc = { ...baseDoc, info: { title: "Impl", version: "1.0.0" } };

// Arrange - Mock file system operations
(fs.existsSync as jest.Mock).mockReturnValue(true);
(fs.readFileSync as jest.Mock).mockImplementation((filePath: string) => {
(fs.existsSync as Mock).mockReturnValue(true);
(fs.readFileSync as Mock).mockImplementation((filePath: string) => {
if (filePath === "spec.yaml") {
return "impl yaml content";
} else if (filePath.includes("openapi.1.0.0.yaml")) {
Expand All @@ -306,7 +308,7 @@ describe("DefaultCheckService", () => {
return "default content";
}
});
(yaml.load as jest.Mock).mockImplementation((content: string) => {
(yaml.load as Mock).mockImplementation((content: string) => {
if (content === "impl yaml content") {
return implDoc;
} else {
Expand Down
16 changes: 8 additions & 8 deletions lib/cli/src/__tests__/commands/check/check.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { describe, it, expect, beforeEach, afterAll, jest } from "@jest/globals";
import { describe, it, expect, beforeEach, afterAll, vi } from "vitest";
import { Command } from "commander";
import { checkCommand } from "../../../commands/check/check";

// Create mock functions outside
const mockCheckApi = jest.fn();
const mockCheckSpec = jest.fn();
const mockCheckApi = vi.fn();
const mockCheckSpec = vi.fn();

// Mock the service with consistent implementation
jest.mock("../../../commands/check/check-service.ts", () => ({
DefaultCheckService: jest.fn(() => ({
vi.mock("../../../commands/check/check-service.ts", () => ({
DefaultCheckService: vi.fn(() => ({
checkApi: mockCheckApi,
checkSpec: mockCheckSpec,
})),
Expand All @@ -19,10 +19,10 @@ describe("checkCommand", () => {
let checkCmd: Command;

// Mock process.exit and console.error
const mockExit = jest.spyOn(process, "exit").mockImplementation(() => {
const mockExit = vi.spyOn(process, "exit").mockImplementation(() => {
throw new Error("process.exit mock");
});
const mockConsoleError = jest.spyOn(console, "error").mockImplementation(() => {});
const mockConsoleError = vi.spyOn(console, "error").mockImplementation(() => {});

beforeEach(() => {
// Create fresh Command instances for each test
Expand Down Expand Up @@ -129,7 +129,7 @@ describe("checkCommand", () => {

it("should handle spec validation with both base and protocolVersion", async () => {
// Mock console.warn to capture the warning message
const mockWarn = jest.spyOn(console, "warn").mockImplementation(() => {});
const mockWarn = vi.spyOn(console, "warn").mockImplementation(() => {});

await checkCmd.parseAsync([
"node",
Expand Down
15 changes: 8 additions & 7 deletions lib/cli/src/__tests__/commands/compile/compile-service.test.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import { beforeEach, describe, it, expect, jest } from "@jest/globals";
import { beforeEach, describe, it, expect, vi } from "vitest";
import { Mock } from "vitest";
import { DefaultCompileService } from "../../../commands/compile/compile-service";
import { spawn } from "child_process";
import { EventEmitter } from "events";
import { ChildProcess } from "child_process";
import { tspBinPath } from "../../../utils/typespec";

jest.mock("child_process", () => ({
spawn: jest.fn(),
vi.mock("child_process", () => ({
spawn: vi.fn(),
}));

describe("DefaultCompileService", () => {
const service = new DefaultCompileService();
let mockSpawn: jest.Mock;
let mockSpawn: Mock;
let mockChildProcess: Partial<ChildProcess> & EventEmitter;
const mockConsole = jest.spyOn(console, "error").mockImplementation(() => {});
const mockConsole = vi.spyOn(console, "error").mockImplementation(() => {});

beforeEach(() => {
jest.clearAllMocks();
mockSpawn = spawn as jest.Mock;
vi.clearAllMocks();
mockSpawn = spawn as Mock;
mockChildProcess = new EventEmitter() as Partial<ChildProcess> & EventEmitter;
mockSpawn.mockReturnValue(mockChildProcess);
});
Expand Down
12 changes: 6 additions & 6 deletions lib/cli/src/__tests__/commands/compile/compile.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { describe, it, expect, beforeAll, beforeEach, afterAll, jest } from "@jest/globals";
import { describe, it, expect, beforeAll, beforeEach, afterAll, vi } from "vitest";
import { Command } from "commander";
import { compileCommand } from "../../../commands/compile/compile";

const mockCompile = jest.fn();
const mockCompile = vi.fn();

jest.mock("../../../commands/compile/compile-service", () => ({
DefaultCompileService: jest.fn(() => ({
vi.mock("../../../commands/compile/compile-service", () => ({
DefaultCompileService: vi.fn(() => ({
compile: mockCompile,
})),
}));
Expand All @@ -15,10 +15,10 @@ describe("compileCommand", () => {
let compileCmd: Command;

// Mock process.exit and console.error
const mockExit = jest.spyOn(process, "exit").mockImplementation(() => {
const mockExit = vi.spyOn(process, "exit").mockImplementation(() => {
throw new Error("process.exit mock");
});
const mockConsoleError = jest.spyOn(console, "error").mockImplementation(() => {});
const mockConsoleError = vi.spyOn(console, "error").mockImplementation(() => {});

beforeAll(() => {
program = new Command();
Expand Down
Loading