import { join } from "path"; import fse from "fs-extra"; import execa from "execa"; import Builder from "../../src/build"; import { DEFAULT_LAMBDA_CODE_DIR, API_LAMBDA_CODE_DIR, IMAGE_LAMBDA_CODE_DIR } from "../../src/build"; import { cleanupDir, removeNewLineChars } from "../test-utils"; import { OriginRequestDefaultHandlerManifest } from "../../src/types"; jest.mock("execa"); describe("Builder Tests (no API routes)", () => { let fseRemoveSpy: jest.SpyInstance; let fseEmptyDirSpy: jest.SpyInstance; let defaultBuildManifest: OriginRequestDefaultHandlerManifest; const fixturePath = join(__dirname, "./simple-app-fixture-no-api"); const outputDir = join(fixturePath, ".test_sls_next_output"); beforeEach(async () => { const mockExeca = execa as jest.Mock; mockExeca.mockResolvedValueOnce(); fseRemoveSpy = jest.spyOn(fse, "remove").mockImplementation(() => { return; }); fseEmptyDirSpy = jest.spyOn(fse, "emptyDir"); const builder = new Builder(fixturePath, outputDir); await builder.build(); defaultBuildManifest = await fse.readJSON( join(outputDir, `${DEFAULT_LAMBDA_CODE_DIR}/manifest.json`) ); }); afterEach(() => { fseEmptyDirSpy.mockRestore(); fseRemoveSpy.mockRestore(); return cleanupDir(outputDir); }); describe("Cleanup", () => { it(".next directory is emptied except for cache/ folder", () => { expect(fseRemoveSpy).toBeCalledWith( join(fixturePath, ".next/serverless") ); expect(fseRemoveSpy).toBeCalledWith( join(fixturePath, ".next/prerender-manifest.json") ); expect(fseRemoveSpy).not.toBeCalledWith(join(fixturePath, ".next/cache")); }); it("output directory is cleanup before building", () => { expect(fseEmptyDirSpy).toBeCalledWith( expect.stringContaining(join(".test_sls_next_output", "default-lambda")) ); expect(fseEmptyDirSpy).toBeCalledWith( expect.stringContaining(join(".test_sls_next_output", "api-lambda")) ); expect(fseEmptyDirSpy).toBeCalledWith( expect.stringContaining(join(".test_sls_next_output", "assets")) ); }); }); describe("Default Handler Manifest", () => { it("adds full manifest", () => { const { buildId, publicFiles, pages: { dynamic, ssg, ssr, html }, trailingSlash } = defaultBuildManifest; expect(removeNewLineChars(buildId)).toEqual("test-build-id"); expect(dynamic).toEqual([ { route: "/blog/[post]", regex: "^\\/blog(?:\\/([^\\/#\\?]+?))[\\/#\\?]?$" }, { route: "/customers/[customer]", regex: "^\\/customers(?:\\/([^\\/#\\?]+?))[\\/#\\?]?$" }, { route: "/customers/[customer]/profile", regex: "^\\/customers(?:\\/([^\\/#\\?]+?))\\/profile[\\/#\\?]?$" }, { route: "/customers/[customer]/[post]", regex: "^\\/customers(?:\\/([^\\/#\\?]+?))(?:\\/([^\\/#\\?]+?))[\\/#\\?]?$" }, { route: "/customers/[...catchAll]", regex: "^\\/customers(?:\\/((?:[^\\/#\\?]+?)(?:\\/(?:[^\\/#\\?]+?))*))?[\\/#\\?]?$" }, { route: "/[root]", regex: "^(?:\\/([^\\/#\\?]+?))[\\/#\\?]?$" } ]); expect(ssg).toEqual({ nonDynamic: { "/": { initialRevalidateSeconds: false, srcRoute: null }, "/contact": { initialRevalidateSeconds: false, srcRoute: null } }, dynamic: {}, notFound: {} }); expect(ssr).toEqual({ nonDynamic: { "/customers/new": "pages/customers/new.js", "/_app": "pages/_app.js", "/_document": "pages/_document.js" }, dynamic: { "/[root]": "pages/[root].js", "/customers/[customer]": "pages/customers/[customer].js", "/customers/[customer]/[post]": "pages/customers/[customer]/[post].js", "/customers/[customer]/profile": "pages/customers/[customer]/profile.js", "/customers/[...catchAll]": "pages/customers/[...catchAll].js" } }); expect(html).toEqual({ nonDynamic: { "/404": "pages/404.html", "/terms": "pages/terms.html", "/about": "pages/about.html" }, dynamic: { "/blog/[post]": "pages/blog/[post].html" } }); expect(publicFiles).toEqual({ "/favicon.ico": "favicon.ico", "/sub/image.png": "sub/image.png", "/sw.js": "sw.js" }); expect(trailingSlash).toBe(false); }); }); describe("API Handler", () => { it("has empty API handler directory", async () => { expect.assertions(1); const apiDir = await fse.readdir( join(outputDir, `${API_LAMBDA_CODE_DIR}`) ); expect(apiDir).toEqual([]); }); }); describe("Images Handler", () => { it("has empty images handler directory", async () => { expect.assertions(1); const imageDir = await fse.readdir( join(outputDir, `${IMAGE_LAMBDA_CODE_DIR}`) ); expect(imageDir).toEqual([]); }); }); describe("Default Handler Artefact Files", () => { it("copies build files", async () => { expect.assertions(7); const files = await fse.readdir( join(outputDir, `${DEFAULT_LAMBDA_CODE_DIR}`) ); const pages = await fse.readdir( join(outputDir, `${DEFAULT_LAMBDA_CODE_DIR}/pages`) ); const customerPages = await fse.readdir( join(outputDir, `${DEFAULT_LAMBDA_CODE_DIR}/pages/customers`) ); const apiDirExists = await fse.pathExists( join(outputDir, `${DEFAULT_LAMBDA_CODE_DIR}/pages/api`) ); expect(files).toEqual( expect.arrayContaining([ "index.js", // there are more chunks but it should at least contain the entry point "manifest.json", "pages", "prerender-manifest.json", "routes-manifest.json" ]) ); // api pages should not be included in the default lambda expect(apiDirExists).toEqual(false); // HTML Prerendered pages or JSON static props files // should not be included in the default lambda expect(pages).not.toContain(["blog.json", "index.json", "contact.json"]); expect(pages).not.toContain([ "about.html", "terms.html", "contact.html", "index.html" ]); // JS files used only for prerendering at build time (contact.js, index.js) are not included since there are no API routes expect(pages).not.toContain(["contact.js", "index.js"]); expect(pages).toEqual(["_error.js", "customers"]); expect(customerPages).toEqual(["[...catchAll].js"]); }); }); });