Skip to content

Commit

Permalink
fix(wait-for): Don't queue microtasks after condition is met (#1073)
Browse files Browse the repository at this point in the history
  • Loading branch information
eps1lon committed Nov 10, 2021
1 parent 2866544 commit 1fc17be
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 2 deletions.
81 changes: 79 additions & 2 deletions src/__tests__/wait-for.js
Expand Up @@ -11,6 +11,17 @@ function deferred() {
return {promise, resolve, reject}
}

let originalConfig
beforeEach(() => {
originalConfig = getConfig()
})

afterEach(() => {
configure(originalConfig)
// restore timers
jest.useRealTimers()
})

test('waits callback to not throw an error', async () => {
const spy = jest.fn()
// we are using random timeout here to simulate a real-time example
Expand Down Expand Up @@ -138,7 +149,6 @@ Ignored nodes: comments, <script />, <style />
})

test('should delegate to config.getElementError', async () => {
const originalConfig = getConfig()
const elementError = new Error('Custom element error')
const getElementError = jest.fn().mockImplementation(() => elementError)
configure({getElementError})
Expand All @@ -153,7 +163,6 @@ test('should delegate to config.getElementError', async () => {

expect(getElementError).toBeCalledTimes(1)
expect(error.message).toMatchInlineSnapshot(`Custom element error`)
configure(originalConfig)
})

test('when a promise is returned, it does not call the callback again until that promise rejects', async () => {
Expand Down Expand Up @@ -255,3 +264,71 @@ test('the real timers => fake timers error shows the original stack trace when c

expect((await waitForError).stack).not.toMatch(__dirname)
})

test('does not work after it resolves', async () => {
jest.useFakeTimers('modern')
let context = 'initial'
configure({
// @testing-library/react usage to ensure `IS_REACT_ACT_ENVIRONMENT` is set when acting.
unstable_advanceTimersWrapper: callback => {
const originalContext = context
context = 'act'
try {
const result = callback()
// eslint-disable-next-line jest/no-if
if (typeof result?.then === 'function') {
const thenable = result
return {
then: (resolve, reject) => {
thenable.then(
returnValue => {
context = originalContext
resolve(returnValue)
},
error => {
context = originalContext
reject(error)
},
)
},
}
} else {
context = originalContext
return undefined
}
} catch {
context = originalContext
return undefined
}
},
asyncWrapper: async callback => {
const originalContext = context
context = 'no-act'
try {
await callback()
} finally {
context = originalContext
}
},
})

let data = null
setTimeout(() => {
data = 'resolved'
}, 100)

await waitFor(
() => {
if (data === null) {
throw new Error('not found')
}
},
{interval: 50},
)

expect(context).toEqual('initial')

await Promise.resolve()

expect(context).toEqual('initial')
})
4 changes: 4 additions & 0 deletions src/wait-for.js
Expand Up @@ -82,6 +82,10 @@ function waitFor(
// an entire day banging my head against a wall on this.
checkCallback()

if (finished) {
break
}

// In this rare case, we *need* to wait for in-flight promises
// to resolve before continuing. We don't need to take advantage
// of parallelization so we're fine.
Expand Down

0 comments on commit 1fc17be

Please sign in to comment.