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
14 changes: 14 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ grunt lint lint-fix # Lint with auto-fix
- **var usage**: Allowed (for ES5 compatibility)
- **const preference**: Not enforced

### Copyright Headers
- **New files**: Use current year only (e.g., `Copyright (c) 2026 NevWare21 Solutions LLC`)
- **Edited files**: Use original year through current year (e.g., `Copyright (c) 2024-2026 NevWare21 Solutions LLC`)
- All source files must include the standard copyright header:
```typescript
/*
* @nevware21/tripwire
* https://github.com/nevware21/tripwire
*
* Copyright (c) YYYY[-YYYY] NevWare21 Solutions LLC
* Licensed under the MIT license.
*/
```

### Coding Patterns
- Support for both ES5 and ES6+ runtimes
- Avoid ES6+ features in core implementation when targeting ES5
Expand Down
21 changes: 14 additions & 7 deletions core/src/assert/assertClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { deepEqualsFunc, deepStrictEqualsFunc, equalsFunc, strictEqualsFunc } fr
import { aboveFunc, belowFunc, leastFunc, mostFunc, withinFunc } from "./ops/numericOp";
import { typeOfFunc } from "./funcs/typeOf";
import { instanceOfFunc } from "./funcs/instanceOf";
import { lengthFunc } from "./funcs/length";

/**
* @internal
Expand Down Expand Up @@ -148,19 +149,19 @@ export function createAssert(): IAssertClass {
isNotOk: "not.ok", // The `isNotOk` function is an alias for `not.truthy`

equal: { scopeFn: equalsFunc, nArgs: 2 },
equals: { scopeFn: equalsFunc, nArgs: 2 },
equals: { alias: "equal" },
strictEqual: { scopeFn: strictEqualsFunc, nArgs: 2 },
strictEquals: { scopeFn: strictEqualsFunc, nArgs: 2 },
strictEquals: { alias: "strictEqual" },
notStrictEqual: { scopeFn: createExprAdapter("not", strictEqualsFunc), nArgs: 2 },
deepEqual: { scopeFn: deepEqualsFunc, nArgs: 2 },
deepEquals: { scopeFn: deepEqualsFunc, nArgs: 2 },
deepEquals: { alias: "deepEqual" },
deepStrictEqual: { scopeFn: deepStrictEqualsFunc, nArgs: 2 },
deepStrictEquals: { scopeFn: deepStrictEqualsFunc, nArgs: 2 },
deepStrictEquals: { alias: "deepStrictEqual" },

notEqual: { scopeFn: createExprAdapter("not", equalsFunc), nArgs: 2 },
notEquals: { scopeFn: createExprAdapter("not", equalsFunc), nArgs: 2 },
notEquals: { alias: "notEqual" },
notDeepEqual: { scopeFn: createExprAdapter("not", deepEqualsFunc), nArgs: 2 },
notDeepEquals: { scopeFn: createExprAdapter("not", deepEqualsFunc), nArgs: 2 },
notDeepEquals: { alias: "notDeepEqual" },

isTrue: isStrictTrueFunc,
isFalse: isStrictFalseFunc,
Expand Down Expand Up @@ -256,7 +257,13 @@ export function createAssert(): IAssertClass {
isAtMost: { scopeFn: mostFunc, nArgs: 2 },
isNotAtMost: { scopeFn: createExprAdapter("not", mostFunc), nArgs: 2 },
isWithin: { scopeFn: withinFunc, nArgs: 3 },
isNotWithin: { scopeFn: createExprAdapter("not", withinFunc), nArgs: 3 }
isNotWithin: { scopeFn: createExprAdapter("not", withinFunc), nArgs: 3 },

// Length checking
lengthOf: { scopeFn: lengthFunc, nArgs: 2 },
notLengthOf: { scopeFn: createExprAdapter("not", lengthFunc), nArgs: 2 },
sizeOf: { alias: "lengthOf" },
notSizeOf: { alias: "notLengthOf" }
};

addAssertFuncs(assert, assertFuncs);
Expand Down
2 changes: 1 addition & 1 deletion core/src/assert/funcs/exists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @nevware21/tripwire
* https://github.com/nevware21/tripwire
*
* Copyright (c) 2024 NevWare21 Solutions LLC
* Copyright (c) 2026 NevWare21 Solutions LLC
* Licensed under the MIT license.
*/

Expand Down
2 changes: 1 addition & 1 deletion core/src/assert/funcs/instanceOf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @nevware21/tripwire
* https://github.com/nevware21/tripwire
*
* Copyright (c) 2024-2026 NevWare21 Solutions LLC
* Copyright (c) 2026 NevWare21 Solutions LLC
* Licensed under the MIT license.
*/

Expand Down
77 changes: 77 additions & 0 deletions core/src/assert/funcs/length.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* @nevware21/tripwire
* https://github.com/nevware21/tripwire
*
* Copyright (c) 2026 NevWare21 Solutions LLC
* Licensed under the MIT license.
*/

import { isNullOrUndefined, isObject, isString } from "@nevware21/ts-utils";
import { IAssertScope } from "../interface/IAssertScope";
import { MsgSource } from "../interface/types";

/**
* Asserts that the target has a length or size property equal to the given number.
* Works with arrays, strings, Maps, Sets, and any object with a length or size property.
* @since 0.1.5
* @param this - The assert scope.
* @param length - The expected length or size.
* @param evalMsg - The message to display if the assertion fails.
* @returns - The assert scope.
* @example
* ```typescript
* import { assert } from "@nevware21/tripwire";
*
* assert([1, 2, 3]).has.lengthOf(3); // Passes - array length is 3
* assert("hello").has.lengthOf(5); // Passes - string length is 5
* assert(new Map([["a", 1]])).has.lengthOf(1); // Passes - Map size is 1
* assert(new Set([1, 2])).has.lengthOf(2); // Passes - Set size is 2
* assert([1, 2]).has.lengthOf(3); // Fails - array length is 2, not 3
* ```
*/
export function lengthFunc<R>(this: IAssertScope, length: number, evalMsg?: MsgSource): R {
let context = this.context;
let value = context.value;

context.set("expected", length);

// Check if the value has a 'length' property
// For strings and objects (includes arrays, functions, etc.)
let hasLength = false;
let hasSize = false;

if (!isNullOrUndefined(value)) {
// Strings have length as a property
if (isString(value)) {
hasLength = true;
} else if (isObject(value)) {
// Use 'in' operator only for objects
hasLength = "length" in value;
hasSize = "size" in value;
}
}

if (!hasLength && !hasSize) {
context.eval(
false,
evalMsg || "expected {value} to have property \"length\" or \"size\""
);
} else {
let actualLength = hasLength ? (value as any).length : (value as any).size;
context.set("length", actualLength);

if (hasLength) {
context.eval(
actualLength === length,
evalMsg || "expected {value} to have a length of {expected} but got {length}"
);
} else {
context.eval(
actualLength === length,
evalMsg || "expected {value} to have a size of {expected} but got {length}"
);
}
}

return this.that;
}
2 changes: 1 addition & 1 deletion core/src/assert/funcs/nested.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @nevware21/tripwire
* https://github.com/nevware21/tripwire
*
* Copyright (c) 2024 NevWare21 Solutions LLC
* Copyright (c) 2026 NevWare21 Solutions LLC
* Licensed under the MIT license.
*/

Expand Down
2 changes: 1 addition & 1 deletion core/src/assert/funcs/typeOf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @nevware21/tripwire
* https://github.com/nevware21/tripwire
*
* Copyright (c) 2024-2026 NevWare21 Solutions LLC
* Copyright (c) 2026 NevWare21 Solutions LLC
* Licensed under the MIT license.
*/

Expand Down
100 changes: 99 additions & 1 deletion core/src/assert/interface/IAssertClass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @nevware21/tripwire
* https://github.com/nevware21/tripwire
*
* Copyright (c) 2024 NevWare21 Solutions LLC
* Copyright (c) 2024-2026 NevWare21 Solutions LLC
* Licensed under the MIT license.
*/

Expand Down Expand Up @@ -1900,6 +1900,104 @@ export interface IAssertClass<AssertInst extends IAssertInst = IAssertInst> {
* ```
*/
isNotWithin(value: number | Date, start: number | Date, finish: number | Date, initMsg?: MsgSource): AssertInst;

/**
* Asserts that the target has a length or size property equal to the given number.
*
* Works with arrays, strings, Maps, Sets, and any object with a length or size property.
*
* @since 0.1.5
* @param object - The object to check (array, string, Map, Set, or any object with length/size).
* @param length - The expected length or size.
* @param initMsg - The message to display if the assertion fails.
* @asserts That `object` has a length or size equal to `length` and throws {@link AssertionFailure} if it does not.
* @example
* ```typescript
* assert.lengthOf([1, 2, 3], 3); // Passes
* assert.lengthOf("hello", 5); // Passes
* assert.lengthOf(new Map([["a", 1]]), 1); // Passes
* assert.lengthOf(new Set([1, 2]), 2); // Passes
* assert.lengthOf([1, 2], 3); // Throws AssertionFailure
* ```
*/
lengthOf<T extends { readonly length?: number | undefined } | { readonly size?: number | undefined }>(
object: T,
length: number,
initMsg?: MsgSource
): AssertInst;

/**
* Asserts that the target does not have a length or size property equal to the given number.
*
* Works with arrays, strings, Maps, Sets, and any object with a length or size property.
* This is the inverse of {@link lengthOf}.
*
* @since 0.1.5
* @param object - The object to check (array, string, Map, Set, or any object with length/size).
* @param length - The length or size that the object should not have.
* @param initMsg - The message to display if the assertion fails.
* @asserts That `object` does not have a length or size equal to `length` and throws {@link AssertionFailure} if it does.
* @example
* ```typescript
* assert.notLengthOf([1, 2, 3], 2); // Passes
* assert.notLengthOf("hello", 4); // Passes
* assert.notLengthOf([1, 2], 2); // Throws AssertionFailure
* ```
*/
notLengthOf<T extends { readonly length?: number | undefined } | { readonly size?: number | undefined }>(
object: T,
length: number,
initMsg?: MsgSource
): AssertInst;

/**
* Asserts that the target has a size or length property equal to the given number.
*
* Works with arrays, strings, Maps, Sets, and any object with a length or size property.
*
* @since 0.1.5
* @param object - The object to check (array, string, Map, Set, or any object with length/size).
* @param length - The expected length or size.
* @param initMsg - The message to display if the assertion fails.
* @asserts That `object` has a length or size equal to `length` and throws {@link AssertionFailure} if it does not.
* @example
* ```typescript
* assert.sizeOf([1, 2, 3], 3); // Passes
* assert.sizeOf("hello", 5); // Passes
* assert.sizeOf(new Map([["a", 1]]), 1); // Passes
* assert.sizeOf(new Set([1, 2]), 2); // Passes
* assert.sizeOf([1, 2], 3); // Throws AssertionFailure
* ```
*/
sizeOf<T extends { readonly length?: number | undefined } | { readonly size?: number | undefined }>(
object: T,
length: number,
initMsg?: MsgSource
): AssertInst;

/**
* Asserts that the target does not have a size or length property equal to the given number.
*
* Works with arrays, strings, Maps, Sets, and any object with a length or size property.
* This is the inverse of {@link sizeOf}.
*
* @since 0.1.5
* @param object - The object to check (array, string, Map, Set, or any object with length/size).
* @param length - The length or size that the object should not have.
* @param initMsg - The message to display if the assertion fails.
* @asserts That `object` does not have a length or size equal to `length` and throws {@link AssertionFailure} if it does.
* @example
* ```typescript
* assert.notSizeOf([1, 2, 3], 2); // Passes
* assert.notSizeOf("hello", 4); // Passes
* assert.notSizeOf([1, 2], 2); // Throws AssertionFailure
* ```
*/
notSizeOf<T extends { readonly length?: number | undefined } | { readonly size?: number | undefined }>(
object: T,
length: number,
initMsg?: MsgSource
): AssertInst;
}

export type IExtendedAssert<T = any> = IAssertClass<IAssertInst & T> & T;
8 changes: 8 additions & 0 deletions core/src/assert/interface/IAssertInstHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/*
* @nevware21/tripwire
* https://github.com/nevware21/tripwire
*
* Copyright (c) 2024 NevWare21 Solutions LLC
* Licensed under the MIT license.
*/

import { IAssertScope } from "./IAssertScope";

/**
Expand Down
35 changes: 34 additions & 1 deletion core/src/assert/interface/ops/IHasOp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @nevware21/tripwire
* https://github.com/nevware21/tripwire
*
* Copyright (c) 2024 NevWare21 Solutions LLC
* Copyright (c) 2024-2026 NevWare21 Solutions LLC
* Licensed under the MIT license.
*/

Expand All @@ -12,6 +12,7 @@ import { IAllOp } from "./IAllOp";
import { IAnyOp } from "./IAnyOp";
import { IOwnOp } from "./IOwnOp";
import { INestedOp } from "./INestedOp";
import { MsgSource } from "../types";

/**
* Provides the 'has' operation for the assert scope.
Expand Down Expand Up @@ -82,4 +83,36 @@ export interface IHasOp<R> {
* ```
*/
iterator: SymbolFn;

/**
* Asserts that the target has a length or size property equal to the given number.
* Works with arrays, strings, Maps, Sets, and any object with a length or size property.
* @since 0.1.5
* @param length - The expected length or size.
* @param evalMsg - Optional error message.
* @example
* ```typescript
* expect([1, 2, 3]).has.lengthOf(3); // Passes - array length is 3
* expect("hello").has.lengthOf(5); // Passes - string length is 5
* expect(new Map([["a", 1]])).has.lengthOf(1); // Passes - Map size is 1
* expect(new Set([1, 2])).has.lengthOf(2); // Passes - Set size is 2
* ```
*/
lengthOf(n: number, evalMsg?: MsgSource): R;

/**
* Asserts that the target has a size or length property equal to the given number.
* Works with arrays, strings, Maps, Sets, and any object with a length or size property.
* @since 0.1.5
* @param size - The expected size or length.
* @param evalMsg - Optional error message.
* @example
* ```typescript
* expect([1, 2, 3]).has.sizeOf(3); // Passes - array length is 3
* expect("hello").has.sizeOf(5); // Passes - string length is 5
* expect(new Map([["a", 1]])).has.sizeOf(1); // Passes - Map size is 1
* expect(new Set([1, 2])).has.sizeOf(2); // Passes - Set size is 2
* ```
*/
sizeOf(n: number, evalMsg?: MsgSource): R;
}
2 changes: 1 addition & 1 deletion core/src/assert/interface/ops/INestedOp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* @nevware21/tripwire
* https://github.com/nevware21/tripwire
*
* Copyright (c) 2024 NevWare21 Solutions LLC
* Copyright (c) 2026 NevWare21 Solutions LLC
* Licensed under the MIT license.
*/

Expand Down
Loading
Loading