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
8 changes: 4 additions & 4 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -478,9 +478,9 @@
"storybook": "npm --prefix storybook run storybook",
"build:storybook": "npm --prefix storybook run build-storybook",
"storybook:check": "deno test --no-lock --config=scripts/test.deno.json --no-check --allow-read scripts/storybook/storybook-workbench.test.ts",
"lint": "DENO_NO_PACKAGE_JSON=1 deno lint src/ cli/ react/ && deno lint --config=scripts/test.deno.json scripts/test/ scripts/build/dnt-polyfill.ts scripts/build/dnt-polyfill.test.ts scripts/build/npm-package-metadata.test.ts && deno lint --config=scripts/codemods/deno.json scripts/codemods/",
"fmt": "deno fmt src/ cli/ react/ && deno fmt --config=scripts/test.deno.json scripts/test/ scripts/build/dnt-polyfill.ts scripts/build/dnt-polyfill.test.ts && deno fmt --config=scripts/codemods/deno.json scripts/codemods/",
"fmt:check": "deno fmt --check src/ cli/ react/ && deno fmt --check --config=scripts/test.deno.json scripts/test/ scripts/build/dnt-polyfill.ts scripts/build/dnt-polyfill.test.ts && deno fmt --check --config=scripts/codemods/deno.json scripts/codemods/",
"lint": "DENO_NO_PACKAGE_JSON=1 deno lint src/ cli/ react/ && deno lint --config=scripts/test.deno.json scripts/test/ scripts/build/dnt-polyfill.ts scripts/build/dnt-polyfill.test.ts scripts/build/npm-package-metadata.test.ts scripts/build/prepare-framework-sources.test.ts && deno lint --config=scripts/codemods/deno.json scripts/codemods/",
"fmt": "deno fmt src/ cli/ react/ && deno fmt --config=scripts/test.deno.json scripts/test/ scripts/build/dnt-polyfill.ts scripts/build/dnt-polyfill.test.ts scripts/build/prepare-framework-sources.test.ts && deno fmt --config=scripts/codemods/deno.json scripts/codemods/",
"fmt:check": "deno fmt --check src/ cli/ react/ && deno fmt --check --config=scripts/test.deno.json scripts/test/ scripts/build/dnt-polyfill.ts scripts/build/dnt-polyfill.test.ts scripts/build/prepare-framework-sources.test.ts && deno fmt --check --config=scripts/codemods/deno.json scripts/codemods/",
"typecheck": "deno task generate:manifests:check && deno check src/index.ts cli/main.ts src/server/index.ts src/routing/api/index.ts src/rendering/index.ts src/platform/index.ts src/platform/adapters/index.ts src/build/index.ts src/build/production-build/index.ts src/transforms/index.ts src/config/index.ts src/utils/index.ts src/data/index.ts src/security/index.ts src/middleware/index.ts src/server/handlers/dev/index.ts src/server/handlers/request/api/index.ts src/rendering/cache/index.ts src/rendering/cache/stores/index.ts src/rendering/rsc/actions/index.ts src/html/index.ts src/html/hydration-script-builder/runtime/main.ts src/modules/index.ts src/proxy/main.ts src/react/components/ui/index.ts src/chat/index.ts src/markdown/index.ts src/mdx/index.ts src/fs/index.ts src/oauth/index.ts src/agent/index.ts src/agent/service/route-export.check.ts src/eval/index.ts src/tool/index.ts src/workflow/index.ts src/prompt/index.ts src/resource/index.ts src/runs/index.ts src/mcp/index.ts src/provider/index.ts",
"verify": "deno task generate:manifests:check && deno task fmt:check && deno task lint && deno task lint:style && deno task lint:chat-composability && deno task lint:chat-ratchets && deno task lint:esm-sh-codemod && deno task lint:cli-boundary && deno task lint:wildcard-exports && deno task lint:barrel-jsdoc && deno task lint:ban-test-only && deno task lint:sanitizer-baseline && deno task lint:skipped-tests && deno task lint:ban-zod && deno task lint:core-deps && deno task lint:dependency-boundaries && deno task lint:module-boundaries && deno task lint:extension-contracts && deno task lint:extension-capabilities && deno task docs:validate && deno task typecheck && deno task typecheck:consumer && deno task test && deno task test:e2e:binary",
"verify:quick": "deno task generate:manifests:check && deno task fmt:check && deno task lint && deno task lint:style && deno task lint:chat-composability && deno task lint:chat-ratchets && deno task lint:esm-sh-codemod && deno task lint:cli-boundary && deno task lint:wildcard-exports && deno task lint:barrel-jsdoc && deno task lint:ban-test-only && deno task lint:sanitizer-baseline && deno task lint:skipped-tests && deno task lint:ban-zod && deno task lint:core-deps && deno task lint:dependency-boundaries && deno task lint:module-boundaries && deno task lint:extension-contracts && deno task lint:extension-capabilities && deno task docs:validate && deno task typecheck",
Expand Down Expand Up @@ -519,7 +519,7 @@
"lint:ban-test-only": "deno run --allow-read scripts/lint/ban-test-only.ts",
"lint:sanitizer-baseline": "deno run --allow-read scripts/lint/check-sanitizer-baseline.ts",
"lint:skipped-tests": "deno run --allow-read scripts/lint/check-skipped-tests-baseline.ts",
"test:scripts": "deno test --config=scripts/test.deno.json --no-check --allow-read --allow-write --allow-run scripts/ci/publish-npm-packages.test.ts scripts/build/dnt-polyfill.test.ts scripts/build/generate-sbom.test.ts scripts/build/npm-dependency-sources.test.ts scripts/build/npm-extension-package-metadata.test.ts scripts/build/npm-package-metadata.test.ts scripts/build/npm-react-shims.test.ts scripts/docs/docs-coverage.test.ts scripts/docs/generate-api-reference.test.ts scripts/docs/guide-validation.test.ts scripts/lint/audit-core-deps.test.ts scripts/lint/audit-dependency-boundaries.test.ts scripts/lint/audit-extension-capabilities.test.ts scripts/lint/audit-extension-contracts.test.ts scripts/lint/audit-deps.test.ts scripts/lint/check-module-boundaries.test.ts scripts/lint/ban-test-only.test.ts scripts/lint/check-sanitizer-baseline.test.ts scripts/lint/check-skipped-tests-baseline.test.ts scripts/lint/check-test-typecheck-baseline.test.ts scripts/lint/check-coverage.test.ts scripts/security/audit-npm.test.ts scripts/security/submit-dependency-snapshot.test.ts && deno task test:tool-search-live",
"test:scripts": "deno test --config=scripts/test.deno.json --no-check --allow-read --allow-write --allow-run scripts/ci/publish-npm-packages.test.ts scripts/build/dnt-polyfill.test.ts scripts/build/generate-sbom.test.ts scripts/build/npm-dependency-sources.test.ts scripts/build/npm-extension-package-metadata.test.ts scripts/build/npm-package-metadata.test.ts scripts/build/npm-react-shims.test.ts scripts/build/prepare-framework-sources.test.ts scripts/docs/docs-coverage.test.ts scripts/docs/generate-api-reference.test.ts scripts/docs/guide-validation.test.ts scripts/lint/audit-core-deps.test.ts scripts/lint/audit-dependency-boundaries.test.ts scripts/lint/audit-extension-capabilities.test.ts scripts/lint/audit-extension-contracts.test.ts scripts/lint/audit-deps.test.ts scripts/lint/check-module-boundaries.test.ts scripts/lint/ban-test-only.test.ts scripts/lint/check-sanitizer-baseline.test.ts scripts/lint/check-skipped-tests-baseline.test.ts scripts/lint/check-test-typecheck-baseline.test.ts scripts/lint/check-coverage.test.ts scripts/security/audit-npm.test.ts scripts/security/submit-dependency-snapshot.test.ts && deno task test:tool-search-live",
"test:sentry-runtime-packages": "deno test --config=scripts/test.deno.json --no-check --no-lock --allow-read --allow-write --allow-run --allow-env=DENO_DIR,HOME,XDG_CACHE_HOME,LOCALAPPDATA,USERPROFILE scripts/build/sentry-runtime-packages.test.ts",
"test:tool-search-live": "VF_DISABLE_LRU_INTERVAL=1 deno test --no-check -A tests/agent/verify-tool-search-live.test.ts",
"test:cross-runtime": "deno run --allow-all src/platform/compat/cross-runtime.test.ts",
Expand Down
61 changes: 61 additions & 0 deletions scripts/build/prepare-framework-sources.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { assertEquals } from "#std/assert";
import { join } from "#std/path.ts";
import { describe, it } from "#std/testing/bdd";
import { prepareFrameworkSources } from "./prepare-framework-sources.ts";

describe("prepareFrameworkSources", () => {
it("excludes tests and test helpers from binary framework sources", async () => {
const temporaryRoot = await Deno.makeTempDir();
const sourceRoot = join(temporaryRoot, "src");
const outputRoot = join(temporaryRoot, "dist");

try {
await Deno.mkdir(sourceRoot, { recursive: true });
await Promise.all([
Deno.writeTextFile(
join(sourceRoot, "runtime.ts"),
"export const runtime = true;\n",
),
Deno.writeTextFile(
join(sourceRoot, "runtime.test.ts"),
"throw new Error('test');\n",
),
Deno.writeTextFile(
join(sourceRoot, "react-root.test-helpers.ts"),
"throw new Error('test helper');\n",
),
]);

const result = await prepareFrameworkSources({
srcRoot: sourceRoot,
outputDir: outputRoot,
});

assertEquals(result.fileCount, 1);
assertEquals(
await Deno.readTextFile(join(outputRoot, "runtime.ts.src")),
"export const runtime = true;\n",
);
assertEquals(
await exists(join(outputRoot, "runtime.test.ts.src")),
false,
);
assertEquals(
await exists(join(outputRoot, "react-root.test-helpers.ts.src")),
false,
);
} finally {
await Deno.remove(temporaryRoot, { recursive: true });
}
});
});

async function exists(path: string): Promise<boolean> {
try {
await Deno.stat(path);
return true;
} catch (error) {
if (error instanceof Deno.errors.NotFound) return false;
throw error;
}
}
18 changes: 12 additions & 6 deletions scripts/build/prepare-framework-sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* This is run automatically before `deno compile` in production builds.
*/

import { walk } from "@std/fs";
import { walk } from "#std/fs";
import { dirname, fromFileUrl, join, relative } from "#std/path.ts";

const FRAMEWORK_ROOT = fromFileUrl(new URL("../..", import.meta.url));
Expand Down Expand Up @@ -54,10 +54,12 @@ export async function prepareFrameworkSources(
let totalBytes = 0;
const encoder = new TextEncoder();

for await (const entry of walk(srcRoot, {
exts: SOURCE_EXTENSIONS.map((extension) => extension.slice(1)),
includeDirs: false,
})) {
for await (
const entry of walk(srcRoot, {
exts: SOURCE_EXTENSIONS.map((extension) => extension.slice(1)),
includeDirs: false,
})
) {
const relativePath = relative(srcRoot, entry.path);
const normalizedPath = relativePath.replaceAll("\\", "/");
if (
Expand Down Expand Up @@ -90,7 +92,11 @@ async function main(): Promise<void> {

const { fileCount, totalBytes } = await prepareFrameworkSources();

console.log(`[prepare-framework-sources] Complete: ${fileCount} files, ${(totalBytes / 1024).toFixed(1)} KB`);
console.log(
`[prepare-framework-sources] Complete: ${fileCount} files, ${
(totalBytes / 1024).toFixed(1)
} KB`,
);
}

if (import.meta.main) await main();
5 changes: 3 additions & 2 deletions src/react/components/chat/agent-picker.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createRoot } from "react-dom/client";
import { flushSync } from "react-dom";
import { renderToString } from "react-dom/server";
import { JSDOM } from "npm:jsdom@28.0.0";
import { unmountReactRoot } from "#veryfront/react/react-root.test-helpers.ts";
import { assert, assertEquals, assertStringIncludes } from "#veryfront/testing/assert";
import { describe, it } from "#veryfront/testing/bdd";
import type { AgentOption } from "./agent-picker.tsx";
Expand Down Expand Up @@ -111,7 +112,7 @@ describe("AgentPicker — preset (back-compat)", () => {
assertEquals(loading.getAttribute("aria-selected"), "false");
assertEquals(loading.querySelectorAll('[aria-hidden="true"]').length, 3);

flushSync(() => root.unmount());
await unmountReactRoot(root);
await settle();
} finally {
dom.restore();
Expand Down Expand Up @@ -228,7 +229,7 @@ describe("AgentPicker — composability contract", () => {
assertEquals(created, 1);
assertEquals(managed, 0);

flushSync(() => root.unmount());
await unmountReactRoot(root);
await settle();
} finally {
dom.restore();
Expand Down
9 changes: 5 additions & 4 deletions src/react/components/chat/chat-agent-picker.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createRoot } from "react-dom/client";
import { flushSync } from "react-dom";
import { JSDOM } from "npm:jsdom@28.0.0";
import { unmountReactRoot } from "#veryfront/react/react-root.test-helpers.ts";
import { assert, assertEquals, assertStringIncludes } from "#veryfront/testing/assert.ts";
import { describe, it } from "#veryfront/testing/bdd.ts";
import { agentsToPickerOptions, ChatAgentPicker } from "./chat-agent-picker.tsx";
Expand Down Expand Up @@ -109,7 +110,7 @@ describe("react/components/chat/chat-agent-picker: connected", () => {
assert(trigger, "picker trigger renders after the agents resolve");
assertStringIncludes(trigger.textContent ?? "", "Select agent");

flushSync(() => root.unmount());
await unmountReactRoot(root);
await settle();
} finally {
restoreFetch();
Expand All @@ -134,7 +135,7 @@ describe("react/components/chat/chat-agent-picker: connected", () => {
assert(trigger, "picker trigger renders");
assertStringIncludes(trigger.textContent ?? "", "Sales Agent");

flushSync(() => root.unmount());
await unmountReactRoot(root);
await settle();
} finally {
restoreFetch();
Expand All @@ -155,7 +156,7 @@ describe("react/components/chat/chat-agent-picker: connected", () => {
assertEquals(rootElement.querySelector("button"), null);
assertEquals(rootElement.textContent, "");

flushSync(() => root.unmount());
await unmountReactRoot(root);
await settle();
} finally {
restoreFetch();
Expand All @@ -181,7 +182,7 @@ describe("react/components/chat/chat-agent-picker: connected", () => {
assertEquals(fetches, 0);
assertEquals(rootElement.querySelector("button"), null);

flushSync(() => root.unmount());
await unmountReactRoot(root);
await settle();
} finally {
globalThis.fetch = previousFetch;
Expand Down
5 changes: 3 additions & 2 deletions src/react/components/chat/chat/chat.controlled.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { flushSync } from "react-dom";
import { createRoot } from "react-dom/client";
import { renderToString } from "react-dom/server";
import { JSDOM } from "npm:jsdom@28.0.0";
import { unmountReactRoot } from "#veryfront/react/react-root.test-helpers.ts";
import { assert, assertEquals } from "#veryfront/testing/assert";
import { describe, it } from "#veryfront/testing/bdd";
import type { ChatMessage, UseChatResult } from "#veryfront/agent/react";
Expand Down Expand Up @@ -117,7 +118,7 @@ describe("Chat — controlled via chat={useChat()}", () => {
);
});

it("submits externally controlled attachments through the chat session", () => {
it("submits externally controlled attachments through the chat session", async () => {
const dom = new JSDOM(
'<!doctype html><html><body><div id="root"></div></body></html>',
{ url: "https://example.com/" },
Expand Down Expand Up @@ -171,7 +172,7 @@ describe("Chat — controlled via chat={useChat()}", () => {
}],
});
assertEquals(removed, ["file-1"]);
root.unmount();
await unmountReactRoot(root);
} finally {
restore();
}
Expand Down
28 changes: 18 additions & 10 deletions src/react/components/chat/chat/components/code-block.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { flushSync } from "react-dom";
import { createRoot, type Root } from "react-dom/client";
import { renderToString } from "react-dom/server";
import { JSDOM } from "npm:jsdom@28.0.0";
import { unmountReactRoot } from "#veryfront/react/react-root.test-helpers.ts";
import { assert, assertEquals, assertStringIncludes } from "#veryfront/testing/assert";
import { describe, it } from "#veryfront/testing/bdd";
import { RichCodeBlock } from "./code-block.tsx";
Expand Down Expand Up @@ -48,12 +49,20 @@ async function settle(): Promise<void> {
flushSync(() => {});
}

// Unmounting leaves a scheduler callback queued; drain it so the leak
// sanitizer does not attribute that timer to the test.
async function unmount(root: Root | undefined): Promise<void> {
if (!root) return;
flushSync(() => root.unmount());
await new Promise((resolve) => setTimeout(resolve, 0));
/**
* Poll for a state transition instead of assuming `settle()` covered it.
*
* A copy result travels through a promise chain and then a React commit, and a
* single macrotask does not always cover both under load.
*/
async function waitFor(condition: () => boolean, timeoutMs = 3000): Promise<void> {
const startedAt = Date.now();
while (!condition()) {
if (Date.now() - startedAt > timeoutMs) {
throw new Error("Timed out waiting for code block state");
}
await settle();
}
}

describe("RichCodeBlock — inline mode", () => {
Expand Down Expand Up @@ -133,7 +142,7 @@ describe("RichCodeBlock — block mode", () => {
);
assertEquals(document.querySelectorAll("textarea").length, 0);
} finally {
await unmount(root);
await unmountReactRoot(root);
dom.restore();
}
});
Expand Down Expand Up @@ -170,16 +179,15 @@ describe("RichCodeBlock — block mode", () => {
assertEquals(pending.length, 2);

pending[1]?.();
await settle();
assertEquals(secondButton.textContent?.trim(), "Copied");
await waitFor(() => secondButton.textContent?.trim() === "Copied");

pending[0]?.();
await settle();
assertEquals(secondButton.textContent?.trim(), "Copied");
assertStringIncludes(rootElement.textContent ?? "", "new code");
assert(!(rootElement.textContent ?? "").includes("old code"));
} finally {
await unmount(root);
await unmountReactRoot(root);
dom.restore();
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { flushSync } from "react-dom";
import { createRoot } from "react-dom/client";
import { JSDOM } from "npm:jsdom@28.0.0";
import { unmountReactRoot } from "#veryfront/react/react-root.test-helpers.ts";
import { assert, assertEquals } from "#veryfront/testing/assert.ts";
import { describe, it } from "#veryfront/testing/bdd.ts";
import { waitFor } from "#veryfront/testing/deno-compat.ts";
Expand Down Expand Up @@ -84,7 +85,7 @@ describe("InlineCitation", () => {
assert(card, "Expected citation card to render after hover");
assertEquals(card.textContent, "Custom citation card");

root.unmount();
await unmountReactRoot(root);
} finally {
restore();
}
Expand Down
Loading