summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/tests/integration/test_tests.rs6
-rw-r--r--cli/tests/testdata/test/interval.out5
-rw-r--r--cli/tests/testdata/test/interval.ts1
-rw-r--r--cli/tests/testdata/test/unresolved_promise.out5
-rw-r--r--cli/tests/testdata/test/unresolved_promise.ts12
-rw-r--r--cli/tools/test.rs41
-rw-r--r--core/runtime.rs91
7 files changed, 129 insertions, 32 deletions
diff --git a/cli/tests/integration/test_tests.rs b/cli/tests/integration/test_tests.rs
index d9d1eed45..0baf94422 100644
--- a/cli/tests/integration/test_tests.rs
+++ b/cli/tests/integration/test_tests.rs
@@ -61,6 +61,12 @@ itest!(load_unload {
output: "test/load_unload.out",
});
+itest!(interval {
+ args: "test test/interval.ts",
+ exit_code: 0,
+ output: "test/interval.out",
+});
+
itest!(doc {
args: "test --doc --allow-all test/doc.ts",
exit_code: 1,
diff --git a/cli/tests/testdata/test/interval.out b/cli/tests/testdata/test/interval.out
new file mode 100644
index 000000000..024698dde
--- /dev/null
+++ b/cli/tests/testdata/test/interval.out
@@ -0,0 +1,5 @@
+Check [WILDCARD]/test/interval.ts
+running 0 tests from [WILDCARD]/test/interval.ts
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WILDCARD])
+
diff --git a/cli/tests/testdata/test/interval.ts b/cli/tests/testdata/test/interval.ts
new file mode 100644
index 000000000..7eb588c59
--- /dev/null
+++ b/cli/tests/testdata/test/interval.ts
@@ -0,0 +1 @@
+setInterval(function () {}, 0);
diff --git a/cli/tests/testdata/test/unresolved_promise.out b/cli/tests/testdata/test/unresolved_promise.out
index cd505bcbf..4e5c20a3b 100644
--- a/cli/tests/testdata/test/unresolved_promise.out
+++ b/cli/tests/testdata/test/unresolved_promise.out
@@ -1,6 +1,5 @@
Check [WILDCARD]/test/unresolved_promise.ts
-running 2 tests from [WILDCARD]/test/unresolved_promise.ts
-test unresolved promise ...
-test result: FAILED. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WILDCARD])
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ([WILDCARD])
error: Module evaluation is still pending but there are no pending ops or dynamic imports. This situation is often caused by unresolved promise.
diff --git a/cli/tests/testdata/test/unresolved_promise.ts b/cli/tests/testdata/test/unresolved_promise.ts
index 8f50e907a..25fe70762 100644
--- a/cli/tests/testdata/test/unresolved_promise.ts
+++ b/cli/tests/testdata/test/unresolved_promise.ts
@@ -1,11 +1 @@
-Deno.test({
- name: "unresolved promise",
- fn() {
- return new Promise((_resolve, _reject) => {});
- },
-});
-
-Deno.test({
- name: "ok",
- fn() {},
-});
+await new Promise((_resolve, _reject) => {});
diff --git a/cli/tools/test.rs b/cli/tools/test.rs
index 819f9980c..859e23934 100644
--- a/cli/tools/test.rs
+++ b/cli/tools/test.rs
@@ -11,6 +11,7 @@ use crate::flags::Flags;
use crate::fs_util::collect_specifiers;
use crate::fs_util::is_supported_test_ext;
use crate::fs_util::is_supported_test_path;
+use crate::located_script_name;
use crate::media_type::MediaType;
use crate::module_graph;
use crate::module_graph::GraphBuilder;
@@ -264,21 +265,6 @@ async fn test_specifier(
test_source.push_str(&format!("import \"{}\";\n", specifier));
}
- test_source
- .push_str("await new Promise(resolve => setTimeout(resolve, 0));\n");
-
- test_source.push_str("window.dispatchEvent(new Event('load'));\n");
-
- test_source.push_str(&format!(
- "await Deno[Deno.internal].runTests({});\n",
- json!({
- "filter": filter,
- "shuffle": shuffle,
- }),
- ));
-
- test_source.push_str("window.dispatchEvent(new Event('unload'));\n");
-
let test_file = File {
local: test_specifier.to_file_path().unwrap(),
maybe_types: None,
@@ -323,9 +309,28 @@ async fn test_specifier(
worker.execute_module(&test_specifier).await?;
- worker
- .run_event_loop(maybe_coverage_collector.is_none())
- .await?;
+ worker.js_runtime.execute_script(
+ &located_script_name!(),
+ "window.dispatchEvent(new Event('load'));",
+ )?;
+
+ let test_result = worker.js_runtime.execute_script(
+ &located_script_name!(),
+ &format!(
+ r#"Deno[Deno.internal].runTests({})"#,
+ json!({
+ "filter": filter,
+ "shuffle": shuffle,
+ }),
+ ),
+ )?;
+
+ worker.js_runtime.resolve_value(test_result).await?;
+
+ worker.js_runtime.execute_script(
+ &located_script_name!(),
+ "window.dispatchEvent(new Event('unload'));",
+ )?;
if let Some(coverage_collector) = maybe_coverage_collector.as_mut() {
worker
diff --git a/core/runtime.rs b/core/runtime.rs
index 8edd0466c..d5089569d 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -647,6 +647,48 @@ impl JsRuntime {
scope.perform_microtask_checkpoint();
}
+ /// Waits for the given value to resolve while polling the event loop.
+ ///
+ /// This future resolves when either the value is resolved or the event loop runs to
+ /// completion.
+ pub async fn resolve_value(
+ &mut self,
+ global: v8::Global<v8::Value>,
+ ) -> Result<v8::Global<v8::Value>, AnyError> {
+ poll_fn(|cx| {
+ let state = self.poll_event_loop(cx, false);
+
+ let mut scope = self.handle_scope();
+ let local = v8::Local::<v8::Value>::new(&mut scope, &global);
+
+ if let Ok(promise) = v8::Local::<v8::Promise>::try_from(local) {
+ match promise.state() {
+ v8::PromiseState::Pending => match state {
+ Poll::Ready(Ok(_)) => {
+ let msg = "Promise resolution is still pending but the event loop has already resolved.";
+ Poll::Ready(Err(generic_error(msg)))
+ },
+ Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
+ Poll::Pending => Poll::Pending,
+ },
+ v8::PromiseState::Fulfilled => {
+ let value = promise.result(&mut scope);
+ let value_handle = v8::Global::new(&mut scope, value);
+ Poll::Ready(Ok(value_handle))
+ }
+ v8::PromiseState::Rejected => {
+ let exception = promise.result(&mut scope);
+ Poll::Ready(exception_to_err_result(&mut scope, exception, false))
+ }
+ }
+ } else {
+ let value_handle = v8::Global::new(&mut scope, local);
+ Poll::Ready(Ok(value_handle))
+ }
+ })
+ .await
+ }
+
/// Runs event loop to completion
///
/// This future resolves when:
@@ -1628,6 +1670,55 @@ pub mod tests {
}
}
+ #[tokio::test]
+ async fn test_resolve_value() {
+ let mut runtime = JsRuntime::new(Default::default());
+ let value_global = runtime
+ .execute_script("a.js", "Promise.resolve(1 + 2)")
+ .unwrap();
+ let result_global = runtime.resolve_value(value_global).await.unwrap();
+ {
+ let scope = &mut runtime.handle_scope();
+ let value = result_global.get(scope);
+ assert_eq!(value.integer_value(scope).unwrap(), 3);
+ }
+
+ let value_global = runtime
+ .execute_script(
+ "a.js",
+ "Promise.resolve(new Promise(resolve => resolve(2 + 2)))",
+ )
+ .unwrap();
+ let result_global = runtime.resolve_value(value_global).await.unwrap();
+ {
+ let scope = &mut runtime.handle_scope();
+ let value = result_global.get(scope);
+ assert_eq!(value.integer_value(scope).unwrap(), 4);
+ }
+
+ let value_global = runtime
+ .execute_script("a.js", "Promise.reject(new Error('fail'))")
+ .unwrap();
+ let err = runtime.resolve_value(value_global).await.unwrap_err();
+ assert_eq!(
+ "Uncaught Error: fail",
+ err.downcast::<JsError>().unwrap().message
+ );
+
+ let value_global = runtime
+ .execute_script("a.js", "new Promise(resolve => {})")
+ .unwrap();
+ let error_string = runtime
+ .resolve_value(value_global)
+ .await
+ .unwrap_err()
+ .to_string();
+ assert_eq!(
+ "Promise resolution is still pending but the event loop has already resolved.",
+ error_string,
+ );
+ }
+
#[test]
fn terminate_execution() {
let (mut isolate, _dispatch_count) = setup(Mode::Async);