Skip to content

Commit

Permalink
Added forEach implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrei15193 committed Jun 24, 2024
1 parent 486cd1b commit 82677ff
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 17 deletions.
12 changes: 10 additions & 2 deletions src/collections/observableMap/ReadOnlyObservableMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,14 @@ export class ReadOnlyObservableMap<TKey, TItem> extends ViewModel implements IRe
* @see [Map.forEach](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map/forEach)
*/
public forEach<TContext = void>(callback: (this: TContext, item: TItem, key: TKey, map: this) => void, thisArg?: TContext): void {
throw new Error("Method not implemented.");
const changeTokenCopy = this._changeToken;

for (const [key, item] of this) {
callback.call(thisArg, item, key, this);

if (changeTokenCopy !== this._changeToken)
throw new Error('Map has changed while being iterated.');
}
}

/**
Expand Down Expand Up @@ -164,7 +171,8 @@ export class ReadOnlyObservableMap<TKey, TItem> extends ViewModel implements IRe
* @see [Map.clear](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Map/clear)
*/
protected clear(): void {
throw new Error("Method not implemented.");
this._changeToken = 1;
// throw new Error("Method not implemented.");
}
}

Expand Down
117 changes: 117 additions & 0 deletions src/collections/observableMap/tests/ObservableMap.forEach.tests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { ObservableMap } from '../ObservableMap';
import { testBlankMutatingOperation } from './common';

describe('ObservableMap.forEach', (): void => {
it('iterating over an empty map does not invoke the callback', (): void => {
let mapInvocationCount = 0;
let observableMapInvocationCount = 0;

testBlankMutatingOperation<string, number>({
initialState: [],

applyOperation: {
applyMapOperation: map => map.forEach(_ => mapInvocationCount++),
applyObservableMapOperation: observableMap => observableMap.forEach(_ => observableMapInvocationCount++)
},

expectedResult: undefined
});

expect(mapInvocationCount).toBe(0);
expect(observableMapInvocationCount).toBe(0);
});

it('iterating over a map invokes the callback for each item', (): void => {
const mapItems: (readonly [string, number])[] = [];
const observableMapItems: (readonly [string, number])[] = [];

testBlankMutatingOperation<string, number>({
initialState: [
['a', 1],
['b', 2],
['c', 3]
],

applyOperation: {
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);
});

it('calling forEach passes arguments to each parameter accordingly', (): void => {
let invocationCount = 0;
const observableMap = new ObservableMap<number, string>([
[1, 'a']
]);
observableMap.forEach((item, key, map) => {
invocationCount++;

expect(key).toBe(1);
expect(item).toBe('a');
expect(map).toStrictEqual(observableMap);

return true;
});

expect(invocationCount).toBe(1);
});

it('calling forEach with context passes it to the callback', (): void => {
let invocationCount = 0;
const context = {};
const observableMap = new ObservableMap<number, string>([
[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);

return true;
},
context
);

expect(invocationCount).toBe(1);
});

it('modifying the map while executing forEach throws exception', (): void => {
expect(
() => {
const observableMap = new ObservableMap<number, string>([
[1, 'a'],
[2, 'b'],
[3, 'c']
]);
observableMap.forEach(_ => {
observableMap.clear();
});
})
.toThrow(new Error('Map has changed while being iterated.'));
});

it('calling forEach while iterating will not break iterators', (): void => {
expect(
() => {
const observableMap = new ObservableMap<number, string>([
[1, 'a'],
[2, 'b'],
[3, 'c']
]);

for (const _ of observableMap)
observableMap.forEach(_ => { });
})
.not
.toThrow();
});
});
12 changes: 6 additions & 6 deletions src/collections/observableMap/tests/ObservableMap.get.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ describe('ObserableMap.get', (): void => {
it('looking up item by non-existing key returns undefined', (): void => {
testBlankMutatingOperation<number, string>({
initialState: [
[1, '1'],
[2, '2'],
[3, '3']
[1, 'a'],
[2, 'b'],
[3, 'c']
],

applyOperation: map => map.get(4),
Expand All @@ -46,9 +46,9 @@ describe('ObserableMap.get', (): void => {
expect(
() => {
const observableMap = new ObservableMap<number, string>([
[1, '1'],
[2, '2'],
[3, '3']
[1, 'a'],
[2, 'b'],
[3, 'c']
]);

let valueToCheck = 2;
Expand Down
18 changes: 9 additions & 9 deletions src/collections/observableMap/tests/ObservableMap.has.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ describe('ObserableMap.has', (): void => {
it('checking if existing item is part of map returns true', (): void => {
testBlankMutatingOperation<number, string>({
initialState: [
[1, '1'],
[2, '2'],
[3, '3']
[1, 'a'],
[2, 'b'],
[3, 'c']
],

applyOperation: map => map.has(2),
Expand All @@ -29,9 +29,9 @@ describe('ObserableMap.has', (): void => {
it('checking if non-existing item is part of map returns false', (): void => {
testBlankMutatingOperation<number, string>({
initialState: [
[1, '1'],
[2, '2'],
[3, '3']
[1, 'a'],
[2, 'b'],
[3, 'c']
],

applyOperation: map => map.has(4),
Expand All @@ -44,9 +44,9 @@ describe('ObserableMap.has', (): void => {
expect(
() => {
const observableMap = new ObservableMap<number, string>([
[1, '1'],
[2, '2'],
[3, '3']
[1, 'a'],
[2, 'b'],
[3, 'c']
]);

let valueToCheck = 2;
Expand Down

0 comments on commit 82677ff

Please sign in to comment.