|
| 1 | +import { readFileSync } from 'node:fs'; |
| 2 | + |
| 3 | +/** |
| 4 | + * Guards version couplings that CI depends on but that no single manifest |
| 5 | + * expresses. Renovate (or a manual edit) can move one half of a pair and leave |
| 6 | + * the other behind, which otherwise only surfaces as a confusing failure deep in |
| 7 | + * the test or visual-regression run. Asserting the couplings here — against the |
| 8 | + * versions actually installed — turns that into a fast, explicit failure. |
| 9 | + * |
| 10 | + * The vitest couplings are exact peer dependencies: `@vitest/browser-playwright` |
| 11 | + * and `@vitest/coverage-v8` peer on the same `vitest` version that `vite-plus` |
| 12 | + * bundles, and `vite` must resolve to the `@voidzero-dev/vite-plus-core` build |
| 13 | + * that `vite-plus` ships. |
| 14 | + */ |
| 15 | +const repoRoot = new URL('../../', import.meta.url); |
| 16 | + |
| 17 | +function readJson<T>(path: string): T { |
| 18 | + return JSON.parse(readFileSync(new URL(path, repoRoot), 'utf8')) as T; |
| 19 | +} |
| 20 | + |
| 21 | +type PackageJson = { |
| 22 | + name?: string; |
| 23 | + version: string; |
| 24 | + dependencies?: Record<string, string>; |
| 25 | +}; |
| 26 | + |
| 27 | +const workflow = readFileSync(new URL('.github/workflows/test.yml', repoRoot), 'utf8'); |
| 28 | + |
| 29 | +const playwright = readJson<PackageJson>('node_modules/playwright/package.json'); |
| 30 | +const vitest = readJson<PackageJson>('node_modules/vitest/package.json'); |
| 31 | +const browserPlaywright = readJson<PackageJson>( |
| 32 | + 'node_modules/@vitest/browser-playwright/package.json', |
| 33 | +); |
| 34 | +const coverageV8 = readJson<PackageJson>('node_modules/@vitest/coverage-v8/package.json'); |
| 35 | +const vitePlus = readJson<PackageJson>('node_modules/vite-plus/package.json'); |
| 36 | +const vite = readJson<PackageJson>('node_modules/vite/package.json'); |
| 37 | + |
| 38 | +const PINNED_PLAYWRIGHT_IMAGE = |
| 39 | + /container:\s+mcr\.microsoft\.com\/playwright:v([^@\s]+)-noble@(sha256:[0-9a-f]{64})/; |
| 40 | + |
| 41 | +describe('CI dependency compatibility', () => { |
| 42 | + describe('playwright', () => { |
| 43 | + it('pins the visual-regression image to the installed playwright version', () => { |
| 44 | + const pinned = workflow.match(PINNED_PLAYWRIGHT_IMAGE); |
| 45 | + |
| 46 | + expect( |
| 47 | + pinned, |
| 48 | + 'Expected a digest-pinned mcr.microsoft.com/playwright container in the visual-regression job of .github/workflows/test.yml.', |
| 49 | + ).not.toBeNull(); |
| 50 | + |
| 51 | + expect( |
| 52 | + pinned?.[1], |
| 53 | + [ |
| 54 | + `The visual-regression container must run mcr.microsoft.com/playwright:v${playwright.version}-noble to match the installed playwright (driven by the package.json devDependency).`, |
| 55 | + 'Update the tag and refresh the pinned digest with:', |
| 56 | + ` docker buildx imagetools inspect mcr.microsoft.com/playwright:v${playwright.version}-noble --format '{{.Manifest.Digest}}'`, |
| 57 | + ].join('\n'), |
| 58 | + ).toBe(playwright.version); |
| 59 | + }); |
| 60 | + }); |
| 61 | + |
| 62 | + describe('vitest family', () => { |
| 63 | + it('installs the browser provider and coverage reporter at the vitest version', () => { |
| 64 | + expect(browserPlaywright.version).toBe(vitest.version); |
| 65 | + expect(coverageV8.version).toBe(vitest.version); |
| 66 | + }); |
| 67 | + |
| 68 | + it('installs vitest at the version bundled by vite-plus', () => { |
| 69 | + expect(vitest.version).toBe(vitePlus.dependencies?.vitest); |
| 70 | + }); |
| 71 | + }); |
| 72 | + |
| 73 | + describe('vite family', () => { |
| 74 | + it('resolves vite to the vite-plus-core build that vite-plus ships', () => { |
| 75 | + expect(vite.name).toBe('@voidzero-dev/vite-plus-core'); |
| 76 | + expect(vitePlus.dependencies?.vite).toBe(`npm:${vite.name}@${vite.version}`); |
| 77 | + }); |
| 78 | + }); |
| 79 | +}); |
0 commit comments