diff --git a/.claude/skills/triage-ci-flake/SKILL.md b/.claude/skills/triage-ci-flake/SKILL.md index c286b88daee..fe4d4604ab6 100644 --- a/.claude/skills/triage-ci-flake/SKILL.md +++ b/.claude/skills/triage-ci-flake/SKILL.md @@ -24,12 +24,11 @@ Systematic workflow for triaging and fixing test failures in CI, especially flak **YOU MUST EXECUTE THESE COMMANDS. Reading code or analyzing logs does NOT count as reproduction.** 1. **Extract** suite name, test name, and error from CI logs -2. **EXECUTE**: Kill port 3000 to avoid conflicts -3. **EXECUTE**: `pnpm dev $SUITE_NAME` (use run_in_background=true) -4. **EXECUTE**: Wait for server to be ready (check with curl or sleep) -5. **EXECUTE**: Run the specific failing test with Playwright directly (npx playwright test test/TEST_SUITE_NAME/e2e.spec.ts:31:3 --headed -g "TEST_DESCRIPTION_TARGET_GOES_HERE") -6. **If test passes**, **EXECUTE**: `pnpm prepare-run-test-against-prod` -7. **EXECUTE**: `pnpm dev:prod $SUITE_NAME` and run test again +2. **EXECUTE**: `pnpm dev $SUITE_NAME` (use run_in_background=true) — portless handles port assignment +3. **EXECUTE**: Wait for server to be ready (check with curl to http://payload-monorepo.localhost:1355) +4. **EXECUTE**: Run the specific failing test with Playwright directly (npx playwright test test/TEST_SUITE_NAME/e2e.spec.ts:31:3 --headed -g "TEST_DESCRIPTION_TARGET_GOES_HERE") +5. **If test passes**, **EXECUTE**: `pnpm prepare-run-test-against-prod` +6. **EXECUTE**: Stop dev server, then `pnpm dev:prod $SUITE_NAME` and run test again **Only after EXECUTING these commands and seeing their output** can you proceed to analysis and fixes. @@ -89,30 +88,24 @@ From CI logs or GitHub Actions URL, identify: **SERVER MANAGEMENT RULES:** -1. **ALWAYS kill all servers before starting a new one** -2. **NEVER assume ports are free** -3. **ALWAYS wait for server ready confirmation before running tests** +Portless handles port assignment automatically — no need to guess or free ports. If a stale dev server from another suite is running, stop it first (Ctrl+C the running `pnpm dev`, or `pkill -f "test/dev.ts"` if the process is orphaned) before starting a new one. Do NOT kill port 1355 — that's the portless proxy daemon which should stay running. ```bash # ======================================== -# STEP 2A: STOP ALL SERVERS -# ======================================== -lsof -ti:3000 | xargs kill -9 2>/dev/null || echo "Port 3000 clear" - -# ======================================== -# STEP 2B: START DEV SERVER +# STEP 2A: START DEV SERVER # ======================================== # Start dev server with the suite (in background with run_in_background=true) +# Portless auto-assigns a port and serves at http://payload-monorepo.localhost:1355 pnpm dev $SUITE_NAME # ======================================== -# STEP 2C: WAIT FOR SERVER READY +# STEP 2B: WAIT FOR SERVER READY # ======================================== # Wait for server to be ready (REQUIRED - do not skip) -until curl -s http://localhost:3000/admin > /dev/null 2>&1; do sleep 1; done && echo "Server ready" +until curl -s http://payload-monorepo.localhost:1355/admin > /dev/null 2>&1; do sleep 1; done && echo "Server ready" # ======================================== -# STEP 2D: RUN SPECIFIC TEST +# STEP 2C: RUN SPECIFIC TEST # ======================================== # Run ONLY the specific failing test using Playwright directly # For E2E tests (DO NOT use pnpm test:e2e as it spawns its own server): @@ -131,36 +124,30 @@ pnpm test:int $SUITE_NAME -t "exact test name" If test passed with dev code, the issue is likely in bundled/production code. -**IMPORTANT: You MUST stop the dev server before starting prod server.** +**IMPORTANT: Stop the dev server (Ctrl+C) before starting prod server.** ```bash # ======================================== -# STEP 3A: STOP ALL SERVERS (INCLUDING DEV SERVER FROM STEP 2) -# ======================================== -lsof -ti:3000 | xargs kill -9 2>/dev/null || echo "Port 3000 clear" - -# ======================================== -# STEP 3B: BUILD AND PACK FOR PROD +# STEP 3A: BUILD AND PACK FOR PROD # ======================================== # Build all packages and pack them (this takes time - be patient) pnpm prepare-run-test-against-prod # ======================================== -# STEP 3C: START PROD SERVER +# STEP 3B: START PROD SERVER # ======================================== # Start prod dev server (in background with run_in_background=true) +# Portless reuses the same URL: http://payload-monorepo.localhost:1355 pnpm dev:prod $SUITE_NAME # ======================================== -# STEP 3D: WAIT FOR SERVER READY +# STEP 3C: WAIT FOR SERVER READY # ======================================== -# Wait for server to be ready (REQUIRED - do not skip) -until curl -s http://localhost:3000/admin > /dev/null 2>&1; do sleep 1; done && echo "Server ready" +until curl -s http://payload-monorepo.localhost:1355/admin > /dev/null 2>&1; do sleep 1; done && echo "Server ready" # ======================================== -# STEP 3E: RUN SPECIFIC TEST +# STEP 3D: RUN SPECIFIC TEST # ======================================== -# Run the specific test again using Playwright directly pnpm exec playwright test test/$SUITE_NAME/e2e.spec.ts -g "exact test name" # OR for integration tests: pnpm test:int $SUITE_NAME -t "exact test name" @@ -237,7 +224,7 @@ When fixing e2e tests, be aware of these eslint rules: After fixing: ```bash -# Ensure dev server is running on port 3000 +# Ensure dev server is running via portless (pnpm dev $SUITE_NAME) # Run test multiple times to confirm stability for i in {1..10}; do pnpm exec playwright test test/$SUITE_NAME/e2e.spec.ts -g "exact test name" || break @@ -247,10 +234,10 @@ done pnpm exec playwright test test/$SUITE_NAME/e2e.spec.ts # If you modified bundled code, test with prod build -lsof -ti:3000 | xargs kill -9 2>/dev/null +# Stop dev server first (Ctrl+C), then: pnpm prepare-run-test-against-prod pnpm dev:prod $SUITE_NAME -until curl -s http://localhost:3000/admin > /dev/null; do sleep 1; done +until curl -s http://payload-monorepo.localhost:1355/admin > /dev/null; do sleep 1; done pnpm exec playwright test test/$SUITE_NAME/e2e.spec.ts ``` @@ -317,14 +304,12 @@ Agent: - Error: "Error loading language: en" TypeError: network error 2. Executing reproduction workflow: - [Uses Bash tool to kill ports] - $ lsof -ti:3000 | xargs kill -9 2>/dev/null || echo "Port 3000 clear" - [Uses Bash tool with run_in_background=true] $ pnpm dev i18n + # Portless auto-assigns port, serves at http://payload-monorepo.localhost:1355 [Waits for server to start with curl check] - $ until curl -s http://localhost:3000/admin > /dev/null; do sleep 1; done && echo "Server ready" + $ until curl -s http://payload-monorepo.localhost:1355/admin > /dev/null; do sleep 1; done && echo "Server ready" [Uses Bash tool to run Playwright directly] $ pnpm exec playwright test test/i18n/e2e.spec.ts -g "ensure translations update correctly when switching language" @@ -332,8 +317,7 @@ Agent: Output shows: Test PASSED locally 3. Test passed with dev code, trying bundled code: - [Kills ports again] - $ lsof -ti:3000 | xargs kill -9 2>/dev/null + [Stops dev server (Ctrl+C)] [Uses Bash tool] $ pnpm prepare-run-test-against-prod @@ -342,7 +326,7 @@ Agent: $ pnpm dev:prod i18n [Waits for server] - $ until curl -s http://localhost:3000/admin > /dev/null; do sleep 1; done + $ until curl -s http://payload-monorepo.localhost:1355/admin > /dev/null; do sleep 1; done [Uses Bash tool] $ pnpm exec playwright test test/i18n/e2e.spec.ts -g "ensure translations update correctly when switching language" diff --git a/CLAUDE.md b/CLAUDE.md index d622ed32c41..910b1a2358b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -118,7 +118,7 @@ Screenshots are saved to `.playwright-mcp/` and displayed inline. **Usage flow:** -1. Ensure dev server is running on `localhost:3000` +1. Ensure dev server is running on `http://payload-monorepo.localhost:1355` 2. Call `browser_navigate` to open a page 3. Call `browser_snapshot` to get element refs 4. Use refs to interact with `browser_click`, `browser_fill_form`, etc. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f9cc45a35e..ba4a894ed44 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -87,7 +87,7 @@ This project includes: **Prerequisites:** - The dev server must be running (`pnpm dev`) before using the Playwright MCP -- The AI will navigate to `localhost:3000` to interact with the app +- The AI will navigate to `http://payload-monorepo.localhost:1355` to interact with the app Without MCP, the AI cannot interactively browse and test the running application. It would be limited to writing test code without being able to verify the UI directly. diff --git a/ISSUE_GUIDE.md b/ISSUE_GUIDE.md index d7e9fbc321d..8864e703d35 100644 --- a/ISSUE_GUIDE.md +++ b/ISSUE_GUIDE.md @@ -66,4 +66,4 @@ Once they are installed you can open the `testing` tab in vscode sidebar and dri #### Notes -- It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `localhost:3000/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as email and `test` as password. +- It is recommended to add the test credentials (located in `test/credentials.ts`) to your autofill for `http://payload-monorepo.localhost:1355/admin` as this will be required on every nodemon restart. The default credentials are `dev@payloadcms.com` as email and `test` as password. diff --git a/docs/plans/2026-03-25-portless-server-url.md b/docs/plans/2026-03-25-portless-server-url.md new file mode 100644 index 00000000000..6266579ea86 --- /dev/null +++ b/docs/plans/2026-03-25-portless-server-url.md @@ -0,0 +1,548 @@ +# Portless Server URL Migration + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Replace all hardcoded `localhost:3000` references in the monorepo's test infrastructure with a single `serverURL` helper that reads `process.env.PORTLESS_URL`, set automatically by portless. + +**Architecture:** A single shared helper (`test/__helpers/shared/serverURL.ts`) exports `serverURL` from `process.env.PORTLESS_URL`. All test infra, configs, and fixtures import from it. Client-side components switch to relative URLs. The `pnpm dev` scripts already wrap commands with `portless payload-monorepo`, which sets `PORTLESS_URL=http://payload-monorepo.localhost:1355`. + +**Tech Stack:** TypeScript, Vitest, Playwright, Next.js, portless + +--- + +### Task 1: Create the shared serverURL helper + +**Files:** + +- Create: `test/__helpers/shared/serverURL.ts` + +**Step 1: Create the helper file** + +```ts +export const serverURL: string = process.env.PORTLESS_URL +``` + +**Step 2: Commit** + +```bash +git add test/__helpers/shared/serverURL.ts +git commit -m "feat: add shared serverURL helper reading PORTLESS_URL" +``` + +--- + +### Task 2: Update test infrastructure entry points + +**Files:** + +- Modify: `test/__helpers/shared/initPayloadE2ENoConfig.ts` +- Modify: `test/__helpers/shared/NextRESTClient.ts:82` +- Modify: `test/__helpers/shared/getSDK.ts:24` +- Modify: `test/dev.ts:81` + +**Step 1: Update `initPayloadE2ENoConfig.ts`** + +Replace lines 29-33: + +```ts +const port = 3000 +process.env.PORT = String(port) +process.env.PAYLOAD_CI_DEPENDENCY_CHECKER = 'true' + +const serverURL = `http://localhost:${port}` +``` + +With: + +```ts +process.env.PAYLOAD_CI_DEPENDENCY_CHECKER = 'true' + +const { serverURL } = await import('./serverURL.js') +``` + +Remove the unused `port` variable entirely. Do NOT set `process.env.PORT` — portless handles port assignment. + +**Step 2: Update `NextRESTClient.ts`** + +Replace line 82: + +```ts +serverURL: string = 'http://localhost:3000' +``` + +With: + +```ts +serverURL: string = process.env.PORTLESS_URL +``` + +Note: This file cannot import from `serverURL.ts` because it's a class field default. Use `process.env.PORTLESS_URL` directly here — this is the ONE exception. The config override on line 86-88 still takes precedence. + +**Step 3: Update `getSDK.ts`** + +Replace line 24: + +```ts +const url = `${config.serverURL || 'http://localhost:3000'}${config.routes.api}/${slugs}${search ? `?${search}` : ''}` +``` + +With: + +```ts +const url = `${config.serverURL || process.env.PORTLESS_URL}${config.routes.api}/${slugs}${search ? `?${search}` : ''}` +``` + +Same exception as NextRESTClient — inline env read is acceptable since these are fallback defaults. + +**Step 4: Update `dev.ts`** + +Replace line 81: + +```ts +await open(`http://localhost:3000${adminRoute}`) +``` + +With: + +```ts +await open(`${process.env.PORTLESS_URL}${adminRoute}`) +``` + +This runs inside the portless child process where `PORTLESS_URL` is set. + +**Step 5: Commit** + +```bash +git add test/__helpers/shared/initPayloadE2ENoConfig.ts test/__helpers/shared/NextRESTClient.ts test/__helpers/shared/getSDK.ts test/dev.ts +git commit -m "feat: update test infra to use PORTLESS_URL instead of localhost:3000" +``` + +--- + +### Task 3: Update `runE2E.ts` server detection + +**Files:** + +- Modify: `test/runE2E.ts:164-174` + +**Step 1: Replace TCP port check with HTTP fetch to portless URL** + +Replace lines 1 and 4 (imports): + +```ts +import { spawn } from 'child_process' +... +import { createServer } from 'net' +``` + +With: + +```ts +import { spawn } from 'child_process' +... +``` + +Remove the `createServer` import from `'net'` (no longer needed). + +Replace lines 162-183: + +```ts +process.env.START_MEMORY_DB = 'true' + +const portInUse = await new Promise((resolve) => { + const server = createServer() + server.once('error', () => resolve(true)) + server.once('listening', () => server.close(() => resolve(false))) + server.listen(3000) +}) + +let child: ReturnType | undefined + +if (portInUse) { + console.log('Port 3000 is already in use — reusing existing dev server.') +} else { + child = spawn('pnpm', spawnDevArgs, { + cwd: path.resolve(dirname, '..'), + env: { + ...process.env, + }, + stdio: 'inherit', + }) +} +``` + +With: + +```ts +process.env.START_MEMORY_DB = 'true' + +const serverURL = process.env.PORTLESS_URL + +const serverAlreadyRunning = await fetch(serverURL) + .then(() => true) + .catch(() => false) + +let child: ReturnType | undefined + +if (serverAlreadyRunning) { + console.log(`Dev server already running at ${serverURL} — reusing.`) +} else { + child = spawn('pnpm', spawnDevArgs, { + cwd: path.resolve(dirname, '..'), + env: { + ...process.env, + }, + stdio: 'inherit', + }) +} +``` + +**Step 2: Commit** + +```bash +git add test/runE2E.ts +git commit -m "feat: check portless URL instead of port 3000 in E2E runner" +``` + +--- + +### Task 4: Update test configs — cors/csrf arrays + +These configs use `localhost:3000` in cors/csrf arrays. Import `serverURL` and use it. + +**Files:** + +- Modify: `test/admin-root/config.ts:34` +- Modify: `test/sort/config.ts:46` +- Modify: `test/live-preview/config.ts:57-58` +- Modify: `test/lexical-mdx/config.ts:35` +- Modify: `test/select/getConfig.ts:123` + +**Step 1: For each file, add import and replace cors/csrf arrays** + +Add import at top of each file: + +```ts +import { serverURL } from '../__helpers/shared/serverURL.js' +``` + +Replace cors arrays like: + +```ts +cors: ['http://localhost:3000', 'http://localhost:3001'], +``` + +With: + +```ts +cors: [serverURL, 'http://localhost:3001'], +``` + +Do the same for csrf arrays in `live-preview/config.ts`. + +Note: Adjust the relative import path per file depth. For `select/getConfig.ts` it's `../__helpers/shared/serverURL.js`. For `admin-root/config.ts` it's also `../__helpers/shared/serverURL.js`. + +**Step 2: Commit** + +```bash +git add test/admin-root/config.ts test/sort/config.ts test/live-preview/config.ts test/lexical-mdx/config.ts test/select/getConfig.ts +git commit -m "feat: use serverURL helper in test config cors/csrf arrays" +``` + +--- + +### Task 5: Update test configs — serverURL and livePreview properties + +**Files:** + +- Modify: `test/base-path/config.ts:18` +- Modify: `test/server-url/config.ts:12` +- Modify: `test/admin/config.ts:157` +- Modify: `test/live-preview/collections/CollectionLevelConfig.ts:11` + +**Step 1: For each file, add import and replace** + +Add import: + +```ts +import { serverURL } from '../__helpers/shared/serverURL.js' +``` + +Replace: + +- `test/base-path/config.ts:18`: `serverURL: 'http://localhost:3000'` -> `serverURL,` +- `test/server-url/config.ts:12`: `serverURL: 'http://localhost:3000'` -> `serverURL,` +- `test/admin/config.ts:157`: `url: 'http://localhost:3000'` -> `url: serverURL` +- `test/live-preview/collections/CollectionLevelConfig.ts:11`: `url: 'http://localhost:3000/live-preview'` -> `` url: `${serverURL}/live-preview` `` + +Adjust relative import paths per file depth. + +**Step 2: Commit** + +```bash +git add test/base-path/config.ts test/server-url/config.ts test/admin/config.ts test/live-preview/collections/CollectionLevelConfig.ts +git commit -m "feat: use serverURL helper in test config serverURL and livePreview properties" +``` + +--- + +### Task 6: Update live-preview test files + +**Files:** + +- Modify: `test/live-preview/app/live-preview/_api/serverURL.ts:1` +- Modify: `test/live-preview/prod/app/live-preview/_api/serverURL.ts:1` +- Modify: `test/live-preview/seed/tenant-1.ts:5` +- Modify: `test/live-preview/int.spec.ts:43,83` + +**Step 1: Update the two `_api/serverURL.ts` files** + +Both files currently export: + +```ts +export const PAYLOAD_SERVER_URL = 'http://localhost:3000' +``` + +Replace with: + +```ts +export const PAYLOAD_SERVER_URL = process.env.PORTLESS_URL +``` + +These are Next.js server-side files (inside `app/`), so `process.env` is available. + +**Step 2: Update `live-preview/seed/tenant-1.ts`** + +Add import: + +```ts +import { serverURL } from '../__helpers/shared/serverURL.js' +``` + +Replace line 5: + +```ts +clientURL: 'http://localhost:3000', +``` + +With: + +```ts +clientURL: serverURL, +``` + +Adjust import path: `../../__helpers/shared/serverURL.js` + +**Step 3: Update `live-preview/int.spec.ts`** + +Add import: + +```ts +import { serverURL } from '../__helpers/shared/serverURL.js' +``` + +Remove line 43: + +```ts +const serverURL: string = 'http://localhost:3000' +``` + +The imported `serverURL` replaces it. + +Replace line 83: + +```ts +clientURL: 'http://localhost:3000', +``` + +With: + +```ts +clientURL: serverURL, +``` + +**Step 4: Commit** + +```bash +git add test/live-preview/ +git commit -m "feat: use serverURL helper in live-preview test files" +``` + +--- + +### Task 7: Update client-side components to use relative URLs + +These are `'use client'` React components. They cannot access `process.env` at runtime. Use relative URLs instead — these fetch calls target the same origin. + +**Files:** + +- Modify: `test/plugin-sentry/TestErrors.tsx:9,15,24,34,44,58` +- Modify: `test/plugin-ecommerce/app/components/CheckoutStripe.tsx:22` +- Modify: `test/admin-bar/app/admin-bar/layout.tsx:20` + +**Step 1: Update `TestErrors.tsx`** + +Replace all 6 occurrences of `http://localhost:3000` with empty string (making paths relative): + +- `'http://localhost:3000/api/users/notFound'` -> `'/api/users/notFound'` +- `'http://localhost:3000/api/posts'` -> `'/api/posts'` +- `'http://localhost:3000/api/users/login'` -> `'/api/users/login'` +- `'http://localhost:3000/api/users/forgot-password'` -> `'/api/users/forgot-password'` +- `'http://localhost:3000/api/users/reset-password'` -> `'/api/users/reset-password'` +- `'http://localhost:3000/api/users/unlock'` -> `'/api/users/unlock'` + +**Step 2: Update `CheckoutStripe.tsx`** + +Replace line 22: + +```ts +return_url: 'http://localhost:3000/shop/confirm-order', +``` + +With: + +```ts +return_url: `${window.location.origin}/shop/confirm-order`, +``` + +Stripe's `return_url` requires an absolute URL, so use `window.location.origin`. + +**Step 3: Update `admin-bar/layout.tsx`** + +This is a server component (no `'use client'`). Replace line 20: + +```tsx +cmsURL = 'http://localhost:3000' +``` + +With: + +```tsx +cmsURL={process.env.PORTLESS_URL} +``` + +**Step 4: Commit** + +```bash +git add test/plugin-sentry/TestErrors.tsx test/plugin-ecommerce/app/components/CheckoutStripe.tsx test/admin-bar/app/admin-bar/layout.tsx +git commit -m "feat: use relative URLs and PORTLESS_URL in client/server test components" +``` + +--- + +### Task 8: Update E2E spec files with hardcoded URLs + +**Files:** + +- Modify: `test/lexical/collections/Lexical/e2e/main/e2e.spec.ts:1458,1494` +- Modify: `test/lexical/collections/Lexical/e2e/blocks/e2e.spec.ts:1277,1329,1491` + +**Step 1: Add import to both spec files** + +```ts +import { serverURL } from '../../../../__helpers/shared/serverURL.js' +``` + +**Step 2: Replace hardcoded URLs** + +In `main/e2e.spec.ts`: + +- Line 1458: `'http://localhost:3000/admin/collections/rich-text-fields?limit=10'` -> `` `${serverURL}/admin/collections/rich-text-fields?limit=10` `` +- Line 1494: `'http://localhost:3000/admin/collections/LexicalInBlock?limit=10'` -> `` `${serverURL}/admin/collections/LexicalInBlock?limit=10` `` + +In `blocks/e2e.spec.ts`: + +- Lines 1277, 1329, 1491: same pattern, replace `'http://localhost:3000/admin/collections/LexicalInBlock?limit=10'` -> `` `${serverURL}/admin/collections/LexicalInBlock?limit=10` `` + +**Step 3: Commit** + +```bash +git add test/lexical/collections/Lexical/e2e/main/e2e.spec.ts test/lexical/collections/Lexical/e2e/blocks/e2e.spec.ts +git commit -m "feat: use serverURL helper in lexical E2E specs" +``` + +--- + +### Task 9: Update remaining files + +**Files:** + +- Modify: `test/field-error-states/collections/PrevValue/index.ts:37` +- Modify: `test/plugin-cloud-storage/.env.emulated:3` +- Modify: `test/plugin-import-export/.env.emulated:3` +- Modify: `test/uploads/config.ts:787` (comment only) + +**Step 1: Update `field-error-states/collections/PrevValue/index.ts`** + +This is a server-side collection hook. Add import: + +```ts +import { serverURL } from '../../../../__helpers/shared/serverURL.js' +``` + +Replace line 37: + +```ts +`http://localhost:3000/api/${collectionSlugs.prevValueRelation}${query}`, +``` + +With: + +```ts +`${serverURL}/api/${collectionSlugs.prevValueRelation}${query}`, +``` + +Adjust import path based on file depth: `../../../__helpers/shared/serverURL.js` + +**Step 2: Update `.env.emulated` files** + +In `test/plugin-cloud-storage/.env.emulated` and `test/plugin-import-export/.env.emulated`, replace: + +``` +PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000 +``` + +With: + +``` +PAYLOAD_PUBLIC_SERVER_URL=http://payload-monorepo.localhost:1355 +``` + +These are env files that can't import JS — hardcode the URL here. This is acceptable since they're test fixtures, not production code. + +**Step 3: Update comment in `uploads/config.ts`** + +Replace line 787's comment reference from `http://localhost:3000/media` to `http://payload-monorepo.localhost:1355/media`. + +**Step 4: Commit** + +```bash +git add test/field-error-states/collections/PrevValue/index.ts test/plugin-cloud-storage/.env.emulated test/plugin-import-export/.env.emulated test/uploads/config.ts +git commit -m "feat: replace remaining localhost:3000 references with portless URL" +``` + +--- + +### Task 10: Verify no remaining localhost:3000 in test/ + +**Step 1: Search for remaining references** + +Run: + +```bash +grep -r "localhost:3000" test/ --include="*.ts" --include="*.tsx" --include="*.env*" -l +``` + +Expected: no results. + +**Step 2: Verify the dev server starts and PORTLESS_URL is set** + +Run: + +```bash +pnpm dev _community +``` + +Verify output shows `PORTLESS_URL=http://payload-monorepo.localhost:1355`. + +**Step 3: Final commit if any fixups needed** diff --git a/package.json b/package.json index cf6722b44bf..e38d9cc9d48 100644 --- a/package.json +++ b/package.json @@ -73,14 +73,14 @@ "clean:build": "node ./scripts/delete-recursively.js 'media/' '**/dist/' '**/.cache/' '**/.next/' '**/.turbo/' '**/tsconfig.tsbuildinfo' '**/payload*.tgz' '**/meta_*.json'", "clean:build:allowtgz": "node ./scripts/delete-recursively.js 'media/' '**/dist/' '**/.cache/' '**/.next/' '**/.turbo/' '**/tsconfig.tsbuildinfo' '**/meta_*.json'", "clean:cache": "node ./scripts/delete-recursively.js node_modules/.cache! packages/payload/node_modules/.cache! .next/*", - "dev": "cross-env NODE_OPTIONS=\"--no-deprecation --max-old-space-size=16384\" tsx ./test/dev.ts", + "dev": "portless --name payload-monorepo cross-env NODE_OPTIONS=\"--no-deprecation --max-old-space-size=16384\" tsx ./test/dev.ts", "dev:generate-db-schema": "pnpm runts ./test/generateDatabaseSchema.ts", "dev:generate-graphql-schema": "pnpm runts ./test/generateGraphQLSchema.ts", "dev:generate-importmap": "pnpm runts ./test/generateImportMap.ts", "dev:generate-types": "pnpm runts ./test/generateTypes.ts", - "dev:postgres": "cross-env PAYLOAD_DATABASE=postgres pnpm runts ./test/dev.ts", - "dev:prod": "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/dev.ts --prod", - "dev:vercel-postgres": "cross-env PAYLOAD_DATABASE=vercel-postgres pnpm runts ./test/dev.ts", + "dev:postgres": "portless --name payload-monorepo cross-env PAYLOAD_DATABASE=postgres pnpm runts ./test/dev.ts", + "dev:prod": "portless --name payload-monorepo cross-env NODE_OPTIONS=--no-deprecation tsx ./test/dev.ts --prod", + "dev:vercel-postgres": "portless --name payload-monorepo cross-env PAYLOAD_DATABASE=vercel-postgres pnpm runts ./test/dev.ts", "devsafe": "node ./scripts/delete-recursively.js '**/.next' && pnpm dev", "docker:clean": "node ./scripts/docker-clean.js", "docker:start": "pnpm docker:clean && docker compose -f test/docker-compose.yml --profile all up -d --wait", @@ -223,6 +223,7 @@ "open": "^10.1.0", "p-limit": "^5.0.0", "pg": "8.16.3", + "portless": "0.7.2", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "prettier": "3.5.3", diff --git a/packages/payload/src/uploads/endpoints/getFileFromURL.ts b/packages/payload/src/uploads/endpoints/getFileFromURL.ts index 04cad6ce788..fcab1bd5fef 100644 --- a/packages/payload/src/uploads/endpoints/getFileFromURL.ts +++ b/packages/payload/src/uploads/endpoints/getFileFromURL.ts @@ -118,11 +118,15 @@ export const getFileFromURLHandler: PayloadHandler = async (req) => { // Strip quotes, backslashes, and control chars from the ASCII fallback const asciiFileName = safeFileName.replace(/["\\\r\n]/g, '_') - return new Response(response.body, { - headers: { - 'Content-Disposition': `attachment; filename="${asciiFileName}"; filename*=UTF-8''${encodedFileName}`, - 'Content-Length': response.headers.get('content-length') || '', - 'Content-Type': response.headers.get('content-type') || 'application/octet-stream', - }, - }) + const headers: Record = { + 'Content-Disposition': `attachment; filename="${asciiFileName}"; filename*=UTF-8''${encodedFileName}`, + 'Content-Type': response.headers.get('content-type') || 'application/octet-stream', + } + + const contentLength = response.headers.get('content-length') + if (contentLength) { + headers['Content-Length'] = contentLength + } + + return new Response(response.body, { headers }) } diff --git a/packages/plugin-cloud-storage/src/plugin.ts b/packages/plugin-cloud-storage/src/plugin.ts index e41d9269836..96b19b34656 100644 --- a/packages/plugin-cloud-storage/src/plugin.ts +++ b/packages/plugin-cloud-storage/src/plugin.ts @@ -132,16 +132,16 @@ export const cloudStoragePlugin = return entryKeys.length === 1 && entry.hostname === 'localhost' }) - const localhostEntry = + const localhostEntries = process.env.NODE_ENV !== 'production' && !hasExactLocalhostMatch - ? [{ hostname: 'localhost' }] + ? [{ hostname: 'localhost' }, { hostname: 'payload-monorepo.localhost' }] : [] - return [...existingSkipSafeFetch, ...localhostEntry] + return [...existingSkipSafeFetch, ...localhostEntries] } if (process.env.NODE_ENV !== 'production') { - return [{ hostname: 'localhost' }] + return [{ hostname: 'localhost' }, { hostname: 'payload-monorepo.localhost' }] } return false diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 743a90bc116..67c01dda80e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -160,6 +160,9 @@ importers: pg: specifier: 8.16.3 version: 8.16.3 + portless: + specifier: 0.7.2 + version: 0.7.2 postcss: specifier: ^8.4.49 version: 8.5.8 @@ -1876,7 +1879,7 @@ importers: version: 16.8.1 next: specifier: 16.2.1 - version: 16.2.1(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.98.0) + version: 16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.98.0) payload: specifier: workspace:* version: link:../../packages/payload @@ -1913,7 +1916,7 @@ importers: version: 9.39.2(jiti@2.6.1) eslint-config-next: specifier: 16.2.1 - version: 16.2.1(eslint-plugin-import-x@4.6.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3) + version: 16.2.1(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-plugin-import-x@4.6.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3) jsdom: specifier: 28.0.0 version: 28.0.0(@noble/hashes@1.8.0) @@ -2015,7 +2018,7 @@ importers: version: 8.6.0(react@19.2.4) geist: specifier: ^1.3.0 - version: 1.7.0(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.98.0)) + version: 1.7.0(next@16.2.1(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.98.0)) graphql: specifier: 16.8.1 version: 16.8.1 @@ -2027,7 +2030,7 @@ importers: version: 0.563.0(react@19.2.4) next: specifier: 16.2.1 - version: 16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.98.0) + version: 16.2.1(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.98.0) next-themes: specifier: 0.4.6 version: 0.4.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -2103,7 +2106,7 @@ importers: version: 9.39.2(jiti@2.6.1) eslint-config-next: specifier: 16.2.1 - version: 16.2.1(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-plugin-import-x@4.6.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3) + version: 16.2.1(eslint-plugin-import-x@4.6.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3) eslint-plugin-jsx-a11y: specifier: ^6.10.2 version: 6.10.2(eslint@9.39.2(jiti@2.6.1)) @@ -12408,6 +12411,12 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} + portless@0.7.2: + resolution: {integrity: sha512-bwtBBTqCe4K2FL3MMTCMFw3ZEBsv5lJeu9VgVdDqFPhb04jgAaWu3ibXP/dZ6y7OfqV/Vphj0e7Z6bQxcr8uFg==} + engines: {node: '>=20'} + os: [darwin, linux, win32] + hasBin: true + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -23318,7 +23327,7 @@ snapshots: eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.6.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.6.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@2.6.1)) @@ -23375,7 +23384,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.6.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-import-x: 4.6.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3) transitivePeerDependencies: - supports-color @@ -23437,7 +23446,7 @@ snapshots: - supports-color - typescript - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.6.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.57.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.7.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -24219,10 +24228,6 @@ snapshots: - encoding - supports-color - geist@1.7.0(next@16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.98.0)): - dependencies: - next: 16.2.1(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.98.0) - geist@1.7.0(next@16.2.1(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.98.0)): dependencies: next: 16.2.1(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.98.0) @@ -26425,6 +26430,10 @@ snapshots: pluralize@8.0.0: {} + portless@0.7.2: + dependencies: + chalk: 5.6.2 + possible-typed-array-names@1.1.0: {} postcss-resolve-nested-selector@0.1.6: {} diff --git a/test/__helpers/e2e/helpers.ts b/test/__helpers/e2e/helpers.ts index 79016019bb3..56ce03350e3 100644 --- a/test/__helpers/e2e/helpers.ts +++ b/test/__helpers/e2e/helpers.ts @@ -120,6 +120,22 @@ export async function ensureCompilationIsDone({ .toBe(true) } + // Also verify that the API is ready (not just the admin page). + // With a reverse proxy, the admin page can load while API routes + // are still compiling, causing JSON parse errors in tests. + const apiReady = await page.evaluate(async (url) => { + try { + const res = await fetch(`${url}/api/access`) + return res.headers.get('content-type')?.includes('application/json') ?? false + } catch { + return false + } + }, serverURL) + + if (!apiReady) { + throw new Error('API routes not ready yet') + } + console.log('Successfully compiled') if (browser) { await page.close() diff --git a/test/__helpers/shared/NextRESTClient.ts b/test/__helpers/shared/NextRESTClient.ts index 5a0823d6fb3..a0ae5db47b1 100644 --- a/test/__helpers/shared/NextRESTClient.ts +++ b/test/__helpers/shared/NextRESTClient.ts @@ -13,6 +13,7 @@ import * as qs from 'qs-esm' import { devUser } from '../../credentials.js' import { getFormDataSize } from './getFormDataSize.js' +import { serverURL as serverURL_ } from './serverURL.js' type ValidPath = `/${string}` type RequestOptions = { @@ -79,7 +80,7 @@ export class NextRESTClient { private token: string - serverURL: string = 'http://localhost:3000' + serverURL: string = serverURL_ constructor(config: SanitizedConfig) { this.config = config diff --git a/test/__helpers/shared/getSDK.ts b/test/__helpers/shared/getSDK.ts index 6036cd9b613..7d134f38de7 100644 --- a/test/__helpers/shared/getSDK.ts +++ b/test/__helpers/shared/getSDK.ts @@ -3,6 +3,8 @@ import type { GeneratedTypes, SanitizedConfig } from 'payload' import { REST_DELETE, REST_GET, REST_PATCH, REST_POST, REST_PUT } from '@payloadcms/next/routes' import { PayloadSDK } from '@payloadcms/sdk' +import { serverURL as serverURL_ } from './serverURL.js' + export type TypedPayloadSDK = PayloadSDK /** @@ -21,7 +23,7 @@ export const getSDK = (config: SanitizedConfig) => { baseURL: ``, fetch: (path: string, init: RequestInit) => { const [slugs, search] = path.slice(1).split('?') - const url = `${config.serverURL || 'http://localhost:3000'}${config.routes.api}/${slugs}${search ? `?${search}` : ''}` + const url = `${config.serverURL || serverURL_}${config.routes.api}/${slugs}${search ? `?${search}` : ''}` if (init.body instanceof FormData) { const file = init.body.get('file') as Blob diff --git a/test/__helpers/shared/initPayloadE2ENoConfig.ts b/test/__helpers/shared/initPayloadE2ENoConfig.ts index 14dae3332dc..6780acdfea3 100644 --- a/test/__helpers/shared/initPayloadE2ENoConfig.ts +++ b/test/__helpers/shared/initPayloadE2ENoConfig.ts @@ -26,11 +26,9 @@ export async function initPayloadE2ENoConfig>({ }: Args): Promise> { const testSuiteName = path.basename(dirname) - const port = 3000 - process.env.PORT = String(port) process.env.PAYLOAD_CI_DEPENDENCY_CHECKER = 'true' - const serverURL = `http://localhost:${port}` + const { serverURL } = await import('./serverURL.js') const { rootDir } = getNextRootDir(testSuiteName) diff --git a/test/__helpers/shared/serverURL.ts b/test/__helpers/shared/serverURL.ts new file mode 100644 index 00000000000..f65e770d865 --- /dev/null +++ b/test/__helpers/shared/serverURL.ts @@ -0,0 +1,2 @@ +export const serverURL: string = + process.env.PORTLESS_URL || 'http://payload-monorepo.localhost:1355' diff --git a/test/_community/payload-types.ts b/test/_community/payload-types.ts index 944c99099f4..d4b81957515 100644 --- a/test/_community/payload-types.ts +++ b/test/_community/payload-types.ts @@ -96,6 +96,9 @@ export interface Config { menu: MenuSelect | MenuSelect; }; locale: null; + widgets: { + collections: CollectionsWidget; + }; user: User; jobs: { tasks: unknown; @@ -435,6 +438,16 @@ export interface MenuSelect { createdAt?: T; globalType?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "collections_widget". + */ +export interface CollectionsWidget { + data?: { + [k: string]: unknown; + }; + width: 'full'; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "auth". diff --git a/test/access-control/e2e.spec.ts b/test/access-control/e2e.spec.ts index c8473e3a48f..e3bc89ce0e5 100644 --- a/test/access-control/e2e.spec.ts +++ b/test/access-control/e2e.spec.ts @@ -798,7 +798,7 @@ describe('Access Control', () => { await context.addCookies([ { name: 'payload-token', - domain: 'localhost', + domain: new URL(serverURL).hostname, httpOnly: true, path: '/', secure: true, diff --git a/test/admin-bar/app/admin-bar/layout.tsx b/test/admin-bar/app/admin-bar/layout.tsx index 3e797358ee0..5934b14d7e6 100644 --- a/test/admin-bar/app/admin-bar/layout.tsx +++ b/test/admin-bar/app/admin-bar/layout.tsx @@ -3,6 +3,7 @@ import type { Metadata } from 'next' import { PayloadAdminBar } from '@payloadcms/admin-bar' import React from 'react' +import { serverURL } from '../../../__helpers/shared/serverURL.js' import './app.scss' export const metadata: Metadata = { @@ -17,7 +18,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) { diff --git a/test/dev.ts b/test/dev.ts index 44be1b7dc55..ab13dd118c0 100644 --- a/test/dev.ts +++ b/test/dev.ts @@ -78,7 +78,8 @@ nextEnvImport.updateInitialEnv(process.env) // Open the admin if the -o flag is passed if (args.o) { - await open(`http://localhost:3000${adminRoute}`) + const { serverURL } = await import('./__helpers/shared/serverURL.js') + await open(`${serverURL}${adminRoute}`) } const findOpenPort = (startPort: number): Promise => { diff --git a/test/field-error-states/collections/PrevValue/index.ts b/test/field-error-states/collections/PrevValue/index.ts index e43f97e15bf..853a126ebbf 100644 --- a/test/field-error-states/collections/PrevValue/index.ts +++ b/test/field-error-states/collections/PrevValue/index.ts @@ -2,6 +2,7 @@ import type { CollectionConfig } from 'payload' import * as QueryString from 'qs-esm' +import { serverURL } from '../../../__helpers/shared/serverURL.js' import { collectionSlugs } from '../../shared.js' export const PrevValue: CollectionConfig = { @@ -34,7 +35,7 @@ export const PrevValue: CollectionConfig = { try { const relatedDocs = await fetch( - `http://localhost:3000/api/${collectionSlugs.prevValueRelation}${query}`, + `${serverURL}/api/${collectionSlugs.prevValueRelation}${query}`, { credentials: 'include', headers: { diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index 375cc364075..724835725e2 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -403,241 +403,23 @@ export interface ArrayField { */ export interface BlockField { id: string; - blocks: ( - | { - text: string; - richText?: - | { - [k: string]: unknown; - }[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'content'; - } - | { - title: string; - id?: string | null; - blockName?: string | null; - blockType: 'withIcon'; - } - | { - text?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'noBlockname'; - } - | { - number: number; - id?: string | null; - blockName?: string | null; - blockType: 'number'; - } - | { - subBlocks?: - | ( - | { - text: string; - id?: string | null; - blockName?: string | null; - blockType: 'textRequired'; - } - | { - number: number; - id?: string | null; - blockName?: string | null; - blockType: 'number'; - } - )[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'subBlocks'; - } - | { - textInCollapsible?: string | null; - textInRow?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'tabs'; - } - )[]; - duplicate: ( - | { - text: string; - richText?: - | { - [k: string]: unknown; - }[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'content'; - } - | { - title: string; - id?: string | null; - blockName?: string | null; - blockType: 'withIcon'; - } - | { - text?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'noBlockname'; - } - | { - number: number; - id?: string | null; - blockName?: string | null; - blockType: 'number'; - } - | { - subBlocks?: - | ( - | { - text: string; - id?: string | null; - blockName?: string | null; - blockType: 'textRequired'; - } - | { - number: number; - id?: string | null; - blockName?: string | null; - blockType: 'number'; - } - )[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'subBlocks'; - } - | { - textInCollapsible?: string | null; - textInRow?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'tabs'; - } - )[]; + blocks: (ContentBlock | WithIconBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[]; + duplicate: (ContentBlock | WithIconBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[]; collapsedByDefaultBlocks: ( - | { - text: string; - richText?: - | { - [k: string]: unknown; - }[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'localizedContent'; - } - | { - title: string; - id?: string | null; - blockName?: string | null; - blockType: 'localizedWithIcon'; - } - | { - text?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'localizedNoBlockname'; - } - | { - number: number; - id?: string | null; - blockName?: string | null; - blockType: 'localizedNumber'; - } - | { - subBlocks?: - | ( - | { - text: string; - id?: string | null; - blockName?: string | null; - blockType: 'textRequired'; - } - | { - number: number; - id?: string | null; - blockName?: string | null; - blockType: 'number'; - } - )[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'localizedSubBlocks'; - } - | { - textInCollapsible?: string | null; - textInRow?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'localizedTabs'; - } + | LocalizedContentBlock + | LocalizedWithIconBlock + | LocalizedNoBlockname + | LocalizedNumberBlock + | LocalizedSubBlocksBlock + | LocalizedTabsBlock )[]; disableSort: ( - | { - text: string; - richText?: - | { - [k: string]: unknown; - }[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'localizedContent'; - } - | { - title: string; - id?: string | null; - blockName?: string | null; - blockType: 'localizedWithIcon'; - } - | { - text?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'localizedNoBlockname'; - } - | { - number: number; - id?: string | null; - blockName?: string | null; - blockType: 'localizedNumber'; - } - | { - subBlocks?: - | ( - | { - text: string; - id?: string | null; - blockName?: string | null; - blockType: 'textRequired'; - } - | { - number: number; - id?: string | null; - blockName?: string | null; - blockType: 'number'; - } - )[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'localizedSubBlocks'; - } - | { - textInCollapsible?: string | null; - textInRow?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'localizedTabs'; - } + | LocalizedContentBlock + | LocalizedWithIconBlock + | LocalizedNoBlockname + | LocalizedNumberBlock + | LocalizedSubBlocksBlock + | LocalizedTabsBlock )[]; localizedBlocks: ( | LocalizedContentBlock @@ -768,38 +550,10 @@ export interface BlockField { blockType: 'text'; }[] | null; - deduplicatedBlocks?: - | { - deduplicatedText?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'ConfigBlockTest'; - }[] - | null; - deduplicatedBlocks2?: - | { - deduplicatedText?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'ConfigBlockTest'; - }[] - | null; - localizedReferencesLocalizedBlock?: - | { - text?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'localizedTextReference'; - }[] - | null; - localizedReferences?: - | { - text?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'localizedTextReference2'; - }[] - | null; + deduplicatedBlocks?: ConfigBlockTest[] | null; + deduplicatedBlocks2?: ConfigBlockTest[] | null; + localizedReferencesLocalizedBlock?: LocalizedTextReference[] | null; + localizedReferences?: LocalizedTextReference2[] | null; /** * The purpose of this field is to test Block groups. */ @@ -890,6 +644,82 @@ export interface BlockField { updatedAt: string; createdAt: string; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "ContentBlock". + */ +export interface ContentBlock { + text: string; + richText?: + | { + [k: string]: unknown; + }[] + | null; + id?: string | null; + blockName?: string | null; + blockType: 'content'; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "WithIconBlock". + */ +export interface WithIconBlock { + title: string; + id?: string | null; + blockName?: string | null; + blockType: 'withIcon'; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "NoBlockname". + */ +export interface NoBlockname { + text?: string | null; + id?: string | null; + blockName?: string | null; + blockType: 'noBlockname'; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "NumberBlock". + */ +export interface NumberBlock { + number: number; + id?: string | null; + blockName?: string | null; + blockType: 'number'; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "SubBlocksBlock". + */ +export interface SubBlocksBlock { + subBlocks?: + | ( + | { + text: string; + id?: string | null; + blockName?: string | null; + blockType: 'textRequired'; + } + | NumberBlock + )[] + | null; + id?: string | null; + blockName?: string | null; + blockType: 'subBlocks'; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "TabsBlock". + */ +export interface TabsBlock { + textInCollapsible?: string | null; + textInRow?: string | null; + id?: string | null; + blockName?: string | null; + blockType: 'tabs'; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "localizedContentBlock". @@ -948,12 +778,7 @@ export interface LocalizedSubBlocksBlock { blockName?: string | null; blockType: 'textRequired'; } - | { - number: number; - id?: string | null; - blockName?: string | null; - blockType: 'number'; - } + | NumberBlock )[] | null; id?: string | null; @@ -988,6 +813,7 @@ export interface TextField { */ disabledTextField?: string | null; localizedText?: string | null; + localizedRequiredText: string; /** * en description */ @@ -1830,19 +1656,7 @@ export interface TabsField { text: string; id?: string | null; }[]; - blocks: ( - | ContentBlock - | WithIconBlock - | NoBlockname - | { - number: number; - id?: string | null; - blockName?: string | null; - blockType: 'number'; - } - | SubBlocksBlock - | TabsBlock - )[]; + blocks: (ContentBlock | WithIconBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[]; group: { number: number; }; @@ -1895,82 +1709,6 @@ export interface TabsField { updatedAt: string; createdAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "ContentBlock". - */ -export interface ContentBlock { - text: string; - richText?: - | { - [k: string]: unknown; - }[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'content'; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "WithIconBlock". - */ -export interface WithIconBlock { - title: string; - id?: string | null; - blockName?: string | null; - blockType: 'withIcon'; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "NoBlockname". - */ -export interface NoBlockname { - text?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'noBlockname'; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "SubBlocksBlock". - */ -export interface SubBlocksBlock { - subBlocks?: - | ( - | { - text: string; - id?: string | null; - blockName?: string | null; - blockType: 'textRequired'; - } - | NumberBlock - )[] - | null; - id?: string | null; - blockName?: string | null; - blockType: 'subBlocks'; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "NumberBlock". - */ -export interface NumberBlock { - number: number; - id?: string | null; - blockName?: string | null; - blockType: 'number'; -} -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "TabsBlock". - */ -export interface TabsBlock { - textInCollapsible?: string | null; - textInRow?: string | null; - id?: string | null; - blockName?: string | null; - blockType: 'tabs'; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "TabWithName". @@ -3764,6 +3502,7 @@ export interface TextFieldsSelect { adminHiddenTextField?: T; disabledTextField?: T; localizedText?: T; + localizedRequiredText?: T; i18nText?: T; defaultString?: T; defaultEmptyString?: T; diff --git a/test/i18n/payload-types.ts b/test/i18n/payload-types.ts index c7c100ca534..649178bce73 100644 --- a/test/i18n/payload-types.ts +++ b/test/i18n/payload-types.ts @@ -94,6 +94,9 @@ export interface Config { global: GlobalSelect | GlobalSelect; }; locale: null; + widgets: { + collections: CollectionsWidget; + }; user: User; jobs: { tasks: unknown; @@ -328,6 +331,16 @@ export interface GlobalSelect { createdAt?: T; globalType?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "collections_widget". + */ +export interface CollectionsWidget { + data?: { + [k: string]: unknown; + }; + width: 'full'; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "auth". diff --git a/test/lexical-mdx/config.ts b/test/lexical-mdx/config.ts index 4f027d2ccde..25b60342189 100644 --- a/test/lexical-mdx/config.ts +++ b/test/lexical-mdx/config.ts @@ -5,6 +5,7 @@ import path from 'path' import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js' import { devUser } from '../credentials.js' +import { serverURL } from '../__helpers/shared/serverURL.js' import { MediaCollection } from './collections/Media/index.js' import { PostsCollection } from './collections/Posts/index.js' import { docsBasePath } from './collections/Posts/shared.js' @@ -32,7 +33,7 @@ export default buildConfigWithDefaults({ }, }, editor: lexicalEditor({}), - cors: ['http://localhost:3000', 'http://localhost:3001'], + cors: [serverURL, 'http://localhost:3001'], globals: [], onInit: async (payload) => { await payload.create({ diff --git a/test/lexical/collections/Lexical/e2e/blocks/e2e.spec.ts b/test/lexical/collections/Lexical/e2e/blocks/e2e.spec.ts index 8ac05b8e949..e8067f4bad0 100644 --- a/test/lexical/collections/Lexical/e2e/blocks/e2e.spec.ts +++ b/test/lexical/collections/Lexical/e2e/blocks/e2e.spec.ts @@ -1274,7 +1274,7 @@ describe('lexicalBlocks', () => { test('ensure nested lexical field displays field label and description', async () => { // Previously, we had the issue that nested lexical fields did not display the field label and description, as // their client field configs were generated incorrectly on the server. - await page.goto('http://localhost:3000/admin/collections/LexicalInBlock?limit=10') + await page.goto(`${serverURL}/admin/collections/LexicalInBlock?limit=10`) // Wait for table to be fully loaded await expect(page.locator('tbody tr')).not.toHaveCount(0) @@ -1326,7 +1326,7 @@ describe('lexicalBlocks', () => { }) test('ensure individual inline blocks in lexical editor within a block have initial state on initial load', async () => { - await page.goto('http://localhost:3000/admin/collections/LexicalInBlock?limit=10') + await page.goto(`${serverURL}/admin/collections/LexicalInBlock?limit=10`) // Wait for table to be fully loaded await expect(page.locator('tbody tr')).not.toHaveCount(0) @@ -1488,7 +1488,7 @@ describe('lexicalBlocks', () => { }) test('ensure inline blocks restore their state after undoing a removal', async () => { - await page.goto('http://localhost:3000/admin/collections/LexicalInBlock?limit=10') + await page.goto(`${serverURL}/admin/collections/LexicalInBlock?limit=10`) // Wait for table to be fully loaded await expect(page.locator('tbody tr')).not.toHaveCount(0) diff --git a/test/lexical/collections/Lexical/e2e/main/e2e.spec.ts b/test/lexical/collections/Lexical/e2e/main/e2e.spec.ts index 59e5c8e3501..c3e1303cae1 100644 --- a/test/lexical/collections/Lexical/e2e/main/e2e.spec.ts +++ b/test/lexical/collections/Lexical/e2e/main/e2e.spec.ts @@ -1455,7 +1455,7 @@ describe('lexicalMain', () => { // https://github.com/payloadcms/payload/issues/5146 test('Preserve indent and text-align when converting Lexical <-> HTML', async () => { - await page.goto('http://localhost:3000/admin/collections/rich-text-fields?limit=10') + await page.goto(`${serverURL}/admin/collections/rich-text-fields?limit=10`) await expect(page.locator('tbody tr').first()).toBeVisible() @@ -1491,7 +1491,7 @@ describe('lexicalMain', () => { test('ensure lexical fields in blocks have correct value when moving blocks', async () => { // Previously, we had the issue that the lexical field values did not update when moving blocks, as the MOVE_ROW form action did not request // re-rendering of server components - await page.goto('http://localhost:3000/admin/collections/LexicalInBlock?limit=10') + await page.goto(`${serverURL}/admin/collections/LexicalInBlock?limit=10`) // Wait for table to be fully loaded await expect(page.locator('tbody tr')).not.toHaveCount(0) diff --git a/test/live-preview/app/live-preview/_api/serverURL.ts b/test/live-preview/app/live-preview/_api/serverURL.ts index 84de9f1ec38..5eaa2afe76b 100644 --- a/test/live-preview/app/live-preview/_api/serverURL.ts +++ b/test/live-preview/app/live-preview/_api/serverURL.ts @@ -1 +1,3 @@ -export const PAYLOAD_SERVER_URL = 'http://localhost:3000' +import { serverURL } from '../../../../__helpers/shared/serverURL.js' + +export const PAYLOAD_SERVER_URL = serverURL diff --git a/test/live-preview/collections/CollectionLevelConfig.ts b/test/live-preview/collections/CollectionLevelConfig.ts index 784a4886c04..a011ef82e82 100644 --- a/test/live-preview/collections/CollectionLevelConfig.ts +++ b/test/live-preview/collections/CollectionLevelConfig.ts @@ -1,5 +1,6 @@ import type { CollectionConfig } from 'payload' +import { serverURL } from '../../__helpers/shared/serverURL.js' import { collectionLevelConfigSlug } from '../shared.js' export const CollectionLevelConfig: CollectionConfig = { @@ -8,7 +9,7 @@ export const CollectionLevelConfig: CollectionConfig = { description: "Live Preview is enabled on this collection's own config, not the root config.", useAsTitle: 'title', livePreview: { - url: 'http://localhost:3000/live-preview', + url: `${serverURL}/live-preview`, }, }, access: { diff --git a/test/live-preview/config.ts b/test/live-preview/config.ts index 3d5cadd3cb4..4c16d5a590f 100644 --- a/test/live-preview/config.ts +++ b/test/live-preview/config.ts @@ -3,6 +3,7 @@ import path from 'path' const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js' +import { serverURL } from '../__helpers/shared/serverURL.js' import { MediaBlock } from './blocks/MediaBlock/index.js' import { Categories } from './collections/Categories.js' import { CollectionLevelConfig } from './collections/CollectionLevelConfig.js' @@ -54,8 +55,8 @@ export default buildConfigWithDefaults({ globals: ['header', 'footer'], }, }, - cors: ['http://localhost:3000', 'http://localhost:3001'], - csrf: ['http://localhost:3000', 'http://localhost:3001'], + cors: [serverURL, 'http://localhost:3001'], + csrf: [serverURL, 'http://localhost:3001'], collections: [ Users, Pages, diff --git a/test/live-preview/int.spec.ts b/test/live-preview/int.spec.ts index 92d84508db1..9530dc2e503 100644 --- a/test/live-preview/int.spec.ts +++ b/test/live-preview/int.spec.ts @@ -11,6 +11,7 @@ import { fileURLToPath } from 'url' import { afterAll, beforeAll, describe, expect, it } from 'vitest' import type { NextRESTClient } from '../__helpers/shared/NextRESTClient.js' +import { serverURL } from '../__helpers/shared/serverURL.js' import type { Media, Page, Post, Tenant } from './payload-types.js' import { pagesSlug, postsSlug, tenantsSlug } from './shared.js' @@ -40,8 +41,6 @@ const requestHandler: CollectionPopulationRequestHandler = ({ data, endpoint }) } describe('Collections - Live Preview', () => { - const serverURL: string = 'http://localhost:3000' - let testPost: Post let tenant: Tenant let media: Media @@ -80,7 +79,7 @@ describe('Collections - Live Preview', () => { collection: tenantsSlug, data: { title: 'Tenant 1', - clientURL: 'http://localhost:3000', + clientURL: serverURL, }, }) diff --git a/test/live-preview/prod/app/live-preview/_api/serverURL.ts b/test/live-preview/prod/app/live-preview/_api/serverURL.ts index 84de9f1ec38..deff6fcb335 100644 --- a/test/live-preview/prod/app/live-preview/_api/serverURL.ts +++ b/test/live-preview/prod/app/live-preview/_api/serverURL.ts @@ -1 +1,4 @@ -export const PAYLOAD_SERVER_URL = 'http://localhost:3000' +// Injected by next.config.mjs from the shared serverURL helper. +// The prod build cannot resolve imports outside the app directory, +// so we read the value via NEXT_PUBLIC_ env var instead. +export const PAYLOAD_SERVER_URL: string = process.env.NEXT_PUBLIC_SERVER_URL! diff --git a/test/live-preview/prod/next.config.mjs b/test/live-preview/prod/next.config.mjs index f2e6bedd514..5900bd69c3b 100644 --- a/test/live-preview/prod/next.config.mjs +++ b/test/live-preview/prod/next.config.mjs @@ -3,6 +3,8 @@ import nextConfig from '../../../next.config.mjs' import path from 'path' import { fileURLToPath } from 'url' +import { serverURL } from '../../__helpers/shared/serverURL.js' + const __filename = fileURLToPath(import.meta.url) const dirname = path.dirname(__filename) @@ -11,5 +13,6 @@ export default { env: { PAYLOAD_CORE_DEV: 'true', ROOT_DIR: path.resolve(dirname), + NEXT_PUBLIC_SERVER_URL: serverURL, }, } diff --git a/test/live-preview/seed/tenant-1.ts b/test/live-preview/seed/tenant-1.ts index fcf5866953d..b3fccf78838 100644 --- a/test/live-preview/seed/tenant-1.ts +++ b/test/live-preview/seed/tenant-1.ts @@ -1,6 +1,8 @@ import type { Tenant } from '../payload-types.js' +import { serverURL } from '../../__helpers/shared/serverURL.js' + export const tenant1: Omit = { title: 'Tenant 1', - clientURL: 'http://localhost:3000', + clientURL: serverURL, } diff --git a/test/plugin-cloud-storage/.env.emulated b/test/plugin-cloud-storage/.env.emulated index 733abcbf147..3e888e02483 100644 --- a/test/plugin-cloud-storage/.env.emulated +++ b/test/plugin-cloud-storage/.env.emulated @@ -1,6 +1,6 @@ # Sample creds for working locally with docker-compose -PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000 +PAYLOAD_PUBLIC_SERVER_URL=http://payload-monorepo.localhost:1355 PAYLOAD_SECRET=45ligj345ligj4wl5igj4lw5igj45ligj45wlijl PAYLOAD_CONFIG_PATH=config.ts diff --git a/test/plugin-ecommerce/app/components/CheckoutStripe.tsx b/test/plugin-ecommerce/app/components/CheckoutStripe.tsx index 321d71f509c..f7755e6e4f7 100644 --- a/test/plugin-ecommerce/app/components/CheckoutStripe.tsx +++ b/test/plugin-ecommerce/app/components/CheckoutStripe.tsx @@ -19,7 +19,7 @@ export const CheckoutStripe = () => { //`Elements` instance that was used to create the Payment Element elements, confirmParams: { - return_url: 'http://localhost:3000/shop/confirm-order', + return_url: `${window.location.origin}/shop/confirm-order`, }, }) diff --git a/test/plugin-import-export/.env.emulated b/test/plugin-import-export/.env.emulated index 733abcbf147..3e888e02483 100644 --- a/test/plugin-import-export/.env.emulated +++ b/test/plugin-import-export/.env.emulated @@ -1,6 +1,6 @@ # Sample creds for working locally with docker-compose -PAYLOAD_PUBLIC_SERVER_URL=http://localhost:3000 +PAYLOAD_PUBLIC_SERVER_URL=http://payload-monorepo.localhost:1355 PAYLOAD_SECRET=45ligj345ligj4wl5igj4lw5igj45ligj45wlijl PAYLOAD_CONFIG_PATH=config.ts diff --git a/test/plugin-sentry/TestErrors.tsx b/test/plugin-sentry/TestErrors.tsx index a2448087199..adfac6c221a 100644 --- a/test/plugin-sentry/TestErrors.tsx +++ b/test/plugin-sentry/TestErrors.tsx @@ -6,13 +6,13 @@ export const TestErrors = () => { const [throwClientSide, setThrowClientSide] = useState(false) const notFound = async () => { - const req = await fetch('http://localhost:3000/api/users/notFound', { + const req = await fetch('/api/users/notFound', { method: 'GET', }) } const cannotCreate = async () => { - const req = await fetch('http://localhost:3000/api/posts', { + const req = await fetch('/api/posts', { body: JSON.stringify({ text: 'New post', }), @@ -21,7 +21,7 @@ export const TestErrors = () => { } const badLogin = async () => { - const req = await fetch('http://localhost:3000/api/users/login', { + const req = await fetch('/api/users/login', { body: JSON.stringify({ email: 'sorry@whoareyou.com', password: '123456', @@ -31,7 +31,7 @@ export const TestErrors = () => { } const badReq = async () => { - const req = await fetch('http://localhost:3000/api/users/forgot-password', { + const req = await fetch('/api/users/forgot-password', { credentials: 'include', headers: { 'Content-Type': 'application/json', @@ -41,7 +41,7 @@ export const TestErrors = () => { } const badReset = async () => { - const req = await fetch('http://localhost:3000/api/users/reset-password', { + const req = await fetch('/api/users/reset-password', { body: JSON.stringify({ password: 'newPassword', token: '7eac3830ffcfc7f9f66c00315dabeb11575dba91', @@ -55,7 +55,7 @@ export const TestErrors = () => { } const badVerify = async () => { - const req = await fetch('http://localhost:3000/api/users/unlock', { + const req = await fetch('/api/users/unlock', { headers: { 'Content-Type': 'application/json', }, diff --git a/test/runE2E.ts b/test/runE2E.ts index ca3a70039f5..feb1eb49b70 100644 --- a/test/runE2E.ts +++ b/test/runE2E.ts @@ -1,7 +1,6 @@ import { spawn } from 'child_process' import globby from 'globby' import minimist from 'minimist' -import { createServer } from 'net' import path from 'path' import shelljs from 'shelljs' import slash from 'slash' @@ -161,17 +160,16 @@ async function executePlaywright( process.env.START_MEMORY_DB = 'true' - const portInUse = await new Promise((resolve) => { - const server = createServer() - server.once('error', () => resolve(true)) - server.once('listening', () => server.close(() => resolve(false))) - server.listen(3000) - }) + const { serverURL } = await import('./__helpers/shared/serverURL.js') + + const serverAlreadyRunning = await fetch(serverURL) + .then((res) => res.status !== 404) + .catch(() => false) let child: ReturnType | undefined - if (portInUse) { - console.log('Port 3000 is already in use — reusing existing dev server.') + if (serverAlreadyRunning) { + console.log(`Dev server already running at ${serverURL} — reusing.`) } else { child = spawn('pnpm', spawnDevArgs, { cwd: path.resolve(dirname, '..'), diff --git a/test/select/getConfig.ts b/test/select/getConfig.ts index dd11deece3b..dace499b7a4 100644 --- a/test/select/getConfig.ts +++ b/test/select/getConfig.ts @@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url' import path from 'path' import { devUser } from '../credentials.js' +import { serverURL } from '../__helpers/shared/serverURL.js' import { CustomID } from './collections/CustomID/index.js' import { DeepPostsCollection } from './collections/DeepPosts/index.js' import { ForceSelect } from './collections/ForceSelect/index.js' @@ -120,7 +121,7 @@ export const getConfig: () => Partial = () => ({ editor: lexicalEditor({ features: ({ defaultFeatures }) => [...defaultFeatures], }), - cors: ['http://localhost:3000', 'http://localhost:3001'], + cors: [serverURL, 'http://localhost:3001'], onInit: async (payload) => { await payload.create({ collection: 'users', diff --git a/test/server-url/config.ts b/test/server-url/config.ts index 03c5a2f90a6..c385b09f8fc 100644 --- a/test/server-url/config.ts +++ b/test/server-url/config.ts @@ -3,13 +3,14 @@ import { fileURLToPath } from 'node:url' import path from 'path' import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js' +import { serverURL } from '../__helpers/shared/serverURL.js' import { devUser } from '../credentials.js' const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) export default buildConfigWithDefaults({ - serverURL: 'http://localhost:3000', + serverURL, admin: { autoLogin: false, importMap: { diff --git a/test/sort/config.ts b/test/sort/config.ts index d183fa14232..604d3a21140 100644 --- a/test/sort/config.ts +++ b/test/sort/config.ts @@ -2,6 +2,7 @@ import { fileURLToPath } from 'node:url' import path from 'path' import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js' +import { serverURL } from '../__helpers/shared/serverURL.js' import { DefaultSortCollection } from './collections/DefaultSort/index.js' import { DraftsCollection } from './collections/Drafts/index.js' import { LocalizedCollection } from './collections/Localized/index.js' @@ -43,7 +44,7 @@ export default buildConfigWithDefaults({ }, }, ], - cors: ['http://localhost:3000', 'http://localhost:3001'], + cors: [serverURL, 'http://localhost:3001'], localization: { locales: ['en', 'nb'], defaultLocale: 'en', diff --git a/test/uploads/config.ts b/test/uploads/config.ts index c81740352bc..50838f24bd5 100644 --- a/test/uploads/config.ts +++ b/test/uploads/config.ts @@ -502,6 +502,7 @@ export default buildConfigWithDefaults({ allowList: [ { protocol: 'http', hostname: '127.0.0.1', port: '', search: '' }, { protocol: 'http', hostname: 'localhost', port: '', search: '' }, + { protocol: 'http', hostname: 'payload-monorepo.localhost', port: '', search: '' }, { protocol: 'http', hostname: '[::1]', port: '', search: '' }, { protocol: 'http', hostname: '10.0.0.1', port: '', search: '' }, { protocol: 'http', hostname: '192.168.1.1', port: '', search: '' }, @@ -784,7 +785,7 @@ export default buildConfigWithDefaults({ slug: 'externally-served-media', fields: [], upload: { - // Either use another web server like `npx serve -l 4000` (http://localhost:4000) or use the static server from the previous collection to serve the media folder (http://localhost:3000/media) + // Either use another web server like `npx serve -l 4000` (http://localhost:4000) or use the static server from the previous collection to serve the media folder (http://payload-monorepo.localhost:1355/media) staticDir: path.resolve(dirname, './media'), }, }, diff --git a/tsconfig.base.json b/tsconfig.base.json index 5cf62cb893f..5994c9dfc84 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -32,7 +32,7 @@ ], "paths": { "@payloadcms/figma": ["../enterprise-plugins/packages/figma/src/index.ts"], - "@payload-config": ["./test/evals/config.ts"], + "@payload-config": ["./test/i18n/config.ts"], "@payloadcms/admin-bar": ["./packages/admin-bar/src"], "@payloadcms/live-preview": ["./packages/live-preview/src"], "@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],