diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7fb1696..64545de4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -91,3 +91,30 @@ jobs: with: command: test args: -p rspotify -p rspotify-http -p rspotify-model -p rspotify-macros --no-default-features --features=${{ matrix.features }} + + test-wasm: + name: Test WASM client + runs-on: ubuntu-latest + env: + RUST_BACKTRACE: 1 + + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + components: clippy + + - name: Install node + uses: actions/setup-node@v4 + + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Run wasm-pack test + run: wasm-pack test --node \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 470e31fe..f2b0e161 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.13.0 (2024.02.28) + +**New features** +- ([#458](https://github.com/ramsayleung/rspotify/pull/458)) Support for the `wasm32-unknown-unknown` build target + ## 0.12.1 (Unreleased) **Bugfixes** - ([#440](https://github.com/ramsayleung/rspotify/issues/440)) Add Smartwatch device type, fix for json parse error: unknown variant Smartwatch. diff --git a/Cargo.toml b/Cargo.toml index 7d8ff1b1..b617ca53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,7 +36,7 @@ base64 = "0.21.2" chrono = { version = "0.4.19", features = ["serde"] } dotenvy = { version = "0.15.0", optional = true } futures = { version = "0.3.17", optional = true } -getrandom = "0.2.3" + log = "0.4.14" maybe-async = "0.2.6" serde = { version = "1.0.130", default-features = false } @@ -46,10 +46,23 @@ thiserror = "1.0.29" url = "2.2.2" webbrowser = { version = "0.8.0", optional = true } +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { version = "0.2.3", features = ["js"] } + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +getrandom = "0.2.3" + [dev-dependencies] env_logger = { version = "0.11.0", default-features = false } -tokio = { version = "1.11.0", features = ["rt-multi-thread", "macros"] } futures-util = "0.3.17" +wasm-bindgen-test = "0.3.34" + +[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] +tokio = { version = "1.11.0", features = ["rt-multi-thread", "macros"] } + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +tokio = { version = "1.11.0", features = ["rt", "macros"] } +dotenvy_macro = { version = "0.15.7" } [features] default = ["client-reqwest", "reqwest-default-tls"] diff --git a/README.md b/README.md index d208de72..fd0ec1f1 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,16 @@ $ cargo build --all-features Because in order to switch between clients, the different clients have to implement the same base trait in [src/http/mod.rs](https://github.com/ramsayleung/rspotify/blob/master/src/http/mod.rs), so if you build with all features, you'll get `duplicate definitions` error. As every coin has two sides, you can only have one side at a time, not all sides of it. +## WASM support + +RSpotify supports building for the `wasm32-unknown-unknown` target. It should be as easy as: + +```sh +$ cargo build --target wasm32-unknown-unknown +``` + +Refer to the [documentation](https://docs.rs/rspotify/latest/rspotify/#webassembly) for more details + ## License [MIT](./LICENSE) diff --git a/rspotify-http/src/common.rs b/rspotify-http/src/common.rs index 596286a5..2e611d0c 100644 --- a/rspotify-http/src/common.rs +++ b/rspotify-http/src/common.rs @@ -18,7 +18,8 @@ pub type Form<'a> = HashMap<&'a str, &'a str>; /// different ways (`Value::Null`, an empty `Value::Object`...), so this removes /// redundancy and edge cases (a `Some(Value::Null), for example, doesn't make /// much sense). -#[maybe_async] +#[cfg_attr(target_arch = "wasm32", maybe_async(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), maybe_async)] pub trait BaseHttpClient: Send + Default + Clone + fmt::Debug { type Error; diff --git a/rspotify-http/src/reqwest.rs b/rspotify-http/src/reqwest.rs index 3498b37a..238f0a79 100644 --- a/rspotify-http/src/reqwest.rs +++ b/rspotify-http/src/reqwest.rs @@ -4,6 +4,8 @@ use super::{BaseHttpClient, Form, Headers, Query}; use std::convert::TryInto; + +#[cfg(not(target_arch = "wasm32"))] use std::time::Duration; use maybe_async::async_impl; @@ -56,6 +58,7 @@ pub struct ReqwestClient { client: reqwest::Client, } +#[cfg(not(target_arch = "wasm32"))] impl Default for ReqwestClient { fn default() -> Self { let client = reqwest::ClientBuilder::new() @@ -67,6 +70,17 @@ impl Default for ReqwestClient { } } +#[cfg(target_arch = "wasm32")] +impl Default for ReqwestClient { + fn default() -> Self { + let client = reqwest::ClientBuilder::new() + .build() + // building with these options cannot fail + .unwrap(); + Self { client } + } +} + impl ReqwestClient { async fn request( &self, @@ -109,7 +123,8 @@ impl ReqwestClient { } } -#[async_impl] +#[cfg_attr(target_arch = "wasm32", async_impl(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), async_impl)] impl BaseHttpClient for ReqwestClient { type Error = ReqwestError; diff --git a/src/auth_code.rs b/src/auth_code.rs index 7b5f7ce2..d11f1ac0 100644 --- a/src/auth_code.rs +++ b/src/auth_code.rs @@ -72,7 +72,8 @@ pub struct AuthCodeSpotify { } /// This client has access to the base methods. -#[maybe_async] +#[cfg_attr(target_arch = "wasm32", maybe_async(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), maybe_async)] impl BaseClient for AuthCodeSpotify { fn get_http(&self) -> &HttpClient { &self.http @@ -126,7 +127,8 @@ impl BaseClient for AuthCodeSpotify { /// This client includes user authorization, so it has access to the user /// private endpoints in [`OAuthClient`]. -#[maybe_async] +#[cfg_attr(target_arch = "wasm32", maybe_async(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), maybe_async)] impl OAuthClient for AuthCodeSpotify { fn get_oauth(&self) -> &OAuth { &self.oauth diff --git a/src/auth_code_pkce.rs b/src/auth_code_pkce.rs index e87d8ced..367f3340 100644 --- a/src/auth_code_pkce.rs +++ b/src/auth_code_pkce.rs @@ -44,7 +44,8 @@ pub struct AuthCodePkceSpotify { } /// This client has access to the base methods. -#[maybe_async] +#[cfg_attr(target_arch = "wasm32", maybe_async(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), maybe_async)] impl BaseClient for AuthCodePkceSpotify { fn get_http(&self) -> &HttpClient { &self.http @@ -88,7 +89,8 @@ impl BaseClient for AuthCodePkceSpotify { /// This client includes user authorization, so it has access to the user /// private endpoints in [`OAuthClient`]. -#[maybe_async] +#[cfg_attr(target_arch = "wasm32", maybe_async(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), maybe_async)] impl OAuthClient for AuthCodePkceSpotify { fn get_oauth(&self) -> &OAuth { &self.oauth diff --git a/src/client_creds.rs b/src/client_creds.rs index ffdde5e2..f4364282 100644 --- a/src/client_creds.rs +++ b/src/client_creds.rs @@ -30,7 +30,8 @@ pub struct ClientCredsSpotify { } /// This client has access to the base methods. -#[maybe_async] +#[cfg_attr(target_arch = "wasm32", maybe_async(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), maybe_async)] impl BaseClient for ClientCredsSpotify { fn get_http(&self) -> &HttpClient { &self.http diff --git a/src/clients/base.rs b/src/clients/base.rs index 0298c9c8..ae1b81c0 100644 --- a/src/clients/base.rs +++ b/src/clients/base.rs @@ -21,7 +21,8 @@ use serde_json::Value; /// This trait implements the basic endpoints from the Spotify API that may be /// accessed without user authorization, including parts of the authentication /// flow that are shared, and the endpoints. -#[maybe_async] +#[cfg_attr(target_arch = "wasm32", maybe_async(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), maybe_async)] pub trait BaseClient where Self: Send + Sync + Default + Clone + fmt::Debug, diff --git a/src/clients/oauth.rs b/src/clients/oauth.rs index 222c1161..e9154b63 100644 --- a/src/clients/oauth.rs +++ b/src/clients/oauth.rs @@ -27,7 +27,8 @@ use url::Url; /// [`user_playlist`](crate::clients::BaseClient::user_playlist). This trait /// only separates endpoints that *always* need authorization from the base /// ones. -#[maybe_async] +#[cfg_attr(target_arch = "wasm32", maybe_async(?Send))] +#[cfg_attr(not(target_arch = "wasm32"), maybe_async)] pub trait OAuthClient: BaseClient { fn get_oauth(&self) -> &OAuth; diff --git a/src/clients/pagination/mod.rs b/src/clients/pagination/mod.rs index ac6fcab7..1bf57d8a 100644 --- a/src/clients/pagination/mod.rs +++ b/src/clients/pagination/mod.rs @@ -25,10 +25,18 @@ #[cfg(feature = "__sync")] mod iter; -#[cfg(feature = "__async")] + +#[cfg(all(feature = "__async", not(target_arch = "wasm32")))] mod stream; +#[cfg(all(feature = "__async", target_arch = "wasm32"))] +mod wasm_stream; + #[cfg(feature = "__sync")] pub use iter::{paginate, paginate_with_ctx, Paginator}; -#[cfg(feature = "__async")] + +#[cfg(all(feature = "__async", not(target_arch = "wasm32")))] pub use stream::{paginate, paginate_with_ctx, Paginator}; + +#[cfg(all(feature = "__async", target_arch = "wasm32"))] +pub use wasm_stream::{paginate, paginate_with_ctx, Paginator}; diff --git a/src/clients/pagination/wasm_stream.rs b/src/clients/pagination/wasm_stream.rs new file mode 100644 index 00000000..58df6dbd --- /dev/null +++ b/src/clients/pagination/wasm_stream.rs @@ -0,0 +1,62 @@ +//! Asynchronous implementation of automatic pagination requests. + +use crate::{model::Page, ClientResult}; + +use std::pin::Pin; + +use futures::{future::Future, stream::Stream}; + +/// Alias for `futures::stream::Stream`, since async mode is enabled. +pub type Paginator<'a, T> = Pin + 'a>>; + +pub type RequestFuture<'a, T> = Pin>>>>; + +/// This is used to handle paginated requests automatically. +pub fn paginate_with_ctx<'a, Ctx: 'a, T, Request>( + ctx: Ctx, + req: Request, + page_size: u32, +) -> Paginator<'a, ClientResult> +where + T: 'a + Unpin, + Request: 'a + for<'ctx> Fn(&'ctx Ctx, u32, u32) -> RequestFuture<'ctx, T>, +{ + use async_stream::stream; + let mut offset = 0; + Box::pin(stream! { + loop { + let request = req(&ctx, page_size, offset); + let page = request.await?; + offset += page.items.len() as u32; + for item in page.items { + yield Ok(item); + } + if page.next.is_none() { + break; + } + } + }) +} + +pub fn paginate<'a, T, Fut, Request>(req: Request, page_size: u32) -> Paginator<'a, ClientResult> +where + T: 'a + Unpin, + Fut: Future>>, + Request: 'a + Fn(u32, u32) -> Fut, +{ + use async_stream::stream; + let mut offset = 0; + Box::pin(stream! { + loop { + let request = req(page_size, offset); + let page = request.await?; + offset += page.items.len() as u32; + for item in page.items { + yield Ok(item); + } + if page.next.is_none() { + break; + } + } + }) +} diff --git a/src/lib.rs b/src/lib.rs index 67ea075e..2947fb10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,6 +108,21 @@ //! the [`.env` file](https://github.com/ramsayleung/rspotify/blob/master/.env) //! for more details. //! +//! ### WebAssembly +//! +//! RSpotify supports the `wasm32-unknown-unknown` target in combination +//! with the `client-reqwest` feature. HTTP requests must be processed async. +//! Other HTTP client configurations are not supported. +//! +//! [Spotify recommends][spotify-auth-flows] using [`AuthCodePkceSpotify`] for +//! authorization flows on the web. +//! +//! Importing the Client ID via `RSPOTIFY_CLIENT_ID` is not possible since WASM +//! web runtimes are isolated from the host environment. The client ID must be +//! passed explicitly to [`Credentials::new_pkce`]. Alternatively, it can be +//! embedded at compile time with the [`std::env!`] or +//! [`dotenv!`](https://crates.io/crates/dotenvy) macros. +//! //! ### Examples //! //! There are some [available examples on the GitHub @@ -442,11 +457,13 @@ impl OAuth { } #[cfg(test)] -mod test { +pub mod test { use crate::{alphabets, generate_random_string, Credentials}; use std::collections::HashSet; + use wasm_bindgen_test::*; #[test] + #[wasm_bindgen_test] fn test_generate_random_string() { let mut containers = HashSet::new(); for _ in 1..101 { @@ -456,6 +473,7 @@ mod test { } #[test] + #[wasm_bindgen_test] fn test_basic_auth() { let creds = Credentials::new_pkce("ramsay"); let headers = creds.auth_headers(); diff --git a/tests/test_enums.rs b/tests/test_enums.rs index 546f68a4..5141a315 100644 --- a/tests/test_enums.rs +++ b/tests/test_enums.rs @@ -1,72 +1,85 @@ use rspotify::model::*; +use wasm_bindgen_test::*; #[test] +#[wasm_bindgen_test] fn test_include_external() { let audio = IncludeExternal::Audio; assert_eq!("audio", <&str>::from(audio)); } #[test] +#[wasm_bindgen_test] fn test_repeat_state() { let context = RepeatState::Context; assert_eq!(<&str>::from(context), "context"); } #[test] +#[wasm_bindgen_test] fn test_disallow_key() { let toggling_shuffle = DisallowKey::TogglingShuffle; assert_eq!(<&str>::from(toggling_shuffle), "toggling_shuffle"); } #[test] +#[wasm_bindgen_test] fn test_time_range() { let medium_range = TimeRange::MediumTerm; assert_eq!(<&str>::from(medium_range), "medium_term"); } #[test] +#[wasm_bindgen_test] fn test_date_precision() { let month = DatePrecision::Month; assert_eq!(<&str>::from(month), "month"); } #[test] +#[wasm_bindgen_test] fn test_album_type_convert_from_str() { let appears_on = AlbumType::AppearsOn; assert_eq!("appears_on", <&str>::from(appears_on)); } #[test] +#[wasm_bindgen_test] fn test_convert_search_type_from_str() { let search_type = SearchType::Artist; assert_eq!("artist", <&str>::from(search_type)); } #[test] +#[wasm_bindgen_test] fn test_type_convert_from_str() { let artist = Type::Artist; assert_eq!(<&str>::from(artist), "artist"); } #[test] +#[wasm_bindgen_test] fn test_additional_type() { let episode = AdditionalType::Episode; assert_eq!(<&str>::from(episode), "episode"); } #[test] +#[wasm_bindgen_test] fn test_current_playing_type() { let ad = CurrentlyPlayingType::Advertisement; assert_eq!(<&str>::from(ad), "ad"); } #[test] +#[wasm_bindgen_test] fn test_search_type() { let episode = SearchType::Episode; assert_eq!(<&str>::from(episode), "episode"); } #[test] +#[wasm_bindgen_test] fn test_convert_country_from_str() { let zimbabwe = Country::Zimbabwe; assert_eq!(<&str>::from(zimbabwe), "ZW"); diff --git a/tests/test_models.rs b/tests/test_models.rs index b08340d7..c7b92325 100644 --- a/tests/test_models.rs +++ b/tests/test_models.rs @@ -1,6 +1,7 @@ use chrono::{DateTime, Duration, NaiveDateTime, Utc}; use rspotify::model::*; use serde::de::DeserializeOwned; +use wasm_bindgen_test::*; #[track_caller] fn deserialize(data: impl AsRef) -> T @@ -18,6 +19,7 @@ where } #[test] +#[wasm_bindgen_test] fn test_simplified_track() { let json_str = r#" { @@ -55,6 +57,7 @@ fn test_simplified_track() { } #[test] +#[wasm_bindgen_test] fn test_public_user() { let json_str = r#" { @@ -89,6 +92,7 @@ fn test_public_user() { } #[test] +#[wasm_bindgen_test] fn test_private_user() { let json_str = r#" { @@ -119,6 +123,7 @@ fn test_private_user() { } #[test] +#[wasm_bindgen_test] fn test_full_artist() { let json_str = r#" { @@ -153,6 +158,7 @@ fn test_full_artist() { } #[test] +#[wasm_bindgen_test] fn test_simplified_episode() { let json_str = r#" { @@ -199,6 +205,7 @@ fn test_simplified_episode() { } #[test] +#[wasm_bindgen_test] fn test_full_episode() { let json_str = r#" { @@ -267,6 +274,7 @@ fn test_full_episode() { } #[test] +#[wasm_bindgen_test] fn test_copyright() { let json_str = r#" [ { @@ -280,6 +288,7 @@ fn test_copyright() { } #[test] +#[wasm_bindgen_test] fn test_audio_analysis_section() { let json_str = r#" { @@ -303,6 +312,7 @@ fn test_audio_analysis_section() { } #[test] +#[wasm_bindgen_test] fn test_audio_analysis_segments() { let json_str = r#" { @@ -327,6 +337,7 @@ fn test_audio_analysis_segments() { } #[test] +#[wasm_bindgen_test] fn test_actions() { let json_str = r#" { @@ -340,6 +351,7 @@ fn test_actions() { } #[test] +#[wasm_bindgen_test] fn test_recommendations_seed() { let json_str = r#" { @@ -356,6 +368,7 @@ fn test_recommendations_seed() { } #[test] +#[wasm_bindgen_test] fn test_full_playlist() { let json_str_images = r#" [ @@ -493,6 +506,7 @@ fn test_full_playlist() { } #[test] +#[wasm_bindgen_test] fn test_audio_features() { let json = r#" { @@ -522,6 +536,7 @@ fn test_audio_features() { } #[test] +#[wasm_bindgen_test] fn test_full_track() { let json = r#" { @@ -601,6 +616,7 @@ fn test_full_track() { } #[test] +#[wasm_bindgen_test] fn test_resume_point() { let json = r#" { @@ -614,6 +630,7 @@ fn test_resume_point() { } #[test] +#[wasm_bindgen_test] fn test_resume_point_negative() { let json = r#" { @@ -627,6 +644,7 @@ fn test_resume_point_negative() { } #[test] +#[wasm_bindgen_test] fn test_currently_playing_context() { let json = r#" { @@ -741,6 +759,7 @@ fn test_currently_playing_context() { } #[test] +#[wasm_bindgen_test] fn test_current_playback_context() { let json = r#" { @@ -854,6 +873,7 @@ fn test_current_playback_context() { } #[test] +#[wasm_bindgen_test] fn test_current_user_queue() { let json = r#" { @@ -1045,6 +1065,7 @@ fn test_current_user_queue() { } #[test] +#[wasm_bindgen_test] fn test_audio_analysis_track() { let json = r#" { @@ -1081,6 +1102,7 @@ fn test_audio_analysis_track() { } #[test] +#[wasm_bindgen_test] fn test_simplified_playlist() { let json = r#" { @@ -1129,6 +1151,7 @@ fn test_simplified_playlist() { } #[test] +#[wasm_bindgen_test] fn test_collectionyourepisodes_type() { let json = r#" { diff --git a/tests/test_oauth2.rs b/tests/test_oauth2.rs index 34378e70..dcec1b73 100644 --- a/tests/test_oauth2.rs +++ b/tests/test_oauth2.rs @@ -5,8 +5,10 @@ use rspotify::{ }; use std::{collections::HashMap, fs, io::Read, path::PathBuf}; use url::Url; +use wasm_bindgen_test::*; #[test] +#[wasm_bindgen_test] fn test_get_authorize_url() { let oauth = OAuth { state: "fdsafdsfa".to_owned(), @@ -111,6 +113,7 @@ async fn test_write_token() { } #[test] +#[wasm_bindgen_test] fn test_token_is_expired() { let expires_in = Duration::seconds(20); let tok = Token { @@ -134,6 +137,7 @@ fn test_token_is_expired() { } #[test] +#[wasm_bindgen_test] fn test_parse_response_code() { // A random state is generated by default let spotify = AuthCodeSpotify::default(); diff --git a/tests/test_with_credential.rs b/tests/test_with_credential.rs index 15e33c39..dd76a8e3 100644 --- a/tests/test_with_credential.rs +++ b/tests/test_with_credential.rs @@ -1,35 +1,40 @@ +mod util; + use rspotify::{ model::{AlbumId, AlbumType, ArtistId, Country, Market, PlaylistId, TrackId, UserId}, prelude::*, - ClientCredsSpotify, Credentials, + ClientCredsSpotify, }; use maybe_async::maybe_async; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::*; + /// Generating a new basic client for the requests. #[maybe_async] pub async fn creds_client() -> ClientCredsSpotify { - // The credentials must be available in the environment. - let creds = Credentials::from_env().unwrap_or_else(|| { - panic!( - "No credentials configured. Make sure that either the `env-file` \ - feature is enabled, or that the required environment variables are \ - exported (`RSPOTIFY_CLIENT_ID`, `RSPOTIFY_CLIENT_SECRET`)" - ) - }); - + let creds = util::get_credentials(); let spotify = ClientCredsSpotify::new(creds); spotify.request_token().await.unwrap(); spotify } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_album() { let birdy_uri = AlbumId::from_uri("spotify:album:0sNOF9WDwhWunNAHPD3Baj").unwrap(); creds_client().await.album(birdy_uri, None).await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_albums() { let track_uris = [ AlbumId::from_uri("spotify:album:41MnTivkwTO3UUJ8DrqEJJ").unwrap(), @@ -39,7 +44,11 @@ async fn test_albums() { creds_client().await.albums(track_uris, None).await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_album_tracks() { let birdy_uri = AlbumId::from_uri("spotify:album:6akEvsycLGftJxYudPjmqK").unwrap(); creds_client() @@ -49,7 +58,11 @@ async fn test_album_tracks() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_artist_related_artists() { let birdy_uri = ArtistId::from_uri("spotify:artist:43ZHCT0cAZBISjO8DG9PnE").unwrap(); creds_client() @@ -59,13 +72,21 @@ async fn test_artist_related_artists() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_artist() { let birdy_uri = ArtistId::from_uri("spotify:artist:2WX2uTcsvV5OnS0inACecP").unwrap(); creds_client().await.artist(birdy_uri).await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_artists_albums() { let birdy_uri = ArtistId::from_uri("spotify:artist:2WX2uTcsvV5OnS0inACecP").unwrap(); creds_client() @@ -81,7 +102,11 @@ async fn test_artists_albums() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_artists_albums_with_multiple_album_types() { let birdy_uri = ArtistId::from_uri("spotify:artist:2WX2uTcsvV5OnS0inACecP").unwrap(); let include_groups = [ @@ -103,7 +128,11 @@ async fn test_artists_albums_with_multiple_album_types() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_artists_albums_with_zero_album_type() { let birdy_uri = ArtistId::from_uri("spotify:artist:2WX2uTcsvV5OnS0inACecP").unwrap(); creds_client() @@ -119,7 +148,11 @@ async fn test_artists_albums_with_zero_album_type() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_artists() { let artist_uris = [ ArtistId::from_uri("spotify:artist:0oSGxfWSnnOXhD2fKuz2Gy").unwrap(), @@ -128,7 +161,11 @@ async fn test_artists() { creds_client().await.artists(artist_uris).await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_artist_top_tracks() { let birdy_uri = ArtistId::from_uri("spotify:artist:2WX2uTcsvV5OnS0inACecP").unwrap(); creds_client() @@ -138,19 +175,31 @@ async fn test_artist_top_tracks() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_audio_analysis() { let track = TrackId::from_id("06AKEBrKUckW0KREUWRnvT").unwrap(); creds_client().await.track_analysis(track).await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_audio_features() { let track = TrackId::from_uri("spotify:track:06AKEBrKUckW0KREUWRnvT").unwrap(); creds_client().await.track_features(track).await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_audios_features() { let mut tracks_ids = vec![]; let track_id1 = TrackId::from_uri("spotify:track:4JpKVNYnVcJ8tuMKjAj50A").unwrap(); @@ -166,19 +215,31 @@ async fn test_audios_features() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_user() { let birdy_uri = UserId::from_id("tuggareutangranser").unwrap(); creds_client().await.user(birdy_uri).await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_track() { let birdy_uri = TrackId::from_uri("spotify:track:6rqhFgbbKwnb9MLmUQDhG6").unwrap(); creds_client().await.track(birdy_uri, None).await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_tracks() { let track_uris = [ TrackId::from_uri("spotify:track:3n3Ppam7vgaVa1iaRUc9Lp").unwrap(), @@ -187,7 +248,11 @@ async fn test_tracks() { creds_client().await.tracks(track_uris, None).await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_existing_playlist() { let playlist_id = PlaylistId::from_id("37i9dQZF1DZ06evO45P0Eo").unwrap(); creds_client() @@ -197,14 +262,18 @@ async fn test_existing_playlist() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] async fn test_fake_playlist() { let playlist_id = PlaylistId::from_id("fakeid").unwrap(); let playlist = creds_client().await.playlist(playlist_id, None, None).await; assert!(playlist.is_err()); } -mod test_pagination { +pub mod test_pagination { use super::*; static ALBUM: &str = "spotify:album:2T7DdrOvsqOqU9bGTkjBYu"; @@ -239,7 +308,8 @@ mod test_pagination { /// This test iterates a request of 10 items, with 5 requests of 2 items. #[cfg(feature = "__async")] - #[tokio::test] + #[cfg_attr(not(target_arch = "wasm32"), tokio::test)] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] async fn test_pagination_async() { use futures_util::StreamExt; diff --git a/tests/test_with_oauth.rs b/tests/test_with_oauth.rs index 18a2f0d8..ac20f11b 100644 --- a/tests/test_with_oauth.rs +++ b/tests/test_with_oauth.rs @@ -14,6 +14,8 @@ //! with the `oauth_tokens` example: //! //! cargo run --example oauth_tokens --features=env-file,cli +//! +mod util; use rspotify::{ clients::pagination::Paginator, @@ -23,36 +25,45 @@ use rspotify::{ SearchType, ShowId, TimeLimits, TimeRange, TrackId, UserId, }, prelude::*, - scopes, AuthCodeSpotify, ClientResult, Credentials, OAuth, Token, + scopes, AuthCodeSpotify, ClientResult, OAuth, Token, }; -use std::env; - use chrono::{prelude::*, Duration}; use maybe_async::maybe_async; +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::*; + +#[cfg(not(target_arch = "wasm32"))] +fn get_access_tokens() -> (Option, Option) { + use std::env; + ( + env::var("RSPOTIFY_ACCESS_TOKEN").ok(), + env::var("RSPOTIFY_REFRESH_TOKEN").ok(), + ) +} + +#[cfg(target_arch = "wasm32")] +fn get_access_tokens() -> (Option, Option) { + let access_token = option_env!("RSPOTIFY_ACCESS_TOKEN").map(|s| s.to_string()); + let refresh_token = option_env!("RSPOTIFY_REFRESH_TOKEN").map(|s| s.to_string()); + (access_token, refresh_token) +} + /// Generating a new OAuth client for the requests. #[maybe_async] pub async fn oauth_client() -> AuthCodeSpotify { - if let Ok(access_token) = env::var("RSPOTIFY_ACCESS_TOKEN") { + let (access_token, refresh_token) = get_access_tokens(); + + if let Some(access_token) = access_token { let tok = Token { access_token, ..Default::default() }; AuthCodeSpotify::from_token(tok) - } else if let Ok(refresh_token) = env::var("RSPOTIFY_REFRESH_TOKEN") { - // The credentials must be available in the environment. Enable - // `env-file` in order to read them from an `.env` file. - let creds = Credentials::from_env().unwrap_or_else(|| { - panic!( - "No credentials configured. Make sure that either the \ - `env-file` feature is enabled, or that the required \ - environment variables are exported (`RSPOTIFY_CLIENT_ID`, \ - `RSPOTIFY_CLIENT_SECRET`)." - ) - }); - + } else if let Some(refresh_token) = refresh_token { + let creds = util::get_credentials(); let scopes = scopes!( "playlist-modify-private", "playlist-modify-public", @@ -110,7 +121,11 @@ async fn fetch_all(paginator: Paginator<'_, ClientResult>) -> Vec { } } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_categories() { oauth_client() @@ -125,7 +140,11 @@ async fn test_categories() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_category_playlists() { oauth_client() @@ -140,7 +159,11 @@ async fn test_category_playlists() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_current_playback() { oauth_client() @@ -150,7 +173,11 @@ async fn test_current_playback() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_current_playing() { oauth_client() @@ -160,7 +187,11 @@ async fn test_current_playing() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_current_user_followed_artists() { oauth_client() @@ -170,7 +201,11 @@ async fn test_current_user_followed_artists() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_current_user_playing_track() { oauth_client() @@ -180,7 +215,11 @@ async fn test_current_user_playing_track() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_current_user_recently_played() { let limit = TimeLimits::After(Utc::now() - Duration::days(2)); @@ -191,7 +230,11 @@ async fn test_current_user_recently_played() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_current_user_saved_albums() { let album_ids = [ @@ -228,7 +271,11 @@ async fn test_current_user_saved_albums() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_current_user_saved_tracks_add() { let client = oauth_client().await; @@ -262,7 +309,11 @@ async fn test_current_user_saved_tracks_add() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_current_user_top_artists() { oauth_client() @@ -272,7 +323,11 @@ async fn test_current_user_top_artists() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_current_user_top_tracks() { oauth_client() @@ -282,7 +337,11 @@ async fn test_current_user_top_tracks() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_featured_playlists() { let now: DateTime = Utc::now(); @@ -293,13 +352,21 @@ async fn test_featured_playlists() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_me() { oauth_client().await.me().await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_new_releases() { oauth_client() @@ -309,7 +376,11 @@ async fn test_new_releases() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_new_releases_with_from_token() { oauth_client() @@ -319,7 +390,11 @@ async fn test_new_releases_with_from_token() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_playback() { let client = oauth_client().await; @@ -402,7 +477,11 @@ async fn test_playback() { } } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_recommendations() { let seed_artists = [ArtistId::from_id("4NHQUGzhtTLFvgF5SZesLK").unwrap()]; @@ -426,7 +505,11 @@ async fn test_recommendations() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_repeat() { let client = oauth_client().await; @@ -441,7 +524,11 @@ async fn test_repeat() { } } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_search_album() { let query = "album:arrival artist:abba"; @@ -452,7 +539,11 @@ async fn test_search_album() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_search_artist() { let query = "tania bowra"; @@ -470,7 +561,11 @@ async fn test_search_artist() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_search_playlist() { let query = "\"doom metal\""; @@ -488,7 +583,11 @@ async fn test_search_playlist() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_search_track() { let query = "abba"; @@ -508,7 +607,11 @@ async fn test_search_track() { // This also tests percentage signs in search queries to avoid regressions of // https://github.com/ramsayleung/rspotify/issues/141 -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_search_show() { let query = "99% invisible"; @@ -519,7 +622,11 @@ async fn test_search_show() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_seek_track() { let client = oauth_client().await; @@ -541,7 +648,11 @@ async fn test_seek_track() { } } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_shuffle() { let client = oauth_client().await; @@ -556,7 +667,11 @@ async fn test_shuffle() { } } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_user_follow_artist() { let client = oauth_client().await; @@ -572,7 +687,11 @@ async fn test_user_follow_artist() { client.user_unfollow_artists(artists).await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_user_follow_users() { let client = oauth_client().await; @@ -588,7 +707,11 @@ async fn test_user_follow_users() { client.user_unfollow_users(users).await.unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_user_follow_playlist() { let client = oauth_client().await; @@ -761,7 +884,11 @@ async fn check_playlist_follow(client: &AuthCodeSpotify, playlist: &FullPlaylist .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_playlist() { let client = oauth_client().await; @@ -771,7 +898,11 @@ async fn test_playlist() { check_playlist_follow(&client, &playlist).await; } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_volume() { let client = oauth_client().await; @@ -793,7 +924,11 @@ async fn test_volume() { } } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_add_queue() { // NOTE: unfortunately it's impossible to revert this test @@ -807,7 +942,11 @@ async fn test_add_queue() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_get_several_shows() { let shows = [ @@ -822,7 +961,11 @@ async fn test_get_several_shows() { .unwrap(); } -#[maybe_async::test(feature = "__sync", async(feature = "__async", tokio::test))] +#[maybe_async::test( + feature = "__sync", + async(all(feature = "__async", not(target_arch = "wasm32")), tokio::test), + async(all(feature = "__async", target_arch = "wasm32"), wasm_bindgen_test) +)] #[ignore] async fn test_get_several_episodes() { let episodes = [ diff --git a/tests/util.rs b/tests/util.rs new file mode 100644 index 00000000..2698fd70 --- /dev/null +++ b/tests/util.rs @@ -0,0 +1,21 @@ +use rspotify::Credentials; + +#[cfg(not(target_arch = "wasm32"))] +pub fn get_credentials() -> Credentials { + // The credentials must be available in the environment. + Credentials::from_env().unwrap_or_else(|| { + panic!( + "No credentials configured. Make sure that either the `env-file` \ + feature is enabled, or that the required environment variables are \ + exported (`RSPOTIFY_CLIENT_ID`, `RSPOTIFY_CLIENT_SECRET`)" + ) + }) +} + +#[cfg(target_arch = "wasm32")] +pub fn get_credentials() -> Credentials { + // The credentials must be available in the environment. Panics if they are not available + let id = dotenvy_macro::dotenv!("RSPOTIFY_CLIENT_ID"); + let secret = dotenvy_macro::dotenv!("RSPOTIFY_CLIENT_SECRET"); + Credentials::new(&id, &secret) +}