Code Monkey home page Code Monkey logo

zombie's Introduction

Zombie.js

Insanely fast, headless full-stack testing using Node.js

NPM Changelog Travis.ci JS.ORG

The Bite

If you're going to write an insanely fast, headless browser, how can you not call it Zombie? Zombie it is.

Zombie.js is a lightweight framework for testing client-side JavaScript code in a simulated environment. No browser required.

Let's try to sign up to a page and see what happens:

const Browser = require('zombie');

// We're going to make requests to http://example.com/signup
// Which will be routed to our test server localhost:3000
Browser.localhost('example.com', 3000);

describe('User visits signup page', function() {

  const browser = new Browser();

  before(function(done) {
    browser.visit('/signup', done);
  });

  describe('submits form', function() {

    before(function(done) {
      browser.fill('email', '[email protected]')
      .then(() => browser.fill('password', 'eat-the-living'))
      .then(() => browser.pressButton('Sign Me Up!', done));
    });

    it('should be successful', function() {
      browser.assert.success();
    });

    it('should see welcome page', function() {
      browser.assert.text('title', 'Welcome To Brains Depot');
    });
  });
});

This example uses the Mocha testing framework, but Zombie will work with other testing frameworks. Since Mocha supports promises, we can also write the test like this:

const Browser = require('zombie');

// We're going to make requests to http://example.com/signup
// Which will be routed to our test server localhost:3000
Browser.localhost('example.com', 3000);

describe('User visits signup page', function() {

  const browser = new Browser();

  before(function() {
    return browser.visit('/signup');
  });

  describe('submits form', function() {

    before(function() {
      return browser.fill('email', '[email protected]')
      .then(() => browser.fill('password', 'eat-the-living'))
      .then(() => browser.pressButton('Sign Me Up!'));
    });

    it('should be successful', function() {
      browser.assert.success();
    });

    it('should see welcome page', function() {
      browser.assert.text('title', 'Welcome To Brains Depot');
    });
  });

});

Well, that was easy.

WARNING: Crawling untrusted web pages with Zombie.js is not safe.

Table of Contents

Installing

To install Zombie.js you will need Node.js:

$ npm install zombie --save-dev

Browser

browser.assert

Methods for making assertions against the browser, such as browser.assert.element('.foo').

See Assertions for detailed discussion.

browser.referer

You can use this to set the HTTP Referer header.

browser.resources

Access to history of retrieved resources. See Resources for detailed discussion.

browser.pipeline

Access to the pipeline for making requests and processing responses. Use this to add new request/response handlers the pipeline for a single browser instance, or use Pipeline.addHandler to modify all instances. See Pipeline.

browser.tabs

Array of all open tabs (windows). Allows you to operate on more than one open window at a time.

See Tabs for detailed discussion.

browser.proxy

The proxy option takes a URL so you can tell Zombie what protocol, host and port to use. Also supports Basic authentication, e.g.:

browser.proxy = 'http://me:secret@myproxy:8080'

browser.attach(selection, filename, callback)

Attaches a file to the specified input field. The second argument is the file name. callback - called with error or nothing.

browser.back([callback])

Navigate to the previous page in history. Returns the zombie.browser.Browser to allow function chaining.

browser.body

Returns a zombie.dom.DOMNode representing the body element of the current document.

browser.check(selector, [callback])

Checks a checkbox. If called without a callback, returns a promise.

browser.choose(selector, [callback])

selects a radio box option. If called without a callback, returns a promise.

browser.clickLink(selector, callback)

Clicks on a link. Clicking on a link can trigger other events, load new page, etc. Use a callback to be notified of completion. Finds link by text content or selector.

browser.dump([output])

Dump information to the console: Zombie version, current URL, history, cookies, event loop, etc. Useful for debugging and submitting error reports. output defaults to process.stdout.

browser.evaluate(code, filename)

Evaluates a JavaScript expression in the context of the current window and returns the result. When evaluating external script, also include filename.

You can also use this to evaluate a function in the context of the window: for timers and asynchronous callbacks (e.g. XHR).

browser.field(selector)

Find and return an input field (INPUT, TEXTAREA or SELECT) based on a CSS selector, field name (its name attribute) or the text value of a label associated with that field (case sensitive, but ignores leading/trailing spaces).

browser.fill(selector, value, [callback])

Fill in an input field or text area with the provided value. If called without a callback, returns a promise.

browser.fire(selector, eventName, [callback])

Fire a DOM event. You can use this to simulate a DOM event, e.g. clicking a link. These events will bubble up and can be cancelled. Like wait this method takes an optional callback. If called without a callback, returns a promise.

browser.link(selector)

Finds and returns a link by its text content or selector.

browser.load(html, [callback])

Loads the HTML, processes events and calls the callback. Without a callback, returns a promise.

browser.location

Return the location of the current document (same as window.location).

browser.pressButton(selector, [callback])

Press a button (button element or input of type submit). Typically this will submit the form. Use the callback to wait for the from submission, page to load and all events run their course.

browser.query(selector, [context])

Evaluates the CSS selector against the document (or context node) and return an element.
context defaults to document.

browser.queryAll(selector, [context])

Evaluates the CSS selector against the document (or context node) and return array of nodes. context defaults to document.

browser.querySelector(selector)

Selects the first matching element and returns it.

browser.redirected

Returns True if the page request followed a redirect.

browser.reload()

Reloads the current page. Returns the zombie.browser.Browser to allow function chaining.

browser.select(selector, value, [callback])

Selects an option inside of selector with given value. If called without a callback, returns a promise.

browser.selectOption(selector, [callback])

Selects an option.

browser.status

Returns the status code returned for this window (200, 303, etc). The same as browser.statusCode

browser.success

Return true if last response had status code 200 .. 299

browser.text(selector, [context])

Returns the text contents of the selected elements. context defaults to document.

browser.uncheck(selector, [callback])

Unchecks a checkbox. If called without a callback, returns a promise.

browser.unselect(selector, value, [callback])

Unselects an option. If called without a callback, returns a promise.

browser.unselectOption(selector, [callback])

Unselects the an option. If called without a callback, returns a promise.

browser.visit(url, options, [callback])

Loads document from the specified URL, processes events and calls the callback, or returns a promise.

browser.click(selector, [callback])

Click on the selected element. If called without callback, returns a promise.

browser.errors

Collection of errors accumulated by the browser while loading page and executing scripts.

browser.source

Returns a string of the source HTML from the last response.

browser.html(element)

Returns a string of HTML for a selected HTML element. If argument element is undefined, the function returns a string of the source HTML from the last response.

Example uses:

browser.html('div');
browser.html('div#contain');
browser.html('.selector');
browser.html();

Browser.localhost(host, port)

Allows you to make requests against a named domain and HTTP/S port, and will route it to the test server running on localhost and unprivileged port.

For example, if you want to call your application "example.com", and redirect traffic from port 80 to the test server that's listening on port 3000, you can do this:

Browser.localhost('example.com', 3000)
browser.visit('/path', function() {
  console.log(browser.location.href);
});
=> 'http://example.com/path'

The first time you call Browser.localhost, if you didn't specify Browser.site, it will set it to the hostname (in the above example, "example.com"). Whenever you call browser.visit with a relative URL, it appends it to Browser.site, so you don't need to repeat the full URL in every test case.

You can use wildcards to map domains and all hosts within these domains, and you can specify the source port to map protocols other than HTTP. For example:

// HTTP requests for example.test www.example.test will be answered by localhost
// server running on port 3000
Browser.localhost('*.example.test', 3000);
// HTTPS requests will be answered by localhost server running on port 3001
Browser.localhost('*.example.test:443', 3001);

The underlying implementation hacks net.Socket.connect, so it will route any TCP connection made by the Node application, whether Zombie or any other library. It does not affect other processes running on your machine.

Browser.extend

You can use this to customize new browser instances for your specific needs. The extension function is called for every new browser instance, and can change properties, bind methods, register event listeners, etc.

Browser.extend(function(browser) {
  browser.on('console', function(level, message) {
    logger.log(message);
  });
  browser.on('log', function(level, message) {
    logger.log(message);
  });
});

Assertions

To make life easier, Zombie introduces a set of convenience assertions that you can access directly from the browser object. For example, to check that a page loaded successfully:

browser.assert.success();
browser.assert.text('title', 'My Awesome Site');
browser.assert.element('#main');

These assertions are available from the browser object since they operate on a particular browser instance -- generally dependent on the currently open window, or document loaded in that window.

Many assertions require an element/elements as the first argument, for example, to compare the text content (assert.text), or attribute value (assert.attribute). You can pass one of the following values:

  • An HTML element or an array of HTML elements
  • A CSS selector string (e.g. "h2", ".book", "#first-name")

Many assertions take an expected value and compare it against the actual value. For example, assert.text compares the expected value against the text contents of one or more strings. The expected value can be one of:

  • A JavaScript primitive value (string, number)
  • undefined or null are used to assert the lack of value
  • A regular expression
  • A function that is called with the actual value and returns true if the assertion is true
  • Any other object will be matched using assert.deepEqual

Note that in some cases the DOM specification indicates that lack of value is an empty string, not null/undefined.

All assertions take an optional last argument that is the message to show if the assertion fails. Better yet, use a testing framework like Mocha that has good diff support and don't worry about these messages.

Available Assertions

The following assertions are available:

assert.attribute(selection, name, expected, message)

Asserts the named attribute of the selected element(s) has the expected value.

Fails if no element found.

browser.assert.attribute('form', 'method', 'post');
browser.assert.attribute('form', 'action', '/customer/new');
// Disabled with no attribute value, i.e. <button disabled>
browser.assert.attribute('button', 'disabled', '');
// No disabled attribute i.e. <button>
browser.assert.attribute('button', 'disabled', null);

assert.className(selection, className, message)

Asserts that selected element(s) has that and only that class name. May also be space-separated list of class names.

Fails if no element found.

browser.assert.className('form input[name=email]', 'has-error');

assert.cookie(identifier, expected, message)

Asserts that a cookie exists and has the expected value, or if expected is null, that no such cookie exists.

The identifier is either the name of a cookie, or an object with the property name and the optional properties domain and path.

browser.assert.cookie('flash', 'Missing email address');

assert.element(selection, message)

Asserts that one element matching selection exists.

Fails if no element or more than one matching element are found.

browser.assert.element('form');
browser.assert.element('form input[name=email]');
browser.assert.element('form input[name=email].has-error');

assert.elements(selection, count, message)

Asserts how many elements exist in the selection.

The argument count can be a number, or an object with the following properties:

  • atLeast - Expecting to find at least that many elements
  • atMost - Expecting to find at most that many elements
  • exactly - Expecting to find exactly that many elements
browser.assert.elements('form', 1);
browser.assert.elements('form input', 3);
browser.assert.elements('form input.has-error', { atLeast: 1 });
browser.assert.elements('form input:not(.has-error)', { atMost: 2 });

assert.evaluate(expression, expected, message)

Evaluates the JavaScript expression in the context of the currently open window.

With one argument, asserts that the value is equal to true.

With two/three arguments, asserts that the returned value matches the expected value.

browser.assert.evaluate('$("form").data("valid")');
browser.assert.evaluate('$("form").data("errors").length', 3);

assert.global(name, expected, message)

Asserts that the global (window) property has the expected value.

assert.hasClass(selection, className, message)

Asserts that selected element(s) have the expected class name. Elements may have other class names (unlike assert.className).

Fails if no element found.

browser.assert.hasClass('form input[name=email]', 'has-error');

assert.hasFocus(selection, message)

Asserts that selected element has the focus.

If the first argument is null, asserts that no element has the focus.

Otherwise, fails if element not found, or if more than one element found.

browser.assert.hasFocus('form input:nth-child(1)');

assert.hasNoClass(selection, className, message)

Asserts that selected element(s) does not have the expected class name. Elements may have other class names (unlike assert.className).

Fails if no element found.

browser.assert.hasNoClass('form input', 'has-error');

assert.input(selection, expected, message)

Asserts that selected input field(s) (input, textarea, select etc) have the expected value.

Fails if no element found.

browser.assert.input('form input[name=text]', 'Head Eater');

assert.link(selection, text, url, message)

Asserts that at least one link exists with the given selector, text and URL. The selector can be a, but a more specific selector is recommended.

URL can be relative to the current document, or a regular expression.

Fails if no element is selected that also has the specified text content and URL.

browser.assert.link('footer a', 'Privacy Policy', '/privacy');

assert.redirected(message)

Asserts the browser was redirected when retrieving the current page.

assert.success(message)

Asserts the current page loaded successfully (status code 2xx or 3xx).

assert.status(code, message)

Asserts the current page loaded with the expected status code.

browser.assert.status(404);

assert.style(selection, style, expected, message)

Asserts that selected element(s) have the expected value for the named style property. For example:

Fails if no element found, or element style does not match expected value.

browser.assert.style('#show-hide.hidden', 'display', 'none');
browser.assert.style('#show-hide:not(.hidden)', 'display', '');

assert.text(selection, expected, message)

Asserts that selected element(s) have the expected text content. For example:

Fails if no element found that has that text content.

browser.assert.text('title', 'My Awesome Page');

assert.url(url, message)

Asserts the current page has the expected URL.

The expected URL can be one of:

  • The full URL as a string
  • A regular expression
  • A function, called with the URL and returns true if the assertion is true
  • An object, in which case individual properties are matched against the URL

For example:

browser.assert.url('http://localhost/foo/bar');
browser.assert.url(new RegExp('^http://localhost/foo/\\w+$'));
browser.assert.url({ pathname: '/foo/bar' });
browser.assert.url({ query: { name: 'joedoe' } });

Roll Your Own Assertions

Not seeing an assertion you want? You can add your own assertions to the prototype of Browser.Assert.

For example:

// Asserts the browser has the expected number of open tabs.
Browser.Assert.prototype.openTabs = function(expected, message) {
  assert.equal(this.browser.tabs.length, expected, message);
};

Or application specific:

// Asserts which links is highlighted in the navigation bar
Browser.Assert.navigationOn = function(linkText) {
  this.assert.element('.navigation-bar');
  this.assert.text('.navigation-bar a.highlighted', linkText);
};

Cookies

Are delicious. Also, somewhat tricky to work with. A browser will only send a cookie to the server if it matches the request domain and path.

Most modern Web applications don't care so much about the path and set all cookies to the root path of the application (/), but do pay attention to the domain.

Consider this code:

browser.setCookie({ name: 'session', domain: 'example.com', value: 'delicious' });
browser.visit('http://example.com', function() {
  const value = browser.getCookie('session');
  console.log('Cookie', value);
});

In order for the cookie to be set in this example, we need to specify the cookie name, domain and path. In this example we omit the path and choose the default /.

To get the cookie in this example, we only need the cookie name, because at that point the browser has an open document, and it can use the domain of that document to find the right cookie. We do need to specify a domain if we're interested in other cookies, e.g for a 3rd party widget.

There may be multiple cookies that match the same host, for example, cookies set for .example.com and www.example.com will both match www.example.com, but only the former will match example.com. Likewise, cookies set for / and /foo will both match a request for /foo/bar.

getCookie, setCookie and deleteCookie always operate on a single cookie, and they match the most specific one, starting with the cookies that have the longest matching domain, followed by the cookie that has the longest matching path.

If the first argument is a string, they look for a cookie with that name using the hostname of the currently open page as the domain and / as the path. To be more specific, the first argument can be an object with the properties name, domain and path.

The following are equivalent:

browser.getCookie('session');
browser.getCookie({ name: 'session',
                    domain: browser.location.hostname,
                    path: browser.location.pathname });

getCookie take a second argument. If false (or missing), it returns the value of the cookie. If true, it returns an object with all the cookie properties: name, value, domain, path, expires, httpOnly and secure.

browser.cookies

Returns an object holding all cookies used by this browser.

browser.cookies.dump(output?)

Dumps all cookies to standard output, or the output stream.

browser.deleteCookie(identifier)

Deletes a cookie matching the identifier.

The identifier is either the name of a cookie, or an object with the property name and the optional properties domain and path.

browser.deleteCookies()

Deletes all cookies.

browser.getCookie(identifier, allProperties?)

Returns a cookie matching the identifier.

The identifier is either the name of a cookie, or an object with the property name and the optional properties domain and path.

If allProperties is true, returns an object with all the cookie properties, otherwise returns the cookie value.

browser.setCookie(name, value)

Sets the value of a cookie based on its name.

browser.setCookie(cookie)

Sets the value of a cookie based on the following properties:

  • domain - Domain of the cookie (requires, defaults to hostname of currently open page)
  • expires - When cookie it set to expire (Date, optional, defaults to session)
  • maxAge - How long before cookie expires (in seconds, defaults to session)
  • name - Cookie name (required)
  • path - Path for the cookie (defaults to /)
  • httpOnly - True if HTTP-only (not accessible from client-side JavaScript, defaults to false)
  • secure - True if secure (requires HTTPS, defaults to false)
  • value - Cookie value (required)

Tabs

Just like your favorite Web browser, Zombie manages multiple open windows as tabs. New browsers start without any open tabs. As you visit the first page, Zombie will open a tab for it.

All operations against the browser object operate on the currently active tab (window) and most of the time you only need to interact with that one tab. You can access it directly via browser.window.

Web pages can open additional tabs using the window.open method, or whenever a link or form specifies a target (e.g. target=_blank or target=window-name). You can also open additional tabs by calling browser.open. To close the currently active tab, close the window itself.

You can access all open tabs from browser.tabs. This property is an associative array, you can access each tab by its index number, and iterate over all open tabs using functions like forEach and map.

If a window was opened with a name, you can also access it by its name. Since names may conflict with reserved properties/methods, you may need to use browser.tabs.find.

The value of a tab is the currently active window. That window changes when you navigate forwards and backwards in history. For example, if you visited the URL '/foo' and then the URL '/bar', the first tab (browser.tabs[0]) would be a window with the document from '/bar'. If you then navigate back in history, the first tab would be the window with the document '/foo'.

The following operations are used for managing tabs:

browser.tabs

Returns an array of all open tabs.

browser.tabs[number]

Returns the tab with that index number.

browser.tabs[string]

browser.tabs.find(string)

Returns the tab with that name.

browser.tabs.closeAll()

Closes all tabs.

browser.tabs.current

This is a read/write property. It returns the currently active tab.

Can also be used to change the currently active tab. You can set it to a window (e.g. as currently returned from browser.current), a window name or the tab index number.

browser.tabs.dump(output?)

Dump a list of all open tabs to standard output, or the output stream.

browser.tabs.index

Returns the index of the currently active tab.

browser.tabs.length

Returns the number of currently opened tabs.

browser.open (url)

Opens and returns a new tab. Supported options are:

  • name - Window name.
  • url - Load document from this URL.

browser.window

Returns the currently active window, same as browser.tabs.current.

Debugging

To see what your code is doing, you can use console.log and friends from both client-side scripts and your test code.

To see everything Zombie does (opening windows, loading URLs, firing events, etc), set the environment variable DEBUG=zombie. Zombie uses the debug module. For example:

$ DEBUG=zombie mocha

You can also turn debugging on from your code (e.g. a specific test you're trying to troubleshoot) by calling browser.debug().

Some objects, like the browser, history, resources, tabs and windows also include dump method that will dump the current state to the console, or an output stream of your choice. For example:

browser.dump();
browser.dump(process.stderr);

If you want to disable console output from scripts, set browser.silent = true or once for all browser instances with Browser.silent = true.

Events

Each browser instance is an EventEmitter, and will emit a variety of events you can listen to.

Some things you can do with events:

  • Trace what the browser is doing, e.g. log every page loaded, every DOM event emitted, every timeout fired
  • Wait for something to happen, e.g. form submitted, link clicked, input element getting the focus
  • Strip out code from HTML pages, e.g remove analytics code when running tests
  • Add event listeners to the page before any JavaScript executes
  • Mess with the browser, e.g. modify loaded resources, capture and change DOM events

console (level, message)

Emitted whenever a message is printed to the console (console.log, console.error, console.trace, etc).

The first argument is the logging level, and the second argument is the message.

The logging levels are: debug, error, info, log, trace and warn.

active (window)

Emitted when this window becomes the active window.

closed (window)

Emitted when this window is closed.

done ()

Emitted when the event loop goes empty.

error (error)

Error when loading a resource, or evaluating JavaScript.

evaluated (code, result, filename)

Emitted after JavaScript code is evaluated.

The first argument is the JavaScript function or code (string). The second argument is the result. The third argument is the filename.

event (event, target)

Emitted whenever a DOM event is fired on the target element, document or window.

focus (element)

Emitted whenever an element receives the focus.

idle ()

Event loop is idle.

inactive (window)

Emitted when this window is no longer the active window.

interval (function, interval)

Emitted whenever an interval (setInterval) is fired.

The first argument is the function or code to evaluate, the second argument is the interval in milliseconds.

link (url, target)

Emitted when a link is clicked.

The first argument is the URL of the new location, the second argument identifies the target window (_self, _blank, window name, etc).

loaded (document)

Emitted when a document has been loaded into a window or frame.

This event is emitted after the HTML is parsed, and some scripts executed.

loading (document)

Emitted when a document is about to be loaded into a window or frame.

This event is emitted when the document is still empty, before parsing any HTML.

opened (window)

Emitted when a new window is opened.

redirect (request, response)

Emitted when following a redirect.

request (request)

Emitted before making a request to retrieve a resource.

The first argument is the request object. See Resources for more details.

response (request, response)

Emitted after receiving the response (excluding redirects).

The first argument is the request object, the second argument is the response object. See Resources for more details.

serverEvent ()

Browser received server initiated event (e.g. EventSource message).

setInterval (function, interval)

Event loop fired a setInterval event.

setTimeout (function, delay)

Event loop fired a setTimeout event.

submit (url, target)

Emitted whenever a form is submitted.

The first argument is the URL of the new location, the second argument identifies the target window (_self, _blank, window name, etc).

timeout (function, delay)

Emitted whenever a timeout (setTimeout) is fired.

The first argument is the function or code to evaluate, the second argument is the delay in milliseconds.

xhr (event, url)

Called for each XHR event (progress, abort, readystatechange, loadend, etc).

Authentication

Zombie supports HTTP basic access authentication. To provide the login credentials:

browser.on('authenticate', function(authentication) {
  authentication.username = 'myusername';
  authentication.password = 'mypassword';
});

browser.visit('/mypage');

Resources

Zombie can retrieve with resources - HTML pages, scripts, XHR requests - over HTTP, HTTPS and from the file system.

Most work involving resources is done behind the scenes, but there are few notable features that you'll want to know about. Specifically, if you need to do any of the following:

  • Inspect the history of retrieved resources, useful for troubleshooting issues related to resource loading
  • Request resources directly, but have Zombie handle cookies, authentication, etc
  • Implement new mechanism for retrieving resources, for example, add new protocols or support new headers (see Pipeline)

The Resources List

Each browser provides access to the list of resources loaded by the currently open window via browser.resources. You can iterate over this list just like any JavaScript array.

Each resource provides three properties:

  • request - The request object
  • response - The resource object (if received)
  • error - The error generated (no response)

The request object is based on the Fetch API Request object.

The response object is based on the Fetch API Response object. Note that the fetch API has the property status, whereas Node HTTP module uses statusCode.

browser.fetch(input, init)

You can use the browser directly to make requests against external resources. These requests will share the same cookies, authentication and other browser settings (also pipeline).

The fetch method is based on the Fetch API.

For example:

browser.fetch(url)
  .then(function(response) {
    console.log('Status code:', response.status);
    if (response.status === 200)
      return response.text();
  })
  .then(function(text) {
    console.log('Document:', text);
  })
  .catch(function(error) {
    console.log('Network error');
  });

To access the response document body as a Node buffer, use the following:

response.arrayBuffer()
  .then(Buffer) // arrayBuffer -> Buffer
  .then(function(buffer) {
    assert( Buffer.isBuffer(buffer) );
  });

resources.dump(output?)

Dumps the resources list to the output stream (defaults to standard output stream).

Pipeline

Zombie uses a pipeline to operate on resources. You can extend that pipeline with your own set of handlers, for example, to support additional protocols, content types, special handlers, etc.

The pipeline consists of a set of handlers. There are two types of handlers:

Functions with two arguments deal with requests. They are called with the browser and request object. They may modify the request object, and they may either return null (pass control to the next handler) or return the Response object, or return a promise that resolves to either outcome.

Functions with three arguments deal with responses. They are called with the browser, request and response objects. They may modify the response object, and must return a Response object, either the same as the argument or a new Response object, either directly or through a promise.

To add a new handle to the end of the pipeline:

browser.pipeline.addHandler(function(browser, request) {
  // Let's delay this request by 1/10th second
  return new Promise(function(resolve) {
    setTimeout(resolve, 100);
  });
});

You can add handlers to all browsers via Pipeline.addHandler. These handlers are automatically added to every new browser.pipeline instance.

Pipeline.addHandler(function(browser, request, response) {
  // Log the response body
  console.log('Response body: ' + response.body);
});

zombie's People

Contributors

aslakhellesoy avatar assaf avatar audreyt avatar boblail avatar dcolens avatar ddopson avatar djanowski avatar dstendardi avatar eungjun-yi avatar forbeslindesay avatar frozencow avatar iangreenleaf avatar iloire avatar jadell avatar jagoda avatar jamlen avatar josevalim avatar mdlavin avatar nlacasse avatar pscheit avatar rafis avatar sintendo avatar stefanodevuono avatar stof avatar svbatalov avatar timbeyer avatar tricknotes avatar vinibaggio avatar vladgurgov avatar wernight avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zombie's Issues

location.search should be empty string

Context: Running QUnit testsuite via Zombie.js.

Problem: QUnit checks for window.location, and if defined, assumes that window.location.search is defined and at least an empty string. When running with Zombie.js, window.location is present, but location.search is undefined.

Proposed solution: Initialize the search property with an empty string, if no actual search value is present.

PS: Could be a jsdon issue. Didn't yet get a chance to dig into the source.

Timeouts with strings instead of function references fail

Here's an example



<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>


<script>
function test(){
console.log('Hey!');
}
$(document).ready(function(){
setTimeout("test()",100);
})
</script>

Will fail.
node.js:66
throw e; // process.nextTick error, or 'error' event on first tick
^
test is not defined

Yet, evaling the same thing works?
$(document).ready(function(){
setTimeout(function(){
eval("test()");
},100);
})

Zombie: Running script from http://bjn-chase:1337/zombietest:undefined:undefined<script>
Hey!
chase@bjn-chase:~/zombie$

Strange. I haven't gone through how you're sandboxing the javascript context yet, but since eval doesn't seem to throw the error, I would suspect it's an easy fix.

I don't like putting code in quotes like that for timeouts, but it exists in the wild :\

Inline scripts get executed after external scripts

When doing zombie tests with our app, I've noticed, that the inline scripts in the head get executed after the loaded scripts. For example in the following test, it'll run external jquery first and the script in the head after that. In the browser the inline scripts get executed immediately.

I'm running v0.8.6.
var zombie = require('zombie');
var assert = require("assert");

// Load the page
zombie.visit("http://jsbin.com/onomi4", {debug:true}, function (err, browser) {
  if(err){
    console.error('!!', err.message);
  }else{
  }
});

Here's the log output:

Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: GET http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js
Zombie: GET /js/render/edit.js
Zombie: GET http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js => 200
Zombie: Running script from /ajax/libs/jquery/1.4.4/jquery.min.js
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
Zombie: GET /js/render/edit.js => 200
Zombie: Running script from /js/render/edit.js
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
this is called in the HEAD tag
Zombie: Running script from http://jsbin.com/onomi4:undefined:undefined<script>
this is called in the BODY tag

as you can see it executes jquery before the inline script in the head :(

Form submit

Hello,

It seems I can't get a form to submit. I used the browser.pressButton and browser.fire("click"...) with no success. The callback is triggered without errors but the browser location is not changed and no request are made.

I am using the latest node.js v0.3.7-pre and zombie 8.10 thru capybara-zombie (with some added code to do logging and check pressButton)

Am I missing something? Should I do anything special?

Thanks,
Julien.

Parse error

When trying the following I get a parse error:
var zombie = require("zombie");

b = new zombie.Browser();
b.debug(true);
b.visit("http://www.esn.me", function (err, browser) {
  if (err) {
    console.log("Error");
    return;
  }
});

Not sure how I'm supposed to debug this efficiently. It's a public website, try running it yourself.
I'm running zombie 0.7.7 off npm.

Typo in README.md

"Zombie.js is a lightweight framefork" => "... framework"

By the way, I love the documentation. "The Guts", "The Brains" really had me chuckling!

Broken tests on master

After following the setup instructions, with from-scratch Node, NPM and Coffee-Script installs, I get 8 failures on master:

  adding script using document.write
    ✗ should run script
      » expected 'Script document.write',
    got  '' (==) // vows.js:95

  adding script using appendChild
    ✗ should run script
      » expected 'Script appendChild',
    got  '' (==) // vows.js:95

  open page
    ✗ should run jQuery.onready
      » expected 'Awesome',
    got  'Whatever' (==) // vows.js:95

  click link
    ✗ should run all events
      » expected 'The Dead',
    got  '' (==) // vows.js:95

  receive cookies
    ✗ should process cookies in XHR response
      » expected 'lol',
    got  undefined (==) // vows.js:95

  redirect
    ✗ should send cookies in XHR response
      » expected 'redirected: yes',
    got  undefined (==) // vows.js:95

  load asynchronously
    ✗ should load resource
      » expected expression to evaluate to true, but was undefined // vows.js:95

  send cookies
    ✗ should send cookies in XHR response
      » expected 'yes',
    got  undefined (==) // vows.js:95

✗ Broken » 181 honored ∙ 8 broken (2.725s)

Zombie.js install fails on base64

npm info using [email protected]
npm info using [email protected]

...

npm ERR! Some dependencies failed to bundle
npm ERR! Bundle them separately with
npm ERR! npm bundle install
npm ERR! Error: [email protected] install: node-waf configure build
npm ERR! sh failed with 1
npm ERR! at ChildProcess. (/usr/local/lib/node/.npm/npm/0.2.14-3/package/lib/utils/exec.js:25:18)
npm ERR! at ChildProcess.emit (events.js:34:17)
npm ERR! at ChildProcess.onexit (child_process.js:164:12)
npm ERR!
npm ERR! Failed at the [email protected] install script.
npm ERR! This is most likely a problem with the base64 package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node-waf configure build
npm ERR! You can get their info via:
npm ERR! npm owner ls base64
npm ERR! There may be additional logging output above.
npm not ok

On OS X.

Missing dependency: htmlparser

After a clean install with npm, I get the following error multiple times when calling zombie.visit(url):

###########################################################
#  WARNING: No HTML parser could be found.
#  Element.innerHTML setter support has been disabled
#  Element.innerHTML getter support will still function
#  Download: http://github.com/tautologistics/node-htmlparser
###########################################################

After manually installing htmlparser everything works fine. As I haven't found any occurrence of "htmlparser" in zombie.js, I guess it's a (indirect) bug in one of its dependencies.

Doesn't work on CentOS 5.5

My hands are tied and I'm forced to use CentOS 5.5. npm installed zombie and the various dependencies.

However, this:

var zombie = require("zombie");

console.log("Starting Zombie tests...");

b = new zombie.Browser();
b.visit("http://hudson2.company.com/login", function(err, browser) {
    console.log("wtf");
});


zombie.visit("http://hudson2.company.com/login", function(err, browser) {
    console.log("Logged in?");
    console.log(err);
    browser.
    fill("__ac_name", "username").
    fill("__ac_password", "password").
    pressButton("Login", function(err, browser) {
        console.log("what");
        console.log(err);
        console.log(browser.querySelector("li.username.first"));
    });
});

...doesn't work. Nothing in the functions passed to visit() seems to execute.

Have callbacks on fil, check, choose, etc

Hello,

When your app is responding to user interactions like field changes, it would be good to have a callback to know when the action is completely completed (any XHR for exemple).

I modified my copy to do so, you can have a look here.
juggy@aac88ea

I think it should be in the main repo.

Thanks,
Julien.

top/window.top does not work properly

This is my first time playing with Zombie (or Node.js for that matter) so forgive me if I'm missing something obvious!

Basic framebuster code in javascript raises errors in Zombie that no browsers seem to exhibit - see a simple example of the JS here: http://en.wikipedia.org/wiki/Framekiller

My test script is:

var zombie = require("zombie");

zombie.visit("http://vm?version=v2", { debug: false }, function(err, browser) {
    if (err) {
        console.log(err.message);
    }
    else {
        console.log(browser.html());
        browser.clickLink("#thelogin a", function(err, browser) {
            if (err) {
                console.log(err.message);
            }
            else {
                console.log(browser.lastResponse);
                browser.dump();
            }
        });
    }

});

Produces the following output:

[successful requests]
Zombie: GET http://vm/js/default.js => 200
Zombie: Running script from /js/default.js
Zombie: ReferenceError: top is not defined, at Object. (/js/default.js:100:2)
top is not defined
[requests on rest of page]

You can check the actual JS file here: https://www.wepay.com/js/default.js. The relevant piece is here:

if (top != self) {
    // tries three times to bust frame, if successful will redirect away
    for (i = 0; top != self && i < 3; i++) {
        window.setTimeout(top.location.replace(self.location.href), 200);
    }
    // if redirect fails, throw up a big warning
}

Commenting out the framebuster entirely fixes the error. Adding if (typeof top == 'undefined') var top = window.top; causes window.setTimeout(top.location.replace(self.location.href), 200); to fail with the following error:
Zombie: TypeError: Cannot read property 'location' of undefined, at Object. (/js/default.js:110:25)
Cannot read property 'location' of undefined

Should cookies be set for the hostname and path they specify?

In history.coffee you set a cookie for a hostname and path like this:

browser.cookies(url.hostname, url.pathname).update response.headers["set-cookie"]

Should cookies instead be stored for the domain and path they they specify (if they do)? or not? Is that a security issue?

boblail/zombie@c483e8f is a commit that uses the cookie's path instead of the current request's path.

This commit causes zombie.js to behave as Capybara expects, but a few of Zombie's vows are broken. Before I changed those, I wanted to see if this was the way you would like Zombie to function.

Apache error: request without hostname

When I try it on my apache which has passenger on it, I get this error from apache log:

client sent HTTP/1.1 request without hostname (see RFC2616 section 14.23): GET

HTML parse error?

Tried scripting some more zombies and hit this error while trying out http://beaconpush.com

This is the script:
var zombie = require("zombie");
b = new zombie.Browser();
b.visit("http://beaconpush.com/", function (err, browser) {
if (err) {
console.log("Error");
return;
}
});

and running it returns this output:

$ node test.js 

/usr/local/lib/node/.npm/html5/0.2.5/package/lib/html5/tokenizer.js:62
                throw(e);
    ^
Error
    at Object.appendChild (/usr/local/lib/node/.npm/jsdom/0.1.22/package/lib/jsdom/level1/core.js:1312:13)
    at Object.insert_html_element (/usr/local/lib/node/.npm/html5/0.2.5/package/lib/html5/parser/before_html_phase.js:41:21)
    at Object.processStartTag (/usr/local/lib/node/.npm/html5/0.2.5/package/lib/html5/parser/before_html_phase.js:29:7)
    at EventEmitter.do_token (/usr/local/lib/node/.npm/html5/0.2.5/package/lib/html5/parser.js:94:20)
    at EventEmitter.<anonymous> (/usr/local/lib/node/.npm/html5/0.2.5/package/lib/html5/parser.js:112:30)
    at EventEmitter.emit (events:27:15)
    at EventEmitter.emitToken (/usr/local/lib/node/.npm/html5/0.2.5/package/lib/html5/tokenizer.js:84:7)
    at EventEmitter.emit_current_token (/usr/local/lib/node/.npm/html5/0.2.5/package/lib/html5/tokenizer.js:813:7)
    at EventEmitter.tag_name_state (/usr/local/lib/node/.npm/html5/0.2.5/package/lib/html5/tokenizer.js:358:8)
    at EventEmitter.<anonymous> (/usr/local/lib/node/.npm/html5/0.2.5/package

Feels like there's something up with the HTML parser. Possibly due to bad HTML?

Scripts are loaded in distinct environments

In a browser, the environment in which each script is loaded is shared among all scripts. So when you declare a top-level var in script A, script B will see it. That doesn't seem to work with Zombie, which I'm guessing is either directly due to jsdom or to how it's used. This breaks require.js but it's likely to break other scripts.

To reproduce, here are a few dummy files:

index.html

<html>
  <head>
    <script src="first.js"></script>
    <script src="second.js"></script>
  </head>
  <body><p>Rock on.</p></body>
</html>

first.js

var poof = 2;

second.js

console.log(poof);

The output will be:

Zombie: GET http://hommie/zombie-test/index.html => 200
Zombie: GET first.js
Zombie: GET second.js
Zombie: GET first.js => 200
Zombie: Running script from /zombie-test/first.js
Zombie: GET second.js => 200
Zombie: Running script from /zombie-test/second.js
poof is not defined

I'll see if I can dig out a fix, just need to read some code ;-) Thanks for zombie.js by the way.

Jammit issues

I'm not sure if this is Jammit or not, but basically it seems like I'm having load order problems with my javascript dependencies.

I created a sample application using Rails 3/backbone.js/jammit

The example is here:

http://backbone-rails3.heroku.com/#addresses

This works fine in firefox/chrome -- no warnings or errors.

When I try to run this through zombie.js (node v0.2.6) like:

var zombie = require("zombie");

zombie.visit("http://backbone-rails3.heroku.com/#addresses", { debug: true },
  function(err, browser) {
    if (err)
      console.log(err.stack);
    else
      console.log("The page:", browser.html());
  }
);

I get:

[~] node zombie-test.js
Zombie: GET http://backbone-rails3.heroku.com/#addresses
Zombie: GET http://backbone-rails3.heroku.com/#addresses => 200
Zombie: GET /assets/application.js?1294036300
Zombie: GET /assets/application.js?1294036300 => 200
Zombie: Running script from /assets/application.js
$ is not defined
Zombie: Running script from http://backbone-rails3.heroku.com/#addresses:undefined:undefined<script>
Zombie: Running script from http://backbone-rails3.heroku.com/#addresses:undefined:undefined<script>
The page: <!DOCTYPE html>
<--SNIP-->

Al the JS is 'jammed' up, so it's hard to read. I get errors locally too (when it's not compressed), about other symbols not being defined. Let me know if I can do anything to provide better information.

Scripts with window.eval do not work.

If a script contains a window.eval() zombie fails.

var zombie = require("zombie");
b = new zombie.Browser();
b.visit("http://cowholio4.com/pages/window", function (err, browser) {
  if (err) {
    console.log(err.message + "\n" + err.stack);
    return;
  }
});

I tried to create document.eval but can't seem to get it to work correctly. For some reason doing this in the sandbox is harder than it should be. Any help would be appreciated.

Capybara's drag & drop examples fail (because elements don't have position or dimension)

The mousedown, mousemove, and mouseup events are being fired as expected, but jQuery checks that the dragging element intersects with the drop target. I'm pretty sure drag & drop is failing at that juncture.

What we need is a way of calculating elements' position, height, and width; but these values should be calculated on-demand so that zombie is generally, well, insanely fast.

Multiple Cookies not handled correctly?

I'm using v0.8.3 with a very simple script:

var zombie = require("zombie");

var browser = new zombie.Browser({ debug: true });

browser.visit("http://bing.com", function (err, browser) {

  if(err) {
    throw(err.message);
  }

  browser.
    fill("q", "kittens").
    pressButton("#sb_form_go", function(err, browser) {
      assert.ok(browser.querySelector("#results"));
    })

});

Which produces:

Zombie: GET http://bing.com
Zombie: GET http://bing.com => 301
Zombie: GET http://www.bing.com/
Zombie: GET http://www.bing.com/ => 200
(DEBUG:) typeof serialized:  object ownProperties:  [ '0', '1', '2', '3', '4', '5', '6', 'length' ]
(DEBUG:) [ '_SS=SID=653506CC3A554354AA0CA7F48FA28C5F; domain=.bing.com; path=/',
  'MUID=698EF70D22284689B8B959E8D806AEEC; expires=Sun, 30-Dec-2012 04:23:48 GMT; domain=.bing.com; path=/',
  'OrigMUID=698EF70D22284689B8B959E8D806AEEC%2caf2347e4c093413f8f4c21ff585f2795; expires=Sun, 30-Dec-2012 04:23:48 GMT; domain=.bing.com; path=/',
  'OVR=flt=0&flt2=0&flt3=0&flt4=0&flt5=0&ramp1=0&release=or3&preallocation=0&R=1; domain=.bing.com; path=/',
  'SRCHD=D=1577063&MS=1577063&AF=NOFORM; expires=Sun, 30-Dec-2012 04:23:48 GMT; domain=.bing.com; path=/',
  'SRCHUID=V=2&GUID=5E16F3E5B50F445DB1DE8E6C9E5064FD; expires=Sun, 30-Dec-2012 04:23:48 GMT; path=/',
  'SRCHUSR=AUTOREDIR=0&GEOVAR=&DOB=20101231; expires=Sun, 30-Dec-2012 04:23:48 GMT; domain=.bing.com; path=/' ]

/Users/barnaby/local/lib/node/.npm/zombie/0.8.3/package/lib/zombie/cookies.js:117
      _ref = serialized.split(/,(?=[^;,]*=)|,$/);
                        ^
TypeError: Object _SS=SID=653506CC3A554354AA0CA7F48FA28C5F; domain=.bing.com; path=/,MUID=698EF70D22284689B8B959E8D806AEEC; expires=Sun, 30-Dec-2012 04:23:48 GMT; domain=.bing.com; path=/,OrigMUID=698EF70D22284689B8B959E8D806AEEC%2caf2347e4c093413f8f4c21ff585f2795; expires=Sun, 30-Dec-2012 04:23:48 GMT; domain=.bing.com; path=/,OVR=flt=0&flt2=0&flt3=0&flt4=0&flt5=0&ramp1=0&release=or3&preallocation=0&R=1; domain=.bing.com; path=/,SRCHD=D=1577063&MS=1577063&AF=NOFORM; expires=Sun, 30-Dec-2012 04:23:48 GMT; domain=.bing.com; path=/,SRCHUID=V=2&GUID=5E16F3E5B50F445DB1DE8E6C9E5064FD; expires=Sun, 30-Dec-2012 04:23:48 GMT; path=/,SRCHUSR=AUTOREDIR=0&GEOVAR=&DOB=20101231; expires=Sun, 30-Dec-2012 04:23:48 GMT; domain=.bing.com; path=/ has no method 'split'
    at Cookies.update (/Users/barnaby/local/lib/node/.npm/zombie/0.8.3/package/lib/zombie/cookies.js:117:25)
    at History.<anonymous> (/Users/barnaby/local/lib/node/.npm/zombie/0.8.3/package/lib/zombie/history.js:113:63)
    at IncomingMessage.<anonymous> (

I added the "(DEBUG:)" output to Cookies.update which I believe is actually expecting a string . Simple fix? :)

Thanks!

jQuery & append

Hello,

I do the following:
var t = $("#my-template").clone();
$("body").append(t);
$(".line", t).append("<a href=...");

The first append is working, the second is not. After investigating, $(".line", t) does not return any nodes. Works in a browser.

Any idea or pointers for me to look at?
Thanks
Julien.

Js variable with minus sign.

Hi. I have problem with Js variables containing minus "-" or "&" sign.
Problem occurs only when when variables are defined in html in script tags.

e.g.

...
<body>

<script>

var error1 = "-aaaa";

var error2 = "&aaaa";

</script>

</body>
...

generates error:
Error: SyntaxError: Unexpected token ILLEGAL
26 Jan 09:43:26 - The onerror handler

on target

{ frames: [ [Circular] ]
, contentWindow: [Circular]
, window: [Circular]
, self: [Circular]
, location: [Getter/Setter]
, addEventListener: [Function]
, dispatchEvent: [Function]
, browser: [Getter]
, title: [Getter/Setter]
, cookies: [Getter]
, sessionStorage: [Getter]
, localStorage: [Getter]
, setTimeout: [Function]
, setInterval: [Function]
, clearTimeout: [Function]
, clearInterval: [Function]
, perform: [Function]
, wait: [Function]
, request: [Function]
, history: [Getter]
, alert: [Function]
, confirm: [Function]
, prompt: [Function]
, XMLHttpRequest: [Function]
, JSON: {}
, onerror: [Function]
, Image: [Function]
, _document:
{ _childNodes: [ [Object], [Object] ]
, _version: 29
, _children:
{ '0': [Circular]
, '1': [Circular]
, _element: [Circular]
, _query: [Function]
, _version: 28
, _snapshot: [Circular]
, _length: 2
}
, _nodeValue: null
, _parentNode: null
, _ownerDocument: [Circular]
, _attributes:
{ _nodes: {}
, _length: 0
, _ownerDocument: null
, _readonly: false
, _parentNode: [Circular]
}
, _nodeName: '#document'
, _readonly: false
, style: { position: 'static' }
, _tagName: '#document'
, _contentType: 'text/html'
, _doctype:
{ _childNodes: []
, _version: 0
, _children: [Object]
, _nodeValue: null
, _parentNode: null
, _ownerDocument: [Circular]
, _attributes: [Object]
, _nodeName: 'HTML'
, _readonly: false
, style: [Object]
, _name: 'HTML'
, _tagName: 'HTML'
, _entities: [Object]
, _notations: [Object]
, _publicId: ''
, _systemId: ''
, _fullDT: ''
, toString: [Function]
}
, _implementation: { _ownerDocument: undefined, _features: [Object] }
, _documentElement: [Circular]
, _ids: {}
, _URL: 'http://localhost/start.html'
, _documentRoot: 'http://localhost'
, _queue: { paused: false, tail: [Object], push: [Function] }
, readyState: 'complete'
, createWindow: [Function]
, _parentWindow: [Circular]
, _sessionStorage:
{ length: [Getter]
, key: [Function]
, getItem: [Function]
, setItem: [Function]
, removeItem: [Function]
, clear: [Function]
, dump: [Function]
}
, _localStorage:
{ length: [Getter]
, key: [Function]
, getItem: [Function]
, setItem: [Function]
, removeItem: [Function]
, clear: [Function]
, dump: [Function]
}
}
, _vars:
[ [ 'pageXOffset', 0 ]
, [ 'pageYOffset', 0 ]
, [ 'screenX', 0 ]
, [ 'screenY', 0 ]
, [ 'screenLeft', 0 ]
, [ 'screenTop', 0 ]
, [ 'scrollX', 0 ]
, [ 'scrollY', 0 ]
, [ 'scrollTop', 0 ]
, [ 'scrollLeft', 0 ]
]
}

threw error

'Unexpected token ILLEGAL'

handling event

{ _eventType: 'HTMLEvents'
, _type: 'error'
, _bubbles: true
, _cancelable: false
, _target:
{ frames: [ [Circular] ]
, contentWindow: [Circular]
, window: [Circular]
, self: [Circular]
, location: [Getter/Setter]
, addEventListener: [Function]
, dispatchEvent: [Function]
, browser: [Getter]
, title: [Getter/Setter]
, cookies: [Getter]
, sessionStorage: [Getter]
, localStorage: [Getter]
, setTimeout: [Function]
, setInterval: [Function]
, clearTimeout: [Function]
, clearInterval: [Function]
, perform: [Function]
, wait: [Function]
, request: [Function]
, history: [Getter]
, alert: [Function]
, confirm: [Function]
, prompt: [Function]
, XMLHttpRequest: [Function]
, JSON: {}
, onerror: [Function]
, Image: [Function]
, _document:
{ _childNodes: [Object]
, _version: 29
, _children: [Object]
, _nodeValue: null
, _parentNode: null
, _ownerDocument: [Circular]
, _attributes: [Object]
, _nodeName: '#document'
, _readonly: false
, style: [Object]
, _tagName: '#document'
, _contentType: 'text/html'
, _doctype: [Object]
, _implementation: [Object]
, _documentElement: [Object]
, _ids: {}
, _URL: 'http://localhost/start.html'
, _documentRoot: 'http://localhost'
, _queue: [Object]
, readyState: 'complete'
, createWindow: [Function]
, _parentWindow: [Circular]
, _sessionStorage: [Object]
, _localStorage: [Object]
}
, _vars:
[ [Object]
, [Object]
, [Object]
, [Object]
, [Object]
, [Object]
, [Object]
, [Object]
, [Object]
, [Object]
]
}
, _currentTarget: null
, _eventPhase: 2
, _timeStamp: null
, _preventDefault: false
, _stopPropagation: false
, error:
{ message: [Getter/Setter]
, stack: [Getter/Setter]
, type: 'unexpected_token'
, arguments: [ 'ILLEGAL' ]
}
}

Resources.stringify breaks on string data

I'm doing a simple XHR POST using JQuery like this:

$.post("/login", function() { ... });

The entity body is empty, in which case JQuery seem to pass down the string "" as XHR data. This kills the whole XHR request and Zombie ends up in a not really dead, not really alive state (eh!). I tracked it down to Resources.makeRequest which does a stringify in the "application/x-www-form-urlencoded". Resources.stringify expects something that accepts map and a string doesn't.

I've fixed it by adding the following as first statement in stringify:

return object.toString() unless object.map

Also makeRequest should probably be surrounded by a try/catch to invoke the callback in case of error, otherwise errors go totally unnoticed.

CSSOM

I've noticed your repo includes CSSOM.js in dep/cssom.js and as a submodule. May I ask you whats's for?

One-file version of CSSOM.js (e.g. dep/cssom.js) primarily suited for browsers. It doesn't have CommonJS stuff like exports and require.

Today I've published cssom on npm. You can use it if you like.

window.load event not triggered when bound via addEventListener

When trying to execute a QUnit testpage, the window.load event is never triggered. Here's my test script:

require("zombie").visit("http://jquery.bassistance.de/qunit/test/", { debug: true }, function(err, browser) {
    if (err) {
        throw err;
    }
    console.log(browser.text("#qunit-testresult"))
});

I also tried to build a simpler testpage using jsbin, but that fails due to some Google Analytics code: http://jsbin.com/ocino5

Here's a seperate file with just an inline script and a window-load event listener, no overhead and fails to output anything: http://jquery.bassistance.de/qunit/load-test.html

Serializing multiple complex values (e.g. addresses[][city]) does not work as expected

A Capybara test fails for the capybara-zombie driver because zombie.js doesn't serialize this:

<input name="addresses[][street]" value="Pennsylvania Ave" id="address1_street" />
<input name="addresses[][city]" value="Washington, DC" id="address1_city" />
<input name="addresses[][street]" value="Michigan Ave" id="address2_street" />
<input name="addresses[][city]" value="Chicago" id="address2_city" />

as an array, addresses, of two objects that each have street and city keys.

In fact, zombie.js serializes them as an array of four objects (each with one key).

(I added vows for these on a topic branch: boblail/zombie@588c740)

Is Capybara's expectation in line with how Zombie should behave?

Got no response from a vanilla zombie.visit()

Hey! Thanks for creating Zombie.js, I'm just trying it out and playing with it a little.

So far, with a specific url, I'm not getting callback execution when calling zombie.visit(), but seems like the url is loaded as far as I can tell doing a console.log(body) in History.js:104

What am I leaving out here?

Here's the code:
var zombie = require("zombie");
zombie.visit("http://vi.sualize.us/", function (err, browser) {
console.log(err);
console.log('got in');
});

Thanks!

Need our own RunInContext

Node.js implements RunInContext by creating a context and using the sandbox to copy properties in/out of the context.

To make it work synchronously we start by having Windows be the context and the define certain properties to be non-configurable (location, storage, etc) so Node doesn't attempt to copy them (e.g. copying location from context to sandbox redirects the page).

But that doesn't work asynchronously, asynchronous events that run later on (e.g. XHR) operate on the context, properties changed them (global/window) do not propagate back to the sandbox.

So we're going to need our own implementation that can evaluate a script in the context of an object we give it (Window) without all the variable copying. And we know we can trust the evaluated code because, well, we're running tests.

Install with "cake setup" failing

I am sorry if this is stupid, but I have tried to install several times with different versions of node and am having no luck. If anyone could point me in the right direction that would be very helpful.

I am getting this output when running cake setup:

Need runtime dependencies, installing into node_modules ...
Need development dependencies, installing ...
Installing coffee-script >= 1.0.0
Installing docco 0.3.0
Installing express 1.0.1
Installing ronn 0.3.5
Installing vows 0.5.3
Error: Command failed: npm info it worked if it ends with ok npm info using [email protected]
npm info using [email protected]
npm ERR! sudon't!
npm ERR! sudon't! Running npm as root is not recommended!
npm ERR! sudon't! Seriously, don't do this!
npm ERR! sudon't!
npm info range opts@>=1.2.0
npm info preinstall [email protected]
npm info preinstall [email protected]
npm WARN bins installing to /home/node/zombie/node_modules/.bin, outside PATH
npm ERR! linking /home/node/zombie/node_modules/.npm/opts/1.2.1 to /home/node/zombie/node_modules/.npm/ronn/0.3.5/dependson/[email protected]
npm ERR! install failed Error: EPERM, Operation not permitted './../../../opts/1.2.1'
npm info install failed rollback
npm info uninstall [ '[email protected]', '[email protected]' ]
npm ERR! linking /home/node/zombie/node_modules/.npm/ronn/0.3.5 to /home/node/zombie/node_modules/.npm/opts/1.2.1/dependents/[email protected]
npm info preuninstall [email protected]
npm info preuninstall [email protected]
npm info uninstall [email protected]
npm info uninstall [email protected]
npm info auto-deactive not symlink
npm info auto-deactive not symlink
npm info postuninstall [email protected]
npm info postuninstall [email protected]
npm info uninstall [email protected] complete
npm info uninstall [email protected] complete
npm info install failed rolled back
npm ERR! Error: EPERM, Operation not permitted './../../../opts/1.2.1'
npm ERR! Report this *entire* log at <http://github.com/isaacs/npm/issues>
npm ERR! or email it to <[email protected]>
npm ERR! Just tweeting a tiny part of the error will not be helpful.
npm not ok

at ChildProcess.exithandler (child_process.js:76:15)
at ChildProcess.emit (events.js:34:17)
at ChildProcess.onexit (child_process.js:164:12)

You can see I ran this one as root, but I have tried both ways. Sorry again if this is not the right place to post.

Trouble with YUI 3 style JavaScript loading

I got Zombie setup, then pointed this zombie script at my site and got the following output:

yui: NOT loaded: ttw-window-landing
Cannot read property 'LandingWindow' of undefined
TipTheWeb — Support Your Favorite Stuff Online

This would mean that something is getting messed up when zombie is loading our external external scripts. Our script loading is using some advanced techniques with YUI 3 and combing files together on the server and maybe part of the issue.

These external script files should be loaded by zombie before the following callback in an inline script is executed:

function(Y){
    (new Y.TTW.LandingWindow({
        "appRootPath":"/",
        "sublimeUrl":"http://cdn.sublimevideo.net/js/lrsfz74x.js"
    }));
}

Here is the output of the same zombie script with debug turned on:

Zombie: GET http://tiptheweb.org/
Zombie: GET http://tiptheweb.org/ => 200
Zombie: Running script from http://tiptheweb.org/:undefined:undefined<script>
Zombie: Running script from http://tiptheweb.org/:undefined:undefined<script>
Zombie: GET /combo/?yui3:_v_3.3.0.PR3/yui/yui-min.js;44b0m6neh3zsx55rvs35hebp68&yui3:_v_3.3.0.PR3/loader/loader-min.js;xpp217cqc6ah58aceqp499k5ac&utils:yui_config;05p40pwqscw0n5g7675gfrecbc&res:yui_config;bw1knegscrpwbj2sftpfzw0f68
Zombie: GET /combo/?yui3:_v_3.3.0.PR3/yui/yui-min.js;44b0m6neh3zsx55rvs35hebp68&yui3:_v_3.3.0.PR3/loader/loader-min.js;xpp217cqc6ah58aceqp499k5ac&utils:yui_config;05p40pwqscw0n5g7675gfrecbc&res:yui_config;bw1knegscrpwbj2sftpfzw0f68 => 200
Zombie: Running script from /combo/
Zombie: Running script from http://tiptheweb.org/:undefined:undefined<script>
Zombie: Running script from http://tiptheweb.org/:undefined:undefined<script>
Zombie: Running script from http://tiptheweb.org/:undefined:undefined<script>
Zombie: Running script from http://tiptheweb.org/:undefined:undefined<script>
Zombie: Running script from http://tiptheweb.org/:undefined:undefined<script>
Zombie: Running script from http://tiptheweb.org/:undefined:undefined<script>
yui: NOT loaded: ttw-window-landing
Cannot read property 'LandingWindow' of undefined
Zombie: Running script from http://tiptheweb.org/:undefined:undefined<script>
Zombie: GET http://www.google-analytics.com/ga.js
Zombie: GET http://www.google-analytics.com/ga.js => 200
Zombie: Running script from /ga.js
TipTheWeb — Support Your Favorite Stuff Online

Also I'm not sure what all this :undefined:undefined stuff is…

HTML5 Parser error

I don't know where is the html5 module used by Zombie so I'll report this error here:

Zombie: GET http://www.facebook.com/group.php?gid=104369172929110&_fb_noscript=1 => 200

/usr/local/lib/node/.npm/html5/0.2.12/package/lib/html5/tokenizer.js:62
                throw(e);
    ^
TypeError: Cannot call method 'toLowerCase' of undefined
    at Object.endTagFormatting (/usr/local/lib/node/.npm/html5/0.2.12/package/lib/html5/parser/in_body_phase.js:646:85)
    at Object.processEndTag (/usr/local/lib/node/.npm/html5/0.2.12/package/lib/html5/parser/phase.js:50:36)
    at EventEmitter.do_token (/usr/local/lib/node/.npm/html5/0.2.12/package/lib/html5/parser.js:97:20)
    at EventEmitter.<anonymous> (/usr/local/lib/node/.npm/html5/0.2.12/package/lib/html5/parser.js:112:30)
    at EventEmitter.emit (events:27:15)
    at EventEmitter.emitToken (/usr/local/lib/node/.npm/html5/0.2.12/package/lib/html5/tokenizer.js:84:7)
    at EventEmitter.emit_current_token (/usr/local/lib/node/.npm/html5/0.2.12/package/lib/html5/tokenizer.js:813:7)
    at EventEmitter.tag_name_state (/usr/local/lib/node/.npm/html5/0.2.12/package/lib/html5/tokenizer.js:358:8)
    at EventEmitter.<anonymous> (/usr/local/lib/node/.npm/html5/0.2.12/package/lib/html5/tokenizer.js:59:25)
    at EventEmitter.emit (events:27:15)

Test failures

Hi again!

I just ran the test suite and got some failures. I expect you did not see them when pushing the code. So, where do these come from? Is it the platform (here: Ubuntu 10.04 64bit, node 0.2.5) or the dependencies? Can you publish your environment in which all tests pass?

It looks like the failures gather around the more sophisticated DOM manipulation, XHR and event stuff.

Another strange thing I noticed in my own code: right after a jQuery.prependTo() DOM manipulation the newly inserted element is available through browser.querySelector([id=foobar]) but not through browser.querySelector("#foobar").

Here's the dump of the failed tests:

♢ XMLHttpRequest

  load asynchronously
    ✗ should load resource
      » expected expression to evaluate to true, but was undefined // vows.js:95
  redirect
    ✗ should send cookies in XHR response
      » expected 'redirected: yes',
    got  undefined (==) // vows.js:95
  send cookies
    ✗ should send cookies in XHR response
      » expected 'yes',
    got  undefined (==) // vows.js:95
  receive cookies
    ✗ should process cookies in XHR response
      » expected 'lol',
    got  undefined (==) // vows.js:95

♢ Browser

  adding script using document.write
    ✗ should run script
      » expected 'Script document.write',
    got  '' (==) // vows.js:95
  adding script using appendChild
    ✗ should run script
      » expected 'Script appendChild',
    got  '' (==) // vows.js:95

  open page
    ✓ should create HTML document
    ✓ should load document from server
    ✓ should load external scripts
    ✗ should run jQuery.onready
      » expected 'Awesome',
    got  'Whatever' (==) // vows.js:95
  click link
    ✓ should change location
    ✗ should run all events
      » expected 'The Dead',
    got  '' (==) // vows.js:95

TypeError: Cannot call method 'addEventListener' of undefined

Trying to run tests, I get the following error:

[23:07][:~/src/zombie(master)]$ vows

node.js:63
    throw e;
    ^
TypeError: Cannot call method 'addEventListener' of undefined
    at Browser.visit (/Users/torgo/src/zombie/src/zombie/browser.coffee:102:25)
    at Browser.<anonymous> (/Users/torgo/src/zombie/spec/helpers.coffee:72:19)
    at Array.<anonymous> (/Users/torgo/src/zombie/spec/helpers.coffee:3:63)
    at EventEmitter._tickCallback (node.js:55:22)
    at node.js:773:9

I'm not sure where to go from here. Any suggestions?

assert is not defined?

I'm bumping into some weird issues with assert not being defined. I'm running node 0.2.6 (though it happened on 0.2.5 as well) and zombie 0.8.6.

This happens any time I try an assert, but here's a simple illustration test.js:

var zombie = require("zombie");
var browser = new zombie.Browser();

var url = "http://semanticart.com";

browser.visit(url, function(err, browser) {
  var title = browser.text("title");

  assert.equal (title, "Semantic Art");   // ugh, assert not defined
});

which gives this error:

node.js:63
    throw e;
    ^
ReferenceError: assert is not defined
    at /Users/torgo/src/zombie/test.js:9:3
    at Browser.<anonymous> (/usr/local/lib/node/.npm/zombie/0.8.6/package/lib/zombie/browser.js:81:18)
    at /usr/local/lib/node/.npm/zombie/0.8.6/package/lib/zombie/browser.js:9:60
    at EventLoop.<anonymous> (/usr/local/lib/node/.npm/zombie/0.8.6/package/lib/zombie/eventloop.js:137:18)
    at Array.<anonymous> (/usr/local/lib/node/.npm/zombie/0.8.6/package/lib/zombie/eventloop.js:2:61)
    at EventEmitter._tickCallback (node.js:55:22)
    at node.js:773:9

I tried requiring assert in test_with_require.js:

var vows = require('vows'),
    assert = require('assert');

var zombie = require("zombie");
var browser = new zombie.Browser();

var url = "http://semanticart.com";

browser.visit(url, function(err, browser) {
  var title = browser.text("title");

  assert.equal (title, "Semantic Art");   // This is correct and throws no errors
  assert.equal (title, "Robocop Villa");  // This is incorrect, but the error message is poor
});

but the failing assertion gives a poor error message:

node.js:63
    throw e;
    ^
<error: TypeError: Cannot call method 'match' of undefined>
    at /Users/torgo/src/zombie/test_with_require.js:13:10
    at Browser.<anonymous> (/usr/local/lib/node/.npm/zombie/0.8.6/package/lib/zombie/browser.js:81:18)
    at /usr/local/lib/node/.npm/zombie/0.8.6/package/lib/zombie/browser.js:9:60
    at EventLoop.<anonymous> (/usr/local/lib/node/.npm/zombie/0.8.6/package/lib/zombie/eventloop.js:137:18)
    at Array.<anonymous> (/usr/local/lib/node/.npm/zombie/0.8.6/package/lib/zombie/eventloop.js:2:61)
    at EventEmitter._tickCallback (node.js:55:22)
    at node.js:773:9

I'm probably missing something obvious. Any help would be greatly appreciated.

PHP Sessions not held between requests

The PHP session id is not being held between requests. This has been run on 2 different LAMP / Node setups and both return the same result. If the form in submitted in a browser (FF 3.6.13 in this case) we see 'logged in' as the response. But if we run the node file it shows the form and the session id's do not match between requests. I have not had a chance to see if this is only PHP related or not.

The Zombie

    var zombie = require("zombie");
    var assert = require("assert");
    b = new zombie.Browser();
    // Load the page from localhost
    b.visit("http://elnormo.net/login_test.php", function (err, browser) {
      console.log('intial page load');
      intialSessionId = browser.cookies('elnormo.net').get('PHPSESSID');
      console.log(intialSessionId);
      // Fill from and submit form
      browser.
        fill("username", "pedro").
        fill("password", "runaway").
        pressButton("login", function(err, br) {
            console.log('form posted');
            console.log(br.lastRequest);
            console.log(br.lastResponse);
            console.log(br.lastError);
            innerSessionId = br.cookies('elnormo.net').get('PHPSESSID');
            console.log(intialSessionId);
            console.log(innerSessionId);
            assert.equal(intialSessionId, innerSessionId);

        })
    });

The PHP

<?php
session_start();

if(!empty($_POST)){
   $_SESSION['user'] = 'test';
   header('Location: /login_test.php');
   exit();
}

// if no session show form
if(!isset($_SESSION['user'])){ ?>
<!DOCTYPE html>
<html>
<head>
    <title>Test HTML5!</title>
    <meta charset="utf-8" />
</head>
<body>

   <form method="POST" action="/login_test.php">
      <input name="username" />
      <input name="password" />
      <input type="submit" name="login" value="login"/>
   </form>

</body>
</html>
<?php
}else{
   exit('logged in');
}
?>

zombie.visit issues DNS request even for localhost/127.0.0.1

Example:

zombie.visit("http://127.0.0.1/", function (err, browser) {
    console.log(err);
});

yields

events:12
    throw arguments[1];
                   ^
Error: ECONNREFUSED, Could not contact DNS servers
    at IOWatcher.callback (dns:52:15)
    at node.js:773:9

when there's no internet connection. This breaks offline development.

I tried the following without the indirection through zombie, and this works perfectly fine:

var http = require('http');
var client = http.createClient(80, 'localhost');
var request = client.request('GET', '/', {'host': 'localhost'});
request.end();

Cookie behavior

I have noticed a strange things in Cookies in general. I have an app in my hands that sets the cookie as below:

Set-Cookie: test=test

I've been reading the set-cookie documentation and it clearly says that the path for the cookie must be set to the path of the current resource setting the cookie:

      Path   Defaults to the path of the request URL that generated the
                Set-Cookie response, up to, but not including, the
                right-most /.

This is current Zombie.js behavior, it is completely fine. But all browsers that I've tested (Google Chrome and Firefox 3), they set the path to "/", which makes Zombie.js and browsers navigation inconsistent with each other.

I've been able to make a patch in Zombie.js to reflect browsers' behavior, but sounds really strange. Do you know anything in the issue?

Errors on a couple of well-known sites

I'm not sure if this is an issue with zombie or a problem with either my install or these sites, but I'm getting very long and unclear errors when I try to visit Google, Wikipedia, or the W3C Validation Service. I was trying to verify that I had installed zombie correctly and was able to write a test on something that (I thought) would work before moving on to a new project.

var zombie = require("zombie");

// var url = "http://google.com";
// var url = "http://en.wikipedia.org";
var url = "http://validator.w3.org/";

zombie.visit(url, {debug: true}, function(err, browser) {
  if (err) {
    console.log(err.message);
  } else {
    browser.dump();
  }
});

Each produces what looks to be a javascript error. Here's the one for the validation service (I can supply the other ones if requested. I think Google may have an illegal character.):

Zombie: GET http://validator.w3.org/
Zombie: GET http://validator.w3.org/ => 200
Zombie: GET scripts/mootools.js
Zombie: GET scripts/w3c-validator.js
Zombie: GET http://www.w3.org/QA/Tools/don_prog.js
Zombie: GET scripts/mootools.js => 200
Zombie: Running script from /scripts/mootools.js
Zombie: TypeError: Cannot set property 'constructor' of undefined,    at new <anonymous> (/scripts/mootools.js:3:374)
Cannot set property 'constructor' of undefined
Zombie: GET scripts/w3c-validator.js => 200
Zombie: Running script from /scripts/w3c-validator.js
Zombie: TypeError: Object [object Context] has no method 'addEvent',    at /scripts/w3c-validator.js:177:8
13 Jan 23:05:25 - The onerror handler

 on target 

{ frames: [ [Circular] ]
, contentWindow: [Circular]
, window: [Circular]
, self: [Circular]
, location: [Getter/Setter]
, addEventListener: [Function]
, dispatchEvent: [Function]
, browser: [Getter]
, cookies: [Getter]
, sessionStorage: [Getter]
, localStorage: [Getter]
, setTimeout: [Function]
, setInterval: [Function]
, clearTimeout: [Function]
, clearInterval: [Function]
, perform: [Function]
, wait: [Function]
, request: [Function]
, history: [Getter]
, XMLHttpRequest: [Function]
, JSON: {}
, onerror: [Function]
, Image: [Function]
, _document: 
   { _childNodes: [ [Object], [Object] ]
   , _version: 2179
   , _children: 
      { '0': [Circular]
      , '1': [Circular]
      , _element: [Circular]
      , _query: [Function]
      , _version: 2179
      , _snapshot: [Circular]
      , _length: 2
      }
   , _nodeValue: null
   , _parentNode: null
   , _ownerDocument: [Circular]
   , _attributes: 
      { _nodes: {}
      , _length: 0
      , _ownerDocument: null
      , _readonly: false
      , _parentNode: [Circular]
      }
   , _nodeName: '#document'
   , _readonly: false
   , style: { position: 'static' }
   , _tagName: '#document'
   , _contentType: 'text/html'
   , _doctype: null
   , _implementation: { _ownerDocument: undefined, _features: [Object] }
   , _documentElement: [Circular]
   , _ids: 
      { banner: [Object]
      , title: [Object]
      , logo: [Object]
      , tagline: [Object]
      , frontforms: [Object]
      , tabset_tabs: [Object]
      , fields: [Object]
      , 'validate-by-uri': [Object]
      , uri: [Object]
      , extra_opt_uri: [Object]
      , toggleiconURI: [Object]
      , 'uri-charset': [Object]
      , 'uri-fbc': [Object]
      , 'uri-doctype': [Object]
      , 'uri-fbd': [Object]
      , urigroup_no: [Object]
      , urigroup_yes: [Object]
      , 'uri-ss': [Object]
      , 'uri-st': [Object]
      , 'uri-outline': [Object]
      , 'uri-No200': [Object]
      , 'uri-verbose': [Object]
      , 'validate-by-upload': [Object]
      , uploaded_file: [Object]
      , extra_opt_upload: [Object]
      , 'upload-charset': [Object]
      , 'upload-fbc': [Object]
      , 'upload-doctype': [Object]
      , 'upload-fbd': [Object]
      , uploadgroup_no: [Object]
      , uploadgroup_yes: [Object]
      , 'upload-ss': [Object]
      , 'upload-st': [Object]
      , 'upload-outline': [Object]
      , 'upload-No200': [Object]
      , 'upload-verbose': [Object]
      , 'validate-by-input': [Object]
      , fragment: [Object]
      , extra_opt_direct: [Object]
      , direct_prefill_no: [Object]
      , choice_full: [Object]
      , 'direct-doctype': [Object]
      , 'direct-fbd': [Object]
      , direct_prefill_yes: [Object]
      , choice_frag: [Object]
      , directfill_doctype_html401: [Object]
      , directfill_doctype_xhtml10: [Object]
      , directgroup_no: [Object]
      , directgroup_yes: [Object]
      , 'direct-ss': [Object]
      , 'direct-st': [Object]
      , 'direct-outline': [Object]
      , 'direct-No200': [Object]
      , 'direct-verbose': [Object]
      , don_program: [Object]
      , menu: [Object]
      , footer: [Object]
      , activity_logos: [Object]
      , support_logo: [Object]
      , version_info: [Object]
      }
   , _URL: 'http://validator.w3.org/'
   , _documentRoot: 'http:/'
   , _queue: { paused: false, tail: [Object], push: [Function] }
   , readyState: 'complete'
   , createWindow: [Function]
   , _parentWindow: [Circular]
   , _sessionStorage: 
      { length: [Getter]
      , key: [Function]
      , getItem: [Function]
      , setItem: [Function]
      , removeItem: [Function]
      , clear: [Function]
      , dump: [Function]
      }
   , _localStorage: 
      { length: [Getter]
      , key: [Function]
      , getItem: [Function]
      , setItem: [Function]
      , removeItem: [Function]
      , clear: [Function]
      , dump: [Function]
      }
   , window: [Circular]
   , uid: 2
   , html: [Circular]
   , '$family': { name: 'document' }
   }
, uid: 1
, Element: [Function: $empty]
, '$family': { name: 'window' }
, _vars: 
   [ [ 'pageXOffset', 0 ]
   , [ 'pageYOffset', 0 ]
   , [ 'screenX', 0 ]
   , [ 'screenY', 0 ]
   , [ 'screenLeft', 0 ]
   , [ 'screenTop', 0 ]
   , [ 'scrollX', 0 ]
   , [ 'scrollY', 0 ]
   , [ 'scrollTop', 0 ]
   , [ 'scrollLeft', 0 ]
   ]
}

 threw error 

{ message: 'Uncaught, unspecified \'error\' event.'
, stack: [Getter/Setter]
}

 handling event 

{ _eventType: 'HTMLEvents'
, _type: 'error'
, _bubbles: true
, _cancelable: false
, _target: 
   { frames: [ [Circular] ]
   , contentWindow: [Circular]
   , window: [Circular]
   , self: [Circular]
   , location: [Getter/Setter]
   , addEventListener: [Function]
   , dispatchEvent: [Function]
   , browser: [Getter]
   , cookies: [Getter]
   , sessionStorage: [Getter]
   , localStorage: [Getter]
   , setTimeout: [Function]
   , setInterval: [Function]
   , clearTimeout: [Function]
   , clearInterval: [Function]
   , perform: [Function]
   , wait: [Function]
   , request: [Function]
   , history: [Getter]
   , XMLHttpRequest: [Function]
   , JSON: {}
   , onerror: [Function]
   , Image: [Function]
   , _document: 
      { _childNodes: [Object]
      , _version: 2179
      , _children: [Object]
      , _nodeValue: null
      , _parentNode: null
      , _ownerDocument: [Circular]
      , _attributes: [Object]
      , _nodeName: '#document'
      , _readonly: false
      , style: [Object]
      , _tagName: '#document'
      , _contentType: 'text/html'
      , _doctype: null
      , _implementation: [Object]
      , _documentElement: [Object]
      , _ids: [Object]
      , _URL: 'http://validator.w3.org/'
      , _documentRoot: 'http:/'
      , _queue: [Object]
      , readyState: 'complete'
      , createWindow: [Function]
      , _parentWindow: [Circular]
      , _sessionStorage: [Object]
      , _localStorage: [Object]
      , window: [Circular]
      , uid: 2
      , html: [Circular]
      , '$family': [Object]
      }
   , uid: 1
   , Element: [Function: $empty]
   , '$family': { name: 'window' }
   , _vars: 
      [ [Object]
      , [Object]
      , [Object]
      , [Object]
      , [Object]
      , [Object]
      , [Object]
      , [Object]
      , [Object]
      , [Object]
      ]
   }
, _currentTarget: null
, _eventPhase: 2
, _timeStamp: null
, _preventDefault: false
, _stopPropagation: false
, error: 
   { message: [Getter/Setter]
   , stack: [Getter/Setter]
   , type: 'undefined_method'
   , arguments: [ 'addEvent', [Circular] ]
   }
}
Zombie: GET http://www.w3.org/QA/Tools/don_prog.js => 200
Zombie: Running script from /QA/Tools/don_prog.js
Zombie: GET http://api.flattr.com/js/0.6/load.js?mode=auto
Zombie: GET http://api.flattr.com/js/0.6/load.js?mode=auto => 200
Zombie: Running script from /js/0.6/load.js

So, is it ok to ignore these and assume that I can start a greenfield project and use zombie for testing?

Thanks.

cake setup fails

/zombie$ cake setup
Need Vows and Express to run test suite, installing ...
Need Ronn and Docco to generate documentation, installing ...
Need runtime dependencies, installing ...
Error: Command failed: npm info it worked if it ends with ok
npm info using [email protected]
npm info using [email protected]
npm ERR! Error: Using '>=' with 0.5.x makes no sense. Don't do it.
npm ERR! at /home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:94:29
npm ERR! at String.replace (native)
npm ERR! at replaceXRange (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:88:25)
npm ERR! at Array.map (native)
npm ERR! at replaceXRanges (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:84:17)
npm ERR! at Array.map (native)
npm ERR! at toComparators (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:66:8)
npm ERR! at Object.validRange (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:138:11)
npm ERR! at install_ (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/install.js:101:22)
npm ERR! at /home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/install.js:76:42
npm ERR! Report this entire log at http://github.com/isaacs/npm/issues
npm ERR! or email it to [email protected]
npm ERR! Just tweeting a tiny part of the error will not be helpful.
npm not ok

at ChildProcess.exithandler (child_process:80:15)
at ChildProcess.emit (events:27:15)
at ChildProcess.onexit (child_process:168:12)
at node.js:773:9

Error: Command failed: npm info it worked if it ends with ok
npm info using [email protected]
npm info using [email protected]
npm ERR! Error: Using '>=' with 0.3.x makes no sense. Don't do it.
npm ERR! at /home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:94:29
npm ERR! at String.replace (native)
npm ERR! at replaceXRange (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:88:25)
npm ERR! at Array.map (native)
npm ERR! at replaceXRanges (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:84:17)
npm ERR! at Array.map (native)
npm ERR! at toComparators (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:66:8)
npm ERR! at Object.validRange (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:138:11)
npm ERR! at install_ (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/install.js:101:22)
npm ERR! at /home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/install.js:76:42
npm ERR! Report this entire log at http://github.com/isaacs/npm/issues
npm ERR! or email it to [email protected]
npm ERR! Just tweeting a tiny part of the error will not be helpful.
npm not ok

at ChildProcess.exithandler (child_process:80:15)
at ChildProcess.emit (events:27:15)
at ChildProcess.onexit (child_process:168:12)
at node.js:773:9

Error: Command failed: npm info it worked if it ends with ok
npm info using [email protected]
npm info using [email protected]
npm ERR! Error: Using '>=' with 0.3.x makes no sense. Don't do it.
npm ERR! at /home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:94:29
npm ERR! at String.replace (native)
npm ERR! at replaceXRange (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:88:25)
npm ERR! at Array.map (native)
npm ERR! at replaceXRanges (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:84:17)
npm ERR! at Array.map (native)
npm ERR! at toComparators (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:66:8)
npm ERR! at Object.validRange (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:138:11)
npm ERR! at install_ (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/install.js:101:22)
npm ERR! at /home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/install.js:76:42
npm ERR! Report this entire log at http://github.com/isaacs/npm/issues
npm ERR! or email it to [email protected]
npm ERR! Just tweeting a tiny part of the error will not be helpful.
npm not ok

at ChildProcess.exithandler (child_process:80:15)
at ChildProcess.emit (events:27:15)
at ChildProcess.onexit (child_process:168:12)
at node.js:773:9

Error: Command failed: npm info it worked if it ends with ok
npm info using [email protected]
npm info using [email protected]
npm ERR! Error: Using '>=' with 1.0.x makes no sense. Don't do it.
npm ERR! at /home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:94:29
npm ERR! at String.replace (native)
npm ERR! at replaceXRange (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:88:25)
npm ERR! at Array.map (native)
npm ERR! at replaceXRanges (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:84:17)
npm ERR! at Array.map (native)
npm ERR! at toComparators (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:66:8)
npm ERR! at Object.validRange (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/utils/semver.js:138:11)
npm ERR! at install_ (/home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/install.js:101:22)
npm ERR! at /home/alfred/local/lib/node/.npm/npm/0.2.12-1/package/lib/install.js:76:42
npm ERR! Report this entire log at http://github.com/isaacs/npm/issues
npm ERR! or email it to [email protected]
npm ERR! Just tweeting a tiny part of the error will not be helpful.
npm not ok

at ChildProcess.exithandler (child_process:80:15)
at ChildProcess.emit (events:27:15)
at ChildProcess.onexit (child_process:168:12)
at node.js:773:9

NPM install still fails

npm install zombie
npm info it worked if it ends with ok
npm info using [email protected]
npm info using [email protected]
npm info preinstall [email protected]
npm info preinstall [email protected]
npm info install [email protected]
npm info postinstall [email protected]
npm info preactivate [email protected]
npm info activate [email protected]
npm info postactivate [email protected]
npm info install [email protected]
Checking for program g++ or c++ : /usr/bin/g++
Checking for program cpp : /usr/bin/cpp
Checking for program ar : /usr/bin/ar
Checking for program ranlib : /usr/bin/ranlib
Checking for g++ : ok
Checking for node path : ok /home/felipe/.node_libraries
Checking for node prefix : ok /usr/local
'configure' finished successfully (0.063s)
Waf: Entering directory /home/felipe/.node_libraries/.npm/base64/1.0.1/package/build' [1/2] cxx: base64.cc -> build/default/base64_1.o ../base64.cc: In function ‘v8::Handle<v8::Value> base64_encode_binding(const v8::Arguments&)’: ../base64.cc:132: error: ‘class node::Buffer’ has no member named ‘data’ ../base64.cc:132: error: ‘class node::Buffer’ has no member named ‘length’ ../base64.cc: In function ‘v8::Handle<v8::Value> base64_decode_binding(const v8::Arguments&)’: ../base64.cc:150: error: ‘class node::Buffer’ has no member named ‘data’ ../base64.cc:150: error: ‘class node::Buffer’ has no member named ‘length’ Waf: Leaving directory/home/felipe/.node_libraries/.npm/base64/1.0.1/package/build'
Build failed: -> task failed (err #1):
{task: cxx base64.cc -> base64_1.o}
npm info [email protected] Failed to exec install script
npm ERR! install failed Error: [email protected] install: node-waf configure build
npm ERR! install failed sh failed with 1
npm ERR! install failed at ChildProcess. (/home/felipe/.node_libraries/.npm/npm/0.2.15/package/lib/utils/exec.js:25:18)
npm ERR! install failed at ChildProcess.emit (events.js:34:17)
npm ERR! install failed at ChildProcess.onexit (child_process.js:164:12)
npm info install failed rollback
npm info uninstall [ '[email protected]',
npm info uninstall '[email protected]' ]
npm info preuninstall [email protected]
npm info preuninstall [email protected]
npm info uninstall [email protected]
npm info uninstall [email protected]
npm info auto-deactive not symlink
npm info postuninstall [email protected]
npm info predeactivate [email protected]
npm info deactivate [email protected]
npm info postdeactivate [email protected]
npm info postuninstall [email protected]
npm info uninstall [email protected] complete
npm info uninstall [email protected] complete
npm info install failed rolled back
npm ERR! Error: [email protected] install: node-waf configure build
npm ERR! sh failed with 1
npm ERR! at ChildProcess. (/home/felipe/.node_libraries/.npm/npm/0.2.15/package/lib/utils/exec.js:25:18)
npm ERR! at ChildProcess.emit (events.js:34:17)
npm ERR! at ChildProcess.onexit (child_process.js:164:12)
npm ERR!
npm ERR! Failed at the [email protected] install script.
npm ERR! This is most likely a problem with the base64 package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR! node-waf configure build
npm ERR! You can get their info via:
npm ERR! npm owner ls base64
npm ERR! There may be additional logging output above.
npm not ok

base64 dependency missing

The dependency is missing from package.json. I wanted to add it on my own, but were unsure which of the four base64 for node.js implementations I found on github you were intended to use ;)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.