[K/JS] Fix collections interop iterables for Map#keys ^KT-69928 Fixed
diff --git a/js/js.translator/testData/typescript-export/common.tsconfig.json b/js/js.translator/testData/typescript-export/common.tsconfig.json
index 84be715..0eea504 100644
--- a/js/js.translator/testData/typescript-export/common.tsconfig.json
+++ b/js/js.translator/testData/typescript-export/common.tsconfig.json
@@ -2,6 +2,7 @@
   "compilerOptions": {
     "target": "es5",
     "strict": true,
+    "downlevelIteration": true,
     "newLine": "lf",
     "lib": ["es2015", "dom"]
   }
diff --git a/js/js.translator/testData/typescript-export/js/collections-in-exported-file/collections__main.ts b/js/js.translator/testData/typescript-export/js/collections-in-exported-file/collections__main.ts
index b2f6ea7..0ebc971 100644
--- a/js/js.translator/testData/typescript-export/js/collections-in-exported-file/collections__main.ts
+++ b/js/js.translator/testData/typescript-export/js/collections-in-exported-file/collections__main.ts
@@ -25,8 +25,8 @@
 
 function assertThrow(fn: () => void, message: string) {
     try {
-       fn();
-       throw message;
+        fn();
+        throw message;
     } catch (e) {}
 }
 
@@ -173,6 +173,8 @@
     assert(mapReadonlyView.has("a"), "Problem with accessing element in map readonly view")
     assert(mapReadonlyView.get("a") == 1, "Problem with accessing element in map readonly view")
     assert(joinSetOrMap(mapReadonlyView) == "[a:1][b:2][c:3]", "Problem with map readonly view iterator")
+    assert(joinMapKeys(mapReadonlyView) == "[a][b][c]", "Problem with map readonly view keys function")
+    assert(joinMapValues(mapReadonlyView) == "[1][2][3]", "Problem with map readonly view values function")
     assert(consumeMap(map), "Problem with consumption of a Kotlin map")
     assertThrow(() => { (mapReadonlyView as Map<string, number>).set("d", 4) }, "Map readonly view have ability to mutate the map by 'set'")
     assertThrow(() => { (mapReadonlyView as Map<string, number>).delete("a") }, "Map readonly view have ability to mutate the map by 'delete'")
@@ -188,9 +190,13 @@
     assert(mutableMapReadonlyView.has("d"), "Problem with accessing element in mutable map readonly view")
     assert(mutableMapReadonlyView.get("d") == 4, "Problem with accessing element in mutable map readonly view")
     assert(joinSetOrMap(mutableMapReadonlyView) == "[d:4][e:5][f:6]", "Problem with mutable map readonly view")
+    assert(joinMapKeys(mutableMapReadonlyView) == "[d][e][f]", "Problem with mutable map readonly view keys function")
+    assert(joinMapValues(mutableMapReadonlyView) == "[4][5][6]", "Problem with mutable map readonly view values function")
     assert(!consumeMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a map")
     assert(consumeMutableMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a mutable map")
     assert(joinSetOrMap(mutableMapReadonlyView) == "[d:4][e:5][f:6][g:7]", "Problem with mutable map readonly view after original map is mutated")
+    assert(joinMapKeys(mutableMapReadonlyView) == "[d][e][f][g]", "Problem with mutable map readonly view keys function after original map is mutated")
+    assert(joinMapValues(mutableMapReadonlyView) == "[4][5][6][7]", "Problem with mutable map readonly view values function after original map is mutated")
     assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).set("d", 4) }, "Mutable map readonly view have ability to mutate the map by 'set'")
     assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).delete("a") }, "Mutable map readonly view have ability to mutate the map by 'delete'")
     assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).clear() }, "Mutable map readonly view have ability to mutate the map by 'clear'")
@@ -202,16 +208,24 @@
     assert(mutableMapView.has("d"), "Problem with accessing element in mutable map view")
     assert(mutableMapView.get("d") == 4, "Problem with accessing element in mutable map view")
     assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6]", "Problem with mutable map view")
+    assert(joinMapKeys(mutableMapView) == "[d][e][f]", "Problem with mutable map view keys function")
+    assert(joinMapValues(mutableMapView) == "[4][5][6]", "Problem with mutable map view values function")
     assert(consumeMutableMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a mutable map")
     assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6][g:7]", "Problem with mutable map view after original map is mutated")
+    assert(joinMapKeys(mutableMapView) == "[d][e][f][g]", "Problem with mutable map view keys function after original map is mutated")
+    assert(joinMapValues(mutableMapView) == "[4][5][6][7]", "Problem with mutable map view values function after original map is mutated")
 
     mutableMapView.set("h", 8)
 
     assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6][g:7][h:8]", "Problem with mutable map view after the view is mutated")
+    assert(joinMapKeys(mutableMapView) == "[d][e][f][g][h]", "Problem with mutable map view keys function after the view is mutated")
+    assert(joinMapValues(mutableMapView) == "[4][5][6][7][8]", "Problem with mutable map view values function after the view is mutated")
 
     mutableMapView.clear()
 
     assert(joinSetOrMap(mutableMapView) == "", "Problem with mutable map view after the view is mutated")
+    assert(joinMapKeys(mutableMapView) == "", "Problem with mutable map view keys function after the view is mutated")
+    assert(joinMapValues(mutableMapView) == "", "Problem with mutable map view values function after the view is mutated")
 
     assert(consumeMap(KtMutableMap.fromJsMap(new Map([["a", 1], ["b", 2], ["c", 3]]))), "Problem with map to map conversion for Kotlin mutable map")
     assert(consumeMutableMap(KtMutableMap.fromJsMap(new Map([["d", 4], ["e", 5], ["f", 6]]))), "Problem with map to mutable map conversion for Kotlin mutable map")
@@ -231,4 +245,24 @@
     }
 
     return result
-}
\ No newline at end of file
+}
+
+function joinMapKeys(map: ReadonlyMap<string, number>): string {
+    let result = ""
+
+    for (const key of map.keys()) {
+        result += `[${key}]`
+    }
+
+    return result
+}
+
+function joinMapValues(map: ReadonlyMap<string, number>): string {
+    let result = ""
+
+    for (const value of map.values()) {
+        result += `[${value}]`
+    }
+
+    return result
+}
diff --git a/js/js.translator/testData/typescript-export/js/collections/collections__main.ts b/js/js.translator/testData/typescript-export/js/collections/collections__main.ts
index b2f6ea7..0611b93 100644
--- a/js/js.translator/testData/typescript-export/js/collections/collections__main.ts
+++ b/js/js.translator/testData/typescript-export/js/collections/collections__main.ts
@@ -173,6 +173,8 @@
     assert(mapReadonlyView.has("a"), "Problem with accessing element in map readonly view")
     assert(mapReadonlyView.get("a") == 1, "Problem with accessing element in map readonly view")
     assert(joinSetOrMap(mapReadonlyView) == "[a:1][b:2][c:3]", "Problem with map readonly view iterator")
+    assert(joinMapKeys(mapReadonlyView) == "[a][b][c]", "Problem with map readonly view keys function")
+    assert(joinMapValues(mapReadonlyView) == "[1][2][3]", "Problem with map readonly view values function")
     assert(consumeMap(map), "Problem with consumption of a Kotlin map")
     assertThrow(() => { (mapReadonlyView as Map<string, number>).set("d", 4) }, "Map readonly view have ability to mutate the map by 'set'")
     assertThrow(() => { (mapReadonlyView as Map<string, number>).delete("a") }, "Map readonly view have ability to mutate the map by 'delete'")
@@ -188,9 +190,13 @@
     assert(mutableMapReadonlyView.has("d"), "Problem with accessing element in mutable map readonly view")
     assert(mutableMapReadonlyView.get("d") == 4, "Problem with accessing element in mutable map readonly view")
     assert(joinSetOrMap(mutableMapReadonlyView) == "[d:4][e:5][f:6]", "Problem with mutable map readonly view")
+    assert(joinMapKeys(mutableMapReadonlyView) == "[d][e][f]", "Problem with mutable map readonly view keys function")
+    assert(joinMapValues(mutableMapReadonlyView) == "[4][5][6]", "Problem with mutable map readonly view values function")
     assert(!consumeMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a map")
     assert(consumeMutableMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a mutable map")
     assert(joinSetOrMap(mutableMapReadonlyView) == "[d:4][e:5][f:6][g:7]", "Problem with mutable map readonly view after original map is mutated")
+    assert(joinMapKeys(mutableMapReadonlyView) == "[d][e][f][g]", "Problem with mutable map readonly view keys function after original map is mutated")
+    assert(joinMapValues(mutableMapReadonlyView) == "[4][5][6][7]", "Problem with mutable map readonly view values function after original map is mutated")
     assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).set("d", 4) }, "Mutable map readonly view have ability to mutate the map by 'set'")
     assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).delete("a") }, "Mutable map readonly view have ability to mutate the map by 'delete'")
     assertThrow(() => { (mutableMapReadonlyView as Map<string, number>).clear() }, "Mutable map readonly view have ability to mutate the map by 'clear'")
@@ -202,16 +208,24 @@
     assert(mutableMapView.has("d"), "Problem with accessing element in mutable map view")
     assert(mutableMapView.get("d") == 4, "Problem with accessing element in mutable map view")
     assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6]", "Problem with mutable map view")
+    assert(joinMapKeys(mutableMapView) == "[d][e][f]", "Problem with mutable map view keys function")
+    assert(joinMapValues(mutableMapView) == "[4][5][6]", "Problem with mutable map view values function")
     assert(consumeMutableMap(mutableMap), "Problem with consumption of a Kotlin mutable map as a mutable map")
     assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6][g:7]", "Problem with mutable map view after original map is mutated")
+    assert(joinMapKeys(mutableMapView) == "[d][e][f][g]", "Problem with mutable map view keys function after original map is mutated")
+    assert(joinMapValues(mutableMapView) == "[4][5][6][7]", "Problem with mutable map view values function after original map is mutated")
 
     mutableMapView.set("h", 8)
 
     assert(joinSetOrMap(mutableMapView) == "[d:4][e:5][f:6][g:7][h:8]", "Problem with mutable map view after the view is mutated")
+    assert(joinMapKeys(mutableMapView) == "[d][e][f][g][h]", "Problem with mutable map view keys function after the view is mutated")
+    assert(joinMapValues(mutableMapView) == "[4][5][6][7][8]", "Problem with mutable map view values function after the view is mutated")
 
     mutableMapView.clear()
 
     assert(joinSetOrMap(mutableMapView) == "", "Problem with mutable map view after the view is mutated")
+    assert(joinMapKeys(mutableMapView) == "", "Problem with mutable map view keys function after the view is mutated")
+    assert(joinMapValues(mutableMapView) == "", "Problem with mutable map view values function after the view is mutated")
 
     assert(consumeMap(KtMutableMap.fromJsMap(new Map([["a", 1], ["b", 2], ["c", 3]]))), "Problem with map to map conversion for Kotlin mutable map")
     assert(consumeMutableMap(KtMutableMap.fromJsMap(new Map([["d", 4], ["e", 5], ["f", 6]]))), "Problem with map to mutable map conversion for Kotlin mutable map")
@@ -231,4 +245,24 @@
     }
 
     return result
-}
\ No newline at end of file
+}
+
+function joinMapKeys(map: ReadonlyMap<string, number>): string {
+    let result = ""
+
+    for (const key of map.keys()) {
+       result += `[${key}]`
+    }
+
+    return result
+}
+
+function joinMapValues(map: ReadonlyMap<string, number>): string {
+    let result = ""
+
+    for (const value of map.values()) {
+       result += `[${value}]`
+    }
+
+    return result
+}
diff --git a/libraries/stdlib/js/runtime/collectionsInterop.kt b/libraries/stdlib/js/runtime/collectionsInterop.kt
index 33f4eb9..4052cf4 100644
--- a/libraries/stdlib/js/runtime/collectionsInterop.kt
+++ b/libraries/stdlib/js/runtime/collectionsInterop.kt
@@ -200,7 +200,7 @@
     forEach: (dynamic, dynamic) -> Unit,
 ): dynamic {
     val mapView = objectCreate<JsMapView<K, V>>().also {
-        js("it[Symbol.iterator] = entriesIterator")
+        js("it[typeof Symbol !== \"undefined\" ? Symbol.iterator : null] = entriesIterator")
         defineProp(it, "size", mapSize, VOID)
     }
 
@@ -211,7 +211,7 @@
             'delete': mapRemove,
             clear: mapClear,
             has: mapContains,
-            keys: valuesIterator,
+            keys: keysIterator,
             values: valuesIterator,
             entries: entriesIterator,
             forEach: function (cb, thisArg) { forEach(cb, thisArg || mapView) }
@@ -223,13 +223,17 @@
 private fun <T> createJsIteratorFrom(iterator: Iterator<T>, transform: (T) -> dynamic = { it }): dynamic {
     val iteratorNext = { iterator.next() }
     val iteratorHasNext = { iterator.hasNext() }
-    return js("""{
+    val jsIterator =  js("""{
         next: function() {
             var result = { done: !iteratorHasNext() };
             if (!result.done) result.value = transform(iteratorNext());
             return result;
-        }
+        },
     }""")
+
+    js("jsIterator[typeof Symbol !== \"undefined\" ? Symbol.iterator : null] = function() { return jsIterator }")
+
+    return jsIterator
 }
 
 private fun forEach(cb: (dynamic, dynamic, dynamic) -> Unit, thisArg: dynamic) {