Skip to content
This repository has been archived by the owner on Dec 16, 2023. It is now read-only.

Commit

Permalink
Merge branch 'lazy-promise'
Browse files Browse the repository at this point in the history
Conflicts:
	CHANGELOG.md
	package.json
  • Loading branch information
assaf committed Dec 5, 2014
2 parents 2bc2ee6 + 7badf3e commit 2c555e8
Show file tree
Hide file tree
Showing 12 changed files with 420 additions and 263 deletions.
22 changes: 19 additions & 3 deletions CHANGELOG.md
@@ -1,10 +1,26 @@
## Version 2.4.1 2014-12-03
## Version 2.5.0 2014-12-01

ADDED support for navigator.mimeTypes

698 passing (12s)
8 pending
CHANGED wait() returns a lazy promise

Prior to this change, calling `wait()` without a callback would return a
promise, which will resolve by running the event loop for completion, even if
you don't provide any callbacks.

This is not specifically a problem with `wait`, but with methods that end by
calling `wait`, like `clickLink` and `pressButton`.

After this change, `wait()` will do nothing, unless you either supply a
callback, or use the promise by means of calling `then/catch/done` on it.

You can achieve the old behavior by calling `browser.wait().done()`.

REMOVED Passing an options object to browser.visit is deprecated and will be
removed soon. Passing an options object to Browser.visit is still supported.

696 passing (12s)
8 pending


## Version 2.4.0 2014-11-27
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -46,6 +46,7 @@
"eventsource": "^0.1.4",
"iconv-lite": "^0.4.0",
"jsdom": "1.3.2",
"lazybird": "^1.0.0",
"mime": "^1.2.11",
"ms": "^0.7.0",
"request": "^2.48.0",
Expand Down
21 changes: 10 additions & 11 deletions src/zombie/browser.coffee
Expand Up @@ -212,11 +212,9 @@ class Browser extends EventEmitter
return ifMissing


# Changes the browser options, and calls the function with a callback (reset). When you're done processing, call the
# reset function to bring options back to their previous values.
#
# See `visit` if you want to see this method in action.
# Deprecated
withOptions: (options, fn)->
console.log "visit with options is deprecated and will be removed soon"
if options
restore = {}
for k,v of options
Expand Down Expand Up @@ -497,17 +495,15 @@ class Browser extends EventEmitter
# ----------

# ### browser.visit(url, callback?)
# ### browser.visit(url, options, callback)
#
# Loads document from the specified URL, processes events and calls the callback. If the second argument are options,
# uses these options for the duration of the request and resets the options afterwards.
#
# The callback is called with error, the browser and status code.
# Loads document from the specified URL, processes events and calls the callback, or returns a promise.
visit: (url, options, callback)->
if typeof options == "function" && !callback
[callback, options] = [options, null]

resetOptions = @withOptions(options)
if options
resetOptions = @withOptions(options)

if site = @site
site = "http://#{site}" unless /^(https?:|file:)/i.test(site)
url = URL.resolve(site, URL.parse(URL.format(url)))
Expand All @@ -516,7 +512,10 @@ class Browser extends EventEmitter
@tabs.close(@window)
@tabs.open(url: url, referer: @referer)

promise = @wait(options).finally(resetOptions)
if options
promise = @wait(options).finally(resetOptions)
else
promise = @wait(options)
if callback
promise.done(callback, callback)
return
Expand Down
107 changes: 56 additions & 51 deletions src/zombie/eventloop.coffee
Expand Up @@ -20,6 +20,7 @@ Domain = require("domain")
{ EventEmitter } = require("events")
ms = require("ms")
{ Promise } = require("bluebird")
Lazybird = require("lazybird")


# The browser event loop.
Expand Down Expand Up @@ -81,63 +82,67 @@ class EventLoop extends EventEmitter
waitDuration = ms(waitDuration.toString()) || @browser.waitDuration
timeoutOn = Date.now() + waitDuration

# Someone (us) just started paying attention, start processing events
++@waiting
if @waiting == 1
setImmediate =>
if @active
@run()

timer = null
ontick = null
onerror = null
ondone = null

promise = new Promise((resolve, reject)=>
timer = global.setTimeout(resolve, waitDuration)

ontick = (next)=>
if next >= timeoutOn
# Next event too long in the future, or no events in queue
# (Infinity), no point in waiting
resolve()
else if completionFunction && @active.document.documentElement
try
waitFor = Math.max(next - Date.now(), 0)
# Event processed, are we ready to complete?
completed = completionFunction(@active, waitFor)
if completed
resolve()
catch error
reject(error)
return
@on("tick", ontick)
lazy = new Lazybird((resolve, reject)=>

ondone = resolve
@once("done", ondone)
# Someone (us) just started paying attention, start processing events
++@waiting
if @waiting == 1
setImmediate =>
if @active
@run()

timer = null
ontick = null
onerror = null
ondone = null

work = new Promise((resolve, reject)=>
timer = global.setTimeout(resolve, waitDuration)

ontick = (next)=>
if next >= timeoutOn
# Next event too long in the future, or no events in queue
# (Infinity), no point in waiting
resolve()
else if completionFunction && @active.document.documentElement
try
waitFor = Math.max(next - Date.now(), 0)
# Event processed, are we ready to complete?
completed = completionFunction(@active, waitFor)
if completed
resolve()
catch error
reject(error)
return
@on("tick", ontick)

ondone = resolve
@once("done", ondone)


# Don't wait if browser encounters an error (event loop errors also
# propagate to browser)
onerror = reject
@browser.once("error", onerror)
return
)

# Don't wait if browser encounters an error (event loop errors also
# propagate to browser)
onerror = reject
@browser.once("error", onerror)
return
)
finalized = work.finally(=>

promise = promise.finally(=>

clearInterval(timer)
@removeListener("tick", ontick)
@removeListener("done", ondone)
@browser.removeListener("error", onerror)
clearInterval(timer)
@removeListener("tick", ontick)
@removeListener("done", ondone)
@browser.removeListener("error", onerror)

--@waiting
if @waiting == 0
@browser.emit("done")
return
)
--@waiting
if @waiting == 0
@browser.emit("done")
return
)

return promise
resolve(finalized)
)
return lazy


dump: ()->
Expand Down
36 changes: 10 additions & 26 deletions src/zombie/index.coffee
@@ -1,38 +1,28 @@
Assert = require("./assert")
Resources = require("./resources")
Browser = require("./browser")
Path = require("path")


Browser.Assert = Assert
Browser.Resources = Resources


# ### zombie.visit(url, callback)
# ### zombie.visit(url, options, callback)
# ### zombie.visit(url, options? callback)
#
# Creates a new Browser, opens window to the URL and calls the callback when
# done processing all events.
#
# For example:
# zombie = require("zombie")
#
# vows.describe("Brains").addBatch(
# "seek":
# topic: ->
# zombie.browse "http://localhost:3000/brains", @callback
# "should find": (browser)->
# assert.ok browser.html("#brains")[0]
# ).export(module);
#
# * url -- URL of page to open
# * options -- Initialize the browser with these options
# * callback -- Called with error, browser
visit = (url, options, callback)->
if arguments.length == 2
Browser.visit = (url, options, callback)->
if arguments.length == 2 && typeof(options) == "function"
[options, callback] = [null, options]
browser = Browser.create(options)
if callback
browser.visit url, options, (error)->
callback(error, browser)
browser.visit(url, (error)-> callback(error, browser))
else
return browser.visit(url, options).then(-> browser);
return browser.visit(url).then(-> browser);


# ### listen port, callback
Expand All @@ -42,15 +32,9 @@ visit = (url, options, callback)->
# Ask Zombie to listen on the specified port for requests. The default
# port is 8091, or you can specify a socket name. The callback is
# invoked once Zombie is ready to accept new connections.
listen = (port, callback)->
Browser.listen = (port, callback)->
require("./zombie/protocol").listen(port, callback)


Browser.listen = listen
Browser.visit = visit
Browser.Assert = Assert
Browser.Resources = Resources


# Export the globals from browser.coffee
module.exports = Browser
36 changes: 15 additions & 21 deletions test/browser_object_test.js
Expand Up @@ -77,43 +77,37 @@ describe('Browser', function() {
describe('visit', function() {

describe('successful', function() {
let callbackBrowser;
let browser;

before(async function() {
callbackBrowser = await Browser.visit('/browser/scripted');
browser = await Browser.visit('/browser/scripted');
});

it('should pass browser to callback', function() {
assert(callbackBrowser instanceof Browser);
});
it('should pass status code to callback', function() {
callbackBrowser.assert.success();
it('should resolve to browser object', function() {
assert(browser.visit && browser.wait);
});
it('should indicate success', function() {
assert(callbackBrowser.success);
browser.assert.success();
});
it('should reset browser errors', function() {
assert.equal(callbackBrowser.errors.length, 0);
});
it('should have a resources object', function() {
assert(callbackBrowser.resources);
assert.equal(browser.errors.length, 0);
});
});

describe('with error', function() {
let error;
let browser;

before(async function() {
try {
await browser.visit('/browser/errored');
assert(false, 'Should have errored');
} catch (callbackError) {
error = callbackError;
}
before(function(done) {
Browser.visit('/browser/errored', function() {
error = arguments[0];
browser = arguments[1];
done();
});
});

it('should call callback with error', function() {
assert.equal(error.constructor.name, 'TypeError');
assert(error.message && error.stack);
});
it('should indicate success', function() {
browser.assert.success();
Expand Down Expand Up @@ -270,7 +264,7 @@ describe('Browser', function() {
browser.once('done', done);
done = true;
browser.location = '/browser/scripted';
browser.wait();
browser.wait().done();
});
});

Expand Down

0 comments on commit 2c555e8

Please sign in to comment.