From c86f042f54ee9200b034769635a7ecab46432939 Mon Sep 17 00:00:00 2001 From: Juho Teperi Date: Fri, 15 Mar 2019 17:33:46 +0200 Subject: [PATCH] Implement #238, handle trailing slashes in frontend match-by-path --- .../reitit-frontend/src/reitit/frontend.cljs | 24 +++++- test/cljs/reitit/frontend/core_test.cljs | 86 +++++++++++++++++++ 2 files changed, 106 insertions(+), 4 deletions(-) diff --git a/modules/reitit-frontend/src/reitit/frontend.cljs b/modules/reitit-frontend/src/reitit/frontend.cljs index 8145404a..5ff4f09a 100644 --- a/modules/reitit-frontend/src/reitit/frontend.cljs +++ b/modules/reitit-frontend/src/reitit/frontend.cljs @@ -1,5 +1,6 @@ (ns reitit.frontend (:require [clojure.set :as set] + [clojure.string :as str] [reitit.coercion :as coercion] [reitit.coercion :as rc] [reitit.core :as r]) @@ -16,10 +17,18 @@ (defn match-by-path "Given routing tree and current path, return match with possibly - coerced parameters. Return nil if no match found." + coerced parameters. Returns nil if no match found." [router path] - (let [uri (.parse Uri path)] - (if-let [match (r/match-by-path router (.getPath uri))] + (let [uri (.parse Uri path) + path (.getPath uri)] + (if-let [match (or (r/match-by-path router path) + (if-let [trailing-slash-handling (:trailing-slash-handling (r/options router))] + (if (str/ends-with? path "/") + (if (not= trailing-slash-handling :add) + (r/match-by-path router (subs path 0 (dec (count path))))) + (if (not= trailing-slash-handling :remove) + (r/match-by-path router (str path "/"))))))] + ;; User can update browser location in on-navigate call using replace-state (let [q (query-params uri) match (assoc match :query-params q) ;; Return uncoerced values if coercion is not enabled - so @@ -40,7 +49,14 @@ (defn router "Create a `reitit.core.router` from raw route data and optionally an options map. - Enables request coercion. See [[reitit.core/router]] for details on options." + Enables request coercion. See [[reitit.core/router]] for details on options. + + Additional options: + + | key | description | + | -------------|-------------| + | :trailing-slash-handling | TODO | + " ([raw-routes] (router raw-routes {})) ([raw-routes opts] diff --git a/test/cljs/reitit/frontend/core_test.cljs b/test/cljs/reitit/frontend/core_test.cljs index 20009088..1a56934b 100644 --- a/test/cljs/reitit/frontend/core_test.cljs +++ b/test/cljs/reitit/frontend/core_test.cljs @@ -104,3 +104,89 @@ (capture-console (fn [] (rf/match-by-name! router ::foo {})))))))))) + + +(deftest trailing-slash-handling-test + (testing ":both" + (let [router (r/router ["/" + ["" ::frontpage] + ["foo" ::foo] + ["bar/" ::bar]] + {:trailing-slash-handling :both})] + (is (= (r/map->Match + {:template "/foo" + :data {:name ::foo} + :path-params {} + :query-params {} + :path "/foo" + :parameters {:query {} + :path {}}}) + (rf/match-by-path router "/foo/") + (rf/match-by-path router "/foo"))) + + (is (= (r/map->Match + {:template "/bar/" + :data {:name ::bar} + :path-params {} + :query-params {} + :path "/bar/" + :parameters {:query {} + :path {}}}) + (rf/match-by-path router "/bar/") + (rf/match-by-path router "/bar"))) )) + + (testing ":add" + (let [router (r/router ["/" + ["" ::frontpage] + ["foo" ::foo] + ["bar/" ::bar]] + {:trailing-slash-handling :add})] + (is (= (r/map->Match + {:template "/foo" + :data {:name ::foo} + :path-params {} + :query-params {} + :path "/foo" + :parameters {:query {} + :path {}}}) + (rf/match-by-path router "/foo"))) + (is (nil? (rf/match-by-path router "/foo/"))) + + (is (= (r/map->Match + {:template "/bar/" + :data {:name ::bar} + :path-params {} + :query-params {} + :path "/bar/" + :parameters {:query {} + :path {}}}) + (rf/match-by-path router "/bar/") + (rf/match-by-path router "/bar"))))) + + (testing ":remove" + (let [router (r/router ["/" + ["" ::frontpage] + ["foo" ::foo] + ["bar/" ::bar]] + {:trailing-slash-handling :remove})] + (is (= (r/map->Match + {:template "/foo" + :data {:name ::foo} + :path-params {} + :query-params {} + :path "/foo" + :parameters {:query {} + :path {}}}) + (rf/match-by-path router "/foo/") + (rf/match-by-path router "/foo"))) + + (is (= (r/map->Match + {:template "/bar/" + :data {:name ::bar} + :path-params {} + :query-params {} + :path "/bar/" + :parameters {:query {} + :path {}}}) + (rf/match-by-path router "/bar/"))) + (is (nil? (rf/match-by-path router "/bar"))))))