At OnyCloud, testing is an important part of our development activities. Trakr, our issue tracking and project management app, has about 250 tests and nearly 1000 assertions. Those are all tests for the server code. However, for a JavaScript-heavy web application, only testing the server side covers at most half of the code base. The robustness of programs written in dynamic languages, JavaScript in particular, relies on testing. Therefore, we spent significant amount of time looking for good web testing solutions.
We used to use watir-webdriver, which is a nice Ruby library to drive browsers, but we had to write the tests in Ruby. Since we use Clojure as the main language, we prefer to write our functional tests in Clojure too. clj-webdriver is a Clojure library for driving a web browser using Selenium-WebDriver as the backend. The project README provides detailed documentation. As an example for its usage in tests, the following test checks that the proper error message is shown when a user tries to register with an invalid email address.
(deftest invalid-email
(let [b *browser*]
(get-url b "http://localhost:8887/trakr/")
(-> b
(find-it {:href "/signup"})
click)
(wait-until-present (find-it b {:name "email"}))
(-> b
(find-it {:name "email"})
(input-text "tester@localhost"))
(-> b
(find-it {:type "submit"})
click)
(wait-until-present
(find-it b {:id "flash-error" :text #"Invalid email"}))))
When we trigger an action, we do not know how long it takes for the JavaScript code to execute and produce the expected effects. The helper macro wait-until-present makes it easy to wait for some element to become visible on the page:
(defmacro wait-until-present [find-it-form
& {:keys [timeout] :or {timeout 5}}]
`(wait-until
(and (exists? ~find-it-form)
(visible? ~find-it-form)) :timeout ~timeout))
wait-until is a more general macro to wait for some predicate to become true. It repeatedly evaluates the predicate until it is true. When the predicate evaluates to false, the macro waits for an exponentially increasing interval, until the timeout is hit.
(defmacro wait-once [pred ms-to-wait]
`(if (try ~pred (catch Exception _# false))
true
(do
(Thread/sleep ~ms-to-wait) false)))
(defmacro wait-until [pred & {:keys [timeout] :or {timeout 5}}]
`(let [ms# (* 1000 ~timeout)]
(loop [interval# 1
remaining# ms#]
(when (<= remaining# 0) (throw (Exception. "Timeout reached!")))
(when-not (wait-once ~pred interval#)
(recur (* 2 interval#) (- remaining# interval#))))))
Hello, we have just rolled out a few new features for Trakr:
We have added a user setting page where you can change your email and password.

You can now watch public projects. By “public” we mean
How can I make my project public? Go to project setting page and press the “Make my project public” button.
How can I publicize my public project? Spread your project URL!
How can people remember my public project URL? After people land on your public project, they will see a “Watch” button.

If they click on the button, they will be able to track your project on their dashboards afterwards.

Try out a public project trakr-feedback.
A news stream flowing too fast is harmful to your mental health. We used to generate too many news updates, because every single change of an issue attribute will result in an individual news update. Now we have changed the mechanism on issue detail page so that changes are aggregated and pushed to news stream in one shot.

No more page flickering on switching project news stream.
Many bug fixes.
Send your feedback by adding issues at trakr-feedback.
We have just pushed out a new version of Trakr.
This update includes an overhaul of the user interface. The new UI is a lot cleaner and more consistent. We hope you like it.

On the issue details page, attachments and status can only be modified in edit mode now, which is more consistent with the rest of the page. We also made various other usability enhancements over the last week. Those changes are the prelude for some major improvements we are planning.

If you do not already have a Trakr account, sign up for free now.
If you have any feedback, please do not hesitate to contact us.
A few days ago, we decided to create a dashboard in order to better visualize some statistics of our production systems. One important function is to plot the average latency as a time-series graph, so we can see the trend over time. Since MongoDB implemented MapReduce, and we store our logs in MongoDB, MapReduce seems a natural fit for log analysis.
One issue with MongoDB’s implementation of MapReduce is that no matter what language you use, you have to pass JavaScript code as strings to MongoDB. Storing code written in another language as strings in a program is … inelegant, to say the least.
Fortunately, Clojure being a homoiconic language, it is relatively easy to transform Clojure forms into code snippets of other languages using Clojure itself in the same program. In other words, it is possible to embed JavaScript programs in a Clojure program without actually seeing any JavaScript syntax. There are already a number of libraries, with different level of maturity, that allow you to transform Clojure forms to JavaScript. I haven’t done an extensive survey, but ClojureJS is good enough for our purpose.
The following the mapper to collect the latency numbers in the request log so that they are keyed by date:
(def req-latency-mapper
(js (fn []
(let [time (:time this)
date (do (.setHours time 1)
(.setMinutes time 0)
(.setSeconds time 0)
(.setMilliseconds time 0)
time)
latency_ms (:latency_ms this)]
(emit date {:num_requests 1, :sum_latency_ms latency_ms})))))
js is a macro defined in ClojureJS that takes a Clojure form and returns JavaScript code as a string.
Then define a reducer to compute the sum of latency and the number of requests in each day.
(def req-latency-day-reducer
(js (fn [date vals]
(let [result {:num_requests 0, :sum_latency_ms 0}]
(.forEach vals
(fn [val]
(set! result.num_requests
(+ result.num_requests val.num_requests))
(set! result.sum_latency_ms
(+ result.sum_latency_ms val.sum_latency_ms))))
result))))
We also need a finalizer to compute the average.
(def average-finalizer
(js (fn [date val]
(let [{:keys [num_requests sum_latency_ms]} val
average (/ sum_latency_ms num_requests)]
(set! val.average_latency_ms average)
val))))
Finally, we call map-reduce, which is a wrapper around the MongoDB Java API recently implemented in CongoMongo.
(defn gen-daily-average-req-latency []
(map-reduce :oc_request_log
req-latency-mapper
req-latency-day-reducer
:oc_average_daily_req_latency
:finalize average-finalizer))
Now the job is done, with a nice and clean Clojure program.
Issue status is now color coded.

When a milestone is selected, its completion status is shown.

Various bug fixes.
We are happy to announce the public availability of Trakr.
Trakr is an easy-to-use and powerful issue tracking system for team and individuals who want to focus on delivering great products. Whether you are a one-person shop or a team of hundreds, Trakr takes care of the mechanics in issue tracking and let you focus on what you are good at.
Trakr is free for all users during the public beta and will be always free for public projects. We will announce a pricing plan after the beta. Our pricing will be very competitive to similar products on the market, and even if you do not want to stay with us (we hope you do!), you will have plenty of time to export all your data.