-
Notifications
You must be signed in to change notification settings - Fork 34
Handle unhandled Promise rejections in JavaScript #65
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| import envoy | ||
| import gleam/dynamic | ||
| import gleam/function | ||
| import gleeunit/internal/gleam_panic.{ | ||
| Assert, BinaryOperator, Expression, FunctionCall, LetAssert, Literal, | ||
| OtherExpression, Panic, Todo, Unevaluated, | ||
| } | ||
| import shellout | ||
| import testhelper | ||
|
|
||
| @external(erlang, "gleeunit_test_ffi", "rescue") | ||
|
|
@@ -206,3 +208,65 @@ pub fn assert_binary_operator_test() { | |
| assert right.end == end | ||
| assert right.kind == Unevaluated | ||
| } | ||
|
|
||
| pub fn javascript_unhandled_promise_test() { | ||
| case envoy.get("GLEEUNIT_RUN_UNHANDLED_PROMISE_TEST") { | ||
| Error(_) -> { | ||
| // Spawn gleam test | ||
| let assert Error(#(code, _)) = | ||
| shellout.command( | ||
| run: "gleam", | ||
| with: ["test", "-t", "javascript"], | ||
| in: ".", | ||
| opt: [ | ||
| shellout.SetEnvironment([ | ||
| #("GLEEUNIT_RUN_UNHANDLED_PROMISE_TEST", "1"), | ||
| ]), | ||
| ], | ||
| ) | ||
| as "immediate rejected promise: expected error due to unhandled promise rejection" | ||
|
|
||
| assert code == 1 | ||
| as "immediate rejected promise: expected exit code 1 due to unhandled promise rejection" | ||
|
|
||
| // Spawn gleam test | ||
| let assert Error(#(code, _)) = | ||
| shellout.command( | ||
| run: "gleam", | ||
| with: ["test", "-t", "javascript"], | ||
| in: ".", | ||
| opt: [ | ||
| shellout.SetEnvironment([ | ||
| #("GLEEUNIT_RUN_UNHANDLED_PROMISE_TEST", "2"), | ||
| ]), | ||
| ], | ||
| ) | ||
| as "delayed rejected promise: expected error due to unhandled promise rejection" | ||
|
|
||
| assert code == 1 | ||
| as "delayed rejected promise: expected exit code 1 due to unhandled promise rejection" | ||
| } | ||
| Ok("1") -> { | ||
| echo "inside spawned gleam test, testing immediate reject" | ||
| javascript_test_promise() | ||
| } | ||
| Ok(_) -> { | ||
| echo "inside spawned gleam test, testing delayed reject" | ||
| javascript_test_delayed_promise() | ||
| } | ||
| } | ||
| } | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand this test, what is it doing? Why is it reading environment variables and spawning OS processes?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It respawns |
||
|
|
||
| @external(javascript, "./gleeunit_test_ffi.mjs", "promise_fail_test") | ||
| fn javascript_test_promise() -> Nil { | ||
| // Not relevant to other targets | ||
| echo "This is not relevant to other targets!" | ||
| Nil | ||
| } | ||
|
|
||
| @external(javascript, "./gleeunit_test_ffi.mjs", "delayed_promise_fail_test") | ||
| fn javascript_test_delayed_promise() -> Nil { | ||
| // Not relevant to other targets | ||
| echo "This is not relevant to other targets!" | ||
| Nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,28 @@ | ||
| import { Ok, Error } from "./gleam.mjs"; | ||
|
|
||
| // The maximum amount of time running all tests is expected to take. | ||
| // This is used to set the timeout for the delayed promise test, to ensure | ||
| // it runs after all other tests have had a chance to execute. | ||
| const MAX_TEST_TIME = 4000; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is a bit confusing to me, it sounds like it is being used as the timeout for tests being run, but it seems to instead be used to reject one promise in the FFI below?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The intention is for the Promise's rejection to outlast the regular time of the tests, as it's testing to make sure |
||
|
|
||
| export function rescue(f) { | ||
| try { | ||
| return new Ok(f()); | ||
| } catch (e) { | ||
| return new Error(e); | ||
| } | ||
| } | ||
|
|
||
| export function promise_fail_test() { | ||
| new Promise(() => { | ||
| throw new Error("Promise panicked"); | ||
| }); | ||
| } | ||
|
|
||
| export function delayed_promise_fail_test() { | ||
| new Promise((_, reject) => { | ||
| setTimeout(() => { | ||
| reject(new Error("Promise panicked")); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This says the promise panics, but there's no panic here.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it, i'll update the message! |
||
| }, MAX_TEST_TIME); | ||
| }); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very cool! Which runtimes is this API not available for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Node, Deno, and Bun all cover it, so the edge case is if someone decides to run it on a Vercel Edge runtime or some really early version of Deno/Bun or a browser or something :p i can remove it if you want