diff --git a/src/collections/index.ts b/src/collections/index.ts index 8c9db2f..71c9677 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -17,7 +17,7 @@ export { ObservableCollection, ReadOnlyObservableCollection -} from './observableCollections' +} from './observableCollections'; export { type INotifySetChanged, diff --git a/src/collections/observableCollections/ICollectionChangedEvent.ts b/src/collections/observableCollections/ICollectionChangedEvent.ts index 848052b..a0b5581 100644 --- a/src/collections/observableCollections/ICollectionChangedEvent.ts +++ b/src/collections/observableCollections/ICollectionChangedEvent.ts @@ -1,5 +1,5 @@ -import type { IEvent } from '../../events'; import type { ICollectionChange } from './ICollectionChange'; +import type { IEvent } from '../../events'; /** * A specialized event for subscribing and unsubscribing from collection changed events. diff --git a/src/collections/observableCollections/ICollectionChangedEventHandler.ts b/src/collections/observableCollections/ICollectionChangedEventHandler.ts index e6103f7..7bb1a2f 100644 --- a/src/collections/observableCollections/ICollectionChangedEventHandler.ts +++ b/src/collections/observableCollections/ICollectionChangedEventHandler.ts @@ -1,5 +1,5 @@ -import type { IEventHandler } from '../../events'; import type { ICollectionChange } from './ICollectionChange'; +import type { IEventHandler } from '../../events'; /** * A specialized interface for handling collection changed events. diff --git a/src/collections/observableCollections/ICollectionReorderedEvent.ts b/src/collections/observableCollections/ICollectionReorderedEvent.ts index f7493ec..e329a2c 100644 --- a/src/collections/observableCollections/ICollectionReorderedEvent.ts +++ b/src/collections/observableCollections/ICollectionReorderedEvent.ts @@ -1,5 +1,5 @@ -import type { IEvent } from '../../events'; import type { ICollectionReorder } from './ICollectionReorder'; +import type { IEvent } from '../../events'; /** * A specialized event for subscribing and unsubscribing from collection reordering events. diff --git a/src/collections/observableCollections/ICollectionReorderedEventHandler.ts b/src/collections/observableCollections/ICollectionReorderedEventHandler.ts index 7434bd0..06bd9b5 100644 --- a/src/collections/observableCollections/ICollectionReorderedEventHandler.ts +++ b/src/collections/observableCollections/ICollectionReorderedEventHandler.ts @@ -1,5 +1,5 @@ -import type { IEventHandler } from '../../events'; import type { ICollectionReorder } from './ICollectionReorder'; +import type { IEventHandler } from '../../events'; /** * A specialized interface for handling collection reorder events. diff --git a/src/collections/observableCollections/INotifyCollectionReordered.ts b/src/collections/observableCollections/INotifyCollectionReordered.ts index 7cc1ea1..ebe6333 100644 --- a/src/collections/observableCollections/INotifyCollectionReordered.ts +++ b/src/collections/observableCollections/INotifyCollectionReordered.ts @@ -1,5 +1,5 @@ -import type { INotifyCollectionChanged } from './INotifyCollectionChanged'; import type { ICollectionReorderedEvent } from './ICollectionReorderedEvent'; +import type { INotifyCollectionChanged } from './INotifyCollectionChanged'; /** * Notifies when a collection has its items reordered. Adding and removing items is handled through the {@linkcode INotifyCollectionChanged} interface. @@ -7,7 +7,7 @@ import type { ICollectionReorderedEvent } from './ICollectionReorderedEvent'; * * Any collection change can be reduced to [Array.splice](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice), * event handlers can splice entire mapped collections to get all items in the same order. - * + * * Additionally, event handlers receive all the necessary information about how each item has moved inside the collection making it easy to * add animations when it happens. * @template TItem The type of items the collection contains. diff --git a/src/collections/observableCollections/IObservableCollection.ts b/src/collections/observableCollections/IObservableCollection.ts index 490f13a..90881bb 100644 --- a/src/collections/observableCollections/IObservableCollection.ts +++ b/src/collections/observableCollections/IObservableCollection.ts @@ -24,7 +24,7 @@ export interface IObservableCollection extends IReadOnlyObservableCollect * Removes the last element from the collection and returns it. If the collection is empty, `undefined` is returned. * @returns The last element in the collection that was removed. * @see [Array.pop](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/pop) - */ + */ pop(): TItem | undefined; /** @@ -39,7 +39,7 @@ export interface IObservableCollection extends IReadOnlyObservableCollect * Removes the first element from the collection and returns it. If the collection is empty, `undefined` is returned. * @returns The first element in the collection that was removed. * @see [Array.shift](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) - */ + */ shift(): TItem | undefined; /** @@ -74,7 +74,7 @@ export interface IObservableCollection extends IReadOnlyObservableCollect * @returns The observable collection on which the operation is performed. * @see [Array.sort](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) */ - sort(compareCallback?: (left: Exclude, right: Exclude) => number): this; + sort(compareCallback?: (left: Exclude, right: Exclude)=> number): this; /** * Reverses the items in the collections and returns the observable collection. @@ -101,4 +101,4 @@ export interface IObservableCollection extends IReadOnlyObservableCollect * @see [Array.fill](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/fill) */ fill(item: TItem, start?: number, end?: number): this; -} +} \ No newline at end of file diff --git a/src/collections/observableCollections/IReadOnlyObservableCollection.ts b/src/collections/observableCollections/IReadOnlyObservableCollection.ts index 17ee7d6..6eba40d 100644 --- a/src/collections/observableCollections/IReadOnlyObservableCollection.ts +++ b/src/collections/observableCollections/IReadOnlyObservableCollection.ts @@ -1,7 +1,7 @@ -import type { INotifyPropertiesChanged } from '../../viewModels'; import type { INotifyCollectionChanged } from './INotifyCollectionChanged'; import type { INotifyCollectionReordered } from './INotifyCollectionReordered'; import type { ObservableCollection } from './ObservableCollection'; +import type { INotifyPropertiesChanged } from '../../viewModels'; /** * Represents a read-only observable collection based on the [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) interface. @@ -75,7 +75,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @param thisArg A value to use as context when processing items. * @see [Array.forEach](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) */ - forEach(callback: (this: TContext, item: TItem, index: number, collection: this) => void, thisArg?: TContext): void; + forEach(callback: (this: TContext, item: TItem, index: number, collection: this)=> void, thisArg?: TContext): void; /** * Checks whether the provided item is in the collection. @@ -112,7 +112,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns the index of the first item for which the provided `predicate` evaluates to `true`; otherwise `-1`. * @see [Array.findIndex](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex) */ - findIndex(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): number; + findIndex(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): number; /** * Returns the index of the last item that satisfies the given condition. @@ -122,7 +122,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns the index of the last item for which the provided `predicate` evaluates to `true`; otherwise `-1`. * @see [Array.findLastIndex](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex) */ - findLastIndex(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): number; + findLastIndex(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): number; /** * Returns the first item that satisfies the given condition. @@ -132,7 +132,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns the first item for which the provided `predicate` evaluates to `true`; otherwise `undefined`. * @see [Array.find](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/find) */ - find(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): TItem | undefined; + find(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): TItem | undefined; /** * Returns the first item that satisfies the given condition. * @template TResult The type of item to return. @@ -142,7 +142,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns the first item for which the provided `predicate` evaluates to `true`; otherwise `undefined`. * @see [Array.find](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/find) */ - find(predicate: (this: TContext, item: TItem, index: number, collection: this) => item is TResult, thisArg?: TContext): TResult | undefined; + find(predicate: (this: TContext, item: TItem, index: number, collection: this)=> item is TResult, thisArg?: TContext): TResult | undefined; /** * Returns the last item that satisfies the given condition. @@ -152,7 +152,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns the last item for which the provided `predicate` evaluates to `true`; otherwise `undefined`. * @see [Array.findLast](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast) */ - findLast(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): TItem | undefined; + findLast(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): TItem | undefined; /** * Returns the last item that satisfies the given condition. * @template TResult The type of item to return. @@ -162,7 +162,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns the last item for which the provided `predicate` evaluates to `true`; otherwise `undefined`. * @see [Array.findLast](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast) */ - findLast(predicate: (this: TContext, item: TItem, index: number, collection: this) => item is TResult, thisArg?: TContext): TResult | undefined; + findLast(predicate: (this: TContext, item: TItem, index: number, collection: this)=> item is TResult, thisArg?: TContext): TResult | undefined; /** * Merges the current collection with the given [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) and returns a new JavaScript [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array). @@ -181,7 +181,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns A new [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing the mapped items. * @see [Array.map](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map) */ - map(callback: (this: TContext, item: TItem, index: number, collection: this) => TResult, thisArg?: TContext): TResult[]; + map(callback: (this: TContext, item: TItem, index: number, collection: this)=> TResult, thisArg?: TContext): TResult[]; /** * Creates a new JavaScript [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing only the items the satisfy the given collection. @@ -191,7 +191,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns A new [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing the items for which the provided `predicate` evaluated to `true`. * @see [Array.filter](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) */ - filter(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): TItem[]; + filter(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): TItem[]; /** * Creates a new JavaScript [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing only the items the satisfy the given collection. * @template TContext The context type in which the callback is executed. @@ -201,7 +201,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns A new [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing the items for which the provided `predicate` evaluated to `true`. * @see [Array.filter](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) */ - filter(predicate: (this: TContext, item: TItem, index: number, collection: this) => item is TResult, thisArg?: TContext): TResult[]; + filter(predicate: (this: TContext, item: TItem, index: number, collection: this)=> item is TResult, thisArg?: TContext): TResult[]; /** * Returns a new JavaScript [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing the elements starting at the provided `start` index up to, but not including, the provided `end` index. @@ -228,7 +228,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns `true` if the provided `predicate` is `true` for at least one item; otherwise `false`. * @see [Array.some](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/some) */ - some(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): boolean; + some(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): boolean; /** * Checks whether all elements in the collection satisfy a given condition. @@ -238,7 +238,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`. * @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every) */ - every(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): boolean; + every(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): boolean; /** * Reduces the collection to a single item. @@ -246,7 +246,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns a single aggregated item. * @see [Array.reduce](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) */ - reduce(callback: (previousItem: TItem, currentItem: TItem, currentIndex: number, collection: this) => TItem): TItem; + reduce(callback: (previousItem: TItem, currentItem: TItem, currentIndex: number, collection: this)=> TItem): TItem; /** * Reduces the collection to a single item. * @template TResult The result value type to which items are aggregated. @@ -255,7 +255,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns the value containing the aggregated collection. * @see [Array.reduce](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) */ - reduce(callback: (result: TResult, item: TItem, index: number, collection: this) => TResult, initialValue: TResult): TResult; + reduce(callback: (result: TResult, item: TItem, index: number, collection: this)=> TResult, initialValue: TResult): TResult; /** * Reduces the collection to a single item iterating the collection from end to start. @@ -263,7 +263,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns a single aggregated item. * @see [Array.reduceRight](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight) */ - reduceRight(callback: (previousItem: TItem, currentItem: TItem, currentIndex: number, collection: this) => TItem): TItem; + reduceRight(callback: (previousItem: TItem, currentItem: TItem, currentIndex: number, collection: this)=> TItem): TItem; /** * Reduces the collection to a single item iterating the collection from end to start. * @template TResult The result value type to which items are aggregated. @@ -272,7 +272,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns Returns the value containing the aggregated collection. * @see [Array.reduceRight](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight) */ - reduceRight(callback: (result: TResult, item: TItem, index: number, collection: this) => TResult, initialValue: TResult): TResult; + reduceRight(callback: (result: TResult, item: TItem, index: number, collection: this)=> TResult, initialValue: TResult): TResult; /** * Converts the observable collection to a native JavaScript [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array). @@ -293,7 +293,7 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @returns A new [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing the elements sorted in ascending order. * @see [Array.toSorted](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted) */ - toSorted(compareCallback?: (a: Exclude, b: Exclude) => number): TItem[]; + toSorted(compareCallback?: (a: Exclude, b: Exclude)=> number): TItem[]; /** * Returns a JavaScript [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing the spliced items of the collection. @@ -305,4 +305,4 @@ export interface IReadOnlyObservableCollection extends Iterable, A * @see [Array.toSpliced](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/toSpliced) */ toSpliced(start: number, deleteCount?: number, ...items: readonly TItem[]): TItem[]; -} +} \ No newline at end of file diff --git a/src/collections/observableCollections/ObservableCollection.ts b/src/collections/observableCollections/ObservableCollection.ts index 743468c..71e1054 100644 --- a/src/collections/observableCollections/ObservableCollection.ts +++ b/src/collections/observableCollections/ObservableCollection.ts @@ -107,7 +107,7 @@ export class ObservableCollection extends ReadOnlyObservableCollection, right: Exclude) => number): this { + public sort(compareCallback?: (left: Exclude, right: Exclude)=> number): this { return super.sort.apply(this, arguments); } diff --git a/src/collections/observableCollections/ReadOnlyObservableCollection.ts b/src/collections/observableCollections/ReadOnlyObservableCollection.ts index b66d145..b3eb2f4 100644 --- a/src/collections/observableCollections/ReadOnlyObservableCollection.ts +++ b/src/collections/observableCollections/ReadOnlyObservableCollection.ts @@ -1,7 +1,7 @@ -import type { ICollectionChangedEvent } from './ICollectionChangedEvent'; import type { ICollectionChange } from './ICollectionChange'; -import type { ICollectionReorderedEvent } from './ICollectionReorderedEvent'; +import type { ICollectionChangedEvent } from './ICollectionChangedEvent'; import type { ICollectionReorder, ICollectionItemMove } from './ICollectionReorder'; +import type { ICollectionReorderedEvent } from './ICollectionReorderedEvent'; import type { IReadOnlyObservableCollection } from './IReadOnlyObservableCollection'; import type { ObservableCollection } from './ObservableCollection'; import { EventDispatcher } from '../../events'; @@ -47,7 +47,6 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR /** An event that is raised when the collection changed by adding or removing items. */ public readonly collectionChanged: ICollectionChangedEvent; - /** An event that is raised when the collection is reordered. */ public readonly collectionReordered: ICollectionReorderedEvent; @@ -121,7 +120,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR public [Symbol.iterator](): IterableIterator { const changeTokenCopy = this._changeToken; - return new ObservableCollectionIterator(this, () => changeTokenCopy !== this._changeToken, index => this[index]); + return new ObservableCollectionIterator(this, () => changeTokenCopy !== this._changeToken, (index) => this[index]); } /** @@ -131,7 +130,8 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR */ public entries(): IterableIterator<[number, TItem]> { const changeTokenCopy = this._changeToken; - return new ObservableCollectionIterator(this, () => changeTokenCopy !== this._changeToken, index => [index, this[index]]); + + return new ObservableCollectionIterator(this, () => changeTokenCopy !== this._changeToken, (index) => [index, this[index]]); } /** @@ -141,7 +141,8 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR */ public keys(): IterableIterator { const changeTokenCopy = this._changeToken; - return new ObservableCollectionIterator(this, () => changeTokenCopy !== this._changeToken, index => index); + + return new ObservableCollectionIterator(this, () => changeTokenCopy !== this._changeToken, (index) => index); } /** @@ -151,7 +152,8 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR */ public values(): IterableIterator { const changeTokenCopy = this._changeToken; - return new ObservableCollectionIterator(this, () => changeTokenCopy !== this._changeToken, index => this[index]); + + return new ObservableCollectionIterator(this, () => changeTokenCopy !== this._changeToken, (index) => this[index]); } /** @@ -199,7 +201,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR public concat(...items: readonly (TItem | readonly TItem[])[]): TItem[] { const result = this.toArray(); - items.forEach(item => { + items.forEach((item) => { if (Array.isArray(item)) result.push(...item); else @@ -207,7 +209,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR }); return result; - }; + } /** * Aggregates the contained items into a {@linkcode String} placing the provided `separator` between them. @@ -216,7 +218,8 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @see [Array.join](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/join) */ public join(separator?: string | null): string { - return this.toArray().join(separator!); + return this.toArray() + .join(separator!); } /** @@ -236,6 +239,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR const result = new Array(normalizedEndIndex - normalizedStartIndex); for (let index = 0; index < result.length; index++) result[index] = this[index + normalizedStartIndex]; + return result; } } @@ -286,7 +290,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns `true` if the provided `predicate` is `true` for all items; otherwise `false`. * @see [Array.every](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every) */ - public every(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): boolean { + public every(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): boolean { let result = true; const changeTokenCopy = this._changeToken; @@ -310,7 +314,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns `true` if the provided `predicate` is `true` for at least one item; otherwise `false`. * @see [Array.some](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/some) */ - public some(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): boolean { + public some(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): boolean { let result = false; const changeTokenCopy = this._changeToken; @@ -333,7 +337,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @param thisArg A value to use as context when processing items. * @see [Array.forEach](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) */ - public forEach(callback: (this: TContext, item: TItem, index: number, collection: this) => void, thisArg?: TContext): void { + public forEach(callback: (this: TContext, item: TItem, index: number, collection: this)=> void, thisArg?: TContext): void { const changeTokenCopy = this._changeToken; for (let index = 0; index < this._length; index++) { @@ -353,7 +357,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns A new [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing the mapped items. * @see [Array.map](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map) */ - public map(callback: (this: TContext, item: TItem, index: number, collection: this) => TResult, thisArg?: TContext): TResult[] { + public map(callback: (this: TContext, item: TItem, index: number, collection: this)=> TResult, thisArg?: TContext): TResult[] { const changeTokenCopy = this._changeToken; const result = new Array(this._length); @@ -375,7 +379,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns A new [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing the items for which the provided `predicate` evaluated to `true`. * @see [Array.filter](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) */ - public filter(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): TItem[]; + public filter(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): TItem[]; /** * Creates a new JavaScript [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing only the items the satisfy the given collection. * @template TContext The context type in which the callback is executed. @@ -385,9 +389,9 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns A new [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing the items for which the provided `predicate` evaluated to `true`. * @see [Array.filter](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) */ - public filter(predicate: (this: TContext, item: TItem, index: number, collection: this) => item is TResult, thisArg?: TContext): TResult[]; + public filter(predicate: (this: TContext, item: TItem, index: number, collection: this)=> item is TResult, thisArg?: TContext): TResult[]; - public filter(predicate: (this: TContext, item: TItem, index: number, collection: this) => item is TResult, thisArg?: TContext): TResult[] { + public filter(predicate: (this: TContext, item: TItem, index: number, collection: this)=> item is TResult, thisArg?: TContext): TResult[] { const changeTokenCopy = this._changeToken; const result: TResult[] = []; @@ -409,7 +413,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns a single aggregated item. * @see [Array.reduce](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) */ - public reduce(callback: (previousItem: TItem, currentItem: TItem, currentIndex: number, collection: this) => TItem): TItem; + public reduce(callback: (previousItem: TItem, currentItem: TItem, currentIndex: number, collection: this)=> TItem): TItem; /** * Reduces the collection to a single item. * @template TResult The result value type to which items are aggregated. @@ -418,7 +422,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns the value containing the aggregated collection. * @see [Array.reduce](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce) */ - public reduce(callback: (result: TResult, item: TItem, index: number, collection: this) => TResult, initialValue: TResult): TResult; + public reduce(callback: (result: TResult, item: TItem, index: number, collection: this)=> TResult, initialValue: TResult): TResult; public reduce(callback: any, initialValue?: any): any { if (arguments.length === 1 && this._length === 0) @@ -444,7 +448,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns a single aggregated item. * @see [Array.reduceRight](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight) */ - public reduceRight(callback: (previousItem: TItem, currentItem: TItem, currentIndex: number, collection: this) => TItem): TItem; + public reduceRight(callback: (previousItem: TItem, currentItem: TItem, currentIndex: number, collection: this)=> TItem): TItem; /** * Reduces the collection to a single item iterating the collection from end to start. * @template TResult The result value type to which items are aggregated. @@ -453,7 +457,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns the value containing the aggregated collection. * @see [Array.reduceRight](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/reduceRight) */ - public reduceRight(callback: (result: TResult, item: TItem, index: number, collection: this) => TResult, initialValue: TResult): TResult; + public reduceRight(callback: (result: TResult, item: TItem, index: number, collection: this)=> TResult, initialValue: TResult): TResult; public reduceRight(callback: any, initialValue?: any): any { if (arguments.length === 1 && this._length === 0) @@ -481,7 +485,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns the first item for which the provided `predicate` evaluates to `true`; otherwise `undefined`. * @see [Array.find](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/find) */ - public find(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): TItem | undefined; + public find(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): TItem | undefined; /** * Returns the first item that satisfies the given condition. * @template TResult The type of item to return. @@ -491,9 +495,9 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns the first item for which the provided `predicate` evaluates to `true`; otherwise `undefined`. * @see [Array.find](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/find) */ - public find(predicate: (this: TContext, item: TItem, index: number, collection: this) => item is TResult, thisArg?: TContext): TResult | undefined; + public find(predicate: (this: TContext, item: TItem, index: number, collection: this)=> item is TResult, thisArg?: TContext): TResult | undefined; - public find(predicate: (item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): TResult | undefined { + public find(predicate: (item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): TResult | undefined { const changeTokenCopy = this._changeToken; let foundItem = false; let index = 0; @@ -523,7 +527,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns the last item for which the provided `predicate` evaluates to `true`; otherwise `undefined`. * @see [Array.findLast](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast) */ - public findLast(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): TItem | undefined; + public findLast(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): TItem | undefined; /** * Returns the last item that satisfies the given condition. * @template TResult The type of item to return. @@ -533,9 +537,9 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns the last item for which the provided `predicate` evaluates to `true`; otherwise `undefined`. * @see [Array.findLast](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast) */ - public findLast(predicate: (this: TContext, item: TItem, index: number, collection: this) => item is TResult, thisArg?: TContext): TResult | undefined; + public findLast(predicate: (this: TContext, item: TItem, index: number, collection: this)=> item is TResult, thisArg?: TContext): TResult | undefined; - public findLast(predicate: (item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): TResult | undefined { + public findLast(predicate: (item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): TResult | undefined { const changeTokenCopy = this._changeToken; let foundItem = false; let index = this._length - 1; @@ -565,7 +569,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns the index of the first item for which the provided `predicate` evaluates to `true`; otherwise `-1`. * @see [Array.findIndex](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex) */ - public findIndex(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): number { + public findIndex(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): number { const changeTokenCopy = this._changeToken; let foundItem = false; let index = 0; @@ -594,7 +598,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns Returns the index of the last item for which the provided `predicate` evaluates to `true`; otherwise `-1`. * @see [Array.findLastIndex](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex) */ - public findLastIndex(predicate: (this: TContext, item: TItem, index: number, collection: this) => boolean, thisArg?: TContext): number { + public findLastIndex(predicate: (this: TContext, item: TItem, index: number, collection: this)=> boolean, thisArg?: TContext): number { const changeTokenCopy = this._changeToken; let foundItem = false; let index = this._length - 1; @@ -654,7 +658,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns A new [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) containing the elements sorted in ascending order. * @see [Array.toSorted](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted) */ - public toSorted(compareCallback?: (a: Exclude, b: Exclude) => number): TItem[] { + public toSorted(compareCallback?: (a: Exclude, b: Exclude)=> number): TItem[] { const changeTokenCopy = this._changeToken; const sortedIndexes = sortIndexes(this, compareCallback, () => changeTokenCopy !== this._changeToken); @@ -982,7 +986,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR * @returns The observable collection on which the operation is performed. * @see [Array.sort](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) */ - protected sort(compareCallback?: (left: Exclude, right: Exclude) => number): this { + protected sort(compareCallback?: (left: Exclude, right: Exclude)=> number): this { if (this.length > 1) { const changeTokenCopy = this._changeToken; const sortedIndexes = sortIndexes(this, compareCallback, () => changeTokenCopy !== this._changeToken); @@ -1030,7 +1034,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR if (this.length > 1) { this._changeToken = (this._changeToken + 1) % Number.MAX_VALUE; - const evenLength = this.length - this.length % 2; + const evenLength = this.length - (this.length % 2); const movedItems = new Array>(evenLength); const changedIndexes = new Array(evenLength); @@ -1115,7 +1119,7 @@ export class ReadOnlyObservableCollection extends ViewModel implements IR addedItems, removedItems, startIndex: normalizedTargetIndex - }) + }); this.notifyPropertiesChanged.apply(this, changedIndexes); } @@ -1168,10 +1172,10 @@ class ObservableCollectionIterator implements Iterator; - private readonly _collectionChanged: () => boolean; - private readonly _getCurrentValue: (index: number, observableCollection: IReadOnlyObservableCollection) => TValue; + private readonly _collectionChanged: ()=> boolean; + private readonly _getCurrentValue: (index: number, observableCollection: IReadOnlyObservableCollection)=> TValue; - public constructor(observableCollection: IReadOnlyObservableCollection, collectionChanged: () => boolean, getCurrentValue: (index: number, observableCollection: IReadOnlyObservableCollection) => TValue) { + public constructor(observableCollection: IReadOnlyObservableCollection, collectionChanged: ()=> boolean, getCurrentValue: (index: number, observableCollection: IReadOnlyObservableCollection)=> TValue) { this._index = 0; this._observableCollection = observableCollection; this._completed = this._index >= this._observableCollection.length; @@ -1282,7 +1286,7 @@ function defineIndexProperty(collection: ArrayLike, index: number, item }); } -function sortIndexes(items: ArrayLike, compareCallback: ((a: TItem, b: TItem) => number) | undefined, hasCollectionChanged: () => boolean): readonly number[] { +function sortIndexes(items: ArrayLike, compareCallback: ((a: TItem, b: TItem)=> number) | undefined, hasCollectionChanged: ()=> boolean): readonly number[] { if (items.length <= 1) return [0]; else if (compareCallback === null || compareCallback === undefined) { @@ -1312,7 +1316,7 @@ function sortIndexes(items: ArrayLike, compareCallback: ((a: TItem }); } -function mergeSortIndexes(items: ArrayLike, compareCallback: (a: TItem, b: TItem) => number): readonly number[] { +function mergeSortIndexes(items: ArrayLike, compareCallback: (a: TItem, b: TItem)=> number): readonly number[] { let result: number[]; let sourceIndexes = new Array(items.length); @@ -1330,13 +1334,12 @@ function mergeSortIndexes(items: ArrayLike, compareCallback: (a: T result = destinationIndexes; destinationIndexes = sourceIndexes; sourceIndexes = result; - } while (rangeLength < items.length); return result; } -function mergeIndexes(items: ArrayLike, sourceIndexes: readonly number[], result: number[], rangeStart: number, rangeLength: number, compareCallback: (a: TItem, b: TItem) => number) { +function mergeIndexes(items: ArrayLike, sourceIndexes: readonly number[], result: number[], rangeStart: number, rangeLength: number, compareCallback: (a: TItem, b: TItem)=> number) { let leftIndex = rangeStart; const leftEnd = Math.min(items.length, rangeStart + Math.floor(rangeLength / 2)); let rightIndex = leftEnd; @@ -1376,6 +1379,6 @@ function mergeIndexes(items: ArrayLike, sourceIndexes: readonly nu while (rightIndex < rightEnd) { result[index] = sourceIndexes[rightIndex]; rightIndex++; - index++ + index++; } } \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.concat.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.concat.test.ts index 18c61c3..7224083 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.concat.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.concat.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.concat', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: collection => collection.concat(), + applyOperation: (collection) => collection.concat(), expectedResult: [1, 2, 3] }); @@ -16,7 +16,7 @@ describe('ObservableCollection.concat', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: collection => collection.concat(4, 5, 6), + applyOperation: (collection) => collection.concat(4, 5, 6), expectedResult: [1, 2, 3, 4, 5, 6] }); @@ -26,7 +26,7 @@ describe('ObservableCollection.concat', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: collection => collection.concat([4, 5, 6]), + applyOperation: (collection) => collection.concat([4, 5, 6]), expectedResult: [1, 2, 3, 4, 5, 6] }); @@ -36,7 +36,7 @@ describe('ObservableCollection.concat', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: collection => collection.concat(4, [5, 6], 7, [8]), + applyOperation: (collection) => collection.concat(4, [5, 6], 7, [8]), expectedResult: [1, 2, 3, 4, 5, 6, 7, 8] }); @@ -49,7 +49,8 @@ describe('ObservableCollection.concat', (): void => { for (const _ of observableCollection) observableCollection.concat(4); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.copyWithin.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.copyWithin.test.ts index bed17b1..7668528 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.copyWithin.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.copyWithin.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.copyWithin', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.copyWithin(0, 1), + applyOperation: (collection) => collection.copyWithin(0, 1), expectedResult: selfResult }); @@ -16,7 +16,7 @@ describe('ObservableCollection.copyWithin', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.copyWithin(0, 0), + applyOperation: (collection) => collection.copyWithin(0, 0), expectedResult: selfResult }); @@ -28,7 +28,7 @@ describe('ObservableCollection.copyWithin', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7], changedProperties: [2, 3, 4], - applyOperation: collection => collection.copyWithin(2, 4), + applyOperation: (collection) => collection.copyWithin(2, 4), expectedResult: selfResult, expectedCollection: [1, 2, 5, 6, 7, 6, 7] @@ -39,7 +39,7 @@ describe('ObservableCollection.copyWithin', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5, 6, 7], - applyOperation: collection => collection.copyWithin(4, 7), + applyOperation: (collection) => collection.copyWithin(4, 7), expectedResult: selfResult }); @@ -51,7 +51,7 @@ describe('ObservableCollection.copyWithin', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], changedProperties: [2, 3], - applyOperation: collection => collection.copyWithin(2, 4, 6), + applyOperation: (collection) => collection.copyWithin(2, 4, 6), expectedResult: selfResult, expectedCollection: [1, 2, 5, 6, 5, 6, 7, 8, 9] @@ -64,7 +64,7 @@ describe('ObservableCollection.copyWithin', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], changedProperties: [2, 3, 4, 5], - applyOperation: collection => collection.copyWithin(2, 4, -1), + applyOperation: (collection) => collection.copyWithin(2, 4, -1), expectedResult: selfResult, expectedCollection: [1, 2, 5, 6, 7, 8, 7, 8, 9] @@ -75,7 +75,7 @@ describe('ObservableCollection.copyWithin', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.copyWithin(4, 2, 2), + applyOperation: (collection) => collection.copyWithin(4, 2, 2), expectedResult: selfResult }); @@ -85,7 +85,7 @@ describe('ObservableCollection.copyWithin', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.copyWithin(4, 3, 2), + applyOperation: (collection) => collection.copyWithin(4, 3, 2), expectedResult: selfResult }); @@ -98,8 +98,9 @@ describe('ObservableCollection.copyWithin', (): void => { for (const _ of observableCollection) observableCollection.copyWithin(1, 2); - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); it('copying within of an empty collection while iterating does not break iterators', (): void => { @@ -111,9 +112,10 @@ describe('ObservableCollection.copyWithin', (): void => { observableCollection.copyWithin(1, 2); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); it('copying within when target index the same as start index while iterating does not break iterators', (): void => { @@ -125,9 +127,10 @@ describe('ObservableCollection.copyWithin', (): void => { observableCollection.copyWithin(2, 2); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); it('copying within when end index is less than start index while iterating does not break iterators', (): void => { @@ -139,8 +142,9 @@ describe('ObservableCollection.copyWithin', (): void => { observableCollection.copyWithin(1, 3, 2); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.every.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.every.test.ts index 203a254..7ee5367 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.every.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.every.test.ts @@ -7,8 +7,8 @@ describe('ObservableCollection.every', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.every(_ => false), - applyCollectionOperation: collection => collection.every(_ => false) + applyArrayOperation: (array) => array.every((_) => false), + applyCollectionOperation: (collection) => collection.every((_) => false) }, expectedResult: true @@ -20,8 +20,8 @@ describe('ObservableCollection.every', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.every(item => item === 3), - applyCollectionOperation: collection => collection.every(item => item === 3) + applyArrayOperation: (array) => array.every((item) => item === 3), + applyCollectionOperation: (collection) => collection.every((item) => item === 3) }, expectedResult: false @@ -33,8 +33,8 @@ describe('ObservableCollection.every', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.every(item => item > 0), - applyCollectionOperation: collection => collection.every(item => item > 0) + applyArrayOperation: (array) => array.every((item) => item > 0), + applyCollectionOperation: (collection) => collection.every((item) => item > 0) }, expectedResult: true @@ -48,14 +48,18 @@ describe('ObservableCollection.every', (): void => { observableCollection.every((item, index, collection) => { invocationCount++; - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('calling every with context passes it to the callback', (): void => { @@ -66,28 +70,35 @@ describe('ObservableCollection.every', (): void => { function (item, index, collection) { invocationCount++; - expect(this).toStrictEqual(context); - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(this) + .toStrictEqual(context); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }, context ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the collection while executing every throws exception', (): void => { expect( () => { const observableCollection = new ObservableCollection([1, 2, 3]); - observableCollection.every(_ => { + observableCollection.every((_) => { observableCollection.pop(); + return false; }); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); @@ -97,8 +108,9 @@ describe('ObservableCollection.every', (): void => { const observableCollection = new ObservableCollection([1, 2, 3]); for (const _ of observableCollection) - observableCollection.every(_ => true); - }) + observableCollection.every((_) => true); + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.fill.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.fill.test.ts index cadd48f..0a63ba9 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.fill.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.fill.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.fill', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.fill(10), + applyOperation: (collection) => collection.fill(10), expectedResult: selfResult }); @@ -18,7 +18,7 @@ describe('ObservableCollection.fill', (): void => { initialState: [1, 2, 3, 4, 5], changedProperties: [0, 1, 2, 3, 4], - applyOperation: collection => collection.fill(10), + applyOperation: (collection) => collection.fill(10), expectedResult: selfResult, expectedCollection: [10, 10, 10, 10, 10] @@ -31,7 +31,7 @@ describe('ObservableCollection.fill', (): void => { initialState: [1, 2, 3, 4, 5], changedProperties: [2, 3, 4], - applyOperation: collection => collection.fill(10, 2), + applyOperation: (collection) => collection.fill(10, 2), expectedResult: selfResult, expectedCollection: [1, 2, 10, 10, 10] @@ -44,7 +44,7 @@ describe('ObservableCollection.fill', (): void => { initialState: [1, 2, 3, 4, 5], changedProperties: [3, 4], - applyOperation: collection => collection.fill(10, -2), + applyOperation: (collection) => collection.fill(10, -2), expectedResult: selfResult, expectedCollection: [1, 2, 3, 10, 10] @@ -55,7 +55,7 @@ describe('ObservableCollection.fill', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.fill(10, 5), + applyOperation: (collection) => collection.fill(10, 5), expectedResult: selfResult }); @@ -67,7 +67,7 @@ describe('ObservableCollection.fill', (): void => { initialState: [1, 2, 3, 4, 5], changedProperties: [0, 1, 2, 3, 4], - applyOperation: collection => collection.fill(10, -5), + applyOperation: (collection) => collection.fill(10, -5), expectedResult: selfResult, expectedCollection: [10, 10, 10, 10, 10] @@ -80,7 +80,7 @@ describe('ObservableCollection.fill', (): void => { initialState: [1, 2, 3, 4, 5], changedProperties: [1, 2], - applyOperation: collection => collection.fill(10, 1, 3), + applyOperation: (collection) => collection.fill(10, 1, 3), expectedResult: selfResult, expectedCollection: [1, 10, 10, 4, 5] @@ -93,7 +93,7 @@ describe('ObservableCollection.fill', (): void => { initialState: [1, 2, 3, 4, 5], changedProperties: [1, 2, 3], - applyOperation: collection => collection.fill(10, 1, -1), + applyOperation: (collection) => collection.fill(10, 1, -1), expectedResult: selfResult, expectedCollection: [1, 10, 10, 10, 5] @@ -104,7 +104,7 @@ describe('ObservableCollection.fill', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.fill(10, 3, 3), + applyOperation: (collection) => collection.fill(10, 3, 3), expectedResult: selfResult }); @@ -114,7 +114,7 @@ describe('ObservableCollection.fill', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.fill(10, 3, 2), + applyOperation: (collection) => collection.fill(10, 3, 2), expectedResult: selfResult }); @@ -127,8 +127,9 @@ describe('ObservableCollection.fill', (): void => { for (const _ of observableCollection) observableCollection.fill(1); - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); it('filling items in an empty collection while iterating does not break iterators', (): void => { @@ -140,9 +141,10 @@ describe('ObservableCollection.fill', (): void => { observableCollection.fill(1); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); it('filling items when end index the same as start index while iterating does not break iterators', (): void => { @@ -154,9 +156,10 @@ describe('ObservableCollection.fill', (): void => { observableCollection.fill(10, 3, 3); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); it('filling items when end index is less than start index while iterating does not break iterators', (): void => { @@ -168,8 +171,9 @@ describe('ObservableCollection.fill', (): void => { observableCollection.fill(10, 3, 2); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.filter.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.filter.test.ts index e5db03b..ee11d16 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.filter.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.filter.test.ts @@ -7,8 +7,8 @@ describe('ObservableCollection.filter', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.filter(item => item > 0), - applyCollectionOperation: collection => collection.filter(item => item > 0) + applyArrayOperation: (array) => array.filter((item) => item > 0), + applyCollectionOperation: (collection) => collection.filter((item) => item > 0) }, expectedResult: [] @@ -20,8 +20,8 @@ describe('ObservableCollection.filter', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.filter(item => item === 2), - applyCollectionOperation: collection => collection.filter(item => item === 2) + applyArrayOperation: (array) => array.filter((item) => item === 2), + applyCollectionOperation: (collection) => collection.filter((item) => item === 2) }, expectedResult: [2] @@ -34,14 +34,18 @@ describe('ObservableCollection.filter', (): void => { observableCollection.filter((item, index, collection) => { invocationCount++; - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('calling filter with context passes it to the callback', (): void => { @@ -52,28 +56,35 @@ describe('ObservableCollection.filter', (): void => { function (item, index, collection) { invocationCount++; - expect(this).toStrictEqual(context); - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(this) + .toStrictEqual(context); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }, context ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the collection while executing filter throws exception', (): void => { expect( () => { const observableCollection = new ObservableCollection([1, 2, 3]); - observableCollection.filter(_ => { + observableCollection.filter((_) => { observableCollection.pop(); + return true; }); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); @@ -83,8 +94,9 @@ describe('ObservableCollection.filter', (): void => { const observableCollection = new ObservableCollection([1, 2, 3]); for (const _ of observableCollection) - observableCollection.filter(item => item > 1); - }) + observableCollection.filter((item) => item > 1); + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.find.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.find.test.ts index 775f464..e661d69 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.find.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.find.test.ts @@ -7,8 +7,8 @@ describe('ObservableCollection.find', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.find(item => item === 3), - applyCollectionOperation: collection => collection.find(item => item === 3) + applyArrayOperation: (array) => array.find((item) => item === 3), + applyCollectionOperation: (collection) => collection.find((item) => item === 3) }, expectedResult: undefined @@ -20,8 +20,8 @@ describe('ObservableCollection.find', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7], applyOperation: { - applyArrayOperation: array => array.find(item => item === 10), - applyCollectionOperation: collection => collection.find(item => item === 10) + applyArrayOperation: (array) => array.find((item) => item === 10), + applyCollectionOperation: (collection) => collection.find((item) => item === 10) }, expectedResult: undefined @@ -33,8 +33,8 @@ describe('ObservableCollection.find', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7], applyOperation: { - applyArrayOperation: array => array.find(item => item % 2 === 0), - applyCollectionOperation: collection => collection.find(item => item % 2 === 0) + applyArrayOperation: (array) => array.find((item) => item % 2 === 0), + applyCollectionOperation: (collection) => collection.find((item) => item % 2 === 0) }, expectedResult: 2 @@ -47,14 +47,18 @@ describe('ObservableCollection.find', (): void => { observableCollection.find((item, index, collection) => { invocationCount++; - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('calling find with context passes it to the callback', (): void => { @@ -65,28 +69,35 @@ describe('ObservableCollection.find', (): void => { function (item, index, collection) { invocationCount++; - expect(this).toStrictEqual(context); - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(this) + .toStrictEqual(context); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }, context ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the collection while executing find throws exception', (): void => { expect( () => { const observableCollection = new ObservableCollection([1, 2, 3]); - observableCollection.find(_ => { + observableCollection.find((_) => { observableCollection.pop(); + return true; }); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); @@ -96,8 +107,9 @@ describe('ObservableCollection.find', (): void => { const observableCollection = new ObservableCollection([1, 2, 3]); for (const _ of observableCollection) - observableCollection.find(item => item % 2 === 0); - }) + observableCollection.find((item) => item % 2 === 0); + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.findIndex.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.findIndex.test.ts index 299da22..5a2aaf5 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.findIndex.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.findIndex.test.ts @@ -7,8 +7,8 @@ describe('ObservableCollection.findIndex', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.findIndex(item => item === 3), - applyCollectionOperation: collection => collection.findIndex(item => item === 3) + applyArrayOperation: (array) => array.findIndex((item) => item === 3), + applyCollectionOperation: (collection) => collection.findIndex((item) => item === 3) }, expectedResult: -1 @@ -20,8 +20,8 @@ describe('ObservableCollection.findIndex', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7], applyOperation: { - applyArrayOperation: array => array.findIndex(item => item === 10), - applyCollectionOperation: collection => collection.findIndex(item => item === 10) + applyArrayOperation: (array) => array.findIndex((item) => item === 10), + applyCollectionOperation: (collection) => collection.findIndex((item) => item === 10) }, expectedResult: -1 @@ -33,8 +33,8 @@ describe('ObservableCollection.findIndex', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7], applyOperation: { - applyArrayOperation: array => array.findIndex(item => item % 2 === 0), - applyCollectionOperation: collection => collection.findIndex(item => item % 2 === 0) + applyArrayOperation: (array) => array.findIndex((item) => item % 2 === 0), + applyCollectionOperation: (collection) => collection.findIndex((item) => item % 2 === 0) }, expectedResult: 1 @@ -47,14 +47,18 @@ describe('ObservableCollection.findIndex', (): void => { observableCollection.findIndex((item, index, collection) => { invocationCount++; - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('calling findIndex with context passes it to the callback', (): void => { @@ -65,28 +69,35 @@ describe('ObservableCollection.findIndex', (): void => { function (item, index, collection) { invocationCount++; - expect(this).toStrictEqual(context); - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(this) + .toStrictEqual(context); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }, context ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the collection while executing findIndex throws exception', (): void => { expect( () => { const observableCollection = new ObservableCollection([1, 2, 3]); - observableCollection.findIndex(_ => { + observableCollection.findIndex((_) => { observableCollection.pop(); + return true; }); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); @@ -96,8 +107,9 @@ describe('ObservableCollection.findIndex', (): void => { const observableCollection = new ObservableCollection([1, 2, 3]); for (const _ of observableCollection) - observableCollection.findIndex(item => item % 2 === 0); - }) + observableCollection.findIndex((item) => item % 2 === 0); + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.findLast.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.findLast.test.ts index 5551b1f..262d2ad 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.findLast.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.findLast.test.ts @@ -7,8 +7,8 @@ describe('ObservableCollection.findLast', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.findLast(item => item === 3), - applyCollectionOperation: collection => collection.findLast(item => item === 3) + applyArrayOperation: (array) => array.findLast((item) => item === 3), + applyCollectionOperation: (collection) => collection.findLast((item) => item === 3) }, expectedResult: undefined @@ -20,8 +20,8 @@ describe('ObservableCollection.findLast', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7], applyOperation: { - applyArrayOperation: array => array.findLast(item => item === 10), - applyCollectionOperation: collection => collection.findLast(item => item === 10) + applyArrayOperation: (array) => array.findLast((item) => item === 10), + applyCollectionOperation: (collection) => collection.findLast((item) => item === 10) }, expectedResult: undefined @@ -33,8 +33,8 @@ describe('ObservableCollection.findLast', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7], applyOperation: { - applyArrayOperation: array => array.findLast(item => item % 2 === 0), - applyCollectionOperation: collection => collection.findLast(item => item % 2 === 0) + applyArrayOperation: (array) => array.findLast((item) => item % 2 === 0), + applyCollectionOperation: (collection) => collection.findLast((item) => item % 2 === 0) }, expectedResult: 6 @@ -47,14 +47,18 @@ describe('ObservableCollection.findLast', (): void => { observableCollection.findLast((item, index, collection) => { invocationCount++; - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('calling findLast with context passes it to the callback', (): void => { @@ -65,28 +69,35 @@ describe('ObservableCollection.findLast', (): void => { function (item, index, collection) { invocationCount++; - expect(this).toStrictEqual(context); - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(this) + .toStrictEqual(context); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }, context ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the collection while executing findLast throws exception', (): void => { expect( () => { const observableCollection = new ObservableCollection([1, 2, 3]); - observableCollection.findLast(_ => { + observableCollection.findLast((_) => { observableCollection.pop(); + return true; }); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); @@ -96,8 +107,9 @@ describe('ObservableCollection.findLast', (): void => { const observableCollection = new ObservableCollection([1, 2, 3]); for (const _ of observableCollection) - observableCollection.findLast(item => item % 2 === 0); - }) + observableCollection.findLast((item) => item % 2 === 0); + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.findLastIndex.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.findLastIndex.test.ts index ce99a60..a306487 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.findLastIndex.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.findLastIndex.test.ts @@ -7,8 +7,8 @@ describe('ObservableCollection.findLastIndex', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.findLastIndex(item => item === 3), - applyCollectionOperation: collection => collection.findLastIndex(item => item === 3) + applyArrayOperation: (array) => array.findLastIndex((item) => item === 3), + applyCollectionOperation: (collection) => collection.findLastIndex((item) => item === 3) }, expectedResult: -1 @@ -20,8 +20,8 @@ describe('ObservableCollection.findLastIndex', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7], applyOperation: { - applyArrayOperation: array => array.findLastIndex(item => item === 10), - applyCollectionOperation: collection => collection.findLastIndex(item => item === 10) + applyArrayOperation: (array) => array.findLastIndex((item) => item === 10), + applyCollectionOperation: (collection) => collection.findLastIndex((item) => item === 10) }, expectedResult: -1 @@ -33,8 +33,8 @@ describe('ObservableCollection.findLastIndex', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7], applyOperation: { - applyArrayOperation: array => array.findLastIndex(item => item % 2 === 0), - applyCollectionOperation: collection => collection.findLastIndex(item => item % 2 === 0) + applyArrayOperation: (array) => array.findLastIndex((item) => item % 2 === 0), + applyCollectionOperation: (collection) => collection.findLastIndex((item) => item % 2 === 0) }, expectedResult: 5 @@ -47,14 +47,18 @@ describe('ObservableCollection.findLastIndex', (): void => { observableCollection.findLastIndex((item, index, collection) => { invocationCount++; - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('calling findLastIndex with context passes it to the callback', (): void => { @@ -65,28 +69,35 @@ describe('ObservableCollection.findLastIndex', (): void => { function (item, index, collection) { invocationCount++; - expect(this).toStrictEqual(context); - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(this) + .toStrictEqual(context); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }, context ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the collection while executing findLastIndex throws exception', (): void => { expect( () => { const observableCollection = new ObservableCollection([1, 2, 3]); - observableCollection.findLastIndex(_ => { + observableCollection.findLastIndex((_) => { observableCollection.pop(); + return true; }); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); @@ -96,8 +107,9 @@ describe('ObservableCollection.findLastIndex', (): void => { const observableCollection = new ObservableCollection([1, 2, 3]); for (const _ of observableCollection) - observableCollection.findLastIndex(item => item % 2 === 0); - }) + observableCollection.findLastIndex((item) => item % 2 === 0); + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.forEach.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.forEach.test.ts index 219e125..ee747f1 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.forEach.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.forEach.test.ts @@ -10,15 +10,17 @@ describe('ObservableCollection.forEach', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.forEach(_ => arrayInvocationCount++), - applyCollectionOperation: collection => collection.forEach(_ => collectionInvocationCount++) + applyArrayOperation: (array) => array.forEach((_) => arrayInvocationCount++), + applyCollectionOperation: (collection) => collection.forEach((_) => collectionInvocationCount++) }, expectedResult: undefined }); - expect(arrayInvocationCount).toBe(0); - expect(collectionInvocationCount).toBe(0); + expect(arrayInvocationCount) + .toBe(0); + expect(collectionInvocationCount) + .toBe(0); }); it('iterating over a collection invokes the callback for each item', (): void => { @@ -29,14 +31,15 @@ describe('ObservableCollection.forEach', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.forEach(item => arrayItems.push(item)), - applyCollectionOperation: collection => collection.forEach(item => collectionItems.push(item)) + applyArrayOperation: (array) => array.forEach((item) => arrayItems.push(item)), + applyCollectionOperation: (collection) => collection.forEach((item) => collectionItems.push(item)) }, expectedResult: undefined }); - expect(collectionItems).toEqual(arrayItems); + expect(collectionItems) + .toEqual(arrayItems); }); it('calling forEach passes arguments to each parameter accordingly', (): void => { @@ -45,14 +48,18 @@ describe('ObservableCollection.forEach', (): void => { observableCollection.forEach((item, index, collection) => { invocationCount++; - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('calling forEach with context passes it to the callback', (): void => { @@ -63,27 +70,33 @@ describe('ObservableCollection.forEach', (): void => { function (item, index, collection) { invocationCount++; - expect(this).toStrictEqual(context); - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(this) + .toStrictEqual(context); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }, context ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the collection while executing forEach throws exception', (): void => { expect( () => { const observableCollection = new ObservableCollection([1, 2, 3]); - observableCollection.forEach(_ => { + observableCollection.forEach((_) => { observableCollection.pop(); }); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); @@ -93,8 +106,10 @@ describe('ObservableCollection.forEach', (): void => { const observableCollection = new ObservableCollection([1, 2, 3]); for (const _ of observableCollection) - observableCollection.forEach(_ => {}); - }) + observableCollection.forEach((_) => { + }); + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.includes.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.includes.test.ts index 8f9d747..24af191 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.includes.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.includes.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.includes', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.includes(1), + applyOperation: (collection) => collection.includes(1), expectedResult: false }); @@ -16,7 +16,7 @@ describe('ObservableCollection.includes', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5, 6], - applyOperation: collection => collection.includes(10), + applyOperation: (collection) => collection.includes(10), expectedResult: false }); @@ -26,7 +26,7 @@ describe('ObservableCollection.includes', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5, 6], - applyOperation: collection => collection.includes(2), + applyOperation: (collection) => collection.includes(2), expectedResult: true }); @@ -36,7 +36,7 @@ describe('ObservableCollection.includes', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5, 6], - applyOperation: collection => collection.includes(2, 3), + applyOperation: (collection) => collection.includes(2, 3), expectedResult: false }); @@ -46,7 +46,7 @@ describe('ObservableCollection.includes', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5, 6], - applyOperation: collection => collection.includes(4, 3), + applyOperation: (collection) => collection.includes(4, 3), expectedResult: true }); @@ -56,7 +56,7 @@ describe('ObservableCollection.includes', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5, 6], - applyOperation: collection => collection.includes(2, -3), + applyOperation: (collection) => collection.includes(2, -3), expectedResult: false }); @@ -66,7 +66,7 @@ describe('ObservableCollection.includes', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5, 6], - applyOperation: collection => collection.includes(5, -3), + applyOperation: (collection) => collection.includes(5, -3), expectedResult: true }); @@ -76,7 +76,7 @@ describe('ObservableCollection.includes', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5, 6], - applyOperation: collection => collection.includes(2, -10), + applyOperation: (collection) => collection.includes(2, -10), expectedResult: true }); @@ -86,7 +86,7 @@ describe('ObservableCollection.includes', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5, 6], - applyOperation: collection => collection.includes(2, 10), + applyOperation: (collection) => collection.includes(2, 10), expectedResult: false }); @@ -99,7 +99,8 @@ describe('ObservableCollection.includes', (): void => { for (const _ of observableCollection) observableCollection.includes(2); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.indexOf.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.indexOf.test.ts index b59353a..4a5c831 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.indexOf.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.indexOf.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.indexOf', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.indexOf(1), + applyOperation: (collection) => collection.indexOf(1), expectedResult: -1 }); @@ -16,7 +16,7 @@ describe('ObservableCollection.indexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.indexOf(4), + applyOperation: (collection) => collection.indexOf(4), expectedResult: -1 }); @@ -26,7 +26,7 @@ describe('ObservableCollection.indexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.indexOf(2), + applyOperation: (collection) => collection.indexOf(2), expectedResult: 1 }); @@ -36,7 +36,7 @@ describe('ObservableCollection.indexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.indexOf(2, 3), + applyOperation: (collection) => collection.indexOf(2, 3), expectedResult: 4 }); @@ -46,7 +46,7 @@ describe('ObservableCollection.indexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.indexOf(2, -5), + applyOperation: (collection) => collection.indexOf(2, -5), expectedResult: 1 }); @@ -56,7 +56,7 @@ describe('ObservableCollection.indexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.indexOf(2, -10), + applyOperation: (collection) => collection.indexOf(2, -10), expectedResult: 1 }); @@ -66,7 +66,7 @@ describe('ObservableCollection.indexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.indexOf(2, 6), + applyOperation: (collection) => collection.indexOf(2, 6), expectedResult: -1 }); @@ -76,7 +76,7 @@ describe('ObservableCollection.indexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.indexOf(2, 10), + applyOperation: (collection) => collection.indexOf(2, 10), expectedResult: -1 }); @@ -89,7 +89,8 @@ describe('ObservableCollection.indexOf', (): void => { for (const _ of observableCollection) observableCollection.indexOf(2); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.join.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.join.test.ts index 50d28d9..8e6e65c 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.join.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.join.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.join', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.join(), + applyOperation: (collection) => collection.join(), expectedResult: '' }); @@ -16,7 +16,7 @@ describe('ObservableCollection.join', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.join('-'), + applyOperation: (collection) => collection.join('-'), expectedResult: '' }); @@ -26,9 +26,10 @@ describe('ObservableCollection.join', (): void => { testBlankMutatingOperation({ initialState: [null, undefined, 1, 'A', { prop: 'value' }, new Date('2024-05-05'), ObservableCollection], - applyOperation: collection => collection.join(), + applyOperation: (collection) => collection.join(), - expectedResult: `,,1,A,[object Object],${new Date('2024-05-05').toString()},${ObservableCollection.toString()}` + expectedResult: `,,1,A,[object Object],${new Date('2024-05-05') + .toString()},${ObservableCollection.toString()}` }); }); @@ -36,9 +37,10 @@ describe('ObservableCollection.join', (): void => { testBlankMutatingOperation({ initialState: [null, undefined, 1, 'A', { prop: 'value' }, new Date('2024-05-05'), ObservableCollection], - applyOperation: collection => collection.join('-'), + applyOperation: (collection) => collection.join('-'), - expectedResult: `--1-A-[object Object]-${new Date('2024-05-05').toString()}-${ObservableCollection.toString()}` + expectedResult: `--1-A-[object Object]-${new Date('2024-05-05') + .toString()}-${ObservableCollection.toString()}` }); }); @@ -46,9 +48,10 @@ describe('ObservableCollection.join', (): void => { testBlankMutatingOperation({ initialState: [null, undefined, 1, 'A', { prop: 'value' }, new Date('2024-05-05'), ObservableCollection], - applyOperation: collection => collection.join(undefined), + applyOperation: (collection) => collection.join(undefined), - expectedResult: `,,1,A,[object Object],${new Date('2024-05-05').toString()},${ObservableCollection.toString()}` + expectedResult: `,,1,A,[object Object],${new Date('2024-05-05') + .toString()},${ObservableCollection.toString()}` }); }); @@ -57,11 +60,12 @@ describe('ObservableCollection.join', (): void => { initialState: [null, undefined, 1, 'A', { prop: 'value' }, new Date('2024-05-05'), ObservableCollection], applyOperation: { - applyArrayOperation: collection => collection.join(null!), - applyCollectionOperation: collection => collection.join(null), + applyArrayOperation: (collection) => collection.join(null!), + applyCollectionOperation: (collection) => collection.join(null) }, - expectedResult: `nullnull1nullAnull[object Object]null${new Date('2024-05-05').toString()}null${ObservableCollection.toString()}` + expectedResult: `nullnull1nullAnull[object Object]null${new Date('2024-05-05') + .toString()}null${ObservableCollection.toString()}` }); }); @@ -72,7 +76,8 @@ describe('ObservableCollection.join', (): void => { for (const _ of observableCollection) observableCollection.join(); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.lastIndexOf.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.lastIndexOf.test.ts index 459f99c..4c6c51d 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.lastIndexOf.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.lastIndexOf.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.lastIndexOf', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.lastIndexOf(1), + applyOperation: (collection) => collection.lastIndexOf(1), expectedResult: -1 }); @@ -16,7 +16,7 @@ describe('ObservableCollection.lastIndexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.lastIndexOf(4), + applyOperation: (collection) => collection.lastIndexOf(4), expectedResult: -1 }); @@ -26,7 +26,7 @@ describe('ObservableCollection.lastIndexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.lastIndexOf(2), + applyOperation: (collection) => collection.lastIndexOf(2), expectedResult: 4 }); @@ -36,7 +36,7 @@ describe('ObservableCollection.lastIndexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.lastIndexOf(2, 3), + applyOperation: (collection) => collection.lastIndexOf(2, 3), expectedResult: 1 }); @@ -46,7 +46,7 @@ describe('ObservableCollection.lastIndexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.lastIndexOf(2, -5), + applyOperation: (collection) => collection.lastIndexOf(2, -5), expectedResult: 1 }); @@ -56,7 +56,7 @@ describe('ObservableCollection.lastIndexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.lastIndexOf(2, -10), + applyOperation: (collection) => collection.lastIndexOf(2, -10), expectedResult: -1 }); @@ -66,7 +66,7 @@ describe('ObservableCollection.lastIndexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.lastIndexOf(2, 6), + applyOperation: (collection) => collection.lastIndexOf(2, 6), expectedResult: 4 }); @@ -76,7 +76,7 @@ describe('ObservableCollection.lastIndexOf', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 1, 2, 3], - applyOperation: collection => collection.lastIndexOf(2, 10), + applyOperation: (collection) => collection.lastIndexOf(2, 10), expectedResult: 4 }); @@ -89,7 +89,8 @@ describe('ObservableCollection.lastIndexOf', (): void => { for (const _ of observableCollection) observableCollection.lastIndexOf(2); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.length.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.length.test.ts index 1f6cb74..c76f9e0 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.length.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.length.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.length', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: collection => { + applyOperation: (collection) => { collection.length = 3; }, @@ -20,7 +20,7 @@ describe('ObservableCollection.length', (): void => { initialState: [1, 2, 3], changedProperties: ['length', 1, 2], - applyOperation: collection => { + applyOperation: (collection) => { collection.length = 1; }, @@ -35,7 +35,7 @@ describe('ObservableCollection.length', (): void => { initialState: [1, 2, 3], changedProperties: ['length', 3, 4], - applyOperation: collection => { + applyOperation: (collection) => { collection.length = 5; }, @@ -51,8 +51,9 @@ describe('ObservableCollection.length', (): void => { for (const _ of observableCollection) observableCollection.length = 10; - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); it('setting collection length to the same value while iterating does not break iterators', (): void => { @@ -62,8 +63,9 @@ describe('ObservableCollection.length', (): void => { for (const _ of observableCollection) observableCollection.length = 3; - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.map.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.map.test.ts index 233bbeb..14d4183 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.map.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.map.test.ts @@ -7,8 +7,8 @@ describe('ObservableCollection.map', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.map(item => item), - applyCollectionOperation: collection => collection.map(item => item) + applyArrayOperation: (array) => array.map((item) => item), + applyCollectionOperation: (collection) => collection.map((item) => item) }, expectedResult: [] @@ -20,8 +20,8 @@ describe('ObservableCollection.map', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.map(item => item + 10), - applyCollectionOperation: collection => collection.map(item => item + 10) + applyArrayOperation: (array) => array.map((item) => item + 10), + applyCollectionOperation: (collection) => collection.map((item) => item + 10) }, expectedResult: [11, 12, 13] @@ -34,14 +34,18 @@ describe('ObservableCollection.map', (): void => { observableCollection.map((item, index, collection) => { invocationCount++; - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return item; }); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('calling map with context passes it to the callback', (): void => { @@ -52,27 +56,33 @@ describe('ObservableCollection.map', (): void => { function (item, index, collection) { invocationCount++; - expect(this).toStrictEqual(context); - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(this) + .toStrictEqual(context); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return item; }, context ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the collection while executing map throws exception', (): void => { expect( () => { const observableCollection = new ObservableCollection([1, 2, 3]); - observableCollection.map(_ => { + observableCollection.map((_) => { observableCollection.pop(); }); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); @@ -82,8 +92,9 @@ describe('ObservableCollection.map', (): void => { const observableCollection = new ObservableCollection([1, 2, 3]); for (const _ of observableCollection) - observableCollection.map(item => item); - }) + observableCollection.map((item) => item); + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.pop.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.pop.test.ts index 0bedc90..9ce542d 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.pop.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.pop.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.pop', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.pop(), + applyOperation: (collection) => collection.pop(), expectedResult: undefined }); @@ -18,7 +18,7 @@ describe('ObservableCollection.pop', (): void => { initialState: [1, 2, 3], changedProperties: ['length', 2], - applyOperation: collection => collection.pop(), + applyOperation: (collection) => collection.pop(), expectedCollection: [1, 2], expectedResult: 3 @@ -32,8 +32,9 @@ describe('ObservableCollection.pop', (): void => { for (const _ of observableCollection) observableCollection.pop(); - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); it('popping items from empty collection while iterating does not break iterators', (): void => { @@ -45,8 +46,9 @@ describe('ObservableCollection.pop', (): void => { observableCollection.pop(); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.push.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.push.test.ts index 28863af..497d898 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.push.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.push.test.ts @@ -8,7 +8,7 @@ describe('ObservableCollection.push', (): void => { initialState: [], changedProperties: ['length', 0], - applyOperation: collection => collection.push(1), + applyOperation: (collection) => collection.push(1), expectedCollection: [1], expectedResult: 1 @@ -21,7 +21,7 @@ describe('ObservableCollection.push', (): void => { initialState: [1, 2, 3], changedProperties: ['length', 3], - applyOperation: collection => collection.push(4), + applyOperation: (collection) => collection.push(4), expectedCollection: [1, 2, 3, 4], expectedResult: 4 @@ -34,7 +34,7 @@ describe('ObservableCollection.push', (): void => { initialState: [], changedProperties: ['length', 0, 1, 2], - applyOperation: collection => collection.push(1, 2, 3), + applyOperation: (collection) => collection.push(1, 2, 3), expectedCollection: [1, 2, 3], expectedResult: 3 @@ -47,7 +47,7 @@ describe('ObservableCollection.push', (): void => { initialState: [1, 2, 3], changedProperties: ['length', 3, 4, 5], - applyOperation: collection => collection.push(4, 5, 6), + applyOperation: (collection) => collection.push(4, 5, 6), expectedCollection: [1, 2, 3, 4, 5, 6], expectedResult: 6 @@ -58,7 +58,7 @@ describe('ObservableCollection.push', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: collection => collection.push(), + applyOperation: (collection) => collection.push(), expectedResult: 3 }); @@ -68,7 +68,7 @@ describe('ObservableCollection.push', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.push(), + applyOperation: (collection) => collection.push(), expectedResult: 0 }); @@ -81,8 +81,9 @@ describe('ObservableCollection.push', (): void => { for (const _ of observableCollection) observableCollection.push(1); - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); it('not pushing items while iterating does not break iterators', (): void => { @@ -92,8 +93,9 @@ describe('ObservableCollection.push', (): void => { for (const _ of observableCollection) observableCollection.push(); - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.reduce.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.reduce.test.ts index 7fde77c..c544808 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.reduce.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.reduce.test.ts @@ -8,7 +8,8 @@ describe('ObservableCollection.reduce', (): void => { expect( () => { observableCollection.reduce((previous, current) => previous + current); - }) + } + ) .toThrow(new Error('Cannot reduce an empty collection without providing an initial value.')); }); @@ -17,8 +18,8 @@ describe('ObservableCollection.reduce', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.reduce((previous, current) => previous * 10 + current), - applyCollectionOperation: collection => collection.reduce((previous, current) => previous * 10 + current) + applyArrayOperation: (array) => array.reduce((previous, current) => (previous * 10) + current), + applyCollectionOperation: (collection) => collection.reduce((previous, current) => (previous * 10) + current) }, expectedResult: 123 @@ -32,8 +33,8 @@ describe('ObservableCollection.reduce', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.reduce(result => result, initialValue), - applyCollectionOperation: collection => collection.reduce(result => result, initialValue) + applyArrayOperation: (array) => array.reduce((result) => result, initialValue), + applyCollectionOperation: (collection) => collection.reduce((result) => result, initialValue) }, expectedResult: initialValue @@ -45,8 +46,8 @@ describe('ObservableCollection.reduce', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.reduce((result, current) => result + '0' + current.toString(), '0'), - applyCollectionOperation: collection => collection.reduce((result, current) => result + '0' + current.toString(), '0') + applyArrayOperation: (array) => array.reduce((result, current) => result + '0' + current.toString(), '0'), + applyCollectionOperation: (collection) => collection.reduce((result, current) => result + '0' + current.toString(), '0') }, expectedResult: '0010203' @@ -61,17 +62,22 @@ describe('ObservableCollection.reduce', (): void => { (result, item, index, collection) => { invocationCount++; - expect(result).toBe(initialValue); - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(result) + .toBe(initialValue); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return item; }, initialValue ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the collection while executing reduce throws exception', (): void => { @@ -80,9 +86,11 @@ describe('ObservableCollection.reduce', (): void => { const observableCollection = new ObservableCollection([1, 2, 3]); observableCollection.reduce((previous, current) => { observableCollection.pop(); + return previous + current; }); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); @@ -93,7 +101,8 @@ describe('ObservableCollection.reduce', (): void => { for (const _ of observableCollection) observableCollection.reduce((previous, current) => previous + current); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.reduceRight.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.reduceRight.test.ts index 4bfd75b..decabc6 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.reduceRight.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.reduceRight.test.ts @@ -8,7 +8,8 @@ describe('ObservableCollection.reduceRight', (): void => { expect( () => { observableCollection.reduceRight((previous, current) => previous + current); - }) + } + ) .toThrow(new Error('Cannot reduce an empty collection without providing an initial value.')); }); @@ -17,8 +18,8 @@ describe('ObservableCollection.reduceRight', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.reduceRight((previous, current) => previous * 10 + current), - applyCollectionOperation: collection => collection.reduceRight((previous, current) => previous * 10 + current) + applyArrayOperation: (array) => array.reduceRight((previous, current) => (previous * 10) + current), + applyCollectionOperation: (collection) => collection.reduceRight((previous, current) => (previous * 10) + current) }, expectedResult: 321 @@ -32,8 +33,8 @@ describe('ObservableCollection.reduceRight', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.reduceRight(result => result, initialValue), - applyCollectionOperation: collection => collection.reduceRight(result => result, initialValue) + applyArrayOperation: (array) => array.reduceRight((result) => result, initialValue), + applyCollectionOperation: (collection) => collection.reduceRight((result) => result, initialValue) }, expectedResult: initialValue @@ -45,8 +46,8 @@ describe('ObservableCollection.reduceRight', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.reduceRight((result, current) => result + '0' + current.toString(), '0'), - applyCollectionOperation: collection => collection.reduceRight((result, current) => result + '0' + current.toString(), '0') + applyArrayOperation: (array) => array.reduceRight((result, current) => result + '0' + current.toString(), '0'), + applyCollectionOperation: (collection) => collection.reduceRight((result, current) => result + '0' + current.toString(), '0') }, expectedResult: '0030201' @@ -61,17 +62,22 @@ describe('ObservableCollection.reduceRight', (): void => { (result, item, index, collection) => { invocationCount++; - expect(result).toBe(initialValue); - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(result) + .toBe(initialValue); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return item; }, initialValue ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the collection while executing reduceRight throws exception', (): void => { @@ -80,20 +86,23 @@ describe('ObservableCollection.reduceRight', (): void => { const observableCollection = new ObservableCollection([1, 2, 3]); observableCollection.reduceRight((previous, current) => { observableCollection.pop(); + return previous + current; }); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); it('calling reduceRight while iterating does not break iterators', (): void => { expect( () => { - const observableCollection = new ObservableCollection([1, 2, 3]); + const observableCollection = new ObservableCollection([1, 2, 3]); for (const _ of observableCollection) observableCollection.reduceRight((previous, current) => previous + current); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.reverse.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.reverse.test.ts index e966a5f..f593162 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.reverse.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.reverse.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.reverse', (): void => { testBlankReorderingOperation({ initialState: [], - applyOperation: collection => collection.reverse(), + applyOperation: (collection) => collection.reverse(), expectedResult: selfResult }); @@ -16,7 +16,7 @@ describe('ObservableCollection.reverse', (): void => { testBlankReorderingOperation({ initialState: [1], - applyOperation: collection => collection.reverse(), + applyOperation: (collection) => collection.reverse(), expectedResult: selfResult }); @@ -28,7 +28,7 @@ describe('ObservableCollection.reverse', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8], changedProperties: [0, 1, 2, 3, 4, 5, 6, 7], - applyOperation: collection => collection.reverse(), + applyOperation: (collection) => collection.reverse(), expectedResult: selfResult, expectedCollection: [8, 7, 6, 5, 4, 3, 2, 1] @@ -41,7 +41,7 @@ describe('ObservableCollection.reverse', (): void => { initialState: [1, 2, 3, 4, 5], changedProperties: [0, 1, 3, 4], - applyOperation: collection => collection.reverse(), + applyOperation: (collection) => collection.reverse(), expectedResult: selfResult, expectedCollection: [5, 4, 3, 2, 1] @@ -55,8 +55,9 @@ describe('ObservableCollection.reverse', (): void => { for (const _ of observableCollection) observableCollection.reverse(); - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); it('reversing an empty collection while iterating does not break iterators', (): void => { @@ -68,9 +69,10 @@ describe('ObservableCollection.reverse', (): void => { observableCollection.reverse(); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); it('reversing a collection with one item while iterating does not break iterators', (): void => { @@ -82,8 +84,9 @@ describe('ObservableCollection.reverse', (): void => { observableCollection.reverse(); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.set.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.set.test.ts index 393d944..a0d43cb 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.set.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.set.test.ts @@ -11,9 +11,10 @@ describe('ObservableCollection.set', (): void => { applyOperation: { applyArrayOperation(array) { array[1] = 10; + return array.length; }, - applyCollectionOperation: collection => collection.set(1, 10) + applyCollectionOperation: (collection) => collection.set(1, 10) }, expectedCollection: [1, 10, 3], @@ -30,9 +31,10 @@ describe('ObservableCollection.set', (): void => { applyOperation: { applyArrayOperation(array) { array[2] = 10; + return array.length; }, - applyCollectionOperation: collection => collection.set(-1, 10) + applyCollectionOperation: (collection) => collection.set(-1, 10) }, expectedCollection: [1, 2, 10], @@ -49,9 +51,10 @@ describe('ObservableCollection.set', (): void => { applyOperation: { applyArrayOperation(array) { array[0] = 10; + return array.length; }, - applyCollectionOperation: collection => collection.set(-10, 10) + applyCollectionOperation: (collection) => collection.set(-10, 10) }, expectedCollection: [10, 2, 3], @@ -68,9 +71,10 @@ describe('ObservableCollection.set', (): void => { applyOperation: { applyArrayOperation(array) { array[10] = 100; + return array.length; }, - applyCollectionOperation: collection => collection.set(10, 100) + applyCollectionOperation: (collection) => collection.set(10, 100) }, expectedCollection: [1, 2, 3, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 100], @@ -85,7 +89,8 @@ describe('ObservableCollection.set', (): void => { for (const _ of observableCollection) observableCollection.set(1, 10); - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); }); \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.shift.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.shift.test.ts index c9fee05..37b62e7 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.shift.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.shift.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.shift', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.shift(), + applyOperation: (collection) => collection.shift(), expectedResult: undefined }); @@ -18,7 +18,7 @@ describe('ObservableCollection.shift', (): void => { initialState: [1, 2, 3], changedProperties: ['length', 0, 1, 2], - applyOperation: collection => collection.shift(), + applyOperation: (collection) => collection.shift(), expectedCollection: [2, 3], expectedResult: 1 @@ -32,8 +32,9 @@ describe('ObservableCollection.shift', (): void => { for (const _ of observableCollection) observableCollection.shift(); - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); it('shifting items from empty collection while iterating does not break iterators', (): void => { @@ -45,8 +46,9 @@ describe('ObservableCollection.shift', (): void => { observableCollection.shift(); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.slice.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.slice.test.ts index b98d044..ded1766 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.slice.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.slice.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.slice', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.slice(), + applyOperation: (collection) => collection.slice(), expectedResult: [] }); @@ -16,7 +16,7 @@ describe('ObservableCollection.slice', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: collection => collection.slice(), + applyOperation: (collection) => collection.slice(), expectedResult: [1, 2, 3] }); @@ -26,7 +26,7 @@ describe('ObservableCollection.slice', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.slice(2), + applyOperation: (collection) => collection.slice(2), expectedResult: [3, 4, 5] }); @@ -36,7 +36,7 @@ describe('ObservableCollection.slice', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.slice(-3), + applyOperation: (collection) => collection.slice(-3), expectedResult: [3, 4, 5] }); @@ -46,7 +46,7 @@ describe('ObservableCollection.slice', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.slice(-10), + applyOperation: (collection) => collection.slice(-10), expectedResult: [1, 2, 3, 4, 5] }); @@ -56,7 +56,7 @@ describe('ObservableCollection.slice', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.slice(1, 3), + applyOperation: (collection) => collection.slice(1, 3), expectedResult: [2, 3] }); @@ -66,7 +66,7 @@ describe('ObservableCollection.slice', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.slice(2, -1), + applyOperation: (collection) => collection.slice(2, -1), expectedResult: [3, 4] }); @@ -76,7 +76,7 @@ describe('ObservableCollection.slice', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.slice(2, 10), + applyOperation: (collection) => collection.slice(2, 10), expectedResult: [3, 4, 5] }); @@ -86,7 +86,7 @@ describe('ObservableCollection.slice', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.slice(3, 2), + applyOperation: (collection) => collection.slice(3, 2), expectedResult: [] }); @@ -99,7 +99,8 @@ describe('ObservableCollection.slice', (): void => { for (const _ of observableCollection) observableCollection.slice(); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.some.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.some.test.ts index d337795..899de84 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.some.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.some.test.ts @@ -7,8 +7,8 @@ describe('ObservableCollection.some', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.some(_ => true), - applyCollectionOperation: collection => collection.some(_ => true) + applyArrayOperation: (array) => array.some((_) => true), + applyCollectionOperation: (collection) => collection.some((_) => true) }, expectedResult: true @@ -20,8 +20,8 @@ describe('ObservableCollection.some', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.some(item => item === 3), - applyCollectionOperation: collection => collection.some(item => item === 3) + applyArrayOperation: (array) => array.some((item) => item === 3), + applyCollectionOperation: (collection) => collection.some((item) => item === 3) }, expectedResult: true @@ -33,8 +33,8 @@ describe('ObservableCollection.some', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.some(item => item < 0), - applyCollectionOperation: collection => collection.some(item => item < 0) + applyArrayOperation: (array) => array.some((item) => item < 0), + applyCollectionOperation: (collection) => collection.some((item) => item < 0) }, expectedResult: false @@ -47,14 +47,18 @@ describe('ObservableCollection.some', (): void => { observableCollection.some((item, index, collection) => { invocationCount++; - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('calling some with context passes it to the callback', (): void => { @@ -65,28 +69,35 @@ describe('ObservableCollection.some', (): void => { function (item, index, collection) { invocationCount++; - expect(this).toStrictEqual(context); - expect(item).toBe(1); - expect(index).toBe(0); - expect(collection).toStrictEqual(observableCollection); + expect(this) + .toStrictEqual(context); + expect(item) + .toBe(1); + expect(index) + .toBe(0); + expect(collection) + .toStrictEqual(observableCollection); return true; }, context ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the collection while executing some throws exception', (): void => { expect( () => { const observableCollection = new ObservableCollection([1, 2, 3]); - observableCollection.some(_ => { + observableCollection.some((_) => { observableCollection.pop(); + return true; }); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); @@ -96,8 +107,9 @@ describe('ObservableCollection.some', (): void => { const observableCollection = new ObservableCollection([1, 2, 3]); for (const _ of observableCollection) - observableCollection.some(_ => true); - }) + observableCollection.some((_) => true); + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.sort.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.sort.test.ts index acf16fe..84dde24 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.sort.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.sort.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.sort', (): void => { testBlankReorderingOperation({ initialState: [], - applyOperation: collection => collection.sort(), + applyOperation: (collection) => collection.sort(), expectedResult: selfResult }); @@ -16,7 +16,7 @@ describe('ObservableCollection.sort', (): void => { testBlankReorderingOperation({ initialState: [1], - applyOperation: collection => collection.sort(), + applyOperation: (collection) => collection.sort(), expectedResult: selfResult }); @@ -28,7 +28,7 @@ describe('ObservableCollection.sort', (): void => { initialState: [1, undefined, 2, 3, -1, undefined, 3, 100, null, 22, 11, 200, -100], changedProperties: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], - applyOperation: collection => collection.sort(), + applyOperation: (collection) => collection.sort(), expectedResult: selfResult, expectedCollection: [-1, -100, 1, 100, 11, 2, 200, 22, 3, 3, null, undefined, undefined] @@ -42,8 +42,8 @@ describe('ObservableCollection.sort', (): void => { changedProperties: [1, 2, 3], applyOperation: { - applyArrayOperation: collection => collection.sort((left, right) => left! - right!), - applyCollectionOperation: collection => collection.sort((left, right) => left - right) + applyArrayOperation: (collection) => collection.sort((left, right) => left! - right!), + applyCollectionOperation: (collection) => collection.sort((left, right) => left - right) }, expectedResult: selfResult, @@ -57,7 +57,7 @@ describe('ObservableCollection.sort', (): void => { initialState: [1, 4, 3, 2, 5], changedProperties: [1, 3], - applyOperation: collection => collection.sort(), + applyOperation: (collection) => collection.sort(), expectedResult: selfResult, expectedCollection: [1, 2, 3, 4, 5] @@ -70,10 +70,12 @@ describe('ObservableCollection.sort', (): void => { const observableCollection = new ObservableCollection([1, 2]); observableCollection.sort(() => { observableCollection.pop(); + return 0; }); - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); it('sorting sorted items while iterating does not break iterators', (): void => { @@ -83,9 +85,10 @@ describe('ObservableCollection.sort', (): void => { for (const _ of observableCollection) observableCollection.sort(); - }) + } + ) .not - .toThrow() + .toThrow(); }); it('sorting unsorted items while iterating breaks iterators', (): void => { @@ -95,8 +98,9 @@ describe('ObservableCollection.sort', (): void => { for (const _ of observableCollection) observableCollection.sort(); - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); it('sorting an empty collection while iterating does not break iterators', (): void => { @@ -108,9 +112,10 @@ describe('ObservableCollection.sort', (): void => { observableCollection.sort(); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); it('sorting a collection with one item while iterating does not break iterators', (): void => { @@ -122,8 +127,9 @@ describe('ObservableCollection.sort', (): void => { observableCollection.sort(); iterator.next(); - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.splice.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.splice.test.ts index 52f9221..ae4edfe 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.splice.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.splice.test.ts @@ -6,7 +6,7 @@ describe('ObservableCollection.splice', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.splice(0, 2), + applyOperation: (collection) => collection.splice(0, 2), expectedResult: [] }); @@ -18,7 +18,7 @@ describe('ObservableCollection.splice', (): void => { initialState: [1, 2, 3, 4, 5], changedProperties: ['length', 2, 3, 4], - applyOperation: collection => collection.splice(2), + applyOperation: (collection) => collection.splice(2), expectedCollection: [1, 2], expectedResult: [3, 4, 5] @@ -31,7 +31,7 @@ describe('ObservableCollection.splice', (): void => { initialState: [1, 2, 3, 4, 5], changedProperties: ['length', 3, 4], - applyOperation: collection => collection.splice(-2), + applyOperation: (collection) => collection.splice(-2), expectedCollection: [1, 2, 3], expectedResult: [4, 5] @@ -44,7 +44,7 @@ describe('ObservableCollection.splice', (): void => { initialState: [1, 2, 3, 4, 5], changedProperties: ['length', 0, 1, 2, 3, 4], - applyOperation: collection => collection.splice(-10), + applyOperation: (collection) => collection.splice(-10), expectedCollection: [], expectedResult: [1, 2, 3, 4, 5] @@ -55,7 +55,7 @@ describe('ObservableCollection.splice', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5], - applyOperation: collection => collection.splice(10), + applyOperation: (collection) => collection.splice(10), expectedResult: [] }); @@ -67,7 +67,7 @@ describe('ObservableCollection.splice', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], changedProperties: ['length', 2, 3, 4, 5, 6, 7, 8], - applyOperation: collection => collection.splice(2, 3), + applyOperation: (collection) => collection.splice(2, 3), expectedCollection: [1, 2, 6, 7, 8, 9], expectedResult: [3, 4, 5] @@ -80,7 +80,7 @@ describe('ObservableCollection.splice', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], changedProperties: ['length', 2, 3, 4, 5, 6, 7, 8], - applyOperation: collection => collection.splice(2, 10), + applyOperation: (collection) => collection.splice(2, 10), expectedCollection: [1, 2], expectedResult: [3, 4, 5, 6, 7, 8, 9] @@ -91,7 +91,7 @@ describe('ObservableCollection.splice', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], - applyOperation: collection => collection.splice(2, -2), + applyOperation: (collection) => collection.splice(2, -2), expectedResult: [] }); @@ -103,7 +103,7 @@ describe('ObservableCollection.splice', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], changedProperties: [2, 3, 4], - applyOperation: collection => collection.splice(2, 3, 10, 20, 30), + applyOperation: (collection) => collection.splice(2, 3, 10, 20, 30), expectedCollection: [1, 2, 10, 20, 30, 6, 7, 8, 9], expectedResult: [3, 4, 5] @@ -119,13 +119,14 @@ describe('ObservableCollection.splice', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], changedProperties: ['length', 2, 3, 4, 5, 6, 7, 8], - applyOperation: collection => collection.splice(2, 3, 10), + applyOperation: (collection) => collection.splice(2, 3, 10), expectedCollection: [1, 2, 10, 6, 7, 8, 9], expectedResult: [3, 4, 5] }); - expect(collectionItems).toEqual(arrayItems); + expect(collectionItems) + .toEqual(arrayItems); }); it('splicing a collection using start, delete count and more replacement items updates the collection', (): void => { @@ -134,7 +135,7 @@ describe('ObservableCollection.splice', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], changedProperties: ['length', 2, 3, 4, 5, 6, 7, 8, 9, 10], - applyOperation: collection => collection.splice(2, 3, 10, 20, 30, 40, 50), + applyOperation: (collection) => collection.splice(2, 3, 10, 20, 30, 40, 50), expectedCollection: [1, 2, 10, 20, 30, 40, 50, 6, 7, 8, 9], expectedResult: [3, 4, 5] @@ -147,7 +148,7 @@ describe('ObservableCollection.splice', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], changedProperties: ['length', 2, 3, 4, 5, 6, 7, 8], - applyOperation: collection => collection.splice(2, 10, 100, 200, 300, 400, 500), + applyOperation: (collection) => collection.splice(2, 10, 100, 200, 300, 400, 500), expectedCollection: [1, 2, 100, 200, 300, 400, 500], expectedResult: [3, 4, 5, 6, 7, 8, 9] @@ -160,7 +161,7 @@ describe('ObservableCollection.splice', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], changedProperties: ['length', 9, 10, 11, 12, 13], - applyOperation: collection => collection.splice(20, 2, 100, 200, 300, 400, 500), + applyOperation: (collection) => collection.splice(20, 2, 100, 200, 300, 400, 500), expectedCollection: [1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 200, 300, 400, 500], expectedResult: [] @@ -174,7 +175,8 @@ describe('ObservableCollection.splice', (): void => { for (const _ of observableCollection) observableCollection.splice(1); - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); }); \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.toReversed.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.toReversed.test.ts index 74ca033..6eeab8e 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.toReversed.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.toReversed.test.ts @@ -7,8 +7,9 @@ describe('ObservableCollection.toReversed', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.slice().reverse(), - applyCollectionOperation: collection => collection.toReversed() + applyArrayOperation: (array) => array.slice() + .reverse(), + applyCollectionOperation: (collection) => collection.toReversed() }, expectedResult: [] @@ -20,8 +21,9 @@ describe('ObservableCollection.toReversed', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => array.slice().reverse(), - applyCollectionOperation: collection => collection.toReversed() + applyArrayOperation: (array) => array.slice() + .reverse(), + applyCollectionOperation: (collection) => collection.toReversed() }, expectedResult: [3, 2, 1] @@ -35,7 +37,8 @@ describe('ObservableCollection.toReversed', (): void => { for (const _ of observableCollection) observableCollection.toReversed(); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.toSorted.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.toSorted.test.ts index 568bb8f..8450317 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.toSorted.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.toSorted.test.ts @@ -7,8 +7,9 @@ describe('ObservableCollection.toSorted', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => array.slice().sort(), - applyCollectionOperation: collection => collection.toSorted() + applyArrayOperation: (array) => array.slice() + .sort(), + applyCollectionOperation: (collection) => collection.toSorted() }, expectedResult: [] @@ -20,8 +21,9 @@ describe('ObservableCollection.toSorted', (): void => { initialState: [1, undefined, 2, 3, -1, undefined, 3, 100, null, 22, 11, 200, -100], applyOperation: { - applyArrayOperation: array => array.slice().sort(), - applyCollectionOperation: collection => collection.toSorted() + applyArrayOperation: (array) => array.slice() + .sort(), + applyCollectionOperation: (collection) => collection.toSorted() }, expectedResult: [-1, -100, 1, 100, 11, 2, 200, 22, 3, 3, null, undefined, undefined] @@ -33,8 +35,9 @@ describe('ObservableCollection.toSorted', (): void => { initialState: [1, undefined, 2, 3], applyOperation: { - applyArrayOperation: array => array.slice().sort((left, right) => left! - right!), - applyCollectionOperation: collection => collection.toSorted((left, right) => left - right) + applyArrayOperation: (array) => array.slice() + .sort((left, right) => left! - right!), + applyCollectionOperation: (collection) => collection.toSorted((left, right) => left - right) }, expectedResult: [1, 2, 3, undefined] @@ -47,10 +50,12 @@ describe('ObservableCollection.toSorted', (): void => { const observableCollection = new ObservableCollection([1, 2]); observableCollection.toSorted(() => { observableCollection.pop(); + return 0; }); - }) - .toThrow(new Error('Collection has changed while being iterated.')) + } + ) + .toThrow(new Error('Collection has changed while being iterated.')); }); it('calling toSorted while iterating does not break iterators', (): void => { @@ -60,7 +65,8 @@ describe('ObservableCollection.toSorted', (): void => { for (const _ of observableCollection) observableCollection.toSorted(); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.toSpliced.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.toSpliced.test.ts index 18b6707..12a114f 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.toSpliced.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.toSpliced.test.ts @@ -7,12 +7,13 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(0, 2); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(0, 2) + applyCollectionOperation: (collection) => collection.toSpliced(0, 2) }, expectedResult: [] @@ -24,12 +25,13 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(2); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(2) + applyCollectionOperation: (collection) => collection.toSpliced(2) }, expectedResult: [1, 2] @@ -41,12 +43,13 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(-2); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(-2) + applyCollectionOperation: (collection) => collection.toSpliced(-2) }, expectedResult: [1, 2, 3] @@ -58,12 +61,13 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(-10); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(-10) + applyCollectionOperation: (collection) => collection.toSpliced(-10) }, expectedResult: [] @@ -75,12 +79,13 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(10); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(10) + applyCollectionOperation: (collection) => collection.toSpliced(10) }, expectedResult: [1, 2, 3, 4, 5] @@ -92,12 +97,13 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(2, 3); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(2, 3) + applyCollectionOperation: (collection) => collection.toSpliced(2, 3) }, expectedResult: [1, 2, 6, 7, 8, 9] @@ -109,12 +115,13 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(2, 10); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(2, 10) + applyCollectionOperation: (collection) => collection.toSpliced(2, 10) }, expectedResult: [1, 2] @@ -126,15 +133,16 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(2, -2); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(2, -2) + applyCollectionOperation: (collection) => collection.toSpliced(2, -2) }, - expectedResult: [1, 2, 3, 4, 5, 6, 7, 8, 9], + expectedResult: [1, 2, 3, 4, 5, 6, 7, 8, 9] }); }); @@ -143,15 +151,16 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(2, 3, 10, 20, 30); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(2, 3, 10, 20, 30) + applyCollectionOperation: (collection) => collection.toSpliced(2, 3, 10, 20, 30) }, - expectedResult: [1, 2, 10, 20, 30, 6, 7, 8, 9], + expectedResult: [1, 2, 10, 20, 30, 6, 7, 8, 9] }); }); @@ -160,15 +169,16 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(2, 3, 10); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(2, 3, 10) + applyCollectionOperation: (collection) => collection.toSpliced(2, 3, 10) }, - expectedResult: [1, 2, 10, 6, 7, 8, 9], + expectedResult: [1, 2, 10, 6, 7, 8, 9] }); }); @@ -177,15 +187,16 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(2, 3, 10, 20, 30, 40, 50); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(2, 3, 10, 20, 30, 40, 50) + applyCollectionOperation: (collection) => collection.toSpliced(2, 3, 10, 20, 30, 40, 50) }, - expectedResult: [1, 2, 10, 20, 30, 40, 50, 6, 7, 8, 9], + expectedResult: [1, 2, 10, 20, 30, 40, 50, 6, 7, 8, 9] }); }); @@ -194,15 +205,16 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); - copy.splice(2, 10, 100, 200, 300, 400, 500) + copy.splice(2, 10, 100, 200, 300, 400, 500); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(2, 10, 100, 200, 300, 400, 500) + applyCollectionOperation: (collection) => collection.toSpliced(2, 10, 100, 200, 300, 400, 500) }, - expectedResult: [1, 2, 100, 200, 300, 400, 500], + expectedResult: [1, 2, 100, 200, 300, 400, 500] }); }); @@ -211,15 +223,16 @@ describe('ObservableCollection.toSpliced', (): void => { initialState: [1, 2, 3, 4, 5, 6, 7, 8, 9], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy.splice(20, 2, 100, 200, 300, 400, 500); + return copy; }, - applyCollectionOperation: collection => collection.toSpliced(20, 2, 100, 200, 300, 400, 500) + applyCollectionOperation: (collection) => collection.toSpliced(20, 2, 100, 200, 300, 400, 500) }, - expectedResult: [1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 200, 300, 400, 500], + expectedResult: [1, 2, 3, 4, 5, 6, 7, 8, 9, 100, 200, 300, 400, 500] }); }); @@ -230,7 +243,8 @@ describe('ObservableCollection.toSpliced', (): void => { for (const _ of observableCollection) observableCollection.toSpliced(1); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.unshift.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.unshift.test.ts index c20008d..a6a2423 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.unshift.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.unshift.test.ts @@ -8,7 +8,7 @@ describe('ObservableCollection.unshift', (): void => { initialState: [], changedProperties: ['length', 0], - applyOperation: collection => collection.unshift(1), + applyOperation: (collection) => collection.unshift(1), expectedCollection: [1], expectedResult: 1 @@ -21,7 +21,7 @@ describe('ObservableCollection.unshift', (): void => { initialState: [1, 2, 3], changedProperties: ['length', 0, 1, 2, 3], - applyOperation: collection => collection.unshift(4), + applyOperation: (collection) => collection.unshift(4), expectedCollection: [4, 1, 2, 3], expectedResult: 4 @@ -34,7 +34,7 @@ describe('ObservableCollection.unshift', (): void => { initialState: [], changedProperties: ['length', 0, 1, 2], - applyOperation: collection => collection.unshift(1, 2, 3), + applyOperation: (collection) => collection.unshift(1, 2, 3), expectedCollection: [1, 2, 3], expectedResult: 3 @@ -47,7 +47,7 @@ describe('ObservableCollection.unshift', (): void => { initialState: [1, 2, 3], changedProperties: ['length', 0, 1, 2, 3, 4, 5], - applyOperation: collection => collection.unshift(4, 5, 6), + applyOperation: (collection) => collection.unshift(4, 5, 6), expectedCollection: [4, 5, 6, 1, 2, 3], expectedResult: 6 @@ -58,7 +58,7 @@ describe('ObservableCollection.unshift', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: collection => collection.unshift(), + applyOperation: (collection) => collection.unshift(), expectedResult: 3 }); @@ -68,7 +68,7 @@ describe('ObservableCollection.unshift', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: collection => collection.unshift(), + applyOperation: (collection) => collection.unshift(), expectedResult: 0 }); @@ -81,7 +81,8 @@ describe('ObservableCollection.unshift', (): void => { for (const _ of observableCollection) observableCollection.unshift(1); - }) + } + ) .toThrow(new Error('Collection has changed while being iterated.')); }); @@ -92,7 +93,8 @@ describe('ObservableCollection.unshift', (): void => { for (const _ of observableCollection) observableCollection.unshift(); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/ObservableCollection.with.test.ts b/src/collections/observableCollections/__tests__/ObservableCollection.with.test.ts index 746fb4a..03984db 100644 --- a/src/collections/observableCollections/__tests__/ObservableCollection.with.test.ts +++ b/src/collections/observableCollections/__tests__/ObservableCollection.with.test.ts @@ -7,13 +7,13 @@ describe('ObservableCollection.with', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy[2] = 10; return copy; }, - applyCollectionOperation: collection => collection.with(2, 10) + applyCollectionOperation: (collection) => collection.with(2, 10) }, expectedResult: [1, 2, 10] @@ -25,13 +25,13 @@ describe('ObservableCollection.with', (): void => { initialState: [1, 2, 3], applyOperation: { - applyArrayOperation: array => { + applyArrayOperation: (array) => { const copy = array.slice(); copy[1] = 10; return copy; }, - applyCollectionOperation: collection => collection.with(-2, 10) + applyCollectionOperation: (collection) => collection.with(-2, 10) }, expectedResult: [1, 10, 3] @@ -43,7 +43,8 @@ describe('ObservableCollection.with', (): void => { () => { const observableCollection = new ObservableCollection([1, 2, 3]); observableCollection.with(3, 10); - }) + } + ) .toThrow(new RangeError('The provided index \'3\' is outside the bounds of the collection.')); }); @@ -52,7 +53,8 @@ describe('ObservableCollection.with', (): void => { () => { const observableCollection = new ObservableCollection([1, 2, 3]); observableCollection.with(-4, 10); - }) + } + ) .toThrow(new RangeError('The provided index \'-4\' is outside the bounds of the collection.')); }); @@ -66,7 +68,8 @@ describe('ObservableCollection.with', (): void => { observableCollection.with(index, 10); index++; } - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableCollections/__tests__/common/expectCollectionsToBeEqual.ts b/src/collections/observableCollections/__tests__/common/expectCollectionsToBeEqual.ts index a89a935..da25a42 100644 --- a/src/collections/observableCollections/__tests__/common/expectCollectionsToBeEqual.ts +++ b/src/collections/observableCollections/__tests__/common/expectCollectionsToBeEqual.ts @@ -1,11 +1,15 @@ import type { IReadOnlyObservableCollection } from '../../IReadOnlyObservableCollection'; export function expectCollectionsToBeEqual(observableCollection: IReadOnlyObservableCollection, array: readonly TItem[]): void { - expect(observableCollection.length).toBe(array.length); - expect(observableCollection.toArray()).toEqual(array); + expect(observableCollection.length) + .toBe(array.length); + expect(observableCollection.toArray()) + .toEqual(array); for (let index = 0; index < observableCollection.length; index++) { - expect(observableCollection[index]).toBe(array[index]); - expect(observableCollection.at(index)).toBe(array[index]); + expect(observableCollection[index]) + .toBe(array[index]); + expect(observableCollection.at(index)) + .toBe(array[index]); } expectIndexesToBeDefined(observableCollection); @@ -14,13 +18,18 @@ export function expectCollectionsToBeEqual(observableCollection: IReadOnl } function expectIndexesToBeDefined(observableCollection: IReadOnlyObservableCollection): void { - expect(-1 in observableCollection).toBe(false); - expect(observableCollection.length in observableCollection).toBe(false); - expect((observableCollection.length + 1) in observableCollection).toBe(false); + expect(-1 in observableCollection) + .toBe(false); + expect(observableCollection.length in observableCollection) + .toBe(false); + expect((observableCollection.length + 1) in observableCollection) + .toBe(false); for (let index = 0; index < observableCollection.length; index++) { - expect(index in observableCollection).toBe(true); - expect(observableCollection[index]).toStrictEqual(observableCollection.at(index)); + expect(index in observableCollection) + .toBe(true); + expect(observableCollection[index]) + .toStrictEqual(observableCollection.at(index)); } } @@ -33,11 +42,15 @@ function expectIterationsToBeEqual(observableCollection: IReadOnlyObserva for (const item of array) arrayIterationResult.push(item); - expect(observableCollectionIterationResult).toEqual(arrayIterationResult); + expect(observableCollectionIterationResult) + .toEqual(arrayIterationResult); } function expectRelatedIteratorsToBeEqual(observableCollection: IReadOnlyObservableCollection, array: readonly TItem[]): void { - expect(Array.from(observableCollection.keys())).toEqual(Array.from(array.keys())); - expect(Array.from(observableCollection.entries())).toEqual(Array.from(array.entries())); - expect(Array.from(observableCollection.values())).toEqual(Array.from(array.values())); + expect(Array.from(observableCollection.keys())) + .toEqual(Array.from(array.keys())); + expect(Array.from(observableCollection.entries())) + .toEqual(Array.from(array.entries())); + expect(Array.from(observableCollection.values())) + .toEqual(Array.from(array.values())); } \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/common/testBlankMutatingOperation.ts b/src/collections/observableCollections/__tests__/common/testBlankMutatingOperation.ts index a433fb9..8082ef4 100644 --- a/src/collections/observableCollections/__tests__/common/testBlankMutatingOperation.ts +++ b/src/collections/observableCollections/__tests__/common/testBlankMutatingOperation.ts @@ -9,7 +9,7 @@ import { selfResult } from './selfResult'; export interface ITestBlankMutatingOperationOptions { readonly initialState: readonly TItem[]; - readonly applyOperation: ((collection: TItem[] | IObservableCollection) => unknown) | { + readonly applyOperation: ((collection: TItem[] | IObservableCollection)=> unknown) | { applyArrayOperation(array: TItem[]): unknown; applyCollectionOperation(colleciton: IObservableCollection): unknown; }; @@ -61,14 +61,22 @@ export function testBlankMutatingOperation({ initialState, applyOperation const observableCollectionResult = typeof applyOperation === 'function' ? applyOperation(observableCollectionAfterOperation) : applyOperation.applyCollectionOperation(observableCollectionAfterOperation); expectCollectionsToBeEqual(observableCollectionAfterOperation, initialState); - expect(observableCollectionResult).toEqual(expectedResult === selfResult ? observableCollectionAfterOperation : expectedResult); + expect(observableCollectionResult) + .toEqual(expectedResult === selfResult ? observableCollectionAfterOperation : expectedResult); - expect(collectionChangedRaiseCount).toBe(0); - expect(propertiesChangedRaiseCount).toBe(0); + expect(collectionChangedRaiseCount) + .toBe(0); + expect(collectionReorderedRaiseCount) + .toBe(0); + expect(propertiesChangedRaiseCount) + .toBe(0); expectCollectionsToBeEqual(observableCollectionAfterOperation, arrayAfterOperation); - expect(observableCollectionResult).toEqual(expectedResult === selfResult ? observableCollectionAfterOperation : arrayResult); + expect(observableCollectionResult) + .toEqual(expectedResult === selfResult ? observableCollectionAfterOperation : arrayResult); - expect(arrayAfterOperation).toEqual(arrayBeforeOperation); - expect(observableCollectionAfterOperation).toEqual(observableCollectionBeforeOperation); + expect(arrayAfterOperation) + .toEqual(arrayBeforeOperation); + expect(observableCollectionAfterOperation) + .toEqual(observableCollectionBeforeOperation); } \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/common/testBlankReorderingOperation.ts b/src/collections/observableCollections/__tests__/common/testBlankReorderingOperation.ts index 73c9961..ff891ab 100644 --- a/src/collections/observableCollections/__tests__/common/testBlankReorderingOperation.ts +++ b/src/collections/observableCollections/__tests__/common/testBlankReorderingOperation.ts @@ -9,7 +9,7 @@ import { selfResult } from './selfResult'; export interface ITestBlankReorderingOperationOptions { readonly initialState: readonly TItem[]; - readonly applyOperation: ((collection: TItem[] | IObservableCollection) => unknown) | { + readonly applyOperation: ((collection: TItem[] | IObservableCollection)=> unknown) | { applyArrayOperation(array: TItem[]): unknown; applyCollectionOperation(colleciton: IObservableCollection): unknown; }; @@ -61,14 +61,22 @@ export function testBlankReorderingOperation({ initialState, applyOperati const observableCollectionResult = typeof applyOperation === 'function' ? applyOperation(observableCollectionAfterOperation) : applyOperation.applyCollectionOperation(observableCollectionAfterOperation); expectCollectionsToBeEqual(observableCollectionAfterOperation, initialState); - expect(observableCollectionResult).toEqual(expectedResult === selfResult ? observableCollectionAfterOperation : expectedResult); + expect(observableCollectionResult) + .toEqual(expectedResult === selfResult ? observableCollectionAfterOperation : expectedResult); - expect(collectionChangedRaiseCount).toBe(0); - expect(propertiesChangedRaiseCount).toBe(0); + expect(collectionChangedRaiseCount) + .toBe(0); + expect(collectionReorderedRaiseCount) + .toBe(0); + expect(propertiesChangedRaiseCount) + .toBe(0); expectCollectionsToBeEqual(observableCollectionAfterOperation, arrayAfterOperation); - expect(arrayResult).toEqual(expectedResult === selfResult ? arrayAfterOperation : expectedResult); + expect(arrayResult) + .toEqual(expectedResult === selfResult ? arrayAfterOperation : expectedResult); - expect(arrayAfterOperation).toEqual(arrayBeforeOperation); - expect(observableCollectionAfterOperation).toEqual(observableCollectionBeforeOperation); + expect(arrayAfterOperation) + .toEqual(arrayBeforeOperation); + expect(observableCollectionAfterOperation) + .toEqual(observableCollectionBeforeOperation); } \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/common/testMutatingOperation.ts b/src/collections/observableCollections/__tests__/common/testMutatingOperation.ts index f53c7e1..8ed16ac 100644 --- a/src/collections/observableCollections/__tests__/common/testMutatingOperation.ts +++ b/src/collections/observableCollections/__tests__/common/testMutatingOperation.ts @@ -9,10 +9,10 @@ export interface ITestMutatingOperationOptions { readonly initialState: readonly TItem[]; readonly changedProperties: readonly ('length' | number)[]; - readonly applyOperation: ((collection: TItem[] | IObservableCollection) => unknown) | { + readonly applyOperation: ((collection: TItem[] | IObservableCollection)=> unknown) | { applyArrayOperation(array: TItem[]): unknown; applyCollectionOperation(colleciton: IObservableCollection): unknown; - } + }; readonly expectedCollection: readonly TItem[]; readonly expectedResult: unknown; @@ -40,7 +40,8 @@ export function testMutatingOperation({ collectionOperation, initialState handle(subject, changedProperties) { propertiesChangedRaiseCount++; - expect(subject).toStrictEqual(observableCollection); + expect(subject) + .toStrictEqual(observableCollection); actualChangedProperties = changedProperties; } }); @@ -48,14 +49,17 @@ export function testMutatingOperation({ collectionOperation, initialState handle(subject, { operation, startIndex, addedItems, removedItems }) { collectionChangedRaiseCount++; - expect(subject).toStrictEqual(observableCollection); - expect(operation).toEqual(collectionOperation); + expect(subject) + .toStrictEqual(observableCollection); + expect(operation) + .toEqual(collectionOperation); const spliceArray = initialState.slice(); const spliceRemovedItems = spliceArray.splice(startIndex, removedItems.length, ...addedItems); expectCollectionsToBeEqual(observableCollection, spliceArray); - expect(spliceRemovedItems).toEqual(removedItems); + expect(spliceRemovedItems) + .toEqual(removedItems); } }); observableCollection.collectionReordered.subscribe({ @@ -70,13 +74,19 @@ export function testMutatingOperation({ collectionOperation, initialState const observableCollectionResult = typeof applyOperation === 'function' ? applyOperation(observableCollection) : applyOperation.applyCollectionOperation(observableCollection); expectCollectionsToBeEqual(observableCollection, expectedState); - expect(observableCollectionResult).toEqual(expectedResult === selfResult ? observableCollection : expectedResult); + expect(observableCollectionResult) + .toEqual(expectedResult === selfResult ? observableCollection : expectedResult); - expect(propertiesChangedRaiseCount).toBe(1); - expect(collectionChangedRaiseCount).toBe(1); - expect(collectionReorderedRaiseCount).toBe(0); - expect(actualChangedProperties).toEqual(changedProperties); + expect(propertiesChangedRaiseCount) + .toBe(1); + expect(collectionChangedRaiseCount) + .toBe(1); + expect(collectionReorderedRaiseCount) + .toBe(0); + expect(actualChangedProperties) + .toEqual(changedProperties); expectCollectionsToBeEqual(observableCollection, array); - expect(observableCollectionResult).toEqual(expectedResult === selfResult ? observableCollection : arrayResult); + expect(observableCollectionResult) + .toEqual(expectedResult === selfResult ? observableCollection : arrayResult); } \ No newline at end of file diff --git a/src/collections/observableCollections/__tests__/common/testReorderingOperation.ts b/src/collections/observableCollections/__tests__/common/testReorderingOperation.ts index 06d490a..761e9c1 100644 --- a/src/collections/observableCollections/__tests__/common/testReorderingOperation.ts +++ b/src/collections/observableCollections/__tests__/common/testReorderingOperation.ts @@ -9,10 +9,10 @@ export interface ITestReorderingOperationOptions { readonly initialState: readonly TItem[]; readonly changedProperties: readonly ('length' | number)[]; - readonly applyOperation: ((collection: TItem[] | IObservableCollection) => unknown) | { + readonly applyOperation: ((collection: TItem[] | IObservableCollection)=> unknown) | { applyArrayOperation(array: TItem[]): unknown; applyCollectionOperation(colleciton: IObservableCollection): unknown; - } + }; readonly expectedCollection: readonly TItem[]; readonly expectedResult: unknown; @@ -40,7 +40,8 @@ export function testReorderingOperation({ collectionOperation, initialSta handle(subject, changedProperties) { propertiesChangedRaiseCount++; - expect(subject).toStrictEqual(observableCollection); + expect(subject) + .toStrictEqual(observableCollection); actualChangedProperties = changedProperties; } }); @@ -53,8 +54,10 @@ export function testReorderingOperation({ collectionOperation, initialSta handle(subject, { operation, movedItems }) { collectionReorderedRaiseCount++; - expect(subject).toStrictEqual(observableCollection); - expect(operation).toEqual(collectionOperation); + expect(subject) + .toStrictEqual(observableCollection); + expect(operation) + .toEqual(collectionOperation); const copyArray = initialState.slice(); movedItems.forEach(({ currentIndex, currentItem }) => { @@ -71,13 +74,19 @@ export function testReorderingOperation({ collectionOperation, initialSta const observableCollectionResult = typeof applyOperation === 'function' ? applyOperation(observableCollection) : applyOperation.applyCollectionOperation(observableCollection); expectCollectionsToBeEqual(observableCollection, expectedCollection); - expect(observableCollectionResult).toEqual(expectedResult === selfResult ? observableCollection : expectedResult); + expect(observableCollectionResult) + .toEqual(expectedResult === selfResult ? observableCollection : expectedResult); - expect(propertiesChangedRaiseCount).toBe(1); - expect(collectionChangedRaiseCount).toBe(0); - expect(collectionReorderedRaiseCount).toBe(1); - expect(actualChangedProperties).toEqual(changedProperties); + expect(propertiesChangedRaiseCount) + .toBe(1); + expect(collectionChangedRaiseCount) + .toBe(0); + expect(collectionReorderedRaiseCount) + .toBe(1); + expect(actualChangedProperties) + .toEqual(changedProperties); expectCollectionsToBeEqual(observableCollection, array); - expect(arrayResult).toEqual(expectedResult === selfResult ? array : expectedResult); + expect(arrayResult) + .toEqual(expectedResult === selfResult ? array : expectedResult); } \ No newline at end of file diff --git a/src/collections/observableMap/IMapChangedEvent.ts b/src/collections/observableMap/IMapChangedEvent.ts index 3b383a9..1cb2dc8 100644 --- a/src/collections/observableMap/IMapChangedEvent.ts +++ b/src/collections/observableMap/IMapChangedEvent.ts @@ -1,5 +1,5 @@ -import type { IEvent } from '../../events'; import type { IMapChange } from './IMapChange'; +import type { IEvent } from '../../events'; /** * A specialized event for subscribing and unsubscribing from map changed events. diff --git a/src/collections/observableMap/IMapChangedEventHandler.ts b/src/collections/observableMap/IMapChangedEventHandler.ts index 725263d..e7d78b9 100644 --- a/src/collections/observableMap/IMapChangedEventHandler.ts +++ b/src/collections/observableMap/IMapChangedEventHandler.ts @@ -1,5 +1,5 @@ -import type { IEventHandler } from '../../events'; import type { IMapChange } from './IMapChange'; +import type { IEventHandler } from '../../events'; /** * A specialized interface for handling map changed events. diff --git a/src/collections/observableMap/IReadOnlyObservableMap.ts b/src/collections/observableMap/IReadOnlyObservableMap.ts index 8538999..1379589 100644 --- a/src/collections/observableMap/IReadOnlyObservableMap.ts +++ b/src/collections/observableMap/IReadOnlyObservableMap.ts @@ -1,5 +1,5 @@ -import type { INotifyPropertiesChanged } from '../../viewModels'; import type { INotifyMapChanged } from './INotifyMapChanged'; +import type { INotifyPropertiesChanged } from '../../viewModels'; /** * Represents a read-only observable map based on the [Map](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map) interface. @@ -64,7 +64,7 @@ export interface IReadOnlyObservableMap extends Iterable<[TKey, TIt * @param thisArg A value to use as context when processing entries. * @see [Map.forEach](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach) */ - forEach(callback: (this: TContext, item: TItem, key: TKey, map: this) => void, thisArg?: TContext): void; + forEach(callback: (this: TContext, item: TItem, key: TKey, map: this)=> void, thisArg?: TContext): void; /** * Converts the observable map to a native JavaScript [Map](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map). diff --git a/src/collections/observableMap/ReadOnlyObservableMap.ts b/src/collections/observableMap/ReadOnlyObservableMap.ts index 3445cda..16c9ec1 100644 --- a/src/collections/observableMap/ReadOnlyObservableMap.ts +++ b/src/collections/observableMap/ReadOnlyObservableMap.ts @@ -54,7 +54,8 @@ export class ReadOnlyObservableMap extends ViewModel implements IRe * @see [Map.entries](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map/entries) */ public entries(): IterableIterator<[TKey, TItem]> { - var changeTokenCopy = this._changeToken; + let changeTokenCopy = this._changeToken; + return new ObservableMapIterator<[TKey, TItem]>(this._map.entries(), () => changeTokenCopy !== this._changeToken); } @@ -64,7 +65,8 @@ export class ReadOnlyObservableMap extends ViewModel implements IRe * @see [Map.keys](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map/keys) */ public keys(): IterableIterator { - var changeTokenCopy = this._changeToken; + let changeTokenCopy = this._changeToken; + return new ObservableMapIterator(this._map.keys(), () => changeTokenCopy !== this._changeToken); } @@ -74,7 +76,8 @@ export class ReadOnlyObservableMap extends ViewModel implements IRe * @see [Map.values](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map/values) */ public values(): IterableIterator { - var changeTokenCopy = this._changeToken; + let changeTokenCopy = this._changeToken; + return new ObservableMapIterator(this._map.values(), () => changeTokenCopy !== this._changeToken); } @@ -105,7 +108,7 @@ export class ReadOnlyObservableMap extends ViewModel implements IRe * @param thisArg A value to use as context when processing entries. * @see [Map.forEach](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach) */ - public forEach(callback: (this: TContext, item: TItem, key: TKey, map: this) => void, thisArg?: TContext): void { + public forEach(callback: (this: TContext, item: TItem, key: TKey, map: this)=> void, thisArg?: TContext): void { const changeTokenCopy = this._changeToken; for (const [key, item] of this) { @@ -199,9 +202,9 @@ export class ReadOnlyObservableMap extends ViewModel implements IRe class ObservableMapIterator implements Iterator { private _completed: boolean; private readonly _iterator: Iterator; - private readonly _mapChanged: () => boolean; + private readonly _mapChanged: ()=> boolean; - public constructor(iterator: Iterator, mapChanged: () => boolean) { + public constructor(iterator: Iterator, mapChanged: ()=> boolean) { this._iterator = iterator; this._mapChanged = mapChanged; } @@ -223,6 +226,7 @@ class ObservableMapIterator implements Iterator { testBlankMutatingOperation({ initialState: [], - applyOperation: map => map.clear(), + applyOperation: (map) => map.clear(), expectedResult: undefined }); @@ -22,7 +22,7 @@ describe('ObservableMap.clear', (): void => { ], changedProperties: ['size'], - applyOperation: map => map.clear(), + applyOperation: (map) => map.clear(), expectedMap: [], expectedResult: undefined @@ -40,7 +40,8 @@ describe('ObservableMap.clear', (): void => { for (const _ of observableMap) observableMap.clear(); - }) + } + ) .toThrow(new Error('Map has changed while being iterated.')); }); }); \ No newline at end of file diff --git a/src/collections/observableMap/__tests__/ObservableMap.delete.test.ts b/src/collections/observableMap/__tests__/ObservableMap.delete.test.ts index 7197f4e..66bfcf2 100644 --- a/src/collections/observableMap/__tests__/ObservableMap.delete.test.ts +++ b/src/collections/observableMap/__tests__/ObservableMap.delete.test.ts @@ -6,7 +6,7 @@ describe('ObservableMap.delete', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: map => map.delete(1), + applyOperation: (map) => map.delete(1), expectedResult: false }); @@ -22,7 +22,7 @@ describe('ObservableMap.delete', (): void => { ], changedProperties: ['size'], - applyOperation: map => map.delete(2), + applyOperation: (map) => map.delete(2), expectedMap: [ [1, 'a'], @@ -40,7 +40,7 @@ describe('ObservableMap.delete', (): void => { [3, 'c'] ], - applyOperation: map => map.delete(4), + applyOperation: (map) => map.delete(4), expectedResult: false }); @@ -57,7 +57,8 @@ describe('ObservableMap.delete', (): void => { for (const _ of observableMap) observableMap.delete(2); - }) + } + ) .toThrow(new Error('Map has changed while being iterated.')); }); @@ -72,7 +73,8 @@ describe('ObservableMap.delete', (): void => { for (const _ of observableMap) observableMap.delete(4); - }) + } + ) .not .toThrow(); }); diff --git a/src/collections/observableMap/__tests__/ObservableMap.forEach.test.ts b/src/collections/observableMap/__tests__/ObservableMap.forEach.test.ts index eb17a2a..a98c454 100644 --- a/src/collections/observableMap/__tests__/ObservableMap.forEach.test.ts +++ b/src/collections/observableMap/__tests__/ObservableMap.forEach.test.ts @@ -10,15 +10,17 @@ describe('ObservableMap.forEach', (): void => { initialState: [], applyOperation: { - applyMapOperation: map => map.forEach(_ => mapInvocationCount++), - applyObservableMapOperation: observableMap => observableMap.forEach(_ => observableMapInvocationCount++) + applyMapOperation: (map) => map.forEach((_) => mapInvocationCount++), + applyObservableMapOperation: (observableMap) => observableMap.forEach((_) => observableMapInvocationCount++) }, expectedResult: undefined }); - expect(mapInvocationCount).toBe(0); - expect(observableMapInvocationCount).toBe(0); + expect(mapInvocationCount) + .toBe(0); + expect(observableMapInvocationCount) + .toBe(0); }); it('iterating over a map invokes the callback for each item', (): void => { @@ -33,55 +35,61 @@ describe('ObservableMap.forEach', (): void => { ], applyOperation: { - applyMapOperation: map => map.forEach((item, key) => mapItems.push([key, item])), - applyObservableMapOperation: observableMap => observableMap.forEach((item, key) => observableMapItems.push([key, item])) + applyMapOperation: (map) => map.forEach((item, key) => mapItems.push([key, item])), + applyObservableMapOperation: (observableMap) => observableMap.forEach((item, key) => observableMapItems.push([key, item])) }, expectedResult: undefined }); - expect(observableMapItems).toEqual(mapItems); + expect(observableMapItems) + .toEqual(mapItems); }); it('calling forEach passes arguments to each parameter accordingly', (): void => { let invocationCount = 0; - const observableMap = new ObservableMap([ - [1, 'a'] - ]); + const observableMap = new ObservableMap([[1, 'a']]); observableMap.forEach((item, key, map) => { invocationCount++; - expect(key).toBe(1); - expect(item).toBe('a'); - expect(map).toStrictEqual(observableMap); + expect(key) + .toBe(1); + expect(item) + .toBe('a'); + expect(map) + .toStrictEqual(observableMap); return true; }); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('calling forEach with context passes it to the callback', (): void => { let invocationCount = 0; const context = {}; - const observableMap = new ObservableMap([ - [1, 'a'] - ]); + const observableMap = new ObservableMap([[1, 'a']]); observableMap.forEach( function (item, key, map) { invocationCount++; - expect(this).toStrictEqual(context); - expect(key).toBe(1); - expect(item).toBe('a'); - expect(map).toStrictEqual(observableMap); + expect(this) + .toStrictEqual(context); + expect(key) + .toBe(1); + expect(item) + .toBe('a'); + expect(map) + .toStrictEqual(observableMap); return true; }, context ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the map while executing forEach throws exception', (): void => { @@ -92,10 +100,11 @@ describe('ObservableMap.forEach', (): void => { [2, 'b'], [3, 'c'] ]); - observableMap.forEach(_ => { + observableMap.forEach((_) => { observableMap.clear(); }); - }) + } + ) .toThrow(new Error('Map has changed while being iterated.')); }); @@ -109,8 +118,11 @@ describe('ObservableMap.forEach', (): void => { ]); for (const _ of observableMap) - observableMap.forEach(_ => { }); - }) + observableMap.forEach((_) => { + + }); + } + ) .not .toThrow(); }); diff --git a/src/collections/observableMap/__tests__/ObservableMap.get.test.ts b/src/collections/observableMap/__tests__/ObservableMap.get.test.ts index d8a5c1d..d563a27 100644 --- a/src/collections/observableMap/__tests__/ObservableMap.get.test.ts +++ b/src/collections/observableMap/__tests__/ObservableMap.get.test.ts @@ -6,7 +6,7 @@ describe('ObservableMap.get', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: map => map.get(1), + applyOperation: (map) => map.get(1), expectedResult: undefined }); @@ -22,7 +22,7 @@ describe('ObservableMap.get', (): void => { [3, {}] ], - applyOperation: map => map.get(2), + applyOperation: (map) => map.get(2), expectedResult: expectedItem }); @@ -36,7 +36,7 @@ describe('ObservableMap.get', (): void => { [3, 'c'] ], - applyOperation: map => map.get(4), + applyOperation: (map) => map.get(4), expectedResult: undefined }); @@ -56,8 +56,9 @@ describe('ObservableMap.get', (): void => { observableMap.get(valueToCheck); valueToCheck++; } - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableMap/__tests__/ObservableMap.has.test.ts b/src/collections/observableMap/__tests__/ObservableMap.has.test.ts index 4ca7d0b..94310d7 100644 --- a/src/collections/observableMap/__tests__/ObservableMap.has.test.ts +++ b/src/collections/observableMap/__tests__/ObservableMap.has.test.ts @@ -6,7 +6,7 @@ describe('ObservableMap.has', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: map => map.has(1), + applyOperation: (map) => map.has(1), expectedResult: false }); @@ -20,7 +20,7 @@ describe('ObservableMap.has', (): void => { [3, 'c'] ], - applyOperation: map => map.has(2), + applyOperation: (map) => map.has(2), expectedResult: true }); @@ -34,7 +34,7 @@ describe('ObservableMap.has', (): void => { [3, 'c'] ], - applyOperation: map => map.has(4), + applyOperation: (map) => map.has(4), expectedResult: false }); @@ -54,8 +54,9 @@ describe('ObservableMap.has', (): void => { observableMap.has(valueToCheck); valueToCheck++; } - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableMap/__tests__/ObservableMap.set.test.ts b/src/collections/observableMap/__tests__/ObservableMap.set.test.ts index 1f5748f..7e9b5f2 100644 --- a/src/collections/observableMap/__tests__/ObservableMap.set.test.ts +++ b/src/collections/observableMap/__tests__/ObservableMap.set.test.ts @@ -8,7 +8,7 @@ describe('ObservableMap.set', (): void => { initialState: [], changedProperties: ['size'], - applyOperation: map => map.set(1, 'a'), + applyOperation: (map) => map.set(1, 'a'), expectedMap: [[1, 'a']], expectedResult: selfResult @@ -25,7 +25,7 @@ describe('ObservableMap.set', (): void => { ], changedProperties: ['size'], - applyOperation: map => map.set(4, 'd'), + applyOperation: (map) => map.set(4, 'd'), expectedMap: [ [1, 'a'], @@ -47,7 +47,7 @@ describe('ObservableMap.set', (): void => { ], changedProperties: [], - applyOperation: map => map.set(2, 'd'), + applyOperation: (map) => map.set(2, 'd'), expectedMap: [ [1, 'a'], @@ -66,7 +66,7 @@ describe('ObservableMap.set', (): void => { [3, 'c'] ], - applyOperation: map => map.set(2, 'b'), + applyOperation: (map) => map.set(2, 'b'), expectedResult: selfResult }); }); @@ -82,7 +82,8 @@ describe('ObservableMap.set', (): void => { for (const _ of observableMap) observableMap.set(2, 'b'); - }) + } + ) .not .toThrow(); }); @@ -98,7 +99,8 @@ describe('ObservableMap.set', (): void => { for (const _ of observableMap) observableMap.set(4, 'd'); - }) + } + ) .toThrow(new Error('Map has changed while being iterated.')); }); @@ -113,7 +115,8 @@ describe('ObservableMap.set', (): void => { for (const _ of observableMap) observableMap.set(2, 'd'); - }) + } + ) .toThrow(new Error('Map has changed while being iterated.')); }); }); \ No newline at end of file diff --git a/src/collections/observableMap/__tests__/common/expectMapsToBeEqual.ts b/src/collections/observableMap/__tests__/common/expectMapsToBeEqual.ts index b01e170..9251973 100644 --- a/src/collections/observableMap/__tests__/common/expectMapsToBeEqual.ts +++ b/src/collections/observableMap/__tests__/common/expectMapsToBeEqual.ts @@ -1,8 +1,10 @@ import type { IReadOnlyObservableMap } from '../../IReadOnlyObservableMap'; export function expectMapsToBeEqual(observableMap: IReadOnlyObservableMap, map: Map): void { - expect(observableMap.size).toBe(map.size); - expect(observableMap.toMap()).toEqual(map); + expect(observableMap.size) + .toBe(map.size); + expect(observableMap.toMap()) + .toEqual(map); expectLookupsToBeEqual(observableMap, map); expectIterationsToBeEqual(observableMap, map); @@ -11,9 +13,11 @@ export function expectMapsToBeEqual(observableMap: IReadOnlyObserva function expectLookupsToBeEqual(observableMap: IReadOnlyObservableMap, map: Map): void { for (const key of observableMap.keys()) - expect(observableMap.get(key)).toBe(map.get(key)); + expect(observableMap.get(key)) + .toBe(map.get(key)); for (const key of map.keys()) - expect(map.get(key)).toBe(observableMap.get(key)); + expect(map.get(key)) + .toBe(observableMap.get(key)); } function expectIterationsToBeEqual(observableMap: IReadOnlyObservableMap, map: Map): void { @@ -25,11 +29,21 @@ function expectIterationsToBeEqual(observableMap: IReadOnlyObservab for (const item of map) mapIterationResult.push(item); - expect(observableMapIterationResult.sort()).toEqual(mapIterationResult.sort()); + expect(observableMapIterationResult.sort()) + .toEqual(mapIterationResult.sort()); } function expectRelatedIteratorsToBeEqual(observableMap: IReadOnlyObservableMap, map: Map): void { - expect(Array.from(observableMap.keys()).sort()).toEqual(Array.from(map.keys()).sort()); - expect(Array.from(observableMap.entries()).sort()).toEqual(Array.from(map.entries()).sort()); - expect(Array.from(observableMap.values()).sort()).toEqual(Array.from(map.values()).sort()); + expect(Array.from(observableMap.keys()) + .sort()) + .toEqual(Array.from(map.keys()) + .sort()); + expect(Array.from(observableMap.entries()) + .sort()) + .toEqual(Array.from(map.entries()) + .sort()); + expect(Array.from(observableMap.values()) + .sort()) + .toEqual(Array.from(map.values()) + .sort()); } \ No newline at end of file diff --git a/src/collections/observableMap/__tests__/common/testBlankMutatingOperation.ts b/src/collections/observableMap/__tests__/common/testBlankMutatingOperation.ts index 62c24ec..07171a2 100644 --- a/src/collections/observableMap/__tests__/common/testBlankMutatingOperation.ts +++ b/src/collections/observableMap/__tests__/common/testBlankMutatingOperation.ts @@ -6,10 +6,10 @@ import { selfResult } from './selfResult'; export interface ITestBlankMutatingOperationOptions { readonly initialState: readonly (readonly [TKey, TItem])[]; - readonly applyOperation: ((map: Map | IObservableMap) => unknown) | { + readonly applyOperation: ((map: Map | IObservableMap)=> unknown) | { applyMapOperation(map: Map): unknown; applyObservableMapOperation(observableMap: IObservableMap): unknown; - } + }; readonly expectedResult: unknown; } @@ -44,11 +44,15 @@ export function testBlankMutatingOperation({ initialState, expected const observableMapResult = typeof applyOperation === 'function' ? applyOperation(observableMap) : applyOperation.applyObservableMapOperation(observableMap); expectMapsToBeEqual(observableMap, new Map(initialState)); - expect(observableMapResult).toEqual(expectedResult === selfResult ? observableMap : expectedResult); + expect(observableMapResult) + .toEqual(expectedResult === selfResult ? observableMap : expectedResult); - expect(propertiesChangedRaiseCount).toBe(0); - expect(mapChangedRaiseCount).toBe(0); + expect(propertiesChangedRaiseCount) + .toBe(0); + expect(mapChangedRaiseCount) + .toBe(0); expectMapsToBeEqual(observableMap, map); - expect(observableMapResult).toEqual(expectedResult === selfResult ? observableMap : mapResult); + expect(observableMapResult) + .toEqual(expectedResult === selfResult ? observableMap : mapResult); } \ No newline at end of file diff --git a/src/collections/observableMap/__tests__/common/testMutatingOperation.ts b/src/collections/observableMap/__tests__/common/testMutatingOperation.ts index 3d94997..74dd142 100644 --- a/src/collections/observableMap/__tests__/common/testMutatingOperation.ts +++ b/src/collections/observableMap/__tests__/common/testMutatingOperation.ts @@ -1,5 +1,5 @@ -import type { IObservableMap } from '../../IObservableMap'; import type { MapChangeOperation } from '../../IMapChange'; +import type { IObservableMap } from '../../IObservableMap'; import { ObservableMap } from '../../ObservableMap'; import { expectMapsToBeEqual } from './expectMapsToBeEqual'; import { selfResult } from './selfResult'; @@ -9,10 +9,10 @@ export interface ITestMutatingOperationOptions { readonly initialState: readonly (readonly [TKey, TItem])[]; readonly changedProperties: readonly ('size')[]; - readonly applyOperation: ((map: Map | IObservableMap) => unknown) | { + readonly applyOperation: ((map: Map | IObservableMap)=> unknown) | { applyMapOperation(map: Map): unknown; applyObservableMapOperation(observableMap: IObservableMap): unknown; - } + }; readonly expectedMap: readonly (readonly [TKey, TItem])[]; readonly expectedResult: unknown; @@ -36,7 +36,8 @@ export function testMutatingOperation({ mapOperation, initialState, handle(subject, changedProperties) { propertiesChangedRaiseCount++; - expect(subject).toStrictEqual(observableMap); + expect(subject) + .toStrictEqual(observableMap); actualChangedProperties = changedProperties; } }); @@ -44,8 +45,10 @@ export function testMutatingOperation({ mapOperation, initialState, handle(subject, { operation, addedEntries, removedEntries }) { mapChangedRaiseCount++; - expect(subject).toStrictEqual(observableMap); - expect(operation).toEqual(mapOperation); + expect(subject) + .toStrictEqual(observableMap); + expect(operation) + .toEqual(mapOperation); removedEntries.forEach(([removedKey]) => map.delete(removedKey)); addedEntries.forEach(([addedKey, addedItem]) => map.set(addedKey, addedItem)); @@ -58,12 +61,17 @@ export function testMutatingOperation({ mapOperation, initialState, const observableMapResult = typeof applyOperation === 'function' ? applyOperation(observableMap) : applyOperation.applyObservableMapOperation(observableMap); expectMapsToBeEqual(observableMap, new Map(expectedState)); - expect(observableMapResult).toEqual(expectedResult === selfResult ? observableMap : expectedResult); + expect(observableMapResult) + .toEqual(expectedResult === selfResult ? observableMap : expectedResult); - expect(propertiesChangedRaiseCount).toBe(changedProperties.length === 0 ? 0 : 1); - expect(mapChangedRaiseCount).toBe(1); - expect(actualChangedProperties).toEqual(changedProperties); + expect(propertiesChangedRaiseCount) + .toBe(changedProperties.length === 0 ? 0 : 1); + expect(mapChangedRaiseCount) + .toBe(1); + expect(actualChangedProperties) + .toEqual(changedProperties); expectMapsToBeEqual(observableMap, map); - expect(observableMapResult).toEqual(expectedResult === selfResult ? observableMap : mapResult); + expect(observableMapResult) + .toEqual(expectedResult === selfResult ? observableMap : mapResult); } \ No newline at end of file diff --git a/src/collections/observableSet/IReadOnlyObservableSet.ts b/src/collections/observableSet/IReadOnlyObservableSet.ts index 0d89e35..c763c57 100644 --- a/src/collections/observableSet/IReadOnlyObservableSet.ts +++ b/src/collections/observableSet/IReadOnlyObservableSet.ts @@ -1,6 +1,6 @@ -import type { INotifyPropertiesChanged } from '../../viewModels'; import type { INotifySetChanged } from './INotifySetChanged'; import type { ISetLike } from './ISetLike'; +import type { INotifyPropertiesChanged } from '../../viewModels'; /** * Represents a read-only observable set based on the [Set](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set) interface. @@ -112,7 +112,7 @@ export interface IReadOnlyObservableSet extends Iterable, ISetLike * @param thisArg A value to use as context when processing items. * @see [Set.forEach](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set/forEach) */ - forEach(callback: (this: TContext, item: TItem, key: TItem, set: this) => void, thisArg?: TContext): void; + forEach(callback: (this: TContext, item: TItem, key: TItem, set: this)=> void, thisArg?: TContext): void; /** * Converts the observable set to a native JavaScript [Set](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set). diff --git a/src/collections/observableSet/ISetChangedEvent.ts b/src/collections/observableSet/ISetChangedEvent.ts index 1c729f7..251dbf7 100644 --- a/src/collections/observableSet/ISetChangedEvent.ts +++ b/src/collections/observableSet/ISetChangedEvent.ts @@ -1,5 +1,5 @@ -import type { IEvent } from '../../events'; import type { ISetChange } from './ISetChange'; +import type { IEvent } from '../../events'; /** * A specialized event for subscribing and unsubscribing from set changed events. diff --git a/src/collections/observableSet/ISetChangedEventHandler.ts b/src/collections/observableSet/ISetChangedEventHandler.ts index 9fd8ef7..69669ee 100644 --- a/src/collections/observableSet/ISetChangedEventHandler.ts +++ b/src/collections/observableSet/ISetChangedEventHandler.ts @@ -1,5 +1,5 @@ -import type { IEventHandler } from '../../events'; import type { ISetChange } from './ISetChange'; +import type { IEventHandler } from '../../events'; /** * A specialized interface for handling set changed events. diff --git a/src/collections/observableSet/ReadOnlyObservableSet.ts b/src/collections/observableSet/ReadOnlyObservableSet.ts index 183cf01..ec7bc57 100644 --- a/src/collections/observableSet/ReadOnlyObservableSet.ts +++ b/src/collections/observableSet/ReadOnlyObservableSet.ts @@ -1,7 +1,7 @@ -import type { ISetLike } from './ISetLike'; import type { IReadOnlyObservableSet } from './IReadOnlyObservableSet'; import type { ISetChange } from './ISetChange'; import type { ISetChangedEvent } from './ISetChangedEvent'; +import type { ISetLike } from './ISetLike'; import { EventDispatcher } from '../../events'; import { ViewModel } from '../../viewModels'; import { isSetLike } from './isSetLike'; @@ -55,7 +55,8 @@ export class ReadOnlyObservableSet extends ViewModel implements IReadOnly * @see [Set.entries](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set/entries) */ public entries(): IterableIterator<[TItem, TItem]> { - var changeTokenCopy = this._changeToken; + let changeTokenCopy = this._changeToken; + return new ObservableSetIterator<[TItem, TItem]>(this._set.entries(), () => changeTokenCopy !== this._changeToken); } @@ -74,7 +75,8 @@ export class ReadOnlyObservableSet extends ViewModel implements IReadOnly * @see [Set.values](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set/values) */ public values(): IterableIterator { - var changeTokenCopy = this._changeToken; + let changeTokenCopy = this._changeToken; + return new ObservableSetIterator(this._set[Symbol.iterator](), () => changeTokenCopy !== this._changeToken); } @@ -232,7 +234,7 @@ export class ReadOnlyObservableSet extends ViewModel implements IReadOnly * @param thisArg A value to use as context when processing items. * @see [Set.forEach](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Set/forEach) */ - public forEach(callback: (this: TContext, item: TItem, key: TItem, set: this) => void, thisArg?: TContext): void { + public forEach(callback: (this: TContext, item: TItem, key: TItem, set: this)=> void, thisArg?: TContext): void { const changeTokenCopy = this._changeToken; for (const item of this) { @@ -320,9 +322,9 @@ export class ReadOnlyObservableSet extends ViewModel implements IReadOnly class ObservableSetIterator implements Iterator { private _completed: boolean; private readonly _iterator: Iterator; - private readonly _setChanged: () => boolean; + private readonly _setChanged: ()=> boolean; - public constructor(iterator: Iterator, setChanged: () => boolean) { + public constructor(iterator: Iterator, setChanged: ()=> boolean) { this._iterator = iterator; this._setChanged = setChanged; } @@ -344,6 +346,7 @@ class ObservableSetIterator implements Iterator { initialState: [], changedProperties: ['size'], - applyOperation: set => set.add(1), + applyOperation: (set) => set.add(1), expectedSet: [1], expectedResult: selfResult @@ -21,7 +21,7 @@ describe('ObservableSet.add', (): void => { initialState: [1, 2, 3], changedProperties: ['size'], - applyOperation: set => set.add(4), + applyOperation: (set) => set.add(4), expectedSet: [1, 2, 3, 4], expectedResult: selfResult @@ -32,7 +32,7 @@ describe('ObservableSet.add', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: set => set.add(2), + applyOperation: (set) => set.add(2), expectedResult: selfResult }); @@ -45,8 +45,9 @@ describe('ObservableSet.add', (): void => { for (const _ of observableSet) observableSet.add(4); - }) - .toThrow(new Error('Set has changed while being iterated.')) + } + ) + .toThrow(new Error('Set has changed while being iterated.')); }); it('adding existing item while iterating does not break iterators', (): void => { @@ -56,8 +57,9 @@ describe('ObservableSet.add', (): void => { for (const _ of observableSet) observableSet.add(1); - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/ObservableSet.clear.test.ts b/src/collections/observableSet/__tests__/ObservableSet.clear.test.ts index a3f9bba..07c4cdb 100644 --- a/src/collections/observableSet/__tests__/ObservableSet.clear.test.ts +++ b/src/collections/observableSet/__tests__/ObservableSet.clear.test.ts @@ -8,7 +8,7 @@ describe('ObservableSet.clear', (): void => { initialState: [1, 2, 3], changedProperties: ['size'], - applyOperation: set => set.clear(), + applyOperation: (set) => set.clear(), expectedSet: [], expectedResult: undefined @@ -19,7 +19,7 @@ describe('ObservableSet.clear', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: set => set.clear(), + applyOperation: (set) => set.clear(), expectedResult: undefined }); @@ -32,7 +32,8 @@ describe('ObservableSet.clear', (): void => { for (const _ of observableSet) observableSet.clear(); - }) - .toThrow(new Error('Set has changed while being iterated.')) + } + ) + .toThrow(new Error('Set has changed while being iterated.')); }); }); \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/ObservableSet.delete.test.ts b/src/collections/observableSet/__tests__/ObservableSet.delete.test.ts index 3c41ac8..1e01dc9 100644 --- a/src/collections/observableSet/__tests__/ObservableSet.delete.test.ts +++ b/src/collections/observableSet/__tests__/ObservableSet.delete.test.ts @@ -1,5 +1,5 @@ import { ObservableSet } from '../ObservableSet'; -import { selfResult, testBlankMutatingOperation, testMutatingOperation } from './common'; +import { testBlankMutatingOperation, testMutatingOperation } from './common'; describe('ObservableSet.delete', (): void => { it('deleting an item removes it from the set', (): void => { @@ -8,7 +8,7 @@ describe('ObservableSet.delete', (): void => { initialState: [1, 2, 3], changedProperties: ['size'], - applyOperation: set => set.delete(1), + applyOperation: (set) => set.delete(1), expectedSet: [2, 3], expectedResult: true @@ -19,7 +19,7 @@ describe('ObservableSet.delete', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: set => set.delete(2), + applyOperation: (set) => set.delete(2), expectedResult: false }); @@ -29,7 +29,7 @@ describe('ObservableSet.delete', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: set => set.delete(4), + applyOperation: (set) => set.delete(4), expectedResult: false }); @@ -42,8 +42,9 @@ describe('ObservableSet.delete', (): void => { for (const _ of observableSet) observableSet.delete(1); - }) - .toThrow(new Error('Set has changed while being iterated.')) + } + ) + .toThrow(new Error('Set has changed while being iterated.')); }); it('deleting non-existing item while iterating does not break iterators', (): void => { @@ -53,8 +54,9 @@ describe('ObservableSet.delete', (): void => { for (const _ of observableSet) observableSet.delete(4); - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/ObservableSet.difference.test.ts b/src/collections/observableSet/__tests__/ObservableSet.difference.test.ts index c3ed778..9488129 100644 --- a/src/collections/observableSet/__tests__/ObservableSet.difference.test.ts +++ b/src/collections/observableSet/__tests__/ObservableSet.difference.test.ts @@ -9,8 +9,8 @@ describe('ObservableSet.difference', (): void => { initialState: [4, 5, 6], applyOperation: { - applyObservableSetOperation: set => set.difference(other), - applySetOperation: set => difference(set, other) + applyObservableSetOperation: (set) => set.difference(other), + applySetOperation: (set) => difference(set, other) }, expectedResult: new Set([4, 5, 6]) @@ -24,8 +24,8 @@ describe('ObservableSet.difference', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.difference(other), - applySetOperation: set => difference(set, other) + applyObservableSetOperation: (set) => set.difference(other), + applySetOperation: (set) => difference(set, other) }, expectedResult: new Set() @@ -39,8 +39,8 @@ describe('ObservableSet.difference', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.difference(other), - applySetOperation: set => difference(set, other) + applyObservableSetOperation: (set) => set.difference(other), + applySetOperation: (set) => difference(set, other) }, expectedResult: new Set([1, 2]) @@ -54,8 +54,8 @@ describe('ObservableSet.difference', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.difference(other), - applySetOperation: set => difference(set, other) + applyObservableSetOperation: (set) => set.difference(other), + applySetOperation: (set) => difference(set, other) }, expectedResult: new Set([1, 2, 3]) @@ -69,8 +69,8 @@ describe('ObservableSet.difference', (): void => { initialState: [], applyOperation: { - applyObservableSetOperation: set => set.difference(other), - applySetOperation: set => difference(set, other) + applyObservableSetOperation: (set) => set.difference(other), + applySetOperation: (set) => difference(set, other) }, expectedResult: new Set() @@ -94,12 +94,14 @@ describe('ObservableSet.difference', (): void => { has: other.includes.bind(other) }); } - }) + } + ) .not - .toThrow() + .toThrow(); }); }); function difference(set: Set, other: readonly TItem[]): Set { - return new Set(Array.from(set.keys()).filter(item => !other.includes(item))); + return new Set(Array.from(set.keys()) + .filter((item) => !other.includes(item))); } \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/ObservableSet.forEach.test.ts b/src/collections/observableSet/__tests__/ObservableSet.forEach.test.ts index 02c0745..0a0a37b 100644 --- a/src/collections/observableSet/__tests__/ObservableSet.forEach.test.ts +++ b/src/collections/observableSet/__tests__/ObservableSet.forEach.test.ts @@ -10,15 +10,17 @@ describe('ObservableSet.forEach', (): void => { initialState: [], applyOperation: { - applySetOperation: set => set.forEach(_ => setInvocationCount++), - applyObservableSetOperation: observableSet => observableSet.forEach(_ => observableSetInvocationCount++) + applySetOperation: (set) => set.forEach((_) => setInvocationCount++), + applyObservableSetOperation: (observableSet) => observableSet.forEach((_) => observableSetInvocationCount++) }, expectedResult: undefined }); - expect(setInvocationCount).toBe(0); - expect(observableSetInvocationCount).toBe(0); + expect(setInvocationCount) + .toBe(0); + expect(observableSetInvocationCount) + .toBe(0); }); it('iterating over a set invokes the callback for each item', (): void => { @@ -29,14 +31,15 @@ describe('ObservableSet.forEach', (): void => { initialState: [1, 2, 3], applyOperation: { - applySetOperation: set => set.forEach(item => setItems.push(item)), - applyObservableSetOperation: observableSet => observableSet.forEach(item => observableSetItems.push(item)) + applySetOperation: (set) => set.forEach((item) => setItems.push(item)), + applyObservableSetOperation: (observableSet) => observableSet.forEach((item) => observableSetItems.push(item)) }, expectedResult: undefined }); - expect(observableSetItems).toEqual(setItems); + expect(observableSetItems) + .toEqual(setItems); }); it('calling forEach passes arguments to each parameter accordingly', (): void => { @@ -45,14 +48,18 @@ describe('ObservableSet.forEach', (): void => { observableSet.forEach((item, key, set) => { invocationCount++; - expect(item).toBe(1); - expect(key).toBe(item); - expect(set).toStrictEqual(observableSet); + expect(item) + .toBe(1); + expect(key) + .toBe(item); + expect(set) + .toStrictEqual(observableSet); return true; }); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('calling forEach with context passes it to the callback', (): void => { @@ -63,27 +70,33 @@ describe('ObservableSet.forEach', (): void => { function (item, key, set) { invocationCount++; - expect(this).toStrictEqual(context); - expect(item).toBe(1); - expect(key).toBe(item); - expect(set).toStrictEqual(observableSet); + expect(this) + .toStrictEqual(context); + expect(item) + .toBe(1); + expect(key) + .toBe(item); + expect(set) + .toStrictEqual(observableSet); return true; }, context ); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('modifying the set while executing forEach throws exception', (): void => { expect( () => { const observableSet = new ObservableSet([1, 2, 3]); - observableSet.forEach(_ => { + observableSet.forEach((_) => { observableSet.clear(); }); - }) + } + ) .toThrow(new Error('Set has changed while being iterated.')); }); @@ -93,8 +106,10 @@ describe('ObservableSet.forEach', (): void => { const observableSet = new ObservableSet([1, 2, 3]); for (const _ of observableSet) - observableSet.forEach(_ => {}); - }) + observableSet.forEach((_) => { + }); + } + ) .not .toThrow(); }); diff --git a/src/collections/observableSet/__tests__/ObservableSet.has.test.ts b/src/collections/observableSet/__tests__/ObservableSet.has.test.ts index d3e8166..75b41b4 100644 --- a/src/collections/observableSet/__tests__/ObservableSet.has.test.ts +++ b/src/collections/observableSet/__tests__/ObservableSet.has.test.ts @@ -6,7 +6,7 @@ describe('ObservableSet.has', (): void => { testBlankMutatingOperation({ initialState: [], - applyOperation: set => set.has(1), + applyOperation: (set) => set.has(1), expectedResult: false }); @@ -16,7 +16,7 @@ describe('ObservableSet.has', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: set => set.has(2), + applyOperation: (set) => set.has(2), expectedResult: true }); @@ -26,7 +26,7 @@ describe('ObservableSet.has', (): void => { testBlankMutatingOperation({ initialState: [1, 2, 3], - applyOperation: set => set.has(4), + applyOperation: (set) => set.has(4), expectedResult: false }); @@ -42,8 +42,9 @@ describe('ObservableSet.has', (): void => { observableSet.has(valueToCheck); valueToCheck++; } - }) + } + ) .not - .toThrow() + .toThrow(); }); }); \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/ObservableSet.intersection.test.ts b/src/collections/observableSet/__tests__/ObservableSet.intersection.test.ts index 1d6f394..cf1c062 100644 --- a/src/collections/observableSet/__tests__/ObservableSet.intersection.test.ts +++ b/src/collections/observableSet/__tests__/ObservableSet.intersection.test.ts @@ -9,8 +9,8 @@ describe('ObservableSet.intersection', (): void => { initialState: [4, 5, 6], applyOperation: { - applyObservableSetOperation: set => set.intersection(other), - applySetOperation: set => intersection(set, other) + applyObservableSetOperation: (set) => set.intersection(other), + applySetOperation: (set) => intersection(set, other) }, expectedResult: new Set() @@ -24,8 +24,8 @@ describe('ObservableSet.intersection', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.intersection(other), - applySetOperation: set => intersection(set, other) + applyObservableSetOperation: (set) => set.intersection(other), + applySetOperation: (set) => intersection(set, other) }, expectedResult: new Set([1, 2, 3]) @@ -39,8 +39,8 @@ describe('ObservableSet.intersection', (): void => { initialState: [1, 2, 3, 4], applyOperation: { - applyObservableSetOperation: set => set.intersection(other), - applySetOperation: set => intersection(set, other) + applyObservableSetOperation: (set) => set.intersection(other), + applySetOperation: (set) => intersection(set, other) }, expectedResult: new Set([3, 4]) @@ -54,8 +54,8 @@ describe('ObservableSet.intersection', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.intersection(other), - applySetOperation: set => intersection(set, other) + applyObservableSetOperation: (set) => set.intersection(other), + applySetOperation: (set) => intersection(set, other) }, expectedResult: new Set() @@ -69,8 +69,8 @@ describe('ObservableSet.intersection', (): void => { initialState: [], applyOperation: { - applyObservableSetOperation: set => set.intersection(other), - applySetOperation: set => intersection(set, other) + applyObservableSetOperation: (set) => set.intersection(other), + applySetOperation: (set) => intersection(set, other) }, expectedResult: new Set() @@ -94,12 +94,14 @@ describe('ObservableSet.intersection', (): void => { has: other.includes.bind(other) }); } - }) + } + ) .not - .toThrow() + .toThrow(); }); }); function intersection(set: Set, other: readonly TItem[]): Set { - return new Set(Array.from(set.keys()).filter(item => other.includes(item))); + return new Set(Array.from(set.keys()) + .filter((item) => other.includes(item))); } \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/ObservableSet.isDisjointFrom.test.ts b/src/collections/observableSet/__tests__/ObservableSet.isDisjointFrom.test.ts index 6a7bc5c..afcbb86 100644 --- a/src/collections/observableSet/__tests__/ObservableSet.isDisjointFrom.test.ts +++ b/src/collections/observableSet/__tests__/ObservableSet.isDisjointFrom.test.ts @@ -9,8 +9,8 @@ describe('ObservableSet.isDisjointFrom', (): void => { initialState: [3, 4, 5], applyOperation: { - applyObservableSetOperation: set => set.isDisjointFrom(other), - applySetOperation: set => isDisjointFrom(set, other) + applyObservableSetOperation: (set) => set.isDisjointFrom(other), + applySetOperation: (set) => isDisjointFrom(set, other) }, expectedResult: false @@ -24,8 +24,8 @@ describe('ObservableSet.isDisjointFrom', (): void => { initialState: [4, 5, 6], applyOperation: { - applyObservableSetOperation: set => set.isDisjointFrom(other), - applySetOperation: set => isDisjointFrom(set, other) + applyObservableSetOperation: (set) => set.isDisjointFrom(other), + applySetOperation: (set) => isDisjointFrom(set, other) }, expectedResult: true @@ -39,8 +39,8 @@ describe('ObservableSet.isDisjointFrom', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.isDisjointFrom(other), - applySetOperation: set => isDisjointFrom(set, other) + applyObservableSetOperation: (set) => set.isDisjointFrom(other), + applySetOperation: (set) => isDisjointFrom(set, other) }, expectedResult: true @@ -54,8 +54,8 @@ describe('ObservableSet.isDisjointFrom', (): void => { initialState: [], applyOperation: { - applyObservableSetOperation: set => set.isDisjointFrom(other), - applySetOperation: set => isDisjointFrom(set, other) + applyObservableSetOperation: (set) => set.isDisjointFrom(other), + applySetOperation: (set) => isDisjointFrom(set, other) }, expectedResult: true @@ -79,15 +79,17 @@ describe('ObservableSet.isDisjointFrom', (): void => { has: other.includes.bind(other) }); } - }) + } + ) .not - .toThrow() + .toThrow(); }); }); function isDisjointFrom(set: Set, other: readonly TItem[]): boolean { return ( - Array.from(set).every(item => !other.includes(item)) - && other.every(item => !set.has(item)) + Array.from(set) + .every((item) => !other.includes(item)) + && other.every((item) => !set.has(item)) ); } \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/ObservableSet.isSubsetOf.test.ts b/src/collections/observableSet/__tests__/ObservableSet.isSubsetOf.test.ts index b310d20..6399ace 100644 --- a/src/collections/observableSet/__tests__/ObservableSet.isSubsetOf.test.ts +++ b/src/collections/observableSet/__tests__/ObservableSet.isSubsetOf.test.ts @@ -9,8 +9,8 @@ describe('ObservableSet.isSubsetOf', (): void => { initialState: [1, 2], applyOperation: { - applyObservableSetOperation: set => set.isSubsetOf(other), - applySetOperation: set => isSubsetOf(set, other) + applyObservableSetOperation: (set) => set.isSubsetOf(other), + applySetOperation: (set) => isSubsetOf(set, other) }, expectedResult: true @@ -24,8 +24,8 @@ describe('ObservableSet.isSubsetOf', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.isSubsetOf(other), - applySetOperation: set => isSubsetOf(set, other) + applyObservableSetOperation: (set) => set.isSubsetOf(other), + applySetOperation: (set) => isSubsetOf(set, other) }, expectedResult: false @@ -39,8 +39,8 @@ describe('ObservableSet.isSubsetOf', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.isSubsetOf(other), - applySetOperation: set => isSubsetOf(set, other) + applyObservableSetOperation: (set) => set.isSubsetOf(other), + applySetOperation: (set) => isSubsetOf(set, other) }, expectedResult: false @@ -54,8 +54,8 @@ describe('ObservableSet.isSubsetOf', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.isSubsetOf(other), - applySetOperation: set => isSubsetOf(set, other) + applyObservableSetOperation: (set) => set.isSubsetOf(other), + applySetOperation: (set) => isSubsetOf(set, other) }, expectedResult: false @@ -69,8 +69,8 @@ describe('ObservableSet.isSubsetOf', (): void => { initialState: [], applyOperation: { - applyObservableSetOperation: set => set.isSubsetOf(other), - applySetOperation: set => isSubsetOf(set, other) + applyObservableSetOperation: (set) => set.isSubsetOf(other), + applySetOperation: (set) => isSubsetOf(set, other) }, expectedResult: true @@ -94,14 +94,16 @@ describe('ObservableSet.isSubsetOf', (): void => { has: other.includes.bind(other) }); } - }) + } + ) .not - .toThrow() + .toThrow(); }); }); function isSubsetOf(set: Set, other: readonly TItem[]): boolean { return ( - Array.from(set).every(item => other.includes(item)) + Array.from(set) + .every((item) => other.includes(item)) ); } \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/ObservableSet.isSupersetOf.test.ts b/src/collections/observableSet/__tests__/ObservableSet.isSupersetOf.test.ts index aab6652..d675cff 100644 --- a/src/collections/observableSet/__tests__/ObservableSet.isSupersetOf.test.ts +++ b/src/collections/observableSet/__tests__/ObservableSet.isSupersetOf.test.ts @@ -9,8 +9,8 @@ describe('ObservableSet.isSupersetOf', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.isSupersetOf(other), - applySetOperation: set => isSupersetOf(set, other) + applyObservableSetOperation: (set) => set.isSupersetOf(other), + applySetOperation: (set) => isSupersetOf(set, other) }, expectedResult: true @@ -24,8 +24,8 @@ describe('ObservableSet.isSupersetOf', (): void => { initialState: [1, 2], applyOperation: { - applyObservableSetOperation: set => set.isSupersetOf(other), - applySetOperation: set => isSupersetOf(set, other) + applyObservableSetOperation: (set) => set.isSupersetOf(other), + applySetOperation: (set) => isSupersetOf(set, other) }, expectedResult: false @@ -39,8 +39,8 @@ describe('ObservableSet.isSupersetOf', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.isSupersetOf(other), - applySetOperation: set => isSupersetOf(set, other) + applyObservableSetOperation: (set) => set.isSupersetOf(other), + applySetOperation: (set) => isSupersetOf(set, other) }, expectedResult: false @@ -54,8 +54,8 @@ describe('ObservableSet.isSupersetOf', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.isSupersetOf(other), - applySetOperation: set => isSupersetOf(set, other) + applyObservableSetOperation: (set) => set.isSupersetOf(other), + applySetOperation: (set) => isSupersetOf(set, other) }, expectedResult: true @@ -69,8 +69,8 @@ describe('ObservableSet.isSupersetOf', (): void => { initialState: [], applyOperation: { - applyObservableSetOperation: set => set.isSupersetOf(other), - applySetOperation: set => isSupersetOf(set, other) + applyObservableSetOperation: (set) => set.isSupersetOf(other), + applySetOperation: (set) => isSupersetOf(set, other) }, expectedResult: false @@ -94,14 +94,16 @@ describe('ObservableSet.isSupersetOf', (): void => { has: other.includes.bind(other) }); } - }) + } + ) .not - .toThrow() + .toThrow(); }); }); function isSupersetOf(set: Set, other: readonly TItem[]): boolean { return ( - Array.from(other).every(item => set.has(item)) + Array.from(other) + .every((item) => set.has(item)) ); } \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/ObservableSet.symmetricDifference.test.ts b/src/collections/observableSet/__tests__/ObservableSet.symmetricDifference.test.ts index 0be7ce4..d201a97 100644 --- a/src/collections/observableSet/__tests__/ObservableSet.symmetricDifference.test.ts +++ b/src/collections/observableSet/__tests__/ObservableSet.symmetricDifference.test.ts @@ -9,8 +9,8 @@ describe('ObservableSet.symmetricDifference', (): void => { initialState: [4, 5, 6], applyOperation: { - applyObservableSetOperation: set => set.symmetricDifference(other), - applySetOperation: set => symmetricDifference(set, other) + applyObservableSetOperation: (set) => set.symmetricDifference(other), + applySetOperation: (set) => symmetricDifference(set, other) }, expectedResult: new Set([1, 2, 3, 4, 5, 6]) @@ -24,8 +24,8 @@ describe('ObservableSet.symmetricDifference', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.symmetricDifference(other), - applySetOperation: set => symmetricDifference(set, other) + applyObservableSetOperation: (set) => set.symmetricDifference(other), + applySetOperation: (set) => symmetricDifference(set, other) }, expectedResult: new Set() @@ -39,8 +39,8 @@ describe('ObservableSet.symmetricDifference', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.symmetricDifference(other), - applySetOperation: set => symmetricDifference(set, other) + applyObservableSetOperation: (set) => set.symmetricDifference(other), + applySetOperation: (set) => symmetricDifference(set, other) }, expectedResult: new Set([1, 2, 4, 5]) @@ -54,8 +54,8 @@ describe('ObservableSet.symmetricDifference', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.symmetricDifference(other), - applySetOperation: set => symmetricDifference(set, other) + applyObservableSetOperation: (set) => set.symmetricDifference(other), + applySetOperation: (set) => symmetricDifference(set, other) }, expectedResult: new Set([1, 2, 3]) @@ -69,8 +69,8 @@ describe('ObservableSet.symmetricDifference', (): void => { initialState: [], applyOperation: { - applyObservableSetOperation: set => set.symmetricDifference(other), - applySetOperation: set => symmetricDifference(set, other) + applyObservableSetOperation: (set) => set.symmetricDifference(other), + applySetOperation: (set) => symmetricDifference(set, other) }, expectedResult: new Set([1, 2, 3]) @@ -94,9 +94,10 @@ describe('ObservableSet.symmetricDifference', (): void => { has: other.includes.bind(other) }); } - }) + } + ) .not - .toThrow() + .toThrow(); }); }); @@ -104,7 +105,7 @@ function symmetricDifference(set: Set, other: readonly TItem[]): S return new Set( Array .from(set.keys()) - .filter(item => !other.includes(item)) - .concat(other.filter(item => !set.has(item))) + .filter((item) => !other.includes(item)) + .concat(other.filter((item) => !set.has(item))) ); } \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/ObservableSet.union.test.ts b/src/collections/observableSet/__tests__/ObservableSet.union.test.ts index 4492174..4990e66 100644 --- a/src/collections/observableSet/__tests__/ObservableSet.union.test.ts +++ b/src/collections/observableSet/__tests__/ObservableSet.union.test.ts @@ -9,8 +9,8 @@ describe('ObservableSet.union', (): void => { initialState: [4, 5, 6], applyOperation: { - applyObservableSetOperation: set => set.union(other), - applySetOperation: set => union(set, other) + applyObservableSetOperation: (set) => set.union(other), + applySetOperation: (set) => union(set, other) }, expectedResult: new Set([1, 2, 3, 4, 5, 6]) @@ -24,8 +24,8 @@ describe('ObservableSet.union', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.union(other), - applySetOperation: set => union(set, other) + applyObservableSetOperation: (set) => set.union(other), + applySetOperation: (set) => union(set, other) }, expectedResult: new Set([1, 2, 3]) @@ -39,8 +39,8 @@ describe('ObservableSet.union', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.union(other), - applySetOperation: set => union(set, other) + applyObservableSetOperation: (set) => set.union(other), + applySetOperation: (set) => union(set, other) }, expectedResult: new Set([1, 2, 3, 4, 5]) @@ -54,8 +54,8 @@ describe('ObservableSet.union', (): void => { initialState: [1, 2, 3], applyOperation: { - applyObservableSetOperation: set => set.union(other), - applySetOperation: set => union(set, other) + applyObservableSetOperation: (set) => set.union(other), + applySetOperation: (set) => union(set, other) }, expectedResult: new Set([1, 2, 3]) @@ -69,8 +69,8 @@ describe('ObservableSet.union', (): void => { initialState: [], applyOperation: { - applyObservableSetOperation: set => set.union(other), - applySetOperation: set => union(set, other) + applyObservableSetOperation: (set) => set.union(other), + applySetOperation: (set) => union(set, other) }, expectedResult: new Set([1, 2, 3]) @@ -94,12 +94,14 @@ describe('ObservableSet.union', (): void => { has: other.includes.bind(other) }); } - }) + } + ) .not - .toThrow() + .toThrow(); }); }); function union(set: Set, other: readonly TItem[]): Set { - return new Set(Array.from(set.keys()).concat(other)); + return new Set(Array.from(set.keys()) + .concat(other)); } \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/common/expectSetsToBeEqual.ts b/src/collections/observableSet/__tests__/common/expectSetsToBeEqual.ts index 5682a7f..74087df 100644 --- a/src/collections/observableSet/__tests__/common/expectSetsToBeEqual.ts +++ b/src/collections/observableSet/__tests__/common/expectSetsToBeEqual.ts @@ -1,8 +1,10 @@ import type { IReadOnlyObservableSet } from '../../IReadOnlyObservableSet'; export function expectSetsToBeEqual(observableSet: IReadOnlyObservableSet, set: Set): void { - expect(observableSet.size).toBe(set.size); - expect(observableSet.toSet()).toEqual(set); + expect(observableSet.size) + .toBe(set.size); + expect(observableSet.toSet()) + .toEqual(set); expectIterationsToBeEqual(observableSet, set); expectRelatedIteratorsToBeEqual(observableSet, set); @@ -17,11 +19,21 @@ function expectIterationsToBeEqual(observableSet: IReadOnlyObservableSet< for (const item of set) setIterationResult.push(item); - expect(observableSetIterationResult.sort()).toEqual(setIterationResult.sort()); + expect(observableSetIterationResult.sort()) + .toEqual(setIterationResult.sort()); } function expectRelatedIteratorsToBeEqual(observableSet: IReadOnlyObservableSet, set: Set): void { - expect(Array.from(observableSet.keys()).sort()).toEqual(Array.from(set.keys()).sort()); - expect(Array.from(observableSet.entries()).sort()).toEqual(Array.from(set.entries()).sort()); - expect(Array.from(observableSet.values()).sort()).toEqual(Array.from(set.values()).sort()); + expect(Array.from(observableSet.keys()) + .sort()) + .toEqual(Array.from(set.keys()) + .sort()); + expect(Array.from(observableSet.entries()) + .sort()) + .toEqual(Array.from(set.entries()) + .sort()); + expect(Array.from(observableSet.values()) + .sort()) + .toEqual(Array.from(set.values()) + .sort()); } \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/common/testBlankMutatingOperation.ts b/src/collections/observableSet/__tests__/common/testBlankMutatingOperation.ts index 6f9d35d..cc62c62 100644 --- a/src/collections/observableSet/__tests__/common/testBlankMutatingOperation.ts +++ b/src/collections/observableSet/__tests__/common/testBlankMutatingOperation.ts @@ -6,10 +6,10 @@ import { selfResult } from './selfResult'; export interface ITestBlankMutatingOperationOptions { readonly initialState: readonly TItem[]; - readonly applyOperation: ((set: Set | IObservableSet) => unknown) | { + readonly applyOperation: ((set: Set | IObservableSet)=> unknown) | { applySetOperation(set: Set): unknown; applyObservableSetOperation(observableSet: IObservableSet): unknown; - } + }; readonly expectedResult: unknown; } @@ -44,11 +44,15 @@ export function testBlankMutatingOperation({ initialState, expectedResult const observableSetResult = typeof applyOperation === 'function' ? applyOperation(observableSet) : applyOperation.applyObservableSetOperation(observableSet); expectSetsToBeEqual(observableSet, new Set(initialState)); - expect(observableSetResult).toEqual(expectedResult === selfResult ? observableSet : expectedResult); + expect(observableSetResult) + .toEqual(expectedResult === selfResult ? observableSet : expectedResult); - expect(propertiesChangedRaiseCount).toBe(0); - expect(setChangedRaiseCount).toBe(0); + expect(propertiesChangedRaiseCount) + .toBe(0); + expect(setChangedRaiseCount) + .toBe(0); expectSetsToBeEqual(observableSet, set); - expect(observableSetResult).toEqual(expectedResult === selfResult ? observableSet : setResult); + expect(observableSetResult) + .toEqual(expectedResult === selfResult ? observableSet : setResult); } \ No newline at end of file diff --git a/src/collections/observableSet/__tests__/common/testMutatingOperation.ts b/src/collections/observableSet/__tests__/common/testMutatingOperation.ts index a147c42..011c465 100644 --- a/src/collections/observableSet/__tests__/common/testMutatingOperation.ts +++ b/src/collections/observableSet/__tests__/common/testMutatingOperation.ts @@ -9,10 +9,10 @@ export interface ITestMutatingOperationOptions { readonly initialState: readonly TItem[]; readonly changedProperties: readonly ('size')[]; - readonly applyOperation: ((set: Set | IObservableSet) => unknown) | { + readonly applyOperation: ((set: Set | IObservableSet)=> unknown) | { applySetOperation(set: Set): unknown; applyObservableSetOperation(observableSet: IObservableSet): unknown; - } + }; readonly expectedSet: readonly TItem[]; readonly expectedResult: unknown; @@ -36,7 +36,8 @@ export function testMutatingOperation({ setOperation, initialState, chang handle(subject, changedProperties) { propertiesChangedRaiseCount++; - expect(subject).toStrictEqual(observableSet); + expect(subject) + .toStrictEqual(observableSet); actualChangedProperties = changedProperties; } }); @@ -44,11 +45,13 @@ export function testMutatingOperation({ setOperation, initialState, chang handle(subject, { operation, addedItems, removedItems }) { setChangedRaiseCount++; - expect(subject).toStrictEqual(observableSet); - expect(operation).toEqual(setOperation); + expect(subject) + .toStrictEqual(observableSet); + expect(operation) + .toEqual(setOperation); - addedItems.forEach(addedItem => set.add(addedItem)); - removedItems.forEach(removedItem => set.delete(removedItem)); + addedItems.forEach((addedItem) => set.add(addedItem)); + removedItems.forEach((removedItem) => set.delete(removedItem)); } }); @@ -58,12 +61,17 @@ export function testMutatingOperation({ setOperation, initialState, chang const observableSetResult = typeof applyOperation === 'function' ? applyOperation(observableSet) : applyOperation.applyObservableSetOperation(observableSet); expectSetsToBeEqual(observableSet, new Set(expectedState)); - expect(observableSetResult).toEqual(expectedResult === selfResult ? observableSet : expectedResult); + expect(observableSetResult) + .toEqual(expectedResult === selfResult ? observableSet : expectedResult); - expect(propertiesChangedRaiseCount).toBe(1); - expect(setChangedRaiseCount).toBe(1); - expect(actualChangedProperties).toEqual(changedProperties); + expect(propertiesChangedRaiseCount) + .toBe(1); + expect(setChangedRaiseCount) + .toBe(1); + expect(actualChangedProperties) + .toEqual(changedProperties); expectSetsToBeEqual(observableSet, set); - expect(observableSetResult).toEqual(expectedResult === selfResult ? observableSet : setResult); + expect(observableSetResult) + .toEqual(expectedResult === selfResult ? observableSet : setResult); } \ No newline at end of file diff --git a/src/dependencies/DependencyContainer.ts b/src/dependencies/DependencyContainer.ts index 2aafa27..e9c85fc 100644 --- a/src/dependencies/DependencyContainer.ts +++ b/src/dependencies/DependencyContainer.ts @@ -7,322 +7,322 @@ type DependencyFactoryKey = DependencyToken | BasicDependency | SimpleD * Represents a dependency container for configuring and later on resolving dependencies similar to a dependency injection mechanism. */ export class DependencyContainer implements IDependencyContainer, IDependencyResolver { - private static _dependencyResolutionChain: any[] = []; - - private readonly _parent: DependencyContainer | null; - private readonly _singletonDependencyFactories = new Map, IDependencyFactory>(); - private readonly _scopedDependencyFactories = new Map, IDependencyFactory>(); - - /** - * Initializes a new instance of the {@linkcode DependencyContainer} class. - * @param parent Optional, a parent container to use as fallback when resolving dependencies. - */ - public constructor(parent?: DependencyContainer) { - this._parent = parent === undefined ? null : parent; - - this.createScope = this.createScope.bind(this); - this.resolve = this.resolve.bind(this); - } - - /** - * Creates a scoped dependency resolver. All scoped configured dependencies are resolved for each scope individually, - * while singletons are unique from the scope that configured them downwards. - * @returns Returns a scoped dependency resolver. - */ - public createScope(): IDependencyResolver { - return new DependencyContainer(this); - } - - /** - * Registers the provided type as a singleton dependency. - * @template T The dependency type to configure. - * @param type The type to configure. - * @returns Returns the dependency container. - */ - public registerSingletonType(type: ConfigurableDependency): DependencyContainer; - /** - * Registers the provided type as a singleton dependency. - * @template T The dependency type to configure. - * @param type The type to configure. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - public registerSingletonType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): DependencyContainer; - - public registerSingletonType(type: BasicDependency | SimpleDependency, factoryCallback: DependencyFactoryCallback = dependecyResolver => new type(dependecyResolver)): DependencyContainer { - this._singletonDependencyFactories.set(type, new CachedDelayedDependencyFactory(this, factoryCallback)); - this._scopedDependencyFactories.delete(type); - - return this; - } - - /** - * Registers the provided instance for the given token. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param instance The instance to return when resolving the dependency token. - * @returns Returns the dependency container. - */ - public registerInstanceToToken(token: DependencyToken, instance: T): DependencyContainer { - this._singletonDependencyFactories.set(token, new InstanceDependencyFactory(instance)); - this._scopedDependencyFactories.delete(token); - - return this; - } - - /** - * Registers the provided type for the given token as a singleton dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param type The type to configure. - * @returns Returns the dependency container. - */ - public registerSingletonTypeToToken(token: DependencyToken, type: ConfigurableDependency): DependencyContainer { - return this.registerSingletonFactoryToToken(token, dependencyResolver => new type(dependencyResolver)); - } - - /** - * Registers the provided callback for the given token as a singleton dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - public registerSingletonFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): DependencyContainer { - this._singletonDependencyFactories.set(token, new CachedDelayedDependencyFactory(this, factoryCallback)); - this._scopedDependencyFactories.delete(token); - - return this; - } - - /** - * Registers the provided type as a scoped dependency. - * @template T The dependency type to configure. - * @param type The type to configure. - * @returns Returns the dependency container. - */ - public registerScopedType(type: ConfigurableDependency): DependencyContainer; - /** - * Registers the provided type as a scoped dependency. - * @template T The dependency type to configure. - * @param type The type to configure. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - public registerScopedType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): DependencyContainer; - - public registerScopedType(type: BasicDependency | SimpleDependency, factoryCallback: DependencyFactoryCallback = dependecyResolver => new type(dependecyResolver)): DependencyContainer { - this._singletonDependencyFactories.delete(type); - this._scopedDependencyFactories.set(type, new CachedDelayedDependencyFactory(this, factoryCallback)); - - return this; - } - - /** - * Registers the provided type for the given token as a scoped dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param type The type to configure. - * @returns Returns the dependency container. - */ - public registerScopedTypeToToken(token: DependencyToken, type: ConfigurableDependency): DependencyContainer { - return this.registerScopedFactoryToToken(token, dependencyResolver => new type(dependencyResolver)); - } - - /** - * Registers the provided callback for the given token as a scoped dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - public registerScopedFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): DependencyContainer { - this._singletonDependencyFactories.delete(token); - this._scopedDependencyFactories.set(token, new CachedDelayedDependencyFactory(this, factoryCallback)); - - return this; - } - - /** - * Registers the provided type as a transient dependency. - * @template T The dependency type to configure. - * @param type The type to configure. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - public registerTransientType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): DependencyContainer { - this._singletonDependencyFactories.delete(type); - this._scopedDependencyFactories.set(type, new DelayedDependencyFactory(this, factoryCallback)); - - return this; - } - - /** - * Registers the provided type for the given token as a transient dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param type The type to configure. - * @returns Returns the dependency container. - */ - public registerTransientTypeToToken(token: DependencyToken, type: ConfigurableDependency): DependencyContainer { - return this.registerTransientFactoryToToken(token, dependencyResolver => new type(dependencyResolver)); - } - - /** - * Registers the provided callback for the given token as a transient dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - public registerTransientFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): DependencyContainer { - this._singletonDependencyFactories.delete(token); - this._scopedDependencyFactories.set(token, new DelayedDependencyFactory(this, factoryCallback)); - - return this; - } - - /** - * Resolves a dependency based on its configuration, if any. All unconfigured dependencies are transient. - * - * @template T The dependency type to resolve. - * - * @param dependency The dependnecy to resolve. - * - * @returns The resolved dependency. - */ - public resolve(dependency: ResolvableSimpleDependency): T; - /** - * Resolves a complex dependency. All such dependencies are transient as they require - * additional dependencies on the constructor. - * - * @template T The dependency type to resolve. - * @template TAdditional A tuple representing additional parameters required by the constructor. - * - * @param dependency The complex dependnecy to resolve. - * @param additionalDependencies Additional dependencies requested by the constructor besides the dependency resolver. - * - * @returns The resolved dependency. - */ - public resolve(dependency: ComplexDependency, additionalDependencies: TAdditional): T; - - public resolve(dependency: ResolvableSimpleDependency | ComplexDependency, additionalDependencies?: TAdditional): T { - try { - DependencyContainer._dependencyResolutionChain.push(dependency); - if (DependencyContainer._dependencyResolutionChain.lastIndexOf(dependency, -2) >= 0) - throw new Error(`Circular dependency detected while trying to resolve '${DependencyContainer._dependencyResolutionChain.map(dependency => dependency?.name ?? dependency).join(' -> ')}'.`) - - if (!isDependency(dependency)) - return dependency; - - if (isComplexDependency(dependency, additionalDependencies)) - return new dependency(this, ...additionalDependencies!); - - let resolvedDependencyFactory: IDependencyFactory | null = null; - let scope: DependencyContainer | null = this; - do { - let dependencyFactory = scope._scopedDependencyFactories.get(dependency); - - if (dependencyFactory !== null && dependencyFactory !== undefined) - if (scope === this) - resolvedDependencyFactory = dependencyFactory; - else { - resolvedDependencyFactory = dependencyFactory.withScope(this); - this._scopedDependencyFactories.set(dependency, resolvedDependencyFactory); - } - else { - dependencyFactory = scope._singletonDependencyFactories.get(dependency); - - if (dependencyFactory !== null && dependencyFactory !== undefined) - resolvedDependencyFactory = dependencyFactory; - else - scope = scope._parent; - } - } while (scope !== null && resolvedDependencyFactory === null); - - if (resolvedDependencyFactory === null) - if (dependency instanceof DependencyToken) - throw new Error(`There is no configured dependency for token '${dependency.toString()}'.`); - else - return new dependency(this); - else - return resolvedDependencyFactory.resolve() as T; + private static _dependencyResolutionChain: any[] = []; + private readonly _parent: DependencyContainer | null; + private readonly _singletonDependencyFactories = new Map, IDependencyFactory>(); + private readonly _scopedDependencyFactories = new Map, IDependencyFactory>(); + + /** + * Initializes a new instance of the {@linkcode DependencyContainer} class. + * @param parent Optional, a parent container to use as fallback when resolving dependencies. + */ + public constructor(parent?: DependencyContainer) { + this._parent = parent === undefined ? null : parent; + + this.createScope = this.createScope.bind(this); + this.resolve = this.resolve.bind(this); + } + + /** + * Creates a scoped dependency resolver. All scoped configured dependencies are resolved for each scope individually, + * while singletons are unique from the scope that configured them downwards. + * @returns Returns a scoped dependency resolver. + */ + public createScope(): IDependencyResolver { + return new DependencyContainer(this); + } + + /** + * Registers the provided type as a singleton dependency. + * @template T The dependency type to configure. + * @param type The type to configure. + * @returns Returns the dependency container. + */ + public registerSingletonType(type: ConfigurableDependency): DependencyContainer; + /** + * Registers the provided type as a singleton dependency. + * @template T The dependency type to configure. + * @param type The type to configure. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + public registerSingletonType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): DependencyContainer; + + public registerSingletonType(type: BasicDependency | SimpleDependency, factoryCallback: DependencyFactoryCallback = (dependecyResolver) => new type(dependecyResolver)): DependencyContainer { + this._singletonDependencyFactories.set(type, new CachedDelayedDependencyFactory(this, factoryCallback)); + this._scopedDependencyFactories.delete(type); + + return this; + } + + /** + * Registers the provided instance for the given token. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param instance The instance to return when resolving the dependency token. + * @returns Returns the dependency container. + */ + public registerInstanceToToken(token: DependencyToken, instance: T): DependencyContainer { + this._singletonDependencyFactories.set(token, new InstanceDependencyFactory(instance)); + this._scopedDependencyFactories.delete(token); + + return this; + } + + /** + * Registers the provided type for the given token as a singleton dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param type The type to configure. + * @returns Returns the dependency container. + */ + public registerSingletonTypeToToken(token: DependencyToken, type: ConfigurableDependency): DependencyContainer { + return this.registerSingletonFactoryToToken(token, (dependencyResolver) => new type(dependencyResolver)); + } + + /** + * Registers the provided callback for the given token as a singleton dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + public registerSingletonFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): DependencyContainer { + this._singletonDependencyFactories.set(token, new CachedDelayedDependencyFactory(this, factoryCallback)); + this._scopedDependencyFactories.delete(token); + + return this; + } + + /** + * Registers the provided type as a scoped dependency. + * @template T The dependency type to configure. + * @param type The type to configure. + * @returns Returns the dependency container. + */ + public registerScopedType(type: ConfigurableDependency): DependencyContainer; + /** + * Registers the provided type as a scoped dependency. + * @template T The dependency type to configure. + * @param type The type to configure. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + public registerScopedType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): DependencyContainer; + + public registerScopedType(type: BasicDependency | SimpleDependency, factoryCallback: DependencyFactoryCallback = (dependecyResolver) => new type(dependecyResolver)): DependencyContainer { + this._singletonDependencyFactories.delete(type); + this._scopedDependencyFactories.set(type, new CachedDelayedDependencyFactory(this, factoryCallback)); + + return this; + } + + /** + * Registers the provided type for the given token as a scoped dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param type The type to configure. + * @returns Returns the dependency container. + */ + public registerScopedTypeToToken(token: DependencyToken, type: ConfigurableDependency): DependencyContainer { + return this.registerScopedFactoryToToken(token, (dependencyResolver) => new type(dependencyResolver)); + } + + /** + * Registers the provided callback for the given token as a scoped dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + public registerScopedFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): DependencyContainer { + this._singletonDependencyFactories.delete(token); + this._scopedDependencyFactories.set(token, new CachedDelayedDependencyFactory(this, factoryCallback)); + + return this; + } + + /** + * Registers the provided type as a transient dependency. + * @template T The dependency type to configure. + * @param type The type to configure. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + public registerTransientType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): DependencyContainer { + this._singletonDependencyFactories.delete(type); + this._scopedDependencyFactories.set(type, new DelayedDependencyFactory(this, factoryCallback)); + + return this; } - finally { - DependencyContainer._dependencyResolutionChain.pop(); + + /** + * Registers the provided type for the given token as a transient dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param type The type to configure. + * @returns Returns the dependency container. + */ + public registerTransientTypeToToken(token: DependencyToken, type: ConfigurableDependency): DependencyContainer { + return this.registerTransientFactoryToToken(token, (dependencyResolver) => new type(dependencyResolver)); + } + + /** + * Registers the provided callback for the given token as a transient dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + public registerTransientFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): DependencyContainer { + this._singletonDependencyFactories.delete(token); + this._scopedDependencyFactories.set(token, new DelayedDependencyFactory(this, factoryCallback)); + + return this; + } + + /** + * Resolves a dependency based on its configuration, if any. All unconfigured dependencies are transient. + * + * @template T The dependency type to resolve. + * + * @param dependency The dependnecy to resolve. + * + * @returns The resolved dependency. + */ + public resolve(dependency: ResolvableSimpleDependency): T; + /** + * Resolves a complex dependency. All such dependencies are transient as they require + * additional dependencies on the constructor. + * + * @template T The dependency type to resolve. + * @template TAdditional A tuple representing additional parameters required by the constructor. + * + * @param dependency The complex dependnecy to resolve. + * @param additionalDependencies Additional dependencies requested by the constructor besides the dependency resolver. + * + * @returns The resolved dependency. + */ + public resolve(dependency: ComplexDependency, additionalDependencies: TAdditional): T; + + public resolve(dependency: ResolvableSimpleDependency | ComplexDependency, additionalDependencies?: TAdditional): T { + try { + DependencyContainer._dependencyResolutionChain.push(dependency); + if (DependencyContainer._dependencyResolutionChain.lastIndexOf(dependency, -2) >= 0) + throw new Error(`Circular dependency detected while trying to resolve '${DependencyContainer._dependencyResolutionChain.map((dependency) => dependency?.name ?? dependency) + .join(' -> ')}'.`); + + if (!isDependency(dependency)) + return dependency; + + if (isComplexDependency(dependency, additionalDependencies)) + return new dependency(this, ...additionalDependencies!); + + let resolvedDependencyFactory: IDependencyFactory | null = null; + let scope: DependencyContainer | null = this; + do { + let dependencyFactory = scope._scopedDependencyFactories.get(dependency); + + if (dependencyFactory !== null && dependencyFactory !== undefined) + if (scope === this) + resolvedDependencyFactory = dependencyFactory; + else { + resolvedDependencyFactory = dependencyFactory.withScope(this); + this._scopedDependencyFactories.set(dependency, resolvedDependencyFactory); + } + else { + dependencyFactory = scope._singletonDependencyFactories.get(dependency); + + if (dependencyFactory !== null && dependencyFactory !== undefined) + resolvedDependencyFactory = dependencyFactory; + else + scope = scope._parent; + } + } while (scope !== null && resolvedDependencyFactory === null); + + if (resolvedDependencyFactory === null) + if (dependency instanceof DependencyToken) + throw new Error(`There is no configured dependency for token '${dependency.toString()}'.`); + else + return new dependency(this); + else + return resolvedDependencyFactory.resolve() as T; + } + finally { + DependencyContainer._dependencyResolutionChain.pop(); + } } - } } function isDependency(maybeDependency: any): maybeDependency is DependencyToken | BasicDependency | SimpleDependency | ComplexDependency { - return maybeDependency !== null && maybeDependency !== undefined && (typeof maybeDependency === 'function' || maybeDependency instanceof DependencyToken); + return maybeDependency !== null && maybeDependency !== undefined && (typeof maybeDependency === 'function' || maybeDependency instanceof DependencyToken); } function isComplexDependency(maybeDependency: any, maybeAdditionalDependencies: any): maybeDependency is ComplexDependency { - return maybeAdditionalDependencies !== null && maybeAdditionalDependencies !== undefined && Array.isArray(maybeAdditionalDependencies) && maybeAdditionalDependencies.length > 0; + return maybeAdditionalDependencies !== null && maybeAdditionalDependencies !== undefined && Array.isArray(maybeAdditionalDependencies) && maybeAdditionalDependencies.length > 0; } interface IDependencyFactory { - resolve(): T; + resolve(): T; - withScope(dependencyResolver: IDependencyResolver): IDependencyFactory; + withScope(dependencyResolver: IDependencyResolver): IDependencyFactory; } class CachedDelayedDependencyFactory implements IDependencyFactory { - private _initialized: boolean = false; - private _instance: T | null = null; - private readonly _dependencyResolver: IDependencyResolver; - private readonly _factoryCallback: DependencyFactoryCallback; - - public constructor(dependencyResolver: IDependencyResolver, factoryCallback: DependencyFactoryCallback) { - this._dependencyResolver = dependencyResolver; - this._factoryCallback = factoryCallback; - } - - public resolve(): T { - if (!this._initialized) { - this._instance = this._factoryCallback(this._dependencyResolver); - this._initialized = true; + private _initialized: boolean = false; + private _instance: T | null = null; + private readonly _dependencyResolver: IDependencyResolver; + private readonly _factoryCallback: DependencyFactoryCallback; + + public constructor(dependencyResolver: IDependencyResolver, factoryCallback: DependencyFactoryCallback) { + this._dependencyResolver = dependencyResolver; + this._factoryCallback = factoryCallback; } - return this._instance!; - } + public resolve(): T { + if (!this._initialized) { + this._instance = this._factoryCallback(this._dependencyResolver); + this._initialized = true; + } + + return this._instance!; + } - public withScope(dependencyResolver: IDependencyResolver): IDependencyFactory { - return new CachedDelayedDependencyFactory(dependencyResolver, this._factoryCallback); - } + public withScope(dependencyResolver: IDependencyResolver): IDependencyFactory { + return new CachedDelayedDependencyFactory(dependencyResolver, this._factoryCallback); + } } class DelayedDependencyFactory implements IDependencyFactory { - private readonly _dependencyResolver: IDependencyResolver; - private readonly _factoryCallback: DependencyFactoryCallback; + private readonly _dependencyResolver: IDependencyResolver; + private readonly _factoryCallback: DependencyFactoryCallback; - public constructor(dependencyResolver: IDependencyResolver, factoryCallback: DependencyFactoryCallback) { - this._dependencyResolver = dependencyResolver; - this._factoryCallback = factoryCallback; - } + public constructor(dependencyResolver: IDependencyResolver, factoryCallback: DependencyFactoryCallback) { + this._dependencyResolver = dependencyResolver; + this._factoryCallback = factoryCallback; + } - public resolve(): T { - return this._factoryCallback(this._dependencyResolver); - } + public resolve(): T { + return this._factoryCallback(this._dependencyResolver); + } - public withScope(dependencyResolver: IDependencyResolver): IDependencyFactory { - return new DelayedDependencyFactory(dependencyResolver, this._factoryCallback); - } + public withScope(dependencyResolver: IDependencyResolver): IDependencyFactory { + return new DelayedDependencyFactory(dependencyResolver, this._factoryCallback); + } } class InstanceDependencyFactory implements IDependencyFactory { - private readonly _instance: T; + private readonly _instance: T; - public constructor(instance: T) { - this._instance = instance; - } + public constructor(instance: T) { + this._instance = instance; + } - public resolve(): T { - return this._instance; - } + public resolve(): T { + return this._instance; + } - public withScope(dependencyResolver: IDependencyResolver): IDependencyFactory { - return this; - } + public withScope(dependencyResolver: IDependencyResolver): IDependencyFactory { + return this; + } } \ No newline at end of file diff --git a/src/dependencies/DependencyResolverContext.ts b/src/dependencies/DependencyResolverContext.ts index 02cab6c..61ec2e5 100644 --- a/src/dependencies/DependencyResolverContext.ts +++ b/src/dependencies/DependencyResolverContext.ts @@ -1,8 +1,8 @@ -import type { IDependencyResolver, ResolvableSimpleDependency } from './IDependencyResolver'; import type { IDependencyContainer, ConfigurableDependency } from './IDependencyContainer'; +import type { IDependencyResolver, ResolvableSimpleDependency } from './IDependencyResolver'; import type { useDependency } from './UseDependency'; -import { DependencyContainer } from './DependencyContainer'; import { type PropsWithChildren, createContext, createElement, useContext, useMemo, useRef } from 'react'; +import { DependencyContainer } from './DependencyContainer'; const DependencyResolverContext = createContext(new DependencyContainer()); @@ -25,7 +25,7 @@ export function useDependencyResolver(): IDependencyResolver { /** * Represents the dependency resolver context provider props. - * + * * @see {@link DependencyResolverProvider} */ export interface IDependencyResolverProviderProps { @@ -46,6 +46,7 @@ export interface IDependencyResolverProviderProps { */ export function DependencyResolverProvider(props: PropsWithChildren): JSX.Element { const { dependencyResolver, children } = props; + return createElement(DependencyResolverContext.Provider, { value: dependencyResolver, children @@ -81,18 +82,15 @@ export function DependencyResolverScope({ deps, children }: PropsWithChildren(null); const cachedDepsRef = useRef(normalizedDeps); - if (cachedDepsRef.current.length !== normalizedDeps.length || cachedDepsRef.current.some((cachedDep, depIndex) => cachedDep !== normalizedDeps[depIndex])) + if (scopedDependencyResolverRef.current === null || cachedDepsRef.current.length !== normalizedDeps.length || cachedDepsRef.current.some((cachedDep, depIndex) => cachedDep !== normalizedDeps[depIndex])) { + scopedDependencyResolverRef.current = parentDependencyResolver.createScope(); cachedDepsRef.current = normalizedDeps.slice(); - const { current: cachedDeps } = cachedDepsRef; - - const scopedDependencyResolver = useMemo( - () => parentDependencyResolver.createScope(), - [parentDependencyResolver, cachedDeps] - ); + } return createElement(DependencyResolverContext.Provider, { - value: scopedDependencyResolver, + value: scopedDependencyResolverRef.current, children: children }); } \ No newline at end of file diff --git a/src/dependencies/IDependencyContainer.ts b/src/dependencies/IDependencyContainer.ts index f61b3c5..be61c4c 100644 --- a/src/dependencies/IDependencyContainer.ts +++ b/src/dependencies/IDependencyContainer.ts @@ -18,43 +18,43 @@ export type ConfigurableDependency = BasicDependency | SimpleDependency /** * Represents a callback for initializing dependencies. - * + * * @template T The dependency type that is resolved. - * + * * @see {@link IDependencyResolver} * @see {@link IDependencyContainer} * @see {@link ResolvableSimpleDependency} * @see {@link ConfigurableDependency} * @see {@link useDependency} */ -export type DependencyFactoryCallback = (dependecyResolver: IDependencyResolver) => T; +export type DependencyFactoryCallback = (dependecyResolver: IDependencyResolver)=> T; /** * Represents a dependency container for configuring and later on resolving dependencies similar to a dependency injection mechanism. - * + * * @description There are three levels for configuring dependencies. - * + * * **Singleton** - dependencies are initialized only once and each time they are requested the same instance is returned. - * + * * **Scoped** - dependencies are initialized only one for each scope. Whenever the same dependency is resolved in the same * scope, the same instance is returned. When a dependency is requested in different scopes, different instances are returned. - * + * * **Transient** - dependencies are initialized each time they are requested, this is the default for all types allowing * for seamless use of the container regardless of whether types are configured or not. - * + * * ---- - * + * * The above is exemplified using the following code snippet. - * + * * ```ts * const dependencyContainer = new DependencyContainer() * .registerSingletonType(MyFirstClass) * .registerScopedType(MySecondClass); - * + * * const singletonInstance1 = dependencyContainer.resolve(MyFirstClass); * const singletonInstance2 = dependencyContainer.resolve(MyFirstClass); * singletonInstance1 === singletonInstance2; // true - * + * * const scope1 = dependencyContainer.createScope(); * const scope2 = dependencyContainer.createScope(); * const scopedInstance1 = scope1.resolve(MySecondClass); @@ -62,7 +62,7 @@ export type DependencyFactoryCallback = (dependecyResolver: IDependencyResolv * const scopedInstance3 = scope2.resolve(MySecondClass); * scopedInstance1 === scopedInstance2; // true * scopedInstance1 === scopedInstance3; // false - * + * * const transient1 = dependencyContainer.resolve(MyThirdClass); * const transient2 = dependencyContainer.resolve(MyThirdClass); * transient1 === transient2; // false @@ -73,103 +73,103 @@ export type DependencyFactoryCallback = (dependecyResolver: IDependencyResolv * @see {@link useDependency} */ export interface IDependencyContainer { - /** - * Registers the provided type as a singleton dependency. - * @template T The dependency type to configure. - * @param type The type to configure. - * @returns Returns the dependency container. - */ - registerSingletonType(type: ConfigurableDependency): IDependencyContainer; - /** - * Registers the provided type as a singleton dependency. - * @template T The dependency type to configure. - * @param type The type to configure. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - registerSingletonType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): IDependencyContainer; + /** + * Registers the provided type as a singleton dependency. + * @template T The dependency type to configure. + * @param type The type to configure. + * @returns Returns the dependency container. + */ + registerSingletonType(type: ConfigurableDependency): IDependencyContainer; + /** + * Registers the provided type as a singleton dependency. + * @template T The dependency type to configure. + * @param type The type to configure. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + registerSingletonType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): IDependencyContainer; - /** - * Registers the provided instance for the given token. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param instance The instance to return when resolving the dependency token. - * @returns Returns the dependency container. - */ - registerInstanceToToken(token: DependencyToken, instance: T): IDependencyContainer; - /** - * Registers the provided type for the given token as a singleton dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param type The type to configure. - * @returns Returns the dependency container. - */ - registerSingletonTypeToToken(token: DependencyToken, type: ConfigurableDependency): IDependencyContainer; - /** - * Registers the provided callback for the given token as a singleton dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - registerSingletonFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): IDependencyContainer; + /** + * Registers the provided instance for the given token. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param instance The instance to return when resolving the dependency token. + * @returns Returns the dependency container. + */ + registerInstanceToToken(token: DependencyToken, instance: T): IDependencyContainer; + /** + * Registers the provided type for the given token as a singleton dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param type The type to configure. + * @returns Returns the dependency container. + */ + registerSingletonTypeToToken(token: DependencyToken, type: ConfigurableDependency): IDependencyContainer; + /** + * Registers the provided callback for the given token as a singleton dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + registerSingletonFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): IDependencyContainer; - /** - * Registers the provided type as a scoped dependency. - * @template T The dependency type to configure. - * @param type The type to configure. - * @returns Returns the dependency container. - */ - registerScopedType(type: ConfigurableDependency): IDependencyContainer; - /** - * Registers the provided type as a scoped dependency. - * @template T The dependency type to configure. - * @param type The type to configure. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - registerScopedType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): IDependencyContainer; + /** + * Registers the provided type as a scoped dependency. + * @template T The dependency type to configure. + * @param type The type to configure. + * @returns Returns the dependency container. + */ + registerScopedType(type: ConfigurableDependency): IDependencyContainer; + /** + * Registers the provided type as a scoped dependency. + * @template T The dependency type to configure. + * @param type The type to configure. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + registerScopedType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): IDependencyContainer; - /** - * Registers the provided type for the given token as a scoped dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param type The type to configure. - * @returns Returns the dependency container. - */ - registerScopedTypeToToken(token: DependencyToken, type: ConfigurableDependency): IDependencyContainer; - /** - * Registers the provided callback for the given token as a scoped dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - registerScopedFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): IDependencyContainer; + /** + * Registers the provided type for the given token as a scoped dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param type The type to configure. + * @returns Returns the dependency container. + */ + registerScopedTypeToToken(token: DependencyToken, type: ConfigurableDependency): IDependencyContainer; + /** + * Registers the provided callback for the given token as a scoped dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + registerScopedFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): IDependencyContainer; - /** - * Registers the provided type as a transient dependency. - * @template T The dependency type to configure. - * @param type The type to configure. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - registerTransientType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): IDependencyContainer; + /** + * Registers the provided type as a transient dependency. + * @template T The dependency type to configure. + * @param type The type to configure. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + registerTransientType(type: ConfigurableDependency, factoryCallback: DependencyFactoryCallback): IDependencyContainer; - /** - * Registers the provided type for the given token as a transient dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param type The type to configure. - * @returns Returns the dependency container. - */ - registerTransientTypeToToken(token: DependencyToken, type: ConfigurableDependency): IDependencyContainer; - /** - * Registers the provided callback for the given token as a transient dependency. - * @template T The dependency type to configure. - * @param token The token used to resolve dependencies. - * @param factoryCallback A callback for initializing an instace of the given type. - * @returns Returns the dependency container. - */ - registerTransientFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): IDependencyContainer; + /** + * Registers the provided type for the given token as a transient dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param type The type to configure. + * @returns Returns the dependency container. + */ + registerTransientTypeToToken(token: DependencyToken, type: ConfigurableDependency): IDependencyContainer; + /** + * Registers the provided callback for the given token as a transient dependency. + * @template T The dependency type to configure. + * @param token The token used to resolve dependencies. + * @param factoryCallback A callback for initializing an instace of the given type. + * @returns Returns the dependency container. + */ + registerTransientFactoryToToken(token: DependencyToken, factoryCallback: DependencyFactoryCallback): IDependencyContainer; } \ No newline at end of file diff --git a/src/dependencies/IDependencyResolver.ts b/src/dependencies/IDependencyResolver.ts index 66e5c07..0c6c826 100644 --- a/src/dependencies/IDependencyResolver.ts +++ b/src/dependencies/IDependencyResolver.ts @@ -10,43 +10,43 @@ import type { useDependency } from './UseDependency'; * @see {@link useDependency} */ export interface IDependencyResolver { - /** - * Creates a scoped dependency resolver. All scoped configured dependencies are resolved for each scope individually, - * while singletons are unique from the scope that configured them downwards. - * - * @returns Returns a scoped dependency resolver. - */ - createScope(): IDependencyResolver; + /** + * Creates a scoped dependency resolver. All scoped configured dependencies are resolved for each scope individually, + * while singletons are unique from the scope that configured them downwards. + * + * @returns Returns a scoped dependency resolver. + */ + createScope(): IDependencyResolver; - /** - * Resolves a dependency based on its configuration, if any. All unconfigured dependencies are transient. - * - * @template T The dependency type to resolve. - * - * @param dependency The dependnecy to resolve. - * - * @returns The resolved dependency. - */ - resolve(dependency: ResolvableSimpleDependency): T; - /** - * Resolves a complex dependency. All such dependencies are transient as they require - * additional dependencies on the constructor. - * - * @template T The dependency type to resolve. - * @template TAdditional A tuple representing additional parameters required by the constructor. - * - * @param dependency The complex dependnecy to resolve. - * @param additionalDependencies Additional dependencies requested by the constructor besides the dependency resolver. - * - * @returns The resolved dependency. - */ - resolve(dependency: ComplexDependency, additionalDependencies: TAdditional): T; + /** + * Resolves a dependency based on its configuration, if any. All unconfigured dependencies are transient. + * + * @template T The dependency type to resolve. + * + * @param dependency The dependnecy to resolve. + * + * @returns The resolved dependency. + */ + resolve(dependency: ResolvableSimpleDependency): T; + /** + * Resolves a complex dependency. All such dependencies are transient as they require + * additional dependencies on the constructor. + * + * @template T The dependency type to resolve. + * @template TAdditional A tuple representing additional parameters required by the constructor. + * + * @param dependency The complex dependnecy to resolve. + * @param additionalDependencies Additional dependencies requested by the constructor besides the dependency resolver. + * + * @returns The resolved dependency. + */ + resolve(dependency: ComplexDependency, additionalDependencies: TAdditional): T; } /** * Represents a resolvable dependency, this being an already resolved instance which is returned immediatelly, * a dependency token, a basic or a simple dependency. - * + * * Resolvable dependencies provide an option where a dependency is resolved, but if there is an edge case where * said dependency was already resolved in a different way, it is provided through the same call allowing for * different use cases. @@ -76,7 +76,7 @@ export type ResolvableSimpleDependency = Exclude | DependencyTok * @see {@link useDependency} */ export type BasicDependency = { - new(): T; + new(): T; }; /** @@ -91,15 +91,15 @@ export type BasicDependency = { * @see {@link useDependency} */ export type SimpleDependency = { - new(dependencyResolver: IDependencyResolver): T; + new(dependencyResolver: IDependencyResolver): T; }; /** * Represents a complex dependency where additional dependencies are both resolved through the provided dependnecy resolver * as well as providing them as constructor parameters. - * + * * This is a more complex case where some parameters are page or component specific, such as the entity ID that is loaded. - * + * * @template T The complex dependency type. * @template TAdditional A tuple representing additional parameters required by the constructor. * @@ -110,16 +110,16 @@ export type SimpleDependency = { * @see {@link useDependency} */ export type ComplexDependency = { - new(dependencyResolver: IDependencyResolver, ...additionalDependencies: TAdditional): T; + new(dependencyResolver: IDependencyResolver, ...additionalDependencies: TAdditional): T; }; /** * Represents a dependency token for which types, factories or instances can be configured. - * + * * This is an abstraction where definitions cannot be configured directly, such as interfaces. * Instead binding an interface to an implementation, a dependency token is configured for * an implementation. - * + * * @template T The type that is associated with the dependency token. * * @see {@link IDependencyResolver} @@ -129,23 +129,23 @@ export type ComplexDependency = { * @see {@link useDependency} */ export class DependencyToken { - /** - * Initializes a new instance of the {@linkcode DependencyToken} class. - * @param description A textual description of the token used in exception messages. - */ - public constructor(description: string) { - this.description = description; - } + /** + * Initializes a new instance of the {@linkcode DependencyToken} class. + * @param description A textual description of the token used in exception messages. + */ + public constructor(description: string) { + this.description = description; + } - /** - * Gets the dependency token textual description. - */ - public readonly description: string; + /** + * Gets the dependency token textual description. + */ + public readonly description: string; - /** - * Gets the string representation of the dependency token. - */ - public toString(): string { - return this.description; - } + /** + * Gets the string representation of the dependency token. + */ + public toString(): string { + return this.description; + } } \ No newline at end of file diff --git a/src/dependencies/UseDependency.ts b/src/dependencies/UseDependency.ts index 5c2508e..c1952d7 100644 --- a/src/dependencies/UseDependency.ts +++ b/src/dependencies/UseDependency.ts @@ -1,5 +1,5 @@ -import type { IDependencyResolver, ResolvableSimpleDependency, ComplexDependency } from './IDependencyResolver'; import type { IDependencyContainer } from './IDependencyContainer'; +import type { IDependencyResolver, ResolvableSimpleDependency, ComplexDependency } from './IDependencyResolver'; import type { useViewModelDependency } from './UseViewModelDependency'; import { useMemo, useRef } from 'react'; import { useDependencyResolver, type DependencyResolverProvider, type DependencyResolverScope } from './DependencyResolverContext'; @@ -47,21 +47,21 @@ export function useDependency(dependency: ResolvableSimpleDependency): T; export function useDependency(dependency: ComplexDependency, additionalDependencies: TAdditional): T; export function useDependency(dependency: ResolvableSimpleDependency | ComplexDependency, additionalDependencies?: TAdditional): T { - const normalizedAdditionalDependencies = additionalDependencies === null || additionalDependencies == undefined || !Array.isArray(additionalDependencies) || additionalDependencies.length === 0 - ? emptyAdditionalDependencies as TAdditional - : additionalDependencies; + const normalizedAdditionalDependencies = additionalDependencies === null || additionalDependencies === undefined || !Array.isArray(additionalDependencies) || additionalDependencies.length === 0 + ? emptyAdditionalDependencies as TAdditional + : additionalDependencies; - const dependecyResolver = useDependencyResolver(); + const dependecyResolver = useDependencyResolver(); - const cachedAdditionalDependenciesRef = useRef(normalizedAdditionalDependencies); - if (cachedAdditionalDependenciesRef.current.length !== normalizedAdditionalDependencies.length || cachedAdditionalDependenciesRef.current.some((cachedAdditionalDependency, additionalDependencyIndex) => !Object.is(cachedAdditionalDependency, normalizedAdditionalDependencies[additionalDependencyIndex]))) - cachedAdditionalDependenciesRef.current = normalizedAdditionalDependencies.slice() as any as TAdditional; - const { current: cachedAdditionalDependencies } = cachedAdditionalDependenciesRef; + const cachedAdditionalDependenciesRef = useRef(normalizedAdditionalDependencies); + if (cachedAdditionalDependenciesRef.current.length !== normalizedAdditionalDependencies.length || cachedAdditionalDependenciesRef.current.some((cachedAdditionalDependency, additionalDependencyIndex) => !Object.is(cachedAdditionalDependency, normalizedAdditionalDependencies[additionalDependencyIndex]))) + cachedAdditionalDependenciesRef.current = normalizedAdditionalDependencies.slice() as any as TAdditional; + const { current: cachedAdditionalDependencies } = cachedAdditionalDependenciesRef; - const instance = useMemo( - () => dependecyResolver.resolve(dependency as ComplexDependency, cachedAdditionalDependencies), - [dependecyResolver, dependency, cachedAdditionalDependencies] - ); + const instance = useMemo( + () => dependecyResolver.resolve(dependency as ComplexDependency, cachedAdditionalDependencies), + [dependecyResolver, dependency, cachedAdditionalDependencies] + ); - return instance; + return instance; } \ No newline at end of file diff --git a/src/dependencies/UseViewModelDependency.ts b/src/dependencies/UseViewModelDependency.ts index 15dc6d4..46c06db 100644 --- a/src/dependencies/UseViewModelDependency.ts +++ b/src/dependencies/UseViewModelDependency.ts @@ -1,6 +1,6 @@ import type { INotifyPropertiesChanged } from '../viewModels'; +import type { IDependencyContainer, ConfigurableDependency } from './IDependencyContainer'; import type { IDependencyResolver, ResolvableSimpleDependency, ComplexDependency } from './IDependencyResolver'; -import type { IDependencyContainer, ConfigurableDependency } from './IDependencyContainer' import { useViewModel } from '../hooks'; import { useDependency } from './UseDependency'; @@ -57,8 +57,8 @@ export function useViewModelDependency(viewModelDependency: ComplexDependency, additionalDependencies: TAdditional): TViewModel; export function useViewModelDependency(viewModelDependency: ResolvableSimpleDependency | ComplexDependency, additionalDependencies?: TAdditional): TViewModel { - const viewModel = useDependency(viewModelDependency as ComplexDependency, additionalDependencies!); - useViewModel(viewModel); + const viewModel = useDependency(viewModelDependency as ComplexDependency, additionalDependencies!); + useViewModel(viewModel); - return viewModel; + return viewModel; } \ No newline at end of file diff --git a/src/dependencies/__tests__/DependencyContainer.circular.test.ts b/src/dependencies/__tests__/DependencyContainer.circular.test.ts index 59abbb9..00e99a6 100644 --- a/src/dependencies/__tests__/DependencyContainer.circular.test.ts +++ b/src/dependencies/__tests__/DependencyContainer.circular.test.ts @@ -1,90 +1,91 @@ -import { DependencyToken, type IDependencyResolver } from '../IDependencyResolver'; import { DependencyContainer } from '../DependencyContainer'; +import { DependencyToken, type IDependencyResolver } from '../IDependencyResolver'; describe('DependencyContainer.circular', (): void => { - test('Attempting to resolve self referencing dependency throws exception', () => { - class A { - private static _resolveCount = 0; - - constructor({ resolve }: IDependencyResolver) { - A._resolveCount++; - if (A._resolveCount > 1) - throw new Error('This is only thrown to avoid a stack overflow and stop the test faster. This point should never be reached.'); - - resolve(A); - } - } - - const { resolve } = new DependencyContainer() - .registerSingletonType(A); - - expect(() => resolve(A)).toThrow(new Error('Circular dependency detected while trying to resolve \'A -> A\'.')); - }); - - test('Attempting to resolve circular dependency throws exception', () => { - class A { - private static _resolveCount = 0; - - constructor({ resolve }: IDependencyResolver) { - A._resolveCount++; - if (A._resolveCount > 1) - throw new Error('This is only thrown to avoid a stack overflow and stop the test faster. This point should never be reached.'); - - resolve(B); - } - } - - class B { - constructor({ resolve }: IDependencyResolver) { - resolve(C); - } - } - - class C { - constructor({ resolve }: IDependencyResolver) { - resolve(A); - } - } - - const { resolve } = new DependencyContainer(); - - expect(() => resolve(A)).toThrow(new Error('Circular dependency detected while trying to resolve \'A -> B -> C -> A\'.')); - }); - - - test('Attempting to resolve circular dependency with tokens throws exception', () => { - const dependencyTokenA = new DependencyToken('token A'); - const dependencyTokenB = new DependencyToken('token B'); - - - class A { - private static _resolveCount = 0; - - constructor({ resolve }: IDependencyResolver) { - A._resolveCount++; - if (A._resolveCount > 2) - throw new Error('This is only thrown to avoid a stack overflow and stop the test faster. This point should never be reached.'); - - resolve(dependencyTokenB); - } - } - - class B { - constructor({ resolve }: IDependencyResolver) { - resolve(C); - } - } - - class C { - constructor({ resolve }: IDependencyResolver) { - resolve(dependencyTokenA); - } - } - - const { resolve } = new DependencyContainer() - .registerScopedTypeToToken(dependencyTokenA, A) - .registerTransientTypeToToken(dependencyTokenB, B); - - expect(() => resolve(A)).toThrow(new Error('Circular dependency detected while trying to resolve \'A -> token B -> C -> token A -> token B\'.')); - }); + test('Attempting to resolve self referencing dependency throws exception', () => { + class A { + private static _resolveCount = 0; + + constructor({ resolve }: IDependencyResolver) { + A._resolveCount++; + if (A._resolveCount > 1) + throw new Error('This is only thrown to avoid a stack overflow and stop the test faster. This point should never be reached.'); + + resolve(A); + } + } + + const { resolve } = new DependencyContainer() + .registerSingletonType(A); + + expect(() => resolve(A)) + .toThrow(new Error('Circular dependency detected while trying to resolve \'A -> A\'.')); + }); + + test('Attempting to resolve circular dependency throws exception', () => { + class A { + private static _resolveCount = 0; + + constructor({ resolve }: IDependencyResolver) { + A._resolveCount++; + if (A._resolveCount > 1) + throw new Error('This is only thrown to avoid a stack overflow and stop the test faster. This point should never be reached.'); + + resolve(B); + } + } + + class B { + constructor({ resolve }: IDependencyResolver) { + resolve(C); + } + } + + class C { + constructor({ resolve }: IDependencyResolver) { + resolve(A); + } + } + + const { resolve } = new DependencyContainer(); + + expect(() => resolve(A)) + .toThrow(new Error('Circular dependency detected while trying to resolve \'A -> B -> C -> A\'.')); + }); + + test('Attempting to resolve circular dependency with tokens throws exception', () => { + const dependencyTokenA = new DependencyToken('token A'); + const dependencyTokenB = new DependencyToken('token B'); + + class A { + private static _resolveCount = 0; + + constructor({ resolve }: IDependencyResolver) { + A._resolveCount++; + if (A._resolveCount > 2) + throw new Error('This is only thrown to avoid a stack overflow and stop the test faster. This point should never be reached.'); + + resolve(dependencyTokenB); + } + } + + class B { + constructor({ resolve }: IDependencyResolver) { + resolve(C); + } + } + + class C { + constructor({ resolve }: IDependencyResolver) { + resolve(dependencyTokenA); + } + } + + const { resolve } = new DependencyContainer() + .registerScopedTypeToToken(dependencyTokenA, A) + .registerTransientTypeToToken(dependencyTokenB, B); + + expect(() => resolve(A)) + .toThrow(new Error('Circular dependency detected while trying to resolve \'A -> token B -> C -> token A -> token B\'.')); + }); }); \ No newline at end of file diff --git a/src/dependencies/__tests__/DependencyContainer.config.test.ts b/src/dependencies/__tests__/DependencyContainer.config.test.ts index fbe728e..0711909 100644 --- a/src/dependencies/__tests__/DependencyContainer.config.test.ts +++ b/src/dependencies/__tests__/DependencyContainer.config.test.ts @@ -1,319 +1,360 @@ -import { DependencyToken } from '../IDependencyResolver'; import { DependencyContainer } from '../DependencyContainer'; +import { DependencyToken } from '../IDependencyResolver'; describe('DependencyContainer.config', (): void => { - test('Configuring a type as singleton then as scoped updates the configuration', () => { - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerSingletonType(MyClass) - .registerScopedType(MyClass) - .createScope(); - - const firstInstance = dependencyResolver.resolve(MyClass); - const secondInstance = dependencyResolver.resolve(MyClass); - const thirdInstance = dependencyResolver.createScope().resolve(MyClass); - - expect(firstInstance).toBe(secondInstance); - expect(firstInstance).not.toBe(thirdInstance); - expect(secondInstance).not.toBe(thirdInstance); - }); - - test('Configuring a type as scoped then as singleton updates the configuration', () => { - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerScopedType(MyClass) - .registerSingletonType(MyClass) - .createScope(); - - const firstInstance = dependencyResolver.resolve(MyClass); - const secondInstance = dependencyResolver.resolve(MyClass); - const thirdInstance = dependencyResolver.createScope().resolve(MyClass); - - expect(firstInstance).toBe(secondInstance); - expect(firstInstance).toBe(thirdInstance); - expect(secondInstance).toBe(thirdInstance); - }); - - test('Configuring a type as singleton then as transient updates the configuration', () => { - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerSingletonType(MyClass) - .registerTransientType(MyClass, () => new MyClass()) - .createScope(); - - const firstInstance = dependencyResolver.resolve(MyClass); - const secondInstance = dependencyResolver.resolve(MyClass); - const thirdInstance = dependencyResolver.createScope().resolve(MyClass); - - expect(firstInstance).not.toBe(secondInstance); - expect(firstInstance).not.toBe(thirdInstance); - expect(secondInstance).not.toBe(thirdInstance); - }); - - test('Configuring a type as transient then as singleton updates the configuration', () => { - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerTransientType(MyClass, () => new MyClass()) - .registerSingletonType(MyClass) - .createScope(); - - const firstInstance = dependencyResolver.resolve(MyClass); - const secondInstance = dependencyResolver.resolve(MyClass); - const thirdInstance = dependencyResolver.createScope().resolve(MyClass); - - expect(firstInstance).toBe(secondInstance); - expect(firstInstance).toBe(thirdInstance); - expect(secondInstance).toBe(thirdInstance); - }); - - test('Configuring a type as scoped then as transient updates the configuration', () => { - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerScopedType(MyClass) - .registerTransientType(MyClass, () => new MyClass()) - .createScope(); - - const firstInstance = dependencyResolver.resolve(MyClass); - const secondInstance = dependencyResolver.resolve(MyClass); - const thirdInstance = dependencyResolver.createScope().resolve(MyClass); - - expect(firstInstance).not.toBe(secondInstance); - expect(firstInstance).not.toBe(thirdInstance); - expect(secondInstance).not.toBe(thirdInstance); - }); - - test('Configuring a type as transient then as scoped updates the configuration', () => { - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerTransientType(MyClass, () => new MyClass()) - .registerScopedType(MyClass) - .createScope(); - - const firstInstance = dependencyResolver.resolve(MyClass); - const secondInstance = dependencyResolver.resolve(MyClass); - const thirdInstance = dependencyResolver.createScope().resolve(MyClass); - - expect(firstInstance).toBe(secondInstance); - expect(firstInstance).not.toBe(thirdInstance); - expect(secondInstance).not.toBe(thirdInstance); - }); - - test('Configuring a token as singleton then as scoped updates the configuration', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerSingletonFactoryToToken(token, () => new MyClass()) - .registerScopedFactoryToToken(token, () => new MyClass()) - .createScope(); - - const firstInstance = dependencyResolver.resolve(token); - const secondInstance = dependencyResolver.resolve(token); - const thirdInstance = dependencyResolver.createScope().resolve(token); - - expect(firstInstance).toBe(secondInstance); - expect(firstInstance).not.toBe(thirdInstance); - expect(secondInstance).not.toBe(thirdInstance); - }); - - test('Configuring a token as scoped then as singleton updates the configuration', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerScopedTypeToToken(token, MyClass) - .registerSingletonTypeToToken(token, MyClass) - .createScope(); - - const firstInstance = dependencyResolver.resolve(token); - const secondInstance = dependencyResolver.resolve(token); - const thirdInstance = dependencyResolver.createScope().resolve(token); - - expect(firstInstance).toBe(secondInstance); - expect(firstInstance).toBe(thirdInstance); - expect(secondInstance).toBe(thirdInstance); - }); - - test('Configuring a token as singleton then as transient updates the configuration', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerSingletonFactoryToToken(token, () => new MyClass()) - .registerTransientTypeToToken(token, MyClass) - .createScope(); - - const firstInstance = dependencyResolver.resolve(token); - const secondInstance = dependencyResolver.resolve(token); - const thirdInstance = dependencyResolver.createScope().resolve(token); - - expect(firstInstance).not.toBe(secondInstance); - expect(firstInstance).not.toBe(thirdInstance); - expect(secondInstance).not.toBe(thirdInstance); - }); - - test('Configuring a token as transient then as singleton updates the configuration', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerTransientTypeToToken(token, MyClass) - .registerInstanceToToken(token, {}) - .createScope(); - - const firstInstance = dependencyResolver.resolve(token); - const secondInstance = dependencyResolver.resolve(token); - const thirdInstance = dependencyResolver.createScope().resolve(token); - - expect(firstInstance).toBe(secondInstance); - expect(firstInstance).toBe(thirdInstance); - expect(secondInstance).toBe(thirdInstance); - }); - - test('Configuring a token as scoped then as transient updates the configuration', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerScopedFactoryToToken(token, () => new MyClass()) - .registerTransientFactoryToToken(token, () => new MyClass()) - .createScope(); - - const firstInstance = dependencyResolver.resolve(token); - const secondInstance = dependencyResolver.resolve(token); - const thirdInstance = dependencyResolver.createScope().resolve(token); - - expect(firstInstance).not.toBe(secondInstance); - expect(firstInstance).not.toBe(thirdInstance); - expect(secondInstance).not.toBe(thirdInstance); - }); - - test('Configuring a token as transient then as scoped updates the configuration', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } - - const dependencyResolver = new DependencyContainer() - .registerTransientTypeToToken(token, MyClass) - .registerScopedTypeToToken(token, MyClass) - .createScope(); - - const firstInstance = dependencyResolver.resolve(token); - const secondInstance = dependencyResolver.resolve(token); - const thirdInstance = dependencyResolver.createScope().resolve(token); - - expect(firstInstance).toBe(secondInstance); - expect(firstInstance).not.toBe(thirdInstance); - expect(secondInstance).not.toBe(thirdInstance); - }); - - test('Child dependency container overrides parent singleton configuraiton without affecting it', () => { - class MyClass { - } - - const parentDependencyContainer = new DependencyContainer() - .registerSingletonType(MyClass); - const childDependencyContainer = new DependencyContainer(parentDependencyContainer) - .registerTransientType(MyClass, () => new MyClass()); - - const firstInstanceFromParent = parentDependencyContainer.resolve(MyClass); - const secondInstanceFromParent = parentDependencyContainer.resolve(MyClass); - const firstInstanceFromChild = childDependencyContainer.resolve(MyClass); - const secondInstanceFromChild = childDependencyContainer.resolve(MyClass); - - expect(firstInstanceFromParent).toBe(secondInstanceFromParent); - - expect(firstInstanceFromParent).not.toBe(firstInstanceFromChild); - expect(firstInstanceFromParent).not.toBe(secondInstanceFromChild); - - expect(secondInstanceFromParent).not.toBe(firstInstanceFromChild); - expect(secondInstanceFromParent).not.toBe(secondInstanceFromChild); - - expect(firstInstanceFromChild).not.toBe(secondInstanceFromChild); - }); - - test('Child dependency container overrides parent transient configuraiton without affecting it', () => { - class MyClass { - } - - const parentDependencyContainer = new DependencyContainer() - .registerTransientType(MyClass, () => new MyClass()); - const childDependencyContainer = new DependencyContainer(parentDependencyContainer) - .registerSingletonType(MyClass); - - const firstInstanceFromParent = parentDependencyContainer.resolve(MyClass); - const secondInstanceFromParent = parentDependencyContainer.resolve(MyClass); - const firstInstanceFromChild = childDependencyContainer.resolve(MyClass); - const secondInstanceFromChild = childDependencyContainer.resolve(MyClass); - - expect(firstInstanceFromParent).not.toBe(secondInstanceFromParent); - - expect(firstInstanceFromParent).not.toBe(firstInstanceFromChild); - expect(firstInstanceFromParent).not.toBe(secondInstanceFromChild); - - expect(secondInstanceFromParent).not.toBe(firstInstanceFromChild); - expect(secondInstanceFromParent).not.toBe(secondInstanceFromChild); - - expect(firstInstanceFromChild).toBe(secondInstanceFromChild); - }); - - test('Child dependency container uses parent configuraiton as fallback', () => { - class MyFirstClass { - } - class MySecondClass { - public constructor(public instance: MyFirstClass) { - } - } - - const parentDependencyContainer = new DependencyContainer() - .registerSingletonType(MyFirstClass); - const childDependencyContainer = new DependencyContainer(parentDependencyContainer) - .registerScopedType(MySecondClass, ({ resolve }) => new MySecondClass(resolve(MyFirstClass))); - - const parentInstance = parentDependencyContainer.resolve(MyFirstClass); - const firstChildInstance = childDependencyContainer.resolve(MyFirstClass); - const secondChildInstance = childDependencyContainer.resolve(MySecondClass); - - expect(firstChildInstance).toBe(parentInstance); - expect(secondChildInstance.instance).toBe(parentInstance); - }); - - test('Child dependency container uses own configuraiton when overridden', () => { - class MyFirstClass { - } - class MySecondClass { - public constructor(public instance: MyFirstClass) { - } - } - - const parentDependencyContainer = new DependencyContainer() - .registerSingletonType(MyFirstClass); - const childDependencyContainer = new DependencyContainer(parentDependencyContainer) - .registerSingletonType(MyFirstClass) - .registerScopedType(MySecondClass, ({ resolve }) => new MySecondClass(resolve(MyFirstClass))); - - const parentInstance = parentDependencyContainer.resolve(MyFirstClass); - const firstChildInstance = childDependencyContainer.resolve(MyFirstClass); - const secondChildInstance = childDependencyContainer.resolve(MySecondClass); - - expect(firstChildInstance).not.toBe(parentInstance); - expect(secondChildInstance.instance).toBe(firstChildInstance); - }); + test('Configuring a type as singleton then as scoped updates the configuration', () => { + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerSingletonType(MyClass) + .registerScopedType(MyClass) + .createScope(); + + const firstInstance = dependencyResolver.resolve(MyClass); + const secondInstance = dependencyResolver.resolve(MyClass); + const thirdInstance = dependencyResolver.createScope() + .resolve(MyClass); + + expect(firstInstance) + .toBe(secondInstance); + expect(firstInstance).not.toBe(thirdInstance); + expect(secondInstance).not.toBe(thirdInstance); + }); + + test('Configuring a type as scoped then as singleton updates the configuration', () => { + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerScopedType(MyClass) + .registerSingletonType(MyClass) + .createScope(); + + const firstInstance = dependencyResolver.resolve(MyClass); + const secondInstance = dependencyResolver.resolve(MyClass); + const thirdInstance = dependencyResolver.createScope() + .resolve(MyClass); + + expect(firstInstance) + .toBe(secondInstance); + expect(firstInstance) + .toBe(thirdInstance); + expect(secondInstance) + .toBe(thirdInstance); + }); + + test('Configuring a type as singleton then as transient updates the configuration', () => { + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerSingletonType(MyClass) + .registerTransientType(MyClass, () => new MyClass()) + .createScope(); + + const firstInstance = dependencyResolver.resolve(MyClass); + const secondInstance = dependencyResolver.resolve(MyClass); + const thirdInstance = dependencyResolver.createScope() + .resolve(MyClass); + + expect(firstInstance).not.toBe(secondInstance); + expect(firstInstance).not.toBe(thirdInstance); + expect(secondInstance).not.toBe(thirdInstance); + }); + + test('Configuring a type as transient then as singleton updates the configuration', () => { + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerTransientType(MyClass, () => new MyClass()) + .registerSingletonType(MyClass) + .createScope(); + + const firstInstance = dependencyResolver.resolve(MyClass); + const secondInstance = dependencyResolver.resolve(MyClass); + const thirdInstance = dependencyResolver.createScope() + .resolve(MyClass); + + expect(firstInstance) + .toBe(secondInstance); + expect(firstInstance) + .toBe(thirdInstance); + expect(secondInstance) + .toBe(thirdInstance); + }); + + test('Configuring a type as scoped then as transient updates the configuration', () => { + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerScopedType(MyClass) + .registerTransientType(MyClass, () => new MyClass()) + .createScope(); + + const firstInstance = dependencyResolver.resolve(MyClass); + const secondInstance = dependencyResolver.resolve(MyClass); + const thirdInstance = dependencyResolver.createScope() + .resolve(MyClass); + + expect(firstInstance).not.toBe(secondInstance); + expect(firstInstance).not.toBe(thirdInstance); + expect(secondInstance).not.toBe(thirdInstance); + }); + + test('Configuring a type as transient then as scoped updates the configuration', () => { + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerTransientType(MyClass, () => new MyClass()) + .registerScopedType(MyClass) + .createScope(); + + const firstInstance = dependencyResolver.resolve(MyClass); + const secondInstance = dependencyResolver.resolve(MyClass); + const thirdInstance = dependencyResolver.createScope() + .resolve(MyClass); + + expect(firstInstance) + .toBe(secondInstance); + expect(firstInstance).not.toBe(thirdInstance); + expect(secondInstance).not.toBe(thirdInstance); + }); + + test('Configuring a token as singleton then as scoped updates the configuration', () => { + const token = new DependencyToken('test-dependency-token'); + + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerSingletonFactoryToToken(token, () => new MyClass()) + .registerScopedFactoryToToken(token, () => new MyClass()) + .createScope(); + + const firstInstance = dependencyResolver.resolve(token); + const secondInstance = dependencyResolver.resolve(token); + const thirdInstance = dependencyResolver.createScope() + .resolve(token); + + expect(firstInstance) + .toBe(secondInstance); + expect(firstInstance).not.toBe(thirdInstance); + expect(secondInstance).not.toBe(thirdInstance); + }); + + test('Configuring a token as scoped then as singleton updates the configuration', () => { + const token = new DependencyToken('test-dependency-token'); + + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerScopedTypeToToken(token, MyClass) + .registerSingletonTypeToToken(token, MyClass) + .createScope(); + + const firstInstance = dependencyResolver.resolve(token); + const secondInstance = dependencyResolver.resolve(token); + const thirdInstance = dependencyResolver.createScope() + .resolve(token); + + expect(firstInstance) + .toBe(secondInstance); + expect(firstInstance) + .toBe(thirdInstance); + expect(secondInstance) + .toBe(thirdInstance); + }); + + test('Configuring a token as singleton then as transient updates the configuration', () => { + const token = new DependencyToken('test-dependency-token'); + + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerSingletonFactoryToToken(token, () => new MyClass()) + .registerTransientTypeToToken(token, MyClass) + .createScope(); + + const firstInstance = dependencyResolver.resolve(token); + const secondInstance = dependencyResolver.resolve(token); + const thirdInstance = dependencyResolver.createScope() + .resolve(token); + + expect(firstInstance).not.toBe(secondInstance); + expect(firstInstance).not.toBe(thirdInstance); + expect(secondInstance).not.toBe(thirdInstance); + }); + + test('Configuring a token as transient then as singleton updates the configuration', () => { + const token = new DependencyToken('test-dependency-token'); + + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerTransientTypeToToken(token, MyClass) + .registerInstanceToToken(token, {}) + .createScope(); + + const firstInstance = dependencyResolver.resolve(token); + const secondInstance = dependencyResolver.resolve(token); + const thirdInstance = dependencyResolver.createScope() + .resolve(token); + + expect(firstInstance) + .toBe(secondInstance); + expect(firstInstance) + .toBe(thirdInstance); + expect(secondInstance) + .toBe(thirdInstance); + }); + + test('Configuring a token as scoped then as transient updates the configuration', () => { + const token = new DependencyToken('test-dependency-token'); + + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerScopedFactoryToToken(token, () => new MyClass()) + .registerTransientFactoryToToken(token, () => new MyClass()) + .createScope(); + + const firstInstance = dependencyResolver.resolve(token); + const secondInstance = dependencyResolver.resolve(token); + const thirdInstance = dependencyResolver.createScope() + .resolve(token); + + expect(firstInstance).not.toBe(secondInstance); + expect(firstInstance).not.toBe(thirdInstance); + expect(secondInstance).not.toBe(thirdInstance); + }); + + test('Configuring a token as transient then as scoped updates the configuration', () => { + const token = new DependencyToken('test-dependency-token'); + + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerTransientTypeToToken(token, MyClass) + .registerScopedTypeToToken(token, MyClass) + .createScope(); + + const firstInstance = dependencyResolver.resolve(token); + const secondInstance = dependencyResolver.resolve(token); + const thirdInstance = dependencyResolver.createScope() + .resolve(token); + + expect(firstInstance) + .toBe(secondInstance); + expect(firstInstance).not.toBe(thirdInstance); + expect(secondInstance).not.toBe(thirdInstance); + }); + + test('Child dependency container overrides parent singleton configuraiton without affecting it', () => { + class MyClass { + } + + const parentDependencyContainer = new DependencyContainer() + .registerSingletonType(MyClass); + const childDependencyContainer = new DependencyContainer(parentDependencyContainer) + .registerTransientType(MyClass, () => new MyClass()); + + const firstInstanceFromParent = parentDependencyContainer.resolve(MyClass); + const secondInstanceFromParent = parentDependencyContainer.resolve(MyClass); + const firstInstanceFromChild = childDependencyContainer.resolve(MyClass); + const secondInstanceFromChild = childDependencyContainer.resolve(MyClass); + + expect(firstInstanceFromParent) + .toBe(secondInstanceFromParent); + + expect(firstInstanceFromParent).not.toBe(firstInstanceFromChild); + expect(firstInstanceFromParent).not.toBe(secondInstanceFromChild); + + expect(secondInstanceFromParent).not.toBe(firstInstanceFromChild); + expect(secondInstanceFromParent).not.toBe(secondInstanceFromChild); + + expect(firstInstanceFromChild).not.toBe(secondInstanceFromChild); + }); + + test('Child dependency container overrides parent transient configuraiton without affecting it', () => { + class MyClass { + } + + const parentDependencyContainer = new DependencyContainer() + .registerTransientType(MyClass, () => new MyClass()); + const childDependencyContainer = new DependencyContainer(parentDependencyContainer) + .registerSingletonType(MyClass); + + const firstInstanceFromParent = parentDependencyContainer.resolve(MyClass); + const secondInstanceFromParent = parentDependencyContainer.resolve(MyClass); + const firstInstanceFromChild = childDependencyContainer.resolve(MyClass); + const secondInstanceFromChild = childDependencyContainer.resolve(MyClass); + + expect(firstInstanceFromParent).not.toBe(secondInstanceFromParent); + + expect(firstInstanceFromParent).not.toBe(firstInstanceFromChild); + expect(firstInstanceFromParent).not.toBe(secondInstanceFromChild); + + expect(secondInstanceFromParent).not.toBe(firstInstanceFromChild); + expect(secondInstanceFromParent).not.toBe(secondInstanceFromChild); + + expect(firstInstanceFromChild) + .toBe(secondInstanceFromChild); + }); + + test('Child dependency container uses parent configuraiton as fallback', () => { + class MyFirstClass { + } + + class MySecondClass { + public constructor(public instance: MyFirstClass) { + } + } + + const parentDependencyContainer = new DependencyContainer() + .registerSingletonType(MyFirstClass); + const childDependencyContainer = new DependencyContainer(parentDependencyContainer) + .registerScopedType(MySecondClass, ({ resolve }) => new MySecondClass(resolve(MyFirstClass))); + + const parentInstance = parentDependencyContainer.resolve(MyFirstClass); + const firstChildInstance = childDependencyContainer.resolve(MyFirstClass); + const secondChildInstance = childDependencyContainer.resolve(MySecondClass); + + expect(firstChildInstance) + .toBe(parentInstance); + expect(secondChildInstance.instance) + .toBe(parentInstance); + }); + + test('Child dependency container uses own configuraiton when overridden', () => { + class MyFirstClass { + } + + class MySecondClass { + public constructor(public instance: MyFirstClass) { + } + } + + const parentDependencyContainer = new DependencyContainer() + .registerSingletonType(MyFirstClass); + const childDependencyContainer = new DependencyContainer(parentDependencyContainer) + .registerSingletonType(MyFirstClass) + .registerScopedType(MySecondClass, ({ resolve }) => new MySecondClass(resolve(MyFirstClass))); + + const parentInstance = parentDependencyContainer.resolve(MyFirstClass); + const firstChildInstance = childDependencyContainer.resolve(MyFirstClass); + const secondChildInstance = childDependencyContainer.resolve(MySecondClass); + + expect(firstChildInstance).not.toBe(parentInstance); + expect(secondChildInstance.instance) + .toBe(firstChildInstance); + }); }); \ No newline at end of file diff --git a/src/dependencies/__tests__/DependencyContainer.scope.test.ts b/src/dependencies/__tests__/DependencyContainer.scope.test.ts index 269b289..d3c6538 100644 --- a/src/dependencies/__tests__/DependencyContainer.scope.test.ts +++ b/src/dependencies/__tests__/DependencyContainer.scope.test.ts @@ -1,259 +1,326 @@ -import { DependencyToken } from '../IDependencyResolver'; import { DependencyContainer } from '../DependencyContainer'; +import { DependencyToken } from '../IDependencyResolver'; describe('DependencyContainer.scope', (): void => { - test('Resolving scoped type dependency returns instance', () => { - class MyClass { - } + test('Resolving scoped type dependency returns instance', () => { + class MyClass { + } + + const scopedDependencyResolver = new DependencyContainer() + .registerScopedType(MyClass) + .createScope(); + + const instance = scopedDependencyResolver.resolve(MyClass); + + expect(instance) + .toBeInstanceOf(MyClass); + }); + + test('Resolving scoped type dependency returns same instance', () => { + class MyClass { + } + + const scopedDependencyResolver = new DependencyContainer() + .registerScopedType(MyClass) + .createScope(); + + const firstInstance = scopedDependencyResolver.resolve(MyClass); + const secondInstance = scopedDependencyResolver.resolve(MyClass); + + expect(firstInstance) + .toBe(secondInstance); + }); + + test('Resolving singleton type dependency returns same instance across scopes', () => { + class MyClass { + } + + const dependencyResolver = new DependencyContainer() + .registerSingletonType(MyClass); + + const firstInstance = dependencyResolver.resolve(MyClass); + const secondInstance = dependencyResolver.createScope() + .resolve(MyClass); + const thirdInstance = dependencyResolver.createScope() + .resolve(MyClass); + + expect(firstInstance) + .toBe(secondInstance); + expect(secondInstance) + .toBe(thirdInstance); + }); + + test('Resolving scoped type dependency returns different instance across scopes', () => { + class MyClass { + } + + const dependencyContainer = new DependencyContainer() + .registerScopedType(MyClass); + + const firstInstance = dependencyContainer.createScope() + .resolve(MyClass); + const secondInstance = dependencyContainer.createScope() + .resolve(MyClass); + + expect(firstInstance).not.toBe(secondInstance); + }); + + test('Resolving scoped type dependency returns different instance from parent scope', () => { + class MyClass { + } + + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerScopedType(MyClass); + const scopedDependencyResolver = dependencyContainer.createScope(); + + const firstInstance = dependencyContainer.resolve(MyClass); + const secondInstance = scopedDependencyResolver.resolve(MyClass); + + expect(firstInstance).not.toBe(secondInstance); + }); + + test('Resolving scoped type dependency calls factory once', () => { + class MyClass { + + } + + let callCount = 0; + + const scopedDependencyResolver = new DependencyContainer() + .registerScopedType(MyClass, () => { + callCount++; + + return new MyClass(); + }) + .createScope(); - const scopedDependencyResolver = new DependencyContainer() - .registerScopedType(MyClass) - .createScope(); + scopedDependencyResolver.resolve(MyClass); + scopedDependencyResolver.resolve(MyClass); - const instance = scopedDependencyResolver.resolve(MyClass); + expect(callCount) + .toStrictEqual(1); + }); - expect(instance).toBeInstanceOf(MyClass); - }); + test('Resolving scoped type dependency calls factory once per scope', () => { + class MyClass { - test('Resolving scoped type dependency returns same instance', () => { - class MyClass { - } + } - const scopedDependencyResolver = new DependencyContainer() - .registerScopedType(MyClass) - .createScope(); + let callCount = 0; - const firstInstance = scopedDependencyResolver.resolve(MyClass); - const secondInstance = scopedDependencyResolver.resolve(MyClass); + const dependencyContainer = new DependencyContainer() + .registerScopedType(MyClass, () => { + callCount++; - expect(firstInstance).toBe(secondInstance); - }); + return new MyClass(); + }); - test('Resolving singleton type dependency returns same instance across scopes', () => { - class MyClass { - } + dependencyContainer.createScope() + .resolve(MyClass); + dependencyContainer.createScope() + .resolve(MyClass); - const dependencyResolver = new DependencyContainer() - .registerSingletonType(MyClass); + expect(callCount) + .toStrictEqual(2); + }); - const firstInstance = dependencyResolver.resolve(MyClass); - const secondInstance = dependencyResolver.createScope().resolve(MyClass); - const thirdInstance = dependencyResolver.createScope().resolve(MyClass); + test('Resolving scoped type dependency returns cached instance', () => { + class MyClass { - expect(firstInstance).toBe(secondInstance); - expect(secondInstance).toBe(thirdInstance); - }); + } - test('Resolving scoped type dependency returns different instance across scopes', () => { - class MyClass { - } + const instance = {}; - const dependencyContainer = new DependencyContainer().registerScopedType(MyClass); + const scopedDependencyResolver = new DependencyContainer() + .registerScopedType(MyClass, () => instance) + .createScope(); - const firstInstance = dependencyContainer.createScope().resolve(MyClass); - const secondInstance = dependencyContainer.createScope().resolve(MyClass); + const firstInstance = scopedDependencyResolver.resolve(MyClass); + const secondInstance = scopedDependencyResolver.resolve(MyClass); - expect(firstInstance).not.toBe(secondInstance); - }); + expect(firstInstance) + .toBe(instance); + expect(secondInstance) + .toBe(instance); + }); - test('Resolving scoped type dependency returns different instance from parent scope', () => { - class MyClass { - } + test('Resolving scoped type dependency returns cached instance across scopes', () => { + class MyClass { - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerScopedType(MyClass); - const scopedDependencyResolver = dependencyContainer.createScope(); + } - const firstInstance = dependencyContainer.resolve(MyClass); - const secondInstance = scopedDependencyResolver.resolve(MyClass); + const instance = {}; - expect(firstInstance).not.toBe(secondInstance); - }); + const dependnecyContainer = new DependencyContainer() + .registerScopedType(MyClass, () => instance); - test('Resolving scoped type dependency calls factory once', () => { - class MyClass { } - let callCount = 0; + const firstInstance = dependnecyContainer.createScope() + .resolve(MyClass); + const secondInstance = dependnecyContainer.createScope() + .resolve(MyClass); - const scopedDependencyResolver = new DependencyContainer() - .registerScopedType(MyClass, () => { - callCount++; - return new MyClass(); - }) - .createScope(); + expect(firstInstance) + .toBe(instance); + expect(secondInstance) + .toBe(instance); + }); - scopedDependencyResolver.resolve(MyClass); - scopedDependencyResolver.resolve(MyClass); + test('Resolving scoped unconfigured token dependency throws exception', () => { + const token = new DependencyToken('test-dependency-token'); - expect(callCount).toStrictEqual(1); - }); + const scopedDependencyResolver = new DependencyContainer() + .createScope(); - test('Resolving scoped type dependency calls factory once per scope', () => { - class MyClass { } - let callCount = 0; + expect(() => scopedDependencyResolver.resolve(token)) + .toThrow(new Error('There is no configured dependency for token \'test-dependency-token\'.')); + }); - const dependencyContainer = new DependencyContainer() - .registerScopedType(MyClass, () => { - callCount++; - return new MyClass(); - }); + test('Resolving scoped type token dependency returns instance', () => { + const token = new DependencyToken('test-dependency-token'); - dependencyContainer.createScope().resolve(MyClass); - dependencyContainer.createScope().resolve(MyClass); + class MyClass { - expect(callCount).toStrictEqual(2); - }); + } - test('Resolving scoped type dependency returns cached instance', () => { - class MyClass { } - const instance = {}; + const scopedDependencyResolver = new DependencyContainer() + .registerScopedTypeToToken(token, MyClass) + .createScope(); - const scopedDependencyResolver = new DependencyContainer() - .registerScopedType(MyClass, () => instance) - .createScope(); + const instance = scopedDependencyResolver.resolve(token); - const firstInstance = scopedDependencyResolver.resolve(MyClass); - const secondInstance = scopedDependencyResolver.resolve(MyClass); + expect(instance) + .toBeInstanceOf(MyClass); + }); - expect(firstInstance).toBe(instance); - expect(secondInstance).toBe(instance); - }); + test('Resolving scoped type token dependency returns same instance', () => { + const token = new DependencyToken('test-dependency-token'); - test('Resolving scoped type dependency returns cached instance across scopes', () => { - class MyClass { } - const instance = {}; + class MyClass { + } - const dependnecyContainer = new DependencyContainer() - .registerScopedType(MyClass, () => instance); + const scopedDependencyResolver = new DependencyContainer() + .registerScopedTypeToToken(token, MyClass) + .createScope(); - const firstInstance = dependnecyContainer.createScope().resolve(MyClass); - const secondInstance = dependnecyContainer.createScope().resolve(MyClass); + const firstInstance = scopedDependencyResolver.resolve(token); + const secondInstance = scopedDependencyResolver.resolve(token); - expect(firstInstance).toBe(instance); - expect(secondInstance).toBe(instance); - }); + expect(firstInstance) + .toBe(secondInstance); + }); - test('Resolving scoped unconfigured token dependency throws exception', () => { - const token = new DependencyToken('test-dependency-token'); + test('Resolving scoped type token dependency returns different instances across scopes', () => { + const token = new DependencyToken('test-dependency-token'); - const scopedDependencyResolver = new DependencyContainer().createScope(); + class MyClass { + } - expect(() => scopedDependencyResolver.resolve(token)).toThrow(new Error('There is no configured dependency for token \'test-dependency-token\'.')); - }); + const dependnecyContainer = new DependencyContainer() + .registerScopedTypeToToken(token, MyClass); - test('Resolving scoped type token dependency returns instance', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { } + const firstInstance = dependnecyContainer.createScope() + .resolve(token); + const secondInstance = dependnecyContainer.createScope() + .resolve(token); - const scopedDependencyResolver = new DependencyContainer() - .registerScopedTypeToToken(token, MyClass) - .createScope(); + expect(firstInstance).not.toBe(secondInstance); + }); - const instance = scopedDependencyResolver.resolve(token); + test('Resolving scoped factory token dependency returns instance', () => { + const token = new DependencyToken('test-dependency-token'); - expect(instance).toBeInstanceOf(MyClass); - }); + class MyClass { - test('Resolving scoped type token dependency returns same instance', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } + } - const scopedDependencyResolver = new DependencyContainer() - .registerScopedTypeToToken(token, MyClass) - .createScope(); + const scopedDependencyResolver = new DependencyContainer() + .registerScopedFactoryToToken(token, () => new MyClass()) + .createScope(); - const firstInstance = scopedDependencyResolver.resolve(token); - const secondInstance = scopedDependencyResolver.resolve(token); + const instance = scopedDependencyResolver.resolve(token); - expect(firstInstance).toBe(secondInstance); - }); + expect(instance) + .toBeInstanceOf(MyClass); + }); - test('Resolving scoped type token dependency returns different instances across scopes', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } + test('Resolving scoped factory token dependency returns same instance', () => { + const token = new DependencyToken('test-dependency-token'); - const dependnecyContainer = new DependencyContainer().registerScopedTypeToToken(token, MyClass); + class MyClass { + } - const firstInstance = dependnecyContainer.createScope().resolve(token); - const secondInstance = dependnecyContainer.createScope().resolve(token); + const scopedDependencyResolver = new DependencyContainer() + .registerScopedFactoryToToken(token, () => new MyClass()) + .createScope(); - expect(firstInstance).not.toBe(secondInstance); - }); + const firstInstance = scopedDependencyResolver.resolve(token); + const secondInstance = scopedDependencyResolver.resolve(token); - test('Resolving scoped factory token dependency returns instance', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { } + expect(firstInstance) + .toBe(secondInstance); + }); - const scopedDependencyResolver = new DependencyContainer() - .registerScopedFactoryToToken(token, () => new MyClass()) - .createScope(); + test('Resolving scoped factory token dependency returns different instances across scopes', () => { + const token = new DependencyToken('test-dependency-token'); - const instance = scopedDependencyResolver.resolve(token); + class MyClass { + } - expect(instance).toBeInstanceOf(MyClass); - }); + const dependencyContainer = new DependencyContainer() + .registerScopedFactoryToToken(token, () => new MyClass()); - test('Resolving scoped factory token dependency returns same instance', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } + const firstInstance = dependencyContainer.createScope() + .resolve(token); + const secondInstance = dependencyContainer.createScope() + .resolve(token); - const scopedDependencyResolver = new DependencyContainer() - .registerScopedFactoryToToken(token, () => new MyClass()) - .createScope(); + expect(firstInstance).not.toBe(secondInstance); + }); - const firstInstance = scopedDependencyResolver.resolve(token); - const secondInstance = scopedDependencyResolver.resolve(token); + test('Resolving scoped factory token dependency calls factory once', () => { + let callCount = 0; + const token = new DependencyToken('test-dependency-token'); - expect(firstInstance).toBe(secondInstance); - }); + class MyClass { - test('Resolving scoped factory token dependency returns different instances across scopes', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } + } - const dependencyContainer = new DependencyContainer().registerScopedFactoryToToken(token, () => new MyClass()); + const scopedDependencyResolver = new DependencyContainer() + .registerScopedFactoryToToken(token, () => { + callCount++; - const firstInstance = dependencyContainer.createScope().resolve(token); - const secondInstance = dependencyContainer.createScope().resolve(token); + return new MyClass(); + }) + .createScope(); - expect(firstInstance).not.toBe(secondInstance); - }); + scopedDependencyResolver.resolve(token); + scopedDependencyResolver.resolve(token); - test('Resolving scoped factory token dependency calls factory once', () => { - let callCount = 0; - const token = new DependencyToken('test-dependency-token'); - class MyClass { } + expect(callCount) + .toStrictEqual(1); + }); - const scopedDependencyResolver = new DependencyContainer() - .registerScopedFactoryToToken(token, () => { - callCount++; - return new MyClass(); - }) - .createScope(); + test('Resolving scoped factory token dependency calls factory once per scope', () => { + let callCount = 0; + const token = new DependencyToken('test-dependency-token'); - scopedDependencyResolver.resolve(token); - scopedDependencyResolver.resolve(token); + class MyClass { - expect(callCount).toStrictEqual(1); - }); + } - test('Resolving scoped factory token dependency calls factory once per scope', () => { - let callCount = 0; - const token = new DependencyToken('test-dependency-token'); - class MyClass { } + const dependencyContainer = new DependencyContainer() + .registerScopedFactoryToToken(token, () => { + callCount++; - const dependencyContainer = new DependencyContainer() - .registerScopedFactoryToToken(token, () => { - callCount++; - return new MyClass(); - }); + return new MyClass(); + }); - dependencyContainer.createScope().resolve(token); - dependencyContainer.createScope().resolve(token); + dependencyContainer.createScope() + .resolve(token); + dependencyContainer.createScope() + .resolve(token); - expect(callCount).toStrictEqual(2); - }); + expect(callCount) + .toStrictEqual(2); + }); }); \ No newline at end of file diff --git a/src/dependencies/__tests__/DependencyContainer.singleton.test.ts b/src/dependencies/__tests__/DependencyContainer.singleton.test.ts index 239a2b8..ddac9d0 100644 --- a/src/dependencies/__tests__/DependencyContainer.singleton.test.ts +++ b/src/dependencies/__tests__/DependencyContainer.singleton.test.ts @@ -1,140 +1,170 @@ -import { DependencyToken } from '../IDependencyResolver'; import { DependencyContainer } from '../DependencyContainer'; +import { DependencyToken } from '../IDependencyResolver'; describe('DependencyContainer.singleton', (): void => { - test('Resolving singleton type dependency returns instance', () => { - class MyClass { - } + test('Resolving singleton type dependency returns instance', () => { + class MyClass { + } + + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerSingletonType(MyClass); + + const instance = dependencyContainer.resolve(MyClass); + + expect(instance) + .toBeInstanceOf(MyClass); + }); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerSingletonType(MyClass); + test('Resolving singleton type dependency returns same instance', () => { + class MyClass { + } - const instance = dependencyContainer.resolve(MyClass); + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerSingletonType(MyClass); - expect(instance).toBeInstanceOf(MyClass); - }); + const firstInstance = dependencyContainer.resolve(MyClass); + const secondInstance = dependencyContainer.resolve(MyClass); - test('Resolving singleton type dependency returns same instance', () => { - class MyClass { - } + expect(firstInstance) + .toBe(secondInstance); + }); + + test('Resolving singleton type dependency calls factory once', () => { + class MyClass { + + } - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerSingletonType(MyClass); + let callCount = 0; - const firstInstance = dependencyContainer.resolve(MyClass); - const secondInstance = dependencyContainer.resolve(MyClass); + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerSingletonType(MyClass, () => { + callCount++; - expect(firstInstance).toBe(secondInstance); - }); + return new MyClass(); + }); - test('Resolving singleton type dependency calls factory once', () => { - class MyClass { } - let callCount = 0; + dependencyContainer.resolve(MyClass); + dependencyContainer.resolve(MyClass); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerSingletonType(MyClass, () => { - callCount++; - return new MyClass(); + expect(callCount) + .toStrictEqual(1); }); - dependencyContainer.resolve(MyClass); - dependencyContainer.resolve(MyClass); + test('Resolving singleton type dependency returns same instance', () => { + class MyClass { + + } + + const instance = {}; + + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerSingletonType(MyClass, () => instance); - expect(callCount).toStrictEqual(1); - }); + const firstInstance = dependencyContainer.resolve(MyClass); + const secondInstance = dependencyContainer.resolve(MyClass); - test('Resolving singleton type dependency returns same instance', () => { - class MyClass { } - const instance = {}; + expect(firstInstance) + .toBe(instance); + expect(secondInstance) + .toBe(instance); + }); + + test('Resolving singleton type token dependency returns instance', () => { + const token = new DependencyToken('test-dependency-token'); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerSingletonType(MyClass, () => instance); + class MyClass { - const firstInstance = dependencyContainer.resolve(MyClass); - const secondInstance = dependencyContainer.resolve(MyClass); + } - expect(firstInstance).toBe(instance); - expect(secondInstance).toBe(instance); - }); + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerSingletonTypeToToken(token, MyClass); - test('Resolving singleton type token dependency returns instance', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { } + const instance = dependencyContainer.resolve(token); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerSingletonTypeToToken(token, MyClass); + expect(instance) + .toBeInstanceOf(MyClass); + }); - const instance = dependencyContainer.resolve(token); + test('Resolving singleton type token dependency returns same instance', () => { + const token = new DependencyToken('test-dependency-token'); - expect(instance).toBeInstanceOf(MyClass); - }); + class MyClass { + } - test('Resolving singleton type token dependency returns same instance', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerSingletonTypeToToken(token, MyClass); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerSingletonTypeToToken(token, MyClass); + const firstInstance = dependencyContainer.resolve(token); + const secondInstance = dependencyContainer.resolve(token); - const firstInstance = dependencyContainer.resolve(token); - const secondInstance = dependencyContainer.resolve(token); + expect(firstInstance) + .toBe(secondInstance); + }); - expect(firstInstance).toBe(secondInstance); - }); + test('Resolving instance token dependency returns same instance', () => { + const token = new DependencyToken('test-dependency-token'); + const instance = {}; - test('Resolving instance token dependency returns same instance', () => { - const token = new DependencyToken('test-dependency-token'); - const instance = {}; + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerInstanceToToken(token, instance); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerInstanceToToken(token, instance); + const resolvedInstance = dependencyContainer.resolve(token); - const resolvedInstance = dependencyContainer.resolve(token); + expect(resolvedInstance) + .toBe(instance); + }); - expect(resolvedInstance).toBe(instance); - }); + test('Resolving singleton factory token dependency returns instance', () => { + const token = new DependencyToken('test-dependency-token'); - test('Resolving singleton factory token dependency returns instance', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { } + class MyClass { - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerSingletonFactoryToToken(token, () => new MyClass()); + } - const instance = dependencyContainer.resolve(token); + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerSingletonFactoryToToken(token, () => new MyClass()); - expect(instance).toBeInstanceOf(MyClass); - }); + const instance = dependencyContainer.resolve(token); - test('Resolving singleton factory token dependency returns same instance', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } + expect(instance) + .toBeInstanceOf(MyClass); + }); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerSingletonFactoryToToken(token, () => new MyClass()); + test('Resolving singleton factory token dependency returns same instance', () => { + const token = new DependencyToken('test-dependency-token'); - const firstInstance = dependencyContainer.resolve(token); - const secondInstance = dependencyContainer.resolve(token); + class MyClass { + } - expect(firstInstance).toBe(secondInstance); - }); + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerSingletonFactoryToToken(token, () => new MyClass()); - test('Resolving singleton factory token dependency calls factory once', () => { - let callCount = 0; - const token = new DependencyToken('test-dependency-token'); - class MyClass { } + const firstInstance = dependencyContainer.resolve(token); + const secondInstance = dependencyContainer.resolve(token); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerSingletonFactoryToToken(token, () => { - callCount++; - return new MyClass(); + expect(firstInstance) + .toBe(secondInstance); }); - dependencyContainer.resolve(token); - dependencyContainer.resolve(token); + test('Resolving singleton factory token dependency calls factory once', () => { + let callCount = 0; + const token = new DependencyToken('test-dependency-token'); - expect(callCount).toStrictEqual(1); - }); + class MyClass { + + } + + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerSingletonFactoryToToken(token, () => { + callCount++; + + return new MyClass(); + }); + + dependencyContainer.resolve(token); + dependencyContainer.resolve(token); + + expect(callCount) + .toStrictEqual(1); + }); }); \ No newline at end of file diff --git a/src/dependencies/__tests__/DependencyContainer.transient.test.ts b/src/dependencies/__tests__/DependencyContainer.transient.test.ts index c6143f8..c42ecdd 100644 --- a/src/dependencies/__tests__/DependencyContainer.transient.test.ts +++ b/src/dependencies/__tests__/DependencyContainer.transient.test.ts @@ -1,114 +1,135 @@ -import { DependencyToken } from '../IDependencyResolver'; import { DependencyContainer } from '../DependencyContainer'; +import { DependencyToken } from '../IDependencyResolver'; describe('DependencyContainer.transient', (): void => { - test('Resolving transient type dependency returns instance', () => { - class MyClass { - } + test('Resolving transient type dependency returns instance', () => { + class MyClass { + } + + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerTransientType(MyClass, () => new MyClass()); + + const instance = dependencyContainer.resolve(MyClass); + + expect(instance) + .toBeInstanceOf(MyClass); + }); + + test('Resolving transient type dependency returns different instances', () => { + class MyClass { + } - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerTransientType(MyClass, () => new MyClass()); + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerTransientType(MyClass, () => new MyClass()); - const instance = dependencyContainer.resolve(MyClass); + const firstInstance = dependencyContainer.resolve(MyClass); + const secondInstance = dependencyContainer.resolve(MyClass); - expect(instance).toBeInstanceOf(MyClass); - }); + expect(firstInstance).not.toBe(secondInstance); + }); - test('Resolving transient type dependency returns different instances', () => { - class MyClass { - } + test('Resolving transient type dependency calls factory each time', () => { + class MyClass { - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerTransientType(MyClass, () => new MyClass()); + } - const firstInstance = dependencyContainer.resolve(MyClass); - const secondInstance = dependencyContainer.resolve(MyClass); + let callCount = 0; - expect(firstInstance).not.toBe(secondInstance); - }); + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerTransientType(MyClass, () => { + callCount++; - test('Resolving transient type dependency calls factory each time', () => { - class MyClass { } - let callCount = 0; + return new MyClass(); + }); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerTransientType(MyClass, () => { - callCount++; - return new MyClass(); + dependencyContainer.resolve(MyClass); + dependencyContainer.resolve(MyClass); + + expect(callCount) + .toStrictEqual(2); }); - dependencyContainer.resolve(MyClass); - dependencyContainer.resolve(MyClass); + test('Resolving transient type token dependency returns instance', () => { + const token = new DependencyToken('test-dependency-token'); + + class MyClass { - expect(callCount).toStrictEqual(2); - }); + } - test('Resolving transient type token dependency returns instance', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { } + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerTransientTypeToToken(token, MyClass); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerTransientTypeToToken(token, MyClass); + const instance = dependencyContainer.resolve(token); - const instance = dependencyContainer.resolve(token); + expect(instance) + .toBeInstanceOf(MyClass); + }); - expect(instance).toBeInstanceOf(MyClass); - }); + test('Resolving transient type token dependency returns different instances', () => { + const token = new DependencyToken('test-dependency-token'); - test('Resolving transient type token dependency returns different instances', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } + class MyClass { + } - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerTransientTypeToToken(token, MyClass); + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerTransientTypeToToken(token, MyClass); - const firstInstance = dependencyContainer.resolve(token); - const secondInstance = dependencyContainer.resolve(token); + const firstInstance = dependencyContainer.resolve(token); + const secondInstance = dependencyContainer.resolve(token); - expect(firstInstance).not.toBe(secondInstance); - }); + expect(firstInstance).not.toBe(secondInstance); + }); - test('Resolving transient factory token dependency returns instance', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { } + test('Resolving transient factory token dependency returns instance', () => { + const token = new DependencyToken('test-dependency-token'); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerTransientFactoryToToken(token, () => new MyClass()); + class MyClass { - const instance = dependencyContainer.resolve(token); + } - expect(instance).toBeInstanceOf(MyClass); - }); + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerTransientFactoryToToken(token, () => new MyClass()); - test('Resolving transient factory token dependency returns different instances', () => { - const token = new DependencyToken('test-dependency-token'); - class MyClass { - } + const instance = dependencyContainer.resolve(token); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerTransientFactoryToToken(token, () => new MyClass()); + expect(instance) + .toBeInstanceOf(MyClass); + }); - const firstInstance = dependencyContainer.resolve(token); - const secondInstance = dependencyContainer.resolve(token); + test('Resolving transient factory token dependency returns different instances', () => { + const token = new DependencyToken('test-dependency-token'); - expect(firstInstance).not.toBe(secondInstance); - }); + class MyClass { + } - test('Resolving transient factory token dependency calls factory each time', () => { - let callCount = 0; - const token = new DependencyToken('test-dependency-token'); - class MyClass { } + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerTransientFactoryToToken(token, () => new MyClass()); - const dependencyContainer = new DependencyContainer(); - dependencyContainer.registerTransientFactoryToToken(token, () => { - callCount++; - return new MyClass(); + const firstInstance = dependencyContainer.resolve(token); + const secondInstance = dependencyContainer.resolve(token); + + expect(firstInstance).not.toBe(secondInstance); }); - dependencyContainer.resolve(token); - dependencyContainer.resolve(token); + test('Resolving transient factory token dependency calls factory each time', () => { + let callCount = 0; + const token = new DependencyToken('test-dependency-token'); + + class MyClass { - expect(callCount).toStrictEqual(2); - }); + } + + const dependencyContainer = new DependencyContainer(); + dependencyContainer.registerTransientFactoryToToken(token, () => { + callCount++; + + return new MyClass(); + }); + + dependencyContainer.resolve(token); + dependencyContainer.resolve(token); + + expect(callCount) + .toStrictEqual(2); + }); }); \ No newline at end of file diff --git a/src/dependencies/__tests__/DependencyContainer.unconfigured.test.ts b/src/dependencies/__tests__/DependencyContainer.unconfigured.test.ts index d9c0623..5df2015 100644 --- a/src/dependencies/__tests__/DependencyContainer.unconfigured.test.ts +++ b/src/dependencies/__tests__/DependencyContainer.unconfigured.test.ts @@ -1,170 +1,186 @@ -import { type IDependencyResolver, DependencyToken } from '../IDependencyResolver'; import { DependencyContainer } from '../DependencyContainer'; +import { type IDependencyResolver, DependencyToken } from '../IDependencyResolver'; describe('DependencyContainer.unconfigured', (): void => { - test('Resolve works without context', () => { - const { resolve } = new DependencyContainer(); + test('Resolve works without context', () => { + const { resolve } = new DependencyContainer(); + + const instance = resolve(class { + + }); - const instance = resolve(class { }); + expect(instance).not.toBeNull(); + }); - expect(instance).not.toBeNull(); - }); + test('Resolving null dependency returns null', () => { + const dependencyContainer = new DependencyContainer(); - test('Resolving null dependency returns null', () => { - const dependencyContainer = new DependencyContainer(); + const resolvedDependency = dependencyContainer.resolve(null); - const resolvedDependency = dependencyContainer.resolve(null); + expect(resolvedDependency) + .toBeNull(); + }); - expect(resolvedDependency).toBeNull(); - }); + test('Resolving undefined dependency returns undefined', () => { + const dependencyContainer = new DependencyContainer(); - test('Resolving undefined dependency returns undefined', () => { - const dependencyContainer = new DependencyContainer(); + const resolvedDependency = dependencyContainer.resolve(undefined); - const resolvedDependency = dependencyContainer.resolve(undefined); + expect(resolvedDependency) + .toBeUndefined(); + }); - expect(resolvedDependency).toBeUndefined(); - }); + test('Resolving object dependency returns object', () => { + const instnace = {}; + const dependencyContainer = new DependencyContainer(); - test('Resolving object dependency returns object', () => { - const instnace = {}; - const dependencyContainer = new DependencyContainer(); + const resolvedDependency = dependencyContainer.resolve(instnace); - const resolvedDependency = dependencyContainer.resolve(instnace); + expect(resolvedDependency) + .toStrictEqual(instnace); + }); - expect(resolvedDependency).toStrictEqual(instnace); - }); + test('Resolving a basic dependency returns instance', () => { + class MyClass { - test('Resolving a basic dependency returns instance', () => { - class MyClass { } + } - const dependencyContainer = new DependencyContainer(); + const dependencyContainer = new DependencyContainer(); - const instance = dependencyContainer.resolve(MyClass); + const instance = dependencyContainer.resolve(MyClass); - expect(instance).toBeInstanceOf(MyClass); - }); + expect(instance) + .toBeInstanceOf(MyClass); + }); - test('Resolving a simple dependency returns instance', () => { - class MyClass { - constructor(dependencyResolver: IDependencyResolver) { - } - } + test('Resolving a simple dependency returns instance', () => { + class MyClass { + constructor(dependencyResolver: IDependencyResolver) { + } + } - const dependencyContainer = new DependencyContainer(); + const dependencyContainer = new DependencyContainer(); - const instance = dependencyContainer.resolve(MyClass); + const instance = dependencyContainer.resolve(MyClass); - expect(instance).toBeInstanceOf(MyClass); - }); + expect(instance) + .toBeInstanceOf(MyClass); + }); - test('Resolving a simple dependency receives dependecy container as first constructor parameter', () => { - let receivedDependencyResolver: IDependencyResolver | null = null; + test('Resolving a simple dependency receives dependecy container as first constructor parameter', () => { + let receivedDependencyResolver: IDependencyResolver | null = null; - class MyClass { - constructor(dependencyResolver: IDependencyResolver) { - receivedDependencyResolver = dependencyResolver; - } - } + class MyClass { + constructor(dependencyResolver: IDependencyResolver) { + receivedDependencyResolver = dependencyResolver; + } + } - const dependencyContainer = new DependencyContainer(); + const dependencyContainer = new DependencyContainer(); - dependencyContainer.resolve(MyClass); + dependencyContainer.resolve(MyClass); - expect(receivedDependencyResolver).toBe(dependencyContainer); - }); + expect(receivedDependencyResolver) + .toBe(dependencyContainer); + }); - test('Resolving a complex dependency returns instance', () => { - class MyClass { - constructor(dependencyResolver: IDependencyResolver, id: number) { - } - } + test('Resolving a complex dependency returns instance', () => { + class MyClass { + constructor(dependencyResolver: IDependencyResolver, id: number) { + } + } - const dependencyContainer = new DependencyContainer(); + const dependencyContainer = new DependencyContainer(); - const instance = dependencyContainer.resolve(MyClass, [1]); + const instance = dependencyContainer.resolve(MyClass, [1]); - expect(instance).toBeInstanceOf(MyClass); - }); + expect(instance) + .toBeInstanceOf(MyClass); + }); - test('Resolving a complex dependency receives dependecy container as first constructor parameter', () => { - let receivedDependencyResolver: IDependencyResolver | null = null; + test('Resolving a complex dependency receives dependecy container as first constructor parameter', () => { + let receivedDependencyResolver: IDependencyResolver | null = null; - class MyClass { - constructor(dependencyResolver: IDependencyResolver, id: number) { - receivedDependencyResolver = dependencyResolver; - } - } + class MyClass { + constructor(dependencyResolver: IDependencyResolver, id: number) { + receivedDependencyResolver = dependencyResolver; + } + } - const dependencyContainer = new DependencyContainer(); + const dependencyContainer = new DependencyContainer(); - dependencyContainer.resolve(MyClass, [1]); + dependencyContainer.resolve(MyClass, [1]); - expect(receivedDependencyResolver).toBe(dependencyContainer); - }); + expect(receivedDependencyResolver) + .toBe(dependencyContainer); + }); - test('Resolving a complex dependency receives additional dependency as second constructor parameter', () => { - let receivedAdditionalDependency: object | null = null; - const additionalDependency = {}; + test('Resolving a complex dependency receives additional dependency as second constructor parameter', () => { + let receivedAdditionalDependency: object | null = null; + const additionalDependency = {}; - class MyClass { - constructor(dependencyResolver: IDependencyResolver, additionalDependency: object) { - receivedAdditionalDependency = additionalDependency; - } - } + class MyClass { + constructor(dependencyResolver: IDependencyResolver, additionalDependency: object) { + receivedAdditionalDependency = additionalDependency; + } + } - const dependencyContainer = new DependencyContainer(); + const dependencyContainer = new DependencyContainer(); - dependencyContainer.resolve(MyClass, [additionalDependency]); + dependencyContainer.resolve(MyClass, [additionalDependency]); - expect(receivedAdditionalDependency).toBe(additionalDependency); - }); + expect(receivedAdditionalDependency) + .toBe(additionalDependency); + }); - test('Resolving a complex dependency receives additional dependency as third constructor parameter', () => { - let receivedAdditionalDependency: object | null = null; - const additionalDependency = {}; + test('Resolving a complex dependency receives additional dependency as third constructor parameter', () => { + let receivedAdditionalDependency: object | null = null; + const additionalDependency = {}; - class MyClass { - constructor(dependencyResolver: IDependencyResolver, id: number, additionalDependency: object) { - receivedAdditionalDependency = additionalDependency; - } - } + class MyClass { + constructor(dependencyResolver: IDependencyResolver, id: number, additionalDependency: object) { + receivedAdditionalDependency = additionalDependency; + } + } - const dependencyContainer = new DependencyContainer(); + const dependencyContainer = new DependencyContainer(); - dependencyContainer.resolve(MyClass, [1, additionalDependency]); + dependencyContainer.resolve(MyClass, [1, additionalDependency]); - expect(receivedAdditionalDependency).toBe(additionalDependency); - }); + expect(receivedAdditionalDependency) + .toBe(additionalDependency); + }); - test('Resolving type dependency returns instance', () => { - class MyClass { - } + test('Resolving type dependency returns instance', () => { + class MyClass { + } - const dependencyContainer = new DependencyContainer(); + const dependencyContainer = new DependencyContainer(); - const instance = dependencyContainer.resolve(MyClass); + const instance = dependencyContainer.resolve(MyClass); - expect(instance).toBeInstanceOf(MyClass); - }); + expect(instance) + .toBeInstanceOf(MyClass); + }); - test('Resolving type dependency returns different instance each time', () => { - class MyClass { - } + test('Resolving type dependency returns different instance each time', () => { + class MyClass { + } - const dependencyContainer = new DependencyContainer(); + const dependencyContainer = new DependencyContainer(); - const firstInstance = dependencyContainer.resolve(MyClass); - const secondInstance = dependencyContainer.resolve(MyClass); + const firstInstance = dependencyContainer.resolve(MyClass); + const secondInstance = dependencyContainer.resolve(MyClass); - expect(firstInstance).not.toBe(secondInstance); - }); + expect(firstInstance).not.toBe(secondInstance); + }); - test('Resolving unconfigured token dependency throws exception', () => { - const token = new DependencyToken('test-dependency-token'); + test('Resolving unconfigured token dependency throws exception', () => { + const token = new DependencyToken('test-dependency-token'); - const dependencyContainer = new DependencyContainer(); + const dependencyContainer = new DependencyContainer(); - expect(() => dependencyContainer.resolve(token)).toThrow(new Error('There is no configured dependency for token \'test-dependency-token\'.')); - }); + expect(() => dependencyContainer.resolve(token)) + .toThrow(new Error('There is no configured dependency for token \'test-dependency-token\'.')); + }); }); \ No newline at end of file diff --git a/src/events/EventDispatcher.ts b/src/events/EventDispatcher.ts index f4ff620..d5dac5a 100644 --- a/src/events/EventDispatcher.ts +++ b/src/events/EventDispatcher.ts @@ -38,7 +38,7 @@ export class EventDispatcher implements IEvent { + this._eventHandlers.forEach((eventHandler) => { if (this._eventHandlers.indexOf(eventHandler) >= 0) eventHandler.handle(subject, args); }); diff --git a/src/events/index.ts b/src/events/index.ts index 00a0d21..8f9ff9f 100644 --- a/src/events/index.ts +++ b/src/events/index.ts @@ -2,4 +2,4 @@ export type { IEvent } from './IEvent'; export type { IEventHandler } from './IEventHandler'; export { isEvent } from './isEvent'; -export { EventDispatcher } from './EventDispatcher' \ No newline at end of file +export { EventDispatcher } from './EventDispatcher'; \ No newline at end of file diff --git a/src/forms/Form.ts b/src/forms/Form.ts index 84e7759..9321b45 100644 --- a/src/forms/Form.ts +++ b/src/forms/Form.ts @@ -1,9 +1,9 @@ import type { IPropertiesChangedEventHandler } from '../viewModels'; +import type { IFormFieldConfig, FormField } from './FormField'; import type { IReadOnlyFormCollection } from './IReadOnlyFormCollection'; import type { ReadOnlyFormCollection } from './ReadOnlyFormCollection'; import { type IReadOnlyObservableCollection, type IObservableCollection, type ICollectionChangedEventHandler, type ICollectionReorderedEventHandler, ObservableCollection, ReadOnlyObservableCollection } from '../collections'; import { type IValidatable, type IObjectValidator, type WellKnownValidationTrigger, type ValidationTrigger, Validatable, ObjectValidator } from '../validation'; -import { type IFormFieldConfig, FormField } from './FormField'; import { FormCollection } from './FormCollection'; /** @@ -373,11 +373,11 @@ export class Form extends Validatable { + removedFields.forEach((removedField) => { removedField.propertiesChanged.unsubscribe(fieldChangedEventHandler); removedField.reset(); }); - addedFields.forEach(addedField => { + addedFields.forEach((addedField) => { addedField.propertiesChanged.subscribe(fieldChangedEventHandler); }); } @@ -388,10 +388,10 @@ export class Form extends Validatable { + removedSections.forEach((removedSection) => { removedSection.propertiesChanged.unsubscribe(sectionChangedEventHandler); }); - addedSections.forEach(addedSection => { + addedSections.forEach((addedSection) => { addedSection.propertiesChanged.subscribe(sectionChangedEventHandler); }); } @@ -402,11 +402,11 @@ export class Form extends Validatable { + removedSectionsCollections.forEach((removedSectionsCollection) => { removedSectionsCollection.propertiesChanged.unsubscribe(sectionsCollectionsChangedEventHandler); removedSectionsCollection.reset(); }); - addedSectionsConllections.forEach(addedSectionsCollection => { + addedSectionsConllections.forEach((addedSectionsCollection) => { addedSectionsCollection.propertiesChanged.subscribe(sectionsCollectionsChangedEventHandler); }); } @@ -471,7 +471,7 @@ export class Form extends Validatable extends Validatable; - /** * Gets the fields defined within the form instance. */ public readonly fields: IReadOnlyObservableCollection>; - /** * Gets the sections defined within the form instance. */ public readonly sections: IReadOnlyObservableCollection>; - /** * Gets the sections collections defined within the form instance. */ @@ -635,8 +632,8 @@ export class Form extends Validatable field.isValid) - && this.sectionsCollections.every(sectionsCollection => sectionsCollection.isValid) + && this.fields.every((field) => field.isValid) + && this.sectionsCollections.every((sectionsCollection) => sectionsCollection.isValid) ); } @@ -648,8 +645,8 @@ export class Form extends Validatable field.isInvalid) - || this.sectionsCollections.some(sectionsCollection => sectionsCollection.isInvalid) + || this.fields.some((field) => field.isInvalid) + || this.sectionsCollections.some((sectionsCollection) => sectionsCollection.isInvalid) ); } @@ -659,10 +656,10 @@ export class Form extends Validatable { + this.sectionsCollections.forEach((sectionsCollection) => { sectionsCollection.reset(); }); - this.fields.forEach(field => field.reset()); + this.fields.forEach((field) => field.reset()); this.validation.reset(); } @@ -852,7 +849,7 @@ export class Form extends Validatable, changedProperties: readonly (keyof FormField)[]) { - if (changedProperties.some(changedProperty => changedProperty === 'isValid' || changedProperty === 'isInvalid')) + if (changedProperties.some((changedProperty) => changedProperty === 'isValid' || changedProperty === 'isInvalid')) this.notifyPropertiesChanged('isValid', 'isInvalid'); } @@ -866,7 +863,7 @@ export class Form extends Validatable, TValidationError>, changedProperties: readonly (keyof IReadOnlyFormCollection, TValidationError>)[]) { - if (changedProperties.some(changedProperty => changedProperty === 'isValid' || changedProperty === 'isInvalid')) + if (changedProperties.some((changedProperty) => changedProperty === 'isValid' || changedProperty === 'isInvalid')) this.notifyPropertiesChanged('isValid', 'isInvalid'); } @@ -877,7 +874,7 @@ export class Form extends Validatable changedProperty !== 'error' && changedProperty !== 'isValid' && changedProperty !== 'isInvalid'); + return changedProperties.some((changedProperty) => changedProperty !== 'error' && changedProperty !== 'isValid' && changedProperty !== 'isInvalid'); } } @@ -909,17 +906,18 @@ class AggregateObservableCollection { - removedCollections.forEach(removedCollection => { + removedCollections.forEach((removedCollection) => { removedCollection.collectionReordered.unsubscribe(collectionReorderedEventHandler); removedCollection.collectionChanged.unsubscribe(collectionChangedEventHandler); }); - addedCollections.forEach(addedCollection => { + addedCollections.forEach((addedCollection) => { addedCollection.collectionChanged.subscribe(collectionChangedEventHandler); addedCollection.collectionReordered.subscribe(collectionReorderedEventHandler); }); - let offset = 0, index = 0; + let offset = 0, + index = 0; while (index < startIndex && index < this.aggregatedCollections.length) { offset += this.aggregatedCollections[index].length; index++; @@ -931,6 +929,7 @@ class AggregateObservableCollection { addedItems.push(...addedCollection); + return addedItems; }, new Array() diff --git a/src/forms/FormCollection.ts b/src/forms/FormCollection.ts index 8499489..153b39f 100644 --- a/src/forms/FormCollection.ts +++ b/src/forms/FormCollection.ts @@ -8,7 +8,7 @@ import { ReadOnlyFormCollection } from './ReadOnlyFormCollection'; * * @template TForm The concrete type of the form. * @template TValidationError The concrete type for representing validation errors (strings, enums, numbers etc.). - * + * * @see {@linkcode Form} */ export class FormCollection, TValidationError = string> extends ReadOnlyFormCollection implements IFormCollection { @@ -112,7 +112,7 @@ export class FormCollection, TValidationErr * @returns The observable collection on which the operation is performed. * @see [Array.sort](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) */ - public sort(compareCallback?: (left: Exclude, right: Exclude) => number): this { + public sort(compareCallback?: (left: Exclude, right: Exclude)=> number): this { return super.sort.apply(this, arguments); } diff --git a/src/forms/FormField.ts b/src/forms/FormField.ts index c749d8d..b844c6d 100644 --- a/src/forms/FormField.ts +++ b/src/forms/FormField.ts @@ -133,7 +133,7 @@ export interface IFormFieldConfig { * Unavoidably, we end up creating components for specific types of inputs to avoid repetitive code * as well as ensure they all behave in the same way. Binding is generally handled inside these components * using DOM event handlers/callbacks. - * + * * This is the most basic form of two-way binding, there's full control over it. The value coming from * the field can be transformed in the component. Similarly, when the input changes its value can be * converted back to something the form field may understand. There is full control over how the two @@ -195,10 +195,11 @@ export class FormField extends Validatable { return this.onShouldTriggerValidation(changedProperties); } - }) + }); this.validation.add.apply(this.validation, validators); - resolveAllValidationTriggers(validationTriggers).forEach(this.validation.triggers.add, this.validation.triggers); + resolveAllValidationTriggers(validationTriggers) + .forEach(this.validation.triggers.add, this.validation.triggers); } /** @@ -278,6 +279,6 @@ export class FormField extends Validatable changedProperty === 'value'); + return changedProperties.some((changedProperty) => changedProperty === 'value'); } -} +} \ No newline at end of file diff --git a/src/forms/IConfigurableFormCollection.ts b/src/forms/IConfigurableFormCollection.ts index f6c4343..1569e0f 100644 --- a/src/forms/IConfigurableFormCollection.ts +++ b/src/forms/IConfigurableFormCollection.ts @@ -2,11 +2,11 @@ import type { Form } from './Form'; /** * Represents a callback used to configure an individual form section within a collection. - * + * * @template TSection The form section type to configure. * @template TValidationError The concrete type for representing validation errors (strings, enums, numbers etc.). */ -export type FormSetupCallback, TValidationError = string> = (section: TSection) => void; +export type FormSetupCallback, TValidationError = string> = (section: TSection)=> void; /** * Represents collection of form sections that can be configured. This is useful for cases like having a list of editable items diff --git a/src/forms/IFormCollection.ts b/src/forms/IFormCollection.ts index 4eceacd..d8d28ef 100644 --- a/src/forms/IFormCollection.ts +++ b/src/forms/IFormCollection.ts @@ -2,7 +2,7 @@ import type { IObservableCollection } from '../collections'; import type { Form } from './Form'; import type { IReadOnlyFormCollection } from './IReadOnlyFormCollection'; -/** +/** * Represents a configurable observable collection of forms. Callbacks can be configured for setting up individual * form sections for cases where validation and other aspects are based on the state of an entity or the form itself. * diff --git a/src/forms/IReadOnlyFormCollection.ts b/src/forms/IReadOnlyFormCollection.ts index ee69ec0..87d118d 100644 --- a/src/forms/IReadOnlyFormCollection.ts +++ b/src/forms/IReadOnlyFormCollection.ts @@ -3,7 +3,7 @@ import type { IObjectValidator, IValidatable } from '../validation'; import type { Form } from './Form'; import type { IConfigurableFormCollection } from './IConfigurableFormCollection'; -/** +/** * Represents a read-only configurable observable collection of form sections. Callbacks can be configured for setting * up individual form sections for cases where validation and other aspects are based on the state of an entity or the * form itself. diff --git a/src/forms/ReadOnlyFormCollection.ts b/src/forms/ReadOnlyFormCollection.ts index 970af9b..01ff3b7 100644 --- a/src/forms/ReadOnlyFormCollection.ts +++ b/src/forms/ReadOnlyFormCollection.ts @@ -2,8 +2,8 @@ import type { IPropertiesChangedEventHandler } from '../viewModels'; import type { Form } from './Form'; import type { FormSetupCallback } from './IConfigurableFormCollection'; import type { IReadOnlyFormCollection } from './IReadOnlyFormCollection'; -import { ObjectValidator, type IObjectValidator, type IValidatable } from '../validation'; import { ReadOnlyObservableCollection } from '../collections'; +import { ObjectValidator, type IObjectValidator, type IValidatable } from '../validation'; /** * Represents a configurable read-only observable collection of form sections. Callbacks can be configured for setting @@ -35,19 +35,19 @@ export class ReadOnlyFormCollection, TValid const sectionChangedEventHandler: IPropertiesChangedEventHandler> = { handle: this.onSectionChanged.bind(this) }; - this.forEach(section => { + this.forEach((section) => { section.propertiesChanged.subscribe(sectionChangedEventHandler); }); this.collectionChanged.subscribe({ handle: (_, { addedItems: addedSections, removedItems: removedSections }) => { - removedSections.forEach(removedSection => { + removedSections.forEach((removedSection) => { removedSection.propertiesChanged.unsubscribe(sectionChangedEventHandler); removedSection.reset(); }); - addedSections.forEach(addedSection => { + addedSections.forEach((addedSection) => { addedSection.propertiesChanged.subscribe(sectionChangedEventHandler); - this._setupCallbacks.forEach(setupCallback => { + this._setupCallbacks.forEach((setupCallback) => { setupCallback(addedSection); }); }); @@ -60,7 +60,7 @@ export class ReadOnlyFormCollection, TValid /** * Gets the validation configuration for the form. Fields have their own individual validation config as well. - * + * * @see {@linkcode Form.validation} */ readonly validation: IObjectValidator; @@ -71,7 +71,7 @@ export class ReadOnlyFormCollection, TValid * A section collection is valid only when itself is valid and all contained sections are valid. */ public get isValid(): boolean { - return this._error === null && this.every(section => section.isValid); + return this._error === null && this.every((section) => section.isValid); } /** @@ -80,7 +80,7 @@ export class ReadOnlyFormCollection, TValid * A section collection is invalid when itself is invalid or any contained sections is invalid. */ public get isInvalid(): boolean { - return this._error !== null || this.some(section => section.isInvalid); + return this._error !== null || this.some((section) => section.isInvalid); } /** @@ -110,7 +110,7 @@ export class ReadOnlyFormCollection, TValid public withItemSetup(setupCallback: FormSetupCallback): this { if (typeof setupCallback === 'function') { this._setupCallbacks.push(setupCallback); - this.forEach(section => { + this.forEach((section) => { setupCallback(section); }); } @@ -128,7 +128,7 @@ export class ReadOnlyFormCollection, TValid const setupCallbackIndex = this._setupCallbacks.indexOf(setupCallback); if (setupCallbackIndex > 0) { this._setupCallbacks.splice(setupCallbackIndex, 1); - this.forEach(section => section.reset()); + this.forEach((section) => section.reset()); this._setupSections(); } } @@ -141,7 +141,7 @@ export class ReadOnlyFormCollection, TValid */ public clearItemSetups(): void { this._setupCallbacks.splice(0, Number.POSITIVE_INFINITY); - this.forEach(section => section.reset()); + this.forEach((section) => section.reset()); } /** @@ -151,7 +151,7 @@ export class ReadOnlyFormCollection, TValid */ public reset(): void { this._setupCallbacks.splice(0, Number.POSITIVE_INFINITY); - this.forEach(section => section.reset()); + this.forEach((section) => section.reset()); this.validation.reset(); } @@ -159,7 +159,7 @@ export class ReadOnlyFormCollection, TValid * Invoked when a section's properies change, this is a plugin method through which notification propagation can be made with ease. */ protected onSectionChanged(section: Form, changedProperties: readonly (keyof Form)[]) { - if (changedProperties.some(changedProperty => changedProperty === 'isValid' || changedProperty === 'isInvalid')) + if (changedProperties.some((changedProperty) => changedProperty === 'isValid' || changedProperty === 'isInvalid')) this.notifyPropertiesChanged('isValid', 'isInvalid'); } @@ -170,12 +170,12 @@ export class ReadOnlyFormCollection, TValid * @returns Returns `true` if a validation should be triggered for the given changed properties; otherwise `false`. */ protected onShouldTriggerValidation(changedProperties: readonly (keyof this)[]): boolean { - return changedProperties.some(changedProperty => changedProperty !== 'error' && changedProperty !== 'isValid' && changedProperty !== 'isInvalid'); + return changedProperties.some((changedProperty) => changedProperty !== 'error' && changedProperty !== 'isValid' && changedProperty !== 'isInvalid'); } private _setupSections(): void { - this.forEach(section => { - this._setupCallbacks.forEach(setupCallback => setupCallback(section)); + this.forEach((section) => { + this._setupCallbacks.forEach((setupCallback) => setupCallback(section)); }); } } \ No newline at end of file diff --git a/src/forms/__tests__/Form.test.ts b/src/forms/__tests__/Form.test.ts index 9f9f5e6..c1546e4 100644 --- a/src/forms/__tests__/Form.test.ts +++ b/src/forms/__tests__/Form.test.ts @@ -1,61 +1,119 @@ import type { IObservableCollection } from '../../collections'; -import { FormField } from '../FormField'; -import { FormCollection } from '../FormCollection'; +import type { FormCollection } from '../FormCollection'; import { Form } from '../Form'; +import { FormField } from '../FormField'; describe('Form', (): void => { it('adding fields when initializing collections adds them to the form', (): void => { const form = new TestForm(); - const field1 = new FormField({ name: 'field 1', initialValue: {} }); - const field2 = new FormField({ name: 'field 2', initialValue: {} }); + const field1 = new FormField({ + name: 'field 1', + initialValue: {} + }); + const field2 = new FormField({ + name: 'field 2', + initialValue: {} + }); form.withFields(field1, field2); - expect(form.fields.length).toBe(2); - expect(form.fields.toArray()).toEqual([field1, field2]); + expect(form.fields.length) + .toBe(2); + expect(form.fields.toArray()) + .toEqual([field1, field2]); }); it('adding fields to field collections adds them to the form', (): void => { const form = new TestForm(); - const field1 = new FormField({ name: 'field 1', initialValue: {} }); - const field2 = new FormField({ name: 'field 2', initialValue: {} }); + const field1 = new FormField({ + name: 'field 1', + initialValue: {} + }); + const field2 = new FormField({ + name: 'field 2', + initialValue: {} + }); const fields = form.withFields(); fields.push(field1, field2); - expect(form.fields.length).toBe(2); - expect(form.fields.toArray()).toEqual([field1, field2]); + expect(form.fields.length) + .toBe(2); + expect(form.fields.toArray()) + .toEqual([field1, field2]); }); it('initializing two field collections adds all to the form', (): void => { const form = new TestForm(); - const field1 = new FormField({ name: 'field 1', initialValue: {} }); - const field2 = new FormField({ name: 'field 2', initialValue: {} }); + const field1 = new FormField({ + name: 'field 1', + initialValue: {} + }); + const field2 = new FormField({ + name: 'field 2', + initialValue: {} + }); form.withFields(field1); form.withFields(field2); - expect(form.fields.length).toBe(2); - expect(form.fields.toArray()).toEqual([field1, field2]); + expect(form.fields.length) + .toBe(2); + expect(form.fields.toArray()) + .toEqual([field1, field2]); }); it('changing field collections keeps the entire collection in sync', (): void => { const form = new TestForm(); + function expectFields(fields: readonly FormField[]) { - expect(form.fields.length).toBe(fields.length); - expect(form.fields.toArray().map(({ name }) => ({ name }))).toEqual(fields.map(({ name }) => ({ name }))); + expect(form.fields.length) + .toBe(fields.length); + expect(form.fields.toArray() + .map(({ name }) => ({ name }))) + .toEqual(fields.map(({ name }) => ({ name }))); } - const field1 = new FormField({ name: 'field 1', initialValue: {} }); - const field2 = new FormField({ name: 'field 2', initialValue: {} }); - const field3 = new FormField({ name: 'field 3', initialValue: {} }); - const field4 = new FormField({ name: 'field 4', initialValue: {} }); - const field5 = new FormField({ name: 'field 5', initialValue: {} }); - const field6 = new FormField({ name: 'field 6', initialValue: {} }); - const field7 = new FormField({ name: 'field 7', initialValue: {} }); - const field8 = new FormField({ name: 'field 8', initialValue: {} }); - const field9 = new FormField({ name: 'field 9', initialValue: {} }); - const field10 = new FormField({ name: 'field 10', initialValue: {} }); + const field1 = new FormField({ + name: 'field 1', + initialValue: {} + }); + const field2 = new FormField({ + name: 'field 2', + initialValue: {} + }); + const field3 = new FormField({ + name: 'field 3', + initialValue: {} + }); + const field4 = new FormField({ + name: 'field 4', + initialValue: {} + }); + const field5 = new FormField({ + name: 'field 5', + initialValue: {} + }); + const field6 = new FormField({ + name: 'field 6', + initialValue: {} + }); + const field7 = new FormField({ + name: 'field 7', + initialValue: {} + }); + const field8 = new FormField({ + name: 'field 8', + initialValue: {} + }); + const field9 = new FormField({ + name: 'field 9', + initialValue: {} + }); + const field10 = new FormField({ + name: 'field 10', + initialValue: {} + }); const fieldCollection1 = form.withFields(field1, field2, field3); const fieldCollection2 = form.withFields(field4, field5, field6, field7); @@ -69,7 +127,8 @@ describe('Form', (): void => { expectFields([field3, field2, field1, field7, field6, field5, field4, field8, field9, field10]); const [removedField] = fieldCollection3.splice(1, 1); - expect(removedField).toEqual(field9); + expect(removedField) + .toEqual(field9); expectFields([field3, field2, field1, field7, field6, field5, field4, field8, field10]); fieldCollection1.push(removedField); @@ -88,16 +147,21 @@ describe('Form', (): void => { it('removing a form field resets it', () => { let resetInvocationCount = 0; const form = new TestForm(); - const field = new FormField({ name: 'field', initialValue: null }); + const field = new FormField({ + name: 'field', + initialValue: null + }); field.reset = () => { resetInvocationCount++; }; const sectionCollection = form.withFields(field); - expect(resetInvocationCount).toBe(0); + expect(resetInvocationCount) + .toBe(0); sectionCollection.splice(0); - expect(resetInvocationCount).toBe(1); + expect(resetInvocationCount) + .toBe(1); }); it('adding sections when initializing collections adds them to the form', (): void => { @@ -107,8 +171,10 @@ describe('Form', (): void => { form.withSections(section1, section2); - expect(form.sections.length).toBe(2); - expect(form.sections.toArray()).toEqual([section1, section2]); + expect(form.sections.length) + .toBe(2); + expect(form.sections.toArray()) + .toEqual([section1, section2]); }); it('adding sections to section collections adds them to the form', (): void => { @@ -119,8 +185,10 @@ describe('Form', (): void => { const sections = form.withSections(); sections.push(section1, section2); - expect(form.sections.length).toBe(2); - expect(form.sections.toArray()).toEqual([section1, section2]); + expect(form.sections.length) + .toBe(2); + expect(form.sections.toArray()) + .toEqual([section1, section2]); }); it('initializing two section collections adds all to the form', (): void => { @@ -131,15 +199,20 @@ describe('Form', (): void => { form.withSections(section1); form.withSections(section2); - expect(form.sections.length).toBe(2); - expect(form.sections.toArray()).toEqual([section1, section2]); + expect(form.sections.length) + .toBe(2); + expect(form.sections.toArray()) + .toEqual([section1, section2]); }); it('changing section collections keeps the entire collection in sync', (): void => { const form = new TestForm(); + function expectSections(sections: readonly Form[]) { - expect(form.sections.length).toBe(sections.length); - expect(form.sections.toArray()).toEqual(sections); + expect(form.sections.length) + .toBe(sections.length); + expect(form.sections.toArray()) + .toEqual(sections); } const section1 = new Form(); @@ -162,7 +235,8 @@ describe('Form', (): void => { expectSections([section1, section2, section3, section7, section6, section5, section4, section8, section9, section10]); const [removedSection] = sectionCollection3.splice(1, 1); - expect(removedSection).toEqual(section9); + expect(removedSection) + .toEqual(section9); expectSections([section1, section2, section3, section7, section6, section5, section4, section8, section10]); sectionCollection1.push(removedSection); @@ -187,10 +261,12 @@ describe('Form', (): void => { }; const sectionCollection = form.withSections(section); - expect(resetInvocationCount).toBe(0); + expect(resetInvocationCount) + .toBe(0); sectionCollection.splice(0); - expect(resetInvocationCount).toBe(1); + expect(resetInvocationCount) + .toBe(1); }); it('invalidating a field makes the entire form invalid', (): void => { @@ -202,8 +278,10 @@ describe('Form', (): void => { field.error = 'invalid'; - expect(form.isValid).toBeFalsy(); - expect(form.isInvalid).toBeTruthy(); + expect(form.isValid) + .toBeFalsy(); + expect(form.isInvalid) + .toBeTruthy(); }); it('invalidating a field propagates property change notifications', (): void => { @@ -212,11 +290,14 @@ describe('Form', (): void => { form.propertiesChanged.subscribe({ handle(_, changedProperties) { invocationCount++; - expect(changedProperties.length).toBe(2); - expect(changedProperties).toContain('isValid'); - expect(changedProperties).toContain('isInvalid'); + expect(changedProperties.length) + .toBe(2); + expect(changedProperties) + .toContain('isValid'); + expect(changedProperties) + .toContain('isInvalid'); } - }) + }); const field = new FormField({ name: 'field', initialValue: null @@ -225,7 +306,8 @@ describe('Form', (): void => { field.error = 'invalid'; - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('invalidating a section makes the entire form invalid', (): void => { @@ -236,8 +318,10 @@ describe('Form', (): void => { section.error = 'invalid'; - expect(form.isValid).toBeFalsy(); - expect(form.isInvalid).toBeTruthy(); + expect(form.isValid) + .toBeFalsy(); + expect(form.isInvalid) + .toBeTruthy(); }); it('invalidating a section propagates property change notifications', (): void => { @@ -246,18 +330,22 @@ describe('Form', (): void => { form.propertiesChanged.subscribe({ handle(_, changedProperties) { invocationCount++; - expect(changedProperties.length).toBe(2); - expect(changedProperties).toContain('isValid'); - expect(changedProperties).toContain('isInvalid'); + expect(changedProperties.length) + .toBe(2); + expect(changedProperties) + .toContain('isValid'); + expect(changedProperties) + .toContain('isInvalid'); } - }) + }); const [section] = form.withSections( new Form() ); section.error = 'invalid'; - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('invalidating a sections collection makes the entire form invalid', (): void => { @@ -268,8 +356,10 @@ describe('Form', (): void => { sections.error = 'invalid'; - expect(form.isValid).toBeFalsy(); - expect(form.isInvalid).toBeTruthy(); + expect(form.isValid) + .toBeFalsy(); + expect(form.isInvalid) + .toBeTruthy(); }); it('invalidating a sections collection propagates property change notifications', (): void => { @@ -278,9 +368,12 @@ describe('Form', (): void => { form.propertiesChanged.subscribe({ handle(_, changedProperties) { invocationCount++; - expect(changedProperties.length).toBe(2); - expect(changedProperties).toContain('isValid'); - expect(changedProperties).toContain('isInvalid'); + expect(changedProperties.length) + .toBe(2); + expect(changedProperties) + .toContain('isValid'); + expect(changedProperties) + .toContain('isInvalid'); } }); const sections = form.withSections( @@ -289,7 +382,8 @@ describe('Form', (): void => { sections.error = 'invalid'; - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('configuring a form section collection initializes each added section', () => { @@ -298,15 +392,17 @@ describe('Form', (): void => { const sectionCollection = form.withSections(); const formSection = new Form(); sectionCollection.withItemSetup( - section => { + (section) => { invocationCount++; - expect(section).toStrictEqual(formSection); + expect(section) + .toStrictEqual(formSection); } ); sectionCollection.push(formSection); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('removing a configuration callback reconfigures the section', () => { @@ -318,15 +414,17 @@ describe('Form', (): void => { const formSection = new Form(); formSection.reset = () => { resetInvocationCount++; - } + }; const setup1 = (section: Form) => { setup1InvocationCount++; - expect(section).toStrictEqual(formSection); + expect(section) + .toStrictEqual(formSection); }; const setup2 = (section: Form) => { setup2InvocationCount++; - expect(section).toStrictEqual(formSection); + expect(section) + .toStrictEqual(formSection); }; sectionCollection.withItemSetup(setup1); sectionCollection.withItemSetup(setup2); @@ -335,9 +433,12 @@ describe('Form', (): void => { sectionCollection.withoutItemSetup(setup2); - expect(setup1InvocationCount).toBe(2); - expect(resetInvocationCount).toBe(1); - expect(setup2InvocationCount).toBe(1); + expect(setup1InvocationCount) + .toBe(2); + expect(resetInvocationCount) + .toBe(1); + expect(setup2InvocationCount) + .toBe(1); }); it('clearing configuration callbacks resets the section', () => { @@ -347,13 +448,16 @@ describe('Form', (): void => { const formSection = new Form(); formSection.reset = () => { resetInvocationCount++; - } - sectionCollection.withItemSetup(() => { }); + }; + sectionCollection.withItemSetup(() => { + + }); sectionCollection.push(formSection); sectionCollection.clearItemSetups(); - expect(resetInvocationCount).toBe(1); + expect(resetInvocationCount) + .toBe(1); }); it('resetting a form section resets fields, sections and sections collection configurations', () => { @@ -363,36 +467,61 @@ describe('Form', (): void => { const form = new TestForm(); form.validation.add(() => 'error').triggers.add(form); - const field = new FormField({ name: 'field', initialValue: null }); - field.reset = () => { fieldResetInvocationCount++; }; + const field = new FormField({ + name: 'field', + initialValue: null + }); + field.reset = () => { + fieldResetInvocationCount++; + }; form.withFields(field); const formSection = new Form(); - formSection.reset = () => { sectionResetInvocationCount++; } + formSection.reset = () => { + sectionResetInvocationCount++; + }; const formSectionsCollection = form.withSections(formSection); formSectionsCollection.withItemSetup( - () => { sectionSetupInvocationCount++; } + () => { + sectionSetupInvocationCount++; + } ); - expect(fieldResetInvocationCount).toBe(0); - expect(sectionResetInvocationCount).toBe(0); - expect(sectionSetupInvocationCount).toBe(1); - expect(form.error).toBe('error'); - expect(form.isValid).toBeFalsy(); - expect(form.isInvalid).toBeTruthy(); - expect(form.validation.validators.length).toBe(1); - expect(form.validation.triggers.size).toBe(1); + expect(fieldResetInvocationCount) + .toBe(0); + expect(sectionResetInvocationCount) + .toBe(0); + expect(sectionSetupInvocationCount) + .toBe(1); + expect(form.error) + .toBe('error'); + expect(form.isValid) + .toBeFalsy(); + expect(form.isInvalid) + .toBeTruthy(); + expect(form.validation.validators.length) + .toBe(1); + expect(form.validation.triggers.size) + .toBe(1); form.reset(); formSectionsCollection.push(new Form()); - expect(fieldResetInvocationCount).toBe(1); - expect(sectionResetInvocationCount).toBe(1); - expect(sectionSetupInvocationCount).toBe(1); - expect(form.error).toBeNull(); - expect(form.isValid).toBeTruthy(); - expect(form.isInvalid).toBeFalsy(); - expect(form.validation.validators.length).toBe(0); - expect(form.validation.triggers.size).toBe(0); + expect(fieldResetInvocationCount) + .toBe(1); + expect(sectionResetInvocationCount) + .toBe(1); + expect(sectionSetupInvocationCount) + .toBe(1); + expect(form.error) + .toBeNull(); + expect(form.isValid) + .toBeTruthy(); + expect(form.isInvalid) + .toBeFalsy(); + expect(form.validation.validators.length) + .toBe(0); + expect(form.validation.triggers.size) + .toBe(0); }); }); diff --git a/src/forms/__tests__/FormCollection.test.ts b/src/forms/__tests__/FormCollection.test.ts index 62c7593..a4cf77c 100644 --- a/src/forms/__tests__/FormCollection.test.ts +++ b/src/forms/__tests__/FormCollection.test.ts @@ -1,5 +1,5 @@ -import { FormCollection } from '../FormCollection'; import { Form } from '../Form'; +import { FormCollection } from '../FormCollection'; describe('FormCollection', (): void => { it('adding sections when initializing collections adds them to the collection', (): void => { @@ -8,8 +8,10 @@ describe('FormCollection', (): void => { const formCollection = new FormCollection([section1, section2]); - expect(formCollection.length).toBe(2); - expect(formCollection.toArray()).toEqual([section1, section2]); + expect(formCollection.length) + .toBe(2); + expect(formCollection.toArray()) + .toEqual([section1, section2]); }); it('adding sections to section collections adds them to the collection', (): void => { @@ -19,8 +21,10 @@ describe('FormCollection', (): void => { const formCollection = new FormCollection(); formCollection.push(section1, section2); - expect(formCollection.length).toBe(2); - expect(formCollection.toArray()).toEqual([section1, section2]); + expect(formCollection.length) + .toBe(2); + expect(formCollection.toArray()) + .toEqual([section1, section2]); }); it('initializing two section collections adds all to the collection', (): void => { @@ -31,8 +35,10 @@ describe('FormCollection', (): void => { formCollection.push(section1); formCollection.push(section2); - expect(formCollection.length).toBe(2); - expect(formCollection.toArray()).toEqual([section1, section2]); + expect(formCollection.length) + .toBe(2); + expect(formCollection.toArray()) + .toEqual([section1, section2]); }); it('removing a form section resets it', () => { @@ -43,10 +49,12 @@ describe('FormCollection', (): void => { resetInvocationCount++; }; - expect(resetInvocationCount).toBe(0); + expect(resetInvocationCount) + .toBe(0); formCollection.splice(0); - expect(resetInvocationCount).toBe(1); + expect(resetInvocationCount) + .toBe(1); }); it('invalidating a section makes the entire form invalid', (): void => { @@ -55,8 +63,10 @@ describe('FormCollection', (): void => { section.error = 'invalid'; - expect(formCollection.isValid).toBeFalsy(); - expect(formCollection.isInvalid).toBeTruthy(); + expect(formCollection.isValid) + .toBeFalsy(); + expect(formCollection.isInvalid) + .toBeTruthy(); }); it('invalidating a section propagates property change notifications', (): void => { @@ -66,15 +76,19 @@ describe('FormCollection', (): void => { formCollection.propertiesChanged.subscribe({ handle(_, changedProperties) { invocationCount++; - expect(changedProperties.length).toBe(2); - expect(changedProperties).toContain('isValid'); - expect(changedProperties).toContain('isInvalid'); + expect(changedProperties.length) + .toBe(2); + expect(changedProperties) + .toContain('isValid'); + expect(changedProperties) + .toContain('isInvalid'); } - }) + }); section.error = 'invalid'; - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('configuring a form section collection initializes each added section', () => { @@ -82,15 +96,17 @@ describe('FormCollection', (): void => { const formCollection = new FormCollection(); const formSection = new Form(); formCollection.withItemSetup( - section => { + (section) => { invocationCount++; - expect(section).toStrictEqual(formSection); + expect(section) + .toStrictEqual(formSection); } ); formCollection.push(formSection); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('removing a configuration callback reconfigures the section', () => { @@ -101,15 +117,17 @@ describe('FormCollection', (): void => { const formSection = new Form(); formSection.reset = () => { resetInvocationCount++; - } + }; const setup1 = (section: Form) => { setup1InvocationCount++; - expect(section).toStrictEqual(formSection); + expect(section) + .toStrictEqual(formSection); }; const setup2 = (section: Form) => { setup2InvocationCount++; - expect(section).toStrictEqual(formSection); + expect(section) + .toStrictEqual(formSection); }; formCollection.withItemSetup(setup1); formCollection.withItemSetup(setup2); @@ -118,9 +136,12 @@ describe('FormCollection', (): void => { formCollection.withoutItemSetup(setup2); - expect(setup1InvocationCount).toBe(2); - expect(resetInvocationCount).toBe(1); - expect(setup2InvocationCount).toBe(1); + expect(setup1InvocationCount) + .toBe(2); + expect(resetInvocationCount) + .toBe(1); + expect(setup2InvocationCount) + .toBe(1); }); it('clearing configuration callbacks resets the section', () => { @@ -129,13 +150,16 @@ describe('FormCollection', (): void => { const formSection = new Form(); formSection.reset = () => { resetInvocationCount++; - } - formCollection.withItemSetup(() => { }); + }; + formCollection.withItemSetup(() => { + + }); formCollection.push(formSection); formCollection.clearItemSetups(); - expect(resetInvocationCount).toBe(1); + expect(resetInvocationCount) + .toBe(1); }); it('resetting a collection resets all contained sections configurations', () => { @@ -145,19 +169,25 @@ describe('FormCollection', (): void => { const section = new Form(); section.reset = () => { sectionResetInvocationCount++; - } + }; const formCollection = new FormCollection([section]); formCollection.withItemSetup( - () => { sectionSetupInvocationCount++; } + () => { + sectionSetupInvocationCount++; + } ); - expect(sectionResetInvocationCount).toBe(0); - expect(sectionSetupInvocationCount).toBe(1); + expect(sectionResetInvocationCount) + .toBe(0); + expect(sectionSetupInvocationCount) + .toBe(1); formCollection.reset(); formCollection.push(new Form()); - expect(sectionResetInvocationCount).toBe(1); - expect(sectionSetupInvocationCount).toBe(1); + expect(sectionResetInvocationCount) + .toBe(1); + expect(sectionSetupInvocationCount) + .toBe(1); }); }); \ No newline at end of file diff --git a/src/forms/__tests__/FormField.test.ts b/src/forms/__tests__/FormField.test.ts index 7680334..1a77531 100644 --- a/src/forms/__tests__/FormField.test.ts +++ b/src/forms/__tests__/FormField.test.ts @@ -10,12 +10,18 @@ describe('FormField', (): void => { initialValue }); - expect(field.name).toBe('name'); - expect(field.error).toBeNull(); - expect(field.isValid).toBe(true); - expect(field.isInvalid).toBe(false); - expect(field.value).toStrictEqual(initialValue); - expect(field.initialValue).toStrictEqual(initialValue); + expect(field.name) + .toBe('name'); + expect(field.error) + .toBeNull(); + expect(field.isValid) + .toBe(true); + expect(field.isInvalid) + .toBe(false); + expect(field.value) + .toStrictEqual(initialValue); + expect(field.initialValue) + .toStrictEqual(initialValue); }); it('creating a field with value initializes it', (): void => { @@ -27,12 +33,18 @@ describe('FormField', (): void => { initialValue }); - expect(field.name).toBe('name'); - expect(field.error).toBeNull(); - expect(field.isValid).toBe(true); - expect(field.isInvalid).toBe(false); - expect(field.value).toStrictEqual(value); - expect(field.initialValue).toStrictEqual(initialValue); + expect(field.name) + .toBe('name'); + expect(field.error) + .toBeNull(); + expect(field.isValid) + .toBe(true); + expect(field.isInvalid) + .toBe(false); + expect(field.value) + .toStrictEqual(value); + expect(field.initialValue) + .toStrictEqual(initialValue); }); it('creating a field with validators initializes it', (): void => { @@ -42,14 +54,22 @@ describe('FormField', (): void => { validators: [() => 'error'] }); - expect(field.name).toBe('name'); - expect(field.error).toBe('error'); - expect(field.isValid).toBe(false); - expect(field.isInvalid).toBe(true); - expect(field.value).toBeNull(); - expect(field.initialValue).toBeNull(); - expect(field.validation.validators.length).toBe(1); - expect(field.validation.triggers.size).toBe(0); + expect(field.name) + .toBe('name'); + expect(field.error) + .toBe('error'); + expect(field.isValid) + .toBe(false); + expect(field.isInvalid) + .toBe(true); + expect(field.value) + .toBeNull(); + expect(field.initialValue) + .toBeNull(); + expect(field.validation.validators.length) + .toBe(1); + expect(field.validation.triggers.size) + .toBe(0); }); it('creating a field with validation triggers initializes it', (): void => { @@ -61,16 +81,28 @@ describe('FormField', (): void => { validationTriggers: [validationTrigger] }); - expect(field.name).toBe('name'); - expect(field.error).toBe('error'); - expect(field.isValid).toBe(false); - expect(field.isInvalid).toBe(true); - expect(field.value).toBeNull(); - expect(field.initialValue).toBeNull(); - expect(field.validation.validators.length).toBe(1); - expect(field.validation.triggers.size).toBe(2); - expect(Array.from(field.validation.triggers).some(trigger => trigger instanceof CollectionChangedValidationTrigger)).toBeTruthy(); - expect(Array.from(field.validation.triggers).some(trigger => trigger instanceof CollectionReorderedValidationTrigger)).toBeTruthy(); + expect(field.name) + .toBe('name'); + expect(field.error) + .toBe('error'); + expect(field.isValid) + .toBe(false); + expect(field.isInvalid) + .toBe(true); + expect(field.value) + .toBeNull(); + expect(field.initialValue) + .toBeNull(); + expect(field.validation.validators.length) + .toBe(1); + expect(field.validation.triggers.size) + .toBe(2); + expect(Array.from(field.validation.triggers) + .some((trigger) => trigger instanceof CollectionChangedValidationTrigger)) + .toBeTruthy(); + expect(Array.from(field.validation.triggers) + .some((trigger) => trigger instanceof CollectionReorderedValidationTrigger)) + .toBeTruthy(); }); it('changing a trigger revalidates the field', (): void => { @@ -86,9 +118,12 @@ describe('FormField', (): void => { error = 'error 2'; validationTrigger.push({}); - expect(field.error).toBe('error 2'); - expect(field.isValid).toBe(false); - expect(field.isInvalid).toBe(true); + expect(field.error) + .toBe('error 2'); + expect(field.isValid) + .toBe(false); + expect(field.isInvalid) + .toBe(true); }); it('resetting validation on a field resets the error message', (): void => { @@ -101,11 +136,16 @@ describe('FormField', (): void => { field.validation.reset(); - expect(field.error).toBeNull(); - expect(field.isValid).toBe(true); - expect(field.isInvalid).toBe(false); - expect(field.validation.validators.length).toBe(0); - expect(field.validation.triggers.size).toBe(0); + expect(field.error) + .toBeNull(); + expect(field.isValid) + .toBe(true); + expect(field.isInvalid) + .toBe(false); + expect(field.validation.validators.length) + .toBe(0); + expect(field.validation.triggers.size) + .toBe(0); }); it('resetting a field resets the error message', (): void => { @@ -117,9 +157,12 @@ describe('FormField', (): void => { field.reset(); - expect(field.error).toBeNull(); - expect(field.isValid).toBe(true); - expect(field.isInvalid).toBe(false); + expect(field.error) + .toBeNull(); + expect(field.isValid) + .toBe(true); + expect(field.isInvalid) + .toBe(false); }); it('resetting a field resets validation', (): void => { @@ -132,10 +175,15 @@ describe('FormField', (): void => { field.reset(); - expect(field.error).toBeNull(); - expect(field.isValid).toBe(true); - expect(field.isInvalid).toBe(false); - expect(field.validation.validators.length).toBe(0); - expect(field.validation.triggers.size).toBe(0); + expect(field.error) + .toBeNull(); + expect(field.isValid) + .toBe(true); + expect(field.isInvalid) + .toBe(false); + expect(field.validation.validators.length) + .toBe(0); + expect(field.validation.triggers.size) + .toBe(0); }); }); \ No newline at end of file diff --git a/src/forms/index.ts b/src/forms/index.ts index 71081f1..b937967 100644 --- a/src/forms/index.ts +++ b/src/forms/index.ts @@ -1,8 +1,8 @@ export { Form } from './Form'; export { type IFormFieldConfig, FormField } from './FormField'; -export type { IConfigurableFormCollection, FormSetupCallback } from './IConfigurableFormCollection' +export type { IConfigurableFormCollection, FormSetupCallback } from './IConfigurableFormCollection'; export type { IReadOnlyFormCollection } from './IReadOnlyFormCollection'; -export type { IFormCollection } from './IFormCollection' +export type { IFormCollection } from './IFormCollection'; export { ReadOnlyFormCollection } from './ReadOnlyFormCollection'; export { FormCollection } from './FormCollection'; \ No newline at end of file diff --git a/src/hooks/UseObservableCollection.ts b/src/hooks/UseObservableCollection.ts index 76b305e..5d9fddd 100644 --- a/src/hooks/UseObservableCollection.ts +++ b/src/hooks/UseObservableCollection.ts @@ -17,7 +17,7 @@ export function useObservableCollection { if (observableMap !== null && observableMap !== undefined) observableMap.mapChanged.unsubscribe(mapChangedEventHandler); - } + }; }, [observableMap] ); diff --git a/src/hooks/UseObservableSet.ts b/src/hooks/UseObservableSet.ts index 87b809f..583cbdc 100644 --- a/src/hooks/UseObservableSet.ts +++ b/src/hooks/UseObservableSet.ts @@ -17,7 +17,7 @@ export function useObservableSet handle() { setState({}); } - } + }; if (observableSet !== null && observableSet !== undefined) observableSet.setChanged.subscribe(setChangedEventHandler); @@ -25,7 +25,7 @@ export function useObservableSet return () => { if (observableSet !== null && observableSet !== undefined) observableSet.setChanged.unsubscribe(setChangedEventHandler); - } + }; }, [observableSet] ); diff --git a/src/hooks/UseViewModel.ts b/src/hooks/UseViewModel.ts index 26ed13e..4ca8a39 100644 --- a/src/hooks/UseViewModel.ts +++ b/src/hooks/UseViewModel.ts @@ -1,6 +1,5 @@ -import type { INotifyPropertiesChanged, IPropertiesChangedEventHandler } from '../viewModels'; import { useState, useEffect, useRef } from 'react'; -import { isViewModel } from '../viewModels'; +import { type INotifyPropertiesChanged, type IPropertiesChangedEventHandler, isViewModel } from '../viewModels'; const emptyConstructorArgs: readonly unknown[] = []; @@ -73,7 +72,7 @@ export function useViewModel> = { handle(viewModel, changedProperties: readonly (keyof TViewModel)[]) { let hasChanges = false; - changedProperties.forEach(changedProperty => { + changedProperties.forEach((changedProperty) => { const viewModelPropertyValue = viewModel[changedProperty]; hasChanges = hasChanges || cachedViewModelPropertyValues.get(changedProperty) !== viewModelPropertyValue; @@ -87,14 +86,15 @@ export function useViewModel { if (viewModel !== null && viewModel !== undefined) viewModel.propertiesChanged.unsubscribe(viewModelPropertiesChangedEventHandler); cachedViewModelPropertyValues.clear(); - } + }; }, [viewModel, cachedViewModelPropertyValues, setState] - ) + ); return viewModel!; } \ No newline at end of file diff --git a/src/hooks/UseViewModelMemo.ts b/src/hooks/UseViewModelMemo.ts index f101ac7..d20d7e3 100644 --- a/src/hooks/UseViewModelMemo.ts +++ b/src/hooks/UseViewModelMemo.ts @@ -8,7 +8,7 @@ const emptyDeps: DependencyList = []; * Represents a view model factory callback. * @template TViewModel The type of view model to create. */ -export type ViewModelFactory = () => TViewModel; +export type ViewModelFactory = ()=> TViewModel; /** * Ensures a view models instance per component generated by the factory and watched for changes. Whenever the provided deps diff --git a/src/index.ts b/src/index.ts index 3def985..3644ffa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,7 +22,6 @@ export { type ICollectionChange, type CollectionChangeOperation, - type INotifyCollectionReordered, type ICollectionReorderedEvent, type ICollectionReorderedEventHandler, @@ -72,7 +71,7 @@ export { type IConfigurableFormCollection, type FormSetupCallback, ReadOnlyFormCollection, - FormCollection, + FormCollection } from './forms'; export { @@ -119,4 +118,4 @@ export { useDependencyResolver, useDependency, useViewModelDependency -} from './dependencies' \ No newline at end of file +} from './dependencies'; \ No newline at end of file diff --git a/src/validation/IValidator.ts b/src/validation/IValidator.ts index 992a961..5074ef6 100644 --- a/src/validation/IValidator.ts +++ b/src/validation/IValidator.ts @@ -24,7 +24,7 @@ export interface IValidator { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); collection.push(1, 2, 3); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is not triggered when collection changes but check returns false', (): void => { @@ -32,13 +34,15 @@ describe('CollectionChangedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); collection.push(1, 2, 3); - expect(invocationCount).toBe(0); + expect(invocationCount) + .toBe(0); }); it('validation is triggered when collection changes and check returns true', (): void => { @@ -53,12 +57,14 @@ describe('CollectionChangedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); collection.push(1, 2, 3); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); }); \ No newline at end of file diff --git a/src/validation/__tests__/CollectionItemValidationTrigger.test.ts b/src/validation/__tests__/CollectionItemValidationTrigger.test.ts index c2effa1..b08d569 100644 --- a/src/validation/__tests__/CollectionItemValidationTrigger.test.ts +++ b/src/validation/__tests__/CollectionItemValidationTrigger.test.ts @@ -16,13 +16,15 @@ describe('CollectionItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); item.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is triggered when an item is added to the collection', (): void => { @@ -37,14 +39,16 @@ describe('CollectionItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); const item = new TestItem(); collection.push(item); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is triggered once when the collection contains the same item multiple times and it changes', (): void => { @@ -60,13 +64,15 @@ describe('CollectionItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); item.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is triggered each time the same item is added to the collection and triggered once when it changes ', (): void => { @@ -81,19 +87,23 @@ describe('CollectionItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); const item = new TestItem(); collection.push(item); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); collection.push(item, item); - expect(invocationCount).toBe(2); + expect(invocationCount) + .toBe(2); item.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(3); + expect(invocationCount) + .toBe(3); }); it('validation is no longer triggered when a removed item changes', () => { @@ -110,19 +120,24 @@ describe('CollectionItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); const [removedItem] = collection.splice(0, 1); - expect(removedItem).toStrictEqual(item1); - expect(invocationCount).toBe(1); + expect(removedItem) + .toStrictEqual(item1); + expect(invocationCount) + .toBe(1); item1.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); item2.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(2); + expect(invocationCount) + .toBe(2); }); it('validation is still triggered when the same item was added multiple times, removed, but at least one instance is still contained by the collection', () => { @@ -139,52 +154,70 @@ describe('CollectionItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); collection.push(item1, item1); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); const [removedItem1] = collection.splice(0, 1); - expect(removedItem1).toStrictEqual(item1); - expect(invocationCount).toBe(2); + expect(removedItem1) + .toStrictEqual(item1); + expect(invocationCount) + .toBe(2); item1.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(3); + expect(invocationCount) + .toBe(3); item2.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(4); - + expect(invocationCount) + .toBe(4); + const [removedItem2] = collection.splice(1, 1); - expect(removedItem2).toStrictEqual(item1); - expect(invocationCount).toBe(5); + expect(removedItem2) + .toStrictEqual(item1); + expect(invocationCount) + .toBe(5); item1.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(6); + expect(invocationCount) + .toBe(6); item2.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(7); - + expect(invocationCount) + .toBe(7); + const [removedItem3] = collection.splice(1, 1); - expect(removedItem3).toStrictEqual(item1); - expect(invocationCount).toBe(8); + expect(removedItem3) + .toStrictEqual(item1); + expect(invocationCount) + .toBe(8); item1.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(8); + expect(invocationCount) + .toBe(8); item2.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(9); + expect(invocationCount) + .toBe(9); const [removedItem4] = collection.splice(0); - expect(removedItem4).toStrictEqual(item2); - expect(invocationCount).toBe(10); + expect(removedItem4) + .toStrictEqual(item2); + expect(invocationCount) + .toBe(10); item1.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(10); + expect(invocationCount) + .toBe(10); item2.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(10); + expect(invocationCount) + .toBe(10); }); it('validation is not triggered when check returns false', () => { @@ -199,21 +232,26 @@ describe('CollectionItemValidationTrigger', (): void => { }, shouldTriggerValidation(actualItem) { checkInvocationCount++; - expect(actualItem).toStrictEqual(item); + expect(actualItem) + .toStrictEqual(item); + return false; } }); validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); item.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(0); - expect(checkInvocationCount).toBe(1); + expect(invocationCount) + .toBe(0); + expect(checkInvocationCount) + .toBe(1); }); }); diff --git a/src/validation/__tests__/CollectionReorderedValidationTrigger.test.ts b/src/validation/__tests__/CollectionReorderedValidationTrigger.test.ts index 8527ab4..fbdeed4 100644 --- a/src/validation/__tests__/CollectionReorderedValidationTrigger.test.ts +++ b/src/validation/__tests__/CollectionReorderedValidationTrigger.test.ts @@ -11,13 +11,15 @@ describe('CollectionReorderedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); collection.reverse(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is not triggered when collection changes but check returns false', (): void => { @@ -32,13 +34,15 @@ describe('CollectionReorderedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); collection.reverse(); - expect(invocationCount).toBe(0); + expect(invocationCount) + .toBe(0); }); it('validation is triggered when collection changes and check returns true', (): void => { @@ -53,12 +57,14 @@ describe('CollectionReorderedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); collection.reverse(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); }); \ No newline at end of file diff --git a/src/validation/__tests__/MapChangedValidationTrigger.test.ts b/src/validation/__tests__/MapChangedValidationTrigger.test.ts index d9f5f53..b778034 100644 --- a/src/validation/__tests__/MapChangedValidationTrigger.test.ts +++ b/src/validation/__tests__/MapChangedValidationTrigger.test.ts @@ -11,13 +11,15 @@ describe('MapChangedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); map.set(1, 'A'); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is not triggered when map changes but check returns false', (): void => { @@ -32,13 +34,15 @@ describe('MapChangedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); map.set(1, 'A'); - expect(invocationCount).toBe(0); + expect(invocationCount) + .toBe(0); }); it('validation is triggered when map changes and check returns true', (): void => { @@ -53,12 +57,14 @@ describe('MapChangedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); map.set(1, 'A'); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); }); \ No newline at end of file diff --git a/src/validation/__tests__/MapItemValidationTrigger.test.ts b/src/validation/__tests__/MapItemValidationTrigger.test.ts index a8e75b2..72fe20b 100644 --- a/src/validation/__tests__/MapItemValidationTrigger.test.ts +++ b/src/validation/__tests__/MapItemValidationTrigger.test.ts @@ -16,13 +16,15 @@ describe('MapItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); item.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is triggered when an item is added to the map', (): void => { @@ -37,14 +39,16 @@ describe('MapItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); const item = new TestItem(); map.set(1, item); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is triggered once when the map contains the same item multiple times and it changes', (): void => { @@ -60,13 +64,15 @@ describe('MapItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); item.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is triggered each time the same item is added to the map and triggered once when it changes ', (): void => { @@ -81,22 +87,27 @@ describe('MapItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); const item = new TestItem(); map.set(1, item); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); map.set(2, item); - expect(invocationCount).toBe(2); + expect(invocationCount) + .toBe(2); map.set(3, item); - expect(invocationCount).toBe(3); + expect(invocationCount) + .toBe(3); item.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(4); + expect(invocationCount) + .toBe(4); }); it('validation is no longer triggered when a removed item changes', () => { @@ -113,18 +124,22 @@ describe('MapItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); map.delete(1); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); item1.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); item2.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(2); + expect(invocationCount) + .toBe(2); }); it('validation is still triggered when the same item was added multiple times, removed, but at least one instance is still contained by the map', () => { @@ -141,51 +156,66 @@ describe('MapItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); map.set(3, item1); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); map.set(4, item1); - expect(invocationCount).toBe(2); + expect(invocationCount) + .toBe(2); map.delete(1); - expect(invocationCount).toBe(3); + expect(invocationCount) + .toBe(3); item1.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(4); + expect(invocationCount) + .toBe(4); item2.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(5); + expect(invocationCount) + .toBe(5); map.delete(3); - expect(invocationCount).toBe(6); + expect(invocationCount) + .toBe(6); item1.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(7); + expect(invocationCount) + .toBe(7); item2.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(8); + expect(invocationCount) + .toBe(8); map.delete(4); - expect(invocationCount).toBe(9); + expect(invocationCount) + .toBe(9); item1.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(9); + expect(invocationCount) + .toBe(9); item2.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(10); + expect(invocationCount) + .toBe(10); map.delete(2); - expect(invocationCount).toBe(11); + expect(invocationCount) + .toBe(11); item1.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(11); + expect(invocationCount) + .toBe(11); item2.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(11); + expect(invocationCount) + .toBe(11); }); it('validation is not triggered when check returns false', () => { @@ -200,21 +230,26 @@ describe('MapItemValidationTrigger', (): void => { }, shouldTriggerValidation(actualItem) { checkInvocationCount++; - expect(actualItem).toStrictEqual(item); + expect(actualItem) + .toStrictEqual(item); + return false; } }); validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); item.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(0); - expect(checkInvocationCount).toBe(1); + expect(invocationCount) + .toBe(0); + expect(checkInvocationCount) + .toBe(1); }); }); diff --git a/src/validation/__tests__/ObjectValidator.test.ts b/src/validation/__tests__/ObjectValidator.test.ts index 86456f2..129e9dd 100644 --- a/src/validation/__tests__/ObjectValidator.test.ts +++ b/src/validation/__tests__/ObjectValidator.test.ts @@ -1,6 +1,6 @@ import type { IValidatable } from '../IValidatable'; -import { ViewModel } from '../../viewModels'; import { ObservableCollection, ObservableSet, ObservableMap } from '../../collections'; +import { ViewModel } from '../../viewModels'; import { ObjectValidator } from '../objectValidator/ObjectValidator'; describe('ObjectValidator', () => { @@ -11,11 +11,14 @@ describe('ObjectValidator', () => { const objectValidator = new ObjectValidator({ target: validatable }); objectValidator.add(() => { invocationCount++; + return 'test error'; }); - expect(invocationCount).toBe(1); - expect(validatable.error).toBe('test error'); + expect(invocationCount) + .toBe(1); + expect(validatable.error) + .toBe('test error'); }); it('changing the target triggers a validation', () => { @@ -25,12 +28,15 @@ describe('ObjectValidator', () => { const objectValidator = new ObjectValidator({ target: validatable }); objectValidator.add(() => { invocationCount++; + return 'test error'; }); validatable.notifyPropertiesChanged(); - expect(invocationCount).toBe(2); - expect(validatable.error).toBe('test error'); + expect(invocationCount) + .toBe(2); + expect(validatable.error) + .toBe('test error'); }); it('changing the target triggers the validation one more time when changed target properties are ignored', () => { @@ -45,12 +51,15 @@ describe('ObjectValidator', () => { }); objectValidator.add(() => { invocationCount++; + return 'test error'; }); validatable.notifyPropertiesChanged(); - expect(invocationCount).toBe(3); - expect(validatable.error).toBe('test error'); + expect(invocationCount) + .toBe(3); + expect(validatable.error) + .toBe('test error'); }); it('using multiple validators executes them until first invalid one', () => { @@ -62,30 +71,35 @@ describe('ObjectValidator', () => { { validate() { validatorCalls.push('validator 1'); + return null; } }, { validate() { validatorCalls.push('validator 2'); + return undefined; } }, { validate() { validatorCalls.push('validator 3'); + return ''; } }, { validate() { validatorCalls.push('validator 4'); + return null; } } ); - expect(validatorCalls).toEqual(['validator 1', 'validator 2', 'validator 3']); + expect(validatorCalls) + .toEqual(['validator 1', 'validator 2', 'validator 3']); }); it('adding a view model trigger validates target when it changes', () => { @@ -97,14 +111,17 @@ describe('ObjectValidator', () => { objectValidator .add(() => { invocationCount++; + return 'test error'; }) .triggers .add(viewModelValidationTrigger); viewModelValidationTrigger.notifyPropertiesChanged(); - expect(invocationCount).toBe(2); - expect(validatable.error).toBe('test error'); + expect(invocationCount) + .toBe(2); + expect(validatable.error) + .toBe('test error'); }); it('adding an observable collection trigger validates target when it changes', () => { @@ -116,14 +133,17 @@ describe('ObjectValidator', () => { objectValidator .add(() => { invocationCount++; + return 'test error'; }) .triggers .add(observableCollectionValidationTrigger); observableCollectionValidationTrigger.push(1); - expect(invocationCount).toBe(2); - expect(validatable.error).toBe('test error'); + expect(invocationCount) + .toBe(2); + expect(validatable.error) + .toBe('test error'); }); it('adding an observable collection trigger validates target when it reorders', () => { @@ -135,14 +155,17 @@ describe('ObjectValidator', () => { objectValidator .add(() => { invocationCount++; + return 'test error'; }) .triggers .add(observableCollectionValidationTrigger); observableCollectionValidationTrigger.reverse(); - expect(invocationCount).toBe(2); - expect(validatable.error).toBe('test error'); + expect(invocationCount) + .toBe(2); + expect(validatable.error) + .toBe('test error'); }); it('adding an observable set trigger validates target when it changes', () => { @@ -154,14 +177,17 @@ describe('ObjectValidator', () => { objectValidator .add(() => { invocationCount++; + return 'test error'; }) .triggers .add(observableSetValidationTrigger); observableSetValidationTrigger.add(1); - expect(invocationCount).toBe(2); - expect(validatable.error).toBe('test error'); + expect(invocationCount) + .toBe(2); + expect(validatable.error) + .toBe('test error'); }); it('adding an observable map trigger validates target when it changes', () => { @@ -173,14 +199,17 @@ describe('ObjectValidator', () => { objectValidator .add(() => { invocationCount++; + return 'test error'; }) .triggers .add(observableMapValidationTrigger); observableMapValidationTrigger.set(1, 'a'); - expect(invocationCount).toBe(2); - expect(validatable.error).toBe('test error'); + expect(invocationCount) + .toBe(2); + expect(validatable.error) + .toBe('test error'); }); it('adding an item trigger validates target when an item changes', () => { @@ -193,15 +222,18 @@ describe('ObjectValidator', () => { objectValidator .add(() => { invocationCount++; + return 'test error'; }) .triggers - .add([observableCollection, item => [item]]); + .add([observableCollection, (item) => [item]]); item.notifyPropertiesChanged(); - expect(invocationCount).toBe(2); - expect(validatable.error).toBe('test error'); + expect(invocationCount) + .toBe(2); + expect(validatable.error) + .toBe('test error'); }); it('adding a set item trigger validates target when an item changes', () => { @@ -214,15 +246,18 @@ describe('ObjectValidator', () => { objectValidator .add(() => { invocationCount++; + return 'test error'; }) .triggers - .add([observableSet, item => [item]]); + .add([observableSet, (item) => [item]]); item.notifyPropertiesChanged(); - expect(invocationCount).toBe(2); - expect(validatable.error).toBe('test error'); + expect(invocationCount) + .toBe(2); + expect(validatable.error) + .toBe('test error'); }); it('adding a map item trigger validates target when an item changes', () => { @@ -235,15 +270,18 @@ describe('ObjectValidator', () => { objectValidator .add(() => { invocationCount++; + return 'test error'; }) .triggers - .add([observableMap, item => [item]]); + .add([observableMap, (item) => [item]]); item.notifyPropertiesChanged(); - expect(invocationCount).toBe(2); - expect(validatable.error).toBe('test error'); + expect(invocationCount) + .toBe(2); + expect(validatable.error) + .toBe('test error'); }); it('adding validation triggers multiple times only registers them once', () => { @@ -257,7 +295,7 @@ describe('ObjectValidator', () => { objectValidator .add( () => 'test error 1', - () => 'test error 2', + () => 'test error 2' ) .triggers .add(viewModelValidationTrigger) @@ -269,11 +307,16 @@ describe('ObjectValidator', () => { .add(observableSetValidationTrigger) .add(observableMapValidationTrigger); - expect(objectValidator.triggers.size).toBe(4); - expect(objectValidator.triggers).toContain(viewModelValidationTrigger); - expect(objectValidator.triggers).toContain(observableCollectionValidationTrigger); - expect(objectValidator.triggers).toContain(observableSetValidationTrigger); - expect(objectValidator.triggers).toContain(observableMapValidationTrigger); + expect(objectValidator.triggers.size) + .toBe(4); + expect(objectValidator.triggers) + .toContain(viewModelValidationTrigger); + expect(objectValidator.triggers) + .toContain(observableCollectionValidationTrigger); + expect(objectValidator.triggers) + .toContain(observableSetValidationTrigger); + expect(objectValidator.triggers) + .toContain(observableMapValidationTrigger); }); it('removing validation triggers no longer trigger validation', () => { @@ -285,6 +328,7 @@ describe('ObjectValidator', () => { objectValidator .add(() => { invocationCount++; + return 'test error 1'; }) .triggers @@ -293,7 +337,8 @@ describe('ObjectValidator', () => { objectValidator.triggers.clear(); viewModelValidationTrigger.notifyPropertiesChanged(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('adding a validator calls its onAdd hook', () => { @@ -303,7 +348,8 @@ describe('ObjectValidator', () => { const objectValidator = new ObjectValidator({ target: validatable }); objectValidator.add({ onAdd(target) { - expect(target).toBe(validatable); + expect(target) + .toBe(validatable); hookInvocationCount++; }, validate() { @@ -311,7 +357,8 @@ describe('ObjectValidator', () => { } }); - expect(hookInvocationCount).toBe(1); + expect(hookInvocationCount) + .toBe(1); }); it('removing a validator calls its onRemove hook', () => { @@ -321,7 +368,8 @@ describe('ObjectValidator', () => { const objectValidator = new ObjectValidator({ target: validatable }); objectValidator.add({ onRemove(target) { - expect(target).toBe(validatable); + expect(target) + .toBe(validatable); hookInvocationCount++; }, validate() { @@ -330,7 +378,8 @@ describe('ObjectValidator', () => { }); objectValidator.reset(); - expect(hookInvocationCount).toBe(1); + expect(hookInvocationCount) + .toBe(1); }); }); diff --git a/src/validation/__tests__/SetChangedValidationTrigger.test.ts b/src/validation/__tests__/SetChangedValidationTrigger.test.ts index 16d1851..a40339f 100644 --- a/src/validation/__tests__/SetChangedValidationTrigger.test.ts +++ b/src/validation/__tests__/SetChangedValidationTrigger.test.ts @@ -11,13 +11,15 @@ describe('SetChangedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); set.add(1); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is not triggered when set changes but check returns false', (): void => { @@ -32,13 +34,15 @@ describe('SetChangedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); set.add(1); - expect(invocationCount).toBe(0); + expect(invocationCount) + .toBe(0); }); it('validation is triggered when set changes and check returns true', (): void => { @@ -53,12 +57,14 @@ describe('SetChangedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); set.add(1); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); }); \ No newline at end of file diff --git a/src/validation/__tests__/SetItemValidationTrigger.test.ts b/src/validation/__tests__/SetItemValidationTrigger.test.ts index d56e2f2..97c3e66 100644 --- a/src/validation/__tests__/SetItemValidationTrigger.test.ts +++ b/src/validation/__tests__/SetItemValidationTrigger.test.ts @@ -16,13 +16,15 @@ describe('SetItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); item.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is triggered when an item is added to the set', (): void => { @@ -37,14 +39,16 @@ describe('SetItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); const item = new TestItem(); set.add(item); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is triggered each time a unique item is added to the set and triggered once when it changes ', (): void => { @@ -59,22 +63,27 @@ describe('SetItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); const item = new TestItem(); set.add(item); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); set.add(item); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); set.add(item); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); item.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(2); + expect(invocationCount) + .toBe(2); }); it('validation is no longer triggered when a removed item changes', () => { @@ -91,18 +100,22 @@ describe('SetItemValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); set.delete(item1); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); item1.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); item2.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(2); + expect(invocationCount) + .toBe(2); }); it('validation is not triggered when check returns false', () => { @@ -117,21 +130,26 @@ describe('SetItemValidationTrigger', (): void => { }, shouldTriggerValidation(actualItem) { checkInvocationCount++; - expect(actualItem).toStrictEqual(item); + expect(actualItem) + .toStrictEqual(item); + return false; } }); validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); item.viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(0); - expect(checkInvocationCount).toBe(1); + expect(invocationCount) + .toBe(0); + expect(checkInvocationCount) + .toBe(1); }); }); diff --git a/src/validation/__tests__/ViewModelChangedValidationTrigger.test.ts b/src/validation/__tests__/ViewModelChangedValidationTrigger.test.ts index 163585a..6eb20fa 100644 --- a/src/validation/__tests__/ViewModelChangedValidationTrigger.test.ts +++ b/src/validation/__tests__/ViewModelChangedValidationTrigger.test.ts @@ -11,13 +11,15 @@ describe('ViewModelChangedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); it('validation is not triggered when view model changes but check returns false', (): void => { @@ -32,13 +34,15 @@ describe('ViewModelChangedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(0); + expect(invocationCount) + .toBe(0); }); it('validation is triggered when view model changes and check returns true', (): void => { @@ -53,13 +57,15 @@ describe('ViewModelChangedValidationTrigger', (): void => { validationTrigger.validationTriggered.subscribe({ handle(subject) { invocationCount++; - expect(subject).toStrictEqual(validationTrigger); + expect(subject) + .toStrictEqual(validationTrigger); } }); viewModel.notifyPropertiesChanged(); - expect(invocationCount).toBe(1); + expect(invocationCount) + .toBe(1); }); }); diff --git a/src/validation/index.ts b/src/validation/index.ts index 45cfec8..5a5eecf 100644 --- a/src/validation/index.ts +++ b/src/validation/index.ts @@ -9,7 +9,7 @@ export { type IObjectValidator, type IValidationTriggersSet, type IObjectValidatorConfig, - ObjectValidator, + ObjectValidator } from './objectValidator'; export { @@ -27,4 +27,4 @@ export { type IMapItemValidationTriggerConfig, MapItemValidationTrigger, resolveValidationTriggers, resolveAllValidationTriggers -} from './triggers' \ No newline at end of file +} from './triggers'; \ No newline at end of file diff --git a/src/validation/objectValidator/IReadOnlyObjectValidator.ts b/src/validation/objectValidator/IReadOnlyObjectValidator.ts index a469a6d..3d3328d 100644 --- a/src/validation/objectValidator/IReadOnlyObjectValidator.ts +++ b/src/validation/objectValidator/IReadOnlyObjectValidator.ts @@ -27,9 +27,9 @@ export interface IReadOnlyObjectValidator & INotifyPropertiesChanged, TValidationError = string> implements IObjectValidator { private static _defaultShouldTargetTriggerValidation(target: IValidatable, changedProperties: readonly (keyof IValidatable)[]): boolean { - return changedProperties.some(changedProperty => { + return changedProperties.some((changedProperty) => { return changedProperty !== 'error' && changedProperty !== 'isValid' - && changedProperty !== 'isInvalid' + && changedProperty !== 'isInvalid'; }); } @@ -46,8 +45,8 @@ export class ObjectValidator _validate: this.validate.bind(this), handle(_, { addedItems: addedValidators, removedItems: removedValidators }) { - removedValidators.forEach(removedValidator => removedValidator.onRemove && removedValidator.onRemove(this._target)); - addedValidators.forEach(addedValidator => addedValidator.onAdd && addedValidator.onAdd(this._target)); + removedValidators.forEach((removedValidator) => removedValidator.onRemove && removedValidator.onRemove(this._target)); + addedValidators.forEach((addedValidator) => addedValidator.onAdd && addedValidator.onAdd(this._target)); this._validate(); } @@ -69,18 +68,18 @@ export class ObjectValidator this.triggers = new ObservableSet(); this.triggers.setChanged.subscribe({ handle(_, { addedItems: addedTriggers, removedItems: removedTriggers }) { - removedTriggers.forEach(removedTrigger => { + removedTriggers.forEach((removedTrigger) => { resolvedValidationTriggersBySource .get(removedTrigger) - ?.forEach(resolvedValidationTrigger => { + ?.forEach((resolvedValidationTrigger) => { resolvedValidationTrigger.validationTriggered.unsubscribe(validationTriggeredEventHandler); }); resolvedValidationTriggersBySource.delete(removedTrigger); }); - addedTriggers.forEach(addedTrigger => { + addedTriggers.forEach((addedTrigger) => { const resolvedValidationTriggers = resolveValidationTriggers(addedTrigger); - resolvedValidationTriggers.forEach(resolvedValidationTrigger => { + resolvedValidationTriggers.forEach((resolvedValidationTrigger) => { resolvedValidationTrigger.validationTriggered.subscribe(validationTriggeredEventHandler); }); resolvedValidationTriggersBySource.set(addedTrigger, resolvedValidationTriggers); @@ -95,7 +94,6 @@ export class ObjectValidator * Gets the object that is being validated. */ public readonly target: TValidatable; - /** * Gets the validators that have been configured. */ @@ -112,7 +110,7 @@ export class ObjectValidator */ public add(...validators: readonly (IValidator | ValidatorCallback)[]): this { if (validators !== null && validators !== undefined && Array.isArray(validators)) - this.validators.push(...validators.map(validator => { + this.validators.push(...validators.map((validator) => { if (typeof validator === 'function') return { validate: validator @@ -127,7 +125,7 @@ export class ObjectValidator /** * Validates the target using the currently configured validators. Validation does get triggered when the * target changes or when a trigger notifies that a validation should occur. - * + * * Only use this method for specific cases where a validation need to be manually triggered, usually this * should not be the case. */ @@ -144,6 +142,7 @@ export class ObjectValidator } this.target.error = error; + return error; } diff --git a/src/validation/triggers/CollectionChangedValidationTrigger.ts b/src/validation/triggers/CollectionChangedValidationTrigger.ts index b4b35e1..8b51814 100644 --- a/src/validation/triggers/CollectionChangedValidationTrigger.ts +++ b/src/validation/triggers/CollectionChangedValidationTrigger.ts @@ -45,7 +45,7 @@ export class CollectionChangedValidationTrigger { /** * Represents a collection item validation trigger. Instead of triggering a validation only when the collection changes, * a validation may be triggered by any of the contained items when they themselves change. - * + * * This is useful when within the collection there is a field that needs to be unique, * such as a unique name for each item in the collection. - * + * * @template TItem The type of items the collection contains. */ export class CollectionItemValidationTrigger extends ValidationTrigger & Iterable> { private readonly _validationTriggerSelector: ValidationTriggerSelector; - private readonly _shouldTriggerValidation: (item: TItem) => boolean; + private readonly _shouldTriggerValidation: (item: TItem)=> boolean; private readonly _itemValidationTriggersByItem: Map; private readonly _collectionChangedEventHandler: ICollectionChangedEventHandler, TItem>; @@ -66,13 +66,13 @@ export class CollectionItemValidationTrigger extends ValidationTrigger { addedItems.forEach(this._ensureItemValidationTriggers, this); - removedItems.forEach(removedItem => { + removedItems.forEach((removedItem) => { const itemEventHandler = this._itemValidationTriggersByItem.get(removedItem); if (itemEventHandler !== null && itemEventHandler !== undefined) { itemEventHandler.itemCount--; if (itemEventHandler.itemCount === 0) { - itemEventHandler.validationTriggers.forEach(validationTrigger => { + itemEventHandler.validationTriggers.forEach((validationTrigger) => { validationTrigger.validationTriggered.unsubscribe(itemEventHandler.validationTriggerEventHandler); }); this._itemValidationTriggersByItem.delete(removedItem); @@ -81,7 +81,7 @@ export class CollectionItemValidationTrigger extends ValidationTrigger extends ValidationTrigger extends ValidationTrigger { - validationTriggers.forEach(validationTrigger => { + validationTriggers.forEach((validationTrigger) => { validationTrigger.validationTriggered.unsubscribe(validationTriggerEventHandler); }); }); @@ -114,11 +115,11 @@ export class CollectionItemValidationTrigger extends ValidationTrigger { if (this._shouldTriggerValidation(item)) this.notifyValidationTriggered(); - }, + } }; const resolvedValidationTriggers = resolveAllValidationTriggers(this._validationTriggerSelector(item)); - resolvedValidationTriggers.forEach(validationTrigger => { + resolvedValidationTriggers.forEach((validationTrigger) => { validationTrigger.validationTriggered.subscribe(validationTriggerEventHandler); }); diff --git a/src/validation/triggers/CollectionReorderedValidationTrigger.ts b/src/validation/triggers/CollectionReorderedValidationTrigger.ts index 540a8dd..47aee0b 100644 --- a/src/validation/triggers/CollectionReorderedValidationTrigger.ts +++ b/src/validation/triggers/CollectionReorderedValidationTrigger.ts @@ -45,7 +45,7 @@ export class CollectionReorderedValidationTrigger { */ export class MapItemValidationTrigger extends ValidationTrigger & Iterable<[TKey, TItem]>> { private readonly _validationTriggerSelector: ValidationTriggerSelector; - private readonly _shouldTriggerValidation: (item: TItem) => boolean; + private readonly _shouldTriggerValidation: (item: TItem)=> boolean; private readonly _itemValidationTriggersByItem: Map; private readonly _maChangedEventHandler: IMapChangedEventHandler, TKey, TItem>; @@ -74,7 +74,7 @@ export class MapItemValidationTrigger extends ValidationTrigger { + itemEventHandler.validationTriggers.forEach((validationTrigger) => { validationTrigger.validationTriggered.unsubscribe(itemEventHandler.validationTriggerEventHandler); }); this._itemValidationTriggersByItem.delete(removedItem); @@ -83,7 +83,7 @@ export class MapItemValidationTrigger extends ValidationTrigger extends ValidationTrigger this._ensureItemValidationTriggers(item)); + Array.from(this.trigger) + .forEach(([, item]) => this._ensureItemValidationTriggers(item)); this.trigger.mapChanged.subscribe(this._maChangedEventHandler); } @@ -102,7 +103,7 @@ export class MapItemValidationTrigger extends ValidationTrigger { - validationTriggers.forEach(validationTrigger => { + validationTriggers.forEach((validationTrigger) => { validationTrigger.validationTriggered.unsubscribe(validationTriggerEventHandler); }); }); @@ -116,11 +117,11 @@ export class MapItemValidationTrigger extends ValidationTrigger { if (this._shouldTriggerValidation(item)) this.notifyValidationTriggered(); - }, + } }; const resolvedValidationTriggers = resolveAllValidationTriggers(this._validationTriggerSelector(item)); - resolvedValidationTriggers.forEach(validationTrigger => { + resolvedValidationTriggers.forEach((validationTrigger) => { validationTrigger.validationTriggered.subscribe(validationTriggerEventHandler); }); diff --git a/src/validation/triggers/SetChangedValidationTrigger.ts b/src/validation/triggers/SetChangedValidationTrigger.ts index 8ab2134..168ef01 100644 --- a/src/validation/triggers/SetChangedValidationTrigger.ts +++ b/src/validation/triggers/SetChangedValidationTrigger.ts @@ -45,7 +45,7 @@ export class SetChangedValidationTrigger; @@ -41,7 +41,7 @@ export interface ISetItemValidationTriggerConfig { */ export class SetItemValidationTrigger extends ValidationTrigger & Iterable> { private readonly _validationTriggerSelector: ValidationTriggerSelector; - private readonly _shouldTriggerValidation: (item: TItem) => boolean; + private readonly _shouldTriggerValidation: (item: TItem)=> boolean; private readonly _itemValidationTriggersByItem: Map; private readonly _setChangedEventHandler: ISetChangedEventHandler, TItem>; @@ -65,10 +65,10 @@ export class SetItemValidationTrigger extends ValidationTrigger { addedItems.forEach(this._addItemValidationTriggers, this); - removedItems.forEach(removedItem => { + removedItems.forEach((removedItem) => { const itemEventHandler = this._itemValidationTriggersByItem.get(removedItem); if (itemEventHandler !== null && itemEventHandler !== undefined) { - itemEventHandler.validationTriggers.forEach(validationTrigger => { + itemEventHandler.validationTriggers.forEach((validationTrigger) => { validationTrigger.validationTriggered.unsubscribe(itemEventHandler.validationTriggerEventHandler); }); this._itemValidationTriggersByItem.delete(removedItem); @@ -76,7 +76,7 @@ export class SetItemValidationTrigger extends ValidationTrigger extends ValidationTrigger extends ValidationTrigger { - validationTriggers.forEach(validationTrigger => { + validationTriggers.forEach((validationTrigger) => { validationTrigger.validationTriggered.unsubscribe(validationTriggerEventHandler); }); }); @@ -107,11 +108,11 @@ export class SetItemValidationTrigger extends ValidationTrigger { if (this._shouldTriggerValidation(item)) this.notifyValidationTriggered(); - }, + } }; const resolvedValidationTriggers = resolveAllValidationTriggers(this._validationTriggerSelector(item)); - resolvedValidationTriggers.forEach(validationTrigger => { + resolvedValidationTriggers.forEach((validationTrigger) => { validationTrigger.validationTriggered.subscribe(validationTriggerEventHandler); }); diff --git a/src/validation/triggers/ValidationTrigger.ts b/src/validation/triggers/ValidationTrigger.ts index bcd8e4d..61a851f 100644 --- a/src/validation/triggers/ValidationTrigger.ts +++ b/src/validation/triggers/ValidationTrigger.ts @@ -1,9 +1,9 @@ -import type { INotifyPropertiesChanged } from '../../viewModels'; -import type { INotifyCollectionChanged, INotifyCollectionReordered, INotifySetChanged, INotifyMapChanged } from '../../collections'; import type { ValidationTriggerSelector } from './ValidationTriggerSelector'; +import type { INotifyCollectionChanged, INotifyCollectionReordered, INotifySetChanged, INotifyMapChanged } from '../../collections'; +import type { INotifyPropertiesChanged } from '../../viewModels'; import type { IObjectValidator } from '../objectValidator'; -import { Form } from '../../forms'; import { type IEvent, EventDispatcher } from '../../events'; +import { Form } from '../../forms'; /** * Represent a set of well-known validation triggers. These are used to simplify @@ -12,25 +12,25 @@ import { type IEvent, EventDispatcher } from '../../events'; * * @template TKey The type of keys the map contains. * @template TItem The type of items the collection, set, or map contains. - * + * * @see {@link Form.validation} * @see {@link IObjectValidator} * @see {@link ValidationTrigger} */ export type WellKnownValidationTrigger = INotifyPropertiesChanged - | INotifyCollectionChanged - | INotifyCollectionReordered - | INotifySetChanged - | INotifyMapChanged - | [ + | INotifyCollectionChanged + | INotifyCollectionReordered + | INotifySetChanged + | INotifyMapChanged + | [ INotifyMapChanged & Iterable<[TKey, TItem]>, ValidationTriggerSelector - ] - | [ + ] + | [ (INotifyCollectionChanged | INotifySetChanged) & Iterable, ValidationTriggerSelector - ] + ]; /** * Represents a validation trigger. Generally, they wrap an observable object and whenever @@ -51,7 +51,7 @@ export abstract class ValidationTrigger { let addCount = 0; this._validationTriggeredEventDispatcher = new EventDispatcher(); this.validationTriggered = { - subscribe: eventHandler => { + subscribe: (eventHandler) => { addCount++; if (addCount === 1) this.subscribeToTarget(); @@ -59,7 +59,7 @@ export abstract class ValidationTrigger { this._validationTriggeredEventDispatcher.subscribe(eventHandler); }, - unsubscribe: eventHandler => { + unsubscribe: (eventHandler) => { this._validationTriggeredEventDispatcher.unsubscribe(eventHandler); addCount--; @@ -94,4 +94,4 @@ export abstract class ValidationTrigger { protected notifyValidationTriggered(): void { this._validationTriggeredEventDispatcher.dispatch(this); } -} +} \ No newline at end of file diff --git a/src/validation/triggers/ValidationTriggerSelector.ts b/src/validation/triggers/ValidationTriggerSelector.ts index 239c42a..369df94 100644 --- a/src/validation/triggers/ValidationTriggerSelector.ts +++ b/src/validation/triggers/ValidationTriggerSelector.ts @@ -9,17 +9,17 @@ import { Form } from '../../forms'; * * @see {@linkcode WellKnownValidationTrigger} */ -export type ValidationTriggerSelector = (object: T) => ValidationTriggerSet; +export type ValidationTriggerSelector = (object: T)=> ValidationTriggerSet; /** * Represents a single validation trigger or a range of validation triggers that should be * configured for an individual target. - * + * * @see {@linkcode Form.validation} * @see {@linkcode IObjectValidator} * @see {@linkcode WellKnownValidationTrigger} */ export type ValidationTriggerSet = WellKnownValidationTrigger - | ValidationTrigger - | readonly (WellKnownValidationTrigger | ValidationTrigger)[]; \ No newline at end of file + | ValidationTrigger + | readonly (WellKnownValidationTrigger | ValidationTrigger)[]; \ No newline at end of file diff --git a/src/validation/triggers/ViewModelChangedValidationTrigger.ts b/src/validation/triggers/ViewModelChangedValidationTrigger.ts index 884c985..40be21b 100644 --- a/src/validation/triggers/ViewModelChangedValidationTrigger.ts +++ b/src/validation/triggers/ViewModelChangedValidationTrigger.ts @@ -43,7 +43,7 @@ export class ViewModelChangedValidationTrigger(validationTriggers)) if (validationTriggers.length === 0) - return [] + return []; else return validationTriggers.reduce( (resolvedValidationTriggers, validationTrigger) => { resolvedValidationTriggers.push(...resolveValidationTriggers(validationTrigger)); + return resolvedValidationTriggers; }, new Array() diff --git a/src/validation/triggers/resolveValidationTriggers.ts b/src/validation/triggers/resolveValidationTriggers.ts index e099a0c..4bad32c 100644 --- a/src/validation/triggers/resolveValidationTriggers.ts +++ b/src/validation/triggers/resolveValidationTriggers.ts @@ -1,12 +1,12 @@ -import { type WellKnownValidationTrigger, ValidationTrigger } from './ValidationTrigger'; +import type { WellKnownValidationTrigger, ValidationTrigger } from './ValidationTrigger'; import { CollectionChangedValidationTrigger } from './CollectionChangedValidationTrigger'; +import { CollectionItemValidationTrigger } from './CollectionItemValidationTrigger'; import { CollectionReorderedValidationTrigger } from './CollectionReorderedValidationTrigger'; -import { SetChangedValidationTrigger } from './SetChangedValidationTrigger'; import { MapChangedValidationTrigger } from './MapChangedValidationTrigger'; -import { ViewModelChangedValidationTrigger } from './ViewModelChangedValidationTrigger'; -import { CollectionItemValidationTrigger } from './CollectionItemValidationTrigger'; -import { SetItemValidationTrigger } from './SetItemValidationTrigger'; import { MapItemValidationTrigger } from './MapItemValidationTrigger'; +import { SetChangedValidationTrigger } from './SetChangedValidationTrigger'; +import { SetItemValidationTrigger } from './SetItemValidationTrigger'; +import { ViewModelChangedValidationTrigger } from './ViewModelChangedValidationTrigger'; /** * Resolves the given well-known validation trigger to concrete ones. @@ -22,11 +22,20 @@ export function resolveValidationTriggers(validationTrigger: WellKnownValidation if (collection !== null && collection !== undefined && validationTrigger !== null && validationTrigger !== undefined) { if ('collectionChanged' in collection) - validationTriggers.push(new CollectionItemValidationTrigger({ collection, validationTriggerSelector })); + validationTriggers.push(new CollectionItemValidationTrigger({ + collection, + validationTriggerSelector + })); if ('setChanged' in collection) - validationTriggers.push(new SetItemValidationTrigger({ set: collection, validationTriggerSelector })); + validationTriggers.push(new SetItemValidationTrigger({ + set: collection, + validationTriggerSelector + })); if ('mapChanged' in collection) - validationTriggers.push(new MapItemValidationTrigger({ map: collection, validationTriggerSelector })); + validationTriggers.push(new MapItemValidationTrigger({ + map: collection, + validationTriggerSelector + })); } } else { diff --git a/webpack.config.ts b/webpack.config.ts index d25b65f..11d5a4e 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -1,14 +1,15 @@ +/* eslint-disable no-console */ import type { Compiler, Configuration } from 'webpack'; -import path from 'path'; -import { Application, CommentDisplayPart, DeclarationReflection, ParameterReflection, ReferenceType, Reflection, ReflectionKind, ReflectionSymbolId, SignatureReflection, SomeType, TypeParameterReflection } from 'typedoc'; import fs, { type MakeDirectoryOptions, type WriteFileOptions } from 'fs'; +import path from 'path'; +import { Application, type CommentDisplayPart, type DeclarationReflection, type ParameterReflection, type ReferenceType, type Reflection, ReflectionKind, type ReflectionSymbolId, type SignatureReflection, type SomeType, type TypeParameterReflection } from 'typedoc'; export default function (): Configuration { return { entry: './src/index.ts', mode: 'development', resolve: { - extensions: ['.tsx', '.ts', '.js'], + extensions: ['.tsx', '.ts', '.js'] }, output: { filename: 'index.js', @@ -37,9 +38,7 @@ export default function (): Configuration { amd: 'react' } }, - plugins: [ - new GenerateDocumentationPlugin() - ] + plugins: [new GenerateDocumentationPlugin()] }; } @@ -52,25 +51,26 @@ class GenerateDocumentationPlugin { readonly repository: { readonly url: string; - } + }; }; private readonly _declarationsById: Map; private _documentationIndex: IDocumentationIndex = null!; public constructor() { - this._packageInfo = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json')).toString()); + this._packageInfo = JSON.parse(fs.readFileSync(path.join(__dirname, 'package.json')) + .toString()); this._declarationsById = new Map(); } public apply(compiler: Compiler): void { - compiler.hooks.done.tapAsync('generate-documentation', async stats => { + compiler.hooks.done.tapAsync('generate-documentation', async (stats) => { if (stats.hasErrors()) console.error('Compilation has errors, did not generate documentation.'); else { await this._initializeAsync(); await this._generateDocumentationAsync(); } - }) + }); } private async _initializeAsync(): Promise { @@ -88,12 +88,19 @@ class GenerateDocumentationPlugin { if (declaration.variant === 'declaration') switch (declaration.kind) { case ReflectionKind.TypeAlias: + case ReflectionKind.Interface: + case ReflectionKind.Class: + case ReflectionKind.Constructor: + case ReflectionKind.Property: + case ReflectionKind.Accessor: + case ReflectionKind.Method: + case ReflectionKind.Function: this._declarationsById.set(declaration.id, declaration); if (declaration.children && declaration.children.length > 0) @@ -117,66 +124,69 @@ class GenerateDocumentationPlugin { await this._writeFileAsync(path.join(outputDirectory, '_Sidebar.md'), this._getSidebar()); await this._writeFileAsync(path.join(outputDirectory, '_Footer.md'), this._getFooter()); - await Promise.all(Array.from(this._declarationsById.values()).map(async declaration => { - try { - const subDirectory1 = path.join(...declaration.sources!.at(0)!.fileName.split('/').slice(0, -1)); - let subDirectory2: string; - let documentation: string | null = null; - switch (declaration.kind) { - case ReflectionKind.TypeAlias: - subDirectory2 = 'aliases'; - documentation = this._getAliasDocumentation(declaration); - break; - - case ReflectionKind.Interface: - subDirectory2 = 'interfaces'; - documentation = this._getInterfaceDocumentation(declaration); - break; - - case ReflectionKind.Class: - subDirectory2 = 'classes'; - documentation = this._getClassDocumentation(declaration); - break; - - case ReflectionKind.Constructor: - if (!declaration.flags.isInherited) { - subDirectory2 = 'constructors'; - documentation = this._getConstructorDocumentation(declaration); - } - break; - - case ReflectionKind.Property: - case ReflectionKind.Accessor: - if (!declaration.flags.isInherited) { - subDirectory2 = 'properties'; - documentation = this._getPropertyDocumentation(declaration); - } - break; + await Promise.all(Array.from(this._declarationsById.values()) + .map(async (declaration) => { + try { + const subDirectory1 = path.join(...declaration.sources!.at(0)!.fileName.split('/') + .slice(0, -1)); + let subDirectory2: string; + let documentation: string | null = null; + switch (declaration.kind) { + case ReflectionKind.TypeAlias: + subDirectory2 = 'aliases'; + documentation = this._getAliasDocumentation(declaration); + break; + + case ReflectionKind.Interface: + subDirectory2 = 'interfaces'; + documentation = this._getInterfaceDocumentation(declaration); + break; + + case ReflectionKind.Class: + subDirectory2 = 'classes'; + documentation = this._getClassDocumentation(declaration); + break; + + case ReflectionKind.Constructor: + if (!declaration.flags.isInherited) { + subDirectory2 = 'constructors'; + documentation = this._getConstructorDocumentation(declaration); + } + break; + + case ReflectionKind.Property: + + case ReflectionKind.Accessor: + if (!declaration.flags.isInherited) { + subDirectory2 = 'properties'; + documentation = this._getPropertyDocumentation(declaration); + } + break; + + case ReflectionKind.Method: + if (!declaration.flags.isInherited) { + subDirectory2 = 'methods'; + documentation = this._getMethodDocumentation(declaration); + } + break; + + case ReflectionKind.Function: + subDirectory2 = 'functions'; + documentation = this._getFunctionDocumentation(declaration); + break; + } - case ReflectionKind.Method: - if (!declaration.flags.isInherited) { - subDirectory2 = 'methods'; - documentation = this._getMethodDocumentation(declaration); - } - break; + if (documentation !== null) { + const directoryPath = path.join(outputDirectory, subDirectory1!, subDirectory2!); - case ReflectionKind.Function: - subDirectory2 = 'functions'; - documentation = this._getFunctionDocumentation(declaration); - break; + await this._createDirectoryAsync(directoryPath, { recursive: true }); + await this._writeFileAsync(path.join(directoryPath, `${this._getIdentifier(declaration)}.md`), documentation); + } } - - if (documentation !== null) { - const directoryPath = path.join(outputDirectory, subDirectory1!, subDirectory2!); - - await this._createDirectoryAsync(directoryPath, { recursive: true }); - await this._writeFileAsync(path.join(directoryPath, `${this._getIdentifier(declaration)}.md`), documentation); + catch (error) { + throw new Error(`Could not generate documentation for '${declaration}' on '${declaration.parent}'.\n${error}.`); } - } - catch (error) { - throw new Error(`Could not generate documentation for '${declaration}' on '${declaration.parent}'.\n${error}.`); - } - })); + })); } catch (error) { console.error(error); @@ -197,26 +207,26 @@ class GenerateDocumentationPlugin { private _getReadMe(): string { return ( - `${this._packageInfo.description}\n` + - '\n' + - [ + `${this._packageInfo.description}\n` + + '\n' + + [ '[Project Wiki](https://github.com/Andrei15193/react-model-view-viewmodel/wiki)', '[Guides and Tutorials - Getting Started](https://github.com/Andrei15193/react-model-view-viewmodel/discussions/7)', '[Project Discussions](https://github.com/Andrei15193/react-model-view-viewmodel/discussions)', '[Releases](https://github.com/Andrei15193/react-model-view-viewmodel/releases)', '[CodeSandbox](https://codesandbox.io/p/sandbox/react-mvvm-vwsqlv)' - ].join(' | ') + '\n' + - '\n' + - '**API**\n' + - '\n' + - this + ].join(' | ') + '\n' + + '\n' + + '**API**\n' + + '\n' + + this ._documentationIndex .namespaces - .map(namespace => { + .map((namespace) => { return `* **${namespace.name}**\n` + namespace .declarations - .filter(declaration => declaration.promoted) - .map(declaration => ` * [${this._getSimpleName(declaration)}](${this._getProjectReferenceUrl(declaration)})`) + .filter((declaration) => declaration.promoted) + .map((declaration) => ` * [${this._getSimpleName(declaration)}](${this._getProjectReferenceUrl(declaration)})`) .join('\n'); }) .join('\n') @@ -225,20 +235,20 @@ class GenerateDocumentationPlugin { private _getLandingPage(): string { return ( - `${this._packageInfo.description}\n` + - '\n' + - overview.trim() + - '\n' + - landingPageNavigation + - '\n' + - motivation.trim() + - '\n' + - landingPageNavigation + - '\n' + - '### API\n\n' + this + `${this._packageInfo.description}\n` + + '\n' + + overview.trim() + + '\n' + + landingPageNavigation + + '\n' + + motivation.trim() + + '\n' + + landingPageNavigation + + '\n' + + '### API\n\n' + this ._documentationIndex .namespaces - .map(namespace => { + .map((namespace) => { let listMarker = '*'; return `#### **${namespace.name}**\n` + namespace @@ -251,9 +261,9 @@ class GenerateDocumentationPlugin { }) .join('\n'); }) - .join('\n') + '\n' + - '\n' + - landingPageNavigation + .join('\n') + '\n' + + '\n' + + landingPageNavigation ); } @@ -269,11 +279,11 @@ class GenerateDocumentationPlugin { + this ._documentationIndex .namespaces - .map(namespace => { + .map((namespace) => { return `**${namespace.name}** \n` + namespace .declarations - .filter(declaration => declaration.promoted) - .map(declaration => `[${declaration.name}](${this._getProjectReferenceUrl(declaration)})`) + .filter((declaration) => declaration.promoted) + .map((declaration) => `[${declaration.name}](${this._getProjectReferenceUrl(declaration)})`) .join(' \n'); }) .join('\n\n') @@ -314,7 +324,8 @@ ${this._getRemarks(aliasDeclaration)} ${this._getGuidance(aliasDeclaration)} ${this._getReferences(aliasDeclaration)} -`.replace(/\n{3,}/g, '\n\n').trim(); +`.replace(/\n{3,}/g, '\n\n') + .trim(); } private _getInterfaceDocumentation(interfaceDeclaration: DeclarationReflection): string { @@ -350,7 +361,8 @@ ${this._getImplementations(interfaceDeclaration)} ${this._getGuidance(interfaceDeclaration)} ${this._getReferences(interfaceDeclaration)} -`.replace(/\n{3,}/g, '\n\n').trim(); +`.replace(/\n{3,}/g, '\n\n') + .trim(); } private _getClassDocumentation(classDeclaration: DeclarationReflection): string { @@ -388,17 +400,20 @@ ${this._getClassHierarchy(classDeclaration)} ${this._getGuidance(classDeclaration)} ${this._getReferences(classDeclaration)} -`.replace(/\n{3,}/g, '\n\n').trim(); +`.replace(/\n{3,}/g, '\n\n') + .trim(); } private _getConstructorDocumentation(constructorDeclaration: DeclarationReflection): string { return ` ###### [API](https://github.com/Andrei15193/react-model-view-viewmodel/wiki#api) / [${this._getFullName(constructorDeclaration.parent as DeclarationReflection)}](${this._getProjectReferenceUrl(constructorDeclaration.parent as DeclarationReflection)}) / constructor -${constructorDeclaration.signatures && constructorDeclaration.signatures.length > 1 ? `This constructor has multiple overloads.\n\n----\n\n` : ''} +${constructorDeclaration.signatures && constructorDeclaration.signatures.length > 1 ? 'This constructor has multiple overloads.\n\n----\n\n' : ''} -${(constructorDeclaration.signatures || []).map(this._getConstructorSignatureDocumentation, this).join('\n\n----\n\n')} -`.replace(/\n{3,}/g, '\n\n').trim(); +${(constructorDeclaration.signatures || []).map(this._getConstructorSignatureDocumentation, this) + .join('\n\n----\n\n')} +`.replace(/\n{3,}/g, '\n\n') + .trim(); } private _getConstructorSignatureDocumentation(constructorSignature: SignatureReflection): string { @@ -422,7 +437,8 @@ ${this._getRemarks(constructorSignature)} ${this._getGuidance(constructorSignature)} ${this._getReferences(constructorSignature)} -`.replace(/\n{3,}/g, '\n\n').trim(); +`.replace(/\n{3,}/g, '\n\n') + .trim(); } private _getPropertyDocumentation(propertyDeclaration: DeclarationReflection): string { @@ -454,7 +470,8 @@ ${this._getRemarks(propertyDeclaration)} ${this._getGuidance(propertyDeclaration)} ${this._getReferences(propertyDeclaration)} -`.replace(/\n{3,}/g, '\n\n').trim(); +`.replace(/\n{3,}/g, '\n\n') + .trim(); } private _getMethodDocumentation(methodDeclaration: DeclarationReflection): string { @@ -463,10 +480,12 @@ ${this._getReferences(propertyDeclaration)} ${this._getOverride(methodDeclaration)} -${methodDeclaration.signatures && methodDeclaration.signatures.length > 1 ? `This method has multiple overloads.\n\n----\n\n` : ''} +${methodDeclaration.signatures && methodDeclaration.signatures.length > 1 ? 'This method has multiple overloads.\n\n----\n\n' : ''} -${(methodDeclaration.signatures || []).map(this._getMethodSignatureDocumentation, this).join('\n\n----\n\n')} -`.replace(/\n{3,}/g, '\n\n').trim(); +${(methodDeclaration.signatures || []).map(this._getMethodSignatureDocumentation, this) + .join('\n\n----\n\n')} +`.replace(/\n{3,}/g, '\n\n') + .trim(); } private _getMethodSignatureDocumentation(methodSignature: SignatureReflection): string { @@ -494,7 +513,8 @@ ${this._getRemarks(methodSignature)} ${this._getGuidance(methodSignature)} ${this._getReferences(methodSignature)} -`.replace(/\n{3,}/g, '\n\n').trim(); +`.replace(/\n{3,}/g, '\n\n') + .trim(); } private _getFunctionDocumentation(functionDeclaration: DeclarationReflection): string { @@ -503,8 +523,10 @@ ${this._getReferences(methodSignature)} ${functionDeclaration.signatures && functionDeclaration.signatures.length > 1 ? `This ${functionDeclaration.name.startsWith('use') ? 'hook' : 'function'} has multiple overloads.\n\n----\n\n` : ''} -${(functionDeclaration.signatures || []).map(this._getFunctionSignatureDocumentation, this).join('\n\n----\n\n')} -`.replace(/\n{3,}/g, '\n\n').trim(); +${(functionDeclaration.signatures || []).map(this._getFunctionSignatureDocumentation, this) + .join('\n\n----\n\n')} +`.replace(/\n{3,}/g, '\n\n') + .trim(); } private _getFunctionSignatureDocumentation(functionSignature: SignatureReflection): string { @@ -532,28 +554,35 @@ ${this._getRemarks(functionSignature)} ${this._getGuidance(functionSignature)} ${this._getReferences(functionSignature)} -`.replace(/\n{3,}/g, '\n\n').trim(); +`.replace(/\n{3,}/g, '\n\n') + .trim(); } private _getIdentifier(declaration: DeclarationReflection): string { switch (declaration.kind) { case ReflectionKind.Constructor: + case ReflectionKind.Property: + case ReflectionKind.Accessor: + case ReflectionKind.Method: if (declaration.flags.isInherited) return declaration.inheritedFrom!.reflection!.parent!.name + '.' + declaration.name; - else - return declaration.parent!.name + '.' + declaration.name; + + return declaration.parent!.name + '.' + declaration.name; case ReflectionKind.Class: + case ReflectionKind.Interface: + case ReflectionKind.TypeParameter: + case ReflectionKind.Function: + case ReflectionKind.TypeAlias: return declaration.name; - default: throw new Error(`Unhandled '${declaration}' declaration when trying to determine identifier.`); } @@ -562,14 +591,20 @@ ${this._getReferences(functionSignature)} private _getFullName(declaration: DeclarationReflection): string { switch (declaration.kind) { case ReflectionKind.Constructor: + case ReflectionKind.Property: + case ReflectionKind.Accessor: + case ReflectionKind.Method: return declaration.parent!.name + '.' + this._getSimpleName(declaration); case ReflectionKind.TypeAlias: + case ReflectionKind.Class: + case ReflectionKind.Interface: + case ReflectionKind.Function: return this._getSimpleName(declaration); @@ -580,18 +615,20 @@ ${this._getReferences(functionSignature)} private _getSimpleName(declaration: DeclarationReflection): string { if (declaration.typeParameters && declaration.typeParameters.length > 0) - return `${declaration.name}\\<${declaration.typeParameters.map(typeParameter => typeParameter.name).join(', ')}\\>`; - else - return declaration.name; + return `${declaration.name}\\<${declaration.typeParameters.map((typeParameter) => typeParameter.name) + .join(', ')}\\>`; + + return declaration.name; } private _getInheritaceAndImplementations(declaration: DeclarationReflection): string { try { - let extensions = ""; + let extensions = ''; if (declaration.extendedTypes && declaration.extendedTypes.length > 0) { extensions += 'Extends '; - extensions += declaration.extendedTypes.map(this._getReferenceLink, this).join(', '); + extensions += declaration.extendedTypes.map(this._getReferenceLink, this) + .join(', '); extensions += '.'; } @@ -599,7 +636,8 @@ ${this._getReferences(functionSignature)} if (extensions.length > 0) extensions += ' \n'; extensions += 'Implements '; - extensions += declaration.implementedTypes.map(this._getReferenceLink, this).join(', '); + extensions += declaration.implementedTypes.map(this._getReferenceLink, this) + .join(', '); extensions += '.'; } @@ -620,17 +658,17 @@ ${this._getReferences(functionSignature)} implementations.push(...current.implementedBy); if (current.extendedBy && current.extendedBy.length > 0) - toVisit.unshift(...current.extendedBy.map(extension => this._findDeclaration(extension.reflection))); + toVisit.unshift(...current.extendedBy.map((extension) => this._findDeclaration(extension.reflection))); } while (toVisit.length > 0); if (implementations.length > 0) - return '### Implementations\n\n' + - implementations + return '### Implementations\n\n' + + implementations .sort((left, right) => left.name.localeCompare(right.name, 'en-US')) - .map(implementation => `* ${this._getReferenceLink(implementation)}`) + .map((implementation) => `* ${this._getReferenceLink(implementation)}`) .join('\n'); - else - return ''; + + return ''; } catch (error) { throw new Error(`Could not generate class hierarchy information for ${declaration}.\n${error}`); @@ -670,13 +708,13 @@ ${this._getReferences(functionSignature)} 'increment', ...current .extendedBy - .map(derivative => this._findDeclaration(derivative.reflection)), + .map((derivative) => this._findDeclaration(derivative.reflection)), 'decrement' ); } break; } - } while (toVisit.length > 0) + } while (toVisit.length > 0); return hirerachy; } @@ -693,6 +731,7 @@ ${this._getReferences(functionSignature)} let declarationName: string; switch (declaration.kind) { case ReflectionKind.Property: + case ReflectionKind.Accessor: declarationName = 'property'; break; @@ -721,14 +760,12 @@ ${this._getReferences(functionSignature)} case ReflectionKind.Property: if (declaration.type) return `Property type: ${this._getReferenceLink(declaration.type)}.`; - else - throw new Error(`Property '${declaration.name}' on ${declaration.parent?.name} has no type.`); + throw new Error(`Property '${declaration.name}' on ${declaration.parent?.name} has no type.`); case ReflectionKind.Accessor: if (declaration.getSignature && declaration.getSignature.type) return `Property type: ${this._getReferenceLink(declaration.getSignature.type)}.`; - else - throw new Error(`Property (accessor) '${declaration.name}' on ${declaration.parent?.name} has no type.`); + throw new Error(`Property (accessor) '${declaration.name}' on ${declaration.parent?.name} has no type.`); default: throw new Error(`Unhandled '${declaration.kind}' property type.`); @@ -745,7 +782,8 @@ ${this._getReferences(functionSignature)} let aliasDeclaration = `type ${declaration.name}`; if (declaration.typeParameters && declaration.typeParameters.length > 0) - aliasDeclaration += `<${declaration.typeParameters.map(this._getTypeParameterDeclaration, this).join(', ')}>`; + aliasDeclaration += `<${declaration.typeParameters.map(this._getTypeParameterDeclaration, this) + .join(', ')}>`; switch (declaration.type?.type) { case 'union': @@ -754,14 +792,15 @@ ${this._getReferences(functionSignature)} + declaration .type .types - .map(type => { + .map((type) => { if (type.type === 'tuple') - return `[\n ${type.elements.map(this._getTypeReferenceDeclaration, this).join(',\n ')}\n ]`; - else - return this._getTypeReferenceDeclaration(type); + return `[\n ${type.elements.map(this._getTypeReferenceDeclaration, this) + .join(',\n ')}\n ]`; + + return this._getTypeReferenceDeclaration(type); }) .join('\n| ') - + ';'; + + ';'; case 'reflection': if ( @@ -771,8 +810,8 @@ ${this._getReferences(functionSignature)} && declaration.type.declaration.signatures.at(0)!.name === declaration.type.declaration.name ) return aliasDeclaration + '\n = ' + this._getTypeReferenceDeclaration(declaration.type!) + ';'; - else - return aliasDeclaration + ' = ' + this._getTypeReferenceDeclaration(declaration.type!) + ';'; + + return aliasDeclaration + ' = ' + this._getTypeReferenceDeclaration(declaration.type!) + ';'; case 'indexedAccess': return aliasDeclaration + '\n = ' + this._getTypeReferenceDeclaration(declaration.type!) + ';'; @@ -785,9 +824,11 @@ ${this._getReferences(functionSignature)} let interfaceDeclaration = `interface ${declaration.name}`; if (declaration.typeParameters && declaration.typeParameters.length > 0) - interfaceDeclaration += `<${declaration.typeParameters.map(this._getTypeParameterDeclaration, this).join(', ')}>`; + interfaceDeclaration += `<${declaration.typeParameters.map(this._getTypeParameterDeclaration, this) + .join(', ')}>`; if (declaration.extendedTypes && declaration.extendedTypes.length > 0) - interfaceDeclaration += `\n extends ${declaration.extendedTypes.map(this._getTypeReferenceDeclaration, this).join(', ')}`; + interfaceDeclaration += `\n extends ${declaration.extendedTypes.map(this._getTypeReferenceDeclaration, this) + .join(', ')}`; return interfaceDeclaration; @@ -798,11 +839,14 @@ ${this._getReferences(functionSignature)} classDeclaration = 'abstract ' + classDeclaration; if (declaration.typeParameters && declaration.typeParameters.length > 0) - classDeclaration += `<${declaration.typeParameters.map(this._getTypeParameterDeclaration, this).join(', ')}>`; + classDeclaration += `<${declaration.typeParameters.map(this._getTypeParameterDeclaration, this) + .join(', ')}>`; if (declaration.extendedTypes && declaration.extendedTypes.length > 0) - classDeclaration += `\n extends ${declaration.extendedTypes.map(this._getTypeReferenceDeclaration, this).join(', ')}`; + classDeclaration += `\n extends ${declaration.extendedTypes.map(this._getTypeReferenceDeclaration, this) + .join(', ')}`; if (declaration.implementedTypes && declaration.implementedTypes.length > 0) - classDeclaration += `\n implements ${declaration.implementedTypes.map(this._getTypeReferenceDeclaration, this).join(', ')}`; + classDeclaration += `\n implements ${declaration.implementedTypes.map(this._getTypeReferenceDeclaration, this) + .join(', ')}`; return classDeclaration; @@ -817,7 +861,7 @@ ${this._getReferences(functionSignature)} declaration.parent.flags.isAbstract && 'abstract' ] - .filter(flag => flag) + .filter((flag) => flag) .join(' '); if (constructorDeclaration.length > 0) @@ -850,7 +894,7 @@ ${this._getReferences(functionSignature)} declaration.flags.isReadonly && 'readonly', declaration.flags.isConst && 'const' ] - .filter(flag => flag) + .filter((flag) => flag) .join(' '); propertyDeclaration += ` ${declaration.name}`; @@ -878,14 +922,14 @@ ${this._getReferences(functionSignature)} declaration.flags.isProtected && 'protected', declaration.flags.isPublic && 'public' ] - .filter(flag => flag) + .filter((flag) => flag) .at(0) as string; getSignature += [ declaration.flags.isAbstract && 'abstract', declaration.flags.isStatic && 'static' ] - .filter(flag => flag) + .filter((flag) => flag) .join(' '); getSignature += ' get '; getSignature += declaration.name; @@ -908,14 +952,14 @@ ${this._getReferences(functionSignature)} declaration.flags.isProtected && 'protected', declaration.flags.isPublic && 'public' ] - .filter(flag => flag) + .filter((flag) => flag) .at(0) as string; setSignature += [ declaration.flags.isAbstract && 'abstract', declaration.flags.isStatic && 'static' ] - .filter(flag => flag) + .filter((flag) => flag) .join(' '); setSignature += ' set '; setSignature += declaration.name; @@ -924,7 +968,7 @@ ${this._getReferences(functionSignature)} setSignature += declaration .setSignature .parameters - .map(parameter => `${parameter.name}: ${this._getTypeReferenceDeclaration(parameter.type!)}`); + .map((parameter) => `${parameter.name}: ${this._getTypeReferenceDeclaration(parameter.type!)}`); else throw new Error(`Accessor '${declaration.name}' on '${declaration.parent?.name}' has no set type.`); @@ -946,7 +990,7 @@ ${this._getReferences(functionSignature)} declaration.parent.flags.isAbstract && 'abstract' ] - .filter(flag => flag) + .filter((flag) => flag) .join(' '); if (functionDeclaration.length > 0) @@ -958,7 +1002,8 @@ ${this._getReferences(functionSignature)} functionDeclaration += declaration.name; if (declaration.typeParameters && declaration.typeParameters.length > 0) - functionDeclaration += `<${declaration.typeParameters.map(this._getTypeParameterDeclaration, this).join(', ')}>`; + functionDeclaration += `<${declaration.typeParameters.map(this._getTypeParameterDeclaration, this) + .join(', ')}>`; const paramters = (declaration as SignatureReflection)?.parameters || []; if (paramters.length > 0) { @@ -1013,12 +1058,17 @@ ${this._getReferences(functionSignature)} private _getTypeReferenceDeclaration(typeReference: SomeType): string { switch (typeReference.type) { case 'reference': - return `${typeReference.name}${typeReference.typeArguments && typeReference.typeArguments.length > 0 ? `<${typeReference.typeArguments.map(this._getTypeReferenceDeclaration, this).join(', ')}>` : ''}`; + return `${typeReference.name}${typeReference.typeArguments && typeReference.typeArguments.length > 0 + ? `<${typeReference.typeArguments.map(this._getTypeReferenceDeclaration, this) + .join(', ')}>` + : ''}`; case 'reflection': switch (typeReference.declaration.kind) { case ReflectionKind.TypeAlias: + case ReflectionKind.Class: + case ReflectionKind.Interface: let typeReferenceDeclaration = typeReference.declaration.name; if (typeReference.declaration.typeParameters && typeReference.declaration.typeParameters.length > 0) { @@ -1026,7 +1076,7 @@ ${this._getReferences(functionSignature)} typeReferenceDeclaration += typeReference .declaration .typeParameters - .map(genericParameter => this._getTypeReferenceDeclaration(genericParameter.type!)) + .map((genericParameter) => this._getTypeReferenceDeclaration(genericParameter.type!)) .join(', '); typeReferenceDeclaration += '>'; } @@ -1038,30 +1088,33 @@ ${this._getReferences(functionSignature)} return this._getTypeReferenceDeclaration(typeReference.declaration.type); else if (typeReference.declaration.signatures) { if (typeReference.declaration.signatures.length === 1 && typeReference.declaration.signatures.at(0)!.name === typeReference.declaration.name) { - const signature = typeReference.declaration.signatures.at(0)! - return `(${(signature.parameters || []).map(this._getParameterDeclaration, this).join(', ')}) => ${this._getTypeReferenceDeclaration(signature.type!)}`; - } - else { - let signaturesDeclarations = '{\n '; - signaturesDeclarations += typeReference - .declaration - .signatures - .map(signature => { - if (signature.kind === ReflectionKind.ConstructorSignature) - return `new (${(signature.parameters || []).map(this._getParameterDeclaration, this).join(', ')}): ${this._getTypeReferenceDeclaration(signature.type!)};`; - else { - const genericTypeDeclaration = signature.typeParameters?.map(this._getTypeParameterDeclaration, this).join(', ') || ''; - return `${signature.name}${genericTypeDeclaration.length > 0 ? '<' + genericTypeDeclaration + '>' : ''}(${(signature.parameters || []).map(this._getParameterDeclaration, this).join(', ')}): ${this._getTypeReferenceDeclaration(signature.type!)};`; - } - }) - .join('\n '); - signaturesDeclarations += '\n}'; + const signature = typeReference.declaration.signatures.at(0)!; - return signaturesDeclarations; + return `(${(signature.parameters || []).map(this._getParameterDeclaration, this) + .join(', ')}) => ${this._getTypeReferenceDeclaration(signature.type!)}`; } + + let signaturesDeclarations = '{\n '; + signaturesDeclarations += typeReference + .declaration + .signatures + .map((signature) => { + if (signature.kind === ReflectionKind.ConstructorSignature) + return `new (${(signature.parameters || []).map(this._getParameterDeclaration, this) + .join(', ')}): ${this._getTypeReferenceDeclaration(signature.type!)};`; + + const genericTypeDeclaration = signature.typeParameters?.map(this._getTypeParameterDeclaration, this) + .join(', ') || ''; + + return `${signature.name}${genericTypeDeclaration.length > 0 ? '<' + genericTypeDeclaration + '>' : ''}(${(signature.parameters || []).map(this._getParameterDeclaration, this) + .join(', ')}): ${this._getTypeReferenceDeclaration(signature.type!)};`; + }) + .join('\n '); + signaturesDeclarations += '\n}'; + + return signaturesDeclarations; } - else - throw new Error(`Unhandled '${typeReference.declaration}' type literal reflection reference declaration.`); + throw new Error(`Unhandled '${typeReference.declaration}' type literal reflection reference declaration.`); case ReflectionKind.IndexSignature: return typeReference.declaration.name; @@ -1071,10 +1124,12 @@ ${this._getReferences(functionSignature)} } case 'intersection': - return typeReference.types.map(this._getTypeReferenceDeclaration, this).join(' | '); + return typeReference.types.map(this._getTypeReferenceDeclaration, this) + .join(' | '); case 'union': - return typeReference.types.map(this._getTypeReferenceDeclaration, this).join(' | '); + return typeReference.types.map(this._getTypeReferenceDeclaration, this) + .join(' | '); case 'intrinsic': return typeReference.name; @@ -1087,12 +1142,14 @@ ${this._getReferences(functionSignature)} return 'null'; else if (typeReference.value === 'propertiesChanged') return 'keyof this'; - else switch (typeof typeReference.value) { + switch (typeof typeReference.value) { case 'string': return `"${typeReference.value}"`; case 'number': + case 'bigint': + case 'boolean': return typeReference.value.toString(); @@ -1101,7 +1158,8 @@ ${this._getReferences(functionSignature)} } case 'tuple': - return `[${typeReference.elements.map(this._getTypeReferenceDeclaration, this).join(', ')}]`; + return `[${typeReference.elements.map(this._getTypeReferenceDeclaration, this) + .join(', ')}]`; case 'typeOperator': return `${typeReference.operator} ${this._getTypeReferenceDeclaration(typeReference.target)}`; @@ -1109,15 +1167,17 @@ ${this._getReferences(functionSignature)} case 'array': switch (typeReference.elementType.type) { case 'reference': + case 'reflection': + case 'intrinsic': return `${this._getTypeReferenceDeclaration(typeReference.elementType)}[]`; case 'literal': if (typeReference.elementType.value === 'propertiesChanged') return '(keyof this)[]'; - else - return `${this._getTypeReferenceDeclaration(typeReference.elementType)}[]`; + + return `${this._getTypeReferenceDeclaration(typeReference.elementType)}[]`; default: return `(${this._getTypeReferenceDeclaration(typeReference.elementType)})[]`; @@ -1126,8 +1186,7 @@ ${this._getReferences(functionSignature)} case 'predicate': if (typeReference.targetType) return `${typeReference.name} is ${this._getTypeReferenceDeclaration(typeReference.targetType)}`; - else - throw new Error('Unhandled predicate type declaration.'); + throw new Error('Unhandled predicate type declaration.'); case 'indexedAccess': return this._getTypeReferenceDeclaration(typeReference.objectType) + `[${this._getTypeReferenceDeclaration(typeReference.indexType)}]`; @@ -1234,20 +1293,24 @@ ${this._getReferences(functionSignature)} if (typeReference.typeArguments && typeReference.typeArguments.length > 0) { typeReferenceLink += '\\<'; - typeReferenceLink += typeReference.typeArguments.map(this._getReferenceLink, this).join(', '); + typeReferenceLink += typeReference.typeArguments.map(this._getReferenceLink, this) + .join(', '); typeReferenceLink += '\\>'; } return typeReferenceLink; case 'tuple': - return `\\[${typeReference.elements.map(this._getReferenceLink, this).join(', ')}\\]`; + return `\\[${typeReference.elements.map(this._getReferenceLink, this) + .join(', ')}\\]`; case 'intersection': - return typeReference.types.map(this._getReferenceLink, this).join(' & '); + return typeReference.types.map(this._getReferenceLink, this) + .join(' & '); case 'union': - return typeReference.types.map(this._getReferenceLink, this).join(' | '); + return typeReference.types.map(this._getReferenceLink, this) + .join(' | '); case 'typeOperator': let operatorLink: string; @@ -1269,15 +1332,17 @@ ${this._getReferences(functionSignature)} case 'array': switch (typeReference.elementType.type) { case 'reference': + case 'reflection': + case 'intrinsic': return `${this._getReferenceLink(typeReference.elementType)}[]`; case 'literal': if (typeReference.elementType.value === 'propertiesChanged') return `(${this._getReferenceLink(typeReference.elementType)})[]`; - else - return `${this._getReferenceLink(typeReference.elementType)}[]`; + + return `${this._getReferenceLink(typeReference.elementType)}[]`; default: return `(${this._getReferenceLink(typeReference.elementType)})[]`; @@ -1331,8 +1396,7 @@ ${this._getReferences(functionSignature)} case 'predicate': if (typeReference.targetType) return `\`${typeReference.name}\` is ${this._getReferenceLink(typeReference.targetType)}`; - else - throw new Error('Unhandled predicate type reference.'); + throw new Error('Unhandled predicate type reference.'); case 'reflection': const declaration = typeReference.declaration; @@ -1341,20 +1405,18 @@ ${this._getReferences(functionSignature)} if (declaration.signatures) return declaration .signatures - .map(signature => { - return '(' + - (signature.parameters || []) - .map(parameter => parameter.name + - (parameter.flags.isOptional ? '?: ' : ':') + - this._getReferenceLink(parameter.type!) - ) - .join(', ') + - ') => ' + - this._getReferenceLink(signature.type!) + .map((signature) => { + return '(' + + (signature.parameters || []) + .map((parameter) => parameter.name + + (parameter.flags.isOptional ? '?: ' : ':') + + this._getReferenceLink(parameter.type!)) + .join(', ') + + ') => ' + + this._getReferenceLink(signature.type!); }) .join(' | '); - else - throw new Error(`Unhandled '${declaration}' type literal reflection reference.`) + throw new Error(`Unhandled '${declaration}' type literal reflection reference.`); default: throw new Error(`Unhandled '${typeReference.declaration.kind}' reflection type reference.`); @@ -1365,7 +1427,7 @@ ${this._getReferences(functionSignature)} } } catch (error) { - throw new Error(`Could not get a reference link for '${typeReference}'.\n${error}`) + throw new Error(`Could not get a reference link for '${typeReference}'.\n${error}`); } } @@ -1384,7 +1446,7 @@ ${this._getReferences(functionSignature)} let deprecationNotice = '\n----\n\n'; deprecationNotice += `**This ${declarationName} has been deprecated.**`; - const deprecationDescription = this._getBlock(declaration.comment?.blockTags.find(blockTag => blockTag.tag === '@deprecated')?.content); + const deprecationDescription = this._getBlock(declaration.comment?.blockTags.find((blockTag) => blockTag.tag === '@deprecated')?.content); if (deprecationDescription.length > 0) deprecationNotice += ' \n' + deprecationDescription; @@ -1392,8 +1454,8 @@ ${this._getReferences(functionSignature)} return deprecationNotice; } - else - return ''; + + return ''; } private _getSummary(declaration: DeclarationReflection | SignatureReflection): string { @@ -1407,8 +1469,8 @@ ${this._getReferences(functionSignature)} return this._getBlock(declaration.signatures.at(0)!.comment!.summary); else if (declaration.comment && declaration.comment.summary) return this._getBlock(declaration.comment.summary); - else - return ''; + + return ''; } catch (error) { throw new Error(`Could not process '${declaration}' declaration summary.\n${error}`); @@ -1417,11 +1479,11 @@ ${this._getReferences(functionSignature)} private _getDescription(declaration: DeclarationReflection | SignatureReflection): string { try { - const description = declaration.comment?.blockTags.find(blockTag => blockTag.tag === '@description'); + const description = declaration.comment?.blockTags.find((blockTag) => blockTag.tag === '@description'); if (description !== null && description !== undefined && description.content.length > 0) return '### Description\n\n' + this._getBlock(description.content); - else - return ''; + + return ''; } catch (error) { throw new Error(`Could not process '${declaration}' declaration description.\n${error}`); @@ -1430,11 +1492,11 @@ ${this._getReferences(functionSignature)} private _getRemarks(declaration: DeclarationReflection | SignatureReflection): string { try { - const remarks = declaration.comment?.blockTags.find(blockTag => blockTag.tag === '@remarks'); + const remarks = declaration.comment?.blockTags.find((blockTag) => blockTag.tag === '@remarks'); if (remarks !== null && remarks !== undefined && remarks.content.length > 0) return '### Remarks\n\n' + this._getBlock(remarks.content); - else - return ''; + + return ''; } catch (error) { throw new Error(`Could not process '${declaration}' declaration description.\n${error}`); @@ -1442,12 +1504,15 @@ ${this._getReferences(functionSignature)} } private _getGuidance(declaration: DeclarationReflection | SignatureReflection): string { - const examples = declaration.comment?.blockTags.filter(blockTag => blockTag.tag === '@guidance') || []; + const examples = declaration.comment?.blockTags.filter((blockTag) => blockTag.tag === '@guidance') || []; return examples - .map(example => { - const [title, ...content] = this._getBlock(example.content).split('\n'); - return `### Guidance: ${title.trim()}\n\n${content.join('\n').trim()}`; + .map((example) => { + const [title, ...content] = this._getBlock(example.content) + .split('\n'); + + return `### Guidance: ${title.trim()}\n\n${content.join('\n') + .trim()}`; }) .join('\n'); } @@ -1457,7 +1522,7 @@ ${this._getReferences(functionSignature)} return '### Generic Parameters\n\n' + declaration .typeParameters .map( - typeParameter => { + (typeParameter) => { let genericParameter = `* **${typeParameter.name}**`; let genericParameterDescription = this._getBlock(typeParameter.comment?.summary); @@ -1470,17 +1535,18 @@ ${this._getReferences(functionSignature)} typeParameter.type && ` Type constraints: ${this._getReferenceLink(typeParameter.type)}.`, typeParameter.default && ` Default value: ${this._getReferenceLink(typeParameter.default)}.` ] - .filter(description => description) + .filter((description) => description) .join(' \n'); if (genericParameterConstraints.length > 0) genericParameter += '\n\n' + genericParameterConstraints; return genericParameter; - }, - ).join('\n\n'); + } + ) + .join('\n\n'); } - else - return ''; + + return ''; } private _getParameters(declaration: SignatureReflection): string { @@ -1488,7 +1554,7 @@ ${this._getReferences(functionSignature)} return '### Parameters\n\n' + declaration .parameters .map( - parameterDeclaration => { + (parameterDeclaration) => { let parameter = `* **${parameterDeclaration.name}**${parameterDeclaration.flags.isRest ? ' ([rest](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/rest_parameters))' : ''}: ${this._getReferenceLink(parameterDeclaration.type!)}`; let parameterDescription = this._getBlock(parameterDeclaration.comment?.summary); @@ -1499,17 +1565,18 @@ ${this._getReferences(functionSignature)} parameter += `\n\n Default value: \`${parameterDeclaration.defaultValue}\`.`; return parameter; - }, - ).join('\n\n'); + } + ) + .join('\n\n'); } - else - return ''; + + return ''; } private _getReturn(declaration: SignatureReflection): string { if (declaration.type) { let returnDocumentation = `### Returns: ${this._getReferenceLink(declaration.type)}`; - const returnDescription = this._getBlock(declaration.comment?.blockTags.find(blocKTag => blocKTag.tag === '@returns')?.content); + const returnDescription = this._getBlock(declaration.comment?.blockTags.find((blocKTag) => blocKTag.tag === '@returns')?.content); if (returnDescription.length > 0) returnDocumentation += '\n\n' + returnDescription; else if (declaration.type.type === 'intrinsic' && declaration.type.name === 'void') @@ -1517,62 +1584,68 @@ ${this._getReferences(functionSignature)} return returnDocumentation; } - else - return ''; + + return ''; } private _getConstructorsList(declaration: DeclarationReflection): string { const constructors = declaration .children - ?.filter(childDeclaration => childDeclaration.kind === ReflectionKind.Constructor && !childDeclaration.flags.isInherited && !childDeclaration.flags.isPrivate) + ?.filter((childDeclaration) => childDeclaration.kind === ReflectionKind.Constructor && !childDeclaration.flags.isInherited && !childDeclaration.flags.isPrivate) .sort(this._sortCompareDeclarations.bind(this)); if (constructors !== null && constructors !== undefined && constructors.length > 0) { return '### Constructors\n\n' + constructors - .map(constructorDeclaration => { - const summary = this._getSummary(constructorDeclaration).split('\n')[0].trim(); + .map((constructorDeclaration) => { + const summary = this._getSummary(constructorDeclaration) + .split('\n')[0].trim(); + return `* ${this._getFlagSummary(constructorDeclaration)}**[${constructorDeclaration.name}](${this._getProjectReferenceUrl(constructorDeclaration)})**${summary.length > 0 ? ' - ' + summary : ''}`; }) - .join('\n') + .join('\n'); } - else - return ''; + + return ''; } private _getPropertiesList(declaration: DeclarationReflection): string { const properties = declaration .children - ?.filter(childDeclaration => (childDeclaration.kind === ReflectionKind.Property || childDeclaration.kind === ReflectionKind.Accessor) && !childDeclaration.flags.isPrivate) + ?.filter((childDeclaration) => (childDeclaration.kind === ReflectionKind.Property || childDeclaration.kind === ReflectionKind.Accessor) && !childDeclaration.flags.isPrivate) .sort(this._sortCompareDeclarations.bind(this)); if (properties !== null && properties !== undefined && properties.length > 0) { return '### Properties\n\n' + properties - .map(propertyDeclaration => { - const summary = this._getSummary(propertyDeclaration).split('\n')[0].trim(); + .map((propertyDeclaration) => { + const summary = this._getSummary(propertyDeclaration) + .split('\n')[0].trim(); + return `* ${this._getFlagSummary(propertyDeclaration)}**[${propertyDeclaration.name}](${this._getProjectReferenceUrl(propertyDeclaration)})**${summary.length > 0 ? ' - ' + summary : ''}`; }) - .join('\n') + .join('\n'); } - else - return ''; + + return ''; } private _getMethodsList(declaration: DeclarationReflection): string { const methods = declaration .children - ?.filter(childDeclaration => childDeclaration.kind === ReflectionKind.Method && !childDeclaration.flags.isInherited && !childDeclaration.flags.isPrivate) + ?.filter((childDeclaration) => childDeclaration.kind === ReflectionKind.Method && !childDeclaration.flags.isInherited && !childDeclaration.flags.isPrivate) .sort(this._sortCompareDeclarations.bind(this)); if (methods !== null && methods !== undefined && methods.length > 0) { return '### Methods\n\n' + methods - .map(methodDeclaration => { - const summary = this._getSummary(methodDeclaration).split('\n')[0].trim(); + .map((methodDeclaration) => { + const summary = this._getSummary(methodDeclaration) + .split('\n')[0].trim(); + return `* ${this._getFlagSummary(methodDeclaration)}**[${methodDeclaration.name}](${this._getProjectReferenceUrl(methodDeclaration)})**${summary.length > 0 ? ' - ' + summary : ''}`; }) - .join('\n') + .join('\n'); } - else - return ''; + + return ''; } private _getFlagSummary(declaration: DeclarationReflection): string { @@ -1586,7 +1659,7 @@ ${this._getReferences(functionSignature)} declaration.flags.isReadonly && '`readonly`', declaration.flags.isOptional && '`optional`' ] - .filter(value => !!value) + .filter((value) => !!value) .join(' '); if (flagsSummary.length > 0) @@ -1628,20 +1701,20 @@ ${this._getReferences(functionSignature)} } private _getReferences(declaration: DeclarationReflection | SignatureReflection): string { - const references = declaration.comment?.blockTags.filter(blockTag => blockTag.tag === '@see') || []; + const references = declaration.comment?.blockTags.filter((blockTag) => blockTag.tag === '@see') || []; if (references.length > 0) - return '### See also\n\n' + - references + return '### See also\n\n' + + references .map( - reference => this._getBlock(reference.content) + (reference) => this._getBlock(reference.content) .split(/^[ \t]*-[ \t]*/gm) - .filter(reference => reference) - .map(reference => '* ' + reference) + .filter((reference) => reference) + .map((reference) => '* ' + reference) .join('') ) .join(''); - else - return ''; + + return ''; } private _getBlock(comments: readonly CommentDisplayPart[] | null | undefined): string { @@ -1665,6 +1738,7 @@ ${this._getReferences(functionSignature)} case 'inline-tag': switch (comment.tag) { case '@link': + case '@linkcode': const getDisplayText = (text: string): string => ( comment.tag === '@linkcode' @@ -1677,7 +1751,8 @@ ${this._getReferences(functionSignature)} if (declarationReference.resolutionStart === 'global') switch (declarationReference.moduleSource) { case 'typescript': - const typeScriptReference = declarationReference.symbolReference?.path?.map(componentPath => componentPath.path).join('.') || ''; + const typeScriptReference = declarationReference.symbolReference?.path?.map((componentPath) => componentPath.path) + .join('.') || ''; switch (typeScriptReference) { case 'String': return `[${getDisplayText('String')}](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)`; @@ -1699,6 +1774,7 @@ ${this._getReferences(functionSignature)} return getDisplayText(reflectionTarget.name); const targetDeclaration = this._findDeclaration(comment.target); + return `[${getDisplayText(this._getFullName(targetDeclaration))}](${this._getProjectReferenceUrl(targetDeclaration)})`; default: @@ -1716,25 +1792,26 @@ ${this._getReferences(functionSignature)} private _getSourceReference(declaration: DeclarationReflection | SignatureReflection): string { if (declaration.sources && declaration.sources.length > 0) { - const repositoryUrlPath = `${this._packageInfo.repository.url.split('+').at(-1)!.split('.git')[0]}/tree/${this._packageInfo.version}/src`; + const repositoryUrlPath = `${this._packageInfo.repository.url.split('+') + .at(-1)!.split('.git')[0]}/tree/${this._packageInfo.version}/src`; if (declaration.sources.length === 1) { const [{ fileName, line }] = declaration.sources; + return `Source reference: [\`src/${fileName}:${line}\`](${repositoryUrlPath}/${fileName}#L${line}).`; } - else { - return 'Source references:\n' + declaration - .sources - .map(({ fileName, line }) => `* [\`src/${fileName}:${line}\`](${repositoryUrlPath}/${fileName}#L${line})`) - .join('\n') - } + + return 'Source references:\n' + declaration + .sources + .map(({ fileName, line }) => `* [\`src/${fileName}:${line}\`](${repositoryUrlPath}/${fileName}#L${line})`) + .join('\n'); } - else - return ''; + + return ''; } - private _createDirectoryAsync(path: string, options?: MakeDirectoryOptions & { recursive: true; }): Promise { - return new Promise((resolve, reject) => fs.mkdir(path, options, error => { + private _createDirectoryAsync(path: string, options?: MakeDirectoryOptions & { recursive: true }): Promise { + return new Promise((resolve, reject) => fs.mkdir(path, options, (error) => { if (error) reject(error); else @@ -1742,13 +1819,13 @@ ${this._getReferences(functionSignature)} })); } - private _writeFileAsync(path: string, contents: string, options: WriteFileOptions = "utf-8"): Promise { - return new Promise((resolve, reject) => fs.writeFile(path, contents, options, error => { + private _writeFileAsync(path: string, contents: string, options: WriteFileOptions = 'utf-8'): Promise { + return new Promise((resolve, reject) => fs.writeFile(path, contents, options, (error) => { if (error) reject(error); else resolve(); - })) + })); } } @@ -1758,7 +1835,7 @@ class DocumentationIndex implements IDocumentationIndex { this.namespaces = documentationIndex .namespaces - .map(namespace => ({ + .map((namespace) => ({ name: DocumentationIndex.getNamespaceDisplayName(namespace.id), declarations: namespace .modules @@ -1766,7 +1843,7 @@ class DocumentationIndex implements IDocumentationIndex { (declarations, module) => declarations.concat(module.declarations), new Array() ) - .map(declaration => Object.assign({}, declaration, { promoted: DocumentationIndex.getDeclarationPromotionSortOrder(declaration) !== null })) + .map((declaration) => Object.assign({}, declaration, { promoted: DocumentationIndex.getDeclarationPromotionSortOrder(declaration) !== null })) .sort((left, right) => { const leftSortOrder = DocumentationIndex.getDeclarationPromotionSortOrder(left); const rightSortOrder = DocumentationIndex.getDeclarationPromotionSortOrder(right); @@ -1775,8 +1852,8 @@ class DocumentationIndex implements IDocumentationIndex { return rightSortOrder === null ? 0 : 1; else if (rightSortOrder === null) return -1; - else - return leftSortOrder - rightSortOrder; + + return leftSortOrder - rightSortOrder; }) })); } @@ -1787,11 +1864,14 @@ class DocumentationIndex implements IDocumentationIndex { return { namespaces: Array .from(declarations) - .filter(declaration => { + .filter((declaration) => { switch (declaration.kind) { case ReflectionKind.TypeAlias: + case ReflectionKind.Interface: + case ReflectionKind.Class: + case ReflectionKind.Function: return true; @@ -1801,12 +1881,14 @@ class DocumentationIndex implements IDocumentationIndex { }) .reduce( (namespaces, declaration) => { - let namespaceId = declaration.sources?.at(0)?.fileName.split('/').slice(0, -1).join('/')!; + let namespaceId = declaration.sources?.at(0)?.fileName.split('/') + .slice(0, -1) + .join('/')!; if (namespaceId === 'validation/objectValidator') namespaceId = 'validation'; const moduleId = declaration.sources?.at(0)?.fileName!; - let namespace = namespaces.find(namespace => namespace.id === namespaceId); + let namespace = namespaces.find((namespace) => namespace.id === namespaceId); if (!namespace) { namespace = { id: namespaceId, @@ -1815,7 +1897,7 @@ class DocumentationIndex implements IDocumentationIndex { namespaces.push(namespace); } - let module = namespace.modules.find(module => module.id === moduleId); + let module = namespace.modules.find((module) => module.id === moduleId); if (!module) { module = { id: moduleId, @@ -1824,19 +1906,19 @@ class DocumentationIndex implements IDocumentationIndex { namespace.modules.push(module); } - module.declarations.push(declaration) + module.declarations.push(declaration); return namespaces; }, new Array() ) .sort((left, right) => DocumentationIndex.getNamespaceSortOrder(left.id) - DocumentationIndex.getNamespaceSortOrder(right.id)) - .map(namespace => { + .map((namespace) => { namespace .modules .sort((left, right) => DocumentationIndex.getModuleSortOrder(left) - DocumentationIndex.getModuleSortOrder(right)) - .forEach(module => { - module.declarations.sort((left, right) => DocumentationIndex.getDeclarationSortOrder(left) - DocumentationIndex.getDeclarationSortOrder(right)) + .forEach((module) => { + module.declarations.sort((left, right) => DocumentationIndex.getDeclarationSortOrder(left) - DocumentationIndex.getDeclarationSortOrder(right)); }); return namespace; @@ -1857,13 +1939,16 @@ class DocumentationIndex implements IDocumentationIndex { case 'validation': return 4; + case 'validation/triggers': return 6; case 'collections/observableCollections': return 7; + case 'collections/observableMap': return 8; + case 'collections/observableSet': return 9; @@ -1891,13 +1976,16 @@ class DocumentationIndex implements IDocumentationIndex { case 'validation': return 'Validation'; + case 'validation/triggers': return 'Validation / Triggers'; case 'collections/observableCollections': return 'Observable Collection'; + case 'collections/observableMap': return 'Observable Map'; + case 'collections/observableSet': return 'Observable Set'; @@ -1913,11 +2001,11 @@ class DocumentationIndex implements IDocumentationIndex { } private static getModuleSortOrder(module: IModuleDeclarationIndex): number { - if (module.declarations.every(declaration => declaration.kind === ReflectionKind.TypeAlias)) + if (module.declarations.every((declaration) => declaration.kind === ReflectionKind.TypeAlias)) return 1; - if (module.declarations.every(declaration => declaration.kind === ReflectionKind.Interface)) + if (module.declarations.every((declaration) => declaration.kind === ReflectionKind.Interface)) return 2; - if (module.declarations.every(declaration => declaration.kind === ReflectionKind.TypeAlias || declaration.kind === ReflectionKind.Interface)) + if (module.declarations.every((declaration) => declaration.kind === ReflectionKind.TypeAlias || declaration.kind === ReflectionKind.Interface)) return 3; return 1000; @@ -1946,97 +2034,130 @@ class DocumentationIndex implements IDocumentationIndex { switch (declaration.name) { case 'IEvent': return 1; + case 'IEventHandler': return 2; + case 'EventDispatcher': return 3; case 'INotifyPropertiesChanged': return 1; + case 'ViewModel': return 2; case 'Form': return 1; + case 'IFormFieldConfig': return 2; + case 'FormField': return 3; + case 'ReadOnlyFormCollection': return 4; + case 'FormCollection': return 5; + case 'IConfigurableFormCollection': return 6; + case 'FormSetupCallback': return 7; case 'IValidator': return 1; + case 'ValidatorCallback': return 2; + case 'IObjectValidator': return 3; + case 'IValidatable': return 4; case 'WellKnownValidationTrigger': return 1; + case 'ValidationTrigger': return 2; case 'ReadOnlyObservableCollection': return 1; + case 'ObservableCollection': return 2; + case 'INotifyCollectionChanged': return 3; + case 'CollectionChangeOperation': return 4; + case 'INotifyCollectionReordered': return 5; + case 'CollectionReorderOperation': return 6; case 'ReadOnlyObservableMap': return 1; + case 'ObservableMap': return 2; + case 'INotifyMapChanged': return 3; + case 'MapChangeOperation': return 4; case 'ReadOnlyObservableSet': return 1; + case 'ObservableSet': return 2; + case 'INotifySetChanged': return 3; + case 'SetChangeOperation': return 4; case 'IDependencyResolver': return 1; + case 'IDependencyContainer': return 2; + case 'DependencyContainer': return 3; + case 'useDependency': return 4; + case 'useViewModelDependency': return 5; + case 'useDependencyResolver': return 6; case 'useViewModel': return 1; + case 'useViewModelMemo': return 2; + case 'useObservableCollection': return 3; + case 'useObservableMap': return 4; + case 'useObservableSet': return 5; @@ -2166,4 +2287,4 @@ const landingPageNavigation = '###### Go to: ' + [ '[API](#api)', '[Guides and Tutorials - Getting Started](https://github.com/Andrei15193/react-model-view-viewmodel/discussions/7)', '[CodeSandbox](https://codesandbox.io/p/sandbox/react-mvvm-vwsqlv)' -].join(' | '); +].join(' | '); \ No newline at end of file