diff --git a/CHANGELOG.md b/CHANGELOG.md index 9608744d..8a43f061 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,9 @@ We use [Break Versioning][breakver]. The version numbers follow a `.> q (.getKeys) (map (juxt keyword #(query-param q %))) (into {})))) +(defn set-query-params + "Given Reitit-frontend path, update the query params + with given function and arguments. + + Note: coercion is not applied to the query params" + [path new-query-or-update-fn] + (let [^goog.Uri uri (goog.Uri/parse path) + new-query (if (fn? new-query-or-update-fn) + (new-query-or-update-fn (query-params uri)) + new-query-or-update-fn)] + ;; NOTE: Differences to reitit.impl/query-string? + ;; reitit fn adds "=" even if value is empty string + ;; reitit encodes " " as "+" while browser and goog.Uri encode as "%20" + (.setQueryData uri (goog.Uri.QueryData/createFromMap (clj->js new-query))) + (.toString uri))) + (defn match-by-path "Given routing tree and current path, return match with possibly coerced parameters. Return nil if no match found. @@ -27,7 +43,7 @@ :on-coercion-error - a sideeffecting fn of `match exception -> nil`" ([router path] (match-by-path router path nil)) ([router path {:keys [on-coercion-error]}] - (let [uri (.parse Uri path) + (let [uri (.parse goog.Uri path) coerce! (if on-coercion-error (fn [match] (try (coercion/coerce! match) diff --git a/modules/reitit-frontend/src/reitit/frontend/easy.cljs b/modules/reitit-frontend/src/reitit/frontend/easy.cljs index 8a76b900..bcd47f88 100644 --- a/modules/reitit-frontend/src/reitit/frontend/easy.cljs +++ b/modules/reitit-frontend/src/reitit/frontend/easy.cljs @@ -101,3 +101,44 @@ (rfh/replace-state @history name path-params nil)) ([name path-params query-params] (rfh/replace-state @history name path-params query-params))) + +;; This duplicates previous two, but the map parameter will be easier way to +;; extend the functions, e.g. to work with fragment string. Toggling push vs +;; replace can be also simpler with a flag. +;; Navigate and set-query are also similer to react-router API. +(defn + ^{:see-also ["reitit.frontend.history/navigate"]} + navigate + "Updates the browser location and either pushes new entry to the history stack + or replaces the latest entry in the the history stack (controlled by + `replace` option) using URL built from a route defined by name given + parameters. + + Will also trigger on-navigate callback on Reitit frontend History handler. + + Note: currently collections in query-parameters are encoded as field-value + pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them + differently, convert the collections to strings first. + + See also: + https://developer.mozilla.org/en-US/docs/Web/API/History/pushState + https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState" + ([name] + (rfh/navigate @history name)) + ([name {:keys [path-params query-params replace] :as opts}] + (rfh/navigate @history name opts))) + +(defn + ^{:see-also ["reitit.frontend.history/set-query"]} + set-query + "Update query parameters for the current route. + + New query params can be given as a map, or a function taking + the old params and returning the new modified params. + + Note: The query parameter values aren't coereced, so the + update fn will see string values for all query params." + ([new-query-or-update-fn] + (rfh/set-query @history new-query-or-update-fn)) + ([new-query-or-update-fn {:keys [replace] :as opts}] + (rfh/set-query @history new-query-or-update-fn opts))) diff --git a/modules/reitit-frontend/src/reitit/frontend/history.cljs b/modules/reitit-frontend/src/reitit/frontend/history.cljs index f95dcf12..5764e10f 100644 --- a/modules/reitit-frontend/src/reitit/frontend/history.cljs +++ b/modules/reitit-frontend/src/reitit/frontend/history.cljs @@ -3,15 +3,15 @@ events." (:require [goog.events :as gevents] [reitit.core :as reitit] - [reitit.frontend :as rf]) - (:import goog.Uri)) + [reitit.frontend :as rf] + goog.Uri)) (defprotocol History (-init [this] "Create event listeners") (-stop [this] "Remove event listeners") - (-on-navigate [this path]) - (-get-path [this]) - (-href [this path])) + (-on-navigate [this path] "Find a match for current routing path and call on-navigate callback") + (-get-path [this] "Get the current routing path") + (-href [this path] "Converts given routing path to browser location")) ;; This version listens for both pop-state and hash-change for ;; compatibility for old browsers not supporting History API. @@ -78,7 +78,7 @@ the page location is updated using History API." [router e el uri] (let [current-domain (if (exists? js/location) - (.getDomain (.parse Uri js/location)))] + (.getDomain (.parse goog.Uri js/location)))] (and (or (and (not (.hasScheme uri)) (not (.hasDomain uri))) (= current-domain (.getDomain uri))) (not (.-altKey e)) @@ -109,7 +109,7 @@ ignore-anchor-click (fn [e] ;; Returns the next matching ancestor of event target (when-let [el (closest-by-tag (event-target e) "a")] - (let [uri (.parse Uri (.-href el))] + (let [uri (.parse goog.Uri (.-href el))] (when (ignore-anchor-click-predicate router e el uri) (.preventDefault e) (let [path (str (.getPath uri) @@ -177,7 +177,9 @@ (if history (-stop history))) -(defn href +(defn + ^{:see-also ["reitit.core/match->path"]} + href "Generate a URL for a route defined by name, with given path-params and query-params. The URL is formatted using Reitit frontend history handler, so using it with @@ -219,7 +221,9 @@ (.pushState js/window.history nil "" (-href history path)) (-on-navigate history path)))) -(defn replace-state +(defn + ^{:see-also ["reitit.core/match->path"]} + replace-state "Updates the browser location and replaces latest entry in the history stack using URL built from a route defined by name, with given path-params and query-params. @@ -241,3 +245,50 @@ path (reitit/match->path match query-params)] (.replaceState js/window.history nil "" (-href history path)) (-on-navigate history path)))) + +(defn + ^{:see-also ["reitit.core/match->path"]} + navigate + "Updates the browser location and either pushes new entry to the history stack + or replaces the latest entry in the the history stack (controlled by + `replace` option) using URL built from a route defined by name given + parameters. + + Will also trigger on-navigate callback on Reitit frontend History handler. + + Note: currently collections in query-parameters are encoded as field-value + pairs separated by &, i.e. \"?a=1&a=2\", if you want to encode them + differently, convert the collections to strings first. + + See also: + https://developer.mozilla.org/en-US/docs/Web/API/History/pushState + https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState" + ([history name] + (navigate history name nil)) + ([history name {:keys [path-params query-params replace] :as opts}] + (let [match (rf/match-by-name! (:router history) name path-params) + path (reitit/match->path match query-params)] + (if replace + (.replaceState js/window.history nil "" (-href history path)) + (.pushState js/window.history nil "" (-href history path))) + (-on-navigate history path)))) + +(defn + ^{:see-also ["reitit.frontend/set-query-params"]} + set-query + "Update query parameters for the current route. + + New query params can be given as a map, or a function taking + the old params and returning the new modified params. + + Note: The query parameter values aren't coereced, so the + update fn will see string values for all query params." + ([history new-query-or-update-fn] + (set-query history new-query-or-update-fn nil)) + ([history new-query-or-update-fn {:keys [replace] :as opts}] + (let [current-path (-get-path history) + new-path (rf/set-query-params current-path new-query-or-update-fn)] + (if replace + (.replaceState js/window.history nil "" (-href history new-path)) + (.pushState js/window.history nil "" (-href history new-path))) + (-on-navigate history new-path)))) diff --git a/modules/reitit-http/project.clj b/modules/reitit-http/project.clj index 607c68e0..78ded043 100644 --- a/modules/reitit-http/project.clj +++ b/modules/reitit-http/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-core] diff --git a/modules/reitit-interceptors/project.clj b/modules/reitit-interceptors/project.clj index e688027d..cc7224e5 100644 --- a/modules/reitit-interceptors/project.clj +++ b/modules/reitit-interceptors/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-ring] diff --git a/modules/reitit-malli/project.clj b/modules/reitit-malli/project.clj index 5b036be5..044d3bba 100644 --- a/modules/reitit-malli/project.clj +++ b/modules/reitit-malli/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-core] diff --git a/modules/reitit-middleware/project.clj b/modules/reitit-middleware/project.clj index e98be6cb..c81b27f8 100644 --- a/modules/reitit-middleware/project.clj +++ b/modules/reitit-middleware/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :scm "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-ring] diff --git a/modules/reitit-openapi/project.clj b/modules/reitit-openapi/project.clj index 1fa0b322..079f8cac 100644 --- a/modules/reitit-openapi/project.clj +++ b/modules/reitit-openapi/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.8"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-core]]) diff --git a/modules/reitit-pedestal/project.clj b/modules/reitit-pedestal/project.clj index c1677aac..5e7ed9a3 100644 --- a/modules/reitit-pedestal/project.clj +++ b/modules/reitit-pedestal/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[io.pedestal/pedestal.service] diff --git a/modules/reitit-ring/project.clj b/modules/reitit-ring/project.clj index 27386b51..2b6b8053 100644 --- a/modules/reitit-ring/project.clj +++ b/modules/reitit-ring/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-core] diff --git a/modules/reitit-schema/project.clj b/modules/reitit-schema/project.clj index 989dba3c..e5e44be5 100644 --- a/modules/reitit-schema/project.clj +++ b/modules/reitit-schema/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-core] diff --git a/modules/reitit-sieppari/project.clj b/modules/reitit-sieppari/project.clj index c719fdb3..ba69d102 100644 --- a/modules/reitit-sieppari/project.clj +++ b/modules/reitit-sieppari/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-core] diff --git a/modules/reitit-spec/project.clj b/modules/reitit-spec/project.clj index 6f7afc90..b9cf5f84 100644 --- a/modules/reitit-spec/project.clj +++ b/modules/reitit-spec/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-core] diff --git a/modules/reitit-swagger-ui/project.clj b/modules/reitit-swagger-ui/project.clj index e855f9ac..318076a3 100644 --- a/modules/reitit-swagger-ui/project.clj +++ b/modules/reitit-swagger-ui/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-ring] diff --git a/modules/reitit-swagger/project.clj b/modules/reitit-swagger/project.clj index ee2401c3..a52ca20d 100644 --- a/modules/reitit-swagger/project.clj +++ b/modules/reitit-swagger/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-core]]) diff --git a/modules/reitit/project.clj b/modules/reitit/project.clj index 41ec3d9a..14be2f42 100644 --- a/modules/reitit/project.clj +++ b/modules/reitit/project.clj @@ -6,7 +6,7 @@ :scm {:name "git" :url "https://github.com/metosin/reitit" :dir "../.."} - :plugins [[lein-parent "0.3.2"]] + :plugins [[lein-parent "0.3.9"]] :parent-project {:path "../../project.clj" :inherit [:deploy-repositories :managed-dependencies]} :dependencies [[metosin/reitit-core] diff --git a/project.clj b/project.clj index 0960ec97..801a7706 100644 --- a/project.clj +++ b/project.clj @@ -31,13 +31,13 @@ [metosin/reitit-frontend "0.6.0"] [metosin/reitit-sieppari "0.6.0"] [metosin/reitit-pedestal "0.6.0"] - [metosin/ring-swagger-ui "4.15.5"] + [metosin/ring-swagger-ui "4.18.1"] [metosin/spec-tools "0.10.5"] [metosin/schema-tools "0.13.0"] [metosin/muuntaja "0.6.8"] [metosin/jsonista "0.3.7"] [metosin/sieppari "0.0.0-alpha13"] - [metosin/malli "0.10.4"] + [metosin/malli "0.11.0"] ;; https://clojureverse.org/t/depending-on-the-right-versions-of-jackson-libraries/5111 [com.fasterxml.jackson.core/jackson-core "2.14.2"] @@ -48,12 +48,13 @@ [expound "0.9.0"] [lambdaisland/deep-diff "0.0-47"] [com.bhauman/spell-spec "0.1.2"] - [ring/ring-core "1.9.6"] + [ring/ring-core "1.10.0"] [io.pedestal/pedestal.service "0.5.10"]] :plugins [[jonase/eastwood "1.3.0"] ;[lein-virgil "0.1.7"] + [lein-ancient "1.0.0-RC3"] [lein-doo "0.1.11"] [lein-cljsbuild "1.1.8"] [lein-cloverage "1.2.4"] @@ -91,7 +92,7 @@ [metosin/muuntaja "0.6.8"] [metosin/sieppari "0.0.0-alpha13"] [metosin/jsonista "0.3.7"] - [metosin/malli "0.10.4"] + [metosin/malli "0.11.0"] [lambdaisland/deep-diff "0.0-47"] [meta-merge "1.0.0"] [com.bhauman/spell-spec "0.1.2"] @@ -100,21 +101,21 @@ [orchestra "2021.01.01-1"] - [ring "1.9.6"] + [ring "1.10.0"] [ikitommi/immutant-web "3.0.0-alpha1"] [metosin/ring-http-response "0.9.3"] - [metosin/ring-swagger-ui "4.15.5"] + [metosin/ring-swagger-ui "4.18.1"] [criterium "0.4.6"] [org.clojure/test.check "1.1.1"] [org.clojure/tools.namespace "1.4.4"] [com.gfredericks/test.chuck "0.2.14"] - [nubank/matcher-combinators "3.8.4"] + [nubank/matcher-combinators "3.8.5"] [io.pedestal/pedestal.service "0.5.10"] [org.clojure/core.async "1.6.673"] - [manifold "0.3.0"] + [manifold "0.4.0"] [funcool/promesa "10.0.594"] [com.clojure-goes-fast/clj-async-profiler "1.0.3"] @@ -133,7 +134,7 @@ [io.pedestal/pedestal.jetty "0.5.10"] [calfpath "0.8.1"] [org.clojure/core.async "1.6.673"] - [manifold "0.3.0"] + [manifold "0.4.0"] [funcool/promesa "10.0.594"] [metosin/sieppari] [yada "1.2.16"] diff --git a/test/cljc/reitit/openapi_test.clj b/test/cljc/reitit/openapi_test.clj index 563125ed..0788432e 100644 --- a/test/cljc/reitit/openapi_test.clj +++ b/test/cljc/reitit/openapi_test.clj @@ -614,3 +614,59 @@ keys)))) (testing "spec is valid" (is (nil? (validate spec)))))))))) + +(deftest recursive-test + ;; Recursive schemas only properly supported for malli + ;; See https://github.com/metosin/schema-tools/issues/41 + (let [app (ring/ring-handler + (ring/router + [["/parameters" + {:post {:description "parameters" + :coercion malli/coercion + :parameters {:request + {:body + [:schema + {:registry {"friend" [:map + [:age int?] + [:pet [:ref "pet"]]] + "pet" [:map + [:name :string] + [:friends [:vector [:ref "friend"]]]]}} + "friend"]}} + :handler (fn [req] + {:status 200 + :body (-> req :parameters :request)})}}] + ["/openapi.json" + {:get {:handler (openapi/create-openapi-handler) + :openapi {:info {:title "" :version "0.0.1"}} + :no-doc true}}]] + {:validate reitit.ring.spec/validate + :data {:middleware [openapi/openapi-feature + rrc/coerce-request-middleware + rrc/coerce-response-middleware]}})) + spec (-> {:request-method :get + :uri "/openapi.json"} + app + :body)] + (is (= {:info {:title "" :version "0.0.1"} + :openapi "3.1.0" + :x-id #{:reitit.openapi/default} + :paths {"/parameters" + {:post + {:description "parameters" + :requestBody + {:content + {"application/json" + {:schema {:$ref "#/definitions/friend" + :definitions {"friend" {:properties {:age {:type "integer"} + :pet {:$ref "#/definitions/pet"}} + :required [:age :pet] + :type "object"} + "pet" {:properties {:friends {:items {:$ref "#/definitions/friend"} + :type "array"} + :name {:type "string"}} + :required [:name :friends] + :type "object"}}}}}}}}}} + spec)) + (testing "spec is valid" + (is (nil? (validate spec)))))) diff --git a/test/cljs/reitit/frontend/core_test.cljs b/test/cljs/reitit/frontend/core_test.cljs index d0684398..a0e0abe0 100644 --- a/test/cljs/reitit/frontend/core_test.cljs +++ b/test/cljs/reitit/frontend/core_test.cljs @@ -6,7 +6,21 @@ [schema.core :as s] [reitit.coercion.schema :as rcs] [reitit.coercion.malli :as rcm] - [reitit.frontend.test-utils :refer [capture-console]])) + [reitit.frontend.test-utils :refer [capture-console]] + [reitit.impl :as impl])) + +(deftest query-params-test + (is (= {:foo "1"} + (rf/query-params (.parse goog.Uri "?foo=1")))) + + (is (= {:foo "1" :bar "aaa"} + (rf/query-params (.parse goog.Uri "?foo=1&bar=aaa")))) + + (is (= {:foo ""} + (rf/query-params (.parse goog.Uri "?foo=")))) + + (is (= {:foo ""} + (rf/query-params (.parse goog.Uri "?foo"))))) (defn m [x] (assoc x :data nil :result nil)) @@ -227,3 +241,44 @@ :token_type "bearer" :expires_in 3600}}}) (m (rf/match-by-path router "/5?mode=foo#access_token=foo&refresh_token=bar&provider_token=baz&token_type=bearer&expires_in=3600")))))))) + +(deftest set-query-params-test + (is (= "foo?bar=1" + (rf/set-query-params "foo" {:bar 1}) + (rf/set-query-params "foo" #(assoc % :bar 1)) + ;; Also compare to reitit.impl version which is used by match->path (and history fns) + (str "foo?" (impl/query-string {:bar 1})))) + + (testing "Encoding" + (is (= "foo?bar=foo%20bar" + (rf/set-query-params "foo" {:bar "foo bar"}) + (rf/set-query-params "foo" #(assoc % :bar "foo bar")) + ;; FIXME: Reitit.impl encodes space as "+" + ; (str "foo?" (impl/query-string {:bar "foo bar"})) + ))) + + (testing "Keep fragment" + (is (= "foo?bar=1&zzz=2#aaa" + (rf/set-query-params "foo?bar=1#aaa" #(assoc % :zzz 2))))) + + (is (= "foo?asd=1&bar=1" + (rf/set-query-params "foo?asd=1" #(assoc % :bar 1)))) + + (is (= "foo?bar=1" + (rf/set-query-params "foo?asd=1&bar=1" #(dissoc % :asd)))) + + (is (= "foo?bar" + (rf/set-query-params "foo?asd=1&bar" #(dissoc % :asd)))) + + (is (= "foo?bar" + (rf/set-query-params "foo" #(assoc % :bar "")) + ;; FIXME: Reitit.impl adds "=" for empty string values + ; (str "foo?" (impl/query-string {:bar ""})) + )) + + (is (= "foo" + (rf/set-query-params "foo?asd=1" #(dissoc % :asd)))) + + (testing "Need to coerce current values manually" + (is (= "foo?foo=2" + (rf/set-query-params "foo?foo=1" (fn [q] (update q :foo #(inc (js/parseInt %))))))))) diff --git a/test/cljs/reitit/frontend/easy_test.cljs b/test/cljs/reitit/frontend/easy_test.cljs index 7def2701..abe11545 100644 --- a/test/cljs/reitit/frontend/easy_test.cljs +++ b/test/cljs/reitit/frontend/easy_test.cljs @@ -30,33 +30,53 @@ (is (= "/" url) "start at root") (rfe/push-state ::foo)) + ;; 0. / + ;; 1. /foo 2 (do (is (= "/foo" url) "push-state") (.back js/window.history)) + ;; 0. / 3 (do (is (= "/" url) "go back") - (rfe/push-state ::bar {:id 1})) + (rfe/navigate ::bar {:path-params {:id 1}})) + ;; 0. / + ;; 1. /bar/1 4 (do (is (= "/bar/1" url) "push-state 2") (rfe/replace-state ::bar {:id 2})) + ;; 0. / + ;; 1. /bar/2 5 (do (is (= "/bar/2" url) "replace-state") - (.back js/window.history)) - 6 (do (is (= "/" url) - "go back after replace state") + (rfe/set-query {:a 1})) + ;; 0. / + ;; 1. /bar/2 + ;; 2. /bar/2?a=1 + 6 (do (is (= "/bar/2?a=1" url) + "update-query with map") + (rfe/set-query #(assoc % :b "foo") {:replace true})) + ;; 0. / + ;; 1. /bar/2 + ;; 2. /bar/2?a=1&b=foo + 7 (do (is (= "/bar/2?a=1&b=foo" url) + "update-query with fn") + (.go js/window.history -2)) + ;; 0. / + 8 (do (is (= "/" url) + "go back two events") ;; Reset to ensure old event listeners aren't called (rfe/start! router (fn on-navigate [match history] (let [url (rfh/-get-path history)] (case (swap! n inc) - 7 (do (is (= "/" url) + 9 (do (is (= "/" url) "start at root") (rfe/push-state ::foo)) - 8 (do (is (= "/foo" url) - "push-state") - (rfh/stop! @rfe/history) - (done)) + 10 (do (is (= "/foo" url) + "push-state") + (rfh/stop! @rfe/history) + (done)) (do (is false (str "extra event 2" {:n @n, :url url})) (done)))))