diff --git a/packages/fresh/src/handlers_test.ts b/packages/fresh/src/handlers_test.ts new file mode 100644 index 00000000000..8668c2b0814 --- /dev/null +++ b/packages/fresh/src/handlers_test.ts @@ -0,0 +1,63 @@ +import { expect } from "@std/expect/expect"; +import { isHandlerByMethod, page } from "./handlers.ts"; + +Deno.test("handlers - page() with no args returns undefined data", () => { + const result = page(); + expect(result.data).toBeUndefined(); + expect(result.status).toBeUndefined(); + expect(result.headers).toBeUndefined(); +}); + +Deno.test("handlers - page() with data returns that data", () => { + const result = page("hello"); + expect(result.data).toBe("hello"); +}); + +Deno.test("handlers - page() with null data returns undefined", () => { + // `null ?? undefined` evaluates to `undefined` due to the ?? operator + const result = page(null); + expect(result.data).toBeUndefined(); +}); + +Deno.test("handlers - page() with options returns status and headers", () => { + const result = page({ msg: "hi" }, { + headers: { "X-Custom": "1" }, + status: 201, + }); + expect(result.data).toEqual({ msg: "hi" }); + expect(result.status).toBe(201); + expect(result.headers).toEqual({ "X-Custom": "1" }); +}); + +Deno.test("handlers - page() with partial options", () => { + const result = page({ msg: "hi" }, { status: 404 }); + expect(result.data).toEqual({ msg: "hi" }); + expect(result.status).toBe(404); + expect(result.headers).toBeUndefined(); +}); + +// -- isHandlerByMethod ------------------------------------------------- + +Deno.test("handlers - isHandlerByMethod with object returns true", () => { + const handler = { GET: () => new Response("ok") }; + expect(isHandlerByMethod(handler)).toBe(true); +}); + +Deno.test("handlers - isHandlerByMethod with empty object returns true", () => { + // An empty object is still technically an object — the type guard + // distinguishes between functions and method maps. + expect(isHandlerByMethod({})).toBe(true); +}); + +Deno.test("handlers - isHandlerByMethod with function returns false", () => { + const handler = () => new Response("ok"); + expect(isHandlerByMethod(handler)).toBe(false); +}); + +Deno.test("handlers - isHandlerByMethod with array returns false", () => { + expect(isHandlerByMethod([])).toBe(false); +}); + +Deno.test("handlers - isHandlerByMethod with null returns false", () => { + expect(isHandlerByMethod(null)).toBe(false); +}); diff --git a/packages/fresh/src/otel_test.ts b/packages/fresh/src/otel_test.ts new file mode 100644 index 00000000000..2ed0e6826bb --- /dev/null +++ b/packages/fresh/src/otel_test.ts @@ -0,0 +1,70 @@ +import { expect } from "@std/expect/expect"; +import type { Span } from "@opentelemetry/api"; +import { SpanStatusCode } from "@opentelemetry/api"; +import { recordSpanError } from "./otel.ts"; + +/** Creates a trackable Span mock without external mock libraries. */ +function mockSpan() { + const calls: Record = {}; + + return { + _calls: calls, + span: { + recordException(err: Error) { + (calls.recordException ??= []).push([err]); + }, + setStatus(status: { code: number; message: string }) { + (calls.setStatus ??= []).push([status]); + }, + } as Span, + }; +} + +Deno.test("otel - recordSpanError with Error calls recordException", () => { + const err = new Error("test error"); + const { span, _calls } = mockSpan(); + + recordSpanError(span, err); + + expect(_calls.recordException).toBeDefined(); + expect(_calls.recordException[0][0]).toBe(err); + expect(_calls.setStatus).toBeUndefined(); +}); + +Deno.test("otel - recordSpanError with string calls setStatus", () => { + const { span, _calls } = mockSpan(); + + recordSpanError(span, "something broke"); + + expect(_calls.recordException).toBeUndefined(); + expect(_calls.setStatus).toBeDefined(); + expect(_calls.setStatus[0][0]).toEqual({ + code: SpanStatusCode.ERROR, + message: "something broke", + }); +}); + +Deno.test("otel - recordSpanError with object calls setStatus", () => { + const customErr = { custom: "problem" }; + const { span, _calls } = mockSpan(); + + recordSpanError(span, customErr); + + expect(_calls.setStatus).toBeDefined(); + expect(_calls.setStatus[0][0]).toEqual({ + code: SpanStatusCode.ERROR, + message: String(customErr), + }); +}); + +Deno.test("otel - recordSpanError with number calls setStatus", () => { + const { span, _calls } = mockSpan(); + + recordSpanError(span, 404); + + expect(_calls.setStatus).toBeDefined(); + expect(_calls.setStatus[0][0]).toEqual({ + code: SpanStatusCode.ERROR, + message: "404", + }); +}); diff --git a/packages/plugin-vite/src/mod.ts b/packages/plugin-vite/src/mod.ts index c49c244526c..059119e69dd 100644 --- a/packages/plugin-vite/src/mod.ts +++ b/packages/plugin-vite/src/mod.ts @@ -1,3 +1,24 @@ +/** + * @module + * + * The official Vite plugin for the + * [Fresh](https://usefresh.dev) web framework for Deno. + * + * This plugin uses Vite's Environment API to provide dual + * client/server builds, HMR, island-based code splitting, + * and Deno-native module resolution for Fresh applications. + * + * @example vite.config.ts + * ```ts + * import { defineConfig } from "vite"; + * import { fresh } from "@fresh/plugin-vite"; + * + * export default defineConfig({ + * plugins: [fresh()], + * }); + * ``` + */ + import type { Plugin } from "vite"; import { type FreshViteConfig, @@ -27,7 +48,11 @@ import { isBuiltin } from "node:module"; import { load as stdLoadEnv } from "@std/dotenv"; import path from "node:path"; +// -- Types ---------------------------------------------------------- + +/** @category Types */ export type { FreshViteConfig }; +/** @category Types */ export type { ImportCheck, ImportCheckDiagnostic, @@ -41,6 +66,7 @@ export type { * * @param config Fresh config options * @returns Vite plugin with Fresh support + * @category Plugin * * @example Basic usage * ```ts vite.config.ts