Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@
"@typescript-eslint",
"eslint-plugin-import",
"eslint-plugin-jsdoc",
"etc"
"etc",
"sonarjs"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:etc/recommended",
"plugin:import/typescript"
"plugin:import/typescript",
"plugin:sonarjs/recommended"
],
"overrides": [{
"files": "*.ts?(x)",
Expand Down Expand Up @@ -153,7 +155,10 @@
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
"import/newline-after-import": "error",
"import/no-absolute-path": "error",
"import/no-cycle": ["error", { "ignoreExternal": true }],
"import/no-cycle": ["error", {
"allowUnsafeDynamicCyclicDependency": true,
"ignoreExternal": true
}],
"import/no-duplicates": "error",
"import/no-import-module-exports": "error",
"import/no-namespace": "error",
Expand All @@ -170,8 +175,7 @@
}],
"jsdoc/check-alignment": "error",
"jsdoc/check-indentation": ["error", { "excludeTags": ["example", "param", "returns"] }],
"jsdoc/newline-after-description": "error",
"jsx-quotes": "error",
"jsdoc/tag-lines": ["error", "any", { "startLines": 1 }],
"keyword-spacing": "error",
"linebreak-style": "error",
"max-classes-per-file": ["error", 1],
Expand Down Expand Up @@ -212,6 +216,9 @@
"radix": "error",
"rest-spread-spacing": "error",
"semi-spacing": "error",
"sonarjs/cognitive-complexity": "off",
"sonarjs/no-duplicate-string": "off",
"sonarjs/no-inverted-boolean-check": "error",
"sort-keys": "error",
"space-before-blocks": "error",
"space-in-parens": "error",
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
- 17
- 18
- 19
- 20

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
19.6.0
20.3.1
222 changes: 111 additions & 111 deletions .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs

Large diffs are not rendered by default.

873 changes: 0 additions & 873 deletions .yarn/releases/yarn-3.4.1.cjs

This file was deleted.

874 changes: 874 additions & 0 deletions .yarn/releases/yarn-3.6.0.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ plugins:
- path: .yarn/plugins/@yarnpkg/plugin-engines.cjs
spec: "https://raw.githubusercontent.com/devoto13/yarn-plugin-engines/main/bundles/%40yarnpkg/plugin-engines.js"

yarnPath: .yarn/releases/yarn-3.4.1.cjs
yarnPath: .yarn/releases/yarn-3.6.0.cjs
10 changes: 5 additions & 5 deletions examples/jest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
"devDependencies": {
"@examples/symbol-plugin": "workspace:^",
"@stackbuilders/assertive-ts": "workspace:^",
"@types/jest": "^29.4.0",
"@types/node": "^18.14.2",
"jest": "^29.4.3",
"ts-jest": "^29.0.5",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.2",
"jest": "^29.5.0",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"typescript": "^4.9.5"
"typescript": "^5.1.6"
}
}
4 changes: 2 additions & 2 deletions examples/mocha/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
"@examples/symbol-plugin": "workspace:^",
"@stackbuilders/assertive-ts": "workspace:^",
"@types/mocha": "^10.0.1",
"@types/node": "^18.14.2",
"@types/node": "^20.3.2",
"mocha": "^10.2.0",
"ts-node": "^10.9.1",
"typescript": "^4.9.5"
"typescript": "^5.1.6"
}
}
2 changes: 1 addition & 1 deletion examples/symbolPlugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
},
"devDependencies": {
"@stackbuilders/assertive-ts": "workspace:^",
"typescript": "^4.9.5"
"typescript": "^5.1.6"
},
"peerDependencies": {
"@stackbuilders/assertive-ts": "*"
Expand Down
21 changes: 11 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"author": "Stack Builders <info@stackbuilders.com>",
"license": "MIT",
"engines": {
"node": ">=16 <=19"
"node": ">=16"
},
"packageManager": "yarn@3.4.1",
"packageManager": "yarn@3.6.0",
"workspaces": [
"package/",
"examples/*"
Expand All @@ -23,14 +23,15 @@
"test": "turbo run test"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.54.0",
"@typescript-eslint/parser": "^5.54.0",
"eslint": "^8.35.0",
"eslint-import-resolver-typescript": "^3.5.3",
"eslint-plugin-etc": "^2.0.2",
"@typescript-eslint/eslint-plugin": "^5.60.1",
"@typescript-eslint/parser": "^5.60.1",
"eslint": "^8.43.0",
"eslint-import-resolver-typescript": "^3.5.5",
"eslint-plugin-etc": "^2.0.3",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsdoc": "^40.0.0",
"turbo": "^1.8.3",
"typescript": "^4.9.5"
"eslint-plugin-jsdoc": "^46.4.2",
"eslint-plugin-sonarjs": "^0.19.0",
"turbo": "^1.10.6",
"typescript": "^5.1.6"
}
}
18 changes: 9 additions & 9 deletions package/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@
},
"devDependencies": {
"@types/mocha": "^10.0.1",
"@types/node": "^18.14.2",
"@types/sinon": "^10.0.13",
"all-contributors-cli": "^6.24.0",
"@types/node": "^20.3.2",
"@types/sinon": "^10.0.15",
"all-contributors-cli": "^6.26.0",
"cross-env": "^7.0.3",
"mocha": "^10.2.0",
"semantic-release": "^20.1.0",
"sinon": "^15.0.1",
"semantic-release": "^21.0.6",
"sinon": "^15.2.0",
"ts-node": "^10.9.1",
"typedoc": "^0.23.26",
"typedoc-plugin-markdown": "^3.14.0",
"typedoc-plugin-merge-modules": "^4.0.1",
"typescript": "^4.9.5"
"typedoc": "^0.24.8",
"typedoc-plugin-markdown": "^3.15.3",
"typedoc-plugin-merge-modules": "^5.0.1",
"typescript": "^5.1.6"
}
}
9 changes: 6 additions & 3 deletions package/src/lib/Assertion.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { UnsupportedOperationError } from "./errors/UnsupportedOperationError";
import { type TypeFactory } from "./helpers/TypeFactories";
import { isJSObject, isKeyOf } from "./helpers/guards";
import { isStruct, isKeyOf } from "./helpers/guards";
import { prettify } from "./helpers/messages";

import { AssertionError } from "assert";
Expand Down Expand Up @@ -366,7 +366,10 @@ export class Assertion<T> {
return this.actual.getTime() === expected.getTime();
}

if (isJSObject(this.actual) && isJSObject(expected)) {
if (
(isStruct(this.actual) && isStruct(expected))
|| (Array.isArray(this.actual) && Array.isArray(expected))
) {
const actualKeys = Object.keys(this.actual);
const expectedKeys = Object.keys(expected);
const sizeMatch = actualKeys.length === expectedKeys.length;
Expand All @@ -375,7 +378,7 @@ export class Assertion<T> {
return sizeMatch && valuesMatch;
}

return false;
return Object.is(this.actual, expected);
};

const areBothNaN = typeof this.actual === "number"
Expand Down
6 changes: 6 additions & 0 deletions package/src/lib/FunctionAssertion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import { isDeepStrictEqual } from "util";

export type AnyFunction = (...args: unknown[]) => unknown;

/**
* Helper symbol used to indicate that no error was captured during
* the assertion.
*
* @hidden
*/
const NoThrow = Symbol("NoThrow");

/**
Expand Down
15 changes: 6 additions & 9 deletions package/src/lib/ObjectAssertion.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
import { Assertion } from "./Assertion";
import { prettify } from "./helpers/messages";
import { Entry, Struct } from "./helpers/types";

import { AssertionError } from "assert";
import { isDeepStrictEqual } from "util";

export type JSObject = Record<keyof unknown, unknown>;

export type Entry<T, K = keyof T> = K extends keyof T
? [K, T[K]]
: never;

/**
* Encapsulates assertion methods applicable to objects.
*
* @param T the object's definition type
*/
export class ObjectAssertion<T extends JSObject> extends Assertion<T> {
export class ObjectAssertion<T extends Struct> extends Assertion<T> {

public constructor(actual: T) {
super(actual);
}

private hasOwnProp(prop: PropertyKey): boolean {
return Object.prototype.hasOwnProperty.call(this.actual, prop);
private hasOwnProp(prop: PropertyKey | undefined): boolean {
return prop !== undefined
? Object.prototype.hasOwnProperty.call(this.actual, prop)
: false;
}

/**
Expand Down
9 changes: 5 additions & 4 deletions package/src/lib/expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { DateAssertion } from "./DateAssertion";
import { ErrorAssertion } from "./ErrorAssertion";
import { AnyFunction, FunctionAssertion } from "./FunctionAssertion";
import { NumberAssertion } from "./NumberAssertion";
import { JSObject, ObjectAssertion } from "./ObjectAssertion";
import { ObjectAssertion } from "./ObjectAssertion";
import { PromiseAssertion } from "./PromiseAssertion";
import { StringAssertion } from "./StringAssertion";
import { config } from "./config/Config";
import { isAnyFunction, isJSObject, isPromise } from "./helpers/guards";
import { isAnyFunction, isStruct, isPromise } from "./helpers/guards";
import { Struct } from "./helpers/types";

export interface Expect {
(actual: boolean): BooleanAssertion;
Expand All @@ -20,7 +21,7 @@ export interface Expect {
<T>(actual: Promise<T>): PromiseAssertion<T>;
<T extends AnyFunction>(actual: T): FunctionAssertion<T>;
<T extends Error>(actual: T): ErrorAssertion<T>;
<T extends JSObject>(actual: T): ObjectAssertion<T>;
<T extends Struct>(actual: T): ObjectAssertion<T>;
<T>(actual: T): Assertion<T>;
}

Expand Down Expand Up @@ -61,7 +62,7 @@ function expectMatcher<T>(actual: T): ReturnType<Expect> {
return new plugin.Assertion(actual);
}

if (isJSObject(actual)) {
if (isStruct(actual)) {
return new ObjectAssertion(actual);
}

Expand Down
11 changes: 6 additions & 5 deletions package/src/lib/helpers/TypeFactories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { BooleanAssertion } from "../BooleanAssertion";
import { DateAssertion } from "../DateAssertion";
import { type AnyFunction, FunctionAssertion } from "../FunctionAssertion";
import { NumberAssertion } from "../NumberAssertion";
import { JSObject, ObjectAssertion } from "../ObjectAssertion";
import { ObjectAssertion } from "../ObjectAssertion";
import { StringAssertion } from "../StringAssertion";

import { isJSObject } from "./guards";
import { isStruct } from "./guards";
import { Struct } from "./types";

export type AssertionFactory<S, A extends Assertion<S>> = new(actual: S) => A;

Expand Down Expand Up @@ -99,7 +100,7 @@ export interface StaticTypeFactories {
* ```
* @typeParam T the type of the object
*/
object<T extends JSObject>(): TypeFactory<T, ObjectAssertion<T>>;
object<T extends Struct>(): TypeFactory<T, ObjectAssertion<T>>;
}

export const TypeFactories: Readonly<StaticTypeFactories> = {
Expand Down Expand Up @@ -145,10 +146,10 @@ export const TypeFactories: Readonly<StaticTypeFactories> = {
typeName: type.name,
};
},
object<T extends JSObject>() {
object<T extends Struct>() {
return {
Factory: ObjectAssertion,
predicate: (value): value is T => isJSObject(value),
predicate: (value): value is T => isStruct(value),
typeName: "object",
};
},
Expand Down
10 changes: 6 additions & 4 deletions package/src/lib/helpers/guards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import type {
LowInclusiveBetweenOptions,
} from "../NumberAssertion";

export function isJSObject<T>(value: T): value is { [K in keyof T]: T[K] } {
return value !== null
&& typeof value === "object"
&& typeof value !== "function";
import { Struct } from "./types";

export function isStruct<T>(value: T): value is { [K in keyof T]: T[K] } & Struct {
return typeof value === "object"
&& value !== null
&& !Array.isArray(value);
}

export function isKeyOf<T extends object>(target: T, key: unknown): key is keyof T {
Expand Down
19 changes: 19 additions & 0 deletions package/src/lib/helpers/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Backwards compatibility alias of the `Struct` type.
*
* @deprecated in favor of {@link Struct}
*/
export type JSObject = Struct;

/**
* Utility type that represents any kind of structured object.
*/
export type Struct = Record<keyof unknown, unknown>;

/**
* Mapped type which transforms an structured object `<T>` to entries., i.e. a
* key-value tuples.
*
* @param T an structured object which extends {@link Struct}
*/
export type Entry<T extends Struct> = { [K in keyof T]: [K, T[K]]; }[keyof T];
Loading