Skip to content

JS-2311 S2187: False positive on Angular-style "environment.<env>.ts" config files - #7815

Open
oleksandr-selehenenko-sonarsource wants to merge 9 commits into
masterfrom
sasha/JS-2311
Open

JS-2311 S2187: False positive on Angular-style "environment.<env>.ts" config files#7815
oleksandr-selehenenko-sonarsource wants to merge 9 commits into
masterfrom
sasha/JS-2311

Conversation

@oleksandr-selehenenko-sonarsource

@oleksandr-selehenenko-sonarsource oleksandr-selehenenko-sonarsource commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Part of JS-2311


Summary by Gitar

  • Angular configuration fixes:
    • Excluded Angular-style environments/environment.*.ts config files from being flagged as test files by isTestFile and isTestRelatedFile.
    • Added dependency detection for @angular/core to restrict the environment config exclusion strictly to Angular projects.

This will update automatically on new commits.

@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Aug 25, 2026

Copy link
Copy Markdown

JS-2311

Comment thread packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts Outdated
Comment thread packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts Outdated
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Ruling Report

No changes to ruling expected issues in this PR

@sonarqube-next

Copy link
Copy Markdown

@francois-mora-sonarsource francois-mora-sonarsource left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice catch on the FP, and thanks for the thorough path tests. I'd like one scoping change before merge, though.

The exception currently fires on the path shape alone, so any project with environments/environment.test.ts gets it, Angular or not. That matters more than usual here because the path is genuinely ambiguous rather than just coincidental: Angular's convention is environment.<env>.ts, so this FP only arises when an environment happens to be named test/spec/cy — which is also exactly what a real colocated test for an environments/environment.ts module looks like. The pattern alone can't distinguish them.

We already have the machinery to prove it's an Angular project: getDependenciesSanitizePaths(context) in helpers/dependency-manifests/dependencies.ts — the same helper S8783 uses for cypress. It reads dependencies/devDependencies/peerDependencies/optionalDependencies from the closest manifest and is cached.

Since isTestFile/isTestRelatedFile are pure path predicates, I'd keep them pure and pass the signal in as an optional argument defaulting to false:

export function isTestFile(filePath: string, extensions?: string[], isAngularProject = false) {
  return testFilePattern(extensions).test(filePath) && !(isAngularProject && isEnvironmentConfigFile(filePath));
}

S2187/rule.ts:123 then passes a small shared isAngularProject(context) helper — worth putting next to getReactVersion/getVueVersion so the dependency list isn't duplicated if S2925/S8959/S9162 want the same carve-out later.

The opt-in default also fixes a wider side effect. isTestRelatedFile is called from common/filter/filter-path.ts:110, which does MAIN/TEST classification. When sonar.tests isn't configured, that filename heuristic is the only thing marking these files as TEST — so as written, src/environments/environment.test.ts stops being TEST and becomes MAIN if it's under sonar.sources (or is dropped from analysis otherwise). For a genuine Angular config file that's arguably the right outcome, but for a real colocated test in a non-Angular project it means S2925/S8959/S9162 silently stop applying and every main-code rule starts running on the file. That's a blast radius well beyond the S2187 fix, and worth a Peach baseline check either way. Defaulting the new argument to false leaves that path untouched.

Nit, non-blocking: the boolean gets computed before we know the path even matches environments/environment.*, so the dependency lookup runs on every invocation. It's cached, so almost certainly noise — just flagging it.

…etection

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts Outdated
Comment thread packages/analysis/src/jsts/rules/S2187/rule.ts Outdated
…-config shape

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
, S9162, S8959)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts Outdated
Comment thread packages/analysis/src/common/filter/filter-path.ts Outdated
Comment thread packages/analysis/src/jsts/rules/S2187/unit.test.ts Outdated
create(context: Rule.RuleContext) {
const { filename, settings } = context;
if (!isTestFile(filename, settings?.testFileExtensions as string[] | undefined)) {
if (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

rules should not have logic to classify scope of the files. This should go into the test file detection heuristic. Once a file is classified as a test by the heuristic, the rule selection in the linter will discard the test files

… rule test coverage, fix filter-path comment

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…p rules and test-file predicates pure

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gitar-bot

gitar-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 7 resolved / 8 findings

Fixes false positives on Angular-style environments/environment.*.ts config files by excluding them from test-file classification in Angular projects, with dependency detection for @angular/core to scope the exclusion appropriately. Consider clarifying the isAngularProject documentation: it currently states the check uses the "closest" manifest, but the implementation walks up to the repository root, which in monorepos could incorrectly treat environments/environment.test.ts files as Angular-related even when the immediate package has no Angular dependency.

💡 Quality: isAngularProject doc says "closest manifest" but scans up to baseDir

📄 packages/analysis/src/jsts/rules/helpers/dependency-manifests/dependencies.ts:158-172 📄 packages/analysis/src/jsts/rules/helpers/dependency-manifests/dependencies.ts:78-92 📄 packages/analysis/src/common/filter/filter-path.ts:154-167

isAngularProject is documented as deciding "based on the presence of @angular/core in the closest dependency manifest", but getDependencies returns the union of every manifest from the closest manifest dir up to topDir (getDependencyManifests(closestDependencyManifestDir, topDir, fs) walks each parent dir). Concretely, in a monorepo whose root package.json declares @angular/core while packages/api/ (its own package.json, no Angular) contains environments/environment.test.ts, the file is treated as Angular and its test-rule selection is carved out — the opposite of what the doc comment implies. Fix the comment (or restrict the check to the closest manifest) so the monorepo behaviour is explicit for future maintainers.

Align the doc comment with the aggregated (closest-to-topDir) lookup that getDependencies performs.
/**
 * Determines whether a file belongs to an Angular project, based on the presence of
 * `@angular/core` in the dependency manifests visible to the file: the closest manifest and
 * every manifest up to `topDir` (so a root manifest declaring Angular also marks nested
 * packages as Angular).
 * ...
 */
✅ 7 resolved
Bug: Environment-config exclusion missing from isTestRelatedFile

📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:45 📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:47-60 📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:69-71
The new ENVIRONMENT_CONFIG_FILE_PATTERN is only applied in isTestFile, so isTestRelatedFile (same file, unchanged) still reports src/environments/environment.test.ts as test-related. Concrete consequence: for a project that does not set sonar.tests, filter-path.ts:110 uses isTestRelatedFile as the heuristic, so the Angular env config is still classified as TEST scope — main-scope rules and metrics are skipped for it — and S2925/S8959/S9162 still activate on it, i.e. the same FP class the PR set out to fix persists one layer down. The file's own new doc comment ("are not test files") now contradicts isTestRelatedFile. Apply the exclusion in isTestRelatedFile too, while keeping files under __tests__/__mocks__ matched.

Edge Case: Exclusion is path-agnostic, hiding real environment.spec.ts tests

📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:45 📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:56-60 📄 packages/analysis/tests/jsts/rules/helpers/test-file-pattern.test.ts:65-79
ENVIRONMENT_CONFIG_FILE_PATTERN matches environment.(test|spec|cy).* in any directory, not just the Angular src/environments/ folder it is meant to cover. A genuine unit test for an environment.ts module — e.g. src/app/core/environment.spec.ts containing only a describe with no test cases — now makes isTestFile return false, so S2187/rule.ts:123-125 bails out and the "Add some tests to this file or delete it" issue is never raised (false negative). Narrow the pattern to require the conventional environments/ parent directory (and update the two added expectations for the bare environment.test.ts filename accordingly).

Performance: isAngularProject runs a manifest lookup for every linted file in S2187

📄 packages/analysis/src/jsts/rules/S2187/rule.ts:124-132 📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:69-78 📄 packages/analysis/src/jsts/rules/helpers/dependency-manifests/dependencies.ts:126-140 📄 packages/analysis/src/jsts/rules/helpers/dependency-manifests/dependencies.ts:218-220
isAngularProject(context) is passed as an eager argument, so every file S2187 creates a listener for — including the vast majority that are not test files — now performs normalizeToAbsolutePath on filename/cwd, a find-up manifest resolution and (when Deno inline deps are set) a full Map copy in withCurrentFileInlineDependencies, work that did not happen before this PR. Make the signal lazy so it is only computed for paths that actually look like an environment config file.

Bug: isTestRelatedFile Angular carve-out is never reached in production

📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:93-101
The new third parameter of isTestRelatedFile defaults to false and none of its four production callers passes it: S2925 (rule.ts:39), S9162 (rule.ts:72) and S8959 (rule.ts:54) all call isTestRelatedFile(context.filename, context.settings?.testFileExtensions) even though a Rule.RuleContext is available for isAngularProject(context), and filter-path.ts:110 has no context at all. So for an Angular src/environments/environment.test.ts the file is still treated as test-related everywhere except S2187 — the exclusion added to isTestRelatedFile is unreachable code and the behaviour of the two predicates is now inconsistent for the same path. Either wire isAngularProject(context) through the three rule call sites (and document why filter-path.ts intentionally keeps the old classification) or drop the parameter from isTestRelatedFile.

Edge Case: Angular carve-out misses environment.e2e.ts / .mock.ts markers

📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:38-46 📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:99-107 📄 packages/analysis/src/jsts/rules/S2925/rule.ts:40-46 📄 packages/analysis/src/jsts/rules/S8959/rule.ts:55-61 📄 packages/analysis/src/jsts/rules/S9162/rule.ts:73-79
This commit makes the Angular carve-out reachable through isTestRelatedFile (S2925/S8959/S9162), but ENVIRONMENT_CONFIG_FILE_PATTERN only recognises the test|spec|cy markers while testRelatedFilePattern also matches .e2e. and .mock.. So in an Angular project src/environments/environment.test.ts is carved out, yet src/environments/environment.e2e.ts (a common Angular fileReplacements target) and src/environments/environment.mock.ts are still classified test-related by exactly the three rules this commit wires — an inconsistency introduced by extending the carve-out to the test-related predicate without extending the marker set. Either add e2e|mock to the environment-config pattern (and cover them in tests/jsts/rules/helpers/test-file-pattern.test.ts) or document why those markers are deliberately excluded.

...and 2 more resolved from earlier reviews

🤖 Prompt for agents
Code Review: Fixes false positives on Angular-style `environments/environment.*.ts` config files by excluding them from test-file classification in Angular projects, with dependency detection for `@angular/core` to scope the exclusion appropriately. Consider clarifying the `isAngularProject` documentation: it currently states the check uses the "closest" manifest, but the implementation walks up to the repository root, which in monorepos could incorrectly treat `environments/environment.test.ts` files as Angular-related even when the immediate package has no Angular dependency.

1. 💡 Quality: isAngularProject doc says "closest manifest" but scans up to baseDir
   Files: packages/analysis/src/jsts/rules/helpers/dependency-manifests/dependencies.ts:158-172, packages/analysis/src/jsts/rules/helpers/dependency-manifests/dependencies.ts:78-92, packages/analysis/src/common/filter/filter-path.ts:154-167

   `isAngularProject` is documented as deciding "based on the presence of `@angular/core` in the closest dependency manifest", but `getDependencies` returns the union of every manifest from the closest manifest dir up to `topDir` (`getDependencyManifests(closestDependencyManifestDir, topDir, fs)` walks each parent dir). Concretely, in a monorepo whose root `package.json` declares `@angular/core` while `packages/api/` (its own package.json, no Angular) contains `environments/environment.test.ts`, the file is treated as Angular and its test-rule selection is carved out — the opposite of what the doc comment implies. Fix the comment (or restrict the check to the closest manifest) so the monorepo behaviour is explicit for future maintainers.

   Fix (Align the doc comment with the aggregated (closest-to-topDir) lookup that getDependencies performs.):
   /**
    * Determines whether a file belongs to an Angular project, based on the presence of
    * `@angular/core` in the dependency manifests visible to the file: the closest manifest and
    * every manifest up to `topDir` (so a root manifest declaring Angular also marks nested
    * packages as Angular).
    * ...
    */

Implementation Status ✅ 1 of 1 objectives covered
JS-2311 - 1 of 1 objectives covered

This PR fixes the S2187 false positive on Angular-style environment configuration files by carving them out of test-file classification in Angular projects.

✅ 1 covered here
  • ✅ Fix S2187 false positive on Angular-style environment.test.ts config files
Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

# Conflicts:
#	packages/analysis/src/common/filter/filter-path.ts
…ifest union

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gitar-bot

gitar-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 8 resolved / 8 findings

Fixes S2187 false positives on Angular-style environment.*.ts config files by moving the environment-config carve-out into the scope classifier, restricting it to Angular projects via @angular/core dependency detection, extending it to e2e/mock markers, and wiring the Angular signal through isTestRelatedFile call sites in S2925, S8959, and S9162. All findings resolved.

✅ 8 resolved
Bug: Environment-config exclusion missing from isTestRelatedFile

📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:45 📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:47-60 📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:69-71
The new ENVIRONMENT_CONFIG_FILE_PATTERN is only applied in isTestFile, so isTestRelatedFile (same file, unchanged) still reports src/environments/environment.test.ts as test-related. Concrete consequence: for a project that does not set sonar.tests, filter-path.ts:110 uses isTestRelatedFile as the heuristic, so the Angular env config is still classified as TEST scope — main-scope rules and metrics are skipped for it — and S2925/S8959/S9162 still activate on it, i.e. the same FP class the PR set out to fix persists one layer down. The file's own new doc comment ("are not test files") now contradicts isTestRelatedFile. Apply the exclusion in isTestRelatedFile too, while keeping files under __tests__/__mocks__ matched.

Edge Case: Exclusion is path-agnostic, hiding real environment.spec.ts tests

📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:45 📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:56-60 📄 packages/analysis/tests/jsts/rules/helpers/test-file-pattern.test.ts:65-79
ENVIRONMENT_CONFIG_FILE_PATTERN matches environment.(test|spec|cy).* in any directory, not just the Angular src/environments/ folder it is meant to cover. A genuine unit test for an environment.ts module — e.g. src/app/core/environment.spec.ts containing only a describe with no test cases — now makes isTestFile return false, so S2187/rule.ts:123-125 bails out and the "Add some tests to this file or delete it" issue is never raised (false negative). Narrow the pattern to require the conventional environments/ parent directory (and update the two added expectations for the bare environment.test.ts filename accordingly).

Performance: isAngularProject runs a manifest lookup for every linted file in S2187

📄 packages/analysis/src/jsts/rules/S2187/rule.ts:124-132 📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:69-78 📄 packages/analysis/src/jsts/rules/helpers/dependency-manifests/dependencies.ts:126-140 📄 packages/analysis/src/jsts/rules/helpers/dependency-manifests/dependencies.ts:218-220
isAngularProject(context) is passed as an eager argument, so every file S2187 creates a listener for — including the vast majority that are not test files — now performs normalizeToAbsolutePath on filename/cwd, a find-up manifest resolution and (when Deno inline deps are set) a full Map copy in withCurrentFileInlineDependencies, work that did not happen before this PR. Make the signal lazy so it is only computed for paths that actually look like an environment config file.

Bug: isTestRelatedFile Angular carve-out is never reached in production

📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:93-101
The new third parameter of isTestRelatedFile defaults to false and none of its four production callers passes it: S2925 (rule.ts:39), S9162 (rule.ts:72) and S8959 (rule.ts:54) all call isTestRelatedFile(context.filename, context.settings?.testFileExtensions) even though a Rule.RuleContext is available for isAngularProject(context), and filter-path.ts:110 has no context at all. So for an Angular src/environments/environment.test.ts the file is still treated as test-related everywhere except S2187 — the exclusion added to isTestRelatedFile is unreachable code and the behaviour of the two predicates is now inconsistent for the same path. Either wire isAngularProject(context) through the three rule call sites (and document why filter-path.ts intentionally keeps the old classification) or drop the parameter from isTestRelatedFile.

Edge Case: Angular carve-out misses environment.e2e.ts / .mock.ts markers

📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:38-46 📄 packages/analysis/src/jsts/rules/helpers/test-file-pattern.ts:99-107 📄 packages/analysis/src/jsts/rules/S2925/rule.ts:40-46 📄 packages/analysis/src/jsts/rules/S8959/rule.ts:55-61 📄 packages/analysis/src/jsts/rules/S9162/rule.ts:73-79
This commit makes the Angular carve-out reachable through isTestRelatedFile (S2925/S8959/S9162), but ENVIRONMENT_CONFIG_FILE_PATTERN only recognises the test|spec|cy markers while testRelatedFilePattern also matches .e2e. and .mock.. So in an Angular project src/environments/environment.test.ts is carved out, yet src/environments/environment.e2e.ts (a common Angular fileReplacements target) and src/environments/environment.mock.ts are still classified test-related by exactly the three rules this commit wires — an inconsistency introduced by extending the carve-out to the test-related predicate without extending the marker set. Either add e2e|mock to the environment-config pattern (and cover them in tests/jsts/rules/helpers/test-file-pattern.test.ts) or document why those markers are deliberately excluded.

...and 3 more resolved from earlier reviews

Implementation Status ✅ 1 of 1 objectives covered
JS-2311 - 1 of 1 objectives covered

This PR implements the fix for S2187 false positives on Angular-style environment.test.ts configuration files by carving them out of test path classification in Angular projects.

✅ 1 covered here
  • ✅ Fix S2187 false positive on Angular-style environment.test.ts config files
Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqube-next

sonarqube-next Bot commented Sep 2, 2026

Copy link
Copy Markdown

@vdiez vdiez left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The scope-classification approach looks sound. I have a few suggestions to make the intent exact and keep the test-marker metadata single-sourced.

* recognised by `isTestRelatedFile`.
*/
const ANGULAR_ENVIRONMENT_CONFIG_PATTERN =
/(?:^|\/)environments?\/environment\.(?:test|spec|cy|e2e|mock)\.[^/]+$/;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

These markers duplicate the set in test-file-pattern.ts; this already drifted once when e2e/mock were added. Please keep a single canonical definition by exporting a pure isAngularEnvironmentConfigFileCandidate(filePath) predicate from that helper, building both regexes from shared marker constants, and using it here. The Angular dependency check should remain in this classifier.

Comment on lines +154 to +160
// Angular per-environment config files (`environments/environment.<env>.ts`) match the filename
// heuristic by coincidence when the environment is named test/spec/cy/e2e/mock. In an Angular
// project they are production config, not tests, so keep them out of the test rule-selection
// classification: this drops every Test-scoped rule (S2187/S2925/S8959/S9162) for them without
// any rule needing its own scope logic. Because the carve-out lives here (rule selection only),
// the scanner/path-derived file type used for metrics is unaffected. The Angular check is gated
// behind the cheap path match, and its manifest lookup is cached.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This comment treats the Angular dependency as proof that the individual file is production configuration and embeds a rule inventory that can go stale. Describe it as an intentional rule-selection classification and avoid naming its current consumers.

Suggested change
// Angular per-environment config files (`environments/environment.<env>.ts`) match the filename
// heuristic by coincidence when the environment is named test/spec/cy/e2e/mock. In an Angular
// project they are production config, not tests, so keep them out of the test rule-selection
// classification: this drops every Test-scoped rule (S2187/S2925/S8959/S9162) for them without
// any rule needing its own scope logic. Because the carve-out lives here (rule selection only),
// the scanner/path-derived file type used for metrics is unaffected. The Angular check is gated
// behind the cheap path match, and its manifest lookup is cached.
// Angular environment-config candidates can match the filename heuristic by coincidence.
// When `@angular/core` is visible to the file, keep them MAIN for rule selection so test-scoped
// rules do not run. This affects only rule selection; the scanner/path-derived file type used
// for metrics is unchanged. The Angular check is gated by the cheap path predicate, and the
// underlying manifest lookup is cached.

Comment on lines +158 to +170
/**
* Determines whether a file belongs to an Angular project, based on the presence of
* `@angular/core` in the closest dependency manifest.
*
* This is a path-based predicate (no `Rule.RuleContext`), so it can be used both by the
* scope-classification heuristic in `common/filter/filter-path.ts` — which decides MAIN/TEST
* before any rule runs — and, if needed, by rules. Keeping the Angular signal in one shared
* helper avoids duplicating the dependency list, and the underlying lookup is cached.
*
* @param filePath normalized absolute path of the file
* @param topDir normalized absolute directory bounding the upward manifest search (project base dir)
* @returns true when `@angular/core` is declared in the closest manifest
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

getDependencies intentionally aggregates every dependency manifest from the closest manifest directory through topDir, rather than inspecting only the closest manifest. Align the documentation with that behavior.

Suggested change
/**
* Determines whether a file belongs to an Angular project, based on the presence of
* `@angular/core` in the closest dependency manifest.
*
* This is a path-based predicate (no `Rule.RuleContext`), so it can be used both by the
* scope-classification heuristic in `common/filter/filter-path.ts` which decides MAIN/TEST
* before any rule runs and, if needed, by rules. Keeping the Angular signal in one shared
* helper avoids duplicating the dependency list, and the underlying lookup is cached.
*
* @param filePath normalized absolute path of the file
* @param topDir normalized absolute directory bounding the upward manifest search (project base dir)
* @returns true when `@angular/core` is declared in the closest manifest
*/
/**
* Determines whether `@angular/core` is declared in the dependency manifests visible to a file.
*
* This path-based predicate (no `Rule.RuleContext`) can be used by the scope-classification
* heuristic in `common/filter/filter-path.ts`, which decides MAIN/TEST before any rule runs.
* Keeping the Angular signal in one shared helper avoids duplicating the dependency list, and the
* underlying lookup is cached.
*
* @param filePath normalized absolute path of the file
* @param topDir normalized absolute directory bounding the upward manifest search (project base dir)
* @returns true when `@angular/core` is declared in any manifest from the closest manifest
* directory through `topDir`
*/

Comment on lines +330 to +335
// JS-2311: Angular per-environment config files (environments/environment.<env>.ts) match the
// filename heuristic by coincidence. In an Angular project they are production config, so the
// heuristic used for rule selection carves them out (proved by a real on-disk package.json
// declaring @angular/core) — keeping them MAIN so Test-scoped rules do not run on them, while
// the scanner/path-derived file type used for metrics is untouched. Everywhere else they stay
// TEST for rule selection.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@angular/core establishes the Angular signal, but does not prove the purpose of an individual file; “everywhere else” also overlooks explicit scope configuration. This states the tested behavior more precisely.

Suggested change
// JS-2311: Angular per-environment config files (environments/environment.<env>.ts) match the
// filename heuristic by coincidence. In an Angular project they are production config, so the
// heuristic used for rule selection carves them out (proved by a real on-disk package.json
// declaring @angular/core) — keeping them MAIN so Test-scoped rules do not run on them, while
// the scanner/path-derived file type used for metrics is untouched. Everywhere else they stay
// TEST for rule selection.
// JS-2311: When sonar.tests is not configured, Angular environment-config filenames can match
// the test-file heuristic by coincidence. If `@angular/core` is visible to the file, keep these
// candidates MAIN for rule selection. The scanner/path-derived file type used for metrics is
// unchanged; non-Angular candidates continue through the normal test-file heuristic.

Comment on lines +388 to +389
it('respects an explicit TEST classification (sonar.tests) over the Angular carve-out', () => {
// When the scanner already typed the file as TEST, rule selection keeps it TEST.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This test passes TEST directly; it does not configure sonar.tests. Name the behavior it actually exercises.

Suggested change
it('respects an explicit TEST classification (sonar.tests) over the Angular carve-out', () => {
// When the scanner already typed the file as TEST, rule selection keeps it TEST.
it('preserves an existing TEST classification over the Angular carve-out', () => {
// An upstream TEST classification takes precedence over the filename heuristic.

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.

3 participants