Files
lodash/test/memoize.spec.js
2023-09-21 07:45:49 -07:00

176 lines
4.8 KiB
JavaScript

import lodashStable from 'lodash';
import { noop, stubTrue, identity } from './utils';
import memoize from '../src/memoize';
import isFunction from '../src/isFunction';
describe('memoize', () => {
function CustomCache() {
this.clear();
}
CustomCache.prototype = {
clear: function () {
this.__data__ = [];
return this;
},
get: function (key) {
const entry = lodashStable.find(this.__data__, ['key', key]);
return entry && entry.value;
},
has: function (key) {
return lodashStable.some(this.__data__, ['key', key]);
},
set: function (key, value) {
this.__data__.push({ key: key, value: value });
return this;
},
};
function ImmutableCache() {
this.__data__ = [];
}
ImmutableCache.prototype = lodashStable.create(CustomCache.prototype, {
constructor: ImmutableCache,
clear: function () {
return new ImmutableCache();
},
set: function (key, value) {
const result = new ImmutableCache();
result.__data__ = this.__data__.concat({ key: key, value: value });
return result;
},
});
it('should memoize results based on the first argument given', () => {
const memoized = memoize((a, b, c) => a + b + c);
expect(memoized(1, 2, 3)).toBe(6);
expect(memoized(1, 3, 5)).toBe(6);
});
it('should support a `resolver`', () => {
const fn = function (a, b, c) {
return a + b + c;
};
const memoized = memoize(fn, fn);
expect(memoized(1, 2, 3)).toBe(6);
expect(memoized(1, 3, 5)).toBe(9);
});
it('should use `this` binding of function for `resolver`', () => {
const fn = function (a, b, c) {
return a + this.b + this.c;
};
const memoized = memoize(fn, fn);
const object = { memoized: memoized, b: 2, c: 3 };
expect(object.memoized(1)).toBe(6);
object.b = 3;
object.c = 5;
expect(object.memoized(1)).toBe(9);
});
it('should throw a TypeError if `resolve` is truthy and not a function', () => {
expect(() => {
memoize(noop, true);
}).toThrowError(TypeError);
});
it('should not error if `resolver` is nullish', () => {
const values = [, null, undefined];
const expected = lodashStable.map(values, stubTrue);
const actual = lodashStable.map(values, (resolver, index) => {
try {
return isFunction(index ? memoize(noop, resolver) : memoize(noop));
} catch (e) {}
});
expect(actual).toEqual(expected);
});
it('should check cache for own properties', () => {
const props = [
'constructor',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'toLocaleString',
'toString',
'valueOf',
];
const memoized = memoize(identity);
const actual = lodashStable.map(props, (value) => memoized(value));
expect(actual).toEqual(props);
});
it('should cache the `__proto__` key', () => {
const array = [];
const key = '__proto__';
lodashStable.times(2, (index) => {
let count = 0;
const resolver = index ? identity : undefined;
const memoized = memoize(() => {
count++;
return array;
}, resolver);
const cache = memoized.cache;
memoized(key);
memoized(key);
expect(count).toBe(1);
expect(cache.get(key)).toBe(array);
expect(cache.__data__ instanceof Array).toBe(false);
expect(cache.delete(key)).toBe(true);
});
});
it('should allow `_.memoize.Cache` to be customized', () => {
const oldCache = memoize.Cache;
memoize.Cache = CustomCache;
const memoized = memoize((object) => object.id);
const cache = memoized.cache;
const key1 = { id: 'a' };
const key2 = { id: 'b' };
expect(memoized(key1)).toBe('a');
expect(cache.has(key1)).toBe(true);
expect(memoized(key2)).toBe('b');
expect(cache.has(key2)).toBe(true);
memoize.Cache = oldCache;
});
it('should works with an immutable `_.memoize.Cache` ', () => {
const oldCache = memoize.Cache;
memoize.Cache = ImmutableCache;
const memoized = memoize((object) => object.id);
const key1 = { id: 'a' };
const key2 = { id: 'b' };
memoized(key1);
memoized(key2);
const cache = memoized.cache;
expect(cache.has(key1)).toBe(true);
expect(cache.has(key2)).toBe(true);
memoize.Cache = oldCache;
});
});