summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/utils/throttle/throttle_tests.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/mail/static/src/utils/throttle/throttle_tests.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/mail/static/src/utils/throttle/throttle_tests.js')
-rw-r--r--addons/mail/static/src/utils/throttle/throttle_tests.js407
1 files changed, 407 insertions, 0 deletions
diff --git a/addons/mail/static/src/utils/throttle/throttle_tests.js b/addons/mail/static/src/utils/throttle/throttle_tests.js
new file mode 100644
index 00000000..d3e6ad66
--- /dev/null
+++ b/addons/mail/static/src/utils/throttle/throttle_tests.js
@@ -0,0 +1,407 @@
+odoo.define('mail/static/src/utils/throttle/throttle_tests.js', function (require) {
+'use strict';
+
+const { afterEach, beforeEach, start } = require('mail/static/src/utils/test_utils.js');
+const throttle = require('mail/static/src/utils/throttle/throttle.js');
+const { nextTick } = require('mail/static/src/utils/utils.js');
+
+const { ThrottleReinvokedError, ThrottleCanceledError } = throttle;
+
+QUnit.module('mail', {}, function () {
+QUnit.module('utils', {}, function () {
+QUnit.module('throttle', {}, function () {
+QUnit.module('throttle_tests.js', {
+ beforeEach() {
+ beforeEach(this);
+ this.throttles = [];
+
+ this.start = async params => {
+ const { env, widget } = await start(Object.assign({}, params, {
+ data: this.data,
+ }));
+ this.env = env;
+ this.widget = widget;
+ };
+ },
+ afterEach() {
+ // Important: tests should cleanly intercept cancelation errors that
+ // may result from this teardown.
+ for (const t of this.throttles) {
+ t.clear();
+ }
+ afterEach(this);
+ },
+});
+
+QUnit.test('single call', async function (assert) {
+ assert.expect(6);
+
+ await this.start({
+ hasTimeControl: true,
+ });
+
+ let hasInvokedFunc = false;
+ const throttledFunc = throttle(
+ this.env,
+ () => {
+ hasInvokedFunc = true;
+ return 'func_result';
+ },
+ 0
+ );
+ this.throttles.push(throttledFunc);
+
+ assert.notOk(
+ hasInvokedFunc,
+ "func should not have been invoked on immediate throttle initialization"
+ );
+
+ await this.env.testUtils.advanceTime(0);
+ assert.notOk(
+ hasInvokedFunc,
+ "func should not have been invoked from throttle initialization after 0ms"
+ );
+
+ throttledFunc().then(res => {
+ assert.step('throttle_observed_invoke');
+ assert.strictEqual(
+ res,
+ 'func_result',
+ "throttle call return should forward result of inner func"
+ );
+ });
+ await nextTick();
+ assert.ok(
+ hasInvokedFunc,
+ "func should have been immediately invoked on first throttle call"
+ );
+ assert.verifySteps(
+ ['throttle_observed_invoke'],
+ "throttle should have observed invoked on first throttle call"
+ );
+});
+
+QUnit.test('2nd (throttled) call', async function (assert) {
+ assert.expect(8);
+
+ await this.start({
+ hasTimeControl: true,
+ });
+
+ let funcCalledAmount = 0;
+ const throttledFunc = throttle(
+ this.env,
+ () => {
+ funcCalledAmount++;
+ return `func_result_${funcCalledAmount}`;
+ },
+ 1000
+ );
+ this.throttles.push(throttledFunc);
+
+ throttledFunc().then(result => {
+ assert.step('throttle_observed_invoke_1');
+ assert.strictEqual(
+ result,
+ 'func_result_1',
+ "throttle call return should forward result of inner func 1"
+ );
+ });
+ await nextTick();
+ assert.verifySteps(
+ ['throttle_observed_invoke_1'],
+ "inner function of throttle should have been invoked on 1st call (immediate return)"
+ );
+
+ throttledFunc().then(res => {
+ assert.step('throttle_observed_invoke_2');
+ assert.strictEqual(
+ res,
+ 'func_result_2',
+ "throttle call return should forward result of inner func 2"
+ );
+ });
+ await nextTick();
+ assert.verifySteps(
+ [],
+ "inner function of throttle should not have been immediately invoked after 2nd call immediately after 1st call (throttled with 1s internal clock)"
+ );
+
+ await this.env.testUtils.advanceTime(999);
+ assert.verifySteps(
+ [],
+ "inner function of throttle should not have been invoked after 999ms of 2nd call (throttled with 1s internal clock)"
+ );
+
+ await this.env.testUtils.advanceTime(1);
+ assert.verifySteps(
+ ['throttle_observed_invoke_2'],
+ "inner function of throttle should not have been invoked after 1s of 2nd call (throttled with 1s internal clock)"
+ );
+});
+
+QUnit.test('throttled call reinvocation', async function (assert) {
+ assert.expect(11);
+
+ await this.start({
+ hasTimeControl: true,
+ });
+
+ let funcCalledAmount = 0;
+ const throttledFunc = throttle(
+ this.env,
+ () => {
+ funcCalledAmount++;
+ return `func_result_${funcCalledAmount}`;
+ },
+ 1000,
+ { silentCancelationErrors: false }
+ );
+ this.throttles.push(throttledFunc);
+
+ throttledFunc().then(result => {
+ assert.step('throttle_observed_invoke_1');
+ assert.strictEqual(
+ result,
+ 'func_result_1',
+ "throttle call return should forward result of inner func 1"
+ );
+ });
+ await nextTick();
+ assert.verifySteps(
+ ['throttle_observed_invoke_1'],
+ "inner function of throttle should have been invoked on 1st call (immediate return)"
+ );
+
+ throttledFunc()
+ .then(() => {
+ throw new Error("2nd throttle call should not be resolved (should have been canceled by reinvocation)");
+ })
+ .catch(error => {
+ assert.ok(
+ error instanceof ThrottleReinvokedError,
+ "Should generate a Throttle reinvoked error (from another throttle function call)"
+ );
+ assert.step('throttle_reinvoked_1');
+ });
+ await nextTick();
+ assert.verifySteps(
+ [],
+ "inner function of throttle should not have been immediately invoked after 2nd call immediately after 1st call (throttled with 1s internal clock)"
+ );
+
+ await this.env.testUtils.advanceTime(999);
+ assert.verifySteps(
+ [],
+ "inner function of throttle should not have been invoked after 999ms of 2nd call (throttled with 1s internal clock)"
+ );
+
+ throttledFunc()
+ .then(result => {
+ assert.step('throttle_observed_invoke_2');
+ assert.strictEqual(
+ result,
+ 'func_result_2',
+ "throttle call return should forward result of inner func 2"
+ );
+ });
+ await nextTick();
+ assert.verifySteps(
+ ['throttle_reinvoked_1'],
+ "2nd throttle call should have been canceled from 3rd throttle call (reinvoked before cooling down phase has ended)"
+ );
+
+ await this.env.testUtils.advanceTime(1);
+ assert.verifySteps(
+ ['throttle_observed_invoke_2'],
+ "inner function of throttle should have been invoked after 1s of 1st call (throttled with 1s internal clock, 3rd throttle call re-use timer of 2nd throttle call)"
+ );
+});
+
+QUnit.test('flush throttled call', async function (assert) {
+ assert.expect(9);
+
+ await this.start({
+ hasTimeControl: true,
+ });
+
+ const throttledFunc = throttle(
+ this.env,
+ () => {},
+ 1000,
+ );
+ this.throttles.push(throttledFunc);
+
+ throttledFunc().then(() => assert.step('throttle_observed_invoke_1'));
+ await nextTick();
+ assert.verifySteps(
+ ['throttle_observed_invoke_1'],
+ "inner function of throttle should have been invoked on 1st call (immediate return)"
+ );
+
+ throttledFunc().then(() => assert.step('throttle_observed_invoke_2'));
+ await nextTick();
+ assert.verifySteps(
+ [],
+ "inner function of throttle should not have been immediately invoked after 2nd call immediately after 1st call (throttled with 1s internal clock)"
+ );
+
+ await this.env.testUtils.advanceTime(10);
+ assert.verifySteps(
+ [],
+ "inner function of throttle should not have been invoked after 10ms of 2nd call (throttled with 1s internal clock)"
+ );
+
+ throttledFunc.flush();
+ await nextTick();
+ assert.verifySteps(
+ ['throttle_observed_invoke_2'],
+ "inner function of throttle should have been invoked from 2nd call after flush"
+ );
+
+ throttledFunc().then(() => assert.step('throttle_observed_invoke_3'));
+ await nextTick();
+ await this.env.testUtils.advanceTime(999);
+ assert.verifySteps(
+ [],
+ "inner function of throttle should not have been invoked after 999ms of 3rd call (throttled with 1s internal clock)"
+ );
+
+ await this.env.testUtils.advanceTime(1);
+ assert.verifySteps(
+ ['throttle_observed_invoke_3'],
+ "inner function of throttle should not have been invoked after 999ms of 3rd call (throttled with 1s internal clock)"
+ );
+});
+
+QUnit.test('cancel throttled call', async function (assert) {
+ assert.expect(10);
+
+ await this.start({
+ hasTimeControl: true,
+ });
+
+ const throttledFunc = throttle(
+ this.env,
+ () => {},
+ 1000,
+ { silentCancelationErrors: false }
+ );
+ this.throttles.push(throttledFunc);
+
+ throttledFunc().then(() => assert.step('throttle_observed_invoke_1'));
+ await nextTick();
+ assert.verifySteps(
+ ['throttle_observed_invoke_1'],
+ "inner function of throttle should have been invoked on 1st call (immediate return)"
+ );
+
+ throttledFunc()
+ .then(() => {
+ throw new Error("2nd throttle call should not be resolved (should have been canceled)");
+ })
+ .catch(error => {
+ assert.ok(
+ error instanceof ThrottleCanceledError,
+ "Should generate a Throttle canceled error (from `.cancel()`)"
+ );
+ assert.step('throttle_canceled');
+ });
+ await nextTick();
+ assert.verifySteps(
+ [],
+ "inner function of throttle should not have been immediately invoked after 2nd call immediately after 1st call (throttled with 1s internal clock)"
+ );
+
+ await this.env.testUtils.advanceTime(500);
+ assert.verifySteps(
+ [],
+ "inner function of throttle should not have been invoked after 500ms of 2nd call (throttled with 1s internal clock)"
+ );
+
+ throttledFunc.cancel();
+ await nextTick();
+ assert.verifySteps(
+ ['throttle_canceled'],
+ "2nd throttle function call should have been canceled"
+ );
+
+ throttledFunc().then(() => assert.step('throttle_observed_invoke_3'));
+ await nextTick();
+ assert.verifySteps(
+ [],
+ "3rd throttle function call should not have invoked inner function yet (cancel reuses inner clock of throttle)"
+ );
+
+ await this.env.testUtils.advanceTime(500);
+ assert.verifySteps(
+ ['throttle_observed_invoke_3'],
+ "3rd throttle function call should have invoke inner function after 500ms (cancel reuses inner clock of throttle which was at 500ms in, throttle set at 1ms)"
+ );
+});
+
+QUnit.test('clear throttled call', async function (assert) {
+ assert.expect(9);
+
+ await this.start({
+ hasTimeControl: true,
+ });
+
+ const throttledFunc = throttle(
+ this.env,
+ () => {},
+ 1000,
+ { silentCancelationErrors: false }
+ );
+ this.throttles.push(throttledFunc);
+
+ throttledFunc().then(() => assert.step('throttle_observed_invoke_1'));
+ await nextTick();
+ assert.verifySteps(
+ ['throttle_observed_invoke_1'],
+ "inner function of throttle should have been invoked on 1st call (immediate return)"
+ );
+
+ throttledFunc()
+ .then(() => {
+ throw new Error("2nd throttle call should not be resolved (should have been canceled from clear)");
+ })
+ .catch(error => {
+ assert.ok(
+ error instanceof ThrottleCanceledError,
+ "Should generate a Throttle canceled error (from `.clear()`)"
+ );
+ assert.step('throttle_canceled');
+ });
+ await nextTick();
+ assert.verifySteps(
+ [],
+ "inner function of throttle should not have been immediately invoked after 2nd call immediately after 1st call (throttled with 1s internal clock)"
+ );
+
+ await this.env.testUtils.advanceTime(500);
+ assert.verifySteps(
+ [],
+ "inner function of throttle should not have been invoked after 500ms of 2nd call (throttled with 1s internal clock)"
+ );
+
+ throttledFunc.clear();
+ await nextTick();
+ assert.verifySteps(
+ ['throttle_canceled'],
+ "2nd throttle function call should have been canceled (from `.clear()`)"
+ );
+
+ throttledFunc().then(() => assert.step('throttle_observed_invoke_3'));
+ await nextTick();
+ assert.verifySteps(
+ ['throttle_observed_invoke_3'],
+ "3rd throttle function call should have invoke inner function immediately (`.clear()` flushes throttle)"
+ );
+});
+
+});
+});
+});
+
+});