feature(test): Add a `wasm-pack test` subcommand

This uses `wasm-bindgen-test` under the hood.

Fixes #248
master
Nick Fitzgerald 7 years ago
parent 2cd4504b64
commit 577d1b33e6
  1. 3
      .appveyor.yml
  2. 5
      .gitignore
  3. 13
      .travis.yml
  4. 130
      Cargo.lock
  5. 1
      Cargo.toml
  6. 243
      src/binaries.rs
  7. 165
      src/bindgen.rs
  8. 24
      src/build.rs
  9. 32
      src/command/build.rs
  10. 12
      src/command/mod.rs
  11. 2
      src/command/pack.rs
  12. 2
      src/command/publish.rs
  13. 402
      src/command/test.rs
  14. 5
      src/command/utils.rs
  15. 12
      src/error.rs
  16. 4
      src/lib.rs
  17. 1
      src/logger.rs
  18. 116
      src/manifest.rs
  19. 15
      src/target.rs
  20. 51
      src/test/mod.rs
  21. 134
      src/test/webdriver.rs
  22. 6
      tests/all/main.rs
  23. 13
      tests/all/manifest.rs
  24. 164
      tests/all/test.rs
  25. 122
      tests/all/utils/fixture.rs
  26. 26
      tests/all/webdriver.rs
  27. 16
      tests/fixtures/wbg-test-bad-versions/Cargo.toml
  28. 7
      tests/fixtures/wbg-test-bad-versions/src/lib.rs
  29. 8
      tests/fixtures/wbg-test-bad-versions/tests/node.rs
  30. 13
      tests/fixtures/wbg-test-browser/Cargo.toml
  31. 5
      tests/fixtures/wbg-test-browser/src/lib.rs
  32. 9
      tests/fixtures/wbg-test-browser/tests/browser.rs
  33. 13
      tests/fixtures/wbg-test-fail/Cargo.toml
  34. 5
      tests/fixtures/wbg-test-fail/src/lib.rs
  35. 7
      tests/fixtures/wbg-test-fail/tests/node.rs
  36. 13
      tests/fixtures/wbg-test-node/Cargo.toml
  37. 7
      tests/fixtures/wbg-test-node/src/lib.rs
  38. 8
      tests/fixtures/wbg-test-node/tests/node.rs

@ -3,9 +3,12 @@ environment:
RUSTFLAGS: -C target-feature=+crt-static
install:
- ps: Install-Product node 10
- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain nightly
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- set RUST_BACKTRACE=1
- rustup target add wasm32-unknown-unknown --toolchain nightly
- rustc -V
- cargo -V

5
.gitignore vendored

@ -1,6 +1,5 @@
target/
**/target
**/*.rs.bk
**/pkg
tests/fixtures/**/Cargo.lock
tests/.crates.toml
tests/bin
wasm-pack.log

@ -1,6 +1,13 @@
language: rust
sudo: false
INSTALL_NODE_VIA_NVM: &INSTALL_NODE_VIA_NVM
|
rustup target add wasm32-unknown-unknown
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
source ~/.nvm/nvm.sh
nvm install lts/carbon
# Cache `cargo install`ed tools, but don't cache the project's `target`
# directory (which ends up over-caching and filling all disk space!)
cache:
@ -32,9 +39,13 @@ matrix:
include:
# tests pass
- env: JOB=test RUST_BACKTRACE=1
rust: nightly
addons:
firefox: latest
chrome: stable
install:
- *INSTALL_NODE_VIA_NVM
script:
- cargo test --locked
- rustup component add rustfmt-preview

130
Cargo.lock generated

@ -1,3 +1,8 @@
[[package]]
name = "adler32"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "aho-corasick"
version = "0.6.8"
@ -50,11 +55,34 @@ name = "bitflags"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "build_const"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bzip2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bzip2-sys"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.23"
@ -131,6 +159,14 @@ dependencies = [
"walkdir 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "crc"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "curl"
version = "0.4.14"
@ -174,7 +210,7 @@ name = "failure_derive"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -197,6 +233,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
"miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"miniz_oxide_c_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -234,8 +271,8 @@ dependencies = [
"backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"os_type 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -332,6 +369,35 @@ dependencies = [
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "miniz_oxide"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "miniz_oxide_c_api"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)",
"crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
"miniz_oxide 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "msdos_time"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-integer"
version = "0.1.39"
@ -424,9 +490,14 @@ name = "pkg-config"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "podio"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.4.15"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -437,7 +508,7 @@ name = "quote"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -534,15 +605,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.75"
version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
version = "1.0.75"
version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -554,7 +625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -627,7 +698,7 @@ name = "structopt-derive"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -637,7 +708,7 @@ name = "syn"
version = "0.14.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -647,7 +718,7 @@ name = "synstructure"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -756,7 +827,7 @@ name = "toml"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -839,8 +910,8 @@ dependencies = [
"lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"openssl 0.10.11 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
"slog 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"slog-async 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -850,6 +921,7 @@ dependencies = [
"tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"which 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"zip 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -906,14 +978,30 @@ dependencies = [
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zip"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"flate2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
]
[metadata]
"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c"
"checksum aho-corasick 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "68f56c7353e5a9547cbd76ed90f7bb5ffc3ba09d4ea9bd1d8c06c8b1142eeb5a"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a"
"checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781"
"checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b"
"checksum bzip2-sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2c5162604199bbb17690ede847eaa6120a3f33d5ab4dcc8e7c25b16d849ae79b"
"checksum cc 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)" = "c37f0efaa4b9b001fa6f02d4b644dee4af97d3414df07c51e3e4f015f3a3e131"
"checksum cfg-if 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0c4e7bb64a8ebb0d856483e1e682ea3422f883c5f5615a90d51a2c82fe87fdd3"
"checksum chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "45912881121cb26fad7c38c17ba7daa18764771836b34fab7d3fbd93ed633878"
@ -922,6 +1010,7 @@ dependencies = [
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum console 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7649ca90478264b9686aac8d269fcb014f14c2bed7c79a7e51b9f6afd4d783eb"
"checksum copy_dir 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e4281031634644843bd2f5aa9c48cf98fc48d6b083bd90bb11becf10deaf8b0"
"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
"checksum curl 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)" = "444c2f9e71458b34e75471ed8d756947a0bb920b8b8b9bfc56dfcc4fc6819a13"
"checksum curl-sys 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "981bd902fcd8b8b999cf71b81447e27d66c3493a7f62f1372866fd32986c0c82"
"checksum failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7efb22686e4a466b1ec1a15c2898f91fa9cb340452496dca654032de20ff95b9"
@ -944,6 +1033,9 @@ dependencies = [
"checksum lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "949826a5ccf18c1b3a7c3d57692778d21768b79e46eb9dd07bfc4c2160036c54"
"checksum memchr 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a3b4142ab8738a78c51896f704f83c11df047ff1bda9a92a661aa6361552d93d"
"checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4"
"checksum miniz_oxide 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9ba430291c9d6cedae28bcd2d49d1c32fc57d60cd49086646c5dd5673a870eb5"
"checksum miniz_oxide_c_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5a5b8234d6103ebfba71e29786da4608540f862de5ce980a1c94f86a40ca0d51"
"checksum msdos_time 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aad9dfe950c057b1bfe9c1f2aa51583a8468ef2a5baba2ebbe06d775efeb7729"
"checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea"
"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe"
"checksum openssl 0.10.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6c24d3508b4fb6da175c10baac54c578b33f09c89ae90c6fe9788b3b4768efdc"
@ -955,7 +1047,8 @@ dependencies = [
"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5"
"checksum parking_lot_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06a2b6aae052309c2fd2161ef58f5067bc17bb758377a0de9d4b279d603fdd8a"
"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
"checksum proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)" = "295af93acfb1d5be29c16ca5b3f82d863836efd9cb0c14fd83811eb9a110e452"
"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
"checksum proc-macro2 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7ee80fbbe0ae60bcad82d15bc59d5fe2aaebc1b83fbfb145abc8c74f8e769549"
"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5"
"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd"
"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
@ -969,8 +1062,8 @@ dependencies = [
"checksum ryu 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7153dd96dade874ab973e098cb62fcdbb89a03682e46b144fd09550998d4a4a7"
"checksum schannel 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "dc1fabf2a7b6483a141426e1afd09ad543520a77ac49bd03c286e7696ccfd77f"
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "22d340507cea0b7e6632900a176101fea959c7065d93ba555072da90aaaafc87"
"checksum serde_derive 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "234fc8b737737b148ccd625175fc6390f5e4dacfdaa543cb93a3430d984a9119"
"checksum serde 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)" = "d00c69ae39089576cddfd235556e3b21bf41c2d80018063cb5ab8a1183c917fd"
"checksum serde_derive 1.0.76 (registry+https://github.com/rust-lang/crates.io-index)" = "7d8384360683b7114fc6eeeb41dde6ee37eeba2cf3660deef3a7a0d7548e55e9"
"checksum serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "44dd2cfde475037451fa99b7e5df77aa3cfd1536575fa8e7a538ab36dcde49ae"
"checksum slog 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "09e4f1d0276ac7d448d98db16f0dab0220c24d4842d88ce4dad4b306fa234f1d"
"checksum slog-async 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e544d16c6b230d84c866662fe55e31aacfca6ae71e6fc49ae9a311cb379bfc2f"
@ -1014,3 +1107,4 @@ dependencies = [
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767"
"checksum xattr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c"
"checksum zip 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "36b9e08fb518a65cf7e08a1e482573eb87a2f4f8c6619316612a3c1f162fe822"

@ -28,6 +28,7 @@ structopt = "0.2"
tar = "0.4.16"
toml = "0.4"
which = "2.0.0"
zip = "0.4.2"
[dev-dependencies]
copy_dir = "0.1.2"

@ -0,0 +1,243 @@
//! Utilities for finding and installing binaries that we depend on.
use curl;
use error::Error;
use failure;
use flate2;
use slog::Logger;
use std::collections::HashSet;
use std::ffi;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use tar;
use target;
use which::which;
use zip;
/// Get the path for a crate's directory of locally-installed binaries.
///
/// This does not check whether or ensure that the directory exists.
pub fn local_bin_dir(crate_path: &Path) -> PathBuf {
crate_path.join("bin")
}
/// Ensure that the crate's directory for locally-installed binaries exists.
pub fn ensure_local_bin_dir(crate_path: &Path) -> io::Result<()> {
fs::create_dir_all(local_bin_dir(crate_path))
}
/// Get the path for where `bin` would be if we have a crate-local install for
/// it.
///
/// This does *not* check whether there is a file at that path or not.
///
/// This will automatically add the `.exe` extension for windows.
pub fn local_bin_path(crate_path: &Path, bin: &str) -> PathBuf {
let mut p = local_bin_dir(crate_path).join(bin);
if target::WINDOWS {
p.set_extension("exe");
}
p
}
/// Get the local (at `$CRATE/bin/$BIN`; preferred) or global (on `$PATH`) path
/// for the given binary.
///
/// If this function returns `Some(path)`, then a file at that path exists (or
/// at least existed when we checked! In general, we aren't really worried about
/// racing with an uninstall of a tool that we rely on.)
pub fn bin_path(log: &Logger, crate_path: &Path, bin: &str) -> Option<PathBuf> {
assert!(!bin.ends_with(".exe"));
debug!(log, "Searching for {} binary...", bin);
// Return the path to the local binary, if it exists.
let local_path = |crate_path: &Path| -> Option<PathBuf> {
let p = local_bin_path(crate_path, bin);
debug!(log, "Checking for local {} binary at {}", bin, p.display());
if p.is_file() {
Some(p)
} else {
None
}
};
// Return the path to the global binary, if it exists.
let global_path = || -> Option<PathBuf> {
debug!(log, "Looking for global {} binary on $PATH", bin);
if let Ok(p) = which(bin) {
Some(p)
} else {
None
}
};
local_path(crate_path)
.or_else(global_path)
.map(|p| {
let p = p.canonicalize().unwrap_or(p);
debug!(log, "Using {} binary at {}", bin, p.display());
p
}).or_else(|| {
debug!(log, "Could not find {} binary.", bin);
None
})
}
fn with_url_context<T, E>(url: &str, r: Result<T, E>) -> Result<T, impl failure::Fail>
where
Result<T, E>: failure::ResultExt<T, E>,
{
use failure::ResultExt;
r.with_context(|_| format!("when requesting {}", url))
}
fn transfer(
url: &str,
easy: &mut curl::easy::Easy,
data: &mut Vec<u8>,
) -> Result<(), failure::Error> {
let mut transfer = easy.transfer();
with_url_context(
url,
transfer.write_function(|part| {
data.extend_from_slice(part);
Ok(part.len())
}),
)?;
with_url_context(url, transfer.perform())?;
Ok(())
}
fn curl(url: &str) -> Result<Vec<u8>, failure::Error> {
let mut data = Vec::new();
let mut easy = curl::easy::Easy::new();
with_url_context(url, easy.follow_location(true))?;
with_url_context(url, easy.url(url))?;
transfer(url, &mut easy, &mut data)?;
let status_code = with_url_context(url, easy.response_code())?;
if 200 <= status_code && status_code < 300 {
Ok(data)
} else {
Err(Error::http(&format!(
"received a bad HTTP status code ({}) when requesting {}",
status_code, url
)).into())
}
}
/// Download the `.tar.gz` file at the given URL and unpack the given binaries
/// from it into the given crate.
///
/// Upon success, every `$BIN` in `binaries` will be at `$CRATE/bin/$BIN`.
pub fn install_binaries_from_targz_at_url<'a, I>(
crate_path: &Path,
url: &str,
binaries: I,
) -> Result<(), Error>
where
I: IntoIterator<Item = &'a str>,
{
let mut binaries: HashSet<_> = binaries.into_iter().map(ffi::OsStr::new).collect();
let tarball = curl(&url).map_err(|e| Error::http(&e.to_string()))?;
let mut archive = tar::Archive::new(flate2::read::GzDecoder::new(&tarball[..]));
ensure_local_bin_dir(crate_path)?;
let bin = local_bin_dir(crate_path);
for entry in archive.entries()? {
let mut entry = entry?;
let dest = match entry.path()?.file_stem() {
Some(f) if binaries.contains(f) => {
binaries.remove(f);
bin.join(entry.path()?.file_name().unwrap())
}
_ => continue,
};
entry.unpack(dest)?;
}
if binaries.is_empty() {
Ok(())
} else {
Err(Error::archive(&format!(
"the tarball at {} was missing expected executables: {}",
url,
binaries
.into_iter()
.map(|s| s.to_string_lossy())
.collect::<Vec<_>>()
.join(", "),
)))
}
}
/// Install binaries from within the given zip at the given URL.
///
/// Upon success, the binaries will be at the `$CRATE/bin/$BIN` path.
pub fn install_binaries_from_zip_at_url<'a, I>(
crate_path: &Path,
url: &str,
binaries: I,
) -> Result<(), Error>
where
I: IntoIterator<Item = &'a str>,
{
let mut binaries: HashSet<_> = binaries.into_iter().map(ffi::OsStr::new).collect();
let data = curl(&url).map_err(|e| Error::http(&e.to_string()))?;
let data = io::Cursor::new(data);
let mut zip = zip::ZipArchive::new(data)?;
ensure_local_bin_dir(crate_path)?;
let bin = local_bin_dir(crate_path);
for i in 0..zip.len() {
let mut entry = zip.by_index(i).unwrap();
let entry_path = entry.sanitized_name();
match entry_path.file_stem() {
Some(f) if binaries.contains(f) => {
binaries.remove(f);
let mut dest = bin_open_options()
.write(true)
.create_new(true)
.open(bin.join(entry_path.file_name().unwrap()))?;
io::copy(&mut entry, &mut dest)?;
}
_ => continue,
};
}
if binaries.is_empty() {
Ok(())
} else {
Err(Error::archive(&format!(
"the zip at {} was missing expected executables: {}",
url,
binaries
.into_iter()
.map(|s| s.to_string_lossy())
.collect::<Vec<_>>()
.join(", "),
)))
}
}
#[cfg(unix)]
fn bin_open_options() -> fs::OpenOptions {
use std::os::unix::fs::OpenOptionsExt;
let mut opts = fs::OpenOptions::new();
opts.mode(0o755);
opts
}
#[cfg(not(unix))]
fn bin_open_options() -> fs::OpenOptions {
fs::OpenOptions::new()
}

@ -1,18 +1,13 @@
//! Functionality related to installing and running `wasm-bindgen`.
use curl;
use binaries::{self, bin_path, install_binaries_from_targz_at_url};
use emoji;
use error::Error;
use failure;
use flate2;
use progressbar::Step;
use slog::Logger;
use std::ffi;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use tar;
use which::which;
use target;
use PBAR;
/// Install the `wasm-bindgen` CLI.
@ -29,8 +24,8 @@ pub fn install_wasm_bindgen(
log: &Logger,
) -> Result<(), Error> {
// If the `wasm-bindgen` dependency is already met, print a message and return.
if wasm_bindgen_path(root_path)
.map(|bindgen_path| wasm_bindgen_version_check(&bindgen_path, version))
if wasm_bindgen_path(log, root_path)
.map(|bindgen_path| wasm_bindgen_version_check(&bindgen_path, version, log))
.unwrap_or(false)
{
let msg = format!("{}wasm-bindgen already installed...", emoji::DOWN_ARROW);
@ -57,56 +52,13 @@ pub fn install_wasm_bindgen(
})
}
fn curl(url: &str) -> Result<Vec<u8>, failure::Error> {
let mut data = Vec::new();
fn with_url_context<T, E>(url: &str, r: Result<T, E>) -> Result<T, impl failure::Fail>
where
Result<T, E>: failure::ResultExt<T, E>,
{
use failure::ResultExt;
r.with_context(|_| format!("when requesting {}", url))
}
let mut easy = curl::easy::Easy::new();
with_url_context(url, easy.follow_location(true))?;
with_url_context(url, easy.url(url))?;
{
let mut transfer = easy.transfer();
with_url_context(
url,
transfer.write_function(|part| {
data.extend_from_slice(part);
Ok(part.len())
}),
)?;
with_url_context(url, transfer.perform())?;
}
let code = with_url_context(url, easy.response_code())?;
if 200 <= code && code < 300 {
Ok(data)
} else {
Err(Error::http(&format!(
"received a bad HTTP status code ({}) when requesting {}",
code, url
)).into())
}
}
/// Download a tarball containing a pre-built `wasm-bindgen` binary.
pub fn download_prebuilt_wasm_bindgen(root_path: &Path, version: &str) -> Result<(), Error> {
let linux = cfg!(target_os = "linux");
let macos = cfg!(target_os = "macos");
let windows = cfg!(windows);
let x86_64 = cfg!(target_arch = "x86_64");
let target = if linux && x86_64 {
let target = if target::LINUX && target::x86_64 {
"x86_64-unknown-linux-musl"
} else if macos && x86_64 {
} else if target::MACOS && target::x86_64 {
"x86_64-apple-darwin"
} else if windows && x86_64 {
} else if target::WINDOWS && target::x86_64 {
"x86_64-pc-windows-msvc"
} else {
return Err(Error::unsupported(
@ -120,45 +72,16 @@ pub fn download_prebuilt_wasm_bindgen(root_path: &Path, version: &str) -> Result
target
);
let tarball = curl(&url).map_err(|e| Error::http(&e.to_string()))?;
let mut archive = tar::Archive::new(flate2::read::GzDecoder::new(&tarball[..]));
let bin = root_path.join("bin");
fs::create_dir_all(&bin)?;
let mut found_wasm_bindgen = false;
let mut found_test_runner = false;
for entry in archive.entries()? {
let mut entry = entry?;
let dest = match entry.path()?.file_stem() {
Some(f) if f == ffi::OsStr::new("wasm-bindgen") => {
found_wasm_bindgen = true;
bin.join(entry.path()?.file_name().unwrap())
}
Some(f) if f == ffi::OsStr::new("wasm-bindgen-test-runner") => {
found_test_runner = true;
bin.join(entry.path()?.file_name().unwrap())
}
_ => continue,
};
entry.unpack(dest)?;
}
if found_wasm_bindgen && found_test_runner {
Ok(())
} else {
Err(Error::archive(
"the wasm-bindgen tarball was missing expected executables",
))
}
install_binaries_from_targz_at_url(
root_path,
&url,
vec!["wasm-bindgen", "wasm-bindgen-test-runner"],
)
}
/// Use `cargo install` to install the `wasm-bindgen` CLI to the given root
/// path.
pub fn cargo_install_wasm_bindgen(root_path: &Path, version: &str) -> Result<(), Error> {
/// Use `cargo install` to install the `wasm-bindgen` CLI locally into the given
/// crate.
pub fn cargo_install_wasm_bindgen(crate_path: &Path, version: &str) -> Result<(), Error> {
let output = Command::new("cargo")
.arg("install")
.arg("--force")
@ -166,7 +89,7 @@ pub fn cargo_install_wasm_bindgen(root_path: &Path, version: &str) -> Result<(),
.arg("--version")
.arg(version)
.arg("--root")
.arg(root_path)
.arg(crate_path)
.output()?;
if !output.status.success() {
let message = "Installing wasm-bindgen failed".to_string();
@ -176,11 +99,7 @@ pub fn cargo_install_wasm_bindgen(root_path: &Path, version: &str) -> Result<(),
stderr: s.to_string(),
})
} else {
if cfg!(target_os = "windows") {
assert!(root_path.join("bin").join("wasm-bindgen.exe").is_file());
} else {
assert!(root_path.join("bin").join("wasm-bindgen").is_file());
}
assert!(binaries::local_bin_path(crate_path, "wasm-bindgen").is_file());
Ok(())
}
}
@ -195,6 +114,7 @@ pub fn wasm_bindgen_build(
target: &str,
debug: bool,
step: &Step,
log: &Logger,
) -> Result<(), Error> {
let msg = format!("{}Running WASM-bindgen...", emoji::RUNNER);
PBAR.step(step, &msg);
@ -204,7 +124,7 @@ pub fn wasm_bindgen_build(
let out_dir = out_dir.to_str().unwrap();
if let Some(wasm_bindgen_path) = wasm_bindgen_path(path) {
if let Some(wasm_bindgen_path) = wasm_bindgen_path(log, path) {
let wasm_path = format!(
"target/wasm32-unknown-unknown/{}/{}.wasm",
release_or_debug, binary_name
@ -239,7 +159,7 @@ pub fn wasm_bindgen_build(
}
/// Check if the `wasm-bindgen` dependency is locally satisfied.
fn wasm_bindgen_version_check(bindgen_path: &PathBuf, dep_version: &str) -> bool {
fn wasm_bindgen_version_check(bindgen_path: &PathBuf, dep_version: &str, log: &Logger) -> bool {
Command::new(bindgen_path)
.arg("--version")
.output()
@ -250,38 +170,27 @@ fn wasm_bindgen_version_check(bindgen_path: &PathBuf, dep_version: &str) -> bool
.trim()
.split_whitespace()
.nth(1)
.map(|v| v == dep_version)
.unwrap_or(false)
.map(|v| {
info!(
log,
"Checking installed `wasm-bindgen` version == expected version: {} == {}",
v,
dep_version
);
v == dep_version
}).unwrap_or(false)
}).unwrap_or(false)
}
/// Return a `PathBuf` containing the path to either the local wasm-bindgen
/// version, or the globally installed version if there is no local version.
fn wasm_bindgen_path(crate_path: &Path) -> Option<PathBuf> {
// Return the path to the local `wasm-bindgen`, if it exists.
let local_bindgen_path = |crate_path: &Path| -> Option<PathBuf> {
let mut p = crate_path.to_path_buf();
p.push("bin");
if cfg!(target_os = "windows") {
p.push("wasm-bindgen.exe");
} else {
p.push("wasm-bindgen");
}
if p.is_file() {
Some(p)
} else {
None
}
};
// Return the path to the global `wasm-bindgen`, if it exists.
let global_bindgen_path = || -> Option<PathBuf> {
if let Ok(p) = which("wasm-bindgen") {
Some(p)
} else {
None
}
};
fn wasm_bindgen_path(log: &Logger, crate_path: &Path) -> Option<PathBuf> {
bin_path(log, crate_path, "wasm-bindgen")
}
local_bindgen_path(crate_path).or_else(global_bindgen_path)
/// Return a `PathBuf` containing the path to either the local
/// wasm-bindgen-test-runner version, or the globally installed version if there
/// is no local version.
pub fn wasm_bindgen_test_runner_path(log: &Logger, crate_path: &Path) -> Option<PathBuf> {
bin_path(log, crate_path, "wasm-bindgen-test-runner")
}

@ -67,3 +67,27 @@ pub fn cargo_build_wasm(path: &Path, debug: bool, step: &Step) -> Result<(), Err
Ok(())
}
}
/// Run `cargo build --tests` with the `nightly` toolchain and targetting
/// `wasm32-unknown-unknown`.
pub fn cargo_build_wasm_tests(path: &Path, debug: bool) -> Result<(), Error> {
let output = {
let mut cmd = Command::new("cargo");
cmd.current_dir(path)
.arg("+nightly")
.arg("build")
.arg("--tests");
if !debug {
cmd.arg("--release");
}
cmd.arg("--target").arg("wasm32-unknown-unknown");
cmd.output()?
};
if !output.status.success() {
let s = String::from_utf8_lossy(&output.stderr);
Error::cli("Compilation of your program failed", s)
} else {
Ok(())
}
}

@ -1,3 +1,5 @@
//! Implementation of the `wasm-pack build` command.
use bindgen;
use build;
use command::utils::{create_pkg_dir, set_crate_path};
@ -9,6 +11,7 @@ use progressbar::Step;
use readme;
use slog::Logger;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Instant;
use PBAR;
@ -27,6 +30,7 @@ pub(crate) struct Build {
/// The `BuildMode` determines which mode of initialization we are running, and
/// what build and install steps we perform.
#[derive(Clone, Copy, Debug)]
pub enum BuildMode {
/// Perform all the build and install steps.
Normal,
@ -35,6 +39,23 @@ pub enum BuildMode {
Noinstall,
}
impl Default for BuildMode {
fn default() -> BuildMode {
BuildMode::Normal
}
}
impl FromStr for BuildMode {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> {
match s {
"no-install" => Ok(BuildMode::Noinstall),
"normal" => Ok(BuildMode::Normal),
_ => Error::crate_config(&format!("Unknown build mode: {}", s)).map(|_| unreachable!()),
}
}
}
/// Everything required to configure and run the `wasm-pack build` command.
#[derive(Debug, StructOpt)]
pub struct BuildOptions {
@ -48,7 +69,7 @@ pub struct BuildOptions {
#[structopt(long = "mode", short = "m", default_value = "normal")]
/// Sets steps to be run. [possible values: no-install, normal]
pub mode: String,
pub mode: BuildMode,
#[structopt(long = "no-typescript")]
/// By default a *.d.ts file is generated for the generated JS file, but
@ -74,13 +95,9 @@ type BuildStep = fn(&mut Build, &Step, &Logger) -> Result<(), Error>;
impl Build {
/// Construct a build command from the given options.
pub fn try_from_opts(build_opts: BuildOptions) -> Result<Self, Error> {
let crate_path = set_crate_path(build_opts.path);
let crate_path = set_crate_path(build_opts.path)?;
let crate_name = manifest::get_crate_name(&crate_path)?;
let out_dir = crate_path.join(PathBuf::from(build_opts.out_dir));
let mode = match build_opts.mode.as_str() {
"no-install" => BuildMode::Noinstall,
_ => BuildMode::Normal,
};
// let build_config = manifest::xxx(&crate_path).xxx();
Ok(Build {
crate_path,
@ -88,7 +105,7 @@ impl Build {
disable_dts: build_opts.disable_dts,
target: build_opts.target,
debug: build_opts.debug,
mode,
mode: build_opts.mode,
// build_config,
crate_name,
out_dir,
@ -258,6 +275,7 @@ impl Build {
&self.target,
self.debug,
step,
log,
)?;
info!(&log, "wasm bindings were built at {:#?}.", &self.out_dir);
Ok(())

@ -1,15 +1,17 @@
//! CLI command structures, parsing, and execution.
mod build;
pub mod build;
mod login;
mod pack;
mod publish;
pub mod test;
pub mod utils;
use self::build::{Build, BuildOptions};
use self::login::login;
use self::pack::pack;
use self::publish::publish;
use self::test::{Test, TestOptions};
use error::Error;
use slog::Logger;
use std::path::PathBuf;
@ -70,6 +72,10 @@ pub enum Command {
/// strategies besides classic username/password entry in legacy npm.
auth_type: Option<String>,
},
#[structopt(name = "test")]
/// 👩🔬 test your wasm!
Test(TestOptions),
}
/// Run a command with the given logger!
@ -108,6 +114,10 @@ pub fn run_wasm_pack(command: Command, log: &Logger) -> result::Result<(), Error
);
login(registry, scope, always_auth, auth_type, &log)
}
Command::Test(test_opts) => {
info!(&log, "Running test command...");
Test::try_from_opts(test_opts).and_then(|t| t.run(&log))
}
};
match status {

@ -9,7 +9,7 @@ use PBAR;
/// Executes the 'npm pack' command on the 'pkg' directory
/// which creates a tarball that can be published to the NPM registry
pub fn pack(path: Option<PathBuf>, log: &Logger) -> result::Result<(), Error> {
let crate_path = set_crate_path(path);
let crate_path = set_crate_path(path)?;
info!(&log, "Packing up the npm package...");
let pkg_directory = find_pkg_directory(&crate_path).ok_or(Error::PkgNotFound {

@ -9,7 +9,7 @@ use PBAR;
/// Creates a tarball from a 'pkg' directory
/// and publishes it to the NPM registry
pub fn publish(path: Option<PathBuf>, log: &Logger) -> result::Result<(), Error> {
let crate_path = set_crate_path(path);
let crate_path = set_crate_path(path)?;
info!(&log, "Publishing the npm package...");
info!(&log, "npm info located in the npm debug log");

@ -0,0 +1,402 @@
//! Implementation of the `wasm-pack test` command.
use super::build::BuildMode;
use bindgen;
use build;
use command::utils::set_crate_path;
use emoji;
use error::Error;
use indicatif::HumanDuration;
use manifest;
use progressbar::Step;
use slog::Logger;
use std::path::PathBuf;
use std::time::Instant;
use test::{self, webdriver};
use PBAR;
#[derive(Debug, Default, StructOpt)]
/// Everything required to configure the `wasm-pack test` command.
pub struct TestOptions {
#[structopt(parse(from_os_str))]
/// The path to the Rust crate.
pub path: Option<PathBuf>,
#[structopt(long = "node")]
/// Run the tests in Node.js.
pub node: bool,
#[structopt(long = "firefox")]
/// Run the tests in Firefox. This machine must have a Firefox installation.
/// If the `geckodriver` WebDriver client is not on the `$PATH`, and not
/// specified with `--geckodriver`, then `wasm-pack` will download a local
/// copy.
pub firefox: bool,
#[structopt(long = "geckodriver", parse(from_os_str))]
/// The path to the `geckodriver` WebDriver client for testing in
/// Firefox. Implies `--firefox`.
pub geckodriver: Option<PathBuf>,
#[structopt(long = "chrome")]
/// Run the tests in Chrome. This machine must have a Chrome installation.
/// If the `chromedriver` WebDriver client is not on the `$PATH`, and not
/// specified with `--chromedriver`, then `wasm-pack` will download a local
/// copy.
pub chrome: bool,
#[structopt(long = "chromedriver", parse(from_os_str))]
/// The path to the `chromedriver` WebDriver client for testing in
/// Chrome. Implies `--chrome`.
pub chromedriver: Option<PathBuf>,
#[structopt(long = "safari")]
/// Run the tests in Safari. This machine must have a Safari installation,
/// and the `safaridriver` WebDriver client must either be on the `$PATH` or
/// specified explicitly with the `--safaridriver` flag. `wasm-pack` cannot
/// download the `safaridriver` WebDriver client for you.
pub safari: bool,
#[structopt(long = "safaridriver", parse(from_os_str))]
/// The path to the `safaridriver` WebDriver client for testing in
/// Safari. Implies `--safari`.
pub safaridriver: Option<PathBuf>,
#[structopt(long = "headless")]
/// When running browser tests, run the browser in headless mode without any
/// UI or windows.
pub headless: bool,
#[structopt(long = "mode", short = "m", default_value = "normal")]
/// Sets steps to be run. [possible values: no-install, normal]
pub mode: BuildMode,
#[structopt(long = "release", short = "r")]
/// Build with the release profile.
pub release: bool,
}
/// A configured `wasm-pack test` command.
pub struct Test {
crate_path: PathBuf,
node: bool,
mode: BuildMode,
firefox: bool,
geckodriver: Option<PathBuf>,
chrome: bool,
chromedriver: Option<PathBuf>,
safari: bool,
safaridriver: Option<PathBuf>,
headless: bool,
release: bool,
test_runner_path: Option<PathBuf>,
}
type TestStep = fn(&mut Test, &Step, &Logger) -> Result<(), Error>;
impl Test {
/// Construct a test command from the given options.
pub fn try_from_opts(test_opts: TestOptions) -> Result<Self, Error> {
let TestOptions {
path,
node,
mode,
headless,
release,
chrome,
chromedriver,
firefox,
geckodriver,
safari,
safaridriver,
} = test_opts;
let crate_path = set_crate_path(path)?;
// let geckodriver = get_web_driver("geckodriver", test_opts.geckodriver, test_opts.firefox)?;
// let chromedriver =
// get_web_driver("chromedriver", test_opts.chromedriver, test_opts.chrome)?;
// let safaridriver =
// get_web_driver("safaridriver", test_opts.safaridriver, test_opts.safari)?;
let any_browser = chrome || firefox || safari;
if !node && !any_browser {
return Error::crate_config(
"Must specify at least one of `--node`, `--chrome`, `--firefox`, or `--safari`",
).map(|_| unreachable!());
}
if headless && !any_browser {
return Error::crate_config(
"The `--headless` flag only applies to browser tests. Node does not provide a UI, \
so it doesn't make sense to talk about a headless version of Node tests.",
).map(|_| unreachable!());
}
Ok(Test {
crate_path,
node,
mode,
chrome,
chromedriver,
firefox,
geckodriver,
safari,
safaridriver,
headless,
release,
test_runner_path: None,
})
}
/// Execute this test command.
pub fn run(mut self, log: &Logger) -> Result<(), Error> {
let process_steps = self.get_process_steps();
let mut step_counter = Step::new(process_steps.len());
let started = Instant::now();
for (_, process_step) in process_steps {
process_step(&mut self, &step_counter, log)?;
step_counter.inc();
}
let duration = HumanDuration(started.elapsed());
info!(&log, "Done in {}.", &duration);
Ok(())
}
fn get_process_steps(&self) -> Vec<(&'static str, TestStep)> {
macro_rules! steps {
($($name:ident $(if $e:expr)* ),+) => {
{
let mut steps: Vec<(&'static str, TestStep)> = Vec::new();
$(
$(if $e)* {
steps.push((stringify!($name), Test::$name));
}
)*
steps
}
};
($($name:ident $(if $e:expr)* ,)*) => (steps![$($name $(if $e)* ),*])
}
match self.mode {
BuildMode::Normal => steps![
step_check_crate_config,
step_add_wasm_target,
step_build_tests,
step_install_wasm_bindgen,
step_test_node if self.node,
step_get_chromedriver if self.chrome && self.chromedriver.is_none(),
step_test_chrome if self.chrome,
step_get_geckodriver if self.firefox && self.geckodriver.is_none(),
step_test_firefox if self.firefox,
step_get_safaridriver if self.safari && self.safaridriver.is_none(),
step_test_safari if self.safari,
],
BuildMode::Noinstall => steps![
step_check_crate_config,
step_build_tests,
step_install_wasm_bindgen,
step_test_node if self.node,
step_get_chromedriver if self.chrome && self.chromedriver.is_none(),
step_test_chrome if self.chrome,
step_get_geckodriver if self.firefox && self.geckodriver.is_none(),
step_test_firefox if self.firefox,
step_get_safaridriver if self.safari && self.safaridriver.is_none(),
step_test_safari if self.safari,
],
}
}
fn step_check_crate_config(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(log, "Checking crate configuration...");
manifest::check_crate_config(&self.crate_path, step)?;
info!(log, "Crate is correctly configured.");
Ok(())
}
fn step_add_wasm_target(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Adding wasm-target...");
build::rustup_add_wasm_target(step)?;
info!(&log, "Adding wasm-target was successful.");
Ok(())
}
fn step_build_tests(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(log, "Compiling tests to wasm...");
let msg = format!("{}Compiling tests to WASM...", emoji::CYCLONE);
PBAR.step(step, &msg);
build::cargo_build_wasm_tests(&self.crate_path, !self.release)?;
info!(log, "Finished compiling tests to wasm.");
Ok(())
}
fn step_install_wasm_bindgen(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
info!(&log, "Identifying wasm-bindgen dependency...");
let bindgen_version = manifest::get_wasm_bindgen_version(&self.crate_path)?;
info!(&log, "Installing wasm-bindgen-cli...");
let install_permitted = match self.mode {
BuildMode::Normal => true,
BuildMode::Noinstall => false,
};
bindgen::install_wasm_bindgen(
&self.crate_path,
&bindgen_version,
install_permitted,
step,
log,
)?;
self.test_runner_path = Some(bindgen::wasm_bindgen_test_runner_path(log, &self.crate_path)
.expect("if installing wasm-bindgen succeeded, then we should have wasm-bindgen-test-runner too"));
info!(&log, "Installing wasm-bindgen-cli was successful.");
Ok(())
}
fn step_test_node(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
assert!(self.node);
info!(log, "Running tests in node...");
PBAR.step(step, "Running tests in node...");
test::cargo_test_wasm(
&self.crate_path,
self.release,
log,
Some((
"CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER",
&self.test_runner_path.as_ref().unwrap(),
)),
)?;
info!(log, "Finished running tests in node.");
Ok(())
}
fn step_get_chromedriver(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Getting chromedriver...");
assert!(self.chrome && self.chromedriver.is_none());
self.chromedriver = Some(webdriver::get_or_install_chromedriver(
log,
&self.crate_path,
self.mode,
)?);
Ok(())
}
fn step_test_chrome(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Running tests in Chrome...");
let chromedriver = self.chromedriver.as_ref().unwrap().display().to_string();
let chromedriver = chromedriver.as_str();
info!(
log,
"Running tests in Chrome with chromedriver at {}", chromedriver
);
let test_runner = self
.test_runner_path
.as_ref()
.unwrap()
.display()
.to_string();
let test_runner = test_runner.as_str();
info!(log, "Using wasm-bindgen test runner at {}", test_runner);
let mut envs = vec![
("CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER", test_runner),
("CHROMEDRIVER", chromedriver),
];
if !self.headless {
envs.push(("NO_HEADLESS", "1"));
}
test::cargo_test_wasm(&self.crate_path, self.release, log, envs)
}
fn step_get_geckodriver(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Getting geckodriver...");
assert!(self.firefox && self.geckodriver.is_none());
self.geckodriver = Some(webdriver::get_or_install_geckodriver(
log,
&self.crate_path,
self.mode,
)?);
Ok(())
}
fn step_test_firefox(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Running tests in Firefox...");
let geckodriver = self.geckodriver.as_ref().unwrap().display().to_string();
let geckodriver = geckodriver.as_str();
info!(
log,
"Running tests in Firefox with geckodriver at {}", geckodriver
);
let test_runner = self
.test_runner_path
.as_ref()
.unwrap()
.display()
.to_string();
let test_runner = test_runner.as_str();
info!(log, "Using wasm-bindgen test runner at {}", test_runner);
let mut envs = vec![
("CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER", test_runner),
("GECKODRIVER", geckodriver),
];
if !self.headless {
envs.push(("NO_HEADLESS", "1"));
}
test::cargo_test_wasm(&self.crate_path, self.release, log, envs)
}
fn step_get_safaridriver(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Getting safaridriver...");
assert!(self.safari && self.safaridriver.is_none());
self.safaridriver = Some(webdriver::get_safaridriver(log, &self.crate_path)?);
Ok(())
}
fn step_test_safari(&mut self, step: &Step, log: &Logger) -> Result<(), Error> {
PBAR.step(step, "Running tests in Safari...");
let safaridriver = self.safaridriver.as_ref().unwrap().display().to_string();
let safaridriver = safaridriver.as_str();
info!(
log,
"Running tests in Safari with safaridriver at {}", safaridriver
);
let test_runner = self
.test_runner_path
.as_ref()
.unwrap()
.display()
.to_string();
let test_runner = test_runner.as_str();
info!(log, "Using wasm-bindgen test runner at {}", test_runner);
let mut envs = vec![
("CARGO_TARGET_WASM32_UNKNOWN_UNKNOWN_RUNNER", test_runner),
("SAFARIDRIVER", safaridriver),
];
if !self.headless {
envs.push(("NO_HEADLESS", "1"));
}
test::cargo_test_wasm(&self.crate_path, self.release, log, envs)
}
}

@ -4,18 +4,19 @@ use emoji;
use error::Error;
use progressbar::Step;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use PBAR;
/// If an explicit path is given, then use it, otherwise assume the current
/// directory is the crate path.
pub fn set_crate_path(path: Option<PathBuf>) -> PathBuf {
pub fn set_crate_path(path: Option<PathBuf>) -> io::Result<PathBuf> {
let crate_path = match path {
Some(p) => p,
None => PathBuf::from("."),
};
crate_path
crate_path.canonicalize()
}
/// Construct our `pkg` directory in the crate.

@ -4,6 +4,7 @@ use serde_json;
use std::borrow::Cow;
use std::io;
use toml;
use zip;
/// Errors that can potentially occur in `wasm-pack`.
#[derive(Debug, Fail)]
@ -24,6 +25,10 @@ pub enum Error {
/// A curl error.
Curl(#[cause] curl::Error),
#[fail(display = "{}", _0)]
/// An error handling zip archives.
Zip(#[cause] zip::result::ZipError),
/// An error invoking another CLI tool.
#[fail(display = "{}. stderr:\n\n{}", message, stderr)]
Cli {
@ -113,6 +118,7 @@ impl Error {
Error::Io(_) => "There was an I/O error. Details:\n\n",
Error::SerdeJson(_) => "There was an JSON error. Details:\n\n",
Error::SerdeToml(_) => "There was an TOML error. Details:\n\n",
Error::Zip(_) => "There was an error handling zip files. Details:\n\n",
Error::Cli {
message: _,
stderr: _,
@ -149,6 +155,12 @@ impl From<serde_json::Error> for Error {
}
}
impl From<zip::result::ZipError> for Error {
fn from(e: zip::result::ZipError) -> Self {
Error::Zip(e)
}
}
impl From<toml::de::Error> for Error {
fn from(e: toml::de::Error) -> Self {
Error::SerdeToml(e)

@ -23,7 +23,9 @@ extern crate slog_term;
extern crate tar;
extern crate toml;
extern crate which;
extern crate zip;
pub mod binaries;
pub mod bindgen;
pub mod build;
pub mod command;
@ -34,6 +36,8 @@ pub mod manifest;
pub mod npm;
pub mod progressbar;
pub mod readme;
pub mod target;
pub mod test;
use progressbar::ProgressOutput;

@ -39,6 +39,7 @@ fn log_file_path(cmd: &Command) -> PathBuf {
Command::Build(build_opts) => &build_opts.path,
Command::Pack { path } => path,
Command::Publish { path } => path,
Command::Test(test_opts) => &test_opts.path,
Command::Login { .. } => &None,
};

@ -13,14 +13,41 @@ use serde_json;
use toml;
use PBAR;
#[derive(Deserialize)]
#[derive(Debug, Deserialize)]
struct CargoManifest {
package: CargoPackage,
dependencies: Option<HashMap<String, CargoDependency>>,
#[serde(rename = "dev-dependencies")]
dev_dependencies: Option<HashMap<String, CargoDependency>>,
lib: Option<CargoLib>,
}
#[derive(Deserialize)]
fn normalize_dependency_name(dep: &str) -> String {
dep.replace("-", "_")
}
fn normalize_dependencies(
deps: HashMap<String, CargoDependency>,
) -> HashMap<String, CargoDependency> {
let mut new_deps = HashMap::with_capacity(deps.len());
for (key, val) in deps {
new_deps.insert(normalize_dependency_name(&key), val);
}
new_deps
}
impl CargoManifest {
fn normalize_dependencies(&mut self) {
if let Some(deps) = self.dependencies.take() {
self.dependencies = Some(normalize_dependencies(deps));
}
if let Some(dev_deps) = self.dev_dependencies.take() {
self.dev_dependencies = Some(normalize_dependencies(dev_deps));
}
}
}
#[derive(Debug, Deserialize)]
struct CargoPackage {
name: String,
authors: Vec<String>,
@ -30,19 +57,19 @@ struct CargoPackage {
repository: Option<String>,
}
#[derive(Deserialize)]
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum CargoDependency {
Simple(String),
Detailed(DetailedCargoDependency),
}
#[derive(Deserialize)]
#[derive(Debug, Deserialize)]
struct DetailedCargoDependency {
version: Option<String>,
}
#[derive(Deserialize)]
#[derive(Debug, Deserialize)]
struct CargoLib {
#[serde(rename = "crate-type")]
crate_type: Option<Vec<String>>,
@ -86,7 +113,10 @@ fn read_cargo_toml(path: &Path) -> Result<CargoManifest, Error> {
let mut cargo_contents = String::new();
cargo_file.read_to_string(&mut cargo_contents)?;
Ok(toml::from_str(&cargo_contents)?)
let mut manifest: CargoManifest = toml::from_str(&cargo_contents)?;
manifest.normalize_dependencies();
Ok(manifest)
}
impl CargoManifest {
@ -192,6 +222,7 @@ pub fn check_crate_config(path: &Path, step: &Step) -> Result<(), Error> {
let msg = format!("{}Checking crate configuration...", emoji::WRENCH);
PBAR.step(&step, &msg);
check_wasm_bindgen(path)?;
check_wasm_bindgen_test(path)?;
check_crate_type(path)?;
Ok(())
}
@ -201,6 +232,24 @@ fn check_wasm_bindgen(path: &Path) -> Result<(), Error> {
Ok(())
}
fn check_wasm_bindgen_test(path: &Path) -> Result<(), Error> {
let expected_version = get_wasm_bindgen_version(path)?;
// Only do the version check if `wasm-bindgen-test` is actually a
// dependency. Not every crate needs to have tests!
if let Ok(actual_version) = get_wasm_bindgen_test_version(path) {
if expected_version != actual_version {
return Error::crate_config(&format!(
"The `wasm-bindgen-test` dependency version ({}) must match \
the `wasm-bindgen` dependency version ({}), but it does not.",
actual_version, expected_version
));
}
}
Ok(())
}
fn check_crate_type(path: &Path) -> Result<(), Error> {
if read_cargo_toml(path)?.lib.map_or(false, |lib| {
lib.crate_type
@ -209,17 +258,22 @@ fn check_crate_type(path: &Path) -> Result<(), Error> {
return Ok(());
}
Error::crate_config(
"crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your Cargo.toml file:\n\n[lib]\ncrate-type = [\"cdylib\"]"
"crate-type must be cdylib to compile to wasm32-unknown-unknown. Add the following to your \
Cargo.toml file:\n\n\
[lib]\n\
crate-type = [\"cdylib\"]"
)
}
/// Get the version of `wasm-bindgen` specified as a dependency.
pub fn get_wasm_bindgen_version(path: &Path) -> Result<String, Error> {
if let Some(deps) = read_cargo_toml(path)?.dependencies {
match deps
.get("wasm-bindgen")
.or_else(|| deps.get("wasm_bindgen"))
{
fn get_dependency_version(
dependencies: Option<&HashMap<String, CargoDependency>>,
dependency: &str,
dependencies_section_name: &str,
version_suggestion: &str,
) -> Result<String, Error> {
if let Some(deps) = dependencies {
let dependency = normalize_dependency_name(dependency);
match deps.get(&dependency) {
Some(CargoDependency::Simple(version))
| Some(CargoDependency::Detailed(DetailedCargoDependency {
version: Some(version),
@ -227,14 +281,20 @@ pub fn get_wasm_bindgen_version(path: &Path) -> Result<String, Error> {
Some(CargoDependency::Detailed(DetailedCargoDependency { version: None })) => {
let msg = format!(
"\"{}\" dependency is missing its version number",
style("wasm-bindgen").bold().dim()
style(&dependency).bold().dim()
);
Err(Error::CrateConfig { message: msg })
}
None => {
let message = format!(
"Ensure that you have \"{}\" as a dependency in your Cargo.toml file:\n[dependencies]\nwasm-bindgen = \"0.2\"",
style("wasm-bindgen").bold().dim());
"Ensure that you have \"{}\" as a dependency in your Cargo.toml file:\n\
[{}]\n\
{} = \"{}\"",
style(&dependency).bold().dim(),
dependencies_section_name,
dependency,
version_suggestion
);
Err(Error::CrateConfig { message })
}
}
@ -243,3 +303,25 @@ pub fn get_wasm_bindgen_version(path: &Path) -> Result<String, Error> {
Err(Error::CrateConfig { message })
}
}
/// Get the version of `wasm-bindgen` specified as a dependency.
pub fn get_wasm_bindgen_version(path: &Path) -> Result<String, Error> {
let toml = read_cargo_toml(path)?;
get_dependency_version(
toml.dependencies.as_ref(),
"wasm-bindgen",
"dependencies",
"0.2",
)
}
/// Get the version of `wasm-bindgen-test` specified as a dependency.
pub fn get_wasm_bindgen_test_version(path: &Path) -> Result<String, Error> {
let toml = read_cargo_toml(path)?;
get_dependency_version(
toml.dev_dependencies.as_ref(),
"wasm-bindgen-test",
"dev-dependencies",
"0.2",
)
}

@ -0,0 +1,15 @@
//! Information about the target wasm-pack is currently being compiled for.
//!
//! That is, whether we are building wasm-pack for windows vs linux, and x86 vs
//! x86-64, etc.
#![allow(missing_docs)]
pub const LINUX: bool = cfg!(target_os = "linux");
pub const MACOS: bool = cfg!(target_os = "macos");
pub const WINDOWS: bool = cfg!(target_os = "windows");
#[allow(non_upper_case_globals)]
pub const x86_64: bool = cfg!(target_arch = "x86_64");
#[allow(non_upper_case_globals)]
pub const x86: bool = cfg!(target_arch = "x86");

@ -0,0 +1,51 @@
//! Testing a Rust crate compiled to wasm.
pub mod webdriver;
use error::Error;
use slog::Logger;
use std::ffi::OsStr;
use std::path::Path;
use std::process::Command;
/// Run `cargo test` with the `nightly` toolchain and targeting
/// `wasm32-unknown-unknown`.
pub fn cargo_test_wasm<I, K, V>(
path: &Path,
release: bool,
log: &Logger,
envs: I,
) -> Result<(), Error>
where
I: IntoIterator<Item = (K, V)>,
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
use std::sync::Mutex;
lazy_static! {
static ref ONE_TEST_AT_A_TIME: Mutex<()> = Mutex::new(());
}
let _locked = ONE_TEST_AT_A_TIME.lock().unwrap();
let output = {
let mut cmd = Command::new("cargo");
cmd.envs(envs);
cmd.current_dir(path).arg("+nightly").arg("test");
if release {
cmd.arg("--release");
}
cmd.arg("--target").arg("wasm32-unknown-unknown");
cmd.output()?
};
if !output.status.success() {
let s = String::from_utf8_lossy(&output.stderr);
Error::cli("Running wasm tests failed", s)
} else {
for line in String::from_utf8_lossy(&output.stdout).lines() {
info!(log, "test output: {}", line);
println!("{}", line);
}
Ok(())
}
}

@ -0,0 +1,134 @@
//! Getting WebDriver client binaries.
use binaries::{
self, bin_path, install_binaries_from_targz_at_url, install_binaries_from_zip_at_url,
};
use command::build::BuildMode;
use error::Error;
use slog::Logger;
use std::path::{Path, PathBuf};
use target;
/// Get the path to an existing `chromedriver`, or install it if no existing
/// binary is found.
pub fn get_or_install_chromedriver(
log: &Logger,
crate_path: &Path,
mode: BuildMode,
) -> Result<PathBuf, Error> {
match (mode, bin_path(log, crate_path, "chromedriver")) {
(_, Some(path)) => Ok(path),
(BuildMode::Normal, None) => install_chromedriver(crate_path),
(BuildMode::Noinstall, None) => Error::crate_config(
"No crate-local `chromedriver` binary found, and could not find a global \
`chromedriver` on the `$PATH`. Not installing `chromedriver` because of noinstall \
mode.",
).map(|_| unreachable!()),
}
}
fn get_local_chromedriver_path(crate_path: &Path) -> PathBuf {
binaries::local_bin_path(crate_path, "chromedriver")
}
fn get_chromedriver_url() -> Result<String, Error> {
let target = if target::LINUX && target::x86_64 {
"linux64"
} else if target::MACOS && target::x86_64 {
"mac64"
} else if target::WINDOWS && target::x86 {
"win32"
} else {
return Err(Error::unsupported(
"geckodriver binaries are unavailable for this target",
));
};
Ok(format!(
"https://chromedriver.storage.googleapis.com/2.41/chromedriver_{}.zip",
target
))
}
/// Download and install a pre-built `chromedriver` binary.
pub fn install_chromedriver(crate_path: &Path) -> Result<PathBuf, Error> {
let url = get_chromedriver_url()?;
install_binaries_from_zip_at_url(crate_path, &url, Some("chromedriver"))?;
let chromedriver = get_local_chromedriver_path(crate_path);
assert!(chromedriver.is_file());
Ok(chromedriver)
}
/// Get the path to an existing `geckodriver`, or install it if no existing
/// binary is found.
pub fn get_or_install_geckodriver(
log: &Logger,
crate_path: &Path,
mode: BuildMode,
) -> Result<PathBuf, Error> {
match (mode, bin_path(log, crate_path, "geckodriver")) {
(_, Some(path)) => Ok(path),
(BuildMode::Normal, None) => install_geckodriver(crate_path),
(BuildMode::Noinstall, None) => Error::crate_config(
"No crate-local `geckodriver` binary found, and could not find a global `geckodriver` \
on the `$PATH`. Not installing `geckodriver` because of noinstall mode.",
).map(|_| unreachable!()),
}
}
fn get_geckodriver_url() -> Result<String, Error> {
let (target, ext) = if target::LINUX && target::x86 {
("linux32", "tar.gz")
} else if target::LINUX && target::x86_64 {
("linux64", "tar.gz")
} else if target::MACOS {
("macos", "tar.gz")
} else if target::WINDOWS && target::x86 {
("win32", "zip")
} else if target::WINDOWS && target::x86_64 {
("win64", "zip")
} else {
return Err(Error::unsupported(
"geckodriver binaries are unavailable for this target",
));
};
Ok(format!(
"https://github.com/mozilla/geckodriver/releases/download/v0.21.0/geckodriver-v0.21.0-{}.{}",
target,
ext,
))
}
fn get_local_geckodriver_path(crate_path: &Path) -> PathBuf {
binaries::local_bin_path(crate_path, "geckodriver")
}
/// Download and install a pre-built `geckodriver` binary.
pub fn install_geckodriver(crate_path: &Path) -> Result<PathBuf, Error> {
let url = get_geckodriver_url()?;
if url.ends_with("tar.gz") {
install_binaries_from_targz_at_url(crate_path, &url, Some("geckodriver"))?;
} else {
assert!(url.ends_with("zip"));
install_binaries_from_zip_at_url(crate_path, &url, Some("geckodriver"))?;
}
let geckodriver = get_local_geckodriver_path(crate_path);
assert!(geckodriver.is_file());
Ok(geckodriver)
}
/// Get the path to an existing `safaridriver`.
///
/// We can't install `safaridriver` if an existing one is not found because
/// Apple does not provide pre-built binaries. However, `safaridriver` *should*
/// be present by default.
pub fn get_safaridriver(log: &Logger, crate_path: &Path) -> Result<PathBuf, Error> {
if let Some(p) = bin_path(log, crate_path, "safaridriver") {
Ok(p)
} else {
Error::crate_config("could not find `safaridriver` on the `$PATH`").map(|_| unreachable!())
}
}

@ -1,16 +1,18 @@
extern crate copy_dir;
extern crate failure;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate structopt;
extern crate tempfile;
extern crate wasm_pack;
#[macro_use]
extern crate lazy_static;
mod bindgen;
mod build;
mod manifest;
mod readme;
mod test;
mod utils;
mod webdriver;

@ -250,3 +250,16 @@ fn it_gets_wasm_bindgen_version_with_underscores() {
"0.2"
);
}
#[test]
fn the_wasm_bindgen_test_version_should_match_the_wasm_bindgen_version() {
let fixture = fixture::fixture("tests/fixtures/wbg-test-bad-versions");
let step = wasm_pack::progressbar::Step::new(1);
let result = manifest::check_crate_config(&fixture.path, &step);
assert!(result.is_err());
let msg = result.unwrap_err().to_string();
assert!(msg.contains(&format!(
"The `wasm-bindgen-test` dependency version (0.2.19) must match \
the `wasm-bindgen` dependency version (0.2.21), but it does not."
)));
}

@ -0,0 +1,164 @@
use std::env;
use std::fs;
use tempfile;
use utils::fixture::fixture;
use wasm_pack::binaries;
use wasm_pack::command::{self, build, test, Command};
use wasm_pack::logger;
#[test]
fn it_can_run_node_tests() {
let fixture = fixture("tests/fixtures/wbg-test-node");
fixture.install_local_wasm_bindgen();
let cmd = Command::Test(test::TestOptions {
path: Some(fixture.path.clone()),
node: true,
mode: build::BuildMode::Noinstall,
..Default::default()
});
let logger = logger::new(&cmd, 3).unwrap();
command::run_wasm_pack(cmd, &logger).expect("should run test command OK");
}
#[test]
#[cfg(any(
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "x86"),
all(target_os = "windows", target_arch = "x86_64")
))]
fn it_can_run_browser_tests() {
let fixture = fixture("tests/fixtures/wbg-test-browser");
fixture.install_local_wasm_bindgen();
let firefox = cfg!(any(
all(target_os = "linux", target_arch = "x86"),
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "x86"),
all(target_os = "windows", target_arch = "x86_64")
));
if firefox {
fixture.install_local_geckodriver();
}
let chrome = cfg!(any(
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "x86")
));
if chrome {
fixture.install_local_chromedriver();
}
let safari = cfg!(target_os = "macos");
if !firefox && !chrome && !safari {
return;
}
let cmd = Command::Test(test::TestOptions {
path: Some(fixture.path.clone()),
firefox,
chrome,
safari,
headless: true,
mode: build::BuildMode::Noinstall,
..Default::default()
});
let logger = logger::new(&cmd, 3).unwrap();
command::run_wasm_pack(cmd, &logger).expect("should run test command OK");
}
#[test]
fn it_can_run_failing_tests() {
let fixture = fixture("tests/fixtures/wbg-test-fail");
fixture.install_local_wasm_bindgen();
let cmd = Command::Test(test::TestOptions {
path: Some(fixture.path.clone()),
node: true,
mode: build::BuildMode::Noinstall,
..Default::default()
});
let logger = logger::new(&cmd, 3).unwrap();
assert!(
command::run_wasm_pack(cmd, &logger).is_err(),
"failing tests should return Err"
);
}
#[test]
#[cfg(any(
all(target_os = "linux", target_arch = "x86"),
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "x86"),
all(target_os = "windows", target_arch = "x86_64")
))]
fn it_can_find_a_webdriver_on_path() {
let fixture = fixture("tests/fixtures/wbg-test-browser");
fixture.install_local_wasm_bindgen();
fixture.install_local_geckodriver();
let geckodriver_dir = tempfile::TempDir::new().unwrap();
let local_geckodriver = binaries::local_bin_path(&fixture.path, "geckodriver");
fs::copy(
&local_geckodriver,
geckodriver_dir
.path()
.join(local_geckodriver.file_name().unwrap()),
).unwrap();
fs::remove_file(&local_geckodriver).unwrap();
let mut paths: Vec<_> = env::split_paths(&env::var("PATH").unwrap()).collect();
paths.insert(0, geckodriver_dir.path().into());
env::set_var("PATH", env::join_paths(paths).unwrap());
let cmd = Command::Test(test::TestOptions {
path: Some(fixture.path.clone()),
firefox: true,
headless: true,
mode: build::BuildMode::Noinstall,
..Default::default()
});
let logger = logger::new(&cmd, 3).unwrap();
command::run_wasm_pack(cmd, &logger).expect("should run test command OK");
}
#[test]
fn it_requires_node_or_a_browser() {
let fixture = fixture("tests/fixtures/wbg-test-node");
fixture.install_local_wasm_bindgen();
let cmd = Command::Test(test::TestOptions {
path: Some(fixture.path.clone()),
mode: build::BuildMode::Noinstall,
// Note: not setting node or any browser to true here.
..Default::default()
});
let logger = logger::new(&cmd, 3).unwrap();
assert!(
command::run_wasm_pack(cmd, &logger).is_err(),
"need to enable node or browser testing"
);
}
#[test]
fn the_headless_flag_requires_a_browser() {
let fixture = fixture("tests/fixtures/wbg-test-node");
fixture.install_local_wasm_bindgen();
let cmd = Command::Test(test::TestOptions {
path: Some(fixture.path.clone()),
node: true,
mode: build::BuildMode::Noinstall,
headless: true,
..Default::default()
});
let logger = logger::new(&cmd, 3).unwrap();
assert!(
command::run_wasm_pack(cmd, &logger).is_err(),
"running headless tests in node doesn't make sense"
);
}

@ -1,10 +1,19 @@
use std::env;
use std::fs;
use std::mem::ManuallyDrop;
use std::path::{Path, PathBuf};
use std::sync::{Once, ONCE_INIT};
use std::thread;
use wasm_pack;
use copy_dir::copy_dir;
use tempfile;
pub struct Fixture {
pub dir: tempfile::TempDir,
// NB: we wrap the fixture's tempdir in a `ManuallyDrop` so that if a test
// fails, its directory isn't deleted, and we have a chance to manually
// inspect its state and figure out what is going on.
pub dir: ManuallyDrop<tempfile::TempDir>,
pub path: PathBuf,
}
@ -17,11 +26,21 @@ pub fn fixture<P>(fixture: P) -> Fixture
where
P: AsRef<Path>,
{
// Make sure that all fixtures end up sharing a target dir, and we don't
// recompile wasm-bindgen and friends many times over.
static SET_TARGET_DIR: Once = ONCE_INIT;
SET_TARGET_DIR.call_once(|| {
env::set_var(
"CARGO_TARGET_DIR",
Path::new(env!("CARGO_MANIFEST_DIR")).join("target"),
);
});
let fixture = fixture
.as_ref()
.canonicalize()
.expect("should canonicalize fixture path OK");
let dir = tempfile::tempdir().expect("should create temporary directory OK");
let dir = ManuallyDrop::new(tempfile::tempdir().expect("should create temporary directory OK"));
let path = dir.path().join("wasm-pack");
println!(
"wasm-pack: copying test fixture '{}' to temporary directory '{}'",
@ -44,3 +63,102 @@ where
Fixture { dir, path }
}
impl Fixture {
/// Install a local wasm-bindgen for this fixture.
///
/// Takes care not to re-install for every fixture, but only the one time
/// for the whole test suite.
pub fn install_local_wasm_bindgen(&self) {
static INSTALL_WASM_BINDGEN: Once = ONCE_INIT;
let tests = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests");
let bin = tests.join("bin");
INSTALL_WASM_BINDGEN.call_once(|| {
if bin.join("wasm-bindgen").is_file() {
return;
}
const WASM_BINDGEN_VERSION: &str = "0.2.21";
wasm_pack::bindgen::download_prebuilt_wasm_bindgen(&tests, WASM_BINDGEN_VERSION)
.or_else(|_| {
wasm_pack::bindgen::cargo_install_wasm_bindgen(&tests, WASM_BINDGEN_VERSION)
}).unwrap();
});
copy_dir(bin, self.path.join("bin")).expect("could not copy `bin` directory into temp dir");
}
/// Download `geckodriver` and return its path.
///
/// Takes care to ensure that only one `geckodriver` is downloaded for the whole
/// test suite.
pub fn install_local_geckodriver(&self) {
static FETCH_GECKODRIVER: Once = ONCE_INIT;
let tests = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests");
wasm_pack::binaries::ensure_local_bin_dir(&tests)
.expect("could not create fixture's `bin` directory");
let geckodriver = wasm_pack::binaries::local_bin_path(&tests, "geckodriver");
FETCH_GECKODRIVER.call_once(|| {
if geckodriver.is_file() {
return;
}
wasm_pack::test::webdriver::install_geckodriver(&tests).unwrap();
assert!(geckodriver.is_file());
});
wasm_pack::binaries::ensure_local_bin_dir(&self.path)
.expect("could not create fixture's `bin` directory");
fs::copy(
&geckodriver,
wasm_pack::binaries::local_bin_path(&self.path, "geckodriver"),
).expect("could not copy `geckodriver` to fixture directory");
}
/// Download `chromedriver` and return its path.
///
/// Takes care to ensure that only one `chromedriver` is downloaded for the whole
/// test suite.
pub fn install_local_chromedriver(&self) {
static FETCH_CHROMEDRIVER: Once = ONCE_INIT;
let tests = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests");
wasm_pack::binaries::ensure_local_bin_dir(&tests)
.expect("could not create fixture's `bin` directory");
let chromedriver = wasm_pack::binaries::local_bin_path(&tests, "chromedriver");
FETCH_CHROMEDRIVER.call_once(|| {
if chromedriver.is_file() {
return;
}
wasm_pack::test::webdriver::install_chromedriver(&tests).unwrap();
assert!(chromedriver.is_file());
});
wasm_pack::binaries::ensure_local_bin_dir(&self.path)
.expect("could not create fixture's `bin` directory");
fs::copy(
&chromedriver,
wasm_pack::binaries::local_bin_path(&self.path, "chromedriver"),
).expect("could not copy `chromedriver` to fixture directory");
}
}
impl Drop for Fixture {
fn drop(&mut self) {
if !thread::panicking() {
unsafe { ManuallyDrop::drop(&mut self.dir) }
}
}
}

@ -0,0 +1,26 @@
use utils::fixture::fixture;
use wasm_pack::test::webdriver;
#[test]
#[cfg(any(
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "x86")
))]
fn can_install_chromedriver() {
let fixture = fixture("tests/fixtures/js-hello-world");
assert!(webdriver::install_chromedriver(&fixture.path).is_ok());
}
#[test]
#[cfg(any(
all(target_os = "linux", target_arch = "x86"),
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "windows", target_arch = "x86"),
all(target_os = "windows", target_arch = "x86_64")
))]
fn can_install_geckodriver() {
let fixture = fixture("tests/fixtures/js-hello-world");
assert!(webdriver::install_geckodriver(&fixture.path).is_ok());
}

@ -0,0 +1,16 @@
[package]
name = "wbg-test-node"
version = "0.1.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
[lib]
crate-type = ["cdylib"]
[dependencies]
# We depend on wasm-bindgen 0.2.21
wasm-bindgen = "0.2.21"
[dev-dependencies]
# And we depend on wasm-bindgen-test 0.2.19. But this should match the
# wasm-bindgen dependency!
wasm-bindgen-test = "0.2.19"

@ -0,0 +1,7 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

@ -0,0 +1,8 @@
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1, 1);
}

@ -0,0 +1,13 @@
[package]
name = "wbg-test-browser"
version = "0.1.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.21"
[dev-dependencies]
wasm-bindgen-test = "0.2.21"

@ -0,0 +1,5 @@
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn hello() {}

@ -0,0 +1,9 @@
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1, 1);
}

@ -0,0 +1,13 @@
[package]
name = "wbg-test-ok"
version = "0.1.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.21"
[dev-dependencies]
wasm-bindgen-test = "0.2.21"

@ -0,0 +1,5 @@
extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn hi() {}

@ -0,0 +1,7 @@
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
#[wasm_bindgen_test]
fn fail() {
assert_eq!(1, 2);
}

@ -0,0 +1,13 @@
[package]
name = "wbg-test-node"
version = "0.1.0"
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.21"
[dev-dependencies]
wasm-bindgen-test = "0.2.21"

@ -0,0 +1,7 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}

@ -0,0 +1,8 @@
extern crate wasm_bindgen_test;
use wasm_bindgen_test::*;
#[wasm_bindgen_test]
fn pass() {
assert_eq!(1, 1);
}
Loading…
Cancel
Save