Skip to content

Instantly share code, notes, and snippets.

@mike-thompson-day8
Last active July 25, 2016 00:27
Show Gist options
  • Save mike-thompson-day8/f4ab1859a386087c2afe05340ecb7263 to your computer and use it in GitHub Desktop.
Save mike-thompson-day8/f4ab1859a386087c2afe05340ecb7263 to your computer and use it in GitHub Desktop.

I've only written this from a cljs point of view.
code is untested

Callback version:

(defn wait-for-event
  "Waits for an event to occur and then calls 'callback-fn'.
  'ids' can be a single event id, or a collection of ids.
  'callback-fn' is called when any one of the nominated event ids occurs.
  'callback-fn' is called with one arguement: the full triggering event.
  If one of the failure ids is seen, then cause the test to fail.
  This is a one-off wait. The callback-fn will be called at most once."
  [ids failure-ids callback-fn]
  (let [ok-set (if (coll? ids) (set ids) #{ids})
        fail-set (if (coll? failure-ids) (set failure-ids) #{failure-ids})]
    (re-frame.core.add-post-event-callback

      ;; this listner is called post event processing.
      ;; the two arguments are: (1) the full event  (2) the current queue
      (fn listner
        [new-event _]
        (let [new-id (first new-event)]

          ;; if the new event has a failure id, then fail the test
          (when (fail-set new-id)
            (re-frame.core.remove-post-event-callback listner)
            (#?(:cljs cljs.test/do-report  :clj clojure.test/do-report)
              {:type     :fail
               :message  "wait-for-event: didn't get expected event."
               :expected ok-set
               :actual   new-event}))

          ;; if the new event is one we are waiting for, then call callback-fn
          (when (ok-set new-id)
            (re-frame.core.remove-post-event-callback listner)
            (callback-fn new-event)))))))

Example Use:

(deftest test-wait-for-event

  (async          ;; <-- we'll neeed to write a version of this macro for JVM
    done          ;; <-- this is the function to call when async test in finished

    (dispatch-sync [:boot])
    (is (= true  @(subscribe [:booted?])))
    (dispatch-sync [:something-else])
    (dispatch-sync [:get-user])

    (wait-for-event
      :got-user       ;; waiting for this event to happen
      :failed-user    ;; this event means failure
      (fn [_]

        ;; the event has happened, so continue on now we containue on with test
        (dispatch [:next-step])
        (is (= :this @(subscribe [:that])))
        (done)))))         ;; must call 'done' when the test is finished

go block Version

(defn <!wait-for-event
  "works like wait-for-event but without a callback-fn. Instead
  it returns a core.asysnc channel through which the waited-for
  event is returned.
  Without callbacks the test flow looks more sequential, at the expense
  of introducing a go block."
  [ids failure-ids]
  (let [ch (chan) ]
    (wait-for-event
      ids
      failure-ids
      (fn [new-event]
        (>!! ch new-event)))
    ch))

Example use:

(deftest test-with-go-block

  (async
    done
    (go
      (dispatch-sync [:boot])
      (is (= true  @(subscribe [:booted?])))
      (dispatch-sync [:something-else])
      (dispatch-sync [:get-user])

      (<! (<!wait-for-event :got-user :failed-user))

      (dispatch [:next-step])
      (is (= :this @(subscribe [:that])))
      (done))))        ;; must call this

Notes:

  1. No built-in notion of timeouts. I figure that features under test will handle this. For example, the :get-user process will look after timeouts and and we'll see a :fail-user event in the case of a timeout, and that will be one of the failure events we can look for.

    Of course, adding timeouts to the go block version is easy enough because we have full power of core async available

  2. How do we write the JVM version of 'async' Name it: re-frame.async-test. Make it cross platform.

  3. Still need a way to "push the state of re-frame" and then "pop the state of reframe" via a fixture.

@escherize
Copy link

pretty rough draft^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment