mirror of
https://github.com/stablyai/orca.git
synced 2026-11-01 09:37:33 +00:00
Adds an oxlint plugin rule that reports Enter-key handlers on raw input, textarea, and contentEditable elements (and keydown listeners on text-field receivers) unless the handler reaches the shared IME guard. Also routes the automation run history window listener through isImeOwnedKeyboardEvent, whose previous nativeEvent check never fired for native events.
130 lines
4.0 KiB
JavaScript
130 lines
4.0 KiB
JavaScript
import path from 'node:path'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { runOxlintPluginOnSource } from './oxlint-plugin-test-runner.mjs'
|
|
|
|
const pluginPath = path.resolve('config/oxlint-plugins/ime-enter-guard.mjs')
|
|
|
|
function lintSource(source) {
|
|
return runOxlintPluginOnSource({
|
|
pluginName: 'ime-enter-guard',
|
|
pluginPath,
|
|
source,
|
|
rules: { 'ime-enter-guard/require-ime-guard': 'warn' }
|
|
})
|
|
}
|
|
|
|
const violations = [
|
|
[
|
|
'inline Enter on a raw input',
|
|
"export const X = () => <input onKeyDown={(e) => { if (e.key === 'Enter') go() }} />"
|
|
],
|
|
[
|
|
'Enter on a raw textarea',
|
|
"export const X = () => <textarea onKeyDown={(e) => e.key === 'Enter' && send()} />"
|
|
],
|
|
[
|
|
'Enter by code on a text-typed input',
|
|
'export const X = () => <input type="search" onKeyDown={(e) => { if (e.code === "NumpadEnter") go() }} />'
|
|
],
|
|
[
|
|
'Enter by keyCode',
|
|
'export const X = () => <input onKeyPress={(e) => { if (e.keyCode === 13) go() }} />'
|
|
],
|
|
[
|
|
'Enter on a contentEditable element',
|
|
"export const X = () => <div contentEditable onKeyDown={(e) => { if (e.key === 'Enter') go() }} />"
|
|
],
|
|
[
|
|
'named local handler',
|
|
`export function X() {
|
|
const onKeyDown = (e) => { if (e.key === 'Enter') submit() }
|
|
return <input onKeyDown={onKeyDown} />
|
|
}`
|
|
],
|
|
[
|
|
'useCallback handler',
|
|
`export function X() {
|
|
const handleKeyDown = useCallback((e) => { if (e.key === 'Enter') submit() }, [])
|
|
return <input onKeyDownCapture={handleKeyDown} />
|
|
}`
|
|
],
|
|
[
|
|
'keydown listener on an input ref',
|
|
`export function attach(inputRef) {
|
|
inputRef.current.addEventListener('keydown', (e) => { if (e.key === 'Enter') go() })
|
|
}`
|
|
],
|
|
[
|
|
'keydown listener on a created textarea',
|
|
`export function attach() {
|
|
const el = document.createElement('textarea')
|
|
el.addEventListener('keydown', (e) => { if (e.key === 'Enter') go() })
|
|
}`
|
|
]
|
|
]
|
|
|
|
const accepted = [
|
|
[
|
|
'shared guard inline',
|
|
"export const X = () => <input onKeyDown={(e) => { if (isImeCompositionKeyDown(e)) return; if (e.key === 'Enter') go() }} />"
|
|
],
|
|
[
|
|
'shared guard in a called helper',
|
|
`function ignoreIme(e) { return isImeOwnedKeyboardEvent(e) }
|
|
export function X() {
|
|
const onKeyDown = (e) => { if (ignoreIme(e)) return; if (e.key === 'Enter') go() }
|
|
return <textarea onKeyDown={onKeyDown} />
|
|
}`
|
|
],
|
|
[
|
|
'gesture ownership guard',
|
|
"export const X = () => <input onKeyDown={(e) => { if (ime.ownsKeyDown(e)) return; if (e.key === 'Enter') go() }} />"
|
|
],
|
|
[
|
|
'element tracks composition itself',
|
|
"export const X = () => <input onCompositionStart={start} onKeyDown={(e) => { if (e.key === 'Enter') go() }} />"
|
|
],
|
|
[
|
|
'Input primitive',
|
|
"export const X = () => <Input onKeyDown={(e) => { if (e.key === 'Enter') go() }} />"
|
|
],
|
|
[
|
|
'ImeTextarea',
|
|
"export const X = () => <ImeTextarea onKeyDown={(e) => { if (e.key === 'Enter') go() }} />"
|
|
],
|
|
[
|
|
'non-text input type',
|
|
'export const X = () => <input type="checkbox" onKeyDown={(e) => { if (e.key === "Enter") toggle() }} />'
|
|
],
|
|
[
|
|
'list row button',
|
|
"export const X = () => <button onKeyDown={(e) => { if (e.key === 'Enter') open() }} />"
|
|
],
|
|
[
|
|
'contentEditable disabled',
|
|
"export const X = () => <div contentEditable={false} onKeyDown={(e) => { if (e.key === 'Enter') go() }} />"
|
|
],
|
|
[
|
|
'non-Enter key handling',
|
|
"export const X = () => <input onKeyDown={(e) => { if (e.key === 'Escape') cancel() }} />"
|
|
],
|
|
[
|
|
'handler passed in from props',
|
|
'export const X = ({ onKeyDown }) => <input onKeyDown={onKeyDown} />'
|
|
],
|
|
[
|
|
'window keydown listener',
|
|
"export const attach = () => window.addEventListener('keydown', (e) => { if (e.key === 'Enter') go() })"
|
|
]
|
|
]
|
|
|
|
describe('IME Enter guard Oxlint plugin', () => {
|
|
it.each(violations)('reports %s', (_name, source) => {
|
|
expect(lintSource(source)).toHaveLength(1)
|
|
})
|
|
|
|
it.each(accepted)('accepts %s', (_name, source) => {
|
|
expect(lintSource(source)).toEqual([])
|
|
})
|
|
})
|