Code Monkey home page Code Monkey logo

chai-as-promised's Introduction

Promises/A+ logo

Chai Assertions for Promises

Chai as Promised extends Chai with a fluent language for asserting facts about promises.

Instead of manually wiring up your expectations to a promise's fulfilled and rejected handlers:

doSomethingAsync().then(
    function (result) {
        result.should.equal("foo");
        done();
    },
    function (err) {
       done(err);
    }
);

you can write code that expresses what you really mean:

return doSomethingAsync().should.eventually.equal("foo");

or if you have a case where return is not preferable (e.g. style considerations) or not possible (e.g. the testing framework doesn't allow returning promises to signal asynchronous test completion), then you can use the following workaround (where done() is supplied by the test framework):

doSomethingAsync().should.eventually.equal("foo").notify(done);

Notice: either return or notify(done) must be used with promise assertions. This can be a slight departure from the existing format of assertions being used on a project or by a team. Those other assertions are likely synchronous and thus do not require special handling.

How to Use

should/expect Interface

The most powerful extension provided by Chai as Promised is the eventually property. With it, you can transform any existing Chai assertion into one that acts on a promise:

(2 + 2).should.equal(4);

// becomes
return Promise.resolve(2 + 2).should.eventually.equal(4);


expect({ foo: "bar" }).to.have.property("foo");

// becomes
return expect(Promise.resolve({ foo: "bar" })).to.eventually.have.property("foo");

There are also a few promise-specific extensions (with the usual expect equivalents also available):

return promise.should.be.fulfilled;
return promise.should.eventually.deep.equal("foo");
return promise.should.become("foo"); // same as `.eventually.deep.equal`
return promise.should.be.rejected;
return promise.should.be.rejectedWith(Error); // other variants of Chai's `throw` assertion work too.

assert Interface

As with the should/expect interface, Chai as Promised provides an eventually extender to chai.assert, allowing any existing Chai assertion to be used on a promise:

assert.equal(2 + 2, 4, "This had better be true");

// becomes
return assert.eventually.equal(Promise.resolve(2 + 2), 4, "This had better be true, eventually");

And there are, of course, promise-specific extensions:

return assert.isFulfilled(promise, "optional message");

return assert.becomes(promise, "foo", "optional message");
return assert.doesNotBecome(promise, "foo", "optional message");

return assert.isRejected(promise, Error, "optional message");
return assert.isRejected(promise, /error message regex matcher/, "optional message");
return assert.isRejected(promise, "substring to search error message for", "optional message");

Progress Callbacks

Chai as Promised does not have any intrinsic support for testing promise progress callbacks. The properties you would want to test are probably much better suited to a library like Sinon.JS, perhaps in conjunction with Sinon–Chai:

var progressSpy = sinon.spy();

return promise.then(null, null, progressSpy).then(function () {
    progressSpy.should.have.been.calledWith("33%");
    progressSpy.should.have.been.calledWith("67%");
    progressSpy.should.have.been.calledThrice;
});

Customizing Output Promises

By default, the promises returned by Chai as Promised's assertions are regular Chai assertion objects, extended with a single then method derived from the input promise. To change this behavior, for instance to output a promise with more useful sugar methods such as are found in most promise libraries, you can override chaiAsPromised.transferPromiseness. Here's an example that transfer's Q's finally and done methods:

chaiAsPromised.transferPromiseness = function (assertion, promise) {
    assertion.then = promise.then.bind(promise); // this is all you get by default
    assertion.finally = promise.finally.bind(promise);
    assertion.done = promise.done.bind(promise);
};

Transforming Arguments to the Asserters

Another advanced customization hook Chai as Promised allows is if you want to transform the arguments to the asserters, possibly asynchronously. Here is a toy example:

chaiAsPromised.transformAsserterArgs = function (args) {
    return args.map(function (x) { return x + 1; });
}

Promise.resolve(2).should.eventually.equal(2); // will now fail!
Promise.resolve(3).should.eventually.equal(2); // will now pass!

The transform can even be asynchronous, returning a promise for an array instead of an array directly. An example of that might be using Promise.all so that an array of promises becomes a promise for an array. If you do that, then you can compare promises against other promises using the asserters:

// This will normally fail, since within() only works on numbers.
Promise.resolve(2).should.eventually.be.within(Promise.resolve(1), Promise.resolve(6));

chaiAsPromised.transformAsserterArgs = function (args) {
    return Promise.all(args);
};

// But now it will pass, since we transformed the array of promises for numbers into
// (a promise for) an array of numbers
Promise.resolve(2).should.eventually.be.within(Promise.resolve(1), Promise.resolve(6));

Compatibility

Chai as Promised is compatible with all promises following the Promises/A+ specification.

Notably, jQuery's promises were not up to spec before jQuery 3.0, and Chai as Promised will not work with them. In particular, Chai as Promised makes extensive use of the standard transformation behavior of then, which jQuery<3.0 does not support.

Angular promises have a special digest cycle for their processing, and need extra setup code to work with Chai as Promised.

Working with Non-Promise–Friendly Test Runners

Some test runners (e.g. Jasmine, QUnit, or tap/tape) do not have the ability to use the returned promise to signal asynchronous test completion. If possible, I'd recommend switching to ones that do, such as Mocha, Buster, or blue-tape. But if that's not an option, Chai as Promised still has you covered. As long as your test framework takes a callback indicating when the asynchronous test run is over, Chai as Promised can adapt to that situation with its notify method, like so:

it("should be fulfilled", function (done) {
    promise.should.be.fulfilled.and.notify(done);
});

it("should be rejected", function (done) {
    otherPromise.should.be.rejected.and.notify(done);
});

In these examples, if the conditions are not met, the test runner will receive an error of the form "expected promise to be fulfilled but it was rejected with [Error: error message]", or "expected promise to be rejected but it was fulfilled."

There's another form of notify which is useful in certain situations, like doing assertions after a promise is complete. For example:

it("should change the state", function (done) {
    otherState.should.equal("before");
    promise.should.be.fulfilled.then(function () {
        otherState.should.equal("after");
    }).should.notify(done);
});

Notice how .notify(done) is hanging directly off of .should, instead of appearing after a promise assertion. This indicates to Chai as Promised that it should pass fulfillment or rejection directly through to the testing framework. Thus, the above code will fail with a Chai as Promised error ("expected promise to be fulfilled…") if promise is rejected, but will fail with a simple Chai error (expected "before" to equal "after") if otherState does not change.

Working with async/await and Promise-Friendly Test Runners

Since any assertion that must wait on a promise returns a promise itself, if you're able to use async/await and your test runner supports returning a promise from test methods, you can await assertions in tests. In many cases you can avoid using Chai as Promised at all by performing a synchronous assertion after an await, but awaiting rejectedWith is often more convenient than using try/catch blocks without Chai as Promised:

it('should work well with async/await', async () => {
  (await Promise.resolve(42)).should.equal(42)
  await Promise.reject(new Error()).should.be.rejectedWith(Error);
});

Multiple Promise Assertions

To perform assertions on multiple promises, use Promise.all to combine multiple Chai as Promised assertions:

it("should all be well", function () {
    return Promise.all([
        promiseA.should.become("happy"),
        promiseB.should.eventually.have.property("fun times"),
        promiseC.should.be.rejectedWith(TypeError, "only joyful types are allowed")
    ]);
});

This will pass any failures of the individual promise assertions up to the test framework, instead of wrapping them in an "expected promise to be fulfilled…" message as would happen if you did return Promise.all([…]).should.be.fulfilled. If you can't use return, then use .should.notify(done), similar to the previous examples.

Installation and Setup

Node

Do an npm install chai-as-promised to get up and running. Then:

var chai = require("chai");
var chaiAsPromised = require("chai-as-promised");

chai.use(chaiAsPromised);

// Then either:
var expect = chai.expect;
// or:
var assert = chai.assert;
// or:
chai.should();
// according to your preference of assertion style

You can of course put this code in a common test fixture file; for an example using Mocha, see the Chai as Promised tests themselves.

Note when using other Chai plugins: Chai as Promised finds all currently-registered asserters and promisifies them, at the time it is installed. Thus, you should install Chai as Promised last, after any other Chai plugins, if you expect their asserters to be promisified.

In the Browser

To use Chai as Promised in environments that don't support Node.js-like CommonJS modules, you'll need to use a bundling tool like browserify. See also the note below about browser compatibility.

Karma

If you're using Karma, check out the accompanying karma-chai-as-promised plugin.

Browser/Node Compatibility

Chai as Promised requires Node v4+ or a browser with equivalent support for modern JavaScript syntax. If your browser doesn't support modern JavaScript syntax, you'll need to transpile it down using a tool like Babel.

chai-as-promised's People

Contributors

btbonval avatar cycymomo avatar domenic avatar dtinth avatar groner avatar ikhemissi avatar james-firth avatar jamiemill avatar jedwards1211 avatar joshperry avatar keithamus avatar klamping avatar lddubeau avatar leedm777 avatar martletandco avatar matthijsgroen avatar meeber avatar mgol avatar raine avatar robotlolita avatar ryckes avatar scottohara avatar sebv avatar stalniy 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

chai-as-promised's Issues

Question with .notfiy(callback)

I have a question that deals with .notify(). I wasn't sure the best place to post it. If you want I can move it over to Stackoverflow.

I'm using Protractor, cucumberjs, chai & chai-as-promised. We've been changing around our code and now running into issues of test runs just stopping completely without a reason why, which is an issue with Protractor or cucumber.

I'm aware that chai-as-promises uses the Promises/A+ specification, so we do something like this.

var promise = element(by.css('.pass')).isDisplayed();
expect(promise).to.eventually.to.equal(true, "Link should be displayed').and.notify(next);

1st Question -
If we have 2 steps in a scenario, should / can they both have notify(next) in them. I thought notify next was used to signal the next scenario to start.

I thought it need to be structured like so

  Scenario 1
    Step 1 (expect(p).to.eventually.to.equal(true).and.notify(next);)
    Step 2 (expect(p2).to.eventually.to.equal(false).and.notify(next);)
Scenario 2
     .......

Or should it be like

  Scenario 1
    Step 1 (expect(p).to.eventually.to.equal(true);)
    Step 2 (expect(p2).to.eventually.to.equal(false).and.notify(next);)
Scenario 2
     .......

Question 2 -
I was looking through http://chaijs.com/plugins/chai-as-promised and saw that you are calling notify on the should of then's.

    promise.should.be.fulfilled.then(function () {
        expect(p).to.eventually.to.equal(true)
        otherState.should.equal("after");
    }).should.notify(done);

I'm assuming that chai-as-promised can call notify with-in the then with out issues. (FYI - when the expect(p) fails, this halts the test run with no indication with what failed)

    promise.should.be.fulfilled.then(function () {
        expect(p).to.eventually.to.equal(true)
        should.equal("after").should.notify(done);
        otherState.should.equal("after").should.notify(done);
    });

Question 3 -
We need to verify that many elements are set so we do something like this. I believe this is wrong because .notify() is being called for each iteration.

var divs = element.all(by.css('div.wrapper')); // gets all divs with wrapper class
divs.each(function(div){
    expect(div.element(by.css('.title')).getText()).to.eventually.to.equal('A title');
    expect(div.element(by.css('.error')).getText()).to.eventually.to.equal('An Error').and.notify(done);
});

Instead we should do something like this. We can't call should notify directly off the then because it's a protractor promise. I was thinking of a way to use Q.all([]) but I don't there's a clean way to do it.

var divs = element.all(by.css('div.wrapper')); // gets all divs with wrapper class
divs.each(function(div){
    expect(div.element(by.css('.title')).getText()).to.eventually.to.equal('A title');
    expect(div.element(by.css('.error')).getText()).to.eventually.to.equal('An Error');
}).then(function(){
    "1".should.equal("1").should.notify(done);
})

Like I said earlier, if this isn't the place to post this, I'll move it over to stack overflow.

Diff of two arrays gets lost

Something seems to get lost when trying to compare two arrays using promise oriented expects (seems to behave the same with shoulds as well).

E.g. if I do:

expect(Q(['foo'])).to.eventually.equal(['bar']);

I'd expect to get a diff showing that one array has "foo" and then other "bar", but instead I get:

      [
        "bar"
      ]{}

NB: because I'll lose the coloring in GitHub the [ "bar" ] is the expected and the {} is the actual.

The equivalent immediate test:

expect(['foo']).to.equal(['bar']);

displays the right diff.

Here's a full test case that demonstrates the problem:

describe('fail', function() {
    var chai = require("chai");
    chai.use(require("chai-as-promised"));
    var expect = chai.expect;
    var Q = require('q');
    require("mocha-as-promised")();

    it('should display diff', function() {
        return expect(Q(['foo'])).to.eventually.equal(['bar']);
    });

    it('does display a diff', function() {
        expect(['foo']).to.equal(['bar']);
    });
});

with a screen shot from running it:
mocha-as-promised

browser support fails because of utils

Hi, I'm using chai and i'd like to use chai as promised as i use promises in my code ;)
When i include the chai-as-promised.js it fails at

else {
       // Other environment (usually <script> tag): plug in to global chai instance directly.
       chai.use(chaiAsPromised);
 }

as the call
function chaiAsPromised(chai, utils)

utis is undefined, so at the end

function property(name, asserter) {
        utils.addProperty(Assertion.prototype, name, function () {

throws an undefined exception

[I'm using last version of chai] I thought i was using last version of chai, that was the problem!
With last version is ok :)

Thanks

expect/assert return different promises

I'm using this with Cucumber.js, which requires a callback to be called after each test runs.

We basically want to do this:
expect(someElement.getText()).to.eventually.equal('foo').then(function () { callback(); }, function(failure) { callback(failure); });

We originally used the expect() syntax. However, this appears to return the original promise from protractor (or some variation of it). Since this promise is already resolved, it can't be rejected if the test fails. This has caused caused a lot of flakiness in our tests when they fail. (They tend to just hang.)

Recently we tried the assert() syntax. This appears to return a new promise (unless an error message is specified ??? - in which case it throws an exception). This new promise is rejected if the assertion fails.

Should these do the same thing? Is there a way to force expect() to return a new promise that is resolved or rejected based on the outcome of the expectation?

does not work with chained promises

Chained promises like this does not work:

fn = path.join __dirname,'../../text.txt'
someText = 'Too legit, too legit to quit.'
p = Q.nfcall fs.writeFile,fn,someText
p.then( ->
  Q.nfcall fs.readFile,fn
).then((data) ->
  assert data.toString() is someText        #just be ignored
,(err) ->
  console.log err   
).done()

assert.isFulfilled p

I have to change test case as follows to make it run:

fn = path.join __dirname,'../../text.txt'
someText = 'Too legit, too legit to quit.'
p = Q.nfcall fs.writeFile,fn,someText
p2 = p.then( ->
  Q.nfcall fs.readFile,fn
)
p2.then((data) ->
  assert data.toString() is someText 
,(err) ->
  console.log err   
).done()

assert.isFulfilled p
assert.isFulfilled p2 

Assert rejection reason should support non-Error objects

expect(promise).to.be.rejected.with() seems to support only cases where the rejections reason is an object with a message property. I assume this is so in order to support the use-case of rejecting with an Error object.

Why not support custom rejection reasons?

Add an error for web driver promises

People keep trying to use Chai as Promised with them, but they are not Promises/A+ compliant and thus do not work. We should throw immediately when they show up, similarly to how we do for jQuery.

Chai-As-Promised is eating assertion errors

I'm using chai-as-promised + mocha for writing some selenium-webdriver tests. Since webdriver extensively uses promises, I imagined it would be better if I used chai-as-promised for those type of tests.

The problem is that when the tests fail, the error is not being caught properly by mocha, and it just fails without outputting anything.

Sample code:

it 'tests log', (next) ->
  log = @client.findElement(By.css("..."))
  Q.all([
    expect(log.getText()).to.eventually.equal("My Text")
    expect(log.findElement(By.css(".image")).getAttribute('src'))
      .to.eventually.equal("/test.png")
  ]).should.notify(next)

According to documented behaviour, chai-as-promised should pass the errors along to mocha when the expectation fails. Right?

As a variation,

I've also tried these, but to no avail:

2nd Try (same, no error on failure)

it 'tests log', (next) ->
  log = @client.findElement(By.css("..."))
  Q.all([
    expect(log.getText()).to.eventually.equal("My Text")
    expect(log.findElement(By.css(".image")).getAttribute('src'))
      .to.eventually.equal("/test.png")
  ]).should.notify(next)

3rd Try (same, no error shown on failure)

it 'tests log', (next) ->
  log = @client.findElement(By.css("..."))
  expect(log.getText()).to.eventually.equal("My Text")
  .then ->
     expect(log.findElement(By.css(".image")).getAttribute('src'))
       .to.eventually.equal("/test.png").should.notify(next)

4th try (DOES NOT EVEN PASS)

it 'tests log', (next) ->            
  log = @client.findElement(By.css("..."))
  Q.all([
    expect(log.getText()).to.eventually.equal("My Text")
    expect(log.findElement(By.css(".image")).getAttribute('src'))
      .to.eventually.equal("/test.png")
  ])
  .then ->
    next()
  .fail (err) ->
    console.log(err)
  .done()

Right now, I'm using this to show the assertion errors. It's a nasty looking workaround. Can you please help?

it 'tests log', (next) ->
  log = @client.findElement(By.css("..."))
  Q.all([
    expect(log.getText()).to.eventually.equal("My Text")
    expect(log.findElement(By.css(".image")).getAttribute('src'))
      .to.eventually.equal("/test.png")
  ]).should.notify((err) ->
      console.log err if err   # <-- this line does the trick
      next(err)
  )

Assertion does not fail if promise is rejected without reason

In the following code, the two test cases should fail. However, they pass.

var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var assert = chai.assert;
var Q = require('q');

describe('Chai-test', function () {

  it('Should fail if rejected with no value ', function () {
    var promise = Q.reject();
    return assert.becomes(promise,2);
  });

  it('Should fail if rejected with no value (eventually.equal)', function () {
    var promise = Q.reject();
    return assert.eventually.equal(promise,2);
  });
});

Unhandled rejection reason

My unit test is passing even though the promise gets rejected.
return Q.all([ promise.should.eventually.be.an('Array'), promise.should.eventually.have.property('length', 4) ]);
In my logs I am getting the following error:
Unhandled rejection reason: expected [ Array(3) ] to have a property 'length' of 4, but got 3
Mocha and Chai as Promised seems to work for other tests, but what am I potentially doing wrong here?

Does not work with jQuery Promises

You currently can't use chai-as-promised with jQuery's Deferred Promises. Specifically, the rejected assertions don't returned a fulfilled promise and the following will fail on the second assert:

var assertPromise = assert.isRejected(jQuery.Deferred().reject(new Error("fail!")));
assert.isFulfilled(assertPromise, "isRejected returns fulfilled promise");

The reason for this is that unlike the CommonJS Promises/A specification, jQuery's then() method returns the original promise rather than a new promise.

jQuery has a pipe() method which is similar to the Promises/A's then(). Changing the rejectedAsserter to use the following makes it work correctly for jQuery.

var transformedPromise = Object.create(this.obj.pipe(onOriginalFulfilled, onOriginalRejected));

It would be great if chai-as-promised were aware of jQuery's behavior or if the creation of the transformedPromise was pluggable s.t. jQuery tests could implement it using pipe().

Expected are not working on failing test

Using Mocha, this test:

var deferred =  q.defer();

expect(deferred.promise).to.eventually
    .equal('hello')
    .and.notify(done);

deferred.resolve('goodbye');

Will fail as expected, but the failure message is:

actual   expected
{}       "hello"

(well kinda - I doctored the output so it would make sense without color support)

Testing RSVP.Promises

Howdy! In writing some tests for code using https://github.com/tildeio/rsvp.js, we've run into unexpected behavior. The fulfilled assertion helper is always passing, regardless of if the promise is actually rejected.

Promises in RSVP are deferred in different ways depending on your env. In Node, they are deferred with nextTick. In Firefox and Webkit with MutationObserver and a disconnected DOM node. In all other envs with setTimeout. See https://github.com/tildeio/rsvp.js/blob/master/lib/rsvp.js#L3 for detail.

This deferral means sinon's normal fake timers don't force the code to be synchronous. I was hoping this is the problem chai-as-promised was addressing. Here is a fiddle of chai-as-promised allowing an assertion that should be false:

http://jsfiddle.net/TqA5R/9/

fulfill a promise then navigate to a url

I am able to navigate to a url before a test but not during. this simple example locks up consistently for me after 'getting google'....

it('should fulfill a promise then navigate to a url', function() {
var myPromise = function() {
var defer = q.defer();
defer.resolve();
return defer.promise;
};
return myPromise().then(function() {
console.log('getting google');
return browser.driver.get('http://www.google.com/');
}).then(function() {
console.log('loaded google!');
});
});

not sure if this is a bug or i am using it wrong.

TypeError: Chai as Promised is incompatible with jQuery's so-called “promises.” Sorry!

I came acros this error message when writing test for jQuery defers. When I bypassed the sniffing of 'chai-as-promised' by def.pipe = null

the test failed as expected and passed as expected. So the matcher tested exactly the behavior I was looking for. I could not find any comments why it is incompatible with jQuery defers.

Why is this check here and why are jquery defers not supported?

How to use expect for notify?

Could this example be rewritten with expect instead of should?

it("should change the state", function (done) {
    otherState.should.equal("before");
    promise.should.be.fulfilled.then(function () {
        otherState.should.equal("after");
    }).should.notify(done);
});

Assertion passing for message when it's not exact match

Hi, I was writing a test where I'm expecting a string to be returned from a rejected promise.

For example, we have an expected result like:

before(function () {
    var data = 'some error message different';

    sinon.stub(myModule, 'myMethod').returns(new RSVP.Promise(function (resolve, reject) {
        reject(data);
    }));

    result = myService.go(input);
});

it('Should return an error message', function (done) {
    expect(result).to.eventually.be.rejectedWith('some error message').notify(done);
});

My assertion is passing since the rejectWith method is using indexOf() for strings and not a value comparison. Was this intentional or is this a bug?

Cannot read property 'be' of undefined

I'm trying to use chai-as-promised. However I always get errors on the be property because should is undefined. I'm using q as promise library.

Part of testcase:

'use strict';

var sinon = require('sinon'),
    sinonChai = require('sinon-chai'),
    chaiAsPromised = require("chai-as-promised"),
    chai = require('chai'),
    should = chai.should(),
    expect = chai.expect,
    config = require('./../lib/modules/configuration'),
    Configuration = config.Configuration;

chai.Assertion.includeStack = true;
chai.use(chaiAsPromised);
chai.use(sinonChai);

beforeEach(function() {
    module.sinon = sinon.sandbox.create();
});

afterEach(function(){
    module.sinon.restore();
});

describe('loading a non existing configuration', function () {
    var configPromise;

    before(function () {
        var configuration = new Configuration('failing/path/');
        configPromise = configuration.load();
    });

    it('should result in an error', function () {
        //this succeeds
        configPromise.then(null, function (err) {
            expect(err).to.exist;
        });
        //this fails with the error 
        configPromise.should.be.rejected.and.notify(done);
    });

    after(function () {
        config.reset();
    });
});

The error:

TypeError: Cannot read property 'be' of undefined
      at Context.<anonymous> (/myproj/spec/configuration.spec.js:25:29)
      at Test.Runnable.run (/myproj/node_modules/grunt-mocha-chai-sinon/node_modules/mocha/lib/runnable.js:211:32)
      at Runner.runTest (/myproj/node_modules/grunt-mocha-chai-sinon/node_modules/mocha/lib/runner.js:358:10)
      at /myproj/node_modules/grunt-mocha-chai-sinon/node_modules/mocha/lib/runner.js:404:12
      at next (/myproj/node_modules/grunt-mocha-chai-sinon/node_modules/mocha/lib/runner.js:284:14)
      at /myproj/node_modules/grunt-mocha-chai-sinon/node_modules/mocha/lib/runner.js:293:7
      at next (/myproj/node_modules/grunt-mocha-chai-sinon/node_modules/mocha/lib/runner.js:237:23)
      at Object._onImmediate (/myproj/node_modules/grunt-mocha-chai-sinon/node_modules/mocha/lib/runner.js:261:5)
      at processImmediate [as _immediateCallback] (timers.js:330:15)

Is `notify` always required?

Sorry, this is probably just a case of me not entirely understanding how to use this correctly, but I've noticed that this library doesn't seem to work without still using the done function and chaining notify, even on the latest version of mocha which supposedly has promise support. Example:

describe 'test', ->

  it 'works without notify', ->
    W.resolve('foo').should.eventually.equal('bar')

  # => this test passes

  it 'works with notify', (done) ->
    W.resolve('foo').should.eventually.equal('bar').notify(done)

  # => this one fails

Here I'm using when.js to create the resolved promises.

Relevant versions of dependencies:

And finally, if it helps, the full project that I used to produce this test case: http://cl.ly/V2Et

rejected not available in konacha command line?

When I have the following test:

it 'rejects the promise', (done) ->
  def = Q.defer()
  def.reject('raise error')
  def.promise.should.be.rejected.and.notify(done)

When I run the suite using rake konacha:run the test fails, stating "TypeError: 'undefined' is not a function". When I run the test in the browser, it passes.

I don't know where in the test it fails but it looks like the keyword rejected has something to do with it. In a test where I test should.become(@something) it passes in the CLI and in the browser.

So chai-as-promised seems to be loaded correctly.

Any hints what the cause of this failure on the CLI could be?

Using Sinon restore

Hi,

Not really an issue, but more so a question - apologies if not appropriate here (maybe should be on stackoverflow).

I've just started to implement chai-as-promised in a few places in my mocha test suites and it's working out very well.

I have this case in a few tests where I need to restore a sinon stub.

I've tinkered a little but I'm not sure if chai-as-promises can handle this use case?

utilsArticles.count(req)
  .then(function () {
    // no success
  },
  function (err) {
    err.should.match(/unable/);
  })
  .finally(function () {
    mongoose.Model.count.restore();
    done();
  })
  .done();

My code style preference is to use .done() unless I need to return and I also
put the mocha done() call in all promise tests in finally() just so it's uniform.

Am I able to use chai-as-promised in the above test or best to leave this type in the more verbose style?

Thanks!

provide a way to enrich/customize output promise in 4.0

I was using chai-as-promised to do some chaining like this:

browser
  .init({browserName:'chrome'})
  .get("http://admc.io/wd/test-pages/guinea-pig.html")
  .title()
    .should.become('I am a page title - Sauce Labs')
  .elementById('i am a link')
  .click()
  .eval("window.location.href")
    .should.eventually.include('guinea-pig2')
  .fin(function() { return browser.quit(); })
  .done();

(see wd project)

It was working fine before 4.0, cause the input and output promise were the same, but now the chain broken cause all there is is a then method.

Now I understand why the 4.0 rewrite is simpler, but it would be good to be able to enrich the output. For example if you are using Q promise, and you cannot do something like this, it does not feel right.

var promise = Q.fcall(function() {return "1234";});
promise.should.become('1234').done();

extending object.should with eventually does not work

I have [email protected] and [email protected] installed via npm.

I'm using chai-as-promised under mocha in protractor with the following preamble:

var chai = require('chai');
global.expect = chai.expect;
global.should = chai.should();
chai.use(require('chai-as-promised'));

The following type of asserts pass:

should.not.exist(null)
expect("foo").to.be.eql("foo")
"bar".should.be.eql("bar")
var title = browser.getTitle()
expect(title).to.eventually.be.eql(CONFIG.Title)

But the following assertion fails:

title.should.eventually.equal(CONFIG.Title)

With the following error:

 TypeError: Cannot read property 'eventually' of undefined

This is very strange, as Object.prototype.should exists and object.should... assert style works - but not with promises.

the title promise looks like this:

{
  then: [Function: then],
  cancel: [Function: cancel],
  isPending: [Function: isPending]
}

Rewrite with a different strategy for better compatibility

Spurred by the following snipppets from emails from @RubenVerborgh:


The following should work:

Q.resolve([ { a: 1 }, { b: 2 } ]).should.eventually
    .contain.something.that.deep.equals({ a: 1 });

but it doesn’t, and fails with the following message:

expected [ { a: 1 }, { b: 2 } ] to deeply equal { a: 1 }

This indicates that Chai as Promised somehow bypasses Chai Things, because the expected error message in case of failure is:

expected an element of [ { a: 1 }, { b: 2 } ] to deeply equal { a: 2 }

In other words, Chai as Promised acts like the assertion is

Q.resolve([ { a: 1 }, { b: 2 } ]).should.eventually.deep.equal({ a: 1 })

which of course fails.

Clearly, one of our two libraries is failing, and seen your amount of experience with Chai and promises, it’s probably mine :-) However, debugging tells me that Chai as Promised completely skips my promise property.

Could you help me shed some light on this? I’ve created a gist for experimenting.

Probably the problems stem from the fact that both libraries are using a similar mechanism, e.g., providing modified versions of the basic assertion methods. I think that Chai as Promised takes it a little too far by also (and thus again) modifying the methods returned by my promise object, but that’s just my two cents.


After a night’s sleep, I realized that rewriting Chai Things to function using flags rather than by returning modified Assertion objects could change things. And it did.

What didn’t work with the previous version now works as it should.

This leaves me with two (minor) open questions:

  1. Would there be still something to fix in Chai as Promised so that it is compatible with libraries that also return modified Assertion objects? I think the problem is in recursively calling makeAssertionPromise, which changes the chained method to the CaP-overriden version of the original Assertion object every time (as opposed to modifying the methods of the currently returned assertion object).
  2. Would it be possible to rewrite Chai as Promised to also work on a flag-based way?

This would likely also help with performance per #14.

Should chai-as-promised work with arrays?

I'm new to chai, but I'm trying to assert an array will eventually have a thing in it...
ex:
(mocha btw)

describe("test", function(){
    it("will work with a promise", function(done){

        var thing = [1, 2, "the];
         //fails:
         expect(thing).to.eventually.contain(2);
         //also fails:
         expect(thing).to.eventually.contain(2).notify(done);
         //passes (because this isn't async)
         expect(thing).to.contain(2);
    });

    it("promises test", function(done){
         var asyncthing = setTimeout(function(done){
              return ["another thing!", "word"];
         }, 500);
         expect(asyncthing).to.eventually.contain("word");
    });



});

deep.equal problems

In chai,
actual.should.deep.equal(['A', 'B'])
works as expected,
but in chai-as-promised,
actualPromise.should.eventually.deep.equal(['A', 'B'])
is never rejected

Is this a bug?

Thanks for writing chai-as-promised. With mocha-as-promised, it has simplified most of the tests I write.

isRejected not behaving as assert.throw

assert.throw accepts a string/regex to compare against the thrown error.message

eg:
assert.throw(function() {
throw new Error('error message');
}, 'this is not the error message', 'my msg');

fails with:
AssertionError: expected [Function] to throw error including 'this is not the error message' but got 'error message'

but:
var promise = new Promise(function(resolve, reject) {
reject(new Error('error message'));
});
return assert.isRejected(promise, 'this is not the error message', "optional msg");

does not fail

.eventually.equal did not set the `actual` property of the error object properly.

I and @llun discovered that when .eventually.equal assertion fail, the actual property is the promise, instead of the value returned by the promise.

Here is an example run in the REPL that demonstrates this problem:

> var chai = require('chai')
> chai.use(require('chai-as-promised'))
> var q = require('q')
> chai.should()

> var r = q.resolve(555).should.eventually.equal(3)
// this assertion should return a rejected promise

> var e
> r.fail(function(err) { e = err })

> e
{ message: 'expected 555 to equal 3',
  actual:
   { ... /* the promise object */ },
  expected: 3,
  operator: undefined,
  showDiff: true }

Here the actual property should be 555, instead of the promise.

Mocha used this property to show a diff between the actual/expected value. With this bug mocha reports the actual value as {} instead.

Consider adding `.should.notify(done)`

Right now I find myself doing a lot of this:

promise().then(function () {
   state.should.have.changed;
}).should.be.fulfilled.notify(done);

This is a bit awkward, both grammatically and because if state.should.have.changed fails, you get as your error "expected promise to be fulfilled but instead it was rejected with AssertionError." Ideally, you want the actual assertion error, just like if you had written synchronous code.

One solution, I think, would be some syntax like

promise().should.be.fulfilled.then(function () {
    state.should.have.changed;
}).should.notify(done);

This will give an "expected promise to be fulfilled" error only if the original promise was rejected, whereas, because it's not passing through the .should.be.fulfilled chain, the failure of state.should.have.changed would pass the original AssertionError up to the test runner, instead of wrapping it in a "should have been fulfilled but instead got AssertionError" message.

Unable to write custom promises

I'm using Mocha 1.6.0, Chai 1.3.0, and Chai-as-promised 3.2.3. Testing fails with TypeError: Object prototype may only be an Object or null even when I use a generic promise.

it('handles promises correctly', function(done) {
    expect({
        then: function (callback) {
            setTimeout(1000, callback);
        }
    }).to.eventually.be.fulfilled.and.notify(done);
});

Is this the correct way to create promises, or is this a problem with Chai-as-promised?

Test rejectionReason to have specific values

I wanted to write sth like:

getPromiseSomehow({oh:'this is bad arg').should.be.rejectedWith({
  status: 400,
  name: 'you messed up arg'
});

I'd like chai to check whether rejection reason has these guys with these values. Is that possible? If not, does it make sense?

Support for Promises/A progress callback

It would be useful if assertion could test the value of the progress callback as defined by CommonJS Promises/A.

E.g.

promise.should.updateProgress;
promise.should.updateProgress.eql("progress message");
assert.updatesProgress(promise);
assert.updatesProgress(promise, /progress message/);

Using when.js, a test would look like this:

var dfd = when.defer();
dfd.progress("progress message");
dfd.reject(new Error("Fail!"));
assert.updatesProgress(dfd.promise, /message/);

Asserting no errors are thrown after done()

Right now I'm trying to assert that a method handles a promise while swallowing any errors which might otherwise be raised when the method terminates the chain with done(). However, I'm unable to get the assertion in at the right time.

methodUnderTest = (promise) ->
  # This method is written in such a way that I'd like it to fail the test below
  promise.then(-> doSomething()).done()

it "does not raise an error on failure", (done) ->
  Q.onerror = (err) -> throw err
  sinon.spy(Q, "onerror")

  promise = Q.reject(false)
  methodUnderTest(promise)
  promise.should.be.rejected.then((->
    # I expect the following to fail, but it doesn't because it's run before Q.onerror gets called
    Q.onerror.should.not.have.been.called
  )).should.notify(done)

Is there a way to achieve this without using setTimeout or similar? I'm using Mocha via the Konacha test harness.

Working with asynchronously rejected promises.

I'm having trouble using this library on promises that are resolved asynchronously.

I assumed I could write something like this:

(where getPendingPromise() is a function that returns a promise that will resolve in a few milliseconds)

it('eventually fails with error', function(done) {
    var promise = getPendingPromise();
    promise.should.be.rejected.with('Some Error').and.notify(done);
});

In the code above, my promise is being resolved in the background, and I assumed this library would wait for resolution before making its assertion.

But what actually happens is it times out and done is never called.

If I change the code to add a timeout, then the test passes:

it('eventually fails with error', function(done) {
    var promise = getPendingPromise();
    setTimeout(function() {
        promise.should.be.rejected.with('Some Error').and.notify(done);
    }, 1000);
});

So I wonder if this library can only work with resolved promises? All the internal tests seem to be articulated with already-resolved promises (the fulfilledPromise() and rejectedPromise() factories generate them).

Any clarification would be appreciated.

expect(Q.promise).to.be.STATE does not seem to work

Using

I can run the following code, everything passes...and I'm quite confused.

var q = require('q'),
chai = require('chai'),
expect = chai.expect;

// dependencies & test lib configuration
chai.use(require('sinon-chai'));
chai.use(require('chai-as-promised'));

describe('pls work', function() {
  it('does not seem to work :(', function() {
    // this does not make sense to me
    expect(q.resolve('foo').thenReject('baz')).to.eventually.be.fulfilled;

    // same here
    expect(q.resolve('foo').thenResolve('baz')).to.eventually.be.rejected;

    var defer = q.defer();
    defer.reject('bar');
    defer.promise.done(null, function(err) {
      console.log('huh?');
      return false;
    });
    // state: rejected ...?
    console.log(defer.promise.inspect());
    // what am I doing wrong?
    expect(defer.promise).to.eventually.be.fulfilled;

    var blah = function(thing) {
      return thing;
    },
    obj = {
      bleh: function() {
        var defer = q.defer();
        q.resolve('foo')
        .then(blah)
        .then(function(woo) {
          defer.resolve(woo);
        })
        .done();
        return defer.promise;
      },
    };
    // I'm going nuts.
    expect(obj.bleh()).to.eventually.be.rejected;
  });
});

Is there anything that I am blatantly doing wrong?
Thanks in advance!

Tests failing with TypeError (Object has no method 'indexOf')

I just forked the repo in order to contribute some bugfixes, but the tests are failing for me right off the bat:

 TypeError: Object #<Object> has no method 'indexOf'
  at formatProperty (/Users/jason/Desktop/Dropbox/projects/chai-as-promised/node_modules/chai/lib/chai/utils/inspect.js:245:15)
  at /Users/jason/Desktop/Dropbox/projects/chai-as-promised/node_modules/chai/lib/chai/utils/inspect.js:158:14
  at Array.map (native)
  at formatValue (/Users/jason/Desktop/Dropbox/projects/chai-as-promised/node_modules/chai/lib/chai/utils/inspect.js:157:19)
  at Object.inspect (/Users/jason/Desktop/Dropbox/projects/chai-as-promised/node_modules/chai/lib/chai/utils/inspect.js:27:10)
  at onTransformedFulfilled (/Users/jason/Desktop/Dropbox/projects/chai-as-promised/lib/chai-as-promised.js:176:66)
  at _fulfilled (/Users/jason/Desktop/Dropbox/projects/chai-as-promised/node_modules/q/q.js:1020:54)
  at resolvedValue.promiseDispatch.done (/Users/jason/Desktop/Dropbox/projects/chai-as-promised/node_modules/q/q.js:1050:30)
  at makePromise.promise.promiseDispatch (/Users/jason/Desktop/Dropbox/projects/chai-as-promised/node_modules/q/q.js:642:13)
  at Object._onImmediate (/Users/jason/Desktop/Dropbox/projects/chai-as-promised/node_modules/q/q.js:521:44)
  at processImmediate [as _immediateCallback] (timers.js:330:15)

Somehow chai-as-promised is calling utils.inspect, and chai itself is choking on it. This is the code from chai-as-promised.js:173-177 that's part of the stack trace above:

if (Constructor) {
    assertion.assert(constructorIsGood(),
                "expected promise to be rejected with " + Constructor.prototype.name + " but it " +
                "was rejected with " + utils.inspect(rejectionReason));
}

Maybe a version incompatibility? If the tests are working for you all, can someone do an npm ls and paste the output? Here's what I've got for reference:

[email protected] /Users/jason/Desktop/Dropbox/projects/chai-as-promised
├── [email protected]
├── [email protected]
├─┬ [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └─┬ [email protected]
│   └── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ └─┬ [email protected]
│   ├── [email protected]
│   └── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ ├── [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ └── [email protected]
├─┬ [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├── [email protected]
│ ├─┬ [email protected]
│ │ └── [email protected]
│ ├── [email protected]
│ └── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]

Tests failures

Hi there,

I tried to work with chai-as-promised in order to open an issue related to this discussion. Before I could come up with anything on that topic, though, I tried to run the tests on my fresh clone and… some fail.

So, to recap:

$ git clone [email protected]:domenic/chai-as-promised.git
$ npm install .
$ npm test
>   ✖ 5 of 157 tests failed

All complain that Object [object Function] has no method 'apply' (yup…).

Is this supposed to be normal?

Regards,
Matti

`rejectedWith` not reading constructor names

With plain Chai, if I do:

expect(function () {
    throw new Error();
}).to.throw(Array);

I get:

AssertionError: expected [Function] to throw 'Array' but 'Error' was thrown

With Chai as Promised, if I do the equivalent:

var d = Q.defer();
d.reject(new Error());
return expect(d.promise).to.eventually.be.rejectedWith(Array);

I get:

AssertionError: expected promise to be rejected with undefined but it was rejected with [Error]

Chai as Promised thinks that the Array constructor’s name is undefined.

I had a brief look into this:

constructorName = (new Constructor()).name;
. Should we be reading the name property from the constructor function, instead of creating an instance and looking for a name on that? Alternatively the name could be read as instance.constructor.name. Here is what looks like the equivalent line but in Chai: https://github.com/chaijs/chai/blob/ab999d89171634e3b953765c6d0c8a7d454a4b59/lib/chai/core/assertions.js#L1054

Also, the error message format is slightly difference compared to standard Chai. Is this intentional, out of curiosity?

have.deep.property does not work

The following code snippet fails when run in Mocha:

chai = require 'chai'
cap = require 'chai-as-promised'
chai.use cap
P = require 'node-promise'
expect = require('chai').expect

it "should succeed but it fails", (done)->
  foo = ->
    def = P.defer()
    def.resolve a:b:2
    return def.promise
  expect(foo()).to.eventually.have.deep.property('a.b').notify(done)

I am getting "expected { a: { b: 2 } } to have a property 'a.b'". When I try

expect(a:b:2).to.have.deep.property("a.b")

it works fine.
Q promises (Kriskowal) fail as well.

TypeError: Cannot call method 'equal' of undefined

I'm trying to use mongoose promises and Q promises with chai-as-promised, but I keep on getting:

TypeError: Cannot call method 'equal' of undefined

Code:

var chai = require("chai")
  , chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);

...

describe('#getProject', function() {
  it ('should get Project', function(done) {
    return Project.findOne({_id:"000000000000000000000000"}).exec().should.eventually.equal("something");
  });
});

I feel like I'm missing something obvious, but haven't been able to find it.

Seems not to work with mongoose promises

I tried to test some of my mongoose models with mocha using the mongoose own promises (http://mongoosejs.com/docs/api.html#promise-js) with chai as promised.

If I try a simple

expect(myMongooseModel.findOne({}).exec()).to.eventually.exist.and.notify(done)

the mocha done() function is never called. I looked into it and it seems the fullfilment function (https://github.com/domenic/chai-as-promised/blob/master/lib/chai-as-promised.js#L244-L252) never gets called. Sorry I can not elaborate the error any deeper.

Q-lib promises work fine for me.

According to the source code mongoose promises correspond to the promises/a+ specification (https://github.com/LearnBoost/mongoose/blob/3.6.11/lib/promise.js#L171), so it should work.

test passed with wrong promise result

Hello
In the code below i'm testing promise result. As it can be seen from the example . The function named myFunction should reject promise for parameters 'a' and 's'. But when I run test I see the test passed in spite of that promise expected to be rejected. What was wrong in the code below ?
Thanks in advance

var chai = require('chai');
var sinon = require('sinon');
var util = require('util');
var Q = require('q');
var chaiAsPromised = require('chai-as-promised');
var assert = chai.assert;
chai.use(chaiAsPromised);

var myFunction = function(src,dest){
    var d = Q.defer();
    if (dest.indexOf('goodFile')>=0) {
        console.log('Good name!!!');
        d.resolve();
    }
    else {
        console.log('Bad name!!!');
        d.reject('Bad file name');
    }
    return d.promise;
};
describe('my tests',function(){
    it('test promise',function(done){
            var testPromise = myFunction('a','s');
            testPromise.finally(function(){
                assert.isFulfilled(testPromise, "optional message");    
                done();                     
            });                         
    }); 
});

Cannot get promiseFor working

The examples for expect refer to a method named promiseFor, does this exist?

If so where?

I would love to fix the examples but i can't for the life of me get it to work.

Cheers

Performance issue

Chai as Promised seems to perform very slow, even after a promise has been resolved:

describe "The promise", ->
  it "should eventually be string", ->
    Q.delay('x', 10).should.eventually.be.a('string')

  it "should eventually be string", ->
    Q.delay('x', 10).should.eventually.be.a('string') for x in [1..20

results in

  The array promise
    ✓ should eventually be string (48ms)
    ✓ should eventually be string (4763ms)

Full code in this gist.

I expect the second test to take more or less the same time as the first, since time should be dedicated to checking 200 times whether 'x 'is a string.

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.