Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw meaningful error stubbing ECMAScript Module #1715

Merged
merged 4 commits into from Mar 5, 2018

Conversation

fatso83
Copy link
Contributor

@fatso83 fatso83 commented Feb 28, 2018

Purpose (TL;DR)

Throw a meaningful error when encountering ES Modules in sinon.stub()

Background

Ref #1711 and #1623 for background.

This feature is simply about throwing a meaningful error, instead of one that tells the user that the property cannot be deleted. As the exports from ECMAScript modules are immutable once created we
cannot do anything about them from the view of Sinon, so it's preferable to simply tell the user that when we can.

This should not affect transpiled code (say, using Babel), as the resulting modules are no longer true ECMAScript modules (but rather some form of CommonJS module).

How to verify - mandatory

  1. Check out this branch
  2. npm install
  3. npm run test-es-module (included in npm test)

Checklist for author

  • npm run lint passes
  • References to standard library functions are cached.

typeof Symbol !== "undefined" &&
object[Symbol.toStringTag] === "Module"
) {
throw new TypeError("No support for stubbing ES Modules");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sound a bit like "Sinon does not have this feature", but really, it's impossible. Maybe "ES modules cannot be stubbed"?

Copy link
Member

@mroderick mroderick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice PR!

Only a few comments from me

const {assert} = referee;

describe("Explicit lack of support for stubbing Modules", function() {
it("should give a proper error message for modules without default export", function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any export (or rather import) would be read-only, wouldn't it? This seems to suggest that read-only only applies to default exports.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of adding some additional tests, but couldn't be bothered at that time of the night :-) I added some more tests now, and as you can see, it's only-halfway true. You can actually stub some forms of exports. Although you can't modify the exported properties, one can modify the objects that has been exported (check the content of the tests), so one can modify the props of export default { a: 1, b: 2 }, just not point default to another object.

describe("Explicit lack of support for stubbing Modules", function() {
it("should give a proper error message for modules without default export", function() {
assert.exception(function() {
sinon.stub(aModule, "anExport").returns(400);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have the same problem for spies, won't we?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Pushed a fix for this.

@@ -18,6 +18,14 @@ function stub(object, property) {
throw new TypeError("stub(obj, 'meth', fn) has been removed, see documentation");
}

if (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to move this into a function, so it can be re-used (upcoming sinon@next has a new replace method).

Copy link
Contributor Author

@fatso83 fatso83 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All comments addressed and more tests added!



  sinon.stub()
    Modules with objects as their default export
      ✓ should NOT result in error
      ✓ should spy/stub an exported function
    Modules without default export
      ✓ should give a proper error message
    Modules that exports a function as their default export
      ✓ should not be possible to spy/stub the default export using a wrapper for the exports

  sinon.spy()
    Modules with objects as their default export
      ✓ should NOT result in error
      ✓ should spy/stub an exported function
    Modules without default export
      ✓ should give a proper error message
    Modules that exports a function as their default export
      ✓ should not be possible to spy/stub the default export using a wrapper for the exports


  8 passing (17ms)

describe("Explicit lack of support for stubbing Modules", function() {
it("should give a proper error message for modules without default export", function() {
assert.exception(function() {
sinon.stub(aModule, "anExport").returns(400);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Pushed a fix for this.

const {assert} = referee;

describe("Explicit lack of support for stubbing Modules", function() {
it("should give a proper error message for modules without default export", function() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of adding some additional tests, but couldn't be bothered at that time of the night :-) I added some more tests now, and as you can see, it's only-halfway true. You can actually stub some forms of exports. Although you can't modify the exported properties, one can modify the objects that has been exported (check the content of the tests), so one can modify the props of export default { a: 1, b: 2 }, just not point default to another object.

Copy link
Member

@mantoni mantoni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is good to merge. Nice work 👍

Copy link
Member

@mroderick mroderick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple more comments from me

var stub;
var errorRegEx = /TypeError: ES Modules cannot be (stubbed|spied)/;

afterEach(function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems duplicated with the same afterEach inside the describe below ... is that intentional?


describe("sinon." + action + "()", function(){
describe("Modules with objects as their default export", function() {
afterEach(function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully ESLint complains about indentation here ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it did not, but I have now added a lint-staged script to catch this.

Ref #1711 and #1623 for background.

This feature is simply about throwing a meaningful error, instead
one that tells the user that the property cannot be deleted. As
the exports from ECMAScript modules are immutable once created we
cannot do anything about them from the view of Sinon, so it's
preferable to simply tell the user that when we can.

This should not affect transpiled code (say, using Babel), as the
resulting modules are no longer true ECMAScript modules (but rather
some form of CommonJS module).
Also fixes the linting issues and some feedback from the PR.
@fatso83 fatso83 force-pushed the sinon-es6-module-detection branch from 0d35d47 to 51cdafe Compare March 5, 2018 11:14
@fatso83 fatso83 merged commit d7fb7d5 into master Mar 5, 2018
@fatso83 fatso83 deleted the sinon-es6-module-detection branch March 5, 2018 11:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants