chore: add typecheck script and fix pre-existing TypeScript errors

- Add `typecheck` script (`tsc --noEmit`) to package.json
- Remove conflicting `declare global` from test/setup.ts (superseded
  by @cloudflare/workers-types); use `globalThis as any` for test globals
  and declare minimal `require` locally to avoid pulling in @types/node
- Cast `createMockEnv()` and `deleteRes.json()` results in admin.test.ts
  to silence strict `unknown` / MockKV-vs-KVNamespace errors

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Julien Herr
2026-05-20 22:54:32 +02:00
parent 3ed9d2ee22
commit 29446a2aac
3 changed files with 32 additions and 38 deletions
+2 -1
View File
@@ -10,7 +10,8 @@
"deploy": "wrangler deploy --env production",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage"
"test:coverage": "vitest run --coverage",
"typecheck": "tsc --noEmit"
},
"author": "",
"license": "MIT",
+6 -6
View File
@@ -11,10 +11,10 @@ describe("Admin Routes", () => {
let loginAndGetCookie: () => Promise<string>;
beforeEach(() => {
mockEnv = createMockEnv();
mockEnv = createMockEnv() as unknown as Env;
testApp = new Hono();
testApp.route("/admin", app);
request = (path, init = {}) => testApp.request(path, init, mockEnv);
request = (path, init = {}) => Promise.resolve(testApp.request(path, init, mockEnv));
loginAndGetCookie = async () => {
const formData = new FormData();
formData.append("password", "test-password");
@@ -161,8 +161,8 @@ describe("Admin Routes", () => {
"json",
);
expect(feedConfig).toBeTruthy();
expect(feedConfig.title).toBe("Test Feed");
expect(feedConfig.description).toBe("Test Description");
expect((feedConfig as any).title).toBe("Test Feed");
expect((feedConfig as any).description).toBe("Test Description");
});
it("should reject feed creation with missing title", async () => {
@@ -297,7 +297,7 @@ describe("Admin Routes", () => {
);
expect(deleteRes.status).toBe(200);
const payload = await deleteRes.json();
const payload = (await deleteRes.json()) as any;
expect(payload.ok).toBe(true);
expect(payload.feedId).toBe(feedId);
});
@@ -419,7 +419,7 @@ describe("Admin Routes", () => {
);
expect(deleteRes.status).toBe(200);
const payload = await deleteRes.json();
const payload = (await deleteRes.json()) as any;
expect(payload.ok).toBe(true);
expect(payload.emailKey).toBe(emailKey);
+24 -31
View File
@@ -1,26 +1,10 @@
import { beforeAll, afterAll, afterEach } from "vitest";
import { setupServer } from "msw/node";
/**
* Mock implementation of Cloudflare Workers runtime environment
* Based on: https://developers.cloudflare.com/workers/testing/
*/
// Define Cloudflare Workers runtime globals
declare global {
// CF Worker specific globals
var caches: CacheStorage;
var crypto: Crypto;
var Response: typeof Response;
var Request: typeof Request;
var URLSearchParams: typeof URLSearchParams;
var URL: typeof URL;
var Headers: typeof Headers;
var FormData: typeof FormData;
var Blob: typeof Blob;
var atob: (data: string) => string;
var btoa: (data: string) => string;
}
// Minimal Node.js built-ins used only in this test setup file.
// Declared locally to avoid pulling in the full @types/node package,
// which would conflict with @cloudflare/workers-types globals.
declare function require(id: string): any;
/**
* Mock KV namespace implementation
@@ -122,36 +106,45 @@ beforeAll(() => {
// Setup MSW server
server.listen({ onUnhandledRequest: "error" });
// Type-safe access to Node's global object for setting Workers-like globals in tests
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const g = globalThis as any;
// Mock Cloudflare Workers runtime globals
global.caches = {
g.caches = {
default: new MockCache(),
open: async () => new MockCache(),
} as unknown as CacheStorage;
// Mock crypto for generating random values
if (!global.crypto) {
global.crypto = require("crypto").webcrypto;
if (!g.crypto) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
g.crypto = require("crypto").webcrypto;
}
// Ensure other required globals are available
if (!global.FormData) {
if (!g.FormData) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { FormData } = require("undici");
global.FormData = FormData;
g.FormData = FormData;
}
if (!global.Headers) {
if (!g.Headers) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { Headers } = require("undici");
global.Headers = Headers;
g.Headers = Headers;
}
if (!global.Request) {
if (!g.Request) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { Request } = require("undici");
global.Request = Request;
g.Request = Request;
}
if (!global.Response) {
if (!g.Response) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { Response } = require("undici");
global.Response = Response;
g.Response = Response;
}
});