This commit is contained in:
@@ -10,10 +10,33 @@ interface PoiCandidate {
|
||||
|
||||
interface GuideDiagnostics {
|
||||
isInitialModelReady: () => boolean
|
||||
getFloors: () => Array<{ floorId: string }>
|
||||
getReport: () => { floorId: string; activeFocusPoiId: string }
|
||||
getFloors: () => Array<{ floorId: string; label: string }>
|
||||
getReport: () => {
|
||||
floorId: string
|
||||
activeFocusPoiId: string
|
||||
activeView: 'overview' | 'floor' | 'multi'
|
||||
visibleVisualMarkerWithoutTextCount: number
|
||||
}
|
||||
resetToViewBaseline: (options: { view: 'floor'; floorId: string; reason: 'floor-reset' }) => Promise<unknown>
|
||||
switchFloor: (floorId: string) => Promise<unknown>
|
||||
showMultiFloor: () => Promise<unknown>
|
||||
getFloorPois: (floorId: string) => Promise<PoiCandidate[]>
|
||||
getVisiblePoiScreenPositions: () => Array<{
|
||||
poiId: string
|
||||
screen: { x: number; y: number }
|
||||
}>
|
||||
getPoiFocusState: (poiId?: string) => {
|
||||
poiId: string
|
||||
selectedPoiId: string
|
||||
dataTier: 'fast' | 'full' | null
|
||||
focusStartedDataTier: 'fast' | 'full' | null
|
||||
markerCount: number
|
||||
displayPositionCount: number
|
||||
baseAffordanceCount: number
|
||||
pulseAffordanceCount: number
|
||||
glowAffordanceCount: number
|
||||
modelHighlightRootCount: number
|
||||
}
|
||||
focusTargetPoi: (request: PoiCandidate & { requestId: string }) => Promise<unknown>
|
||||
clearTargetFocus: () => void
|
||||
}
|
||||
@@ -27,12 +50,57 @@ const openGuide = async (page: Page) => {
|
||||
)), { timeout: 180_000 }).toBe(true)
|
||||
}
|
||||
|
||||
const resetToActiveFloor = async (page: Page) => page.evaluate(async () => {
|
||||
const resetToFloorWithLandmarkLabels = async (page: Page) => page.evaluate(async () => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
if (!api) throw new Error('ThreeMap diagnostics unavailable')
|
||||
await api.resetToViewBaseline({ view: 'floor', floorId: api.getReport().floorId, reason: 'floor-reset' })
|
||||
return api.getReport().floorId
|
||||
|
||||
for (const floor of api.getFloors()) {
|
||||
const pois = await api.getFloorPois(floor.floorId)
|
||||
const hasLandmark = pois.some((poi) => poi.primaryCategory.startsWith('exhibition_hall'))
|
||||
if (!hasLandmark) continue
|
||||
|
||||
await api.resetToViewBaseline({ view: 'floor', floorId: floor.floorId, reason: 'floor-reset' })
|
||||
return floor.floorId
|
||||
}
|
||||
|
||||
throw new Error('No floor contains landmark POIs')
|
||||
})
|
||||
|
||||
const resetToB2WithDuplicatedBusinessName = async (page: Page) => page.evaluate(async () => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
if (!api) throw new Error('ThreeMap diagnostics unavailable')
|
||||
|
||||
const floor = api.getFloors().find((candidate) => candidate.label === 'B2')
|
||||
if (floor) {
|
||||
await api.resetToViewBaseline({ view: 'floor', floorId: floor.floorId, reason: 'floor-reset' })
|
||||
return floor.floorId
|
||||
}
|
||||
|
||||
throw new Error('B2 landmark floor is unavailable')
|
||||
})
|
||||
|
||||
const resetToFloorByLabel = async (page: Page, label: string) => page.evaluate(async (floorLabel) => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
if (!api) throw new Error('ThreeMap diagnostics unavailable')
|
||||
const floor = api.getFloors().find((candidate) => candidate.label === floorLabel)
|
||||
if (!floor) throw new Error(`Floor ${floorLabel} is unavailable`)
|
||||
|
||||
await api.resetToViewBaseline({ view: 'floor', floorId: floor.floorId, reason: 'floor-reset' })
|
||||
return floor.floorId
|
||||
}, label)
|
||||
|
||||
test('uses the current backend name for the exterior Honghuatan Road label', async ({ page }) => {
|
||||
await openGuide(page)
|
||||
|
||||
const roadLabel = page.locator(
|
||||
'[data-poi-label-kind="ambient"][data-poi-id="overview-honghua-road"]:visible'
|
||||
)
|
||||
await expect(roadLabel).toBeVisible({ timeout: 30_000 })
|
||||
await expect(roadLabel.locator('.three-poi-dom-label__title')).toHaveText('红花潭路')
|
||||
await expect(roadLabel.locator('.three-poi-dom-label__icon')).toHaveAttribute('data-poi-icon', 'road')
|
||||
})
|
||||
|
||||
for (const deviceScaleFactor of [1, 3]) {
|
||||
@@ -41,18 +109,49 @@ for (const deviceScaleFactor of [1, 3]) {
|
||||
|
||||
test('renders measured native labels and clears focused labels without residue', async ({ page }, testInfo) => {
|
||||
await openGuide(page)
|
||||
const floorId = await resetToActiveFloor(page)
|
||||
const floorId = await resetToFloorWithLandmarkLabels(page)
|
||||
await expect.poll(async () => page.evaluate(() => (
|
||||
(window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getPoiFocusState().dataTier || null
|
||||
)), { timeout: 30_000 }).toBe('full')
|
||||
// Exterior labels are scene-owned. Once an indoor floor commits, no
|
||||
// exterior DOM label may survive an earlier asynchronous load.
|
||||
await expect(page.locator('.three-poi-dom-label--overview')).toHaveCount(0)
|
||||
const labels = page.locator('[data-poi-label-kind="ambient"]:visible')
|
||||
await expect(labels.first()).toBeVisible({ timeout: 30_000 })
|
||||
|
||||
const hall = page.locator('[data-poi-label-kind="ambient"].three-poi-dom-label--hall:visible').first()
|
||||
const facility = page.locator('[data-poi-label-kind="ambient"].three-poi-dom-label--service').first()
|
||||
const markerPositions = new Map<string, { x: number; y: number }>()
|
||||
;(await page.evaluate(() => (
|
||||
(window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getVisiblePoiScreenPositions() || []
|
||||
))).forEach((marker) => {
|
||||
// A merged logical POI can have several physical markers, while its
|
||||
// single DOM label is anchored to the first (canonical) marker.
|
||||
if (!markerPositions.has(marker.poiId)) markerPositions.set(marker.poiId, marker.screen)
|
||||
})
|
||||
const labelCount = await labels.count()
|
||||
await expect(labels.first().locator('.three-poi-dom-label__icon')).toBeVisible()
|
||||
for (let index = 0; index < labelCount; index += 1) {
|
||||
const label = labels.nth(index)
|
||||
const poiId = await label.getAttribute('data-poi-id')
|
||||
const bounds = await label.boundingBox()
|
||||
const marker = poiId ? markerPositions.get(poiId) : null
|
||||
if (!bounds || !marker) continue
|
||||
|
||||
// Only the one calibrated B2 pair may use a compact screen-only
|
||||
// offset; all other labels keep their management-calibrated anchor.
|
||||
expect(Math.abs(bounds.x + bounds.width / 2 - marker.x)).toBeLessThanOrEqual(44)
|
||||
expect(Math.abs(bounds.y + bounds.height - marker.y)).toBeLessThanOrEqual(25)
|
||||
await expect(page.locator(`[data-poi-leader-for="${poiId}"]`)).toHaveCount(0)
|
||||
}
|
||||
|
||||
const hall = page.locator('[data-poi-label-kind="ambient"].three-poi-dom-label--landmark:visible').first()
|
||||
await expect(hall).toBeVisible()
|
||||
|
||||
for (const locator of [hall, facility]) {
|
||||
for (const locator of [hall]) {
|
||||
await expect(locator).toHaveCSS('pointer-events', 'none')
|
||||
const fontSize = await locator.evaluate((element) => Number.parseFloat(getComputedStyle(element).fontSize))
|
||||
expect(fontSize).toBeGreaterThanOrEqual(locator === hall ? 14 : 13)
|
||||
expect(fontSize).toBeGreaterThanOrEqual(12)
|
||||
}
|
||||
|
||||
for (const locator of [hall]) {
|
||||
@@ -64,7 +163,7 @@ for (const deviceScaleFactor of [1, 3]) {
|
||||
expect(box!.y + box!.height).toBeLessThanOrEqual(844)
|
||||
}
|
||||
|
||||
const poiId = await facility.getAttribute('data-poi-id')
|
||||
const poiId = await hall.getAttribute('data-poi-id')
|
||||
expect(poiId).toBeTruthy()
|
||||
await page.evaluate(async ({ floorId: targetFloorId, targetPoiId }) => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
@@ -75,11 +174,12 @@ for (const deviceScaleFactor of [1, 3]) {
|
||||
await api.focusTargetPoi({ ...poi, requestId: `poi-dom-label-${targetPoiId}` })
|
||||
}, { floorId, targetPoiId: poiId! })
|
||||
|
||||
const focus = page.locator('[data-poi-label-kind="focus"][data-focus-label="active"]')
|
||||
const focus = page.locator(`[data-poi-label-kind="ambient"][data-poi-id="${poiId}"]`)
|
||||
await expect(focus).toBeVisible({ timeout: 20_000 })
|
||||
await expect(focus).toHaveCSS('pointer-events', 'none')
|
||||
await expect(focus.locator('.three-poi-dom-label__title')).toHaveCSS('font-size', '16px')
|
||||
await expect(focus.locator('.three-poi-dom-label__meta')).toHaveCSS('font-size', '13px')
|
||||
await expect(focus).toHaveClass(/three-poi-dom-label--selected/)
|
||||
await expect(focus.locator('.three-poi-dom-label__title')).toHaveCSS('font-size', '12px')
|
||||
await expect(focus.locator('.three-poi-dom-label__meta')).toHaveCount(0)
|
||||
await page.screenshot({ path: testInfo.outputPath(`poi-dom-labels-dpr-${deviceScaleFactor}-focus.png`) })
|
||||
|
||||
await page.evaluate(() => {
|
||||
@@ -87,8 +187,311 @@ for (const deviceScaleFactor of [1, 3]) {
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
api?.clearTargetFocus()
|
||||
})
|
||||
await expect(focus).toHaveCount(0)
|
||||
await expect(focus).not.toHaveClass(/three-poi-dom-label--selected/)
|
||||
await page.screenshot({ path: testInfo.outputPath(`poi-dom-labels-dpr-${deviceScaleFactor}-ambient.png`) })
|
||||
})
|
||||
|
||||
test('keeps the B2 landmark pair separate and merges duplicate business labels', async ({ page }, testInfo) => {
|
||||
await openGuide(page)
|
||||
await resetToB2WithDuplicatedBusinessName(page)
|
||||
|
||||
const vr = page.locator('[data-poi-label-kind="ambient"]', { hasText: 'XR虚拟现实影院' })
|
||||
const youth = page.locator('[data-poi-label-kind="ambient"]', { hasText: '青少年探索中心' })
|
||||
await expect(vr).toBeVisible({ timeout: 30_000 })
|
||||
await expect(youth).toBeVisible({ timeout: 30_000 })
|
||||
|
||||
await expect.poll(async () => {
|
||||
const vrBox = await vr.boundingBox()
|
||||
const youthBox = await youth.boundingBox()
|
||||
if (!vrBox || !youthBox) return null
|
||||
return vrBox.x < youthBox.x + youthBox.width
|
||||
&& vrBox.x + vrBox.width > youthBox.x
|
||||
&& vrBox.y < youthBox.y + youthBox.height
|
||||
&& vrBox.y + vrBox.height > youthBox.y
|
||||
}, { timeout: 30_000 }).toBe(false)
|
||||
|
||||
await expect(page.locator('[data-poi-label-kind="ambient"]:visible', { hasText: '秦匠' })).toHaveCount(1)
|
||||
|
||||
const artisan = await page.evaluate(async () => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
if (!api) throw new Error('ThreeMap diagnostics unavailable')
|
||||
const floorId = api.getReport().floorId
|
||||
return (await api.getFloorPois(floorId)).find((poi) => poi.name === '秦匠') || null
|
||||
})
|
||||
expect(artisan).not.toBeNull()
|
||||
await page.evaluate(async (request) => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
if (!api || !request) throw new Error('ThreeMap diagnostics unavailable')
|
||||
await api.focusTargetPoi({ ...request, requestId: 'poi-dom-label-merged-business' })
|
||||
}, artisan!)
|
||||
await expect.poll(async () => page.evaluate(() => (
|
||||
(window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getReport().activeFocusPoiId || ''
|
||||
))).toBe(artisan!.poiId)
|
||||
|
||||
await expect.poll(async () => page.evaluate((poiId) => (
|
||||
(window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getPoiFocusState(poiId).dataTier || null
|
||||
), artisan!.poiId), { timeout: 30_000 }).toBe('full')
|
||||
|
||||
const focusState = await page.evaluate((poiId) => (
|
||||
(window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getPoiFocusState(poiId) || null
|
||||
), artisan!.poiId)
|
||||
expect(focusState).toMatchObject({
|
||||
poiId: artisan!.poiId,
|
||||
selectedPoiId: artisan!.poiId,
|
||||
dataTier: 'full',
|
||||
markerCount: 1,
|
||||
displayPositionCount: 2,
|
||||
baseAffordanceCount: 0,
|
||||
pulseAffordanceCount: 0,
|
||||
glowAffordanceCount: 0,
|
||||
modelHighlightRootCount: 2
|
||||
})
|
||||
await page.screenshot({ path: testInfo.outputPath(`qinjiang-focus-dpr-${deviceScaleFactor}.png`) })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
test('keeps floor ambient labels scene-owned across floor, multi, and floor transitions', async ({ page }) => {
|
||||
test.setTimeout(240_000)
|
||||
await openGuide(page)
|
||||
const floorId = await resetToFloorWithLandmarkLabels(page)
|
||||
const ambientLabels = page.locator('[data-poi-label-kind="ambient"]:visible')
|
||||
await expect(ambientLabels.first()).toBeVisible({ timeout: 30_000 })
|
||||
await expect.poll(async () => page.evaluate(() => (
|
||||
(window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getReport().visibleVisualMarkerWithoutTextCount ?? -1
|
||||
))).toBe(0)
|
||||
|
||||
await page.evaluate(async () => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
if (!api) throw new Error('ThreeMap diagnostics unavailable')
|
||||
await api.showMultiFloor()
|
||||
})
|
||||
await expect.poll(async () => page.evaluate(() => (
|
||||
(window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getReport().activeView || ''
|
||||
)), { timeout: 120_000 }).toBe('multi')
|
||||
await expect(ambientLabels).toHaveCount(0)
|
||||
await page.waitForTimeout(1500)
|
||||
await expect(ambientLabels).toHaveCount(0)
|
||||
|
||||
await page.evaluate(async (targetFloorId) => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
if (!api) throw new Error('ThreeMap diagnostics unavailable')
|
||||
await api.resetToViewBaseline({ view: 'floor', floorId: targetFloorId, reason: 'floor-reset' })
|
||||
}, floorId)
|
||||
await expect(ambientLabels.first()).toBeVisible({ timeout: 30_000 })
|
||||
const visibleFloorIds = await ambientLabels.evaluateAll((elements) => (
|
||||
elements.map((element) => (element as HTMLElement).dataset.floorId || '')
|
||||
))
|
||||
expect(new Set(visibleFloorIds)).toEqual(new Set([floorId]))
|
||||
})
|
||||
|
||||
test('shows the eleven fresh B2 default destinations after the map floor changes', async ({ page }) => {
|
||||
test.setTimeout(240_000)
|
||||
await openGuide(page)
|
||||
const b2FloorId = await page.evaluate(async () => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
if (!api) throw new Error('ThreeMap diagnostics unavailable')
|
||||
const floor = api.getFloors().find((candidate) => candidate.label === 'B2')
|
||||
if (!floor) throw new Error('B2 is unavailable')
|
||||
await api.switchFloor(floor.floorId)
|
||||
return floor.floorId
|
||||
})
|
||||
await expect.poll(async () => page.evaluate(() => {
|
||||
const report = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getReport()
|
||||
return report ? `${report.activeView}:${report.floorId}` : ''
|
||||
}), { timeout: 120_000 }).toBe(`floor:${b2FloorId}`)
|
||||
|
||||
await page.locator('.search-box').click()
|
||||
await expect(page.locator('[data-testid="poi-result-list"] .result-row')).toHaveCount(11, {
|
||||
timeout: 30_000
|
||||
})
|
||||
await expect(page.locator('.result-count')).toContainText('11 处地点')
|
||||
})
|
||||
|
||||
test('merges the four numbered L1 ticket machines across labels, markers, and search', async ({ page }) => {
|
||||
await openGuide(page)
|
||||
const l1FloorId = await resetToFloorByLabel(page, '1F')
|
||||
|
||||
await expect.poll(async () => page.evaluate(() => (
|
||||
(window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getPoiFocusState('5682').dataTier || null
|
||||
)), { timeout: 30_000 }).toBe('full')
|
||||
|
||||
await expect(page.locator('[data-poi-label-kind="ambient"][data-poi-id="5682"]:visible')).toHaveCount(1, {
|
||||
timeout: 30_000
|
||||
})
|
||||
await expect(page.locator('[data-poi-label-kind="ambient"][data-poi-id="5591"]:visible')).toHaveCount(1)
|
||||
await expect(page.locator(
|
||||
'[data-poi-label-kind="ambient"][data-poi-id="352502409978861752"]:visible'
|
||||
)).toHaveCount(1)
|
||||
|
||||
const l1Pois = await page.evaluate(async () => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
if (!api) throw new Error('ThreeMap diagnostics unavailable')
|
||||
const floor = api.getFloors().find((candidate) => candidate.label === '1F')
|
||||
if (!floor) throw new Error('1F is unavailable')
|
||||
return api.getFloorPois(floor.floorId)
|
||||
})
|
||||
expect(l1Pois.find((poi) => poi.poiId === '5591')?.positionGltf).toEqual([
|
||||
3.587204,
|
||||
1.205524,
|
||||
13.269173
|
||||
])
|
||||
|
||||
const floorExits = [
|
||||
['floor-exit-2077983437229297849', '展览入口'],
|
||||
['floor-exit-2077983437229297850', '观众次入口'],
|
||||
['floor-exit-2077983437229297851', '影剧院入口']
|
||||
] as const
|
||||
for (const [poiId, name] of floorExits) {
|
||||
const exitPoi = l1Pois.find((poi) => poi.poiId === poiId)
|
||||
expect(exitPoi).toMatchObject({ name, primaryCategory: 'navigation_anchor' })
|
||||
}
|
||||
const floorExitStates = await page.evaluate((poiIds) => {
|
||||
const api = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__
|
||||
if (!api) throw new Error('ThreeMap diagnostics unavailable')
|
||||
return poiIds.map((poiId) => api.getPoiFocusState(poiId))
|
||||
}, floorExits.map(([poiId]) => poiId))
|
||||
expect(floorExitStates).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({ markerCount: 1, displayPositionCount: 1 }),
|
||||
expect.objectContaining({ markerCount: 1, displayPositionCount: 1 }),
|
||||
expect.objectContaining({ markerCount: 1, displayPositionCount: 1 })
|
||||
]))
|
||||
await expect(page.locator(
|
||||
'[data-poi-label-kind="ambient"][data-poi-id="floor-exit-2077983437229297849"]:visible'
|
||||
)).toHaveCount(1)
|
||||
await expect(page.locator(
|
||||
'[data-poi-label-kind="ambient"][data-poi-id="floor-exit-2077983437229297851"]:visible'
|
||||
)).toHaveCount(1)
|
||||
|
||||
const markerState = await page.evaluate(() => (
|
||||
(window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getPoiFocusState('5682') || null
|
||||
))
|
||||
expect(markerState).toMatchObject({
|
||||
dataTier: 'full',
|
||||
markerCount: 4,
|
||||
displayPositionCount: 4
|
||||
})
|
||||
|
||||
await page.getByTestId('poi-category-ticket-office').first().click()
|
||||
await expect(page.getByTestId('poi-result-5682')).toHaveCount(1, { timeout: 30_000 })
|
||||
await expect(page.getByTestId('poi-result-5683')).toHaveCount(0, { timeout: 30_000 })
|
||||
await expect(page.getByTestId('poi-result-5684')).toHaveCount(0, { timeout: 30_000 })
|
||||
await expect(page.getByTestId('poi-result-5685')).toHaveCount(0, { timeout: 30_000 })
|
||||
await expect(page.getByTestId('poi-result-5591')).toHaveCount(1, { timeout: 30_000 })
|
||||
await expect(page.getByTestId('poi-result-352502409978861752')).toHaveCount(1, { timeout: 30_000 })
|
||||
await expect(page.getByTestId('poi-result-5682').locator('.result-name')).toHaveText('售票机')
|
||||
await expect(page.getByTestId('poi-result-5682').locator('.result-meta')).toHaveText('1F · 售票处')
|
||||
await expect(page.locator('.home-category-results-meta')).toHaveText('当前楼层 1F · 3 处地点')
|
||||
await expect(page.locator('.floor-switcher')).toBeVisible()
|
||||
await expect(page.locator('.clear-button')).toHaveCount(0)
|
||||
await expect(page.getByTestId('poi-category-cancel')).toHaveCount(1)
|
||||
|
||||
await page.locator('canvas').hover()
|
||||
await page.mouse.wheel(0, 3000)
|
||||
await expect.poll(async () => page.evaluate(() => {
|
||||
const report = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getReport()
|
||||
return report ? `${report.activeView}:${report.floorId}` : ''
|
||||
}), { timeout: 30_000 }).toBe(`floor:${l1FloorId}`)
|
||||
await expect(page.locator('.floor-switcher')).toBeVisible()
|
||||
await expect(page.locator('.home-category-results-meta')).toHaveText('当前楼层 1F · 3 处地点')
|
||||
})
|
||||
|
||||
test('keeps the floor mode toggle light while committing multi and single views', async ({ page }) => {
|
||||
await openGuide(page)
|
||||
const l1FloorId = await resetToFloorByLabel(page, '1F')
|
||||
const floorHeader = page.locator('.floor-header')
|
||||
|
||||
await page.getByTestId('poi-category-ticket-office').first().click()
|
||||
await expect(page.getByTestId('poi-result-5682')).toBeVisible({ timeout: 30_000 })
|
||||
await expect(floorHeader).toBeVisible({ timeout: 30_000 })
|
||||
await expect(floorHeader.locator('.floor-header-label')).toHaveText('多层')
|
||||
const singleLayout = await floorHeader.evaluate((element) => {
|
||||
const icon = element.querySelector('.floor-header-icon')?.getBoundingClientRect()
|
||||
const label = element.querySelector('.floor-header-label')?.getBoundingClientRect()
|
||||
if (!icon || !label) return null
|
||||
return {
|
||||
iconBottom: icon.bottom,
|
||||
labelTop: label.top,
|
||||
iconCenter: icon.left + icon.width / 2,
|
||||
labelCenter: label.left + label.width / 2
|
||||
}
|
||||
})
|
||||
expect(singleLayout).not.toBeNull()
|
||||
expect(singleLayout!.iconBottom).toBeLessThanOrEqual(singleLayout!.labelTop)
|
||||
expect(Math.abs(singleLayout!.iconCenter - singleLayout!.labelCenter)).toBeLessThan(1)
|
||||
await floorHeader.click()
|
||||
|
||||
await expect.poll(async () => page.evaluate(() => (
|
||||
(window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getReport().activeView || ''
|
||||
)), { timeout: 120_000 }).toBe('multi')
|
||||
await expect(floorHeader).toHaveClass(/active/)
|
||||
await expect(floorHeader.locator('.floor-header-label')).toHaveText('单层')
|
||||
await expect(floorHeader).toHaveCSS('background-color', 'rgb(237, 241, 255)')
|
||||
|
||||
await page.getByTestId('poi-category-cancel').click()
|
||||
await expect(page.locator('.home-category-results')).toHaveCount(0)
|
||||
await expect(floorHeader).toBeVisible()
|
||||
await floorHeader.click()
|
||||
await expect.poll(async () => page.evaluate(() => {
|
||||
const report = (window as Window & { __GUIDE_3D_VISUAL_STABILITY__?: GuideDiagnostics })
|
||||
.__GUIDE_3D_VISUAL_STABILITY__?.getReport()
|
||||
return report ? `${report.activeView}:${report.floorId}` : ''
|
||||
}), { timeout: 120_000 }).toBe(`floor:${l1FloorId}`)
|
||||
await expect(floorHeader).not.toHaveClass(/active/)
|
||||
await expect(floorHeader.locator('.floor-header-label')).toHaveText('多层')
|
||||
await expect(floorHeader).toHaveCSS('background-color', 'rgb(255, 255, 255)')
|
||||
})
|
||||
|
||||
test('uses one visual specification for landmark and service labels', async ({ page }) => {
|
||||
await openGuide(page)
|
||||
await resetToFloorByLabel(page, '1F')
|
||||
|
||||
await expect.poll(async () => page.locator(
|
||||
'[data-poi-label-kind="ambient"]:visible'
|
||||
).count(), { timeout: 30_000 }).toBeGreaterThan(3)
|
||||
|
||||
const metrics = await page.locator('[data-poi-label-kind="ambient"]:visible').evaluateAll((elements) => (
|
||||
elements.map((element) => {
|
||||
const icon = element.querySelector('.three-poi-dom-label__icon')
|
||||
const style = window.getComputedStyle(element)
|
||||
const iconStyle = icon ? window.getComputedStyle(icon) : null
|
||||
return {
|
||||
height: element.getBoundingClientRect().height,
|
||||
fontSize: style.fontSize,
|
||||
lineHeight: style.lineHeight,
|
||||
iconWidth: iconStyle?.width || '',
|
||||
iconHeight: iconStyle?.height || ''
|
||||
}
|
||||
})
|
||||
))
|
||||
|
||||
expect(metrics).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({ height: 22, fontSize: '12px', lineHeight: '15px', iconWidth: '16px', iconHeight: '16px' })
|
||||
]))
|
||||
for (const metric of metrics) {
|
||||
expect(metric).toEqual({
|
||||
height: 22,
|
||||
fontSize: '12px',
|
||||
lineHeight: '15px',
|
||||
iconWidth: '16px',
|
||||
iconHeight: '16px'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user