8
0
Просмотр исходного кода

Added tests for the throttle, moved throttle to separate file.

Maciej Bukowski 7 лет назад
Родитель
Сommit
196cbf1866

+ 1 - 60
packages/ckeditor5-autosave/src/autosave.js

@@ -10,6 +10,7 @@
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import PendingActions from '@ckeditor/ckeditor5-core/src/pendingactions';
 import DomEmitterMixin from '@ckeditor/ckeditor5-utils/src/dom/emittermixin';
+import throttle from './throttle';
 
 /* globals window */
 
@@ -233,63 +234,3 @@ export default class Autosave extends Plugin {
  * @method #save
  * @returns {Promise.<*>|undefined}
  */
-
-/**
- * Throttle function - a helper that provides ability to specify minimum time gap between calling the original function.
- * Comparing to the lodash implementation, this provides an information if calling the throttled function will result in
- * calling the original function.
- *
- * @private
- * @param {Function} fn Original function that will be called.
- * @param {Number} wait Minimum amount of time between original function calls.
- */
-function throttle( fn, wait ) {
-	// Time in ms of the last call.
-	let lastCallTime = 0;
-
-	// Timeout id that enables stopping scheduled call.
-	let timeoutId = null;
-
-	// @returns {Boolean} `true` if the original function was or will be called.
-	function throttledFn() {
-		const now = Date.now();
-
-		// Call instantly, as the fn wasn't called within the `time` period.
-		if ( now > lastCallTime + wait ) {
-			call();
-			return true;
-		}
-
-		// Cancel call, as the next call is scheduled.
-		if ( timeoutId ) {
-			return false;
-		}
-
-		// Set timeout, so the fn will be called `time` ms after the last call.
-		timeoutId = window.setTimeout( call, lastCallTime + wait - now );
-
-		return true;
-	}
-
-	throttledFn.flush = flush;
-
-	function flush() {
-		if ( timeoutId ) {
-			window.clearTimeout( timeoutId );
-			timeoutId = null;
-
-			call();
-		}
-
-		lastCallTime = 0;
-	}
-
-	// Calls the original function and updates internals.
-	function call() {
-		lastCallTime = Date.now();
-
-		fn();
-	}
-
-	return throttledFn;
-}

+ 68 - 0
packages/ckeditor5-autosave/src/throttle.js

@@ -0,0 +1,68 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module autosave/throttle
+ */
+
+/* globals window */
+
+/**
+ * Throttle function - a helper that provides ability to specify minimum time gap between calling the original function.
+ * Comparing to the lodash implementation, this provides an information if calling the throttled function will result in
+ * calling the original function.
+ *
+ * @param {Function} fn Original function that will be called.
+ * @param {Number} wait Minimum amount of time between original function calls.
+ */
+export default function throttle( fn, wait ) {
+	// Time in ms of the last call.
+	let lastCallTime = 0;
+
+	// Timeout id that enables stopping scheduled call.
+	let timeoutId = null;
+
+	// @returns {Boolean} `true` if the original function was or will be called.
+	function throttledFn() {
+		const now = Date.now();
+
+		// Cancel call, as the next call is scheduled.
+		if ( timeoutId ) {
+			return false;
+		}
+
+		// Call instantly, as the fn wasn't called within the `time` period.
+		if ( now > lastCallTime + wait ) {
+			call();
+			return true;
+		}
+
+		// Set timeout, so the fn will be called `time` ms after the last call.
+		timeoutId = window.setTimeout( call, lastCallTime + wait - now );
+
+		return true;
+	}
+
+	throttledFn.flush = flush;
+
+	function flush() {
+		if ( timeoutId ) {
+			window.clearTimeout( timeoutId );
+			call();
+		}
+
+		lastCallTime = 0;
+	}
+
+	// Calls the original function and updates internals.
+	function call() {
+		lastCallTime = Date.now();
+		timeoutId = null;
+
+		fn();
+	}
+
+	return throttledFn;
+}

+ 6 - 6
packages/ckeditor5-autosave/tests/autosave.js

@@ -435,10 +435,10 @@ describe( 'Autosave', () => {
 				} );
 		} );
 	} );
-
-	function wait( time ) {
-		return new Promise( res => {
-			window.setTimeout( res, time );
-		} );
-	}
 } );
+
+function wait( time ) {
+	return new Promise( res => {
+		window.setTimeout( res, time );
+	} );
+}

+ 165 - 0
packages/ckeditor5-autosave/tests/throttle.js

@@ -0,0 +1,165 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import throttle from '../src/throttle';
+
+describe( 'throttle', () => {
+	const sandbox = sinon.sandbox.create( {
+		useFakeTimers: true
+	} );
+
+	beforeEach( () => {
+		sandbox.useFakeTimers( { now: 1000 } );
+	} );
+
+	afterEach( () => {
+		sandbox.restore();
+	} );
+
+	it( 'should run first call synchronously', () => {
+		const spy = sinon.spy();
+		const throttledFn = throttle( spy, 100 );
+
+		throttledFn();
+
+		sinon.assert.calledOnce( spy );
+
+		sandbox.clock.runAll();
+		sinon.assert.calledOnce( spy );
+	} );
+
+	it( 'should run next calls at the specified maximum rate', () => {
+		const spy = sinon.spy();
+		const throttledFn = throttle( spy, 100 );
+
+		throttledFn();
+		throttledFn();
+
+		sinon.assert.calledOnce( spy );
+
+		sandbox.clock.tick( 99 );
+
+		sinon.assert.calledOnce( spy );
+
+		sandbox.clock.tick( 1 );
+
+		sinon.assert.calledTwice( spy );
+		sandbox.clock.runAll();
+	} );
+
+	it( 'should run next calls at the specified maximum rate', () => {
+		const spy = sinon.spy();
+		const throttledFn = throttle( spy, 100 );
+
+		throttledFn();
+		throttledFn();
+
+		sinon.assert.calledOnce( spy );
+
+		sandbox.clock.tick( 99 );
+
+		sinon.assert.calledOnce( spy );
+
+		sandbox.clock.tick( 1 );
+
+		sinon.assert.calledTwice( spy );
+
+		sandbox.clock.runAll();
+		sinon.assert.calledTwice( spy );
+	} );
+
+	it( 'should skip the call if another call is scheduled', () => {
+		const spy = sinon.spy();
+		const throttledFn = throttle( spy, 100 );
+
+		const isFirstInvoked = throttledFn();
+		const willSecondInvoke = throttledFn();
+		const willThirdInvoke = throttledFn();
+
+		expect( isFirstInvoked ).to.be.true;
+		expect( willSecondInvoke ).to.be.true;
+		expect( willThirdInvoke ).to.be.false;
+
+		sandbox.clock.runAll();
+		sinon.assert.calledTwice( spy );
+	} );
+
+	it( 'should call the next call after the specified amount of time', () => {
+		const spy = sinon.spy();
+		const throttledFn = throttle( spy, 100 );
+
+		throttledFn();
+		throttledFn();
+
+		sandbox.clock.tick( 50 );
+
+		sinon.assert.calledOnce( spy );
+
+		sandbox.clock.tick( 50 );
+
+		sinon.assert.calledTwice( spy );
+
+		throttledFn();
+
+		sandbox.clock.tick( 100 );
+
+		sinon.assert.calledThrice( spy );
+	} );
+
+	describe( 'flush', () => {
+		it( 'should be provide as a method on the throttled function', () => {
+			const spy = sinon.spy();
+			const throttledFn = throttle( spy, 100 );
+
+			expect( throttledFn.flush ).to.be.a( 'function' );
+		} );
+
+		it( 'should enable calling the throttled call immediately', () => {
+			const spy = sinon.spy();
+			const throttledFn = throttle( spy, 100 );
+
+			throttledFn();
+			throttledFn();
+
+			sinon.assert.calledOnce( spy );
+
+			throttledFn.flush();
+			sinon.assert.calledTwice( spy );
+
+			sandbox.clock.runAll();
+			sinon.assert.calledTwice( spy );
+		} );
+
+		it( 'should do nothing if there is no scheduled call', () => {
+			const spy = sinon.spy();
+			const throttledFn = throttle( spy, 100 );
+
+			throttledFn();
+
+			sinon.assert.calledOnce( spy );
+
+			throttledFn.flush();
+			sinon.assert.calledOnce( spy );
+
+			sandbox.clock.runAll();
+			sinon.assert.calledOnce( spy );
+		} );
+
+		it( 'should enable calling after the flushed call', () => {
+			const spy = sinon.spy();
+			const throttledFn = throttle( spy, 100 );
+
+			throttledFn();
+			throttledFn();
+			throttledFn.flush();
+			throttledFn();
+
+			sinon.assert.calledThrice( spy );
+
+			sandbox.clock.runAll();
+			sinon.assert.calledThrice( spy );
+		} );
+	} );
+} );