From 6d8fbc30a7041cef943091c7e7ea4f40c52988c7 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 18 Apr 2023 14:56:55 +0200 Subject: [PATCH 01/14] chore: move tests --- packages/runtime/src/templates/edge-shared/rsc-data.test.ts | 0 test/{ => helpers}/analysis.spec.ts | 4 ++-- test/{ => helpers}/edge.spec.ts | 2 +- test/{ => helpers}/functionsMetaData.spec.ts | 4 ++-- test/{ => helpers}/matchers.spec.ts | 4 ++-- test/{ => templates/edge-shared}/rsc-data.spec.ts | 2 +- test/{ => templates}/handlerUtils.spec.ts | 2 +- test/{ => templates}/server.spec.ts | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) delete mode 100644 packages/runtime/src/templates/edge-shared/rsc-data.test.ts rename test/{ => helpers}/analysis.spec.ts (96%) rename test/{ => helpers}/edge.spec.ts (97%) rename test/{ => helpers}/functionsMetaData.spec.ts (94%) rename test/{ => helpers}/matchers.spec.ts (97%) rename test/{ => templates/edge-shared}/rsc-data.spec.ts (94%) rename test/{ => templates}/handlerUtils.spec.ts (97%) rename test/{ => templates}/server.spec.ts (97%) diff --git a/packages/runtime/src/templates/edge-shared/rsc-data.test.ts b/packages/runtime/src/templates/edge-shared/rsc-data.test.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/analysis.spec.ts b/test/helpers/analysis.spec.ts similarity index 96% rename from test/analysis.spec.ts rename to test/helpers/analysis.spec.ts index 223f4bcf4f..d3f54f86a3 100644 --- a/test/analysis.spec.ts +++ b/test/helpers/analysis.spec.ts @@ -1,6 +1,6 @@ -import { extractConfigFromFile } from '../packages/runtime/src/helpers/analysis' +import { extractConfigFromFile } from '../../packages/runtime/src/helpers/analysis' import { resolve } from 'pathe' -import { getDependenciesOfFile } from '../packages/runtime/src/helpers/files' +import { getDependenciesOfFile } from '../../packages/runtime/src/helpers/files' import { dirname } from 'path' describe('static source analysis', () => { beforeEach(() => { diff --git a/test/edge.spec.ts b/test/helpers/edge.spec.ts similarity index 97% rename from test/edge.spec.ts rename to test/helpers/edge.spec.ts index 80d6d782ce..907c46610b 100644 --- a/test/edge.spec.ts +++ b/test/helpers/edge.spec.ts @@ -1,4 +1,4 @@ -import { generateRscDataEdgeManifest } from '../packages/runtime/src/helpers/edge' +import { generateRscDataEdgeManifest } from '../../packages/runtime/src/helpers/edge' import type { PrerenderManifest } from 'next/dist/build' const basePrerenderManifest: PrerenderManifest = { diff --git a/test/functionsMetaData.spec.ts b/test/helpers/functionsMetaData.spec.ts similarity index 94% rename from test/functionsMetaData.spec.ts rename to test/helpers/functionsMetaData.spec.ts index 9c130d454f..e445b39a5b 100644 --- a/test/functionsMetaData.spec.ts +++ b/test/helpers/functionsMetaData.spec.ts @@ -1,8 +1,8 @@ import { readJSON } from 'fs-extra' import mock from 'mock-fs' import { join } from 'pathe' -import { NEXT_PLUGIN_NAME } from '../packages/runtime/src/constants' -import { writeFunctionConfiguration } from '../packages/runtime/src/helpers/functionsMetaData' +import { NEXT_PLUGIN_NAME } from '../../packages/runtime/src/constants' +import { writeFunctionConfiguration } from '../../packages/runtime/src/helpers/functionsMetaData' describe('writeFunctionConfiguration', () => { afterEach(() => { diff --git a/test/matchers.spec.ts b/test/helpers/matchers.spec.ts similarity index 97% rename from test/matchers.spec.ts rename to test/helpers/matchers.spec.ts index b5d3e38697..8201808ed5 100644 --- a/test/matchers.spec.ts +++ b/test/helpers/matchers.spec.ts @@ -1,5 +1,5 @@ -import { makeLocaleOptional, stripLookahead } from '../packages/runtime/src/helpers/matchers' -import { getEdgeFunctionPatternForPage } from '../packages/runtime/src/helpers/edge' +import { makeLocaleOptional, stripLookahead } from '../../packages/runtime/src/helpers/matchers' +import { getEdgeFunctionPatternForPage } from '../../packages/runtime/src/helpers/edge' const makeDataPath = (path: string) => `/_next/data/build-id${path === '/' ? '/index' : path}.json` function checkPath(path: string, regex: string) { diff --git a/test/rsc-data.spec.ts b/test/templates/edge-shared/rsc-data.spec.ts similarity index 94% rename from test/rsc-data.spec.ts rename to test/templates/edge-shared/rsc-data.spec.ts index 27418c72ec..c20bd1c945 100644 --- a/test/rsc-data.spec.ts +++ b/test/templates/edge-shared/rsc-data.spec.ts @@ -1,4 +1,4 @@ -import { getRscDataRouter, PrerenderManifest } from '../packages/runtime/src/templates/edge-shared/rsc-data' +import { getRscDataRouter, PrerenderManifest } from '../../../packages/runtime/src/templates/edge-shared/rsc-data' const basePrerenderManifest: PrerenderManifest = { version: 3, diff --git a/test/handlerUtils.spec.ts b/test/templates/handlerUtils.spec.ts similarity index 97% rename from test/handlerUtils.spec.ts rename to test/templates/handlerUtils.spec.ts index caf41d5c7d..c43b6e847a 100644 --- a/test/handlerUtils.spec.ts +++ b/test/templates/handlerUtils.spec.ts @@ -3,7 +3,7 @@ import { unlocalizeRoute, localizeRoute, localizeDataRoute, -} from '../packages/runtime/src/templates/handlerUtils' +} from '../../packages/runtime/src/templates/handlerUtils' describe('normalizeRoute', () => { it('removes a trailing slash from a route', () => { diff --git a/test/server.spec.ts b/test/templates/server.spec.ts similarity index 97% rename from test/server.spec.ts rename to test/templates/server.spec.ts index 330f82d41a..692760e468 100644 --- a/test/server.spec.ts +++ b/test/templates/server.spec.ts @@ -1,8 +1,8 @@ import { mockRequest } from 'next/dist/server/lib/mock-request' import { Options } from 'next/dist/server/next-server' -import { getNextServer, NextServerType, netlifyApiFetch } from '../packages/runtime/src/templates/handlerUtils' -import { NetlifyNextServer, NetlifyConfig } from '../packages/runtime/src/templates/server' +import { getNextServer, NextServerType, netlifyApiFetch } from '../../packages/runtime/src/templates/handlerUtils' +import { NetlifyNextServer, NetlifyConfig } from '../../packages/runtime/src/templates/server' jest.mock('../packages/runtime/src/templates/handlerUtils', () => { const originalModule = jest.requireActual('../packages/runtime/src/templates/handlerUtils') From 78b2d4a2856080928f2e31bbfd97188b8c5a0d92 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 18 Apr 2023 15:02:41 +0200 Subject: [PATCH 02/14] chore: convert intex test to TS --- test/{index.spec.js => index.spec.ts} | 51 ++++++++++++++------------- 1 file changed, 26 insertions(+), 25 deletions(-) rename test/{index.spec.js => index.spec.ts} (98%) diff --git a/test/index.spec.js b/test/index.spec.ts similarity index 98% rename from test/index.spec.js rename to test/index.spec.ts index 06a22330e8..9a48da0b58 100644 --- a/test/index.spec.js +++ b/test/index.spec.ts @@ -1,4 +1,3 @@ -import execa from 'execa' import { relative } from 'pathe' import { getAllPageDependencies } from '../packages/runtime/src/templates/getPageResolver' @@ -9,8 +8,8 @@ jest.mock('../packages/runtime/src/helpers/utils', () => { } }) -const Chance = require('chance') -const { +import Chance from "chance" +import { writeJSON, unlink, existsSync, @@ -21,34 +20,36 @@ const { pathExists, writeFile, move, -} = require('fs-extra') -const path = require('path') -const process = require('process') -const os = require('os') -const cpy = require('cpy') -const { dir: getTmpDir } = require('tmp-promise') -const { downloadFile } = require('../packages/runtime/src/templates/handlerUtils') -const { getExtendedApiRouteConfigs } = require('../packages/runtime/src/helpers/functions') -const nextRuntimeFactory = require('../packages/runtime/src') +} from "fs-extra" +import path from "path" +import process from "process" +import os from "os" +import cpy from "cpy" +import { dir as getTmpDir } from "tmp-promise" +import { downloadFile } from "../packages/runtime/src/templates/handlerUtils" +import { getExtendedApiRouteConfigs } from "../packages/runtime/src/helpers/functions" +// @ts-expect-error - TODO: Convert runtime export to ES6 +import nextRuntimeFactory from "../packages/runtime/src" const nextRuntime = nextRuntimeFactory({}) -const { watchForMiddlewareChanges } = require('../packages/runtime/src/helpers/compiler') -const { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME, IMAGE_FUNCTION_NAME } = require('../packages/runtime/src/constants') -const { join } = require('pathe') -const { +import { watchForMiddlewareChanges } from "../packages/runtime/src/helpers/compiler" +import { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME, IMAGE_FUNCTION_NAME } from "../packages/runtime/src/constants" +import { join } from "pathe" +import { matchMiddleware, stripLocale, matchesRedirect, matchesRewrite, patchNextFiles, unpatchNextFiles, -} = require('../packages/runtime/src/helpers/files') -const { +} from "../packages/runtime/src/helpers/files" +import { getRequiredServerFiles, updateRequiredServerFiles, generateCustomHeaders, -} = require('../packages/runtime/src/helpers/config') -const { dirname, resolve } = require('path') -const { getProblematicUserRewrites } = require('../packages/runtime/src/helpers/verification') +} from "../packages/runtime/src/helpers/config" +import { dirname, resolve } from "path" +import { getProblematicUserRewrites } from "../packages/runtime/src/helpers/verification" +import type { NetlifyPluginOptions } from '@netlify/build' const chance = new Chance() const FIXTURES_DIR = `${__dirname}/fixtures` @@ -57,7 +58,7 @@ const constants = { INTERNAL_FUNCTIONS_SRC: '.netlify/functions-internal', PUBLISH_DIR: '.next', FUNCTIONS_DIST: '.netlify/functions', -} +} as unknown as NetlifyPluginOptions["constants"] const utils = { build: { failBuild(message) { @@ -69,7 +70,7 @@ const utils = { save: jest.fn(), restore: jest.fn(), }, -} +} as unknown as NetlifyPluginOptions["utils"] const normalizeChunkNames = (source) => source.replaceAll(/\/chunks\/\d+\.js/g, '/chunks/CHUNK_ID.js') @@ -174,12 +175,12 @@ const useFixture = async function (fixtureName) { await cpy('**', process.cwd(), { cwd: fixtureDir, parents: true, overwrite: true, dot: true }) } -const netlifyConfig = { build: { command: 'npm run build' }, functions: {}, redirects: [], headers: [] } +const netlifyConfig = { build: { command: 'npm run build' }, functions: {}, redirects: [], headers: [] } as NetlifyPluginOptions["netlifyConfig"] const defaultArgs = { netlifyConfig, utils, constants, -} +} as NetlifyPluginOptions let restoreCwd let cleanup From 309be605545eaad25704203e2096edf5e7df348f Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 18 Apr 2023 15:08:51 +0200 Subject: [PATCH 03/14] chore: move files utils to separate file --- test/helpers/files.spec.ts | 207 +++++++++++++++++++++++++++++++++++++ test/index.spec.ts | 197 ----------------------------------- 2 files changed, 207 insertions(+), 197 deletions(-) create mode 100644 test/helpers/files.spec.ts diff --git a/test/helpers/files.spec.ts b/test/helpers/files.spec.ts new file mode 100644 index 0000000000..4e35fa0069 --- /dev/null +++ b/test/helpers/files.spec.ts @@ -0,0 +1,207 @@ +import { + matchMiddleware, + stripLocale, + matchesRedirect, + matchesRewrite, + patchNextFiles, + unpatchNextFiles, +} from "../../packages/runtime/src/helpers/files" +import { + readFileSync, + copy, + ensureDir, +} from "fs-extra" +import path from "path" +import { dirname } from "path" +import { join } from "pathe" +import { Rewrites } from "../../packages/runtime/src/helpers/types" + +const REDIRECTS: Rewrites = [ + { + source: '/:file((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+)/', + destination: '/:file', + locale: false, + internal: true, + statusCode: 308, + regex: '^(?:/((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+))/$', + }, + { + source: '/:notfile((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+)', + destination: '/:notfile/', + locale: false, + internal: true, + statusCode: 308, + regex: '^(?:/((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+))$', + }, + { + source: '/en/redirectme', + destination: '/', + statusCode: 308, + regex: '^(?!/_next)/en/redirectme(?:/)?$', + }, + { + source: '/:nextInternalLocale(en|es|fr)/redirectme', + destination: '/:nextInternalLocale/', + statusCode: 308, + regex: '^(?!/_next)(?:/(en|es|fr))/redirectme(?:/)?$', + }, +] + +const REWRITES: Rewrites = [ + { + source: '/:nextInternalLocale(en|es|fr)/old/:path*', + destination: '/:nextInternalLocale/:path*', + regex: '^(?:/(en|es|fr))/old(?:/((?:[^/]+?)(?:/(?:[^/]+?))*))?(?:/)?$', + statusCode: 308, + }, +] + +describe('files utility functions', () => { + test('middleware tester matches correct paths', () => { + const middleware = ['middle', 'sub/directory'] + const paths = [ + 'middle.html', + 'middle', + 'middle/', + 'middle/ware', + 'sub/directory', + 'sub/directory.html', + 'sub/directory/child', + 'sub/directory/child.html', + ] + for (const path of paths) { + expect(matchMiddleware(middleware, path)).toBeTruthy() + } + }) + + test('middleware tester does not match incorrect paths', () => { + const middleware = ['middle', 'sub/directory'] + const paths = [ + 'middl', + '', + 'somethingelse', + 'another.html', + 'another/middle.html', + 'sub/anotherdirectory.html', + 'sub/directoryelse', + 'sub/directoryelse.html', + ] + for (const path of paths) { + expect(matchMiddleware(middleware, path)).toBeFalsy() + } + }) + + test('middleware tester matches root middleware', () => { + const middleware = [''] + const paths = [ + 'middl', + '', + 'somethingelse', + 'another.html', + 'another/middle.html', + 'sub/anotherdirectory.html', + 'sub/directoryelse', + 'sub/directoryelse.html', + ] + for (const path of paths) { + expect(matchMiddleware(middleware, path)).toBeTruthy() + } + }) + + test('middleware tester matches root middleware', () => { + const paths = [ + 'middl', + '', + 'somethingelse', + 'another.html', + 'another/middle.html', + 'sub/anotherdirectory.html', + 'sub/directoryelse', + 'sub/directoryelse.html', + ] + for (const path of paths) { + expect(matchMiddleware(undefined, path)).toBeFalsy() + } + }) + + test('stripLocale correctly strips matching locales', () => { + const locales = ['en', 'fr', 'en-GB'] + const paths = [ + ['en/file.html', 'file.html'], + ['fr/file.html', 'file.html'], + ['en-GB/file.html', 'file.html'], + ['file.html', 'file.html'], + ] + + for (const [path, expected] of paths) { + expect(stripLocale(path, locales)).toEqual(expected) + } + }) + + test('stripLocale does not touch non-matching matching locales', () => { + const locales = ['en', 'fr', 'en-GB'] + const paths = ['de/file.html', 'enfile.html', 'en-US/file.html'] + for (const path of paths) { + expect(stripLocale(path, locales)).toEqual(path) + } + }) + + test('matchesRedirect correctly matches paths with locales', () => { + const paths = ['en/redirectme.html', 'en/redirectme.json', 'fr/redirectme.html', 'fr/redirectme.json'] + paths.forEach((path) => { + expect(matchesRedirect(path, REDIRECTS)).toBeTruthy() + }) + }) + + test("matchesRedirect doesn't match paths with invalid locales", () => { + const paths = ['dk/redirectme.html', 'dk/redirectme.json', 'gr/redirectme.html', 'gr/redirectme.json'] + paths.forEach((path) => { + expect(matchesRedirect(path, REDIRECTS)).toBeFalsy() + }) + }) + + test("matchesRedirect doesn't match internal redirects", () => { + const paths = ['en/notrailingslash'] + paths.forEach((path) => { + expect(matchesRedirect(path, REDIRECTS)).toBeFalsy() + }) + }) + + it('matchesRewrite matches array of rewrites', () => { + expect(matchesRewrite('en/old/page.html', REWRITES)).toBeTruthy() + }) + + it('matchesRewrite matches beforeFiles rewrites', () => { + expect(matchesRewrite('en/old/page.html', { beforeFiles: REWRITES })).toBeTruthy() + }) + + it("matchesRewrite doesn't match afterFiles rewrites", () => { + expect(matchesRewrite('en/old/page.html', { afterFiles: REWRITES })).toBeFalsy() + }) + + it('matchesRewrite matches various paths', () => { + const paths = ['en/old/page.html', 'fr/old/page.html', 'en/old/deep/page.html', 'en/old.html'] + paths.forEach((path) => { + expect(matchesRewrite(path, REWRITES)).toBeTruthy() + }) + }) + + test('patches Next server files', async () => { + const root = path.resolve(dirname(__dirname)) + await copy(join(root, 'package.json'), path.join(process.cwd(), 'package.json')) + await ensureDir(path.join(process.cwd(), 'node_modules')) + await copy(path.join(root, 'node_modules', 'next'), path.join(process.cwd(), 'node_modules', 'next')) + + await patchNextFiles(process.cwd()) + const serverFile = path.resolve(process.cwd(), 'node_modules', 'next', 'dist', 'server', 'base-server.js') + const patchedData = await readFileSync(serverFile, 'utf8') + expect(patchedData.includes('_REVALIDATE_SSG')).toBeTruthy() + expect(patchedData.includes('private: isPreviewMode && cachedData')).toBeTruthy() + + await unpatchNextFiles(process.cwd()) + + const unPatchedData = await readFileSync(serverFile, 'utf8') + expect(unPatchedData.includes('_REVALIDATE_SSG')).toBeFalsy() + expect(unPatchedData.includes('private: isPreviewMode && cachedData')).toBeFalsy() + }) +}) \ No newline at end of file diff --git a/test/index.spec.ts b/test/index.spec.ts index 9a48da0b58..bc739ec50d 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -34,14 +34,6 @@ const nextRuntime = nextRuntimeFactory({}) import { watchForMiddlewareChanges } from "../packages/runtime/src/helpers/compiler" import { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME, IMAGE_FUNCTION_NAME } from "../packages/runtime/src/constants" import { join } from "pathe" -import { - matchMiddleware, - stripLocale, - matchesRedirect, - matchesRewrite, - patchNextFiles, - unpatchNextFiles, -} from "../packages/runtime/src/helpers/files" import { getRequiredServerFiles, updateRequiredServerFiles, @@ -74,45 +66,6 @@ const utils = { const normalizeChunkNames = (source) => source.replaceAll(/\/chunks\/\d+\.js/g, '/chunks/CHUNK_ID.js') -const REDIRECTS = [ - { - source: '/:file((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+)/', - destination: '/:file', - locale: false, - internal: true, - statusCode: 308, - regex: '^(?:/((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+))/$', - }, - { - source: '/:notfile((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+)', - destination: '/:notfile/', - locale: false, - internal: true, - statusCode: 308, - regex: '^(?:/((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+))$', - }, - { - source: '/en/redirectme', - destination: '/', - statusCode: 308, - regex: '^(?!/_next)/en/redirectme(?:/)?$', - }, - { - source: '/:nextInternalLocale(en|es|fr)/redirectme', - destination: '/:nextInternalLocale/', - statusCode: 308, - regex: '^(?!/_next)(?:/(en|es|fr))/redirectme(?:/)?$', - }, -] - -const REWRITES = [ - { - source: '/:nextInternalLocale(en|es|fr)/old/:path*', - destination: '/:nextInternalLocale/:path*', - regex: '^(?:/(en|es|fr))/old(?:/((?:[^/]+?)(?:/(?:[^/]+?))*))?(?:/)?$', - }, -] - // Temporary switch cwd const changeCwd = function (cwd) { const originalCwd = process.cwd() @@ -1133,156 +1086,6 @@ describe('onPostBuild', () => { }) }) -describe('utility functions', () => { - test('middleware tester matches correct paths', () => { - const middleware = ['middle', 'sub/directory'] - const paths = [ - 'middle.html', - 'middle', - 'middle/', - 'middle/ware', - 'sub/directory', - 'sub/directory.html', - 'sub/directory/child', - 'sub/directory/child.html', - ] - for (const path of paths) { - expect(matchMiddleware(middleware, path)).toBeTruthy() - } - }) - - test('middleware tester does not match incorrect paths', () => { - const middleware = ['middle', 'sub/directory'] - const paths = [ - 'middl', - '', - 'somethingelse', - 'another.html', - 'another/middle.html', - 'sub/anotherdirectory.html', - 'sub/directoryelse', - 'sub/directoryelse.html', - ] - for (const path of paths) { - expect(matchMiddleware(middleware, path)).toBeFalsy() - } - }) - - test('middleware tester matches root middleware', () => { - const middleware = [''] - const paths = [ - 'middl', - '', - 'somethingelse', - 'another.html', - 'another/middle.html', - 'sub/anotherdirectory.html', - 'sub/directoryelse', - 'sub/directoryelse.html', - ] - for (const path of paths) { - expect(matchMiddleware(middleware, path)).toBeTruthy() - } - }) - - test('middleware tester matches root middleware', () => { - const paths = [ - 'middl', - '', - 'somethingelse', - 'another.html', - 'another/middle.html', - 'sub/anotherdirectory.html', - 'sub/directoryelse', - 'sub/directoryelse.html', - ] - for (const path of paths) { - expect(matchMiddleware(undefined, path)).toBeFalsy() - } - }) - - test('stripLocale correctly strips matching locales', () => { - const locales = ['en', 'fr', 'en-GB'] - const paths = [ - ['en/file.html', 'file.html'], - ['fr/file.html', 'file.html'], - ['en-GB/file.html', 'file.html'], - ['file.html', 'file.html'], - ] - - for (const [path, expected] of paths) { - expect(stripLocale(path, locales)).toEqual(expected) - } - }) - - test('stripLocale does not touch non-matching matching locales', () => { - const locales = ['en', 'fr', 'en-GB'] - const paths = ['de/file.html', 'enfile.html', 'en-US/file.html'] - for (const path of paths) { - expect(stripLocale(path, locales)).toEqual(path) - } - }) - - test('matchesRedirect correctly matches paths with locales', () => { - const paths = ['en/redirectme.html', 'en/redirectme.json', 'fr/redirectme.html', 'fr/redirectme.json'] - paths.forEach((path) => { - expect(matchesRedirect(path, REDIRECTS)).toBeTruthy() - }) - }) - - test("matchesRedirect doesn't match paths with invalid locales", () => { - const paths = ['dk/redirectme.html', 'dk/redirectme.json', 'gr/redirectme.html', 'gr/redirectme.json'] - paths.forEach((path) => { - expect(matchesRedirect(path, REDIRECTS)).toBeFalsy() - }) - }) - - test("matchesRedirect doesn't match internal redirects", () => { - const paths = ['en/notrailingslash'] - paths.forEach((path) => { - expect(matchesRedirect(path, REDIRECTS)).toBeFalsy() - }) - }) - - it('matchesRewrite matches array of rewrites', () => { - expect(matchesRewrite('en/old/page.html', REWRITES)).toBeTruthy() - }) - - it('matchesRewrite matches beforeFiles rewrites', () => { - expect(matchesRewrite('en/old/page.html', { beforeFiles: REWRITES })).toBeTruthy() - }) - - it("matchesRewrite doesn't match afterFiles rewrites", () => { - expect(matchesRewrite('en/old/page.html', { afterFiles: REWRITES })).toBeFalsy() - }) - - it('matchesRewrite matches various paths', () => { - const paths = ['en/old/page.html', 'fr/old/page.html', 'en/old/deep/page.html', 'en/old.html'] - paths.forEach((path) => { - expect(matchesRewrite(path, REWRITES)).toBeTruthy() - }) - }) - - test('patches Next server files', async () => { - const root = path.resolve(dirname(__dirname)) - await copy(join(root, 'package.json'), path.join(process.cwd(), 'package.json')) - await ensureDir(path.join(process.cwd(), 'node_modules')) - await copy(path.join(root, 'node_modules', 'next'), path.join(process.cwd(), 'node_modules', 'next')) - - await patchNextFiles(process.cwd()) - const serverFile = path.resolve(process.cwd(), 'node_modules', 'next', 'dist', 'server', 'base-server.js') - const patchedData = await readFileSync(serverFile, 'utf8') - expect(patchedData.includes('_REVALIDATE_SSG')).toBeTruthy() - expect(patchedData.includes('private: isPreviewMode && cachedData')).toBeTruthy() - - await unpatchNextFiles(process.cwd()) - - const unPatchedData = await readFileSync(serverFile, 'utf8') - expect(unPatchedData.includes('_REVALIDATE_SSG')).toBeFalsy() - expect(unPatchedData.includes('private: isPreviewMode && cachedData')).toBeFalsy() - }) -}) - describe('function helpers', () => { it('downloadFile can download a file', async () => { const url = From 63b5d69ca25cacae357a17565632aaf70d624b4c Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 18 Apr 2023 15:11:57 +0200 Subject: [PATCH 04/14] chore: move downloadFile util test --- test/index.spec.ts | 33 ---------------------- test/templates/handlerUtils.spec.ts | 44 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/test/index.spec.ts b/test/index.spec.ts index bc739ec50d..870813780b 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -26,7 +26,6 @@ import process from "process" import os from "os" import cpy from "cpy" import { dir as getTmpDir } from "tmp-promise" -import { downloadFile } from "../packages/runtime/src/templates/handlerUtils" import { getExtendedApiRouteConfigs } from "../packages/runtime/src/helpers/functions" // @ts-expect-error - TODO: Convert runtime export to ES6 import nextRuntimeFactory from "../packages/runtime/src" @@ -1087,38 +1086,6 @@ describe('onPostBuild', () => { }) describe('function helpers', () => { - it('downloadFile can download a file', async () => { - const url = - 'https://raw.githubusercontent.com/netlify/next-runtime/c2668af24a78eb69b33222913f44c1900a3bce23/manifest.yml' - const tmpFile = join(os.tmpdir(), 'next-test', 'downloadfile.txt') - await ensureDir(path.dirname(tmpFile)) - await downloadFile(url, tmpFile) - expect(existsSync(tmpFile)).toBeTruthy() - expect(readFileSync(tmpFile, 'utf8')).toMatchInlineSnapshot(` - "name: netlify-plugin-nextjs-experimental - " - `) - await unlink(tmpFile) - }) - - it('downloadFile throws on bad domain', async () => { - const url = 'https://nonexistentdomain.example' - const tmpFile = join(os.tmpdir(), 'next-test', 'downloadfile.txt') - await ensureDir(path.dirname(tmpFile)) - await expect(downloadFile(url, tmpFile)).rejects.toThrowErrorMatchingInlineSnapshot( - `"getaddrinfo ENOTFOUND nonexistentdomain.example"`, - ) - }) - - it('downloadFile throws on 404', async () => { - const url = 'https://example.com/nonexistentfile' - const tmpFile = join(os.tmpdir(), 'next-test', 'downloadfile.txt') - await ensureDir(path.dirname(tmpFile)) - await expect(downloadFile(url, tmpFile)).rejects.toThrowError( - 'Failed to download https://example.com/nonexistentfile: 404 Not Found', - ) - }) - describe('config', () => { describe('dependency tracing', () => { it('extracts a list of all dependencies', async () => { diff --git a/test/templates/handlerUtils.spec.ts b/test/templates/handlerUtils.spec.ts index c43b6e847a..795c48dd36 100644 --- a/test/templates/handlerUtils.spec.ts +++ b/test/templates/handlerUtils.spec.ts @@ -3,7 +3,17 @@ import { unlocalizeRoute, localizeRoute, localizeDataRoute, + downloadFile, } from '../../packages/runtime/src/templates/handlerUtils' +import { join } from "pathe" +import os from "os" +import path from "path" +import { + unlink, + existsSync, + readFileSync, + ensureDir, +} from "fs-extra" describe('normalizeRoute', () => { it('removes a trailing slash from a route', () => { @@ -85,3 +95,37 @@ describe('localizeDataRoute', () => { expect(localizeDataRoute('/foo.rsc', '/foo')).toEqual('/foo.rsc') }) }) + +describe('downloadFile', () => { + it('can download a file', async () => { + const url = + 'https://raw.githubusercontent.com/netlify/next-runtime/c2668af24a78eb69b33222913f44c1900a3bce23/manifest.yml' + const tmpFile = join(os.tmpdir(), 'next-test', 'downloadfile.txt') + await ensureDir(path.dirname(tmpFile)) + await downloadFile(url, tmpFile) + expect(existsSync(tmpFile)).toBeTruthy() + expect(readFileSync(tmpFile, 'utf8')).toMatchInlineSnapshot(` + "name: netlify-plugin-nextjs-experimental + " + `) + await unlink(tmpFile) + }) + + it('throws on bad domain', async () => { + const url = 'https://nonexistentdomain.example' + const tmpFile = join(os.tmpdir(), 'next-test', 'downloadfile.txt') + await ensureDir(path.dirname(tmpFile)) + await expect(downloadFile(url, tmpFile)).rejects.toThrowErrorMatchingInlineSnapshot( + `"getaddrinfo ENOTFOUND nonexistentdomain.example"`, + ) + }) + + it('throws on 404', async () => { + const url = 'https://example.com/nonexistentfile' + const tmpFile = join(os.tmpdir(), 'next-test', 'downloadfile.txt') + await ensureDir(path.dirname(tmpFile)) + await expect(downloadFile(url, tmpFile)).rejects.toThrowError( + 'Failed to download https://example.com/nonexistentfile: 404 Not Found', + ) + }) +}) From ec4bc1f9f9246c916a6fc4ed8be917c5dd0624da Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 18 Apr 2023 15:22:45 +0200 Subject: [PATCH 05/14] chore: move test-utils to own file and move functions to own file --- test/helpers/functions.spec.ts | 24 +++++++++ test/index.spec.ts | 89 +--------------------------------- test/test-utils.ts | 72 +++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 87 deletions(-) create mode 100644 test/helpers/functions.spec.ts create mode 100644 test/test-utils.ts diff --git a/test/helpers/functions.spec.ts b/test/helpers/functions.spec.ts new file mode 100644 index 0000000000..b5e95df2ec --- /dev/null +++ b/test/helpers/functions.spec.ts @@ -0,0 +1,24 @@ +import { getExtendedApiRouteConfigs } from "../../packages/runtime/src/helpers/functions" +import { moveNextDist } from "../test-utils" + +describe('api route file analysis', () => { + it('extracts correct route configs from source files', async () => { + await moveNextDist() + const configs = await getExtendedApiRouteConfigs('.next', process.cwd()) + // Using a Set means the order doesn't matter + expect(new Set(configs)).toEqual( + new Set([ + { + compiled: 'pages/api/hello-background.js', + config: { type: 'experimental-background' }, + route: '/api/hello-background', + }, + { + compiled: 'pages/api/hello-scheduled.js', + config: { schedule: '@hourly', type: 'experimental-scheduled' }, + route: '/api/hello-scheduled', + }, + ]), + ) + }) +}) \ No newline at end of file diff --git a/test/index.spec.ts b/test/index.spec.ts index 870813780b..c3b41d476f 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -14,7 +14,6 @@ import { unlink, existsSync, readFileSync, - copy, ensureDir, readJson, pathExists, @@ -24,9 +23,7 @@ import { import path from "path" import process from "process" import os from "os" -import cpy from "cpy" import { dir as getTmpDir } from "tmp-promise" -import { getExtendedApiRouteConfigs } from "../packages/runtime/src/helpers/functions" // @ts-expect-error - TODO: Convert runtime export to ES6 import nextRuntimeFactory from "../packages/runtime/src" const nextRuntime = nextRuntimeFactory({}) @@ -38,13 +35,12 @@ import { updateRequiredServerFiles, generateCustomHeaders, } from "../packages/runtime/src/helpers/config" -import { dirname, resolve } from "path" +import { resolve } from "path" import { getProblematicUserRewrites } from "../packages/runtime/src/helpers/verification" import type { NetlifyPluginOptions } from '@netlify/build' +import { changeCwd, useFixture, moveNextDist } from "./test-utils" const chance = new Chance() -const FIXTURES_DIR = `${__dirname}/fixtures` -const SAMPLE_PROJECT_DIR = `${__dirname}/../demos/default` const constants = { INTERNAL_FUNCTIONS_SRC: '.netlify/functions-internal', PUBLISH_DIR: '.next', @@ -65,68 +61,9 @@ const utils = { const normalizeChunkNames = (source) => source.replaceAll(/\/chunks\/\d+\.js/g, '/chunks/CHUNK_ID.js') -// Temporary switch cwd -const changeCwd = function (cwd) { - const originalCwd = process.cwd() - process.chdir(cwd) - return () => { - process.chdir(originalCwd) - } -} - const onBuildHasRun = (netlifyConfig) => Boolean(netlifyConfig.functions[HANDLER_FUNCTION_NAME]?.included_files?.some((file) => file.includes('BUILD_ID'))) -const rewriteAppDir = async function (dir = '.next') { - const manifest = path.join(dir, 'required-server-files.json') - const manifestContent = await readJson(manifest) - manifestContent.appDir = process.cwd() - - await writeJSON(manifest, manifestContent) -} - -// Move .next from sample project to current directory -export const moveNextDist = async function (dir = '.next', copyMods = false) { - if (copyMods) { - await copyModules(['next', 'sharp']) - } else { - await stubModules(['next', 'sharp']) - } - await ensureDir(dirname(dir)) - await copy(path.join(SAMPLE_PROJECT_DIR, '.next'), path.join(process.cwd(), dir)) - - for (const file of ['pages', 'app', 'src', 'components', 'public', 'components', 'hello.txt', 'package.json']) { - const source = path.join(SAMPLE_PROJECT_DIR, file) - if (existsSync(source)) { - await copy(source, path.join(process.cwd(), file)) - } - } - - await rewriteAppDir(dir) -} - -const copyModules = async function (modules) { - for (const mod of modules) { - const source = dirname(require.resolve(`${mod}/package.json`)) - const dest = path.join(process.cwd(), 'node_modules', mod) - await copy(source, dest) - } -} - -const stubModules = async function (modules) { - for (const mod of modules) { - const dir = path.join(process.cwd(), 'node_modules', mod) - await ensureDir(dir) - await writeJSON(path.join(dir, 'package.json'), { name: mod }) - } -} - -// Copy fixture files to the current directory -const useFixture = async function (fixtureName) { - const fixtureDir = `${FIXTURES_DIR}/${fixtureName}` - await cpy('**', process.cwd(), { cwd: fixtureDir, parents: true, overwrite: true, dot: true }) -} - const netlifyConfig = { build: { command: 'npm run build' }, functions: {}, redirects: [], headers: [] } as NetlifyPluginOptions["netlifyConfig"] const defaultArgs = { netlifyConfig, @@ -1528,28 +1465,6 @@ describe('function helpers', () => { }) }) -describe('api route file analysis', () => { - it('extracts correct route configs from source files', async () => { - await moveNextDist() - const configs = await getExtendedApiRouteConfigs('.next', process.cwd()) - // Using a Set means the order doesn't matter - expect(new Set(configs)).toEqual( - new Set([ - { - compiled: 'pages/api/hello-background.js', - config: { type: 'experimental-background' }, - route: '/api/hello-background', - }, - { - compiled: 'pages/api/hello-scheduled.js', - config: { schedule: '@hourly', type: 'experimental-scheduled' }, - route: '/api/hello-scheduled', - }, - ]), - ) - }) -}) - const middlewareSourceTs = /* typescript */ ` import { NextResponse } from 'next/server' export async function middleware(req: NextRequest) { diff --git a/test/test-utils.ts b/test/test-utils.ts new file mode 100644 index 0000000000..7abc8d8fd4 --- /dev/null +++ b/test/test-utils.ts @@ -0,0 +1,72 @@ +import path from "path" +import { dirname } from "path" +import cpy from "cpy" +import { + writeJSON, + existsSync, + ensureDir, + readJson, + copy, +} from "fs-extra" + +const FIXTURES_DIR = `${__dirname}/fixtures` +const SAMPLE_PROJECT_DIR = `${__dirname}/../demos/default` + +// Temporary switch cwd +export const changeCwd = function (cwd) { + const originalCwd = process.cwd() + process.chdir(cwd) + return () => { + process.chdir(originalCwd) + } +} + +const rewriteAppDir = async function (dir = '.next') { + const manifest = path.join(dir, 'required-server-files.json') + const manifestContent = await readJson(manifest) + manifestContent.appDir = process.cwd() + + await writeJSON(manifest, manifestContent) +} + +// Move .next from sample project to current directory +export const moveNextDist = async function (dir = '.next', copyMods = false) { + if (copyMods) { + await copyModules(['next', 'sharp']) + } else { + await stubModules(['next', 'sharp']) + } + await ensureDir(dirname(dir)) + await copy(path.join(SAMPLE_PROJECT_DIR, '.next'), path.join(process.cwd(), dir)) + + for (const file of ['pages', 'app', 'src', 'components', 'public', 'components', 'hello.txt', 'package.json']) { + const source = path.join(SAMPLE_PROJECT_DIR, file) + if (existsSync(source)) { + await copy(source, path.join(process.cwd(), file)) + } + } + + await rewriteAppDir(dir) +} + +export const copyModules = async function (modules) { + for (const mod of modules) { + const source = dirname(require.resolve(`${mod}/package.json`)) + const dest = path.join(process.cwd(), 'node_modules', mod) + await copy(source, dest) + } +} + +export const stubModules = async function (modules) { + for (const mod of modules) { + const dir = path.join(process.cwd(), 'node_modules', mod) + await ensureDir(dir) + await writeJSON(path.join(dir, 'package.json'), { name: mod }) + } +} + +// Copy fixture files to the current directory +export const useFixture = async function (fixtureName) { + const fixtureDir = `${FIXTURES_DIR}/${fixtureName}` + await cpy('**', process.cwd(), { cwd: fixtureDir, parents: true, overwrite: true, dot: true }) +} \ No newline at end of file From 77344cb39ca0f050a2a2c8cc26773f9e769a3f6c Mon Sep 17 00:00:00 2001 From: LekoArts Date: Tue, 18 Apr 2023 15:47:45 +0200 Subject: [PATCH 06/14] chore: move more stuff --- test/helpers/config.spec.ts | 429 ++++++++++++++++++++++++++++ test/helpers/verification.spec.ts | 35 ++- test/index.spec.ts | 451 ------------------------------ 3 files changed, 463 insertions(+), 452 deletions(-) create mode 100644 test/helpers/config.spec.ts diff --git a/test/helpers/config.spec.ts b/test/helpers/config.spec.ts new file mode 100644 index 0000000000..2373e2dc9c --- /dev/null +++ b/test/helpers/config.spec.ts @@ -0,0 +1,429 @@ +import { + generateCustomHeaders, + NextConfig +} from "../../packages/runtime/src/helpers/config" +import type { NetlifyPluginOptions } from '@netlify/build' + +const netlifyConfig = { build: { command: 'npm run build' }, functions: {}, redirects: [], headers: [] } as NetlifyPluginOptions["netlifyConfig"] + +describe('generateCustomHeaders', () => { + // The routesManifest is the contents of the routes-manifest.json file which will already contain the generated + // header paths which take locales and base path into account which is why you'll see them in the paths already + // in test data. + + it('sets custom headers in the Netlify configuration', () => { + const nextConfig = { + routesManifest: { + headers: [ + // single header for a route + { + source: '/', + headers: [ + { + key: 'X-Unit-Test', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + // multiple headers for a route + { + source: '/unit-test', + headers: [ + { + key: 'X-Another-Unit-Test', + value: 'true', + }, + { + key: 'X-Another-Unit-Test-Again', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + ], + }, + } as unknown as NextConfig + + generateCustomHeaders(nextConfig, netlifyConfig.headers) + + expect(netlifyConfig.headers).toEqual([ + { + for: '/', + values: { + 'X-Unit-Test': 'true', + }, + }, + { + for: '/unit-test', + values: { + 'X-Another-Unit-Test': 'true', + 'X-Another-Unit-Test-Again': 'true', + }, + }, + ]) + }) + + it('sets custom headers using a splat instead of a named splat in the Netlify configuration', () => { + netlifyConfig.headers = [] + + const nextConfig = { + routesManifest: { + headers: [ + // single header for a route + { + source: '/:path*', + headers: [ + { + key: 'X-Unit-Test', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + // multiple headers for a route + { + source: '/some-other-path/:path*', + headers: [ + { + key: 'X-Another-Unit-Test', + value: 'true', + }, + { + key: 'X-Another-Unit-Test-Again', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + { + source: '/some-other-path/yolo/:path*', + headers: [ + { + key: 'X-Another-Unit-Test', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + ], + }, + } as unknown as NextConfig + + generateCustomHeaders(nextConfig, netlifyConfig.headers) + + expect(netlifyConfig.headers).toEqual([ + { + for: '/*', + values: { + 'X-Unit-Test': 'true', + }, + }, + { + for: '/some-other-path/*', + values: { + 'X-Another-Unit-Test': 'true', + 'X-Another-Unit-Test-Again': 'true', + }, + }, + { + for: '/some-other-path/yolo/*', + values: { + 'X-Another-Unit-Test': 'true', + }, + }, + ]) + }) + + it('appends custom headers in the Netlify configuration', () => { + netlifyConfig.headers = [ + { + for: '/', + values: { + 'X-Existing-Header': 'true', + }, + }, + ] + + const nextConfig = { + routesManifest: { + headers: [ + // single header for a route + { + source: '/', + headers: [ + { + key: 'X-Unit-Test', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + // multiple headers for a route + { + source: '/unit-test', + headers: [ + { + key: 'X-Another-Unit-Test', + value: 'true', + }, + { + key: 'X-Another-Unit-Test-Again', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + ], + }, + } as unknown as NextConfig + + generateCustomHeaders(nextConfig, netlifyConfig.headers) + + expect(netlifyConfig.headers).toEqual([ + { + for: '/', + values: { + 'X-Existing-Header': 'true', + }, + }, + { + for: '/', + values: { + 'X-Unit-Test': 'true', + }, + }, + { + for: '/unit-test', + values: { + 'X-Another-Unit-Test': 'true', + 'X-Another-Unit-Test-Again': 'true', + }, + }, + ]) + }) + + it('sets custom headers using basePath in the Next.js configuration', () => { + netlifyConfig.headers = [] + + const basePath = '/base-path' + const nextConfig = { + routesManifest: { + headers: [ + // single header for a route + { + source: `${basePath}/:path*`, + headers: [ + { + key: 'X-Unit-Test', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + // multiple headers for a route + { + source: `${basePath}/some-other-path/:path*`, + headers: [ + { + key: 'X-Another-Unit-Test', + value: 'true', + }, + { + key: 'X-Another-Unit-Test-Again', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + ], + }, + } as unknown as NextConfig + + generateCustomHeaders(nextConfig, netlifyConfig.headers) + + expect(netlifyConfig.headers).toEqual([ + { + for: '/base-path/*', + values: { + 'X-Unit-Test': 'true', + }, + }, + { + for: '/base-path/some-other-path/*', + values: { + 'X-Another-Unit-Test': 'true', + 'X-Another-Unit-Test-Again': 'true', + }, + }, + ]) + }) + + it('sets custom headers omitting basePath when a header has basePath set to false', () => { + netlifyConfig.headers = [] + + const basePath = '/base-path' + + const nextConfig = { + routesManifest: { + headers: [ + // single header for a route + { + source: '/:path*', + headers: [ + { + key: 'X-Unit-Test', + value: 'true', + }, + ], + basePath: false, + regex: '^/(?:/)?$', + }, + // multiple headers for a route + { + source: `${basePath}/some-other-path/:path*`, + headers: [ + { + key: 'X-Another-Unit-Test', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + ], + }, + } as unknown as NextConfig + + generateCustomHeaders(nextConfig, netlifyConfig.headers) + + expect(netlifyConfig.headers).toEqual([ + { + for: '/*', + values: { + 'X-Unit-Test': 'true', + }, + }, + { + for: '/base-path/some-other-path/*', + values: { + 'X-Another-Unit-Test': 'true', + }, + }, + ]) + }) + + it('prepends locales set in the next.config to paths for custom headers', () => { + netlifyConfig.headers = [] + + // I'm not setting locales in the nextConfig, because at this point in the post build when this runs, + // Next.js has modified the routesManifest to have the locales in the source. + const nextConfig = { + i18n: { + locales: ['en-US', 'fr'], + defaultLocale: 'en', + }, + routesManifest: { + headers: [ + { + source: '/:nextInternalLocale(en\\-US|fr)/with-locale/:path*', + headers: [ + { + key: 'X-Unit-Test', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + ], + }, + } as unknown as NextConfig + + generateCustomHeaders(nextConfig, netlifyConfig.headers) + + expect(netlifyConfig.headers).toEqual([ + { + for: '/with-locale/*', + values: { + 'X-Unit-Test': 'true', + }, + }, + { + for: '/en-US/with-locale/*', + values: { + 'X-Unit-Test': 'true', + }, + }, + { + for: '/fr/with-locale/*', + values: { + 'X-Unit-Test': 'true', + }, + }, + ]) + }) + + it('does not prepend locales set in the next.config to custom headers that have locale set to false', () => { + netlifyConfig.headers = [] + + const nextConfig = { + i18n: { + locales: ['en', 'fr'], + defaultLocale: 'en', + }, + routesManifest: { + headers: [ + { + source: '/:nextInternalLocale(en|fr)/with-locale/:path*', + headers: [ + { + key: 'X-Unit-Test', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + { + source: '/fr/le-custom-locale-path/:path*', + locale: false, + headers: [ + { + key: 'X-Unit-Test', + value: 'true', + }, + ], + regex: '^/(?:/)?$', + }, + ], + }, + } as unknown as NextConfig + + generateCustomHeaders(nextConfig, netlifyConfig.headers) + + expect(netlifyConfig.headers).toEqual([ + { + for: '/with-locale/*', + values: { + 'X-Unit-Test': 'true', + }, + }, + { + for: '/en/with-locale/*', + values: { + 'X-Unit-Test': 'true', + }, + }, + { + for: '/fr/with-locale/*', + values: { + 'X-Unit-Test': 'true', + }, + }, + { + for: '/fr/le-custom-locale-path/*', + values: { + 'X-Unit-Test': 'true', + }, + }, + ]) + }) +}) \ No newline at end of file diff --git a/test/helpers/verification.spec.ts b/test/helpers/verification.spec.ts index 85096d5194..2edcb24699 100644 --- a/test/helpers/verification.spec.ts +++ b/test/helpers/verification.spec.ts @@ -1,6 +1,10 @@ import Chance from 'chance' -import { checkNextSiteHasBuilt, checkZipSize } from '../../packages/runtime/src/helpers/verification' +import { checkNextSiteHasBuilt, checkZipSize, getProblematicUserRewrites } from '../../packages/runtime/src/helpers/verification' import { outdent } from 'outdent' +import type { NetlifyPluginOptions } from '@netlify/build' +import { moveNextDist } from "../test-utils" + +const netlifyConfig = { build: { command: 'npm run build' }, functions: {}, redirects: [], headers: [] } as NetlifyPluginOptions["netlifyConfig"] import type { NetlifyPluginUtils } from '@netlify/build' type FailBuild = NetlifyPluginUtils['build']['failBuild'] @@ -100,3 +104,32 @@ describe('checkZipSize', () => { expect(consoleSpy).toHaveBeenCalledWith('Function bundle size check was DISABLED with the DISABLE_BUNDLE_ZIP_SIZE_CHECK environment variable. Your deployment will break if it exceeds the maximum supported size of function zip files in your account.') }) }) + +describe("getProblematicUserRewrites", () => { + it('finds problematic user rewrites', async () => { + await moveNextDist() + const rewrites = getProblematicUserRewrites({ + redirects: [ + { from: '/previous', to: '/rewrites-are-a-problem', status: 200 }, + { from: '/api', to: '/.netlify/functions/are-ok', status: 200 }, + { from: '/remote', to: 'http://example.com/proxying/is/ok', status: 200 }, + { from: '/old', to: '/redirects-are-fine' }, + { from: '/*', to: '/404-is-a-problem', status: 404 }, + ...netlifyConfig.redirects, + ], + basePath: '', + }) + expect(rewrites).toEqual([ + { + from: '/previous', + status: 200, + to: '/rewrites-are-a-problem', + }, + { + from: '/*', + status: 404, + to: '/404-is-a-problem', + }, + ]) + }) +}) diff --git a/test/index.spec.ts b/test/index.spec.ts index c3b41d476f..b2ef84c861 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -33,10 +33,8 @@ import { join } from "pathe" import { getRequiredServerFiles, updateRequiredServerFiles, - generateCustomHeaders, } from "../packages/runtime/src/helpers/config" import { resolve } from "path" -import { getProblematicUserRewrites } from "../packages/runtime/src/helpers/verification" import type { NetlifyPluginOptions } from '@netlify/build' import { changeCwd, useFixture, moveNextDist } from "./test-utils" @@ -770,33 +768,6 @@ describe('onPostBuild', () => { delete process.env.NEXT_PLUGIN_FORCE_RUN }) - test('finds problematic user rewrites', async () => { - await moveNextDist() - const rewrites = getProblematicUserRewrites({ - redirects: [ - { from: '/previous', to: '/rewrites-are-a-problem', status: 200 }, - { from: '/api', to: '/.netlify/functions/are-ok', status: 200 }, - { from: '/remote', to: 'http://example.com/proxying/is/ok', status: 200 }, - { from: '/old', to: '/redirects-are-fine' }, - { from: '/*', to: '/404-is-a-problem', status: 404 }, - ...netlifyConfig.redirects, - ], - basePath: '', - }) - expect(rewrites).toEqual([ - { - from: '/previous', - status: 200, - to: '/rewrites-are-a-problem', - }, - { - from: '/*', - status: 404, - to: '/404-is-a-problem', - }, - ]) - }) - test('adds headers to Netlify configuration', async () => { await moveNextDist() @@ -1040,428 +1011,6 @@ describe('function helpers', () => { expect(filesExist.every((exists) => exists)).toBeTruthy() }) }) - - describe('generateCustomHeaders', () => { - // The routesManifest is the contents of the routes-manifest.json file which will already contain the generated - // header paths which take locales and base path into account which is why you'll see them in the paths already - // in test data. - - it('sets custom headers in the Netlify configuration', () => { - const nextConfig = { - routesManifest: { - headers: [ - // single header for a route - { - source: '/', - headers: [ - { - key: 'X-Unit-Test', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - // multiple headers for a route - { - source: '/unit-test', - headers: [ - { - key: 'X-Another-Unit-Test', - value: 'true', - }, - { - key: 'X-Another-Unit-Test-Again', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - ], - }, - } - - generateCustomHeaders(nextConfig, netlifyConfig.headers) - - expect(netlifyConfig.headers).toEqual([ - { - for: '/', - values: { - 'X-Unit-Test': 'true', - }, - }, - { - for: '/unit-test', - values: { - 'X-Another-Unit-Test': 'true', - 'X-Another-Unit-Test-Again': 'true', - }, - }, - ]) - }) - - it('sets custom headers using a splat instead of a named splat in the Netlify configuration', () => { - netlifyConfig.headers = [] - - const nextConfig = { - routesManifest: { - headers: [ - // single header for a route - { - source: '/:path*', - headers: [ - { - key: 'X-Unit-Test', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - // multiple headers for a route - { - source: '/some-other-path/:path*', - headers: [ - { - key: 'X-Another-Unit-Test', - value: 'true', - }, - { - key: 'X-Another-Unit-Test-Again', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - { - source: '/some-other-path/yolo/:path*', - headers: [ - { - key: 'X-Another-Unit-Test', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - ], - }, - } - - generateCustomHeaders(nextConfig, netlifyConfig.headers) - - expect(netlifyConfig.headers).toEqual([ - { - for: '/*', - values: { - 'X-Unit-Test': 'true', - }, - }, - { - for: '/some-other-path/*', - values: { - 'X-Another-Unit-Test': 'true', - 'X-Another-Unit-Test-Again': 'true', - }, - }, - { - for: '/some-other-path/yolo/*', - values: { - 'X-Another-Unit-Test': 'true', - }, - }, - ]) - }) - - it('appends custom headers in the Netlify configuration', () => { - netlifyConfig.headers = [ - { - for: '/', - values: { - 'X-Existing-Header': 'true', - }, - }, - ] - - const nextConfig = { - routesManifest: { - headers: [ - // single header for a route - { - source: '/', - headers: [ - { - key: 'X-Unit-Test', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - // multiple headers for a route - { - source: '/unit-test', - headers: [ - { - key: 'X-Another-Unit-Test', - value: 'true', - }, - { - key: 'X-Another-Unit-Test-Again', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - ], - }, - } - - generateCustomHeaders(nextConfig, netlifyConfig.headers) - - expect(netlifyConfig.headers).toEqual([ - { - for: '/', - values: { - 'X-Existing-Header': 'true', - }, - }, - { - for: '/', - values: { - 'X-Unit-Test': 'true', - }, - }, - { - for: '/unit-test', - values: { - 'X-Another-Unit-Test': 'true', - 'X-Another-Unit-Test-Again': 'true', - }, - }, - ]) - }) - - it('sets custom headers using basePath in the Next.js configuration', () => { - netlifyConfig.headers = [] - - const basePath = '/base-path' - const nextConfig = { - routesManifest: { - headers: [ - // single header for a route - { - source: `${basePath}/:path*`, - headers: [ - { - key: 'X-Unit-Test', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - // multiple headers for a route - { - source: `${basePath}/some-other-path/:path*`, - headers: [ - { - key: 'X-Another-Unit-Test', - value: 'true', - }, - { - key: 'X-Another-Unit-Test-Again', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - ], - }, - } - - generateCustomHeaders(nextConfig, netlifyConfig.headers) - - expect(netlifyConfig.headers).toEqual([ - { - for: '/base-path/*', - values: { - 'X-Unit-Test': 'true', - }, - }, - { - for: '/base-path/some-other-path/*', - values: { - 'X-Another-Unit-Test': 'true', - 'X-Another-Unit-Test-Again': 'true', - }, - }, - ]) - }) - - it('sets custom headers omitting basePath when a header has basePath set to false', () => { - netlifyConfig.headers = [] - - const basePath = '/base-path' - - const nextConfig = { - routesManifest: { - headers: [ - // single header for a route - { - source: '/:path*', - headers: [ - { - key: 'X-Unit-Test', - value: 'true', - }, - ], - basePath: false, - regex: '^/(?:/)?$', - }, - // multiple headers for a route - { - source: `${basePath}/some-other-path/:path*`, - headers: [ - { - key: 'X-Another-Unit-Test', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - ], - }, - } - - generateCustomHeaders(nextConfig, netlifyConfig.headers) - - expect(netlifyConfig.headers).toEqual([ - { - for: '/*', - values: { - 'X-Unit-Test': 'true', - }, - }, - { - for: '/base-path/some-other-path/*', - values: { - 'X-Another-Unit-Test': 'true', - }, - }, - ]) - }) - - it('prepends locales set in the next.config to paths for custom headers', () => { - netlifyConfig.headers = [] - - // I'm not setting locales in the nextConfig, because at this point in the post build when this runs, - // Next.js has modified the routesManifest to have the locales in the source. - const nextConfig = { - i18n: { - locales: ['en-US', 'fr'], - defaultLocale: 'en', - }, - routesManifest: { - headers: [ - { - source: '/:nextInternalLocale(en\\-US|fr)/with-locale/:path*', - headers: [ - { - key: 'X-Unit-Test', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - ], - }, - } - - generateCustomHeaders(nextConfig, netlifyConfig.headers) - - expect(netlifyConfig.headers).toEqual([ - { - for: '/with-locale/*', - values: { - 'X-Unit-Test': 'true', - }, - }, - { - for: '/en-US/with-locale/*', - values: { - 'X-Unit-Test': 'true', - }, - }, - { - for: '/fr/with-locale/*', - values: { - 'X-Unit-Test': 'true', - }, - }, - ]) - }) - - it('does not prepend locales set in the next.config to custom headers that have locale set to false', () => { - netlifyConfig.headers = [] - - const nextConfig = { - i18n: { - locales: ['en', 'fr'], - defaultLocale: 'en', - }, - routesManifest: { - headers: [ - { - source: '/:nextInternalLocale(en|fr)/with-locale/:path*', - headers: [ - { - key: 'X-Unit-Test', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - { - source: '/fr/le-custom-locale-path/:path*', - locale: false, - headers: [ - { - key: 'X-Unit-Test', - value: 'true', - }, - ], - regex: '^/(?:/)?$', - }, - ], - }, - } - - generateCustomHeaders(nextConfig, netlifyConfig.headers) - - expect(netlifyConfig.headers).toEqual([ - { - for: '/with-locale/*', - values: { - 'X-Unit-Test': 'true', - }, - }, - { - for: '/en/with-locale/*', - values: { - 'X-Unit-Test': 'true', - }, - }, - { - for: '/fr/with-locale/*', - values: { - 'X-Unit-Test': 'true', - }, - }, - { - for: '/fr/le-custom-locale-path/*', - values: { - 'X-Unit-Test': 'true', - }, - }, - ]) - }) - }) }) }) From 4a56e153136ed877eb0ef336bfb6be5b21f5b215 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Wed, 19 Apr 2023 13:14:59 +0200 Subject: [PATCH 07/14] chore: update snapshot --- test/__snapshots__/{index.spec.js.snap => index.spec.ts.snap} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/__snapshots__/{index.spec.js.snap => index.spec.ts.snap} (100%) diff --git a/test/__snapshots__/index.spec.js.snap b/test/__snapshots__/index.spec.ts.snap similarity index 100% rename from test/__snapshots__/index.spec.js.snap rename to test/__snapshots__/index.spec.ts.snap From 2cc0f52cac303eb7164f3c4b72a56d2db62eaa8c Mon Sep 17 00:00:00 2001 From: LekoArts Date: Wed, 19 Apr 2023 13:20:26 +0200 Subject: [PATCH 08/14] chore: server.spec.ts fix --- test/templates/server.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/templates/server.spec.ts b/test/templates/server.spec.ts index 692760e468..23f9337e83 100644 --- a/test/templates/server.spec.ts +++ b/test/templates/server.spec.ts @@ -4,8 +4,8 @@ import { Options } from 'next/dist/server/next-server' import { getNextServer, NextServerType, netlifyApiFetch } from '../../packages/runtime/src/templates/handlerUtils' import { NetlifyNextServer, NetlifyConfig } from '../../packages/runtime/src/templates/server' -jest.mock('../packages/runtime/src/templates/handlerUtils', () => { - const originalModule = jest.requireActual('../packages/runtime/src/templates/handlerUtils') +jest.mock('../../packages/runtime/src/templates/handlerUtils', () => { + const originalModule = jest.requireActual('../../packages/runtime/src/templates/handlerUtils') return { __esModule: true, From 6971f9675f7602a3ec238ed15b5fe45dced9baee Mon Sep 17 00:00:00 2001 From: LekoArts Date: Wed, 19 Apr 2023 13:26:53 +0200 Subject: [PATCH 09/14] chore: fix analysis --- test/helpers/analysis.spec.ts | 32 +++++++++++--------------------- test/helpers/files.spec.ts | 11 +++++++++++ 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/test/helpers/analysis.spec.ts b/test/helpers/analysis.spec.ts index d3f54f86a3..a066f56508 100644 --- a/test/helpers/analysis.spec.ts +++ b/test/helpers/analysis.spec.ts @@ -1,7 +1,6 @@ import { extractConfigFromFile } from '../../packages/runtime/src/helpers/analysis' import { resolve } from 'pathe' -import { getDependenciesOfFile } from '../../packages/runtime/src/helpers/files' -import { dirname } from 'path' + describe('static source analysis', () => { beforeEach(() => { // Spy on console.error @@ -12,36 +11,36 @@ describe('static source analysis', () => { ;(console.error as jest.Mock).mockRestore() }) it('should extract config values from a source file', async () => { - const config = await extractConfigFromFile(resolve(__dirname, 'fixtures/analysis/background.js')) + const config = await extractConfigFromFile(resolve(__dirname, '../fixtures/analysis/background.js')) expect(config).toEqual({ type: 'experimental-background', }) }) it('should extract config values from a TypeScript source file', async () => { - const config = await extractConfigFromFile(resolve(__dirname, 'fixtures/analysis/background.ts')) + const config = await extractConfigFromFile(resolve(__dirname, '../fixtures/analysis/background.ts')) expect(config).toEqual({ type: 'experimental-background', }) }) it('should return an empty config if not defined', async () => { - const config = await extractConfigFromFile(resolve(__dirname, 'fixtures/analysis/missing.ts')) + const config = await extractConfigFromFile(resolve(__dirname, '../fixtures/analysis/missing.ts')) expect(config).toEqual({}) }) it('should return an empty config if config is invalid', async () => { - const config = await extractConfigFromFile(resolve(__dirname, 'fixtures/analysis/invalid.ts')) + const config = await extractConfigFromFile(resolve(__dirname, '../fixtures/analysis/invalid.ts')) expect(config).toEqual({}) }) it('should extract schedule values from a source file', async () => { - const config = await extractConfigFromFile(resolve(__dirname, 'fixtures/analysis/scheduled.ts')) + const config = await extractConfigFromFile(resolve(__dirname, '../fixtures/analysis/scheduled.ts')) expect(config).toEqual({ type: 'experimental-scheduled', schedule: '@daily', }) }) it('should throw if schedule is provided when type is background', async () => { - await expect(extractConfigFromFile(resolve(__dirname, 'fixtures/analysis/background-schedule.ts'))).rejects.toThrow( + await expect(extractConfigFromFile(resolve(__dirname, '../fixtures/analysis/background-schedule.ts'))).rejects.toThrow( 'Unsupported config value in test/fixtures/analysis/background-schedule.ts', ) expect(console.error).toHaveBeenCalledWith( @@ -49,7 +48,7 @@ describe('static source analysis', () => { ) }) it('should throw if schedule is provided when type is default', async () => { - await expect(extractConfigFromFile(resolve(__dirname, 'fixtures/analysis/default-schedule.ts'))).rejects.toThrow( + await expect(extractConfigFromFile(resolve(__dirname, '../fixtures/analysis/default-schedule.ts'))).rejects.toThrow( 'Unsupported config value in test/fixtures/analysis/default-schedule.ts', ) expect(console.error).toHaveBeenCalledWith( @@ -57,7 +56,7 @@ describe('static source analysis', () => { ) }) it('should throw if schedule is not provided when type is scheduled', async () => { - await expect(extractConfigFromFile(resolve(__dirname, 'fixtures/analysis/missing-schedule.ts'))).rejects.toThrow( + await expect(extractConfigFromFile(resolve(__dirname, '../fixtures/analysis/missing-schedule.ts'))).rejects.toThrow( 'Unsupported config value in test/fixtures/analysis/missing-schedule.ts', ) expect(console.error).toHaveBeenCalledWith( @@ -65,7 +64,7 @@ describe('static source analysis', () => { ) }) it('should throw if edge runtime is specified for scheduled functions', async () => { - await expect(extractConfigFromFile(resolve(__dirname, 'fixtures/analysis/scheduled-edge.ts'))).rejects.toThrow( + await expect(extractConfigFromFile(resolve(__dirname, '../fixtures/analysis/scheduled-edge.ts'))).rejects.toThrow( 'Unsupported config value in test/fixtures/analysis/scheduled-edge.ts', ) expect(console.error).toHaveBeenCalledWith( @@ -73,7 +72,7 @@ describe('static source analysis', () => { ) }) it('should throw if edge runtime is specified for background functions', async () => { - await expect(extractConfigFromFile(resolve(__dirname, 'fixtures/analysis/background-edge.ts'))).rejects.toThrow( + await expect(extractConfigFromFile(resolve(__dirname, '../fixtures/analysis/background-edge.ts'))).rejects.toThrow( 'Unsupported config value in test/fixtures/analysis/background-edge.ts', ) expect(console.error).toHaveBeenCalledWith( @@ -81,12 +80,3 @@ describe('static source analysis', () => { ) }) }) - -describe('dependency tracing', () => { - it('generates dependency list from a source file', async () => { - const dependencies = await getDependenciesOfFile(resolve(__dirname, 'fixtures/analysis/background.js')) - expect(dependencies).toEqual( - ['test/webpack-api-runtime.js', 'package.json'].map((dep) => resolve(dirname(__dirname), dep)), - ) - }) -}) diff --git a/test/helpers/files.spec.ts b/test/helpers/files.spec.ts index 4e35fa0069..9ae5b5b498 100644 --- a/test/helpers/files.spec.ts +++ b/test/helpers/files.spec.ts @@ -5,6 +5,7 @@ import { matchesRewrite, patchNextFiles, unpatchNextFiles, + getDependenciesOfFile, } from "../../packages/runtime/src/helpers/files" import { readFileSync, @@ -13,6 +14,7 @@ import { } from "fs-extra" import path from "path" import { dirname } from "path" +import { resolve } from 'pathe' import { join } from "pathe" import { Rewrites } from "../../packages/runtime/src/helpers/types" @@ -204,4 +206,13 @@ describe('files utility functions', () => { expect(unPatchedData.includes('_REVALIDATE_SSG')).toBeFalsy() expect(unPatchedData.includes('private: isPreviewMode && cachedData')).toBeFalsy() }) +}) + +describe('dependency tracing', () => { + it('generates dependency list from a source file', async () => { + const dependencies = await getDependenciesOfFile(resolve(__dirname, '../fixtures/analysis/background.js')) + expect(dependencies).toEqual( + ['test/webpack-api-runtime.js', 'package.json'].map((dep) => resolve(dirname(__dirname), dep)), + ) + }) }) \ No newline at end of file From 28309de8773fddea3eb157d95b861994a2155477 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Wed, 19 Apr 2023 13:45:47 +0200 Subject: [PATCH 10/14] chore: wip --- test/helpers/files.spec.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/helpers/files.spec.ts b/test/helpers/files.spec.ts index 9ae5b5b498..8608d400ed 100644 --- a/test/helpers/files.spec.ts +++ b/test/helpers/files.spec.ts @@ -188,19 +188,20 @@ describe('files utility functions', () => { }) }) - test('patches Next server files', async () => { + test.only('patches Next server files', async () => { const root = path.resolve(dirname(__dirname)) - await copy(join(root, 'package.json'), path.join(process.cwd(), 'package.json')) - await ensureDir(path.join(process.cwd(), 'node_modules')) - await copy(path.join(root, 'node_modules', 'next'), path.join(process.cwd(), 'node_modules', 'next')) + console.log({ root }) + // await copy(join(root, 'package.json'), path.join(process.cwd(), 'package.json')) + // await ensureDir(path.join(process.cwd(), 'node_modules')) + // await copy(path.join(root, 'node_modules', 'next'), path.join(process.cwd(), 'node_modules', 'next')) - await patchNextFiles(process.cwd()) + // await patchNextFiles(process.cwd()) const serverFile = path.resolve(process.cwd(), 'node_modules', 'next', 'dist', 'server', 'base-server.js') const patchedData = await readFileSync(serverFile, 'utf8') expect(patchedData.includes('_REVALIDATE_SSG')).toBeTruthy() expect(patchedData.includes('private: isPreviewMode && cachedData')).toBeTruthy() - await unpatchNextFiles(process.cwd()) + // await unpatchNextFiles(process.cwd()) const unPatchedData = await readFileSync(serverFile, 'utf8') expect(unPatchedData.includes('_REVALIDATE_SSG')).toBeFalsy() From 9e67e2e96736b5a6f00f1e88a53f70f75e937e10 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Wed, 19 Apr 2023 14:18:27 +0200 Subject: [PATCH 11/14] chore: create describeCwdTmpDir helper --- test/helpers/files.spec.ts | 20 +++++++++++--------- test/test-utils.ts | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/test/helpers/files.spec.ts b/test/helpers/files.spec.ts index 8608d400ed..4e1cb3f596 100644 --- a/test/helpers/files.spec.ts +++ b/test/helpers/files.spec.ts @@ -17,6 +17,7 @@ import { dirname } from "path" import { resolve } from 'pathe' import { join } from "pathe" import { Rewrites } from "../../packages/runtime/src/helpers/types" +import { describeCwdTmpDir, moveNextDist } from "../test-utils" const REDIRECTS: Rewrites = [ { @@ -187,21 +188,22 @@ describe('files utility functions', () => { expect(matchesRewrite(path, REWRITES)).toBeTruthy() }) }) +}) - test.only('patches Next server files', async () => { - const root = path.resolve(dirname(__dirname)) - console.log({ root }) - // await copy(join(root, 'package.json'), path.join(process.cwd(), 'package.json')) - // await ensureDir(path.join(process.cwd(), 'node_modules')) - // await copy(path.join(root, 'node_modules', 'next'), path.join(process.cwd(), 'node_modules', 'next')) +describeCwdTmpDir('file patching', () => { + it('patches Next server files', async () => { + const root = path.resolve(dirname(resolve(__dirname, '..'))) + await copy(join(root, 'package.json'), path.join(process.cwd(), 'package.json')) + await ensureDir(path.join(process.cwd(), 'node_modules')) + await copy(path.join(root, 'node_modules', 'next'), path.join(process.cwd(), 'node_modules', 'next')) - // await patchNextFiles(process.cwd()) + await patchNextFiles(process.cwd()) const serverFile = path.resolve(process.cwd(), 'node_modules', 'next', 'dist', 'server', 'base-server.js') const patchedData = await readFileSync(serverFile, 'utf8') expect(patchedData.includes('_REVALIDATE_SSG')).toBeTruthy() expect(patchedData.includes('private: isPreviewMode && cachedData')).toBeTruthy() - // await unpatchNextFiles(process.cwd()) + await unpatchNextFiles(process.cwd()) const unPatchedData = await readFileSync(serverFile, 'utf8') expect(unPatchedData.includes('_REVALIDATE_SSG')).toBeFalsy() @@ -213,7 +215,7 @@ describe('dependency tracing', () => { it('generates dependency list from a source file', async () => { const dependencies = await getDependenciesOfFile(resolve(__dirname, '../fixtures/analysis/background.js')) expect(dependencies).toEqual( - ['test/webpack-api-runtime.js', 'package.json'].map((dep) => resolve(dirname(__dirname), dep)), + ['test/webpack-api-runtime.js', 'package.json'].map((dep) => resolve(dirname(resolve(__dirname, '..')), dep)), ) }) }) \ No newline at end of file diff --git a/test/test-utils.ts b/test/test-utils.ts index 7abc8d8fd4..0d5ac9f23b 100644 --- a/test/test-utils.ts +++ b/test/test-utils.ts @@ -8,6 +8,7 @@ import { readJson, copy, } from "fs-extra" +import { dir as getTmpDir } from "tmp-promise" const FIXTURES_DIR = `${__dirname}/fixtures` const SAMPLE_PROJECT_DIR = `${__dirname}/../demos/default` @@ -69,4 +70,25 @@ export const stubModules = async function (modules) { export const useFixture = async function (fixtureName) { const fixtureDir = `${FIXTURES_DIR}/${fixtureName}` await cpy('**', process.cwd(), { cwd: fixtureDir, parents: true, overwrite: true, dot: true }) +} + +// Change current cwd() to a temporary directory +export const describeCwdTmpDir = (name: string, fn: () => void): void => { + describe(name, () => { + let restoreCwd + let cleanup + + beforeEach(async () => { + const tmpDir = await getTmpDir({ unsafeCleanup: true }) + restoreCwd = changeCwd(tmpDir.path) + cleanup = tmpDir.cleanup + }) + + afterEach(async () => { + restoreCwd() + await cleanup() + }) + + fn() + }) } \ No newline at end of file From 570b4bd043457cd24cd868897b2ddd57c1125963 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Wed, 19 Apr 2023 14:21:02 +0200 Subject: [PATCH 12/14] chore: update functions --- test/helpers/functions.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/helpers/functions.spec.ts b/test/helpers/functions.spec.ts index b5e95df2ec..66abefae0a 100644 --- a/test/helpers/functions.spec.ts +++ b/test/helpers/functions.spec.ts @@ -1,7 +1,7 @@ import { getExtendedApiRouteConfigs } from "../../packages/runtime/src/helpers/functions" -import { moveNextDist } from "../test-utils" +import { describeCwdTmpDir, moveNextDist } from "../test-utils" -describe('api route file analysis', () => { +describeCwdTmpDir('api route file analysis', () => { it('extracts correct route configs from source files', async () => { await moveNextDist() const configs = await getExtendedApiRouteConfigs('.next', process.cwd()) From e6a11acf29d1613ca20dca7efc5c0a3292767d7d Mon Sep 17 00:00:00 2001 From: LekoArts Date: Thu, 20 Apr 2023 08:21:46 +0200 Subject: [PATCH 13/14] chore: path fix --- test/helpers/edge.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/helpers/edge.spec.ts b/test/helpers/edge.spec.ts index d217c3cc92..6179d29421 100644 --- a/test/helpers/edge.spec.ts +++ b/test/helpers/edge.spec.ts @@ -1,10 +1,10 @@ import { generateRscDataEdgeManifest } from '../../packages/runtime/src/helpers/edge' import type { PrerenderManifest } from 'next/dist/build' -jest.mock('../packages/runtime/src/helpers/functionsMetaData', () => { +jest.mock('../../packages/runtime/src/helpers/functionsMetaData', () => { const { NEXT_PLUGIN_NAME } = require('../packages/runtime/src/constants') return { - ...jest.requireActual('../packages/runtime/src/helpers/functionsMetaData'), + ...jest.requireActual('../../packages/runtime/src/helpers/functionsMetaData'), getPluginVersion: async () => `${NEXT_PLUGIN_NAME}@1.0.0`, } }) From 910be7a8927da6b928938dbf7e9375163b04f2c9 Mon Sep 17 00:00:00 2001 From: LekoArts Date: Thu, 20 Apr 2023 08:30:09 +0200 Subject: [PATCH 14/14] chore: fix typo --- test/helpers/edge.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helpers/edge.spec.ts b/test/helpers/edge.spec.ts index 6179d29421..f4ee91f28e 100644 --- a/test/helpers/edge.spec.ts +++ b/test/helpers/edge.spec.ts @@ -2,7 +2,7 @@ import { generateRscDataEdgeManifest } from '../../packages/runtime/src/helpers/ import type { PrerenderManifest } from 'next/dist/build' jest.mock('../../packages/runtime/src/helpers/functionsMetaData', () => { - const { NEXT_PLUGIN_NAME } = require('../packages/runtime/src/constants') + const { NEXT_PLUGIN_NAME } = require('../../packages/runtime/src/constants') return { ...jest.requireActual('../../packages/runtime/src/helpers/functionsMetaData'), getPluginVersion: async () => `${NEXT_PLUGIN_NAME}@1.0.0`,