94 lines
3.2 KiB
TypeScript
94 lines
3.2 KiB
TypeScript
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||
|
|
import { focusEditorWithRetries } from './editorFocusUtils'
|
||
|
|
|
||
|
|
describe('editorFocusUtils extra coverage', () => {
|
||
|
|
beforeEach(() => {
|
||
|
|
document.body.innerHTML = ''
|
||
|
|
})
|
||
|
|
|
||
|
|
afterEach(() => {
|
||
|
|
vi.restoreAllMocks()
|
||
|
|
document.body.innerHTML = ''
|
||
|
|
})
|
||
|
|
|
||
|
|
it('uses window focus and selection fallback before logging successful focus timing', () => {
|
||
|
|
const editable = document.createElement('div')
|
||
|
|
editable.className = 'ProseMirror'
|
||
|
|
editable.contentEditable = 'true'
|
||
|
|
editable.setAttribute('contenteditable', 'true')
|
||
|
|
editable.tabIndex = -1
|
||
|
|
Object.defineProperty(editable, 'isContentEditable', { configurable: true, value: true })
|
||
|
|
document.body.appendChild(editable)
|
||
|
|
|
||
|
|
const realFocus = HTMLElement.prototype.focus.bind(editable)
|
||
|
|
let focusCalls = 0
|
||
|
|
vi.spyOn(editable, 'focus').mockImplementation(() => {
|
||
|
|
focusCalls += 1
|
||
|
|
if (focusCalls >= 2) realFocus()
|
||
|
|
})
|
||
|
|
|
||
|
|
const selection = {
|
||
|
|
removeAllRanges: vi.fn(),
|
||
|
|
addRange: vi.fn(),
|
||
|
|
} as unknown as Selection
|
||
|
|
|
||
|
|
vi.spyOn(window.navigator, 'userAgent', 'get').mockReturnValue('Mozilla/5.0')
|
||
|
|
const windowFocusSpy = vi.spyOn(window, 'focus').mockImplementation(() => {})
|
||
|
|
vi.spyOn(window, 'getSelection').mockReturnValue(selection)
|
||
|
|
vi.spyOn(performance, 'now').mockReturnValue(150)
|
||
|
|
const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {})
|
||
|
|
|
||
|
|
focusEditorWithRetries({ focus: vi.fn() }, false, 100)
|
||
|
|
|
||
|
|
expect(windowFocusSpy).toHaveBeenCalled()
|
||
|
|
expect(selection.removeAllRanges).toHaveBeenCalled()
|
||
|
|
expect(selection.addRange).toHaveBeenCalledTimes(1)
|
||
|
|
expect(debugSpy).toHaveBeenCalledWith('[perf] createNote → focus: 50.0ms')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('uses the fallback editable selector and treats mixed heading content as empty text', () => {
|
||
|
|
const wrapper = document.createElement('div')
|
||
|
|
wrapper.className = 'bn-editor'
|
||
|
|
const editable = document.createElement('div')
|
||
|
|
editable.contentEditable = 'true'
|
||
|
|
editable.setAttribute('contenteditable', 'true')
|
||
|
|
editable.tabIndex = -1
|
||
|
|
Object.defineProperty(editable, 'isContentEditable', { configurable: true, value: true })
|
||
|
|
wrapper.appendChild(editable)
|
||
|
|
document.body.appendChild(wrapper)
|
||
|
|
|
||
|
|
const realFocus = HTMLElement.prototype.focus.bind(editable)
|
||
|
|
vi.spyOn(editable, 'focus').mockImplementation(() => realFocus())
|
||
|
|
|
||
|
|
const rAF = vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => {
|
||
|
|
cb(0)
|
||
|
|
return 1
|
||
|
|
})
|
||
|
|
|
||
|
|
const setTextCursorPosition = vi.fn()
|
||
|
|
|
||
|
|
focusEditorWithRetries({
|
||
|
|
focus: vi.fn(),
|
||
|
|
document: [
|
||
|
|
{
|
||
|
|
id: 'title',
|
||
|
|
type: 'heading',
|
||
|
|
content: [{ kind: 'ignored' }, { type: 'text' }],
|
||
|
|
},
|
||
|
|
],
|
||
|
|
setTextCursorPosition,
|
||
|
|
}, true, undefined)
|
||
|
|
|
||
|
|
expect(rAF).toHaveBeenCalled()
|
||
|
|
expect(setTextCursorPosition).toHaveBeenCalledWith('title', 'start')
|
||
|
|
})
|
||
|
|
|
||
|
|
it('schedules another animation frame when nothing focusable is available yet', () => {
|
||
|
|
const rAF = vi.spyOn(window, 'requestAnimationFrame').mockImplementation(() => 1)
|
||
|
|
|
||
|
|
focusEditorWithRetries({ focus: vi.fn() }, false, undefined)
|
||
|
|
|
||
|
|
expect(rAF).toHaveBeenCalledTimes(1)
|
||
|
|
})
|
||
|
|
})
|