- `,
- )
- 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, images, 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, images, 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,81 +392,83 @@ 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()
- })
-
- test('Handles Image CDN requests', async () => {
- const IMAGE_WIDTH = 800
- const IMAGE_HEIGHT = 400
+ 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')
- const remoteServer = new HTTPServer(
- createImageServerHandler(() => {
- return { width: IMAGE_WIDTH, height: IMAGE_HEIGHT }
- }),
- )
+ await server.close()
+ await fixture.destroy()
+ })
- const remoteServerAddress = await remoteServer.start()
+ test('Handles Image CDN requests', async () => {
+ const IMAGE_WIDTH = 800
+ const IMAGE_HEIGHT = 400
- const fixture = new Fixture()
- .withFile(
- 'netlify.toml',
- `[images]
- remote_images = [
- "^${remoteServerAddress}/allowed/.*"
- ]`,
+ const remoteServer = new HTTPServer(
+ createImageServerHandler(() => {
+ return { width: IMAGE_WIDTH, height: IMAGE_HEIGHT }
+ }),
)
- .withFile(
- 'vite.config.js',
- `import { defineConfig } from 'vite';
- import netlify from '@netlify/vite-plugin';
- export default defineConfig({
- plugins: [
- netlify({
- middleware: true,
- })
- ]
- });`,
- )
- .withFile(
- 'index.html',
- `
+ const remoteServerAddress = await remoteServer.start()
+
+ const fixture = new Fixture()
+ .withFile(
+ 'netlify.toml',
+ `[images]
+ remote_images = [
+ "^${remoteServerAddress}/allowed/.*"
+ ]`,
+ )
+ .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
@@ -475,165 +478,166 @@ defined on your team and site and much more. Run npx netlify init to get started
`,
- )
- .withFile('local/image.jpg', await generateImage(IMAGE_WIDTH, IMAGE_HEIGHT))
-
- const directory = await fixture.create()
- await fixture
- .withPackages({
- vite: '6.0.0',
- '@netlify/vite-plugin': pathToFileURL(path.resolve(directory, PLUGIN_PATH)).toString(),
+ )
+ .withFile('local/image.jpg', await generateImage(IMAGE_WIDTH, IMAGE_HEIGHT))
+
+ 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, url } = await startTestServer({
+ root: directory,
+ logLevel: 'info',
+ customLogger: mockLogger,
})
- .create()
- const mockLogger = createMockViteLogger()
- const { server, url } = await startTestServer({
- root: directory,
- logLevel: 'info',
- customLogger: mockLogger,
- })
-
- await page.goto(url)
+ await page.goto(url)
- const getImageSize = (locator: Locator) => {
- return locator.evaluate((img: HTMLImageElement) => {
- if (img.naturalWidth === 0 || img.naturalHeight === 0) {
- throw new Error(`Image was not loaded`)
- }
+ const getImageSize = (locator: Locator) => {
+ return locator.evaluate((img: HTMLImageElement) => {
+ if (img.naturalWidth === 0 || img.naturalHeight === 0) {
+ throw new Error(`Image was not loaded`)
+ }
- return {
- width: img.naturalWidth,
- height: img.naturalHeight,
- }
- })
- }
+ return {
+ width: img.naturalWidth,
+ height: img.naturalHeight,
+ }
+ })
+ }
- expect(await getImageSize(page.locator('#local-image'))).toEqual({ width: 100, height: 50 })
- expect(await getImageSize(page.locator('#allowed-remote-image'))).toEqual({ width: 100, height: 50 })
+ expect(await getImageSize(page.locator('#local-image'))).toEqual({ width: 100, height: 50 })
+ expect(await getImageSize(page.locator('#allowed-remote-image'))).toEqual({ width: 100, height: 50 })
- await expect(
- async () => await getImageSize(page.locator('#not-allowed-remote-image')),
- 'Not allowed remote image should not load',
- ).rejects.toThrowError(`Image was not loaded`)
+ await expect(
+ async () => await getImageSize(page.locator('#not-allowed-remote-image')),
+ 'Not allowed remote image should not load',
+ ).rejects.toThrowError(`Image was not loaded`)
- await server.close()
- await fixture.destroy()
+ 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';
+ 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 () =>
-
-