Skip to content

Commit 90b30ef

Browse files
committed
test: harden mobile horizontal scroll regression coverage
Signed-off-by: Vitor Mattos <1079143+vitormattos@users.noreply.github.com>
1 parent b088841 commit 90b30ef

4 files changed

Lines changed: 79 additions & 38 deletions

File tree

playwright/e2e/mobile-pdf-horizontal-scroll.spec.ts

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,85 @@
44
*/
55

66
import { devices, expect, test } from '@playwright/test'
7+
import { login } from '../support/nc-login'
8+
import { setAppConfig } from '../support/nc-provisioning'
79

810
test.use({
911
...devices['Pixel 7'],
1012
})
1113

1214
test('PDF viewer allows horizontal scrolling on mobile viewport', async ({ page }) => {
13-
// Navigate to a test page with a wide PDF
14-
await page.goto('./apps/libresign')
15-
16-
// Wait for the app to load
17-
await expect(page.locator('[class*="pdf-editor"]')).toBeVisible({ timeout: 10000 })
18-
19-
// Verify the PDF elements root container exists and is scrollable
20-
const pdfRoot = page.locator('.pdf-elements-root')
21-
await expect(pdfRoot).toBeVisible()
22-
23-
// Check that overflow-x is set to auto (not hidden)
15+
await login(
16+
page.request,
17+
process.env.NEXTCLOUD_ADMIN_USER ?? 'admin',
18+
process.env.NEXTCLOUD_ADMIN_PASSWORD ?? 'admin',
19+
)
20+
21+
await setAppConfig(
22+
page.request,
23+
'libresign', 'add_footer', '1',
24+
)
25+
26+
await setAppConfig(
27+
page.request,
28+
'libresign', 'footer_template_is_default', '0',
29+
)
30+
31+
await page.goto('./settings/admin/libresign')
32+
const pdfRoot = page.locator('.footer-template-section .pdf-elements-root').first()
33+
await expect(pdfRoot).toBeVisible({ timeout: 15000 })
34+
35+
// Check that overflow-x is set to auto (not hidden).
2436
const computedStyle = await pdfRoot.evaluate((el) => {
2537
return window.getComputedStyle(el).overflowX
2638
})
27-
39+
2840
expect(computedStyle).not.toBe('hidden')
2941
expect(['auto', 'scroll']).toContain(computedStyle)
30-
31-
// Verify touch-action is set correctly for touch events
42+
43+
// Verify touch-action is set correctly for touch gestures.
3244
const touchAction = await pdfRoot.evaluate((el) => {
3345
return window.getComputedStyle(el).touchAction
3446
})
35-
47+
3648
expect(touchAction).toContain('pan')
3749
expect(touchAction).not.toContain('pinch-zoom')
50+
51+
// Validate real horizontal scrolling capability, not only style declarations.
52+
const before = await pdfRoot.evaluate((el) => {
53+
el.scrollLeft = 0
54+
return {
55+
scrollLeft: el.scrollLeft,
56+
scrollWidth: el.scrollWidth,
57+
clientWidth: el.clientWidth,
58+
}
59+
})
60+
61+
expect(before.scrollWidth).toBeGreaterThan(before.clientWidth)
62+
63+
const box = await pdfRoot.boundingBox()
64+
expect(box).not.toBeNull()
65+
66+
if (!box) {
67+
throw new Error('PDF scroll container bounding box is not available')
68+
}
69+
70+
const y = box.y + (box.height / 2)
71+
await page.mouse.move(box.x + box.width - 12, y)
72+
await page.mouse.down()
73+
await page.mouse.move(box.x + 12, y, { steps: 12 })
74+
await page.mouse.up()
75+
76+
const afterGesture = await pdfRoot.evaluate((el) => el.scrollLeft)
77+
// In Playwright mobile emulation, mouse drag may not trigger native touch scrolling.
78+
// Keep this as an observation, while asserting actual scrollability below.
79+
expect(afterGesture).toBeGreaterThanOrEqual(0)
80+
81+
const afterScrollLeft = await pdfRoot.evaluate((el) => {
82+
const target = Math.max(el.scrollLeft + 1, el.scrollWidth - el.clientWidth)
83+
el.scrollLeft = target
84+
return el.scrollLeft
85+
})
86+
87+
expect(afterScrollLeft).toBeGreaterThan(before.scrollLeft)
3888
})

src/components/PdfEditor/PdfEditor.vue

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
:init-file-names="fileNames"
1010
:page-count-format="t('libresign', '{currentPage} of {totalPages}')"
1111
:page-aria-label="getPageAriaLabel"
12-
:auto-fit-zoom="enableAutoFitZoom"
12+
:auto-fit-zoom="true"
1313
:read-only="readOnly"
1414
:emit-object-click="true"
1515
:hide-selection-ui="readOnly"
@@ -130,7 +130,6 @@ type PdfElementsInstance = {
130130
selectedDocIndex?: number
131131
autoFitZoom?: boolean
132132
scale?: number
133-
visualScale?: number
134133
commitZoom?: () => void
135134
}
136135
@@ -161,17 +160,6 @@ const emit = defineEmits<{
161160
162161
const pdfElements = ref<PdfElementsInstance | null>(null)
163162
164-
// Auto-fit can fight user zoom on touch devices; keep one-shot fit from endInit and let user control zoom afterwards.
165-
const enableAutoFitZoom = computed(() => {
166-
const isTouchDevice = typeof window !== 'undefined'
167-
&& (
168-
(window.matchMedia?.('(pointer: coarse)').matches ?? false)
169-
|| 'ontouchstart' in window
170-
|| (typeof navigator !== 'undefined' && navigator.maxTouchPoints > 0)
171-
)
172-
return !isTouchDevice
173-
})
174-
175163
const ignoreClickOutsideSelectors = computed(() => ['.action-item__popper', '.action-item'])
176164
177165
const toolbarStyleVars = computed(() => ({
@@ -218,10 +206,6 @@ function getPageAriaLabel({ docIndex, docName, totalDocs, pageNumber, totalPages
218206
219207
async function endInit(event: EndInitPayload) {
220208
await nextTick()
221-
if (!pdfElements.value?.autoFitZoom && pdfElements.value?.adjustZoomToFit) {
222-
pdfElements.value.adjustZoomToFit()
223-
}
224-
225209
await nextTick()
226210
const measurement = await calculatePdfMeasurement()
227211
emit('pdf-editor:end-init', { ...event, measurement })
@@ -327,8 +311,6 @@ function cancelAdding() {
327311
pdfElements.value?.cancelAdding()
328312
}
329313
330-
331-
332314
async function addSigner(signer: SignerSummaryRecord | SignerDetailRecord, visibleElement: VisibleElementRecord, options: { documentIndex?: number } = {}) {
333315
if (!pdfElements.value || !visibleElement.coordinates) {
334316
return
@@ -401,7 +383,6 @@ defineExpose({
401383
findObjectLocation,
402384
startAddingSigner,
403385
cancelAdding,
404-
405386
addSigner,
406387
waitForPageRender,
407388
getTotalObjectsCount,

src/tests/components/FooterTemplateEditor.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,15 @@ describe('FooterTemplateEditor.vue', () => {
199199

200200
expect(wrapper.vm.zoomLevel).toBe(120)
201201
})
202+
203+
it('applies zoom level to PDFElements instance scale for preview', async () => {
204+
const wrapper = createWrapper()
205+
await flushPromises()
206+
207+
wrapper.vm.pdfPreview = { scale: 1 }
208+
wrapper.vm.zoomLevel = 130
209+
wrapper.vm.updateScale()
210+
211+
expect(wrapper.vm.pdfPreview.scale).toBe(1.3)
212+
})
202213
})

src/tests/components/PdfEditor/PdfEditor.spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -744,9 +744,8 @@ describe('PdfEditor Component - Business Rules', () => {
744744
})
745745

746746
describe('RULE: endInit emits measured dimensions', () => {
747-
it('adjusts zoom when auto-fit is disabled and emits page measurements', async () => {
747+
it('emits page measurements after init', async () => {
748748
Object.assign(getPdfElements(), {
749-
autoFitZoom: false,
750749
adjustZoomToFit: vi.fn(),
751750
pdfDocuments: [
752751
{
@@ -761,7 +760,7 @@ describe('PdfEditor Component - Business Rules', () => {
761760

762761
await wrapper.vm.endInit({ ready: true })
763762

764-
expect(getPdfElements().adjustZoomToFit).toHaveBeenCalledTimes(1)
763+
expect(getPdfElements().adjustZoomToFit).not.toHaveBeenCalled()
765764
expect(wrapper.emitted('pdf-editor:end-init')?.[0]?.[0]).toEqual({
766765
ready: true,
767766
measurement: {

0 commit comments

Comments
 (0)