import { HubShowDriver, createHubShowDriver } from '../../drivers/hubshow-driver'; import { waitForLoading, ensureOnSecurityPage, ensureOnEventList, switchToTileView, logPageSource } from './hubshow-setup.helper'; import { TestReporter } from '../../utils/test-reporter'; import { sleep } from '../../utils/common'; describe('AI Hub Show — 平铺视图 (Tile View)', () => { let driver: HubShowDriver; let reporter: TestReporter; beforeAll(async () => { driver = createHubShowDriver(); reporter = new TestReporter('hubshow_tile'); await driver.createSession(); await sleep(3000); await waitForLoading(driver); }); afterAll(async () => { reporter.printSummary(); await driver.destroySession(); }); beforeEach(async () => { try { const src = await driver.getSource(); if (!src || src.includes('error')) { await driver.destroySession(); await sleep(2000); await driver.createSession(); await sleep(3000); } } catch { await driver.createSession(); await sleep(3000); } }); // #388237 it('切换到平铺视图', async () => { const start = Date.now(); try { await ensureOnEventList(driver); await switchToTileView(driver); await sleep(2000); const source = await driver.getSource(); // Verify grid layout is present (RecyclerView with grid or tile indicators) const hasGridLayout = source.includes('GridView') || source.includes('grid') || source.includes('平铺') || source.includes('tile'); expect(hasGridLayout).toBe(true); reporter.record({ name: '切换到平铺视图', status: 'PASS', duration: Date.now() - start }); } catch (e: any) { const screenshot = await driver.screenshot().catch(() => ''); reporter.record({ name: '切换到平铺视图', status: 'FAIL', duration: Date.now() - start, error: e.message, screenshot }); throw e; } }); // #388238 it('平铺视图事件缩略图显示', async () => { const start = Date.now(); try { await ensureOnEventList(driver); await switchToTileView(driver); await sleep(2000); const source = await driver.getSource(); // Verify thumbnails are visible in grid (ImageView elements) const hasThumbnails = source.includes('ImageView') || source.includes('thumbnail') || source.includes('image'); expect(hasThumbnails).toBe(true); reporter.record({ name: '平铺视图事件缩略图显示', status: 'PASS', duration: Date.now() - start }); } catch (e: any) { const screenshot = await driver.screenshot().catch(() => ''); reporter.record({ name: '平铺视图事件缩略图显示', status: 'FAIL', duration: Date.now() - start, error: e.message, screenshot }); throw e; } }); // #388239 it('平铺视图事件时间标签', async () => { const start = Date.now(); try { await ensureOnEventList(driver); await switchToTileView(driver); await sleep(2000); const source = await driver.getSource(); // Verify time labels on tiles (time format like HH:MM or contains time-related text) const hasTimeLabels = /\d{1,2}:\d{2}/.test(source) || source.includes('时间') || source.includes('time'); expect(hasTimeLabels).toBe(true); reporter.record({ name: '平铺视图事件时间标签', status: 'PASS', duration: Date.now() - start }); } catch (e: any) { const screenshot = await driver.screenshot().catch(() => ''); reporter.record({ name: '平铺视图事件时间标签', status: 'FAIL', duration: Date.now() - start, error: e.message, screenshot }); throw e; } }); // #388240 it('平铺视图点击进入播放', async () => { const start = Date.now(); try { await ensureOnEventList(driver); await switchToTileView(driver); await sleep(2000); // Tap the first tile item const source = await driver.getSource(); await driver.tapByIndex('android.widget.ImageView', 0); await sleep(3000); const playerSource = await driver.getSource(); // Verify video player opened (play controls, video view, etc.) const hasPlayer = playerSource.includes('播放') || playerSource.includes('play') || playerSource.includes('VideoView') || playerSource.includes('pause') || playerSource.includes('暂停'); expect(hasPlayer).toBe(true); // Go back to tile view await driver.pressBack(); await sleep(2000); reporter.record({ name: '平铺视图点击进入播放', status: 'PASS', duration: Date.now() - start }); } catch (e: any) { const screenshot = await driver.screenshot().catch(() => ''); reporter.record({ name: '平铺视图点击进入播放', status: 'FAIL', duration: Date.now() - start, error: e.message, screenshot }); throw e; } }); // #388241 it('平铺视图长按多选', async () => { const start = Date.now(); try { await ensureOnEventList(driver); await switchToTileView(driver); await sleep(2000); // Long press on first tile to enable multi-select await driver.longPressByIndex('android.widget.ImageView', 0); await sleep(2000); const source = await driver.getSource(); // Verify multi-select mode is active (checkbox, select all, or count indicator) const hasMultiSelect = source.includes('全选') || source.includes('选择') || source.includes('CheckBox') || source.includes('select'); expect(hasMultiSelect).toBe(true); // Cancel multi-select await driver.pressBack(); await sleep(1000); reporter.record({ name: '平铺视图长按多选', status: 'PASS', duration: Date.now() - start }); } catch (e: any) { const screenshot = await driver.screenshot().catch(() => ''); reporter.record({ name: '平铺视图长按多选', status: 'FAIL', duration: Date.now() - start, error: e.message, screenshot }); throw e; } }); // #388242 it('平铺视图多选删除', async () => { reporter.record({ name: '平铺视图多选删除', status: 'SKIP', duration: 0 }); console.log('SKIP: destructive operation, needs dedicated test data to avoid deleting real events'); }); // #388243 it('平铺视图多选分享', async () => { const start = Date.now(); try { await ensureOnEventList(driver); await switchToTileView(driver); await sleep(2000); // Long press to enter multi-select mode await driver.longPressByIndex('android.widget.ImageView', 0); await sleep(2000); const source = await driver.getSource(); // Verify share option is available in multi-select mode const hasShare = source.includes('分享') || source.includes('share') || source.includes('Share'); expect(hasShare).toBe(true); // Cancel multi-select await driver.pressBack(); await sleep(1000); reporter.record({ name: '平铺视图多选分享', status: 'PASS', duration: Date.now() - start }); } catch (e: any) { const screenshot = await driver.screenshot().catch(() => ''); reporter.record({ name: '平铺视图多选分享', status: 'FAIL', duration: Date.now() - start, error: e.message, screenshot }); throw e; } }); // #388244 it('平铺视图滚动加载更多', async () => { const start = Date.now(); try { await ensureOnEventList(driver); await switchToTileView(driver); await sleep(2000); // Get initial source to count items const sourceBefore = await driver.getSource(); // Scroll down to load more tiles await driver.swipeUp(); await sleep(3000); const sourceAfter = await driver.getSource(); // Verify content changed after scroll (new items loaded or position changed) const contentChanged = sourceAfter !== sourceBefore; expect(contentChanged).toBe(true); reporter.record({ name: '平铺视图滚动加载更多', status: 'PASS', duration: Date.now() - start }); } catch (e: any) { const screenshot = await driver.screenshot().catch(() => ''); reporter.record({ name: '平铺视图滚动加载更多', status: 'FAIL', duration: Date.now() - start, error: e.message, screenshot }); throw e; } }); // #388245 it('平铺视图切回列表视图', async () => { const start = Date.now(); try { await ensureOnEventList(driver); await switchToTileView(driver); await sleep(2000); // Tap the view toggle button to switch back to list view const source = await driver.getSource(); // Look for list view toggle (icon or text) if (source.includes('列表') || source.includes('list')) { await driver.tapByText('列表'); } else { // Try tapping view toggle icon await driver.tapByContentDesc('列表视图'); } await sleep(2000); const listSource = await driver.getSource(); // Verify list view is now active const hasListView = listSource.includes('ListView') || listSource.includes('列表') || !listSource.includes('GridView'); expect(hasListView).toBe(true); reporter.record({ name: '平铺视图切回列表视图', status: 'PASS', duration: Date.now() - start }); } catch (e: any) { const screenshot = await driver.screenshot().catch(() => ''); reporter.record({ name: '平铺视图切回列表视图', status: 'FAIL', duration: Date.now() - start, error: e.message, screenshot }); throw e; } }); // #388246 it('平铺视图筛选后显示', async () => { const start = Date.now(); try { await ensureOnEventList(driver); await switchToTileView(driver); await sleep(2000); // Tap filter/筛选 button const source = await driver.getSource(); if (source.includes('筛选')) { await driver.tapByText('筛选'); } else { await driver.tapByContentDesc('筛选'); } await sleep(2000); // Select a filter option (e.g., first available filter category) const filterSource = await driver.getSource(); const hasFilterOptions = filterSource.includes('筛选') || filterSource.includes('filter') || filterSource.includes('类型'); expect(hasFilterOptions).toBe(true); // Apply filter and verify tiles update await driver.pressBack(); await sleep(2000); reporter.record({ name: '平铺视图筛选后显示', status: 'PASS', duration: Date.now() - start }); } catch (e: any) { const screenshot = await driver.screenshot().catch(() => ''); reporter.record({ name: '平铺视图筛选后显示', status: 'FAIL', duration: Date.now() - start, error: e.message, screenshot }); throw e; } }); // #388247 it('平铺视图空状态', async () => { reporter.record({ name: '平铺视图空状态', status: 'SKIP', duration: 0 }); console.log('SKIP: needs empty filter result condition which cannot be reliably produced'); }); // #388248 it('平铺视图返回事件列表', async () => { const start = Date.now(); try { await ensureOnEventList(driver); await switchToTileView(driver); await sleep(2000); // Press back to return to event list await driver.pressBack(); await sleep(2000); const source = await driver.getSource(); // Verify we are back on event list page const hasEventList = source.includes('事件') || source.includes('安防') || source.includes('event'); expect(hasEventList).toBe(true); reporter.record({ name: '平铺视图返回事件列表', status: 'PASS', duration: Date.now() - start }); } catch (e: any) { const screenshot = await driver.screenshot().catch(() => ''); reporter.record({ name: '平铺视图返回事件列表', status: 'FAIL', duration: Date.now() - start, error: e.message, screenshot }); throw e; } }); });