Add cljs-test to test-runner via browser/html

- standardised test namespaces: renamed to use -test suffix and moved to eliminate redundant /test folder
- added cljs-test based tests via browser/html. These mimic original karma tests.
   NOTE: previous lein aliases `once` and `auto` have been replaced by `test-once` , `test-auto` & `karma-once`
- update karma.conf & circle.yml
- updated CHANGES.md & CONTRIBUTING.md
This commit is contained in:
hipitihop 2016-07-14 11:21:12 +10:00
parent b75ec509bb
commit a1314eb6c3
11 changed files with 280 additions and 98 deletions

View File

@ -1,87 +1,87 @@
## 0.8.0 (2016.07.XX) ## 0.8.0 (2016.07.XX)
Staying on the leading edge of new buzzwords is obviously critical for any framework. Angular's terrifying faceplant Staying on the leading edge of new buzzwords is obviously critical for any framework. Angular's terrifying faceplant
is a sobering reminder to us all. With this release, re-frame's already impressive buzzword muscles is a sobering reminder to us all. With this release, re-frame's already impressive buzzword muscles
bulge further with new walnuts like "effects", "coeffects", and "de-duplicated signal graph". Yeah, I know, right? bulge further with new walnuts like "effects", "coeffects", and "de-duplicated signal graph". Yeah, I know, right?
Some may even find these new features useful. Some may even find these new features useful.
Headline: Headline:
- re-frame subscriptions are now de-duplicated. As a result, - re-frame subscriptions are now de-duplicated. As a result,
many Signal graphs will be more many Signal graphs will be more
efficient. The new behaviour better matches programmer intuitions about what "should" happen. efficient. The new behaviour better matches programmer intuitions about what "should" happen.
*Explanation* *Explanation*
Each subscription causes a handler to execute, producing Each subscription causes a handler to execute, producing
a reactive stream of updates. Two calls to `(subscribe [:some :query])` results in two copies of the same a reactive stream of updates. Two calls to `(subscribe [:some :query])` results in two copies of the same
subscription handler running, each delivering a stream of updates. Now, if these two subscriptions subscription handler running, each delivering a stream of updates. Now, if these two subscriptions
were running at the same time, this would be inefficient. Both handlers would be were running at the same time, this would be inefficient. Both handlers would be
doing the same computations and delivering the same stream of updates. Unnecessary, duplicate work. doing the same computations and delivering the same stream of updates. Unnecessary, duplicate work.
Starting with this version, this sort of duplication has been eliminated. Two, or more, concurrent Starting with this version, this sort of duplication has been eliminated. Two, or more, concurrent
subscriptions for the same query will now source reactive updates from the one executing handler. subscriptions for the same query will now source reactive updates from the one executing handler.
So, how do we know if two subscriptions are "the same"? Answer: two subscriptions So, how do we know if two subscriptions are "the same"? Answer: two subscriptions
are the same if their query vectors test `=` to each other. are the same if their query vectors test `=` to each other.
So, these two subscriptions are *not* "the same": `[:some-event 42]` `[:some-event "blah"]`. Even So, these two subscriptions are *not* "the same": `[:some-event 42]` `[:some-event "blah"]`. Even
though they involve the same event id, `:some-event`, the query vectors do not test `=`. though they involve the same event id, `:some-event`, the query vectors do not test `=`.
- added a new subscription handler registration function called `re-frame.core/def-sub`. It is an - added a new subscription handler registration function called `re-frame.core/def-sub`. It is an
alternative to `re-frame.core/register-sub` (now renamed to `re-frame.core/def-sub-raw`). alternative to `re-frame.core/register-sub` (now renamed to `re-frame.core/def-sub-raw`).
`def-sub` is significantly easier to use and understand, `def-sub` is significantly easier to use and understand,
while often also being more performant. The design has really fallen out nicely and we're while often also being more performant. The design has really fallen out nicely and we're
delighted with it. delighted with it.
With `def-sub`, you no longer need to use `reaction` explicitly. Subscription handlers are now pure With `def-sub`, you no longer need to use `reaction` explicitly. Subscription handlers are now pure
which makes them easier to understand and test etc. Plus, as you'll see in the docs, there is some which makes them easier to understand and test etc. Plus, as you'll see in the docs, there is some
gratuitous syntactic sugar. gratuitous syntactic sugar.
The todomvc example is a tutorial on the subject: The todomvc example is a tutorial on the subject:
https://github.com/Day8/re-frame/blob/master/examples/todomvc/src/todomvc/subs.cljs https://github.com/Day8/re-frame/blob/master/examples/todomvc/src/todomvc/subs.cljs
- The API for the undo/redo features has been put into `re-frame.core`. - The API for the undo/redo features has been put into `re-frame.core`.
Detailed documentation is now available: https://github.com/Day8/re-frame/wiki/Undo-&-Redo Detailed documentation is now available: https://github.com/Day8/re-frame/wiki/Undo-&-Redo
While undo and redo has been a part of re-frame from the beginning, the feature has been hidden While undo and redo has been a part of re-frame from the beginning, the feature has been hidden
and not documented, so it is nice to see it given proper API status. and not documented, so it is nice to see it given proper API status.
Plus, this release has [a couple of enhancements](https://github.com/Day8/re-frame/wiki/Undo-&-Redo#harvesting-and-re-instating) Plus, this release has [a couple of enhancements](https://github.com/Day8/re-frame/wiki/Undo-&-Redo#harvesting-and-re-instating)
over that which previously existed previously. over that which previously existed previously.
- there's now two kinds of event handlers: pure and effectful. XXX - there's now two kinds of event handlers: pure and effectful. XXX
For description see: https://github.com/Day8/re-frame/wiki/Effectful-Event-Handlers For description see: https://github.com/Day8/re-frame/wiki/Effectful-Event-Handlers
- taking advantage of the new effectful event handlers, there's now a new library - taking advantage of the new effectful event handlers, there's now a new library
which makes it easy to XXXX which makes it easy to XXXX
Breaking: Breaking:
- requires Reagent >= v0.6.0 - requires Reagent >= v0.6.0
- `re-frame.core/register-handler` has been renamed `re-frame.core/def-event`. (There's now - `re-frame.core/register-handler` has been renamed `re-frame.core/def-event`. (There's now
two kinds of event-handlers, pure and effectful. Event handlers of the 2nd, new kind two kinds of event-handlers, pure and effectful. Event handlers of the 2nd, new kind
should be registered via the new function `re-frame.core/def-event-fx`) should be registered via the new function `re-frame.core/def-event-fx`)
- `re-frame.core/register-sub` has been renamed `re-frame.core/def-sub-raw`. This is to indicate that - `re-frame.core/register-sub` has been renamed `re-frame.core/def-sub-raw`. This is to indicate that
this kind of registration is now considered the low level, close to the metal way to this kind of registration is now considered the low level, close to the metal way to
create subscriptions handlers. This release introduced `def-sub` which becomes the preferred way create subscriptions handlers. This release introduced `def-sub` which becomes the preferred way
to register subscription handlers. to register subscription handlers.
- By default, re-frame uses `js/console` functions like `error` and `warn` when logging, but you can - By default, re-frame uses `js/console` functions like `error` and `warn` when logging, but you can
supply alternative functions using `re-frame.core/set-loggers!`. supply alternative functions using `re-frame.core/set-loggers!`.
With this release, any alternatives you supply will be called with different parameters. With this release, any alternatives you supply will be called with different parameters.
Previously loggers were called with a single `str` parameter but now they are expected to act Previously loggers were called with a single `str` parameter but now they are expected to act
like `console.log` itself and take variadic, non string params. Sorry to break things, but like `console.log` itself and take variadic, non string params. Sorry to break things, but
we are trying to maximise use of cljs-devtools and information is lost when strings are we are trying to maximise use of cljs-devtools and information is lost when strings are
output, instead of actual data. output, instead of actual data.
Of course, you need only worry about this if you are using `re-frame.core/set-loggers!` to Of course, you need only worry about this if you are using `re-frame.core/set-loggers!` to
hook in your own loggers. If you are, then, to transition, you'll need to tweak like this: hook in your own loggers. If you are, then, to transition, you'll need to tweak like this:
``` ```
;; your old log function might have looked like this. Single string parameter. ;; your old log function might have looked like this. Single string parameter.
(defn my-logger [s] (do-something-with s)) (defn my-logger [s] (do-something-with s))
;; your new version will have variadic params, and turn them into a string ;; your new version will have variadic params, and turn them into a string
(defn my-logger [& args] (do-something-with (apply str args)) (defn my-logger [& args] (do-something-with (apply str args))
``` ```
@ -92,10 +92,21 @@ Improvements
- XXX todomvc changed to use spec, instead of Schema - XXX todomvc changed to use spec, instead of Schema
- Bug fix: `post-event-callbacks` were not called when `dispatch-sync` was called. - Bug fix: `post-event-callbacks` were not called when `dispatch-sync` was called.
- added new API `re-frame.core/remove-post-event-callback`. See doc string. - added new API `re-frame.core/remove-post-event-callback`. See doc string.
- when an event-handler makes no change to `app-db`, the `debug` middleware now logs a - when an event-handler makes no change to `app-db`, the `debug` middleware now logs a
single line saying so, rather than a "group". Makes it slightly easier to grok single line saying so, rather than a "group". Makes it slightly easier to grok
the absence of change. the absence of change.
- Standardised test namespaces: renamed to use -test suffix and moved to eliminate redundant /test folder
- Added cljs.test based tests via browser/html. These mimic original karma tests. NOTE: previous lein aliases `once` and `auto` have been replaced by `test-once` , `test-auto` & `karma-once` see [CONTRIBUTING.md](CONTRIBUTING.md)
####Other:####
- changed dev deps/plugins
<pre>
binaryage/devtools "0.7.2"
lein-npm "0.6.2"
lein-figwheel "0.5.4-7"
lein-shell "0.5.0" (added)
</pre>
## 0.7.0 (2016-03-14) ## 0.7.0 (2016-03-14)

View File

@ -26,12 +26,21 @@ Use your best judgement on what is needed here.
## Running the tests ## Running the tests
#### Via Browser/HTML
```sh
lein test-once # builds re-frame tests & opens browser on test/test.html
# or lein test-auto # then open a browser on test/test.html
# and refresh browser to rerun tests after each auto compile.
```
#### Via Karma
To run the tests, you must have recent versions of node, npm, Leiningen, and a C++ compiler toolchain installed. If you're on Linux or Mac OS X then you will be fine, if you're on Windows then you need to install Visual Studio Community Edition, and the C++ compiler dependencies. To run the tests, you must have recent versions of node, npm, Leiningen, and a C++ compiler toolchain installed. If you're on Linux or Mac OS X then you will be fine, if you're on Windows then you need to install Visual Studio Community Edition, and the C++ compiler dependencies.
``` ```sh
lein deps # will run lein-npm and install Karma and other node dependencies. Only needed the first time. lein deps # runs lein-npm, installs Karma & other node dependencies. Only needed the first time.
lein once # or lein auto # to build re-frame lein karma-once # to build re-frame tests
karma start # to run the tests with an auto watcher karma start # to run the tests with an auto watcher
``` ```
## Pull requests for bugs ## Pull requests for bugs

View File

@ -10,5 +10,5 @@ dependencies:
- node_modules - node_modules
test: test:
override: override:
- lein once - lein karma-once
- karma start --single-run --reporters junit,dots - karma start --single-run --reporters junit,dots

View File

@ -1,6 +1,6 @@
module.exports = function (config) { module.exports = function (config) {
var root = 'run/compiled/test'; // same as :output-dir var root = 'run/compiled/karma/test'; // same as :output-dir
var junitOutputDir = process.env.CIRCLE_TEST_REPORTS || "run/compiled/test/junit"; var junitOutputDir = process.env.CIRCLE_TEST_REPORTS || "run/compiled/karma/test/junit";
config.set({ config.set({
frameworks: ['cljs-test'], frameworks: ['cljs-test'],
@ -11,7 +11,7 @@ module.exports = function (config) {
], ],
client: { client: {
args: ['re_frame.test.runner.run'] args: ['re_frame.test_runner.run_karma']
}, },
// the default configuration // the default configuration

View File

@ -4,37 +4,52 @@
:license {:name "MIT"} :license {:name "MIT"}
:dependencies [[org.clojure/clojure "1.8.0"] :dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/clojurescript "1.9.89"] [org.clojure/clojurescript "1.9.89"]
[reagent "0.6.0-rc"]] [reagent "0.6.0-rc"]]
:profiles {:debug {:debug true} :profiles {:debug {:debug true}
:dev {:dependencies [[karma-reporter "0.3.0"] :dev {:dependencies [[karma-reporter "0.3.0"]
[binaryage/devtools "0.7.0"]] [binaryage/devtools "0.7.2"]]
:plugins [[lein-cljsbuild "1.1.3"] :plugins [[lein-cljsbuild "1.1.3"]
[lein-npm "0.6.1"] [lein-npm "0.6.2"]
[lein-figwheel "0.5.4-2"]]}} [lein-figwheel "0.5.4-7"]
[lein-shell "0.5.0"]]}}
:clean-targets [:target-path :clean-targets [:target-path "run/compiled"]
"run/compiled"]
:resource-paths ["run/resources"] :resource-paths ["run/resources"]
:jvm-opts ["-Xmx1g" "-XX:+UseConcMarkSweepGC"] :jvm-opts ["-Xmx1g" "-XX:+UseConcMarkSweepGC"]
:source-paths ["src"] :source-paths ["src"]
:test-paths ["test"] :test-paths ["test"]
:shell {:commands {"open" {:windows ["cmd" "/c" "start"]
:macosx "open"
:linux "xdg-open"}}}
:deploy-repositories [["releases" :clojars {:sign-releases false}] :deploy-repositories [["releases" :clojars {:sign-releases false}]
["snapshots" :clojars {:sign-releases false}]] ["snapshots" :clojars {:sign-releases false}]]
:npm {:dependencies [[karma "1.0.0"] :npm {:dependencies [[karma "1.0.0"]
[karma-cljs-test "0.1.0"] [karma-cljs-test "0.1.0"]
[karma-chrome-launcher "0.2.0"] [karma-chrome-launcher "0.2.0"]
[karma-junit-reporter "0.3.8"]]} [karma-junit-reporter "0.3.8"]]}
:cljsbuild {:builds [{:id "test" :cljsbuild {:builds [{:id "test"
:source-paths ["test" "src"] :source-paths ["test" "src"]
:compiler {:output-to "run/compiled/test.js" :compiler {:output-to "run/compiled/browser/test.js"
:source-map "run/compiled/test.js.map" :source-map true
:output-dir "run/compiled/test" :output-dir "run/compiled/browser/test"
:optimizations :none
:source-map-timestamp true
:pretty-print true}}
{:id "karma"
:source-paths ["test" "src"]
:compiler {:output-to "run/compiled/karma/test.js"
:source-map "run/compiled/karma/test.js.map"
:output-dir "run/compiled/karma/test"
:optimizations :whitespace :optimizations :whitespace
:main "re_frame.test_runner"
:pretty-print true}}]} :pretty-print true}}]}
:aliases {"auto" ["do" "clean," "cljsbuild" "auto" "test,"] :aliases {"test-once" ["do" "clean," "cljsbuild" "once" "test," "shell" "open" "test/test.html"]
"once" ["do" "clean," "cljsbuild" "once" "test,"] }) "test-auto" ["do" "clean," "cljsbuild" "auto" "test,"]
"karma-once" ["do" "clean," "cljsbuild" "once" "karma,"]})

View File

@ -1,4 +1,4 @@
(ns re-frame.test.middleware (ns re-frame.middleware-test
(:require [cljs.test :refer-macros [is deftest]] (:require [cljs.test :refer-macros [is deftest]]
[reagent.ratom :refer [atom]] [reagent.ratom :refer [atom]]
[re-frame.middleware :as middleware])) [re-frame.middleware :as middleware]))

View File

@ -1,4 +1,4 @@
(ns re-frame.test.subs (ns re-frame.subs-test
(:require [cljs.test :refer-macros [is deftest]] (:require [cljs.test :refer-macros [is deftest]]
[reagent.ratom :refer-macros [reaction]] [reagent.ratom :refer-macros [reaction]]
[re-frame.subs :as subs] [re-frame.subs :as subs]

View File

@ -1,15 +0,0 @@
(ns re-frame.test.runner
(:require [jx.reporter.karma :as karma :include-macros true]
[re-frame.test.middleware]
[re-frame.test.undo]
[re-frame.test.subs]
[devtools.core :as devtools]))
(devtools/install!) ;; we love https://github.com/binaryage/cljs-devtools
(defn ^:export run [karma]
(karma/run-tests
karma
're-frame.test.middleware
're-frame.test.undo
're-frame.test.subs))

View File

@ -0,0 +1,33 @@
(ns re-frame.test-runner
(:refer-clojure :exclude (set-print-fn!))
(:require
[cljs.test :as cljs-test :include-macros true]
[jx.reporter.karma :as karma :include-macros true]
[devtools.core :as devtools]
;; Test Namespaces -------------------------------
[re-frame.middleware-test]
[re-frame.undo-test]
[re-frame.subs-test]))
(enable-console-print!)
(devtools/install! [:custom-formatters :sanity-hints]) ;; we love https://github.com/binaryage/cljs-devtools
;; ---- BROWSER based tests ----------------------------------------------------
(defn ^:export set-print-fn! [f]
(set! cljs.core.*print-fn* f))
(defn ^:export run-html-tests []
(cljs-test/run-tests
're-frame.middleware-test
're-frame.undo-test
're-frame.subs-test))
;; ---- KARMA -----------------------------------------------------------------
(defn ^:export run-karma [karma]
(karma/run-tests
karma
're-frame.middleware-test
're-frame.undo-test
're-frame.subs-test))

View File

@ -1,4 +1,4 @@
(ns re-frame.test.undo (ns re-frame.undo-test
(:require [cljs.test :refer-macros [is deftest]] (:require [cljs.test :refer-macros [is deftest]]
[re-frame.undo :as undo] [re-frame.undo :as undo]
[re-frame.db :as db] [re-frame.db :as db]

129
test/test.html Normal file
View File

@ -0,0 +1,129 @@
<html lang="en">
<head>
<title>re-frame Unit Tests</title>
<meta charset='utf-8'>
<meta name="google" content="notranslate"/>
<!-- Use a monospaced font and a dark theme -->
<!-- some colour from Palette from http://clrs.cc/ -->
<style>
body {
font-family: Courier, "Courier New", monospace;
font-size: 11;
background-color: #111;
color: #AAA;
margin: 0.25in 0.25in 0.25in 0.25in;
}
h2 {
font-size: 24;
}
.red {
color: #FF4136;
}
.green {
color: #2ECC40;
}
.orange {
color: #FF851B;
}
.blue {
color: #0074D9;
}
.test-header {
color: #EEE;
}
</style>
</head>
<body>
<h2>re-frame Unit Tests</h2>
<div id="output-goes-here"></div>
<script src="../run/compiled/browser/test/goog/base.js" type="text/javascript"></script>
<script src="../run/compiled/browser/test.js" type="text/javascript"></script>
<script>
// This loop is where a lot of important work happens
// It will inject both the unit tests and code-to-be-tested into the page
//find out what requires cljs.core
// reverse nameToPath
var pathToName = {};
for (var key in goog.dependencies_.nameToPath) {
var value = goog.dependencies_.nameToPath[key];
pathToName[value] = key;
}
for (var key in goog.dependencies_.requires) {
if (goog.dependencies_.requires.hasOwnProperty(key)) {
if (goog.dependencies_.requires[key]["cljs.core"]) {
//as key is a path find its namespace
goog.require(pathToName[key]); // will trigger CLOSURE_IMPORT_SCRIPT calls which injectJs into page
}
}
}
// --------------------------------------------------------------------
// Output
var outputDiv = document.getElementById("output-goes-here")
function testPrintLn(line) {
line = line.replace(/\n/g, "");
if (line == "")
return;
line = line.replace(/</g, "&lt;");
line = line.replace(/>/g, "&gt;");
// First, to the console
console.log(line);
// Second, into the HTML
var span = document.createElement("pre");
outputDiv.appendChild(span);
// look for colour markers
if (-1 != line.indexOf('ERRORS:')) {
span.className = "blue"
}
if (-1 != line.indexOf('WARNINGS:')) {
span.className = "blue"
}
if (-1 != line.indexOf('ERROR ')) {
span.className = "orange"
}
if (-1 != line.indexOf('FAIL')) {
span.className = "red"
}
if (-1 != line.indexOf('Testing test.')) {
span.className = "test-header"
}
if (-1 != line.indexOf('failures,')) {
if (-1 != line.indexOf('0 failures, 0 errors')) {
document.body.style.backgroundColor = "#00430D";
span.className = "green";
}
else {
document.body.style.backgroundColor = "771419";
span.className = "red";
}
}
// replace leading blanks with &nbsp; so text lines up
var numLeadingBlanks = line.match(/^\s*/)[0].length;
var leadingNBSP = Array(numLeadingBlanks).join("&nbsp;")
span.innerHTML = leadingNBSP + line + "<br>";
}
// --------------------------------------------------------------------
// Run Tests
//
function run_tests() {
re_frame.test_runner.set_print_fn_BANG_(testPrintLn);
re_frame.test_runner.run_html_tests();
}
// Don't run any tests till this page is fully loaded.
// Remember there'll be lots of async <script> added by the loop above.
window.addEventListener('load', run_tests);
</script>
</body>
</html>