mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
test: batch lint fixtures and remove repeated runtime scan (#29286)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -1,25 +1,25 @@
|
||||
import { cpSync, mkdtempSync, rmSync } from 'node:fs'
|
||||
import { createRequire } from 'node:module'
|
||||
import path from 'node:path'
|
||||
import { afterAll, expect, it } from 'vitest'
|
||||
import { runOxlintPluginOnSource } from './oxlint-plugin-test-runner.mjs'
|
||||
import { afterAll, beforeAll, expect, it } from 'vitest'
|
||||
import { runOxlintPluginOnSources } from './oxlint-plugin-test-runner.mjs'
|
||||
|
||||
const directory = mkdtempSync(path.join(import.meta.dirname, '.anti-slop-scope-'))
|
||||
const source = path.dirname(createRequire(import.meta.url).resolve('oxlint-plugin-anti-slop'))
|
||||
cpSync(source, directory, { recursive: true })
|
||||
afterAll(() => rmSync(directory, { recursive: true, force: true }))
|
||||
|
||||
function lint(source, rule) {
|
||||
return runOxlintPluginOnSource({
|
||||
function lint(sources, rule) {
|
||||
return runOxlintPluginOnSources({
|
||||
pluginName: 'anti-slop',
|
||||
pluginPath: path.join(directory, 'index.ts'),
|
||||
rules: { [`anti-slop/${rule}`]: 'error' },
|
||||
source,
|
||||
sources,
|
||||
extension: 'ts'
|
||||
})
|
||||
}
|
||||
|
||||
it.each([
|
||||
const wideningCases = [
|
||||
['const value = {}; const wide: unknown = value; wide as { id: string };', 1],
|
||||
['const value = {}; const alias = value; const wide: unknown = alias; <{ id: string }>wide;', 1],
|
||||
['function parse(input: unknown) { return input as { id: string }; }', 0],
|
||||
@@ -30,11 +30,9 @@ it.each([
|
||||
0
|
||||
],
|
||||
['const value = {}; const wide: unknown = value; wide as unknown;', 0]
|
||||
])('preserves widening evidence and lexical boundaries: %s', (source, count) => {
|
||||
expect(lint(source, 'no-widen-then-assert')).toHaveLength(count)
|
||||
})
|
||||
]
|
||||
|
||||
it.each([
|
||||
const mockCases = [
|
||||
["import { vi as test } from 'vitest'; test.mock('module');", 1],
|
||||
["import { jest as test } from '@jest/globals'; test['doMock']('module');", 1],
|
||||
["jest.unstable_mockModule('module', () => ({}));", 1],
|
||||
@@ -43,6 +41,27 @@ it.each([
|
||||
["const unrelated = { mock() {} }; unrelated.mock('module');", 0],
|
||||
["const vi = { mock() {} }; vi.mock('module');", 0],
|
||||
["import { vi as test } from 'vitest'; function run(test) { test.mock('module'); }", 0]
|
||||
])('preserves mock method and owner resolution: %s', (source, count) => {
|
||||
expect(lint(source, 'no-module-mocking')).toHaveLength(count)
|
||||
]
|
||||
|
||||
const wideningDiagnostics = new Map()
|
||||
const mockDiagnostics = new Map()
|
||||
beforeAll(() => {
|
||||
for (const [cases, rule, results] of [
|
||||
[wideningCases, 'no-widen-then-assert', wideningDiagnostics],
|
||||
[mockCases, 'no-module-mocking', mockDiagnostics]
|
||||
]) {
|
||||
const sources = cases.map(([source]) => source)
|
||||
lint(sources, rule).forEach((diagnostics, index) => results.set(sources[index], diagnostics))
|
||||
}
|
||||
})
|
||||
|
||||
it.each(wideningCases)(
|
||||
'preserves widening evidence and lexical boundaries: %s',
|
||||
(source, count) => {
|
||||
expect(wideningDiagnostics.get(source)).toHaveLength(count)
|
||||
}
|
||||
)
|
||||
|
||||
it.each(mockCases)('preserves mock method and owner resolution: %s', (source, count) => {
|
||||
expect(mockDiagnostics.get(source)).toHaveLength(count)
|
||||
})
|
||||
|
||||
@@ -241,10 +241,4 @@ describe('the command-line check', () => {
|
||||
expect(await main({ plugins: [addElectron] })).toBe(1)
|
||||
expect(error.mock.calls.join('\n')).toContain('+ src/main/native-chat/transcript-read-cache.ts')
|
||||
}, 120_000)
|
||||
|
||||
// Why real: the value of this gate is the transitive edges, which a fixture cannot model.
|
||||
it('passes on the tree as it is, with nothing reachable importing electron', async () => {
|
||||
vi.spyOn(console, 'log').mockImplementation(() => {})
|
||||
expect(await main()).toBe(0)
|
||||
}, 120_000)
|
||||
})
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import path from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { runOxlintPluginOnSource } from './oxlint-plugin-test-runner.mjs'
|
||||
import { beforeAll, describe, expect, it } from 'vitest'
|
||||
import { runOxlintPluginOnSources } from './oxlint-plugin-test-runner.mjs'
|
||||
|
||||
const pluginPath = path.resolve('config/oxlint-plugins/ime-enter-guard.mjs')
|
||||
|
||||
function lintSource(source) {
|
||||
return runOxlintPluginOnSource({
|
||||
function lintSources(sources) {
|
||||
return runOxlintPluginOnSources({
|
||||
pluginName: 'ime-enter-guard',
|
||||
pluginPath,
|
||||
source,
|
||||
sources,
|
||||
rules: { 'ime-enter-guard/require-ime-guard': 'warn' }
|
||||
})
|
||||
}
|
||||
@@ -118,12 +118,20 @@ const accepted = [
|
||||
]
|
||||
]
|
||||
|
||||
const diagnosticsBySource = new Map()
|
||||
beforeAll(() => {
|
||||
const sources = [...violations, ...accepted].map(([, source]) => source)
|
||||
lintSources(sources).forEach((diagnostics, index) => {
|
||||
diagnosticsBySource.set(sources[index], diagnostics)
|
||||
})
|
||||
})
|
||||
|
||||
describe('IME Enter guard Oxlint plugin', () => {
|
||||
it.each(violations)('reports %s', (_name, source) => {
|
||||
expect(lintSource(source)).toHaveLength(1)
|
||||
expect(diagnosticsBySource.get(source)).toHaveLength(1)
|
||||
})
|
||||
|
||||
it.each(accepted)('accepts %s', (_name, source) => {
|
||||
expect(lintSource(source)).toEqual([])
|
||||
expect(diagnosticsBySource.get(source)).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import path from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { runOxlintPluginOnSource } from './oxlint-plugin-test-runner.mjs'
|
||||
import { beforeAll, describe, expect, it } from 'vitest'
|
||||
import { runOxlintPluginOnSources } from './oxlint-plugin-test-runner.mjs'
|
||||
|
||||
const pluginPath = path.resolve('config/oxlint-plugins/native-chat-design-tokens.mjs')
|
||||
|
||||
function lintSource(source) {
|
||||
return runOxlintPluginOnSource({
|
||||
function lintSources(sources) {
|
||||
return runOxlintPluginOnSources({
|
||||
pluginName: 'native-chat-design-tokens',
|
||||
pluginPath,
|
||||
source,
|
||||
sources,
|
||||
rules: {
|
||||
'native-chat-design-tokens/no-arbitrary-font-size': 'warn',
|
||||
'native-chat-design-tokens/no-softened-foreground': 'warn',
|
||||
@@ -55,12 +55,20 @@ const accepted = [
|
||||
]
|
||||
]
|
||||
|
||||
const diagnosticsBySource = new Map()
|
||||
beforeAll(() => {
|
||||
const sources = [...violations, ...accepted].map(([, source]) => source)
|
||||
lintSources(sources).forEach((diagnostics, index) => {
|
||||
diagnosticsBySource.set(sources[index], diagnostics)
|
||||
})
|
||||
})
|
||||
|
||||
describe('native-chat-design-tokens oxlint plugin', () => {
|
||||
it.each(violations)('reports %s', (_name, source) => {
|
||||
expect(lintSource(source)).toHaveLength(1)
|
||||
expect(diagnosticsBySource.get(source)).toHaveLength(1)
|
||||
})
|
||||
|
||||
it.each(accepted)('accepts %s', (_name, source) => {
|
||||
expect(lintSource(source)).toEqual([])
|
||||
expect(diagnosticsBySource.get(source)).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,19 +10,25 @@ const oxlintPackageDirectory = path.dirname(
|
||||
)
|
||||
const oxlintPath = path.join(oxlintPackageDirectory, 'bin', 'oxlint')
|
||||
|
||||
export function runOxlintPluginOnSource({
|
||||
export function runOxlintPluginOnSource({ source, ...options }) {
|
||||
return runOxlintPluginOnSources({ ...options, sources: [source] })[0]
|
||||
}
|
||||
|
||||
export function runOxlintPluginOnSources({
|
||||
pluginName,
|
||||
pluginPath,
|
||||
rules,
|
||||
source,
|
||||
sources,
|
||||
extension = 'tsx'
|
||||
}) {
|
||||
const directory = mkdtempSync(path.join(tmpdir(), `orca-${pluginName}-lint-`))
|
||||
const sourcePath = path.join(directory, `sample.${extension}`)
|
||||
const sourcePaths = sources.map((_, index) =>
|
||||
path.join(directory, `sample-${index}.${extension}`)
|
||||
)
|
||||
const configPath = path.join(directory, 'oxlint.json')
|
||||
|
||||
try {
|
||||
writeFileSync(sourcePath, source)
|
||||
sources.forEach((source, index) => writeFileSync(sourcePaths[index], source))
|
||||
writeFileSync(
|
||||
configPath,
|
||||
JSON.stringify({
|
||||
@@ -42,7 +48,7 @@ export function runOxlintPluginOnSource({
|
||||
)
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[oxlintPath, '--config', configPath, '--format', 'json', sourcePath],
|
||||
[oxlintPath, '--config', configPath, '--format', 'json', ...sourcePaths],
|
||||
{ encoding: 'utf8' }
|
||||
)
|
||||
if (result.error) {
|
||||
@@ -51,7 +57,16 @@ export function runOxlintPluginOnSource({
|
||||
if (!result.stdout.trim()) {
|
||||
throw new Error(result.stderr || `${pluginName} did not produce Oxlint output`)
|
||||
}
|
||||
return JSON.parse(result.stdout).diagnostics
|
||||
const diagnostics = JSON.parse(result.stdout).diagnostics
|
||||
const byFile = new Map(sourcePaths.map((file) => [file, []]))
|
||||
for (const diagnostic of diagnostics) {
|
||||
const findings = byFile.get(path.resolve(diagnostic.filename))
|
||||
if (!findings) {
|
||||
throw new Error(`Unexpected Oxlint diagnostic file: ${diagnostic.filename}`)
|
||||
}
|
||||
findings.push(diagnostic)
|
||||
}
|
||||
return sourcePaths.map((file) => byFile.get(file))
|
||||
} finally {
|
||||
rmSync(directory, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import path from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { runOxlintPluginOnSource } from './oxlint-plugin-test-runner.mjs'
|
||||
import { beforeAll, describe, expect, it } from 'vitest'
|
||||
import { runOxlintPluginOnSources } from './oxlint-plugin-test-runner.mjs'
|
||||
|
||||
const pluginPath = path.resolve('config/oxlint-plugins/quadratic-buffer-concat.mjs')
|
||||
|
||||
function lintSource(source) {
|
||||
return runOxlintPluginOnSource({
|
||||
function lintSources(sources) {
|
||||
return runOxlintPluginOnSources({
|
||||
pluginName: 'quadratic-buffer-concat',
|
||||
pluginPath,
|
||||
source,
|
||||
sources,
|
||||
extension: 'ts',
|
||||
rules: {
|
||||
'quadratic-buffer-concat/no-loop-carried-concat': 'warn'
|
||||
@@ -99,15 +99,23 @@ const accepted = [
|
||||
['no Buffer concat', 'export const x = 1']
|
||||
]
|
||||
|
||||
const diagnosticsBySource = new Map()
|
||||
beforeAll(() => {
|
||||
const sources = [...violations, ...accepted].map(([, source]) => source)
|
||||
lintSources(sources).forEach((diagnostics, index) => {
|
||||
diagnosticsBySource.set(sources[index], diagnostics)
|
||||
})
|
||||
})
|
||||
|
||||
describe('quadratic Buffer.concat Oxlint plugin', () => {
|
||||
it.each(violations)('reports %s', (_name, source, accumulator) => {
|
||||
const diagnostics = lintSource(source)
|
||||
const diagnostics = diagnosticsBySource.get(source)
|
||||
|
||||
expect(diagnostics).toHaveLength(1)
|
||||
expect(diagnostics[0].message).toContain(accumulator)
|
||||
})
|
||||
|
||||
it.each(accepted)('accepts %s', (_name, source) => {
|
||||
expect(lintSource(source)).toEqual([])
|
||||
expect(diagnosticsBySource.get(source)).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import path from 'node:path'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { beforeAll, describe, expect, it } from 'vitest'
|
||||
import { plainClassName } from '../oxlint-plugins/renderer-scrollbar-style.mjs'
|
||||
import { runOxlintPluginOnSource } from './oxlint-plugin-test-runner.mjs'
|
||||
import { runOxlintPluginOnSources } from './oxlint-plugin-test-runner.mjs'
|
||||
|
||||
const pluginPath = path.resolve('config/oxlint-plugins/renderer-scrollbar-style.mjs')
|
||||
|
||||
function lintSource(source) {
|
||||
return runOxlintPluginOnSource({
|
||||
function lintSources(sources) {
|
||||
return runOxlintPluginOnSources({
|
||||
pluginName: 'renderer-scrollbar-style',
|
||||
pluginPath,
|
||||
source,
|
||||
sources,
|
||||
rules: {
|
||||
'renderer-scrollbar-style/require-styled-vertical-scrollbar': 'warn'
|
||||
}
|
||||
@@ -94,13 +94,21 @@ const accepted = [
|
||||
]
|
||||
]
|
||||
|
||||
const diagnosticsBySource = new Map()
|
||||
beforeAll(() => {
|
||||
const sources = [...violations, ...accepted].map(([, source]) => source)
|
||||
lintSources(sources).forEach((diagnostics, index) => {
|
||||
diagnosticsBySource.set(sources[index], diagnostics)
|
||||
})
|
||||
})
|
||||
|
||||
describe('renderer scrollbar style Oxlint plugin', () => {
|
||||
it.each(violations)('reports %s', (_name, source) => {
|
||||
expect(lintSource(source)).toHaveLength(1)
|
||||
expect(diagnosticsBySource.get(source)).toHaveLength(1)
|
||||
})
|
||||
|
||||
it.each(accepted)('accepts %s', (_name, source) => {
|
||||
expect(lintSource(source)).toEqual([])
|
||||
expect(diagnosticsBySource.get(source)).toEqual([])
|
||||
})
|
||||
|
||||
it.each([
|
||||
|
||||
Reference in New Issue
Block a user