From d76aaf687063d9c737762f2170c2d68bfaa78a24 Mon Sep 17 00:00:00 2001 From: nitishdhar Date: Tue, 5 Sep 2023 20:04:03 -0700 Subject: [PATCH 1/5] Setup root error boundary to gracefully handle all unhandled client & server side errors --- README.md | 14 +++++++- app/error.tsx | 34 +++++++++++++++++++ app/not-found.tsx | 7 ++-- app/playground/client-error/page.client.tsx | 7 ++++ app/playground/client-error/page.tsx | 8 +++++ app/playground/page.tsx | 5 ++- app/playground/server-error/page.tsx | 6 ++++ package.json | 2 +- .../playground/error-boundary.test.tsx | 22 ++++++++++++ 9 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 app/error.tsx create mode 100644 app/playground/client-error/page.client.tsx create mode 100644 app/playground/client-error/page.tsx create mode 100644 app/playground/server-error/page.tsx create mode 100644 tests/playwright/playground/error-boundary.test.tsx diff --git a/README.md b/README.md index fbc4fe9..2c9aabd 100644 --- a/README.md +++ b/README.md @@ -62,15 +62,26 @@ yarn lint yarn test ``` +### Clean + +Removes the `.next` and `node_modules` folders and runs `yarn install` to clean up the local workspace + +```bash +yarn clean +``` + ### Take Screenshots + If you are making changes that impact the UI, ensure to test the change on different view ports. Run the following to auto capture screenshots when tests run. Note that this only runs when you run this command locally. #### Take screenshots of home page + ```bash yarn capture-screenshots ``` #### Take screenshots of a specific page + ```bash PAGE_PATH=/commodities yarn capture-screenshots ``` @@ -116,4 +127,5 @@ yarn dev - Docker ### 🌐 Useful Tools -- [ColorKit](https://colorkit.co/color-palette-generator/272f33-4e5d66-9BB9CB-cddce5-e6eef2/) - Color Palette Generator \ No newline at end of file + +- [ColorKit](https://colorkit.co/color-palette-generator/272f33-4e5d66-9BB9CB-cddce5-e6eef2/) - Color Palette Generator diff --git a/app/error.tsx b/app/error.tsx new file mode 100644 index 0000000..0fc9d3b --- /dev/null +++ b/app/error.tsx @@ -0,0 +1,34 @@ +'use client'; + +import { Button, Center, Flex, Heading, Text } from '@chakra-ui/react'; + +export default function Error({ + error, + reset, +}: { + error: Error; + reset: () => void; +}) { + return ( +
+ + + Something went wrong! + + {error.message} + + +
+ ); +} diff --git a/app/not-found.tsx b/app/not-found.tsx index 9d680a2..074cc9e 100644 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -1,8 +1,7 @@ 'use client'; import NextLink from 'next/link'; -import { Link, Center, Flex, Heading } from '@chakra-ui/react'; -import GetColor from '@/app/_hooks/colorSelector'; +import { Button, Center, Flex, Heading } from '@chakra-ui/react'; export default function NotFound() { return ( @@ -21,9 +20,9 @@ export default function NotFound() { 404 - Page Not Found - + diff --git a/app/playground/client-error/page.client.tsx b/app/playground/client-error/page.client.tsx new file mode 100644 index 0000000..3f669a8 --- /dev/null +++ b/app/playground/client-error/page.client.tsx @@ -0,0 +1,7 @@ +'use client'; + +const PageClient = () => { + throw new Error('This is a simulated client rendering error.'); +}; + +export default PageClient; diff --git a/app/playground/client-error/page.tsx b/app/playground/client-error/page.tsx new file mode 100644 index 0000000..d24bd61 --- /dev/null +++ b/app/playground/client-error/page.tsx @@ -0,0 +1,8 @@ +import PageClient from './page.client'; + +// Avoid statically building this page during build. +export const dynamic = 'force-dynamic'; + +export default async function Page() { + return ; +} diff --git a/app/playground/page.tsx b/app/playground/page.tsx index 858bbdd..b37b1ca 100644 --- a/app/playground/page.tsx +++ b/app/playground/page.tsx @@ -6,9 +6,6 @@ import { } from './_api'; import { ICommodity, IPost } from '../_types'; -// Force dynamic fetching during runtime for playground -export const dynamic = 'force-dynamic'; - export const metadata: Metadata = { title: 'EDPN Playground', description: 'Elite Dangerous Pilots Network', @@ -23,12 +20,14 @@ export default async function Page() { posts = await getPostsForCategoryFromMockApi(); } catch (error) { console.error('Failed to fetch posts data:', error); + throw error; } try { commodity = await getCommodityByNameFromStagingApi('Wine'); } catch (error) { console.error('Failed to fetch commodity data:', error); + throw error; } return ; diff --git a/app/playground/server-error/page.tsx b/app/playground/server-error/page.tsx new file mode 100644 index 0000000..de49ad7 --- /dev/null +++ b/app/playground/server-error/page.tsx @@ -0,0 +1,6 @@ +// Avoid statically building this page during build. +export const dynamic = 'force-dynamic'; + +export default async function Page() { + throw new Error('This is a simulated server rendering error.'); +} diff --git a/package.json b/package.json index 4ae0e9d..1046085 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "analyze:server": "BUNDLE_ANALYZE=server yarn build", "analyze:browser": "BUNDLE_ANALYZE=browser yarn build", "test": "yarn test:e2e && yarn test:unit", - "test:e2e": "playwright test", + "test:e2e": "playwright test --quiet", "test:unit": "jest", "clean": "rm -rf .next && rm -rf node_modules && yarn install", "capture-screenshots": "RUN_SCREENSHOT_TEST=true yarn test" diff --git a/tests/playwright/playground/error-boundary.test.tsx b/tests/playwright/playground/error-boundary.test.tsx new file mode 100644 index 0000000..cd276e3 --- /dev/null +++ b/tests/playwright/playground/error-boundary.test.tsx @@ -0,0 +1,22 @@ +import { test, expect } from '@playwright/test'; + +test('server rendering errors should get caught and gracefully render error', async ({ + page, +}) => { + await page.goto('/playground/server-error'); + await expect(page.locator('h1')).toContainText('Something went wrong!'); + // Error messages for server rendering get replaced for security during builds + await expect(page.locator('samp')).toContainText( + 'An error occurred in the Server Components render.', + ); +}); + +test('client rendering errors should get caught and gracefully render error', async ({ + page, +}) => { + await page.goto('/playground/client-error'); + await expect(page.locator('h1')).toContainText('Something went wrong!'); + await expect(page.locator('samp')).toContainText( + 'This is a simulated client rendering error', + ); +}); From 433ef3daafa1e2c260fabaa5ff575eec54f80b3b Mon Sep 17 00:00:00 2001 From: nitishdhar Date: Tue, 5 Sep 2023 20:27:29 -0700 Subject: [PATCH 2/5] Reverts --quiet flag addition to playqwright --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1046085..4ae0e9d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "analyze:server": "BUNDLE_ANALYZE=server yarn build", "analyze:browser": "BUNDLE_ANALYZE=browser yarn build", "test": "yarn test:e2e && yarn test:unit", - "test:e2e": "playwright test --quiet", + "test:e2e": "playwright test", "test:unit": "jest", "clean": "rm -rf .next && rm -rf node_modules && yarn install", "capture-screenshots": "RUN_SCREENSHOT_TEST=true yarn test" From 4243abbae9c72141e3856c7c7d6914d060b0e599 Mon Sep 17 00:00:00 2001 From: nitishdhar Date: Tue, 5 Sep 2023 20:38:51 -0700 Subject: [PATCH 3/5] Force dynamic rendering for playground --- app/playground/page.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/playground/page.tsx b/app/playground/page.tsx index b37b1ca..a5d7e66 100644 --- a/app/playground/page.tsx +++ b/app/playground/page.tsx @@ -6,6 +6,9 @@ import { } from './_api'; import { ICommodity, IPost } from '../_types'; +// Avoid statically building this page during build. +export const dynamic = 'force-dynamic'; + export const metadata: Metadata = { title: 'EDPN Playground', description: 'Elite Dangerous Pilots Network', From 3b87d2f6b732de61a5080d456d5451457541fc08 Mon Sep 17 00:00:00 2001 From: nitishdhar Date: Thu, 14 Sep 2023 16:38:51 -0700 Subject: [PATCH 4/5] Update fetch cache in playground and use layoutConfig in error & not-found --- app/error.tsx | 3 ++- app/not-found.tsx | 3 ++- app/playground/_api/index.tsx | 4 ++-- app/playground/page.tsx | 3 --- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/error.tsx b/app/error.tsx index 0fc9d3b..4461bdb 100644 --- a/app/error.tsx +++ b/app/error.tsx @@ -1,6 +1,7 @@ 'use client'; import { Button, Center, Flex, Heading, Text } from '@chakra-ui/react'; +import layoutConfig from './_config/layout'; export default function Error({ error, @@ -15,7 +16,7 @@ export default function Error({ flexDirection="column" alignItems="center" gap="24px" - maxWidth="1500px" + maxWidth={layoutConfig.maxWidth} > => { 'Content-Type': 'application/json', }, body: JSON.stringify({ category: 'json' }), - cache: 'no-store', + next: { revalidate: 86400 }, }); if (!res.ok) { @@ -29,7 +29,7 @@ export const getCommodityByNameFromStagingApi = async ( try { const res = await fetch( `${process.env.NEXT_PUBLIC_STAGING_API_URL}/api/v1/trade/commodity/${name}`, - { cache: 'no-store' }, + { next: { revalidate: 86400 } }, ); if (!res.ok) { diff --git a/app/playground/page.tsx b/app/playground/page.tsx index a5d7e66..b37b1ca 100644 --- a/app/playground/page.tsx +++ b/app/playground/page.tsx @@ -6,9 +6,6 @@ import { } from './_api'; import { ICommodity, IPost } from '../_types'; -// Avoid statically building this page during build. -export const dynamic = 'force-dynamic'; - export const metadata: Metadata = { title: 'EDPN Playground', description: 'Elite Dangerous Pilots Network', From 49900c8990ad53a9193d300b1dcf30da25d662f9 Mon Sep 17 00:00:00 2001 From: nitishdhar Date: Thu, 14 Sep 2023 16:48:06 -0700 Subject: [PATCH 5/5] Reverts no-cache for playground --- app/playground/_api/index.tsx | 4 ++-- app/playground/page.tsx | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/playground/_api/index.tsx b/app/playground/_api/index.tsx index cf2df6a..3a39023 100644 --- a/app/playground/_api/index.tsx +++ b/app/playground/_api/index.tsx @@ -8,7 +8,7 @@ export const getPostsForCategoryFromMockApi = async (): Promise => { 'Content-Type': 'application/json', }, body: JSON.stringify({ category: 'json' }), - next: { revalidate: 86400 }, + cache: 'no-store', }); if (!res.ok) { @@ -29,7 +29,7 @@ export const getCommodityByNameFromStagingApi = async ( try { const res = await fetch( `${process.env.NEXT_PUBLIC_STAGING_API_URL}/api/v1/trade/commodity/${name}`, - { next: { revalidate: 86400 } }, + { cache: 'no-store' }, ); if (!res.ok) { diff --git a/app/playground/page.tsx b/app/playground/page.tsx index b37b1ca..a5d7e66 100644 --- a/app/playground/page.tsx +++ b/app/playground/page.tsx @@ -6,6 +6,9 @@ import { } from './_api'; import { ICommodity, IPost } from '../_types'; +// Avoid statically building this page during build. +export const dynamic = 'force-dynamic'; + export const metadata: Metadata = { title: 'EDPN Playground', description: 'Elite Dangerous Pilots Network',