Skip to content

Commit

Permalink
Use collectAsyncCalls with promise
Browse files Browse the repository at this point in the history
  • Loading branch information
amortemousque committed Nov 26, 2024
1 parent bdec463 commit 1568036
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 140 deletions.
76 changes: 33 additions & 43 deletions packages/core/src/domain/error/trackRuntimeError.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,16 @@ describe('trackRuntimeError', () => {
window.onerror = originalOnErrorHandler
})

it('should collect unhandled error', (done) => {
it('should collect unhandled error', async () => {
setTimeout(() => {
throw new Error(ERROR_MESSAGE)
})
collectAsyncCalls(onErrorSpy, 1, () => {
expect(notifyError).toHaveBeenCalledOnceWith(jasmine.objectContaining({ message: ERROR_MESSAGE }))
done()
})

await collectAsyncCalls(onErrorSpy, 1)
expect(notifyError).toHaveBeenCalledOnceWith(jasmine.objectContaining({ message: ERROR_MESSAGE }))
})

it('should collect unhandled rejection', (done) => {
it('should collect unhandled rejection', async () => {
if (!('onunhandledrejection' in window)) {
pending('onunhandledrejection not supported')
}
Expand All @@ -52,10 +51,8 @@ describe('trackRuntimeError', () => {
void Promise.reject(new Error(ERROR_MESSAGE))
})

collectAsyncCalls(onUnhandledrejectionSpy, 1, () => {
expect(notifyError).toHaveBeenCalledOnceWith(jasmine.objectContaining({ message: ERROR_MESSAGE }))
done()
})
await collectAsyncCalls(onUnhandledrejectionSpy, 1)
expect(notifyError).toHaveBeenCalledOnceWith(jasmine.objectContaining({ message: ERROR_MESSAGE }))
})
})

Expand All @@ -82,55 +79,50 @@ describe('instrumentOnError', () => {
stopCollectingUnhandledError()
})

it('should call original error handler', (done) => {
it('should call original error handler', async () => {
setTimeout(() => {
throw new Error(ERROR_MESSAGE)
})
collectAsyncCalls(onErrorSpy, 1, () => {
expect(onErrorSpy).toHaveBeenCalled()
done()
})

await collectAsyncCalls(onErrorSpy, 1)
expect(onErrorSpy).toHaveBeenCalled()
})

it('should notify unhandled error instance', (done) => {
it('should notify unhandled error instance', async () => {
const error = new Error(ERROR_MESSAGE)
setTimeout(() => {
throw error
})
collectAsyncCalls(onErrorSpy, 1, () => {
const [stack, originalError] = callbackSpy.calls.mostRecent().args
expect(originalError).toBe(error)
expect(stack).toBeDefined()
done()
})

await collectAsyncCalls(onErrorSpy, 1)
expect(onErrorSpy).toHaveBeenCalled()
const [stack, originalError] = callbackSpy.calls.mostRecent().args
expect(originalError).toBe(error)
expect(stack).toBeDefined()
})

it('should notify unhandled string', (done) => {
it('should notify unhandled string', async () => {
const error = 'foo' as any
setTimeout(() => {
// eslint-disable-next-line no-throw-literal
throw error
})
collectAsyncCalls(onErrorSpy, 1, () => {
const [stack, originalError] = callbackSpy.calls.mostRecent().args
expect(originalError).toBe(error)
expect(stack).toBeDefined()
done()
})
await collectAsyncCalls(onErrorSpy, 1)
const [stack, originalError] = callbackSpy.calls.mostRecent().args
expect(originalError).toBe(error)
expect(stack).toBeDefined()
})

it('should notify unhandled object', (done) => {
it('should notify unhandled object', async () => {
const error = { a: 'foo' } as any
setTimeout(() => {
// eslint-disable-next-line no-throw-literal
throw error
})
collectAsyncCalls(onErrorSpy, 1, () => {
const [stack, originalError] = callbackSpy.calls.mostRecent().args
expect(originalError).toBe(error)
expect(stack).toBeDefined()
done()
})
await collectAsyncCalls(onErrorSpy, 1)
const [stack, originalError] = callbackSpy.calls.mostRecent().args
expect(originalError).toBe(error)
expect(stack).toBeDefined()
})

describe('uncaught exception handling', () => {
Expand Down Expand Up @@ -164,18 +156,16 @@ describe('instrumentOnError', () => {
})

describe('should handle direct onerror calls', () => {
it('with objects', (done) => {
it('with objects', async () => {
const error = { foo: 'bar' } as any
setTimeout(() => {
window.onerror!(error)
})

collectAsyncCalls(onErrorSpy, 1, () => {
const [stack, originalError] = callbackSpy.calls.mostRecent().args
expect(originalError).toBe(error)
expect(stack).toBeDefined()
done()
})
await collectAsyncCalls(onErrorSpy, 1)
const [stack, originalError] = callbackSpy.calls.mostRecent().args
expect(originalError).toBe(error)
expect(stack).toBeDefined()
})

describe('with undefined arguments', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/transport/httpRequest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe('httpRequest', () => {
})
})

it('should use retry strategy', (done) => {
it('should use retry strategy', async () => {
if (!interceptor.isFetchKeepAliveSupported()) {
pending('no fetch keepalive support')
}
Expand All @@ -90,7 +90,7 @@ describe('httpRequest', () => {

request.send({ data: '{"foo":"bar1"}\n{"foo":"bar2"}', bytesCount: 10 })

collectAsyncCalls(fetchSpy, 2, () => done())
await collectAsyncCalls(fetchSpy, 2)
})
})

Expand Down
41 changes: 22 additions & 19 deletions packages/core/test/collectAsyncCalls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,32 @@ import { getCurrentJasmineSpec } from './getCurrentJasmineSpec'

export function collectAsyncCalls<F extends jasmine.Func>(
spy: jasmine.Spy<F>,
expectedCallsCount: number,
callback: (calls: jasmine.Calls<F>) => void
) {
const currentSpec = getCurrentJasmineSpec()
if (!currentSpec) {
throw new Error('collectAsyncCalls should be called within jasmine code')
}
expectedCallsCount: number
): Promise<jasmine.Calls<F>> {
return new Promise((resolve, reject) => {
const currentSpec = getCurrentJasmineSpec()
if (!currentSpec) {
reject(new Error('collectAsyncCalls should be called within jasmine code'))
return
}

if (spy.calls.count() === expectedCallsCount) {
spy.and.callFake(extraCallDetected as F)
callback(spy.calls)
} else if (spy.calls.count() > expectedCallsCount) {
extraCallDetected()
} else {
spy.and.callFake((() => {
const checkCalls = () => {
if (spy.calls.count() === expectedCallsCount) {
spy.and.callFake(extraCallDetected as F)
callback(spy.calls)
resolve(spy.calls)
} else if (spy.calls.count() > expectedCallsCount) {
extraCallDetected()
}
}

checkCalls()

spy.and.callFake((() => {
checkCalls()
}) as F)
}

function extraCallDetected() {
fail(`Unexpected extra call for spec '${currentSpec!.fullName}'`)
}
function extraCallDetected() {
reject(new Error(`Unexpected extra call for spec '${currentSpec!.fullName}'`))
}
})
}
4 changes: 1 addition & 3 deletions packages/rum/src/boot/startRecording.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,7 @@ describe('startRecording', () => {
}

async function readSentRequests(expectedSentRequestCount: number) {
const calls = await new Promise<jasmine.Calls<HttpRequest['sendOnExit']>>((resolve) =>
collectAsyncCalls(requestSendSpy, expectedSentRequestCount, resolve)
)
const calls = await collectAsyncCalls(requestSendSpy, expectedSentRequestCount)
return Promise.all(calls.all().map((call) => readReplayPayload(call.args[0])))
}
})
Expand Down
138 changes: 67 additions & 71 deletions packages/rum/src/domain/record/record.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ describe('record', () => {
})
})

it('captures stylesheet rules', (done) => {
it('captures stylesheet rules', async () => {
const styleElement = appendElement('<style></style>') as HTMLStyleElement

startRecording()
Expand All @@ -59,92 +59,88 @@ describe('record', () => {
styleSheet.insertRule('body { color: #ccc; }')
}, 10)

collectAsyncCalls(emitSpy, recordsPerFullSnapshot() + 6, () => {
const records = getEmittedRecords()
let i = 0
await collectAsyncCalls(emitSpy, recordsPerFullSnapshot() + 6)

expect(records[i++].type).toEqual(RecordType.Meta)
expect(records[i++].type).toEqual(RecordType.Focus)
expect(records[i++].type).toEqual(RecordType.FullSnapshot)
const records = getEmittedRecords()
let i = 0

if (window.visualViewport) {
expect(records[i++].type).toEqual(RecordType.VisualViewport)
}
expect(records[i++].type).toEqual(RecordType.Meta)
expect(records[i++].type).toEqual(RecordType.Focus)
expect(records[i++].type).toEqual(RecordType.FullSnapshot)

expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
adds: [{ rule: 'body { background: #000; }', index: undefined }],
})
)
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
adds: [{ rule: 'body { background: #111; }', index: undefined }],
})
)
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
removes: [{ index: 0 }],
})
)
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
adds: [{ rule: 'body { color: #fff; }', index: undefined }],
})
)
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
removes: [{ index: 0 }],
})
)
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
adds: [{ rule: 'body { color: #ccc; }', index: undefined }],
})
)
if (window.visualViewport) {
expect(records[i++].type).toEqual(RecordType.VisualViewport)
}

done()
})
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
adds: [{ rule: 'body { background: #000; }', index: undefined }],
})
)
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
adds: [{ rule: 'body { background: #111; }', index: undefined }],
})
)
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
removes: [{ index: 0 }],
})
)
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
adds: [{ rule: 'body { color: #fff; }', index: undefined }],
})
)
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
removes: [{ index: 0 }],
})
)
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data).toEqual(
jasmine.objectContaining({
source: IncrementalSource.StyleSheetRule,
adds: [{ rule: 'body { color: #ccc; }', index: undefined }],
})
)
})

it('flushes pending mutation records before taking a full snapshot', (done) => {
it('flushes pending mutation records before taking a full snapshot', async () => {
startRecording()

appendElement('<hr/>')

// trigger full snapshot by starting a new view
newView()

collectAsyncCalls(emitSpy, 1 + 2 * recordsPerFullSnapshot(), () => {
const records = getEmittedRecords()
let i = 0
await collectAsyncCalls(emitSpy, 1 + 2 * recordsPerFullSnapshot())

expect(records[i++].type).toEqual(RecordType.Meta)
expect(records[i++].type).toEqual(RecordType.Focus)
expect(records[i++].type).toEqual(RecordType.FullSnapshot)
const records = getEmittedRecords()
let i = 0

if (window.visualViewport) {
expect(records[i++].type).toEqual(RecordType.VisualViewport)
}
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data.source).toEqual(IncrementalSource.Mutation)
expect(records[i++].type).toEqual(RecordType.Meta)
expect(records[i++].type).toEqual(RecordType.Focus)
expect(records[i++].type).toEqual(RecordType.FullSnapshot)
expect(records[i++].type).toEqual(RecordType.Meta)
expect(records[i++].type).toEqual(RecordType.Focus)
expect(records[i++].type).toEqual(RecordType.FullSnapshot)

done()
})
if (window.visualViewport) {
expect(records[i++].type).toEqual(RecordType.VisualViewport)
}
expect(records[i].type).toEqual(RecordType.IncrementalSnapshot)
expect((records[i++] as BrowserIncrementalSnapshotRecord).data.source).toEqual(IncrementalSource.Mutation)
expect(records[i++].type).toEqual(RecordType.Meta)
expect(records[i++].type).toEqual(RecordType.Focus)
expect(records[i++].type).toEqual(RecordType.FullSnapshot)
})

describe('Shadow dom', () => {
Expand Down
Loading

0 comments on commit 1568036

Please sign in to comment.