This commit is contained in:
@@ -85,6 +85,12 @@ describe('theme defaults', () => {
|
||||
expect(controlsSource).toContain("overlay.setAttribute('aria-label', '图片全屏预览')")
|
||||
expect(controlsSource).toContain("type ScalableKind = 'table' | 'code'")
|
||||
expect(controlsSource).toContain('easySlidesImagePreview')
|
||||
expect(controlsSource).toContain('const IMAGE_MIN_SCALE = 0.1')
|
||||
expect(controlsSource).toContain('const IMAGE_MAX_SCALE = 4')
|
||||
expect(controlsSource).toContain("type: 'close'")
|
||||
expect(controlsSource).toContain('easy-slides:image-preview:')
|
||||
expect(layoutCss).toContain('.easy-image-scale-toolbar')
|
||||
expect(layoutCss).toContain('.easy-image-lightbox-pannable')
|
||||
expect(controlsSource).toContain('sharedState')
|
||||
expect(controlsSource).toContain('}, 500)')
|
||||
expect(scrollFallbackSource).toContain('easy-slide-scroll-fallback')
|
||||
|
||||
+126
-5
@@ -5,7 +5,8 @@ test('landing page links to a rendered deck', async ({ page }) => {
|
||||
await expect(page.getByRole('heading', { name: 'Engineering Slides', exact: true })).toBeVisible()
|
||||
await page.getByRole('link', { name: 'easy-slides 快速开始' }).click()
|
||||
await expect(page.getByRole('heading', { name: 'easy-slides', exact: true })).toBeVisible()
|
||||
await expect(page.getByRole('button', { name: '演示者' })).toBeVisible()
|
||||
await expect(page.getByRole('button', { name: '演示者' })).toHaveCount(0)
|
||||
await expect(page.getByRole('button', { name: 'Presenter Mode' })).toHaveCount(0)
|
||||
})
|
||||
|
||||
test('presenter token unlocks, survives refresh, and can be locked', async ({ page }) => {
|
||||
@@ -122,15 +123,63 @@ test('presenter sends image previews to every audience window', async ({ context
|
||||
await expect(secondAudienceDialog).toBeVisible()
|
||||
await expect(presenterDialog).toBeHidden()
|
||||
await expect(audienceDialog.locator('img')).toHaveAttribute('src', /cover\.svg/)
|
||||
|
||||
const audiencePreview = audienceDialog.locator('img')
|
||||
const secondAudiencePreview = secondAudienceDialog.locator('img')
|
||||
const sharedPreviewSource = `data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="1200" viewBox="0 0 1600 1200"><rect width="100%" height="100%" fill="#2563eb"/></svg>')}`
|
||||
await Promise.all([
|
||||
audiencePreview.evaluate((element, source) => {
|
||||
;(element as HTMLImageElement).src = source
|
||||
}, sharedPreviewSource),
|
||||
secondAudiencePreview.evaluate((element, source) => {
|
||||
;(element as HTMLImageElement).src = source
|
||||
}, sharedPreviewSource),
|
||||
])
|
||||
await expect.poll(() => audiencePreview.evaluate(element => (element as HTMLImageElement).naturalWidth)).toBe(1600)
|
||||
await expect.poll(() => secondAudiencePreview.evaluate(element => (element as HTMLImageElement).naturalWidth)).toBe(1600)
|
||||
const initialScale = await audiencePreview.evaluate(element => Number(element.getAttribute('data-easy-image-scale')))
|
||||
expect(initialScale).toBeGreaterThanOrEqual(0.1)
|
||||
expect(initialScale).toBeLessThanOrEqual(1)
|
||||
await audienceDialog.getByRole('button', { name: '放大图片' }).click()
|
||||
await expect.poll(() => secondAudiencePreview.evaluate(element => Number(element.getAttribute('data-easy-image-scale'))))
|
||||
.toBeCloseTo(initialScale + 0.1, 3)
|
||||
|
||||
const viewport = audienceDialog.locator('.easy-image-lightbox-viewport')
|
||||
const viewportBox = await viewport.boundingBox()
|
||||
expect(viewportBox).not.toBeNull()
|
||||
await page.mouse.move(viewportBox!.x + viewportBox!.width / 2, viewportBox!.y + viewportBox!.height / 2)
|
||||
await page.mouse.down()
|
||||
await page.mouse.move(viewportBox!.x + viewportBox!.width / 2, viewportBox!.y + viewportBox!.height / 2 - 90, { steps: 5 })
|
||||
await page.mouse.up()
|
||||
const draggedCenterY = await audiencePreview.evaluate(element => Number(element.getAttribute('data-easy-image-center-y')))
|
||||
expect(draggedCenterY).toBeGreaterThan(0.5)
|
||||
await expect.poll(() => secondAudiencePreview.evaluate(element => Number(element.getAttribute('data-easy-image-center-y'))))
|
||||
.toBeCloseTo(draggedCenterY, 3)
|
||||
|
||||
await page.getByRole('button', { name: '关闭图片全屏预览' }).click()
|
||||
await secondAudience.getByRole('button', { name: '关闭图片全屏预览' }).click()
|
||||
await expect(audienceDialog).toBeHidden()
|
||||
await expect(secondAudienceDialog).toBeHidden()
|
||||
|
||||
await nextImage.press('Enter')
|
||||
await expect(audienceDialog).toBeVisible()
|
||||
await expect(secondAudienceDialog).toBeVisible()
|
||||
await expect(presenterDialog).toBeHidden()
|
||||
await page.keyboard.press('Escape')
|
||||
await secondAudience.keyboard.press('Escape')
|
||||
await expect(audienceDialog).toBeHidden()
|
||||
await expect(secondAudienceDialog).toBeHidden()
|
||||
|
||||
await currentImage.click()
|
||||
await expect(audienceDialog).toBeVisible()
|
||||
await expect(secondAudienceDialog).toBeVisible()
|
||||
await secondAudienceDialog.locator('.easy-image-lightbox-viewport').click({ position: { x: 4, y: 4 } })
|
||||
await expect(audienceDialog).toBeHidden()
|
||||
await expect(secondAudienceDialog).toBeHidden()
|
||||
|
||||
const localImage = page.locator('.slidev-layout img.easy-image-preview-trigger').first()
|
||||
await localImage.click()
|
||||
await expect(audienceDialog).toBeVisible()
|
||||
await expect(secondAudienceDialog).toBeHidden()
|
||||
await page.keyboard.press('Escape')
|
||||
})
|
||||
|
||||
test('presenter opens an image locally when no audience acknowledges it', async ({ context, page }) => {
|
||||
@@ -155,13 +204,16 @@ test('presenter opens an image locally when no audience acknowledges it', async
|
||||
await auditPage.close()
|
||||
})
|
||||
|
||||
test('present mode sends image previews across isolated browser contexts', async ({ browser }) => {
|
||||
test('present mode syncs image preview views across isolated browser contexts', async ({ browser }) => {
|
||||
const audienceContext = await browser.newContext()
|
||||
const secondAudienceContext = await browser.newContext()
|
||||
const presenterContext = await browser.newContext()
|
||||
const audience = await audienceContext.newPage()
|
||||
const secondAudience = await secondAudienceContext.newPage()
|
||||
const presenter = await presenterContext.newPage()
|
||||
try {
|
||||
await audience.goto('http://127.0.0.1:4174/4')
|
||||
await secondAudience.goto('http://127.0.0.1:4174/4')
|
||||
await presenter.goto('http://127.0.0.1:4174/presenter/?password=easy-slides-local')
|
||||
await presenter.getByLabel('演示者 token').fill('test-token')
|
||||
await presenter.getByRole('button', { name: '解锁' }).click()
|
||||
@@ -173,11 +225,24 @@ test('present mode sends image previews across isolated browser contexts', async
|
||||
await expect(audience.getByRole('heading', { name: '智能图片' })).toBeVisible()
|
||||
await current.locator('img.easy-image-preview-trigger').first().click()
|
||||
|
||||
await expect(audience.getByRole('dialog', { name: '图片全屏预览' })).toBeVisible()
|
||||
const audienceDialog = audience.getByRole('dialog', { name: '图片全屏预览' })
|
||||
const secondAudienceDialog = secondAudience.getByRole('dialog', { name: '图片全屏预览' })
|
||||
await expect(audienceDialog).toBeVisible()
|
||||
await expect(secondAudienceDialog).toBeVisible()
|
||||
await expect(presenter.getByRole('dialog', { name: '图片全屏预览' })).toBeHidden()
|
||||
|
||||
await audienceDialog.getByRole('button', { name: '放大图片' }).click()
|
||||
const firstScale = await audienceDialog.locator('img').evaluate(element => element.getAttribute('data-easy-image-scale'))
|
||||
await expect.poll(() => secondAudienceDialog.locator('img').evaluate(element => element.getAttribute('data-easy-image-scale')))
|
||||
.toBe(firstScale)
|
||||
|
||||
await secondAudienceDialog.getByRole('button', { name: '关闭图片全屏预览' }).click()
|
||||
await expect(audienceDialog).toBeHidden()
|
||||
await expect(secondAudienceDialog).toBeHidden()
|
||||
}
|
||||
finally {
|
||||
await audienceContext.close()
|
||||
await secondAudienceContext.close()
|
||||
await presenterContext.close()
|
||||
}
|
||||
})
|
||||
@@ -330,6 +395,62 @@ test('image fullscreen preserves layout and table scaling stays out of non-inter
|
||||
expect(overlay).not.toBeNull()
|
||||
expect(overlay!.width).toBe(viewport!.width)
|
||||
expect(overlay!.height).toBe(viewport!.height)
|
||||
|
||||
const previewViewport = dialog.locator('.easy-image-lightbox-viewport')
|
||||
const previewImage = dialog.locator('img')
|
||||
const scaleValue = dialog.locator('.easy-image-scale-value')
|
||||
const interactiveSource = `data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="1200" viewBox="0 0 1600 1200"><rect width="100%" height="100%" fill="#2563eb"/></svg>')}`
|
||||
await previewImage.evaluate((element, source) => {
|
||||
;(element as HTMLImageElement).src = source
|
||||
}, interactiveSource)
|
||||
await expect.poll(() => previewImage.evaluate(element => (element as HTMLImageElement).naturalWidth)).toBe(1600)
|
||||
const initialScale = await previewImage.evaluate(element => Number(element.getAttribute('data-easy-image-scale')))
|
||||
expect(initialScale).toBeGreaterThanOrEqual(0.1)
|
||||
expect(initialScale).toBeLessThanOrEqual(1)
|
||||
await expect(scaleValue).toHaveText(`${Math.round(initialScale * 100)}%`)
|
||||
|
||||
const previewViewportBox = await previewViewport.boundingBox()
|
||||
expect(previewViewportBox).not.toBeNull()
|
||||
await page.mouse.move(
|
||||
previewViewportBox!.x + previewViewportBox!.width * 0.25,
|
||||
previewViewportBox!.y + previewViewportBox!.height * 0.25,
|
||||
)
|
||||
await page.mouse.wheel(0, -180)
|
||||
await expect.poll(() => previewImage.evaluate(element => Number(element.getAttribute('data-easy-image-scale'))))
|
||||
.toBeGreaterThan(initialScale)
|
||||
await expect.poll(() => previewImage.evaluate(element => Number(element.getAttribute('data-easy-image-center-y'))))
|
||||
.not.toBe(0.5)
|
||||
|
||||
const increase = dialog.getByRole('button', { name: '放大图片' })
|
||||
await increase.evaluate((element) => {
|
||||
for (let index = 0; index < 50; index += 1) {
|
||||
const button = element as HTMLButtonElement
|
||||
button.click()
|
||||
}
|
||||
})
|
||||
await expect(scaleValue).toHaveText('400%')
|
||||
await expect(increase).toBeDisabled()
|
||||
|
||||
const decrease = dialog.getByRole('button', { name: '缩小图片' })
|
||||
await decrease.evaluate((element) => {
|
||||
for (let index = 0; index < 50; index += 1) {
|
||||
const button = element as HTMLButtonElement
|
||||
button.click()
|
||||
}
|
||||
})
|
||||
await expect(scaleValue).toHaveText('10%')
|
||||
await expect(decrease).toBeDisabled()
|
||||
|
||||
const pinchCenterX = previewViewportBox!.x + previewViewportBox!.width / 2
|
||||
const pinchCenterY = previewViewportBox!.y + previewViewportBox!.height / 2
|
||||
await previewViewport.dispatchEvent('pointerdown', { buttons: 1, clientX: pinchCenterX - 40, clientY: pinchCenterY, pointerId: 1, pointerType: 'touch' })
|
||||
await previewViewport.dispatchEvent('pointerdown', { buttons: 1, clientX: pinchCenterX + 40, clientY: pinchCenterY, pointerId: 2, pointerType: 'touch' })
|
||||
await previewViewport.dispatchEvent('pointermove', { buttons: 1, clientX: pinchCenterX + 80, clientY: pinchCenterY, pointerId: 2, pointerType: 'touch' })
|
||||
await previewViewport.dispatchEvent('pointerup', { clientX: pinchCenterX - 40, clientY: pinchCenterY, pointerId: 1, pointerType: 'touch' })
|
||||
await previewViewport.dispatchEvent('pointerup', { clientX: pinchCenterX + 80, clientY: pinchCenterY, pointerId: 2, pointerType: 'touch' })
|
||||
await expect.poll(() => previewImage.evaluate(element => Number(element.getAttribute('data-easy-image-scale'))))
|
||||
.toBeGreaterThan(0.1)
|
||||
|
||||
await page.keyboard.press('Escape')
|
||||
await expect(dialog).toBeHidden()
|
||||
const after = await image.evaluate(element => ({
|
||||
|
||||
Reference in New Issue
Block a user