Some checks failed
Test examples / Test Examples (20) (push) Has been cancelled
Test examples / Test Examples (22) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Trigger Release / start (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Update Font Data / create-pull-request (push) Has been cancelled
build-and-deploy / deploy-target (push) Has been cancelled
build-and-deploy / build (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / build-wasm (nodejs) (push) Has been cancelled
build-and-deploy / build-wasm (web) (push) Has been cancelled
build-and-deploy / Deploy preview tarball (push) Has been cancelled
build-and-deploy / Potentially publish release (push) Has been cancelled
build-and-deploy / publish-turbopack-npm-packages (push) Has been cancelled
build-and-deploy / Deploy examples (push) Has been cancelled
build-and-deploy / thank you, build (push) Has been cancelled
build-and-deploy / Upload Turbopack Bytesize metrics to Datadog (push) Has been cancelled
Rspack Next.js development integration tests / Rspack integration tests (push) Has been cancelled
Rspack Next.js production integration tests / Rspack integration tests (push) Has been cancelled
Turbopack Next.js development integration tests / Next.js integration tests (push) Has been cancelled
Turbopack Next.js production integration tests / Next.js integration tests (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack development test manifest (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack production test manifest (push) Has been cancelled
Upload bundler test manifests to areweturboyet.com / Upload test results (push) Has been cancelled
Update React / create-pull-request (push) Has been cancelled
test-e2e-project-reset-cron / reset-test-project (push) Has been cancelled
Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days / run (push) Has been cancelled
155 lines
4.4 KiB
TypeScript
155 lines
4.4 KiB
TypeScript
// Creates an event log. You can write to this during testing and then assert
|
|
// on the result.
|
|
//
|
|
// The main use case is for asynchronous e2e tests. It provides a `waitFor`
|
|
// method that resolves when the log matches some expected asynchronous sequence
|
|
// of events. This is an alternative to setting up a timer loop. It helps catch
|
|
// subtle mistakes where the order of events is not expected, or the same
|
|
// event happens more than it should.
|
|
//
|
|
// Based on the Scheduler.log pattern used in the React repo.
|
|
export function createTestLog() {
|
|
let events: unknown[] = []
|
|
|
|
// Represents a pending waitFor call.
|
|
let pendingExpectation: null | {
|
|
resolve: () => void
|
|
reject: (error: Error) => void
|
|
expectedEvents: Array<any>
|
|
error: Error
|
|
} = null
|
|
|
|
function log(value: unknown) {
|
|
// Add to the event log.
|
|
events.push(value)
|
|
|
|
// Check if we've reached the end of the expected log. If there's a
|
|
// pending waitFor, and we've reached the last of the expected events, this
|
|
// will resolve the promise.
|
|
pingExpectation()
|
|
}
|
|
|
|
function assert(expectedEvents: any[]) {
|
|
if (pendingExpectation !== null) {
|
|
const error = new Error('Cannot assert while a waitFor() is pending.')
|
|
Error.captureStackTrace(error, assert)
|
|
throw error
|
|
}
|
|
|
|
const actualEvents = events
|
|
events = []
|
|
|
|
if (!areLogsEqual(expectedEvents, actualEvents)) {
|
|
// Capture the stack trace of `assert` so that Jest will report the
|
|
// error as originating from the `assert` call instead of here.
|
|
const error = new Error(
|
|
'Expected sequence of events did not occur.\n\n' +
|
|
createDiff(expectedEvents, actualEvents)
|
|
)
|
|
Error.captureStackTrace(error, assert)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
function waitFor(expectedEvents: any[], timeout: number = 5000) {
|
|
// Returns a promise that resolves when the event log matches the
|
|
// expected sequence.
|
|
|
|
// Capture the stack trace of `waitFor` so that if an inner assertion fails,
|
|
// Jest will report the error as originating from the `waitFor` call instead
|
|
// of inside this module's implementation.
|
|
const error = new Error()
|
|
Error.captureStackTrace(error, waitFor)
|
|
|
|
if (pendingExpectation !== null) {
|
|
error.message = 'A previous waitFor() is still pending.'
|
|
throw error
|
|
}
|
|
|
|
let resolve
|
|
let reject
|
|
const promise = new Promise<void>((res, rej) => {
|
|
resolve = res
|
|
reject = rej
|
|
})
|
|
|
|
const thisExpectation = {
|
|
resolve,
|
|
reject,
|
|
expectedEvents,
|
|
error,
|
|
}
|
|
pendingExpectation = thisExpectation
|
|
|
|
setTimeout(() => {
|
|
if (pendingExpectation === thisExpectation) {
|
|
error.message = `waitFor timed out after ${timeout}ms`
|
|
reject(error)
|
|
}
|
|
}, timeout)
|
|
|
|
pingExpectation()
|
|
|
|
return promise
|
|
}
|
|
|
|
function pingExpectation() {
|
|
if (pendingExpectation !== null) {
|
|
const expectedEvents = pendingExpectation.expectedEvents
|
|
if (events.length < expectedEvents.length) {
|
|
return
|
|
}
|
|
|
|
if (areLogsEqual(expectedEvents, events)) {
|
|
// We've reached the end of the expected log. Resolve the promise and
|
|
// reset the log.
|
|
events = []
|
|
pendingExpectation.resolve()
|
|
pendingExpectation = null
|
|
} else {
|
|
// The log does not match what was expected by the test. Reject the
|
|
// promise and reset the log.
|
|
|
|
// Use the error object that we captured at the start of the `waitFor`
|
|
// call. Jest will show that the error originated from `waitFor` call
|
|
// instead of inside this internal function.
|
|
const error = pendingExpectation.error
|
|
error.message =
|
|
'Expected sequence of events did not occur.\n\n' +
|
|
createDiff(expectedEvents, events)
|
|
|
|
events = []
|
|
pendingExpectation.reject(error)
|
|
pendingExpectation = null
|
|
}
|
|
}
|
|
}
|
|
|
|
function createDiff(expected, actual) {
|
|
// TODO: Jest exposes the diffing utility that it uses for `expect`.
|
|
// We could use that here for nicer output.
|
|
return `
|
|
Expected: ${JSON.stringify(expected)}
|
|
Actual: ${JSON.stringify(actual)}
|
|
`
|
|
}
|
|
|
|
function areLogsEqual(a, b) {
|
|
if (a.length !== b.length) {
|
|
return false
|
|
}
|
|
for (let i = 0; i < a.length; i++) {
|
|
if (a[i] !== b[i]) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
return {
|
|
log,
|
|
waitFor,
|
|
assert,
|
|
}
|
|
}
|