import type { Page } from '@playwright/test'; import { adminStorageStatePath, expect, rootStorageStatePath, test, } from './fixtures/cluster'; function nextBatchName(prefix: string) { return `${prefix}-${Date.now()}-${Math.floor(Math.random() * 1000)}`; } async function gotoUserPage(page: Page) { await page.goto('/console/user'); await expect(page.getByTestId('user-page')).toBeVisible(); } async function openMigrationTab(page: Page) { const migrationTab = page.getByTestId('user-migration-tab'); await expect(migrationTab).toBeVisible(); await migrationTab.click(); await expect(page.getByTestId('user-migration-center')).toBeVisible(); } async function selectCandidateUser(page: Page, userId: number) { const row = page.getByTestId(`migration-candidate-row-${userId}`); await expect(row).toBeVisible(); const checkboxInput = row.locator('input[type="checkbox"]'); if ((await checkboxInput.count()) > 0) { await checkboxInput.first().check({ force: true }); return; } await row.locator('.semi-checkbox').first().click(); } async function createBatch( page: Page, batchName: string, selectedUserIds: number[] = [], ) { await page.getByTestId('migration-create-batch-button').click(); await expect( page.getByTestId('migration-selection-mode-group'), ).toBeVisible(); if (selectedUserIds.length > 0) { await page.getByTestId('migration-selection-mode-explicit').click(); await expect(page.getByTestId('migration-candidate-table')).toBeVisible(); for (const userId of selectedUserIds) { await selectCandidateUser(page, userId); } } await page.getByTestId('migration-create-batch-submit').click(); const nameField = page.getByTestId('migration-batch-name-field'); await expect(nameField).toBeVisible(); await nameField.fill(batchName); await page.getByTestId('migration-create-batch-submit').click(); await expect(page.getByTestId('migration-batch-table')).toContainText( batchName, ); await page.getByTestId('migration-batch-table').getByText(batchName).click(); await expect(page.getByTestId('migration-execution-panel')).toBeVisible(); await expect(page.getByTestId('migration-batch-detail')).toBeVisible(); } test.describe('User Migration Access', () => { test.use({ storageState: adminStorageStatePath }); test('non-root admin cannot see migration tab', async ({ page }) => { await gotoUserPage(page); await expect(page.getByTestId('user-migration-tab')).toHaveCount(0); }); }); test.describe.serial('User Migration Smoke', () => { test.use({ storageState: rootStorageStatePath }); test('root can open migration center and create a batch', async ({ page, }) => { const batchName = nextBatchName('ui-create'); await gotoUserPage(page); await openMigrationTab(page); await createBatch(page, batchName); await expect(page.getByTestId('migration-batch-status')).toHaveText( 'draft', ); }); test('root can clear selected batch from the list', async ({ page }) => { const batchName = nextBatchName('ui-clear'); await gotoUserPage(page); await openMigrationTab(page); await createBatch(page, batchName); await expect(page.getByTestId('migration-batch-detail')).toBeVisible(); await page.getByTestId('migration-clear-batch-selection-button').click(); await expect(page.getByTestId('migration-batch-detail')).toHaveCount(0); await expect(page.getByTestId('migration-execution-panel')).toHaveCount(0); }); test('root can create explicit_ids batch from candidate users', async ({ page, }) => { const batchName = nextBatchName('ui-explicit'); await gotoUserPage(page); await openMigrationTab(page); await createBatch(page, batchName, [10000003, 10000004]); await expect(page.getByTestId('migration-batch-table')).toContainText( '指定 2 人', ); await expect(page.getByTestId('migration-selection-summary')).toContainText( '已选 2 人', ); }); test('root can cancel a batch and recreate selected users in a new batch', async ({ page, }) => { const firstBatchName = nextBatchName('ui-cancel-source'); const secondBatchName = nextBatchName('ui-cancel-recreate'); await gotoUserPage(page); await openMigrationTab(page); await createBatch(page, firstBatchName, [10000001, 10000002]); await page.getByTestId('migration-cancel-button').click(); await expect(page.getByTestId('migration-cancel-confirm')).toBeVisible(); await page.getByTestId('migration-cancel-confirm').click(); await expect(page.getByTestId('migration-batch-status')).toHaveText( 'cancelled', ); await createBatch(page, secondBatchName, [10000001, 10000002]); await expect(page.getByTestId('migration-batch-table')).toContainText( secondBatchName, ); }); test('root can scan, resolve conflicts, execute, and verify a batch', async ({ page, }) => { const batchName = nextBatchName('ui-flow'); await gotoUserPage(page); await openMigrationTab(page); await createBatch(page, batchName); await page.getByTestId('migration-scan-button').click(); await expect(page.getByTestId('migration-batch-status')).toHaveText( /scanning|scanned/, ); await expect(page.getByTestId('migration-batch-status')).toHaveText( 'scanned', { timeout: 25_000, }, ); await expect(page.getByTestId('migration-conflict-table')).toBeVisible(); await page.getByTestId('migration-resolve-skip-10000003').click(); await page.getByTestId('migration-resolve-skip-10000004').click(); await expect(page.getByTestId('migration-batch-status')).toHaveText( 'ready', ); await page.getByTestId('migration-execute-button').click(); await expect(page.getByTestId('migration-execute-confirm')).toBeVisible(); await page.getByTestId('migration-execute-confirm').click(); await expect(page.getByTestId('migration-batch-status')).toHaveText( /running|completed/, ); await expect(page.getByTestId('migration-batch-status')).toHaveText( 'completed', { timeout: 25_000, }, ); await page.getByTestId('migration-verify-button').click(); await expect( page.getByTestId('migration-verify-results-table'), ).toBeVisible(); await expect( page.getByTestId('migration-verify-results-table'), ).toContainText('10000001'); await expect( page.getByTestId('migration-batch-detail-table'), ).toContainText('migrated'); }); test('page reload keeps batch list and detail loading available', async ({ page, }) => { const batchName = nextBatchName('ui-refresh'); await gotoUserPage(page); await openMigrationTab(page); await createBatch(page, batchName); await page.getByTestId('migration-scan-button').click(); await expect(page.getByTestId('migration-batch-status')).toHaveText( 'scanned', { timeout: 25_000, }, ); await page.reload(); await gotoUserPage(page); await openMigrationTab(page); await expect(page.getByTestId('migration-batch-table')).toContainText( batchName, ); await page .getByTestId('migration-batch-table') .getByText(batchName) .click(); await expect(page.getByTestId('migration-batch-detail')).toBeVisible(); await expect(page.getByTestId('migration-batch-status')).toHaveText( 'scanned', ); }); });