Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/mpsuesser/effect-oxlint/llms.txt

Use this file to discover all available pages before exploring further.

The Testing module is a dedicated subpath export (effect-oxlint/testing) that provides everything you need to test Effect-first oxlint rules without a real oxlint process. It includes runners that execute a rule against a single mock AST node, assertion helpers that produce readable errors on mismatch, a mock RuleContext factory for it.effect tests, and a large set of AST node builders that produce structurally correct mock nodes without any file parsing.
import * as Testing from 'effect-oxlint/testing'

Rule runners

Testing.runRule

Runs a rule with a single visitor event and returns the collected diagnostics.
export const runRule = (
  rule: CreateRule,
  visitor: string,
  visitorNode: unknown,
  opts?: MockContextOptions
): ReadonlyArray<ReportedDiagnostic>
rule
CreateRule
required
The rule creator returned by Rule.define.
visitor
string
required
The visitor key to fire (e.g., "ThrowStatement", "CallExpression").
visitorNode
unknown
required
The AST node to pass to the visitor handler. Use any of the node builders in this module.
opts
MockContextOptions
Optional mock context options. See MockContextOptions below.
ReadonlyArray<ReportedDiagnostic>
array
All diagnostics reported by the rule during this visitor call.
const result = Testing.runRule(myRule, 'ThrowStatement', Testing.throwStmt())
Testing.expectDiagnostics(result, [{ message: 'No throw in Effect.gen' }])

Testing.runRuleMulti

Runs a rule with multiple visitor/node pairs sequentially on a shared context. Use this when your rule tracks state across multiple visitor calls.
export const runRuleMulti = (
  rule: CreateRule,
  pairs: ReadonlyArray<readonly [visitor: string, node: unknown]>,
  opts?: MockContextOptions
): ReadonlyArray<ReportedDiagnostic>
rule
CreateRule
required
The rule creator returned by Rule.define.
pairs
ReadonlyArray<readonly [visitor: string, node: unknown]>
required
An array of [visitorKey, node] tuples. The rule’s create function is called once, and each pair is dispatched in order to the same visitor object.
opts
MockContextOptions
Optional mock context options.
ReadonlyArray<ReportedDiagnostic>
array
All diagnostics reported across all visitor calls.
// Test a rule that uses Ref to count nested function depth
const result = Testing.runRuleMulti(depthRule, [
  ['FunctionDeclaration', Testing.astNode('FunctionDeclaration')],
  ['ArrowFunctionExpression', Testing.arrowFn()],
  ['ArrowFunctionExpression:exit', Testing.arrowFn()],
  ['FunctionDeclaration:exit', Testing.astNode('FunctionDeclaration')],
])

Assertion helpers

Testing.expectDiagnostics

Asserts that the diagnostics in result match expected. Each matcher is checked partially — only provided fields (message or messageId) are compared, so you do not need to specify every field on a diagnostic.
export const expectDiagnostics = (
  result: ReadonlyArray<ReportedDiagnostic>,
  expected: ReadonlyArray<{
    readonly message?: string;
    readonly messageId?: string;
  }>
): void
Throws an error if the count does not match or if any provided field does not match. The error message lists all actual diagnostics for easy debugging.
result
ReadonlyArray<ReportedDiagnostic>
required
The result returned by runRule or runRuleMulti.
expected
ReadonlyArray<{ message?: string; messageId?: string }>
required
The expected diagnostic patterns. Order must match the order diagnostics were reported.
Testing.expectDiagnostics(result, [
  { message: 'No throw in Effect.gen' },
  { messageId: 'noTryCatch' }
])

Testing.expectNoDiagnostics

Asserts that no diagnostics were reported. Throws an error listing all actual diagnostics if any exist.
export const expectNoDiagnostics = (
  result: ReadonlyArray<ReportedDiagnostic>
): void
result
ReadonlyArray<ReportedDiagnostic>
required
The result returned by runRule or runRuleMulti.
const result = Testing.runRule(myRule, 'Identifier', Testing.id('foo'))
Testing.expectNoDiagnostics(result)

Testing.messages

Extracts the diagnostic messages from a result array. Returns Option.some(message) for diagnostics that use message, and Option.none() for diagnostics that use messageId or have a null message.
export const messages = (
  result: ReadonlyArray<ReportedDiagnostic>
): ReadonlyArray<Option.Option<string>>
result
ReadonlyArray<ReportedDiagnostic>
required
The result returned by runRule or runRuleMulti.
ReadonlyArray<Option.Option<string>>
array
One Option<string> per diagnostic.
import * as Option from 'effect/Option'

const result = Testing.runRule(rule, 'ThrowStatement', Testing.throwStmt())
expect(Testing.messages(result)).toEqual([Option.some('Use Effect.fail instead')])

Testing.messageIds

Extracts the diagnostic messageId values from a result array. Returns Option.some(messageId) for diagnostics that use messageId, and Option.none() for those that use message or have a null messageId.
export const messageIds = (
  result: ReadonlyArray<ReportedDiagnostic>
): ReadonlyArray<Option.Option<string>>
result
ReadonlyArray<ReportedDiagnostic>
required
The result returned by runRule or runRuleMulti.
ReadonlyArray<Option.Option<string>>
array
One Option<string> per diagnostic.

Mock context

Testing.createMockContext

Creates a mock oxlint Context object and a mutable diagnostics array. The mock provides the minimal surface required by fromOxlintContext in RuleContext.
export const createMockContext = (opts?: MockContextOptions): {
  context: OxlintContext;
  diagnostics: Array<ReportedDiagnostic>;
}
opts
MockContextOptions
Optional configuration for the mock context. All fields are optional.
context
OxlintContext
A fully-shaped mock Context object with stubbed sourceCode methods and the provided options applied.
diagnostics
Array<ReportedDiagnostic>
A mutable array that collects every report() call made through context.report.

MockContextOptions

export interface MockContextOptions {
  readonly filename?: string;
  readonly cwd?: string;
  readonly options?: ReadonlyArray<unknown>;
  readonly sourceText?: string;
  readonly comments?: ReadonlyArray<Comment>;
}
filename
string
The mock filename. Defaults to "/test/file.ts".
cwd
string
The mock current working directory. Defaults to "/test".
options
ReadonlyArray<unknown>
Rule options array, as passed via oxlint config. Defaults to [].
sourceText
string
The mock source text returned by sourceCode.getText(). Defaults to "".
comments
ReadonlyArray<Comment>
Comments returned by sourceCode.getAllComments(). Defaults to [].

Testing.mockRuleContextLayer

Creates an Effect Layer that provides a mock RuleContext service. Use this in it.effect tests that need to yield* visitor handlers (which have RuleContext in their requirements).
export const mockRuleContextLayer = (
  opts?: MockContextOptions
): Layer.Layer<RuleContext>
opts
MockContextOptions
Optional mock context options passed to createMockContext.
Layer.Layer<RuleContext>
Layer
A layer that provides the RuleContext service backed by the mock context.
import { it } from '@effect/vitest'
import * as Effect from 'effect/Effect'
import { Testing } from 'effect-oxlint'

it.effect('visitor handler reports for matching node', () =>
  Effect.gen(function* () {
    // yield* the handler directly — it requires RuleContext
    yield* myVisitorHandler(Testing.throwStmt())
    // assertions...
  }).pipe(Effect.provide(Testing.mockRuleContextLayer()))
)

Testing.withMockRuleContext

Provides a mock RuleContext to an effect without explicitly constructing a layer.
export const withMockRuleContext = <A, E>(
  effect: Effect.Effect<A, E, RuleContext>,
  opts?: MockContextOptions
): Effect.Effect<A, E>
effect
Effect.Effect<A, E, RuleContext>
required
An effect that requires RuleContext.
opts
MockContextOptions
Optional mock context options.
Effect.Effect<A, E>
Effect
The same effect with RuleContext satisfied by a mock instance.

AST node builders

All builders return fully-shaped mock AST node objects that satisfy the type shapes expected by the AST, Visitor, and Rule modules. Pass them directly to runRule or runRuleMulti.
Testing.idIdentifier: { type: "Identifier", name }
export const id = (name: string): ESTree.IdentifierName
Testing.memberExprMemberExpression: obj.prop (non-computed)
export const memberExpr = (obj: string, prop: string): ESTree.MemberExpression
Testing.computedMemberExprMemberExpression: obj[prop] (computed)
export const computedMemberExpr = (obj: string, prop: string): ESTree.MemberExpression
Testing.chainedMemberExpr — Chained MemberExpression: a.b.c
export const chainedMemberExpr = (
  ...names: readonly [string, string, ...ReadonlyArray<string>]
): ESTree.MemberExpression
Requires at least two names. Builds left-associative member access.
Testing.callExprCallExpression with bare identifier callee: name(args)
export const callExpr = (
  name: string,
  args: ReadonlyArray<unknown> = []
): ESTree.CallExpression
Testing.callOfMemberCallExpression with MemberExpression callee: obj.prop(args)
export const callOfMember = (
  obj: string,
  prop: string,
  args: ReadonlyArray<unknown> = []
): ESTree.CallExpression
Testing.newExprNewExpression: new callee(args)
export const newExpr: {
  (callee: string, args?: ReadonlyArray<unknown>): ESTree.NewExpression;
  (callee: unknown, args?: ReadonlyArray<unknown>): ESTree.NewExpression;
}
When callee is a string it is automatically wrapped in id(), so newExpr('Date') and newExpr(id('Date')) are equivalent.
Testing.strLiteralStringLiteral: { type: "Literal", value }
export const strLiteral = (value: string): ESTree.StringLiteral
Testing.numLiteralNumericLiteral: { type: "Literal", value }
export const numLiteral = (value: number): ESTree.NumericLiteral
Testing.boolLiteralBooleanLiteral: { type: "Literal", value }
export const boolLiteral = (value: boolean): ESTree.BooleanLiteral
Testing.objectExprObjectExpression with identifier-keyed properties
export const objectExpr = (
  properties: ReadonlyArray<{
    readonly key: string;
    readonly value?: unknown;
  }>
): ESTree.ObjectExpression
Keys are wrapped in id(). Omitted value defaults to strLiteral("").Testing.objectExprLiteralKeysObjectExpression with string literal keys
export const objectExprLiteralKeys = (
  properties: ReadonlyArray<{
    readonly key: string;
    readonly value?: unknown;
  }>
): ESTree.ObjectExpression
Keys are wrapped in strLiteral() instead of id().Testing.objectExprWithSpreadObjectExpression with a single SpreadElement
export const objectExprWithSpread = (spreadArg: unknown): ESTree.ObjectExpression
Testing.throwStmtThrowStatement
export const throwStmt = (): ESTree.ThrowStatement
Testing.tryStmtTryStatement
export const tryStmt = (): ESTree.Node
Testing.returnStmtReturnStatement
export const returnStmt = (argument?: unknown): ESTree.ReturnStatement
argument defaults to null.Testing.blockStmtBlockStatement
export const blockStmt = (body: ReadonlyArray<unknown> = []): ESTree.BlockStatement
Testing.exprStmtExpressionStatement
export const exprStmt = (expression: unknown): ESTree.ExpressionStatement
Testing.ifStmtIfStatement
export const ifStmt = (
  test?: unknown,
  consequent?: unknown,
  alternate?: unknown
): ESTree.IfStatement
All parameters are optional. ifStmt() produces { type: "IfStatement" } with all fields set to null, suitable for enter/exit tracking.Testing.switchStmtSwitchStatement
export const switchStmt = (): ESTree.SwitchStatement
Testing.forStmtForStatement
export const forStmt = (): ESTree.ForStatement
Testing.forInStmtForInStatement
export const forInStmt = (): ESTree.ForInStatement
Testing.forOfStmtForOfStatement
export const forOfStmt = (): ESTree.ForOfStatement
Testing.whileStmtWhileStatement
export const whileStmt = (): ESTree.WhileStatement
Testing.doWhileStmtDoWhileStatement
export const doWhileStmt = (): ESTree.DoWhileStatement
Testing.arrowFnArrowFunctionExpression
export const arrowFn = (
  body?: unknown,
  params: ReadonlyArray<unknown> = []
): ESTree.ArrowFunctionExpression
body defaults to blockStmt().Testing.binaryExprBinaryExpression
export const binaryExpr = (
  operator: string,
  left: unknown,
  right: unknown
): ESTree.BinaryExpression
Testing.yieldExprYieldExpression
export const yieldExpr = (
  argument?: unknown,
  delegate: boolean = false
): ESTree.YieldExpression
argument defaults to null.Testing.unaryExprUnaryExpression
export const unaryExpr = (operator: string, argument: unknown): ESTree.UnaryExpression
Always sets prefix: true.
Testing.varDeclVariableDeclaration: const/let/var name = init
export const varDecl = (
  kind: 'const' | 'let' | 'var',
  name: string,
  init?: unknown
): ESTree.VariableDeclaration
Testing.varDeclaratorVariableDeclarator (standalone, without wrapping declaration)
export const varDeclarator = (name: string, init?: unknown): ESTree.VariableDeclarator
Testing.exportNamedDeclExportNamedDeclaration
export const exportNamedDecl = (declaration?: unknown): ESTree.ExportNamedDeclaration
Testing.importDeclImportDeclaration with no specifiers: import ... from "source"
export const importDecl = (source: string): ESTree.ImportDeclaration
Testing.importDeclWithSpecifiersImportDeclaration with specifiers
export const importDeclWithSpecifiers = (
  source: string,
  specifiers: ReadonlyArray<unknown>,
  importKind: string = 'value'
): ESTree.ImportDeclaration
Testing.importSpecifierImportSpecifier: { imported as local }
export const importSpecifier = (
  imported: string,
  local?: string,
  importKind: string = 'value'
): ESTree.ImportSpecifier
local defaults to imported when omitted.Testing.importNamespaceSpecifierImportNamespaceSpecifier: * as local
export const importNamespaceSpecifier = (local: string): ESTree.ImportNamespaceSpecifier
Testing.classDeclClassDeclaration
export const classDecl = (
  name: string,
  opts: {
    readonly superClass?: unknown;
    readonly members?: ReadonlyArray<unknown>;
  } = {}
): ESTree.Class
// Simple: class Foo {}
Testing.classDecl('Foo')

// With super: class Foo extends Bar {}
Testing.classDecl('Foo', { superClass: Testing.id('Bar') })

// With members: class Foo { x; static y() {} }
Testing.classDecl('Foo', {
  members: [Testing.propertyDef('x'), Testing.methodDef('y', true)]
})
Testing.propertyDefPropertyDefinition (class field)
export const propertyDef = (name: string, isStatic: boolean = false): ESTree.PropertyDefinition
Testing.methodDefMethodDefinition (class method)
export const methodDef = (name: string, isStatic: boolean = false): ESTree.MethodDefinition
Testing.tsAsExprTSAsExpression: expr as Type
export const tsAsExpr = (
  typeKind: string,
  parent?: { readonly type: string; readonly parent?: unknown }
): ESTree.TSAsExpression
The expression is always id('_'). The type annotation is { type: typeKind }.Testing.tsUnionTypeTSUnionType: A | B | C
export const tsUnionType = (typeKinds: ReadonlyArray<string>): ESTree.TSUnionType
Testing.tsTypeRefTSTypeReference: TypeName
export const tsTypeRef = (name: string): ESTree.TSTypeReference
Testing.tsTypeLiteralTSTypeLiteral: { ... } with N TSPropertySignature members
export const tsTypeLiteral = (memberCount: number): ESTree.TSTypeLiteral
Testing.interfaceDeclTSInterfaceDeclaration: interface Name { }
export const interfaceDecl = (name: string): ESTree.TSInterfaceDeclaration
Testing.typeAliasDeclTSTypeAliasDeclaration: type Name = ...
export const typeAliasDecl = (name: string): ESTree.TSTypeAliasDeclaration
Testing.astNode — Generic AST node with type and optional parent pointer
export const astNode = (
  type: string,
  parent?: { readonly type: string; readonly parent?: unknown }
): { readonly type: string; readonly parent?: unknown }
Testing.withParentChain — Build a parent chain from outermost to innermost
export const withParentChain = (
  first: string,
  ...rest: ReadonlyArray<string>
): { readonly type: string; readonly parent?: unknown }
Returns the innermost node with .parent links set on each ancestor.
// FunctionDeclaration → BlockStatement → ThrowStatement
const node = Testing.withParentChain('FunctionDeclaration', 'BlockStatement', 'ThrowStatement')
// node.type === 'ThrowStatement'
// node.parent.type === 'BlockStatement'
// node.parent.parent.type === 'FunctionDeclaration'
Testing.programProgram node
export const program = (
  body: ReadonlyArray<unknown> = [],
  comments: ReadonlyArray<unknown> = []
): ESTree.Program
Always sets sourceType: "module".
Testing.token — Mock Token
export const token = (type: Token['type'], value: string): Token
Produces a token with start: 0, end: value.length, and matching range and loc.Testing.comment — Mock Comment
export const comment = (type: Comment['type'], value: string): Comment
Produces a comment with start: 0, end: value.length + 4 (accounts for delimiters), and matching range and loc.Testing.scope — Mock Scope
export const scope = (
  opts: {
    readonly type?: OxlintScope['type'];
    readonly isStrict?: boolean;
    readonly variables?: ReadonlyArray<Variable>;
    readonly upper?: OxlintScope | null;
  } = {}
): OxlintScope
Defaults: type: "function", isStrict: false, upper: null, no variables.Testing.variable — Mock Variable
export const variable = (
  name: string,
  opts: {
    readonly references?: ReadonlyArray<{
      readonly isRead: () => boolean;
      readonly isWrite: () => boolean;
      readonly isReadOnly: () => boolean;
      readonly isWriteOnly: () => boolean;
      readonly isReadWrite: () => boolean;
    }>;
  } = {}
): Variable

Types

ReportedDiagnostic

A single collected diagnostic from the mock context’s report function.
export interface ReportedDiagnostic {
  readonly diagnostic: OxlintDiagnostic;
}
The Testing module is only available from the effect-oxlint/testing subpath export. It is not included in the main effect-oxlint barrel to avoid pulling test infrastructure into production bundles.

Build docs developers (and LLMs) love