Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Update readme

  • Loading branch information...
commit c3e782f341897985e05d079e4d3d4218dd1d479a 1 parent e44a6d2
@xeqi authored
View
192 README.md
@@ -1,17 +1,197 @@
# kerodon [![Build Status](https://secure.travis-ci.org/xeqi/kerodon.png)](http://travis-ci.org/xeqi/kerodon)
-Acceptance test framework for web applications
+kerodon is an interaction and testing library for [ring](https://github.com/mmcgrana/ring) html based apps. It is intented to look like the interaction a user would have. It is inspired by [capybara](https://github.com/jnicklas/capybara).
-Based on a similar api to Capybara.
+## Dependency Information
+
+kerodon is available from [clojars](http://clojars.org).
+
+### Leiningen
+```clojure
+[kerodon "0.0.4"]
+```
+
+### Maven (requires adding clojars repo):
+
+```xml
+<dependency>
+ <groupId>kerodon</groupId>
+ <artifactId>kerodon</artifactId>
+ <version>0.0.4</version>
+</dependency>
+```
## Usage
-See tests.
+### Example
+
+```clojure
+(use '[kerodon.core])
+(use '[kerodon.test])
+(require '[clojure.java.io :as io])
+
+;imagine ring-app is a login required picture upload
+(-> (session ring-app)
+ (visit "/")
+ (follow "login")
+ (fill-in "User:" "username")
+ (fill-in "Password:" "wrong-password")
+ (press "Login")
+ (follow-redirect)
+ (within [:#user_name]
+ (has (text? "username")
+ "Username shows up in #user_name when logged in"))
+ (press "Login")
+ (follow "update profile")
+ (has (value? "Email:" "example@example.org")
+ "Email field defaults to user's email")
+ (attach-file "Picture:" (io/file "/tmp/foo.png"))
+ (follow-redirect)
+ (within [:#picture]
+ (has (text? "foo.png")
+ "Picture name is near picture.")))
+```
+
+### Interaction
+
+The api namespace for interaction is ```kerodon.core```. If you are using kerodon in tests you may want to have ```(:use [kerodon.core])``` in your ```ns``` declaration. All examples below assume so.
+
+kerodon is based on [peridot](https://github.com/xeqi/peridot), and designed to be used with ->.
+
+#### Initialization
+
+You can create an initial state with ```session```.
+
+```clojure
+(session ring-app) ;Use your ring app
+```
+
+#### Navigation
+
+You can use ```visit``` to send a request to your ring app.
+
+```clojure
+(-> (session ring-app) ;Use your ring app
+ (visit "/"))
+```
+
+You can pass extra arguments like you can to ```peridot.core/request```, but this is not recommended.
+
+kerodon will not follow redirects automatically. To follow a redirect use ```follow-redirect```. This will throw an ```IllegalArgumentException``` when the last response was not a redirect.
+
+```clojure
+(-> (session ring-app) ;Use your ring app
+ (visit "/redirect")
+ (follow-redirect))
+```
+
+You can use ```follow``` to follow a link.
+
+```clojure
+(-> (session ring-app) ;Use your ring app
+ (visit "/")
+ (follow "login"))
+```
+
+The selector can be the text of the link, or a vector of css elements.
+
+#### Form interaction
+
+You can use ```fill-in``` to fill in a form field.
+
+```clojure
+(-> (session ring-app) ;Use your ring app
+ (visit "/")
+ (follow "login")
+ (fill-in "User:" "username")
+ (fill-in "Password:" "password")
+```
+
+The selector can be the text or css of a label with a for element, or the css of the field itself.
+
+
+You can use ```attach-file``` to fill in a file field.
+
+```clojure
+(-> (session ring-app) ;Use your ring app
+ (visit "/")
+ (follow "upload")
+ (attach-file "Picture:" (clojure.java.io/file "/tmp/foo")))
+```
+
+The selector can be the text or css of a label with a for element, or the css of the field itself.
+
+You can use ```press``` to submit a form.
+
+```clojure
+(-> (session ring-app) ;Use your ring app
+ (visit "/")
+ (follow "login")
+ (fill-in "User:" "username")
+ (fill-in "Password:" "password")
+ (press "Login"))
+```
+
+The selector can be the text or css of a submit button.
+
+
+### Within
+
+Sometimes you might have multiple html elements that can match. You can restrict the search space using ```within```.
+
+```clojure
+(-> (session ring-app) ;Use your ring app
+ (visit "/")
+ (within [:#signin2]
+ (press "Login")))
+```
+
+### Testing
+
+The api namespace for testing is ```kerodon.test```. This uses the same machinery as ```clojure.test```. If you are using kerodon in tests you may want to have ```(:use [kerodon.test])``` in your ```ns``` declaration. All examples below assume so.
+
+The main function is ```has```. It allows the verifications to compose using ->. It requires one of the verification functions, and an optional error message.
+
+You can use ```status?``` to validate the status code of the last response.
+You can use ```text?``` to validate the text in the page.
+You can use ```value?``` to validate the value of a field. The selector can be the text or css of a label with a for element, or the css of the field itself.
+
+```clojure
+(-> (session ring-app)
+ (visit "/hello")
+ (has (status? 200)
+ "page is found")
+ (has (text? "hello world")
+ "page says hello world"))
+
+(-> (session ring-app)
+ (visit "/comment/new")
+ (has (value? "name" "anonymous")
+ "comments default to anonymous")
+ (has (value? "comment" "")
+ "comments default empty"))
+```
+
+These should all work with ```within```.
+
+## Transactions and database setup
+
+kerodon runs without an http server and, depending on your setup, transactions can be used to rollback and isolate tests. Some fixtures may be helpful:
+
+```clojure
+(use-fixtures :once
+ (fn [f]
+ (clojure.java.jdbc/with-connection db (f))))
+(use-fixtures :each
+ (fn [f]
+ (clojure.java.jdbc/transaction
+ (clojure.java.jdbc/set-rollback-only)
+ (f))))
+```
-## TODO
+## Building
-* Write this readme
-* Write some doc-strings
+[leiningen](https://github.com/technomancy/leiningen) version 2 is used as the build tool. ```lein2 all test``` will run the test suite against clojure 1.3 and a recent 1.4-beta.
## License
View
22 src/kerodon/core.clj
@@ -2,25 +2,25 @@
(:require [kerodon.impl :as impl]
[peridot.core :as peridot]))
+(def session peridot/session)
+
(defn visit [state & rest]
(impl/include-parse (apply peridot/request state rest)))
-(defn fill-in [state selector input]
- (impl/set-value state selector input))
-
-(defn press [state selector]
- (apply visit state (impl/build-request-details state selector)))
+(defn follow-redirect [state]
+ (impl/include-parse (peridot/follow-redirect state)))
(defn follow [state selector]
(visit state (impl/find-url state selector)))
-(defn follow-redirect [state]
- (impl/include-parse (peridot/follow-redirect state)))
+(defn fill-in [state selector input]
+ (impl/set-value state selector input))
-(def session peridot/session)
+(defn attach-file [state selector file]
+ (impl/set-value state selector file))
+
+(defn press [state selector]
+ (apply visit state (impl/build-request-details state selector)))
(defmacro within [state selector & fns]
`(impl/using ~state ~selector #(-> % ~@fns)))
-
-(defn attach-file [state selector file]
- (impl/set-value state selector file))
View
11 src/kerodon/impl.clj
@@ -86,11 +86,12 @@
query-string))
(defn get-value [state selector]
- (-> (:enlive state)
- (form-element-for selector)
- first
- :attrs
- :value))
+ (or (-> (:enlive state)
+ (form-element-for selector)
+ first
+ :attrs
+ :value)
+ ""))
(defn set-value [state selector input]
(update-in state [:enlive]
View
21 test/kerodon/test/core.clj
@@ -19,7 +19,7 @@
{:get (fn [{:keys [session]}]
(if-let [user (:user session)]
(response/response (str "hi " user))
- (response/response (hiccup/html [:a {:href "/login"} "login"]))))}
+ (response/response (hiccup/html [:a {:id "go-login" :href "/login"} "login"]))))}
["login"]
{:get (constantly
(response/response
@@ -117,6 +117,7 @@
(response/response
(hiccup/html
[:form {:enctype "multipart/form-data"}
+ [:label {:for "file"} "File"]
[:input {:id "file" :name "file" :type "file"}]
[:input {:type "submit" :value "upload"}]])))
:post (fn [{:keys [params body]}]
@@ -172,6 +173,12 @@
#"link could not be found with selector \"NonExistant\""
(follow state "NonExistant")))))
+(deftest follow-by-css
+ (-> (session app)
+ (visit "/")
+ (follow [:#go-login])
+ (has (text? "UserPassword"))))
+
(deftest form-by-css
(-> (session app)
(visit "/login-with-css")
@@ -226,7 +233,7 @@
(within [:#2]
(has (value? "User" "someone")))
(within [:#1]
- (has (value? "User" nil)))
+ (has (value? "User" "")))
(within [:#2]
(press "Login"))
(has (text? "hi"))))
@@ -254,6 +261,16 @@
(:content-type (:request %))))))
(has (text? "hi from uploaded file\n"))))
+(deftest attach-file-by-label
+ (-> (session upload-app)
+ (visit "/upload")
+ (attach-file "File" (io/file (io/resource "file.txt")))
+ (press "upload")
+ (doto
+ (#(is (re-find #"multipart/form-data;"
+ (:content-type (:request %))))))
+ (has (text? "hi from uploaded file\n"))))
+
(deftest attach-file-with-params
(-> (session upload-app-with-params)
(visit "/upload")
Please sign in to comment.
Something went wrong with that request. Please try again.