diff --git a/test/e2e/metrics.spec.js b/test/e2e/metrics.spec.js index 585a44e35..74c8f26d8 100644 --- a/test/e2e/metrics.spec.js +++ b/test/e2e/metrics.spec.js @@ -28,14 +28,16 @@ describe('Segment metrics', function () { mockSegment: true, }, async ({ driver, segmentStub }) => { - const threeSegmentEventsReceived = waitUntilCalled(segmentStub, null, 3) + const threeSegmentEventsReceived = waitUntilCalled(segmentStub, null, { + callCount: 3, + }) await driver.navigate() const passwordField = await driver.findElement(By.css('#password')) await passwordField.sendKeys('correct horse battery staple') await passwordField.sendKeys(Key.ENTER) - await threeSegmentEventsReceived + await threeSegmentEventsReceived() assert.ok(segmentStub.called, 'Segment should receive metrics') diff --git a/test/lib/wait-until-called.js b/test/lib/wait-until-called.js index 752ee7675..8252f6049 100644 --- a/test/lib/wait-until-called.js +++ b/test/lib/wait-until-called.js @@ -1,22 +1,41 @@ +const DEFAULT_TIMEOUT = 10000 + /** - * A function that wraps a sinon stubbed function and returns a Promise - * when this stub was called. + * A function that wraps a sinon stub and returns an asynchronous function + * that resolves if the stubbed function was called enough times, or throws + * if the timeout is exceeded. * * The stub that has been passed in will be setup to call the wrapped function - * directly, then trigger the returned Promise to resolve. + * directly. * * WARNING: Any existing `callsFake` behavior will be overwritten. * * @param {import('sinon').stub} stub - A sinon stub of a function - * @param {unknown} [wrappedThis] - The object the stubbed function was called on, if any (i.e. the `this` value) - * @param {number} [callCount] - The number of calls to wait for. Defaults to 1. - * @returns {Promise} A Promise that resolves when the stub has been called + * @param {unknown} [wrappedThis] - The object the stubbed function was called + * on, if any (i.e. the `this` value) + * @param {Object} [options] - Optional configuration + * @param {number} [options.callCount] - The number of calls to wait for. + * @param {number|null} [options.timeout] - The timeout, in milliseconds. Pass + * in `null` to disable the timeout. + * @returns {Function} An asynchronous function that resolves when the stub is + * called enough times, or throws if the timeout is reached. */ -function waitUntilCalled(stub, wrappedThis = null, callCount = 1) { +function waitUntilCalled( + stub, + wrappedThis = null, + { callCount = 1, timeout = DEFAULT_TIMEOUT } = {}, +) { let numCalls = 0 let resolve + let timeoutHandle const stubHasBeenCalled = new Promise((_resolve) => { resolve = _resolve + if (timeout !== null) { + timeoutHandle = setTimeout( + () => resolve(new Error('Timeout exceeded')), + timeout, + ) + } }) stub.callsFake((...args) => { try { @@ -27,12 +46,21 @@ function waitUntilCalled(stub, wrappedThis = null, callCount = 1) { if (numCalls < callCount) { numCalls += 1 if (numCalls === callCount) { + if (timeoutHandle) { + clearTimeout(timeoutHandle) + } resolve() } } } }) - return stubHasBeenCalled + + return async () => { + const error = await stubHasBeenCalled + if (error) { + throw error + } + } } module.exports = waitUntilCalled diff --git a/test/unit/app/controllers/incoming-transactions-test.js b/test/unit/app/controllers/incoming-transactions-test.js index 8401d7f24..61768cee8 100644 --- a/test/unit/app/controllers/incoming-transactions-test.js +++ b/test/unit/app/controllers/incoming-transactions-test.js @@ -249,7 +249,7 @@ describe('IncomingTransactionsController', function () { ) incomingTransactionsController.start() - await updateStateCalled + await updateStateCalled() const actualState = incomingTransactionsController.store.getState() const generatedTxId = actualState?.incomingTransactions?.['0xfake']?.id @@ -345,8 +345,8 @@ describe('IncomingTransactionsController', function () { try { await Promise.race([ - updateStateCalled, - putStateCalled, + updateStateCalled(), + putStateCalled(), new Promise((_, reject) => { setTimeout(() => reject(new Error('TIMEOUT')), SET_STATE_TIMEOUT) }), @@ -412,8 +412,8 @@ describe('IncomingTransactionsController', function () { try { await Promise.race([ - updateStateCalled, - putStateCalled, + updateStateCalled(), + putStateCalled(), new Promise((_, reject) => { setTimeout(() => reject(new Error('TIMEOUT')), SET_STATE_TIMEOUT) }), @@ -475,8 +475,8 @@ describe('IncomingTransactionsController', function () { try { await Promise.race([ - updateStateCalled, - putStateCalled, + updateStateCalled(), + putStateCalled(), new Promise((_, reject) => { setTimeout(() => reject(new Error('TIMEOUT')), SET_STATE_TIMEOUT) }), @@ -540,8 +540,8 @@ describe('IncomingTransactionsController', function () { try { await Promise.race([ - updateStateCalled, - putStateCalled, + updateStateCalled(), + putStateCalled(), new Promise((_, reject) => { setTimeout(() => reject(new Error('TIMEOUT')), SET_STATE_TIMEOUT) }), @@ -592,7 +592,7 @@ describe('IncomingTransactionsController', function () { // TODO: stop skipping the first event await subscription({ selectedAddress: MOCK_SELECTED_ADDRESS }) await subscription({ selectedAddress: NEW_MOCK_SELECTED_ADDRESS }) - await updateStateCalled + await updateStateCalled() const actualState = incomingTransactionsController.store.getState() const generatedTxId = actualState?.incomingTransactions?.['0xfake']?.id @@ -696,8 +696,8 @@ describe('IncomingTransactionsController', function () { try { await Promise.race([ - updateStateCalled, - putStateCalled, + updateStateCalled(), + putStateCalled(), new Promise((_, reject) => { setTimeout(() => reject(new Error('TIMEOUT')), SET_STATE_TIMEOUT) }), @@ -746,7 +746,7 @@ describe('IncomingTransactionsController', function () { ROPSTEN_CHAIN_ID, ) await subscription(ROPSTEN) - await updateStateCalled + await updateStateCalled() const actualState = incomingTransactionsController.store.getState() const generatedTxId = actualState?.incomingTransactions?.['0xfake']?.id @@ -848,8 +848,8 @@ describe('IncomingTransactionsController', function () { try { await Promise.race([ - updateStateCalled, - putStateCalled, + updateStateCalled(), + putStateCalled(), new Promise((_, reject) => { setTimeout(() => reject(new Error('TIMEOUT')), SET_STATE_TIMEOUT) }), diff --git a/test/unit/app/controllers/metametrics-test.js b/test/unit/app/controllers/metametrics-test.js index d43f48f4f..4e66fec4a 100644 --- a/test/unit/app/controllers/metametrics-test.js +++ b/test/unit/app/controllers/metametrics-test.js @@ -400,7 +400,7 @@ describe('MetaMetricsController', function () { }, { flushImmediately: true }, ) - assert.doesNotReject(flushCalled) + assert.doesNotReject(flushCalled()) }) it('should throw if event or category not provided', function () {