From c1ce14912e5ddaaed274e2f5e41ef781d80be1f2 Mon Sep 17 00:00:00 2001 From: Assaf Arkin Date: Sat, 26 May 2012 20:16:20 -0700 Subject: [PATCH] Some improvements to the docs. --- README.md | 101 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 56 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 815152dee..83554d94b 100644 --- a/README.md +++ b/README.md @@ -275,27 +275,25 @@ Here are some guidelines for writing tests using Zombie, Let's start with a simple example: describe("visit", function() { - var browser = null; - before(function(done) { - browser = new Browser(); - browser + this.browser = new Browser(); + this.browser .visit("/promises") .then(done, done); }); it("should load the promises page", function() { - assert.equal(browser.location.pathname, "/promises"); + assert.equal(this.browser.location.pathname, "/promises"); }); }); The call to `visit` returns a promise. Once the page loads successfully, the -promise will resolve and call the first callback with no arguments. This will -run the test and evaluate the assertion. +promise will resolve and call the first callback (`done`) with no arguments. +This will run the test and evaluate the assertion. Success. -If there's an error, the promise fails and calls the second callback with an -error. It's the same function, but this time calling it with an `Error` -argument causes Mocha to fail the test. +If there's an error, the promise fails and calls the second callback (also +`done`) with an error. Calling it with an `Error` argument causes Mocha to fail +the test. Now let's try to chain promises together: @@ -326,12 +324,12 @@ promise resolves, it calls the function of the second step which fills in two form fields. That step is in itself a promise that resolves to no value. The third step follows, and here we simply return a value. As a result, the -next promise will resolve with that value, as you can see in step 4. Another -way to think about it is: step 4 is chained to a new promise with the value -"OK". +next promise will resolve with that value, as you can see in the fourth step. +Another way to think about it is: step four is chained to a new promise with the +value "OK". -On to step 5 where we press the button, which submits the form and loads the -response page. All of that happens after `pressButton` so we want to wait for +On to step five where we press the button, which submits the form and loads the +response page. All of that happens after `pressButton`, so we want to wait for it before moving to the last step. Luckily, `pressButton` - just like `wait` - returns a promise which gets @@ -339,18 +337,19 @@ fulfilled after the browser is done processing events and loading resources. By returning this new promise, we cause the next and last step to wait for this promise to resolve. -Another way to think about it is: the last step is chained to a new promise +Another way to think about it is: the very last step is chained to a new promise returned by `pressButton`. You can use this pattern whenever you need to wait, after `visit`, `reload`, `clickLink`, etc. -Note: in CoffeeScript a function that doesn't end with explicit `return` -statement would return the value of the last statement. You may need to -explicitly end functions with empty `return`. +**Note:** in CoffeeScript a function that doesn't end with explicit `return` +statement would return the value of the last statement. If you're seeing +promises resolved with unexpected values, you may need to explicitly end +functions with an empty `return`. In real life the ability to chain promises like that is useful because it helps you structure complex scenarios without nesting callbacks. Even more -interesting, you can write simple steps as functions and then combine them into -different scenarios. Like so: +interesting, you can write reusable steps as functions and then combine them +into complex scenarios. Like so: browser .visit("/promises") @@ -362,50 +361,62 @@ different scenarios. Like so: }) .then(done, done); -Let's talk a bit about error handling. In promise land, errors cause the -promise to be rejected with that `Error` object asn a argument. However, no -error is thrown, and so this code will fail silently: +**Note:** This usage of `bind` is one way to allow a function defined in some +other context to use the `Browser` object available in this context. + +Let's talk a bit about error handling. In promise-land, an error causes the +promise to be Qrejected with that `Error` object as an argument. However, +errors are not re-thrown out of the promise, and so this code will fail +silently: browser .visit("/promises") .then(function() { + // This throws an error, which gets caught and rejects + // the promise. assert(false, "I fail!"); }) .then(done); Rejection may not be fun, but you've got to handle it. -If a promise gets rejected with an error, that rejection will travel up the -chain, so you only need to handle it at the very end. The examples above do -that by calling `then` with the same Mocha callback for resolved and rejected -promises. +If a promise gets rejected with an error, that rejection will travel down the +chain, so you only need to handle it at the very end. The examples we started +with do that by calling `then` with the same callback for both resolved and +rejected promises. -Alternatively, you can end the chain so any unhandled error will be thrown, like -so: +If your test case expects an error to happen and must fail only if an error +doesn't happen, write it like this: browser .visit("/promises") .then(function() { - assert(false, "I fail!"); - }) - .fail(done) - .then(function() { - // Mocha will error the test if you pass value to callback - done(true); - }); - -If your test expects an error to happen and must fail only if an error doesn't -happen, you can write something like this: - - browser - .visit("/promises") - .then(function() { - assert(fale, "I fail!") + assert(false, "I fail!") }) .fail(function(error) { + // Error happened, test is done. Otherwise, done never + // gets called and test will fail. done(); }); +Another way of dealing with errors: + + before(function(done) { + browser + .visit("/no-such-page") + .finally(done); + }); + + it("should report an error", function() { + assert(browser.error); + }) + +This is particular to Mocha. If we used `then(done)`, the callback would never +get called since the promise gets rejected (no such page to load). If we used +`fail(done)` the callback would be called with an error, causing the test to +fail. But `finally` calls our callback whether the promise got resolved or +rejected. + Read more [about promises](http://documentup.com/kriskowal/q/).