Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
d0c2d04595 | |||
db49492af6 | |||
855e20a217 | |||
9b488a87a0 | |||
1847838ac3 | |||
91a3dc43d3 |
23
changelog.md
23
changelog.md
@ -1,5 +1,28 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-04-29 - 2.1.1 - fix(Assertion)
|
||||||
|
Improve chainability by fixing return types in assertion methods
|
||||||
|
|
||||||
|
- Update runCheck method to explicitly return the correct chainable type for both async and sync assertions
|
||||||
|
- Ensure customAssertion propagates the chainable Assertion instance
|
||||||
|
- Refactor internal promise handling for clarity and consistency
|
||||||
|
|
||||||
|
## 2025-04-28 - 2.1.0 - feat(core)
|
||||||
|
Add new matchers and improve negation messaging
|
||||||
|
|
||||||
|
- Added expect.any() and expect.anything() matchers for enhanced object pattern matching
|
||||||
|
- Introduced new number matchers: toBeNaN(), toBeFinite(), and toBeWithinRange()
|
||||||
|
- Implemented alias toBeEmpty() for both string and array matchers
|
||||||
|
- Enhanced function matchers with toThrowErrorMatching() and toThrowErrorWithMessage()
|
||||||
|
- Improved negation messaging to provide clearer failure messages (e.g. 'Expected 5 not to be greater than 3')
|
||||||
|
- Enhanced object assertions with a toHaveOwnProperty() shorthand that outputs unified diff-style messages
|
||||||
|
|
||||||
|
## 2025-04-28 - 2.0.1 - fix(assertion-matchers)
|
||||||
|
Refactor matcher implementations to consistently use customAssertion for improved consistency and clarity.
|
||||||
|
|
||||||
|
- Updated ArrayMatchers, BooleanMatchers, DateMatchers, FunctionMatchers, NumberMatchers, ObjectMatchers, StringMatchers, and TypeMatchers to use customAssertion directly.
|
||||||
|
- Aligned Assertion class aliases to delegate to the namespaced matchers with the new customAssertion pattern.
|
||||||
|
|
||||||
## 2025-04-28 - 2.0.0 - BREAKING CHANGE(docs)
|
## 2025-04-28 - 2.0.0 - BREAKING CHANGE(docs)
|
||||||
Update documentation and examples to unify async and sync assertions, add custom matcher guides, and update package configuration
|
Update documentation and examples to unify async and sync assertions, add custom matcher guides, and update package configuration
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@push.rocks/smartexpect",
|
"name": "@push.rocks/smartexpect",
|
||||||
"version": "2.0.0",
|
"version": "2.1.1",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "A testing library to manage expectations in code, offering both synchronous and asynchronous assertion methods.",
|
"description": "A testing library to manage expectations in code, offering both synchronous and asynchronous assertion methods.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
|
@ -21,17 +21,35 @@ This document captures the roadmap for evolving the `expect` / `expectAsync` API
|
|||||||
- [ ] Group matchers under `.string`, `.array`, `.number`, etc. for discoverability.
|
- [ ] Group matchers under `.string`, `.array`, `.number`, etc. for discoverability.
|
||||||
|
|
||||||
## Phase 6: Jest-Style Convenience
|
## Phase 6: Jest-Style Convenience
|
||||||
- [ ] Add `.toMatchObject()`, `.toMatchSnapshot()`, `expect.any()`, `expect.anything()`, etc.
|
- [x] Add `expect.any()` and `expect.anything()` matchers for use in `.toMatchObject()` patterns
|
||||||
|
(Snapshot matchers still TBD)
|
||||||
|
|
||||||
## Phase 7: Error Messages & Diffs
|
The next items to tackle:
|
||||||
- [ ] Integrate a diffing library for clear failure output with colorized diffs.
|
|
||||||
|
|
||||||
## Phase 8: Nested Access Chaining
|
3. Improve negation (`.not`) messaging
|
||||||
- [ ] Provide `.at(path)` or lens-based API for deep property assertions in one go.
|
- Today `.not` simply flips pass/fail, but the failure message isn’t very descriptive. We should capture positive/negative message templates so e.g.
|
||||||
|
> expect(5).not.toBeGreaterThan(3)
|
||||||
|
emits:
|
||||||
|
"Expected 5 not to be greater than 3"
|
||||||
|
|
||||||
## Phase 9: Pluggable Reporters
|
4. Richer error output for objects/arrays
|
||||||
- [ ] Allow consumers to swap output format: JSON, TAP, HTML, etc.
|
- Integrate a diff library (or extend `fast-deep-equal`) to show unified diffs between expected and actual values
|
||||||
|
|
||||||
## Phase 10: API Cleanup
|
5. More built-in matchers
|
||||||
- [ ] Audit and remove legacy aliases and redundant methods.
|
- toBeNaN(), toBeFinite()
|
||||||
- [ ] Finalize deprecations and bump to a major version.
|
- toBeWithinRange(min, max)
|
||||||
|
- toHaveKeys(...), toHaveOwnKeys(...)
|
||||||
|
- toThrowErrorMatching(/regex/), toThrowErrorWithMessage('…')
|
||||||
|
- string/array: toBeEmpty() alias
|
||||||
|
- object: toHaveOwnProperty() shorthand
|
||||||
|
|
||||||
|
6. TypeScript-friendliness
|
||||||
|
- Enhance `.d.ts` so editors autocomplete namespace methods (e.g. `expect(x).string.`)
|
||||||
|
- Statically type matcher arguments to catch wrong types at compile time
|
||||||
|
|
||||||
|
7. Async assertions and timeouts improvements
|
||||||
|
- Support `.not.resolves`, `.rejects.toThrow()`
|
||||||
|
- Provide clearer timeout errors (e.g. "Expected promise to resolve within …")
|
||||||
|
|
||||||
|
8. Plugin/extension API
|
||||||
|
- Formalize `Assertion.extend()` plugin API for shipping matcher bundles
|
32
test/test.diffOutput.ts
Normal file
32
test/test.diffOutput.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { tap, expect as tExpect } from '@push.rocks/tapbundle';
|
||||||
|
import * as smartexpect from '../dist_ts/index.js';
|
||||||
|
|
||||||
|
tap.test('diff-like output for object.toEqual mismatch', async () => {
|
||||||
|
const a = { x: 1, y: 2 };
|
||||||
|
const b = { x: 1, y: 3 };
|
||||||
|
try {
|
||||||
|
smartexpect.expect(a).object.toEqual(b);
|
||||||
|
throw new Error('Assertion did not throw');
|
||||||
|
} catch (err: any) {
|
||||||
|
const msg: string = err.message;
|
||||||
|
tExpect(msg.includes('Expected objects to be deeply equal')).toBeTrue();
|
||||||
|
tExpect(msg.includes('Received:')).toBeTrue();
|
||||||
|
tExpect(msg.includes('"y": 2')).toBeTrue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('diff-like output for array.toContainEqual mismatch', async () => {
|
||||||
|
const arr = [{ id: 1 }, { id: 2 }];
|
||||||
|
const item = { id: 3 };
|
||||||
|
try {
|
||||||
|
smartexpect.expect(arr).array.toContainEqual(item);
|
||||||
|
throw new Error('Assertion did not throw');
|
||||||
|
} catch (err: any) {
|
||||||
|
const msg: string = err.message;
|
||||||
|
tExpect(msg.includes('Expected array to contain equal to')).toBeTrue();
|
||||||
|
tExpect(msg.includes('Received:')).toBeTrue();
|
||||||
|
tExpect(msg.includes('"id": 1')).toBeTrue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
38
test/test.expectAny.ts
Normal file
38
test/test.expectAny.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { tap } from '@push.rocks/tapbundle';
|
||||||
|
import * as smartexpect from '../dist_ts/index.js';
|
||||||
|
|
||||||
|
tap.test('expect.any and expect.anything basic usage', async () => {
|
||||||
|
const obj = { a: 1, b: 'two', d: new Date() };
|
||||||
|
// Using expect.any to match types
|
||||||
|
smartexpect.expect(obj).object.toMatchObject({
|
||||||
|
a: smartexpect.expect.any(Number),
|
||||||
|
b: smartexpect.expect.any(String),
|
||||||
|
d: smartexpect.expect.any(Date),
|
||||||
|
});
|
||||||
|
// Using expect.anything to match any defined value
|
||||||
|
smartexpect.expect(obj).object.toMatchObject({
|
||||||
|
a: smartexpect.expect.anything(),
|
||||||
|
b: smartexpect.expect.anything(),
|
||||||
|
d: smartexpect.expect.anything(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('expect.any mismatch and anything null/undefined rejection', async () => {
|
||||||
|
const obj = { a: 1, b: null };
|
||||||
|
// Mismatch for expect.any
|
||||||
|
try {
|
||||||
|
smartexpect.expect(obj).object.toMatchObject({ a: smartexpect.expect.any(String) });
|
||||||
|
throw new Error('Expected mismatch for expect.any did not throw');
|
||||||
|
} catch (err) {
|
||||||
|
// success: thrown on mismatch
|
||||||
|
}
|
||||||
|
// anything should reject null or undefined
|
||||||
|
try {
|
||||||
|
smartexpect.expect(obj).object.toMatchObject({ b: smartexpect.expect.anything() });
|
||||||
|
throw new Error('Expected anything() to reject null or undefined');
|
||||||
|
} catch (err) {
|
||||||
|
// success: thrown on null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
22
test/test.negation.ts
Normal file
22
test/test.negation.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { tap, expect as tExpect } from '@push.rocks/tapbundle';
|
||||||
|
import * as smartexpect from '../dist_ts/index.js';
|
||||||
|
|
||||||
|
tap.test('negation message for numeric matcher', async () => {
|
||||||
|
try {
|
||||||
|
smartexpect.expect(5).not.toBeGreaterThan(3);
|
||||||
|
throw new Error('Assertion did not throw');
|
||||||
|
} catch (err: any) {
|
||||||
|
tExpect(err.message).toEqual('Expected number not to be greater than 3');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('negation message for string matcher', async () => {
|
||||||
|
try {
|
||||||
|
smartexpect.expect('hello').not.string.toInclude('he');
|
||||||
|
throw new Error('Assertion did not throw');
|
||||||
|
} catch (err: any) {
|
||||||
|
tExpect(err.message).toEqual('Expected string not to include "he"');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/smartexpect',
|
name: '@push.rocks/smartexpect',
|
||||||
version: '2.0.0',
|
version: '2.1.1',
|
||||||
description: 'A testing library to manage expectations in code, offering both synchronous and asynchronous assertion methods.'
|
description: 'A testing library to manage expectations in code, offering both synchronous and asynchronous assertion methods.'
|
||||||
}
|
}
|
||||||
|
14
ts/index.ts
14
ts/index.ts
@ -1,4 +1,4 @@
|
|||||||
import { Assertion } from './smartexpect.classes.assertion.js';
|
import { Assertion, AnyMatcher, AnythingMatcher } from './smartexpect.classes.assertion.js';
|
||||||
// import type { TMatcher } from './smartexpect.classes.assertion.js'; // unused
|
// import type { TMatcher } from './smartexpect.classes.assertion.js'; // unused
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,6 +24,18 @@ export function expect<T>(value: any): Assertion<T> {
|
|||||||
*/
|
*/
|
||||||
export namespace expect {
|
export namespace expect {
|
||||||
export const extend = Assertion.extend;
|
export const extend = Assertion.extend;
|
||||||
|
/**
|
||||||
|
* Matcher for a specific constructor. Passes if value is instance of given constructor.
|
||||||
|
*/
|
||||||
|
export function any(constructor: any) {
|
||||||
|
return new AnyMatcher(constructor);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Matcher for any defined value (not null or undefined).
|
||||||
|
*/
|
||||||
|
export function anything() {
|
||||||
|
return new AnythingMatcher();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Assertion } from '../smartexpect.classes.assertion.js';
|
import { Assertion } from '../smartexpect.classes.assertion.js';
|
||||||
|
import * as plugins from '../plugins.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Namespace for array-specific matchers
|
* Namespace for array-specific matchers
|
||||||
@ -7,38 +8,73 @@ export class ArrayMatchers<T> {
|
|||||||
constructor(private assertion: Assertion<T[]>) {}
|
constructor(private assertion: Assertion<T[]>) {}
|
||||||
|
|
||||||
toBeArray() {
|
toBeArray() {
|
||||||
return this.assertion.toBeArray();
|
return this.assertion.customAssertion(
|
||||||
|
(value) => Array.isArray(value),
|
||||||
|
`Expected value to be array`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toHaveLength(length: number) {
|
toHaveLength(length: number) {
|
||||||
return this.assertion.toHaveLength(length);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => (value as T[]).length === length,
|
||||||
|
`Expected array to have length ${length}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toContain(item: T) {
|
toContain(item: T) {
|
||||||
return this.assertion.toContain(item);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => (value as T[]).includes(item),
|
||||||
|
`Expected array to contain ${JSON.stringify(item)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toContainEqual(item: T) {
|
toContainEqual(item: T) {
|
||||||
return this.assertion.toContainEqual(item);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => (value as T[]).some((e) => plugins.fastDeepEqual(e, item)),
|
||||||
|
(value) =>
|
||||||
|
`Expected array to contain equal to ${JSON.stringify(item)}` +
|
||||||
|
`\nReceived: ${JSON.stringify(value, null, 2)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toContainAll(items: T[]) {
|
toContainAll(items: T[]) {
|
||||||
return this.assertion.toContainAll(items);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => items.every((i) => (value as T[]).includes(i)),
|
||||||
|
`Expected array to contain all ${JSON.stringify(items)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toExclude(item: T) {
|
toExclude(item: T) {
|
||||||
return this.assertion.toExclude(item);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => !(value as T[]).includes(item),
|
||||||
|
`Expected array to exclude ${JSON.stringify(item)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeEmptyArray() {
|
toBeEmptyArray() {
|
||||||
return this.assertion.toBeEmptyArray();
|
return this.assertion.customAssertion(
|
||||||
|
(value) => Array.isArray(value) && (value as T[]).length === 0,
|
||||||
|
`Expected array to be empty`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Alias for empty array check
|
||||||
|
*/
|
||||||
|
toBeEmpty() {
|
||||||
|
return this.toBeEmptyArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
toHaveLengthGreaterThan(length: number) {
|
toHaveLengthGreaterThan(length: number) {
|
||||||
return this.assertion.toHaveLengthGreaterThan(length);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => (value as T[]).length > length,
|
||||||
|
`Expected array to have length greater than ${length}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toHaveLengthLessThan(length: number) {
|
toHaveLengthLessThan(length: number) {
|
||||||
return this.assertion.toHaveLengthLessThan(length);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => (value as T[]).length < length,
|
||||||
|
`Expected array to have length less than ${length}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,18 +7,30 @@ export class BooleanMatchers {
|
|||||||
constructor(private assertion: Assertion<boolean>) {}
|
constructor(private assertion: Assertion<boolean>) {}
|
||||||
|
|
||||||
toBeTrue() {
|
toBeTrue() {
|
||||||
return this.assertion.toBeTrue();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => v === true,
|
||||||
|
`Expected value to be true`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeFalse() {
|
toBeFalse() {
|
||||||
return this.assertion.toBeFalse();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => v === false,
|
||||||
|
`Expected value to be false`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeTruthy() {
|
toBeTruthy() {
|
||||||
return this.assertion.toBeTruthy();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => Boolean(v),
|
||||||
|
`Expected value to be truthy`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeFalsy() {
|
toBeFalsy() {
|
||||||
return this.assertion.toBeFalsy();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => !v,
|
||||||
|
`Expected value to be falsy`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,14 +7,23 @@ export class DateMatchers {
|
|||||||
constructor(private assertion: Assertion<Date>) {}
|
constructor(private assertion: Assertion<Date>) {}
|
||||||
|
|
||||||
toBeDate() {
|
toBeDate() {
|
||||||
return this.assertion.toBeDate();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => v instanceof Date,
|
||||||
|
`Expected value to be a Date instance`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeBeforeDate(date: Date) {
|
toBeBeforeDate(date: Date) {
|
||||||
return this.assertion.toBeBeforeDate(date);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => v instanceof Date && (v as Date).getTime() < date.getTime(),
|
||||||
|
`Expected date to be before ${date.toISOString()}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeAfterDate(date: Date) {
|
toBeAfterDate(date: Date) {
|
||||||
return this.assertion.toBeAfterDate(date);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => v instanceof Date && (v as Date).getTime() > date.getTime(),
|
||||||
|
`Expected date to be after ${date.toISOString()}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,6 +7,55 @@ export class FunctionMatchers {
|
|||||||
constructor(private assertion: Assertion<Function>) {}
|
constructor(private assertion: Assertion<Function>) {}
|
||||||
|
|
||||||
toThrow(expectedError?: any) {
|
toThrow(expectedError?: any) {
|
||||||
return this.assertion.toThrow(expectedError);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => {
|
||||||
|
let threw = false;
|
||||||
|
try {
|
||||||
|
(value as Function)();
|
||||||
|
} catch (e: any) {
|
||||||
|
threw = true;
|
||||||
|
if (expectedError) {
|
||||||
|
if (typeof expectedError === 'function') {
|
||||||
|
return e instanceof expectedError;
|
||||||
|
}
|
||||||
|
return e === expectedError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return threw;
|
||||||
|
},
|
||||||
|
`Expected function to throw${expectedError ? ` ${expectedError}` : ''}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Assert thrown error message matches the given regex
|
||||||
|
*/
|
||||||
|
toThrowErrorMatching(regex: RegExp) {
|
||||||
|
return this.assertion.customAssertion(
|
||||||
|
(value) => {
|
||||||
|
try {
|
||||||
|
(value as Function)();
|
||||||
|
} catch (e: any) {
|
||||||
|
return regex.test(e && e.message);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
`Expected function to throw error matching ${regex}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Assert thrown error message equals the given string
|
||||||
|
*/
|
||||||
|
toThrowErrorWithMessage(expectedMessage: string) {
|
||||||
|
return this.assertion.customAssertion(
|
||||||
|
(value) => {
|
||||||
|
try {
|
||||||
|
(value as Function)();
|
||||||
|
} catch (e: any) {
|
||||||
|
return e && e.message === expectedMessage;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
`Expected function to throw error with message "${expectedMessage}"`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,26 +7,77 @@ export class NumberMatchers {
|
|||||||
constructor(private assertion: Assertion<number>) {}
|
constructor(private assertion: Assertion<number>) {}
|
||||||
|
|
||||||
toBeGreaterThan(value: number) {
|
toBeGreaterThan(value: number) {
|
||||||
return this.assertion.toBeGreaterThan(value);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => (v as number) > value,
|
||||||
|
`Expected number to be greater than ${value}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeLessThan(value: number) {
|
toBeLessThan(value: number) {
|
||||||
return this.assertion.toBeLessThan(value);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => (v as number) < value,
|
||||||
|
`Expected number to be less than ${value}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeGreaterThanOrEqual(value: number) {
|
toBeGreaterThanOrEqual(value: number) {
|
||||||
return this.assertion.toBeGreaterThanOrEqual(value);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => (v as number) >= value,
|
||||||
|
`Expected number to be greater than or equal to ${value}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeLessThanOrEqual(value: number) {
|
toBeLessThanOrEqual(value: number) {
|
||||||
return this.assertion.toBeLessThanOrEqual(value);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => (v as number) <= value,
|
||||||
|
`Expected number to be less than or equal to ${value}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeCloseTo(value: number, precision?: number) {
|
toBeCloseTo(value: number, precision?: number) {
|
||||||
return this.assertion.toBeCloseTo(value, precision);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => {
|
||||||
|
const num = v as number;
|
||||||
|
const p = precision !== undefined ? precision : 2;
|
||||||
|
const diff = Math.abs(num - value);
|
||||||
|
const tolerance = 0.5 * Math.pow(10, -p);
|
||||||
|
return diff <= tolerance;
|
||||||
|
},
|
||||||
|
`Expected number to be close to ${value} within precision ${precision ?? 2}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
/** Equality check for numbers */
|
/** Equality check for numbers */
|
||||||
toEqual(value: number) {
|
toEqual(value: number) {
|
||||||
return this.assertion.toEqual(value);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => (v as number) === value,
|
||||||
|
`Expected number to equal ${value}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Checks for NaN
|
||||||
|
*/
|
||||||
|
toBeNaN() {
|
||||||
|
return this.assertion.customAssertion(
|
||||||
|
(v) => Number.isNaN(v as number),
|
||||||
|
`Expected number to be NaN`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Checks for finite number
|
||||||
|
*/
|
||||||
|
toBeFinite() {
|
||||||
|
return this.assertion.customAssertion(
|
||||||
|
(v) => Number.isFinite(v as number),
|
||||||
|
`Expected number to be finite`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Checks if number is within inclusive range
|
||||||
|
*/
|
||||||
|
toBeWithinRange(min: number, max: number) {
|
||||||
|
return this.assertion.customAssertion(
|
||||||
|
(v) => (v as number) >= min && (v as number) <= max,
|
||||||
|
`Expected number to be within range ${min} - ${max}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import { Assertion } from '../smartexpect.classes.assertion.js';
|
import { Assertion, AnyMatcher, AnythingMatcher } from '../smartexpect.classes.assertion.js';
|
||||||
|
import * as plugins from '../plugins.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Namespace for object-specific matchers
|
* Namespace for object-specific matchers
|
||||||
@ -7,33 +8,125 @@ export class ObjectMatchers<T extends object> {
|
|||||||
constructor(private assertion: Assertion<T>) {}
|
constructor(private assertion: Assertion<T>) {}
|
||||||
|
|
||||||
toEqual(expected: any) {
|
toEqual(expected: any) {
|
||||||
return this.assertion.toEqual(expected);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => plugins.fastDeepEqual(v, expected),
|
||||||
|
(v) =>
|
||||||
|
`Expected objects to be deeply equal to ${JSON.stringify(expected, null, 2)}` +
|
||||||
|
`\nReceived: ${JSON.stringify(v, null, 2)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toMatchObject(expected: object) {
|
toMatchObject(expected: object) {
|
||||||
return this.assertion.toMatchObject(expected);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => {
|
||||||
|
const obj = v as any;
|
||||||
|
for (const key of Object.keys(expected)) {
|
||||||
|
const expectedVal = (expected as any)[key];
|
||||||
|
const actualVal = obj[key];
|
||||||
|
if (expectedVal instanceof AnyMatcher) {
|
||||||
|
const ctor = expectedVal.expectedConstructor;
|
||||||
|
if (ctor === Number) {
|
||||||
|
if (typeof actualVal !== 'number') return false;
|
||||||
|
} else if (ctor === String) {
|
||||||
|
if (typeof actualVal !== 'string') return false;
|
||||||
|
} else if (ctor === Boolean) {
|
||||||
|
if (typeof actualVal !== 'boolean') return false;
|
||||||
|
} else {
|
||||||
|
if (!(actualVal instanceof ctor)) return false;
|
||||||
|
}
|
||||||
|
} else if (expectedVal instanceof AnythingMatcher) {
|
||||||
|
if (actualVal === null || actualVal === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!plugins.fastDeepEqual(actualVal, expectedVal)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
(v) =>
|
||||||
|
`Expected object to match properties ${JSON.stringify(expected, null, 2)}` +
|
||||||
|
`\nReceived: ${JSON.stringify(v, null, 2)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeInstanceOf(constructor: any) {
|
toBeInstanceOf(constructor: any) {
|
||||||
return this.assertion.toBeInstanceOf(constructor);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => (v as any) instanceof constructor,
|
||||||
|
`Expected object to be instance of ${constructor.name || constructor}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toHaveProperty(property: string, value?: any) {
|
toHaveProperty(property: string, value?: any) {
|
||||||
return this.assertion.toHaveProperty(property, value);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => {
|
||||||
|
const obj = v as any;
|
||||||
|
if (!(property in obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (arguments.length === 2) {
|
||||||
|
return plugins.fastDeepEqual(obj[property], value);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
`Expected object to have property ${property}${value !== undefined ? ` with value ${JSON.stringify(value)}` : ''}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toHaveDeepProperty(path: string[]) {
|
toHaveDeepProperty(path: string[]) {
|
||||||
return this.assertion.toHaveDeepProperty(path);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => {
|
||||||
|
let obj: any = v;
|
||||||
|
for (const key of path) {
|
||||||
|
if (obj == null || !(key in obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
obj = obj[key];
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
`Expected object to have deep property path ${JSON.stringify(path)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
toBeNull() {
|
toBeNull() {
|
||||||
return this.assertion.toBeNull();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => v === null,
|
||||||
|
`Expected value to be null`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeUndefined() {
|
toBeUndefined() {
|
||||||
return this.assertion.toBeUndefined();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => v === undefined,
|
||||||
|
`Expected value to be undefined`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeNullOrUndefined() {
|
toBeNullOrUndefined() {
|
||||||
return this.assertion.toBeNullOrUndefined();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => v === null || v === undefined,
|
||||||
|
`Expected value to be null or undefined`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Checks own property only (not inherited)
|
||||||
|
*/
|
||||||
|
toHaveOwnProperty(property: string, value?: any) {
|
||||||
|
return this.assertion.customAssertion(
|
||||||
|
(v) => {
|
||||||
|
const obj = v as any;
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(obj, property)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (arguments.length === 2) {
|
||||||
|
return plugins.fastDeepEqual(obj[property], value);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
(v) =>
|
||||||
|
`Expected object to have own property ${property}` +
|
||||||
|
(value !== undefined ? ` with value ${JSON.stringify(value)}` : ``) +
|
||||||
|
`\nReceived: ${JSON.stringify(v, null, 2)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,26 +7,53 @@ export class StringMatchers {
|
|||||||
constructor(private assertion: Assertion<string>) {}
|
constructor(private assertion: Assertion<string>) {}
|
||||||
|
|
||||||
toStartWith(prefix: string) {
|
toStartWith(prefix: string) {
|
||||||
return this.assertion.toStartWith(prefix);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => (value as string).startsWith(prefix),
|
||||||
|
`Expected string to start with "${prefix}"`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toEndWith(suffix: string) {
|
toEndWith(suffix: string) {
|
||||||
return this.assertion.toEndWith(suffix);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => (value as string).endsWith(suffix),
|
||||||
|
`Expected string to end with "${suffix}"`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toInclude(substring: string) {
|
toInclude(substring: string) {
|
||||||
return this.assertion.toInclude(substring);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => (value as string).includes(substring),
|
||||||
|
`Expected string to include "${substring}"`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toMatch(regex: RegExp) {
|
toMatch(regex: RegExp) {
|
||||||
return this.assertion.toMatch(regex);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => regex.test(value as string),
|
||||||
|
`Expected string to match ${regex}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeOneOf(values: string[]) {
|
toBeOneOf(values: string[]) {
|
||||||
return this.assertion.toBeOneOf(values);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => (values as string[]).includes(value as string),
|
||||||
|
`Expected string to be one of ${JSON.stringify(values)}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
/** Length check for strings */
|
/** Length check for strings */
|
||||||
toHaveLength(length: number) {
|
toHaveLength(length: number) {
|
||||||
return this.assertion.toHaveLength(length);
|
return this.assertion.customAssertion(
|
||||||
|
(value) => (value as string).length === length,
|
||||||
|
`Expected string to have length ${length}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Alias for empty string check
|
||||||
|
*/
|
||||||
|
toBeEmpty() {
|
||||||
|
return this.assertion.customAssertion(
|
||||||
|
(value) => (value as string).length === 0,
|
||||||
|
`Expected string to be empty`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,22 +7,37 @@ export class TypeMatchers {
|
|||||||
constructor(private assertion: Assertion<any>) {}
|
constructor(private assertion: Assertion<any>) {}
|
||||||
|
|
||||||
toBeTypeofString() {
|
toBeTypeofString() {
|
||||||
return this.assertion.toBeTypeofString();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => typeof v === 'string',
|
||||||
|
`Expected type to be 'string'`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeTypeofNumber() {
|
toBeTypeofNumber() {
|
||||||
return this.assertion.toBeTypeofNumber();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => typeof v === 'number',
|
||||||
|
`Expected type to be 'number'`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeTypeofBoolean() {
|
toBeTypeofBoolean() {
|
||||||
return this.assertion.toBeTypeofBoolean();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => typeof v === 'boolean',
|
||||||
|
`Expected type to be 'boolean'`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeTypeOf(typeName: string) {
|
toBeTypeOf(typeName: string) {
|
||||||
return this.assertion.toBeTypeOf(typeName);
|
return this.assertion.customAssertion(
|
||||||
|
(v) => typeof v === typeName,
|
||||||
|
`Expected type to be '${typeName}'`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toBeDefined() {
|
toBeDefined() {
|
||||||
return this.assertion.toBeDefined();
|
return this.assertion.customAssertion(
|
||||||
|
(v) => v !== undefined,
|
||||||
|
`Expected value to be defined`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,6 +18,14 @@ import type { TMatcher, TExecutionType } from './types.js';
|
|||||||
/**
|
/**
|
||||||
* Core assertion class. Generic over the current value type T.
|
* Core assertion class. Generic over the current value type T.
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Internal matcher classes for expect.any and expect.anything
|
||||||
|
*/
|
||||||
|
export class AnyMatcher {
|
||||||
|
constructor(public expectedConstructor: any) {}
|
||||||
|
}
|
||||||
|
export class AnythingMatcher {}
|
||||||
|
|
||||||
export class Assertion<T = unknown> {
|
export class Assertion<T = unknown> {
|
||||||
executionMode: TExecutionType;
|
executionMode: TExecutionType;
|
||||||
baseReference: any;
|
baseReference: any;
|
||||||
@ -33,6 +41,8 @@ export class Assertion<T = unknown> {
|
|||||||
private isResolves = false;
|
private isResolves = false;
|
||||||
private failMessage: string;
|
private failMessage: string;
|
||||||
private successMessage: string;
|
private successMessage: string;
|
||||||
|
/** Computed negation failure message for the current assertion */
|
||||||
|
private negativeMessage: string;
|
||||||
|
|
||||||
constructor(baseReferenceArg: any, executionModeArg: TExecutionType) {
|
constructor(baseReferenceArg: any, executionModeArg: TExecutionType) {
|
||||||
this.baseReference = baseReferenceArg;
|
this.baseReference = baseReferenceArg;
|
||||||
@ -131,6 +141,16 @@ export class Assertion<T = unknown> {
|
|||||||
.replace('{value}', formattedValue)
|
.replace('{value}', formattedValue)
|
||||||
.replace('{path}', drillDown || '');
|
.replace('{path}', drillDown || '');
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Compute a negated failure message by inserting 'not' into the positive message.
|
||||||
|
*/
|
||||||
|
private computeNegationMessage(message: string): string {
|
||||||
|
const idx = message.indexOf(' to ');
|
||||||
|
if (idx !== -1) {
|
||||||
|
return message.slice(0, idx) + ' not' + message.slice(idx);
|
||||||
|
}
|
||||||
|
return 'Negated: ' + message;
|
||||||
|
}
|
||||||
|
|
||||||
public get not() {
|
public get not() {
|
||||||
this.notSetting = true;
|
this.notSetting = true;
|
||||||
@ -183,25 +203,27 @@ export class Assertion<T = unknown> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private runCheck(checkFunction: () => any) {
|
private runCheck(checkFunction: () => any): Assertion<T> | Promise<Assertion<T>> {
|
||||||
const runDirectOrNegated = (checkFunction: () => any) => {
|
const runDirectOrNegated = (checkFunction: () => any) => {
|
||||||
if (!this.notSetting) {
|
if (!this.notSetting) {
|
||||||
return checkFunction();
|
return checkFunction();
|
||||||
} else {
|
} else {
|
||||||
let isOk = false;
|
let isOk = false;
|
||||||
try {
|
try {
|
||||||
runDirectOrNegated(checkFunction());
|
// attempt positive assertion and expect it to throw
|
||||||
|
checkFunction();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
isOk = true;
|
isOk = true;
|
||||||
}
|
}
|
||||||
if (!isOk) {
|
if (!isOk) {
|
||||||
throw new Error(this.failMessage || 'Negated assertion failed');
|
const msg = this.failMessage || this.negativeMessage || 'Negated assertion failed';
|
||||||
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.executionMode === 'async') {
|
if (this.executionMode === 'async') {
|
||||||
const done = plugins.smartpromise.defer();
|
const done = plugins.smartpromise.defer<Assertion<T>>();
|
||||||
const isThenable = this.baseReference && typeof (this.baseReference as any).then === 'function';
|
const isThenable = this.baseReference && typeof (this.baseReference as any).then === 'function';
|
||||||
if (!isThenable) {
|
if (!isThenable) {
|
||||||
done.reject(new Error(`Expected a Promise but received: ${this.formatValue(this.baseReference)}`));
|
done.reject(new Error(`Expected a Promise but received: ${this.formatValue(this.baseReference)}`));
|
||||||
@ -219,45 +241,54 @@ export class Assertion<T = unknown> {
|
|||||||
(res: any) => {
|
(res: any) => {
|
||||||
done.reject(new Error(`Expected Promise to reject but it resolved with ${this.formatValue(res)}`));
|
done.reject(new Error(`Expected Promise to reject but it resolved with ${this.formatValue(res)}`));
|
||||||
},
|
},
|
||||||
(err: any) => {
|
(err: any) => {
|
||||||
this.baseReference = err;
|
this.baseReference = err;
|
||||||
try {
|
try {
|
||||||
const ret = runDirectOrNegated(checkFunction);
|
runDirectOrNegated(checkFunction);
|
||||||
done.resolve(ret);
|
done.resolve(this);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
done.reject(e);
|
done.reject(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
(this.baseReference as Promise<any>).then(
|
(this.baseReference as Promise<any>).then(
|
||||||
(res: any) => {
|
(res: any) => {
|
||||||
this.baseReference = res;
|
this.baseReference = res;
|
||||||
try {
|
try {
|
||||||
const ret = runDirectOrNegated(checkFunction);
|
runDirectOrNegated(checkFunction);
|
||||||
done.resolve(ret);
|
done.resolve(this);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
done.reject(e);
|
done.reject(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(err: any) => {
|
(err: any) => {
|
||||||
done.reject(err);
|
done.reject(err);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return done.promise;
|
// return a promise resolving to this for chaining
|
||||||
|
return done.promise.then(() => this);
|
||||||
}
|
}
|
||||||
return runDirectOrNegated(checkFunction);
|
// sync: run and return this for chaining
|
||||||
|
runDirectOrNegated(checkFunction);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public customAssertion(
|
public customAssertion(
|
||||||
assertionFunction: (value: any) => boolean,
|
assertionFunction: (value: any) => boolean,
|
||||||
errorMessage: string
|
errorMessage: string | ((value: any) => string)
|
||||||
) {
|
): Assertion<T> | Promise<Assertion<T>> {
|
||||||
|
// Prepare negation message based on the positive error template, if static
|
||||||
|
if (typeof errorMessage === 'string') {
|
||||||
|
this.negativeMessage = this.computeNegationMessage(errorMessage);
|
||||||
|
}
|
||||||
return this.runCheck(() => {
|
return this.runCheck(() => {
|
||||||
const value = this.getObjectToTestReference();
|
const value = this.getObjectToTestReference();
|
||||||
if (!assertionFunction(value)) {
|
if (!assertionFunction(value)) {
|
||||||
throw new Error(this.failMessage || errorMessage);
|
const msg = this.failMessage
|
||||||
|
|| (typeof errorMessage === 'function' ? errorMessage(value) : errorMessage);
|
||||||
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -288,6 +319,50 @@ export class Assertion<T = unknown> {
|
|||||||
console.log(`Path: ${this.formatDrillDown() || '(root)'}`);
|
console.log(`Path: ${this.formatDrillDown() || '(root)'}`);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
// Direct (flat) matcher aliases
|
||||||
|
public toEqual(expected: any) {
|
||||||
|
return this.customAssertion(
|
||||||
|
(v) => plugins.fastDeepEqual(v, expected),
|
||||||
|
`Expected value to equal ${JSON.stringify(expected)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
public toBeTrue() { return this.boolean.toBeTrue(); }
|
||||||
|
public toBeFalse() { return this.boolean.toBeFalse(); }
|
||||||
|
public toBeTruthy() { return this.boolean.toBeTruthy(); }
|
||||||
|
public toBeFalsy() { return this.boolean.toBeFalsy(); }
|
||||||
|
public toThrow(expectedError?: any) { return this.function.toThrow(expectedError); }
|
||||||
|
public toBeGreaterThan(value: number) { return this.number.toBeGreaterThan(value); }
|
||||||
|
public toBeLessThan(value: number) { return this.number.toBeLessThan(value); }
|
||||||
|
public toBeGreaterThanOrEqual(value: number) { return this.number.toBeGreaterThanOrEqual(value); }
|
||||||
|
public toBeLessThanOrEqual(value: number) { return this.number.toBeLessThanOrEqual(value); }
|
||||||
|
public toBeCloseTo(value: number, precision?: number) { return this.number.toBeCloseTo(value, precision); }
|
||||||
|
public toBeArray() { return this.array.toBeArray(); }
|
||||||
|
public toContain(item: any) { return this.array.toContain(item); }
|
||||||
|
public toContainEqual(item: any) { return this.array.toContainEqual(item); }
|
||||||
|
public toContainAll(items: any[]) { return this.array.toContainAll(items); }
|
||||||
|
public toExclude(item: any) { return this.array.toExclude(item); }
|
||||||
|
public toBeEmptyArray() { return this.array.toBeEmptyArray(); }
|
||||||
|
public toStartWith(prefix: string) { return this.string.toStartWith(prefix); }
|
||||||
|
public toEndWith(suffix: string) { return this.string.toEndWith(suffix); }
|
||||||
|
public toInclude(substring: string) { return this.string.toInclude(substring); }
|
||||||
|
public toMatch(regex: RegExp) { return this.string.toMatch(regex); }
|
||||||
|
public toBeOneOf(values: any[]) { return this.string.toBeOneOf(values as string[]); }
|
||||||
|
public toHaveProperty(property: string, value?: any) { return this.object.toHaveProperty(property, value); }
|
||||||
|
public toMatchObject(expected: object) { return this.object.toMatchObject(expected); }
|
||||||
|
public toBeInstanceOf(constructor: any) { return this.object.toBeInstanceOf(constructor); }
|
||||||
|
public toHaveDeepProperty(path: string[]) { return this.object.toHaveDeepProperty(path); }
|
||||||
|
public toBeNull() { return this.object.toBeNull(); }
|
||||||
|
public toBeUndefined() { return this.object.toBeUndefined(); }
|
||||||
|
public toBeNullOrUndefined() { return this.object.toBeNullOrUndefined(); }
|
||||||
|
public toBeDate() { return this.date.toBeDate(); }
|
||||||
|
public toBeBeforeDate(date: Date) { return this.date.toBeBeforeDate(date); }
|
||||||
|
public toBeAfterDate(date: Date) { return this.date.toBeAfterDate(date); }
|
||||||
|
public toBeTypeofString() { return this.type.toBeTypeofString(); }
|
||||||
|
public toBeTypeofNumber() { return this.type.toBeTypeofNumber(); }
|
||||||
|
public toBeTypeofBoolean() { return this.type.toBeTypeofBoolean(); }
|
||||||
|
public toBeTypeOf(typeName: string) { return this.type.toBeTypeOf(typeName); }
|
||||||
|
public toBeDefined() { return this.type.toBeDefined(); }
|
||||||
|
|
||||||
// Namespaced matcher accessors
|
// Namespaced matcher accessors
|
||||||
/** String-specific matchers */
|
/** String-specific matchers */
|
||||||
public get string() {
|
public get string() {
|
||||||
|
Reference in New Issue
Block a user