-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[wasm] Upstream ESM Integration jsapi tests #53718
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Ms2ger
merged 3 commits into
web-platform-tests:master
from
guybedford:esm-integration-updates
Nov 6, 2025
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| // META: global=window,dedicatedworker,jsshell,shadowrealm | ||
|
|
||
| "use strict"; | ||
|
|
||
| promise_test(async () => { | ||
| const mod = await import("./resources/exports.wasm"); | ||
|
|
||
| assert_array_equals(Object.getOwnPropertyNames(mod).sort(), [ | ||
| "a\u200Bb\u0300c", | ||
| "func", | ||
| "glob", | ||
| "mem", | ||
| "tab", | ||
| "value with spaces", | ||
| "🎯test-func!", | ||
| ]); | ||
| assert_true(mod.func instanceof Function); | ||
| assert_true(mod.mem instanceof WebAssembly.Memory); | ||
| assert_true(mod.tab instanceof WebAssembly.Table); | ||
|
|
||
| assert_false(mod.glob instanceof WebAssembly.Global); | ||
| assert_equals(typeof mod.glob, "number"); | ||
|
|
||
| assert_throws_js(TypeError, () => { | ||
| mod.func = 2; | ||
| }); | ||
|
|
||
| assert_equals(typeof mod["value with spaces"], "number"); | ||
| assert_equals(mod["value with spaces"], 123); | ||
|
|
||
| assert_true(mod["🎯test-func!"] instanceof Function); | ||
| assert_equals(mod["🎯test-func!"](), 456); | ||
|
|
||
| assert_equals(typeof mod["a\u200Bb\u0300c"], "number"); | ||
| assert_equals(mod["a\u200Bb\u0300c"], 789); | ||
| }, "Exported names from a WebAssembly module"); |
46 changes: 46 additions & 0 deletions
46
wasm/jsapi/esm-integration/global-exports-live-bindings.tentative.any.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // META: global=window,dedicatedworker,jsshell,shadowrealm | ||
|
|
||
| promise_test(async () => { | ||
| const wasmExports = await import("./resources/globals.wasm"); | ||
|
|
||
| wasmExports.setLocalMutI32(555); | ||
| assert_equals(wasmExports.getLocalMutI32(), 555); | ||
| assert_equals(wasmExports.localMutI32, 555); | ||
|
|
||
| wasmExports.setLocalMutI64(444n); | ||
| assert_equals(wasmExports.getLocalMutI64(), 444n); | ||
| assert_equals(wasmExports.localMutI64, 444n); | ||
|
|
||
| wasmExports.setLocalMutF32(3.33); | ||
| assert_equals(Math.round(wasmExports.getLocalMutF32() * 100) / 100, 3.33); | ||
| assert_equals(Math.round(wasmExports.localMutF32 * 100) / 100, 3.33); | ||
|
|
||
| wasmExports.setLocalMutF64(2.22); | ||
| assert_equals(wasmExports.getLocalMutF64(), 2.22); | ||
| assert_equals(wasmExports.localMutF64, 2.22); | ||
|
|
||
| const anotherTestObj = { another: "test object" }; | ||
| wasmExports.setLocalMutExternref(anotherTestObj); | ||
| assert_equals(wasmExports.getLocalMutExternref(), anotherTestObj); | ||
| assert_equals(wasmExports.localMutExternref, anotherTestObj); | ||
| }, "Local mutable global exports should be live bindings"); | ||
|
|
||
| promise_test(async () => { | ||
| const wasmExports = await import("./resources/globals.wasm"); | ||
|
|
||
| wasmExports.setDepMutI32(3001); | ||
| assert_equals(wasmExports.getDepMutI32(), 3001); | ||
| assert_equals(wasmExports.depMutI32, 3001); | ||
|
|
||
| wasmExports.setDepMutI64(30000000001n); | ||
| assert_equals(wasmExports.getDepMutI64(), 30000000001n); | ||
| assert_equals(wasmExports.depMutI64, 30000000001n); | ||
|
|
||
| wasmExports.setDepMutF32(30.01); | ||
| assert_equals(Math.round(wasmExports.getDepMutF32() * 100) / 100, 30.01); | ||
| assert_equals(Math.round(wasmExports.depMutF32 * 100) / 100, 30.01); | ||
|
|
||
| wasmExports.setDepMutF64(300.0001); | ||
| assert_equals(wasmExports.getDepMutF64(), 300.0001); | ||
| assert_equals(wasmExports.depMutF64, 300.0001); | ||
| }, "Dep module mutable global exports should be live bindings"); |
111 changes: 111 additions & 0 deletions
111
wasm/jsapi/esm-integration/global-exports.tentative.any.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| // META: global=window,dedicatedworker,jsshell,shadowrealm | ||
|
|
||
| promise_test(async () => { | ||
| const wasmModule = await import("./resources/globals.wasm"); | ||
|
|
||
| assert_equals(wasmModule.importedI32, 42); | ||
| assert_equals(wasmModule.importedI64, 9223372036854775807n); | ||
| assert_equals(Math.round(wasmModule.importedF32 * 100000) / 100000, 3.14159); | ||
| assert_equals(wasmModule.importedF64, 3.141592653589793); | ||
| assert_not_equals(wasmModule.importedExternref, null); | ||
| assert_equals(wasmModule.importedNullExternref, null); | ||
| }, "WebAssembly module global values should be unwrapped when importing in ESM integration"); | ||
|
|
||
| promise_test(async () => { | ||
| const wasmModule = await import("./resources/globals.wasm"); | ||
|
|
||
| assert_equals(wasmModule.importedMutI32, 100); | ||
| assert_equals(wasmModule.importedMutI64, 200n); | ||
| assert_equals( | ||
| Math.round(wasmModule.importedMutF32 * 100000) / 100000, | ||
| 2.71828 | ||
| ); | ||
| assert_equals(wasmModule.importedMutF64, 2.718281828459045); | ||
| assert_not_equals(wasmModule.importedMutExternref, null); | ||
| assert_equals(wasmModule.importedMutExternref.mutable, "global"); | ||
| }, "WebAssembly mutable global values should be unwrapped when importing in ESM integration"); | ||
|
|
||
| promise_test(async () => { | ||
| const wasmModule = await import("./resources/globals.wasm"); | ||
|
|
||
| assert_equals(wasmModule["🚀localI32"], 42); | ||
| assert_equals(wasmModule.localMutI32, 100); | ||
| assert_equals(wasmModule.localI64, 9223372036854775807n); | ||
| assert_equals(wasmModule.localMutI64, 200n); | ||
| assert_equals(Math.round(wasmModule.localF32 * 100000) / 100000, 3.14159); | ||
| assert_equals(Math.round(wasmModule.localMutF32 * 100000) / 100000, 2.71828); | ||
| assert_equals(wasmModule.localF64, 2.718281828459045); | ||
| assert_equals(wasmModule.localMutF64, 3.141592653589793); | ||
| }, "WebAssembly local global values should be unwrapped when exporting in ESM integration"); | ||
|
|
||
| promise_test(async () => { | ||
| const wasmModule = await import("./resources/globals.wasm"); | ||
|
|
||
| assert_equals(wasmModule.depI32, 1001); | ||
| assert_equals(wasmModule.depMutI32, 2001); | ||
| assert_equals(wasmModule.depI64, 10000000001n); | ||
| assert_equals(wasmModule.depMutI64, 20000000001n); | ||
| assert_equals(Math.round(wasmModule.depF32 * 100) / 100, 10.01); | ||
| assert_equals(Math.round(wasmModule.depMutF32 * 100) / 100, 20.01); | ||
| assert_equals(wasmModule.depF64, 100.0001); | ||
| assert_equals(wasmModule.depMutF64, 200.0001); | ||
| }, "WebAssembly module globals from imported WebAssembly modules should be unwrapped"); | ||
|
|
||
| promise_test(async () => { | ||
| const wasmModule = await import("./resources/globals.wasm"); | ||
|
|
||
| assert_equals(wasmModule.importedI32, 42); | ||
| assert_equals(wasmModule.importedMutI32, 100); | ||
| assert_equals(wasmModule.importedI64, 9223372036854775807n); | ||
| assert_equals(wasmModule.importedMutI64, 200n); | ||
| assert_equals(Math.round(wasmModule.importedF32 * 100000) / 100000, 3.14159); | ||
| assert_equals( | ||
| Math.round(wasmModule.importedMutF32 * 100000) / 100000, | ||
| 2.71828 | ||
| ); | ||
| assert_equals(wasmModule.importedF64, 3.141592653589793); | ||
| assert_equals(wasmModule.importedMutF64, 2.718281828459045); | ||
| assert_equals(wasmModule.importedExternref !== null, true); | ||
| assert_equals(wasmModule.importedMutExternref !== null, true); | ||
| assert_equals(wasmModule.importedNullExternref, null); | ||
|
|
||
| assert_equals(wasmModule["🚀localI32"], 42); | ||
| assert_equals(wasmModule.localMutI32, 100); | ||
| assert_equals(wasmModule.localI64, 9223372036854775807n); | ||
| assert_equals(wasmModule.localMutI64, 200n); | ||
| assert_equals(Math.round(wasmModule.localF32 * 100000) / 100000, 3.14159); | ||
| assert_equals(Math.round(wasmModule.localMutF32 * 100000) / 100000, 2.71828); | ||
| assert_equals(wasmModule.localF64, 2.718281828459045); | ||
| assert_equals(wasmModule.localMutF64, 3.141592653589793); | ||
|
|
||
| assert_equals(wasmModule.getImportedMutI32(), 100); | ||
| assert_equals(wasmModule.getImportedMutI64(), 200n); | ||
| assert_equals( | ||
| Math.round(wasmModule.getImportedMutF32() * 100000) / 100000, | ||
| 2.71828 | ||
| ); | ||
| assert_equals(wasmModule.getImportedMutF64(), 2.718281828459045); | ||
| assert_equals(wasmModule.getImportedMutExternref() !== null, true); | ||
|
|
||
| assert_equals(wasmModule.getLocalMutI32(), 100); | ||
| assert_equals(wasmModule.getLocalMutI64(), 200n); | ||
| assert_equals( | ||
| Math.round(wasmModule.getLocalMutF32() * 100000) / 100000, | ||
| 2.71828 | ||
| ); | ||
| assert_equals(wasmModule.getLocalMutF64(), 3.141592653589793); | ||
| assert_equals(wasmModule.getLocalMutExternref(), null); | ||
|
|
||
| assert_equals(wasmModule.depI32, 1001); | ||
| assert_equals(wasmModule.depMutI32, 2001); | ||
| assert_equals(wasmModule.getDepMutI32(), 2001); | ||
| assert_equals(wasmModule.depI64, 10000000001n); | ||
| assert_equals(wasmModule.depMutI64, 20000000001n); | ||
| assert_equals(wasmModule.getDepMutI64(), 20000000001n); | ||
| assert_equals(Math.round(wasmModule.depF32 * 100) / 100, 10.01); | ||
| assert_equals(Math.round(wasmModule.depMutF32 * 100) / 100, 20.01); | ||
| assert_equals(Math.round(wasmModule.getDepMutF32() * 100) / 100, 20.01); | ||
| assert_equals(wasmModule.depF64, 100.0001); | ||
| assert_equals(wasmModule.depMutF64, 200.0001); | ||
| assert_equals(wasmModule.getDepMutF64(), 200.0001); | ||
| }, "WebAssembly should properly handle all global types"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| // META: global=window,dedicatedworker,jsshell,shadowrealm | ||
|
|
||
| promise_test(async () => { | ||
| const { f } = await import("./resources/js-wasm-cycle.js"); | ||
|
|
||
| assert_equals(f(), 24); | ||
| }, "Check bindings in JavaScript and WebAssembly cycle (JS higher)"); |
80 changes: 80 additions & 0 deletions
80
wasm/jsapi/esm-integration/mutable-global-sharing.tentative.any.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| // META: global=window,dedicatedworker,jsshell,shadowrealm | ||
|
|
||
| promise_test(async () => { | ||
| const exporterModule = await import("./resources/mutable-global-export.wasm"); | ||
| const reexporterModule = await import( | ||
| "./resources/mutable-global-reexport.wasm" | ||
| ); | ||
|
|
||
| assert_equals(exporterModule.mutableValue, 100); | ||
| assert_equals(reexporterModule.reexportedMutableValue, 100); | ||
| }, "WebAssembly modules should export shared mutable globals with correct initial values"); | ||
|
|
||
| promise_test(async () => { | ||
| const exporterModule = await import("./resources/mutable-global-export.wasm"); | ||
| const reexporterModule = await import( | ||
| "./resources/mutable-global-reexport.wasm" | ||
| ); | ||
|
|
||
| exporterModule.setGlobal(500); | ||
|
|
||
| assert_equals(exporterModule.getGlobal(), 500, "exporter should see 500"); | ||
| assert_equals(reexporterModule.getImportedGlobal(), 500); | ||
|
|
||
| reexporterModule.setImportedGlobal(600); | ||
|
|
||
| assert_equals(exporterModule.getGlobal(), 600); | ||
| assert_equals(reexporterModule.getImportedGlobal(), 600); | ||
|
|
||
| exporterModule.setGlobal(700); | ||
|
|
||
| assert_equals(exporterModule.getGlobal(), 700); | ||
| assert_equals(reexporterModule.getImportedGlobal(), 700); | ||
| }, "Wasm-to-Wasm mutable global sharing is live"); | ||
|
|
||
| promise_test(async () => { | ||
| const module1 = await import("./resources/mutable-global-export.wasm"); | ||
| const module2 = await import("./resources/mutable-global-export.wasm"); | ||
|
|
||
| assert_equals(module1, module2); | ||
|
|
||
| module1.setGlobal(800); | ||
| assert_equals(module1.getGlobal(), 800, "module1 should see its own change"); | ||
| assert_equals(module2.getGlobal(), 800); | ||
| }, "Multiple JavaScript imports return the same WebAssembly module instance"); | ||
|
|
||
| promise_test(async () => { | ||
| const exporterModule = await import("./resources/mutable-global-export.wasm"); | ||
| const reexporterModule = await import( | ||
| "./resources/mutable-global-reexport.wasm" | ||
| ); | ||
|
|
||
| assert_equals(exporterModule.getV128Lane(0), 1); | ||
| assert_equals(exporterModule.getV128Lane(1), 2); | ||
| assert_equals(exporterModule.getV128Lane(2), 3); | ||
| assert_equals(exporterModule.getV128Lane(3), 4); | ||
|
|
||
| assert_equals(reexporterModule.getImportedV128Lane(0), 1); | ||
| assert_equals(reexporterModule.getImportedV128Lane(1), 2); | ||
| assert_equals(reexporterModule.getImportedV128Lane(2), 3); | ||
| assert_equals(reexporterModule.getImportedV128Lane(3), 4); | ||
| }, "v128 globals should work correctly in WebAssembly-to-WebAssembly imports"); | ||
|
|
||
| promise_test(async () => { | ||
| const exporterModule = await import("./resources/mutable-global-export.wasm"); | ||
| const reexporterModule = await import( | ||
| "./resources/mutable-global-reexport.wasm" | ||
| ); | ||
|
|
||
| exporterModule.setV128Global(10, 20, 30, 40); | ||
|
|
||
| assert_equals(exporterModule.getV128Lane(0), 10); | ||
| assert_equals(exporterModule.getV128Lane(1), 20); | ||
| assert_equals(exporterModule.getV128Lane(2), 30); | ||
| assert_equals(exporterModule.getV128Lane(3), 40); | ||
|
|
||
| assert_equals(reexporterModule.getImportedV128Lane(0), 10); | ||
| assert_equals(reexporterModule.getImportedV128Lane(1), 20); | ||
| assert_equals(reexporterModule.getImportedV128Lane(2), 30); | ||
| assert_equals(reexporterModule.getImportedV128Lane(3), 40); | ||
| }, "v128 global mutations should work correctly between WebAssembly modules"); |
56 changes: 56 additions & 0 deletions
56
wasm/jsapi/esm-integration/namespace-instance.tentative.any.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // META: global=window,dedicatedworker,jsshell,shadowrealm | ||
|
|
||
| promise_test(async () => { | ||
| const wasmNamespace = await import("./resources/mutable-global-export.wasm"); | ||
| const instance = WebAssembly.namespaceInstance(wasmNamespace); | ||
|
|
||
| const wasmNamespace2 = await import("./resources/mutable-global-export.wasm"); | ||
| const instance2 = WebAssembly.namespaceInstance(wasmNamespace2); | ||
| assert_equals(instance, instance2); | ||
|
|
||
| assert_true(instance instanceof WebAssembly.Instance); | ||
|
|
||
| wasmNamespace.setGlobal(999); | ||
| assert_equals(instance.exports.getGlobal(), 999); | ||
|
|
||
| instance.exports.setGlobal(888); | ||
| assert_equals(wasmNamespace.getGlobal(), 888); | ||
| }, "WebAssembly.namespaceInstance() should return the underlying instance with shared state"); | ||
|
|
||
| promise_test(async () => { | ||
| assert_throws_js(TypeError, () => WebAssembly.namespaceInstance({})); | ||
| assert_throws_js(TypeError, () => WebAssembly.namespaceInstance(null)); | ||
| assert_throws_js(TypeError, () => WebAssembly.namespaceInstance(undefined)); | ||
| assert_throws_js(TypeError, () => WebAssembly.namespaceInstance(42)); | ||
| assert_throws_js(TypeError, () => | ||
| WebAssembly.namespaceInstance("not a namespace") | ||
| ); | ||
| assert_throws_js(TypeError, () => WebAssembly.namespaceInstance([])); | ||
| assert_throws_js(TypeError, () => | ||
| WebAssembly.namespaceInstance(function () {}) | ||
| ); | ||
|
|
||
| const jsModule = await import("./resources/globals.js"); | ||
| assert_throws_js(TypeError, () => WebAssembly.namespaceInstance(jsModule)); | ||
| }, "WebAssembly.namespaceInstance() should throw TypeError for non-WebAssembly namespaces"); | ||
|
|
||
| promise_test(async () => { | ||
| const exportsModule = await import("./resources/exports.wasm"); | ||
| const globalsModule = await import("./resources/globals.wasm"); | ||
|
|
||
| const exportsInstance = WebAssembly.namespaceInstance(exportsModule); | ||
| const globalsInstance = WebAssembly.namespaceInstance(globalsModule); | ||
|
|
||
| assert_not_equals(exportsInstance, globalsInstance); | ||
| assert_true(exportsInstance.exports.func instanceof Function); | ||
| assert_true(globalsInstance.exports.getLocalMutI32 instanceof Function); | ||
|
|
||
| globalsModule.setLocalMutI32(12345); | ||
| assert_equals(globalsInstance.exports.getLocalMutI32(), 12345); | ||
|
|
||
| globalsInstance.exports.setLocalMutI32(54321); | ||
| assert_equals(globalsModule.getLocalMutI32(), 54321); | ||
|
|
||
| const exportsInstance2 = WebAssembly.namespaceInstance(exportsModule); | ||
| assert_equals(exportsInstance, exportsInstance2); | ||
| }, "WebAssembly.namespaceInstance() should work correctly with multiple modules"); | ||
41 changes: 41 additions & 0 deletions
41
wasm/jsapi/esm-integration/reserved-import-names.tentative.any.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| // Test that wasm: and wasm-js: reserved cases should cause WebAssembly.LinkError | ||
|
|
||
| promise_test(async (t) => { | ||
| await promise_rejects_js( | ||
| t, | ||
| WebAssembly.LinkError, | ||
| import("./resources/invalid-import-name.wasm") | ||
| ); | ||
| }, "wasm: reserved import names should cause WebAssembly.LinkError"); | ||
|
|
||
| promise_test(async (t) => { | ||
| await promise_rejects_js( | ||
| t, | ||
| WebAssembly.LinkError, | ||
| import("./resources/invalid-import-name-wasm-js.wasm") | ||
| ); | ||
| }, "wasm-js: reserved import names should cause WebAssembly.LinkError"); | ||
|
|
||
| promise_test(async (t) => { | ||
| await promise_rejects_js( | ||
| t, | ||
| WebAssembly.LinkError, | ||
| import("./resources/invalid-export-name.wasm") | ||
| ); | ||
| }, "wasm: reserved export names should cause WebAssembly.LinkError"); | ||
|
|
||
| promise_test(async (t) => { | ||
| await promise_rejects_js( | ||
| t, | ||
| WebAssembly.LinkError, | ||
| import("./resources/invalid-export-name-wasm-js.wasm") | ||
| ); | ||
| }, "wasm-js: reserved export names should cause WebAssembly.LinkError"); | ||
|
|
||
| promise_test(async (t) => { | ||
| await promise_rejects_js( | ||
| t, | ||
| WebAssembly.LinkError, | ||
| import("./resources/invalid-import-module.wasm") | ||
| ); | ||
| }, "wasm-js: reserved module names should cause WebAssembly.LinkError"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| // META: global=window,dedicatedworker,jsshell,shadowrealm | ||
|
|
||
| promise_test(async (t) => { | ||
| await promise_rejects_js( | ||
| t, | ||
| SyntaxError, | ||
| import("./resources/resolve-export.js") | ||
| ); | ||
| }, "ResolveExport on invalid re-export from WebAssembly"); |
Binary file not shown.
Binary file not shown.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.