diff --git a/tests/server-contexts/.gitignore b/tests/server-contexts/.gitignore new file mode 100644 index 0000000..fd3dbb5 --- /dev/null +++ b/tests/server-contexts/.gitignore @@ -0,0 +1,36 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/tests/server-contexts/app/after/FirstComponent.tsx b/tests/server-contexts/app/after/FirstComponent.tsx new file mode 100644 index 0000000..e6cd99b --- /dev/null +++ b/tests/server-contexts/app/after/FirstComponent.tsx @@ -0,0 +1,16 @@ +import getServerContext from "next-impl-getters/get-server-context"; +import { sleep } from "../../tools/sleep"; +import { AfterContext } from "../../components/AfterContext"; + +const FirstComponent = async () => { + await sleep(1000); + const context = getServerContext(AfterContext); + + return ( +

+ {context?.after} +

+ ) +} + +export default FirstComponent; diff --git a/tests/server-contexts/app/after/page.tsx b/tests/server-contexts/app/after/page.tsx new file mode 100644 index 0000000..f774e96 --- /dev/null +++ b/tests/server-contexts/app/after/page.tsx @@ -0,0 +1,23 @@ +import Nav from "../../components/Nav"; +import { AfterContext } from "../../components/AfterContext"; +import FirstComponent from "./FirstComponent"; + +export default function Page() { + return ( +
+
+ ); +} diff --git a/tests/server-contexts/app/client-router.spec.ts b/tests/server-contexts/app/client-router.spec.ts new file mode 100644 index 0000000..78eb1b6 --- /dev/null +++ b/tests/server-contexts/app/client-router.spec.ts @@ -0,0 +1,46 @@ +import { test, expect } from "@playwright/test"; + +// to-after +// to-inside +// to-uninitialized + +test.describe('Should work correct in client router', async () => { + test(`should return correct values on "after" page by default`, async ({ page }) => { + await page.goto('/'); + await page.click('#to-after'); + await page.waitForSelector('#after-page'); + await page.waitForSelector('#context-first-value'); + + const firstValue = await page.$('#context-first-value'); + expect(await firstValue?.textContent()).toBe('first value'); + + const secondValue = await page.$('#context-second-value'); + expect(await secondValue?.textContent()).toBe('second value'); + }); + + test(`should return correct values on "inside" page by default`, async ({ page }) => { + await page.goto('/'); + await page.click('#to-inside'); + await page.waitForSelector('#inside-page'); + await page.waitForSelector('#context-first-value'); + + const firstValue = await page.$('#context-first-value'); + expect(await firstValue?.textContent()).toBe('first value'); + + const secondValue = await page.$('#context-second-value'); + expect(await secondValue?.textContent()).toBe('second value'); + }); + + test(`should return correct values on "uninitialized" page by default`, async ({ page }) => { + await page.goto('/'); + await page.click('#to-uninitialized'); + await page.waitForSelector('#uninitialized-page'); + await page.waitForSelector('#context-first-value'); + + const firstValue = await page.$('#context-first-value'); + expect(await firstValue?.textContent()).toBe('first value'); + + const secondValue = await page.$('#context-second-value'); + expect(await secondValue?.textContent()).toBe('default value'); + }); +}) diff --git a/tests/server-contexts/app/inside/SecondComponent.tsx b/tests/server-contexts/app/inside/SecondComponent.tsx new file mode 100644 index 0000000..2b88096 --- /dev/null +++ b/tests/server-contexts/app/inside/SecondComponent.tsx @@ -0,0 +1,14 @@ +import getServerContext from "next-impl-getters/get-server-context"; +import { InsideContext } from "../../components/InsideContext"; + +const SecondComponent = async () => { + const context = getServerContext(InsideContext); + + return ( +

+ {context?.inside} +

+ ) +} + +export default SecondComponent; diff --git a/tests/server-contexts/app/inside/page.tsx b/tests/server-contexts/app/inside/page.tsx new file mode 100644 index 0000000..45bff38 --- /dev/null +++ b/tests/server-contexts/app/inside/page.tsx @@ -0,0 +1,23 @@ +import Nav from "../../components/Nav"; +import { InsideContext } from "../../components/InsideContext"; +import SecondComponent from "./SecondComponent"; + +export default function Page() { + return ( +
+
+ ); +} diff --git a/tests/server-contexts/app/layout.tsx b/tests/server-contexts/app/layout.tsx new file mode 100644 index 0000000..225b603 --- /dev/null +++ b/tests/server-contexts/app/layout.tsx @@ -0,0 +1,11 @@ +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/tests/server-contexts/app/page.tsx b/tests/server-contexts/app/page.tsx new file mode 100644 index 0000000..c5c37bf --- /dev/null +++ b/tests/server-contexts/app/page.tsx @@ -0,0 +1,9 @@ +import Nav from "../components/Nav"; + +export default function Page() { + return ( +
+
+ ); +} diff --git a/tests/server-contexts/app/uninitialized/SecondComponent.tsx b/tests/server-contexts/app/uninitialized/SecondComponent.tsx new file mode 100644 index 0000000..863a9ac --- /dev/null +++ b/tests/server-contexts/app/uninitialized/SecondComponent.tsx @@ -0,0 +1,14 @@ +import getServerContext from "next-impl-getters/get-server-context"; +import { UninitializedContext } from "../../components/UninitializedContext"; + +const SecondComponent = async () => { + const context = getServerContext(UninitializedContext); + + return ( +

+ {context?.uninitialized} +

+ ) +} + +export default SecondComponent; diff --git a/tests/server-contexts/app/uninitialized/page.tsx b/tests/server-contexts/app/uninitialized/page.tsx new file mode 100644 index 0000000..fc5b212 --- /dev/null +++ b/tests/server-contexts/app/uninitialized/page.tsx @@ -0,0 +1,22 @@ +import getServerContext from "next-impl-getters/get-server-context"; +import Nav from "../../components/Nav"; +import { UninitializedContext } from "../../components/UninitializedContext"; +import SecondComponent from "./SecondComponent"; + +export default function Page() { + return ( +
+
+ ); +} diff --git a/tests/server-contexts/components/AfterContext/index.tsx b/tests/server-contexts/components/AfterContext/index.tsx new file mode 100644 index 0000000..67acfd0 --- /dev/null +++ b/tests/server-contexts/components/AfterContext/index.tsx @@ -0,0 +1,3 @@ +import createServerContext from 'next-impl-getters/create-server-context'; + +export const AfterContext = createServerContext({ after: 'default value' }); diff --git a/tests/server-contexts/components/InsideContext/index.tsx b/tests/server-contexts/components/InsideContext/index.tsx new file mode 100644 index 0000000..8bd52ea --- /dev/null +++ b/tests/server-contexts/components/InsideContext/index.tsx @@ -0,0 +1,3 @@ +import createServerContext from 'next-impl-getters/create-server-context'; + +export const InsideContext = createServerContext({ inside: 'default value' }); diff --git a/tests/server-contexts/components/Nav/index.tsx b/tests/server-contexts/components/Nav/index.tsx new file mode 100644 index 0000000..3e67555 --- /dev/null +++ b/tests/server-contexts/components/Nav/index.tsx @@ -0,0 +1,11 @@ +import Link from "next/link"; + +export default function Nav() { + return ( + + ); +} diff --git a/tests/server-contexts/components/UninitializedContext/index.tsx b/tests/server-contexts/components/UninitializedContext/index.tsx new file mode 100644 index 0000000..c710926 --- /dev/null +++ b/tests/server-contexts/components/UninitializedContext/index.tsx @@ -0,0 +1,3 @@ +import createServerContext from 'next-impl-getters/create-server-context'; + +export const UninitializedContext = createServerContext({ uninitialized: 'default value' }); diff --git a/tests/server-contexts/package.json b/tests/server-contexts/package.json new file mode 100644 index 0000000..c34af61 --- /dev/null +++ b/tests/server-contexts/package.json @@ -0,0 +1,21 @@ +{ + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "test": "playwright test" + }, + "dependencies": { + "next": "latest", + "next-impl-getters": "link:../../package", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@playwright/test": "^1.40.1", + "@types/node": "20.10.4", + "@types/react": "18.2.45", + "typescript": "5.3.3" + } +} diff --git a/tests/server-contexts/playwright.config.ts b/tests/server-contexts/playwright.config.ts new file mode 100644 index 0000000..40af9c7 --- /dev/null +++ b/tests/server-contexts/playwright.config.ts @@ -0,0 +1,53 @@ +import { defineConfig, devices } from "@playwright/test"; +import path from "path"; + +// Use process.env.PORT by default and fallback to port 3000 +const PORT = process.env.PORT || 3000; + +// Set webServer.url and use.baseURL with the location of the WebServer respecting the correct set port +const baseURL = `http://localhost:${PORT}`; + +// Reference: https://playwright.dev/docs/test-configuration +export default defineConfig({ + // Timeout per test + timeout: 30 * 1000, + // Test directory + testDir: path.join(__dirname, "app"), + // If a test fails, retry it additional 2 times + retries: 2, + // Artifacts folder where screenshots, videos, and traces are stored. + outputDir: "test-results/", + + // Run your local dev server before starting the tests: + // https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests + webServer: { + command: "npm run start", + url: baseURL, + timeout: 120 * 1000, + reuseExistingServer: !process.env.CI, + }, + + use: { + // Use baseURL so to make navigations relative. + // More information: https://playwright.dev/docs/api/class-testoptions#test-options-base-url + baseURL, + + // Retry a test if its failing with enabled tracing. This allows you to analyze the DOM, console logs, network traffic etc. + // More information: https://playwright.dev/docs/trace-viewer + trace: "retry-with-trace", + + // All available context options: https://playwright.dev/docs/api/class-browser#browser-new-context + // contextOptions: { + // ignoreHTTPSErrors: true, + // }, + }, + + projects: [ + { + name: "Desktop Chrome", + use: { + ...devices["Desktop Chrome"], + }, + }, + ], +}); diff --git a/tests/server-contexts/public/favicon.ico b/tests/server-contexts/public/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/tests/server-contexts/public/favicon.ico differ diff --git a/tests/server-contexts/public/vercel.svg b/tests/server-contexts/public/vercel.svg new file mode 100644 index 0000000..fbf0e25 --- /dev/null +++ b/tests/server-contexts/public/vercel.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/tests/server-contexts/tools/sleep.ts b/tests/server-contexts/tools/sleep.ts new file mode 100644 index 0000000..8e68d3f --- /dev/null +++ b/tests/server-contexts/tools/sleep.ts @@ -0,0 +1,5 @@ +export const sleep = (time: number) => { + return new Promise(resolve => { + setTimeout(resolve, time) + }) +} diff --git a/tests/server-contexts/tsconfig.json b/tests/server-contexts/tsconfig.json new file mode 100644 index 0000000..ac7f7f4 --- /dev/null +++ b/tests/server-contexts/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "plugins": [ + { + "name": "next" + } + ], + "strictNullChecks": true + }, + "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +}