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

feat: Add error handling for nonexistent file case with --file option #5086

Open
wants to merge 19 commits into
base: master
Choose a base branch
from

Conversation

khoaHyh
Copy link

@khoaHyh khoaHyh commented Jan 24, 2024

🔎 Overview

Fixes #4047

  • Added validation to the file(s) passed in to the --file option by resolving the argument to an absolute path and check for its existence. We now log a warning and exit if the file does not exist.
    • the errors for --require bubble up to the middleware in lib/cli/run.js. See here. This specific error occurs in lib/cli/run-helpers.js in the singleRun method when we load files asynchronously. Since we were lacking error handling there, the errors appeared the way they previously did before these changes.
  • Added a test to assert our changes

Screenshot from 2024-02-07 00-34-30

- added error handling when using the --file flag to do it the way
  --require does
- added a test to assert that we throw the same type of error
Copy link

linux-foundation-easycla bot commented Jan 24, 2024

CLA Signed

The committers listed above are authorized under a signed CLA.

@khoaHyh khoaHyh marked this pull request as ready for review January 24, 2024 06:57
@khoaHyh khoaHyh changed the title ISSUE #4047: Add error handling for nonexistent file case with --file option feat: Add error handling for nonexistent file case with --file option Jan 24, 2024
- require.resolve() by Node.js follows a Node.js module resolution algo
  which includes checking if the resolved path actually exists on the
  file system.
@coveralls
Copy link

coveralls commented Feb 7, 2024

Coverage Status

coverage: 94.347% (+0.01%) from 94.335%
when pulling f8aedff on khoaHyh:issue/4047
into 3345eff on mochajs:master.

Copy link
Member

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

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

Cool! 😎

Requesting changes on adding a bit more testing. But also let's talk about the direction a bit - I feel nervous adding in process.exit/throws deep within code. Thanks for sending!

Copy link
Member

Choose a reason for hiding this comment

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

[Testing] Since the implementation uses require.resolve, I think it'd be good to make sure there is at least one test each for CJS and ESM. Using module-specific APIs such as require or import scares me, even for ones that should always work... Thoughts?

Copy link
Author

@khoaHyh khoaHyh Mar 10, 2024

Choose a reason for hiding this comment

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

I thought to use require.resolve here because the original stack trace showed require being used. Alternatively, I could use fs.existsSync like how our --require flag does it. If we do go down the route of using require.resolve I agree with adding one test for each CJS and ESM.

Copy link
Author

@khoaHyh khoaHyh Mar 25, 2024

Choose a reason for hiding this comment

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

I removed the use of require.resolve and instead opted to use the path and fs modules to check for file existence instead.

Copy link
Member

Choose a reason for hiding this comment

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

Wait, require.resolve is a good thing to use though! If someone is in CJS mode and passes path/to/place they'll expect that both path/to/place.js and path/to/place/index.js would work. Not to mention .js vs .cjs shenanigans. Right?

I just meant to ask for more testing, not a refactor.

Copy link
Author

Choose a reason for hiding this comment

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

Oops I misunderstood, yeah that makes sense. I'll add the tests and revert back to using require.resolve.

Copy link
Author

Choose a reason for hiding this comment

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

Before I commit my changes, what do we think of this output?

image

Copy link
Member

Choose a reason for hiding this comment

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

That looks pretty good to me!

Though, would it be a lot of work to reduce it to two lines? That screenshot has two saying nothing found and one mentioning the absolutePath. I wonder if it would be cleaner for users to reduce down to, say...

No test file(s) found with the given pattern(s), exiting with code 1
  absolutePath: /home/ka9/dev/mocha/nonexistent.js

...if that's a lot of work to reorder to, then I'd say let's treat that as a followup issue and not a blocker here.

Copy link
Author

Choose a reason for hiding this comment

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

I reduced it to two lines but I kept the absolute path on the same line as the pattern statement in case there are multiple files passed in. Here the --file argument is treated as an array when the values are comma-separated.

image

Copy link
Member

Choose a reason for hiding this comment

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

Beautiful! I'm happy with that. Nice 🔥

Copy link
Author

Choose a reason for hiding this comment

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

Pushed changes and added tests for esm + cjs!

`${symbols.warning} Warning: Cannot find test file "${file}" at the path "${fileAbsolutePath}"`
);
console.warn(`${warningMessage}\n`);
process.exit(1);
Copy link
Member

Choose a reason for hiding this comment

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

[Refactor] I don't love having a process.exit deep within code. n/no-process-exit has good reasoning around it.

It's also not really a "warning" yellow if the whole thing exits. At that point it's an error (red).

I think a more future-facing, approach would be either:

  • (less work, but still partially flawed) have this function throw a special error that any higher-up handler(s) know how to log nicely
  • (more work, but more understandable long-term) have this function return an object explaining its results, and have higher-up handler(s) work nicely with it

I haven't looked deeply enough to know whether either or both is feasible. Before we go into a whole deep dive, what do you think about this direction?

Copy link
Author

Choose a reason for hiding this comment

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

Good to learn about the no-process-exit, thanks! I think the latter where we return an object and having the higher-up handler(s) deal with it sounds like the approach here. The error wasn't handled in run-helpers so maybe we can add something so that bubbles up to run.js like the other methods?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah that approach sounds like a good one to try!

@JoshuaKGoldberg JoshuaKGoldberg added the status: waiting for author waiting on response from OP - more information needed label Mar 4, 2024
@@ -30,7 +32,7 @@ module.exports = ({
sort,
spec
} = {}) => {
const unmatched = [];
const unmatchedSpecFiles = [];
Copy link
Author

Choose a reason for hiding this comment

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

Renamed variable for semantic clarity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting for author waiting on response from OP - more information needed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

🚀 Feature: Better error for --file option with non-existent file
3 participants