Comparing version 5.0.7 to 6.0.0
{ | ||
"name": "tcompare", | ||
"version": "5.0.7", | ||
"version": "6.0.0", | ||
"description": "A comprehensive comparison library, for use in test frameworks", | ||
"main": "index.js", | ||
"repository": { | ||
@@ -12,18 +11,55 @@ "type": "git", | ||
"license": "ISC", | ||
"main": "./dist/cjs/index.js", | ||
"module": "./dist/mjs/index.js", | ||
"exports": { | ||
".": { | ||
"import": "./dist/mjs/index.js", | ||
"require": "./dist/cjs/index.js" | ||
}, | ||
"./plugin-base": { | ||
"import": "./dist/mjs/plugin-base.js", | ||
"require": "./dist/cjs/plugin-base.js" | ||
} | ||
}, | ||
"files": [ | ||
"index.js", | ||
"lib/*.js" | ||
"dist" | ||
], | ||
"dependencies": { | ||
"diff": "^4.0.2" | ||
"diff": "^5.1.0" | ||
}, | ||
"devDependencies": { | ||
"tap": "^15.0.8" | ||
"@types/diff": "^5.0.2", | ||
"@types/node": "^18.11.9", | ||
"@types/tap": "^15.0.7", | ||
"c8": "^7.12.0", | ||
"prettier": "^2.8.0", | ||
"tap": "^16.3.2", | ||
"ts-node": "^10.9.1", | ||
"typedoc": "^0.23.21", | ||
"typescript": "^4.9.3" | ||
}, | ||
"prettier": { | ||
"semi": false, | ||
"printWidth": 60, | ||
"tabWidth": 2, | ||
"useTabs": false, | ||
"singleQuote": true, | ||
"jsxSingleQuote": false, | ||
"bracketSameLine": true, | ||
"arrowParens": "avoid", | ||
"endOfLine": "lf" | ||
}, | ||
"scripts": { | ||
"test": "tap", | ||
"snap": "tap", | ||
"snap": "c8 tap", | ||
"test": "c8 tap", | ||
"preprepare": "rm -rf dist", | ||
"prepare": "tsc -p tsconfig-cjs.json && tsc -p tsconfig-esm.json", | ||
"postprepare": "bash fixup.sh", | ||
"pretest": "npm run prepare", | ||
"presnap": "npm run prepare", | ||
"format": "prettier --write . --loglevel warn", | ||
"preversion": "npm test", | ||
"postversion": "npm publish", | ||
"prepublishOnly": "git push origin --follow-tags" | ||
"prepublishOnly": "git push origin --follow-tags", | ||
"typedoc": "typedoc --tsconfig tsconfig-esm.json ./src/*.ts" | ||
}, | ||
@@ -34,8 +70,13 @@ "tap": { | ||
], | ||
"check-coverage": true, | ||
"coverage-map": "map.js" | ||
"coverage": false, | ||
"node-arg": [ | ||
"--no-warnings", | ||
"--loader", | ||
"ts-node/esm" | ||
], | ||
"ts": false | ||
}, | ||
"engines": { | ||
"node": ">=10" | ||
"node": ">=16" | ||
} | ||
} |
329
README.md
# tcompare | ||
A comprehensive comparison library, for use in test frameworks. Walks an | ||
object once, generating both a simple true/false result, as well as a | ||
nicely formatted human-readable diff string. | ||
A comprehensive comparison library, for use in test frameworks. | ||
Walks an object once, generating both a simple true/false result, | ||
as well as a string representation of both the actual and | ||
expected values (highlighting just the parts that differ) and a | ||
patch-style diff string. | ||
## USAGE | ||
```js | ||
const { match, same, strict, has, hasStrict, format } = require('tcompare') | ||
```ts | ||
// require() is fine too | ||
import { | ||
match, | ||
same, | ||
strict, | ||
has, | ||
hasStrict, | ||
matchOnly, | ||
matchStrict, | ||
} from 'tcompare' | ||
import type { Result } from 'tcompare' | ||
// Result is an object with { Boolean match, String diff } | ||
const result = match(object, pattern) | ||
const result: Result = match(object, pattern) | ||
if (!result.match) { | ||
@@ -20,111 +31,191 @@ console.log(`item did not match pattern`) | ||
} | ||
// raw classes exported also | ||
import { MatchOnly } from 'tcompare' | ||
const mo = new MatchOnly( | ||
{ a: 1, b: 2 }, | ||
{ expect: { a: Number } } | ||
) | ||
const diff: string = mo.print() | ||
console.log(mo.match) // false | ||
console.log(diff) | ||
/* | ||
--- expected | ||
+++ actual | ||
@@ -1,2 +1,3 @@ | ||
Object { | ||
+ "b": 2, | ||
} | ||
*/ | ||
``` | ||
## METHODS | ||
- `indent` - String to indent each nested level. Defaults to `' '`.## METHODS | ||
* `format(object, [options])` - No comparisons performed. Just print out the | ||
object. Returns just the string format. | ||
* `same(object, pattern, [options])` - Ensure that all items in the pattern are | ||
found in the object, and vice versa, matching loosely (so, for example `1` | ||
will match with `'1'`). | ||
* `strict(object, pattern, [options])` - Ensure that all items in the pattern | ||
are found in the object, and vice versa, matching strictly (so, for example | ||
`1` will not match with `'1'`). Objects must have the same constructors, | ||
and all fields will be matched recursively using the same `strict` test. | ||
* `has(object, pattern, [options])` - Ensure that all items in the pattern are | ||
found in the object, but ignore additional items found in the object, | ||
matching loosely. | ||
* `hasStrict(object, pattern, [options])` - Ensure that all items in the | ||
pattern are found in the object, but ignore additional items found in the | ||
object, matching strictly. Constructors do _not_ have to match between | ||
objects, but if `constructor` is set as an ownProperty on the pattern | ||
object, then it will be checked. | ||
* `match(object, pattern, [options])` - Verify that all items in `pattern` are | ||
found in `object`, and that they match. This is the loosest possible | ||
algorithm, allowing cases where we just want to verify that an object | ||
contains a few important properties. The algorithm is the same as | ||
the one used by [tmatch](http://npm.im/tmatch). In a nutshell: | ||
* If the object and pattern are loosely equal, then pass | ||
* If the object and the pattern are both Regular Expressions, Date objects | ||
or Buffers, then pass if they're "equivalent". | ||
* If the pattern is a RegExp, cast object to a string, and test against the | ||
RegExp. | ||
* If both are Strings, pass if pattern appears in object. | ||
* If pattern is a function, and object is an instance of that function, | ||
then pass. (This also applies to Symbol, Number, String, etc.) | ||
* If pattern and object are collections (object, map, set, array or | ||
iterable), then compare their contents. Each type of collection can only | ||
match its same type, with the exception of non-Set iterables (including | ||
`arguments` objects), which are cast to Arrays. | ||
Each method corresponds to an exported class. Except for | ||
`format()` (which returns a string), they all return a `Result` | ||
object. (That is, `{diff:string, match:boolean}`.) | ||
There are classes exported to correspond to each of these. All of these are | ||
instantiated like `new Format(object, options)`. An `expect` option is | ||
required for all classes except `Format`. Call `obj.print()` on the resulting | ||
object to generate a diff. Once the diff (or format) is generated, it'll have | ||
- `format(object, [options])` - No comparisons performed. Just print out the | ||
object. Returns just the formatted string. | ||
- `same(object, pattern, [options])` - Deep equivalence. Ensure | ||
that all items in the pattern are found in the object, and vice | ||
versa, matching loosely (so, for example `1` will match with | ||
`'1'`). | ||
- `strict(object, pattern, [options])` - Deep equality. Ensure | ||
that all items in the pattern are found in the object, and vice | ||
versa, matching strictly (so, for example `1` will not match | ||
with `'1'`). Objects must have the same constructors, and all | ||
fields will be matched recursively using the same `strict` | ||
test. | ||
- `has(object, pattern, [options])` - Ensure that all items in | ||
the pattern are found in the object, but ignore additional | ||
items found in the object, matching loosely. Classes only need | ||
to match loosely, so a plain JavaScript object can be used to | ||
check for fields on a class instance. | ||
- `hasStrict(object, pattern, [options])` - Ensure that all items | ||
in the pattern are found in the object, but ignore additional | ||
items found in the object, matching strictly. Constructors do | ||
_not_ have to match between objects, but if `constructor` is | ||
set as an ownProperty on the pattern object, then it will be | ||
checked for strict equality. | ||
- `match(object, pattern, [options])` - Verify that all items in | ||
`pattern` are found in `object`, and that they match in an | ||
extremely loose way. This is the loosest possible algorithm, | ||
allowing cases where we just want to verify that an object | ||
contains a few important properties. In summary: | ||
- If the object and pattern are loosely equal, then pass | ||
- If the object and the pattern are both Regular Expressions, | ||
Date objects or Buffers, then pass if they represent | ||
equivalent values. | ||
- If the pattern is a RegExp, cast object to a string, and | ||
test against the RegExp. | ||
- If both are Strings, pass if pattern appears in object. ( | ||
- If pattern is a function, and object is an instance of that | ||
function, then pass. (This also applies to Symbol, Number, | ||
String, etc.) | ||
- If pattern and object are collections (object, map, set, | ||
array or iterable), then compare their contents. Each type | ||
of collection can only match its same type, with the | ||
exception of non-Set iterables (including `arguments` | ||
objects), which are cast to Arrays. | ||
- `matchOnly(object, pattern, [options])` - Same comparison | ||
testing as `match()`, but will fail if the `object` has any | ||
properties that are not present in the `pattern`. | ||
- `matchStrict(object, pattern, [options])` - Same comparison | ||
testing as `match()`, but will fail when two values are | ||
equivalent but not strictly equal. (That is, when `a == b && | ||
!(a === b)`.) | ||
There are classes exported to correspond to each of these. All of these are | ||
instantiated like `new Format(object, options)`. An `expect` option is | ||
required for all classes except `Format`. Call `obj.print()` on the resulting | ||
object to generate a diff. Once the diff (or format) is generated, it'll have | ||
a `match` boolean member. | ||
## Classes | ||
The exported classes should usually not be used directly, and | ||
their implementation details are subject to change as needed | ||
between versions. | ||
The class heirarchy is: | ||
``` | ||
Format | ||
+-- Same | ||
+-- Strict | ||
+-- Has | ||
| +-- HasStrict (uses Strict.prototype.test) | ||
| +-- Match | ||
| +-- MatchStrict (fails if a==b && a!==b) | ||
+-- MatchOnly (uses Match.prototype.test) | ||
``` | ||
In order to compare or print an object, instantiate one of the | ||
classes, and call then the `print()` method, which will return | ||
the diff or formatted value. The `match` boolean property will | ||
be set after calling `print()`. If the objects match, then the | ||
returned `diff` will also be an empty string. | ||
## OPTIONS | ||
Each method can take the following options. | ||
### `FormatOptions` type | ||
* `includeEnumerable` - Set to `true` to walk over _all_ enumerable | ||
properties of a given object when comparing or formatting, rather than | ||
the default of only showing enumerable own-properties. Note that | ||
calling getter functions may be hazardous, as they may trigger | ||
side-effects. | ||
Every method and class can take the following options. | ||
* `includeGetters` - Set to `true` to walk over all enumerable getters | ||
on an object's prototype (but not from further down the prototype | ||
chain), in addition to own-properties. This is useful in cases where | ||
you want to compare or print an object with enumerable getters that | ||
return internal values in a read-only manner. Note that calling | ||
getter functions can be hazardous, as they may trigger side-effects. | ||
* `sort` - Set to `true` to sort object keys. This is important when | ||
- `sort` - Set to `true` to sort object keys. This is important when | ||
serializing in a deterministic way. | ||
* `style` - Set to `pretty` for a very human-readable style of object printing. | ||
Set to `js` for a copy-and-paste friendly valid JavaScript output. Set to | ||
`tight` for a minimal white-space js format. Default is `pretty`. Example: | ||
- `style` - Set to `pretty` for a very human-readable style of object printing. | ||
Set to `js` for a copy-and-paste friendly valid JavaScript output. Set to | ||
`tight` for a minimal white-space js format. Default is `pretty`. Example: | ||
``` | ||
// pretty style | ||
Object { | ||
"myMap": Map { | ||
Object { | ||
"a": 1, | ||
} => Object { | ||
"b": 2, | ||
} | ||
``` | ||
// pretty style | ||
Object { | ||
"myMap": Map { | ||
Object { | ||
"a": 1, | ||
} => Object { | ||
"b": 2, | ||
} | ||
} | ||
} | ||
// js style | ||
{ | ||
"myMap": new Map([ | ||
[{ | ||
"a": 1, | ||
}, { | ||
"b": 2, | ||
}] | ||
]) | ||
} | ||
// js style | ||
{ | ||
"myMap": new Map([ | ||
[{ | ||
"a": 1, | ||
}, { | ||
"b": 2, | ||
}] | ||
]) | ||
} | ||
// tight style | ||
{"myMap":new Map([[{"a":1,},{"b":2,}],]),} | ||
``` | ||
// tight style | ||
{"myMap":new Map([[{"a":1,},{"b":2,}],]),} | ||
``` | ||
Note that `tight` is not suitable for comparisons, only formatting. | ||
Note that `tight` is not suitable for comparisons, only formatting. | ||
- `bufferChunkSize` - The number of bytes to show per line when | ||
printing long `Buffer` objects. Defaults to 32. | ||
- `indent` - String to indent each nested level. Defaults to `' '`. | ||
- `includeEnumerable` - Set to `true` to walk over _all_ | ||
enumerable properties of a given object when comparing or | ||
formatting, rather than the default of only showing enumerable | ||
own-properties. Note that calling getter functions may be | ||
hazardous, as they may trigger side-effects. | ||
- `includeGetters` - Set to `true` to walk over all enumerable | ||
getters on an object's prototype (but not from further down the | ||
prototype chain), in addition to own-properties. This is useful | ||
in cases where you want to compare or print an object with | ||
enumerable getters that return internal values in a read-only | ||
manner. Note that calling getter functions can be hazardous, as | ||
they may trigger side-effects. | ||
### `SameOptions` type | ||
Comparison classes also take the following options. | ||
- `expect` - required. The pattern object to compare against. | ||
- `diffContext` - Optional, default 10. Number of lines of | ||
context to show in diff output. | ||
## Circular References | ||
Circular references are displayed using YAML-like references, so it's easy to | ||
determine _which_ item is circularly referenced. | ||
Circular references are displayed using YAML-like references, in | ||
order to determine _which_ item is circularly referenced. | ||
When doing comparisons, a pattern and object will be considered matching if | ||
they contain the _same_ circularity. So, for example, if a pattern refers to | ||
itself, then an object should refer to itself as well. | ||
When doing comparisons, a pattern and object will be considered | ||
matching if they contain the _same_ circularity. So, for example, | ||
if a pattern refers to itself, then an object should refer to | ||
itself as well. | ||
```js | ||
const a = {list: [], b: {}} | ||
const a = { list: [], b: {} } | ||
a.list.push(a) | ||
@@ -150,3 +241,53 @@ a.list.push(a.b) | ||
Note that circular references are never going to be valid JavaScript, even when | ||
using the `js` style. | ||
Note that circular references are never going to be valid | ||
JavaScript, even when using the `js` style. | ||
### Caveat: Circularity Between Pattern and Object Gets Weird | ||
It's possible to get strange output when an object and pattern | ||
refer to one another. | ||
```js | ||
import { same } from 'tcompare' | ||
const a = {} | ||
a.o = a | ||
const b = { o: a } | ||
console.error(same(a, b).diff) | ||
// produces this confusing output: | ||
/* | ||
--- expected | ||
+++ actual | ||
@@ -1,5 +1,3 @@ | ||
&ref_1 Object { | ||
- "o": &ref_1 Object { | ||
- "o": <*ref_1>, | ||
- }, | ||
+ "o": <*ref_1>, | ||
} | ||
*/ | ||
``` | ||
The more correct output would be something like: | ||
``` | ||
--- expected | ||
+++ actual | ||
@@ -1,5 +1,3 @@ | ||
&ref_1 Object { | ||
- "o": &ref_2 Object { | ||
- "o": <*ref_2>, | ||
- }, | ||
+ "o": <*ref_1>, | ||
} | ||
``` | ||
However, this requires tracking IDs in a much more complicated | ||
way, being aware of whether the object is being read as an | ||
pattern object or test object when determining its reference ID. | ||
Since this is a relatively unusual thing to happen, and only | ||
affects the output (but still properly detects whether it should | ||
be treated as a match or not), it will likely not be addressed. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
274777
65
4164
292
9
1
+ Addeddiff@5.2.0(transitive)
- Removeddiff@4.0.2(transitive)
Updateddiff@^5.1.0