From dca5e541d3c32ea57921c4c5ef77dcf80e9fcb97 Mon Sep 17 00:00:00 2001 From: Hezekiah Hendry Date: Tue, 10 Jan 2023 22:30:19 -0500 Subject: [PATCH] feat: add `better-serialize` --- src/index.ts | 4 +- src/lib/Serialize.ts | 213 ++++++++ src/lib/parse.ts | 11 + src/lib/private/index.ts | 1 + src/lib/private/isObject.ts | 3 + src/lib/stringify.ts | 11 + tests/index.test.ts | 7 - tests/lib/Serialize.test.ts | 942 ++++++++++++++++++++++++++++++++++++ tests/lib/parse.test.ts | 10 + tests/lib/stringify.test.ts | 10 + 10 files changed, 1204 insertions(+), 8 deletions(-) create mode 100644 src/lib/Serialize.ts create mode 100644 src/lib/parse.ts create mode 100644 src/lib/private/index.ts create mode 100644 src/lib/private/isObject.ts create mode 100644 src/lib/stringify.ts delete mode 100644 tests/index.test.ts create mode 100644 tests/lib/Serialize.test.ts create mode 100644 tests/lib/parse.test.ts create mode 100644 tests/lib/stringify.test.ts diff --git a/src/index.ts b/src/index.ts index 1fc943f..2d8aad9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1,3 @@ -export const packageName = 'better-serialize'; +export * from './lib/parse'; +export * from './lib/Serialize'; +export * from './lib/stringify'; diff --git a/src/lib/Serialize.ts b/src/lib/Serialize.ts new file mode 100644 index 0000000..1e82608 --- /dev/null +++ b/src/lib/Serialize.ts @@ -0,0 +1,213 @@ +import { isObject } from './private'; + +/** + * The namespace which contains everything for the transformation of raw Node.js data types. + * @since 1.0.0 + */ +export namespace Serialize { + /** + * Converts raw Node.js data types into a JSON-compatible format. + * @since 1.0.0 + * @param data The raw Node.js data type to convert. + * @returns The JSON-compatible format of the raw Node.js data type. + */ + export function toJsonCompatible(data: unknown): JsonCompatible { + if (isObject(data)) return { [Keying.Type]: Type.Object, [Keying.Value]: toJsonCompatibleObject(data) }; + else if (typeof data === 'string') return { [Keying.Type]: Type.String, [Keying.Value]: data }; + else if (typeof data === 'number') { + if (Number.isNaN(data)) return { [Keying.Type]: Type.Number, [Keying.Value]: 'NaN' }; + if ([Infinity, -Infinity].includes(data)) return { [Keying.Type]: Type.Number, [Keying.Value]: String(data) }; + + return { [Keying.Type]: Type.Number, [Keying.Value]: data }; + } else if (typeof data === 'boolean') return { [Keying.Type]: Type.Boolean, [Keying.Value]: data }; + else if (Array.isArray(data)) return { [Keying.Type]: Type.Array, [Keying.Value]: toJsonCompatibleArray(data) }; + else if (data === null) return { [Keying.Type]: Type.Null }; + else if (data instanceof Date) return { [Keying.Type]: Type.Date, [Keying.Value]: data.toJSON() }; + else if (data === undefined) return { [Keying.Type]: Type.Undefined }; + else if (typeof data === 'bigint') return { [Keying.Type]: Type.BigInt, [Keying.Value]: data.toString() }; + else if (data instanceof RegExp) { + return { [Keying.Type]: Type.RegExp, [Keying.Value]: { [Keying.Source]: data.source, [Keying.Flags]: data.flags } }; + } else if (data instanceof Map) return { [Keying.Type]: Type.Map, [Keying.Value]: toJsonCompatibleEntries(Array.from(data)) }; + else if (data instanceof Set) return { [Keying.Type]: Type.Set, [Keying.Value]: toJsonCompatibleArray(Array.from(data)) }; + + throw new TypeError(`Serialize received an unknown type while formatting: "${data.constructor.name}".`); + } + + function toJsonCompatibleArray(array: unknown[]): JsonCompatible[] { + const json: JsonCompatible[] = []; + + for (const value of array) json.push(toJsonCompatible(value)); + + return json; + } + + function toJsonCompatibleEntries(entries: [PropertyKey, unknown][]): [PropertyKey, JsonCompatible][] { + const json: [PropertyKey, JsonCompatible][] = []; + + for (const [key, value] of entries) json.push([key, toJsonCompatible(value)]); + return json; + } + + function toJsonCompatibleObject(object: Record): Record { + const json: Record = {}; + + for (const key in object) { + if (Object.prototype.hasOwnProperty.call(object, key)) { + json[key] = toJsonCompatible(object[key]); + } + } + + return json; + } + + /** + * Converts JSON-compatible data into a raw Node.js data format. + * @since 1.0.0 + * @param json The JSON-compatible data to convert. + * @returns The raw Node.js data format of the JSON-compatible data. + */ + export function fromJsonCompatible(json: JsonCompatible): unknown { + const { [Keying.Type]: type, [Keying.Value]: value } = json; + + switch (type) { + case Type.Array: + return fromJsonCompatibleArray(value as JsonCompatible[]); + + case Type.BigInt: + return BigInt(value as string); + + case Type.Boolean: + return value; + + case Type.Date: + return new Date(value as string); + + case Type.Map: + return new Map(fromJsonCompatibleMap(value as [PropertyKey, JsonCompatible][])); + + case Type.Null: + return null; + + case Type.Number: + if (typeof value === 'string') return Number(value); + + return value; + + case Type.Object: + return fromJsonCompatibleObject(value as Record); + + case Type.RegExp: + return new RegExp((value as Regex)[Keying.Source], (value as Regex)[Keying.Flags]); + + case Type.Set: + return new Set(fromJsonCompatibleArray(value as JsonCompatible[])); + + case Type.String: + return value; + + case Type.Undefined: + return undefined; + + default: + throw new TypeError('Serialize received an unknown type.'); + } + } + + function fromJsonCompatibleArray(json: JsonCompatible[]): unknown[] { + const arr: unknown[] = []; + + for (const value of json) arr.push(fromJsonCompatible(value)); + return arr; + } + + function fromJsonCompatibleMap(json: [PropertyKey, JsonCompatible][]): [PropertyKey, unknown][] { + const arr: [PropertyKey, unknown][] = []; + + for (const [key, value] of json) arr.push([key, fromJsonCompatible(value)]); + return arr; + } + + function fromJsonCompatibleObject(json: Record): Record { + const obj: Record = {}; + + for (const key in json) { + if (Object.prototype.hasOwnProperty.call(json, key)) { + obj[key] = fromJsonCompatible(json[key]); + } + } + + return obj; + } + + /** + * The keying used for JSON-compatible data. + * @since 1.0.0 + */ + export enum Keying { + Type = 't', + + Value = 'v', + + Source = 's', + + Flags = 'f' + } + + /** + * The types of JSON-compatible data. + * @since 1.0.0 + */ + export enum Type { + Array = 4, + + BigInt = 8, + + Boolean = 3, + + Date = 6, + + Map = 10, + + Null = 5, + + Number = 2, + + Object = 0, + + RegExp = 9, + + Set = 11, + + String = 1, + + Undefined = 7 + } + + /** + * The JSON-compatible data format. + * @since 1.0.0 + */ + export interface JsonCompatible { + [Keying.Type]: Type; + + [Keying.Value]?: + | string + | number + | boolean + | null + | JsonCompatible[] + | [PropertyKey, JsonCompatible][] + | Record + | Regex; + } + + /** + * The JSON-compatible RegExp format. + * @since 1.0.0 + */ + export interface Regex { + [Keying.Source]: string; + + [Keying.Flags]: string; + } +} diff --git a/src/lib/parse.ts b/src/lib/parse.ts new file mode 100644 index 0000000..021de56 --- /dev/null +++ b/src/lib/parse.ts @@ -0,0 +1,11 @@ +import { Serialize } from './Serialize'; + +/** + * Parses a {@link Serialize.JsonCompatible} string into it's raw Node.js data format. + * @since 1.0.0 + * @param data The {@link Serialize.JsonCompatible} string to parse. + * @returns The raw Node.js data format of the {@link Serialize.JsonCompatible} string. + */ +export function parse(data: string): unknown { + return Serialize.fromJsonCompatible(JSON.parse(data)); +} diff --git a/src/lib/private/index.ts b/src/lib/private/index.ts new file mode 100644 index 0000000..fa81c1a --- /dev/null +++ b/src/lib/private/index.ts @@ -0,0 +1 @@ +export * from '../private/isObject'; diff --git a/src/lib/private/isObject.ts b/src/lib/private/isObject.ts new file mode 100644 index 0000000..eb00956 --- /dev/null +++ b/src/lib/private/isObject.ts @@ -0,0 +1,3 @@ +export function isObject(value: unknown): value is Record { + return typeof value === 'object' && value?.constructor === Object; +} diff --git a/src/lib/stringify.ts b/src/lib/stringify.ts new file mode 100644 index 0000000..bbd861c --- /dev/null +++ b/src/lib/stringify.ts @@ -0,0 +1,11 @@ +import { Serialize } from './Serialize'; + +/** + * Stringifies a Node.js data format into a {@link Serialize.JsonCompatible} string. + * @since 1.0.0 + * @param data The Node.js data format to stringify. + * @returns The {@link Serialize.JsonCompatible} string of the Node.js data format. + */ +export function stringify(data: unknown): string { + return JSON.stringify(Serialize.toJsonCompatible(data)); +} diff --git a/tests/index.test.ts b/tests/index.test.ts deleted file mode 100644 index 9b3dd78..0000000 --- a/tests/index.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { packageName } from '../src'; - -describe('index', () => { - test('GIVEN packageName THEN returns better-serialize', () => { - expect(packageName).toBe('better-serialize'); - }); -}); diff --git a/tests/lib/Serialize.test.ts b/tests/lib/Serialize.test.ts new file mode 100644 index 0000000..789a31e --- /dev/null +++ b/tests/lib/Serialize.test.ts @@ -0,0 +1,942 @@ +import { Serialize } from '../../src'; + +describe('Serialize', () => { + const rawBigInt = 0n; + const rawBoolean = false; + const rawDate = new Date(); + const rawNull = null; + const rawNumber = 0; + const rawRegExp = /test/g; + const rawString = 'test'; + const rawUndefined = undefined; + const jsonBigInt = rawBigInt.toString(); + const jsonBoolean = rawBoolean; + const jsonDate = rawDate.toJSON(); + const jsonNumber = rawNumber; + const jsonRegExp = { [Serialize.Keying.Source]: rawRegExp.source, [Serialize.Keying.Flags]: rawRegExp.flags }; + const jsonString = rawString; + + describe('Serialize.toJsonCompatible', () => { + test('GIVEN typeof toJsonCompatible THEN returns functions', () => { + expect(typeof Serialize.toJsonCompatible).toBe('function'); + }); + + describe('can format raw data', () => { + describe(Serialize.Type.Array.toString(), () => { + test(Serialize.Type.BigInt.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible([rawBigInt]); + + expect(type).toBe(Serialize.Type.Array); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.BigInt, [Serialize.Keying.Value]: jsonBigInt }]); + }); + + test(Serialize.Type.Boolean.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible([rawBoolean]); + + expect(type).toBe(Serialize.Type.Array); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Boolean, [Serialize.Keying.Value]: jsonBoolean }]); + }); + + test(Serialize.Type.Date.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible([rawDate]); + + expect(type).toBe(Serialize.Type.Array); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Date, [Serialize.Keying.Value]: jsonDate }]); + }); + + test(Serialize.Type.Null.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible([rawNull]); + + expect(type).toBe(Serialize.Type.Array); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Null }]); + }); + + describe(Serialize.Type.Number.toString(), () => { + test('GIVEN number THEN returns number', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible([rawNumber]); + + expect(type).toBe(Serialize.Type.Array); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: jsonNumber }]); + }); + + test('GIVEN NaN THEN returns NaN', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible([NaN]); + + expect(type).toBe(Serialize.Type.Array); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'NaN' }]); + }); + + test('GIVEN Infinity THEN returns Infinity', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible([Infinity]); + + expect(type).toBe(Serialize.Type.Array); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'Infinity' }]); + }); + + test('GIVEN -Infinity THEN returns -Infinity', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible([-Infinity]); + + expect(type).toBe(Serialize.Type.Array); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: '-Infinity' }]); + }); + }); + + test(Serialize.Type.RegExp.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible([rawRegExp]); + + expect(type).toBe(Serialize.Type.Array); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.RegExp, [Serialize.Keying.Value]: jsonRegExp }]); + }); + + test(Serialize.Type.String.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible([rawString]); + + expect(type).toBe(Serialize.Type.Array); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.String, [Serialize.Keying.Value]: jsonString }]); + }); + + test(Serialize.Type.Undefined.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible([rawUndefined]); + + expect(type).toBe(Serialize.Type.Array); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Undefined }]); + }); + }); + + test(Serialize.Type.BigInt.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(rawBigInt); + + expect(type).toBe(Serialize.Type.BigInt); + expect(value).toStrictEqual(jsonBigInt); + }); + + test(Serialize.Type.Boolean.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(rawBoolean); + + expect(type).toBe(Serialize.Type.Boolean); + expect(value).toStrictEqual(jsonBoolean); + }); + + test(Serialize.Type.Date.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(rawDate); + + expect(type).toBe(Serialize.Type.Date); + expect(value).toStrictEqual(jsonDate); + }); + + describe(Serialize.Type.Map.toString(), () => { + test(Serialize.Type.BigInt.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Map([['key', rawBigInt]])); + + expect(type).toBe(Serialize.Type.Map); + expect(value).toStrictEqual([['key', { [Serialize.Keying.Type]: Serialize.Type.BigInt, [Serialize.Keying.Value]: jsonBigInt }]]); + }); + + test(Serialize.Type.Boolean.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Map([['key', rawBoolean]])); + + expect(type).toBe(Serialize.Type.Map); + expect(value).toStrictEqual([['key', { [Serialize.Keying.Type]: Serialize.Type.Boolean, [Serialize.Keying.Value]: jsonBoolean }]]); + }); + + test(Serialize.Type.Date.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Map([['key', rawDate]])); + + expect(type).toBe(Serialize.Type.Map); + expect(value).toStrictEqual([['key', { [Serialize.Keying.Type]: Serialize.Type.Date, [Serialize.Keying.Value]: jsonDate }]]); + }); + + test(Serialize.Type.Null.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Map([['key', rawNull]])); + + expect(type).toBe(Serialize.Type.Map); + expect(value).toStrictEqual([['key', { [Serialize.Keying.Type]: Serialize.Type.Null }]]); + }); + + describe(Serialize.Type.Number.toString(), () => { + test('GIVEN number THEN returns number', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Map([['key', rawNumber]])); + + expect(type).toBe(Serialize.Type.Map); + expect(value).toStrictEqual([['key', { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: jsonNumber }]]); + }); + + test('GIVEN NaN THEN returns NaN', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Map([['key', NaN]])); + + expect(type).toBe(Serialize.Type.Map); + expect(value).toStrictEqual([['key', { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'NaN' }]]); + }); + + test('GIVEN Infinity THEN returns Infinity', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Map([['key', Infinity]])); + + expect(type).toBe(Serialize.Type.Map); + expect(value).toStrictEqual([['key', { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'Infinity' }]]); + }); + + test('GIVEN -Infinity THEN returns -Infinity', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Map([['key', -Infinity]])); + + expect(type).toBe(Serialize.Type.Map); + expect(value).toStrictEqual([['key', { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: '-Infinity' }]]); + }); + }); + + test(Serialize.Type.RegExp.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Map([['key', rawRegExp]])); + + expect(type).toBe(Serialize.Type.Map); + expect(value).toStrictEqual([['key', { [Serialize.Keying.Type]: Serialize.Type.RegExp, [Serialize.Keying.Value]: jsonRegExp }]]); + }); + + test(Serialize.Type.String.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Map([['key', rawString]])); + + expect(type).toBe(Serialize.Type.Map); + expect(value).toStrictEqual([['key', { [Serialize.Keying.Type]: Serialize.Type.String, [Serialize.Keying.Value]: jsonString }]]); + }); + + test(Serialize.Type.Undefined.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Map([['key', rawUndefined]])); + + expect(type).toBe(Serialize.Type.Map); + expect(value).toStrictEqual([['key', { [Serialize.Keying.Type]: Serialize.Type.Undefined }]]); + }); + }); + + test(Serialize.Type.Null.toString(), () => { + const { [Serialize.Keying.Type]: type } = Serialize.toJsonCompatible(rawNull); + + expect(type).toBe(Serialize.Type.Null); + }); + + describe(Serialize.Type.Number.toString(), () => { + test('GIVEN number THEN returns number', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(rawNumber); + + expect(type).toBe(Serialize.Type.Number); + expect(value).toStrictEqual(jsonNumber); + }); + + test('GIVEN NaN THEN returns NaN', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(NaN); + + expect(type).toBe(Serialize.Type.Number); + expect(value).toStrictEqual('NaN'); + }); + + test('GIVEN Infinity THEN returns Infinity', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(Infinity); + + expect(type).toBe(Serialize.Type.Number); + expect(value).toStrictEqual('Infinity'); + }); + + test('GIVEN -Infinity THEN returns -Infinity', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(-Infinity); + + expect(type).toBe(Serialize.Type.Number); + expect(value).toStrictEqual('-Infinity'); + }); + }); + + describe(Serialize.Type.Object.toString(), () => { + test(Serialize.Type.BigInt.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible({ key: rawBigInt }); + + expect(type).toBe(Serialize.Type.Object); + expect(value).toStrictEqual({ key: { [Serialize.Keying.Type]: Serialize.Type.BigInt, [Serialize.Keying.Value]: jsonBigInt } }); + }); + + test(Serialize.Type.Boolean.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible({ key: rawBoolean }); + + expect(type).toBe(Serialize.Type.Object); + expect(value).toStrictEqual({ key: { [Serialize.Keying.Type]: Serialize.Type.Boolean, [Serialize.Keying.Value]: jsonBoolean } }); + }); + + test(Serialize.Type.Date.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible({ key: rawDate }); + + expect(type).toBe(Serialize.Type.Object); + expect(value).toStrictEqual({ key: { [Serialize.Keying.Type]: Serialize.Type.Date, [Serialize.Keying.Value]: jsonDate } }); + }); + + test(Serialize.Type.Null.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible({ key: rawNull }); + + expect(type).toBe(Serialize.Type.Object); + expect(value).toStrictEqual({ key: { [Serialize.Keying.Type]: Serialize.Type.Null } }); + }); + + describe(Serialize.Type.Number.toString(), () => { + test('GIVEN number THEN returns number', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible({ key: rawNumber }); + + expect(type).toBe(Serialize.Type.Object); + expect(value).toStrictEqual({ key: { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: jsonNumber } }); + }); + + test('GIVEN NaN THEN returns NaN', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible({ key: NaN }); + + expect(type).toBe(Serialize.Type.Object); + expect(value).toStrictEqual({ key: { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'NaN' } }); + }); + + test('GIVEN Infinity THEN returns Infinity', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible({ key: Infinity }); + + expect(type).toBe(Serialize.Type.Object); + expect(value).toStrictEqual({ key: { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'Infinity' } }); + }); + + test('GIVEN -Infinity THEN returns -Infinity', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible({ key: -Infinity }); + + expect(type).toBe(Serialize.Type.Object); + expect(value).toStrictEqual({ key: { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: '-Infinity' } }); + }); + }); + + test(Serialize.Type.RegExp.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible({ key: rawRegExp }); + + expect(type).toBe(Serialize.Type.Object); + expect(value).toStrictEqual({ key: { [Serialize.Keying.Type]: Serialize.Type.RegExp, [Serialize.Keying.Value]: jsonRegExp } }); + }); + + test(Serialize.Type.String.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible({ key: rawString }); + + expect(type).toBe(Serialize.Type.Object); + expect(value).toStrictEqual({ key: { [Serialize.Keying.Type]: Serialize.Type.String, [Serialize.Keying.Value]: jsonString } }); + }); + + test(Serialize.Type.Undefined.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible({ key: rawUndefined }); + + expect(type).toBe(Serialize.Type.Object); + expect(value).toStrictEqual({ key: { [Serialize.Keying.Type]: Serialize.Type.Undefined } }); + }); + }); + + test(Serialize.Type.RegExp.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(rawRegExp); + + expect(type).toBe(Serialize.Type.RegExp); + expect(value).toStrictEqual(jsonRegExp); + }); + + describe(Serialize.Type.Set.toString(), () => { + test(Serialize.Type.BigInt.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Set([rawBigInt])); + + expect(type).toBe(Serialize.Type.Set); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.BigInt, [Serialize.Keying.Value]: jsonBigInt }]); + }); + + test(Serialize.Type.Boolean.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Set([rawBoolean])); + + expect(type).toBe(Serialize.Type.Set); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Boolean, [Serialize.Keying.Value]: jsonBoolean }]); + }); + + test(Serialize.Type.Date.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Set([rawDate])); + + expect(type).toBe(Serialize.Type.Set); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Date, [Serialize.Keying.Value]: jsonDate }]); + }); + + test(Serialize.Type.Null.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Set([rawNull])); + + expect(type).toBe(Serialize.Type.Set); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Null }]); + }); + + describe(Serialize.Type.Number.toString(), () => { + test('GIVEN number THEN returns number', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Set([rawNumber])); + + expect(type).toBe(Serialize.Type.Set); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: jsonNumber }]); + }); + + test('GIVEN NaN THEN returns NaN', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Set([NaN])); + + expect(type).toBe(Serialize.Type.Set); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'NaN' }]); + }); + + test('GIVEN Infinity THEN returns Infinity', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Set([Infinity])); + + expect(type).toBe(Serialize.Type.Set); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'Infinity' }]); + }); + + test('GIVEN -Infinity THEN returns -Infinity', () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Set([-Infinity])); + + expect(type).toBe(Serialize.Type.Set); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: '-Infinity' }]); + }); + }); + + test(Serialize.Type.RegExp.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Set([rawRegExp])); + + expect(type).toBe(Serialize.Type.Set); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.RegExp, [Serialize.Keying.Value]: jsonRegExp }]); + }); + + test(Serialize.Type.String.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Set([rawString])); + + expect(type).toBe(Serialize.Type.Set); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.String, [Serialize.Keying.Value]: jsonString }]); + }); + + test(Serialize.Type.Undefined.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(new Set([rawUndefined])); + + expect(type).toBe(Serialize.Type.Set); + expect(value).toStrictEqual([{ [Serialize.Keying.Type]: Serialize.Type.Undefined }]); + }); + }); + + test(Serialize.Type.String.toString(), () => { + const { [Serialize.Keying.Type]: type, [Serialize.Keying.Value]: value } = Serialize.toJsonCompatible(rawString); + + expect(type).toBe(Serialize.Type.String); + expect(value).toStrictEqual(jsonString); + }); + + test(Serialize.Type.Undefined.toString(), () => { + const { [Serialize.Keying.Type]: type } = Serialize.toJsonCompatible(rawUndefined); + + expect(type).toBe(Serialize.Type.Undefined); + }); + }); + + test('GIVEN unknown type THEN throws error', () => { + expect(() => Serialize.toJsonCompatible(() => true)).toThrowError( + new TypeError('Serialize received an unknown type while formatting: "Function".') + ); + }); + }); + + describe('Serialize.fromJsonCompatible', () => { + test('GIVEN typeof fromJsonCompatible THEN returns function', () => { + expect(typeof Serialize.fromJsonCompatible).toBe('function'); + }); + + describe('can parse json data', () => { + describe(Serialize.Type.Array.toString(), () => { + test(Serialize.Type.BigInt.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Array, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.BigInt, [Serialize.Keying.Value]: jsonBigInt }] + }) + ).toStrictEqual([rawBigInt]); + }); + + test(Serialize.Type.Boolean.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Array, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Boolean, [Serialize.Keying.Value]: jsonBoolean }] + }) + ).toStrictEqual([rawBoolean]); + }); + + test(Serialize.Type.Date.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Array, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Date, [Serialize.Keying.Value]: jsonDate }] + }) + ).toStrictEqual([rawDate]); + }); + + test(Serialize.Type.Null.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Array, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Null }] + }) + ).toStrictEqual([rawNull]); + }); + + describe(Serialize.Type.Number.toString(), () => { + test('GIVEN number THEN returns number', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Array, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: jsonNumber }] + }) + ).toStrictEqual([rawNumber]); + }); + + test('GIVEN NaN THEN returns NaN', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Array, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'NaN' }] + }) + ).toStrictEqual([NaN]); + }); + + test('GIVEN Infinity THEN returns Infinity', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Array, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'Infinity' }] + }) + ).toStrictEqual([Infinity]); + }); + + test('GIVEN -Infinity THEN returns -Infinity', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Array, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: '-Infinity' }] + }) + ).toStrictEqual([-Infinity]); + }); + }); + + test(Serialize.Type.RegExp.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Array, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.RegExp, [Serialize.Keying.Value]: jsonRegExp }] + }) + ).toStrictEqual([rawRegExp]); + }); + + test(Serialize.Type.String.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Array, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.String, [Serialize.Keying.Value]: jsonString }] + }) + ).toStrictEqual([rawString]); + }); + + test(Serialize.Type.Undefined.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Array, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Undefined }] + }) + ).toStrictEqual([rawUndefined]); + }); + }); + + test(Serialize.Type.BigInt.toString(), () => { + expect(Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: Serialize.Type.BigInt, [Serialize.Keying.Value]: jsonBigInt })).toBe( + rawBigInt + ); + }); + + test(Serialize.Type.Boolean.toString(), () => { + expect(Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: Serialize.Type.Boolean, [Serialize.Keying.Value]: jsonBoolean })).toBe( + rawBoolean + ); + }); + + test(Serialize.Type.Date.toString(), () => { + expect(Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: Serialize.Type.Date, [Serialize.Keying.Value]: jsonDate })).toStrictEqual( + rawDate + ); + }); + + describe(Serialize.Type.Map.toString(), () => { + test(Serialize.Type.BigInt.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Map, + [Serialize.Keying.Value]: [['key', { [Serialize.Keying.Type]: Serialize.Type.BigInt, [Serialize.Keying.Value]: jsonBigInt }]] + }) + ).toStrictEqual(new Map([['key', rawBigInt]])); + }); + + test(Serialize.Type.Boolean.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Map, + [Serialize.Keying.Value]: [['key', { [Serialize.Keying.Type]: Serialize.Type.Boolean, [Serialize.Keying.Value]: jsonBoolean }]] + }) + ).toStrictEqual(new Map([['key', rawBoolean]])); + }); + + test(Serialize.Type.Date.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Map, + [Serialize.Keying.Value]: [['key', { [Serialize.Keying.Type]: Serialize.Type.Date, [Serialize.Keying.Value]: jsonDate }]] + }) + ).toStrictEqual(new Map([['key', rawDate]])); + }); + + test(Serialize.Type.Null.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Map, + [Serialize.Keying.Value]: [['key', { [Serialize.Keying.Type]: Serialize.Type.Null }]] + }) + ).toStrictEqual(new Map([['key', rawNull]])); + }); + + describe(Serialize.Type.Number.toString(), () => { + test('GIVEN number THEN returns number', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Map, + [Serialize.Keying.Value]: [['key', { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: jsonNumber }]] + }) + ).toStrictEqual(new Map([['key', rawNumber]])); + }); + + test('GIVEN NaN THEN returns NaN', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Map, + [Serialize.Keying.Value]: [['key', { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'NaN' }]] + }) + ).toStrictEqual(new Map([['key', NaN]])); + }); + + test('GIVEN Infinity THEN returns Infinity', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Map, + [Serialize.Keying.Value]: [['key', { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'Infinity' }]] + }) + ).toStrictEqual(new Map([['key', Infinity]])); + }); + + test('GIVEN -Infinity THEN returns -Infinity', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Map, + [Serialize.Keying.Value]: [['key', { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: '-Infinity' }]] + }) + ).toStrictEqual(new Map([['key', -Infinity]])); + }); + }); + + test(Serialize.Type.RegExp.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Map, + [Serialize.Keying.Value]: [['key', { [Serialize.Keying.Type]: Serialize.Type.RegExp, [Serialize.Keying.Value]: jsonRegExp }]] + }) + ).toStrictEqual(new Map([['key', rawRegExp]])); + }); + + test(Serialize.Type.String.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Map, + [Serialize.Keying.Value]: [['key', { [Serialize.Keying.Type]: Serialize.Type.String, [Serialize.Keying.Value]: jsonString }]] + }) + ).toStrictEqual(new Map([['key', rawString]])); + }); + + test(Serialize.Type.Undefined.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Map, + [Serialize.Keying.Value]: [['key', { [Serialize.Keying.Type]: Serialize.Type.Undefined }]] + }) + ).toStrictEqual(new Map([['key', rawUndefined]])); + }); + }); + + test(Serialize.Type.Null.toString(), () => { + expect(Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: Serialize.Type.Null })).toBe(rawNull); + }); + + describe(Serialize.Type.Number.toString(), () => { + test('GIVEN number THEN returns number', () => { + expect(Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: jsonNumber })).toBe( + rawNumber + ); + }); + + test('GIVEN NaN THEN returns NaN', () => { + expect(Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'NaN' })).toBe(NaN); + }); + + test('GIVEN Infinity THEN returns Infinity', () => { + expect(Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'Infinity' })).toBe( + Infinity + ); + }); + + test('GIVEN -Infinity THEN returns -Infinity', () => { + expect(Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: '-Infinity' })).toBe( + -Infinity + ); + }); + }); + + describe(Serialize.Type.Object.toString(), () => { + test(Serialize.Type.BigInt.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Object, + [Serialize.Keying.Value]: { key: { [Serialize.Keying.Type]: Serialize.Type.BigInt, [Serialize.Keying.Value]: jsonBigInt } } + }) + ).toStrictEqual({ + key: rawBigInt + }); + }); + + test(Serialize.Type.Boolean.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Object, + [Serialize.Keying.Value]: { key: { [Serialize.Keying.Type]: Serialize.Type.Boolean, [Serialize.Keying.Value]: jsonBoolean } } + }) + ).toStrictEqual({ + key: rawBoolean + }); + }); + + test(Serialize.Type.Date.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Object, + [Serialize.Keying.Value]: { key: { [Serialize.Keying.Type]: Serialize.Type.Date, [Serialize.Keying.Value]: jsonDate } } + }) + ).toStrictEqual({ + key: rawDate + }); + }); + + test(Serialize.Type.Null.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Object, + [Serialize.Keying.Value]: { key: { [Serialize.Keying.Type]: Serialize.Type.Null } } + }) + ).toStrictEqual({ + key: rawNull + }); + }); + + describe(Serialize.Type.Number.toString(), () => { + test('GIVEN number THEN returns number', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Object, + [Serialize.Keying.Value]: { key: { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: jsonNumber } } + }) + ).toStrictEqual({ + key: rawNumber + }); + }); + + test('GIVEN NaN THEN returns NaN', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Object, + [Serialize.Keying.Value]: { key: { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'NaN' } } + }) + ).toStrictEqual({ + key: NaN + }); + }); + + test('GIVEN Infinity THEN returns Infinity', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Object, + [Serialize.Keying.Value]: { key: { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'Infinity' } } + }) + ).toStrictEqual({ + key: Infinity + }); + }); + + test('GIVEN -Infinity THEN returns -Infinity', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Object, + [Serialize.Keying.Value]: { key: { [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: '-Infinity' } } + }) + ).toStrictEqual({ + key: -Infinity + }); + }); + }); + + test(Serialize.Type.RegExp.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Object, + [Serialize.Keying.Value]: { key: { [Serialize.Keying.Type]: Serialize.Type.RegExp, [Serialize.Keying.Value]: jsonRegExp } } + }) + ).toStrictEqual({ + key: rawRegExp + }); + }); + + test(Serialize.Type.String.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Object, + [Serialize.Keying.Value]: { key: { [Serialize.Keying.Type]: Serialize.Type.String, [Serialize.Keying.Value]: jsonString } } + }) + ).toStrictEqual({ + key: rawString + }); + }); + + test(Serialize.Type.Undefined.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Object, + [Serialize.Keying.Value]: { key: { [Serialize.Keying.Type]: Serialize.Type.Undefined } } + }) + ).toStrictEqual({ + key: rawUndefined + }); + }); + }); + + test(Serialize.Type.RegExp.toString(), () => { + expect(Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: Serialize.Type.RegExp, [Serialize.Keying.Value]: jsonRegExp })).toStrictEqual( + rawRegExp + ); + }); + + describe(Serialize.Type.Set.toString(), () => { + test(Serialize.Type.BigInt.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Set, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.BigInt, [Serialize.Keying.Value]: jsonBigInt }] + }) + ).toStrictEqual(new Set([rawBigInt])); + }); + + test(Serialize.Type.Boolean.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Set, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Boolean, [Serialize.Keying.Value]: jsonBoolean }] + }) + ).toStrictEqual(new Set([rawBoolean])); + }); + + test(Serialize.Type.Date.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Set, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Date, [Serialize.Keying.Value]: jsonDate }] + }) + ).toStrictEqual(new Set([rawDate])); + }); + + test(Serialize.Type.Null.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Set, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Null }] + }) + ).toStrictEqual(new Set([rawNull])); + }); + + describe(Serialize.Type.Number.toString(), () => { + test('GIVEN number THEN returns number', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Set, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: jsonNumber }] + }) + ).toStrictEqual(new Set([rawNumber])); + }); + + test('GIVEN NaN THEN returns NaN', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Set, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'NaN' }] + }) + ).toStrictEqual(new Set([NaN])); + }); + + test('GIVEN Infinity THEN returns Infinity', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Set, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: 'Infinity' }] + }) + ).toStrictEqual(new Set([Infinity])); + }); + + test('GIVEN -Infinity THEN returns -Infinity', () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Set, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Number, [Serialize.Keying.Value]: '-Infinity' }] + }) + ).toStrictEqual(new Set([-Infinity])); + }); + }); + + test(Serialize.Type.RegExp.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Set, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.RegExp, [Serialize.Keying.Value]: jsonRegExp }] + }) + ).toStrictEqual(new Set([rawRegExp])); + }); + + test(Serialize.Type.String.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Set, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.String, [Serialize.Keying.Value]: jsonString }] + }) + ).toStrictEqual(new Set([rawString])); + }); + + test(Serialize.Type.Undefined.toString(), () => { + expect( + Serialize.fromJsonCompatible({ + [Serialize.Keying.Type]: Serialize.Type.Set, + [Serialize.Keying.Value]: [{ [Serialize.Keying.Type]: Serialize.Type.Undefined }] + }) + ).toStrictEqual(new Set([rawUndefined])); + }); + }); + + test(Serialize.Type.String.toString(), () => { + expect(Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: Serialize.Type.String, [Serialize.Keying.Value]: jsonString })).toBe( + rawString + ); + }); + + test(Serialize.Type.Undefined.toString(), () => { + expect(Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: Serialize.Type.Undefined })).toBe(rawUndefined); + }); + }); + + test('GIVEN unknown type THEN throws error', () => { + // @ts-expect-error 2322 - Type 'function' is not assignable to type 'Serialize.Type'. + expect(() => Serialize.fromJsonCompatible({ [Serialize.Keying.Type]: 'function' })).toThrowError('Serialize received an unknown type.'); + }); + }); +}); diff --git a/tests/lib/parse.test.ts b/tests/lib/parse.test.ts new file mode 100644 index 0000000..1c980d1 --- /dev/null +++ b/tests/lib/parse.test.ts @@ -0,0 +1,10 @@ +import { parse } from '../../src'; + +describe('parse', () => { + test('GIVEN string THEN returns data', () => { + const source = '{"t":1,"v":"Hello World!"}'; + const expected = 'Hello World!'; + + expect(parse(source)).toBe(expected); + }); +}); diff --git a/tests/lib/stringify.test.ts b/tests/lib/stringify.test.ts new file mode 100644 index 0000000..d409bae --- /dev/null +++ b/tests/lib/stringify.test.ts @@ -0,0 +1,10 @@ +import { stringify } from '../../src'; + +describe('stringify', () => { + test('GIVEN data THEN returns string', () => { + const source = 'Hello World!'; + const expected = '{"t":1,"v":"Hello World!"}'; + + expect(stringify(source)).toBe(expected); + }); +});