Skip to content

Converted @tryghost/root-utils to Vitest#406

Closed
EvanHahn wants to merge 1 commit intomainfrom
convert-rootutils-package-to-vitest
Closed

Converted @tryghost/root-utils to Vitest#406
EvanHahn wants to merge 1 commit intomainfrom
convert-rootutils-package-to-vitest

Conversation

@EvanHahn
Copy link
Contributor

no ref

This change should have no user impact but makes it easier to migrate to TypeScript in the future.

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Walkthrough

Replaces Mocha+c8 with Vitest for testing and coverage in packages/root-utils/package.json (scripts and devDependencies updated). Updates dependencies caller and find-root to caret ranges. Adds Vitest plugin and env to packages/root-utils/test/.eslintrc.js. Converts tests in packages/root-utils/test/root-utils.test.js to Vitest-style assertions and adds safer try/finally cleanup. No public API changes beyond manifest and test tooling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and clearly summarizes the main change: converting the root-utils package from Mocha to Vitest test framework.
Description check ✅ Passed The description is related to the changeset, explaining that the conversion has no user impact and supports future TypeScript migration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch convert-rootutils-package-to-vitest

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@EvanHahn EvanHahn force-pushed the convert-rootutils-package-to-vitest branch 2 times, most recently from 3449b6f to 6547e0f Compare February 26, 2026 18:35
@EvanHahn EvanHahn marked this pull request as ready for review February 26, 2026 18:36
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/root-utils/test/root-utils.test.js (1)

46-57: Move setup into the guarded block to prevent test-state leaks.

Lines 49-50 run before try, so a setup failure can leave current behind and make later tests flaky.

Safer setup/teardown shape
-        fs.mkdirSync(currentDirectory);
-        fs.closeSync(fs.openSync(packageJSONPath, 'w'));
-
-        try {
+        try {
+            fs.mkdirSync(currentDirectory);
+            fs.closeSync(fs.openSync(packageJSONPath, 'w'));
             expect(getProcessRoot()).toBe(currentDirectory);
         } finally {
-            fs.unlinkSync(packageJSONPath);
-            fs.rmdirSync(currentDirectory);
+            if (fs.existsSync(packageJSONPath)) {
+                fs.unlinkSync(packageJSONPath);
+            }
+            if (fs.existsSync(currentDirectory)) {
+                fs.rmdirSync(currentDirectory);
+            }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/root-utils/test/root-utils.test.js` around lines 46 - 57, The test
creates currentDirectory and packageJSONPath before entering the try/finally,
risking leftover files if setup fails; move the setup (fs.mkdirSync and creating
the package.json file) into the try block immediately before the expect so that
cleanup in the finally always runs even on setup failure, referencing the
variables currentDirectory and packageJSONPath and the tested function
getProcessRoot() to keep teardown (fs.unlinkSync and fs.rmdirSync) paired with
creation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/root-utils/test/root-utils.test.js`:
- Around line 27-29: The current test only checks that a package.json exists at
getCallerRoot(), which can be satisfied by an unrelated root; instead read and
parse the package.json at path.join(callerRoot, 'package.json') and assert its
"name" matches the package name for this module (load the expected package name
by requiring the package.json adjacent to the test), e.g. const pkg =
JSON.parse(fs.readFileSync(path.join(callerRoot,'package.json'),'utf8'));
expect(pkg.name).toBe(require('../package.json').name); this strengthens the
getCallerRoot() assertion and prevents false positives.
- Line 69: Replace the permissive suffix assertion on getProcessRoot() with an
exact path equality check: locate the test in root-utils.test.js that calls
getProcessRoot() (the expect(getProcessRoot().endsWith('root-utils')).toBe(true)
line) and change it to compare the full resolved path returned by
getProcessRoot() to the exact expected path string (or to a path constructed
with path.resolve/path.join to avoid platform issues) using toBe or toEqual so
the test asserts precise equality rather than just a suffix.

---

Nitpick comments:
In `@packages/root-utils/test/root-utils.test.js`:
- Around line 46-57: The test creates currentDirectory and packageJSONPath
before entering the try/finally, risking leftover files if setup fails; move the
setup (fs.mkdirSync and creating the package.json file) into the try block
immediately before the expect so that cleanup in the finally always runs even on
setup failure, referencing the variables currentDirectory and packageJSONPath
and the tested function getProcessRoot() to keep teardown (fs.unlinkSync and
fs.rmdirSync) paired with creation.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93f44e0 and 6547e0f.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • packages/root-utils/package.json
  • packages/root-utils/test/.eslintrc.js
  • packages/root-utils/test/root-utils.test.js

Comment on lines +27 to +29
const callerRoot = getCallerRoot();
expect(typeof callerRoot).toBe('string');
expect(fs.existsSync(path.join(callerRoot, 'package.json'))).toBe(true);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Strengthen the getCallerRoot success assertion.

On Line 29, checking only for package.json existence can pass for unrelated roots (for example, a tool/package root), so this can miss regressions in caller-root resolution.

Suggested tightening
-        expect(typeof callerRoot).toBe('string');
-        expect(fs.existsSync(path.join(callerRoot, 'package.json'))).toBe(true);
+        expect(callerRoot).toBe(path.join(__dirname, '..'));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const callerRoot = getCallerRoot();
expect(typeof callerRoot).toBe('string');
expect(fs.existsSync(path.join(callerRoot, 'package.json'))).toBe(true);
const callerRoot = getCallerRoot();
expect(callerRoot).toBe(path.join(__dirname, '..'));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/root-utils/test/root-utils.test.js` around lines 27 - 29, The
current test only checks that a package.json exists at getCallerRoot(), which
can be satisfied by an unrelated root; instead read and parse the package.json
at path.join(callerRoot, 'package.json') and assert its "name" matches the
package name for this module (load the expected package name by requiring the
package.json adjacent to the test), e.g. const pkg =
JSON.parse(fs.readFileSync(path.join(callerRoot,'package.json'),'utf8'));
expect(pkg.name).toBe(require('../package.json').name); this strengthens the
getCallerRoot() assertion and prevents false positives.


try {
assert.equal(getProcessRoot().endsWith('root-utils'), true);
expect(getProcessRoot().endsWith('root-utils')).toBe(true);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Prefer exact path assertion over suffix matching.

Line 69’s .endsWith('root-utils') is permissive and can pass for incorrect paths. Exact equality keeps this test deterministic.

Precise assertion
-            expect(getProcessRoot().endsWith('root-utils')).toBe(true);
+            expect(getProcessRoot()).toBe(path.join(__dirname, '..'));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(getProcessRoot().endsWith('root-utils')).toBe(true);
expect(getProcessRoot()).toBe(path.join(__dirname, '..'));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/root-utils/test/root-utils.test.js` at line 69, Replace the
permissive suffix assertion on getProcessRoot() with an exact path equality
check: locate the test in root-utils.test.js that calls getProcessRoot() (the
expect(getProcessRoot().endsWith('root-utils')).toBe(true) line) and change it
to compare the full resolved path returned by getProcessRoot() to the exact
expected path string (or to a path constructed with path.resolve/path.join to
avoid platform issues) using toBe or toEqual so the test asserts precise
equality rather than just a suffix.

@codecov-commenter
Copy link

codecov-commenter commented Feb 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (a7c0e5a) to head (08fcfdd).

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #406   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          133       133           
  Lines         8630      8628    -2     
  Branches      1460      1459    -1     
=========================================
- Hits          8630      8628    -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@EvanHahn EvanHahn requested a review from 9larsons March 2, 2026 17:02
no ref

This change should have no user impact but makes it easier to migrate to
TypeScript in the future.
@EvanHahn EvanHahn force-pushed the convert-rootutils-package-to-vitest branch from 6547e0f to 08fcfdd Compare March 2, 2026 17:03
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
packages/root-utils/test/root-utils.test.js (2)

27-29: ⚠️ Potential issue | 🟠 Major

Strengthen caller-root assertion beyond file existence.

At Line 29, checking only for package.json existence is still too permissive and can validate the wrong root. Assert package identity (or exact expected path) to make this regression-proof.

Suggested tightening
         const callerRoot = getCallerRoot();
         expect(typeof callerRoot).toBe('string');
-        expect(fs.existsSync(path.join(callerRoot, 'package.json'))).toBe(true);
+        const pkg = JSON.parse(fs.readFileSync(path.join(callerRoot, 'package.json'), 'utf8'));
+        expect(pkg.name).toBe(require('../package.json').name);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/root-utils/test/root-utils.test.js` around lines 27 - 29, Replace
the loose existence check with a deterministic assertion: call getCallerRoot()
(callerRoot) then read and parse the package.json at path.join(callerRoot,
'package.json') and assert a known field (e.g., packageJson.name ===
'<expected-package-name>') or assert the resolved callerRoot equals the exact
expected root path; update the test expectations accordingly so it validates
package identity rather than just file existence.

69-69: ⚠️ Potential issue | 🟡 Minor

Use exact root assertion instead of suffix match.

At Line 69, .endsWith('root-utils') can still pass for incorrect paths. Use exact equality for determinism.

Precise assertion
-            expect(getProcessRoot().endsWith('root-utils')).toBe(true);
+            expect(getProcessRoot()).toBe(path.join(__dirname, '..'));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/root-utils/test/root-utils.test.js` at line 69, Replace the fuzzy
suffix check with a deterministic equality: compute an expectedRoot using Node's
path.resolve from the test file location (e.g., path.resolve(__dirname, '..',
...) to reach the package root) and assert
expect(getProcessRoot()).toBe(expectedRoot) instead of
expect(...endsWith('root-utils')).toDo this in the test that calls
getProcessRoot so the assertion compares exact full paths (use path.resolve and
__dirname to derive expectedRoot).
🧹 Nitpick comments (2)
packages/root-utils/package.json (1)

31-32: Avoid runtime dependency drift in a test-framework migration.

Line 31-32 broadens runtime dependency resolution (caller, find-root). For a “no user impact” migration, this adds unnecessary behavior risk. If unintentional, revert to prior pinned values; if intentional, document why this is safe.

Conservative revert
-    "caller": "^1.0.1",
-    "find-root": "^1.1.0"
+    "caller": "1.1.0",
+    "find-root": "1.1.0"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/root-utils/package.json` around lines 31 - 32, The dependency
versions for "caller" and "find-root" were loosened which can cause runtime
dependency drift; either revert "caller" and "find-root" to their previous
pinned versions (restore exact versions instead of ^ ranges) so behavior remains
identical, or add a clear comment in package.json and the PR description
documenting the intentional reason this loosened range is safe (including test
coverage/assertions that verify behavior) and mark the change as deliberate;
update the package.json entries for the "caller" and "find-root" dependencies
accordingly and run the lockfile update and tests to verify no regressions.
packages/root-utils/test/root-utils.test.js (1)

46-57: Isolate current directory setup to avoid test collisions.

This still creates current under process.cwd(), which can collide with parallel/leftover state. Prefer a temp cwd per test for deterministic behavior.

Refactor sketch
-        const currentDirectory = path.join(process.cwd(), 'current');
+        const previousCwd = process.cwd();
+        const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'root-utils-current-'));
+        const currentDirectory = path.join(tempDir, 'current');
         const packageJSONPath = path.join(currentDirectory, 'package.json');

-        fs.mkdirSync(currentDirectory);
-        fs.closeSync(fs.openSync(packageJSONPath, 'w'));
-
         try {
+            process.chdir(tempDir);
+            fs.mkdirSync(currentDirectory);
+            fs.closeSync(fs.openSync(packageJSONPath, 'w'));
             expect(getProcessRoot()).toBe(currentDirectory);
         } finally {
-            fs.unlinkSync(packageJSONPath);
-            fs.rmdirSync(currentDirectory);
+            process.chdir(previousCwd);
+            if (fs.existsSync(packageJSONPath)) fs.unlinkSync(packageJSONPath);
+            if (fs.existsSync(currentDirectory)) fs.rmdirSync(currentDirectory);
+            fs.rmdirSync(tempDir);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/root-utils/test/root-utils.test.js` around lines 46 - 57, The test
currently creates a 'current' dir under process.cwd(), risking collisions;
change it to create a unique temporary directory (use fs.mkdtempSync with
os.tmpdir() or fs.mkdtemp) and temporarily chdir into that temp dir before
creating the 'current' folder and package.json; update the currentDirectory and
packageJSONPath variables to be created inside that tempDir, call
getProcessRoot() and assert, and in the finally block restore the original cwd
and remove package.json, 'current', and the tempDir to ensure isolation and
deterministic cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/root-utils/test/root-utils.test.js`:
- Around line 27-29: Replace the loose existence check with a deterministic
assertion: call getCallerRoot() (callerRoot) then read and parse the
package.json at path.join(callerRoot, 'package.json') and assert a known field
(e.g., packageJson.name === '<expected-package-name>') or assert the resolved
callerRoot equals the exact expected root path; update the test expectations
accordingly so it validates package identity rather than just file existence.
- Line 69: Replace the fuzzy suffix check with a deterministic equality: compute
an expectedRoot using Node's path.resolve from the test file location (e.g.,
path.resolve(__dirname, '..', ...) to reach the package root) and assert
expect(getProcessRoot()).toBe(expectedRoot) instead of
expect(...endsWith('root-utils')).toDo this in the test that calls
getProcessRoot so the assertion compares exact full paths (use path.resolve and
__dirname to derive expectedRoot).

---

Nitpick comments:
In `@packages/root-utils/package.json`:
- Around line 31-32: The dependency versions for "caller" and "find-root" were
loosened which can cause runtime dependency drift; either revert "caller" and
"find-root" to their previous pinned versions (restore exact versions instead of
^ ranges) so behavior remains identical, or add a clear comment in package.json
and the PR description documenting the intentional reason this loosened range is
safe (including test coverage/assertions that verify behavior) and mark the
change as deliberate; update the package.json entries for the "caller" and
"find-root" dependencies accordingly and run the lockfile update and tests to
verify no regressions.

In `@packages/root-utils/test/root-utils.test.js`:
- Around line 46-57: The test currently creates a 'current' dir under
process.cwd(), risking collisions; change it to create a unique temporary
directory (use fs.mkdtempSync with os.tmpdir() or fs.mkdtemp) and temporarily
chdir into that temp dir before creating the 'current' folder and package.json;
update the currentDirectory and packageJSONPath variables to be created inside
that tempDir, call getProcessRoot() and assert, and in the finally block restore
the original cwd and remove package.json, 'current', and the tempDir to ensure
isolation and deterministic cleanup.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6547e0f and 08fcfdd.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • packages/root-utils/package.json
  • packages/root-utils/test/.eslintrc.js
  • packages/root-utils/test/root-utils.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/root-utils/test/.eslintrc.js

@9larsons
Copy link
Collaborator

9larsons commented Mar 2, 2026

Closing this out - migrated all packages to vitest.

@9larsons 9larsons closed this Mar 2, 2026
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