Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughConsolidates members routing and UI from Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ab27ec9 to
bcdc536
Compare
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 23843231415 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
ee6d99d to
dbcc942
Compare
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 23847254654 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
9cd2c2e to
eadca30
Compare
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 23848656698 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 23850456209 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1c37481fb8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
apps/admin/src/routes.tsx
Outdated
| path: "/members-forward", | ||
| // TODO: Remove once the legacy Ember members list is deleted. | ||
| handle: emberFallbackHandle, | ||
| loader: () => redirect("/members") |
There was a problem hiding this comment.
Preserve query params on members-forward redirect
The compatibility route for /members-forward now uses redirect('/members'), which drops the existing query string. Any bookmarked/shared URL like /members-forward?filter=label:VIP or /members-forward?search=alice lands on an unfiltered members list, so old deep links no longer reproduce the same state after this migration. Build the redirect target from the incoming request URL so search is carried over.
Useful? React with 👍 / 👎.
| this.searchInput = page.locator([ | ||
| 'input[aria-label="Search members"]:visible', | ||
| 'input[aria-label="Search members mobile"]:visible' | ||
| ].join(', ')).first(); |
There was a problem hiding this comment.
Use semantic locator for members search input
This introduces a CSS selector-based locator (page.locator('input[...]')) for the members search field, which violates the explicit test rule in e2e/AGENTS.md (“Never use CSS/XPath selectors”) and is more brittle than role/label semantics. As the DOM structure or visibility behavior changes, this can silently target the wrong input and create flaky E2E behavior; prefer semantic Playwright locators here.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/posts/test/unit/views/members/import-members/modal.test.tsx (1)
56-74:⚠️ Potential issue | 🟠 MajorRestore the
URLoverrides inafterEach.
Object.defineProperty()mutates the globalURLobject directly, andvi.unstubAllGlobals()only restores globals modified byvi.stubGlobal(), not properties mutated withObject.defineProperty(). These changes will leak into subsequent tests and create order-dependent behavior.🔧 Suggested adjustment
describe('ImportMembersModal', () => { + let createObjectURLDescriptor: PropertyDescriptor | undefined; + let revokeObjectURLDescriptor: PropertyDescriptor | undefined; + beforeEach(() => { vi.stubGlobal('FileReader', MockFileReader); vi.stubGlobal('fetch', vi.fn(async () => new Response(null, {status: 202}))); + createObjectURLDescriptor = Object.getOwnPropertyDescriptor(URL, 'createObjectURL'); + revokeObjectURLDescriptor = Object.getOwnPropertyDescriptor(URL, 'revokeObjectURL'); Object.defineProperty(URL, 'createObjectURL', { configurable: true, writable: true, value: vi.fn(() => 'blob:mock/0') }); @@ afterEach(() => { + if (createObjectURLDescriptor) { + Object.defineProperty(URL, 'createObjectURL', createObjectURLDescriptor); + } else { + Reflect.deleteProperty(URL, 'createObjectURL'); + } + + if (revokeObjectURLDescriptor) { + Object.defineProperty(URL, 'revokeObjectURL', revokeObjectURLDescriptor); + } else { + Reflect.deleteProperty(URL, 'revokeObjectURL'); + } + vi.restoreAllMocks(); vi.unstubAllGlobals(); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/posts/test/unit/views/members/import-members/modal.test.tsx` around lines 56 - 74, The test overrides URL.createObjectURL and URL.revokeObjectURL via Object.defineProperty in beforeEach which mutates the global URL and is not reverted by vi.unstubAllGlobals; update the test to save the original descriptors (e.g., const originalCreate = Object.getOwnPropertyDescriptor(URL, 'createObjectURL') and originalRevoke similarly) before overriding, and in afterEach restore them using Object.defineProperty(URL, 'createObjectURL', originalCreate) and Object.defineProperty(URL, 'revokeObjectURL', originalRevoke) (or delete the properties to revert to defaults) so URL.createObjectURL and URL.revokeObjectURL are restored for other tests.
🧹 Nitpick comments (1)
e2e/tests/admin/members/search-and-filter.test.ts (1)
47-47: Reduce hardcoded/encoded filter URLs in tests.These
page.gotostrings work, but the manual encoding at Line 68 is hard to maintain. A tiny helper keeps intent readable and avoids encoding mistakes.Refactor sketch
+const gotoMembersWithFilter = async (page: import('@playwright/test').Page, filter: string) => { + const params = new URLSearchParams({filter}); + await page.goto(`/ghost/#/members?${params.toString()}`); +}; - await page.goto('/ghost/#/members?filter=label:VIP'); + await gotoMembersWithFilter(page, 'label:VIP'); - await page.goto('/ghost/#/members?filter=name:~%27Alice%27%2Blabel:Premium'); + await gotoMembersWithFilter(page, `name:~'Alice'+label:Premium`); - await page.goto('/ghost/#/members?filter=label:VIP%2Blabel:Premium'); + await gotoMembersWithFilter(page, 'label:VIP+label:Premium');Also applies to: 68-68, 88-88, 91-91
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/tests/admin/members/search-and-filter.test.ts` at line 47, Tests currently use hardcoded, manually-encoded filter query strings in page.goto (e.g., await page.goto('/ghost/#/members?filter=label:VIP')) which is brittle; add a small helper (e.g., encodeMembersFilter or buildMembersUrl) used by the test file search-and-filter.test.ts to produce the members URL with a properly encoded filter param, then replace the direct page.goto string usages (the occurrences around the current page.goto calls) with await page.goto(buildMembersUrl('label:VIP')) or similar; ensure the helper takes the raw filter string or an object and returns '/ghost/#/members?filter=' + encodeURIComponent(filter) so all instances (lines cited: the page.goto calls at the noted locations) consistently use it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/admin/src/members-route-gate.tsx`:
- Around line 8-12: The members gate currently blocks the React import route
because it unconditionally returns EmberFallback when membersForwardEnabled is
false; update the gating logic in members-route-gate.tsx to allow the import
path to bypass the flag by checking the current route (useLocation or useMatch)
and only render EmberFallback when membersForwardEnabled is false AND the
current route is NOT the import route (e.g., path '/members/import'); keep
rendering Outlet for the import route even when membersForwardEnabled is false
so EmberFallback only affects the main members flow.
In `@e2e/tests/admin/members-legacy/export.test.ts`:
- Around line 7-10: Suite inherits global labs state so it may run under the
forward/react members path; pin this legacy suite to the legacy lab by setting
the labs flag for the describe block (e.g., call test.use with
labs.membersForward = false) near the top of this file (around
usePerTestIsolation() / test.describe('Ghost Admin - Member Export', ...)) so
the suite always runs the legacy path regardless of default config.
In `@e2e/tests/admin/members-legacy/import.test.ts`:
- Line 10: The test binds MembersPage to the import route (MembersPage with
{route: 'members/import'}) which causes membersPage.goto() later to reopen the
import page instead of the members list; fix this by using two distinct page
objects or by creating the MembersPage for the list view separately: keep one
instance (e.g., importPage or importMembersPage) for the import modal/actions
and create a second MembersPage instance (or re-initialize membersPage)
targeting the list route (e.g., default 'members' or {route: 'members'}) prior
to the final membersPage.goto() and assertions so the post-import assertions run
against the actual list page.
---
Outside diff comments:
In `@apps/posts/test/unit/views/members/import-members/modal.test.tsx`:
- Around line 56-74: The test overrides URL.createObjectURL and
URL.revokeObjectURL via Object.defineProperty in beforeEach which mutates the
global URL and is not reverted by vi.unstubAllGlobals; update the test to save
the original descriptors (e.g., const originalCreate =
Object.getOwnPropertyDescriptor(URL, 'createObjectURL') and originalRevoke
similarly) before overriding, and in afterEach restore them using
Object.defineProperty(URL, 'createObjectURL', originalCreate) and
Object.defineProperty(URL, 'revokeObjectURL', originalRevoke) (or delete the
properties to revert to defaults) so URL.createObjectURL and URL.revokeObjectURL
are restored for other tests.
---
Nitpick comments:
In `@e2e/tests/admin/members/search-and-filter.test.ts`:
- Line 47: Tests currently use hardcoded, manually-encoded filter query strings
in page.goto (e.g., await page.goto('/ghost/#/members?filter=label:VIP')) which
is brittle; add a small helper (e.g., encodeMembersFilter or buildMembersUrl)
used by the test file search-and-filter.test.ts to produce the members URL with
a properly encoded filter param, then replace the direct page.goto string usages
(the occurrences around the current page.goto calls) with await
page.goto(buildMembersUrl('label:VIP')) or similar; ensure the helper takes the
raw filter string or an object and returns '/ghost/#/members?filter=' +
encodeURIComponent(filter) so all instances (lines cited: the page.goto calls at
the noted locations) consistently use it.
🪄 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
Run ID: f0052e1e-3603-4830-b5a4-106f31d278e0
📒 Files selected for processing (31)
apps/admin/src/layout/app-sidebar/member-sidebar-views.tsapps/admin/src/layout/app-sidebar/nav-content.helpers.test.tsapps/admin/src/layout/app-sidebar/nav-content.helpers.tsapps/admin/src/layout/app-sidebar/nav-content.tsxapps/admin/src/members-route-gate.tsxapps/admin/src/routes.tsxapps/posts/src/routes.tsxapps/posts/src/views/members/components/bulk-action-modals/import-members-modal.tsxapps/posts/src/views/members/components/bulk-action-modals/import-members/state.tsapps/posts/src/views/members/components/bulk-action-modals/import-members/upload.tsapps/posts/src/views/members/components/members-actions.tsxapps/posts/test/unit/views/members/import-members/modal.test.tsxapps/posts/test/unit/views/members/import-members/upload.test.tsapps/posts/test/unit/views/members/members-actions.test.tsxe2e/helpers/pages/admin/members/index.tse2e/helpers/pages/admin/members/members-list-page.tse2e/tests/admin/members-forward/export.test.tse2e/tests/admin/members-legacy/disable-commenting.test.tse2e/tests/admin/members-legacy/export.test.tse2e/tests/admin/members-legacy/filter-actions.test.tse2e/tests/admin/members-legacy/impersonation.test.tse2e/tests/admin/members-legacy/import.test.tse2e/tests/admin/members-legacy/member-activity-events.test.tse2e/tests/admin/members-legacy/members.test.tse2e/tests/admin/members-legacy/stripe-webhooks.test.tse2e/tests/admin/members/bulk-actions.test.tse2e/tests/admin/members/export.test.tse2e/tests/admin/members/list.test.tse2e/tests/admin/members/saved-views.test.tse2e/tests/admin/members/search-and-filter.test.tse2e/tests/admin/members/virtual-window.test.ts
💤 Files with no reviewable changes (2)
- apps/posts/src/routes.tsx
- e2e/tests/admin/members-forward/export.test.ts
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 23852169224 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
e2e/tests/admin/members-legacy/disable-commenting.test.ts (1)
174-186:⚠️ Potential issue | 🟡 MinorMissing
test.use({labs: {membersForward: false}})in second describe block.The
'Ghost Admin - Disable Commenting Cache Invalidation'suite usesMembersPageto navigate to/members, but unlike the first describe block, it lacks thelabs: {membersForward: false}override. This may cause the test to run against the React UI instead of the intended Ember legacy UI.🔧 Proposed fix
test.describe('Ghost Admin - Disable Commenting Cache Invalidation', () => { + test.use({labs: {membersForward: false}}); + let memberFactory: MemberFactory; let postFactory: PostFactory; let commentFactory: CommentFactory;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/tests/admin/members-legacy/disable-commenting.test.ts` around lines 174 - 186, The second test suite "Ghost Admin - Disable Commenting Cache Invalidation" is missing the Playwright override to force the legacy Ember members UI; add test.use({ labs: { membersForward: false } }) at the top of the describe block for "Ghost Admin - Disable Commenting Cache Invalidation" so that MembersPage navigation to /members (and any usage of MembersPage, createMemberFactory/createPostFactory/createCommentFactory) runs against the legacy UI instead of the React UI.
🧹 Nitpick comments (1)
apps/admin/src/members-route-gate.tsx (1)
5-15: Consider normalizing pathname for trailing slash edge case.The exact equality check
location.pathname === "/members"won't match URLs with a trailing slash (/members/). React Router v6+ does not automatically normalize trailing slashes, so this is a valid edge case worth defending against.♻️ Optional fix for trailing slash handling
export function MembersRouteGate() { const membersForwardEnabled = useFeatureFlag("membersForward"); const location = useLocation(); - const isMembersListRoute = location.pathname === "/members"; + const isMembersListRoute = location.pathname === "/members" || location.pathname === "/members/"; if (!membersForwardEnabled && isMembersListRoute) { return <EmberFallback />; } return <Outlet />; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/admin/src/members-route-gate.tsx` around lines 5 - 15, The equality check in MembersRouteGate misses paths with a trailing slash (e.g. "/members/"); update the check by normalizing location.pathname before comparing: compute a normalizedPath (e.g. strip trailing slashes with something like location.pathname.replace(/\/+$/, '') || '/') and then set isMembersListRoute = normalizedPath === "/members"; keep the rest of the logic (return <EmberFallback /> when !membersForwardEnabled && isMembersListRoute, otherwise <Outlet />) so EmberFallback triggers for both "/members" and "/members/".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@e2e/tests/admin/members-legacy/disable-commenting.test.ts`:
- Around line 174-186: The second test suite "Ghost Admin - Disable Commenting
Cache Invalidation" is missing the Playwright override to force the legacy Ember
members UI; add test.use({ labs: { membersForward: false } }) at the top of the
describe block for "Ghost Admin - Disable Commenting Cache Invalidation" so that
MembersPage navigation to /members (and any usage of MembersPage,
createMemberFactory/createPostFactory/createCommentFactory) runs against the
legacy UI instead of the React UI.
---
Nitpick comments:
In `@apps/admin/src/members-route-gate.tsx`:
- Around line 5-15: The equality check in MembersRouteGate misses paths with a
trailing slash (e.g. "/members/"); update the check by normalizing
location.pathname before comparing: compute a normalizedPath (e.g. strip
trailing slashes with something like location.pathname.replace(/\/+$/, '') ||
'/') and then set isMembersListRoute = normalizedPath === "/members"; keep the
rest of the logic (return <EmberFallback /> when !membersForwardEnabled &&
isMembersListRoute, otherwise <Outlet />) so EmberFallback triggers for both
"/members" and "/members/".
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 111bb797-ba08-4404-a23c-d96264f21a85
📒 Files selected for processing (14)
apps/admin/src/members-route-gate.tsxapps/admin/src/routes.tsxapps/posts/src/views/members/components/members-header-search.tsxapps/posts/test/unit/views/members/import-members/modal.test.tsxe2e/helpers/pages/admin/members/members-list-page.tse2e/tests/admin/members-legacy/disable-commenting.test.tse2e/tests/admin/members-legacy/export.test.tse2e/tests/admin/members-legacy/filter-actions.test.tse2e/tests/admin/members-legacy/impersonation.test.tse2e/tests/admin/members-legacy/import.test.tse2e/tests/admin/members-legacy/member-activity-events.test.tse2e/tests/admin/members-legacy/members.test.tse2e/tests/admin/members-legacy/stripe-webhooks.test.tse2e/tests/admin/members/list.test.ts
✅ Files skipped from review due to trivial changes (5)
- e2e/tests/admin/members-legacy/filter-actions.test.ts
- e2e/tests/admin/members-legacy/import.test.ts
- e2e/tests/admin/members-legacy/export.test.ts
- apps/admin/src/routes.tsx
- apps/posts/test/unit/views/members/import-members/modal.test.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- e2e/helpers/pages/admin/members/members-list-page.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ed8639a2d7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return; | ||
| } | ||
|
|
||
| navigate('/members'); |
There was a problem hiding this comment.
Preserve list query when closing import modal
Closing the import route unconditionally navigates to /members, which drops the current filter/search query when a user opens import from a scoped list such as /members?filter=label:VIP and then cancels/closes. This is a regression from the previous in-place modal behavior because users lose their current member view context after closing; the close target should be derived from the prior location/query (or use back/replace semantics) unless a labeled-import redirect is being applied.
Useful? React with 👍 / 👎.
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 23853671589 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
ed8639a to
37c6085
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (4)
apps/posts/test/unit/views/members/members-actions.test.tsx (1)
47-54: Consider clearing mock call history inbeforeEach.The
mockUseNavigateandmockUseLocationmocks retain call history across tests. WhilemockReturnValueis reset, consider addingvi.clearAllMocks()to ensure test isolation.♻️ Suggested improvement
beforeEach(() => { importModalPropsRef.current = null; + vi.clearAllMocks(); mockUseLocation.mockReturnValue({ pathname: '/members', search: '' }); mockUseNavigate.mockReturnValue(vi.fn()); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/posts/test/unit/views/members/members-actions.test.tsx` around lines 47 - 54, The tests keep mock call history between runs which can leak state; in the beforeEach that sets importModalPropsRef.current, mockUseLocation, and mockUseNavigate, call vi.clearAllMocks() (or vi.resetAllMocks()) at the start of the beforeEach to clear mock call history for mockUseNavigate and mockUseLocation so each test is isolated; update the beforeEach surrounding mockUseNavigate/mockUseLocation to include this clear/reset call before reassigning mockReturnValue.e2e/tests/admin/members/virtual-window.test.ts (1)
83-104: Direct locator usage is acceptable but could be encapsulated.The use of
page.getByTestId('members-list').getByTestId('members-list-item')directly in the test bypasses the page object'smemberRowslocator. While this works and uses alloweddata-testidselectors, consider whether this specific locator pattern should be exposed through theMembersPagepage object for consistency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/tests/admin/members/virtual-window.test.ts` around lines 83 - 104, The test directly uses page.getByTestId('members-list').getByTestId('members-list-item') (stored as reactMemberRows) instead of the MembersPage page object locator; update the test to use the existing membersPage.memberRows locator (or add a memberRows getter to MembersPage if it doesn't exist) and then use that locator for counting, nth(), and attribute/text reads so the test consistently uses the MembersPage API alongside membersPage.scrollUntilMaxRenderedIndexAtLeast and membersPage.getScrollParentScrollTop; ensure the subsequent targetRowLocator, targetRow.index/text/scrollTop extraction, click and assertions remain identical but reference the page object locator.e2e/tests/admin/members-legacy/export.test.ts (1)
14-22: Consider moving helper function to outer scope.The
extractDownloadedContentSpecificsfunction is defined inside the test describe block but doesn't depend on any test-scoped variables. Moving it to the module scope would improve readability and satisfy the static analysis hint.♻️ Suggested refactor
import {MemberFactory, createMemberFactory} from '@/data-factory'; import {MembersPage} from '@/helpers/pages'; +function extractDownloadedContentSpecifics(content: string) { + const contentIds = content.match(/[a-z0-9]{24}/gm); + const contentTimestamps = content.match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/gm); + + return { + contentIds, + contentTimestamps + }; +} + usePerTestIsolation(); test.describe('Ghost Admin - Member Export', () => { test.use({labs: {membersForward: false}}); let memberFactory: MemberFactory; - - function extractDownloadedContentSpecifics(content: string) { - const contentIds = content.match(/[a-z0-9]{24}/gm); - const contentTimestamps = content.match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/gm); - - return { - contentIds, - contentTimestamps - }; - }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/tests/admin/members-legacy/export.test.ts` around lines 14 - 22, The helper function extractDownloadedContentSpecifics is currently declared inside the test describe block but it does not use any test-scoped variables; move its declaration to module (outer) scope so it lives alongside other top-level helpers, keeping its signature and return shape unchanged and updating any internal references in the tests to use the same function name; ensure imports/exports are adjusted only if you need the helper in other files.apps/admin/src/members-route-gate.test.tsx (1)
24-44: Add test coverage for the flag-enabled scenario.The current tests only cover behavior when
membersForwardis disabled. Add tests to verify behavior when the flag is enabled:
/members/(or/members) with flag enabled should render the ReactOutlet- Optionally verify that
useFeatureFlagis called with'membersForward'The existing tests for disabled behavior (both
/members/and/members/importpaths) are adequate, though you could add an explicit test for the/memberspath (no trailing slash) for clarity, given thebeforeEachsetup.📝 Suggested test case
+ it('renders React Outlet for /members/ when the flag is enabled', () => { + mockUseFeatureFlag.mockReturnValue(true); + mockUseLocation.mockReturnValue({pathname: '/members/'}); + + render(<MembersRouteGate />); + + expect(screen.getByTestId('outlet')).toBeInTheDocument(); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/admin/src/members-route-gate.test.tsx` around lines 24 - 44, Add tests verifying behavior when the feature flag is enabled: in the MembersRouteGate test suite, create one or two tests that mockUseFeatureFlag to return true and mockUseLocation to return {pathname: '/members'} (and optionally '/members/' ) then render <MembersRouteGate /> and assert the React Outlet is rendered (expect screen.getByTestId('outlet') toBeInTheDocument()); also add an assertion that useFeatureFlag was called with 'membersForward' (or verify mockUseFeatureFlag was invoked with that key) to ensure the flag is actually checked.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/admin/src/members-route-gate.test.tsx`:
- Around line 24-44: Add tests verifying behavior when the feature flag is
enabled: in the MembersRouteGate test suite, create one or two tests that
mockUseFeatureFlag to return true and mockUseLocation to return {pathname:
'/members'} (and optionally '/members/' ) then render <MembersRouteGate /> and
assert the React Outlet is rendered (expect screen.getByTestId('outlet')
toBeInTheDocument()); also add an assertion that useFeatureFlag was called with
'membersForward' (or verify mockUseFeatureFlag was invoked with that key) to
ensure the flag is actually checked.
In `@apps/posts/test/unit/views/members/members-actions.test.tsx`:
- Around line 47-54: The tests keep mock call history between runs which can
leak state; in the beforeEach that sets importModalPropsRef.current,
mockUseLocation, and mockUseNavigate, call vi.clearAllMocks() (or
vi.resetAllMocks()) at the start of the beforeEach to clear mock call history
for mockUseNavigate and mockUseLocation so each test is isolated; update the
beforeEach surrounding mockUseNavigate/mockUseLocation to include this
clear/reset call before reassigning mockReturnValue.
In `@e2e/tests/admin/members-legacy/export.test.ts`:
- Around line 14-22: The helper function extractDownloadedContentSpecifics is
currently declared inside the test describe block but it does not use any
test-scoped variables; move its declaration to module (outer) scope so it lives
alongside other top-level helpers, keeping its signature and return shape
unchanged and updating any internal references in the tests to use the same
function name; ensure imports/exports are adjusted only if you need the helper
in other files.
In `@e2e/tests/admin/members/virtual-window.test.ts`:
- Around line 83-104: The test directly uses
page.getByTestId('members-list').getByTestId('members-list-item') (stored as
reactMemberRows) instead of the MembersPage page object locator; update the test
to use the existing membersPage.memberRows locator (or add a memberRows getter
to MembersPage if it doesn't exist) and then use that locator for counting,
nth(), and attribute/text reads so the test consistently uses the MembersPage
API alongside membersPage.scrollUntilMaxRenderedIndexAtLeast and
membersPage.getScrollParentScrollTop; ensure the subsequent targetRowLocator,
targetRow.index/text/scrollTop extraction, click and assertions remain identical
but reference the page object locator.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ac758a1e-2777-4acf-b951-458791382ced
📒 Files selected for processing (35)
apps/admin/src/layout/app-sidebar/member-sidebar-views.tsapps/admin/src/layout/app-sidebar/nav-content.helpers.test.tsapps/admin/src/layout/app-sidebar/nav-content.helpers.tsapps/admin/src/layout/app-sidebar/nav-content.tsxapps/admin/src/members-route-gate.test.tsxapps/admin/src/members-route-gate.tsxapps/admin/src/routes.tsxapps/posts/src/components/virtual-table/virtual-list-window.test.tsapps/posts/src/routes.tsxapps/posts/src/views/members/components/bulk-action-modals/import-members-modal.tsxapps/posts/src/views/members/components/bulk-action-modals/import-members/state.tsapps/posts/src/views/members/components/bulk-action-modals/import-members/upload.tsapps/posts/src/views/members/components/members-actions.tsxapps/posts/src/views/members/components/members-header-search.tsxapps/posts/test/unit/views/members/import-members/modal.test.tsxapps/posts/test/unit/views/members/import-members/upload.test.tsapps/posts/test/unit/views/members/members-actions.test.tsxe2e/helpers/pages/admin/members/index.tse2e/helpers/pages/admin/members/members-list-page.tse2e/tests/admin/members-forward/export.test.tse2e/tests/admin/members-legacy/disable-commenting.test.tse2e/tests/admin/members-legacy/export.test.tse2e/tests/admin/members-legacy/filter-actions.test.tse2e/tests/admin/members-legacy/impersonation.test.tse2e/tests/admin/members-legacy/import.test.tse2e/tests/admin/members-legacy/member-activity-events.test.tse2e/tests/admin/members-legacy/members.test.tse2e/tests/admin/members-legacy/stripe-webhooks.test.tse2e/tests/admin/members/bulk-actions.test.tse2e/tests/admin/members/export.test.tse2e/tests/admin/members/list.test.tse2e/tests/admin/members/saved-views.test.tse2e/tests/admin/members/search-and-filter.test.tse2e/tests/admin/members/tier-filter-search.test.tse2e/tests/admin/members/virtual-window.test.ts
💤 Files with no reviewable changes (2)
- apps/posts/src/routes.tsx
- e2e/tests/admin/members-forward/export.test.ts
✅ Files skipped from review due to trivial changes (13)
- e2e/tests/admin/members-legacy/member-activity-events.test.ts
- e2e/tests/admin/members-legacy/filter-actions.test.ts
- e2e/tests/admin/members-legacy/disable-commenting.test.ts
- apps/posts/src/views/members/components/members-header-search.tsx
- e2e/tests/admin/members-legacy/members.test.ts
- e2e/helpers/pages/admin/members/index.ts
- e2e/tests/admin/members-legacy/stripe-webhooks.test.ts
- apps/posts/test/unit/views/members/import-members/modal.test.tsx
- e2e/tests/admin/members/search-and-filter.test.ts
- apps/posts/test/unit/views/members/import-members/upload.test.ts
- apps/posts/src/views/members/components/bulk-action-modals/import-members/state.ts
- apps/admin/src/members-route-gate.tsx
- e2e/tests/admin/members-legacy/impersonation.test.ts
🚧 Files skipped from review as they are similar to previous changes (8)
- e2e/tests/admin/members/list.test.ts
- e2e/tests/admin/members-legacy/import.test.ts
- apps/admin/src/layout/app-sidebar/nav-content.helpers.ts
- e2e/tests/admin/members/saved-views.test.ts
- apps/posts/src/views/members/components/bulk-action-modals/import-members/upload.ts
- e2e/tests/admin/members/bulk-actions.test.ts
- apps/admin/src/layout/app-sidebar/nav-content.helpers.test.ts
- apps/admin/src/layout/app-sidebar/nav-content.tsx
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 37c6085f1c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return; | ||
| } | ||
|
|
||
| navigate(`/members${currentSearch}`); |
There was a problem hiding this comment.
Use replace navigation when closing the import modal
Closing the route-backed import modal currently does a normal navigate('/members...'), which pushes a new history entry. In the common flow (members list → import modal → close), pressing Back reopens the modal instead of returning to the previous page, which is a UX regression from the old in-place modal behavior. This occurs whenever users close/cancel import without a label redirect; use replace/back semantics so dismissing the modal unwinds history instead of stacking another entry.
Useful? React with 👍 / 👎.
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 23897853783 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
37c6085 to
583b041
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/posts/src/views/members/components/members-actions.tsx (1)
195-196: Intentional no-op is appropriate for route-driven modal.Since modal open/close is controlled by navigation, the no-op
handleImportModalOpenChangecorrectly decouples the modal's internal state changes from triggering side effects. Consider adding a brief comment for future maintainers.📝 Suggested clarification comment
- const handleImportModalOpenChange = useCallback(() => {}, []); + // No-op: modal visibility is route-driven via `/members/import` + const handleImportModalOpenChange = useCallback(() => {}, []);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/posts/src/views/members/components/members-actions.tsx` around lines 195 - 196, The empty function handleImportModalOpenChange is an intentional no-op because the modal's open/close state is driven by routing; add a short clarifying comment immediately above the handleImportModalOpenChange declaration explaining that it is intentionally empty to decouple modal internal events from navigation-driven state and to prevent future maintainers from removing or modifying it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/posts/src/views/members/components/members-actions.tsx`:
- Around line 195-196: The empty function handleImportModalOpenChange is an
intentional no-op because the modal's open/close state is driven by routing; add
a short clarifying comment immediately above the handleImportModalOpenChange
declaration explaining that it is intentionally empty to decouple modal internal
events from navigation-driven state and to prevent future maintainers from
removing or modifying it.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ae5f1fb9-04a6-4a8b-bdce-6f350d15146c
📒 Files selected for processing (36)
apps/admin/src/layout/app-sidebar/member-sidebar-views.tsapps/admin/src/layout/app-sidebar/nav-content.helpers.test.tsapps/admin/src/layout/app-sidebar/nav-content.helpers.tsapps/admin/src/layout/app-sidebar/nav-content.tsxapps/admin/src/members-route-gate.test.tsxapps/admin/src/members-route-gate.tsxapps/admin/src/routes.tsxapps/posts/src/components/virtual-table/virtual-list-window.test.tsapps/posts/src/routes.tsxapps/posts/src/views/members/components/bulk-action-modals/import-members-modal.tsxapps/posts/src/views/members/components/bulk-action-modals/import-members/state.tsapps/posts/src/views/members/components/bulk-action-modals/import-members/upload.tsapps/posts/src/views/members/components/members-actions.tsxapps/posts/src/views/members/components/members-header-search.tsxapps/posts/test/unit/views/members/import-members/modal.test.tsxapps/posts/test/unit/views/members/import-members/upload.test.tsapps/posts/test/unit/views/members/members-actions.test.tsxe2e/helpers/pages/admin/members/index.tse2e/helpers/pages/admin/members/members-list-page.tse2e/tests/admin/members-forward/export.test.tse2e/tests/admin/members-legacy/disable-commenting.test.tse2e/tests/admin/members-legacy/export.test.tse2e/tests/admin/members-legacy/filter-actions.test.tse2e/tests/admin/members-legacy/impersonation.test.tse2e/tests/admin/members-legacy/import.test.tse2e/tests/admin/members-legacy/member-activity-events.test.tse2e/tests/admin/members-legacy/members.test.tse2e/tests/admin/members-legacy/stripe-webhooks.test.tse2e/tests/admin/members/bulk-actions.test.tse2e/tests/admin/members/export.test.tse2e/tests/admin/members/list.test.tse2e/tests/admin/members/multiselect-filters.test.tse2e/tests/admin/members/saved-views.test.tse2e/tests/admin/members/search-and-filter.test.tse2e/tests/admin/members/tier-filter-search.test.tse2e/tests/admin/members/virtual-window.test.ts
💤 Files with no reviewable changes (2)
- apps/posts/src/routes.tsx
- e2e/tests/admin/members-forward/export.test.ts
✅ Files skipped from review due to trivial changes (12)
- apps/posts/src/views/members/components/members-header-search.tsx
- e2e/tests/admin/members-legacy/member-activity-events.test.ts
- e2e/tests/admin/members-legacy/disable-commenting.test.ts
- e2e/tests/admin/members-legacy/filter-actions.test.ts
- e2e/tests/admin/members-legacy/stripe-webhooks.test.ts
- e2e/tests/admin/members/search-and-filter.test.ts
- e2e/tests/admin/members-legacy/impersonation.test.ts
- e2e/tests/admin/members/saved-views.test.ts
- apps/admin/src/members-route-gate.test.tsx
- apps/posts/test/unit/views/members/members-actions.test.tsx
- e2e/tests/admin/members-legacy/members.test.ts
- apps/posts/src/components/virtual-table/virtual-list-window.test.ts
🚧 Files skipped from review as they are similar to previous changes (15)
- e2e/helpers/pages/admin/members/index.ts
- apps/posts/test/unit/views/members/import-members/modal.test.tsx
- e2e/tests/admin/members/tier-filter-search.test.ts
- apps/admin/src/layout/app-sidebar/member-sidebar-views.ts
- apps/admin/src/layout/app-sidebar/nav-content.helpers.ts
- apps/admin/src/members-route-gate.tsx
- apps/posts/src/views/members/components/bulk-action-modals/import-members/upload.ts
- apps/posts/test/unit/views/members/import-members/upload.test.ts
- apps/admin/src/layout/app-sidebar/nav-content.helpers.test.ts
- e2e/tests/admin/members/virtual-window.test.ts
- e2e/tests/admin/members/export.test.ts
- e2e/tests/admin/members-legacy/export.test.ts
- e2e/tests/admin/members-legacy/import.test.ts
- e2e/helpers/pages/admin/members/members-list-page.ts
- e2e/tests/admin/members/bulk-actions.test.ts
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 23899244219 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f01eb5f9c3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await membersPage.getMenuItem('Import members').click(); | ||
|
|
||
| const importDialog = page.getByRole('dialog', {name: 'Import members'}); | ||
| const fileInput = importDialog.locator('input[type="file"]').first(); |
There was a problem hiding this comment.
Replace CSS selector with semantic file-input locator
This test reintroduces a raw CSS locator (input[type="file"]), which violates the E2E rule in e2e/AGENTS.md (“Never use CSS/XPath selectors”) and is brittle when multiple file inputs are present (for example hidden dropzone inputs plus active dialog inputs), making the import flow test prone to targeting the wrong element. Please switch this to a semantic/test-id based locator in the dialog scope so the test remains stable as markup evolves.
Useful? React with 👍 / 👎.
| const location = useLocation(); | ||
| const sharedViews = useSharedViews('members'); | ||
| const isOnMembersForward = location.pathname === '/members-forward'; | ||
| const isOnMembersListRoute = location.pathname === '/members'; |
There was a problem hiding this comment.
Normalize members path when computing saved-view active state
The members route gate now explicitly treats /members/ as the members list, but this check only matches '/members', so a trailing-slash URL leaves all member saved views inactive even when the filter matches. This creates inconsistent sidebar state for bookmarked/manual /members/ URLs; normalizing trailing slashes here (as done in the gate) avoids that mismatch.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9697d4df66
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const postsExpanded = savedPostsExpanded; | ||
| const isOnMembersForward = location.pathname === '/members-forward'; | ||
| const hasActiveMemberView = isOnMembersForward && memberViews.some(view => view.isActive); | ||
| const isOnMembersRoute = location.pathname === '/members' || location.pathname === '/members/import'; |
There was a problem hiding this comment.
Normalize members pathname before computing nav active state
The new check only treats '/members' and '/members/import' as React-owned members routes, so a valid trailing-slash URL like /members/ is treated as off-route and isMembersNavActive falls back to Ember route state, leaving the Members nav item inactive. This is a regression for bookmarked/manual URLs (and members-route-gate.test.tsx now explicitly treats /members/ as a members route), so the pathname should be normalized (for example by trimming a trailing slash) before this comparison.
Useful? React with 👍 / 👎.
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 23902490780 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release This keeps the members list and import flow on the stable /members routes, preserves the Ember fallback behind the flag, and aligns the e2e suite layout around members and members-legacy.
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release This finishes the members and members-legacy e2e split by moving stale React tests off members-forward and aligning detail navigation with the current members list UI.
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 24068924303 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release The legacy import e2e test was asserting select text, which is unstable in CI because the native select exposes the full option list text. Assert the selected mapping values instead.
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 24070599060 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release The legacy import flow now returns to the members list filtered by the generated import label. Update the e2e assertion to accept the members route with query params and verify the import label filter is applied.
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 24072402220 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release The members import flow stores its filter in the hash route, so URL.searchParams does not expose it. Match the full hash URL instead of parsing top-level search params in the legacy import e2e test.
There was a problem hiding this comment.
💡 Codex Review
e2e/AGENTS.md marks “Never use CSS/XPath selectors” as a critical rule, but searchMultiselectOptions uses page.locator('[cmdk-input]'); this is brittle when multiple command inputs exist (for example, hidden or overlapping popovers) and can cause the test to type into the wrong element, producing flaky member-filter scenarios. Please switch this to a semantic or test-id locator scoped to the active filter popover.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release Preserve query params and permission handling when Ember hands members import off to React, and replace history when closing the route-backed import modal.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c708e85084
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| }, [nql, search]); | ||
|
|
||
| const handleImportModalOpenChange = useCallback(() => {}, []); |
There was a problem hiding this comment.
Reset import modal state when route closes externally
The modal is route-controlled (open={isImportRoute}), but onOpenChange is a no-op here, so closing /members/import via browser Back or any external navigation path does not execute the modal’s close handler/reset logic. In those flows the component stays mounted with its previous file/mapping/import state, so reopening /members/import can show stale state from the last session instead of a fresh import flow.
Useful? React with 👍 / 👎.
| this.importButton = page.locator('[data-test-button="perform-import"]'); | ||
| this.importHeading = page.locator('[data-test-modal="import-members"]').getByRole('heading', {name: /import (in progress|complete)/i}); | ||
| this.closeButton = page.locator('[data-test-button="close-import-members"]'); |
There was a problem hiding this comment.
Use semantic locators for import modal action buttons
These action locators were changed to raw CSS selectors, which violates the critical locator rule in e2e/AGENTS.md (“Never use CSS/XPath selectors - only semantic locators or data-testid”) and makes the page object more brittle if dialog markup introduces duplicate/hidden matching nodes. Prefer role/label (or configured test-id) locators for the import and close actions to keep this flow stable.
Useful? React with 👍 / 👎.
E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 24085550665 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release Scopes the saved views test to the visible React members controls so it does not click the hidden Ember filter button during the membersForward rollout.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9f9400b837
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release This drops the members-specific helper layer and aligns the parent/child highlight behavior with the existing collapsible sidebar pattern while keeping React and Ember members routes distinct.
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release This keeps the React import modal closed unless the import route is actually React-owned, which avoids the duplicate Ember and React import modals on /members/import.
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release The React members import modal should follow the actual route ownership in this branch, which is controlled only by membersForward.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 501f50816e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release The access-control coverage stays in MembersRoute while the feature-flag gate test only reasserted a simple branch in MembersRouteGate.
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release This tightens the members action unit coverage, shares the export assertions across the legacy and React member list suites, and moves saved-view interactions onto the page objects instead of protected internals.
There was a problem hiding this comment.
💡 Codex Review
e2e/AGENTS.md explicitly requires “Never use CSS/XPath selectors”, but this new page object method uses a raw CSS locator and the same pattern continues in nearby helpers (editLabelInput, getFilterItem, openFilterValue). These selectors depend on implementation details like [cmdk-input] and button:not(...), so small markup changes can silently target the wrong element and make members filter tests flaky; this should be switched to semantic/test-id locators to satisfy the repo rule and keep tests stable.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release This removes the low-value route gate test, tightens members action coverage, aligns the member list page objects on a shared surface, and keeps one parameterized export suite instead of separate old and new implementations.
There was a problem hiding this comment.
💡 Codex Review
e2e/AGENTS.md explicitly forbids CSS/XPath selectors, and this button:not(...) locator is structure-dependent, so any extra button or DOM reorder in the filter chip will make this helper click the wrong control and introduce flaky members filter tests. Please use a semantic locator (role/name) or a dedicated data-testid for the value trigger instead.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| await expect(page).toHaveURL(/\/ghost\/#\/members-forward$/); | ||
| await expect(membersPage.getMemberListItemByIndex(targetRow.index)).toContainText(targetRow.text ?? ''); | ||
| await expect(page).toHaveURL(/\/ghost\/#\/members$/); | ||
| await expect(page.getByTestId('members-list').locator(`[data-testid="members-list-item"][data-index="${targetRow.index}"]`)).toContainText(targetRow.text ?? ''); |
There was a problem hiding this comment.
Use semantic locator for virtual row restore assertion
This assertion reintroduces a raw CSS selector (.locator(...)) in an e2e test, which violates the e2e/AGENTS.md rule to avoid CSS/XPath selectors and makes the check brittle to list markup changes in the virtualized table. A semantic or data-testid-driven locator should be used here to keep the scroll-restore test stable.
Useful? React with 👍 / 👎.
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release The React members view is already mounted behind route ownership, so the extra feature-flag check was redundant. This also drops the duplicate route-open unit assertion and keeps only the close/query behavior that the branch still needs unit coverage for.
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release The Ember members import route should keep its existing URL and permission behavior, but it must not render the legacy fullscreen modal when membersForward is enabled because React owns the UI on that route.
There was a problem hiding this comment.
💡 Codex Review
e2e/AGENTS.md explicitly requires semantic/test-id locators, but this helper uses a CSS structural selector (button:not([data-slot="filters-remove"])) to pick the filter value control. That selector is tightly coupled to internal markup order and can click the wrong button when the filter item adds/reorders buttons, creating flaky member filter tests; this should use a semantic locator (or dedicated test id) instead.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release The sidebar now uses the shared routing active-state logic with the custom-view suppression kept in nav-content, and the Ember members list route skips legacy loading/rendering when membersForward is enabled.
ref https://linear.app/ghost/issue/BER-3506/rework-feature-flagging-for-release The extra members page scoping in the React members page object was only there to work around hidden Ember rendering. With the Ember render gating fixed, the helper can go back to using the stable list and action locators directly.
|



Summary
membersForwardso it gates React rendering on/membersinstead of changing sidebar URLs/members/importthrough the React members UI while keeping Ember fallback routes in placemembers/and Ember coverage lives undermembers-legacy/