From 3e4784decf519ed13b0959e94922807fe7bc540e Mon Sep 17 00:00:00 2001 From: Afifurrohman Date: Thu, 30 May 2024 21:07:25 +0700 Subject: [PATCH] feat: Add Unit Test (#8) * feat: Add Unit Test * fix: typo target add rustup cmd * fix: missing install wasm-pack in ci * fix: Missing manifest.json --- .github/workflows/live.yaml | 47 ++++- .github/workflows/preview.yaml | 45 ++++- Cargo.lock | 32 +++ Cargo.toml | 2 + Makefile | 9 +- README.md | 7 + assets/manifest.json | 19 ++ src/common.rs | 26 +++ src/data.rs | 353 +++++++++++++++++++-------------- src/img.rs | 21 +- src/puzzle.rs | 8 +- src/util.rs | 36 +++- 12 files changed, 427 insertions(+), 178 deletions(-) create mode 100644 assets/manifest.json diff --git a/.github/workflows/live.yaml b/.github/workflows/live.yaml index 393366d..79fbb29 100644 --- a/.github/workflows/live.yaml +++ b/.github/workflows/live.yaml @@ -3,20 +3,55 @@ on: branches: main jobs: + check: + runs-on: ubuntu-latest + strategy: + matrix: + profile: [dev,release] + steps: + - uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 + + - run: rustup target add wasm32-unknown-unknown + + - name: Check + run: | + cargo clippy --profile ${{matrix.profile}} && \ + cargo fmt --check + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 + + - run: rustup target add wasm32-unknown-unknown + + - run: cargo install wasm-pack + + - name: Test (unit) + run: make test -j 8 + + spell-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: crate-ci/typos@v1.20.1 + deploy: runs-on: ubuntu-latest + needs: + - check + - test + - spell-check permissions: pull-requests: write checks: write steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - targets: wasm32-unknown-unknown - components: clippy, rustfmt - + - run: rustup target add wasm32-unknown-unknown + - run: cargo install wasm-pack - run: make release -j 8 diff --git a/.github/workflows/preview.yaml b/.github/workflows/preview.yaml index 0c5566d..311f611 100644 --- a/.github/workflows/preview.yaml +++ b/.github/workflows/preview.yaml @@ -3,19 +3,54 @@ on: branches: main jobs: + check: + runs-on: ubuntu-latest + strategy: + matrix: + profile: [dev,release] + steps: + - uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 + + - run: rustup target add wasm32-unknown-unknown + + - name: Check + run: | + cargo clippy --profile ${{matrix.profile}} && \ + cargo fmt --check + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 + + - run: rustup target add wasm32-unknown-unknown + + - run: cargo install wasm-pack + + - name: Test (unit) + run: make test -j 8 + + spell-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: crate-ci/typos@v1.20.1 + preview: runs-on: ubuntu-latest + needs: + - check + - test + - spell-check permissions: pull-requests: write checks: write steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - targets: wasm32-unknown-unknown - components: clippy, rustfmt + - run: rustup target add wasm32-unknown-unknown - run: cargo install wasm-pack diff --git a/Cargo.lock b/Cargo.lock index 87c132c..eac2978 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -217,6 +217,7 @@ dependencies = [ "image", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-bindgen-test", "web-sys", ] @@ -756,6 +757,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -1011,6 +1018,31 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "wasm-bindgen-test" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "web-sys" version = "0.3.69" diff --git a/Cargo.toml b/Cargo.toml index 6d20e7b..2de033c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" image = "0.25" console_error_panic_hook = "0.1" +wasm-bindgen-test = "0.3" [dependencies.web-sys] version = "0.3" @@ -33,6 +34,7 @@ features = [ "Blob", "BlobPropertyBag", "Window", + "console" ] [profile.release] diff --git a/Makefile b/Makefile index 5b98b4c..1277e88 100644 --- a/Makefile +++ b/Makefile @@ -2,17 +2,12 @@ WASM_FLAGS=--no-typescript \ --no-pack -t web \ -d assets build: src/lib.rs - $(MAKE) fix - cargo fmt wasm-pack build --dev $(WASM_FLAGS) release: src/lib.rs - $(MAKE) fix - cargo fmt wasm-pack build $(WASM_FLAGS) -fix: - cargo clippy --fix --allow-dirty --allow-staged - cargo clippy --fix --allow-dirty --allow-staged -r +test: src/lib.rs + wasm-pack test --node server: compose.yaml docker compose up -d diff --git a/README.md b/README.md index 4b129ea..a5718eb 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ - [x] Wasm Pack Cli (version >= 0.12.x) - [x] Git (version >= 2.43.x) - [x] Docker (version >= 24.0.x) +- [ ] Node (version >= 20.14.x) + > Only if you want to test ### Installation @@ -54,3 +56,8 @@ make ```sh make release ``` + +- Test (Unit Test) +```sh +make test +``` diff --git a/assets/manifest.json b/assets/manifest.json new file mode 100644 index 0000000..ecc1ac8 --- /dev/null +++ b/assets/manifest.json @@ -0,0 +1,19 @@ +{ + "name": "Enku - Crack puzzle get the word", + "short_name": "Enku", + "description": "Belajar bahasa inggris melalui game puzzle", + "id": "/", + "scope": "/", + "start_url": "/", + "background_color": "#ffffff", + "theme_color": "#000000", + "display": "standalone", + "icons": [ + ], + "screenshots": [ + ], + "display_override": [ + "standalone", + "window-controls-overlay" + ] +} \ No newline at end of file diff --git a/src/common.rs b/src/common.rs index 9bb2cd2..80c5576 100644 --- a/src/common.rs +++ b/src/common.rs @@ -85,3 +85,29 @@ pub fn bytes_to_js_blob(buffer: &[u8], mime: &str) -> Blob { Blob::new_with_u8_array_sequence_and_options(&arr, &opts).unwrap() } + +#[cfg(test)] +mod tests { + use wasm_bindgen_test::wasm_bindgen_test; + use web_sys::js_sys::Uint8Array; + + #[wasm_bindgen_test] + fn bytes_to_js_blob() { + let mime = "text/plain"; + let blob = super::bytes_to_js_blob(&[0b01100001, 0b01100001, 0b01100001], mime); + + assert_eq!(blob.type_(), mime); + assert_eq!(blob.size(), 3.0); + } + + #[wasm_bindgen_test] + fn js_buffer_to_bytes() { + let arr = &[1, 2, 3]; + let js_bytes = Uint8Array::new_with_length(arr.len() as u32); + js_bytes.copy_from(arr); + + let bytes = super::js_buffer_to_bytes(&js_bytes); + assert_eq!(bytes.len(), arr.len()); + assert_eq!(bytes, arr); + } +} diff --git a/src/data.rs b/src/data.rs index f6b10d2..45c837a 100644 --- a/src/data.rs +++ b/src/data.rs @@ -24,164 +24,215 @@ impl Data { } impl Data { - const N: usize = 15; + const FILL: usize = 15; + pub const DATA: [Data; 100] = [ + Data { + meaning: "Umbrella", + choices: ["Payung", "Jas Hujan", "Mantel"], + }, + Data { + meaning: "Computer", + choices: ["Komputer", "Papan ketik", "Telepon"], + }, + Data { + meaning: "Book", + choices: ["Buku", "Pensil", "Penghapus"], + }, + Data { + meaning: "Window", + choices: ["Jendela", "Pintu", "Rumah"], + }, + Data { + meaning: "Hospital", + choices: ["Rumah sakit", "Rumah", "Hotel"], + }, + Data { + meaning: "Tree", + choices: ["Pohon", "Kayu", "Tumbuhan"], + }, + Data { + meaning: "Summer", + choices: ["Pantai", "Gurun", "Hutan"], + }, + Data { + meaning: "Rain", + choices: ["Hujan", "Panas", "Mendung"], + }, + Data { + meaning: "Bicycle", + choices: ["Sepeda", "Motor", "Mobil"], + }, + Data { + meaning: "Sport", + choices: ["Olahraga", "Bersepeda", "Naik kendaraan"], + }, + Data { + meaning: "Reading", + choices: ["Membaca", "Menulis", "Melihat"], + }, + Data { + meaning: "Painting", + choices: ["Lukisan", "Gambar", "Tulisan"], + }, + Data { + meaning: "Rainbow", + choices: ["Pelangi", "Hujan", "Awan"], + }, + Data { + meaning: "Cloud", + choices: ["Awan", "Mendung", "Angin"], + }, + Data { + meaning: "Highway", + choices: ["Jalan raya", "Sawah", "Pedesaan"], + }, + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + Data::new(), + ]; pub fn rand() -> Self { - let data = &DATA[0..Self::N]; - let i = util::rand(Self::N); + let data = &Self::DATA[0..Self::FILL]; + let i = util::rand(Self::FILL); let Self { meaning, choices } = data.get(i).unwrap(); Self { - meaning, + meaning: *meaning, choices: *choices, } } } -pub const DATA: [Data; 100] = [ - Data { - meaning: "Umbrella", - choices: ["Payung", "Jas Hujan", "Mantel"], - }, - Data { - meaning: "Computer", - choices: ["Komputer", "Papan ketik", "Telepon"], - }, - Data { - meaning: "Book", - choices: ["Buku", "Pensil", "Penghapus"], - }, - Data { - meaning: "Window", - choices: ["Jendela", "Pintu", "Rumah"], - }, - Data { - meaning: "Hospital", - choices: ["Rumah sakit", "Rumah", "Hotel"], - }, - Data { - meaning: "Tree", - choices: ["Pohon", "Kayu", "Tumbuhan"], - }, - Data { - meaning: "Summer", - choices: ["Pantai", "Gurun", "Hutan"], - }, - Data { - meaning: "Rain", - choices: ["Hujan", "Panas", "Mendung"], - }, - Data { - meaning: "Bicycle", - choices: ["Sepeda", "Motor", "Mobil"], - }, - Data { - meaning: "Sport", - choices: ["Olahraga", "Bersepeda", "Naik kendaraan"], - }, - Data { - meaning: "Reading", - choices: ["Membaca", "Menulis", "Melihat"], - }, - Data { - meaning: "Painting", - choices: ["Lukisan", "Gambar", "Tulisan"], - }, - Data { - meaning: "Rainbow", - choices: ["Pelangi", "Hujan", "Awan"], - }, - Data { - meaning: "Cloud", - choices: ["Awan", "Mendung", "Angin"], - }, - Data { - meaning: "Highway", - choices: ["Jalan raya", "Sawah", "Pedesaan"], - }, - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), - Data::new(), -]; +#[cfg(test)] +mod tests { + use wasm_bindgen_test::wasm_bindgen_test; + + use super::Data; + + #[wasm_bindgen_test] + fn new_data() { + let data = Data::new(); + assert!(data.meaning().is_empty()); + assert_eq!(data.choices().len(), 3); + assert_eq!(data.choices(), ["", "", ""]); + } + + #[wasm_bindgen_test] + fn current_fill_data() { + let fill = Data::FILL; + assert_eq!(fill, 15) + } + + #[wasm_bindgen_test] + fn rand_data() { + let data = Data::rand(); + assert_eq!(data.choices().len(), 3); + data.choices() + .iter() + .for_each(|choice| assert!(!choice.is_empty())); + + assert!(!data.meaning().is_empty()); + } + + #[wasm_bindgen_test] + fn current_data() { + let data = Data::DATA; + assert_eq!(data.len(), 100); + + for (i, data) in data.iter().enumerate() { + if i < Data::FILL { + assert!(!data.meaning.is_empty()); + data.choices() + .iter() + .for_each(|choice| assert!(!choice.is_empty())) + } else { + assert!(data.meaning.is_empty()); + data.choices() + .iter() + .for_each(|choice| assert!(choice.is_empty())) + } + } + } +} diff --git a/src/img.rs b/src/img.rs index f76cde2..92d0929 100644 --- a/src/img.rs +++ b/src/img.rs @@ -42,18 +42,19 @@ pub fn crop_image(buff: &[u8]) -> Vec> { } pub fn create_imgs_url(imgs: &[Vec]) -> Vec { - let mut correct_data = Vec::new(); + let mut urls = Vec::new(); for buff in imgs { let blob = common::bytes_to_js_blob(buff, ImageFormat::Jpeg.to_mime_type()); let url = Url::create_object_url_with_blob(&blob).unwrap(); - correct_data.push(url); + urls.push(url); } - correct_data + urls } +#[allow(unused_variables)] pub async fn load_image(query: &str) -> Vec { #[cfg(not(debug_assertions))] let url = format!( @@ -95,3 +96,17 @@ pub async fn load_image(query: &str) -> Vec { common::js_buffer_to_bytes(&js_buff) } + +#[cfg(test)] +mod tests { + use wasm_bindgen_test::wasm_bindgen_test; + + #[wasm_bindgen_test] + fn create_imgs_url() { + let buff = &[vec![0b01100001], vec![0b01100001], vec![0b01100001]]; + let urls = super::create_imgs_url(buff); + assert_eq!(urls.len(), buff.len()); + urls.iter() + .for_each(|url| assert!(url.starts_with("blob:"))) + } +} diff --git a/src/puzzle.rs b/src/puzzle.rs index b139b3b..42faf91 100644 --- a/src/puzzle.rs +++ b/src/puzzle.rs @@ -4,7 +4,7 @@ use wasm_bindgen::closure::Closure; use wasm_bindgen::JsCast; use web_sys::{DragEvent, Element, Event, HtmlImageElement, HtmlLabelElement}; -use super::common::{self, query_all_element}; +use super::common; use super::data::Data; use super::util; @@ -112,7 +112,9 @@ fn display_choices(data: &Data) { util::randomize(data.choices(), |i, choice| { let i = i.to_string(); - let id = if choice == data.choices()[0] { + let correct = data.choices()[0]; + + let id = if choice == correct { "correct" } else { i.as_str() @@ -149,7 +151,7 @@ fn display_choices(data: &Data) { } fn set_choices_event() { - let input_choices = query_all_element("fieldset input").unwrap(); + let input_choices = common::query_all_element("fieldset input").unwrap(); let click_handler = Closure::new(Box::new(|_| { let v = r#"
"#; diff --git a/src/util.rs b/src/util.rs index f95ab67..f5e7753 100644 --- a/src/util.rs +++ b/src/util.rs @@ -15,7 +15,7 @@ where loop { let count = data.len(); - if data.is_empty() || count == 0 { + if data.is_empty() { break; } @@ -28,11 +28,41 @@ where let d = data.remove(i); - f(count, d); + f(count - 1, d); } } #[cfg(test)] mod tests { - //TODO + use wasm_bindgen_test::wasm_bindgen_test; + + #[wasm_bindgen_test] + fn rand() { + let x = super::rand(10); + assert!(x < 10); + #[allow(clippy::absurd_extreme_comparisons)] + #[allow(unused_comparisons)] + let r = x >= 0; + assert!(r) + } + + #[wasm_bindgen_test] + fn randomize() { + let data = &[1, 2, 3, 4, 5, 6, 7, 8]; + let mut n = 0; + super::randomize(data, |i, v| { + #[allow(clippy::absurd_extreme_comparisons)] + #[allow(unused_comparisons)] + let r = i >= 0; + assert!(r); + assert!(i < 8); + assert!(v >= 1); + assert!(v <= 8); + assert!(data.contains(&v)); + + n += 1; + }); + + assert_eq!(data.len(), n); + } }