diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 487f826e..1569f78b 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -5,6 +5,9 @@ on: branches: [main] pull_request: +env: + NEXT_PRIVATE_DEBUG_CACHE: 1 + jobs: test: timeout-minutes: 30 @@ -37,3 +40,10 @@ jobs: name: playwright-report path: ./**/playwright-report retention-days: 1 + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: ./**/e2e/app-router/.open-next + retention-days: 1 diff --git a/examples/common/config-e2e.ts b/examples/common/config-e2e.ts index 5347c5e6..044e27b8 100644 --- a/examples/common/config-e2e.ts +++ b/examples/common/config-e2e.ts @@ -7,6 +7,7 @@ declare const process: typeof nodeProcess; export function configurePlaywright( app: AppName, { + testDir = "./", // Do we run on CI? isCI = false, // Do we run on workers (`wrangler dev`) or on Node (`next dev`) @@ -56,7 +57,7 @@ export function configurePlaywright( * See https://playwright.dev/docs/test-configuration. */ return defineConfig({ - testDir: "./", + testDir, /* ignore runtime specific tests */ testIgnore: isWorker ? "*next.spec.ts" : "*cloudflare.spec.ts", /* Run tests in files in parallel */ @@ -85,6 +86,7 @@ export function configurePlaywright( url: baseURL, reuseExistingServer: !isCI, timeout, + stdout: "pipe", }, }); } diff --git a/examples/e2e/app-router/e2e/only/ssr.test.ts b/examples/e2e/app-router/e2e/only/ssr.test.ts new file mode 100644 index 00000000..ec6f0bfe --- /dev/null +++ b/examples/e2e/app-router/e2e/only/ssr.test.ts @@ -0,0 +1,38 @@ +// NOTE: loading.tsx is currently broken on open - next +// This works locally but not on deployed apps + +import { expect, test } from "@playwright/test"; + +// NOTE: We don't await page load b/c we want to see the Loading page +// loading.tsx doesn't currently work: https://github.com/opennextjs/opennextjs-cloudflare/issues/313 +test.skip("Server Side Render and loading.tsx", async ({ page }) => { + test.setTimeout(600000); + await page.goto("/"); + await page.getByRole("link", { name: "SSR" }).click(); + await page.waitForURL("/ssr"); + + let loading: any; + let lastTime = ""; + + for (let i = 0; i < 5; i++) { + void page.reload(); + + loading = page.getByText("Loading..."); + await expect(loading).toBeVisible(); + const el = page.getByText("Time:"); + await expect(el).toBeVisible(); + const time = await el.textContent(); + expect(time).not.toEqual(lastTime); + lastTime = time!; + await page.waitForTimeout(1000); + } +}); + +test("Fetch cache properly cached", async ({ page }) => { + await page.goto("/ssr"); + const originalDate = await page.getByText("Cached fetch:").textContent(); + await page.waitForTimeout(2000); + await page.reload(); + const newDate = await page.getByText("Cached fetch:").textContent(); + expect(originalDate).toEqual(newDate); +}); diff --git a/examples/e2e/app-router/e2e/playwright.config.ts b/examples/e2e/app-router/e2e/playwright.config.ts index c3e8b9ff..1fa3981a 100644 --- a/examples/e2e/app-router/e2e/playwright.config.ts +++ b/examples/e2e/app-router/e2e/playwright.config.ts @@ -1,3 +1,7 @@ import { configurePlaywright } from "../../../common/config-e2e"; -export default configurePlaywright("app-router", { isCI: !!process.env.CI, multipleBrowsers: false }); +export default configurePlaywright("app-router", { + testDir: "./only", + isCI: !!process.env.CI, + multipleBrowsers: false, +}); diff --git a/examples/e2e/app-router/e2e/ssr.test.ts b/examples/e2e/app-router/e2e/ssr.test.ts index c2156714..ec6f0bfe 100644 --- a/examples/e2e/app-router/e2e/ssr.test.ts +++ b/examples/e2e/app-router/e2e/ssr.test.ts @@ -28,7 +28,7 @@ test.skip("Server Side Render and loading.tsx", async ({ page }) => { } }); -test.skip("Fetch cache properly cached", async ({ page }) => { +test("Fetch cache properly cached", async ({ page }) => { await page.goto("/ssr"); const originalDate = await page.getByText("Cached fetch:").textContent(); await page.waitForTimeout(2000); diff --git a/package.json b/package.json index 02be93cd..dd546284 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,6 @@ }, "scripts": { "fix": "pnpm prettier:fix && pnpm lint:fix", - "build": "pnpm --filter cloudflare build", - "test": "pnpm -r test", - "e2e": "pnpm build && pnpm -r e2e", - "e2e:dev": "pnpm build && pnpm -r e2e:dev", "prettier:check": "prettier --check .", "prettier:fix": "prettier --write .", "lint:check": "pnpm -r lint:check", @@ -22,6 +18,10 @@ "code:checks": "pnpm prettier:check && pnpm lint:check && pnpm ts:check", "install-playwright": "playwright install --with-deps", "postinstall": "pnpm --filter cloudflare build", + "test": "pnpm -r test", + "build": "pnpm --filter cloudflare build", + "e2e": "pnpm build && pnpm -F app-router e2e", + "e2e:dev": "pnpm build && pnpm -r e2e:dev", "benchmark": "pnpm run --filter benchmarking benchmark" } } diff --git a/packages/cloudflare/src/api/kvCache.ts b/packages/cloudflare/src/api/kvCache.ts index e7203a05..25211213 100644 --- a/packages/cloudflare/src/api/kvCache.ts +++ b/packages/cloudflare/src/api/kvCache.ts @@ -17,10 +17,16 @@ export const STATUS_DELETED = 1; class Cache implements IncrementalCache { readonly name = "cloudflare-kv"; + constructor() { + console.error(`========> cloudflare-kv Cache INIT`); + } + async get( key: string, isFetch?: IsFetch ): Promise>> { + console.error(`========> KVCache.get (${key})`); + const cfEnv = getCloudflareContext().env; const kv = cfEnv.NEXT_CACHE_WORKERS_KV; const assets = cfEnv.ASSETS; @@ -39,15 +45,18 @@ class Cache implements IncrementalCache { } | null = null; if (kv) { + console.error(` from KV`); this.debug(`- From KV`); const kvKey = this.getKVKey(key, isFetch); entry = await kv.get(kvKey, "json"); + console.error(` does entry from KV exist? ${!!entry}`); if (entry?.status === STATUS_DELETED) { return {}; } } if (!entry && assets) { + console.error(` from assets`); this.debug(`- From Assets`); const url = this.getAssetUrl(key, isFetch); const response = await assets.fetch(url); @@ -61,10 +70,12 @@ class Cache implements IncrementalCache { lastModified: (globalThis as { __BUILD_TIMESTAMP_MS__?: number }).__BUILD_TIMESTAMP_MS__, }; } + console.error(` does entry from assets exist? ${!!entry}`); } this.debug(entry ? `-> hit` : `-> miss`); return { value: entry?.value, lastModified: entry?.lastModified }; } catch { + console.error(` ERROR! FAILED TO GET Cache`); throw new RecoverableError(`Failed to get cache [${key}]`); } } @@ -74,13 +85,16 @@ class Cache implements IncrementalCache { value: CacheValue, isFetch?: IsFetch ): Promise { + console.error(`========> KVCache.set (${key})`); + const kv = getCloudflareContext().env.NEXT_CACHE_WORKERS_KV; if (!kv) { + console.error(` NO KV!`); throw new IgnorableError(`No KVNamespace`); } - this.debug(`Set ${key}`); + console.error(` from KV`); try { const kvKey = this.getKVKey(key, isFetch); @@ -97,6 +111,7 @@ class Cache implements IncrementalCache { }) ); } catch { + console.error(` ERROR! FAILED TO SET Cache`); throw new RecoverableError(`Failed to set cache [${key}]`); } } @@ -131,7 +146,7 @@ class Cache implements IncrementalCache { protected debug(...args: unknown[]) { if (process.env.NEXT_PRIVATE_DEBUG_CACHE) { - console.log(`[Cache ${this.name}] `, ...args); + console.error(`[Cache ${this.name}] `, ...args); } }