Skip to content

Commit

Permalink
Fix proxies for property assertions (#205)
Browse files Browse the repository at this point in the history
This PR fixes Chai 4's proxy protection for property assertions. Without this fix, proxy protection works for method assertions and chainable method assertions, but not property assertions. The reason it doesn't work for property assertions is because the getters for property assertions incidentally de-proxify the assertion object before returning it. This PR re-proxifies those de-proxified assertion objects from property assertions before returning them.
  • Loading branch information
meeber authored and domenic committed Jul 2, 2017
1 parent a60bf92 commit 248810a
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 2 deletions.
9 changes: 7 additions & 2 deletions lib/chai-as-promised.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ let checkError = require("check-error");
module.exports = (chai, utils) => {
const Assertion = chai.Assertion;
const assert = chai.assert;
const proxify = utils.proxify;

// If we are using a version of Chai that has checkError on it,
// we want to use that version to be consistent. Otherwise, we use
Expand Down Expand Up @@ -36,6 +37,10 @@ module.exports = (chai, utils) => {
}
}

function proxifyIfSupported(assertion) {
return proxify === undefined ? assertion : proxify(assertion);
}

function method(name, asserter) {
utils.addMethod(Assertion.prototype, name, function () {
assertIsAboutPromise(this);
Expand All @@ -46,7 +51,7 @@ module.exports = (chai, utils) => {
function property(name, asserter) {
utils.addProperty(Assertion.prototype, name, function () {
assertIsAboutPromise(this);
return asserter.apply(this, arguments);
return proxifyIfSupported(asserter.apply(this, arguments));
});
}

Expand Down Expand Up @@ -272,7 +277,7 @@ module.exports = (chai, utils) => {
);
} else {
Assertion.overwriteProperty(getterName, originalGetter => function () {
return doAsserterAsyncAndAddThen(originalGetter, this);
return proxifyIfSupported(doAsserterAsyncAndAddThen(originalGetter, this));
});
}
});
Expand Down
79 changes: 79 additions & 0 deletions test/proxy-guard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"use strict";
const chai = require("chai");
const chaiAsPromised = require("..");

chai.should();
chai.use(chaiAsPromised);

function shouldGuard(fn, msg) {
fn.should.throw("Invalid Chai property: " + msg);
}

describe("Proxy guard", () => {
const number = 42;
const promise = Promise.resolve(42);

before(function () {
if (typeof Proxy === "undefined" || typeof Reflect === "undefined" || chai.util.proxify === undefined) {
/* eslint-disable no-invalid-this */
this.skip();
/* eslint-enable no-invalid-this */
}
});

it("should guard against invalid property following `.should`", () => {
shouldGuard(() => number.should.pizza, "pizza");
});

it("should guard against invalid property following overwritten language chain", () => {
shouldGuard(() => number.should.to.pizza, "pizza");
});

it("should guard against invalid property following overwritten property assertion", () => {
shouldGuard(() => number.should.ok.pizza, "pizza");
});

it("should guard against invalid property following uncalled overwritten method assertion", () => {
shouldGuard(() => number.should.equal.pizza, "equal.pizza. See docs");
});

it("should guard against invalid property following called overwritten method assertion", () => {
shouldGuard(() => number.should.equal(number).pizza, "pizza");
});

it("should guard against invalid property following uncalled overwritten chainable method assertion", () => {
shouldGuard(() => number.should.a.pizza, "pizza");
});

it("should guard against invalid property following called overwritten chainable method assertion", () => {
shouldGuard(() => number.should.a("number").pizza, "pizza");
});

it("should guard against invalid property following `.eventually`", () => {
shouldGuard(() => promise.should.eventually.pizza, "pizza");
});

it("should guard against invalid property following `.fulfilled`", () => {
shouldGuard(() => promise.should.fulfilled.pizza, "pizza");
});

it("should guard against invalid property following `.rejected`", () => {
shouldGuard(() => promise.should.rejected.pizza, "pizza");
});

it("should guard against invalid property following called `.rejectedWith`", () => {
shouldGuard(() => promise.should.rejectedWith(42).pizza, "pizza");
});

it("should guard against invalid property following uncalled `.rejectedWith`", () => {
shouldGuard(() => promise.should.rejectedWith.pizza, "rejectedWith.pizza. See docs");
});

it("should guard against invalid property following called `.become`", () => {
shouldGuard(() => promise.should.become(42).pizza, "pizza");
});

it("should guard against invalid property following uncalled `.become`", () => {
shouldGuard(() => promise.should.become.pizza, "become.pizza. See docs");
});
});

0 comments on commit 248810a

Please sign in to comment.