|
|
@@ -54,6 +54,33 @@ test.describe('教学实施模块状态化 UI 契约', () => { |
|
|
expect(keywordBox!.width).toBeGreaterThan(230) |
|
|
expect(keywordBox!.width).toBeGreaterThan(230) |
|
|
expect(keywordBox!.width).toBeLessThanOrEqual(270) |
|
|
expect(keywordBox!.width).toBeLessThanOrEqual(270) |
|
|
expect(createBox!.x + createBox!.width).toBeGreaterThan(toolbarBox!.x + toolbarBox!.width - 40) |
|
|
expect(createBox!.x + createBox!.width).toBeGreaterThan(toolbarBox!.x + toolbarBox!.width - 40) |
|
|
|
|
|
|
|
|
|
|
|
if (path === '/teaching/virtual-training') { |
|
|
|
|
|
const grid = page.locator('.ttc-assignment-grid') |
|
|
|
|
|
const card = page.getByTestId('teaching-assignment-TASK-VIRTUAL-001') |
|
|
|
|
|
const assignmentRunRequests = () => contract.requests.filter((request) => request.method === 'GET' |
|
|
|
|
|
&& request.path === '/runs' |
|
|
|
|
|
&& request.query.get('assignmentId') === 'assignment-virtual-common').length |
|
|
|
|
|
const assignmentRunRequestsBefore = assignmentRunRequests() |
|
|
|
|
|
await expect(grid).toHaveClass(/ttc-assignment-grid--single/) |
|
|
|
|
|
await expect(card).toHaveClass(/virtual-prototype-card/) |
|
|
|
|
|
await expect(card).toContainText('固化定义版本 2.0') |
|
|
|
|
|
await expect(card.locator('.ttc-mode-summary')).toHaveText('演示 / 训练 / 模拟考试') |
|
|
|
|
|
await expect(card).toContainText('分配学员') |
|
|
|
|
|
expect(assignmentRunRequestsBefore).toBe(0) |
|
|
|
|
|
|
|
|
|
|
|
const detailsButton = card.getByRole('button', { name: /学员明细 1/ }) |
|
|
|
|
|
await expect(detailsButton).toHaveAttribute('aria-expanded', 'false') |
|
|
|
|
|
await detailsButton.click() |
|
|
|
|
|
const details = card.locator('.ttc-assignment-runs') |
|
|
|
|
|
await expect(details).toBeVisible() |
|
|
|
|
|
await expect(details.locator('tbody tr')).toHaveCount(1) |
|
|
|
|
|
await expect(card.locator('.ttc-progress-block')).toContainText('100%') |
|
|
|
|
|
await expect.poll(assignmentRunRequests).toBeGreaterThan(assignmentRunRequestsBefore) |
|
|
|
|
|
await expect(detailsButton).toHaveAttribute('aria-expanded', 'true') |
|
|
|
|
|
await detailsButton.click() |
|
|
|
|
|
await expect(details).toHaveCount(0) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
await expect(page.locator('iframe')).toHaveCount(0) |
|
|
await expect(page.locator('iframe')).toHaveCount(0) |
|
|
} |
|
|
} |
|
|
@@ -61,7 +88,7 @@ test.describe('教学实施模块状态化 UI 契约', () => { |
|
|
await page.goto('/teaching/virtual-training') |
|
|
await page.goto('/teaching/virtual-training') |
|
|
await page.getByTestId('teaching-tab-guides').click() |
|
|
await page.getByTestId('teaching-tab-guides').click() |
|
|
await expect(page).toHaveURL(/\/teaching\/virtual-training\?view=guides$/) |
|
|
await expect(page).toHaveURL(/\/teaching\/virtual-training\?view=guides$/) |
|
|
await expect(page.getByRole('heading', { name: '虚拟仿真作业指导书', exact: true })).toBeVisible() |
|
|
|
|
|
|
|
|
await expect(page.getByRole('heading', { name: '虚拟仿真可用指导书', exact: true })).toBeVisible() |
|
|
await expect(page.getByTestId('teaching-guide-library').locator('.guide-card-v2')).toHaveCount(1) |
|
|
await expect(page.getByTestId('teaching-guide-library').locator('.guide-card-v2')).toHaveCount(1) |
|
|
await page.getByRole('button', { name: /打开阅读/ }).first().click() |
|
|
await page.getByRole('button', { name: /打开阅读/ }).first().click() |
|
|
const guideReader = page.locator('.guide-reader-v2') |
|
|
const guideReader = page.locator('.guide-reader-v2') |
|
|
@@ -262,7 +289,7 @@ test.describe('教学实施模块状态化 UI 契约', () => { |
|
|
expect(contract.count('GET', '/runs')).toBe(0) |
|
|
expect(contract.count('GET', '/runs')).toBe(0) |
|
|
expect(contract.administrativeRunsRejected).toBe(0) |
|
|
expect(contract.administrativeRunsRejected).toBe(0) |
|
|
|
|
|
|
|
|
await page.getByRole('button', { name: '查看', exact: true }).click() |
|
|
|
|
|
|
|
|
await page.getByRole('button', { name: '任务详情', exact: true }).click() |
|
|
const drawer = page.locator('.el-drawer:visible') |
|
|
const drawer = page.locator('.el-drawer:visible') |
|
|
await expect(drawer).toContainText('虚拟维修训练') |
|
|
await expect(drawer).toContainText('虚拟维修训练') |
|
|
await expect(drawer.getByText('执行与评定记录', { exact: true })).toHaveCount(0) |
|
|
await expect(drawer.getByText('执行与评定记录', { exact: true })).toHaveCount(0) |
|
|
@@ -325,10 +352,10 @@ test.describe('教学实施模块状态化 UI 契约', () => { |
|
|
|
|
|
|
|
|
await switchIdentity(page, contract, 'teacher') |
|
|
await switchIdentity(page, contract, 'teacher') |
|
|
await page.goto('/teaching/virtual-training') |
|
|
await page.goto('/teaching/virtual-training') |
|
|
await page.getByRole('button', { name: '查看', exact: true }).click() |
|
|
|
|
|
const drawer = page.locator('.el-drawer:visible') |
|
|
|
|
|
await expect(drawer.getByRole('button', { name: '评定', exact: true })).toBeVisible() |
|
|
|
|
|
await drawer.getByRole('button', { name: '评定', exact: true }).click() |
|
|
|
|
|
|
|
|
await page.getByRole('button', { name: /学员明细/ }).click() |
|
|
|
|
|
const details = page.locator('.ttc-assignment-runs') |
|
|
|
|
|
await expect(details.getByRole('button', { name: '评定', exact: true })).toBeVisible() |
|
|
|
|
|
await details.getByRole('button', { name: '评定', exact: true }).click() |
|
|
await visibleDialog(page).getByRole('button', { name: '提交评定', exact: true }).click() |
|
|
await visibleDialog(page).getByRole('button', { name: '提交评定', exact: true }).click() |
|
|
await expect(page.getByText('评定结果已提交', { exact: true })).toBeVisible() |
|
|
await expect(page.getByText('评定结果已提交', { exact: true })).toBeVisible() |
|
|
expect(contract.runs.get('run-virtual-common')?.status).toBe('REVIEWED') |
|
|
expect(contract.runs.get('run-virtual-common')?.status).toBe('REVIEWED') |
|
|
@@ -342,16 +369,16 @@ test.describe('教学实施模块状态化 UI 契约', () => { |
|
|
await contract.install(page) |
|
|
await contract.install(page) |
|
|
|
|
|
|
|
|
await page.goto('/teaching/virtual-training') |
|
|
await page.goto('/teaching/virtual-training') |
|
|
await page.getByRole('button', { name: '查看', exact: true }).click() |
|
|
|
|
|
|
|
|
await page.getByRole('button', { name: '任务详情', exact: true }).click() |
|
|
await expect(page.locator('.el-drawer:visible').getByRole('button', { name: '超时结算', exact: true })).toHaveCount(0) |
|
|
await expect(page.locator('.el-drawer:visible').getByRole('button', { name: '超时结算', exact: true })).toHaveCount(0) |
|
|
expect(contract.count('POST', '/runs/run-timeout/timeout')).toBe(0) |
|
|
expect(contract.count('POST', '/runs/run-timeout/timeout')).toBe(0) |
|
|
|
|
|
|
|
|
await switchIdentity(page, contract, 'teacher') |
|
|
await switchIdentity(page, contract, 'teacher') |
|
|
await page.getByRole('button', { name: '查看', exact: true }).click() |
|
|
|
|
|
const drawer = page.locator('.el-drawer:visible') |
|
|
|
|
|
await expect(drawer.getByRole('button', { name: '超时结算', exact: true })).toBeVisible() |
|
|
|
|
|
await expect(drawer.getByRole('button', { name: '结束运行', exact: true })).toHaveCount(0) |
|
|
|
|
|
await drawer.getByRole('button', { name: '超时结算', exact: true }).click() |
|
|
|
|
|
|
|
|
await page.getByRole('button', { name: /学员明细/ }).click() |
|
|
|
|
|
let details = page.locator('.ttc-assignment-runs') |
|
|
|
|
|
await expect(details.getByRole('button', { name: '超时结算', exact: true })).toBeVisible() |
|
|
|
|
|
await expect(details.getByRole('button', { name: '结束运行', exact: true })).toHaveCount(0) |
|
|
|
|
|
await details.getByRole('button', { name: '超时结算', exact: true }).click() |
|
|
const timeoutDialog = visibleDialog(page) |
|
|
const timeoutDialog = visibleDialog(page) |
|
|
await expect(timeoutDialog.getByRole('heading', { name: '确认超时结算', exact: true })).toBeVisible() |
|
|
await expect(timeoutDialog.getByRole('heading', { name: '确认超时结算', exact: true })).toBeVisible() |
|
|
await expect(timeoutDialog).toContainText('结算后该运行进入待评定状态') |
|
|
await expect(timeoutDialog).toContainText('结算后该运行进入待评定状态') |
|
|
@@ -359,11 +386,12 @@ test.describe('教学实施模块状态化 UI 契约', () => { |
|
|
|
|
|
|
|
|
await expect(page.getByText('运行已超时结算,可继续进行评定', { exact: true })).toBeVisible() |
|
|
await expect(page.getByText('运行已超时结算,可继续进行评定', { exact: true })).toBeVisible() |
|
|
await expect(timeoutDialog).toBeHidden() |
|
|
await expect(timeoutDialog).toBeHidden() |
|
|
await expect(drawer.locator('.el-loading-mask:visible')).toHaveCount(0) |
|
|
|
|
|
expect(contract.last('POST', '/runs/run-timeout/timeout')?.body).toEqual({ version: 2 }) |
|
|
expect(contract.last('POST', '/runs/run-timeout/timeout')?.body).toEqual({ version: 2 }) |
|
|
expect(contract.runs.get('run-timeout')?.status).toBe('SUBMITTED') |
|
|
expect(contract.runs.get('run-timeout')?.status).toBe('SUBMITTED') |
|
|
await expect(drawer.getByRole('button', { name: '评定', exact: true })).toBeVisible() |
|
|
|
|
|
await drawer.getByRole('button', { name: '评定', exact: true }).click() |
|
|
|
|
|
|
|
|
await page.getByRole('button', { name: /学员明细/ }).click() |
|
|
|
|
|
details = page.locator('.ttc-assignment-runs') |
|
|
|
|
|
await expect(details.getByRole('button', { name: '评定', exact: true })).toBeVisible() |
|
|
|
|
|
await details.getByRole('button', { name: '评定', exact: true }).click() |
|
|
await visibleDialog(page).getByRole('button', { name: '提交评定', exact: true }).click() |
|
|
await visibleDialog(page).getByRole('button', { name: '提交评定', exact: true }).click() |
|
|
await expect(page.getByText('评定结果已提交', { exact: true })).toBeVisible() |
|
|
await expect(page.getByText('评定结果已提交', { exact: true })).toBeVisible() |
|
|
expect(contract.runs.get('run-timeout')?.status).toBe('REVIEWED') |
|
|
expect(contract.runs.get('run-timeout')?.status).toBe('REVIEWED') |
|
|
@@ -377,11 +405,11 @@ test.describe('教学实施模块状态化 UI 契约', () => { |
|
|
await contract.install(page) |
|
|
await contract.install(page) |
|
|
|
|
|
|
|
|
await page.goto('/teaching/virtual-training') |
|
|
await page.goto('/teaching/virtual-training') |
|
|
await page.getByRole('button', { name: '查看', exact: true }).click() |
|
|
|
|
|
const drawer = page.locator('.el-drawer:visible') |
|
|
|
|
|
await expect(drawer.getByRole('button', { name: '超时结算', exact: true })).toHaveCount(0) |
|
|
|
|
|
await expect(drawer.getByRole('button', { name: '结束运行', exact: true })).toBeVisible() |
|
|
|
|
|
await drawer.getByRole('button', { name: '结束运行', exact: true }).click() |
|
|
|
|
|
|
|
|
await page.getByRole('button', { name: /学员明细/ }).click() |
|
|
|
|
|
let details = page.locator('.ttc-assignment-runs') |
|
|
|
|
|
await expect(details.getByRole('button', { name: '超时结算', exact: true })).toHaveCount(0) |
|
|
|
|
|
await expect(details.getByRole('button', { name: '结束运行', exact: true })).toBeVisible() |
|
|
|
|
|
await details.getByRole('button', { name: '结束运行', exact: true }).click() |
|
|
|
|
|
|
|
|
const terminateDialog = visibleDialog(page) |
|
|
const terminateDialog = visibleDialog(page) |
|
|
await expect(terminateDialog.getByRole('heading', { name: '结束运行', exact: true })).toBeVisible() |
|
|
await expect(terminateDialog.getByRole('heading', { name: '结束运行', exact: true })).toBeVisible() |
|
|
@@ -396,11 +424,12 @@ test.describe('教学实施模块状态化 UI 契约', () => { |
|
|
|
|
|
|
|
|
await expect(page.getByText('运行已结束并进入待评定状态', { exact: true })).toBeVisible() |
|
|
await expect(page.getByText('运行已结束并进入待评定状态', { exact: true })).toBeVisible() |
|
|
await expect(terminateDialog).toBeHidden() |
|
|
await expect(terminateDialog).toBeHidden() |
|
|
await expect(drawer.locator('.el-loading-mask:visible')).toHaveCount(0) |
|
|
|
|
|
expect(contract.last('POST', '/runs/run-terminate/terminate')?.body).toEqual({ version: 2, reason }) |
|
|
expect(contract.last('POST', '/runs/run-terminate/terminate')?.body).toEqual({ version: 2, reason }) |
|
|
expect(contract.runs.get('run-terminate')?.status).toBe('SUBMITTED') |
|
|
expect(contract.runs.get('run-terminate')?.status).toBe('SUBMITTED') |
|
|
await expect(drawer.getByRole('button', { name: '评定', exact: true })).toBeVisible() |
|
|
|
|
|
await drawer.getByRole('button', { name: '评定', exact: true }).click() |
|
|
|
|
|
|
|
|
await page.getByRole('button', { name: /学员明细/ }).click() |
|
|
|
|
|
details = page.locator('.ttc-assignment-runs') |
|
|
|
|
|
await expect(details.getByRole('button', { name: '评定', exact: true })).toBeVisible() |
|
|
|
|
|
await details.getByRole('button', { name: '评定', exact: true }).click() |
|
|
await visibleDialog(page).getByRole('button', { name: '提交评定', exact: true }).click() |
|
|
await visibleDialog(page).getByRole('button', { name: '提交评定', exact: true }).click() |
|
|
await expect(page.getByText('评定结果已提交', { exact: true })).toBeVisible() |
|
|
await expect(page.getByText('评定结果已提交', { exact: true })).toBeVisible() |
|
|
expect(contract.runs.get('run-terminate')?.status).toBe('REVIEWED') |
|
|
expect(contract.runs.get('run-terminate')?.status).toBe('REVIEWED') |
|
|
@@ -588,7 +617,7 @@ test.describe('教学实施模块状态化 UI 契约', () => { |
|
|
await expect(preflight).toContainText('当前身份仅可查看,无沉浸训练执行权限') |
|
|
await expect(preflight).toContainText('当前身份仅可查看,无沉浸训练执行权限') |
|
|
await expect(preflight).toContainText('不会创建服务端会话,也不会申请浏览器 WebXR 会话') |
|
|
await expect(preflight).toContainText('不会创建服务端会话,也不会申请浏览器 WebXR 会话') |
|
|
const desktopStarts = page.getByRole('button', { name: /只读查看,无法启动/ }) |
|
|
const desktopStarts = page.getByRole('button', { name: /只读查看,无法启动/ }) |
|
|
await expect(desktopStarts).toHaveCount(2) |
|
|
|
|
|
|
|
|
await expect(desktopStarts).toHaveCount(1) |
|
|
for (const button of await desktopStarts.all()) await expect(button).toBeDisabled() |
|
|
for (const button of await desktopStarts.all()) await expect(button).toBeDisabled() |
|
|
await expect(page.getByRole('button', { name: 'VR 沉浸模式' })).toBeDisabled() |
|
|
await expect(page.getByRole('button', { name: 'VR 沉浸模式' })).toBeDisabled() |
|
|
expect(contract.count('POST', /\/runs\/[^/]+\/xr-sessions$/)).toBe(0) |
|
|
expect(contract.count('POST', /\/runs\/[^/]+\/xr-sessions$/)).toBe(0) |
|
|
@@ -635,8 +664,13 @@ test.describe('教学实施模块状态化 UI 契约', () => { |
|
|
await chooseSelectOption(page, page.locator('.teaching-v2-filter-toolbar'), '全部渠道', '虚拟仿真') |
|
|
await chooseSelectOption(page, page.locator('.teaching-v2-filter-toolbar'), '全部渠道', '虚拟仿真') |
|
|
await chooseSelectOption(page, page.locator('.teaching-v2-filter-toolbar'), '全部状态', '已评定') |
|
|
await chooseSelectOption(page, page.locator('.teaching-v2-filter-toolbar'), '全部状态', '已评定') |
|
|
const toolbar = page.locator('.teaching-v2-filter-toolbar') |
|
|
const toolbar = page.locator('.teaching-v2-filter-toolbar') |
|
|
await toolbar.getByRole('combobox', { name: '开始时间' }).fill('2026-08-01 00:00:00') |
|
|
|
|
|
await toolbar.getByRole('combobox', { name: '结束时间' }).fill('2026-08-12 00:00:00') |
|
|
|
|
|
|
|
|
const startAtInput = toolbar.getByRole('combobox', { name: '开始时间' }) |
|
|
|
|
|
const endAtInput = toolbar.getByRole('combobox', { name: '结束时间' }) |
|
|
|
|
|
await startAtInput.fill('2026-08-01 00:00:00') |
|
|
|
|
|
await startAtInput.press('Enter') |
|
|
|
|
|
await endAtInput.fill('2026-08-12 00:00:00') |
|
|
|
|
|
await endAtInput.press('Enter') |
|
|
|
|
|
await page.keyboard.press('Escape') |
|
|
await page.getByRole('button', { name: '搜索', exact: true }).click() |
|
|
await page.getByRole('button', { name: '搜索', exact: true }).click() |
|
|
|
|
|
|
|
|
await expect.poll(() => contract.count('GET', '/analytics/records')).toBeGreaterThan(1) |
|
|
await expect.poll(() => contract.count('GET', '/analytics/records')).toBeGreaterThan(1) |
|
|
|