Skip to content

fix(nextly): stop a custom user field from displacing a built-in one#166

Merged
mobeenabdullah merged 7 commits into
mainfrom
fix/user-field-guards
Jul 16, 2026
Merged

fix(nextly): stop a custom user field from displacing a built-in one#166
mobeenabdullah merged 7 commits into
mainfrom
fix/user-field-guards

Conversation

@mobeenabdullah

@mobeenabdullah mobeenabdullah commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

A custom user field's name becomes a database column and a key on the user object. The built-ins are assigned first and the custom fields over them, so the custom value wins. That makes a field named email replace the real address, and one named id replace the identity authenticate hands to session creation.

The merged Zod schema has the same precedence. Proven rather than reasoned:

baseline (no custom fields)   accepts "not-an-email"?   false
with a custom `email` field   accepts "not-an-email"?   true
with a custom `email` field   accepts NO email at all?  true

A custom text field named email turns the built-in z.string().email() into a bare optional string.

Password checking is not affected — the hash comes from a separate, column-scoped query that custom fields cannot reach, and AuthService.verifyCredentials never touches the merge. What is affected is the identity of the object returned after the credential check, which is what createSession is given.

defineConfig() has always refused these names. Nothing else did: the admin dispatcher, POST /api/user-fields and PATCH /api/user-fields/:id all reach the same service, and it checked only that the field was not code-sourced. The only reserved-name check on that path was in the browser, gated on mode === "create" — with a comment claiming the name was immutable in edit mode, which was false.

What changed

The name check moved into one place both callers share. checkUserFieldName is now used by defineConfig() and by the service, so the two lists cannot drift. The service applies it to every create, and the reason reaches the client as a field error rather than a generic failure.

Name and type are fixed at creation. Both identify the backing column, and the reconciler only ever adds columns — a rename strands the old column and its data, and a type change leaves the column as it was. Values echoed back unchanged are still accepted, so a client sending a whole field back does not have to special-case two keys. The form matches the server and says why, rather than going quietly grey.

A row written before the check is no longer honoured. loadMergedFields drops a stored field whose name would displace a built-in and logs which one. One place covers the column list, the select and both schemas at once. The row is left alone so the name can be recovered by hand.

Browser tests

Added, and running in CI — 14 tests, ~22 seconds, against a real server and a real database.

They cover the class of defect the other two layers structurally cannot: jsdom has no layout engine, so getBoundingClientRect() returns zeros whether a bug is present or not, and it does not cascade CSS. The two defects that reached a person's eyes in this admin — a column at 1024px and control outlines at 1.35:1 — were invisible to the ~3,800 tests that already existed.

Verified by reintroducing the regression:

Error: Name column is 1024px; the cap is 280px

The suite runs the playground on its own port against its own throwaway sqlite file, emptied before each run, so a test may delete things: nothing in it was ever wanted. It also takes its own distDir, because a separate port is not enough — two next dev processes on one app fight over .next. A contributor's dev server on 3000 keeps running beside it.

It lives at the repo root because it tests neither a package nor an app but the composed product. packages/ is what ships, apps/ is what runs; the workspace glob and the lint filter name ./e2e explicitly.

Verification

Every fix is pinned by a test that fails without it — 34 for the guards (24 unit, 10 merge-boundary), 17 integration, 14 browser.

suite before after
nextly unit 410 failed / 2929 passed 410 failed / 2968 passed
nextly integration 3 failed / 115 passed 3 failed / 132 passed
admin unit 37 failed / 835 passed 37 failed / 835 passed

Failures unchanged everywhere; passes up by exactly the new tests. Typecheck, lint and prettier clean.

Upgrade note

Two behaviour changes worth reading before merging:

  • A stored field whose name displaces a built-in stops being applied on the next boot. Its data disappears from users until the field is renamed. It was displacing a built-in rather than sitting beside it, but it is a change.
  • Renaming a field, or changing its type, now fails where it previously appeared to succeed. It never worked — it stranded the column — but a client relying on the silent no-op will now see a 400.

Not in scope

minLength/maxLength/min/max are read by the validator and stored by nothing, so a constraint declared in defineConfig() is silently dropped — including for code-first fields, where the public API documents it. That is a schema change and a broken published promise rather than the UI gap it was filed as, and it needs its own decisions. Written up separately.

Summary by CodeRabbit

  • New Features
    • Added an end-to-end browser test suite covering admin smoke, seeded roles, permissions/checkbox UI accessibility, and custom user-field create/edit flows.
  • Bug Fixes
    • Enforced consistent validation for reserved/invalid user-field names and unsupported types.
    • Prevented changing a user field’s identity details (name/type) after creation; UI now locks identity controls in edit/code-sourced scenarios.
  • Documentation
    • Added guidance for installing, running, and authoring the end-to-end suite.
  • Chores
    • Expanded CI linting and introduced a dedicated end-to-end test job that publishes the HTML report.

A custom user field's name becomes a `user_ext` column and a key on the
user object. The built-ins are assigned first and the custom fields over
them, so a field named `email` replaces the real address, and one named
`id` replaces the identity that `authenticate` hands to session creation.
The merged Zod schema has the same precedence: a custom `email` of type
text turns the built-in `z.string().email()` into a bare optional string,
so a user can be created with no address at all.

`defineConfig()` has always refused these names. Nothing else did: the
admin dispatcher, `POST /api/user-fields` and `PATCH /api/user-fields/:id`
all reach the service, and the service checked only that the field was not
code-sourced. The one reserved-name check on that path was in the browser,
and skipped in edit mode.

The name check moves into a `checkUserFieldName` that `defineConfig()` and
the service now share, so the two cannot drift, and the service applies it
to every create. Name and type are also fixed at creation: both identify
the column, and the reconciler only adds columns, so a rename strands the
old column and its data while a type change leaves the column as it was.
Values echoed back unchanged are still accepted.

A row written before this check must not be honoured either, so
`loadMergedFields` drops one whose name would displace a built-in and logs
which field it dropped. That covers the column list, the select and both
schemas at once. The row is left in place so the name can be recovered.
Adds the layer the other two structurally cannot reach. jsdom has no
layout engine, so `getBoundingClientRect()` returns zeros and computed
styles are never cascaded — which makes a whole class of defect not
untested but untestable there. Two that shipped are the argument: the
permissions matrix Name column reaching 1024px, and control outlines at
1.35:1 and 1.14:1 against a 3:1 requirement. Both were invisible to the
3,800 tests that already existed, and both are now measured.

Fourteen tests, in about twenty seconds. Deliberately small: it only
earns its place while people still run it, so anything expressible in
Vitest belongs in Vitest.

**It runs in CI.** That is the point rather than a detail — the repo
already contains two Playwright specs written in July that have never
run once, because nothing installed the runner and nothing invoked
them, and their package's tsconfig excluded the folder so the broken
import typechecked clean for months. A harness nobody runs is worse than
none, because it reads as coverage.

**It owns its database.** The suite runs the playground on port 3100
against `data/e2e.db`, emptied before the server opens it. That is why a
test here may delete things: nothing in it was ever wanted. A separate
port alone is not enough — two `next dev` processes on one app fight
over `.next`, so the suite gets its own `distDir` and a contributor's
dev server on 3000 keeps running beside it.

It lives at the repo root because it tests neither a package nor an app,
but the composed product: playground, admin, core and plugins together.
`packages/` is what ships and `apps/` is what runs; the workspace glob
and the lint filter name it explicitly instead.

Three things worth knowing, each of which cost a wrong guess first:
dev auto-login issues no session for `127.0.0.1`, so the admin renders
an empty shell forever and every test times out; the theme class lands
on `.nextly-admin.dark` rather than `html.dark`, so setting the latter
by hand measures nothing; and console errors are not a failure signal,
because dev mode reports its own HMR socket and the dashboard
feature-detects seeding with a HEAD it expects to fail. The suite
asserts on 5xx responses and uncaught exceptions, which mean something.
One changeset for the whole PR, all 18 packages at patch, written from
what changes for someone using Nextly rather than from the diff.

Leads with the two behaviour changes an upgrade can be felt through: a
stored field whose name displaces a built-in stops being applied, and a
field's name and type are fixed once it exists.
@github-actions github-actions Bot added scope: core nextly scope: admin @nextlyhq/admin type: docs Documentation only dependencies Dependency updates (label applied by Dependabot) labels Jul 16, 2026
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 50616cdd-911a-4fda-aa38-bab7129052fd

📥 Commits

Reviewing files that changed from the base of the PR and between f57413c and b3ac245.

📒 Files selected for processing (1)
  • e2e/tests/user-fields.spec.ts

📝 Walkthrough

Walkthrough

This PR centralizes user-field validation, enforces immutable field identity, adds admin UI locks, and introduces an isolated Playwright E2E workspace with browser coverage and CI execution.

Changes

User Field Guardrails

Layer / File(s) Summary
Validation contracts and service guards
packages/nextly/src/users/config/..., packages/nextly/src/domains/users/services/...
Shared validators reject reserved, invalid, or unsupported values; services enforce creation checks and immutable name/type identities with unit and integration coverage.
Merged-field filtering and warnings
packages/nextly/src/domains/users/services/..., packages/nextly/src/di/registrations/register-users.ts
Merged fields with unusable names are skipped and logged, and generated SQL excludes them.
Admin identity locks
packages/admin/src/components/features/settings/UserFieldForm/...
Existing-field name and type controls are disabled, with updated UI explanations and create/edit validation behavior.

Browser E2E Pipeline

Layer / File(s) Summary
E2E workspace and isolated runtime
e2e/..., apps/playground/next.config.ts, .github/workflows/ci.yml, pnpm-workspace.yaml, turbo.jsonc, .gitignore
Playwright configuration, authenticated setup, isolated SQLite and Next output directories, database reset behavior, CI execution, reporting, and documentation are added.
Admin smoke and accessibility coverage
e2e/tests/admin-smoke.spec.ts, e2e/tests/permissions-matrix.spec.ts, e2e/tests/support/admin.ts
Browser tests cover admin route rendering, seeded roles, permissions-table geometry, checkbox hit targets, and light/dark contrast.
User-field browser coverage
e2e/tests/user-fields.spec.ts
Browser tests cover field creation, reserved-name errors, immutable identity controls, renaming rejection, and label-only updates.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CI
  participant Turbo
  participant Playwright
  participant E2E_DB
  participant Playground
  CI->>Turbo: Run test:e2e
  Turbo->>Playwright: Start browser tests
  Playwright->>E2E_DB: Reset e2e SQLite files
  Playwright->>Playground: Start isolated dev server
  Playground-->>Playwright: Health endpoint ready
  Playwright->>Playground: Visit /admin
  Playground-->>Playwright: Return authenticated session cookie
  Playwright->>Playground: Run admin and user-field tests
Loading

Suggested labels: scope: ui

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed, but it does not follow the required template sections like Type of change, Changeset, or Checklist. Add the missing template sections and required checkboxes, especially Type of change, Related issues, Changeset, Test plan, Checklist, and optional screenshots/notes.
Docstring Coverage ⚠️ Warning Docstring coverage is 56.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: preventing custom user fields from overriding built-in ones.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/user-field-guards

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/nextly/src/domains/users/services/user-field-definition-service.ts (1)

291-301: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Stale docstring contradicts the new immutability guard.

The comment Note: source cannot be changed after creation, but name can be updated. is now incorrect — assertIdentityUnchanged (called at Line 320) rejects any name or type change. Update the note to reflect that both are now fixed at creation.

📝 Proposed fix
-   * Note: `source` cannot be changed after creation, but `name` can be updated.
+   * Note: `source`, `name`, and `type` cannot be changed after creation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/nextly/src/domains/users/services/user-field-definition-service.ts`
around lines 291 - 301, Update the docstring for the existing user field
definition update method to state that both name and type are immutable after
creation, matching the behavior enforced by assertIdentityUnchanged. Remove the
outdated claim that name can be updated while preserving the source immutability
note.
🧹 Nitpick comments (2)
e2e/tests/user-fields.spec.ts (1)

26-26: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Provision the field independently for each test.

The suite explicitly depends on an earlier test creating job_title. Running a test alone—or losing the creation test—causes unrelated guardrail coverage to fail or be skipped. Create an idempotent fixture in setup and clean it afterward.

Also applies to: 102-115

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/tests/user-fields.spec.ts` at line 26, Update the user-fields test suite
setup around test.describe.configure and the affected tests so each test
independently provisions the job_title field through an idempotent fixture.
Remove reliance on an earlier test’s creation, ensure the fixture runs before
every test, and clean up the provisioned field afterward.
e2e/global-setup.ts (1)

8-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated storageState path definition. The session file path is defined twice: once as the exported STORAGE_STATE constant, and again as a hardcoded literal in the project config. Consolidate to a single source of truth.

  • e2e/playwright.config.ts#L64-73: import STORAGE_STATE from ./global-setup and use it instead of the literal "./.playwright/session.json".
  • e2e/global-setup.ts#L8: keep this as the canonical export; no change needed here beyond being imported by the config.
Suggested fix
+import { STORAGE_STATE } from "./global-setup";
+
 export default defineConfig({
   ...
   projects: [
     {
       name: "chromium",
       use: {
         ...devices["Desktop Chrome"],
-        storageState: "./.playwright/session.json",
+        storageState: STORAGE_STATE,
       },
     },
   ],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/global-setup.ts` at line 8, Use the exported STORAGE_STATE constant from
global-setup as the single source of truth: import it into the Playwright
configuration and replace the hardcoded session path in the configuration’s
storageState setting. Keep the existing STORAGE_STATE definition unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 122-124: Update the e2e job’s Checkout step using
actions/checkout@v7 to set persist-credentials to false, leaving the rest of the
job unchanged.

In `@e2e/playwright.config.ts`:
- Around line 43-45: Update the workers setting in the Playwright configuration
to use exactly one worker for local runs as well as CI, while preserving the
existing forbidOnly and fullyParallel settings.

In `@e2e/tests/user-fields.spec.ts`:
- Around line 117-120: Update the rename assertion around the PATCH request and
renamed response to validate the 400 error payload, asserting path is "name" and
code is "USER_FIELD_NAME_IMMUTABLE", matching the reserved-name assertions
above.

In `@e2e/tsconfig.json`:
- Around line 5-12: Update the e2e TypeScript configuration near the existing
"include" entries to enable allowJs, ensuring the included
e2e/scripts/reset-e2e-db.mjs file is type-checked by check-types while
preserving the current TypeScript and module settings.

In
`@packages/admin/src/components/features/settings/UserFieldForm/schemas/userFieldSchema.ts`:
- Around line 187-190: Update the edit-mode validation around the user field
name schema so immutable names bypass all name validation, including the base
regex, while preserving the existing create-mode checks and reserved-name
refinement. Use a mode-specific schema or compare the submitted name with its
original value to allow backend-valid stored names such as camelCase during
edits.

In `@turbo.jsonc`:
- Around line 297-311: The test:e2e task’s ^build dependency does not build the
packages whose dist outputs the suite consumes. Update the test:e2e
configuration to explicitly build the relevant packages before running, or wire
dependencies so nextly, `@nextlyhq/admin`, and playground builds are included;
preserve the uncached behavior and existing environment settings.

---

Outside diff comments:
In `@packages/nextly/src/domains/users/services/user-field-definition-service.ts`:
- Around line 291-301: Update the docstring for the existing user field
definition update method to state that both name and type are immutable after
creation, matching the behavior enforced by assertIdentityUnchanged. Remove the
outdated claim that name can be updated while preserving the source immutability
note.

---

Nitpick comments:
In `@e2e/global-setup.ts`:
- Line 8: Use the exported STORAGE_STATE constant from global-setup as the
single source of truth: import it into the Playwright configuration and replace
the hardcoded session path in the configuration’s storageState setting. Keep the
existing STORAGE_STATE definition unchanged.

In `@e2e/tests/user-fields.spec.ts`:
- Line 26: Update the user-fields test suite setup around
test.describe.configure and the affected tests so each test independently
provisions the job_title field through an idempotent fixture. Remove reliance on
an earlier test’s creation, ensure the fixture runs before every test, and clean
up the provisioned field afterward.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 99662f3f-5572-4a66-b104-e9f2096b6e52

📥 Commits

Reviewing files that changed from the base of the PR and between 39a6f0d and 55d8eb8.

⛔ Files ignored due to path filters (2)
  • .changeset/user-field-guards-and-browser-tests.md is excluded by !.changeset/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • .github/workflows/ci.yml
  • .gitignore
  • apps/playground/next.config.ts
  • e2e/README.md
  • e2e/eslint.config.mjs
  • e2e/global-setup.ts
  • e2e/package.json
  • e2e/playwright.config.ts
  • e2e/scripts/reset-e2e-db.mjs
  • e2e/tests/admin-smoke.spec.ts
  • e2e/tests/permissions-matrix.spec.ts
  • e2e/tests/support/admin.ts
  • e2e/tests/user-fields.spec.ts
  • e2e/tsconfig.json
  • packages/admin/src/components/features/settings/UserFieldForm/UserFieldForm.tsx
  • packages/admin/src/components/features/settings/UserFieldForm/schemas/userFieldSchema.ts
  • packages/nextly/src/di/registrations/register-users.ts
  • packages/nextly/src/domains/users/services/__tests__/user-ext-reserved-names.test.ts
  • packages/nextly/src/domains/users/services/__tests__/user-field-guards.integration.test.ts
  • packages/nextly/src/domains/users/services/user-ext-schema-service.ts
  • packages/nextly/src/domains/users/services/user-field-definition-service.ts
  • packages/nextly/src/users/config/__tests__/check-user-field-name.test.ts
  • packages/nextly/src/users/config/validate-user-config.ts
  • pnpm-workspace.yaml
  • turbo.jsonc

Comment thread .github/workflows/ci.yml
Comment thread e2e/playwright.config.ts Outdated
Comment thread e2e/tests/user-fields.spec.ts
Comment thread e2e/tsconfig.json
Comment thread turbo.jsonc
Six review findings, each checked against the code before being acted on.
One was a real bug reachable by a user.

**The edit form refused to save a field with a camelCase name.** The
server accepts `/^[a-zA-Z][a-zA-Z0-9_]*$/`, which is what a
`defineConfig()` field usually looks like, but the form's name rule is
lowercase-only and ran even in edit mode — where the input is disabled
and the stored value is submitted straight back. So a field named
`phoneNumber` opened fine and then would not save a label change,
silently: the submit did nothing. The name is now validated only when
the form is inventing it, on create. Proven by a browser test that
creates such a field through the API and edits it through the form,
which fails against the previous schema.

The rest are the harness paying for its own keep:

- `workers: 1`, not just `fullyParallel: false`. The latter serialises
  only within a spec file; separate files still get separate workers,
  and both would share one database and one session. Two files that
  create fields could race. Nothing to win by parallelising a 20s suite.
- `allowJs` on the e2e tsconfig. Without it the `.mjs` reset script was
  included and then skipped — the exact silent gap the include comment
  claims to have closed. Confirmed by injecting an error and watching
  check-types catch it.
- The e2e package now declares the packages the playground loads from
  dist. `dependsOn: ["^build"]` was building only its own config deps,
  so `turbo test:e2e` could run against a stale build. CI was safe only
  by an explicit build step.
- `persist-credentials: false` on the browser-tests checkout. The job
  never pushes; leaving GITHUB_TOKEN in the git config across a pnpm
  install and a browser download is blast radius for nothing.
- The rename test now asserts the error payload — path `name`, code
  `USER_FIELD_NAME_IMMUTABLE` — so "says why" is checked, not just the
  400.
@mobeenabdullah

Copy link
Copy Markdown
Collaborator Author

All 6 findings addressed in f57413c. Each verified against the code first; one was a real user-facing bug.

# Finding Outcome
1 edit form validates a name it cannot change, rejecting camelCase Fixed — real bug, reachable by a user
2 workers unset, so files race a shared DB + session Fixed (workers: 1)
3 .mjs reset script not typechecked Fixed (allowJs), verified by injecting an error
4 rename test asserts only the 400, not the reason Fixed (asserts USER_FIELD_NAME_IMMUTABLE)
5 e2e checkout persists GITHUB_TOKEN needlessly Fixed (persist-credentials: false)
6 ^build builds config deps, not what the playground loads Fixed (e2e declares the runtime packages)

The one that mattered: the edit form ran its lowercase-only name rule in edit mode, where the input is disabled and the value round-trips untouched. The server accepts camelCase, so a field named phoneNumber — what a defineConfig() field normally looks like — opened fine and then silently failed to save a label change. The name is now validated only on create. A new browser test creates such a field through the API and edits its label through the form; it fails against the old schema (the page never leaves the edit URL).

Verification. E2E is now 15 tests (was 14, +1 for the camelCase case), all green in ~28s. allowJs confirmed by watching check-types catch an injected error in the .mjs. Admin unit baseline unchanged at 37/835. Typecheck and lint clean.

One honest note on #6: CI was never actually broken — it has an explicit build step. The fix makes turbo test:e2e correct for a local run and stops the task comment claiming a build order it did not have.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e/tests/user-fields.spec.ts`:
- Around line 126-130: Extend the update flow after the redirect assertion in
the user-fields test to verify that the label persisted as “Mobile Number.”
Re-open the updated field or query the resulting list/API and assert the saved
label, using the existing field-identification and assertion helpers rather than
only checking navigation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9d5de667-aebf-4c9e-8e74-533dc56cdeec

📥 Commits

Reviewing files that changed from the base of the PR and between 55d8eb8 and f57413c.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • .github/workflows/ci.yml
  • e2e/package.json
  • e2e/playwright.config.ts
  • e2e/tests/user-fields.spec.ts
  • e2e/tsconfig.json
  • packages/admin/src/components/features/settings/UserFieldForm/schemas/userFieldSchema.ts
  • turbo.jsonc
🚧 Files skipped from review as they are similar to previous changes (6)
  • e2e/tsconfig.json
  • turbo.jsonc
  • e2e/package.json
  • packages/admin/src/components/features/settings/UserFieldForm/schemas/userFieldSchema.ts
  • .github/workflows/ci.yml
  • e2e/playwright.config.ts

Comment thread e2e/tests/user-fields.spec.ts
The camelCase edit test proved the form navigated back to the list, which
is what the original bug did not do — but a redirect only says the submit
was not blocked, not that the PATCH wrote anything. Read the field back
and assert the label is "Mobile Number".

Confirmed it bites: with the label dropped from the update on the server,
the URL assertion still passes and this one fails, which is the gap it
closes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Dependency updates (label applied by Dependabot) scope: admin @nextlyhq/admin scope: core nextly type: docs Documentation only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant