You need to sign in or sign up before continuing.
useScrollRestoration.test.js 3.32 KB
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'
import { useScrollRestoration } from '../useScrollRestoration'

const create_session_storage = () => {
    const store = {}
    return {
        store,
        setItem: vi.fn((key, value) => {
            store[key] = String(value)
        }),
        getItem: vi.fn((key) => {
            if (Object.prototype.hasOwnProperty.call(store, key)) return store[key]
            return null
        }),
        removeItem: vi.fn((key) => {
            delete store[key]
        }),
        clear: vi.fn(() => {
            Object.keys(store).forEach((key) => delete store[key])
        }),
    }
}

describe('useScrollRestoration', () => {
    const original_window = globalThis.window
    const original_session_storage = globalThis.sessionStorage

    beforeEach(() => {
        const session_storage = create_session_storage()
        vi.stubGlobal('sessionStorage', session_storage)

        const window_stub = {
            scrollY: 0,
            scrollTo: vi.fn(({ top }) => {
                window_stub.scrollY = top
            }),
        }
        vi.stubGlobal('window', window_stub)
    })

    afterEach(() => {
        vi.unstubAllGlobals()
        if (original_window !== undefined) vi.stubGlobal('window', original_window)
        if (original_session_storage !== undefined) vi.stubGlobal('sessionStorage', original_session_storage)
        vi.clearAllMocks()
    })

    it('restore_state 会等待 wait_for 达成后再执行滚动', async () => {
        const { save_state, restore_state } = useScrollRestoration({
            get_key: () => 'scroll_key',
            get_scroll_el: () => window,
        })

        window.scrollY = 200
        save_state({ extra: 1 })

        window.scrollY = 0
        let calls = 0

        await restore_state({
            wait_for: () => {
                calls += 1
                return calls >= 3
            },
            wait_for_timeout_ms: 200,
            wait_for_interval_ms: 1,
            settle_frames: 0,
            get_scroll_top: () => 123,
        })

        expect(calls).toBeGreaterThanOrEqual(3)
        expect(window.scrollTo).toHaveBeenCalled()
        expect(window.scrollY).toBe(123)
    })

    it('wait_for 超时后仍会继续尝试恢复滚动', async () => {
        const { save_state, restore_state } = useScrollRestoration({
            get_key: () => 'scroll_key_timeout',
            get_scroll_el: () => window,
        })

        window.scrollY = 80
        save_state()

        window.scrollY = 0

        await restore_state({
            wait_for: () => false,
            wait_for_timeout_ms: 20,
            wait_for_interval_ms: 5,
            settle_frames: 0,
            get_scroll_top: () => 50,
        })

        expect(window.scrollTo).toHaveBeenCalled()
        expect(window.scrollY).toBe(50)
    })

    it('should_restore 返回 false 时不滚动且会清理状态', async () => {
        const { save_state, restore_state, read_state } = useScrollRestoration({
            get_key: () => 'scroll_key_should_restore',
            get_scroll_el: () => window,
        })

        window.scrollY = 10
        save_state()

        await restore_state({
            should_restore: () => false,
        })

        expect(window.scrollTo).not.toHaveBeenCalled()
        expect(read_state()).toBeNull()
    })
})