summaryrefslogtreecommitdiff
path: root/packages/integrations/netlify/test
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/netlify/test')
-rw-r--r--packages/integrations/netlify/test/functions/cookies.test.js53
-rw-r--r--packages/integrations/netlify/test/functions/edge-middleware.test.js66
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/.gitignore1
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/cookies/.astro/types.d.ts2
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/cookies/astro.config.mjs11
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/cookies/package.json8
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/cookies/src/env.d.ts1
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/404.astro7
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/index.astro6
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/login.js12
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/middleware/.astro/types.d.ts2
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/middleware/astro.config.mjs19
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/middleware/package.json9
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/middleware/src/astronaut.jpgbin0 -> 149509 bytes
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/middleware/src/env.d.ts1
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/middleware/src/middleware.ts8
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/astronaut.astro9
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/index.astro12
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/prerender.astro13
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/redirects/.astro/types.d.ts2
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/redirects/astro.config.mjs11
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/redirects/package.json8
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/redirects/src/env.d.ts1
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/404.astro5
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/index.astro9
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/nope.astro3
-rw-r--r--packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/team/articles/[...slug].astro27
-rw-r--r--packages/integrations/netlify/test/functions/image-cdn.test.js139
-rw-r--r--packages/integrations/netlify/test/functions/redirects.test.js69
-rw-r--r--packages/integrations/netlify/test/hosted/README.md3
-rw-r--r--packages/integrations/netlify/test/hosted/hosted-astro-project/astro.config.mjs19
-rw-r--r--packages/integrations/netlify/test/hosted/hosted-astro-project/netlify.toml3
-rw-r--r--packages/integrations/netlify/test/hosted/hosted-astro-project/package.json12
-rw-r--r--packages/integrations/netlify/test/hosted/hosted-astro-project/src/assets/penguin.pngbin0 -> 7295878 bytes
-rw-r--r--packages/integrations/netlify/test/hosted/hosted-astro-project/src/env.d.ts1
-rw-r--r--packages/integrations/netlify/test/hosted/hosted-astro-project/src/middleware.ts11
-rw-r--r--packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/country.astro7
-rw-r--r--packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/index.astro13
-rw-r--r--packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/time.astro5
-rw-r--r--packages/integrations/netlify/test/hosted/hosted.test.js29
-rw-r--r--packages/integrations/netlify/test/static/fixtures/redirects/.astro/types.d.ts2
-rw-r--r--packages/integrations/netlify/test/static/fixtures/redirects/astro.config.mjs16
-rw-r--r--packages/integrations/netlify/test/static/fixtures/redirects/package.json8
-rw-r--r--packages/integrations/netlify/test/static/fixtures/redirects/src/env.d.ts1
-rw-r--r--packages/integrations/netlify/test/static/fixtures/redirects/src/pages/index.astro6
-rw-r--r--packages/integrations/netlify/test/static/fixtures/redirects/src/pages/nope.astro3
-rw-r--r--packages/integrations/netlify/test/static/fixtures/redirects/src/pages/team/articles/[...slug].astro25
-rw-r--r--packages/integrations/netlify/test/static/headers.test.js25
-rw-r--r--packages/integrations/netlify/test/static/redirects.test.js34
-rw-r--r--packages/integrations/netlify/test/test-utils.js12
50 files changed, 749 insertions, 0 deletions
diff --git a/packages/integrations/netlify/test/functions/cookies.test.js b/packages/integrations/netlify/test/functions/cookies.test.js
new file mode 100644
index 000000000..9d2565d91
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/cookies.test.js
@@ -0,0 +1,53 @@
+import * as assert from 'node:assert/strict';
+import { before, describe, it } from 'node:test';
+import { loadFixture } from '../../../../astro/test/test-utils.js';
+
+describe(
+ 'Cookies',
+ () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ root: new URL('./fixtures/cookies/', import.meta.url) });
+ await fixture.build();
+ });
+
+ it('Can set multiple', async () => {
+ const entryURL = new URL(
+ './fixtures/cookies/.netlify/v1/functions/ssr/ssr.mjs',
+ import.meta.url
+ );
+ const { default: handler } = await import(entryURL);
+ const resp = await handler(
+ new Request('http://example.com/login', { method: 'POST', body: '{}' }),
+ {}
+ );
+ assert.equal(resp.status, 301);
+ assert.equal(resp.headers.get('location'), '/');
+ assert.deepEqual(resp.headers.getSetCookie(), ['foo=foo; HttpOnly', 'bar=bar; HttpOnly']);
+ });
+
+ it('renders dynamic 404 page', async () => {
+ const entryURL = new URL(
+ './fixtures/cookies/.netlify/v1/functions/ssr/ssr.mjs',
+ import.meta.url
+ );
+ const { default: handler } = await import(entryURL);
+ const resp = await handler(
+ new Request('http://example.com/nonexistant-page', {
+ headers: {
+ 'x-test': 'bar',
+ },
+ }),
+ {}
+ );
+ assert.equal(resp.status, 404);
+ const text = await resp.text();
+ assert.equal(text.includes('This is my custom 404 page'), true);
+ assert.equal(text.includes('x-test: bar'), true);
+ });
+ },
+ {
+ timeout: 120000,
+ }
+);
diff --git a/packages/integrations/netlify/test/functions/edge-middleware.test.js b/packages/integrations/netlify/test/functions/edge-middleware.test.js
new file mode 100644
index 000000000..683ec3b01
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/edge-middleware.test.js
@@ -0,0 +1,66 @@
+import * as assert from 'node:assert/strict';
+import { after, before, describe, it } from 'node:test';
+import { loadFixture } from '../../../../astro/test/test-utils.js';
+
+describe(
+ 'Middleware',
+ () => {
+ const root = new URL('./fixtures/middleware/', import.meta.url);
+
+ describe('edgeMiddleware: false', () => {
+ let fixture;
+ before(async () => {
+ process.env.EDGE_MIDDLEWARE = 'false';
+ fixture = await loadFixture({ root });
+ await fixture.build();
+ });
+
+ it('emits no edge function', async () => {
+ assert.equal(
+ fixture.pathExists('../.netlify/v1/edge-functions/middleware/middleware.mjs'),
+ false
+ );
+ });
+
+ it('applies middleware to static files at build-time', async () => {
+ // prerendered page has middleware applied at build time
+ const prerenderedPage = await fixture.readFile('prerender/index.html');
+ assert.equal(prerenderedPage.includes('<title>Middleware</title>'), true);
+ });
+
+ after(async () => {
+ process.env.EDGE_MIDDLEWARE = undefined;
+ await fixture.clean();
+ });
+ });
+
+ describe('edgeMiddleware: true', () => {
+ let fixture;
+ before(async () => {
+ process.env.EDGE_MIDDLEWARE = 'true';
+ fixture = await loadFixture({ root });
+ await fixture.build();
+ });
+
+ it('emits an edge function', async () => {
+ const contents = await fixture.readFile(
+ '../.netlify/v1/edge-functions/middleware/middleware.mjs'
+ );
+ assert.equal(contents.includes('"Hello world"'), false);
+ });
+
+ it.skip('does not apply middleware during prerendering', async () => {
+ const prerenderedPage = await fixture.readFile('prerender/index.html');
+ assert.equal(prerenderedPage.includes('<title></title>'), true);
+ });
+
+ after(async () => {
+ process.env.EDGE_MIDDLEWARE = undefined;
+ await fixture.clean();
+ });
+ });
+ },
+ {
+ timeout: 120000,
+ }
+);
diff --git a/packages/integrations/netlify/test/functions/fixtures/.gitignore b/packages/integrations/netlify/test/functions/fixtures/.gitignore
new file mode 100644
index 000000000..916f60644
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/.gitignore
@@ -0,0 +1 @@
+**/netlify
diff --git a/packages/integrations/netlify/test/functions/fixtures/cookies/.astro/types.d.ts b/packages/integrations/netlify/test/functions/fixtures/cookies/.astro/types.d.ts
new file mode 100644
index 000000000..03d7cc43f
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/cookies/.astro/types.d.ts
@@ -0,0 +1,2 @@
+/// <reference types="astro/client" />
+/// <reference path="content.d.ts" /> \ No newline at end of file
diff --git a/packages/integrations/netlify/test/functions/fixtures/cookies/astro.config.mjs b/packages/integrations/netlify/test/functions/fixtures/cookies/astro.config.mjs
new file mode 100644
index 000000000..033024c1a
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/cookies/astro.config.mjs
@@ -0,0 +1,11 @@
+import netlify from '@astrojs/netlify';
+import { defineConfig } from 'astro/config';
+
+export default defineConfig({
+ output: 'server',
+ adapter: netlify(),
+ site: `http://example.com`,
+ security: {
+ checkOrigin: false
+ }
+}); \ No newline at end of file
diff --git a/packages/integrations/netlify/test/functions/fixtures/cookies/package.json b/packages/integrations/netlify/test/functions/fixtures/cookies/package.json
new file mode 100644
index 000000000..14257a558
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/cookies/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@test/netlify-cookies",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/netlify": "workspace:"
+ }
+}
diff --git a/packages/integrations/netlify/test/functions/fixtures/cookies/src/env.d.ts b/packages/integrations/netlify/test/functions/fixtures/cookies/src/env.d.ts
new file mode 100644
index 000000000..9bc5cb41c
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/cookies/src/env.d.ts
@@ -0,0 +1 @@
+/// <reference path="../.astro/types.d.ts" /> \ No newline at end of file
diff --git a/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/404.astro b/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/404.astro
new file mode 100644
index 000000000..9049fa0fb
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/404.astro
@@ -0,0 +1,7 @@
+---
+export const prerender = false
+const header = Astro.request.headers.get("x-test")
+---
+
+<p>This is my custom 404 page</p>
+<p>x-test: {header}</p> \ No newline at end of file
diff --git a/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/index.astro b/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/index.astro
new file mode 100644
index 000000000..53e029f04
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/index.astro
@@ -0,0 +1,6 @@
+<html>
+<head><title>Testing</title></head>
+<body>
+ <h1>Testing</h1>
+</body>
+</html>
diff --git a/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/login.js b/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/login.js
new file mode 100644
index 000000000..3f1fe17b5
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/cookies/src/pages/login.js
@@ -0,0 +1,12 @@
+
+export function POST() {
+ const headers = new Headers();
+ headers.append('Set-Cookie', `foo=foo; HttpOnly`);
+ headers.append('Set-Cookie', `bar=bar; HttpOnly`);
+ headers.append('Location', '/');
+
+ return new Response('', {
+ status: 301,
+ headers,
+ });
+}
diff --git a/packages/integrations/netlify/test/functions/fixtures/middleware/.astro/types.d.ts b/packages/integrations/netlify/test/functions/fixtures/middleware/.astro/types.d.ts
new file mode 100644
index 000000000..03d7cc43f
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/middleware/.astro/types.d.ts
@@ -0,0 +1,2 @@
+/// <reference types="astro/client" />
+/// <reference path="content.d.ts" /> \ No newline at end of file
diff --git a/packages/integrations/netlify/test/functions/fixtures/middleware/astro.config.mjs b/packages/integrations/netlify/test/functions/fixtures/middleware/astro.config.mjs
new file mode 100644
index 000000000..0da6bf580
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/middleware/astro.config.mjs
@@ -0,0 +1,19 @@
+import netlify from '@astrojs/netlify';
+import { defineConfig } from 'astro/config';
+
+export default defineConfig({
+ output: 'server',
+ adapter: netlify({
+ edgeMiddleware: process.env.EDGE_MIDDLEWARE === 'true',
+ imageCDN: process.env.DISABLE_IMAGE_CDN ? false : undefined,
+ }),
+ image: {
+ remotePatterns: [{
+ protocol: 'https',
+ hostname: '*.example.org',
+ pathname: '/images/*',
+ }],
+ domains: ['example.net', 'secret.example.edu'],
+ },
+ site: `http://example.com`,
+}); \ No newline at end of file
diff --git a/packages/integrations/netlify/test/functions/fixtures/middleware/package.json b/packages/integrations/netlify/test/functions/fixtures/middleware/package.json
new file mode 100644
index 000000000..ddc811223
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/middleware/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@test/netlify-middleware-without-handler-file",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/netlify": "workspace:",
+ "sharp": "^0.33.5"
+ }
+}
diff --git a/packages/integrations/netlify/test/functions/fixtures/middleware/src/astronaut.jpg b/packages/integrations/netlify/test/functions/fixtures/middleware/src/astronaut.jpg
new file mode 100644
index 000000000..d3326bcc7
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/middleware/src/astronaut.jpg
Binary files differ
diff --git a/packages/integrations/netlify/test/functions/fixtures/middleware/src/env.d.ts b/packages/integrations/netlify/test/functions/fixtures/middleware/src/env.d.ts
new file mode 100644
index 000000000..9bc5cb41c
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/middleware/src/env.d.ts
@@ -0,0 +1 @@
+/// <reference path="../.astro/types.d.ts" /> \ No newline at end of file
diff --git a/packages/integrations/netlify/test/functions/fixtures/middleware/src/middleware.ts b/packages/integrations/netlify/test/functions/fixtures/middleware/src/middleware.ts
new file mode 100644
index 000000000..9790b8755
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/middleware/src/middleware.ts
@@ -0,0 +1,8 @@
+import https from 'node:https';
+
+export const onRequest = (context, next) => {
+ context.locals.title = 'Middleware';
+ context.locals.nodePrefixedImportExists = !!https;
+
+ return next();
+};
diff --git a/packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/astronaut.astro b/packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/astronaut.astro
new file mode 100644
index 000000000..b3da724c3
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/astronaut.astro
@@ -0,0 +1,9 @@
+---
+import { Image } from 'astro:assets';
+import astronautImage from "../astronaut.jpg"
+
+export const prerender = true;
+---
+
+<Image src={astronautImage} alt="an astronaut floating in space" />
+
diff --git a/packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/index.astro b/packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/index.astro
new file mode 100644
index 000000000..d97f70698
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/index.astro
@@ -0,0 +1,12 @@
+---
+const title = Astro.locals.title;
+---
+
+<html>
+<head>
+ <title>{title}</title>
+</head>
+<body>
+<h1>{title}</h1>
+</body>
+</html>
diff --git a/packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/prerender.astro b/packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/prerender.astro
new file mode 100644
index 000000000..f0314c053
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/middleware/src/pages/prerender.astro
@@ -0,0 +1,13 @@
+---
+export const prerender = true;
+const title = Astro.locals.title;
+---
+
+<html>
+<head>
+ <title>{title}</title>
+</head>
+<body>
+<h1>{title}</h1>
+</body>
+</html>
diff --git a/packages/integrations/netlify/test/functions/fixtures/redirects/.astro/types.d.ts b/packages/integrations/netlify/test/functions/fixtures/redirects/.astro/types.d.ts
new file mode 100644
index 000000000..03d7cc43f
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/redirects/.astro/types.d.ts
@@ -0,0 +1,2 @@
+/// <reference types="astro/client" />
+/// <reference path="content.d.ts" /> \ No newline at end of file
diff --git a/packages/integrations/netlify/test/functions/fixtures/redirects/astro.config.mjs b/packages/integrations/netlify/test/functions/fixtures/redirects/astro.config.mjs
new file mode 100644
index 000000000..55613bd91
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/redirects/astro.config.mjs
@@ -0,0 +1,11 @@
+import netlify from '@astrojs/netlify';
+import { defineConfig } from 'astro/config';
+
+export default defineConfig({
+ output: 'static',
+ adapter: netlify(),
+ site: `http://example.com`,
+ redirects: {
+ '/other': '/',
+ },
+});
diff --git a/packages/integrations/netlify/test/functions/fixtures/redirects/package.json b/packages/integrations/netlify/test/functions/fixtures/redirects/package.json
new file mode 100644
index 000000000..9970a81de
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/redirects/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@test/netlify-redirects",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/netlify": "workspace:"
+ }
+}
diff --git a/packages/integrations/netlify/test/functions/fixtures/redirects/src/env.d.ts b/packages/integrations/netlify/test/functions/fixtures/redirects/src/env.d.ts
new file mode 100644
index 000000000..9bc5cb41c
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/redirects/src/env.d.ts
@@ -0,0 +1 @@
+/// <reference path="../.astro/types.d.ts" /> \ No newline at end of file
diff --git a/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/404.astro b/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/404.astro
new file mode 100644
index 000000000..b9e3eda13
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/404.astro
@@ -0,0 +1,5 @@
+---
+export const prerender = true
+---
+
+<p>This is my static 404 page</p> \ No newline at end of file
diff --git a/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/index.astro b/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/index.astro
new file mode 100644
index 000000000..41f740c4c
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/index.astro
@@ -0,0 +1,9 @@
+---
+export const prerender = false;
+---
+<html>
+<head><title>Testing</title></head>
+<body>
+ <h1>Testing</h1>
+</body>
+</html>
diff --git a/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/nope.astro b/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/nope.astro
new file mode 100644
index 000000000..f48d767ee
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/nope.astro
@@ -0,0 +1,3 @@
+---
+return Astro.redirect('/');
+---
diff --git a/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/team/articles/[...slug].astro b/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/team/articles/[...slug].astro
new file mode 100644
index 000000000..996cd989e
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/fixtures/redirects/src/pages/team/articles/[...slug].astro
@@ -0,0 +1,27 @@
+---
+export const prerender = false;
+
+export const getStaticPaths = (async () => {
+ const posts = [
+ { slug: 'one', data: {draft: false, title: 'One'} },
+ { slug: 'two', data: {draft: false, title: 'Two'} }
+ ];
+ return posts.map((post) => {
+ return {
+ params: { slug: post.slug },
+ props: { draft: post.data.draft, title: post.data.title },
+ };
+ });
+})
+
+const { slug } = Astro.params;
+const { title } = Astro.props;
+---
+<html>
+ <head>
+ <title>{ title }</title>
+ </head>
+ <body>
+ <h1>{ title }</h1>
+ </body>
+</html>
diff --git a/packages/integrations/netlify/test/functions/image-cdn.test.js b/packages/integrations/netlify/test/functions/image-cdn.test.js
new file mode 100644
index 000000000..45b41e4de
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/image-cdn.test.js
@@ -0,0 +1,139 @@
+import * as assert from 'node:assert/strict';
+import { after, before, describe, it } from 'node:test';
+import { remotePatternToRegex } from '@astrojs/netlify';
+import { loadFixture } from '../../../../astro/test/test-utils.js';
+
+describe(
+ 'Image CDN',
+ () => {
+ const root = new URL('./fixtures/middleware/', import.meta.url);
+
+ describe('when running outside of netlify', () => {
+ it('does not enable Image CDN', async () => {
+ const fixture = await loadFixture({ root });
+ await fixture.build();
+
+ const astronautPage = await fixture.readFile('astronaut/index.html');
+ assert.equal(astronautPage.includes(`src="/_astro/astronaut.`), true);
+ });
+ });
+
+ describe('when running inside of netlify', () => {
+ after(() => {
+ process.env.NETLIFY = undefined;
+ process.env.DISABLE_IMAGE_CDN = undefined;
+ });
+
+ it('enables Netlify Image CDN', async () => {
+ process.env.NETLIFY = 'true';
+ const fixture = await loadFixture({ root });
+ await fixture.build();
+
+ const astronautPage = await fixture.readFile('astronaut/index.html');
+ assert.equal(astronautPage.includes(`src="/.netlify/image`), true);
+ });
+
+ it('respects image CDN opt-out', async () => {
+ process.env.NETLIFY = 'true';
+ process.env.DISABLE_IMAGE_CDN = 'true';
+ const fixture = await loadFixture({ root });
+ await fixture.build();
+
+ const astronautPage = await fixture.readFile('astronaut/index.html');
+ assert.equal(astronautPage.includes(`src="/_astro/astronaut.`), true);
+ });
+ });
+
+ describe('remote image config', () => {
+ let regexes;
+
+ before(async () => {
+ const fixture = await loadFixture({ root });
+ await fixture.build();
+
+ const config = await fixture.readFile('../.netlify/v1/config.json');
+ if (config) {
+ regexes = JSON.parse(config).images.remote_images.map((pattern) => new RegExp(pattern));
+ }
+ });
+
+ it('generates remote image config patterns', async () => {
+ assert.equal(regexes?.length, 3);
+ });
+
+ it('generates correct config for domains', async () => {
+ const domain = regexes[0];
+ assert.equal(domain.test('https://example.net/image.jpg'), true);
+ assert.equal(
+ domain.test('https://www.example.net/image.jpg'),
+ false,
+ 'subdomain should not match'
+ );
+ assert.equal(domain.test('http://example.net/image.jpg'), true, 'http should match');
+ assert.equal(
+ domain.test('https://example.net/subdomain/image.jpg'),
+ true,
+ 'subpath should match'
+ );
+ const subdomain = regexes[1];
+ assert.equal(
+ subdomain.test('https://secret.example.edu/image.jpg'),
+ true,
+ 'should match subdomains'
+ );
+ assert.equal(
+ subdomain.test('https://secretxexample.edu/image.jpg'),
+ false,
+ 'should not use dots in domains as wildcards'
+ );
+ });
+
+ it('generates correct config for remotePatterns', async () => {
+ const patterns = regexes[2];
+ assert.equal(
+ patterns.test('https://example.org/images/1.jpg'),
+ true,
+ 'should match domain'
+ );
+ assert.equal(
+ patterns.test('https://www.example.org/images/2.jpg'),
+ true,
+ 'www subdomain should match'
+ );
+ assert.equal(
+ patterns.test('https://www.subdomain.example.org/images/2.jpg'),
+ false,
+ 'second level subdomain should not match'
+ );
+ assert.equal(
+ patterns.test('https://example.org/not-images/2.jpg'),
+ false,
+ 'wrong path should not match'
+ );
+ });
+
+ it('warns when remotepatterns generates an invalid regex', async (t) => {
+ const logger = {
+ warn: t.mock.fn(),
+ };
+ const regex = remotePatternToRegex(
+ {
+ hostname: '*.examp[le.org',
+ pathname: '/images/*',
+ },
+ logger
+ );
+ assert.strictEqual(regex, undefined);
+ const calls = logger.warn.mock.calls;
+ assert.strictEqual(calls.length, 1);
+ assert.equal(
+ calls[0].arguments[0],
+ 'Could not generate a valid regex from the remotePattern "{"hostname":"*.examp[le.org","pathname":"/images/*"}". Please check the syntax.'
+ );
+ });
+ });
+ },
+ {
+ timeout: 120000,
+ }
+);
diff --git a/packages/integrations/netlify/test/functions/redirects.test.js b/packages/integrations/netlify/test/functions/redirects.test.js
new file mode 100644
index 000000000..ac77056c4
--- /dev/null
+++ b/packages/integrations/netlify/test/functions/redirects.test.js
@@ -0,0 +1,69 @@
+import * as assert from 'node:assert/strict';
+import { createServer } from 'node:http';
+import { before, describe, it } from 'node:test';
+import { loadFixture } from '../../../../astro/test/test-utils.js';
+
+describe(
+ 'SSR - Redirects',
+ () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ root: new URL('./fixtures/redirects/', import.meta.url) });
+ await fixture.build();
+ });
+
+ it('Creates a redirects file', async () => {
+ const redirects = await fixture.readFile('./_redirects');
+ const parts = redirects.split(/\s+/);
+ assert.deepEqual(parts, ['', '/other', '/', '301', '']);
+ // Snapshots are not supported in Node.js test yet (https://github.com/nodejs/node/issues/48260)
+ assert.equal(redirects, '\n/other / 301\n');
+ });
+
+ it('Does not create .html files', async () => {
+ let hasErrored = false;
+ try {
+ await fixture.readFile('/other/index.html');
+ } catch {
+ hasErrored = true;
+ }
+ assert.equal(hasErrored, true, 'this file should not exist');
+ });
+
+ it('renders static 404 page', async () => {
+ const entryURL = new URL(
+ './fixtures/redirects/.netlify/v1/functions/ssr/ssr.mjs',
+ import.meta.url
+ );
+ const { default: handler } = await import(entryURL);
+ const resp = await handler(new Request('http://example.com/nonexistant-page'), {});
+ assert.equal(resp.status, 404);
+ assert.equal(resp.headers.get('content-type'), 'text/html; charset=utf-8');
+ const text = await resp.text();
+ assert.equal(text.includes('This is my static 404 page'), true);
+ });
+
+ it('does not pass through 404 request', async () => {
+ let testServerCalls = 0;
+ const testServer = createServer((req, res) => {
+ testServerCalls++;
+ res.writeHead(200);
+ res.end();
+ });
+ testServer.listen(5678);
+ const entryURL = new URL(
+ './fixtures/redirects/.netlify/v1/functions/ssr/ssr.mjs',
+ import.meta.url
+ );
+ const { default: handler } = await import(entryURL);
+ const resp = await handler(new Request('http://localhost:5678/nonexistant-page'), {});
+ assert.equal(resp.status, 404);
+ assert.equal(testServerCalls, 0);
+ testServer.close();
+ });
+ },
+ {
+ timeout: 120000,
+ }
+);
diff --git a/packages/integrations/netlify/test/hosted/README.md b/packages/integrations/netlify/test/hosted/README.md
new file mode 100644
index 000000000..8c1814844
--- /dev/null
+++ b/packages/integrations/netlify/test/hosted/README.md
@@ -0,0 +1,3 @@
+The tests in this folder are done directly on a deployed Netlify website (hosted at https://curious-boba-495d6d.netlify.app) and are not run by the test suite. They instead run every week through a GitHub action.
+
+The purpose of those tests is to make sure that everything works as expected while deployed. In a way, they're as E2E as it gets.
diff --git a/packages/integrations/netlify/test/hosted/hosted-astro-project/astro.config.mjs b/packages/integrations/netlify/test/hosted/hosted-astro-project/astro.config.mjs
new file mode 100644
index 000000000..94cc00f7b
--- /dev/null
+++ b/packages/integrations/netlify/test/hosted/hosted-astro-project/astro.config.mjs
@@ -0,0 +1,19 @@
+import netlify from '@astrojs/netlify';
+import { defineConfig } from 'astro/config';
+
+// https://astro.build/config
+export default defineConfig({
+ output: 'server',
+ adapter: netlify({
+ edgeMiddleware: true,
+ }),
+ image: {
+ remotePatterns: [
+ {
+ protocol: 'https',
+ hostname: 'images.unsplash.com',
+ pathname: '/photo-1567674867291-b2595ac53ab4',
+ },
+ ],
+ },
+});
diff --git a/packages/integrations/netlify/test/hosted/hosted-astro-project/netlify.toml b/packages/integrations/netlify/test/hosted/hosted-astro-project/netlify.toml
new file mode 100644
index 000000000..55c8404ee
--- /dev/null
+++ b/packages/integrations/netlify/test/hosted/hosted-astro-project/netlify.toml
@@ -0,0 +1,3 @@
+[build]
+command = "pnpm run --filter @test/netlify-hosted-astro-project... build"
+publish = "/packages/netlify/test/hosted/hosted-astro-project/dist"
diff --git a/packages/integrations/netlify/test/hosted/hosted-astro-project/package.json b/packages/integrations/netlify/test/hosted/hosted-astro-project/package.json
new file mode 100644
index 000000000..69914a8db
--- /dev/null
+++ b/packages/integrations/netlify/test/hosted/hosted-astro-project/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "@test/netlify-hosted-astro-project",
+ "version": "0.0.0",
+ "private": true,
+ "scripts": {
+ "build": "astro build"
+ },
+ "dependencies": {
+ "@astrojs/netlify": "workspace:*",
+ "astro": "^5.1.6"
+ }
+}
diff --git a/packages/integrations/netlify/test/hosted/hosted-astro-project/src/assets/penguin.png b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/assets/penguin.png
new file mode 100644
index 000000000..218acde5b
--- /dev/null
+++ b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/assets/penguin.png
Binary files differ
diff --git a/packages/integrations/netlify/test/hosted/hosted-astro-project/src/env.d.ts b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/env.d.ts
new file mode 100644
index 000000000..f7cbe9c1d
--- /dev/null
+++ b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/env.d.ts
@@ -0,0 +1 @@
+/// <reference types="astro/client-image" />
diff --git a/packages/integrations/netlify/test/hosted/hosted-astro-project/src/middleware.ts b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/middleware.ts
new file mode 100644
index 000000000..1112a3566
--- /dev/null
+++ b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/middleware.ts
@@ -0,0 +1,11 @@
+import https from 'node:https';
+
+export const onRequest = (context, next) => {
+ console.info(context.netlify);
+ context.locals.middleware = context?.locals?.netlify?.context?.geo?.country?.code ?? null;
+ context.locals.runtime = 'Deno' in globalThis ? 'Deno' : 'Node';
+ context.locals.title = 'Middleware';
+ context.locals.nodePrefixedImportExists = !!https;
+
+ return next();
+};
diff --git a/packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/country.astro b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/country.astro
new file mode 100644
index 000000000..cad7116d6
--- /dev/null
+++ b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/country.astro
@@ -0,0 +1,7 @@
+---
+const country = Astro.locals.middleware;
+---
+
+<h1>{country}</h1>
+<h3>{country ? 'has context' : 'no context'}</h3>
+<h2>{Astro.locals.runtime}</h2>
diff --git a/packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/index.astro b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/index.astro
new file mode 100644
index 000000000..7d2cfcdc3
--- /dev/null
+++ b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/index.astro
@@ -0,0 +1,13 @@
+---
+import { Image } from 'astro:assets';
+import penguin from '../assets/penguin.png';
+---
+
+<Image src={penguin} width={300} alt="" />
+
+<Image
+ src="https://images.unsplash.com/photo-1567674867291-b2595ac53ab4"
+ width={300}
+ height={400}
+ alt="Astro"
+/>
diff --git a/packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/time.astro b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/time.astro
new file mode 100644
index 000000000..873b5c720
--- /dev/null
+++ b/packages/integrations/netlify/test/hosted/hosted-astro-project/src/pages/time.astro
@@ -0,0 +1,5 @@
+---
+const currentTime = new Date().getTime();
+---
+
+{currentTime}
diff --git a/packages/integrations/netlify/test/hosted/hosted.test.js b/packages/integrations/netlify/test/hosted/hosted.test.js
new file mode 100644
index 000000000..b8562ce82
--- /dev/null
+++ b/packages/integrations/netlify/test/hosted/hosted.test.js
@@ -0,0 +1,29 @@
+import * as assert from 'node:assert/strict';
+import { describe, it } from 'node:test';
+
+const NETLIFY_TEST_URL = 'https://curious-boba-495d6d.netlify.app';
+
+describe('Hosted Netlify Tests', () => {
+ it('Image endpoint works', async () => {
+ const image = await fetch(
+ `${NETLIFY_TEST_URL}/_image?href=%2F_astro%2Fpenguin.e9c64733.png&w=300&f=webp`
+ );
+
+ assert.equal(image.status, 200);
+ });
+
+ it('passes context from edge middleware', async () => {
+ const response = await fetch(`${NETLIFY_TEST_URL}/country`);
+ const body = await response.text();
+ assert.match(body, /has context/);
+ assert.match(body, /Deno/);
+ });
+
+ it('Server returns fresh content', async () => {
+ const responseOne = await fetch(`${NETLIFY_TEST_URL}/time`).then((res) => res.text());
+
+ const responseTwo = await fetch(`${NETLIFY_TEST_URL}/time`).then((res) => res.text());
+
+ assert.notEqual(responseOne.body, responseTwo.body);
+ });
+});
diff --git a/packages/integrations/netlify/test/static/fixtures/redirects/.astro/types.d.ts b/packages/integrations/netlify/test/static/fixtures/redirects/.astro/types.d.ts
new file mode 100644
index 000000000..03d7cc43f
--- /dev/null
+++ b/packages/integrations/netlify/test/static/fixtures/redirects/.astro/types.d.ts
@@ -0,0 +1,2 @@
+/// <reference types="astro/client" />
+/// <reference path="content.d.ts" /> \ No newline at end of file
diff --git a/packages/integrations/netlify/test/static/fixtures/redirects/astro.config.mjs b/packages/integrations/netlify/test/static/fixtures/redirects/astro.config.mjs
new file mode 100644
index 000000000..ce441d8b1
--- /dev/null
+++ b/packages/integrations/netlify/test/static/fixtures/redirects/astro.config.mjs
@@ -0,0 +1,16 @@
+import netlify from '@astrojs/netlify';
+import { defineConfig } from 'astro/config';
+
+export default defineConfig({
+ output: 'static',
+ adapter: netlify(),
+ site: "http://example.com",
+ redirects: {
+ '/other': '/',
+ '/two': {
+ status: 302,
+ destination: '/',
+ },
+ '/blog/[...slug]': '/team/articles/[...slug]',
+ },
+});
diff --git a/packages/integrations/netlify/test/static/fixtures/redirects/package.json b/packages/integrations/netlify/test/static/fixtures/redirects/package.json
new file mode 100644
index 000000000..3e543bf35
--- /dev/null
+++ b/packages/integrations/netlify/test/static/fixtures/redirects/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "@test/netlify-static-redirects",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/netlify": "workspace:"
+ }
+}
diff --git a/packages/integrations/netlify/test/static/fixtures/redirects/src/env.d.ts b/packages/integrations/netlify/test/static/fixtures/redirects/src/env.d.ts
new file mode 100644
index 000000000..9bc5cb41c
--- /dev/null
+++ b/packages/integrations/netlify/test/static/fixtures/redirects/src/env.d.ts
@@ -0,0 +1 @@
+/// <reference path="../.astro/types.d.ts" /> \ No newline at end of file
diff --git a/packages/integrations/netlify/test/static/fixtures/redirects/src/pages/index.astro b/packages/integrations/netlify/test/static/fixtures/redirects/src/pages/index.astro
new file mode 100644
index 000000000..53e029f04
--- /dev/null
+++ b/packages/integrations/netlify/test/static/fixtures/redirects/src/pages/index.astro
@@ -0,0 +1,6 @@
+<html>
+<head><title>Testing</title></head>
+<body>
+ <h1>Testing</h1>
+</body>
+</html>
diff --git a/packages/integrations/netlify/test/static/fixtures/redirects/src/pages/nope.astro b/packages/integrations/netlify/test/static/fixtures/redirects/src/pages/nope.astro
new file mode 100644
index 000000000..f48d767ee
--- /dev/null
+++ b/packages/integrations/netlify/test/static/fixtures/redirects/src/pages/nope.astro
@@ -0,0 +1,3 @@
+---
+return Astro.redirect('/');
+---
diff --git a/packages/integrations/netlify/test/static/fixtures/redirects/src/pages/team/articles/[...slug].astro b/packages/integrations/netlify/test/static/fixtures/redirects/src/pages/team/articles/[...slug].astro
new file mode 100644
index 000000000..716d3bd5d
--- /dev/null
+++ b/packages/integrations/netlify/test/static/fixtures/redirects/src/pages/team/articles/[...slug].astro
@@ -0,0 +1,25 @@
+---
+export const getStaticPaths = (async () => {
+ const posts = [
+ { slug: 'one', data: {draft: false, title: 'One'} },
+ { slug: 'two', data: {draft: false, title: 'Two'} }
+ ];
+ return posts.map((post) => {
+ return {
+ params: { slug: post.slug },
+ props: { draft: post.data.draft, title: post.data.title },
+ };
+ });
+})
+
+const { slug } = Astro.params;
+const { title } = Astro.props;
+---
+<html>
+ <head>
+ <title>{ title }</title>
+ </head>
+ <body>
+ <h1>{ title }</h1>
+ </body>
+</html>
diff --git a/packages/integrations/netlify/test/static/headers.test.js b/packages/integrations/netlify/test/static/headers.test.js
new file mode 100644
index 000000000..5c1400098
--- /dev/null
+++ b/packages/integrations/netlify/test/static/headers.test.js
@@ -0,0 +1,25 @@
+import * as assert from 'node:assert/strict';
+import { before, describe, it } from 'node:test';
+import { loadFixture } from '../../../../astro/test/test-utils.js';
+
+describe('SSG - headers', () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ root: new URL('./fixtures/redirects/', import.meta.url) });
+ await fixture.build();
+ });
+
+ it('Generates headers for static assets', async () => {
+ const config = await fixture.readFile('../.netlify/v1/config.json');
+ const headers = JSON.parse(config).headers;
+ assert.deepEqual(headers, [
+ {
+ for: '/_astro/*',
+ values: {
+ 'Cache-Control': 'public, max-age=31536000, immutable',
+ },
+ },
+ ]);
+ });
+});
diff --git a/packages/integrations/netlify/test/static/redirects.test.js b/packages/integrations/netlify/test/static/redirects.test.js
new file mode 100644
index 000000000..cab954831
--- /dev/null
+++ b/packages/integrations/netlify/test/static/redirects.test.js
@@ -0,0 +1,34 @@
+import * as assert from 'node:assert/strict';
+import { before, describe, it } from 'node:test';
+import { loadFixture } from '../../../../astro/test/test-utils.js';
+
+describe('SSG - Redirects', () => {
+ let fixture;
+
+ before(async () => {
+ fixture = await loadFixture({ root: new URL('./fixtures/redirects/', import.meta.url) });
+ await fixture.build();
+ });
+
+ it('Creates a redirects file', async () => {
+ const redirects = await fixture.readFile('./_redirects');
+ const parts = redirects.split(/\s+/);
+ assert.deepEqual(parts, [
+ '',
+
+ '/two',
+ '/',
+ '302',
+
+ '/other',
+ '/',
+ '301',
+
+ '/blog/*',
+ '/team/articles/*/index.html',
+ '301',
+
+ '',
+ ]);
+ });
+});
diff --git a/packages/integrations/netlify/test/test-utils.js b/packages/integrations/netlify/test/test-utils.js
new file mode 100644
index 000000000..7c012e86a
--- /dev/null
+++ b/packages/integrations/netlify/test/test-utils.js
@@ -0,0 +1,12 @@
+import { execa } from 'execa';
+
+/** Returns a process running the Astro CLI. */
+export function cli(/** @type {string[]} */ ...args) {
+ const spawned = execa('npx', ['astro', ...args], {
+ env: { ASTRO_TELEMETRY_DISABLED: true },
+ });
+
+ spawned.stdout.setEncoding('utf8');
+
+ return spawned;
+}