Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Validate if an element to scroll is actually scrollable #118

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 33 additions & 18 deletions src/browser-scripts/scroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,54 @@ export interface ScrollPosition {
left: number;
}

export function scrollToBottom(selector: string) {
var element = document.querySelector(selector);
export function scrollAction(
action: 'scrollToOffset' | 'scrollToRight' | 'scrollToBottom',
selector: string,
offset?: { top: number; left: number }
) {
const element = document.querySelector(selector);
if (!element) {
throw new Error('Element ' + selector + ' has not been found at the page');
}

element.scrollTop = element.scrollHeight;
}
const overflowDirection =
action === 'scrollToOffset' ? 'overflow' : action === 'scrollToBottom' ? 'overflowY' : 'overflowX';

export function scrollToRight(selector: string) {
var element = document.querySelector(selector);
if (!element) {
throw new Error('Element ' + selector + ' has not been found at the page');
const overflowStyles = getComputedStyle(element)[overflowDirection].split(' ');
if (!overflowStyles.includes('auto') && !overflowStyles.includes('scroll') && element !== document.documentElement) {
throw new Error('Element ' + selector + ' is not scrollable');
}

element.scrollLeft = element.scrollWidth;
}

export function elementScrollTo(selector: string, top: number, left: number) {
var element = document.querySelector(selector);
if (!element) {
throw new Error('Element ' + selector + ' has not been found at the page');
switch (action) {
case 'scrollToOffset':
if (!offset) {
throw new Error('Cannot scroll to offset without an offset');
}
element.scrollTop = offset.top;
element.scrollLeft = offset.left;
break;
case 'scrollToBottom':
element.scrollTop = element.scrollHeight;
break;
case 'scrollToRight':
element.scrollLeft = element.scrollWidth;
break;
default:
throw new Error(`Unsupported scroll action ${action}`);
}
element.scrollTop = top;
element.scrollLeft = left;
}

export function getElementScrollPosition(selector: string): ScrollPosition {
var element = document.querySelector(selector);
if (!element) {
// We cannnot use our custom error types as they are not available inside the browser context
throw new Error('Element ' + selector + ' has not been found at the page');
}

const overflowStyles = getComputedStyle(element).overflow.split(' ');

if (!overflowStyles.includes('auto') && !overflowStyles.includes('scroll')) {
throw new Error('Element ' + selector + ' is not scrollable');
}
return { top: element.scrollTop, left: element.scrollLeft };
}

Expand Down
4 changes: 2 additions & 2 deletions src/page-objects/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import pRetry from 'p-retry';
import {
getElementScrollPosition,
getWindowScrollPosition,
elementScrollTo,
scrollAction,
windowScrollTo,
getViewportSize,
ScrollPosition,
Expand Down Expand Up @@ -135,7 +135,7 @@ export default class BasePageObject {
}

async elementScrollTo(selector: string, { top = 0, left = 0 }: Partial<ScrollPosition>) {
await this.browser.execute(elementScrollTo, selector, top, left);
await this.browser.execute(scrollAction, 'scrollToOffset', selector, { top, left });
}

async waitForVisible(selector: string, shouldDisplay = true, timeout?: number) {
Expand Down
6 changes: 3 additions & 3 deletions src/page-objects/screenshot.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { scrollToBottom, scrollToRight } from '../browser-scripts';
import { scrollAction } from '../browser-scripts';
import { parsePng } from '../image-utils';
import BasePageObject from './base';
import { ElementOffset, ScreenshotCapturingOptions, ScreenshotWithOffset } from './types';
Expand All @@ -16,11 +16,11 @@ export default class ScreenshotPageObject extends BasePageObject {
}

async scrollToBottom(selector: string) {
await this.browser.execute(scrollToBottom, selector);
await this.browser.execute(scrollAction, 'scrollToBottom', selector);
}

async scrollToRight(selector: string) {
await this.browser.execute(scrollToRight, selector);
await this.browser.execute(scrollAction, 'scrollToRight', selector);
}

async fullPageScreenshot() {
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/test-page-object.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
<div id="scrollable-container" style="overflow: auto; max-height: 30px">
<div style="width: 120%; height: 100px">Some scrollable text</div>
</div>
<div id="vertically-scrollable-container" style="overflow-y: scroll; overflow-x: hidden; max-height: 30px">
<div style="height: 100px; width: 100px;">Some scrollable text to layout vertically in a container</div>
</div>
<button id="button">Click me</button>
<button id="update-live-announcement-button">Update live announcement</button>
<div aria-live="polite" id="live-announcement"></div>
Expand Down
17 changes: 17 additions & 0 deletions test/page-object.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,23 @@ test(
})
);

test(
'elementScrollTo should not scroll when trying to scroll a non-scrollable element',
setupTest(async page => {
await expect(() => page.elementScrollTo('#text-content', { left: 40 })).rejects.toThrowError(
/Element #text-content is not scrollable/
);
})
);

test(
'elementScrollTo should scroll when one direction is scrollable',
setupTest(async page => {
await page.elementScrollTo('#vertically-scrollable-container', { top: 40 });
expect(await page.getElementScroll('#vertically-scrollable-container')).toEqual({ top: 40, left: 0 });
})
);

test(
'scrollToRight',
setupTest(async page => {
Expand Down
Loading