From b3c66f61496dda131d01fef556c7d44a31d28e82 Mon Sep 17 00:00:00 2001 From: Philippe Serhal Date: Wed, 4 Jun 2025 14:06:42 -0400 Subject: [PATCH 1/3] fix(deps): loosen vite peer dep to support vite 5 --- package-lock.json | 2 +- packages/vite-plugin/package.json | 2 +- packages/vite-plugin/src/main.test.ts | 68 +++++++++++++++++++++++++-- 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3e405210..a13f8e90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10230,7 +10230,7 @@ "node": "^20.6.1 || >=22" }, "peerDependencies": { - "vite": "^6" + "vite": "^5 || ^6" } }, "packages/vite-plugin/node_modules/@types/node": { diff --git a/packages/vite-plugin/package.json b/packages/vite-plugin/package.json index b233a265..62fdfba9 100644 --- a/packages/vite-plugin/package.json +++ b/packages/vite-plugin/package.json @@ -41,6 +41,6 @@ "chalk": "^5.4.1" }, "peerDependencies": { - "vite": "^6" + "vite": "^5 || ^6" } } diff --git a/packages/vite-plugin/src/main.test.ts b/packages/vite-plugin/src/main.test.ts index bbead52d..fb58f363 100644 --- a/packages/vite-plugin/src/main.test.ts +++ b/packages/vite-plugin/src/main.test.ts @@ -55,6 +55,11 @@ const createMockViteLogger = () => { } } +const originalEnv = { ...process.env } +beforeEach(() => { + process.env = { ...originalEnv } +}) + describe('Plugin constructor', () => { test('Is a no-op when running in the Netlify CLI', () => { process.env.NETLIFY_DEV = 'true' @@ -64,11 +69,6 @@ describe('Plugin constructor', () => { }) describe('configureServer', { timeout: 15_000 }, () => { - const originalEnv = { ...process.env } - beforeEach(() => { - process.env = { ...originalEnv } - }) - test('Populates Netlify runtime environment (globals and env vars)', async () => { const fixture = new Fixture() .withFile( @@ -547,3 +547,61 @@ defined on your team and site and much more. Run npx netlify init to get started }) }) }) + +test('Works with Vite 5', { timeout: 15_000 }, async () => { + const fixture = new Fixture() + .withFile( + 'vite.config.js', + `import { defineConfig } from 'vite'; + import netlify from '@netlify/vite-plugin'; + + export default defineConfig({ + plugins: [netlify()], + });`, + ) + .withFile( + 'index.html', + ` + + Vite 5 Compatibility Test +

Testing with Vite 5

+ `, + ) + const directory = await fixture.create() + await fixture + .withPackages({ + vite: '5.0.0', + '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + }) + .create() + + const mockLogger = createMockViteLogger() + const { server, url } = await startTestServer({ + root: directory, + logLevel: 'warn', + customLogger: mockLogger, + }) + + const browser = await chromium.launch() + const page = await browser.newPage() + const browserErrorLogs: ConsoleMessage[] = [] + page.on('console', (msg) => { + if (msg.type() === 'error') { + browserErrorLogs.push(msg) + } + }) + + expect((globalThis as Record).Netlify).toBeInstanceOf(Object) + expect(process.env).toHaveProperty('NETLIFY_LOCAL', 'true') + expect(process.env).toHaveProperty('CONTEXT', 'dev') + const response = await page.goto(`${url}/index.html`) + expect(response?.status()).toBe(200) + expect(await page.getByRole('heading').textContent()).toBe('Testing with Vite 5') + expect(mockLogger.error).not.toHaveBeenCalled() + expect(mockLogger.warn).not.toHaveBeenCalled() + expect(mockLogger.warnOnce).not.toHaveBeenCalled() + expect(browserErrorLogs).toHaveLength(0) + + await server.close() + await fixture.destroy() +}) From 18220709bf711565c7eb4904363fa5ef74d2a6db Mon Sep 17 00:00:00 2001 From: Philippe Serhal Date: Fri, 6 Jun 2025 10:27:26 -0400 Subject: [PATCH 2/3] test: use a table test for vite 5 and 6 --- packages/vite-plugin/src/main.test.ts | 752 ++++++++++++-------------- 1 file changed, 349 insertions(+), 403 deletions(-) diff --git a/packages/vite-plugin/src/main.test.ts b/packages/vite-plugin/src/main.test.ts index fb58f363..e8560130 100644 --- a/packages/vite-plugin/src/main.test.ts +++ b/packages/vite-plugin/src/main.test.ts @@ -60,20 +60,21 @@ beforeEach(() => { process.env = { ...originalEnv } }) -describe('Plugin constructor', () => { - test('Is a no-op when running in the Netlify CLI', () => { - process.env.NETLIFY_DEV = 'true' +describe.for([['5.0.0'], ['6.0.0']])('Vite %s', ([viteVersion]) => { + describe('Plugin constructor', () => { + test('Is a no-op when running in the Netlify CLI', () => { + process.env.NETLIFY_DEV = 'true' - expect(netlify()).toEqual([]) + expect(netlify()).toEqual([]) + }) }) -}) -describe('configureServer', { timeout: 15_000 }, () => { - test('Populates Netlify runtime environment (globals and env vars)', async () => { - const fixture = new Fixture() - .withFile( - 'vite.config.js', - `import { defineConfig } from 'vite'; + describe('configureServer', { timeout: 15_000 }, () => { + test('Populates Netlify runtime environment (globals and env vars)', async () => { + const fixture = new Fixture() + .withFile( + 'vite.config.js', + `import { defineConfig } from 'vite'; import netlify from '@netlify/vite-plugin'; export default defineConfig({ @@ -81,101 +82,36 @@ describe('configureServer', { timeout: 15_000 }, () => { netlify({ middleware: false }) ] });`, - ) - .withFile( - 'index.html', - ` + ) + .withFile( + 'index.html', + ` Hello World

Hello from the browser

`, - ) - const directory = await fixture.create() - await fixture - .withPackages({ - vite: '6.0.0', - '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), - }) - .create() - - const { server } = await startTestServer({ - root: directory, - }) - - expect((globalThis as Record).Netlify).toBeInstanceOf(Object) - expect(process.env).toHaveProperty('NETLIFY_LOCAL', 'true') - expect(process.env).toHaveProperty('CONTEXT', 'dev') - - await server.close() - await fixture.destroy() - }) + ) + const directory = await fixture.create() + await fixture + .withPackages({ + vite: viteVersion, + '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + }) + .create() - test('Prints a basic message on server start', async () => { - const fixture = new Fixture() - .withFile( - 'vite.config.js', - `import { defineConfig } from 'vite'; - import netlify from '@netlify/vite-plugin'; - - export default defineConfig({ - plugins: [ - netlify({ middleware: false }) - ] - });`, - ) - .withFile( - 'index.html', - ` - - Hello World -

Hello from the browser

- `, - ) - const directory = await fixture.create() - await fixture - .withPackages({ - vite: '6.0.0', - '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + const { server } = await startTestServer({ + root: directory, }) - .create() - - const mockLogger = createMockViteLogger() - const { server } = await startTestServer({ - root: directory, - logLevel: 'info', - customLogger: mockLogger, - }) - - expect(mockLogger.error).not.toHaveBeenCalled() - expect(mockLogger.warn).not.toHaveBeenCalled() - expect(mockLogger.warnOnce).not.toHaveBeenCalled() - expect(mockLogger.info).toHaveBeenCalledTimes(2) - expect(mockLogger.info).toHaveBeenNthCalledWith(1, 'Environment loaded', expect.objectContaining({})) - expect(mockLogger.info).toHaveBeenNthCalledWith( - 2, - '💭 Linking this project to a Netlify site lets you deploy your site, use any environment variables \ -defined on your team and site and much more. Run npx netlify init to get started.', - expect.objectContaining({}), - ) - - await server.close() - await fixture.destroy() - }) - - describe('Middleware enabled', () => { - let browser: Browser - let page: Page - beforeEach(async () => { - browser = await chromium.launch() - page = await browser.newPage() - }) + expect((globalThis as Record).Netlify).toBeInstanceOf(Object) + expect(process.env).toHaveProperty('NETLIFY_LOCAL', 'true') + expect(process.env).toHaveProperty('CONTEXT', 'dev') - afterEach(async () => { - await browser.close() + await server.close() + await fixture.destroy() }) - test('Prints a message listing emulated features on server start', async () => { + test('Prints a basic message on server start', async () => { const fixture = new Fixture() .withFile( 'vite.config.js', @@ -184,10 +120,7 @@ defined on your team and site and much more. Run npx netlify init to get started export default defineConfig({ plugins: [ - netlify({ - middleware: true, - edgeFunctions: { enabled: false }, - }) + netlify({ middleware: false }) ] });`, ) @@ -202,7 +135,7 @@ defined on your team and site and much more. Run npx netlify init to get started const directory = await fixture.create() await fixture .withPackages({ - vite: '6.0.0', + vite: viteVersion, '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), }) .create() @@ -217,16 +150,12 @@ defined on your team and site and much more. Run npx netlify init to get started expect(mockLogger.error).not.toHaveBeenCalled() expect(mockLogger.warn).not.toHaveBeenCalled() expect(mockLogger.warnOnce).not.toHaveBeenCalled() - expect(mockLogger.info).toHaveBeenCalledTimes(3) + expect(mockLogger.info).toHaveBeenCalledTimes(2) expect(mockLogger.info).toHaveBeenNthCalledWith(1, 'Environment loaded', expect.objectContaining({})) expect(mockLogger.info).toHaveBeenNthCalledWith( 2, - 'Middleware loaded. Emulating features: blobs, environmentVariables, functions, headers, redirects, static.', - expect.objectContaining({}), - ) - expect(mockLogger.info).toHaveBeenNthCalledWith( - 3, - expect.stringContaining('Linking this project to a Netlify site'), + '💭 Linking this project to a Netlify site lets you deploy your site, use any environment variables \ +defined on your team and site and much more. Run npx netlify init to get started.', expect.objectContaining({}), ) @@ -234,75 +163,147 @@ defined on your team and site and much more. Run npx netlify init to get started await fixture.destroy() }) - test('Returns static files from project dir', async () => { - const fixture = new Fixture() - .withFile( - 'vite.config.js', - `import { defineConfig } from 'vite'; + describe('Middleware enabled', () => { + let browser: Browser + let page: Page + + beforeEach(async () => { + browser = await chromium.launch() + page = await browser.newPage() + }) + + afterEach(async () => { + await browser.close() + }) + + test('Prints a message listing emulated features on server start', async () => { + const fixture = new Fixture() + .withFile( + 'vite.config.js', + `import { defineConfig } from 'vite'; import netlify from '@netlify/vite-plugin'; export default defineConfig({ plugins: [ netlify({ - middleware: true - }) + middleware: true, + edgeFunctions: { enabled: false }, + }) ] });`, + ) + .withFile( + 'index.html', + ` + + Hello World +

Hello from the browser

+ `, + ) + const directory = await fixture.create() + await fixture + .withPackages({ + vite: viteVersion, + '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + }) + .create() + + const mockLogger = createMockViteLogger() + const { server } = await startTestServer({ + root: directory, + logLevel: 'info', + customLogger: mockLogger, + }) + + expect(mockLogger.error).not.toHaveBeenCalled() + expect(mockLogger.warn).not.toHaveBeenCalled() + expect(mockLogger.warnOnce).not.toHaveBeenCalled() + expect(mockLogger.info).toHaveBeenCalledTimes(3) + expect(mockLogger.info).toHaveBeenNthCalledWith(1, 'Environment loaded', expect.objectContaining({})) + expect(mockLogger.info).toHaveBeenNthCalledWith( + 2, + 'Middleware loaded. Emulating features: blobs, environmentVariables, functions, headers, redirects, static.', + expect.objectContaining({}), ) - .withFile( - 'index.html', - ` - - Hello World - Hello from the static index.html file - - `, + expect(mockLogger.info).toHaveBeenNthCalledWith( + 3, + expect.stringContaining('Linking this project to a Netlify site'), + expect.objectContaining({}), ) - .withFile( - 'contact/email.html', - ` + + await server.close() + await fixture.destroy() + }) + + test('Returns static files from project dir', async () => { + const fixture = new Fixture() + .withFile( + 'vite.config.js', + `import { defineConfig } from 'vite'; + import netlify from '@netlify/vite-plugin'; + + export default defineConfig({ + plugins: [ + netlify({ + middleware: true + }) + ] + });`, + ) + .withFile( + 'index.html', + ` + + Hello World + Hello from the static index.html file + + `, + ) + .withFile( + 'contact/email.html', + ` Contact us via email Hello from another static file `, - ) - .withFile('js/main.js', `console.log('Hello from the browser')`) - const directory = await fixture.create() - await fixture - .withPackages({ - vite: '6.0.0', - '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + ) + .withFile('js/main.js', `console.log('Hello from the browser')`) + const directory = await fixture.create() + await fixture + .withPackages({ + vite: viteVersion, + '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + }) + .create() + + const { server, url } = await startTestServer({ + root: directory, }) - .create() - - const { server, url } = await startTestServer({ - root: directory, - }) - const response = await page.goto(url) - expect(response?.status()).toBe(200) - expect(await response?.text()).toContain('Hello from the static index.html file') + const response = await page.goto(url) + expect(response?.status()).toBe(200) + expect(await response?.text()).toContain('Hello from the static index.html file') - expect(await page.goto(`${url}/js/main.js`).then((r) => r?.text())).toContain('console.log(') + expect(await page.goto(`${url}/js/main.js`).then((r) => r?.text())).toContain('console.log(') - expect(await page.goto(`${url}/contact/email.html`).then((r) => r?.text())).toContain( - 'Hello from another static file', - ) + expect(await page.goto(`${url}/contact/email.html`).then((r) => r?.text())).toContain( + 'Hello from another static file', + ) - // This is Vite's behavior in dev for "404s" - const notFoundResponse = await page.goto(`${url}/wp-admin.php`) - expect(notFoundResponse?.status()).toBe(200) - expect(await notFoundResponse?.text()).toContain('Hello from the static index.html file') + // This is Vite's behavior in dev for "404s" + const notFoundResponse = await page.goto(`${url}/wp-admin.php`) + expect(notFoundResponse?.status()).toBe(200) + expect(await notFoundResponse?.text()).toContain('Hello from the static index.html file') - await server.close() - await fixture.destroy() - }) + await server.close() + await fixture.destroy() + }) - test('Returns static files with configured Netlify headers', async () => { - const fixture = new Fixture() - .withFile( - 'netlify.toml', - `[build] + test('Returns static files with configured Netlify headers', async () => { + const fixture = new Fixture() + .withFile( + 'netlify.toml', + `[build] publish = "dist" [[headers]] for = "/contact/*" @@ -312,10 +313,10 @@ defined on your team and site and much more. Run npx netlify init to get started for = "/*" [headers.values] "X-NF-Hello" = "world"`, - ) - .withFile( - 'vite.config.js', - `import { defineConfig } from 'vite'; + ) + .withFile( + 'vite.config.js', + `import { defineConfig } from 'vite'; import netlify from '@netlify/vite-plugin'; export default defineConfig({ @@ -325,63 +326,63 @@ defined on your team and site and much more. Run npx netlify init to get started }) ] });`, - ) - .withFile( - 'index.html', - ` - - Hello World - Hello from the static index.html file - `, - ) - .withFile( - 'contact/email.html', - ` + ) + .withFile( + 'index.html', + ` + + Hello World + Hello from the static index.html file + `, + ) + .withFile( + 'contact/email.html', + ` Contact us via email Hello from another static file `, - ) - const directory = await fixture.create() - await fixture - .withPackages({ - vite: '6.0.0', - '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + ) + const directory = await fixture.create() + await fixture + .withPackages({ + vite: viteVersion, + '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + }) + .create() + + const { server, url } = await startTestServer({ + root: directory, }) - .create() - const { server, url } = await startTestServer({ - root: directory, - }) - - expect(await page.goto(`${url}/contact/email`).then((r) => r?.headers())).toHaveProperty('x-nf-hello', 'world') - expect(await page.goto(url).then((r) => r?.headers())).toHaveProperty('x-nf-hello', 'world') - expect(await page.goto(`${url}/contact/email`).then((r) => r?.headers())).toHaveProperty( - 'x-contact-type', - 'email', - ) - expect(await page.goto(url).then((r) => r?.headers())).not.toHaveProperty('x-contact-type') + expect(await page.goto(`${url}/contact/email`).then((r) => r?.headers())).toHaveProperty('x-nf-hello', 'world') + expect(await page.goto(url).then((r) => r?.headers())).toHaveProperty('x-nf-hello', 'world') + expect(await page.goto(`${url}/contact/email`).then((r) => r?.headers())).toHaveProperty( + 'x-contact-type', + 'email', + ) + expect(await page.goto(url).then((r) => r?.headers())).not.toHaveProperty('x-contact-type') - await server.close() - await fixture.destroy() - }) + await server.close() + await fixture.destroy() + }) - test('Respects configured Netlify redirects and rewrites', async () => { - const fixture = new Fixture() - .withFile( - 'netlify.toml', - `[[redirects]] - status = 301 - from = "/contact/e-mail" - to = "/contact/email" - [[redirects]] - status = 200 - from = "/beta/*" - to = "/nextgenv3/:splat"`, - ) - .withFile( - 'vite.config.js', - `import { defineConfig } from 'vite'; + test('Respects configured Netlify redirects and rewrites', async () => { + const fixture = new Fixture() + .withFile( + 'netlify.toml', + `[[redirects]] + status = 301 + from = "/contact/e-mail" + to = "/contact/email" + [[redirects]] + status = 200 + from = "/beta/*" + to = "/nextgenv3/:splat"`, + ) + .withFile( + 'vite.config.js', + `import { defineConfig } from 'vite'; import netlify from '@netlify/vite-plugin'; export default defineConfig({ @@ -391,217 +392,162 @@ defined on your team and site and much more. Run npx netlify init to get started }) ] });`, - ) - .withFile( - 'contact/email.html', - ` + ) + .withFile( + 'contact/email.html', + ` Contact us via email Hello from the redirect target `, - ) - .withFile( - 'nextgenv3/pricing.html', - ` + ) + .withFile( + 'nextgenv3/pricing.html', + ` Pricing Hello from the rewrite target `, - ) - const directory = await fixture.create() - await fixture - .withPackages({ - vite: '6.0.0', - '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + ) + const directory = await fixture.create() + await fixture + .withPackages({ + vite: viteVersion, + '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + }) + .create() + + const { server, url } = await startTestServer({ + root: directory, }) - .create() - - const { server, url } = await startTestServer({ - root: directory, - }) - - expect(await page.goto(`${url}/contact/email`).then((r) => r?.text())).toContain('Hello from the redirect target') - expect(await page.goto(`${url}/contact/e-mail`).then((r) => r?.text())).toContain( - 'Hello from the redirect target', - ) - expect(await page.goto(`${url}/beta/pricing`).then((r) => r?.text())).toContain('Hello from the rewrite target') - - await server.close() - await fixture.destroy() - }) - }) - - describe('With @vitejs/plugin-react', () => { - // TODO(serhalp): Skipping on Windows for now. There's an issue on the GitHub Actions - // Windows image with resolving the `src/main.jsx` path for some reason. - test.skipIf(process.platform === 'win32')('Returns static files with configured Netlify headers', async () => { - const fixture = new Fixture() - .withFile( - 'vite.config.js', - `import { defineConfig } from 'vite'; - import netlify from '@netlify/vite-plugin'; - import react from '@vitejs/plugin-react'; - export default defineConfig({ - plugins: [ - netlify({ - middleware: true - }), - react(), - ] - });`, - ) - .withFile( - 'netlify.toml', - `[[headers]] - for = "/" - [headers.values] - "X-NF-Hello" = "world"`, - ) - .withFile( - 'index.html', - ` - - - - Hello from SSR - - -
- - - `, - ) - .withFile( - 'src/main.jsx', - `import { StrictMode } from 'react' - import { createRoot } from 'react-dom/client' - import App from './App.jsx' - import './index.css' - - createRoot(document.getElementById('root')).render( - - - , - )`, - ) - .withFile('src/index.css', 'body { color: red }') - .withFile( - 'src/App.jsx', - `import reactLogo from './assets/react.svg' - - export default () => -
-

Hello from CSR

- -
`, + expect(await page.goto(`${url}/contact/email`).then((r) => r?.text())).toContain( + 'Hello from the redirect target', ) - .withFile( - 'src/assets/react.svg', - '', + expect(await page.goto(`${url}/contact/e-mail`).then((r) => r?.text())).toContain( + 'Hello from the redirect target', ) - const directory = await fixture.create() - await fixture - .withPackages({ - '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), - '@vitejs/plugin-react': '4.5.0', - react: '19.1.0', - 'react-dom': '19.1.0', - vite: '6.3.5', - }) - .create() - - const { server, url } = await startTestServer({ - root: directory, - }) + expect(await page.goto(`${url}/beta/pricing`).then((r) => r?.text())).toContain('Hello from the rewrite target') - const browser = await chromium.launch() - const page = await browser.newPage() - const browserErrorLogs: ConsoleMessage[] = [] - page.on('console', (msg) => { - if (msg.type() === 'error') { - browserErrorLogs.push(msg) - } + await server.close() + await fixture.destroy() }) + }) - const response = await page.goto(url) - expect(response?.status()).toBe(200) - expect(await response?.text()).toContain('Hello from SSR') - expect(response?.headers()).toHaveProperty('x-nf-hello', 'world') - expect(await page.innerHTML('html')).toContain('Hello from CSR') - - // React SPA mode serves index.html for unknown routes - const notFoundResponse = await page.goto(`${url}/wp-admin.php`) - expect(notFoundResponse?.status()).toBe(200) - expect(await notFoundResponse?.text()).toContain('Hello from SSR') - expect(await page.innerHTML('html')).toContain('Hello from CSR') + describe('With @vitejs/plugin-react', () => { + // TODO(serhalp): Skipping on Windows for now. There's an issue on the GitHub Actions + // Windows image with resolving the `src/main.jsx` path for some reason. + test.skipIf(process.platform === 'win32')('Returns static files with configured Netlify headers', async () => { + const fixture = new Fixture() + .withFile( + 'vite.config.js', + `import { defineConfig } from 'vite'; + import netlify from '@netlify/vite-plugin'; + import react from '@vitejs/plugin-react'; - expect( - browserErrorLogs, - `Unexpected error logs in browser: ${JSON.stringify(browserErrorLogs, null, 2)}`, - ).toHaveLength(0) + export default defineConfig({ + plugins: [ + netlify({ + middleware: true + }), + react(), + ] + });`, + ) + .withFile( + 'netlify.toml', + `[[headers]] + for = "/" + [headers.values] + "X-NF-Hello" = "world"`, + ) + .withFile( + 'index.html', + ` + + + + Hello from SSR + + +
+ + + `, + ) + .withFile( + 'src/main.jsx', + `import { StrictMode } from 'react' + import { createRoot } from 'react-dom/client' + import App from './App.jsx' + import './index.css' + + createRoot(document.getElementById('root')).render( + + + , + )`, + ) + .withFile('src/index.css', 'body { color: red }') + .withFile( + 'src/App.jsx', + `import reactLogo from './assets/react.svg' + + export default () => +
+

Hello from CSR

+ +
`, + ) + .withFile( + 'src/assets/react.svg', + '', + ) + const directory = await fixture.create() + await fixture + .withPackages({ + '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + '@vitejs/plugin-react': '4.5.0', + react: '19.1.0', + 'react-dom': '19.1.0', + vite: viteVersion, + }) + .create() + + const { server, url } = await startTestServer({ + root: directory, + }) - await server.close() - await fixture.destroy() - await browser.close() - }) - }) -}) + const browser = await chromium.launch() + const page = await browser.newPage() + const browserErrorLogs: ConsoleMessage[] = [] + page.on('console', (msg) => { + if (msg.type() === 'error') { + browserErrorLogs.push(msg) + } + }) -test('Works with Vite 5', { timeout: 15_000 }, async () => { - const fixture = new Fixture() - .withFile( - 'vite.config.js', - `import { defineConfig } from 'vite'; - import netlify from '@netlify/vite-plugin'; - - export default defineConfig({ - plugins: [netlify()], - });`, - ) - .withFile( - 'index.html', - ` - - Vite 5 Compatibility Test -

Testing with Vite 5

- `, - ) - const directory = await fixture.create() - await fixture - .withPackages({ - vite: '5.0.0', - '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(), + const response = await page.goto(url) + expect(response?.status()).toBe(200) + expect(await response?.text()).toContain('Hello from SSR') + expect(response?.headers()).toHaveProperty('x-nf-hello', 'world') + expect(await page.innerHTML('html')).toContain('Hello from CSR') + + // React SPA mode serves index.html for unknown routes + const notFoundResponse = await page.goto(`${url}/wp-admin.php`) + expect(notFoundResponse?.status()).toBe(200) + expect(await notFoundResponse?.text()).toContain('Hello from SSR') + expect(await page.innerHTML('html')).toContain('Hello from CSR') + + expect( + browserErrorLogs, + `Unexpected error logs in browser: ${JSON.stringify(browserErrorLogs, null, 2)}`, + ).toHaveLength(0) + + await server.close() + await fixture.destroy() + await browser.close() + }) }) - .create() - - const mockLogger = createMockViteLogger() - const { server, url } = await startTestServer({ - root: directory, - logLevel: 'warn', - customLogger: mockLogger, - }) - - const browser = await chromium.launch() - const page = await browser.newPage() - const browserErrorLogs: ConsoleMessage[] = [] - page.on('console', (msg) => { - if (msg.type() === 'error') { - browserErrorLogs.push(msg) - } }) - - expect((globalThis as Record).Netlify).toBeInstanceOf(Object) - expect(process.env).toHaveProperty('NETLIFY_LOCAL', 'true') - expect(process.env).toHaveProperty('CONTEXT', 'dev') - const response = await page.goto(`${url}/index.html`) - expect(response?.status()).toBe(200) - expect(await page.getByRole('heading').textContent()).toBe('Testing with Vite 5') - expect(mockLogger.error).not.toHaveBeenCalled() - expect(mockLogger.warn).not.toHaveBeenCalled() - expect(mockLogger.warnOnce).not.toHaveBeenCalled() - expect(browserErrorLogs).toHaveLength(0) - - await server.close() - await fixture.destroy() }) From 3c8cdc430c8e235989bdd05a7d66df7ab5e7a9b9 Mon Sep 17 00:00:00 2001 From: Philippe Serhal Date: Fri, 6 Jun 2025 11:21:11 -0400 Subject: [PATCH 3/3] chore: empty commit