Files
wasm-bindgen/crates/test/src/rt/detect.rs
Alex Crichton eee71de0ce Support asynchronous tests (#600)
* Tweak the implementation of heap closures

This commit updates the implementation of the `Closure` type to internally store
an `Rc` and be suitable for dropping a `Closure` during the execution of the
closure. This is currently needed for promises but may be generally useful as
well!

* Support asynchronous tests

This commit adds support for executing tests asynchronously. This is modeled
by tests returning a `Future` instead of simply executing inline, and is
signified with `#[wasm_bindgen_test(async)]`.

Support for this is added through a new `wasm-bindgen-futures` crate which is a
binding between the `futures` crate and JS `Promise` objects.

Lots more details can be found in the details of the commit, but one of the end
results is that the `web-sys` tests are now entirely contained in the same test
suite and don't need `npm install` to be run to execute them!

* Review tweaks

* Add some bindings for `Function.call` to `js_sys`

Name them `call0`, `call1`, `call2`, ... for the number of arguments being
passed.

* Use oneshots channels with `JsFuture`

It did indeed clean up the implementation!
2018-08-01 15:52:24 -05:00

62 lines
2.4 KiB
Rust

//! Runtime detection of whether we're in node.js or a browser.
use wasm_bindgen::prelude::*;
use js_sys::Function;
#[wasm_bindgen]
extern {
type This;
#[wasm_bindgen(method, getter, structural, js_name = self)]
fn self_(me: &This) -> JsValue;
}
/// Returns whether it's likely we're executing in a browser environment, as
/// opposed to node.js.
pub fn is_browser() -> bool {
// This is a bit tricky to define. The basic crux of this is that we want to
// test if the `self` identifier is defined. That is defined in browsers
// (and web workers!) but not in Node. To that end you might expect:
//
// #[wasm_bindgen]
// extern {
// #[wasm_bindgen(js_name = self)]
// static SELF: JsValue;
// }
//
// *SELF != JsValue::undefined()
//
// this currently, however, throws a "not defined" error in JS because the
// generated function looks like `function() { return self; }` which throws
// an error in Node because `self` isn't defined.
//
// To work around this limitation we instead lookup the value of `self`
// through the `this` object, basically generating `this.self`.
//
// Unfortunately that's also hard to do! In ESM modes the top-level `this`
// object is undefined, meaning that we can't just generate a function that
// returns `this.self` as it'll throw "can't access field `self` of
// `undefined`" whenever ESMs are being used.
//
// So finally we reach the current implementation. According to
// StackOverflow you can access the global object via:
//
// const global = Function('return this')();
//
// I think that's because the manufactured function isn't in "strict" mode.
// It also turns out that non-strict functions will ignore `undefined`
// values for `this` when using the `apply` function. Add it all up, and you
// get the below code:
//
// * Manufacture a function
// * Call `apply` where we specify `this` but the function ignores it
// * Once we have `this`, use a structural getter to get the value of `self`
// * Last but not least, test whether `self` is defined or not.
//
// Whew!
let this = Function::new_no_args("return this")
.call0(&JsValue::undefined())
.unwrap();
assert!(this != JsValue::undefined());
This::from(this).self_() != JsValue::undefined()
}