• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts {
2    describe("unittests:: createMapShim", () => {
3
4        const stringKeys = [
5            "1",
6            "3",
7            "2",
8            "4",
9            "0",
10            "999",
11            "A",
12            "B",
13            "C",
14            "Z",
15            "X",
16            "X1",
17            "X2",
18            "Y"
19        ];
20
21        const mixedKeys = [
22            true,
23            3,
24            { toString() { return "2"; } },
25            "4",
26            false,
27            null, // eslint-disable-line no-null/no-null
28            undefined,
29            "B",
30            { toString() { return "C"; } },
31            "Z",
32            "X",
33            { toString() { return "X1"; } },
34            "X2",
35            "Y"
36        ];
37
38        function testMapIterationAddedValues<K>(keys: K[], map: ESMap<K, string>, useForEach: boolean): string {
39            let resultString = "";
40
41            map.set(keys[0], "1");
42            map.set(keys[1], "3");
43            map.set(keys[2], "2");
44            map.set(keys[3], "4");
45
46            let addedThree = false;
47            const doForEach = (value: string, key: K) => {
48                resultString += `${key}:${value};`;
49
50                // Add a new key ("0") - the map should provide this
51                // one in the next iteration.
52                if (key === keys[0]) {
53                    map.set(keys[0], "X1");
54                    map.set(keys[4], "X0");
55                    map.set(keys[3], "X4");
56                }
57                else if (key === keys[1]) {
58                    if (!addedThree) {
59                        addedThree = true;
60
61                        // Remove and re-add key "3"; the map should
62                        // visit it after "0".
63                        map.delete(keys[1]);
64                        map.set(keys[1], "Y3");
65
66                        // Change the value of "2"; the map should provide
67                        // it when visiting the key.
68                        map.set(keys[2], "Y2");
69                    }
70                    else {
71                        // Check that an entry added when we visit the
72                        // currently last entry will still be visited.
73                        map.set(keys[5], "999");
74                    }
75                }
76                else if (key === keys[5]) {
77                    // Ensure that clear() behaves correctly same as removing all keys.
78                    map.set(keys[6], "A");
79                    map.set(keys[7], "B");
80                    map.set(keys[8], "C");
81                }
82                else if (key === keys[6]) {
83                    map.clear();
84                    map.set(keys[9], "Z");
85                }
86                else if (key === keys[9]) {
87                    // Check that the map behaves correctly when two items are
88                    // added and removed immediately.
89                    map.set(keys[10], "X");
90                    map.set(keys[11], "X1");
91                    map.set(keys[12], "X2");
92                    map.delete(keys[11]);
93                    map.delete(keys[12]);
94                    map.set(keys[13], "Y");
95                }
96            };
97
98            if (useForEach) {
99                map.forEach(doForEach);
100            }
101            else {
102                // Use an iterator.
103                const iterator = map.entries();
104                while (true) {
105                    const iterResult = iterator.next();
106                    if (iterResult.done) {
107                        break;
108                    }
109
110                    const [key, value] = iterResult.value;
111                    doForEach(value, key);
112                }
113            }
114
115            return resultString;
116        }
117
118        let MapShim!: MapConstructor;
119        beforeEach(() => {
120            function getIterator<I extends readonly any[] | ReadonlySet<any> | ReadonlyESMap<any, any> | undefined>(iterable: I): Iterator<
121                I extends ReadonlyESMap<infer K, infer V> ? [K, V] :
122                I extends ReadonlySet<infer T> ? T :
123                I extends readonly (infer T)[] ? T :
124                I extends undefined ? undefined :
125                never>;
126            function getIterator(iterable: readonly any[] | ReadonlySet<any> | ReadonlyESMap<any, any> | undefined): Iterator<any> | undefined {
127                // override `ts.getIterator` with a version that allows us to iterate over a `MapShim` in an environment with a native `Map`.
128                if (iterable instanceof MapShim) return iterable.entries();
129                return ts.getIterator(iterable);
130            }
131
132            MapShim = ShimCollections.createMapShim(getIterator);
133            afterEach(() => {
134                MapShim = undefined!;
135            });
136        });
137
138        it("iterates values in insertion order and handles changes with string keys", () => {
139            const expectedResult = "1:1;3:3;2:Y2;4:X4;0:X0;3:Y3;999:999;A:A;Z:Z;X:X;Y:Y;";
140
141            // First, ensure the test actually has the same behavior as a native Map.
142            let nativeMap = new Map<string, string>();
143            const nativeMapForEachResult = testMapIterationAddedValues(stringKeys, nativeMap, /* useForEach */ true);
144            assert.equal(nativeMapForEachResult, expectedResult, "nativeMap-forEach");
145
146            nativeMap = new Map<string, string>();
147            const nativeMapIteratorResult = testMapIterationAddedValues(stringKeys, nativeMap, /* useForEach */ false);
148            assert.equal(nativeMapIteratorResult, expectedResult, "nativeMap-iterator");
149
150            // Then, test the map shim.
151            let localShimMap = new MapShim<string, string>();
152            const shimMapForEachResult = testMapIterationAddedValues(stringKeys, localShimMap, /* useForEach */ true);
153            assert.equal(shimMapForEachResult, expectedResult, "shimMap-forEach");
154
155            localShimMap = new MapShim<string, string>();
156            const shimMapIteratorResult = testMapIterationAddedValues(stringKeys, localShimMap, /* useForEach */ false);
157            assert.equal(shimMapIteratorResult, expectedResult, "shimMap-iterator");
158        });
159
160        it("iterates values in insertion order and handles changes with mixed-type keys", () => {
161            const expectedResult = "true:1;3:3;2:Y2;4:X4;false:X0;3:Y3;null:999;undefined:A;Z:Z;X:X;Y:Y;";
162
163            // First, ensure the test actually has the same behavior as a native Map.
164            let nativeMap = new Map<any, string>();
165            const nativeMapForEachResult = testMapIterationAddedValues(mixedKeys, nativeMap, /* useForEach */ true);
166            assert.equal(nativeMapForEachResult, expectedResult, "nativeMap-forEach");
167
168            nativeMap = new Map<any, string>();
169            const nativeMapIteratorResult = testMapIterationAddedValues(mixedKeys, nativeMap, /* useForEach */ false);
170            assert.equal(nativeMapIteratorResult, expectedResult, "nativeMap-iterator");
171
172            // Then, test the map shim.
173            let localShimMap = new MapShim<any, string>();
174            const shimMapForEachResult = testMapIterationAddedValues(mixedKeys, localShimMap, /* useForEach */ true);
175            assert.equal(shimMapForEachResult, expectedResult, "shimMap-forEach");
176
177            localShimMap = new MapShim<any, string>();
178            const shimMapIteratorResult = testMapIterationAddedValues(mixedKeys, localShimMap, /* useForEach */ false);
179            assert.equal(shimMapIteratorResult, expectedResult, "shimMap-iterator");
180        });
181
182        it("create from Array", () => {
183            const map = new MapShim([["a", "b"]]);
184            assert.equal(map.size, 1);
185            assert.isTrue(map.has("a"));
186            assert.equal(map.get("a"), "b");
187        });
188
189        it("create from Map", () => {
190            const map1 = new MapShim([["a", "b"]]);
191            const map2 = new MapShim(map1);
192            assert.equal(map1.size, 1);
193            assert.equal(map2.size, 1);
194            assert.isTrue(map2.has("a"));
195            assert.equal(map2.get("a"), "b");
196        });
197
198        it("set when not present", () => {
199            const map = new MapShim<string, string>();
200            const result = map.set("a", "b");
201            assert.equal(map.size, 1);
202            assert.strictEqual(result, map);
203            assert.isTrue(map.has("a"));
204            assert.equal(map.get("a"), "b");
205        });
206
207        it("set when present", () => {
208            const map = new MapShim<string, string>();
209            map.set("a", "z");
210            const result = map.set("a", "b");
211            assert.equal(map.size, 1);
212            assert.strictEqual(result, map);
213            assert.isTrue(map.has("a"));
214            assert.equal(map.get("a"), "b");
215        });
216
217        it("has when not present", () => {
218            const map = new MapShim<string, string>();
219            assert.isFalse(map.has("a"));
220        });
221
222        it("has when present", () => {
223            const map = new MapShim<string, string>();
224            map.set("a", "b");
225            assert.isTrue(map.has("a"));
226        });
227
228        it("get when not present", () => {
229            const map = new MapShim<string, string>();
230            assert.isUndefined(map.get("a"));
231        });
232
233        it("get when present", () => {
234            const map = new MapShim<string, string>();
235            map.set("a", "b");
236            assert.equal(map.get("a"), "b");
237        });
238
239        it("delete when not present", () => {
240            const map = new MapShim<string, string>();
241            assert.isFalse(map.delete("a"));
242        });
243
244        it("delete when present", () => {
245            const map = new MapShim<string, string>();
246            map.set("a", "b");
247            assert.isTrue(map.delete("a"));
248        });
249
250        it("delete twice when present", () => {
251            const map = new MapShim<string, string>();
252            map.set("a", "b");
253            assert.isTrue(map.delete("a"));
254            assert.isFalse(map.delete("a"));
255        });
256
257        it("remove only item and iterate", () => {
258            const map = new MapShim<string, string>();
259            map.set("a", "b");
260            map.delete("a");
261            const actual = arrayFrom(map.keys());
262            assert.deepEqual(actual, []);
263        });
264
265        it("remove first item and iterate", () => {
266            const map = new MapShim<string, string>();
267            map.set("a", "b");
268            map.set("c", "d");
269            map.delete("a");
270            assert.deepEqual(arrayFrom(map.keys()), ["c"]);
271            assert.deepEqual(arrayFrom(map.values()), ["d"]);
272            assert.deepEqual(arrayFrom(map.entries()), [["c", "d"]]);
273        });
274
275        it("remove last item and iterate", () => {
276            const map = new MapShim<string, string>();
277            map.set("a", "b");
278            map.set("c", "d");
279            map.delete("c");
280            assert.deepEqual(arrayFrom(map.keys()), ["a"]);
281            assert.deepEqual(arrayFrom(map.values()), ["b"]);
282            assert.deepEqual(arrayFrom(map.entries()), [["a", "b"]]);
283        });
284
285        it("remove middle item and iterate", () => {
286            const map = new MapShim<string, string>();
287            map.set("a", "b");
288            map.set("c", "d");
289            map.set("e", "f");
290            map.delete("c");
291            assert.deepEqual(arrayFrom(map.keys()), ["a", "e"]);
292            assert.deepEqual(arrayFrom(map.values()), ["b", "f"]);
293            assert.deepEqual(arrayFrom(map.entries()), [["a", "b"], ["e", "f"]]);
294        });
295
296        it("keys", () => {
297            const map = new MapShim<string, string>();
298            map.set("c", "d");
299            map.set("a", "b");
300            assert.deepEqual(arrayFrom(map.keys()), ["c", "a"]);
301        });
302
303        it("values", () => {
304            const map = new MapShim<string, string>();
305            map.set("c", "d");
306            map.set("a", "b");
307            assert.deepEqual(arrayFrom(map.values()), ["d", "b"]);
308        });
309
310        it("entries", () => {
311            const map = new MapShim<string, string>();
312            map.set("c", "d");
313            map.set("a", "b");
314            assert.deepEqual(arrayFrom(map.entries()), [["c", "d"], ["a", "b"]]);
315        });
316
317        it("forEach", () => {
318            const map = new MapShim<string, string>();
319            map.set("c", "d");
320            map.set("a", "b");
321            const actual: [string, string][] = [];
322            map.forEach((value, key) => { actual.push([key, value]); });
323            assert.deepEqual(actual, [["c", "d"], ["a", "b"]]);
324        });
325    });
326}
327