diff --git a/packages/test/__tests__/build-artifacts.spec.ts b/packages/test/__tests__/build-artifacts.spec.ts new file mode 100644 index 0000000000..21d963dd17 --- /dev/null +++ b/packages/test/__tests__/build-artifacts.spec.ts @@ -0,0 +1,58 @@ +/** + * Verify that the @voidzero-dev/vite-plus-test build output (dist/) + * contains the expected files and that patches applied during the build + * (in build.ts) produce correct artifacts. + * + * This is important because vite-plus re-packages vitest with custom + * patches, and missing exports or incorrect patches can break + * third-party integrations (e.g., @storybook/addon-vitest, #1086). + */ +import fs from 'node:fs'; +import path from 'node:path'; +import url from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +const testPkgDir = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '..'); +const distDir = path.join(testPkgDir, 'dist'); + +describe('build artifacts', () => { + describe('@vitest/browser/context.js', () => { + const contextPath = path.join(distDir, '@vitest/browser/context.js'); + + it('should exist', () => { + expect(fs.existsSync(contextPath), `${contextPath} should exist`).toBe(true); + }); + + it('should export page, cdp, and utils', () => { + const content = fs.readFileSync(contextPath, 'utf-8'); + expect(content).toMatch(/export\s*\{[^}]*page[^}]*\}/); + expect(content).toMatch(/export\s*\{[^}]*cdp[^}]*\}/); + expect(content).toMatch(/export\s*\{[^}]*utils[^}]*\}/); + }); + }); + + /** + * The vitest:vendor-aliases plugin must NOT resolve @vitest/browser/context + * to the static file. If it does, the BrowserContext plugin's virtual module + * (which provides the `server` export) is bypassed. + * + * See: https://github.com/voidzero-dev/vite-plus/issues/1086 + */ + describe('vitest:vendor-aliases plugin (regression test for #1086)', () => { + const browserIndexPath = path.join(distDir, '@vitest/browser/index.js'); + + it('should not map @vitest/browser/context in vendorMap', () => { + const content = fs.readFileSync(browserIndexPath, 'utf-8'); + // The vendorMap inside vitest:vendor-aliases should NOT contain + // '@vitest/browser/context' — it must be left for BrowserContext + // plugin to resolve as a virtual module. + const vendorAliasesMatch = content.match( + /name:\s*['"]vitest:vendor-aliases['"][\s\S]*?const vendorMap\s*=\s*\{([\s\S]*?)\}/, + ); + expect(vendorAliasesMatch, 'vitest:vendor-aliases plugin should exist').toBeTruthy(); + const vendorMapContent = vendorAliasesMatch![1]; + expect(vendorMapContent).not.toContain("'@vitest/browser/context'"); + }); + }); +}); diff --git a/packages/test/build.ts b/packages/test/build.ts index a2c5df4249..f4276d0de5 100644 --- a/packages/test/build.ts +++ b/packages/test/build.ts @@ -1458,8 +1458,16 @@ async function patchVitestBrowserPackage() { // 1. Inject vitest:vendor-aliases plugin into BrowserPlugin return array // This allows imports like @vitest/runner to be resolved to our copied @vitest files + // Exclude @vitest/browser/context from vendor-aliases so that BrowserContext + // plugin's resolveId can intercept the bare specifier and return the virtual + // module (which includes the dynamically generated `server` export). + // Without this, vendor-aliases resolves the bare specifier to the static + // context.js file (which has no `server`), bypassing BrowserContext entirely. + // See: https://github.com/voidzero-dev/vite-plus/issues/1086 + const VENDOR_ALIASES_EXCLUDE = new Set(['@vitest/browser/context']); + const mappingEntries = Object.entries(VITEST_PACKAGE_TO_PATH) - .filter(([pkg]) => pkg.startsWith('@vitest/')) + .filter(([pkg]) => pkg.startsWith('@vitest/') && !VENDOR_ALIASES_EXCLUDE.has(pkg)) .map(([pkg, file]) => `'${pkg}': resolve(packageRoot, '${file}')`) .join(',\n ');