- 
                Notifications
    
You must be signed in to change notification settings  - Fork 502
 
test(debounce): improved the test code for the debounce function #1263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
bd8c78f
              6f4ead9
              043f891
              b101677
              2c2561e
              79e6765
              b394472
              01ffc92
              6ead746
              b77a16e
              4bd3e87
              f1a6789
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| 
          
            
          
           | 
    @@ -3,110 +3,161 @@ import { debounce } from './debounce'; | |
| // adjust the import path as necessary | ||
| import { delay } from '../promise'; | ||
| 
     | 
||
| const DEBOUNCE_MS = 50; | ||
| 
     | 
||
| describe('debounce', () => { | ||
| it('should debounce function calls', async () => { | ||
| const func = vi.fn(); | ||
| const debounceMs = 50; | ||
| const debouncedFunc = debounce(func, debounceMs); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS); | ||
| 
     | 
||
| debouncedFunc(); | ||
| debouncedFunc(); | ||
| debouncedFunc(); | ||
| 
     | 
||
| await delay(debounceMs * 2); | ||
| expect(func).not.toHaveBeenCalled(); | ||
| 
         There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code was added to more clearly test the basic debounce behavior.  | 
||
| 
     | 
||
| await delay(DEBOUNCE_MS * 2); | ||
| 
     | 
||
| expect(func).toHaveBeenCalledTimes(1); | ||
| }); | ||
| 
     | 
||
| it('should delay the function call by the specified wait time', async () => { | ||
| const func = vi.fn(); | ||
| const debounceMs = 50; | ||
| const debouncedFunc = debounce(func, debounceMs); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS); | ||
| 
     | 
||
| debouncedFunc(); | ||
| await delay(debounceMs / 2); | ||
| await delay(DEBOUNCE_MS / 2); | ||
| expect(func).not.toHaveBeenCalled(); | ||
| 
     | 
||
| await delay(debounceMs / 2 + 1); | ||
| await delay(DEBOUNCE_MS / 2 + 1); | ||
| expect(func).toHaveBeenCalledTimes(1); | ||
| }); | ||
| 
     | 
||
| it('should reset the wait time if called again before wait time ends', async () => { | ||
| const func = vi.fn(); | ||
| const debounceMs = 50; | ||
| const debouncedFunc = debounce(func, debounceMs); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS); | ||
| 
     | 
||
| debouncedFunc(); | ||
| await delay(debounceMs / 2); | ||
| await delay(DEBOUNCE_MS / 2); | ||
| debouncedFunc(); | ||
| await delay(debounceMs / 2); | ||
| await delay(DEBOUNCE_MS / 2); | ||
| debouncedFunc(); | ||
| await delay(debounceMs / 2); | ||
| await delay(DEBOUNCE_MS / 2); | ||
| debouncedFunc(); | ||
| 
     | 
||
| expect(func).not.toHaveBeenCalled(); | ||
| 
     | 
||
| await delay(debounceMs + 1); | ||
| await delay(DEBOUNCE_MS + 1); | ||
| expect(func).toHaveBeenCalledTimes(1); | ||
| }); | ||
| 
     | 
||
| it('should cancel the debounced function call', async () => { | ||
| const func = vi.fn(); | ||
| const debounceMs = 50; | ||
| const debouncedFunc = debounce(func, debounceMs); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS); | ||
| 
     | 
||
| debouncedFunc(); | ||
| debouncedFunc.cancel(); | ||
| await delay(debounceMs); | ||
| await delay(DEBOUNCE_MS); | ||
| 
     | 
||
| expect(func).not.toHaveBeenCalled(); | ||
| }); | ||
| 
     | 
||
| it('should immediately invoke the delayed function when flush is called', async () => { | ||
| const func = vi.fn(); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS); | ||
| 
     | 
||
| debouncedFunc(); | ||
| debouncedFunc.flush(); | ||
| 
     | 
||
| expect(func).toHaveBeenCalledTimes(1); | ||
| }); | ||
| 
         
      Comment on lines
    
      +65
     to 
      +73
    
   
  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add test code for the   | 
||
| 
     | 
||
| it('should work correctly if the debounced function is called after the wait time', async () => { | ||
| const func = vi.fn(); | ||
| const debounceMs = 50; | ||
| const debouncedFunc = debounce(func, debounceMs); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS); | ||
| 
     | 
||
| debouncedFunc(); | ||
| await delay(debounceMs + 1); | ||
| await delay(DEBOUNCE_MS + 1); | ||
| debouncedFunc(); | ||
| await delay(debounceMs + 1); | ||
| await delay(DEBOUNCE_MS + 1); | ||
| 
     | 
||
| expect(func).toHaveBeenCalledTimes(2); | ||
| }); | ||
| 
     | 
||
| it('should have no effect if we call cancel when the function is not executed', () => { | ||
| const func = vi.fn(); | ||
| const debounceMs = 50; | ||
| const debouncedFunc = debounce(func, debounceMs); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS); | ||
| 
     | 
||
| expect(() => debouncedFunc.cancel()).not.toThrow(); | ||
| }); | ||
| 
     | 
||
| it('should call the function with correct arguments', async () => { | ||
| const func = vi.fn(); | ||
| const debounceMs = 50; | ||
| const debouncedFunc = debounce(func, debounceMs); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS); | ||
| 
     | 
||
| debouncedFunc('test', 123); | ||
| 
     | 
||
| await delay(debounceMs * 2); | ||
| await delay(DEBOUNCE_MS * 2); | ||
| 
     | 
||
| expect(func).toHaveBeenCalledTimes(1); | ||
| expect(func).toHaveBeenCalledWith('test', 123); | ||
| }); | ||
| 
     | 
||
| it('should execute immediately on first call when edges is set to leading', async () => { | ||
| const func = vi.fn(); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS, { edges: ['leading'] }); | ||
| 
     | 
||
| debouncedFunc(); | ||
| 
     | 
||
| expect(func).toHaveBeenCalledTimes(1); | ||
| 
     | 
||
| debouncedFunc(); | ||
| 
     | 
||
| await delay(DEBOUNCE_MS); | ||
| 
     | 
||
| expect(func).toHaveBeenCalledTimes(1); | ||
| }); | ||
| 
     | 
||
| it('should execute immediately on last call when edges is set to trailing', async () => { | ||
| const func = vi.fn(); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS, { edges: ['trailing'] }); | ||
| 
     | 
||
| debouncedFunc(); | ||
| 
     | 
||
| expect(func).not.toHaveBeenCalled(); | ||
| 
     | 
||
| debouncedFunc(); | ||
| 
     | 
||
| await delay(DEBOUNCE_MS); | ||
| 
     | 
||
| expect(func).toHaveBeenCalledTimes(1); | ||
| }); | ||
| 
     | 
||
| it('should execute immediately on both edges when edges is set to both', async () => { | ||
| const func = vi.fn(); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS, { edges: ['leading', 'trailing'] }); | ||
| 
     | 
||
| debouncedFunc(); | ||
| 
     | 
||
| expect(func).toHaveBeenCalledTimes(1); | ||
| 
     | 
||
| debouncedFunc(); | ||
| 
     | 
||
| await delay(DEBOUNCE_MS); | ||
| 
     | 
||
| expect(func).toHaveBeenCalledTimes(2); | ||
| }); | ||
| 
         
      Comment on lines
    
      +106
     to 
      +149
    
   
  There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added test code for the edges option of the debounce function.  | 
||
| 
     | 
||
| it('should cancel the debounced function call if aborted via AbortSignal', async () => { | ||
| const func = vi.fn(); | ||
| const debounceMs = 50; | ||
| const controller = new AbortController(); | ||
| const signal = controller.signal; | ||
| const debouncedFunc = debounce(func, debounceMs, { signal }); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS, { signal }); | ||
| 
     | 
||
| debouncedFunc(); | ||
| controller.abort(); | ||
| 
     | 
||
| await delay(debounceMs); | ||
| await delay(DEBOUNCE_MS); | ||
| 
     | 
||
| expect(func).not.toHaveBeenCalled(); | ||
| }); | ||
| 
        
          
        
         | 
    @@ -119,24 +170,22 @@ describe('debounce', () => { | |
| 
     | 
||
| const func = vi.fn(); | ||
| 
     | 
||
| const debounceMs = 50; | ||
| const debouncedFunc = debounce(func, debounceMs, { signal }); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS, { signal }); | ||
| 
     | 
||
| debouncedFunc(); | ||
| 
     | 
||
| await delay(debounceMs); | ||
| await delay(DEBOUNCE_MS); | ||
| 
     | 
||
| expect(func).not.toHaveBeenCalled(); | ||
| }); | ||
| 
     | 
||
| it('should not add multiple abort event listeners', async () => { | ||
| const func = vi.fn(); | ||
| const debounceMs = 100; | ||
| const controller = new AbortController(); | ||
| const signal = controller.signal; | ||
| const addEventListenerSpy = vi.spyOn(signal, 'addEventListener'); | ||
| 
     | 
||
| const debouncedFunc = debounce(func, debounceMs, { signal }); | ||
| const debouncedFunc = debounce(func, DEBOUNCE_MS, { signal }); | ||
| 
     | 
||
| debouncedFunc(); | ||
| debouncedFunc(); | ||
| 
          
            
          
           | 
    ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| 
          
            
          
           | 
    @@ -50,6 +50,7 @@ export interface DebouncedFunction<F extends (...args: any[]) => void> { | |
| * @param {number} debounceMs - The number of milliseconds to delay. | ||
| * @param {DebounceOptions} options - The options object | ||
| * @param {AbortSignal} options.signal - An optional AbortSignal to cancel the debounced function. | ||
| * @param {Array<'leading' | 'trailing'>} options.edges - An optional array specifying whether the function should be invoked on the leading edge, trailing edge, or both. | ||
| 
         There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added missing comments. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there's also an export missing for the DebounceOption type, I have a PR waiting for approval, but nobody reviewed it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. type DebounceOptions = Parameters<typeof debounce<() => void>>[2];@codecov-commenter  It's a shame that the review is delayed 🥲 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, but this is a ridiculous way to use it... hopefully we get proper responses on our PRs.  | 
||
| * @returns A new debounced function with a `cancel` method. | ||
| * | ||
| * @example | ||
| 
          
            
          
           | 
    ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To reduce code duplication, change debounceMs to a constant.