Commit 5d16ecf2 authored by Mickaël Bourgier's avatar Mickaël Bourgier
Browse files

First commit

parents
Pipeline #968 passed with stage
in 1 minute and 9 seconds
/dist/
/node_modules/
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:jest/recommended"
],
"parser": "babel-eslint",
"plugins": [
"jest"
],
"rules": {
"no-alert": "error",
"no-console": "error"
}
}
/dist/
/node_modules/
image: node:12.8.1-alpine
test:
stage: test
coverage: '/^\s*Lines\s*:\s*(\d+.\d+\%)/'
before_script:
- yarn install
script:
- yarn test --coverage
const tasks = arr => arr.join(' && ');
module.exports = {
hooks: {
'pre-commit': tasks(['yarn prettier', 'yarn lint'])
}
};
/dist/
/node_modules/
# @webalt/utils
[![pipeline status](https://git.webalternatif.com/webf/js-utils/badges/master/pipeline.svg)](https://git.webalternatif.com/webf/js-utils/commits/master)
[![coverage report](https://git.webalternatif.com/webf/js-utils/badges/master/coverage.svg)](https://git.webalternatif.com/webf/js-utils/commits/master)
## Installation
```shell script
npm install --save @webalt/utils # with npm
yarn add @webalt/utils # with yarn
```
## Usage
```js
import { isNumber } from '@webalt/utils';
console.log(isNumber(3.14));
```
## Run tests
```shell script
yarn test
```
const test = process.env.NODE_ENV === 'test';
const cjs = process.env.BABEL_ENV === 'cjs';
module.exports = {
presets: [
[
'@babel/preset-env',
{
modules: test || cjs ? 'cjs' : false
}
]
],
// plugins: ['@babel/plugin-proposal-class-properties', 'dev-expression'],
ignore: test ? [] : [/\.test\.js/]
};
module.exports = {
coverageReporters: ['text-summary'],
roots: ['<rootDir>/src']
};
{
"name": "@webalt/utils",
"version": "1.0.0",
"description": "A set of utility functions",
"keywords": [
"lib",
"utils",
"webf"
],
"author": "Web|Alternatif <support@webalternatif.com>",
"license": "MIT",
"homepage": "https://git.webalternatif.com/webf/js-utils",
"bugs": {
"url": "https://git.webalternatif.com/webf/js-utils/issues"
},
"repository": {
"type": "git",
"url": "https://git.webalternatif.com/webf/js-utils.git"
},
"files": [
"dist",
"src"
],
"main": "dist/cjs/index.js",
"module": "dist/es/index.js",
"scripts": {
"build": "rm -rf dist && yarn build:cjs && yarn build:es",
"build:cjs": "BABEL_ENV=cjs babel src --out-dir dist/cjs",
"build:es": "BABEL_ENV=es babel src --out-dir dist/es",
"lint": "eslint ./src",
"prepublishOnly": "yarn prettier && yarn lint && yarn build",
"prettier": "prettier --write './src/**/*.js'",
"test": "jest"
},
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"babel-eslint": "^10.0.2",
"babel-jest": "^24.9.0",
"eslint": "^6.2.0",
"eslint-plugin-jest": "^22.15.1",
"husky": "^3.0.4",
"jest": "^24.9.0",
"prettier": "1.18.2"
}
}
module.exports = {
singleQuote: true,
jsxSingleQuote: true
};
/**
* @callback createCachedFunctionCallback
* @param {*} key - Cache key
* @param {...*} args - Arguments given to the final function
*/
/**
* @callback createCachedFunctionResponse
* @param {*} key - Cache key
* @returns {createCachedFunctionResponseResponse} - The final function
*/
/**
* @callback createCachedFunctionResponseResponse
* @param {...*} args
*/
/**
* Returns a cached function, in order to do something like that :
*
* ```js
* const values = {}
* const setter = createCachedFunction((key, value) => {
* values[key] = value
* })
*
* const setA = setter('a')
* const setB = setter('b')
*
* expect(setter('a')).toBe(setA)
*
* setA(42)
* expect(values).toEqual({ a: 42 })
* setB(1337)
* expect(values).toEqual({ a: 42, b: 1337 })
* ```
*
* @param {createCachedFunctionCallback} func
* @returns {createCachedFunctionResponse}
*/
const createCachedFunction = func => {
const keyCache = {};
const cache = {};
return key => {
const stringKey = JSON.stringify(key);
if (!(stringKey in cache)) {
keyCache[stringKey] = key;
cache[stringKey] = (...args) => func(key, ...args);
}
return cache[stringKey];
};
};
export default createCachedFunction;
import createCachedFunction from './createCachedFunction';
describe('createCachedFunction', () => {
it('must call the final function with cache key and arguments', () => {
const fn = jest.fn();
const cachedFn = createCachedFunction(fn);
const f1 = cachedFn('key1');
const f2 = cachedFn('key2');
f1('value1');
f1('value2');
f2('value3', 'value3b');
expect(fn).toHaveBeenCalledTimes(3);
expect(fn).toHaveBeenNthCalledWith(1, 'key1', 'value1');
expect(fn).toHaveBeenNthCalledWith(2, 'key1', 'value2');
expect(fn).toHaveBeenNthCalledWith(3, 'key2', 'value3', 'value3b');
});
it('must return the same function for the same key', () => {
const cachedFn = createCachedFunction(() => {});
expect(cachedFn('key1')).toBe(cachedFn('key1'));
expect(cachedFn('key1')).not.toBe(cachedFn('key2'));
});
test('cache key can be other than a string', () => {
const fn = jest.fn();
const cachedFn = createCachedFunction(fn);
const date = new Date();
expect(cachedFn(date)).toBe(cachedFn(date));
expect(cachedFn([1, 1])).toBe(cachedFn([1, 1]));
expect(cachedFn([1, 1])).not.toBe(cachedFn([1, 2]));
expect(cachedFn({ a: 1 })).toBe(cachedFn({ a: 1 }));
expect(cachedFn({ a: 1 })).not.toBe(cachedFn({ a: 2 }));
const f = cachedFn([1, 2]);
f(1);
f(2, 3);
expect(fn).toHaveBeenCalledTimes(2);
expect(fn).toHaveBeenNthCalledWith(1, [1, 2], 1);
expect(fn).toHaveBeenNthCalledWith(2, [1, 2], 2, 3);
});
});
/**
* Returns a function that will not be called until the last **call** is at
* least `delay` ms old.
*
* @param {function} callback
* @param {number} delay
* @returns {function}
*/
const debounce = (callback, delay) => {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => callback(...args), delay);
};
};
export default debounce;
import debounce from './debounce';
describe('debounce', () => {
beforeEach(jest.useFakeTimers);
afterEach(jest.clearAllTimers);
it('must wait the delay after the last call before executing the callback with the last arguments', () => {
const fn = jest.fn();
const debouncedFn = debounce(fn, 200);
expect(fn).not.toHaveBeenCalled();
debouncedFn('first call');
expect(fn).not.toHaveBeenCalled();
debouncedFn('second call');
expect(fn).not.toHaveBeenCalled();
jest.advanceTimersByTime(199);
expect(fn).not.toHaveBeenCalled();
debouncedFn('third call');
expect(fn).not.toHaveBeenCalled();
jest.advanceTimersByTime(199);
expect(fn).not.toHaveBeenCalled();
jest.advanceTimersByTime(1);
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenLastCalledWith('third call');
});
test('debounced function can be called with multiple arguments', () => {
const fn = jest.fn();
const debouncedFn = debounce(fn, 200);
expect(fn).not.toHaveBeenCalled();
const args = ['first', ['awesome'], { call: 42 }, new Date()];
debouncedFn(...args);
jest.advanceTimersByTime(200);
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenLastCalledWith(...args);
});
});
/**
* Returns a function that will not be called until the last **execution** is
* less than `delay` ms old.
*
* @param {function} callback
* @param {number} delay
* @returns {function}
*/
const throttle = (callback, delay) => {
let last, timer;
return (...args) => {
const now = +new Date();
if (last && now < last + delay) {
clearTimeout(timer);
timer = setTimeout(() => {
last = now;
callback(...args);
}, delay - (now - last));
} else {
last = now;
callback(...args);
}
};
};
export default throttle;
import throttle from './throttle';
describe('throttle', () => {
let advanceTimersByTime, realDate;
beforeEach(() => {
jest.useFakeTimers();
let currentDate = new Date('2019-01-01');
realDate = Date;
global.Date = class extends Date {
constructor(date = currentDate) {
super(date);
}
};
advanceTimersByTime = jest.advanceTimersByTime;
jest.advanceTimersByTime = time => {
advanceTimersByTime(time);
currentDate = new realDate(currentDate.getTime() + time);
};
});
afterEach(() => {
global.Date = realDate;
jest.advanceTimersByTime = advanceTimersByTime;
jest.clearAllTimers();
});
it('must not be called if the last execution is sooner than the delay', () => {
const fn = jest.fn();
const throttledFn = throttle(fn, 200);
expect(fn).not.toHaveBeenCalled();
throttledFn('first call');
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenLastCalledWith('first call');
throttledFn('second call');
expect(fn).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(199);
expect(fn).toHaveBeenCalledTimes(1);
throttledFn('third call');
expect(fn).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(1);
expect(fn).toHaveBeenCalledTimes(2);
expect(fn).toHaveBeenLastCalledWith('third call');
});
test('throttled function can be called with multiple arguments', () => {
const fn = jest.fn();
const throttledFn = throttle(fn, 200);
expect(fn).not.toHaveBeenCalled();
const args = ['first', ['awesome'], { call: 42 }, new Date()];
throttledFn(...args);
expect(fn).toHaveBeenCalledTimes(1);
expect(fn).toHaveBeenLastCalledWith(...args);
});
});
export {
default as createCachedFunction
} from './function/createCachedFunction';
export { default as debounce } from './function/debounce';
export { default as throttle } from './function/throttle';
export { default as randomAlpha } from './random/randomAlpha.js';
export { default as randomAlphaLc } from './random/randomAlphaLc.js';
export { default as randomAlphaNum } from './random/randomAlphaNum.js';
export { default as randomAlphaNumLc } from './random/randomAlphaNumLc.js';
export { default as randomAlphaNumUc } from './random/randomAlphaNumUc.js';
export { default as randomAlphaUc } from './random/randomAlphaUc.js';
export { default as randomBetween } from './random/randomBetween.js';
export { default as randomIn } from './random/randomIn.js';
export { default as randomNum } from './random/randomNum.js';
export { default as lcfirst } from './string/lcfirst';
export { default as ucfirst } from './string/ucfirst';
export { default as isArray } from './test/isArray';
export { default as isBoolean, default as isBool } from './test/isBoolean';
export { default as isDate } from './test/isDate';
export { default as isDocument } from './test/isDocument';
export { default as isDomElement } from './test/isDomElement';
export { default as isEvent } from './test/isEvent';
export { default as isFloat } from './test/isFloat';
export { default as isFunction } from './test/isFunction';
export {
default as isInteger,
default as isInt,
default as isNumber
} from './test/isInteger';
export { default as isObject } from './test/isObject';
export { default as isPlainObject } from './test/isPlainObject';
export { default as isRegExp } from './test/isRegExp';
export { default as isString } from './test/isString';
export { default as isUndefined } from './test/isUndefined';
export { default as isWindow } from './test/isWindow';
/**
* @param {number} value
* @param {?number} min
* @param {?number} max
* @returns {number}
*/
const between = (value, min, max) => {
if (min === null) {
return max === null ? value : Math.min(max, value);
}
return max === null
? Math.max(min, value)
: Math.max(min, Math.min(max, value));
};
export default between;
import between from './between';
describe('between', () => {
it('must return a number between the given ones', () => {
expect(between(1, 10, 20)).toEqual(10);
expect(between(15, 10, 20)).toEqual(15);
expect(between(30, 10, 20)).toEqual(20);
expect(between(1, null, 20)).toEqual(1);
expect(between(30, 10, null)).toEqual(30);
expect(between(42, null, null)).toEqual(42);
expect(between(-20, -10, 10)).toEqual(-10);
expect(between(0, -10, 10)).toEqual(0);
expect(between(20, -10, 10)).toEqual(10);
});
});
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment