describe("Pages Tests", () => { const buildId = Cypress.env("NEXT_BUILD_ID"); before(() => { cy.ensureAllRoutesNotErrored(); }); describe("Dynamic Page at root level with getStaticPaths() fallback: true", () => { [{ path: "/a" }, { path: "/b" }, { path: "/not-prerendered" }].forEach( ({ path }) => { it(`serves and caches page ${path}`, () => { cy.visit(path); cy.location("pathname").should("eq", path); cy.ensureRouteCached(path); cy.visit(path); }); ["HEAD", "GET"].forEach((method) => { it(`allows HTTP method for path ${path}: ${method}`, () => { cy.request({ url: path, method: method }).then((response) => { expect(response.status).to.equal(200); }); }); }); ["DELETE", "POST", "OPTIONS", "PUT", "PATCH"].forEach((method) => { it(`disallows HTTP method for path ${path} with 4xx error: ${method}`, () => { cy.request({ url: path, method: method, failOnStatusCode: false }).then((response) => { expect(response.status).to.be.at.least(400); expect(response.status).to.be.lessThan(500); }); }); }); } ); it("serves non-dynamic SSG page at same level", () => { const path = "/ssg-page"; cy.request({ url: path }).then((response) => { expect(response.body).to.contain("SSG Page"); }); }); it("serves non-dynamic SSR page at same level", () => { const path = "/ssr-page"; cy.request({ url: path }).then((response) => { expect(response.body).to.contain("SSR Page"); }); }); }); describe("Nested Dynamic Page with getStaticPaths() fallback: false", () => { [{ path: "/nested/a" }, { path: "/nested/b" }].forEach(({ path }) => { it(`serves and caches page ${path}`, () => { cy.visit(path); cy.location("pathname").should("eq", path); cy.ensureRouteCached(path); cy.visit(path); }); ["HEAD", "GET"].forEach((method) => { it(`allows HTTP method for path ${path}: ${method}`, () => { cy.request({ url: path, method: method }).then((response) => { expect(response.status).to.equal(200); }); }); }); ["DELETE", "POST", "OPTIONS", "PUT", "PATCH"].forEach((method) => { it(`disallows HTTP method for path ${path} with 4xx error: ${method}`, () => { cy.request({ url: path, method: method, failOnStatusCode: false }).then((response) => { expect(response.status).to.be.at.least(400); expect(response.status).to.be.lessThan(500); }); }); }); }); [{ path: "/nested/not-prerendered" }].forEach(({ path }) => { it(`serves page ${path}`, () => { cy.visit(path, { failOnStatusCode: false }); cy.location("pathname").should("eq", path); cy.visit(path, { failOnStatusCode: false }); }); ["HEAD", "GET"].forEach((method) => { it(`allows HTTP method for path ${path} and serves 404: ${method}`, () => { cy.request({ url: path, method: method, failOnStatusCode: false }).then((response) => { expect(response.status).to.equal(404); }); }); }); ["DELETE", "POST", "OPTIONS", "PUT", "PATCH"].forEach((method) => { it(`disallows HTTP method for path ${path} with 4xx error: ${method}`, () => { cy.request({ url: path, method: method, failOnStatusCode: false }).then((response) => { expect(response.status).to.not.equal(404); expect(response.status).to.be.at.least(400); expect(response.status).to.be.lessThan(500); }); }); }); }); }); describe("Catch-all SSR Page", () => { [{ path: "/a/b" }].forEach(({ path }) => { it(`serves and caches page ${path}`, () => { cy.ensureRouteNotCached(path); cy.visit(path); cy.location("pathname").should("eq", path); cy.request(path).then((response) => { expect(response.body).to.contain("catch-all-ssr"); }); }); ["HEAD", "DELETE", "POST", "GET", "OPTIONS", "PUT", "PATCH"].forEach( (method) => { it(`allows HTTP method for path ${path}: ${method}`, () => { cy.request({ url: path, method: method }).then((response) => { expect(response.status).to.equal(200); }); }); } ); it(`serves data request for ${path}`, () => { const fullPath = `/_next/data/${buildId}${path.replace( /\/$/, "/index" )}.json`; const dataRequestParam = path.replace("/", ""); cy.request({ url: fullPath, method: "GET" }).then((response) => { expect(response.status).to.equal(200); expect(response.body).to.deep.equal({ pageProps: { name: "serverless-next.js", catch: dataRequestParam }, __N_SSP: true }); }); }); }); }); describe("Optional catch-all SSR Page", () => { [ { path: "/optional-catch-all-ssr" }, { path: "/optional-catch-all-ssr/a" }, { path: "/optional-catch-all-ssr/b" } ].forEach(({ path }) => { const param = path .replace("/optional-catch-all-ssr", "") .replace("/", ""); it(`serves and caches page ${path}`, () => { cy.ensureRouteNotCached(path); cy.visit(path); cy.contains("optional-catch-all-ssr"); cy.location("pathname").should("eq", path); // Make sure page itself is SSR'd and contains the dynamic parameter on initial response cy.request(path).then((response) => { expect(response.body).to.contain("optional-catch-all-ssr"); expect(response.body).to.contain(`

${param}

`); }); }); ["HEAD", "DELETE", "POST", "GET", "OPTIONS", "PUT", "PATCH"].forEach( (method) => { it(`allows HTTP method for path ${path}: ${method}`, () => { cy.request({ url: path, method: method }).then((response) => { expect(response.status).to.equal(200); }); }); } ); it(`serves data request for ${path}`, () => { const fullPath = `/_next/data/${buildId}${path.replace( /\/$/, "/index" )}.json`; cy.request({ url: fullPath, method: "GET" }).then((response) => { expect(response.status).to.equal(200); expect(response.body).to.deep.equal({ pageProps: { name: "serverless-next.js", catch: param }, __N_SSP: true }); }); }); }); }); describe("Optional catch-all SSG Page with fallback: false", () => { [ { path: "/optional-catch-all-ssg-no-fallback" }, { path: "/optional-catch-all-ssg-no-fallback/a" }, { path: "/optional-catch-all-ssg-no-fallback/b" } ].forEach(({ path }) => { const param = path .replace("/optional-catch-all-ssg-no-fallback", "") .replace("/", ""); it(`serves and caches page ${path}`, () => { cy.visit(path); cy.contains("optional-catch-all-ssg-no-fallback"); cy.location("pathname").should("eq", path); cy.ensureRouteCached(path); cy.visit(path); // Make sure page itself is SSG'd and contains the pre-built parameter in initial response cy.request(path).then((response) => { expect(response.body).to.contain( "optional-catch-all-ssg-no-fallback" ); expect(response.body).to.contain(`

${param}

`); }); }); ["HEAD", "GET"].forEach((method) => { it(`allows HTTP method for path ${path}: ${method}`, () => { cy.request({ url: path, method: method }).then((response) => { expect(response.status).to.equal(200); }); }); }); ["DELETE", "POST", "OPTIONS", "PUT", "PATCH"].forEach((method) => { it(`disallows HTTP method for path ${path} with 4xx error ${method}`, () => { cy.request({ url: path, method: method, failOnStatusCode: false }).then((response) => { expect(response.status).to.be.gte(400); }); }); }); it(`serves data request for ${path}`, () => { const fullPath = `/_next/data/${buildId}${path.replace( /\/$/, "/index" )}.json`; cy.request({ url: fullPath, method: "GET" }).then((response) => { expect(response.status).to.equal(200); expect(response.body).to.deep.equal({ pageProps: { name: "serverless-next.js", catch: param }, __N_SSG: true }); }); }); }); [{ path: "/optional-catch-all-ssg-no-fallback/not-found" }].forEach( ({ path }) => { ["HEAD", "GET"].forEach((method) => { it(`allows HTTP method for path ${path}: ${method} and returns 404 status`, () => { cy.request({ url: path, method: method, failOnStatusCode: false }).then((response) => { expect(response.status).to.equal(404); }); }); }); ["DELETE", "POST", "OPTIONS", "PUT", "PATCH"].forEach((method) => { it(`disallows HTTP method for path ${path} with 4xx status code: ${method}`, () => { cy.request({ url: path, method: method, failOnStatusCode: false }).then((response) => { expect(response.status).to.be.gte(400); }); }); }); it(`returns 404 on data request for ${path}`, () => { const fullPath = `/_next/data/${buildId}${path.replace( /\/$/, "/index" )}.json`; cy.request({ url: fullPath, method: "GET", failOnStatusCode: false }).then((response) => { expect(response.status).to.equal(404); }); }); } ); }); describe("Optional catch-all SSG Page with fallback: true", () => { [ { path: "/optional-catch-all-ssg-with-fallback", param: "", prerendered: true }, { path: "/optional-catch-all-ssg-with-fallback/a", param: "a", prerendered: true }, { path: "/optional-catch-all-ssg-with-fallback/b", param: "b", prerendered: true }, { path: "/optional-catch-all-ssg-with-fallback/not-prerendered", param: "", prerendered: false } ].forEach(({ path, param, prerendered }) => { it(`serves and caches page ${path}`, () => { cy.visit(path); cy.contains("optional-catch-all-ssg-with-fallback"); cy.location("pathname").should("eq", path); cy.ensureRouteCached(path); cy.visit(path); // Make sure page itself is SSG'd and contains the pre-built parameter in initial response cy.request(path).then((response) => { expect(response.body).to.contain( "optional-catch-all-ssg-with-fallback" ); if (prerendered) { expect(response.body).to.contain(`

${param}

`); } }); }); ["HEAD", "GET"].forEach((method) => { it(`allows HTTP method for path ${path}: ${method}`, () => { cy.request({ url: path, method: method }).then((response) => { expect(response.status).to.equal(200); }); }); }); ["DELETE", "POST", "OPTIONS", "PUT", "PATCH"].forEach((method) => { it(`disallows HTTP method for path ${path} with 4xx error ${method}`, () => { cy.request({ url: path, method: method, failOnStatusCode: false }).then((response) => { expect(response.status).to.be.gte(400); }); }); }); it(`serves data request for ${path}`, () => { const fullPath = `/_next/data/${buildId}${path.replace( /\/$/, "/index" )}.json`; const dataRequestParam = path .replace("/optional-catch-all-ssg-with-fallback", "") .replace("/", ""); cy.request({ url: fullPath, method: "GET" }).then((response) => { expect(response.status).to.equal(200); expect(response.body).to.deep.equal({ pageProps: { name: "serverless-next.js", catch: dataRequestParam }, __N_SSG: true }); }); // If not prerendered, check that the SSG data request gets cached on the 2nd time requested if (!prerendered) { cy.request({ url: fullPath, method: "GET" }).then((response) => { expect(response.status).to.equal(200); expect(response.body).to.deep.equal({ pageProps: { name: "serverless-next.js", catch: dataRequestParam }, __N_SSG: true }); cy.verifyResponseCacheStatus(response, true); }); } }); }); [ { path: "/optional-catch-all-ssg-with-fallback/not-prerendered", param: "not-prerendered" } ].forEach(({ path, param }) => { it(`serves but does not cache fallback page, then caches page ${path}`, () => { // Ensure we hit a new page const now = Date.now(); const newPath = `${path}-${now}`; // Verify first request is to fallback page cy.request(newPath).then((response) => { expect(response.headers["cache-control"]).to.equal( "public, max-age=0, s-maxage=0, must-revalidate" ); expect(response.body).to.contain('

'); }); // TODO: not sure why but I couldn't verify that subsequent request is the prerendered page, Cypress seemed to cache the fallback page response. // However, verified manually that it works correctly. }); }); [{ path: "/optional-catch-all-ssg-with-fallback/not-found" }].forEach( ({ path }) => { ["HEAD", "GET"].forEach((method) => { it(`allows HTTP method for path ${path}: ${method} and returns 200 status`, () => { cy.request({ url: path, method: method, failOnStatusCode: false }).then((response) => { expect(response.status).to.equal(200); }); }); }); ["DELETE", "POST", "OPTIONS", "PUT", "PATCH"].forEach((method) => { it(`disallows HTTP method for path ${path} with 4xx status code: ${method}`, () => { cy.request({ url: path, method: method, failOnStatusCode: false }).then((response) => { expect(response.status).to.be.gte(400); }); }); }); it(`returns 404 on data request for ${path}`, () => { const fullPath = `/_next/data/${buildId}${path.replace( /\/$/, "/index" )}.json`; cy.request({ url: fullPath, method: "GET", failOnStatusCode: false }).then((response) => { expect(response.status).to.equal(404); }); }); } ); }); describe("Dynamic SSR page", () => { [{ path: "/another/1234" }].forEach(({ path }) => { it(`serves and caches page ${path}`, () => { cy.ensureRouteNotCached(path); cy.visit(path); cy.location("pathname").should("eq", path); cy.request(path).then((response) => { expect(response.body).to.contain("dynamic-ssr"); }); }); ["HEAD", "DELETE", "POST", "GET", "OPTIONS", "PUT", "PATCH"].forEach( (method) => { it(`allows HTTP method for path ${path}: ${method}`, () => { cy.request({ url: path, method: method }).then((response) => { expect(response.status).to.equal(200); }); }); } ); }); [{ path: "/another/ssg-prioritized-over-dynamic-ssr" }].forEach( ({ path }) => { it(`serves and caches page ${path}`, () => { cy.visit(path); cy.ensureRouteCached(path); cy.visit(path); cy.location("pathname").should("eq", path); cy.request(path).then((response) => { expect(response.body).to.contain( "ssg-prioritized-over-dynamic-ssr" ); }); }); ["HEAD", "GET"].forEach((method) => { it(`allows HTTP method for path ${path}: ${method}`, () => { cy.request({ url: path, method: method }).then((response) => { expect(response.status).to.equal(200); }); }); }); ["DELETE", "POST", "OPTIONS", "PUT", "PATCH"].forEach((method) => { it(`disallows HTTP method for path ${path} with 4xx error: ${method}`, () => { cy.request({ url: path, method: method, failOnStatusCode: false }).then((response) => { expect(response.status).to.not.equal(404); expect(response.status).to.be.at.least(400); expect(response.status).to.be.lessThan(500); }); }); }); } ); }); });