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

New rule: no-hooks-from-ancestor-modules (#93) #94

Merged
merged 2 commits into from
Feb 13, 2021

Conversation

raycohen
Copy link
Member

No description provided.

@coveralls
Copy link

coveralls commented Aug 10, 2020

Coverage Status

Coverage remained the same at 100.0% when pulling c80c183 on raycohen:no-hooks-from-ancestor-modules into b53470d on platinumazure:master.

@raycohen raycohen force-pushed the no-hooks-from-ancestor-modules branch from 2fdd644 to 3889d07 Compare August 10, 2020 13:43
Copy link
Collaborator

@platinumazure platinumazure left a comment

Choose a reason for hiding this comment

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

Hi @raycohen, thanks for contributing!

This is a good first pass, but there are a few things that could be improved:

First, the documentation's examples are a little confusing, because there are extra hook calls that aren't actually impacted by the rule-- but this could be confusing to the reader, who wants to know what lines will or won't warn based on how they invoke hooks. I've left some comments.

At a high level, just understand that it's more important to show the ESLint workings in this documentation, rather than show sensible/typical QUnit usage. (For example, of course an ancestor module that declares hooks would use them at the outer level before declaring the inner module-- but that's not important for the ESLint rule and it could just get in the way of the user trying to understand this rule.)

Also, this could use some more test cases. Please add some test cases involving:

  1. Use of multiple hooks in a single module level (including multiple uses of the same hook, such as multple beforeEach calls)
  2. Three levels of modules
  3. Anything else you can think of that would show the rule as being more robust 😄

Thanks again for contributing, I really appreciate it! Let me know if you have any questions on this review.

```js

QUnit.module("outer module", function(hooks) {
hooks.beforeEach(function() {});
Copy link
Collaborator

Choose a reason for hiding this comment

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

This line should not warn, so it might be good to simplify this example by removing this line (or maybe just adding a comment saying no warning would appear on this line). That way, the inner hooks.beforeEach is the only code that a reader would interpret as having a lint error.



QUnit.module("outer module", function(outerModuleHooks) {
outerModuleHooks.beforeEach(function() {});
Copy link
Collaborator

Choose a reason for hiding this comment

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

This line should not warn, so it might be good to simplify this example by removing this line (or maybe just adding a comment saying no warning would appear on this line). That way, the inner outerHooks.beforeEach is the only code that a reader would interpret as having a lint error.

outerModuleHooks.beforeEach(function() {});

QUnit.module("inner module", function(innerModuleHooks) {
innerModuleHooks.beforeEach(function() {});
Copy link
Collaborator

Choose a reason for hiding this comment

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

This line should not warn, so it might be good to simplify this example by removing this line (or maybe just adding a comment saying no warning would appear on this line). That way, the outerModuleHooks.beforeEach is the only code that a reader would interpret as having a lint error.

},
fixable: null,
messages: {
noHooksFromAncestorModules: "Do not use hooks from an ancestor module."
Copy link
Collaborator

Choose a reason for hiding this comment

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

This message is fine, but since you're passing a context, you might as well use it here!

I would suggest using the full text of the hook call (use context.getSourceCode().getText() on the callee node of the CallExpression) and use a message similar to the following: "{{hook}} is from an ancestor module."

What do you think?

"CallExpression:exit": function (node) {
if (utils.isModule(node.callee)) {
contextStack.pop();
hooksStack.pop();
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't look correct. It's possible that a module could use multiple hooks (e.g., beforeEach and afterEach), so having only one pop here probably won't always work.

I would suggest having another if branch checking to see if the CallExpression being exited is a hook, and popping the hooksStack at that point.

With the current code, you could write some tests involving both beforeEach and afterEach hooks in the same module. When you run those, you will probably see some weird behavior with regard to the lint message. You can then use those tests to be more confident that your state management is good. 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

I think naming is the issue here - I pushed updates where I tried to clarify what I am storing in the stack and when

@raycohen raycohen force-pushed the no-hooks-from-ancestor-modules branch 2 times, most recently from b231361 to 5e9cbde Compare August 16, 2020 18:04
@raycohen
Copy link
Member Author

@platinumazure thanks for the feedback, I took another pass. Let me know what you think

Copy link
Collaborator

@platinumazure platinumazure left a comment

Choose a reason for hiding this comment

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

Nice improvements! Just a few things left.

Regarding duplicating tests for module calls-- I think it's okay if you just have one or two tests that use module, rather than duplicating all of them. Thanks!

},
fixable: null,
messages: {
"noHooksFromAncestorModules": "Do not use hooks from an ancestor module. You called {{usedHookIdentifierName}}.{{hookName}} inside module({{containingModuleDescription}})."
Copy link
Collaborator

Choose a reason for hiding this comment

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

I love the greater level of detail here! ...Now, unfortunately, I need to ask if we can shorten the message. 😅

Let me know if you need help thinking of how you could shorten the message!

Copy link
Member Author

Choose a reason for hiding this comment

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

I could drop the "inside module(...)" portion, or are you looking for even shorter?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Even shorter might be better! If we want to mention the module's description, maybe we could do something like this?

Suggested change
"noHooksFromAncestorModules": "Do not use hooks from an ancestor module. You called {{usedHookIdentifierName}}.{{hookName}} inside module({{containingModuleDescription}})."
"noHooksFromAncestorModules": "Do not use `{{usedHookIdentifierName}}.{{hookName}}` from module({{containingModuleDescription}}) inside nested module."

Copy link
Member Author

Choose a reason for hiding this comment

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

I've shortened the message even more - since you'll see where the warning is, there's probably no extra value to the description of the containing module.

@platinumazure
Copy link
Collaborator

Friendly ping @raycohen, are you still interested in getting this landed? No rush, just checking 😄

@raycohen
Copy link
Member Author

Yes, just got back from parental leave so I will try to get to this

@raycohen raycohen force-pushed the no-hooks-from-ancestor-modules branch 5 times, most recently from 7ac9109 to 3139573 Compare February 5, 2021 03:06
@raycohen
Copy link
Member Author

raycohen commented Feb 5, 2021

Made some updates. I wasn't sure which config this should go in. I went with 2️⃣ as my guess.

// Helpers
//------------------------------------------------------------------------------

function createError({ invokedMethodName, expectedHookIdentifierName, usedHookIdentifierName, containingModuleDescription }) {
Copy link
Member

Choose a reason for hiding this comment

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

You can also add some plain unit tests for this function at the bottom of this file.

Copy link
Collaborator

Choose a reason for hiding this comment

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

For what it's worth, I don't think it's necessary to test a one-statement helper function in the test when it's pretty clear what it should do.

Copy link
Member

Choose a reason for hiding this comment

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

Oh true. My goal was to ensure the final error string looks right. But now I realized the string is not actually exposed here.

Copy link
Member Author

@raycohen raycohen Feb 7, 2021

Choose a reason for hiding this comment

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

I'll clean up the things I ended up not using

  • clean up unused strings

Sorry, something went wrong.

Copy link
Collaborator

@platinumazure platinumazure left a comment

Choose a reason for hiding this comment

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

Looks good so far. Thanks for helping to resolve the open questions!

Only real issue at this point is the inclusion of the rule in any configurations. We need to avoid modifying the shared configurations in semver-minor releases, which this PR would otherwise fall under. Please either remove the rule from the configurations, or let me know that you're okay with this being marked as a semver-major breaking PR. Thanks!

README.md Outdated
@@ -47,6 +47,7 @@ Each rule has emojis denoting:
| :two: | [no-global-expect](./docs/rules/no-global-expect.md) | disallow global expect|
| :two: | [no-global-module-test](./docs/rules/no-global-module-test.md) | disallow global module/test/asyncTest|
| :two: | [no-global-stop-start](./docs/rules/no-global-stop-start.md) | disallow global stop/start|
| :two: | [no-hooks-from-ancestor-modules](./docs/rules/no-hooks-from-ancestor-modules.md) | disallow the use of hooks from ancestor modules|
Copy link
Collaborator

Choose a reason for hiding this comment

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

As far as configs go, I think this makes sense to have in recommended and two (eventually-- see next paragraph).

We can't make changes to the exported configs until a major release per the semver policy. If you're okay with waiting until a major release for this rule to go in, that's fine, but I don't know when that will be.

I recommend not including this rule in a config in this PR; we can release this as a minor release, and then there should be an issue for v6 planning and we could make sure this gets added into recommended and two configs in the major release. Let me know what you think.

index.js Outdated
@@ -43,6 +43,7 @@ module.exports = {
"qunit/no-global-expect": "error",
"qunit/no-global-module-test": "error",
"qunit/no-global-stop-start": "error",
"qunit/no-hooks-from-ancestor-modules": "error",
Copy link
Collaborator

Choose a reason for hiding this comment

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

As noted in another comment, we can't make changes to the exported configs until a major release. Also, I think this makes sense to include in both "recommended" and "two" (in the next major release). But for this PR, please remove this line (unless you are okay with this going in the next major release).

Copy link
Member Author

Choose a reason for hiding this comment

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

updated!

// Helpers
//------------------------------------------------------------------------------

function createError({ invokedMethodName, expectedHookIdentifierName, usedHookIdentifierName, containingModuleDescription }) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

For what it's worth, I don't think it's necessary to test a one-statement helper function in the test when it's pretty clear what it should do.

@raycohen raycohen force-pushed the no-hooks-from-ancestor-modules branch from 3139573 to c0f316d Compare February 7, 2021 16:18
@raycohen raycohen force-pushed the no-hooks-from-ancestor-modules branch from c0f316d to b612727 Compare February 7, 2021 16:35
Copy link
Collaborator

@platinumazure platinumazure left a comment

Choose a reason for hiding this comment

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

Just one possible issue in the documentation examples, otherwise this looks ready to go! Thanks for all the hard work on this!

@platinumazure
Copy link
Collaborator

Also, feel free to just push new commits on this branch-- makes it easier to review just the most recent changes. No need to squash and force push as I can do that on merge. Thanks!

Co-authored-by: Kevin Partington <platinumazure@gmail.com>
Copy link
Collaborator

@platinumazure platinumazure left a comment

Choose a reason for hiding this comment

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

Looks awesome, thanks! I'll merge ASAP.

@platinumazure platinumazure merged commit 379df8f into qunitjs:master Feb 13, 2021
@platinumazure
Copy link
Collaborator

Thanks for your persistence on this one @raycohen!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants