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