Download __tests__/package-library.test.ts from SaylorTwift/openhands: direct link, hf CLI and curl.
- Browser
- Download file 5.48 kB
-
https://huggingface.co/SaylorTwift/openhands/resolve/main/__tests__/package-library.test.ts
- Command line
-
hf download hf://SaylorTwift/openhands/__tests__/package-library.test.ts
-
curl -L -o package-library.test.ts https://huggingface.co/SaylorTwift/openhands/resolve/main/__tests__/package-library.test.ts
5.48 kB
| // @vitest-environment node | |
| import { spawnSync } from "node:child_process"; | |
| import { readFileSync } from "node:fs"; | |
| import { resolve } from "node:path"; | |
| import { describe, expect, it } from "vitest"; | |
| const packageJson = JSON.parse( | |
| readFileSync(resolve(__dirname, "../package.json"), "utf8"), | |
| ) as { | |
| name: string; | |
| main: string; | |
| module: string; | |
| types: string; | |
| exports: Record<string, unknown>; | |
| scripts: Record<string, string>; | |
| dependencies?: Record<string, string>; | |
| devDependencies?: Record<string, string>; | |
| }; | |
| const EXACT_SEMVER_PATTERN = | |
| /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/; | |
| describe("package library metadata", () => { | |
| const ALLOWED_STACK_PIN_DEPS = new Set([ | |
| "@openhands/extensions", | |
| "@openhands/typescript-client", | |
| ]); | |
| it("publishes the agent-canvas package entrypoints", () => { | |
| expect(packageJson.name).toBe("@openhands/agent-canvas"); | |
| expect(packageJson.main).toBe("./dist/index.cjs"); | |
| expect(packageJson.module).toBe("./dist/index.js"); | |
| expect(packageJson.types).toBe("./dist/index.d.ts"); | |
| expect(packageJson.exports).toMatchObject({ | |
| ".": { | |
| types: "./dist/index.d.ts", | |
| import: "./dist/index.js", | |
| require: "./dist/index.cjs", | |
| }, | |
| "./conversation": { | |
| types: "./dist/components/conversation/index.d.ts", | |
| import: "./dist/components/conversation/index.js", | |
| require: "./dist/components/conversation/index.cjs", | |
| }, | |
| "./settings": { | |
| types: "./dist/components/settings/index.d.ts", | |
| import: "./dist/components/settings/index.js", | |
| require: "./dist/components/settings/index.cjs", | |
| }, | |
| "./terminal": { | |
| types: "./dist/components/terminal/index.d.ts", | |
| import: "./dist/components/terminal/index.js", | |
| require: "./dist/components/terminal/index.cjs", | |
| }, | |
| "./i18n": { | |
| types: "./dist/i18n/index.d.ts", | |
| import: "./dist/i18n/index.js", | |
| require: "./dist/i18n/index.cjs", | |
| }, | |
| }); | |
| }); | |
| // Git dependencies break `npm install -g` because npm clones the repo and | |
| // runs the prepare script without devDependencies. All packages should be | |
| // referenced from a registry. @openhands/extensions is allowed until it is | |
| // published to npm; @openhands/typescript-client is temporarily allowed while | |
| // this stacked PR waits for the subscription client branch to merge/release. | |
| // TODO(#917): remove @openhands/typescript-client exemption once | |
| // OpenHands/typescript-client#178 merges and publishes to npm. | |
| it("does not use git dependencies except approved stack pins", () => { | |
| const GIT_DEP_PATTERN = | |
| /^(git[+:]|github:|bitbucket:|gitlab:|[a-zA-Z0-9_-]+\/)/; | |
| const allDeps = { | |
| ...packageJson.dependencies, | |
| ...packageJson.devDependencies, | |
| }; | |
| const violations = Object.entries(allDeps) | |
| .filter( | |
| ([name, version]) => | |
| GIT_DEP_PATTERN.test(version) && !ALLOWED_STACK_PIN_DEPS.has(name), | |
| ) | |
| .map(([name, version]) => `${name}: ${version}`); | |
| expect(violations).toEqual([]); | |
| }); | |
| it("pins direct dependency versions exactly", () => { | |
| const allDepsBySection = { | |
| dependencies: packageJson.dependencies, | |
| devDependencies: packageJson.devDependencies, | |
| }; | |
| const violations = Object.entries(allDepsBySection).flatMap( | |
| ([section, dependencies]) => | |
| Object.entries(dependencies ?? {}) | |
| .filter( | |
| ([name, version]) => | |
| !EXACT_SEMVER_PATTERN.test(version) && | |
| !ALLOWED_STACK_PIN_DEPS.has(name), | |
| ) | |
| .map(([name, version]) => `${section}.${name}: ${version}`), | |
| ); | |
| expect(violations).toEqual([]); | |
| }); | |
| it("prints startup guidance only for global installs", () => { | |
| const runPostinstall = (isGlobal: boolean) => { | |
| const env = { ...process.env }; | |
| if (isGlobal) { | |
| env.npm_config_global = "true"; | |
| } else { | |
| delete env.npm_config_global; | |
| } | |
| return spawnSync(packageJson.scripts.postinstall, { | |
| encoding: "utf8", | |
| env, | |
| shell: true, | |
| }); | |
| }; | |
| const dependencyInstall = runPostinstall(false); | |
| const globalInstall = runPostinstall(true); | |
| expect(dependencyInstall.status).toBe(0); | |
| expect(dependencyInstall.stdout).toBe(""); | |
| expect(globalInstall.status).toBe(0); | |
| expect(globalInstall.stdout).toContain("To start Agent Canvas, run:"); | |
| }); | |
| it("ships runtime logger dependencies for the published CLI", () => { | |
| expect(packageJson.dependencies).toMatchObject({ | |
| winston: "3.19.0", | |
| "winston-daily-rotate-file": "5.0.0", | |
| }); | |
| expect(packageJson.devDependencies?.winston).toBeUndefined(); | |
| expect( | |
| packageJson.devDependencies?.["winston-daily-rotate-file"], | |
| ).toBeUndefined(); | |
| }); | |
| it("uses local dev commands without Docker", () => { | |
| expect(packageJson.scripts.dev).toBe( | |
| "node --env-file-if-exists=.env scripts/dev-with-automation.mjs", | |
| ); | |
| expect(packageJson.scripts["dev:static"]).toBe( | |
| "node --env-file-if-exists=.env scripts/dev-static.mjs", | |
| ); | |
| expect(packageJson.scripts["dev:minimal"]).toBe( | |
| "node --env-file-if-exists=.env scripts/dev-safe.mjs", | |
| ); | |
| expect(packageJson.scripts["dev:docker"]).toBeUndefined(); | |
| expect(packageJson.scripts["dev:docker:dynamic"]).toBeUndefined(); | |
| expect(packageJson.scripts["dev:dangerously-dockerless"]).toBeUndefined(); | |
| }); | |
| }); | |