From dfda60ad29db4a18ebc0d257fc4a9584033dadfb Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 28 Apr 2026 19:14:51 +0000 Subject: [PATCH] test: add TypeScript type-checking for tests Refs #198 --- .github/workflows/ci-validate.yml | 3 +++ package.json | 1 + scripts/release/calver-plugin.d.cts | 27 +++++++++++++++++++ scripts/release/calver.d.cts | 13 +++++++++ .../unit/services/initiative-service.test.ts | 9 ++++--- tests/unit/services/issue-service.test.ts | 3 ++- tsconfig.json | 5 ++-- tsconfig.test.json | 16 +++++++++++ 8 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 scripts/release/calver-plugin.d.cts create mode 100644 scripts/release/calver.d.cts create mode 100644 tsconfig.test.json diff --git a/.github/workflows/ci-validate.yml b/.github/workflows/ci-validate.yml index b5562e04..6fa37bf3 100644 --- a/.github/workflows/ci-validate.yml +++ b/.github/workflows/ci-validate.yml @@ -77,6 +77,9 @@ jobs: - name: TypeScript type check run: npx tsc --noEmit + - name: Test TypeScript type check + run: npm run test:typecheck + commitlint: name: Validate Commit Messages runs-on: ubuntu-latest diff --git a/package.json b/package.json index d98b26f6..b3992da1 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "clean": "rm -rf dist/", "start": "tsx src/main.ts", "test": "vitest run", + "test:typecheck": "tsc -p tsconfig.test.json --noEmit", "test:integration": "vitest run --config vitest.integration.config.ts", "test:watch": "vitest", "test:ui": "vitest --ui", diff --git a/scripts/release/calver-plugin.d.cts b/scripts/release/calver-plugin.d.cts new file mode 100644 index 00000000..9136d646 --- /dev/null +++ b/scripts/release/calver-plugin.d.cts @@ -0,0 +1,27 @@ +export interface MapCalverReleaseTypeInput { + branchName: string; + releaseType: string | null; + lastVersion: string; + nowIso?: string; +} + +export interface AnalyzeCommitsContext { + branch?: { name?: string }; + lastRelease?: { version?: string }; + logger: { log(message: string): void }; + nextRelease?: { version?: string }; +} + +export declare function mapCalverReleaseType( + input: MapCalverReleaseTypeInput, +): string | null; + +export declare function analyzeCommits( + pluginConfig: unknown, + context: AnalyzeCommitsContext, +): Promise; + +export declare function verifyRelease( + pluginConfig: unknown, + context: AnalyzeCommitsContext, +): Promise; diff --git a/scripts/release/calver.d.cts b/scripts/release/calver.d.cts new file mode 100644 index 00000000..ac415029 --- /dev/null +++ b/scripts/release/calver.d.cts @@ -0,0 +1,13 @@ +export interface ComputeCalverVersionInput { + lastVersion: string; + branchName: string; + nowIso?: string; +} + +export declare function computeCalverVersion( + input: ComputeCalverVersionInput, +): string; + +export declare function isMonthRollover( + input: ComputeCalverVersionInput, +): boolean; diff --git a/tests/unit/services/initiative-service.test.ts b/tests/unit/services/initiative-service.test.ts index d30bafd3..f8433e81 100644 --- a/tests/unit/services/initiative-service.test.ts +++ b/tests/unit/services/initiative-service.test.ts @@ -1,7 +1,10 @@ import { type DocumentNode, type FragmentDefinitionNode, Kind } from "graphql"; import { describe, expect, it, vi } from "vitest"; import type { GraphQLClient } from "../../../src/client/graphql-client.js"; -import { GetInitiativeDocument } from "../../../src/gql/graphql.js"; +import { + GetInitiativeDocument, + PaginationOrderBy, +} from "../../../src/gql/graphql.js"; import { archiveInitiative, createInitiative, @@ -72,7 +75,7 @@ describe("listInitiatives", () => { after: "cursor-1", includeArchived: true, filter: { name: { eqIgnoreCase: "Growth" } }, - orderBy: { createdAt: "Asc" }, + orderBy: PaginationOrderBy.CreatedAt, }); expect(client.request).toHaveBeenCalledWith(expect.anything(), { @@ -80,7 +83,7 @@ describe("listInitiatives", () => { after: "cursor-1", includeArchived: true, filter: { name: { eqIgnoreCase: "Growth" } }, - orderBy: { createdAt: "Asc" }, + orderBy: PaginationOrderBy.CreatedAt, sort: undefined, }); }); diff --git a/tests/unit/services/issue-service.test.ts b/tests/unit/services/issue-service.test.ts index 2906159a..b3df865b 100644 --- a/tests/unit/services/issue-service.test.ts +++ b/tests/unit/services/issue-service.test.ts @@ -368,7 +368,8 @@ describe("getIssueByIdentifierWithComments", () => { }); const result = await getIssueByIdentifierWithComments(client, "ENG", 42); - expect(result.comments.nodes[0].user.displayName).toBe("Ada"); + expect(result.comments.nodes).toHaveLength(1); + expect(result.comments.nodes[0]?.user?.displayName).toBe("Ada"); expect(client.request).toHaveBeenCalledWith( GetIssueByIdentifierWithCommentsDocument, { diff --git a/tsconfig.json b/tsconfig.json index a4033c54..26c3580d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,9 +25,8 @@ "exclude": [ "node_modules", "dist", - // Tests excluded from TypeScript compilation to prevent them from - // being compiled into dist/. Tests are type-checked and validated - // by Vitest at runtime, which provides sufficient type safety. + // Tests are excluded from the production build config because + // tsconfig.test.json handles test-only type-checking with no emit. "tests", "**/*.test.ts", "**/*.spec.ts", diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 00000000..7130d5ba --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,16 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": ".", + "noEmit": true, + "types": ["node", "vitest/globals"] + }, + "include": [ + "src/**/*", + "tests/**/*", + "vitest.base.config.ts", + "vitest.config.ts", + "vitest.integration.config.ts" + ], + "exclude": ["node_modules", "dist"] +}