File size: 2,535 Bytes
8059bf0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'
import { useKeyedDebouncedSearch } from '@/composables/useKeyedDebouncedSearch'

const flushPromises = () => Promise.resolve()

describe('useKeyedDebouncedSearch', () => {
  beforeEach(() => {
    vi.useFakeTimers()
  })

  afterEach(() => {
    vi.useRealTimers()
  })

  it('为不同 key 独立防抖触发搜索', async () => {
    const search = vi.fn().mockResolvedValue([])
    const onSuccess = vi.fn()

    const searcher = useKeyedDebouncedSearch<string[]>({
      delay: 100,
      search,
      onSuccess
    })

    searcher.trigger('a', 'foo')
    searcher.trigger('b', 'bar')

    expect(search).not.toHaveBeenCalled()

    vi.advanceTimersByTime(100)
    await flushPromises()

    expect(search).toHaveBeenCalledTimes(2)
    expect(search).toHaveBeenNthCalledWith(
      1,
      'foo',
      expect.objectContaining({ key: 'a', signal: expect.any(AbortSignal) })
    )
    expect(search).toHaveBeenNthCalledWith(
      2,
      'bar',
      expect.objectContaining({ key: 'b', signal: expect.any(AbortSignal) })
    )
    expect(onSuccess).toHaveBeenCalledTimes(2)
  })

  it('同 key 新请求会取消旧请求并忽略过期响应', async () => {
    const resolves: Array<(value: string[]) => void> = []
    const search = vi.fn().mockImplementation(
      () => new Promise<string[]>((resolve) => {
        resolves.push(resolve)
      })
    )
    const onSuccess = vi.fn()

    const searcher = useKeyedDebouncedSearch<string[]>({
      delay: 50,
      search,
      onSuccess
    })

    searcher.trigger('rule-1', 'first')
    vi.advanceTimersByTime(50)
    await flushPromises()

    searcher.trigger('rule-1', 'second')
    vi.advanceTimersByTime(50)
    await flushPromises()

    expect(search).toHaveBeenCalledTimes(2)

    resolves[1](['second'])
    await flushPromises()
    expect(onSuccess).toHaveBeenCalledTimes(1)
    expect(onSuccess).toHaveBeenLastCalledWith('rule-1', ['second'])

    resolves[0](['first'])
    await flushPromises()
    expect(onSuccess).toHaveBeenCalledTimes(1)
  })

  it('clearKey 会取消未执行任务', () => {
    const search = vi.fn().mockResolvedValue([])
    const onSuccess = vi.fn()

    const searcher = useKeyedDebouncedSearch<string[]>({
      delay: 100,
      search,
      onSuccess
    })

    searcher.trigger('a', 'foo')
    searcher.clearKey('a')

    vi.advanceTimersByTime(100)

    expect(search).not.toHaveBeenCalled()
    expect(onSuccess).not.toHaveBeenCalled()
  })
})