1
0
Fork 0
mirror of https://github.com/heyarne/airsonic-ui.git synced 2026-05-06 18:33:38 +02:00

Improvements to currently playing queue (#48)

* First sloppy import of code from heyarne/reagent-movable

* Consistently use "current queue" to avoid confusion

* Update shadow-cljs, re-frame and debux

* Solve styling problem when sorting table rows

* Make sortable component more reusable

* Refactor playlist to use a sorted-map

* Make sure current queue is displayed again

* Fix sorting when converting a shuffled into a linear playlist

* Implement set-current-track

* Implement song-move in playlist

* Add autoprefixer

* Implement drag and drop reordering in current queue

* Fix broken dev sass build

* Bump some dependencies

* Move airsonic-ui.views.icon to bulma.icon

* Implement reusable dropdown in bulma.dropdown

* Immediately render reordered tracks, reimplement actions in album view

* Use new song-table on search result page

* Make song-table more reusable

* Remove current song

* Implement go to source in current queue

* Remove unused song view
This commit is contained in:
heyarne 2019-03-12 15:22:13 +01:00 committed by GitHub
commit 8bf222a6e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 1773 additions and 869 deletions

View file

@ -1,17 +1,20 @@
(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]
#_[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]
;; NOTE: Should the subscription be moved to the playlist.cljs?
#_(testing "Should provide information about the 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 p/fixture)))))
(= fixtures/song (current-song p/fixture))))
(testing "Should work fine when no song is playing"
(is (nil? (audio/current-song nil [:audio/current-song])))))
(deftest playback-status-subscription
(letfn [(is-playing? [playback-status]

View file

@ -1,29 +1,12 @@
(ns airsonic-ui.audio.playlist-test
(:require [cljs.test :refer [deftest testing is]]
[airsonic-ui.audio.playlist :as playlist]
[airsonic-ui.helpers :refer [find-where]]
[airsonic-ui.fixtures :as fixtures]
[airsonic-ui.test-helpers :as helpers]
[debux.cs.core :refer-macros [dbg]]))
[airsonic-ui.test-helpers :refer [song song-queue]]
#_[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)
@ -33,14 +16,22 @@
(deftest playlist-creation
(testing "Playlist creation"
(testing "should give us the correct current song"
(testing "should give us the correct current song for linear playback-mode"
(let [queue (song-queue 10)]
(doseq [playback-mode [:linear :shuffled]
repeat-mode [:repeat-none :repeat-single :repeat-all]]
(doseq [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)))))
(-> (playlist/->playlist queue :playback-mode :linear :repeat-mode repeat-mode)
(playlist/current-song)))
(str "repeat-mode: " repeat-mode)))))
(testing "any current song for shuffled playback mode"
(let [queue (song-queue 10)]
(doseq [repeat-mode [:repeat-none :repeat-single :repeat-all]]
(is (some? ((set queue)
(-> (playlist/->playlist queue :playback-mode :linear :repeat-mode repeat-mode)
(playlist/current-song))))
(str "repeat-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]
@ -55,21 +46,31 @@
(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 indicate the new playback mode"
(is (= :linear (:playback-mode linear)))
(is (= :shuffled (:playback-mode shuffled))))
(testing "should re-order the tracks"
(is (not= (map :playlist/order (:queue shuffled)) (map :playlist/order (:queue linear)))))
(is (not= (:items shuffled) (:items linear))))
(testing "should not change the currently playing track"
(is (same-song? (playlist/peek linear) (playlist/peek shuffled))))
(is (same-song? (playlist/current-song linear) (playlist/current-song 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 indicate the new playback mode"
(is (= :linear (:playback-mode linear)))
(is (= :shuffled (:playback-mode shuffled))))
(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))))))
(let [linear-order (comp :playlist/linear-order meta)]
(is (every? #(apply same-song? %) (interleave queue (vals (:items linear)))))
;; every song should have a smaller order than its successor
(is (->> (map linear-order (vals (:items linear)))
(partition 2 1)
(every? (fn [[a b]] (< a b)))))))
(testing "should not change the currently playing track"
(is (same-song? (playlist/peek linear) (playlist/peek shuffled))))
(is (same-song? (playlist/current-song linear) (playlist/current-song shuffled))))
(testing "should not change the repeat mode"
(is (= (:repeat-mode shuffled) (:repeat-mode linear))))))))
@ -91,17 +92,18 @@
(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)))
(playlist/current-song)))
(str repeat-mode ", skipped once"))
(is (same-song? (nth queue 2) (-> (playlist/next-song playlist)
(playlist/next-song)
(playlist/peek)))
(playlist/current-song)))
(str repeat-mode ", skipped twice")))))
;; TODO: Write this test
(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))]
played-back (map playlist/current-song (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)))
@ -110,7 +112,7 @@
(is (nil? (-> (song-queue 1)
(playlist/->playlist :playback-mode :linear :repeat-mode :repeat-none)
(playlist/next-song)
(playlist/peek))))))
(playlist/current-song))))))
(deftest shuffled-next-song
(testing "Should play every track once when called for the entire queue"
@ -118,35 +120,34 @@
(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)
(map playlist/current-song)
(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"
(testing "Should keep the song order 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))))))))
next-playlist (-> (playlist/set-current-song playlist 99)
(playlist/next-song))]
(= (playlist/current-song playlist)
(playlist/current-song next-playlist))))
(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")))
(let [playlist (playlist/->playlist (song-queue 10) :playback-mode :shuffled :repeat-mode :repeat-single)
played-back (map playlist/current-song (iterate playlist/next-song playlist))]
(dotimes [i 3]
(is (same-song? (nth played-back i) (nth played-back (inc i)))))))
(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))))))
(playlist/current-song))))))
(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))]
played-back (map playlist/current-song (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)))
@ -158,61 +159,61 @@
(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"
(playlist/current-song)))))))
;; TODO: Should it?
#_(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))))))
(playlist/current-song))))))
(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)))))))
(playlist/current-song)))))))
(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")))
played-back (map playlist/current-song (iterate playlist/next-song playlist))]
(dotimes [i 3]
(is (same-song? (nth played-back i) (nth played-back (inc i)))))))
(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)
(is (same-song? (playlist/current-song playlist)
(-> playlist
(playlist/next-song)
(playlist/previous-song)
(playlist/peek)))
(playlist/current-song)))
(str "for repeat mode " repeat-mode))
(is (same-song? (-> (playlist/next-song playlist)
(playlist/peek))
(playlist/current-song))
(-> (playlist/next-song playlist)
(playlist/next-song)
(playlist/previous-song)
(playlist/peek)))
(playlist/current-song)))
(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'))))))))
(testing "Should keep the song order when repeat-mode is all and we go back to before the first track"
(let [playlist (playlist/->playlist (song-queue 10) :playback-mode :shuffled :repeat-mode :repeat-all)
next-playlist (-> (playlist/previous-song playlist)
(playlist/set-current-song 0))]
(is (= (playlist/current-song playlist)
(playlist/current-song next-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))))))
(doseq [repeat-mode [:repeat-all :repeat-none]]
(let [queue (song-queue 3)
playlist (playlist/->playlist queue :playback-mode :shuffled :repeat-mode repeat-mode)
next-track (-> (playlist/set-current-song playlist 1)
(playlist/current-song))]
(is (not (nil? next-track)))
(is (not (same-song? (playlist/current-song playlist)
next-track)))))))
(deftest enqueue-last
(testing "Should make sure the song is played last"
@ -223,12 +224,12 @@
(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 %)))
(map #(:id (playlist/current-song %)))
(set))
to-enqueue (song)
playlist' (playlist/enqueue-last playlist to-enqueue)]
(is (nil? (played-back (-> (->> (iterate playlist/next-song playlist')
(map playlist/peek))
(map playlist/current-song))
(nth length)
(:id))))
(str "for " playback-mode ", " repeat-mode)))))
@ -240,7 +241,7 @@
played-back-songs (fn played-back-songs [playlist]
(->> (iterate playlist/next-song playlist)
(take length)
(map playlist/peek)
(map playlist/current-song)
(map :playlist/order)))
played-back (played-back-songs playlist)
played-back' (played-back-songs (playlist/enqueue-last playlist (song)))]
@ -249,11 +250,92 @@
(deftest enqueue-next
(testing "Should play the song after the currently playing song"
(doseq [playback-mode '(:linear :shuffled)
repeat-mode '(:repeat-none :repeat-all)]
(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))))))))
(playlist/current-song))))))))
(deftest move-track
(testing "Should correctly set the new order"
(doseq [playback-mode [:linear :shuffled]
repeat-mode [:repeat-none :repeat-all :repeat-single]]
(let [n-songs 10
queue (song-queue n-songs)
playlist (playlist/->playlist queue :repeat-mode repeat-mode :playback-mode playback-mode)]
(is (same-song? (-> (playlist/next-song playlist)
(playlist/next-song)
(playlist/current-song))
(-> (playlist/move-song playlist 2 1)
(playlist/next-song)
(playlist/current-song)))))))
(testing "Should update the currently playing track's index"
(testing "when inserting a track before"
(doseq [playback-mode [:linear :shuffled]
repeat-mode [:repeat-none :repeat-all :repeat-single]]
(let [n-songs 10
queue (song-queue n-songs)
playlist (playlist/->playlist queue :repeat-mode repeat-mode :playback-mode playback-mode)]
(is (= 4 (-> (playlist/set-current-song playlist 3)
(playlist/move-song 5 3)
:current-idx))))))
(testing "when moving a track behind it"
(doseq [playback-mode [:linear :shuffled]
repeat-mode [:repeat-none :repeat-all :repeat-single]]
(let [n-songs 10
queue (song-queue n-songs)
playlist (playlist/->playlist queue :repeat-mode repeat-mode :playback-mode playback-mode)]
(is (= 2 (-> (playlist/set-current-song playlist 3)
(playlist/move-song 2 5)
:current-idx))))))
(testing "when moving it"
(doseq [playback-mode [:linear :shuffled]
repeat-mode [:repeat-none :repeat-all :repeat-single]]
(let [n-songs 10
queue (song-queue n-songs)
playlist (playlist/->playlist queue :repeat-mode repeat-mode :playback-mode playback-mode)]
(is (= 7 (-> (playlist/set-current-song playlist 3)
(playlist/move-song 3 7)
:current-idx))))))
(testing "when the current track is outside of the modified range"
(doseq [playback-mode [:linear :shuffled]
repeat-mode [:repeat-none :repeat-all :repeat-single]]
(let [n-songs 10
queue (song-queue n-songs)
playlist (playlist/->playlist queue :repeat-mode repeat-mode :playback-mode playback-mode)]
(is (= 3 (-> (playlist/set-current-song playlist 3)
(playlist/move-song 4 7)
:current-idx))))))))
(deftest remove-song
(with-redefs [shuffle identity]
(testing "Should remove a single song from the playlist"
(doseq [playback-mode [:linear :shuffled]
repeat-mode [:repeat-none :repeat-all :repeat-single]]
(let [n-songs 10
queue (song-queue n-songs)
playlist (playlist/->playlist queue :repeat-mode repeat-mode :playback-mode playback-mode)
first-removed (playlist/remove-song playlist 0)
middle-removed (playlist/remove-song playlist 5)
last-removed (playlist/remove-song playlist 9)
song-not-in-list? (fn [song playlist]
(every? #(not (same-song? % song))
(vals (:items playlist))))]
(is (= 9 (count first-removed) (count middle-removed) (count last-removed)))
(is (song-not-in-list? (first queue) first-removed))
(is (same-song? (second queue) (get (:items first-removed) 0)))
(is (song-not-in-list? (nth queue 5) middle-removed))
(is (same-song? (nth queue 6) (get (:items middle-removed) 5)))
(is (song-not-in-list? (last queue) last-removed)))))
(testing "Should pause if the currently playing song is removed"
(doseq [playback-mode [:linear :shuffled]
repeat-mode [:repeat-none :repeat-all :repeat-single]]
(let [n-songs 10
queue (song-queue n-songs)]
(is (nil? (-> (playlist/->playlist queue :repeat-mode repeat-mode :playback-mode playback-mode)
(playlist/set-current-song 5)
(playlist/remove-song 5)
(playlist/current-song)))))))))

View file

@ -1,10 +1,39 @@
(ns airsonic-ui.components.audio-player.events-test
(:require [cljs.test :refer-macros [deftest testing is]]
[airsonic-ui.test-helpers :refer [dispatches?]]
[airsonic-ui.audio.core :as audio]
[airsonic-ui.audio.playlist :as playlist]
[airsonic-ui.fixtures :as fixtures]
[airsonic-ui.test-helpers :refer [dispatches? song-queue]]
[airsonic-ui.components.audio-player.events :as events]))
(deftest song-has-ended
(testing "Should play the next song when current song has ended"
(is (not (dispatches? (events/audio-update {} [:audio/update {:ended? false}]) :audio-player/next-song)))
(is (dispatches? (events/audio-update {} [:audio/update {:ended? true}]) :audio-player/next-song))))
(deftest changing-current-song
(testing "Should correctly set the current song index"
(doseq [playback-mode [:linear :shuffled]
repeat-mode [:repeat-none :repeat-single :repeat-all]]
(let [n-songs 100
next-idx (rand-int n-songs)
fixture {:db {:credentials fixtures/credentials
:audio {:current-playlist (playlist/->playlist (song-queue n-songs) :playback-mode playback-mode :repeat-mode repeat-mode)}}}
effects (events/set-current-song fixture [:audio/set-current-song next-idx])]
(is (= next-idx
(-> (:db effects)
(audio/summary [:audio/summary])
(audio/current-playlist [:audio/current-playlist])
(:current-idx)))
(str "for playback-mode " playback-mode " and repeat-mode " repeat-mode))
(is (contains? effects :audio/play))))))
(deftest removing-currently-playing-song
(testing "Should stop all audio when removing the currently playing song"
(doseq [playback-mode [:linear :shuffled]
repeat-mode [:repeat-none :repeat-single :repeat-all]]
(let [n-songs 100
fixture {:db {:credentials fixtures/credentials
:audio {:current-playlist (playlist/->playlist (song-queue n-songs) :playback-mode playback-mode :repeat-mode repeat-mode)}}}]
(is (contains? (events/remove-song fixture [:audio/remove-song 0]) :audio/stop))
(is (not (contains? (events/remove-song fixture [:audio/remove-song 99]) :audio/stop)))))))

View file

@ -2,17 +2,6 @@
(:require [cljs.test :refer [deftest testing is]]
[airsonic-ui.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))))))
(deftest add-classes
(testing "Should add classes to a simple hiccup keyword"
(is (= :div.foo (helpers/add-classes :div :foo)))

View file

@ -17,3 +17,19 @@
(from arr #(-> (str 0 (.toString % 16))
(.substr -2)))
(join "")))))
(defn song []
(hash-map :id (rand-int 9999)
:coverArt (rand-int 9999)
:year (+ 1900 (rand-int 118))
:artist (rand-str)
:artistId (rand-int 100000)
:title (rand-str)
:album (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))))

View file

@ -0,0 +1,40 @@
(ns bulma.dropdown-test
(:require [cljs.test :refer-macros [deftest testing is]]
[bulma.dropdown.subs :as sub]
[bulma.dropdown.events :as ev]))
;; NOTE: Here as well; this code is very much like the modal code
;; Not sure whether to explicitly duplicate it or provide some smarter
;; abstraction that's harder to understand at first sight
(enable-console-print!)
(deftest bulma-dropdowns
(testing "Should create a collection of dropdowns if there is none"
(let [new-db (ev/show-dropdown {} [::ev/show :some-dropdown-id])]
(is (= :some-dropdown-id (sub/visible-dropdown new-db [::sub/visible-dropdown])))))
(testing "Should hide other dropdowns when displaying a new one"
(let [dropdown-ids [:some-id-1 :some-id-2 :some-id-3]
new-db (reduce (fn [db dropdown-id]
(ev/show-dropdown db [::ev/show dropdown-id]))
{} dropdown-ids)]
(is (= :some-id-3 (sub/visible-dropdown new-db [::sub/visible-dropdown])))))
(testing "Should remove a dropdown from the collection when we hide it"
(let [dropdown-ids [:some-id-1 :some-id-2 :some-id-3]
new-db (-> (reduce (fn [db dropdown-id]
(ev/show-dropdown db [::ev/show dropdown-id]))
{} dropdown-ids)
(ev/hide-dropdown [::ev/hide]))]
(is (not (some? (sub/visible-dropdown new-db [::sub/visible-dropdown]))))))
(testing "Should tell us about the visibility of a dropdown with a predicate"
(is (true? (-> (ev/show-dropdown {} [::ev/show :getting-repetitive])
(sub/visible-dropdown [::sub/visible-dropdown])
(sub/visible? [::sub/visible? :getting-repetitive])))))
(testing "Dropdown toggling"
(is (true? (-> (ev/toggle-dropdown {} [::ev/toggle :some-generic-dropdown])
(sub/visible-dropdown [::sub/visible-dropdown])
(sub/visible? [::sub/visible? :some-generic-dropdown]))))
(is (not (true? (-> (ev/toggle-dropdown {} [::ev/toggle :some-generic-dropdown])
(ev/toggle-dropdown [::ev/toggle :some-generic-dropdown])
(sub/visible-dropdown [::sub/visible-dropdown])
(sub/visible? [::sub/visible? :some-generic-dropdown])))))))