From 340ecd2475575b99ae45d182b6d3d0bc8c82ca28 Mon Sep 17 00:00:00 2001 From: rchlfryn Date: Wed, 4 Mar 2026 11:54:14 -0800 Subject: [PATCH 1/6] Sync tenant selector dropdown after tenant save --- src/app/(payload)/admin/importMap.js | 2 ++ .../Tenants/components/SyncTenantsOnSave.tsx | 25 +++++++++++++++++++ src/collections/Tenants/index.ts | 7 ++++++ 3 files changed, 34 insertions(+) create mode 100644 src/collections/Tenants/components/SyncTenantsOnSave.tsx diff --git a/src/app/(payload)/admin/importMap.js b/src/app/(payload)/admin/importMap.js index 9f3e21543..0469a98be 100644 --- a/src/app/(payload)/admin/importMap.js +++ b/src/app/(payload)/admin/importMap.js @@ -35,6 +35,7 @@ import { InviteUser as InviteUser_6042b6804e11048cd4fbe6206cbc2b0f } from '@/col import { ResendInviteButton as ResendInviteButton_e262b7912e5bdc08a1a83eb2731de735 } from '@/collections/Users/components/ResendInviteButton' import { CollectionsField as CollectionsField_49c0311020325b59204cc21d2f536b8d } from '@/collections/Roles/components/CollectionsField' import { RulesCell as RulesCell_649699f5b285e7a5429592dc58fd6f0c } from '@/collections/Roles/components/RulesCell' +import { SyncTenantsOnSave as SyncTenantsOnSave_7025498606b767f7843bf544e6535ee1 } from '@/collections/Tenants/components/SyncTenantsOnSave' import { LinkLabelDescription as LinkLabelDescription_cc2cf53f1598892c0c926f3cb616a721 } from '@/fields/navLink/components/LinkLabelDescription' import { AvalancheCenterName as AvalancheCenterName_acb7f1a03857e27efe1942bb65ab80ad } from '@/collections/Settings/components/AvalancheCenterName' import { USFSLogoDescription as USFSLogoDescription_d2eea91290575f9a545768dce25713f4 } from '@/collections/Settings/components/USFSLogoDescription' @@ -93,6 +94,7 @@ export const importMap = { "@/collections/Users/components/ResendInviteButton#ResendInviteButton": ResendInviteButton_e262b7912e5bdc08a1a83eb2731de735, "@/collections/Roles/components/CollectionsField#CollectionsField": CollectionsField_49c0311020325b59204cc21d2f536b8d, "@/collections/Roles/components/RulesCell#RulesCell": RulesCell_649699f5b285e7a5429592dc58fd6f0c, + "@/collections/Tenants/components/SyncTenantsOnSave#SyncTenantsOnSave": SyncTenantsOnSave_7025498606b767f7843bf544e6535ee1, "@/fields/navLink/components/LinkLabelDescription#LinkLabelDescription": LinkLabelDescription_cc2cf53f1598892c0c926f3cb616a721, "@/collections/Settings/components/AvalancheCenterName#AvalancheCenterName": AvalancheCenterName_acb7f1a03857e27efe1942bb65ab80ad, "@/collections/Settings/components/USFSLogoDescription#USFSLogoDescription": USFSLogoDescription_d2eea91290575f9a545768dce25713f4, diff --git a/src/collections/Tenants/components/SyncTenantsOnSave.tsx b/src/collections/Tenants/components/SyncTenantsOnSave.tsx new file mode 100644 index 000000000..987157a0d --- /dev/null +++ b/src/collections/Tenants/components/SyncTenantsOnSave.tsx @@ -0,0 +1,25 @@ +'use client' + +import { useDocumentInfo } from '@payloadcms/ui' +import { useEffect, useRef } from 'react' + +import { useTenantSelection } from '@/providers/TenantSelectionProvider/index.client' + +/** + * Invisible component placed on the Tenants edit view that syncs the tenant + * selector dropdown whenever a tenant document is saved (created or updated). + */ +export const SyncTenantsOnSave = () => { + const { lastUpdateTime } = useDocumentInfo() + const { syncTenants } = useTenantSelection() + const prevUpdateTime = useRef(lastUpdateTime) + + useEffect(() => { + if (lastUpdateTime !== prevUpdateTime.current) { + prevUpdateTime.current = lastUpdateTime + syncTenants() + } + }, [lastUpdateTime, syncTenants]) + + return null +} diff --git a/src/collections/Tenants/index.ts b/src/collections/Tenants/index.ts index 1ff075756..04513c9a8 100644 --- a/src/collections/Tenants/index.ts +++ b/src/collections/Tenants/index.ts @@ -19,6 +19,13 @@ export const Tenants: CollectionConfig = { useAsTitle: 'name', group: 'Permissions', hidden: ({ user }) => hasReadOnlyAccess(user, 'tenants'), + components: { + edit: { + beforeDocumentControls: [ + '@/collections/Tenants/components/SyncTenantsOnSave#SyncTenantsOnSave', + ], + }, + }, }, labels: { plural: 'Avalanche Centers', From 5eed1c0dc33995115c42f3388abd101bbe047634 Mon Sep 17 00:00:00 2001 From: rchlfryn Date: Wed, 4 Mar 2026 12:11:09 -0800 Subject: [PATCH 2/6] Remove step from onboarding doc --- docs/onboarding.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/onboarding.md b/docs/onboarding.md index 5908f8ab0..d81449b3a 100644 --- a/docs/onboarding.md +++ b/docs/onboarding.md @@ -27,15 +27,6 @@ This outlines steps required when a new center (tenant) comes on board. This doc Manual Troubleshoot:
In Vercel, go to the project → Storage → production Edge Config and edit file - - New tenant option shown in TenantSelectionProvider - Manual - Refresh page - - - Future automation - Use syncTenants - Create documents for Global Collections Manual From e374d56557d56510c8f4031f748fd25b5f5cb691 Mon Sep 17 00:00:00 2001 From: rchlfryn Date: Wed, 4 Mar 2026 15:57:49 -0800 Subject: [PATCH 3/6] add tests for tenant selector on dashboard and check if tenant selector updates --- .../tenant-selector/dashboard.e2e.spec.ts | 73 ++++++++++++++++++ .../tenant-selector/sync-on-save.e2e.spec.ts | 74 +++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 __tests__/e2e/admin/tenant-selector/dashboard.e2e.spec.ts create mode 100644 __tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts diff --git a/__tests__/e2e/admin/tenant-selector/dashboard.e2e.spec.ts b/__tests__/e2e/admin/tenant-selector/dashboard.e2e.spec.ts new file mode 100644 index 000000000..247125065 --- /dev/null +++ b/__tests__/e2e/admin/tenant-selector/dashboard.e2e.spec.ts @@ -0,0 +1,73 @@ +import { expect, tenantSelectorTest as test } from '../../fixtures/tenant-selector.fixture' +import { AdminUrlUtil, CollectionSlugs } from '../../helpers' + +/** + * Dashboard Tests + * + * Tests that the tenant selector is visible and functional on the admin dashboard. + * Previously, the selector was hidden on the dashboard because viewType was never + * set to 'dashboard'. Fixed by detecting dashboard from URL params instead. + * + * Related: https://github.com/NWACus/web/issues/691 + */ + +test.describe.configure({ timeout: 90000 }) + +test.describe('Tenant selector on dashboard', () => { + test('should be visible on the dashboard for super admin', async ({ + loginAs, + isTenantSelectorVisible, + getTenantOptions, + }) => { + const page = await loginAs('superAdmin') + const url = new AdminUrlUtil('http://localhost:3000', CollectionSlugs.pages) + + await page.goto(url.dashboard) + await page.waitForLoadState('networkidle') + + const isVisible = await isTenantSelectorVisible(page) + expect(isVisible).toBe(true) + + const options = await getTenantOptions(page) + expect(options.length).toBeGreaterThanOrEqual(4) + + await page.context().close() + }) + + test('should be visible on the dashboard for multi-tenant admin', async ({ + loginAs, + isTenantSelectorVisible, + getTenantOptions, + }) => { + const page = await loginAs('multiTenantAdmin') + const url = new AdminUrlUtil('http://localhost:3000', CollectionSlugs.pages) + + await page.goto(url.dashboard) + await page.waitForLoadState('networkidle') + + const isVisible = await isTenantSelectorVisible(page) + expect(isVisible).toBe(true) + + const options = await getTenantOptions(page) + expect(options.length).toBeGreaterThanOrEqual(4) + + await page.context().close() + }) + + test('should be hidden on dashboard for single-tenant users', async ({ + loginAs, + isTenantSelectorVisible, + }) => { + const page = await loginAs('singleTenantAdmin') + const url = new AdminUrlUtil('http://localhost:3000', CollectionSlugs.pages) + + await page.goto(url.dashboard) + await page.waitForLoadState('networkidle') + + // Single-tenant users have only 1 option, so selector is hidden + const isVisible = await isTenantSelectorVisible(page) + expect(isVisible).toBe(false) + + await page.context().close() + }) +}) diff --git a/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts b/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts new file mode 100644 index 000000000..cdc626364 --- /dev/null +++ b/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts @@ -0,0 +1,74 @@ +import { openNav } from '../../fixtures/nav.fixture' +import { + expect, + TenantNames, + tenantSelectorTest as test, +} from '../../fixtures/tenant-selector.fixture' +import { AdminUrlUtil, CollectionSlugs, saveDocAndAssert, waitForFormReady } from '../../helpers' + +/** + * Sync on Save Tests + * + * Tests that the tenant selector dropdown refreshes automatically when a + * tenant is saved (created or updated), without requiring a page reload. + * + * The SyncTenantsOnSave component watches useDocumentInfo().lastUpdateTime + * and calls syncTenants() when it changes. + */ + +test.describe.configure({ timeout: 90000 }) + +test.describe('Tenant selector syncs on save', () => { + test('should update tenant name in selector after save without hard refresh', async ({ + loginAs, + getTenantOptions, + }) => { + const page = await loginAs('superAdmin') + const url = new AdminUrlUtil('http://localhost:3000', CollectionSlugs.tenants) + + // Go to tenant list and find DVAC + await page.goto(url.list) + await page.waitForLoadState('networkidle') + + // Click on DVAC to edit it + await page.locator(`text=${TenantNames.dvac}`).click() + await page.waitForLoadState('networkidle') + await waitForFormReady(page) + + // Store the original name + const nameField = page.locator('#field-name') + const originalName = await nameField.inputValue() + + // Change the name and save + const tempName = 'DVAC Renamed Test' + await nameField.fill(tempName) + await saveDocAndAssert(page) + + // Wait for syncTenants to complete its fetch + await page.waitForTimeout(1000) + + // Open nav and click through to a tenant-scoped collection via client-side navigation + // The selector should already have the updated name from syncTenants() + await openNav(page) + const pagesUrl = new AdminUrlUtil('http://localhost:3000', CollectionSlugs.pages) + await page.locator(`nav a[href="${pagesUrl.list}"]`).click() + await page.waitForLoadState('networkidle') + + // The tenant selector should show the updated name without a page reload + const options = await getTenantOptions(page) + expect(options).toContain(tempName) + expect(options).not.toContain(originalName) + + // Restore the original name + await page.goto(url.list) + await page.waitForLoadState('networkidle') + await page.locator(`text=${tempName}`).click() + await page.waitForLoadState('networkidle') + await waitForFormReady(page) + + await page.locator('#field-name').fill(originalName) + await saveDocAndAssert(page) + + await page.context().close() + }) +}) From c907be3a12e7297a08a1bf26cad19846f2279804 Mon Sep 17 00:00:00 2001 From: rchlfryn Date: Wed, 4 Mar 2026 16:47:07 -0800 Subject: [PATCH 4/6] fix failing test --- .../tenant-selector/dashboard.e2e.spec.ts | 2 +- .../tenant-selector/sync-on-save.e2e.spec.ts | 75 ++++++++++--------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/__tests__/e2e/admin/tenant-selector/dashboard.e2e.spec.ts b/__tests__/e2e/admin/tenant-selector/dashboard.e2e.spec.ts index 247125065..1de1ce610 100644 --- a/__tests__/e2e/admin/tenant-selector/dashboard.e2e.spec.ts +++ b/__tests__/e2e/admin/tenant-selector/dashboard.e2e.spec.ts @@ -49,7 +49,7 @@ test.describe('Tenant selector on dashboard', () => { expect(isVisible).toBe(true) const options = await getTenantOptions(page) - expect(options.length).toBeGreaterThanOrEqual(4) + expect(options.length).toBe(2) // NWAC and SNFAC await page.context().close() }) diff --git a/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts b/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts index cdc626364..91f44e2be 100644 --- a/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts +++ b/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts @@ -1,10 +1,12 @@ import { openNav } from '../../fixtures/nav.fixture' +import { expect, tenantSelectorTest as test } from '../../fixtures/tenant-selector.fixture' import { - expect, + AdminUrlUtil, + CollectionSlugs, + saveDocAndAssert, TenantNames, - tenantSelectorTest as test, -} from '../../fixtures/tenant-selector.fixture' -import { AdminUrlUtil, CollectionSlugs, saveDocAndAssert, waitForFormReady } from '../../helpers' + waitForFormReady, +} from '../../helpers' /** * Sync on Save Tests @@ -16,58 +18,63 @@ import { AdminUrlUtil, CollectionSlugs, saveDocAndAssert, waitForFormReady } fro * and calls syncTenants() when it changes. */ +const DVAC_ORIGINAL_NAME = TenantNames.dvac + test.describe.configure({ timeout: 90000 }) test.describe('Tenant selector syncs on save', () => { - test('should update tenant name in selector after save without hard refresh', async ({ + test('should update tenant name in selector after renaming a tenant', async ({ loginAs, getTenantOptions, }) => { const page = await loginAs('superAdmin') - const url = new AdminUrlUtil('http://localhost:3000', CollectionSlugs.tenants) + const tenantsUrl = new AdminUrlUtil('http://localhost:3000', CollectionSlugs.tenants) - // Go to tenant list and find DVAC - await page.goto(url.list) - await page.waitForLoadState('networkidle') + // Find the DVAC tenant by slug (resilient to name changes from prior failed runs) + const response = await page.request.get( + 'http://localhost:3000/api/tenants?where[slug][equals]=dvac&limit=1', + ) + const data = await response.json() + const tenantId = data.docs[0].id - // Click on DVAC to edit it - await page.locator(`text=${TenantNames.dvac}`).click() + // Ensure DVAC has its canonical name before starting (cleanup from prior failed runs) + if (data.docs[0].name !== DVAC_ORIGINAL_NAME) { + await page.request.patch(`http://localhost:3000/api/tenants/${tenantId}`, { + data: { name: DVAC_ORIGINAL_NAME }, + }) + } + + // Navigate to the tenant edit page + await page.goto(tenantsUrl.edit(tenantId)) await page.waitForLoadState('networkidle') await waitForFormReady(page) - // Store the original name - const nameField = page.locator('#field-name') - const originalName = await nameField.inputValue() - - // Change the name and save + // Change the name const tempName = 'DVAC Renamed Test' + const nameField = page.locator('#field-name') await nameField.fill(tempName) - await saveDocAndAssert(page) - // Wait for syncTenants to complete its fetch - await page.waitForTimeout(1000) + // Save - SyncTenantsOnSave should refresh the selector without a page reload + await saveDocAndAssert(page) - // Open nav and click through to a tenant-scoped collection via client-side navigation - // The selector should already have the updated name from syncTenants() + // Navigate to a tenant-scoped collection via client-side nav link (NOT page.goto) + // so we stay in the same SPA session and verify the synced React state await openNav(page) - const pagesUrl = new AdminUrlUtil('http://localhost:3000', CollectionSlugs.pages) - await page.locator(`nav a[href="${pagesUrl.list}"]`).click() + await Promise.all([ + page.waitForURL('**/admin/collections/pages'), + page.locator('nav a[href="/admin/collections/pages"]').click(), + ]) await page.waitForLoadState('networkidle') - // The tenant selector should show the updated name without a page reload + // The tenant selector should show the updated name without a full page reload const options = await getTenantOptions(page) expect(options).toContain(tempName) - expect(options).not.toContain(originalName) - - // Restore the original name - await page.goto(url.list) - await page.waitForLoadState('networkidle') - await page.locator(`text=${tempName}`).click() - await page.waitForLoadState('networkidle') - await waitForFormReady(page) + expect(options).not.toContain(DVAC_ORIGINAL_NAME) - await page.locator('#field-name').fill(originalName) - await saveDocAndAssert(page) + // Restore the original name via API (reliable cleanup) + await page.request.patch(`http://localhost:3000/api/tenants/${tenantId}`, { + data: { name: DVAC_ORIGINAL_NAME }, + }) await page.context().close() }) From 852742609545c6843cd354a311808f82f6476fa3 Mon Sep 17 00:00:00 2001 From: rchlfryn Date: Mon, 9 Mar 2026 20:54:54 -0700 Subject: [PATCH 5/6] Fix failing test --- .../tenant-selector/sync-on-save.e2e.spec.ts | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts b/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts index 91f44e2be..63c2ef8c2 100644 --- a/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts +++ b/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts @@ -19,6 +19,7 @@ import { */ const DVAC_ORIGINAL_NAME = TenantNames.dvac +const TEMP_NAME = 'DVAC Renamed Test' test.describe.configure({ timeout: 90000 }) @@ -44,38 +45,38 @@ test.describe('Tenant selector syncs on save', () => { }) } - // Navigate to the tenant edit page - await page.goto(tenantsUrl.edit(tenantId)) - await page.waitForLoadState('networkidle') - await waitForFormReady(page) + try { + // Navigate to the tenant edit page + await page.goto(tenantsUrl.edit(tenantId)) + await page.waitForLoadState('networkidle') + await waitForFormReady(page) - // Change the name - const tempName = 'DVAC Renamed Test' - const nameField = page.locator('#field-name') - await nameField.fill(tempName) + // Change the name + const nameField = page.locator('#field-name') + await nameField.fill(TEMP_NAME) - // Save - SyncTenantsOnSave should refresh the selector without a page reload - await saveDocAndAssert(page) + // Save - SyncTenantsOnSave should refresh the selector without a page reload + await saveDocAndAssert(page) - // Navigate to a tenant-scoped collection via client-side nav link (NOT page.goto) - // so we stay in the same SPA session and verify the synced React state - await openNav(page) - await Promise.all([ - page.waitForURL('**/admin/collections/pages'), - page.locator('nav a[href="/admin/collections/pages"]').click(), - ]) - await page.waitForLoadState('networkidle') + // Navigate to a tenant-scoped collection via client-side nav link (NOT page.goto) + // so we stay in the same SPA session and verify the synced React state + await openNav(page) + await Promise.all([ + page.waitForURL('**/admin/collections/pages'), + page.locator('nav a[href="/admin/collections/pages"]').click(), + ]) + await page.waitForLoadState('networkidle') - // The tenant selector should show the updated name without a full page reload - const options = await getTenantOptions(page) - expect(options).toContain(tempName) - expect(options).not.toContain(DVAC_ORIGINAL_NAME) - - // Restore the original name via API (reliable cleanup) - await page.request.patch(`http://localhost:3000/api/tenants/${tenantId}`, { - data: { name: DVAC_ORIGINAL_NAME }, - }) - - await page.context().close() + // The tenant selector should show the updated name without a full page reload + const options = await getTenantOptions(page) + expect(options).toContain(TEMP_NAME) + expect(options).not.toContain(DVAC_ORIGINAL_NAME) + } finally { + // Always restore the original name, even if the test fails mid-way + await page.request.patch(`http://localhost:3000/api/tenants/${tenantId}`, { + data: { name: DVAC_ORIGINAL_NAME }, + }) + await page.context().close() + } }) }) From a340ec4d27511aa6091bff08668eb6a9b40bf862 Mon Sep 17 00:00:00 2001 From: rchlfryn Date: Tue, 10 Mar 2026 09:11:39 -0700 Subject: [PATCH 6/6] Fix test to make new temp tenant --- .../tenant-selector/sync-on-save.e2e.spec.ts | 94 ++++++++++++------- 1 file changed, 59 insertions(+), 35 deletions(-) diff --git a/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts b/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts index 63c2ef8c2..5d8126e19 100644 --- a/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts +++ b/__tests__/e2e/admin/tenant-selector/sync-on-save.e2e.spec.ts @@ -1,12 +1,6 @@ import { openNav } from '../../fixtures/nav.fixture' import { expect, tenantSelectorTest as test } from '../../fixtures/tenant-selector.fixture' -import { - AdminUrlUtil, - CollectionSlugs, - saveDocAndAssert, - TenantNames, - waitForFormReady, -} from '../../helpers' +import { AdminUrlUtil, CollectionSlugs, saveDocAndAssert, waitForFormReady } from '../../helpers' /** * Sync on Save Tests @@ -18,48 +12,81 @@ import { * and calls syncTenants() when it changes. */ -const DVAC_ORIGINAL_NAME = TenantNames.dvac -const TEMP_NAME = 'DVAC Renamed Test' +const TEMP_TENANT_SLUG_CREATE = 'e2e-sync-create' +const TEMP_TENANT_NAME_CREATE = 'E2E Sync Create Center' + +const TEMP_TENANT_SLUG_EDIT = 'e2e-sync-edit' +const TEMP_TENANT_NAME_EDIT = 'E2E Sync Edit Center' +const UPDATED_TENANT_NAME = 'E2E Sync Edit Renamed' test.describe.configure({ timeout: 90000 }) test.describe('Tenant selector syncs on save', () => { - test('should update tenant name in selector after renaming a tenant', async ({ + test('should show new tenant in selector after creation', async ({ loginAs, getTenantOptions, }) => { const page = await loginAs('superAdmin') const tenantsUrl = new AdminUrlUtil('http://localhost:3000', CollectionSlugs.tenants) - // Find the DVAC tenant by slug (resilient to name changes from prior failed runs) - const response = await page.request.get( - 'http://localhost:3000/api/tenants?where[slug][equals]=dvac&limit=1', - ) - const data = await response.json() - const tenantId = data.docs[0].id + try { + // Navigate to create a new tenant + await page.goto(tenantsUrl.create) + await page.waitForLoadState('networkidle') + await waitForFormReady(page) + + await page.locator('#field-name').fill(TEMP_TENANT_NAME_CREATE) + await page.locator('#field-slug').fill(TEMP_TENANT_SLUG_CREATE) + await saveDocAndAssert(page) + + // Navigate to a tenant-scoped collection via client-side nav link (NOT page.goto) + await openNav(page) + await Promise.all([ + page.waitForURL('**/admin/collections/pages'), + page.locator('nav a[href="/admin/collections/pages"]').click(), + ]) + await page.waitForLoadState('networkidle') - // Ensure DVAC has its canonical name before starting (cleanup from prior failed runs) - if (data.docs[0].name !== DVAC_ORIGINAL_NAME) { - await page.request.patch(`http://localhost:3000/api/tenants/${tenantId}`, { - data: { name: DVAC_ORIGINAL_NAME }, - }) + // The tenant selector should show the new tenant without a full page reload + const options = await getTenantOptions(page) + expect(options).toContain(TEMP_TENANT_NAME_CREATE) + } finally { + // Clean up: delete the temporary tenant + const response = await page.request.get( + `http://localhost:3000/api/tenants?where[slug][equals]=${TEMP_TENANT_SLUG_CREATE}&limit=1`, + ) + const data = await response.json() + if (data.docs?.[0]) { + await page.request.delete(`http://localhost:3000/api/tenants/${data.docs[0].id}`) + } + await page.context().close() } + }) + + test('should update tenant name in selector after editing', async ({ + loginAs, + getTenantOptions, + }) => { + const page = await loginAs('superAdmin') + const tenantsUrl = new AdminUrlUtil('http://localhost:3000', CollectionSlugs.tenants) + + // Create a temporary tenant via API + const createResponse = await page.request.post('http://localhost:3000/api/tenants', { + data: { name: TEMP_TENANT_NAME_EDIT, slug: TEMP_TENANT_SLUG_EDIT }, + }) + const { doc: tenant } = await createResponse.json() try { // Navigate to the tenant edit page - await page.goto(tenantsUrl.edit(tenantId)) + await page.goto(tenantsUrl.edit(tenant.id)) await page.waitForLoadState('networkidle') await waitForFormReady(page) - // Change the name - const nameField = page.locator('#field-name') - await nameField.fill(TEMP_NAME) - - // Save - SyncTenantsOnSave should refresh the selector without a page reload + // Rename the tenant + await page.locator('#field-name').fill(UPDATED_TENANT_NAME) await saveDocAndAssert(page) // Navigate to a tenant-scoped collection via client-side nav link (NOT page.goto) - // so we stay in the same SPA session and verify the synced React state await openNav(page) await Promise.all([ page.waitForURL('**/admin/collections/pages'), @@ -67,15 +94,12 @@ test.describe('Tenant selector syncs on save', () => { ]) await page.waitForLoadState('networkidle') - // The tenant selector should show the updated name without a full page reload const options = await getTenantOptions(page) - expect(options).toContain(TEMP_NAME) - expect(options).not.toContain(DVAC_ORIGINAL_NAME) + expect(options).toContain(UPDATED_TENANT_NAME) + expect(options).not.toContain(TEMP_TENANT_NAME_EDIT) } finally { - // Always restore the original name, even if the test fails mid-way - await page.request.patch(`http://localhost:3000/api/tenants/${tenantId}`, { - data: { name: DVAC_ORIGINAL_NAME }, - }) + // Clean up: delete the temporary tenant + await page.request.delete(`http://localhost:3000/api/tenants/${tenant.id}`) await page.context().close() } })