import { afterEach, describe, expect, it, vi } from 'vitest'; import type { NextConfig } from 'next'; async function loadConfig(): Promise { vi.resetModules(); const mod = await import('@/next.config'); return mod.default; } describe('Security response headers', () => { afterEach(() => { delete process.env.ALLOWED_FRAME_ANCESTORS; }); describe('default (no ALLOWED_FRAME_ANCESTORS)', () => { it('nextConfig.headers() is defined', async () => { const config = await loadConfig(); expect(config.headers).toBeDefined(); expect(typeof config.headers).toBe('function'); }); it('includes X-Frame-Options SAMEORIGIN on all routes', async () => { const config = await loadConfig(); const headerGroups = await config.headers!(); const allRouteGroup = headerGroups.find((g) => g.source === '/(.*)')!; expect(allRouteGroup).toBeDefined(); expect(allRouteGroup.headers).toContainEqual({ key: 'X-Frame-Options', value: 'SAMEORIGIN', }); }); it("includes Content-Security-Policy frame-ancestors 'self'", async () => { const config = await loadConfig(); const headerGroups = await config.headers!(); const allRouteGroup = headerGroups.find((g) => g.source === '/(.*)')!; expect(allRouteGroup).toBeDefined(); expect(allRouteGroup.headers).toContainEqual({ key: 'Content-Security-Policy', value: "frame-ancestors 'self'", }); }); }); describe('with ALLOWED_FRAME_ANCESTORS', () => { it('appends allowed origins to frame-ancestors', async () => { process.env.ALLOWED_FRAME_ANCESTORS = 'https://partner.example.com'; const config = await loadConfig(); const headerGroups = await config.headers!(); const allRouteGroup = headerGroups.find((g) => g.source === '/(.*)')!; expect(allRouteGroup.headers).toContainEqual({ key: 'Content-Security-Policy', value: "frame-ancestors 'self' https://partner.example.com", }); }); it('omits X-Frame-Options when custom ancestors are set', async () => { process.env.ALLOWED_FRAME_ANCESTORS = 'https://partner.example.com'; const config = await loadConfig(); const headerGroups = await config.headers!(); const allRouteGroup = headerGroups.find((g) => g.source === '/(.*)')!; const xfo = allRouteGroup.headers.find((h) => h.key === 'X-Frame-Options'); expect(xfo).toBeUndefined(); }); it('supports multiple space-separated origins', async () => { process.env.ALLOWED_FRAME_ANCESTORS = 'https://a.example.com https://b.example.com'; const config = await loadConfig(); const headerGroups = await config.headers!(); const allRouteGroup = headerGroups.find((g) => g.source === '/(.*)')!; expect(allRouteGroup.headers).toContainEqual({ key: 'Content-Security-Policy', value: "frame-ancestors 'self' https://a.example.com https://b.example.com", }); }); }); });