From c3bc7cdf55904ac891196aa408583bf8309714d9 Mon Sep 17 00:00:00 2001
From: Tiffany Le-Nguyen
Date: Thu, 4 Nov 2021 11:12:58 -0400
Subject: [PATCH 01/13] chore: move cypress out of demo
---
{demo/cypress => cypress}/config/all.json | 0
{demo/cypress => cypress}/fixtures/example.json | 0
{demo/cypress => cypress}/integration/test.spec.ts | 0
{demo/cypress => cypress}/plugins/index.js | 0
{demo/cypress => cypress}/support/commands.js | 0
{demo/cypress => cypress}/tsconfig.json | 4 ++--
6 files changed, 2 insertions(+), 2 deletions(-)
rename {demo/cypress => cypress}/config/all.json (100%)
rename {demo/cypress => cypress}/fixtures/example.json (100%)
rename {demo/cypress => cypress}/integration/test.spec.ts (100%)
rename {demo/cypress => cypress}/plugins/index.js (100%)
rename {demo/cypress => cypress}/support/commands.js (100%)
rename {demo/cypress => cypress}/tsconfig.json (76%)
diff --git a/demo/cypress/config/all.json b/cypress/config/all.json
similarity index 100%
rename from demo/cypress/config/all.json
rename to cypress/config/all.json
diff --git a/demo/cypress/fixtures/example.json b/cypress/fixtures/example.json
similarity index 100%
rename from demo/cypress/fixtures/example.json
rename to cypress/fixtures/example.json
diff --git a/demo/cypress/integration/test.spec.ts b/cypress/integration/test.spec.ts
similarity index 100%
rename from demo/cypress/integration/test.spec.ts
rename to cypress/integration/test.spec.ts
diff --git a/demo/cypress/plugins/index.js b/cypress/plugins/index.js
similarity index 100%
rename from demo/cypress/plugins/index.js
rename to cypress/plugins/index.js
diff --git a/demo/cypress/support/commands.js b/cypress/support/commands.js
similarity index 100%
rename from demo/cypress/support/commands.js
rename to cypress/support/commands.js
diff --git a/demo/cypress/tsconfig.json b/cypress/tsconfig.json
similarity index 76%
rename from demo/cypress/tsconfig.json
rename to cypress/tsconfig.json
index 8e1ac5581f..5d0c679750 100644
--- a/demo/cypress/tsconfig.json
+++ b/cypress/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "../../tsconfig.json",
+ "extends": "../tsconfig.json",
"compilerOptions": {
"noEmit": true,
// be explicit about types included
@@ -7,7 +7,7 @@
"types": ["cypress", "mocha", "@testing-library/cypress"]
},
"include": [
- "../../node_modules/cypress",
+ "../node_modules/cypress",
"./**/*.ts"
]
}
\ No newline at end of file
From 8ca0d6773865316f74e635e516acfb4660a41428 Mon Sep 17 00:00:00 2001
From: Tiffany Le-Nguyen
Date: Thu, 4 Nov 2021 11:50:57 -0400
Subject: [PATCH 02/13] Update package.json
---
package.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index 85df6a88e0..15f1f27863 100644
--- a/package.json
+++ b/package.json
@@ -9,8 +9,8 @@
],
"scripts": {
"build:demo": "next build demo",
- "cy:open": "cypress open --config-file cypress/config/all.json",
- "cy:run": "cypress run --config-file cypress/config/all.json",
+ "cy:open": "cypress open --config-file ../cypress/config/all.json",
+ "cy:run": "cypress run --config-file ../cypress/config/all.json",
"dev:demo": "next dev demo",
"format": "run-s format:check-fix:*",
"format:ci": "run-s format:check:*",
From f88846214e19848d419e6f2a618d6cbbc5cb2b0a Mon Sep 17 00:00:00 2001
From: Tiffany Le-Nguyen
Date: Thu, 4 Nov 2021 12:03:12 -0400
Subject: [PATCH 03/13] chore: add ci specific config
---
cypress/config/all.json | 3 ++-
cypress/config/ci.json | 8 ++++++++
package.json | 2 +-
3 files changed, 11 insertions(+), 2 deletions(-)
create mode 100644 cypress/config/ci.json
diff --git a/cypress/config/all.json b/cypress/config/all.json
index 1af3269d61..e3093e146e 100644
--- a/cypress/config/all.json
+++ b/cypress/config/all.json
@@ -1,3 +1,4 @@
{
- "video": false
+ "video": false,
+ "baseUrl": "http://localhost:3000"
}
\ No newline at end of file
diff --git a/cypress/config/ci.json b/cypress/config/ci.json
new file mode 100644
index 0000000000..0fbb0d7e9a
--- /dev/null
+++ b/cypress/config/ci.json
@@ -0,0 +1,8 @@
+{
+ "baseUrl": "http://localhost:3000",
+ "integrationFolder": "../cypress/integration",
+ "pluginsFile": "../cypress/plugins",
+ "screenshotsFolder": "../cypress/screenshots",
+ "supportFile": "../cypress/support/index.js",
+ "videoFolder": "../cypress/videos"
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index 15f1f27863..204b68e478 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"scripts": {
"build:demo": "next build demo",
"cy:open": "cypress open --config-file ../cypress/config/all.json",
- "cy:run": "cypress run --config-file ../cypress/config/all.json",
+ "cy:run": "cypress run --config-file ../cypress/config/ci.json",
"dev:demo": "next dev demo",
"format": "run-s format:check-fix:*",
"format:ci": "run-s format:check:*",
From f537dc4cf9da43000a29e150461c87d37af36935 Mon Sep 17 00:00:00 2001
From: Tiffany Le-Nguyen
Date: Thu, 4 Nov 2021 12:08:37 -0400
Subject: [PATCH 04/13] chore: fix testing library support
---
cypress/integration/test.spec.ts | 2 +-
cypress/support/index.js | 2 ++
package.json | 2 +-
3 files changed, 4 insertions(+), 2 deletions(-)
create mode 100644 cypress/support/index.js
diff --git a/cypress/integration/test.spec.ts b/cypress/integration/test.spec.ts
index 796cb62ece..341034cb44 100644
--- a/cypress/integration/test.spec.ts
+++ b/cypress/integration/test.spec.ts
@@ -4,6 +4,6 @@ describe('TypeScript spec', () => {
})
it('loads home page', () => {
- cy.get('h1')
+ cy.findByText('Next Demo!')
})
})
\ No newline at end of file
diff --git a/cypress/support/index.js b/cypress/support/index.js
new file mode 100644
index 0000000000..7a9a126f17
--- /dev/null
+++ b/cypress/support/index.js
@@ -0,0 +1,2 @@
+// eslint-disable-next-line import/no-unassigned-import
+import './commands';
diff --git a/package.json b/package.json
index 204b68e478..d485c179d7 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
],
"scripts": {
"build:demo": "next build demo",
- "cy:open": "cypress open --config-file ../cypress/config/all.json",
+ "cy:open": "cypress open --config-file cypress/config/all.json",
"cy:run": "cypress run --config-file ../cypress/config/ci.json",
"dev:demo": "next dev demo",
"format": "run-s format:check-fix:*",
From fed6aa235ad79cc0acb4e56277008f50a4f88d2d Mon Sep 17 00:00:00 2001
From: Tiffany Le-Nguyen
Date: Thu, 4 Nov 2021 12:12:20 -0400
Subject: [PATCH 05/13] chore: fix config file location
---
demo/netlify.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/demo/netlify.toml b/demo/netlify.toml
index 509fbf8412..40cbfe7866 100644
--- a/demo/netlify.toml
+++ b/demo/netlify.toml
@@ -21,4 +21,4 @@ package = "@netlify/plugin-local-install-core"
package = "netlify-plugin-cypress"
[context.deploy-preview.plugins.inputs]
-configFile = "cypress/config/all.json"
+configFile = "../cypress/config/ci.json"
From 0136ae84ccc9c8a2141d0500e3d3d21c1c912bf3 Mon Sep 17 00:00:00 2001
From: Tiffany Le-Nguyen
Date: Thu, 4 Nov 2021 12:22:22 -0400
Subject: [PATCH 06/13] chore: add data-testid for the lists
---
demo/pages/index.js | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/demo/pages/index.js b/demo/pages/index.js
index d1225acb43..b08bab7082 100644
--- a/demo/pages/index.js
+++ b/demo/pages/index.js
@@ -31,7 +31,7 @@ const Index = ({ shows }) => (
Refresh this page to see it change.
-
+
{shows.map(({ id, name }) => (
@@ -50,7 +50,7 @@ const Index = ({ shows }) => (
Click on a show to check out a server-side rendered page with dynamic routing (/shows/:id).
-
+
{shows.slice(0, 3).map(({ id, name }) => (
@@ -70,7 +70,7 @@ const Index = ({ shows }) => (
Here are three examples:
-
+
/shows/73/whatever/path/you/want
@@ -97,7 +97,7 @@ const Index = ({ shows }) => (
Static pages are pre-rendered and served directly by Netlify's CDN.
-
The single-camera series that mixes live-action and animation stars Jacob Bertrand as the title character. Kirby Buckets introduces viewers to the vivid imagination of charismatic 13-year-old Kirby Buckets, who dreams of becoming a famous animator like his idol, Mac MacCallister. With his two best friends, Fish and Eli, by his side, Kirby navigates his eccentric town of Forest Hills where the trio usually find themselves trying to get out of a predicament before Kirby's sister, Dawn, and her best friend, Belinda, catch them. Along the way, Kirby is joined by his animated characters, each with their own vibrant personality that only he and viewers can see.
The Downton Abbey estate stands a splendid example of confidence and mettle, its family enduring for generations and its staff a well-oiled machine of propriety. But change is afoot at Downton--change far surpassing the new electric lights and telephone. A crisis of inheritance threatens to displace the resident Crawley family, in spite of the best efforts of the noble and compassionate Earl, Robert Crawley; his American heiress wife, Cora his comically implacable, opinionated mother, Violet and his beautiful, eldest daughter, Mary, intent on charting her own course. Reluctantly, the family is forced to welcome its heir apparent, the self-made and proudly modern Matthew Crawley himself none too happy about the new arrangements. As Matthew's bristly relationship with Mary begins to crackle with electricity, hope for the future of Downton's dynasty takes shape. But when petty jealousies and ambitions grow among the family and the staff, scheming and secrets--both delicious and dangerous--threaten to derail the scramble to preserve Downton Abbey. Downton Abbey offers a spot-on portrait of a vanishing way of life.
Girl Meets World is based on ABC's hugely popular sitcom, Boy Meets World (1993). Set in New York City, the show tells the wonderfully funny heartfelt stories that Boy Meets World is renowned for - only this time from a tween girl's perspective - as the curious and bright 7th grader Riley Matthews and her quick-witted friend Maya Fox embark on an unforgettable middle school experience. But their plans for a carefree year will be adjusted slightly under the watchful eyes of Riley's parents - dad Cory, who's also a faculty member (and their new History teacher), and mom Topanga, who owns a trendy after school hangout that specializes in pudding.
In Hell's Kitchen, aspiring chefs are put through an intense culinary academy to prove they possess the right combination of ingredients to win a life-changing grand prize.
The World Series of Poker is where the world's best poker players battle for the title.
",
+ "updated": 1574298803,
+ "_links": {
+ "self": {
+ "href": "https://api.tvmaze.com/shows/254"
+ },
+ "previousepisode": {
+ "href": "https://api.tvmaze.com/episodes/1684225"
+ }
+ }
+}]
From 275be492dc676d77f2b8816dfce128e2f2a903f6 Mon Sep 17 00:00:00 2001
From: Tiffany Le-Nguyen
Date: Tue, 9 Nov 2021 00:55:21 -0500
Subject: [PATCH 08/13] chore: add e2e tests
---
.eslintrc.js | 13 +-
cypress/integration/custom-errors.spec.ts | 14 +
cypress/integration/default.spec.ts | 1 -
cypress/integration/dynamic-routes.spec.ts | 1 -
cypress/integration/headers.spec.ts | 9 +
cypress/integration/i18n.spec.ts | 16 ++
cypress/integration/images.spec.ts | 20 ++
cypress/integration/preview.spec.ts | 21 ++
.../integration/rewrites-redirects.spec.ts | 21 ++
cypress/integration/trailing-slash.spec.ts | 11 +
demo/next.config.js | 28 +-
demo/pages/index.js | 244 ++++++++++--------
package.json | 1 +
13 files changed, 285 insertions(+), 115 deletions(-)
create mode 100644 cypress/integration/custom-errors.spec.ts
create mode 100644 cypress/integration/headers.spec.ts
create mode 100644 cypress/integration/i18n.spec.ts
create mode 100644 cypress/integration/images.spec.ts
create mode 100644 cypress/integration/preview.spec.ts
create mode 100644 cypress/integration/rewrites-redirects.spec.ts
create mode 100644 cypress/integration/trailing-slash.spec.ts
diff --git a/.eslintrc.js b/.eslintrc.js
index 9f4e641bee..416c44a27f 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -34,5 +34,16 @@ module.exports = {
env: {
jest: true,
},
- overrides: [...overrides],
+ overrides: [
+ ...overrides,
+ {
+ files: ['cypress/**/*.spec.ts'],
+ rules: {
+ 'max-nested-callbacks': 0,
+ 'promise/prefer-await-to-then': 0,
+ 'promise/always-return': 0,
+ 'promise/catch-or-return': 0
+ },
+ }
+ ],
}
diff --git a/cypress/integration/custom-errors.spec.ts b/cypress/integration/custom-errors.spec.ts
new file mode 100644
index 0000000000..03478774de
--- /dev/null
+++ b/cypress/integration/custom-errors.spec.ts
@@ -0,0 +1,14 @@
+/**
+ * @see {@link https://nextjs.org/docs/advanced-features/custom-error-page}
+ */
+ describe('Custom error pages', () => {
+ it('should show custom 404 page on /404', () => {
+ cy.visit('/404', {failOnStatusCode: false})
+ cy.findByText('Custom 404 - Page Not Found')
+ })
+
+ it('should show custom 500 page on /500', () => {
+ cy.visit('/500', {failOnStatusCode: false})
+ cy.findByText('Custom 500 - Server-side error occurred')
+ })
+})
\ No newline at end of file
diff --git a/cypress/integration/default.spec.ts b/cypress/integration/default.spec.ts
index 054e33a7d1..28c1c1cdd0 100644
--- a/cypress/integration/default.spec.ts
+++ b/cypress/integration/default.spec.ts
@@ -1,4 +1,3 @@
-/* eslint-disable eslint-comments/disable-enable-pair, max-nested-callbacks */
describe('Default site', () => {
beforeEach(() => {
cy.visit('/')
diff --git a/cypress/integration/dynamic-routes.spec.ts b/cypress/integration/dynamic-routes.spec.ts
index 5153fe54c8..a0cf095942 100644
--- a/cypress/integration/dynamic-routes.spec.ts
+++ b/cypress/integration/dynamic-routes.spec.ts
@@ -1,4 +1,3 @@
-/* eslint-disable eslint-comments/disable-enable-pair, max-nested-callbacks */
describe('Dynamic Routing', () => {
it('loads page', () => {
cy.visit('/shows/250')
diff --git a/cypress/integration/headers.spec.ts b/cypress/integration/headers.spec.ts
new file mode 100644
index 0000000000..2b5551ecae
--- /dev/null
+++ b/cypress/integration/headers.spec.ts
@@ -0,0 +1,9 @@
+describe('Headers', () => {
+ it('should set headers from the next.config.js', () => {
+ cy.request({
+ url: '/',
+ }).then((response) => {
+ expect(response.headers).to.have.property('x-custom-header', 'my custom header value')
+ })
+ })
+})
\ No newline at end of file
diff --git a/cypress/integration/i18n.spec.ts b/cypress/integration/i18n.spec.ts
new file mode 100644
index 0000000000..7dd8d2bd32
--- /dev/null
+++ b/cypress/integration/i18n.spec.ts
@@ -0,0 +1,16 @@
+describe('Localization', () => {
+ it('should use sub routing to determine current locale', () => {
+ cy.visit('/');
+
+ cy.findByText('The current locale is en')
+
+ cy.visit('/fr')
+ cy.findByText('The current locale is fr')
+ })
+
+ it('should use the NEXT_LOCALE cookie to determine the default locale', () => {
+ cy.setCookie('NEXT_LOCALE', 'fr')
+ cy.visit('/');
+ cy.findByText('The current locale is fr')
+ })
+})
\ No newline at end of file
diff --git a/cypress/integration/images.spec.ts b/cypress/integration/images.spec.ts
new file mode 100644
index 0000000000..0d912d33a8
--- /dev/null
+++ b/cypress/integration/images.spec.ts
@@ -0,0 +1,20 @@
+/**
+ * @see {@link https://nextjs.org/docs/api-reference/next/image#required-props}
+ */
+describe('next/images', () => {
+ it('should show static image from /public', () => {
+ cy.visit('/')
+ cy.findByRole('img').should('be.visible').and(($img) => {
+ // "naturalWidth" and "naturalHeight" are set when the image loads
+ expect(
+ $img[0].naturalWidth,
+ 'image has natural width'
+ ).to.be.greaterThan(0)
+ })
+ })
+
+ it('should show image using next/image', () => {
+ cy.visit('/image')
+ cy.findByRole('img', { name: /shiba inu dog looks through a window/i })
+ })
+})
\ No newline at end of file
diff --git a/cypress/integration/preview.spec.ts b/cypress/integration/preview.spec.ts
new file mode 100644
index 0000000000..39d1b66574
--- /dev/null
+++ b/cypress/integration/preview.spec.ts
@@ -0,0 +1,21 @@
+describe('Preview Mode', () => {
+ it('enters and exits preview mode', () => {
+ // preview mode is off by default
+ cy.visit('/previewTest')
+ cy.findByText('Number: 4')
+
+ // enter preview mode
+ cy.request('/api/enterPreview').then(
+ (response) => {
+ expect(response.body).to.have.property('name', 'preview mode')
+ }
+)
+ cy.visit('/previewTest')
+ cy.findByText('Number: 3')
+
+ // exit preview mode
+ cy.request('/api/exitPreview')
+ cy.visit('/previewTest')
+ cy.findByText('Number: 4')
+ })
+})
\ No newline at end of file
diff --git a/cypress/integration/rewrites-redirects.spec.ts b/cypress/integration/rewrites-redirects.spec.ts
new file mode 100644
index 0000000000..43d94d3ab6
--- /dev/null
+++ b/cypress/integration/rewrites-redirects.spec.ts
@@ -0,0 +1,21 @@
+describe('Rewrites and Redirects', () => {
+ it('rewrites: points /old to /', () => {
+ // preview mode is off by default
+ cy.visit('/old')
+ cy.findByText('Next Demo!')
+ cy.url().should('eq', `${Cypress.config().baseUrl}/old`)
+
+ // ensure headers are still set
+ cy.request('/api/enterPreview').then(
+ (response) => {
+ expect(response.body).to.have.property('name', 'preview mode')
+ }
+)
+ })
+
+ it('redirects: redirects /redirectme to /', () => {
+ cy.visit('/redirectme')
+ cy.url().should('eq', `${Cypress.config().baseUrl}/`)
+ }
+ )
+})
\ No newline at end of file
diff --git a/cypress/integration/trailing-slash.spec.ts b/cypress/integration/trailing-slash.spec.ts
new file mode 100644
index 0000000000..e06e7dd982
--- /dev/null
+++ b/cypress/integration/trailing-slash.spec.ts
@@ -0,0 +1,11 @@
+describe('Trailing slash enabled', () => {
+ it('should keep the trailing slash, i.e. points /old/ to /old/', () => {
+ cy.visit('/old/')
+ cy.url().should('eq', `${Cypress.config().baseUrl}/old/`)
+ })
+
+ it('should put a trailing slash when there is none, i.e. points /old to /old/', () => {
+ cy.visit('/old')
+ cy.url().should('eq', `${Cypress.config().baseUrl}/old/`)
+ })
+})
\ No newline at end of file
diff --git a/demo/next.config.js b/demo/next.config.js
index 3b53233b90..93c2cbab23 100644
--- a/demo/next.config.js
+++ b/demo/next.config.js
@@ -6,9 +6,23 @@ module.exports = {
defaultLocale: 'en',
locales: ['en', 'es', 'fr']
},
- // trailingSlash: true,
+ async headers() {
+ return [
+ {
+ source: '/',
+ headers: [
+ {
+ key: 'x-custom-header',
+ value: 'my custom header value',
+ }
+ ],
+ },
+ ]
+ },
+ trailingSlash: true,
// Configurable site features _to_ support:
// basePath: '/docs',
+ // Rewrites allow you to map an incoming request path to a different destination path.
async rewrites() {
return {
beforeFiles: [
@@ -18,5 +32,15 @@ module.exports = {
}
]
}
- }
+ },
+ // Redirects allow you to redirect an incoming request path to a different destination path.
+ async redirects() {
+ return [
+ {
+ source: '/redirectme',
+ destination: '/',
+ permanent: true,
+ },
+ ]
+ },
}
diff --git a/demo/pages/index.js b/demo/pages/index.js
index a7b5a98831..158358e19b 100644
--- a/demo/pages/index.js
+++ b/demo/pages/index.js
@@ -1,121 +1,145 @@
import Link from 'next/link'
import dynamic from 'next/dynamic'
const Header = dynamic(() => import(/* webpackChunkName: 'header' */ '../components/Header'), { ssr: true })
+import { useRouter } from 'next/router'
-const Index = ({ shows }) => (
-
-
-
-
-
-
NextJS on Netlify
-
- This is a demo of a NextJS application with Server-Side Rendering (SSR).
-
- It is hosted on Netlify.
-
- Server-side rendering is handled by Netlify Functions.
-
- Minimal configuration is required.
-
- Everything is handled by the next-on-netlify npm
- package.
-
-
-
1. Server-Side Rendering Made Easy
-
- This page is server-side rendered.
-
- It fetches a random list of five TV shows from the TVmaze REST API.
-
- Refresh this page to see it change.
-
+ This is a demo of a NextJS application with Server-Side Rendering (SSR).
+
+ It is hosted on Netlify.
+
+ Server-side rendering is handled by Netlify Functions.
+
+ Minimal configuration is required.
+
+ Everything is handled by the next-on-netlify npm
+ package.
+
+
+
1. Server-Side Rendering Made Easy
+
+ This page is server-side rendered.
+
+ It fetches a random list of five TV shows from the TVmaze REST API.
+
+ Refresh this page to see it change.
+
+ Dynamic pages, introduced in NextJS 9.2, are fully supported.
+
+ Click on a show to check out a server-side rendered page with dynamic routing (/shows/:id).
+
- Dynamic pages, introduced in NextJS 9.2, are fully supported.
-
- Click on a show to check out a server-side rendered page with dynamic routing (/shows/:id).
-
- next-on-netlify automatically determines which pages are dynamic and which ones are static.
-
- Only dynamic pages are server-side rendered.
-
- Static pages are pre-rendered and served directly by Netlify's CDN.
-
+ next-on-netlify automatically determines which pages are dynamic and which ones are static.
+
+ Only dynamic pages are server-side rendered.
+
+ Static pages are pre-rendered and served directly by Netlify's CDN.
+