mirror of
https://github.com/iflytek/skillhub.git
synced 2026-08-28 11:25:00 +00:00
Adds Playwright E2E tests for batch member import dialog: - Opens dialog and verifies upload step UI elements - Uploads CSV with mixed valid/invalid rows and verifies preview with validation messages Uses admin context pattern to create namespace and add test user as ADMIN member.
108 lines
4.6 KiB
TypeScript
108 lines
4.6 KiB
TypeScript
import { writeFileSync } from 'node:fs'
|
|
import { mkdtempSync, rmSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import path from 'node:path'
|
|
import { expect, test } from '@playwright/test'
|
|
import { setEnglishLocale } from './helpers/auth-fixtures'
|
|
import { loginWithCredentials, registerSession } from './helpers/session'
|
|
import { E2eTestDataBuilder } from './helpers/test-data-builder'
|
|
|
|
function getAdminCredentials() {
|
|
const username = process.env.E2E_ADMIN_USERNAME ?? process.env.BOOTSTRAP_ADMIN_USERNAME ?? 'admin'
|
|
const password = process.env.E2E_ADMIN_PASSWORD ?? process.env.BOOTSTRAP_ADMIN_PASSWORD ?? 'ChangeMe!2026'
|
|
return { username, password }
|
|
}
|
|
|
|
function createCsvFile(content: string): { filePath: string; cleanup: () => void } {
|
|
const tempDir = mkdtempSync(path.join(tmpdir(), 'skillhub-e2e-csv-'))
|
|
const filePath = path.join(tempDir, 'members.csv')
|
|
writeFileSync(filePath, content, 'utf8')
|
|
return {
|
|
filePath,
|
|
cleanup: () => rmSync(tempDir, { recursive: true, force: true }),
|
|
}
|
|
}
|
|
|
|
test.describe('Batch Import Members (Real API)', () => {
|
|
test.beforeEach(async ({ page }, testInfo) => {
|
|
await setEnglishLocale(page)
|
|
await registerSession(page, testInfo)
|
|
})
|
|
|
|
test('opens batch import dialog and shows upload step', async ({ page, browser }, testInfo) => {
|
|
const credentials = await registerSession(page, testInfo)
|
|
const builder = new E2eTestDataBuilder(page, testInfo)
|
|
await builder.init()
|
|
|
|
const adminContext = await browser.newContext()
|
|
const adminPage = await adminContext.newPage()
|
|
const adminBuilder = new E2eTestDataBuilder(adminPage, testInfo)
|
|
|
|
try {
|
|
await loginWithCredentials(adminPage, getAdminCredentials(), testInfo)
|
|
await adminBuilder.init()
|
|
|
|
const namespace = await adminBuilder.createNamespace('e2e-batch')
|
|
const candidates = await adminBuilder.searchNamespaceMemberCandidates(namespace.slug, credentials.username)
|
|
const matched = candidates.find((c) => c.userId === credentials.username || c.displayName === credentials.username) ?? candidates[0]
|
|
if (matched) {
|
|
await adminBuilder.addNamespaceMember(namespace.slug, matched.userId, 'ADMIN')
|
|
}
|
|
|
|
await page.goto(`/dashboard/namespaces/${namespace.slug}/members`)
|
|
await expect(page.getByRole('heading', { name: 'Member Management' })).toBeVisible()
|
|
|
|
const batchButton = page.getByRole('button', { name: 'Batch Import' })
|
|
await expect(batchButton).toBeVisible()
|
|
await batchButton.click()
|
|
|
|
await expect(page.getByText('Batch Import Members')).toBeVisible()
|
|
await expect(page.getByText('Download CSV template')).toBeVisible()
|
|
await expect(page.getByText('Drag a CSV file here')).toBeVisible()
|
|
} finally {
|
|
await builder.cleanup()
|
|
await adminBuilder.cleanup()
|
|
await adminContext.close()
|
|
}
|
|
})
|
|
|
|
test('uploads CSV and shows preview with validation', async ({ page, browser }, testInfo) => {
|
|
const credentials = await registerSession(page, testInfo)
|
|
const builder = new E2eTestDataBuilder(page, testInfo)
|
|
await builder.init()
|
|
const csv = createCsvFile('userId,role\nuser-valid-1,MEMBER\n,ADMIN\nuser-valid-2,BADROLE\n')
|
|
|
|
const adminContext = await browser.newContext()
|
|
const adminPage = await adminContext.newPage()
|
|
const adminBuilder = new E2eTestDataBuilder(adminPage, testInfo)
|
|
|
|
try {
|
|
await loginWithCredentials(adminPage, getAdminCredentials(), testInfo)
|
|
await adminBuilder.init()
|
|
|
|
const namespace = await adminBuilder.createNamespace('e2e-batch')
|
|
const candidates = await adminBuilder.searchNamespaceMemberCandidates(namespace.slug, credentials.username)
|
|
const matched = candidates.find((c) => c.userId === credentials.username || c.displayName === credentials.username) ?? candidates[0]
|
|
if (matched) {
|
|
await adminBuilder.addNamespaceMember(namespace.slug, matched.userId, 'ADMIN')
|
|
}
|
|
|
|
await page.goto(`/dashboard/namespaces/${namespace.slug}/members`)
|
|
await page.getByRole('button', { name: 'Batch Import' }).click()
|
|
await expect(page.getByText('Batch Import Members')).toBeVisible()
|
|
|
|
const fileInput = page.locator('input[type="file"][accept=".csv"]')
|
|
await fileInput.setInputFiles(csv.filePath)
|
|
|
|
await expect(page.getByText('Preview (3 rows)')).toBeVisible()
|
|
await expect(page.getByText('1 valid, 2 invalid')).toBeVisible()
|
|
await expect(page.getByText('Missing user ID')).toBeVisible()
|
|
await expect(page.getByText('Invalid role')).toBeVisible()
|
|
} finally {
|
|
csv.cleanup()
|
|
await builder.cleanup()
|
|
await adminBuilder.cleanup()
|
|
await adminContext.close()
|
|
}
|
|
})
|
|
})
|