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

docs: mention rootDir changes when using projects #12871

Merged
merged 4 commits into from Sep 17, 2022
Merged

docs: mention rootDir changes when using projects #12871

merged 4 commits into from Sep 17, 2022

Conversation

kettanaito
Copy link
Contributor

Not sure this should be represented in the CHANGELOG. Let me know if it should.

Summary

The <rootDir> token changes its behavior when used with the projects option. I'm describing this at length in this issue. This pull request adds a mention to the projects section of the Configuration documentation that explains how <rootDir> will be evaluated.

Test plan

A change to the documentation, no change plan necessary.

@facebook-github-bot
Copy link
Contributor

Hi @kettanaito!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@fb.com. Thanks!

@mrazauskas
Copy link
Contributor

With the projects option enabled, Jest will copy the root-level configuration options to each individual child configuration during the test run, resolving its values in the child's context. This means that the <rootDir> string token will point to the child directory even if it's used in the root-level configuration, which is crucial to keep in mind when constructing paths.

Just a friendly note, I do understand that Jest internals is a deep dive.

Configuration is resolved / normalized before running test. It means that all project configs already have their rootDir resolved before the test run. I am only trying to figure out how to make this note more clear.

By the way, running jest --showConfig simply prints resolved configs without running tests. There you can see that <rootDir> tokens are already replaced.


I was thinking, perhaps an example would be helpful? A snippet from the issue is puzzling:

module.exports = {
  projects: ["<rootDir>/packages/*"],
  collectCoverageFrom: ["<rootDir>/src/*"],
};

Running this with jest --showConfig (here I left only collectCoverageFrom, projects, rootDir):

{
  "configs": [
    {
      "rootDir": "/Users/me/projects/x-jest-projects/packages/one",
    },
    {
      "rootDir": "/Users/me/projects/x-jest-projects/packages/two",
    }
  ],
  "globalConfig": {
    "collectCoverageFrom": ["src/*"],
    "projects": [
      "/Users/me/projects/x-jest-projects/packages/one",
      "/Users/me/projects/x-jest-projects/packages/two"
    ],
    "rootDir": "/Users/me/projects/x-jest-projects",
  }
}

Hm.. Looking at the issue I was expect to see collectCoverageFrom added to each project. What did I miss?


I tried to rework an example from the docs. A comment is added, but all is obvious without a comment, or?

module.exports = {
  projects: [
    {
      displayName: "lint",
      rootDir: "lint-project",
      runner: 'jest-runner-eslint',
      testMatch: ["<rootDir>/**/*.js"], // here `<rootDir>` points to `<cwd>/lint-project`
    },
  ],
  rootDir: "src",
};

One more attempt:

module.exports = {
  projects: [
    "packages/*",
  ],
  testMatch: ["<rootDir>/**/*.js"],
};

Resolved configs (did not expect to see default testMatch in projects, but it makes sense):

{
  "configs": [
    {
      "rootDir": "/Users/me/projects/x-jest-projects/packages/one",
      "testMatch": [
        "**/__tests__/**/*.[jt]s?(x)",
        "**/?(*.)+(spec|test).[tj]s?(x)"
      ],
    },
    {
      "rootDir": "/Users/me/projects/x-jest-projects/packages/two",
      "testMatch": [
        "**/__tests__/**/*.[jt]s?(x)",
        "**/?(*.)+(spec|test).[tj]s?(x)"
      ],
    }
  ],
  "globalConfig": {
    "projects": [
      "/Users/me/projects/x-jest-projects/packages/one",
      "/Users/me/projects/x-jest-projects/packages/two"
    ],
    "rootDir": "/Users/me/projects/x-jest-projects",
  },
}

Apologies for such long comment. I just try to understand what does it mean "Jest will copy the root-level configuration options to each individual child configuration". What did I miss?

@kettanaito
Copy link
Contributor Author

kettanaito commented May 24, 2022

Thanks for the quick look at this, @mrazauskas!

I believe it was Jest's source code where I found that when using projects, it copies all root-level config options to each child config. This may be too specific and, perhaps, unnecessary in the context of this change for the reader to understand what this note means. I'm okay with removing this implementation detail.

The confusion was around the <rootDir> tokens because the following config:

module.exports = {
  projects: ["<rootDir>/packages/*"],
  collectCoverageFrom: ["<rootDir>/src/*"],
};

Would not yield ./src/* as collectCoverageFrom when running a nested package test. <rootDir> would be places as-is (as a token) into packages/foo/jest.config.js and then evaluated, pointing to packages/foo/src/*. I find this behavior confusing since you're writing a root-level config where two options evaluate the same <rootDir> token differently:

  • projects indeed evaluate <rootDir> with the root (let's say CWD).
  • but sibling collectCoverageFrom in the same root-level config file suddenly resolves <rootDir> to the package where the test will be run.

The change proposed here clarifies the different <rootDir> behavior when the projects option is used.

I think a code coverage is a good example, as you want to limit the glob you gather coverage to something less permissive than **. I agree that adding a code example to illustrate this is a good idea but I wanted to double-check with the reviewers if it wouldn't blow this section out of proportion.

I can suggest the following example:

// jest.config.js
module.exports = {
  // Here, "<rootDir>" resolves to the actual root of the project.
  // In this case, it's CWD ("./").
  // This path is relative to the root.
  projects: ['<rootDir>/packages/*'],

  // In this option, however, "<rootDir>" resolves to
  // each individual child "/packages/*" path.
  // For example:
  // /packages/foo/src/**/*.js
  // This path is NOT relative to the root. 
  collectCoverageFrom: ['<rootDir>/src/**/*.js']
}

@mrazauskas
Copy link
Contributor

Thanks. I do follow (; Did you try running this with jest --showConfig with this config?

For me collectCoverageFrom prints only once (in globalConfig) and the value is ["src/**/*.js"]. (I was posting more verbose output above.)

The puzzle for me is: where do you see or get the /packages/foo/src/**/*.js paths which you mentioned (in the comment above collectCoverageFrom, for example)?

@facebook-github-bot
Copy link
Contributor

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@kettanaito
Copy link
Contributor Author

kettanaito commented May 25, 2022

I get it from the actual coverage logic. If you specify the path relative to the root, where the root-level config resides, you won't get any coverage. Nothing will fail, the coverage will simply be 0 as no files would exist at that path:

// ./jest.config.js
module.exports = {
  projects: ['<rootDir>/packages/*'],
  // Here I'm making a mistake thinking "rootDir" means the same
  // it does in the "projects" option above. Since it doesn't,
  // Jest will try to collect coverage from "packages/foo/packages/foo/src/**/*"
  // which doesn't exist. 
  collectCoverageFrom: ['<rootDir>/packages/foo/src/**/*']
}

It's not really an issue of what the config shows in the end but the overall developer's experience of handling two identical tokens (rootDir) in the same file that will eventually resolve to completely different things. In order words: developers shouldn't rely on --showConfig to understand how rootDir works only if you're using projects. For instance, I've been using Jest for more than 4 years and I've never heard of --showConfig 😃

@mrazauskas
Copy link
Contributor

mrazauskas commented May 25, 2022

Thanks again. Just to add --showConfig prints config which is resolved and frozen. It is not changed later in any other parts of the code.


After looking around, it seems like what you see is how rootDir is treated by collectCoverageFrom. First detail to note is that <rootDir> token is simply stripped / ignored in globs of this option (check #5524):

https://github.com/facebook/jest/blob/c5551c4cf456f2d7ab80ca5e3a926c4a59a4bd08/packages/jest-config/src/normalize.ts#L301

Running with --showConfig you can note this. Another interesting detail: coverage reporter is matching the patterns with the paths of files relative to a project config’s rootDir:

https://github.com/facebook/jest/blob/c5551c4cf456f2d7ab80ca5e3a926c4a59a4bd08/packages/jest-reporters/src/CoverageReporter.ts#L119-L122

Each project has its own 💎 implicit 💎 (or explicit) rootDir and this is what was not working for you.


How to fix this? Hm.. Looks like any changes in code would be breaking. It might be a better idea to add a note to documentation.

Take a look at --collectCoverageFrom in CLI options documentation: "A glob pattern relative to rootDir..." Perhaps a note like this could be added to collectCoverageFrom configuration option as well?

Copy link
Member

@SimenB SimenB left a comment

Choose a reason for hiding this comment

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

thanks! can you also update all versioned docs?

docs/Configuration.md Outdated Show resolved Hide resolved
@kettanaito
Copy link
Contributor Author

Each project has its own implicit (or explicit) rootDir and this is what was not working for you.

Unfortunately, that's not the case at all. My projects don't have their own rootDir set, and this behavior was not limited only to the collectCoverageFrom option. Jest really does copy all root level options to each individual config under projects. Perhaps library maintainers can confirm/deny that (I apologize, I'm reluctant to go through the source code at the moment). That's the main idea why I'm advocating to put this note under the "projects" option because it's that very option that affects how the <rootDir> token is treated in any other sibling options.

@kettanaito
Copy link
Contributor Author

Thanks for the review, @SimenB! I've rephrased the explanation a little to make it more concise. I've also updated all the versioned docs, although I'd appreciate your keen eye on whether this behavior applies to the previous versions of Jest. I've experienced it on Jest 27, I suspect Jest 28 has the same behavior.

@kettanaito kettanaito requested a review from SimenB May 26, 2022 15:13
@@ -707,6 +707,8 @@ The projects feature can also be used to run multiple configurations or multiple

_Note: When using multi-project runner, it's recommended to add a `displayName` for each project. This will show the `displayName` of a project next to its tests._

_Note: With the `projects` option enabled, Jest will copy the root-level configuration options to each individual child configuration during the test run, resolving its values in the child's context. This means that string tokens like `<rootDir>` will point to the *child's root directory* even if they are defined in the root-level configuration._
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've noticed Jest 27 versioned docs are not used special tags like :::note so I kept plain italics for consistency. Jest 26 docs, however, use those custom tags.

@kettanaito
Copy link
Contributor Author

Hey, @SimenB. Sorry for troubling you but could you please have another look at this change once you have a spare minute? Thanks!

Copy link
Member

@SimenB SimenB left a comment

Choose a reason for hiding this comment

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

thanks, and sorry for the radio silence!

Could you update this to latest main? (v28 has been combined to 28.x, there's a 29.0 now)

@kettanaito
Copy link
Contributor Author

Hey, @SimenB. Thanks for reaching out!

I've updated this branch against the latest main, the new hint is still present in every version of the docs (25-29). May I ask you for the final round of review on this? If the changes are good, feel free to merge them straight away.

Copy link
Member

@SimenB SimenB left a comment

Choose a reason for hiding this comment

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

thanks!

@SimenB SimenB changed the title docs: mention "rootDir" changes when using "projects" docs: mention rootDir changes when using projects Sep 17, 2022
@SimenB SimenB merged commit 264a116 into jestjs:main Sep 17, 2022
@kettanaito kettanaito deleted the docs/configuration-projects-rootdir branch September 17, 2022 11:01
@github-actions
Copy link

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Oct 18, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Docs]: Mention "rootDir" points to a different directory when using "projects"
4 participants