From 76026448e8f129a422ee3a81307107e68d0d769d Mon Sep 17 00:00:00 2001 From: Roman Scherer Date: Fri, 8 Mar 2013 11:44:44 +0100 Subject: [PATCH 1/3] Added ClojureScript support. --- .gitignore | 2 + project.clj | 24 ++++++- runners/phantomjs.js | 24 +++++++ src/ring/mock/request.cljs | 104 +++++++++++++++++++++++++++++ test/ring/mock/test/request.clj | 6 +- test/ring/mock/test/request.cljs | 111 +++++++++++++++++++++++++++++++ 6 files changed, 265 insertions(+), 6 deletions(-) create mode 100755 runners/phantomjs.js create mode 100644 src/ring/mock/request.cljs create mode 100644 test/ring/mock/test/request.cljs diff --git a/.gitignore b/.gitignore index 4a4602f..0f83c63 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ pom.xml.asc .lein-failures .lein-plugins .lein-env +/.lein-repl-history +/.repl diff --git a/project.clj b/project.clj index 85ab643..0714f31 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,22 @@ -(defproject ring-mock "0.1.3" +(defproject ring-mock "0.1.4-SNAPSHOT" :description "A library for creating mock Ring request maps" - :dependencies [[org.clojure/clojure "1.2.1"]] - :plugins [[codox "0.6.1"]]) + :dependencies [[org.clojure/clojure "1.5.0"]] + :profiles {:dev {:dependencies [[com.cemerick/clojurescript.test "0.0.1"]]}} + :plugins [[codox "0.6.1"] + [lein-cljsbuild "0.3.0"]] + :hooks [leiningen.cljsbuild] + :cljsbuild {:builds + [{:compiler {:output-to "target/ring-mock-debug.js" + :optimizations :whitespace + :pretty-print true} + :source-paths ["src"]} + {:compiler {:output-to "target/ring-mock-test.js" + :optimizations :whitespace + :pretty-print true} + :source-paths ["test"]} + {:compiler {:output-to "target/ring-mock.js" + :optimizations :advanced + :pretty-print false} + :source-paths ["src"] + :jar true}] + :test-commands {"unit-tests" ["runners/phantomjs.js" "target/ring-mock-test.js"]}}) diff --git a/runners/phantomjs.js b/runners/phantomjs.js new file mode 100755 index 0000000..aa29d90 --- /dev/null +++ b/runners/phantomjs.js @@ -0,0 +1,24 @@ +#!/usr/bin/env phantomjs + +// reusable phantomjs script for running clojurescript.test tests +// see http://github.com/cemerick/clojurescript.test for more info + +var p = require('webpage').create(); +p.injectJs(require('system').args[1]); + +p.onConsoleMessage = function (x) { console.log(x); }; +p.evaluate(function () { + // can't just assign console.log directly? + cljs.core._STAR_print_fn_STAR_ = function (x) { + x = x.replace(/\n/g, ""); + console.log(x); + }; +}); + +var success = p.evaluate(function () { + var results = cemerick.cljs.test.run_all_tests(); + cljs.core.println(results); + return cemerick.cljs.test.successful_QMARK_(results); +}); + +phantom.exit(success ? 0 : 1); diff --git a/src/ring/mock/request.cljs b/src/ring/mock/request.cljs new file mode 100644 index 0000000..eb95d6f --- /dev/null +++ b/src/ring/mock/request.cljs @@ -0,0 +1,104 @@ +(ns ring.mock.request + "Functions to create mock request maps." + (:import goog.Uri) + (:require [clojure.string :as string])) + +(defn- encode-params + "Turn a map of parameters into a urlencoded string." + [params] + (let [encode #(-> (js/encodeURIComponent %1) + (string/replace "%20" "+"))] + (string/join "&" + (for [[k v] params] + (str (encode (name k)) "=" + (encode (str v))))))) + +(defn header + "Add a HTTP header to the request map." + [request header value] + (let [header (string/lower-case (name header))] + (assoc-in request [:headers header] (str value)))) + +(defn content-type + "Set the content type of the request map." + [request mime-type] + (-> request + (assoc :content-type mime-type) + (header :content-type mime-type))) + +(defn content-length + "Set the content length of the request map." + [request length] + (-> request + (assoc :content-length length) + (header :content-length length))) + +(defn- combined-query + "Create a query string from a URI and a map of parameters." + [request params] + (let [query (:query-string request)] + (if (or query params) + (string/join "&" + (remove string/blank? + [query (encode-params params)]))))) + +(defn- merge-query + "Merge the supplied parameters into the query string of the request." + [request params] + (assoc request :query-string (combined-query request params))) + +(defn query-string + "Set the query string of the request to a string or a map of parameters." + [request params] + (if (map? params) + (assoc request :query-string (encode-params params)) + (assoc request :query-string params))) + +(defmulti body + "Set the body of the request. The supplied body value can be a string or + a map of parameters to be url-encoded." + {:arglists '([request body-value])} + (fn [request x] (type x))) + +(defmethod body cljs.core/ObjMap [request params] + (-> request + (content-type "application/x-www-form-urlencoded") + (body (encode-params params)))) + +(defmethod body nil [request params] + request) + +(defmethod body :default [request content] + (let [content (str content)] + (-> request + (content-length (count content)) + (assoc :body content)))) + +(defn request + "Create a minimal valid request map from a HTTP method keyword, a string + containing a URI, and an optional map of parameters that will be added to + the query string of the URI. The URI can be relative or absolute. Relative + URIs are assumed to go to http://localhost." + ([method uri] + (request method uri nil)) + ([method uri params] + (let [uri (Uri/parse uri) + host (if (string/blank? (.getDomain uri)) "localhost" (.getDomain uri)) + port (if (not= (.getPort uri) -1) (.getPort uri)) + scheme (.getScheme uri) + path (.getPath uri) + query (if-not (string/blank? (.getQuery uri)) (.getQuery uri)) + request {:server-port (or port 80) + :server-name host + :remote-addr "localhost" + :uri (if (string/blank? path) "/" path) + :query-string query + :scheme (if (string/blank? scheme) + :http (keyword scheme)) + :request-method method + :headers {"host" (if port + (str host ":" port) + host)}}] + (if (#{:get :head} method) + (merge-query request params) + (body request params))))) diff --git a/test/ring/mock/test/request.clj b/test/ring/mock/test/request.clj index d4c9eeb..64cd40b 100644 --- a/test/ring/mock/test/request.clj +++ b/test/ring/mock/test/request.clj @@ -37,7 +37,7 @@ (is (= (:uri (request :get "http://example.com")) "/"))) (testing "added params in :get" (is (= (:query-string (request :get "/" {:x "y" :z "n"})) - "x=y&z=n")) + "z=n&x=y")) (is (= (:query-string (request :get "/?a=b" {:x "y"})) "a=b&x=y")) (is (= (:query-string (request :get "/?" {:x "y"})) @@ -47,7 +47,7 @@ (testing "added params in :post" (let [req (request :post "/" {:x "y" :z "n"})] (is (= (slurp (:body req)) - "x=y&z=n")) + "z=n&x=y")) (is (nil? (:query-string req)))) (let [req (request :post "/?a=b" {:x "y"})] (is (= (slurp (:body req)) @@ -69,7 +69,7 @@ "a=b")))) (testing "added params in :put" (let [req (request :put "/" {:x "y" :z "n"})] - (is (= (slurp (:body req)) "x=y&z=n"))))) + (is (= (slurp (:body req)) "z=n&x=y"))))) (deftest test-header (is (= (header {} "X-Foo" "Bar") diff --git a/test/ring/mock/test/request.cljs b/test/ring/mock/test/request.cljs new file mode 100644 index 0000000..6062cb5 --- /dev/null +++ b/test/ring/mock/test/request.cljs @@ -0,0 +1,111 @@ +(ns ring.mock.test.request + (:require-macros [cemerick.cljs.test :refer [is deftest testing]]) + (:require [cemerick.cljs.test :as t] + [ring.mock.request :refer [body content-length content-type header request query-string]])) + +(deftest test-request + (testing "relative uri" + (is (= (request :get "/foo") + {:server-port 80 + :server-name "localhost" + :remote-addr "localhost" + :uri "/foo" + :query-string nil + :scheme :http + :request-method :get + :headers {"host" "localhost"}}))) + (testing "absolute uri" + (let [request (request :post "https://example.com:8443/foo?bar=baz" {"quux" "zot"}) + literal-request (dissoc request :body) + body (:body request)] + (is (= literal-request + {:server-port 8443 + :server-name "example.com" + :remote-addr "localhost" + :uri "/foo" + :query-string "bar=baz" + :scheme :https + :request-method :post + :content-type "application/x-www-form-urlencoded" + :content-length 8 + :headers {"host" "example.com:8443" + "content-type" "application/x-www-form-urlencoded" + "content-length" "8"}})) + (is (= body "quux=zot")))) + (testing "nil path" + (is (= (:uri (request :get "http://example.com")) "/"))) + (testing "added params in :get" + (is (= (:query-string (request :get "/" {:x "y" :z "n"})) + "x=y&z=n")) + (is (= (:query-string (request :get "/?a=b" {:x "y"})) + "a=b&x=y")) + (is (= (:query-string (request :get "/?" {:x "y"})) + "x=y")) + (is (= (:query-string (request :get "/" {:x "a b"})) + "x=a+b"))) + (testing "added params in :post" + (let [req (request :post "/" {:x "y" :z "n"})] + (is (= (:body req) + "x=y&z=n")) + (is (nil? (:query-string req)))) + (let [req (request :post "/?a=b" {:x "y"})] + (is (= (:body req) + "x=y")) + (is (= (:query-string req) + "a=b"))) + (let [req (request :post "/?" {:x "y"})] + (is (= (:body req) + "x=y")) + (is (nil? (:query-string req)))) + (let [req (request :post "/" {:x "a b"})] + (is (= (:body req) + "x=a+b")) + (is (nil? (:query-string req)))) + (let [req (request :post "/?a=b")] + (is (nil? (:body req))) + (is (= (:query-string req) + "a=b")))) + (testing "added params in :put" + (let [req (request :put "/" {:x "y" :z "n"})] + (is (= (:body req) "x=y&z=n"))))) + +(deftest test-header + (is (= (header {} "X-Foo" "Bar") + {:headers {"x-foo" "Bar"}})) + (is (= (header {} :x-foo "Bar") + {:headers {"x-foo" "Bar"}}))) + +(deftest test-content-type + (is (= (content-type {} "text/html") + {:content-type "text/html" + :headers {"content-type" "text/html"}}))) + +(deftest test-content-length + (is (= (content-length {} 10) + {:content-length 10 + :headers {"content-length" "10"}}))) + +(deftest test-query-string + (testing "string" + (is (= (query-string {} "a=b") + {:query-string "a=b"}))) + (testing "map of params" + (is (= (query-string {} {:a "b"}) + {:query-string "a=b"}))) + (testing "overwriting" + (is (= (-> {} + (query-string {:a "b"}) + (query-string {:c "d"})) + {:query-string "c=d"})))) + +(deftest test-body + (testing "string body" + (let [resp (body {} "Hello World")] + (is (= (:body resp) "Hello World")) + (is (= (:content-length resp) 11)))) + (testing "map body" + (let [resp (body {} {:foo "bar"})] + (is (= (:body resp) "foo=bar")) + (is (= (:content-length resp) 7)) + (is (= (:content-type resp) + "application/x-www-form-urlencoded"))))) From 00c93312e60a3bd88c7fab63c3e6d7c5d589a207 Mon Sep 17 00:00:00 2001 From: Roman Scherer Date: Fri, 8 Mar 2013 11:54:56 +0100 Subject: [PATCH 2/3] Removed cljsbuild jar option to get rid of duplicate entry error. --- project.clj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/project.clj b/project.clj index 0714f31..8054611 100644 --- a/project.clj +++ b/project.clj @@ -2,7 +2,7 @@ :description "A library for creating mock Ring request maps" :dependencies [[org.clojure/clojure "1.5.0"]] :profiles {:dev {:dependencies [[com.cemerick/clojurescript.test "0.0.1"]]}} - :plugins [[codox "0.6.1"] + :plugins [; [codox "0.6.1"] [lein-cljsbuild "0.3.0"]] :hooks [leiningen.cljsbuild] :cljsbuild {:builds @@ -17,6 +17,5 @@ {:compiler {:output-to "target/ring-mock.js" :optimizations :advanced :pretty-print false} - :source-paths ["src"] - :jar true}] + :source-paths ["src"]}] :test-commands {"unit-tests" ["runners/phantomjs.js" "target/ring-mock-test.js"]}}) From 02dc3c52ca13f66ac9c1daaa177062eb1025743c Mon Sep 17 00:00:00 2001 From: Roman Scherer Date: Wed, 13 Mar 2013 18:10:29 +0100 Subject: [PATCH 3/3] Test ClojureScript in advanced mode. --- project.clj | 4 ++-- runners/phantomjs.js | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/project.clj b/project.clj index 8054611..89dcbad 100644 --- a/project.clj +++ b/project.clj @@ -1,7 +1,7 @@ (defproject ring-mock "0.1.4-SNAPSHOT" :description "A library for creating mock Ring request maps" :dependencies [[org.clojure/clojure "1.5.0"]] - :profiles {:dev {:dependencies [[com.cemerick/clojurescript.test "0.0.1"]]}} + :profiles {:dev {:dependencies [[com.cemerick/clojurescript.test "0.0.2"]]}} :plugins [; [codox "0.6.1"] [lein-cljsbuild "0.3.0"]] :hooks [leiningen.cljsbuild] @@ -11,7 +11,7 @@ :pretty-print true} :source-paths ["src"]} {:compiler {:output-to "target/ring-mock-test.js" - :optimizations :whitespace + :optimizations :advanced :pretty-print true} :source-paths ["test"]} {:compiler {:output-to "target/ring-mock.js" diff --git a/runners/phantomjs.js b/runners/phantomjs.js index aa29d90..9200445 100755 --- a/runners/phantomjs.js +++ b/runners/phantomjs.js @@ -8,16 +8,15 @@ p.injectJs(require('system').args[1]); p.onConsoleMessage = function (x) { console.log(x); }; p.evaluate(function () { - // can't just assign console.log directly? - cljs.core._STAR_print_fn_STAR_ = function (x) { - x = x.replace(/\n/g, ""); - console.log(x); - }; + cemerick.cljs.test.set_print_fn_BANG_(function(x) { + x = x.replace(/\n/g, ""); + console.log(x); + }); }); var success = p.evaluate(function () { var results = cemerick.cljs.test.run_all_tests(); - cljs.core.println(results); + console.log(results); return cemerick.cljs.test.successful_QMARK_(results); });