diff --git a/test/e2e/views-dir/app-rendering/next.config.js b/test/e2e/views-dir/app-rendering/next.config.js new file mode 100644 index 0000000000000..4131d5b608757 --- /dev/null +++ b/test/e2e/views-dir/app-rendering/next.config.js @@ -0,0 +1,8 @@ +module.exports = { + experimental: { + viewsDir: true, + runtime: 'nodejs', + reactRoot: true, + serverComponents: true, + }, +} diff --git a/test/e2e/views-dir/app-rendering/pages/.gitkeep b/test/e2e/views-dir/app-rendering/pages/.gitkeep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/e2e/views-dir/app-rendering/views/getserversideprops-only/layout.server.js b/test/e2e/views-dir/app-rendering/views/getserversideprops-only/layout.server.js new file mode 100644 index 0000000000000..0dd5047f4bbb8 --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getserversideprops-only/layout.server.js @@ -0,0 +1,16 @@ +export async function getServerSideProps() { + return { + props: { + message: 'hello from layout', + }, + } +} + +export default function gsspLayout(props) { + return ( + <> +

{props.message}

+ {props.children} + + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getserversideprops-only/nested/page.server.js b/test/e2e/views-dir/app-rendering/views/getserversideprops-only/nested/page.server.js new file mode 100644 index 0000000000000..f98e04183f185 --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getserversideprops-only/nested/page.server.js @@ -0,0 +1,15 @@ +export async function getServerSideProps() { + return { + props: { + message: 'hello from page', + }, + } +} + +export default function nestedPage(props) { + return ( + <> +

{props.message}

+ + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getserversideprops-only/slow/index/page.server.js b/test/e2e/views-dir/app-rendering/views/getserversideprops-only/slow/index/page.server.js new file mode 100644 index 0000000000000..e639971eb019b --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getserversideprops-only/slow/index/page.server.js @@ -0,0 +1,16 @@ +export async function getServerSideProps() { + await new Promise((resolve) => setTimeout(resolve, 5000)) + return { + props: { + message: 'hello from slow page', + }, + } +} + +export default function nestedPage(props) { + return ( + <> +

{props.message}

+ + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getserversideprops-only/slow/layout.server.js b/test/e2e/views-dir/app-rendering/views/getserversideprops-only/slow/layout.server.js new file mode 100644 index 0000000000000..a6793b38bcc8c --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getserversideprops-only/slow/layout.server.js @@ -0,0 +1,17 @@ +export async function getServerSideProps() { + await new Promise((resolve) => setTimeout(resolve, 5000)) + return { + props: { + message: 'hello from slow layout', + }, + } +} + +export default function gsspLayout(props) { + return ( + <> +

{props.message}

+ {props.children} + + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/nested/index/page.server.js b/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/nested/index/page.server.js new file mode 100644 index 0000000000000..12f6a694f92c2 --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/nested/index/page.server.js @@ -0,0 +1,17 @@ +export async function getStaticProps() { + return { + props: { + message: 'hello from page', + nowDuringBuild: Date.now(), + }, + } +} + +export default function nestedPage(props) { + return ( + <> +

{props.message}

+

{props.nowDuringBuild}

+ + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/nested/layout.server.js b/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/nested/layout.server.js new file mode 100644 index 0000000000000..443bf17233cbd --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/nested/layout.server.js @@ -0,0 +1,18 @@ +export async function getServerSideProps() { + return { + props: { + message: 'hello from layout', + nowDuringExecution: Date.now(), + }, + } +} + +export default function gspLayout(props) { + return ( + <> +

{props.message}

+

{props.nowDuringExecution}

+ {props.children} + + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/slow/index/page.server.js b/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/slow/index/page.server.js new file mode 100644 index 0000000000000..451fb50884463 --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/slow/index/page.server.js @@ -0,0 +1,18 @@ +export async function getStaticProps() { + await new Promise((resolve) => setTimeout(resolve, 5000)) + return { + props: { + message: 'hello from slow page', + nowDuringBuild: Date.now(), + }, + } +} + +export default function nestedPage(props) { + return ( + <> +

{props.message}

+

{props.nowDuringBuild}

+ + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/slow/layout.server.js b/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/slow/layout.server.js new file mode 100644 index 0000000000000..9407fe734e7b8 --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getstaticprops-getserversideprops-combined/slow/layout.server.js @@ -0,0 +1,19 @@ +export async function getServerSideProps() { + await new Promise((resolve) => setTimeout(resolve, 5000)) + return { + props: { + message: 'hello from slow layout', + nowDuringExecution: Date.now(), + }, + } +} + +export default function gspLayout(props) { + return ( + <> +

{props.message}

+

{props.nowDuringExecution}

+ {props.children} + + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getstaticprops-isr-multiple/layout.server.js b/test/e2e/views-dir/app-rendering/views/getstaticprops-isr-multiple/layout.server.js new file mode 100644 index 0000000000000..67c77b7f321eb --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getstaticprops-isr-multiple/layout.server.js @@ -0,0 +1,19 @@ +export async function getStaticProps() { + return { + props: { + message: 'hello from layout', + now: Date.now(), + }, + revalidate: 1, + } +} + +export default function gspLayout(props) { + return ( + <> +

{props.message}

+

{props.now}

+ {props.children} + + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getstaticprops-isr-multiple/nested/page.server.js b/test/e2e/views-dir/app-rendering/views/getstaticprops-isr-multiple/nested/page.server.js new file mode 100644 index 0000000000000..dd9f17ee6a864 --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getstaticprops-isr-multiple/nested/page.server.js @@ -0,0 +1,17 @@ +export async function getStaticProps() { + return { + props: { + message: 'hello from page', + now: Date.now(), + }, + revalidate: 1, + } +} +export default function nestedPage(props) { + return ( + <> +

{props.message}

+

{props.now}

+ + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getstaticprops-only/layout.server.js b/test/e2e/views-dir/app-rendering/views/getstaticprops-only/layout.server.js new file mode 100644 index 0000000000000..7dce7706415ba --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getstaticprops-only/layout.server.js @@ -0,0 +1,16 @@ +export async function getStaticProps() { + return { + props: { + message: 'hello from layout', + }, + } +} + +export default function gspLayout(props) { + return ( + <> +

{props.message}

+ {props.children} + + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getstaticprops-only/nested/page.server.js b/test/e2e/views-dir/app-rendering/views/getstaticprops-only/nested/page.server.js new file mode 100644 index 0000000000000..180669062ed99 --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getstaticprops-only/nested/page.server.js @@ -0,0 +1,15 @@ +export async function getStaticProps() { + return { + props: { + message: 'hello from page', + }, + } +} + +export default function nestedPage(props) { + return ( + <> +

{props.message}

+ + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getstaticprops-only/slow/index/page.server.js b/test/e2e/views-dir/app-rendering/views/getstaticprops-only/slow/index/page.server.js new file mode 100644 index 0000000000000..0720e7268c0dd --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getstaticprops-only/slow/index/page.server.js @@ -0,0 +1,16 @@ +export async function getStaticProps() { + await new Promise((resolve) => setTimeout(resolve, 5000)) + return { + props: { + message: 'hello from slow page', + }, + } +} + +export default function nestedPage(props) { + return ( + <> +

{props.message}

+ + ) +} diff --git a/test/e2e/views-dir/app-rendering/views/getstaticprops-only/slow/layout.server.js b/test/e2e/views-dir/app-rendering/views/getstaticprops-only/slow/layout.server.js new file mode 100644 index 0000000000000..2a6bd7cc462a5 --- /dev/null +++ b/test/e2e/views-dir/app-rendering/views/getstaticprops-only/slow/layout.server.js @@ -0,0 +1,17 @@ +export async function getStaticProps() { + await new Promise((resolve) => setTimeout(resolve, 5000)) + return { + props: { + message: 'hello from slow layout', + }, + } +} + +export default function gspLayout(props) { + return ( + <> +

{props.message}

+ {props.children} + + ) +} diff --git a/test/e2e/views-dir/rendering.test.ts b/test/e2e/views-dir/rendering.test.ts new file mode 100644 index 0000000000000..ab8811e7d3183 --- /dev/null +++ b/test/e2e/views-dir/rendering.test.ts @@ -0,0 +1,156 @@ +import { createNext, FileRef } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { renderViaHTTP, fetchViaHTTP, waitFor } from 'next-test-utils' +import path from 'path' +import cheerio from 'cheerio' + +describe('views dir rendering', () => { + if (process.env.NEXT_TEST_REACT_VERSION === '^17') { + it('should skip for react v17', () => {}) + return + } + + const isDev = (global as any).isDev + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + views: new FileRef(path.join(__dirname, 'app-rendering/views')), + pages: new FileRef(path.join(__dirname, 'app-rendering/pages')), + 'next.config.js': new FileRef( + path.join(__dirname, 'app-rendering/next.config.js') + ), + }, + }) + }) + afterAll(() => next.destroy()) + + describe('getServerSideProps only', () => { + it('should run getServerSideProps in layout and page', async () => { + const html = await renderViaHTTP( + next.url, + '/getserversideprops-only/nested' + ) + const $ = cheerio.load(html) + expect($('#layout-message').text()).toBe('hello from layout') + expect($('#page-message').text()).toBe('hello from page') + }) + + it('should run getServerSideProps in parallel', async () => { + const startTime = Date.now() + const html = await renderViaHTTP( + next.url, + '/getserversideprops-only/slow' + ) + const endTime = Date.now() + const duration = endTime - startTime + // Each part takes 5 seconds so it should be below 10 seconds + // Using 7 seconds to ensure external factors causing slight slowness don't fail the tests + expect(duration < 7000).toBe(true) + const $ = cheerio.load(html) + expect($('#slow-layout-message').text()).toBe('hello from slow layout') + expect($('#slow-page-message').text()).toBe('hello from slow page') + }) + }) + + describe('getStaticProps only', () => { + it('should run getStaticProps in layout and page', async () => { + const html = await renderViaHTTP(next.url, '/getstaticprops-only/nested') + const $ = cheerio.load(html) + expect($('#layout-message').text()).toBe('hello from layout') + expect($('#page-message').text()).toBe('hello from page') + }) + + it(`should run getStaticProps in parallel ${ + isDev ? 'during development' : 'and use cached version for production' + }`, async () => { + const startTime = Date.now() + const html = await renderViaHTTP(next.url, '/getstaticprops-only/slow') + const endTime = Date.now() + const duration = endTime - startTime + // Each part takes 5 seconds so it should be below 10 seconds + // Using 7 seconds to ensure external factors causing slight slowness don't fail the tests + // TODO: cache static props in prod + // expect(duration < (isDev ? 7000 : 2000)).toBe(true) + expect(duration < 7000).toBe(true) + const $ = cheerio.load(html) + expect($('#slow-layout-message').text()).toBe('hello from slow layout') + expect($('#slow-page-message').text()).toBe('hello from slow page') + }) + }) + + describe('getStaticProps ISR', () => { + it('should revalidate the page when getStaticProps return revalidate', async () => { + const getPage = async () => { + const res = await fetchViaHTTP( + next.url, + 'getstaticprops-isr-multiple/nested' + ) + const html = await res.text() + + return { + $: cheerio.load(html), + cacheHeader: res.headers['x-nextjs-cache'], + } + } + const { $ } = await getPage() + expect($('#layout-message').text()).toBe('hello from layout') + expect($('#page-message').text()).toBe('hello from page') + + const layoutNow = $('#layout-now').text() + const pageNow = $('#page-now').text() + + await waitFor(2000) + + // TODO: implement + // Trigger revalidate + // const { cacheHeader: revalidateCacheHeader } = await getPage() + // expect(revalidateCacheHeader).toBe('STALE') + + // TODO: implement + const { $: $revalidated /* cacheHeader: revalidatedCacheHeader */ } = + await getPage() + // expect(revalidatedCacheHeader).toBe('REVALIDATED') + + const layoutNowRevalidated = $revalidated('#layout-now').text() + const pageNowRevalidated = $revalidated('#page-now').text() + + // Expect that the `Date.now()` is different as the page have been regenerated + expect(layoutNow).not.toBe(layoutNowRevalidated) + expect(pageNow).not.toBe(pageNowRevalidated) + }) + }) + + // TODO: implement + describe.skip('getStaticProps and getServerSideProps without ISR', () => { + it('should generate getStaticProps data during build an use it', async () => { + const getPage = async () => { + const html = await renderViaHTTP( + next.url, + 'getstaticprops-getserversideprops-combined/nested' + ) + + return { + $: cheerio.load(html), + } + } + const { $ } = await getPage() + expect($('#layout-message').text()).toBe('hello from layout') + expect($('#page-message').text()).toBe('hello from page') + + const layoutNow = $('#layout-now').text() + const pageNow = $('#page-now').text() + + const { $: $second } = await getPage() + + const layoutNowSecond = $second('#layout-now').text() + const pageNowSecond = $second('#page-now').text() + + // Expect that the `Date.now()` is different as it came from getServerSideProps + expect(layoutNow).not.toBe(layoutNowSecond) + // Expect that the `Date.now()` is the same as it came from getStaticProps + expect(pageNow).toBe(pageNowSecond) + }) + }) +})