A comprehensive comparison library, for use in test frameworks
—
tcompare provides extensive configuration options for customizing comparison behavior, diff output, and object formatting. All options are type-safe and well-documented for easy customization.
Options available to all comparison functions, combining formatting and comparison-specific settings.
/**
* Options that can be used to set how diffs are formatted
*/
type CompareOptions = FormatOptions & Pick<SameOptions, 'diffContext'>;Core formatting options available to all formatting and comparison operations.
/**
* Options to control the formatting of objects
*/
interface FormatOptions {
/** Sort object keys alphabetically for deterministic output */
sort?: boolean;
/** Formatting style - controls output format and syntax */
style?: StyleType;
/** Number of bytes to show per line when printing long Buffers (default: 32) */
bufferChunkSize?: number;
/** Include enumerable properties from prototype chain */
includeEnumerable?: boolean;
/** Include getter properties (warning: may trigger side effects) */
includeGetters?: boolean;
/** Represent and compare React elements as JSX strings (pretty style only, default: true) */
reactString?: boolean;
}
type StyleType = 'pretty' | 'js' | 'tight';Usage Examples:
import { same, format } from "tcompare";
// Basic formatting options
const options = {
sort: true, // Alphabetical key sorting
style: "js" as const, // JavaScript syntax
bufferChunkSize: 16 // Smaller buffer chunks
};
const result = same(obj1, obj2, options);
const formatted = format(obj1, options);Extended options for all comparison classes, including the expected pattern and diff configuration.
/**
* Options for all comparison operations
*/
interface SameOptions extends FormatOptions {
/** The pattern to test against (required for comparison classes) */
expect: any;
/** Parent comparison object for nested comparisons */
parent?: Same;
/** Key in parent object being compared */
key?: any;
/** Corresponding key in expected pattern */
expectKey?: any;
/** Number of lines of context to show around changes in diffs (default: 10) */
diffContext?: number;
}Usage Examples:
import { Same } from "tcompare";
// Comprehensive comparison options
const options = {
expect: { name: "Alice", age: 25 },
style: "pretty" as const,
sort: true,
diffContext: 5, // Show 5 lines of context around changes
reactString: false // Disable JSX formatting
};
const comparison = new Same(testObject, options);
const diff = comparison.print();Options for configuring React element formatting when reactString is enabled.
/**
* Options for React element to JSX string conversion
* Re-exported from react-element-to-jsx-string package
*/
interface ReactElementToJSXStringOptions {
/** Show default props in JSX output */
showDefaultProps?: boolean;
/** Show functions as anonymous functions */
showFunctions?: boolean;
/** Function to filter out props from JSX output */
filterProps?: string[];
/** Maximum depth for nested elements */
maxInlineAttributesLineLength?: number;
/** Sort props alphabetically */
sortProps?: boolean;
/** Use single quotes for props */
useBooleanShorthandSyntax?: boolean;
/** Tab size for indentation */
tabStop?: number;
}Usage Examples:
import { format } from "tcompare";
import type { ReactElementToJSXStringOptions } from "tcompare";
// React element formatting with custom options
const reactOptions: ReactElementToJSXStringOptions = {
showDefaultProps: false,
sortProps: true,
useBooleanShorthandSyntax: true
};
const formatOptions = {
style: "pretty" as const,
reactString: true,
// Note: ReactElementToJSXStringOptions are used internally
// when reactString is true
};
const element = <MyComponent name="test" active={true} />;
const formatted = format(element, formatOptions);Three predefined styles with different output characteristics and use cases.
type StyleType = 'pretty' | 'js' | 'tight';
/**
* Style configuration object defining how values are formatted
*/
interface Style {
/** Format functions with optional class names */
fn: (fn: Function | ((...a: any[]) => any), cls: string) => string;
/** Format empty Set objects */
setEmpty: (cls: string) => string;
/** Format Set opening */
setHead: (cls: string) => string;
/** Format Set closing */
setTail: (indent: string) => string;
/** Separator between Set entries */
setEntrySep: () => string;
/** Format empty Map objects */
mapEmpty: (cls: string) => string;
/** Format Map opening */
mapHead: (cls: string) => string;
/** Format Map closing */
mapTail: (indent: string) => string;
/** Format Map key start */
mapKeyStart: () => string;
/** Separator between Map key and value */
mapKeyValSep: () => string;
/** Separator between Map entries */
mapEntrySep: () => string;
/** Format circular references */
circular: (node: Format) => string;
/** Format reference IDs */
nodeId: (id: number) => string;
/** Additional formatting methods for errors, objects, arrays, etc. */
// ... extensive additional methods
}
/**
* Predefined styles for different formatting needs
*/
const styles: { [style in StyleType]: Style };Style Comparison Examples:
import { format, styles } from "tcompare";
const data = {
map: new Map([["key1", "value1"], ["key2", "value2"]]),
set: new Set(["a", "b", "c"]),
array: [1, 2, 3]
};
// Pretty style - human readable with type indicators
console.log(format(data, { style: "pretty" }));
/*
Object {
"map": Map {
"key1" => "value1",
"key2" => "value2",
},
"set": Set {
"a",
"b",
"c",
},
"array": Array [
1,
2,
3,
],
}
*/
// JavaScript style - valid JS syntax
console.log(format(data, { style: "js" }));
/*
{
"map": new Map([
["key1", "value1"],
["key2", "value2"],
]),
"set": new Set([
"a",
"b",
"c",
]),
"array": [
1,
2,
3,
],
}
*/
// Tight style - minimal whitespace
console.log(format(data, { style: "tight" }));
// {"map":new Map([["key1","value1"],["key2","value2"],]),"set":new Set(["a","b","c",]),"array":[1,2,3,],}Control how much context is shown around changes in diff output.
interface SameOptions {
/**
* Number of lines of context to show around changes in diffs
* Higher values show more surrounding unchanged content
* Lower values focus on just the changes
* Default: 10
*/
diffContext?: number;
}Usage Examples:
import { same } from "tcompare";
const obj1 = {
a: 1, b: 2, c: 3, d: 4, e: 5,
f: 6, g: 7, h: 8, i: 9, j: 10,
k: 11, l: 12, m: 13, n: 14, o: 15
};
const obj2 = {
...obj1,
h: 999 // Changed value
};
// Minimal context - focus on changes
const minimalResult = same(obj1, obj2, { diffContext: 1 });
console.log(minimalResult.diff);
/*
--- expected
+++ actual
@@ -6,3 +6,3 @@
"g": 7,
- "h": 8,
+ "h": 999,
"i": 9,
*/
// More context - show surrounding unchanged content
const contextResult = same(obj1, obj2, { diffContext: 5 });
console.log(contextResult.diff);
/*
--- expected
+++ actual
@@ -4,7 +4,7 @@
"e": 5,
"f": 6,
"g": 7,
- "h": 8,
+ "h": 999,
"i": 9,
"j": 10,
"k": 11,
*/Special configuration for Buffer object display.
interface FormatOptions {
/**
* Number of bytes to show per line when printing long Buffer objects
* Each line shows hex values and ASCII representation
* Default: 32 bytes per line
*/
bufferChunkSize?: number;
}Usage Examples:
import { format } from "tcompare";
const buffer = Buffer.from("Hello, World! This is a test buffer with more content.");
// Default chunk size (32 bytes per line)
console.log(format(buffer));
/*
Buffer <
48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 20 54 68 |Hello, World! Th|
69 73 20 69 73 20 61 20 74 65 73 74 20 62 75 66 |is a test buf|
66 65 72 20 77 69 74 68 20 6d 6f 72 65 20 63 6f |fer with more co|
6e 74 65 6e 74 2e |ntent.|
>
*/
// Smaller chunks (16 bytes per line)
console.log(format(buffer, { bufferChunkSize: 16 }));
/*
Buffer <
48 65 6c 6c 6f 2c 20 57 |Hello, W|
6f 72 6c 64 21 20 54 68 |orld! Th|
69 73 20 69 73 20 61 20 |is a |
74 65 73 74 20 62 75 66 |test buf|
// ... continues
>
*/
// Larger chunks (64 bytes per line)
console.log(format(buffer, { bufferChunkSize: 64 }));
/*
Buffer <
48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 20 54 68 69 73 20 69 73 20 61 20 74 65 73 74 20 62 |Hello, World! This is a test b|
75 66 66 65 72 20 77 69 74 68 20 6d 6f 72 65 20 63 6f 6e 74 65 6e 74 2e |uffer with more content.|
>
*/Control how React elements are formatted and compared.
interface FormatOptions {
/**
* Represent and compare React elements as JSX strings
* Only supported in 'pretty' formatting style
* When enabled, React elements are first compared as JSX strings
* If JSX strings match, elements are considered equivalent
* If JSX strings don't match, falls back to object comparison
* Default: true
*/
reactString?: boolean;
}Usage Examples:
import { format, same } from "tcompare";
import React from "react";
const element1 = <div className="container">Hello</div>;
const element2 = <div className="container">Hello</div>;
// With JSX string formatting (default)
console.log(format(element1));
// <div className="container">Hello</div>
const result1 = same(element1, element2, { reactString: true });
console.log(result1.match); // true - JSX strings match
// Without JSX string formatting - shows object structure
console.log(format(element1, { reactString: false }));
/*
Object {
"type": "div",
"props": Object {
"className": "container",
"children": "Hello",
},
}
*/Control which object properties are included in formatting and comparison.
interface FormatOptions {
/**
* Include enumerable properties from prototype chain
* By default, only own properties are included
* Warning: May include unexpected inherited properties
*/
includeEnumerable?: boolean;
/**
* Include getter properties when formatting/comparing
* Warning: Calling getters may trigger side effects
*/
includeGetters?: boolean;
}Usage Examples:
import { format } from "tcompare";
class Parent {
parentProp = "parent";
get parentGetter() { return "parent getter"; }
}
class Child extends Parent {
childProp = "child";
get childGetter() { return "child getter"; }
}
const instance = new Child();
// Default - only own properties
console.log(format(instance));
/*
Child {
"childProp": "child",
}
*/
// Include enumerable properties from prototype
console.log(format(instance, { includeEnumerable: true }));
/*
Child {
"childProp": "child",
"parentProp": "parent",
}
*/
// Include getters (may trigger side effects)
console.log(format(instance, { includeGetters: true }));
/*
Child {
"childProp": "child",
"childGetter": "child getter",
}
*/
// Include both
console.log(format(instance, {
includeEnumerable: true,
includeGetters: true
}));
/*
Child {
"childProp": "child",
"childGetter": "child getter",
"parentProp": "parent",
"parentGetter": "parent getter",
}
*/Enable deterministic output by sorting object keys alphabetically.
interface FormatOptions {
/**
* Sort object keys alphabetically
* Ensures consistent output order regardless of key insertion order
* Important for deterministic serialization and testing
*/
sort?: boolean;
}Usage Examples:
import { format } from "tcompare";
const obj = { z: 1, a: 2, m: 3, b: 4 };
// Default - insertion order preserved
console.log(format(obj));
/*
Object {
"z": 1,
"a": 2,
"m": 3,
"b": 4,
}
*/
// Sorted - alphabetical order
console.log(format(obj, { sort: true }));
/*
Object {
"a": 2,
"b": 4,
"m": 3,
"z": 1,
}
*/includeGetters: false (default) to avoid side effectsdiffContext size for large objects - smaller values improve readabilitytight style is fastest but least readablesort: true for deterministic test outputdiffContext when debugging complex object differencespretty style for human-readable debugging outputjs style when you need to copy-paste test dataincludeEnumerable unless specifically neededincludeGetters in production due to side effectsbufferChunkSize based on your typical buffer sizesstyle based on your output destination (logs, files, etc.)The styles constant provides access to the three predefined formatting styles.
/**
* Collection of predefined formatting styles
*/
const styles: { [style in StyleType]: Style };
/**
* Individual style implementations
*/
interface Style {
/** Format functions with optional class names */
fn: (fn: Function, cls: string) => string;
/** Format empty Set objects */
setEmpty: (cls: string) => string;
/** Format Set opening delimiter */
setHead: (cls: string) => string;
/** Format Set closing delimiter */
setTail: (indent: string) => string;
/** Format Map empty objects */
mapEmpty: (cls: string) => string;
/** Format Map opening delimiter */
mapHead: (cls: string) => string;
/** Format Map closing delimiter */
mapTail: (indent: string) => string;
/** Format circular reference markers */
circular: (node: any) => string;
/** Format node identifiers for circular references */
nodeId: (id: number) => string;
/** Additional formatting functions for Error objects, Buffers, Arrays, etc. */
[key: string]: any;
}Usage Examples:
import { styles, format } from "tcompare";
// Access individual style configurations
const prettyStyle = styles.pretty;
const jsStyle = styles.js;
const tightStyle = styles.tight;
// Custom formatting using specific style
const obj = { name: "test", values: [1, 2, 3] };
// These are equivalent:
console.log(format(obj, { style: "pretty" }));
console.log(new Format(obj, { style: "pretty" }).print());
// Direct style access for custom implementations
const customFormatter = {
...styles.pretty,
// Override specific formatting functions
fn: (fn: Function, cls: string) => `[Function: ${fn.name}]`
};Install with Tessl CLI
npx tessl i tessl/npm-tcompare