2021-07-10 22:32:47 +00:00
|
|
|
/* eslint-disable no-magic-numbers */
|
2020-03-29 13:43:52 +00:00
|
|
|
import { expect } from 'chai';
|
2021-07-25 00:41:28 +00:00
|
|
|
import { array, dictionary, integer, nat, record, string, tuple } from 'fast-check';
|
|
|
|
import { over } from 'mocha-foam';
|
2020-03-29 13:43:52 +00:00
|
|
|
|
|
|
|
import { NotFoundError } from '../../src/error/NotFoundError';
|
|
|
|
import {
|
|
|
|
entriesOf,
|
|
|
|
getHead,
|
|
|
|
getHeadOrDefault,
|
|
|
|
getOrDefault,
|
|
|
|
makeDict,
|
|
|
|
makeMap,
|
2020-08-01 15:11:29 +00:00
|
|
|
mergeMap,
|
2020-03-29 13:43:52 +00:00
|
|
|
mustGet,
|
2020-08-01 15:11:29 +00:00
|
|
|
normalizeMap,
|
2020-03-29 13:43:52 +00:00
|
|
|
pairsToMap,
|
2020-03-30 23:09:36 +00:00
|
|
|
pushMergeMap,
|
2020-08-01 15:11:29 +00:00
|
|
|
setOrPush,
|
2020-03-31 13:29:47 +00:00
|
|
|
} from '../../src/Map';
|
2020-03-29 13:43:52 +00:00
|
|
|
|
|
|
|
const DEFAULT_VALUE = 'default';
|
|
|
|
const mapKey = 'key';
|
|
|
|
const mapValue = 'value';
|
|
|
|
const singleItem = new Map([[mapKey, mapValue]]);
|
|
|
|
const multiItem = new Map([
|
|
|
|
[mapKey, [mapValue]],
|
2021-07-10 22:32:47 +00:00
|
|
|
// eslint-disable-next-line no-null/no-null,@typescript-eslint/no-explicit-any
|
2020-03-29 13:43:52 +00:00
|
|
|
['nilKey', null as any],
|
2021-07-10 22:32:47 +00:00
|
|
|
// eslint-disable-next-line no-null/no-null
|
2020-03-29 13:43:52 +00:00
|
|
|
['nilValue', [null]],
|
|
|
|
]);
|
|
|
|
|
2020-06-30 13:14:30 +00:00
|
|
|
describe('map utils', async () => {
|
|
|
|
describe('make dict', async () => {
|
|
|
|
it('should return an empty dict for nil values', async () => {
|
2020-03-29 13:43:52 +00:00
|
|
|
/* eslint-disable-next-line no-null/no-null */
|
|
|
|
expect(makeDict(null)).to.deep.equal({});
|
|
|
|
expect(makeDict(undefined)).to.deep.equal({});
|
|
|
|
});
|
|
|
|
|
2020-06-30 13:14:30 +00:00
|
|
|
it('should return an existing dict', async () => {
|
2020-03-29 13:43:52 +00:00
|
|
|
const input = {};
|
|
|
|
expect(makeDict(input)).to.equal(input);
|
|
|
|
});
|
2021-08-08 19:58:43 +00:00
|
|
|
|
|
|
|
over('maps of strings', dictionary(string(), string()), (it) => {
|
|
|
|
it('should convert map to dict', async (data) => {
|
|
|
|
const map = new Map(Object.entries(data));
|
|
|
|
expect(makeDict(map)).to.deep.equal(data);
|
|
|
|
});
|
|
|
|
});
|
2020-03-29 13:43:52 +00:00
|
|
|
});
|
|
|
|
|
2020-06-30 13:14:30 +00:00
|
|
|
describe('make map', async () => {
|
|
|
|
it('should convert objects to maps', async () => {
|
2020-03-29 13:43:52 +00:00
|
|
|
const data = {
|
|
|
|
bar: '2',
|
|
|
|
foo: '1',
|
|
|
|
};
|
|
|
|
const map = makeMap(data);
|
|
|
|
|
|
|
|
expect(Array.from(map.entries())).to.deep.equal(Object.entries(data));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-06-30 13:14:30 +00:00
|
|
|
describe('must get helper', async () => {
|
|
|
|
it('should get existing keys', async () => {
|
2020-03-29 13:43:52 +00:00
|
|
|
expect(mustGet(singleItem, mapKey)).to.equal(mapValue);
|
|
|
|
});
|
|
|
|
|
2020-06-30 13:14:30 +00:00
|
|
|
it('should throw on missing keys', async () => {
|
2020-03-29 13:43:52 +00:00
|
|
|
expect(() => {
|
|
|
|
mustGet(singleItem, 'nope');
|
|
|
|
}).to.throw(NotFoundError);
|
|
|
|
});
|
2021-07-25 00:41:28 +00:00
|
|
|
|
|
|
|
over('map with truthy values', array(tuple(string(), nat()), { minLength: 1 }).map((t) => new Map(t)), (it) => {
|
|
|
|
it('should return a value', (val) => {
|
|
|
|
for (const [key, _value] of val) {
|
|
|
|
expect(() => mustGet(val, key)).not.to.throw(Error);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2020-03-29 13:43:52 +00:00
|
|
|
});
|
|
|
|
|
2020-06-30 13:14:30 +00:00
|
|
|
describe('get head helper', async () => {
|
|
|
|
it('should get the first item from existing keys', async () => {
|
2020-03-29 13:43:52 +00:00
|
|
|
expect(getHead(multiItem, mapKey)).to.equal(mapValue);
|
|
|
|
});
|
|
|
|
|
2020-06-30 13:14:30 +00:00
|
|
|
it('should throw on missing keys', async () => {
|
2020-03-29 13:43:52 +00:00
|
|
|
expect(() => {
|
|
|
|
getHead(multiItem, 'nope');
|
|
|
|
}).to.throw(NotFoundError);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-06-30 13:14:30 +00:00
|
|
|
describe('get head or default helper', async () => {
|
|
|
|
it('should get the first item from existing keys', async () => {
|
2020-03-29 13:43:52 +00:00
|
|
|
expect(getHeadOrDefault(multiItem, mapKey, 'nope')).to.equal(mapValue);
|
|
|
|
});
|
|
|
|
|
2020-06-30 13:14:30 +00:00
|
|
|
it('should get the default for missing keys', async () => {
|
2020-03-29 13:43:52 +00:00
|
|
|
expect(getHeadOrDefault(multiItem, 'nope', mapValue)).to.equal(mapValue);
|
|
|
|
});
|
|
|
|
|
2020-06-30 13:14:30 +00:00
|
|
|
it('should return the default value for nil values', async () => {
|
2020-03-29 13:43:52 +00:00
|
|
|
expect(getHeadOrDefault(multiItem, 'nilValue', mapValue)).to.equal(mapValue);
|
|
|
|
});
|
|
|
|
|
2020-06-30 13:14:30 +00:00
|
|
|
it('should return the default value for nil keys', async () => {
|
2020-03-29 13:43:52 +00:00
|
|
|
expect(getHeadOrDefault(multiItem, 'nilKey', mapValue)).to.equal(mapValue);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('get or default helper', () => {
|
|
|
|
it('should get the item for existing keys', () => {
|
|
|
|
expect(getOrDefault(singleItem, mapKey, DEFAULT_VALUE)).to.equal(mapValue);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should get the item for missing keys', () => {
|
|
|
|
expect(getOrDefault(singleItem, 'missing', DEFAULT_VALUE)).to.equal(DEFAULT_VALUE);
|
|
|
|
});
|
|
|
|
|
2020-03-30 23:09:36 +00:00
|
|
|
it('should return the default for nil values', () => {
|
|
|
|
expect(getOrDefault(multiItem, 'nilKey', DEFAULT_VALUE)).to.equal(DEFAULT_VALUE);
|
|
|
|
});
|
|
|
|
|
2020-03-29 13:43:52 +00:00
|
|
|
xit('should return falsy values for existing keys');
|
2021-07-25 00:41:28 +00:00
|
|
|
|
|
|
|
over('map with truthy values', array(tuple(string(), nat()), { minLength: 1 }).map((t) => new Map(t)), (it) => {
|
|
|
|
it('should return a value other than missing', (val) => {
|
|
|
|
const missing = -1;
|
|
|
|
for (const [key, _value] of val) {
|
|
|
|
expect(getOrDefault(val, key, missing)).not.to.equal(missing);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2020-03-29 13:43:52 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('pairs to map helper', () => {
|
|
|
|
it('should convert pairs', () => {
|
|
|
|
const result = pairsToMap([{
|
|
|
|
name: 'foo',
|
|
|
|
value: 3,
|
|
|
|
}]);
|
|
|
|
expect(result.get('foo')).to.equal(3);
|
|
|
|
});
|
2021-07-25 00:41:28 +00:00
|
|
|
|
|
|
|
over('paired values', array(record({
|
|
|
|
name: string(),
|
|
|
|
value: integer(),
|
|
|
|
})), (it) => {
|
|
|
|
it('should include each unique key', (val) => {
|
|
|
|
const keys = new Set(val.map((pair) => pair.name));
|
|
|
|
const map = pairsToMap(val);
|
|
|
|
expect(map.size).to.equal(keys.size);
|
|
|
|
});
|
|
|
|
});
|
2020-03-29 13:43:52 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('entries of helper', () => {
|
|
|
|
it('should return entries for maps', () => {
|
|
|
|
const input: Array<[string, number]> = [['foo', 1], ['bar', 3]];
|
|
|
|
const value = new Map(input);
|
|
|
|
const result = entriesOf(value);
|
|
|
|
expect(result.length).to.equal(2);
|
|
|
|
expect(result).to.deep.equal(input);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return entries for objects', () => {
|
|
|
|
const result = entriesOf({
|
|
|
|
bar: 3,
|
|
|
|
foo: 1,
|
|
|
|
});
|
|
|
|
expect(result.length).to.equal(2);
|
|
|
|
expect(result[0][0]).to.equal('bar');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should return empty entries for nil values', () => {
|
|
|
|
/* eslint-disable-next-line no-null/no-null */
|
|
|
|
expect(entriesOf(null)).to.deep.equal([]);
|
|
|
|
expect(entriesOf(undefined)).to.deep.equal([]);
|
|
|
|
});
|
2021-07-25 00:41:28 +00:00
|
|
|
|
|
|
|
over('dict objects', dictionary(string(), integer()), (it) => {
|
|
|
|
it('should list the properties', (val) => {
|
|
|
|
const props = entriesOf(val);
|
|
|
|
for (const [key, value] of props) {
|
|
|
|
expect(val[key]).to.equal(value);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2020-03-29 13:43:52 +00:00
|
|
|
});
|
2020-03-30 23:09:36 +00:00
|
|
|
|
|
|
|
describe('set or push helper', () => {
|
|
|
|
xit('should insert new lists');
|
|
|
|
|
|
|
|
it('should wrap new items', () => {
|
|
|
|
const values = new Map<string, Array<string>>([]);
|
|
|
|
setOrPush(values, mapKey, 'fin');
|
|
|
|
|
|
|
|
expect(Array.from(values.keys()).length).to.equal(1);
|
|
|
|
expect(mustGet(values, mapKey).length).to.equal(1);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should append to existing items', () => {
|
|
|
|
const values = new Map(multiItem);
|
|
|
|
setOrPush(values, mapKey, 'fin');
|
|
|
|
|
|
|
|
expect(Array.from(values.keys()).length).to.equal(3);
|
|
|
|
expect(mustGet(values, mapKey).length).to.equal(2);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('merge map helper', () => {
|
|
|
|
it('should combine two maps', () => {
|
|
|
|
const firstValue = new Map([[mapKey, mapValue]]);
|
|
|
|
const otherValue = new Map([['foo', 'bar']]);
|
|
|
|
|
|
|
|
const merged = mergeMap(firstValue, otherValue);
|
|
|
|
|
|
|
|
expect(Array.from(merged.keys()).length).to.equal(2);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('push merge map helper', () => {
|
|
|
|
it('should combine two overlapping maps', () => {
|
|
|
|
const firstValue = new Map([[mapKey, [1]]]);
|
|
|
|
const otherValue = new Map([[mapKey, [2, 3]]]);
|
|
|
|
|
|
|
|
const merged = pushMergeMap(firstValue, otherValue);
|
|
|
|
expect(merged).to.have.keys(mapKey);
|
|
|
|
expect(merged.get(mapKey)).to.deep.equal([1, 2, 3]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('make map helper', () => {
|
|
|
|
it('should make an empty map', () => {
|
|
|
|
expect(makeMap(undefined).size).to.equal(0);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should copy an existing map', () => {
|
|
|
|
const copy = makeMap(singleItem);
|
|
|
|
expect(copy).to.deep.equal(singleItem);
|
|
|
|
expect(copy).not.to.equal(singleItem);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should copy a dict', () => {
|
|
|
|
expect(makeMap({
|
|
|
|
[mapKey]: mapValue,
|
|
|
|
})).to.deep.equal(singleItem);
|
|
|
|
});
|
|
|
|
});
|
2020-03-31 02:31:51 +00:00
|
|
|
|
|
|
|
describe('normalize map helper', () => {
|
|
|
|
it('should convert values into arrays of strings', () => {
|
|
|
|
const banVal = [Symbol()];
|
|
|
|
const initial = new Map<string, unknown>([
|
|
|
|
['bar', 'bin'],
|
|
|
|
['ban', banVal],
|
|
|
|
['toad', {
|
|
|
|
toString() {
|
|
|
|
return 'too';
|
|
|
|
},
|
|
|
|
}],
|
|
|
|
]);
|
|
|
|
|
|
|
|
const normalized = normalizeMap(initial);
|
|
|
|
|
|
|
|
expect(normalized.bar).to.deep.equal(['bin']);
|
|
|
|
expect(normalized.ban).to.equal(banVal);
|
|
|
|
expect(normalized.toad).to.deep.equal(['too']);
|
|
|
|
});
|
|
|
|
|
2020-08-01 15:11:29 +00:00
|
|
|
it('should convert number arguments into string values', () => {
|
|
|
|
const INPUT_VALUE = 123;
|
|
|
|
const input = new Map([
|
|
|
|
['foo', INPUT_VALUE],
|
|
|
|
]);
|
|
|
|
const normalized = normalizeMap(input);
|
|
|
|
|
|
|
|
expect(normalized.foo).to.deep.equal([INPUT_VALUE.toString()]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should convert object arguments into string values', () => {
|
|
|
|
const OUTPUT_VALUE = 'bar';
|
|
|
|
const input = new Map([
|
|
|
|
['foo', {
|
|
|
|
toString: () => OUTPUT_VALUE,
|
|
|
|
}],
|
|
|
|
]);
|
|
|
|
const normalized = normalizeMap(input);
|
|
|
|
|
|
|
|
expect(normalized.foo).to.deep.equal([OUTPUT_VALUE]);
|
|
|
|
});
|
2020-03-31 02:31:51 +00:00
|
|
|
});
|
2020-03-29 13:43:52 +00:00
|
|
|
});
|