{"action":"created","comment":{"url":"https://api.github.com/repos/Day8/re-frame-test/pulls/comments/74882421","id":74882421,"diff_hunk":"@@ -1,66 +1,242 @@\n (ns day8.re-frame.test\n-  (:require [re-frame.core :as r]\n-            #?(:clj\n-            [clojure.core.async :as async]\n-               :cljs [cljs.core.async :as async])\n-    #?(:clj\n-            [clojure.test :as t]\n-               :cljs\n-               [cljs.test :as t])))\n-\n-(defmacro async\n-  [done & body]\n-  `(let [p# (promise)\n-         ~done #(deliver p# nil)]\n-     ~@body\n-     (deref p#)))\n-\n-(def ^:dynamic default-wait-timeout-ms 5000)\n-\n-(defn normalize-ids\n-  \"Convert an id or a collection of ids into a set of ids\"\n-  [ids]\n-  (if (coll? ids)\n-    (set ids)\n-    (hash-set ids)))\n-\n-(def ^:dynamic fail-message \"wait-for-event: didn't get expected event.\")\n-\n-(defn wait-for\n-  ([ids failure-ids cb done]\n-   (let [ok-set (if (coll? ids) (set ids) (hash-set ids))\n-         fail-set (if (coll? failure-ids) (set failure-ids) (hash-set failure-ids))\n-         cb-id (gensym \"wait-for-cb-fn\")]\n-     (r/add-post-event-callback\n-       cb-id\n-       ;; Need this to pass test report bindings into new thread.\n-       (#?(:clj bound-fn :cljs fn)\n-         [new-event _]\n-         (let [new-id (first new-event)]\n-           (when (get fail-set new-id)\n-             (r/remove-post-event-callback cb-id)\n-             (t/do-report\n-               {:type     :fail\n-                :message  fail-message\n-                :expected ok-set\n-                :actual   new-event})\n-             (done))\n-\n-           (when (get ok-set new-id)\n-             (r/remove-post-event-callback cb-id)\n-             (cb new-event))))))))\n-\n-(defn wait-for-ch\n-  [ids]\n-  (let [id-set (normalize-ids ids)\n-        ch (async/chan)\n-        cb-id (gensym \"wait-for-ch-cb-fn\")]\n-    (r/add-post-event-callback\n-      cb-id\n-      (fn listener [new-event _]\n-        (let [new-id (first new-event)]\n-          (when (get id-set new-id)\n-            (async/put! ch new-event)\n-            (async/close! ch)\n-            (r/remove-post-event-callback cb-id)))))\n-    ch))\n+  #?(:cljs (:require-macros day8.re-frame.test))\n+  (:require #?(:cljs [cljs.test :as test]\n+               :clj  [clojure.test :as test])\n+            [re-frame.core :as rf]\n+            [re-frame.db :as rf-db]\n+            [re-frame.interop :as rf-int]\n+            [re-frame.registrar :as rf-registrar])\n+  #?(:clj (:import [java.util.concurrent Executors TimeUnit])))\n+\n+\n+;;;;\n+;;;; General test utils\n+;;;;\n+\n+(defn- dequeue!\n+  \"Dequeue an item from a persistent queue which is stored as the value of the\n+  given atom, setting the atom to the new queue (minus dequeued item) and\n+  returning the dequeued item. If the queue is empty, does not alter it and\n+  returns nil.\"\n+  [queue-atom]\n+  (let [queue @queue-atom]\n+    (when (seq queue)\n+      (if (compare-and-set! queue-atom queue (pop queue))\n+        (peek queue)\n+        (recur queue-atom)))))\n+\n+\n+(defn make-re-frame-restore-fn []\n+  (let [handlers @@#'rf-registrar/kind->id->handler\n+        app-db   @@#'rf-db/app-db]\n+    (fn []\n+      (reset! @#'rf-registrar/kind->id->handler handlers)\n+      (reset! @#'rf-db/app-db app-db)\n+      nil)))\n+\n+(defmacro with-restored-re-frame-state\n+  \"Run `body`, but discard whatever effects it may have on re-frame's internal\n+  state (by resetting `app-db` and re-frame's various different types of\n+  handlers after `body` has run).\"\n+  [& body]\n+  `(let [restore-fn# (make-re-frame-restore-fn)]\n+     (try\n+       ~@body\n+       (finally\n+         (restore-fn#)))))\n+\n+\n+;;;;\n+;;;; Async tests\n+;;;;\n+\n+(def ^:dynamic *test-timeout* 5000)\n+\n+(def ^{:dynamic true, :private true} *test-context*\n+  \"`*test-context*` is used to communicate internal details of the test between\n+  `run-test-async*` and `wait-for*`. It is dynamically bound so that it doesn't\n+  need to appear as a lexical argument to a `wait-for` block, since we don't\n+  want it to be visible when you're writing tests.  But care must be taken to\n+  pass it around lexically across callbacks, since ClojureScript doesn't have\n+  `bound-fn`.\"\n+  nil)\n+\n+\n+(defn run-test-async* [f]\n+  (let [test-context {:wait-for-depth     0\n+                      :max-wait-for-depth (atom 0)\n+                      :now-waiting-for    (atom nil)}]\n+    #?(:clj  (let [done-promise (promise)\n+                   executor     (Executors/newSingleThreadExecutor)\n+                   fail-ex      (atom nil)\n+                   test-context (assoc test-context :done #(deliver done-promise ::done))]\n+               (with-redefs [rf-int/executor executor]\n+                 ;; Execute the test code itself on the same thread as the\n+                 ;; re-frame event handlers run, so that we accurately\n+                 ;; simulate the single-threaded JS environment and also so\n+                 ;; that we don't have to worry about making the JVM\n+                 ;; implementation of the re-frame EventQueue thread-safe.\n+                 (rf-int/next-tick #(try\n+                                      (binding [*test-context* test-context]\n+                                        (f))\n+                                      (catch Throwable t\n+                                        (reset! fail-ex t)))))\n+               (let [result (deref done-promise *test-timeout* ::timeout)]\n+                 (doto executor\n+                   (.shutdownNow)\n+                   (.awaitTermination 1 TimeUnit/SECONDS))\n+                 (if-let [ex @fail-ex]\n+                   (throw ex)","path":"src/day8/re_frame/test.cljc","position":154,"original_position":154,"commit_id":"4c7aa72e6b7d5b70e3adcbb69697ea5d39900384","original_commit_id":"4c7aa72e6b7d5b70e3adcbb69697ea5d39900384","user":{"login":"samroberton","id":1249650,"avatar_url":"https://avatars.githubusercontent.com/u/1249650?v=3","gravatar_id":"","url":"https://api.github.com/users/samroberton","html_url":"https://github.com/samroberton","followers_url":"https://api.github.com/users/samroberton/followers","following_url":"https://api.github.com/users/samroberton/following{/other_user}","gists_url":"https://api.github.com/users/samroberton/gists{/gist_id}","starred_url":"https://api.github.com/users/samroberton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samroberton/subscriptions","organizations_url":"https://api.github.com/users/samroberton/orgs","repos_url":"https://api.github.com/users/samroberton/repos","events_url":"https://api.github.com/users/samroberton/events{/privacy}","received_events_url":"https://api.github.com/users/samroberton/received_events","type":"User","site_admin":false},"body":"They're alternate branches of an `if` (well, `if-let`), so no.\r\n\r\nThe logic here is \"if the execution of the test code itself (on the re-frame event queue thread) threw an exception, then rethrow it out of the test thread;  otherwise, just check that the test didn't time out with a normal `clojure.test` assertion\".","created_at":"2016-08-16T06:38:08Z","updated_at":"2016-08-16T06:38:08Z","html_url":"https://github.com/Day8/re-frame-test/pull/2#discussion_r74882421","pull_request_url":"https://api.github.com/repos/Day8/re-frame-test/pulls/2","_links":{"self":{"href":"https://api.github.com/repos/Day8/re-frame-test/pulls/comments/74882421"},"html":{"href":"https://github.com/Day8/re-frame-test/pull/2#discussion_r74882421"},"pull_request":{"href":"https://api.github.com/repos/Day8/re-frame-test/pulls/2"}}},"pull_request":{"url":"https://api.github.com/repos/Day8/re-frame-test/pulls/2","id":80736891,"html_url":"https://github.com/Day8/re-frame-test/pull/2","diff_url":"https://github.com/Day8/re-frame-test/pull/2.diff","patch_url":"https://github.com/Day8/re-frame-test/pull/2.patch","issue_url":"https://api.github.com/repos/Day8/re-frame-test/issues/2","number":2,"state":"open","locked":false,"title":"Initial sync and async test framework","user":{"login":"samroberton","id":1249650,"avatar_url":"https://avatars.githubusercontent.com/u/1249650?v=3","gravatar_id":"","url":"https://api.github.com/users/samroberton","html_url":"https://github.com/samroberton","followers_url":"https://api.github.com/users/samroberton/followers","following_url":"https://api.github.com/users/samroberton/following{/other_user}","gists_url":"https://api.github.com/users/samroberton/gists{/gist_id}","starred_url":"https://api.github.com/users/samroberton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samroberton/subscriptions","organizations_url":"https://api.github.com/users/samroberton/orgs","repos_url":"https://api.github.com/users/samroberton/repos","events_url":"https://api.github.com/users/samroberton/events{/privacy}","received_events_url":"https://api.github.com/users/samroberton/received_events","type":"User","site_admin":false},"body":"Doesn't yet include tests of the test framework itself.\r\n\r\nExample tests incoming as a for-review-but-not-to-be-merged PR for the todomvc example in the re-frame repo.","created_at":"2016-08-10T05:36:19Z","updated_at":"2016-08-16T06:38:08Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6612654397e25295cf2398564070ca5c34a07f09","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/Day8/re-frame-test/pulls/2/commits","review_comments_url":"https://api.github.com/repos/Day8/re-frame-test/pulls/2/comments","review_comment_url":"https://api.github.com/repos/Day8/re-frame-test/pulls/comments{/number}","comments_url":"https://api.github.com/repos/Day8/re-frame-test/issues/2/comments","statuses_url":"https://api.github.com/repos/Day8/re-frame-test/statuses/4c7aa72e6b7d5b70e3adcbb69697ea5d39900384","head":{"label":"samroberton:master","ref":"master","sha":"4c7aa72e6b7d5b70e3adcbb69697ea5d39900384","user":{"login":"samroberton","id":1249650,"avatar_url":"https://avatars.githubusercontent.com/u/1249650?v=3","gravatar_id":"","url":"https://api.github.com/users/samroberton","html_url":"https://github.com/samroberton","followers_url":"https://api.github.com/users/samroberton/followers","following_url":"https://api.github.com/users/samroberton/following{/other_user}","gists_url":"https://api.github.com/users/samroberton/gists{/gist_id}","starred_url":"https://api.github.com/users/samroberton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samroberton/subscriptions","organizations_url":"https://api.github.com/users/samroberton/orgs","repos_url":"https://api.github.com/users/samroberton/repos","events_url":"https://api.github.com/users/samroberton/events{/privacy}","received_events_url":"https://api.github.com/users/samroberton/received_events","type":"User","site_admin":false},"repo":{"id":65278067,"name":"re-frame-test","full_name":"samroberton/re-frame-test","owner":{"login":"samroberton","id":1249650,"avatar_url":"https://avatars.githubusercontent.com/u/1249650?v=3","gravatar_id":"","url":"https://api.github.com/users/samroberton","html_url":"https://github.com/samroberton","followers_url":"https://api.github.com/users/samroberton/followers","following_url":"https://api.github.com/users/samroberton/following{/other_user}","gists_url":"https://api.github.com/users/samroberton/gists{/gist_id}","starred_url":"https://api.github.com/users/samroberton/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/samroberton/subscriptions","organizations_url":"https://api.github.com/users/samroberton/orgs","repos_url":"https://api.github.com/users/samroberton/repos","events_url":"https://api.github.com/users/samroberton/events{/privacy}","received_events_url":"https://api.github.com/users/samroberton/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/samroberton/re-frame-test","description":"Cross platform (cljs and clj) utilities for testing re-frame applications","fork":true,"url":"https://api.github.com/repos/samroberton/re-frame-test","forks_url":"https://api.github.com/repos/samroberton/re-frame-test/forks","keys_url":"https://api.github.com/repos/samroberton/re-frame-test/keys{/key_id}","collaborators_url":"https://api.github.com/repos/samroberton/re-frame-test/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/samroberton/re-frame-test/teams","hooks_url":"https://api.github.com/repos/samroberton/re-frame-test/hooks","issue_events_url":"https://api.github.com/repos/samroberton/re-frame-test/issues/events{/number}","events_url":"https://api.github.com/repos/samroberton/re-frame-test/events","assignees_url":"https://api.github.com/repos/samroberton/re-frame-test/assignees{/user}","branches_url":"https://api.github.com/repos/samroberton/re-frame-test/branches{/branch}","tags_url":"https://api.github.com/repos/samroberton/re-frame-test/tags","blobs_url":"https://api.github.com/repos/samroberton/re-frame-test/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/samroberton/re-frame-test/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/samroberton/re-frame-test/git/refs{/sha}","trees_url":"https://api.github.com/repos/samroberton/re-frame-test/git/trees{/sha}","statuses_url":"https://api.github.com/repos/samroberton/re-frame-test/statuses/{sha}","languages_url":"https://api.github.com/repos/samroberton/re-frame-test/languages","stargazers_url":"https://api.github.com/repos/samroberton/re-frame-test/stargazers","contributors_url":"https://api.github.com/repos/samroberton/re-frame-test/contributors","subscribers_url":"https://api.github.com/repos/samroberton/re-frame-test/subscribers","subscription_url":"https://api.github.com/repos/samroberton/re-frame-test/subscription","commits_url":"https://api.github.com/repos/samroberton/re-frame-test/commits{/sha}","git_commits_url":"https://api.github.com/repos/samroberton/re-frame-test/git/commits{/sha}","comments_url":"https://api.github.com/repos/samroberton/re-frame-test/comments{/number}","issue_comment_url":"https://api.github.com/repos/samroberton/re-frame-test/issues/comments{/number}","contents_url":"https://api.github.com/repos/samroberton/re-frame-test/contents/{+path}","compare_url":"https://api.github.com/repos/samroberton/re-frame-test/compare/{base}...{head}","merges_url":"https://api.github.com/repos/samroberton/re-frame-test/merges","archive_url":"https://api.github.com/repos/samroberton/re-frame-test/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/samroberton/re-frame-test/downloads","issues_url":"https://api.github.com/repos/samroberton/re-frame-test/issues{/number}","pulls_url":"https://api.github.com/repos/samroberton/re-frame-test/pulls{/number}","milestones_url":"https://api.github.com/repos/samroberton/re-frame-test/milestones{/number}","notifications_url":"https://api.github.com/repos/samroberton/re-frame-test/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/samroberton/re-frame-test/labels{/name}","releases_url":"https://api.github.com/repos/samroberton/re-frame-test/releases{/id}","deployments_url":"https://api.github.com/repos/samroberton/re-frame-test/deployments","created_at":"2016-08-09T08:37:24Z","updated_at":"2016-08-09T08:37:25Z","pushed_at":"2016-08-10T05:31:24Z","git_url":"git://github.com/samroberton/re-frame-test.git","ssh_url":"git@github.com:samroberton/re-frame-test.git","clone_url":"https://github.com/samroberton/re-frame-test.git","svn_url":"https://github.com/samroberton/re-frame-test","homepage":null,"size":19,"stargazers_count":0,"watchers_count":0,"language":"Clojure","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"Day8:master","ref":"master","sha":"7138b618629e05f3240c9b9e8420612459fa1bc3","user":{"login":"Day8","id":3033386,"avatar_url":"https://avatars.githubusercontent.com/u/3033386?v=3","gravatar_id":"","url":"https://api.github.com/users/Day8","html_url":"https://github.com/Day8","followers_url":"https://api.github.com/users/Day8/followers","following_url":"https://api.github.com/users/Day8/following{/other_user}","gists_url":"https://api.github.com/users/Day8/gists{/gist_id}","starred_url":"https://api.github.com/users/Day8/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Day8/subscriptions","organizations_url":"https://api.github.com/users/Day8/orgs","repos_url":"https://api.github.com/users/Day8/repos","events_url":"https://api.github.com/users/Day8/events{/privacy}","received_events_url":"https://api.github.com/users/Day8/received_events","type":"Organization","site_admin":false},"repo":{"id":64102309,"name":"re-frame-test","full_name":"Day8/re-frame-test","owner":{"login":"Day8","id":3033386,"avatar_url":"https://avatars.githubusercontent.com/u/3033386?v=3","gravatar_id":"","url":"https://api.github.com/users/Day8","html_url":"https://github.com/Day8","followers_url":"https://api.github.com/users/Day8/followers","following_url":"https://api.github.com/users/Day8/following{/other_user}","gists_url":"https://api.github.com/users/Day8/gists{/gist_id}","starred_url":"https://api.github.com/users/Day8/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Day8/subscriptions","organizations_url":"https://api.github.com/users/Day8/orgs","repos_url":"https://api.github.com/users/Day8/repos","events_url":"https://api.github.com/users/Day8/events{/privacy}","received_events_url":"https://api.github.com/users/Day8/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/Day8/re-frame-test","description":"Cross platform (cljs and clj) utilities for testing re-frame applications","fork":false,"url":"https://api.github.com/repos/Day8/re-frame-test","forks_url":"https://api.github.com/repos/Day8/re-frame-test/forks","keys_url":"https://api.github.com/repos/Day8/re-frame-test/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Day8/re-frame-test/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Day8/re-frame-test/teams","hooks_url":"https://api.github.com/repos/Day8/re-frame-test/hooks","issue_events_url":"https://api.github.com/repos/Day8/re-frame-test/issues/events{/number}","events_url":"https://api.github.com/repos/Day8/re-frame-test/events","assignees_url":"https://api.github.com/repos/Day8/re-frame-test/assignees{/user}","branches_url":"https://api.github.com/repos/Day8/re-frame-test/branches{/branch}","tags_url":"https://api.github.com/repos/Day8/re-frame-test/tags","blobs_url":"https://api.github.com/repos/Day8/re-frame-test/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Day8/re-frame-test/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Day8/re-frame-test/git/refs{/sha}","trees_url":"https://api.github.com/repos/Day8/re-frame-test/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Day8/re-frame-test/statuses/{sha}","languages_url":"https://api.github.com/repos/Day8/re-frame-test/languages","stargazers_url":"https://api.github.com/repos/Day8/re-frame-test/stargazers","contributors_url":"https://api.github.com/repos/Day8/re-frame-test/contributors","subscribers_url":"https://api.github.com/repos/Day8/re-frame-test/subscribers","subscription_url":"https://api.github.com/repos/Day8/re-frame-test/subscription","commits_url":"https://api.github.com/repos/Day8/re-frame-test/commits{/sha}","git_commits_url":"https://api.github.com/repos/Day8/re-frame-test/git/commits{/sha}","comments_url":"https://api.github.com/repos/Day8/re-frame-test/comments{/number}","issue_comment_url":"https://api.github.com/repos/Day8/re-frame-test/issues/comments{/number}","contents_url":"https://api.github.com/repos/Day8/re-frame-test/contents/{+path}","compare_url":"https://api.github.com/repos/Day8/re-frame-test/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Day8/re-frame-test/merges","archive_url":"https://api.github.com/repos/Day8/re-frame-test/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Day8/re-frame-test/downloads","issues_url":"https://api.github.com/repos/Day8/re-frame-test/issues{/number}","pulls_url":"https://api.github.com/repos/Day8/re-frame-test/pulls{/number}","milestones_url":"https://api.github.com/repos/Day8/re-frame-test/milestones{/number}","notifications_url":"https://api.github.com/repos/Day8/re-frame-test/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Day8/re-frame-test/labels{/name}","releases_url":"https://api.github.com/repos/Day8/re-frame-test/releases{/id}","deployments_url":"https://api.github.com/repos/Day8/re-frame-test/deployments","created_at":"2016-07-25T03:56:26Z","updated_at":"2016-08-02T13:05:04Z","pushed_at":"2016-08-10T05:36:20Z","git_url":"git://github.com/Day8/re-frame-test.git","ssh_url":"git@github.com:Day8/re-frame-test.git","clone_url":"https://github.com/Day8/re-frame-test.git","svn_url":"https://github.com/Day8/re-frame-test","homepage":null,"size":17,"stargazers_count":1,"watchers_count":1,"language":"Clojure","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":2,"forks":1,"open_issues":2,"watchers":1,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/Day8/re-frame-test/pulls/2"},"html":{"href":"https://github.com/Day8/re-frame-test/pull/2"},"issue":{"href":"https://api.github.com/repos/Day8/re-frame-test/issues/2"},"comments":{"href":"https://api.github.com/repos/Day8/re-frame-test/issues/2/comments"},"review_comments":{"href":"https://api.github.com/repos/Day8/re-frame-test/pulls/2/comments"},"review_comment":{"href":"https://api.github.com/repos/Day8/re-frame-test/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/Day8/re-frame-test/pulls/2/commits"},"statuses":{"href":"https://api.github.com/repos/Day8/re-frame-test/statuses/4c7aa72e6b7d5b70e3adcbb69697ea5d39900384"}}}}
{"actor":{"display_login":"samroberton"}}