diff --git a/packages/integrations/netlify/test/functions/cookies.test.js b/packages/integrations/netlify/test/functions/cookies.test.js index 6ef16763ed5d..2e5bd03dc1ef 100644 --- a/packages/integrations/netlify/test/functions/cookies.test.js +++ b/packages/integrations/netlify/test/functions/cookies.test.js @@ -2,64 +2,58 @@ 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; +describe('Cookies', { timeout: 120000 }, () => { + let fixture; - before(async () => { - fixture = await loadFixture({ root: new URL('./fixtures/cookies/', import.meta.url) }); - await fixture.build(); - }); + 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('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('Can set partitioned cookie', 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/partitioned'), {}); - assert.equal(resp.status, 200); - const cookie = resp.headers.getSetCookie()[0]; - assert.ok(cookie.includes('Partitioned'), 'Cookie should include Partitioned attribute'); - }); + it('Can set partitioned cookie', 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/partitioned'), {}); + assert.equal(resp.status, 200); + const cookie = resp.headers.getSetCookie()[0]; + assert.ok(cookie.includes('Partitioned'), 'Cookie should include Partitioned attribute'); + }); - 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, - }, -); + 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); + }); +}); diff --git a/packages/integrations/netlify/test/functions/edge-middleware.test.js b/packages/integrations/netlify/test/functions/edge-middleware.test.js index 6d255f4dc7f9..2e84d3048028 100644 --- a/packages/integrations/netlify/test/functions/edge-middleware.test.js +++ b/packages/integrations/netlify/test/functions/edge-middleware.test.js @@ -2,65 +2,59 @@ 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('middlewareMode: classic', () => { - 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, - ); - }); +describe('Middleware', { timeout: 120000 }, () => { + const root = new URL('./fixtures/middleware/', import.meta.url); + + describe('middlewareMode: classic', () => { + let fixture; + before(async () => { + process.env.EDGE_MIDDLEWARE = 'false'; + fixture = await loadFixture({ root }); + await fixture.build(); + }); - 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('Middleware'), true); - }); + it('emits no edge function', async () => { + assert.equal( + fixture.pathExists('../.netlify/v1/edge-functions/middleware/middleware.mjs'), + false, + ); + }); - after(async () => { - process.env.EDGE_MIDDLEWARE = undefined; - await fixture.clean(); - }); + 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('Middleware'), true); }); - describe('middlewareMode: edge', () => { - let fixture; - before(async () => { - process.env.EDGE_MIDDLEWARE = 'true'; - fixture = await loadFixture({ root }); - await fixture.build(); - }); + after(async () => { + process.env.EDGE_MIDDLEWARE = undefined; + await fixture.clean(); + }); + }); + + describe('middlewareMode: edge', () => { + 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('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(''), true); - }); + it.skip('does not apply middleware during prerendering', async () => { + const prerenderedPage = await fixture.readFile('prerender/index.html'); + assert.equal(prerenderedPage.includes(''), true); + }); - after(async () => { - process.env.EDGE_MIDDLEWARE = undefined; - await fixture.clean(); - }); + after(async () => { + process.env.EDGE_MIDDLEWARE = undefined; + await fixture.clean(); }); - }, - { - timeout: 120000, - }, -); + }); +}); diff --git a/packages/integrations/netlify/test/functions/image-cdn.test.js b/packages/integrations/netlify/test/functions/image-cdn.test.js index 8d6196817607..c398130366a2 100644 --- a/packages/integrations/netlify/test/functions/image-cdn.test.js +++ b/packages/integrations/netlify/test/functions/image-cdn.test.js @@ -4,179 +4,169 @@ import { remotePatternToRegex } from '@astrojs/netlify'; import { loadFixture } from '../../../../astro/test/test-utils.js'; import imageService from '../../dist/image-service.js'; -describe( - 'Image CDN', - () => { - const root = new URL('./fixtures/middleware/', import.meta.url); - - describe('configuration', () => { - after(() => { - process.env.DISABLE_IMAGE_CDN = undefined; - }); - - it('enables Netlify Image CDN', async () => { - const fixture = await loadFixture({ root }); - await fixture.build(); +describe('Image CDN', { timeout: 120000 }, () => { + const root = new URL('./fixtures/middleware/', import.meta.url); - const astronautPage = await fixture.readFile('astronaut/index.html'); - assert.equal(astronautPage.includes(`src="/.netlify/image`), true); - }); + describe('configuration', () => { + after(() => { + process.env.DISABLE_IMAGE_CDN = undefined; + }); - it('respects image CDN opt-out', async () => { - process.env.DISABLE_IMAGE_CDN = 'true'; - const fixture = await loadFixture({ root }); - await fixture.build(); + it('enables Netlify 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); - }); + const astronautPage = await fixture.readFile('astronaut/index.html'); + assert.equal(astronautPage.includes(`src="/.netlify/image`), true); }); - describe('remote image config', () => { - let regexes; + it('respects image CDN opt-out', async () => { + process.env.DISABLE_IMAGE_CDN = 'true'; + const fixture = await loadFixture({ root }); + await fixture.build(); - before(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); + }); + }); - const config = await fixture.readFile('../.netlify/v1/config.json'); - if (config) { - regexes = JSON.parse(config).images.remote_images.map((pattern) => new RegExp(pattern)); - } - }); + describe('remote image config', () => { + let regexes; - it('generates remote image config patterns', async () => { - assert.equal(regexes?.length, 3); - }); + before(async () => { + const fixture = await loadFixture({ root }); + await fixture.build(); - 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', - ); - }); + 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 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('generates remote image config patterns', async () => { + assert.equal(regexes?.length, 3); + }); - 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.', - ); - }); + 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', + ); }); - describe('fit parameter', () => { - it('includes fit parameter in image URL', () => { - const url = imageService.getURL({ - src: 'images/astronaut.jpg', - width: 300, - height: 400, - fit: 'cover', - format: 'webp', - }); - assert.ok(url.includes('fit=cover'), `Expected fit=cover in URL, got: ${url}`); - }); + 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('maps Astro fit values to Netlify equivalents', () => { - const cases = [ - ['contain', 'contain'], - ['cover', 'cover'], - ['fill', 'fill'], - ['inside', 'contain'], - ['outside', 'cover'], - ['scale-down', 'contain'], - ]; - for (const [astroFit, netlifyFit] of cases) { - const url = imageService.getURL({ - src: 'img.jpg', - width: 100, - height: 100, - fit: astroFit, - }); - assert.ok( - url.includes(`fit=${netlifyFit}`), - `Expected fit=${netlifyFit} for astro fit="${astroFit}", got: ${url}`, - ); - } + 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.', + ); + }); + }); + + describe('fit parameter', () => { + it('includes fit parameter in image URL', () => { + const url = imageService.getURL({ + src: 'images/astronaut.jpg', + width: 300, + height: 400, + fit: 'cover', + format: 'webp', }); + assert.ok(url.includes('fit=cover'), `Expected fit=cover in URL, got: ${url}`); + }); - it('omits fit parameter when fit is none or unset', () => { - const withNone = imageService.getURL({ + it('maps Astro fit values to Netlify equivalents', () => { + const cases = [ + ['contain', 'contain'], + ['cover', 'cover'], + ['fill', 'fill'], + ['inside', 'contain'], + ['outside', 'cover'], + ['scale-down', 'contain'], + ]; + for (const [astroFit, netlifyFit] of cases) { + const url = imageService.getURL({ src: 'img.jpg', width: 100, height: 100, - fit: 'none', + fit: astroFit, }); assert.ok( - !withNone.includes('fit='), - `Expected no fit param for fit="none", got: ${withNone}`, + url.includes(`fit=${netlifyFit}`), + `Expected fit=${netlifyFit} for astro fit="${astroFit}", got: ${url}`, ); + } + }); - const withoutFit = imageService.getURL({ src: 'img.jpg', width: 100, height: 100 }); - assert.ok( - !withoutFit.includes('fit='), - `Expected no fit param when unset, got: ${withoutFit}`, - ); + it('omits fit parameter when fit is none or unset', () => { + const withNone = imageService.getURL({ + src: 'img.jpg', + width: 100, + height: 100, + fit: 'none', }); + assert.ok( + !withNone.includes('fit='), + `Expected no fit param for fit="none", got: ${withNone}`, + ); + + const withoutFit = imageService.getURL({ src: 'img.jpg', width: 100, height: 100 }); + assert.ok( + !withoutFit.includes('fit='), + `Expected no fit param when unset, got: ${withoutFit}`, + ); }); - }, - { - timeout: 120000, - }, -); + }); +}); diff --git a/packages/integrations/netlify/test/functions/include-files.test.js b/packages/integrations/netlify/test/functions/include-files.test.js index e54e116a78c3..31446785095f 100644 --- a/packages/integrations/netlify/test/functions/include-files.test.js +++ b/packages/integrations/netlify/test/functions/include-files.test.js @@ -6,179 +6,161 @@ import * as cheerio from 'cheerio'; import { globSync } from 'tinyglobby'; import { loadFixture } from '../../../../astro/test/test-utils.js'; -describe( - 'Included vite assets files', - () => { - let fixture; - - const root = new URL('./fixtures/includes/', import.meta.url); - const expectedCwd = new URL('.netlify/v1/functions/ssr/packages/integrations/netlify/', root); - - const expectedAssetsInclude = ['./*.json']; - const excludedAssets = ['./files/exclude-asset.json']; - - before(async () => { - fixture = await loadFixture({ - root, - vite: { - assetsInclude: expectedAssetsInclude, - }, - adapter: netlify({ - excludeFiles: excludedAssets, - }), - }); - await fixture.build(); - }); +describe('Included vite assets files', { timeout: 120000 }, () => { + let fixture; - it('Emits vite assets files', async () => { - for (const pattern of expectedAssetsInclude) { - const files = globSync(pattern); - for (const file of files) { - assert.ok( - existsSync(new URL(file, expectedCwd)), - `Expected file ${pattern} to exist in build`, - ); - } - } + const root = new URL('./fixtures/includes/', import.meta.url); + const expectedCwd = new URL('.netlify/v1/functions/ssr/packages/integrations/netlify/', root); + + const expectedAssetsInclude = ['./*.json']; + const excludedAssets = ['./files/exclude-asset.json']; + + before(async () => { + fixture = await loadFixture({ + root, + vite: { + assetsInclude: expectedAssetsInclude, + }, + adapter: netlify({ + excludeFiles: excludedAssets, + }), }); + await fixture.build(); + }); - it('Does not include vite assets files when excluded', async () => { - for (const file of excludedAssets) { + it('Emits vite assets files', async () => { + for (const pattern of expectedAssetsInclude) { + const files = globSync(pattern); + for (const file of files) { assert.ok( - !existsSync(new URL(file, expectedCwd)), - `Expected file ${file} to not exist in build`, + existsSync(new URL(file, expectedCwd)), + `Expected file ${pattern} to exist in build`, ); } + } + }); + + it('Does not include vite assets files when excluded', async () => { + for (const file of excludedAssets) { + assert.ok( + !existsSync(new URL(file, expectedCwd)), + `Expected file ${file} to not exist in build`, + ); + } + }); + + after(async () => { + await fixture.clean(); + }); +}); + +describe('Included files', { timeout: 120000 }, () => { + let fixture; + + const root = new URL('./fixtures/includes/', import.meta.url); + const expectedCwd = new URL( + '.netlify/v1/functions/ssr/packages/integrations/netlify/test/functions/fixtures/includes/', + root, + ); + + const expectedFiles = [ + './files/include-this.txt', + './files/also-this.csv', + './files/subdirectory/and-this.csv', + ]; + + before(async () => { + fixture = await loadFixture({ + root, + adapter: netlify({ + includeFiles: expectedFiles, + }), }); - - after(async () => { - await fixture.clean(); - }); - }, - { - timeout: 120000, - }, -); - -describe( - 'Included files', - () => { - let fixture; - - const root = new URL('./fixtures/includes/', import.meta.url); - const expectedCwd = new URL( - '.netlify/v1/functions/ssr/packages/integrations/netlify/test/functions/fixtures/includes/', + await fixture.build(); + }); + + it('Emits include files', async () => { + for (const file of expectedFiles) { + assert.ok(existsSync(new URL(file, expectedCwd)), `Expected file ${file} to exist`); + } + }); + + it('Can load included files correctly', async () => { + const entryURL = new URL( + './fixtures/includes/.netlify/v1/functions/ssr/ssr.mjs', + import.meta.url, + ); + const { default: handler } = await import(entryURL); + const resp = await handler(new Request('http://example.com/?file=include-this.txt'), {}); + const html = await resp.text(); + const $ = cheerio.load(html); + assert.equal($('h1').text(), 'hello'); + }); + + it('Includes traced node modules with symlinks', async () => { + const expected = new URL( + '.netlify/v1/functions/ssr/node_modules/.pnpm/cowsay@1.6.0/node_modules/cowsay/cows/happy-whale.cow', root, ); + assert.ok(existsSync(expected, 'Expected excluded file to exist in default build')); + }); - const expectedFiles = [ - './files/include-this.txt', - './files/also-this.csv', - './files/subdirectory/and-this.csv', - ]; - - before(async () => { - fixture = await loadFixture({ - root, - adapter: netlify({ - includeFiles: expectedFiles, - }), - }); - await fixture.build(); - }); + after(async () => { + await fixture.clean(); + }); +}); - it('Emits include files', async () => { - for (const file of expectedFiles) { - assert.ok(existsSync(new URL(file, expectedCwd)), `Expected file ${file} to exist`); - } - }); +describe('Excluded files', { timeout: 120000 }, () => { + let fixture; - it('Can load included files correctly', async () => { - const entryURL = new URL( - './fixtures/includes/.netlify/v1/functions/ssr/ssr.mjs', - import.meta.url, - ); - const { default: handler } = await import(entryURL); - const resp = await handler(new Request('http://example.com/?file=include-this.txt'), {}); - const html = await resp.text(); - const $ = cheerio.load(html); - assert.equal($('h1').text(), 'hello'); - }); + const root = new URL('./fixtures/includes/', import.meta.url); + const expectedCwd = new URL( + '.netlify/v1/functions/ssr/packages/integrations/netlify/test/functions/fixtures/includes/', + root, + ); - it('Includes traced node modules with symlinks', async () => { - const expected = new URL( - '.netlify/v1/functions/ssr/node_modules/.pnpm/cowsay@1.6.0/node_modules/cowsay/cows/happy-whale.cow', - root, - ); - assert.ok(existsSync(expected, 'Expected excluded file to exist in default build')); - }); + const includeFiles = ['./files/**/*.txt']; + const excludedTxt = ['./files/subdirectory/not-this.txt', './files/subdirectory/or-this.txt']; + const excludeFiles = [...excludedTxt, '../../../../../../../node_modules/.pnpm/cowsay@*/**']; - after(async () => { - await fixture.clean(); - }); - }, - { - timeout: 120000, - }, -); - -describe( - 'Excluded files', - () => { - let fixture; - - const root = new URL('./fixtures/includes/', import.meta.url); - const expectedCwd = new URL( - '.netlify/v1/functions/ssr/packages/integrations/netlify/test/functions/fixtures/includes/', + before(async () => { + fixture = await loadFixture({ root, - ); - - const includeFiles = ['./files/**/*.txt']; - const excludedTxt = ['./files/subdirectory/not-this.txt', './files/subdirectory/or-this.txt']; - const excludeFiles = [...excludedTxt, '../../../../../../../node_modules/.pnpm/cowsay@*/**']; - - before(async () => { - fixture = await loadFixture({ - root, - adapter: netlify({ - includeFiles: includeFiles, - excludeFiles: excludeFiles, - }), - }); - await fixture.build(); + adapter: netlify({ + includeFiles: includeFiles, + excludeFiles: excludeFiles, + }), }); + await fixture.build(); + }); - it('Excludes traced node modules', async () => { - const expected = new URL( - '.netlify/v1/functions/ssr/node_modules/.pnpm/cowsay@1.6.0/node_modules/cowsay/cows/happy-whale.cow', - root, - ); - assert.ok(!existsSync(expected), 'Expected excluded file to not exist in build'); - }); + it('Excludes traced node modules', async () => { + const expected = new URL( + '.netlify/v1/functions/ssr/node_modules/.pnpm/cowsay@1.6.0/node_modules/cowsay/cows/happy-whale.cow', + root, + ); + assert.ok(!existsSync(expected), 'Expected excluded file to not exist in build'); + }); - it('Does not include files when excluded', async () => { - for (const pattern of includeFiles) { - const files = globSync(pattern, { ignore: excludedTxt }); - for (const file of files) { - assert.ok( - existsSync(new URL(file, expectedCwd)), - `Expected file ${pattern} to exist in build`, - ); - } - } - for (const file of excludedTxt) { + it('Does not include files when excluded', async () => { + for (const pattern of includeFiles) { + const files = globSync(pattern, { ignore: excludedTxt }); + for (const file of files) { assert.ok( - !existsSync(new URL(file, expectedCwd)), - `Expected file ${file} to not exist in build`, + existsSync(new URL(file, expectedCwd)), + `Expected file ${pattern} to exist in build`, ); } - }); + } + for (const file of excludedTxt) { + assert.ok( + !existsSync(new URL(file, expectedCwd)), + `Expected file ${file} to not exist in build`, + ); + } + }); - after(async () => { - await fixture.clean(); - }); - }, - { - timeout: 120000, - }, -); + after(async () => { + await fixture.clean(); + }); +}); diff --git a/packages/integrations/netlify/test/functions/redirects.test.js b/packages/integrations/netlify/test/functions/redirects.test.js index 2c55aecb9ec5..93df3f16202f 100644 --- a/packages/integrations/netlify/test/functions/redirects.test.js +++ b/packages/integrations/netlify/test/functions/redirects.test.js @@ -3,66 +3,60 @@ 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; +describe('SSR - Redirects', { timeout: 120000 }, () => { + let fixture; - before(async () => { - fixture = await loadFixture({ root: new URL('./fixtures/redirects/', import.meta.url) }); - await fixture.build(); - }); + 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+/); - // based on https://github.com/withastro/astro/issues/16030 for the default option `trailingSlash: 'ignore'` both variants should be generated - assert.deepEqual(parts, ['', '/other/', '/', '301', '/other', '/', '301', '']); - }); + it('Creates a redirects file', async () => { + const redirects = await fixture.readFile('./_redirects'); + const parts = redirects.split(/\s+/); + // based on https://github.com/withastro/astro/issues/16030 for the default option `trailingSlash: 'ignore'` both variants should be generated + assert.deepEqual(parts, ['', '/other/', '/', '301', '/other', '/', '301', '']); + }); - 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('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('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(); + it('does not pass through 404 request', async () => { + let testServerCalls = 0; + const testServer = createServer((_req, res) => { + testServerCalls++; + res.writeHead(200); + res.end(); }); - }, - { - timeout: 120000, - }, -); + 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(); + }); +}); diff --git a/packages/integrations/netlify/test/functions/skew-protection.test.js b/packages/integrations/netlify/test/functions/skew-protection.test.js index ee5f8c840689..6d639d83b586 100644 --- a/packages/integrations/netlify/test/functions/skew-protection.test.js +++ b/packages/integrations/netlify/test/functions/skew-protection.test.js @@ -3,68 +3,62 @@ import { readFile } from 'node:fs/promises'; import { before, describe, it } from 'node:test'; import { loadFixture } from '../../../../astro/test/test-utils.js'; -describe( - 'Skew Protection', - () => { - let fixture; +describe('Skew Protection', { timeout: 120000 }, () => { + let fixture; - before(async () => { - // Set DEPLOY_ID env var for the test - process.env.DEPLOY_ID = 'test-deploy-123'; + before(async () => { + // Set DEPLOY_ID env var for the test + process.env.DEPLOY_ID = 'test-deploy-123'; - fixture = await loadFixture({ - root: new URL('./fixtures/skew-protection/', import.meta.url), - }); - await fixture.build(); - - // Clean up - delete process.env.DEPLOY_ID; + fixture = await loadFixture({ + root: new URL('./fixtures/skew-protection/', import.meta.url), }); + await fixture.build(); - it('Server islands inline adapter headers', async () => { - // Render a page with server islands and check the HTML contains inline headers - const entryURL = new URL( - './fixtures/skew-protection/.netlify/v1/functions/ssr/ssr.mjs', - import.meta.url, - ); - const { default: handler } = await import(entryURL); - const resp = await handler(new Request('http://example.com/server-island'), {}); - const html = await resp.text(); + // Clean up + delete process.env.DEPLOY_ID; + }); - // Check that the HTML contains the inline headers in the server island script - // Should have something like: const headers = new Headers({"X-Netlify-Deploy-ID":"test-deploy-123"}); - assert.ok( - html.includes('test-deploy-123'), - 'Expected server island HTML to include deploy ID in inline script', - ); - }); + it('Server islands inline adapter headers', async () => { + // Render a page with server islands and check the HTML contains inline headers + const entryURL = new URL( + './fixtures/skew-protection/.netlify/v1/functions/ssr/ssr.mjs', + import.meta.url, + ); + const { default: handler } = await import(entryURL); + const resp = await handler(new Request('http://example.com/server-island'), {}); + const html = await resp.text(); - it('Manifest contains internalFetchHeaders', async () => { - // The manifest is embedded in the build output - // Check the manifest file which contains the serialized manifest - const manifestURL = new URL( - './fixtures/skew-protection/.netlify/build/chunks/', - import.meta.url, - ); + // Check that the HTML contains the inline headers in the server island script + // Should have something like: const headers = new Headers({"X-Netlify-Deploy-ID":"test-deploy-123"}); + assert.ok( + html.includes('test-deploy-123'), + 'Expected server island HTML to include deploy ID in inline script', + ); + }); - // Find the manifest file (it has a hash in the name) - const { readdir } = await import('node:fs/promises'); - const files = await readdir(manifestURL); - let found = false; - for (const file of files) { - const contents = await readFile(new URL(file, manifestURL), 'utf-8'); - if (contents.includes('"internalFetchHeaders":{"X-Netlify-Deploy-ID":"test-deploy-123"}')) { - found = true; - break; - } + it('Manifest contains internalFetchHeaders', async () => { + // The manifest is embedded in the build output + // Check the manifest file which contains the serialized manifest + const manifestURL = new URL( + './fixtures/skew-protection/.netlify/build/chunks/', + import.meta.url, + ); + + // Find the manifest file (it has a hash in the name) + const { readdir } = await import('node:fs/promises'); + const files = await readdir(manifestURL); + let found = false; + for (const file of files) { + const contents = await readFile(new URL(file, manifestURL), 'utf-8'); + if (contents.includes('"internalFetchHeaders":{"X-Netlify-Deploy-ID":"test-deploy-123"}')) { + found = true; + break; } - assert.ok( - found, - 'Manifest should include internalFetchHeaders field with the correct deploy ID value', - ); - }); - }, - { - timeout: 120000, - }, -); + } + assert.ok( + found, + 'Manifest should include internalFetchHeaders field with the correct deploy ID value', + ); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2aba4345a3e3..7867b4d32a99 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1921,18 +1921,6 @@ importers: specifier: ^5.54.0 version: 5.55.3 - packages/astro/test/fixtures/alias-tsconfig-baseurl-only: - dependencies: - '@astrojs/svelte': - specifier: workspace:* - version: link:../../../../integrations/svelte - astro: - specifier: workspace:* - version: link:../../.. - svelte: - specifier: ^5.54.0 - version: 5.55.3 - packages/astro/test/fixtures/alias-tsconfig-no-baseurl: dependencies: astro: @@ -2447,12 +2435,6 @@ importers: specifier: ^4.2.2 version: 4.2.2 - packages/astro/test/fixtures/astro-sitemap-rss: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - packages/astro/test/fixtures/astro-slot-with-client: dependencies: '@astrojs/preact': @@ -2637,12 +2619,6 @@ importers: specifier: workspace:* version: link:../../.. - packages/astro/test/fixtures/config-path: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - packages/astro/test/fixtures/config-vite: dependencies: astro: @@ -2730,12 +2706,6 @@ importers: specifier: workspace:* version: link:../../.. - packages/astro/test/fixtures/content-collections-cache-invalidation: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - packages/astro/test/fixtures/content-collections-empty-dir: dependencies: astro: @@ -2766,12 +2736,6 @@ importers: specifier: ^4.3.6 version: 4.3.6 - packages/astro/test/fixtures/content-collections-same-contents: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - packages/astro/test/fixtures/content-collections-type-inference: dependencies: astro: @@ -3191,12 +3155,6 @@ importers: specifier: workspace:* version: link:../../.. - packages/astro/test/fixtures/custom-500-middleware: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - packages/astro/test/fixtures/custom-assets-name: dependencies: '@astrojs/node': @@ -3320,12 +3278,6 @@ importers: specifier: workspace:* version: link:../../.. - packages/astro/test/fixtures/feature-support-message-suppresion: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - packages/astro/test/fixtures/fetch: dependencies: '@astrojs/preact': @@ -3380,12 +3332,6 @@ importers: specifier: workspace:* version: link:../../.. - packages/astro/test/fixtures/head-injection: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - packages/astro/test/fixtures/hmr-markdown: dependencies: astro: @@ -3597,24 +3543,12 @@ importers: specifier: workspace:* version: link:../../.. - packages/astro/test/fixtures/middleware-sequence-request-clone: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - packages/astro/test/fixtures/middleware-sequence-rewrite: dependencies: astro: specifier: workspace:* version: link:../../.. - packages/astro/test/fixtures/middleware-ssg: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - packages/astro/test/fixtures/middleware-tailwind: dependencies: '@tailwindcss/vite': @@ -3627,12 +3561,6 @@ importers: specifier: ^4.2.2 version: 4.2.2 - packages/astro/test/fixtures/middleware-virtual: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - packages/astro/test/fixtures/minification-html: dependencies: astro: @@ -4230,21 +4158,6 @@ importers: specifier: ^10.29.0 version: 10.29.0 - packages/astro/test/fixtures/ssr-split-manifest: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - - packages/astro/test/fixtures/ssr-trailing-slash: - dependencies: - '@astrojs/node': - specifier: workspace:* - version: link:../../../../integrations/node - astro: - specifier: workspace:* - version: link:../../.. - packages/astro/test/fixtures/static-build: dependencies: '@astrojs/preact': @@ -4448,24 +4361,6 @@ importers: specifier: ^3.5.30 version: 3.5.30(typescript@5.9.3) - packages/astro/test/fixtures/with-endpoint-routes: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - - packages/astro/test/fixtures/with-subpath-no-trailing-slash: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - - packages/astro/test/fixtures/without-site-config: - dependencies: - astro: - specifier: workspace:* - version: link:../../.. - packages/create-astro: dependencies: '@astrojs/cli-kit':