mirror of
https://github.com/stablyai/orca.git
synced 2026-10-24 00:04:01 +00:00
Enforce TypeScript coverage across repository
This commit is contained in:
@@ -72,6 +72,10 @@ function failBootstrapWithBanner(options: {
|
||||
return processMock
|
||||
}
|
||||
|
||||
function invokeScheduledCallback(callback: (() => void) | null): void {
|
||||
callback?.()
|
||||
}
|
||||
|
||||
describe('Electron Vite output contract', () => {
|
||||
it('keeps main-process and plain-Node entries at stable CommonJS paths', () => {
|
||||
const output = electronViteConfig.main?.build?.rollupOptions?.output
|
||||
@@ -98,12 +102,20 @@ describe('Electron Vite output contract', () => {
|
||||
expect(external('@xterm/addon-serialize', undefined, false)).toBe(false)
|
||||
expect(external('psl', undefined, false)).toBe(false)
|
||||
expect(external('zod', undefined, false)).toBe(false)
|
||||
expect(electronViteConfig.main?.build?.externalizeDeps?.exclude).toContain('psl')
|
||||
expect(electronViteConfig.main?.build?.externalizeDeps?.exclude).toContain('zod')
|
||||
const externalizeDeps = electronViteConfig.main?.build?.externalizeDeps
|
||||
if (!externalizeDeps || typeof externalizeDeps === 'boolean') {
|
||||
throw new Error('Expected main-process externalizeDeps options')
|
||||
}
|
||||
expect(externalizeDeps.exclude).toContain('psl')
|
||||
expect(externalizeDeps.exclude).toContain('zod')
|
||||
})
|
||||
|
||||
it('bundles validation dependencies used by the sandboxed preload', () => {
|
||||
expect(electronViteConfig.preload?.build?.externalizeDeps?.exclude).toContain('zod')
|
||||
const externalizeDeps = electronViteConfig.preload?.build?.externalizeDeps
|
||||
if (!externalizeDeps || typeof externalizeDeps === 'boolean') {
|
||||
throw new Error('Expected preload externalizeDeps options')
|
||||
}
|
||||
expect(externalizeDeps.exclude).toContain('zod')
|
||||
})
|
||||
|
||||
it('exits when a static import fails before source error guards load', () => {
|
||||
@@ -136,7 +148,7 @@ describe('Electron Vite output contract', () => {
|
||||
|
||||
expect(processMock.exitCode).toBe(1)
|
||||
expect(scheduledExit).not.toBeNull()
|
||||
scheduledExit?.()
|
||||
invokeScheduledCallback(scheduledExit)
|
||||
expect(exitedWith).toBe(1)
|
||||
expect(context).toHaveProperty(BOOTSTRAP_FATAL_EXIT_GUARD_KEY)
|
||||
expect(stderrWrites.join('')).toContain("Cannot find module 'zod'")
|
||||
|
||||
@@ -33,12 +33,12 @@ let checksum = 0
|
||||
let expectedChecksum = 0
|
||||
let validatedCases = 0
|
||||
let cappedCalibrations = 0
|
||||
let sessionSink: NativeChatSession | null = null
|
||||
let messageArraySink: NativeChatMessage[] | null = null
|
||||
let contentSink = ''
|
||||
let pendingMatchSink: Set<number> | null = null
|
||||
let userRowSink: readonly NativeChatUserRow[] | null = null
|
||||
const benchmarkStartedAt = performance.now()
|
||||
const getPendingSink = (): Set<number> | null => pendingMatchSink
|
||||
|
||||
function proseFixture(count: number, bytes: number, withTurnId: boolean): NativeChatMessage[] {
|
||||
const payload = 'Ab Cd '.repeat(Math.ceil(bytes / 7)).slice(0, bytes)
|
||||
@@ -127,7 +127,6 @@ function messageWeight(message: NativeChatMessage, content: string): number {
|
||||
}
|
||||
|
||||
function consumeSession(session: NativeChatSession, index: number): number {
|
||||
sessionSink = session
|
||||
messageArraySink = session.messages
|
||||
const message = session.messages[index % session.messages.length]
|
||||
if (!message) {
|
||||
@@ -326,10 +325,9 @@ benchmark(
|
||||
)
|
||||
|
||||
strictEqual(checksum, expectedChecksum, 'benchmark checksum accounting drifted')
|
||||
strictEqual(sessionSink?.sessionId, 'benchmark', 'session outputs did not escape')
|
||||
strictEqual(Array.isArray(messageArraySink), true, 'message arrays did not escape')
|
||||
strictEqual(contentSink.length > 0, true, 'message content did not escape')
|
||||
strictEqual(pendingMatchSink instanceof Set, true, 'pending baseline scan did not escape')
|
||||
strictEqual(getPendingSink() instanceof Set, true, 'pending baseline scan did not escape')
|
||||
strictEqual(Array.isArray(userRowSink), true, 'user row scan did not escape')
|
||||
console.log(
|
||||
`validated=${validatedCases} cases, checksum=${checksum}, capped calibrations=${cappedCalibrations}, runtime=${(
|
||||
|
||||
@@ -22,17 +22,36 @@ function createOutputDir(): string {
|
||||
return outputDir
|
||||
}
|
||||
|
||||
function outputChunk(
|
||||
name: string,
|
||||
code: string,
|
||||
imports: string[] = [],
|
||||
isEntry = true
|
||||
): Rollup.OutputChunk {
|
||||
const fileName = `${name}.js`
|
||||
// Rolldown brands chunks with a runtime-only symbol that fixture objects cannot construct.
|
||||
return {
|
||||
type: 'chunk',
|
||||
code,
|
||||
dynamicImports: [],
|
||||
exports: [],
|
||||
facadeModuleId: null,
|
||||
fileName,
|
||||
imports,
|
||||
isDynamicEntry: false,
|
||||
isEntry,
|
||||
map: null,
|
||||
modules: {},
|
||||
moduleIds: [],
|
||||
name,
|
||||
preliminaryFileName: fileName,
|
||||
sourcemapFileName: null
|
||||
} as unknown as Rollup.OutputChunk
|
||||
}
|
||||
|
||||
function createBundle(code = ''): Rollup.OutputBundle {
|
||||
return {
|
||||
'daemon-entry.js': {
|
||||
type: 'chunk',
|
||||
code,
|
||||
dynamicImports: [],
|
||||
fileName: 'daemon-entry.js',
|
||||
imports: [],
|
||||
isEntry: true,
|
||||
name: 'daemon-entry'
|
||||
} as Rollup.OutputChunk
|
||||
'daemon-entry.js': outputChunk('daemon-entry', code)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,15 +191,7 @@ describe('worker thread entry guard', () => {
|
||||
}
|
||||
|
||||
function workerChunk(name: string, code: string, imports: string[] = []): Rollup.OutputChunk {
|
||||
return {
|
||||
type: 'chunk',
|
||||
code,
|
||||
dynamicImports: [],
|
||||
fileName: `${name}.js`,
|
||||
imports,
|
||||
isEntry: true,
|
||||
name
|
||||
} as Rollup.OutputChunk
|
||||
return outputChunk(name, code, imports)
|
||||
}
|
||||
|
||||
it('rejects an Electron require reachable from a worker entry', () => {
|
||||
@@ -214,15 +225,7 @@ describe('worker thread entry guard', () => {
|
||||
'require("./chunks/shared.js")',
|
||||
['chunks/shared.js']
|
||||
),
|
||||
'chunks/shared.js': {
|
||||
type: 'chunk',
|
||||
code: 'require("electron")',
|
||||
dynamicImports: [],
|
||||
fileName: 'chunks/shared.js',
|
||||
imports: [],
|
||||
isEntry: false,
|
||||
name: 'shared'
|
||||
} as Rollup.OutputChunk
|
||||
'chunks/shared.js': outputChunk('chunks/shared', 'require("electron")', [], false)
|
||||
} as Rollup.OutputBundle
|
||||
|
||||
expect(() => runWorkerWriteBundle(plugin, bundle)).toThrow('chunks/shared.js')
|
||||
|
||||
@@ -2,18 +2,45 @@ import { spawn } from 'node:child_process'
|
||||
import { availableParallelism } from 'node:os'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
// The three projects overlap heavily in src/shared but have no build dependency on
|
||||
// each other, so tsc can check them concurrently instead of in a `&&` chain.
|
||||
const projects = ['tsconfig.node.json', 'tsconfig.tc.cli.json', 'tsconfig.tc.web.json']
|
||||
const repoRoot = fileURLToPath(new URL('../..', import.meta.url))
|
||||
const tsc = fileURLToPath(new URL('../../node_modules/typescript/bin/tsc', import.meta.url))
|
||||
|
||||
// Why serialize on a single-core runner: three tsc processes there thrash rather than overlap.
|
||||
const concurrent = availableParallelism() > 1
|
||||
// The projects overlap but have no build dependency, so they can check concurrently.
|
||||
const projects = [
|
||||
{
|
||||
name: 'tsconfig.node.json',
|
||||
args: [tsc, '--noEmit', '-p', 'config/tsconfig.node.json']
|
||||
},
|
||||
{
|
||||
name: 'tsconfig.tc.cli.json',
|
||||
args: [tsc, '--noEmit', '-p', 'config/tsconfig.tc.cli.json']
|
||||
},
|
||||
{
|
||||
name: 'tsconfig.tc.web.json',
|
||||
args: [tsc, '--noEmit', '-p', 'config/tsconfig.tc.web.json']
|
||||
},
|
||||
{
|
||||
name: 'tsconfig.tooling.json',
|
||||
args: [tsc, '--noEmit', '-p', 'config/tsconfig.tooling.json']
|
||||
},
|
||||
{
|
||||
name: 'tsconfig.e2e.json',
|
||||
args: [
|
||||
'config/scripts/typecheck-diagnostic-baseline.mjs',
|
||||
'--project',
|
||||
'config/tsconfig.e2e.json',
|
||||
'--baseline',
|
||||
'config/typecheck-e2e-diagnostics.json'
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
// Keep the original three-process ceiling; each additional project holds a full compiler graph.
|
||||
const maxConcurrent = Math.min(availableParallelism(), 3)
|
||||
|
||||
function checkProject(project) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn(process.execPath, [tsc, '--noEmit', '-p', `config/${project}`], {
|
||||
const child = spawn(process.execPath, project.args, {
|
||||
cwd: repoRoot,
|
||||
stdio: 'inherit'
|
||||
})
|
||||
@@ -21,9 +48,9 @@ function checkProject(project) {
|
||||
child.on('error', reject)
|
||||
child.on('exit', (code, signal) => {
|
||||
if (signal) {
|
||||
reject(new Error(`tsc ${project} exited with signal ${signal}`))
|
||||
reject(new Error(`${project.name} exited with signal ${signal}`))
|
||||
} else if (code !== 0) {
|
||||
reject(new Error(`tsc ${project} exited with code ${code}`))
|
||||
reject(new Error(`${project.name} exited with code ${code}`))
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
@@ -31,12 +58,12 @@ function checkProject(project) {
|
||||
})
|
||||
}
|
||||
|
||||
let failures = []
|
||||
if (concurrent) {
|
||||
const results = await Promise.allSettled(projects.map(checkProject))
|
||||
failures = results.filter((result) => result.status === 'rejected').map((result) => result.reason)
|
||||
} else {
|
||||
for (const project of projects) {
|
||||
const failures = []
|
||||
let nextProject = 0
|
||||
async function runProjectWorker() {
|
||||
while (nextProject < projects.length) {
|
||||
const project = projects[nextProject]
|
||||
nextProject += 1
|
||||
try {
|
||||
await checkProject(project)
|
||||
} catch (error) {
|
||||
@@ -44,6 +71,9 @@ if (concurrent) {
|
||||
}
|
||||
}
|
||||
}
|
||||
await Promise.all(
|
||||
Array.from({ length: Math.min(maxConcurrent, projects.length) }, runProjectWorker)
|
||||
)
|
||||
|
||||
if (failures.length > 0) {
|
||||
for (const failure of failures) {
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { readFile, writeFile } from 'node:fs/promises'
|
||||
import { relative, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const repoRoot = fileURLToPath(new URL('../..', import.meta.url))
|
||||
|
||||
function parseArgs(argv) {
|
||||
const options = { write: false }
|
||||
for (let index = 0; index < argv.length; index += 1) {
|
||||
const value = argv[index]
|
||||
if (value === '--write') {
|
||||
options.write = true
|
||||
continue
|
||||
}
|
||||
const next = argv[index + 1]
|
||||
if (!next) {
|
||||
throw new Error(`Missing value for ${value}`)
|
||||
}
|
||||
if (value === '--project') {
|
||||
options.project = next
|
||||
} else if (value === '--baseline') {
|
||||
options.baseline = next
|
||||
} else if (value === '--tsc') {
|
||||
options.tsc = next
|
||||
} else {
|
||||
throw new Error(`Unknown option: ${value}`)
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
if (!options.project || !options.baseline) {
|
||||
throw new Error('Usage: --project <tsconfig> --baseline <json> [--tsc <binary>] [--write]')
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
function sortDiagnostics(diagnostics) {
|
||||
return diagnostics.sort((left, right) =>
|
||||
JSON.stringify(left).localeCompare(JSON.stringify(right), 'en')
|
||||
)
|
||||
}
|
||||
|
||||
function parseDiagnostics(output) {
|
||||
const diagnostics = []
|
||||
let current = null
|
||||
for (const line of output.split(/\r?\n/)) {
|
||||
const match = line.match(/^(.*?)\((\d+),(\d+)\): error TS(\d+): (.*)$/)
|
||||
if (match) {
|
||||
current = {
|
||||
file: match[1].replaceAll('\\', '/'),
|
||||
line: Number(match[2]),
|
||||
column: Number(match[3]),
|
||||
code: Number(match[4]),
|
||||
message: match[5]
|
||||
}
|
||||
diagnostics.push(current)
|
||||
continue
|
||||
}
|
||||
const configMatch = line.match(/^error TS(\d+): (.*)$/)
|
||||
if (configMatch) {
|
||||
current = {
|
||||
file: null,
|
||||
line: null,
|
||||
column: null,
|
||||
code: Number(configMatch[1]),
|
||||
message: configMatch[2]
|
||||
}
|
||||
diagnostics.push(current)
|
||||
continue
|
||||
}
|
||||
if (current && line.trim().length > 0) {
|
||||
current.message += `\n${line.trimEnd()}`
|
||||
}
|
||||
}
|
||||
return sortDiagnostics(diagnostics)
|
||||
}
|
||||
|
||||
function collectDiagnostics(projectPath, tscPath) {
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[tscPath, '--pretty', 'false', '--noEmit', '-p', projectPath],
|
||||
{
|
||||
cwd: repoRoot,
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 64 * 1024 * 1024
|
||||
}
|
||||
)
|
||||
if (result.error) {
|
||||
throw result.error
|
||||
}
|
||||
if (result.signal) {
|
||||
throw new Error(`TypeScript exited with signal ${result.signal}`)
|
||||
}
|
||||
const output = `${result.stdout ?? ''}${result.stderr ?? ''}`
|
||||
const diagnostics = parseDiagnostics(output)
|
||||
if (result.status !== 0 && diagnostics.length === 0) {
|
||||
throw new Error(`TypeScript exited ${result.status} without parseable diagnostics:\n${output}`)
|
||||
}
|
||||
return diagnostics
|
||||
}
|
||||
|
||||
function diagnosticCounts(diagnostics) {
|
||||
const counts = new Map()
|
||||
for (const diagnostic of diagnostics) {
|
||||
const key = JSON.stringify(diagnostic)
|
||||
counts.set(key, (counts.get(key) ?? 0) + 1)
|
||||
}
|
||||
return counts
|
||||
}
|
||||
|
||||
function subtractDiagnostics(left, right) {
|
||||
const remaining = diagnosticCounts(right)
|
||||
return left.filter((diagnostic) => {
|
||||
const key = JSON.stringify(diagnostic)
|
||||
const count = remaining.get(key) ?? 0
|
||||
if (count === 0) {
|
||||
return true
|
||||
}
|
||||
remaining.set(key, count - 1)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
function formatDiagnostic(diagnostic) {
|
||||
const location = diagnostic.file
|
||||
? `${diagnostic.file}:${diagnostic.line}:${diagnostic.column}`
|
||||
: '<config>'
|
||||
return `${location} TS${diagnostic.code}: ${diagnostic.message}`
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const options = parseArgs(process.argv.slice(2))
|
||||
const projectPath = resolve(repoRoot, options.project)
|
||||
const baselinePath = resolve(repoRoot, options.baseline)
|
||||
const tscPath = resolve(repoRoot, options.tsc ?? 'node_modules/typescript/bin/tsc')
|
||||
const diagnostics = collectDiagnostics(projectPath, tscPath)
|
||||
|
||||
if (options.write) {
|
||||
const baseline = { version: 1, diagnostics }
|
||||
await writeFile(baselinePath, `${JSON.stringify(baseline, null, 2)}\n`)
|
||||
console.log(`Wrote ${diagnostics.length} diagnostics to ${relative(repoRoot, baselinePath)}`)
|
||||
return
|
||||
}
|
||||
|
||||
const baseline = JSON.parse(await readFile(baselinePath, 'utf8'))
|
||||
if (baseline.version !== 1 || !Array.isArray(baseline.diagnostics)) {
|
||||
throw new Error(`Invalid diagnostic baseline: ${relative(repoRoot, baselinePath)}`)
|
||||
}
|
||||
const unexpected = subtractDiagnostics(diagnostics, baseline.diagnostics)
|
||||
const stale = subtractDiagnostics(baseline.diagnostics, diagnostics)
|
||||
if (unexpected.length === 0 && stale.length === 0) {
|
||||
console.log(`${options.project}: ${diagnostics.length} known diagnostics, no drift`)
|
||||
return
|
||||
}
|
||||
if (unexpected.length > 0) {
|
||||
console.error(`Unexpected diagnostics (${unexpected.length}):`)
|
||||
for (const diagnostic of unexpected) {
|
||||
console.error(formatDiagnostic(diagnostic))
|
||||
}
|
||||
}
|
||||
if (stale.length > 0) {
|
||||
console.error(`Stale allowlist entries (${stale.length}); remove fixed diagnostics:`)
|
||||
for (const diagnostic of stale) {
|
||||
console.error(formatDiagnostic(diagnostic))
|
||||
}
|
||||
}
|
||||
process.exitCode = 1
|
||||
}
|
||||
|
||||
await main()
|
||||
@@ -0,0 +1,64 @@
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
|
||||
const repoRoot = fileURLToPath(new URL('../..', import.meta.url))
|
||||
const script = join(repoRoot, 'config/scripts/typecheck-diagnostic-baseline.mjs')
|
||||
const tsc = join(repoRoot, 'node_modules/typescript/bin/tsc')
|
||||
let fixtureDir
|
||||
|
||||
afterEach(() => {
|
||||
if (fixtureDir) {
|
||||
rmSync(fixtureDir, { recursive: true, force: true })
|
||||
}
|
||||
fixtureDir = undefined
|
||||
})
|
||||
|
||||
function createFixture(source) {
|
||||
fixtureDir = mkdtempSync(join(tmpdir(), 'orca-typecheck-baseline-'))
|
||||
writeFileSync(
|
||||
join(fixtureDir, 'tsconfig.json'),
|
||||
JSON.stringify({ compilerOptions: { strict: true, noEmit: true }, include: ['source.ts'] })
|
||||
)
|
||||
writeFileSync(join(fixtureDir, 'source.ts'), source)
|
||||
return {
|
||||
baseline: join(fixtureDir, 'baseline.json'),
|
||||
project: join(fixtureDir, 'tsconfig.json'),
|
||||
source: join(fixtureDir, 'source.ts')
|
||||
}
|
||||
}
|
||||
|
||||
function runBaseline(paths, extraArgs = []) {
|
||||
return spawnSync(
|
||||
process.execPath,
|
||||
[script, '--project', paths.project, '--baseline', paths.baseline, '--tsc', tsc, ...extraArgs],
|
||||
{ cwd: repoRoot, encoding: 'utf8' }
|
||||
)
|
||||
}
|
||||
|
||||
describe('typecheck diagnostic baseline', () => {
|
||||
it('accepts only the exact recorded diagnostics', () => {
|
||||
const paths = createFixture("const value: number = 'bad'\n")
|
||||
expect(runBaseline(paths, ['--write']).status).toBe(0)
|
||||
expect(JSON.parse(readFileSync(paths.baseline, 'utf8')).diagnostics).toHaveLength(1)
|
||||
expect(runBaseline(paths).status).toBe(0)
|
||||
|
||||
writeFileSync(paths.source, 'const value: number = 1\n')
|
||||
const stale = runBaseline(paths)
|
||||
expect(stale.status).toBe(1)
|
||||
expect(stale.stderr).toContain('Stale allowlist entries (1)')
|
||||
})
|
||||
|
||||
it('rejects a diagnostic added after a clean baseline', () => {
|
||||
const paths = createFixture('const value: number = 1\n')
|
||||
expect(runBaseline(paths, ['--write']).status).toBe(0)
|
||||
|
||||
writeFileSync(paths.source, "const value: number = 'bad'\n")
|
||||
const unexpected = runBaseline(paths)
|
||||
expect(unexpected.status).toBe(1)
|
||||
expect(unexpected.stderr).toContain('Unexpected diagnostics (1)')
|
||||
})
|
||||
})
|
||||
@@ -1,10 +1,6 @@
|
||||
{
|
||||
// NOT ENFORCED YET, and deliberately so: tests/ has never been typechecked, so this currently
|
||||
// reports ~198 errors across ~94 files. Wiring it into `typecheck` or CI today would just paint
|
||||
// the whole build red. It earns its place anyway — pointing it at the SSH reconnect spec found
|
||||
// two real bugs a passing suite had hidden (a targetId read off the wrong type, and a required
|
||||
// argument omitted), because that spec had never actually executed. Run `pnpm typecheck:e2e`,
|
||||
// fix a file, repeat; when the count reaches zero, add it to the `typecheck` chain.
|
||||
// Enforced by `pnpm typecheck`; the exact pre-existing diagnostics are ratcheted in
|
||||
// typecheck-e2e-diagnostics.json so new errors fail while the backlog shrinks.
|
||||
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
|
||||
// Why: specs import renderer and main modules directly, so the ambient declarations those
|
||||
// modules rely on (vite client types, build-time defines) have to be in the program too.
|
||||
@@ -16,6 +12,8 @@
|
||||
],
|
||||
"exclude": ["../tests/e2e/.cross-version-checkouts/**"],
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": "./tsconfig.e2e.tsbuildinfo",
|
||||
// Why: specs run browser code inside page.evaluate, so the DOM lib has to be visible here
|
||||
// even though the harness itself is Node.
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
|
||||
"include": ["../config/**/*.ts", "../*.ts", "../src/types/**/*"],
|
||||
"compilerOptions": {
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": "./tsconfig.tooling.tsbuildinfo",
|
||||
"paths": {
|
||||
"@renderer/*": ["../src/renderer/src/*"],
|
||||
"@/*": ["../src/renderer/src/*"]
|
||||
},
|
||||
"types": ["electron-vite/node"]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -9,7 +9,7 @@
|
||||
"ios": "expo run:ios",
|
||||
"postinstall": "node scripts/build-terminal-webview-engine.mjs && node scripts/build-mermaid-webview-engine.mjs",
|
||||
"test": "vitest run",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"typecheck": "node ../config/scripts/typecheck-diagnostic-baseline.mjs --project mobile/tsconfig.json --baseline mobile/typecheck-test-diagnostics.json --tsc mobile/node_modules/typescript/bin/tsc",
|
||||
"lint": "oxlint",
|
||||
"format": "oxfmt --write .",
|
||||
"format:check": "oxfmt --check .",
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
import { createElement } from 'react'
|
||||
import { Text } from 'react-native'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { ConnectionVerdict } from '../transport/connection-health'
|
||||
@@ -36,7 +37,7 @@ const loaded: HostWorktreeInfo = {
|
||||
countsProvenAt: Date.now()
|
||||
}
|
||||
|
||||
describe('MobileHostCard', () => {
|
||||
describe('MobileHostCard actions', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
afterEach(() => {
|
||||
@@ -69,7 +70,7 @@ describe('MobileHostCard', () => {
|
||||
)
|
||||
})
|
||||
return renderer!.root
|
||||
.findAllByType('Text')
|
||||
.findAllByType(Text)
|
||||
.flatMap((node) => node.children.filter((child) => typeof child === 'string'))
|
||||
}
|
||||
|
||||
@@ -13,6 +13,5 @@
|
||||
"zod": ["./node_modules/zod"]
|
||||
}
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["**/*.test.ts", "**/*.test.tsx"]
|
||||
"include": ["**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -48,7 +48,7 @@
|
||||
"typecheck:node": "tsc --noEmit -p config/tsconfig.node.json",
|
||||
"typecheck:cli": "tsc --noEmit -p config/tsconfig.tc.cli.json",
|
||||
"typecheck:web": "tsc --noEmit -p config/tsconfig.tc.web.json",
|
||||
"typecheck:e2e": "tsc --noEmit -p config/tsconfig.e2e.json",
|
||||
"typecheck:e2e": "node config/scripts/typecheck-diagnostic-baseline.mjs --project config/tsconfig.e2e.json --baseline config/typecheck-e2e-diagnostics.json",
|
||||
"typecheck": "node config/scripts/run-typecheck-projects-in-parallel.mjs",
|
||||
"typecheck:tsc:node": "tsc --noEmit -p config/tsconfig.node.json --composite false",
|
||||
"typecheck:tsc:cli": "tsc --noEmit -p config/tsconfig.cli.json --composite false",
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ function gitUsernamePrefixSettings(): GlobalSettings {
|
||||
}
|
||||
}
|
||||
|
||||
describe('GitPane branch prefix feedback', () => {
|
||||
describe('GitPane branch-prefix feedback', () => {
|
||||
it('previews the resulting branch name and drops a redundant trailing slash', () => {
|
||||
const html = renderGitPane(customPrefixSettings('team/'))
|
||||
expect(html).toContain('team/feature')
|
||||
+1
-1
@@ -35,7 +35,7 @@ afterEach(() => {
|
||||
document.body.replaceChildren()
|
||||
})
|
||||
|
||||
describe('useWorktreeListScrollToTop', () => {
|
||||
describe('useWorktreeListScrollToTop pointer drag', () => {
|
||||
it('ignores fast programmatic scrolling', () => {
|
||||
const element = createScroller()
|
||||
let now = 0
|
||||
Reference in New Issue
Block a user