Skip to content

Add E2E code coverage#156

Draft
kasperpeulen wants to merge 7 commits intomainfrom
kasper/e2e-coverage
Draft

Add E2E code coverage#156
kasperpeulen wants to merge 7 commits intomainfrom
kasper/e2e-coverage

Conversation

@kasperpeulen
Copy link
Copy Markdown
Member

@kasperpeulen kasperpeulen commented Feb 13, 2026

Collect V8 coverage from the Storybook process spawned during E2E tests and upload it to Codecov alongside unit test coverage.

  • Enable source maps in tsdown builds so V8 coverage can map back to TypeScript source
  • Set NODE_V8_COVERAGE in test:ci to collect coverage from the spawned Storybook process
  • Add test:ci:report-e2e-coverage script using c8 with monocart-coverage-reports to convert V8 coverage to lcov
  • Use monocart instead of c8's default v8-to-istanbul to produce line counts that align with vitest's AST-based coverage (within 1-3 lines per file), enabling proper Codecov merging
  • Filter lcov output to only include src/ files, matching vitest's include pattern
  • Upload both unit test and E2E coverage lcov files to Codecov
  • Add .npmignore to both packages to exclude .map files from published packages

Coverage: 73.90% → 74.96% (+1.06%)

Collect V8 coverage from the Storybook process spawned during E2E tests
and convert it to lcov via c8, so Codecov can merge it with unit test
coverage. Source maps are now generated in the build but excluded from
npm via .npmignore.
Copilot AI review requested due to automatic review settings February 13, 2026 05:57
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Feb 13, 2026

⚠️ No Changeset found

Latest commit: 2e7b48a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Feb 13, 2026

npm i https://pkg.pr.new/storybookjs/mcp/@storybook/addon-mcp@156
npm i https://pkg.pr.new/storybookjs/mcp/@storybook/mcp@156

commit: 2e7b48a

@codecov
Copy link
Copy Markdown

codecov bot commented Feb 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (main@baa0f02). Learn more about missing BASE report.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #156   +/-   ##
=======================================
  Coverage        ?   74.96%           
=======================================
  Files           ?       31           
  Lines           ?      819           
  Branches        ?      204           
=======================================
  Hits            ?      614           
  Misses          ?      158           
  Partials        ?       47           

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds E2E code coverage collection to complement existing unit test coverage. The implementation enables V8 coverage collection from the spawned Storybook process during E2E tests, then uses c8 to convert the raw V8 coverage data to lcov format with source-mapped TypeScript paths.

Changes:

  • Enable source map generation in tsdown builds for V8-to-source mapping
  • Set NODE_V8_COVERAGE environment variable during CI test runs to collect V8 coverage from spawned processes
  • Add c8 dependency and script to convert V8 coverage to lcov format
  • Upload both unit test and E2E coverage reports to Codecov
  • Exclude source maps from published npm packages

Reviewed changes

Copilot reviewed 6 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tsdown-shared.config.ts Enables sourcemap generation for all packages to support V8 coverage source mapping
turbo.json Adds e2e-coverage/** to test:ci task outputs for turbo caching
package.json Adds c8 dependency, sets NODE_V8_COVERAGE in test:ci, adds test:ci:report-e2e-coverage script
packages/mcp/.npmignore Excludes .map files from npm package
packages/addon-mcp/.npmignore Excludes .map files from npm package
.gitignore Ignores e2e-coverage/ and e2e-coverage-report/ directories
.github/workflows/check.yml Adds E2E coverage reporting step and uploads e2e-coverage-report/lcov.info to Codecov
pnpm-lock.yaml Adds c8 and its dependencies; libc annotations from pnpm update
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

@kasperpeulen kasperpeulen changed the title feat: add E2E code coverage Add E2E code coverage Feb 13, 2026
Copilot AI review requested due to automatic review settings February 13, 2026 06:32
@kasperpeulen kasperpeulen force-pushed the kasper/e2e-coverage branch 2 times, most recently from eb8767c to dcc11c4 Compare February 13, 2026 06:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 12 changed files in this pull request and generated 2 comments.

Files not reviewed (4)
  • apps/internal-storybook/pnpm-lock.yaml: Language not supported
  • eval/pnpm-lock.yaml: Language not supported
  • packages/addon-mcp/pnpm-lock.yaml: Language not supported
  • pnpm-lock.yaml: Language not supported

"storybook": "turbo watch storybook --filter=@storybook/mcp-internal-storybook",
"test": "vitest",
"test:ci": "vitest run --coverage --reporter=default --reporter=github-actions --reporter=junit --outputFile=test-report.junit.xml",
"test:ci": "NODE_V8_COVERAGE=./e2e-coverage vitest run --coverage --reporter=default --reporter=github-actions --reporter=junit --outputFile=test-report.junit.xml",
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

NODE_V8_COVERAGE=./e2e-coverage is a relative path, but the E2E tests spawn Storybook with cwd: STORYBOOK_DIR (apps/internal-storybook). That means the Storybook process will write V8 coverage into apps/internal-storybook/e2e-coverage, while the later c8 report --temp-directory ./e2e-coverage step reads from the repo root. This can result in missing/empty E2E coverage (or a failing c8 report). Consider using an absolute path for NODE_V8_COVERAGE (so it’s independent of child process CWD) or explicitly overriding NODE_V8_COVERAGE in the Storybook spawn env to point at the root coverage directory.

Suggested change
"test:ci": "NODE_V8_COVERAGE=./e2e-coverage vitest run --coverage --reporter=default --reporter=github-actions --reporter=junit --outputFile=test-report.junit.xml",
"test:ci": "NODE_V8_COVERAGE=\"$PWD/e2e-coverage\" vitest run --coverage --reporter=default --reporter=github-actions --reporter=junit --outputFile=test-report.junit.xml",

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The relative path works because the Storybook process ends up running from the repo root (turbo resets CWD). Verified locally — all V8 coverage files land in ./e2e-coverage/ at the repo root, none in apps/internal-storybook/e2e-coverage/.

package.json Outdated
"test": "vitest",
"test:ci": "vitest run --coverage --reporter=default --reporter=github-actions --reporter=junit --outputFile=test-report.junit.xml",
"test:ci": "NODE_V8_COVERAGE=./e2e-coverage vitest run --coverage --reporter=default --reporter=github-actions --reporter=junit --outputFile=test-report.junit.xml",
"test:ci:report-e2e-coverage": "c8 report --src ./packages --all -r lcov --temp-directory ./e2e-coverage -o ./e2e-coverage-report",
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

c8 report is run with --all and --src ./packages, which forces every file under ./packages into the E2E lcov output even if Storybook never executed it. When Codecov merges coverage/lcov.info and e2e-coverage-report/lcov.info, this can introduce lots of 0-hit files and reduce overall coverage unexpectedly. If the goal is to report only what the spawned Storybook process actually covered, drop --all and/or narrow the --src scope to the subset of packages/files Storybook can load.

Suggested change
"test:ci:report-e2e-coverage": "c8 report --src ./packages --all -r lcov --temp-directory ./e2e-coverage -o ./e2e-coverage-report",
"test:ci:report-e2e-coverage": "c8 report --src ./packages -r lcov --temp-directory ./e2e-coverage -o ./e2e-coverage-report",

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member Author

@kasperpeulen kasperpeulen Feb 13, 2026

Choose a reason for hiding this comment

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

Investigated this — tried dropping --all but actually Codecov merges by taking the max hit count per line, so --all with --src ./packages is correct. Files at 0% in E2E will be covered by unit tests, and Codecov keeps the higher count. Keeping --all ensures that if we add a new source file and forget to cover it in both unit and E2E, it shows up as 0% rather than being invisible.

Copilot AI review requested due to automatic review settings February 13, 2026 06:45
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 12 changed files in this pull request and generated no new comments.

Files not reviewed (4)
  • apps/internal-storybook/pnpm-lock.yaml: Language not supported
  • eval/pnpm-lock.yaml: Language not supported
  • packages/addon-mcp/pnpm-lock.yaml: Language not supported
  • pnpm-lock.yaml: Language not supported

Copilot AI review requested due to automatic review settings February 13, 2026 07:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 12 changed files in this pull request and generated no new comments.

Files not reviewed (4)
  • apps/internal-storybook/pnpm-lock.yaml: Language not supported
  • eval/pnpm-lock.yaml: Language not supported
  • packages/addon-mcp/pnpm-lock.yaml: Language not supported
  • pnpm-lock.yaml: Language not supported

c8's default v8-to-istanbul produces inflated line counts (e.g. 212 vs
57 for the same file) because it maps all source lines including types
and imports. monocart-coverage-reports uses AST analysis to count only
executable lines, producing line counts within 1-3 of vitest's output.
This enables proper Codecov merging of unit + e2e coverage.
Copilot AI review requested due to automatic review settings February 13, 2026 07:32
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 13 changed files in this pull request and generated 1 comment.

Files not reviewed (4)
  • apps/internal-storybook/pnpm-lock.yaml: Language not supported
  • eval/pnpm-lock.yaml: Language not supported
  • packages/addon-mcp/pnpm-lock.yaml: Language not supported
  • pnpm-lock.yaml: Language not supported

Comment on lines +14 to +21
const filtered = lcov
.split('end_of_record\n')
.filter((section) => {
const match = section.match(/^SF:(.+)$/m);
return match && regex.test(match[1]);
})
.join('end_of_record\n');

Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The filter logic may produce an empty result if no sections match the regex pattern. When all sections are filtered out, the script will write only end_of_record\n to the lcov file, which could cause issues with downstream tools expecting valid coverage data.

Consider adding a check after filtering to verify that at least one section matched, and either log a warning or exit with an error if no matches are found. This would make debugging easier if the pattern is incorrect or if the coverage data structure changes.

Suggested change
const filtered = lcov
.split('end_of_record\n')
.filter((section) => {
const match = section.match(/^SF:(.+)$/m);
return match && regex.test(match[1]);
})
.join('end_of_record\n');
const sections = lcov.split('end_of_record\n');
const filteredSections = sections.filter((section) => {
const match = section.match(/^SF:(.+)$/m);
return match && regex.test(match[1]);
});
if (filteredSections.length === 0) {
console.error(
`filter-lcov: no coverage sections matched pattern ${JSON.stringify(
pattern,
)} in file ${file}`,
);
process.exit(1);
}
const filtered = filteredSections.join('end_of_record\n');

Copilot uses AI. Check for mistakes.
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 13, 2026

Bundle Report

Changes will decrease total bundle size by 39.69kB (-100.0%) ⬇️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
@storybook/mcp-esm (removed) -39.69kB (-100.0%) ⬇️

@JReinhold JReinhold marked this pull request as draft February 16, 2026 09:58
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