npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@velajs/testing

v1.30.0

Published

Testing utilities for Vela framework

Readme

@velajs/testing

npm version License: MIT

Test-module builder for Vela. Compose modules in isolation, override providers/guards/pipes/interceptors/filters, and exercise controllers via Hono's app.request(). Testing uses the production bootstrap and finalization path, including signed internal dispatch and lifecycle hooks.

Install

pnpm add -D @velajs/testing
# Required peers: @velajs/vela ^1.22.1, hono >=4, vitest >=3

No reflect-metadata needed — Vela ships its own polyfill.

Quick Start

import { describe, it, expect } from 'vitest';
import { Test } from '@velajs/testing';
import { Injectable, Module } from '@velajs/vela';

@Injectable()
class CatsService {
  findAll() { return ['cat1', 'cat2']; }
}

@Module({ providers: [CatsService] })
class CatsModule {}

describe('CatsService', () => {
  it('returns cats', async () => {
    const moduleRef = await Test.createTestingModule({
      imports: [CatsModule],
    }).compile();

    try {
      expect(moduleRef.get(CatsService).findAll()).toEqual(['cat1', 'cat2']);
    } finally {
      await moduleRef.close();
    }
  });
});

Keep module decorators registered until the test completes: no per-test cleanup is needed, and MetadataRegistry.reset() (from @velajs/vela/module-kit) after declaring a module removes the metadata that compile() needs. Close each compiled module to run its shutdown hooks and dispose constructed providers. Concurrent calls to `close()` await the same completion. Register owned fixtures with `moduleRef.onClose(async () => { /* cleanup */ })`; callbacks run in reverse order, and cleanup continues after a failure. The Node WebSocket adapter registers its server automatically, so closing the module closes active sockets and the listening port. A closed module rejects new requests and scopes.

`runInRequestScope` uses a managed invocation lifetime, seeds `REQUEST_CONTEXT`, and drains deferred work before disposing the child. Module shutdown waits for already-running scope callbacks. Always await HTTP response bodies and close SSE connections before tearing down a test that consumes streaming responses.

Overriding providers

const moduleRef = await Test.createTestingModule({ imports: [UsersModule] })
  .overrideProvider(UsersService)
  .useValue({ getUsers: () => ['mock-user'] })
  .compile();

useValue, useClass, and useFactory are all supported. Same builder for overrideGuard, overridePipe, overrideInterceptor, overrideFilter.

get(token) and overrides infer their value contract from a class or InjectionToken<Value>. An incompatible mock fails typechecking. Raw string/symbol tokens resolve to unknown; use an injection token when the contract matters.

.overrideGuard(AuthGuard).useValue({ canActivate: () => true })
.overrideInterceptor(LogInterceptor).useClass(NoopInterceptor)
.overrideProvider(CONFIG).useFactory({
  factory: (env) => ({ env: env.APP_ENV }),
  inject: [ENV],
})

Here ENV is the framework environment token from @velajs/vela, with APP_ENV: string declared on VelaEnv, and CONFIG is an InjectionToken<{ env: string }>.

Environment and runtime adapters

The builder's second argument seeds the application's ENV and binds runtime adapters through the same bootstrap as VelaFactory.create:

const moduleRef = await Test.createTestingModule(
  { imports: [ReportsModule] },
  { env: { APP_ENV: 'test' }, adapters: [myRuntimeAdapter] },
).compile();

Adapter configureContainer, request middleware, client-IP resolution and lifecycle hooks all apply, and registerAs namespaces read the seeded env. moduleRef.fetch() and the HTTP and SSE builders send each request with the seeded env as c.env (an explicit fetch(request, env) argument wins), so an adapter that binds requests to its environment accepts them. Pass it the same object, for example { env, adapters: [cloudflareAdapter({ env })] }. overrideProvider(ENV).useValue(env) replaces the environment for every module, with or without a seeded env.

Inline providers (skip importing a module):

const moduleRef = await Test.createTestingModule({
  providers: [InlineService, defineProvider(CONFIG, { useValue: { env: 'test' } })],
}).compile();

Import defineProvider from @velajs/vela. Factory dependency types come from the inject tuple, which a factory without parameters may omit, in providers and in overrideProvider(TOKEN).useFactory({ factory, inject }). Overrides recompute request-scope propagation, including dependencies introduced or removed by a replacement factory. Compile separate testing modules to keep application/environment-specific replacements independent.

For implementations with private state, inject a small interface token rather than casting a partial object to the concrete class:

```ts interface Database { read(id: string): Promise; } const DATABASE = new InjectionToken('database'); const fake = { read: async (id: string) => id } satisfies Database; const moduleRef = await Test.createTestingModule({ providers: [defineProvider(DATABASE, { useValue: fake })], }).compile(); ```

Direct constructor tests remain useful when no module graph or request pipeline is involved. No mock superclass or assertion cast is required.

HTTP testing

const moduleRef = await Test.createTestingModule({ imports: [ItemsModule] }).compile();
const app = await moduleRef.createApplication();

const res = await app.getHonoApp().request('/items');
expect(res.status).toBe(200);
expect(await res.json()).toEqual([{ id: 1, name: 'Item 1' }]);

The fluent HTTP client returns TestResponse. Its json() method returns unknown; pass a schema or defineDto descriptor to validate the body and infer its output:

const response = await moduleRef.http.get('/items').send();
const items = await response.json(z.array(z.object({ id: z.number(), name: z.string() })));
expect(items[0]?.name).toBe('Item 1');

runInRequestScope(callback, init?) creates a real framework request context, including typed RequestContextKey storage, and disposes its child container when the callback finishes.

get(token) resolves from the root container and throws for request-scoped providers. Resolve those with resolveInRequest, which opens a fresh request scope per call, seeded from an optional RequestInit plus url. The scope stays open until close(), so the returned instance and its request dependencies remain usable during the test:

const session = await moduleRef.resolveInRequest(SessionState, {
  url: 'http://localhost/cats',
  headers: { 'x-request-id': 'test-1' },
});

Lifecycle

compile() calls onModuleInit and onApplicationBootstrap automatically. moduleRef.close() triggers onModuleDestroy and onApplicationShutdown.

API

| Export | Purpose | |---|---| | Test.createTestingModule(metadata) | Returns a TestingModuleBuilder. | | TestingModuleBuilder | Chain overrideProvider/Guard/Pipe/Interceptor/Filter, then .compile(). | | TestingModule | Result of compile(): get(token), createApplication(), close(). | | OverrideBy | The fluent intermediate from overrideX() — exposed for type-narrowing. |

Output scoring / evals

@velajs/testing/eval is a small, model-agnostic harness for grading the output of any string-producing function — an LLM turn, an agent loop, a formatter — against heuristics or an injected LLM judge. It pulls in no AI SDK: the only model touchpoint is llmScorer, whose judge is a plain callback you supply.

import { evaluate, keyword, llmScorer } from '@velajs/testing/eval';

const report = await evaluate(
  [{ input: 'where is my order?', expected: 'shipped' }],
  async (input) => askSupportAgent(input),
  {
    coverage: keyword(['shipped']),
    helpful: llmScorer({ criteria: 'answers the question', judge: myModel }),
  },
);

expect(report.aggregate.overall).toBeGreaterThan(0.5);

Each scorer returns a [0, 1] score (auto-clamped) with an optional reason. evaluate runs every case through the producer, grades each output with every scorer, and returns per-case reports plus an aggregate (perScorer means and an overall mean).

| Export | Purpose | |---|---| | evaluate(dataset, run, scorers) | Grade a dataset; returns { cases, aggregate }. | | exactMatch(options?) | 1 when the output equals expected (trimmed by default). | | contains(needles, options?) | Substring match, all-of (default) or any-of. | | keyword(keywords, options?) | Fractional coverage — the share of keywords present. | | regex(pattern) | 1 when the pattern matches (global/sticky flags stripped for reuse). | | llmScorer({ criteria, judge }) | LLM-as-judge with an injected judge callback; fails soft to 0. | | Scorer | (input) => number \| ScoreResult (sync or async) — write your own. |

How it's wired

@velajs/testing builds each testing module with the same bootstrap primitives VelaFactory.create uses, imported from @velajs/vela/internal: bootstrap, applyRuntimeAdapters and finalizeApplication, so test-mode and run-mode app construction stay in lockstep automatically. It also takes createRequestContext and setRequestContainer from @velajs/vela/internal to seed request scopes, and nothing else. MetadataRegistry and Container come from @velajs/vela/module-kit, and VelaApplication from the root @velajs/vela.

HTTP transports and schema validation

Use the same response assertions against an existing remote Worker or an injected fetch handler. Each client owns its headers; there is no global environment, automatic cookie jar, implicit authentication, or request retry.

import { createTestHttpClient } from '@velajs/testing';

const remote = createTestHttpClient({ baseUrl: 'https://staging.example.com/' })
  .withHeaders({ authorization: 'Bearer test-session' });
(await remote.get('/health').send()).assertOk();

// A native Workers test can inject SELF.fetch using an explicit closure.
const native = createTestHttpClient({
  baseUrl: 'https://worker.test/',
  fetch: request => SELF.fetch(request),
});
(await native.get('/health').send()).assertOk();

actingAs needs a local TestingModule; remote clients use explicit headers. forHost preserves the base URL scheme while replacing its host and Host header. Relative paths resolve against baseUrl using standard URL rules.

response.json(schema) accepts Standard Schema v1, defineDto descriptors, and legacy parsers (including parseAsync). It awaits validation and infers the transformed output. The cached value remains the raw JSON: a later json() call still returns unknown, and each requested schema runs against that original value. Validator exceptions propagate; invalid payloads fail the test.

License

MIT