diff --git a/.joker b/.joker index 8d9e936..e0557b9 100644 --- a/.joker +++ b/.joker @@ -1 +1,2 @@ -{:known-macros [cljs.test/deftest]} \ No newline at end of file +{:known-macros [cljs.test/deftest] + :known-namespaces [cljs.core]} \ No newline at end of file diff --git a/shadow-cljs.edn b/shadow-cljs.edn index e14759a..621b22a 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -11,8 +11,9 @@ ;; debugging [day8.re-frame/re-frame-10x "0.3.3-react16"] [day8.re-frame/tracing "0.5.1"] + [philoskim/debux "0.4.11"] ;; for CIDER - [cider/cider-nrepl "0.18.0-SNAPSHOT"]] + [cider/cider-nrepl "0.18.0"]] :nrepl {:port 9000} diff --git a/src/cljs/airsonic_ui/audio.cljs b/src/cljs/airsonic_ui/audio/core.cljs similarity index 92% rename from src/cljs/airsonic_ui/audio.cljs rename to src/cljs/airsonic_ui/audio/core.cljs index 297062f..a1747e5 100644 --- a/src/cljs/airsonic_ui/audio.cljs +++ b/src/cljs/airsonic_ui/audio/core.cljs @@ -1,4 +1,7 @@ -(ns airsonic-ui.audio +(ns airsonic-ui.audio.core + "This namespace contains some JS interop code to interact with an audio player + and receive information about the current playback status so we can use it in + our re-frame app." (:require [re-frame.core :as re-frame])) ;; TODO: Manage buffering diff --git a/src/cljs/airsonic_ui/audio/playlist.cljs b/src/cljs/airsonic_ui/audio/playlist.cljs new file mode 100644 index 0000000..04ce733 --- /dev/null +++ b/src/cljs/airsonic_ui/audio/playlist.cljs @@ -0,0 +1,140 @@ +(ns airsonic-ui.audio.playlist + "Implements playlist queues that support different kinds of repetition and + song ordering." + (:refer-clojure :exclude [peek]) + (:require [airsonic-ui.utils.helpers :refer [find-where]] + [debux.cs.core :refer-macros [dbg]])) + +(defrecord Playlist [queue playback-mode repeat-mode] + cljs.core/ICounted + (-count [this] + (count (:queue this)))) + +(defmulti ->playlist + "Creates a new playlist that behaves according to the given playback- and + repeat-mode parameters." + (fn [queue & {:keys [playback-mode #_repeat-mode]}] + playback-mode)) + +(defn- mark-first-song [queue] + (let [[first-idx _] (find-where #(= 0 (:playlist/order %)) queue)] + (assoc-in queue [first-idx :playlist/currently-playing??] true))) + +(defmethod ->playlist :linear + [queue & {:keys [playback-mode repeat-mode]}] + (let [queue (-> (mapv (fn [order song] (assoc song :playlist/order order)) (range) queue) + (mark-first-song))] + (->Playlist queue playback-mode repeat-mode))) + +(defn- -shuffle-songs [queue] + (->> (shuffle (range (count queue))) + (mapv (fn [song order] (assoc song :playlist/order order)) queue))) + +(defmethod ->playlist :shuffled + [queue & {:keys [playback-mode repeat-mode]}] + (let [queue (conj (mapv #(update % :playlist/order inc) (-shuffle-songs (rest queue))) + (assoc (first queue) :playlist/order 0 :playlist/currently-playing?? true))] + (->Playlist queue playback-mode repeat-mode))) + +(defn set-current-song + "Marks a song in the queue as currently playing, given its ID" + [playlist next-idx] + (let [[current-idx _] (find-where :playlist/currently-playing?? (:queue playlist))] + (-> (if current-idx + (update-in playlist [:queue current-idx] dissoc :playlist/currently-playing??) + playlist) + (assoc-in [:queue next-idx :playlist/currently-playing??] true)))) + +(defn set-playback-mode + "Changes the playback mode of a playlist and re-shuffles it if necessary" + [playlist playback-mode] + (if (= playback-mode :shuffled) + ;; for shuffled playlists we reorder the songs make sure that the currently + ;; playing song has order 0 + (let [playlist (->playlist (:queue playlist) :playback-mode playback-mode :repeat-mode (:repeat-mode playlist)) + [current-idx current-song] (find-where :playlist/currently-playing?? (:queue playlist)) + [swap-idx _] (find-where #(= 0 (:playlist/order %)) (:queue playlist))] + (-> (assoc-in playlist [:queue current-idx :playlist/order] 0) + (assoc-in [:queue swap-idx :playlist/order] (:playlist/order current-song)))) + ;; for linear songs we just make sure that the current does not change + (let [[current-idx _] (find-where :playlist/currently-playing?? (:queue playlist))] + (-> (->playlist (:queue playlist) :playback-mode playback-mode :repeat-mode (:repeat-mode playlist)) + (set-current-song current-idx))))) + +(defn set-repeat-mode + "Allows to change the way the next and previous song of a playlist is selected" + [playlist repeat-mode] + (assoc playlist :repeat-mode repeat-mode)) + +(defn peek + "Returns the song in a playlist that is currently playing" + [playlist] + (->> (:queue playlist) + (filter :playlist/currently-playing??) + (first))) + +(defmulti next-song "Advances the currently playing song" :repeat-mode) + +(defmethod next-song :repeat-none + [playlist] + ;; this is pretty easy; get the next song and stop playing at the at + (let [[current-idx current-song] (find-where :playlist/currently-playing?? (:queue playlist)) + [next-idx _] (find-where #(= (:playlist/order %) (inc (:playlist/order current-song))) (:queue playlist))] + (update playlist :queue + (fn [queue] + (cond-> queue + current-idx (update current-idx dissoc :playlist/currently-playing??) + next-idx (assoc-in [next-idx :playlist/currently-playing??] true)))))) + +(defmethod next-song :repeat-single [playlist] playlist) + +(defmethod next-song :repeat-all + [playlist] + (let [[current-idx current-song] (find-where :playlist/currently-playing?? (:queue playlist)) + [next-idx _] (find-where #(= (:playlist/order %) (inc (:playlist/order current-song))) (:queue playlist))] + (-> (update-in playlist [:queue current-idx] dissoc :playlist/currently-playing??) + (update :queue + (fn [queue] + ;; we need special treatment here if we're playing the last song and + ;; have a shuffled playlist because we need to re-shuffle + (if next-idx + (assoc-in queue [next-idx :playlist/currently-playing??] true) + (case (:playback-mode playlist) + :linear (assoc-in queue [0 :playlist/currently-playing??] true) + :shuffled (let [queue' (-shuffle-songs queue) + [next-idx _] (find-where #(= (:playlist/order %) 0) queue')] + (assoc-in queue' [next-idx :playlist/currently-playing??] true))))))))) + +(defmulti previous-song "Goes back along the playback queue" :repeat-mode) + +(defmethod previous-song :repeat-single [playlist] playlist) + +(defmethod previous-song :repeat-none [playlist] + (let [[current-idx current-song] (find-where :playlist/currently-playing?? (:queue playlist)) + [next-idx _] (find-where #(= (:playlist/order %) (dec (:playlist/order current-song))) (:queue playlist))] + (set-current-song playlist (or next-idx current-idx)))) + +(defmethod previous-song :repeat-all [playlist] + (let [[_ current-song] (find-where :playlist/currently-playing?? (:queue playlist)) + [next-idx _] (find-where #(= (:playlist/order %) + (rem (dec (:playlist/order current-song)) (count playlist))) + (:queue playlist))] + (if next-idx + (set-current-song playlist next-idx) + (if (= :shuffled (:playback-mode playlist)) + (let [highest-order (dec (count playlist)) + playlist (update playlist :queue -shuffle-songs) + [last-idx _] (find-where #(= (:playlist/order %) highest-order) (:queue playlist))] + (set-current-song playlist last-idx)) + (set-current-song playlist (mod (dec (:playlist/order current-song)) (count playlist))))))) + +(defn enqueue-last [playlist song] + (let [highest-order (last (sort (map :playlist/order (:queue playlist))))] + (update playlist :queue conj (assoc song :playlist/order (inc highest-order))))) + +(defn enqueue-next [playlist song] + (let [[_ current-song] (find-where :playlist/currently-playing?? (:queue playlist))] + (update playlist :queue + (fn [queue] + (-> (mapv #(if (> (:playlist/order %) (:playlist/order current-song)) (update % :playlist/order inc) %) queue) + (conj (assoc song :playlist/order (inc (:playlist/order current-song))))))))) diff --git a/src/cljs/airsonic_ui/core.cljs b/src/cljs/airsonic_ui/core.cljs index d61493e..4bb702d 100644 --- a/src/cljs/airsonic_ui/core.cljs +++ b/src/cljs/airsonic_ui/core.cljs @@ -5,7 +5,7 @@ [day8.re-frame.http-fx] [akiroz.re-frame.storage :as storage] ;; our app - [airsonic-ui.audio] ; <- just registers effects here + [airsonic-ui.audio.core] ; <- just registers effects here [airsonic-ui.events :as events] [airsonic-ui.views :as views] [airsonic-ui.config :as config])) diff --git a/src/cljs/airsonic_ui/events.cljs b/src/cljs/airsonic_ui/events.cljs index dd3e619..1d91b6d 100644 --- a/src/cljs/airsonic_ui/events.cljs +++ b/src/cljs/airsonic_ui/events.cljs @@ -3,8 +3,7 @@ [ajax.core :as ajax] [airsonic-ui.routes :as routes] [airsonic-ui.db :as db] - [airsonic-ui.utils.api :as api] - [day8.re-frame.tracing :refer-macros [fn-traced defn-traced]])) ; <- useful to debug handlers + [airsonic-ui.utils.api :as api])) (re-frame/reg-fx ;; a simple effect to keep println statements out of our event handlers diff --git a/src/cljs/airsonic_ui/utils/helpers.cljs b/src/cljs/airsonic_ui/utils/helpers.cljs new file mode 100644 index 0000000..b9e86b7 --- /dev/null +++ b/src/cljs/airsonic_ui/utils/helpers.cljs @@ -0,0 +1,10 @@ +(ns airsonic-ui.utils.helpers + "Assorted helper functions") + +(defn find-where + "Returns the the first item in `coll` with its index for which `(p song)` + is truthy" + [p coll] + (->> (map-indexed vector coll) + (reduce (fn [_ [idx song]] + (when (p song) (reduced [idx song]))) nil))) diff --git a/test/cljs/airsonic_ui/audio/core_test.cljs b/test/cljs/airsonic_ui/audio/core_test.cljs new file mode 100644 index 0000000..4866db4 --- /dev/null +++ b/test/cljs/airsonic_ui/audio/core_test.cljs @@ -0,0 +1,23 @@ +(ns airsonic-ui.audio.core-test + (:require [airsonic-ui.audio.core :as audio] + [airsonic-ui.audio.playlist-test :as p] + [airsonic-ui.fixtures :as fixtures] + [cljs.test :refer [deftest testing is]])) + +(enable-console-print!) + +(deftest current-song-subscription + (letfn [(current-song [db] + (-> (audio/summary db [:audio/summary]) + (audio/current-song [:audio/current-song])))] + (testing "Should provide information about the song" + (= fixtures/song (current-song p/fixture))))) + +(deftest playback-status-subscription + (letfn [(is-playing? [playback-status] + (audio/is-playing? playback-status [:audio/is-playing?]))] + (testing "Should be shown as not playing when the song is paused or has ended" + (is (not (is-playing? {:paused? true, :ended? false}))) + (is (not (is-playing? {:paused? false, :ended? true})))) + (testing "Should be shown as playing when the song is not paused or finished" + (is (is-playing? {:paused? false, :ended? false}))))) diff --git a/test/cljs/airsonic_ui/audio/playlist_test.cljs b/test/cljs/airsonic_ui/audio/playlist_test.cljs new file mode 100644 index 0000000..37a1546 --- /dev/null +++ b/test/cljs/airsonic_ui/audio/playlist_test.cljs @@ -0,0 +1,259 @@ +(ns airsonic-ui.audio.playlist-test + (:require [cljs.test :refer [deftest testing is]] + [airsonic-ui.audio.playlist :as playlist] + [airsonic-ui.utils.helpers :refer [find-where]] + [airsonic-ui.fixtures :as fixtures] + [airsonic-ui.test-helpers :as helpers] + [debux.cs.core :refer-macros [dbg]])) + +(enable-console-print!) + +(defn- song [] + (hash-map :id (rand-int 9999) + :coverArt (rand-int 9999) + :year (+ 1900 (rand-int 118)) + :artist (helpers/rand-str) + :artistId (rand-int 100000) + :title (helpers/rand-str) + :album (helpers/rand-str))) + +(defn- song-queue + "Generates a seq of n different songs" + [n] + (let [r-int (atom 0)] + (with-redefs [rand-int #(mod (swap! r-int inc) %1)] + (repeatedly n song)))) + +(def fixture + {:audio {:current-song fixtures/song + :playlist (song-queue 20) + :playback-status fixtures/playback-status}}) + +(defn- same-song? [a b] (= (:id a) (:id b))) + +(deftest playlist-creation + (testing "Playlist creation" + (testing "should give us the correct current song" + (let [queue (song-queue 10)] + (doseq [playback-mode [:linear :shuffled] + repeat-mode [:repeat-none :repeat-single :repeat-all]] + (is (same-song? (first queue) + (-> (playlist/->playlist queue :playback-mode playback-mode :repeat-mode repeat-mode) + (playlist/peek))) + (str playback-mode ", " repeat-mode))))) + (testing "should give us a playlist with the correct number of tracks" + (let [queue (song-queue 100)] + (doseq [playback-mode [:linear :shuffled] + repeat-mode [:repeat-none :repeat-single :repeat-all]] + (is (= (count queue) + (count (playlist/->playlist queue :playback-mode playback-mode :repeat-mode repeat-mode))) + (str playback-mode ", " repeat-mode))))))) + +(deftest changing-playback-mode + (testing "Changing playback mode" + (testing "from linear to shuffled" + (let [queue (song-queue 10) + linear (playlist/->playlist queue :playback-mode :linear :repeat-mode :repeat-none) + shuffled (playlist/set-playback-mode linear :shuffled)] + (testing "should re-order the tracks" + (is (not= (map :playlist/order (:queue shuffled)) (map :playlist/order (:queue linear))))) + (testing "should not change the currently playing track" + (is (same-song? (playlist/peek linear) (playlist/peek shuffled)))) + (testing "should not change the repeat mode" + (is (= (:repeat-mode shuffled) (:repeat-mode linear)))))) + (testing "from shuffled to linear" + (let [queue (song-queue 10) + shuffled (playlist/->playlist queue :playback-mode :shuffled :repeat-mode :repeat-none) + linear (playlist/set-playback-mode shuffled :linear)] + (testing "should set the correct order for tracks" + (is (every? #(apply same-song? %) (interleave queue (:queue linear)))) + (is (< (:playlist/order (first (:queue linear))) (:playlist/order (last (:queue linear)))))) + (testing "should not change the currently playing track" + (is (same-song? (playlist/peek linear) (playlist/peek shuffled)))) + (testing "should not change the repeat mode" + (is (= (:repeat-mode shuffled) (:repeat-mode linear)))))))) + +(deftest changing-repeat-mode + (testing "Changing the repeat mode" + (testing "should not change the playback mode" + (doseq [playback-mode '(:linear :shuffled) + repeat-mode '(:repeat-none :repeat-single :repeat-all) + next-repeat-mode '(:repeat-none :repeat-single :repeat-all)] + (let [playlist (-> (playlist/->playlist (song-queue 1) :playback-mode playback-mode :repeat-mode repeat-mode) + (playlist/set-repeat-mode next-repeat-mode))] + (is (= playback-mode (:playback-mode playlist))) + (is (= next-repeat-mode (:repeat-mode playlist)) + (str "from " repeat-mode " to " next-repeat-mode))))))) + +(deftest linear-next-song + (testing "Should follow the same order as the queue used for creation" + (doseq [repeat-mode [:repeat-none :repeat-all]] + (let [queue (song-queue 5) + playlist (playlist/->playlist queue :playback-mode :linear :repeat-mode repeat-mode)] + (is (same-song? (nth queue 1) (-> (playlist/next-song playlist) + (playlist/peek))) + (str repeat-mode ", skipped once")) + (is (same-song? (nth queue 2) (-> (playlist/next-song playlist) + (playlist/next-song) + (playlist/peek))) + (str repeat-mode ", skipped twice"))))) + (testing "Should go back to the first song when repeat-mode is all and we played the last song") + (testing "Should always give the same track when repeat-mode is single" + (let [queue (song-queue 3) + playlist (playlist/->playlist queue :playback-mode :linear :repeat-mode :repeat-single) + played-back (map playlist/peek (iterate playlist/next-song playlist))] + (is (same-song? (first queue) (nth played-back 0))) + (is (same-song? (first queue) (nth played-back 1))) + (is (same-song? (first queue) (nth played-back 2))) + (is (same-song? (first queue) (nth played-back 3)) "wrapping around"))) + (testing "Should stop playing at the end of the queue when repeat-mode is none" + (is (nil? (-> (song-queue 1) + (playlist/->playlist :playback-mode :linear :repeat-mode :repeat-none) + (playlist/next-song) + (playlist/peek)))))) + +(deftest shuffled-next-song + (testing "Should play every track once when called for the entire queue" + (doseq [repeat-mode '(:repeat-none :repeat-all)] + (let [length 10 + playlist (playlist/->playlist (song-queue length) :playback-mode :shuffled :repeat-mode repeat-mode) + played-tracks (->> (iterate playlist/next-song playlist) + (map playlist/peek) + (take length))] + (is (= (count played-tracks) (count (set played-tracks))) + (str repeat-mode))))) + (testing "Should re-shuffle the playlist when wrapping around and repeat-mode is all" + (let [playlist (playlist/->playlist (song-queue 100) :playback-mode :shuffled :repeat-mode :repeat-all) + [last-idx _] (find-where #(= (:playlist/order %) 99) (:queue playlist))] + (is (not= (map :playlist/order (:queue playlist)) + (map :playlist/order (:queue (-> (playlist/set-current-song playlist last-idx) + (playlist/next-song)))))))) + (testing "Should always give the same track when repeat-mode is single" + (let [queue (song-queue 3) + playlist (playlist/->playlist queue :playback-mode :shuffled :repeat-mode :repeat-single) + played-back (map playlist/peek (iterate playlist/next-song playlist))] + (is (same-song? (first queue) (nth played-back 0))) + (is (same-song? (first queue) (nth played-back 1))) + (is (same-song? (first queue) (nth played-back 2))) + (is (same-song? (first queue) (nth played-back 3)) "wrapping around"))) + (testing "Should stop playing at the end of the queue when repeat-mode is none" + (is (nil? (-> (song-queue 1) + (playlist/->playlist :playback-mode :linear :repeat-mode :repeat-none) + (playlist/next-song) + (playlist/peek)))))) + +(deftest linear-previous-song + (testing "Should always give the same track when repeat-mode is single" + (let [queue (song-queue 3) + playlist (playlist/->playlist queue :playback-mode :linear :repeat-mode :repeat-single) + played-back (map playlist/peek (iterate playlist/next-song playlist))] + (is (same-song? (first queue) (nth played-back 0))) + (is (same-song? (first queue) (nth played-back 1))) + (is (same-song? (first queue) (nth played-back 2))) + (is (same-song? (first queue) (nth played-back 3)) "wrapping around"))) + (testing "Should keep the linear order when repeat-mode is not single" + (doseq [repeat-mode '(:repeat-none :repeat-all)] + (let [queue (song-queue 3) + playlist (playlist/->playlist queue :playback-mode :linear :repeat-mode repeat-mode)] + (is (same-song? (nth queue 1) (-> (playlist/next-song playlist) + (playlist/next-song) + (playlist/previous-song) + (playlist/peek))))))) + (testing "Should repeatedly give the first song when repeat-mode is none" + (let [queue (song-queue 3) + playlist (playlist/->playlist queue :playback-mode :linear :repeat-mode :repeat-none)] + (is (same-song? (first queue) (-> (playlist/previous-song playlist) + (playlist/peek)))))) + (testing "Should wrap around to last song when repeat-mode is all" + (let [queue (song-queue 3) + playlist (playlist/->playlist queue :playback-mode :linear :repeat-mode :repeat-all)] + (is (same-song? (last queue) (-> (playlist/previous-song playlist) + (playlist/peek))))))) + +(deftest shuffled-previous-song + (with-redefs [shuffle reverse] + (testing "Should always give the same track when repeat-mode is single" + (let [queue (song-queue 3) + playlist (playlist/->playlist queue :playback-mode :shuffled :repeat-mode :repeat-single) + played-back (map playlist/peek (iterate playlist/next-song playlist))] + (is (same-song? (first queue) (nth played-back 0))) + (is (same-song? (first queue) (nth played-back 1))) + (is (same-song? (first queue) (nth played-back 2))) + (is (same-song? (first queue) (nth played-back 3)) "wrapping around"))) + (testing "Should keep the playing order when repeat-mode is not single" + (doseq [repeat-mode '(:repeat-none :repeat-all)] + (let [queue (song-queue 3) + playlist (playlist/->playlist queue :playback-mode :shuffled :repeat-mode repeat-mode)] + (is (same-song? (playlist/peek playlist) + (-> playlist + (playlist/next-song) + (playlist/previous-song) + (playlist/peek))) + (str "for repeat mode " repeat-mode)) + (is (same-song? (-> (playlist/next-song playlist) + (playlist/peek)) + (-> (playlist/next-song playlist) + (playlist/next-song) + (playlist/previous-song) + (playlist/peek))) + (str "for repeat mode " repeat-mode))))) + (testing "Should re-shuffle when repeat-mode is all and we go back to before the first track" + (let [playlist (with-redefs [shuffle identity] + (playlist/->playlist (song-queue 10) :playback-mode :shuffled :repeat-mode :repeat-all)) + playlist' (with-redefs [shuffle reverse] + (playlist/previous-song playlist))] + (is (not= (map :playlist/order (:queue playlist)) (map :playlist/order (:queue playlist')))))))) + +(deftest set-current-song + (testing "Should correctly set the new song" + (let [queue (song-queue 3) + playlist (playlist/->playlist queue :playback-mode :shuffled :repeat-mode :repeat-single) + current-track (first queue) + next-track (-> (playlist/set-current-song playlist 1) + (playlist/peek))] + (is (not (nil? next-track))) + (is (not (same-song? current-track next-track)))))) + +(deftest enqueue-last + (testing "Should make sure the song is played last" + (doseq [playback-mode '(:linear :shuffled) + repeat-mode '(:repeat-none :repeat-all)] + (let [length 5, queue (song-queue length) + playlist (with-redefs [shuffle identity] + (playlist/->playlist queue :playback-mode playback-mode :repeat-mode repeat-mode)) + played-back (->> (iterate playlist/next-song playlist) + (take (dec length)) + (map #(:id (playlist/peek %))) + (set)) + to-enqueue (song) + playlist' (playlist/enqueue-last playlist to-enqueue)] + (is (nil? (played-back (-> (->> (iterate playlist/next-song playlist') + (map playlist/peek)) + (nth length) + (:id)))) + (str "for " playback-mode ", " repeat-mode))))) + (testing "Should not change the order of the songs already in queue" + (doseq [playback-mode '(:linear :shuffled) + repeat-mode '(:repeat-none :repeat-all)] + (let [length 5, queue (song-queue length) + playlist (playlist/->playlist queue :playback-mode playback-mode :repeat-mode repeat-mode) + played-back-songs (fn played-back-songs [playlist] + (->> (iterate playlist/next-song playlist) + (take length) + (map playlist/peek) + (map :playlist/order))) + played-back (played-back-songs playlist) + played-back' (played-back-songs (playlist/enqueue-last playlist (song)))] + (is (= played-back played-back') + (str "for " playback-mode ", " repeat-mode)))))) + +(deftest enqueue-next + (testing "Should play the song after the currently playing song" + (doseq [playback-mode '(:linear :shuffled) + repeat-mode '(:repeat-none :repeat-all)] + (let [length 5, queue (song-queue length) + playlist (playlist/->playlist queue :playback-mode playback-mode :repeat-mode repeat-mode) + next-song (song)] + (is (same-song? next-song (-> (playlist/enqueue-next playlist next-song) + (playlist/next-song) + (playlist/peek)))))))) diff --git a/test/cljs/airsonic_ui/audio_test.cljs b/test/cljs/airsonic_ui/audio_test.cljs deleted file mode 100644 index 2663774..0000000 --- a/test/cljs/airsonic_ui/audio_test.cljs +++ /dev/null @@ -1,40 +0,0 @@ -(ns airsonic-ui.audio-test - (:require [airsonic-ui.audio :as audio] - [airsonic-ui.fixtures :as fixtures] - [airsonic-ui.test-helpers :as helpers] - [cljs.test :refer [deftest testing is]])) - -(enable-console-print!) - -(defn- simulate-playlist [n ] - (repeatedly n #(hash-map :id (rand-int 9999) - :coverArt (rand-int 9999) - :year (+ 1900 (rand-int 118)) - :artist (helpers/rand-str) - :aristId (rand-int 100000) - :title (helpers/rand-str) - :album (helpers/rand-str)))) - -(def fixture - {:audio {:current-song fixtures/song - :playlist (simulate-playlist 20) - :playback-status fixtures/playback-status}}) - -(deftest current-song - (letfn [(current-song [db] - (-> (audio/summary db [:audio/summary]) - (audio/current-song [:audio/current-song])))] - (testing "Should provide information about the song" - (= fixtures/song (current-song fixture))))) - -(deftest playback-status - (letfn [(is-playing? [playback-status] - (audio/is-playing? playback-status [:audio/is-playing?]))] - (testing "Should be shown as not playing when the song is paused or has ended" - (is (not (is-playing? {:paused? true, :ended? false}))) - (is (not (is-playing? {:paused? false, :ended? true})))) - (testing "Should be shown as playing when the song is not paused or finished" - (is (is-playing? {:paused? false, :ended? false}))))) - -#_(deftest current-playlist - (testing "Should show the complete playlist")) diff --git a/test/cljs/airsonic_ui/utils/helpers_test.cljs b/test/cljs/airsonic_ui/utils/helpers_test.cljs new file mode 100644 index 0000000..a53c34d --- /dev/null +++ b/test/cljs/airsonic_ui/utils/helpers_test.cljs @@ -0,0 +1,14 @@ +(ns airsonic-ui.utils.helpers-test + (:require [cljs.test :refer [deftest testing is]] + [airsonic-ui.utils.helpers :as helpers])) + +(deftest find-where + (testing "Finds the correct item and index" + (is (= [0 1] (helpers/find-where (partial = 1) (range 1 10)))) + (is (= [2 {:foo true, :bar false}] (helpers/find-where :foo '({} + {:foo false + :bar true} + {:foo true + :bar false}))))) + (testing "Returns nil when nothing is found" + (is (nil? (helpers/find-where (partial = 2) (range 2))))))