Skip to content

Instantly share code, notes, and snippets.

@mattmcmanus
Created June 8, 2022 16:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattmcmanus/3f27e7994e0913e0a8e87af4cf0d51b5 to your computer and use it in GitHub Desktop.
Save mattmcmanus/3f27e7994e0913e0a8e87af4cf0d51b5 to your computer and use it in GitHub Desktop.
Debounce helper spike
import { helper } from '@ember/component/helper';
import { isDestroyed } from '@ember/destroyable';
import { buildWaiter } from '@ember/test-waiters';
const testWaiter = buildWaiter('debounce-helper');
// WeakMap<Function, { timeoutId, asyncToken }>();
const TIMERS = new WeakMap();
function debounceHelper([context, action], { timeout }) {
return function(...args) {
if (TIMERS.has(action)) {
const { timeoutId, asyncToken } = TIMERS.get(action);
clearTimeout(timeoutId);
testWaiter.endAsync(asyncToken);
}
const asyncToken = testWaiter.beginAsync();
const timeoutId = setTimeout(() => {
if (isDestroyed(context)) {
testWaiter.endAsync(asyncToken);
return;
}
try {
action.call(context, ...args);
}
finally {
testWaiter.endAsync(asyncToken);
}
}, timeout);
TIMERS.set(
action, { timeoutId, asyncToken },
);
};
}
export default helper(debounceHelper);
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, click, settled, typeIn } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Helper | debounce', function(hooks) {
setupRenderingTest(hooks);
test('Debounces the action calls', async function(assert) {
this.set('actionToCall', function() {
assert.step('clicked');
});
await render(hbs`
{{!-- template-lint-disable data-qid-interactive --}}
<button type="button"
{{on 'click' (debounce this this.actionToCall timeout=100)}}
>Click Me</button>
`);
// Don't wait here, we want to click a bunch and wait for settled later
click('button');
click('button');
click('button');
click('button');
await settled();
assert.verifySteps(['clicked']);
});
test('Doesnt error when the context is gone', async function(assert) {
this.set('actionToCall', function() {
assert.step('clicked');
});
await render(hbs`
{{!-- template-lint-disable data-qid-interactive --}}
<button type="button"
{{on 'click' (debounce this this.actionToCall timeout=100)}}
>Click Me</button>
`);
click('button'); // Don't wait because we want the test to finish
assert.verifySteps([]);
});
test('Works on an input as well', async function(assert) {
this.set('value', null);
this.set('setValue', function(value) {
assert.step(value);
this.set('value', value);
});
await render(hbs`
{{!-- template-lint-disable require-input-label --}}
<input
data-qid-input
value={{this.value}}
{{on 'keyup' (pick 'target.value' (debounce this this.setValue timeout=100))}}
/>
`);
await typeIn('[data-qid-input]', 'farm');
assert.verifySteps(['farm']);
assert.equal(this.value, 'farm');
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment