ci(examples): run examples / testing from built lib/js
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "0.25.1",
|
||||
"tsx": "^4.22.4",
|
||||
"@deepseek-ai/dsh-loader-smoke": "workspace:*",
|
||||
"vitest": "^4.1.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -11,7 +11,6 @@ import { cp, mkdtemp, readFile, readdir, rm } from 'node:fs/promises'
|
||||
import { existsSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join, delimiter } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { Readable, Writable } from 'node:stream'
|
||||
import {
|
||||
ClientSideConnection,
|
||||
@@ -23,12 +22,7 @@ import {
|
||||
type RequestPermissionResponse,
|
||||
type SessionNotification,
|
||||
} from '@agentclientprotocol/sdk'
|
||||
|
||||
// Resolve tsx's ESM loader to an ABSOLUTE path once: the child runs with its
|
||||
// cwd in a temp dir OUTSIDE the repo, where a bare `--import tsx` would not
|
||||
// resolve from node_modules. import.meta.resolve gives this package's tsx
|
||||
// regardless of the child cwd.
|
||||
const tsxLoader = fileURLToPath(import.meta.resolve('tsx'))
|
||||
import { resolveExampleLaunch } from '@deepseek-ai/dsh-loader-smoke'
|
||||
|
||||
/**
|
||||
* The agent composition a scenario runs against: which bin to boot and which
|
||||
@@ -37,7 +31,7 @@ const tsxLoader = fileURLToPath(import.meta.resolve('tsx'))
|
||||
* them from its own `import.meta.url`.
|
||||
*/
|
||||
export interface AgentUnderTest {
|
||||
/** The agent bin entry (e.g. `packages/examples/acp-demo/src/bin.ts`), run unbuilt via tsx. */
|
||||
/** The agent bin's SOURCE entry (e.g. `packages/examples/acp-demo/src/bin.ts`); the `lib` bin is derived from it. */
|
||||
binScript: string
|
||||
/**
|
||||
* The example's live `cordis.yml`. Under `DSH_SNAPSHOT=replay` the bin swaps
|
||||
@@ -47,10 +41,8 @@ export interface AgentUnderTest {
|
||||
configPath: string
|
||||
/**
|
||||
* The repo-root tsconfig whose `paths` map resolves the unbuilt workspace
|
||||
* imports. Passed to the child as `TSX_TSCONFIG_PATH`: tsx finds a tsconfig
|
||||
* by searching UP from the child's cwd — a temp dir outside the repo — so
|
||||
* without the explicit pin the dsh-* imports fail before the bin writes a
|
||||
* byte.
|
||||
* imports in `src` mode (passed to the child as `TSX_TSCONFIG_PATH`). Ignored
|
||||
* in `lib` mode, where the example resolves plugins through real `exports`.
|
||||
*/
|
||||
tsconfigPath: string
|
||||
}
|
||||
@@ -184,25 +176,31 @@ export async function runScenario(input: InputScript, opts: RunOptions): Promise
|
||||
if (opts.workspaceDir !== undefined && existsSync(opts.workspaceDir)) {
|
||||
await cp(opts.workspaceDir, cwd, { recursive: true })
|
||||
}
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
...process.env,
|
||||
TSX_TSCONFIG_PATH: opts.agent.tsconfigPath,
|
||||
DSH_SNAPSHOT: opts.mode,
|
||||
DSH_SNAPSHOT_FILE: opts.fixtureFile,
|
||||
DSH_SNAPSHOT_SESSIONS_ROOT: sessionsRoot,
|
||||
DSH_SNAPSHOT_SPILL_ROOT: spillRoot,
|
||||
DSH_HOME: join(cwd, '.dsh'),
|
||||
DSH_AGENTS_HOME: join(cwd, '.agents'),
|
||||
...opts.overrideFile !== undefined ? { DSH_SNAPSHOT_OVERRIDE: opts.overrideFile } : {},
|
||||
...opts.childFiles !== undefined && opts.childFiles.length > 0
|
||||
? { DSH_SNAPSHOT_CHILD_FILES: opts.childFiles.join(delimiter) }
|
||||
: {},
|
||||
}
|
||||
// Boot the agent in the environment's mode (DSH_EXAMPLE_MODE): `src` runs the
|
||||
// source bin under tsx with the paths map; `lib` runs the built bin under plain
|
||||
// Node, resolving plugins through the example's workspace node_modules → lib.
|
||||
const launch = resolveExampleLaunch({
|
||||
srcBin: opts.agent.binScript,
|
||||
configArgs: ['--config', opts.configPath ?? opts.agent.configPath],
|
||||
tsconfigPath: opts.agent.tsconfigPath,
|
||||
env: {
|
||||
DSH_SNAPSHOT: opts.mode,
|
||||
DSH_SNAPSHOT_FILE: opts.fixtureFile,
|
||||
DSH_SNAPSHOT_SESSIONS_ROOT: sessionsRoot,
|
||||
DSH_SNAPSHOT_SPILL_ROOT: spillRoot,
|
||||
DSH_HOME: join(cwd, '.dsh'),
|
||||
DSH_AGENTS_HOME: join(cwd, '.agents'),
|
||||
...opts.overrideFile !== undefined ? { DSH_SNAPSHOT_OVERRIDE: opts.overrideFile } : {},
|
||||
...opts.childFiles !== undefined && opts.childFiles.length > 0
|
||||
? { DSH_SNAPSHOT_CHILD_FILES: opts.childFiles.join(delimiter) }
|
||||
: {},
|
||||
},
|
||||
})
|
||||
|
||||
child = spawn(
|
||||
process.execPath,
|
||||
['--import', tsxLoader, opts.agent.binScript, '--config', opts.configPath ?? opts.agent.configPath],
|
||||
{ cwd, env, stdio: ['pipe', 'pipe', 'pipe'] },
|
||||
launch.command,
|
||||
launch.args,
|
||||
{ cwd, env: { ...process.env, ...launch.env }, stdio: ['pipe', 'pipe', 'pipe'] },
|
||||
)
|
||||
|
||||
child.stderr.setEncoding('utf8')
|
||||
|
||||
@@ -7,5 +7,7 @@
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"references": []
|
||||
"references": [
|
||||
{ "path": "../loader-smoke" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# `@deepseek-ai/dsh-loader-smoke`
|
||||
|
||||
Shared subprocess harness for keyless example smokes that boot the real stdio-agent bin and a real `cordis.yml` through the Cordis Loader. A test supplies absolute bin/config/tsconfig paths, optional environment overrides, and stdin lines; `runLoaderSmoke` owns the isolated cwd, DSH homes, tsx path resolution, 30-second process deadline, captured diagnostics, forced kill, EOF, and cleanup.
|
||||
Shared subprocess harness for tests that boot an app and `cordis.yml` through the Cordis Loader. `resolveExampleLaunch` selects local `src` mode (tsx and root tsconfig paths) or CI `lib` mode (plain Node and package exports) from an explicit mode or `DSH_EXAMPLE_MODE`.
|
||||
|
||||
Successful runs return stdout and stderr only after a zero exit. Non-zero exits and deadlines reject with both captured streams. `LOADER_SMOKE_TEST_TIMEOUT_MS` leaves Vitest enough room for the process-owned diagnostic timeout to fire first.
|
||||
`runLoaderSmoke` owns the isolated cwd, DSH homes, stdin, diagnostics, deadline, termination, and cleanup. It returns both streams after a zero exit and rejects with both streams on failure.
|
||||
|
||||
This is support-tier test infrastructure, not product API. The consumers are the Loader-path smokes under `examples/{echo-agent,coding-agent,cordis-agent}`.
|
||||
This is support-tier test infrastructure, not product API.
|
||||
|
||||
## Model Experience
|
||||
|
||||
@@ -12,6 +12,6 @@ None, as this test-only harness boots example processes and inspects their strea
|
||||
|
||||
## Known Limitations and Deferred Work
|
||||
|
||||
- **Only the unbuilt tsx/Loader path is exercised** — built-bin artifacts remain the responsibility of their separate e2e smokes.
|
||||
- **Built mode requires a prior build** — the config must also resolve every named package upward through `examples/node_modules`.
|
||||
- **Captured stdout and stderr are unbounded** — a runaway child can consume memory until the deadline kills it.
|
||||
- **Timeout kills only the direct child** — a process tree spawned by a faulty fixture can outlive the smoke and needs external cleanup.
|
||||
|
||||
@@ -2,6 +2,14 @@
|
||||
* Shared subprocess harness for keyless example smokes that boot a real
|
||||
* `cordis.yml` through the stdio-agent bin and Cordis Loader.
|
||||
*
|
||||
* It also owns the mode-aware launch resolver every example subprocess harness shares
|
||||
* ({@link resolveExampleLaunch}): booting an example bin from TypeScript source under `tsx` (the
|
||||
* zero-build dev path, resolving `@deepseek-ai/dsh-*` / `@cordisjs/*` through the tsconfig `paths`
|
||||
* map) or from built `lib/` under plain Node (resolving bare packages through real `exports`, as an
|
||||
* installed consumer does, while Node type-strips relative example-local TypeScript plugins).
|
||||
* Consolidating that spawn glue here retires the copies in the ACP snapshot harness and the example
|
||||
* e2e drivers (the `TODO(acp-test-harness)`).
|
||||
*
|
||||
* @module @deepseek-ai/dsh-loader-smoke
|
||||
*/
|
||||
|
||||
@@ -12,23 +20,116 @@ import { join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const DEFAULT_PROCESS_TIMEOUT_MS = 30_000
|
||||
const TSX_LOADER = fileURLToPath(import.meta.resolve('tsx'))
|
||||
|
||||
/** Vitest deadline that leaves room for the subprocess-owned 30-second diagnostic timeout. */
|
||||
export const LOADER_SMOKE_TEST_TIMEOUT_MS = DEFAULT_PROCESS_TIMEOUT_MS + 15_000
|
||||
|
||||
/** Which artifact an example bin is booted from: unbuilt `src` via tsx, or built `lib` via plain Node. */
|
||||
export type ExampleMode = 'src' | 'lib'
|
||||
|
||||
/** Environment variable selecting the mode; CI and pre-push set it to `lib`, dev leaves it unset (`src`). */
|
||||
export const EXAMPLE_MODE_ENV = 'DSH_EXAMPLE_MODE'
|
||||
|
||||
/**
|
||||
* Parse an {@link ExampleMode} from a raw string, defaulting to `src` when absent so an unset
|
||||
* environment reproduces the dev/tsx behavior. Throws on any other value rather than silently
|
||||
* falling back, so a typo in a gate's env fails loud.
|
||||
* @param raw - the raw value; defaults to `process.env.DSH_EXAMPLE_MODE`.
|
||||
* @returns the validated mode.
|
||||
*/
|
||||
export function resolveExampleMode(raw: string | undefined = process.env[EXAMPLE_MODE_ENV]): ExampleMode {
|
||||
switch (raw) {
|
||||
case undefined:
|
||||
case '':
|
||||
case 'src':
|
||||
return 'src'
|
||||
case 'lib':
|
||||
return 'lib'
|
||||
default:
|
||||
throw new Error(`${EXAMPLE_MODE_ENV} must be 'src' or 'lib', got ${JSON.stringify(raw)}.`)
|
||||
}
|
||||
}
|
||||
|
||||
/** Inputs to {@link resolveExampleLaunch}. */
|
||||
export interface ExampleLaunchOptions {
|
||||
/** Absolute path to the example bin's TypeScript source entry (`<pkg>/src/bin.ts`); the `lib` bin is derived from it. */
|
||||
readonly srcBin: string
|
||||
/** Arguments passed after the bin — the config, positional (`[configPath]`) or flagged (`['--config', configPath]`). */
|
||||
readonly configArgs?: readonly string[]
|
||||
/** The mode to launch in; defaults to {@link resolveExampleMode} of the environment. */
|
||||
readonly mode?: ExampleMode
|
||||
/** Absolute repo tsconfig whose `paths` map resolves unbuilt workspace imports. Required in `src` mode, ignored in `lib`. */
|
||||
readonly tsconfigPath?: string
|
||||
/** Prepend `--expose-internals` (the Cordis Loader's bare-plugin resolver needs it for some bins); defaults to `false`. */
|
||||
readonly exposeInternals?: boolean
|
||||
/** Extra environment entries the mode-specific ones layer over; the caller then merges the result over `process.env`. */
|
||||
readonly env?: NodeJS.ProcessEnv
|
||||
}
|
||||
|
||||
/** The resolved spawn: `spawn(command, args, { env: { ...process.env, ...env } })`. */
|
||||
export interface ExampleLaunch {
|
||||
/** The executable to spawn — always the current Node binary. */
|
||||
readonly command: string
|
||||
/** Node flags, the resolved bin, then the caller's `configArgs`. */
|
||||
readonly args: string[]
|
||||
/** Mode-specific environment (`TSX_TSCONFIG_PATH` in `src`, nothing added in `lib`) layered over the caller's `env`. */
|
||||
readonly env: NodeJS.ProcessEnv
|
||||
}
|
||||
|
||||
/** Derive the built-lib bin (`<pkg>/lib/<name>.js`) from a source bin (`<pkg>/src/<name>.ts`). */
|
||||
function toLibBin(srcBin: string): string {
|
||||
const marker = '/src/'
|
||||
const cut = srcBin.lastIndexOf(marker)
|
||||
if (cut === -1) throw new Error(`resolveExampleLaunch: expected a "/src/" segment in bin path ${JSON.stringify(srcBin)}.`)
|
||||
const tail = srcBin.slice(cut + marker.length).replace(/\.ts$/, '.js')
|
||||
return `${srcBin.slice(0, cut)}/lib/${tail}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve how to spawn an example bin in the selected mode.
|
||||
*
|
||||
* `src` yields `node [--expose-internals] --import <tsx> <srcBin> <configArgs>` with `TSX_TSCONFIG_PATH`
|
||||
* set so the tsconfig `paths` map resolves workspace imports to source. `lib` yields
|
||||
* `node [--expose-internals] <libBin> <configArgs>` under plain Node with no tsx and no paths map, so
|
||||
* bare package plugins resolve through real package `exports` into built `lib/`; relative example-local
|
||||
* TypeScript plugins remain source files loaded through Node's built-in type stripping. Bare resolution
|
||||
* requires the config to live below a workspace that declares its `cordis.yml` package dependencies.
|
||||
*
|
||||
* @param options - the source bin, config arguments, mode, and environment.
|
||||
* @returns the command, argument vector, and mode-specific environment to spawn with.
|
||||
*/
|
||||
export function resolveExampleLaunch(options: ExampleLaunchOptions): ExampleLaunch {
|
||||
const mode = options.mode ?? resolveExampleMode()
|
||||
const configArgs = options.configArgs ?? []
|
||||
const flags = options.exposeInternals === true ? ['--expose-internals'] : []
|
||||
const env: NodeJS.ProcessEnv = { ...options.env }
|
||||
|
||||
if (mode === 'src') {
|
||||
if (options.tsconfigPath === undefined) {
|
||||
throw new Error("resolveExampleLaunch: 'src' mode needs tsconfigPath for the workspace paths map.")
|
||||
}
|
||||
const tsxLoader = fileURLToPath(import.meta.resolve('tsx'))
|
||||
env.TSX_TSCONFIG_PATH = options.tsconfigPath
|
||||
return { command: process.execPath, args: [...flags, '--import', tsxLoader, options.srcBin, ...configArgs], env }
|
||||
}
|
||||
|
||||
return { command: process.execPath, args: [...flags, toLibBin(options.srcBin), ...configArgs], env }
|
||||
}
|
||||
|
||||
/** Inputs that vary between real-Loader example smokes. */
|
||||
export interface LoaderSmokeOptions {
|
||||
/** Human-readable example name used in failure diagnostics. */
|
||||
readonly label: string
|
||||
/** Prefix for the isolated temporary process cwd. */
|
||||
readonly tempDirPrefix: string
|
||||
/** Absolute stdio-agent bin path. */
|
||||
/** Absolute stdio-agent bin SOURCE path (`<pkg>/src/bin.ts`); the `lib` bin is derived from it. */
|
||||
readonly binScript: string
|
||||
/** Absolute real Loader config path. */
|
||||
readonly configPath: string
|
||||
/** Absolute repo tsconfig path used for unbuilt workspace-package resolution. */
|
||||
/** Absolute repo tsconfig path used for unbuilt workspace-package resolution (required in `src` mode). */
|
||||
readonly tsconfigPath: string
|
||||
/** Boot from source via tsx (`src`) or built lib via plain Node (`lib`); defaults to the environment's mode. */
|
||||
readonly mode?: ExampleMode
|
||||
/** Environment overrides layered over the parent and isolated DSH homes. */
|
||||
readonly env?: Readonly<NodeJS.ProcessEnv>
|
||||
/** Lines written to stdin before EOF; omitted means immediate EOF. */
|
||||
@@ -48,30 +149,28 @@ export interface LoaderSmokeResult {
|
||||
/**
|
||||
* Boot one real Loader tree from an isolated cwd, write the requested stdin
|
||||
* script, close stdin, and await a clean exit. The helper owns process kill and
|
||||
* temp-directory cleanup on every outcome.
|
||||
* @param options - example paths, environment, stdin, and diagnostic identity.
|
||||
* temp-directory cleanup on every outcome, and picks src/lib via {@link resolveExampleLaunch}.
|
||||
* @param options - example paths, mode, environment, stdin, and diagnostic identity.
|
||||
* @returns captured stdout and stderr after a zero exit.
|
||||
*/
|
||||
export async function runLoaderSmoke(options: LoaderSmokeOptions): Promise<LoaderSmokeResult> {
|
||||
const cwd = await mkdtemp(join(tmpdir(), options.tempDirPrefix))
|
||||
const processTimeoutMs = options.processTimeoutMs ?? DEFAULT_PROCESS_TIMEOUT_MS
|
||||
const launch = resolveExampleLaunch({
|
||||
srcBin: options.binScript,
|
||||
configArgs: [options.configPath],
|
||||
...options.mode !== undefined ? { mode: options.mode } : {},
|
||||
tsconfigPath: options.tsconfigPath,
|
||||
exposeInternals: true,
|
||||
env: { DSH_HOME: join(cwd, '.dsh'), DSH_AGENTS_HOME: join(cwd, '.agents'), ...options.env },
|
||||
})
|
||||
try {
|
||||
return await new Promise((resolve, reject) => {
|
||||
const child = spawn(
|
||||
process.execPath,
|
||||
['--expose-internals', '--import', TSX_LOADER, options.binScript, options.configPath],
|
||||
{
|
||||
cwd,
|
||||
env: {
|
||||
...process.env,
|
||||
DSH_HOME: join(cwd, '.dsh'),
|
||||
DSH_AGENTS_HOME: join(cwd, '.agents'),
|
||||
...options.env,
|
||||
TSX_TSCONFIG_PATH: options.tsconfigPath,
|
||||
},
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
},
|
||||
)
|
||||
const child = spawn(launch.command, launch.args, {
|
||||
cwd,
|
||||
env: { ...process.env, ...launch.env },
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
})
|
||||
let stdout = ''
|
||||
let stderr = ''
|
||||
let deferredFailure: Error | undefined
|
||||
|
||||
96
packages/support/loader-smoke/tests/example-launch.spec.ts
Normal file
96
packages/support/loader-smoke/tests/example-launch.spec.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import {
|
||||
EXAMPLE_MODE_ENV,
|
||||
resolveExampleLaunch,
|
||||
resolveExampleMode,
|
||||
} from '@deepseek-ai/dsh-loader-smoke'
|
||||
|
||||
const SRC_BIN = '/repo/packages/examples/stdio-demo/src/bin.ts'
|
||||
const TSCONFIG = '/repo/tsconfig.json'
|
||||
|
||||
const originalMode = process.env[EXAMPLE_MODE_ENV]
|
||||
afterEach(() => {
|
||||
if (originalMode === undefined) Reflect.deleteProperty(process.env, EXAMPLE_MODE_ENV)
|
||||
else process.env[EXAMPLE_MODE_ENV] = originalMode
|
||||
})
|
||||
|
||||
describe('resolveExampleMode', () => {
|
||||
it('defaults absent/empty/src to src', () => {
|
||||
expect(resolveExampleMode(undefined)).toBe('src')
|
||||
expect(resolveExampleMode('')).toBe('src')
|
||||
expect(resolveExampleMode('src')).toBe('src')
|
||||
})
|
||||
|
||||
it('accepts lib', () => {
|
||||
expect(resolveExampleMode('lib')).toBe('lib')
|
||||
})
|
||||
|
||||
it('throws on any other value', () => {
|
||||
expect(() => resolveExampleMode('prod')).toThrow(/must be 'src' or 'lib'/)
|
||||
})
|
||||
|
||||
it('reads the environment when no argument is given', () => {
|
||||
process.env[EXAMPLE_MODE_ENV] = 'lib'
|
||||
expect(resolveExampleMode()).toBe('lib')
|
||||
Reflect.deleteProperty(process.env, EXAMPLE_MODE_ENV)
|
||||
expect(resolveExampleMode()).toBe('src')
|
||||
})
|
||||
})
|
||||
|
||||
describe('resolveExampleLaunch', () => {
|
||||
it('src mode: --import tsx on the source bin with the tsconfig paths env', () => {
|
||||
const { command, args, env } = resolveExampleLaunch({
|
||||
srcBin: SRC_BIN,
|
||||
configArgs: ['./cordis.yml'],
|
||||
mode: 'src',
|
||||
tsconfigPath: TSCONFIG,
|
||||
})
|
||||
expect(command).toBe(process.execPath)
|
||||
expect(args).toContain('--import')
|
||||
expect(args).toContain(SRC_BIN)
|
||||
expect(args[args.length - 1]).toBe('./cordis.yml')
|
||||
expect(args).not.toContain('--expose-internals')
|
||||
expect(env.TSX_TSCONFIG_PATH).toBe(TSCONFIG)
|
||||
})
|
||||
|
||||
it('src mode: throws without a tsconfig path', () => {
|
||||
expect(() => resolveExampleLaunch({ srcBin: SRC_BIN, mode: 'src' })).toThrow(/needs tsconfigPath/)
|
||||
})
|
||||
|
||||
it('lib mode: plain node on the derived lib bin, no tsx and no paths env', () => {
|
||||
const { args, env } = resolveExampleLaunch({
|
||||
srcBin: SRC_BIN,
|
||||
configArgs: ['--config', './cordis.yml'],
|
||||
mode: 'lib',
|
||||
env: { DSH_HOME: '/tmp/home' },
|
||||
})
|
||||
expect(args).not.toContain('--import')
|
||||
expect(args).toContain('/repo/packages/examples/stdio-demo/lib/bin.js')
|
||||
expect(args.slice(-2)).toEqual(['--config', './cordis.yml'])
|
||||
expect(env.TSX_TSCONFIG_PATH).toBeUndefined()
|
||||
expect(env.DSH_HOME).toBe('/tmp/home')
|
||||
})
|
||||
|
||||
it('prepends --expose-internals when requested', () => {
|
||||
const { args } = resolveExampleLaunch({ srcBin: SRC_BIN, mode: 'lib', exposeInternals: true })
|
||||
expect(args[0]).toBe('--expose-internals')
|
||||
})
|
||||
|
||||
it('lib mode: rewrites only the last /src/ segment', () => {
|
||||
const { args } = resolveExampleLaunch({
|
||||
srcBin: '/repo/src/packages/examples/acp-demo/src/bin.ts',
|
||||
mode: 'lib',
|
||||
})
|
||||
expect(args).toContain('/repo/src/packages/examples/acp-demo/lib/bin.js')
|
||||
})
|
||||
|
||||
it('lib mode: throws when the bin has no /src/ segment', () => {
|
||||
expect(() => resolveExampleLaunch({ srcBin: '/repo/lib/bin.js', mode: 'lib' })).toThrow(/"\/src\/" segment/)
|
||||
})
|
||||
|
||||
it('defaults the mode from the environment', () => {
|
||||
process.env[EXAMPLE_MODE_ENV] = 'lib'
|
||||
const { args } = resolveExampleLaunch({ srcBin: SRC_BIN })
|
||||
expect(args).toContain('/repo/packages/examples/stdio-demo/lib/bin.js')
|
||||
})
|
||||
})
|
||||
@@ -16,6 +16,7 @@ describe('runLoaderSmoke', () => {
|
||||
binScript: fixture('success'),
|
||||
configPath,
|
||||
tsconfigPath,
|
||||
mode: 'src',
|
||||
env: { LOADER_SMOKE_MARKER: 'present' },
|
||||
stdinLines: ['one', 'two'],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user