diff --git a/autotests/pageObjects/pages/Search.ts b/autotests/pageObjects/pages/Search.ts index cc8fc2c3..27a31d20 100644 --- a/autotests/pageObjects/pages/Search.ts +++ b/autotests/pageObjects/pages/Search.ts @@ -1,4 +1,5 @@ import {MobilePage} from 'autotests/pageObjects'; +import {Input} from 'autotests/pageObjects/components'; import {Search as SearchRoute} from 'autotests/routes/pageRoutes'; import {setReadonlyProperty} from 'e2ed/utils'; @@ -16,6 +17,8 @@ export class Search extends MobilePage { */ readonly mobileDevice = 'iphone' as const; + readonly searchInput: Input = new Input('q'); + /** * The search query of the page. */ diff --git a/autotests/tests/main/viewport.ts b/autotests/tests/main/viewport.ts new file mode 100644 index 00000000..afe21434 --- /dev/null +++ b/autotests/tests/main/viewport.ts @@ -0,0 +1,68 @@ +import {test} from 'autotests'; +import {Search} from 'autotests/pageObjects/pages'; +import {expect} from 'e2ed'; +import {navigateToPage, scroll} from 'e2ed/actions'; +import {isSelectorEntirelyInViewport, isSelectorInViewport} from 'e2ed/utils'; + +test( + 'isSelectorInViewport, isSelectorEntirelyInViewport and expect().toBeInViewport', + {meta: {testId: '24'}}, + async () => { + const scrollValueForPartialInViewport = 50; + const scrollValueForNotInViewport = 500; + + const searchQuery = 'foo'; + const searchPage = await navigateToPage(Search, {searchQuery}); + + const searchInputSelector = searchPage.searchInput.input; + + await expect( + isSelectorInViewport(searchInputSelector), + 'isSelectorInViewport: searchInput is in viewport after page load', + ).ok(); + + await expect( + isSelectorEntirelyInViewport(searchInputSelector), + 'isSelectorEntirelyInViewport: searchInput is entirely in viewport after page load', + ).ok(); + + await expect( + searchInputSelector, + 'toBeInViewport: searchInput is in viewport after page load', + ).toBeInViewport(); + + await expect( + searchInputSelector, + 'toBeInViewport with ratio 1: searchInput is entirely in viewport after page load', + ).toBeInViewport({ratio: 1}); + + await scroll(0, scrollValueForPartialInViewport); + + await expect( + isSelectorInViewport(searchInputSelector), + 'isSelectorInViewport: searchInput is in viewport after smaill scroll', + ).ok(); + + await expect( + isSelectorEntirelyInViewport(searchInputSelector), + 'isSelectorEntirelyInViewport: searchInput is not entirely in viewport after small scroll', + ).notOk(); + + await expect( + searchInputSelector, + 'toBeInViewport with ratio 0.1: searchInput is not entirely in viewport after small scroll', + ).toBeInViewport({ratio: 0.1}); + + await scroll(0, scrollValueForNotInViewport); + + await expect( + isSelectorInViewport(searchInputSelector), + 'isSelectorInViewport: searchInput is not in viewport after big scroll', + ).notOk(); + + await expect( + isSelectorEntirelyInViewport(searchInputSelector), + 'isSelectorEntirelyInViewport: searchInput is not entirely in viewport after big scroll', + ).notOk(); + }, +); diff --git a/bin/makeExecutable.sh b/bin/makeExecutable.sh index 9af195af..356805fe 100755 --- a/bin/makeExecutable.sh +++ b/bin/makeExecutable.sh @@ -4,6 +4,6 @@ IFS=$'\n\t' files=("$@") -for file in ${files[*]}; do - sed -i '1s|^|#!/usr/bin/env node\n\n|' $file && chmod +x $file +for file in "${files[@]}"; do + printf '#!/usr/bin/env node\n\n' | cat - "$file" > "$file.tmp" && mv "$file.tmp" "$file" && chmod +x "$file" done diff --git a/package.json b/package.json index 9d18d32f..9dee9e21 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "prebuild": "npm run build:clear", "build": "tsc; echo 'Compilation completed'", "postbuild": "npm run build:rename && npm run build:remove && npm run build:copy && npm run build:init", - "prebuild:rename": "mkdir --parents ./build/node_modules", + "prebuild:rename": "mkdir -p ./build/node_modules", "build:rename": "mv ./build/src ./build/node_modules/e2ed", "build:remove": "npm run build:remove:empty-d-ts && npm run build:remove:types-js", "build:remove:empty-d-ts": "find ./build/node_modules/e2ed/ -name *.d.ts -size 11c -exec rm {} \\;", diff --git a/src/utils/viewport/isSelectorEntirelyInViewport.ts b/src/utils/viewport/isSelectorEntirelyInViewport.ts index e1205175..64fe44e3 100644 --- a/src/utils/viewport/isSelectorEntirelyInViewport.ts +++ b/src/utils/viewport/isSelectorEntirelyInViewport.ts @@ -1,15 +1,20 @@ import type {Selector} from '../../types/internal'; +import {expect as playwrightExpect} from '@playwright/test'; + /** * Returns `true`, if the selector is entirely in the viewport * (all selector points are in the viewport), and `false` otherwise. */ export const isSelectorEntirelyInViewport = async (selector: Selector): Promise => { - const htmlElementSelector = selector.createSelector('html'); - - const {height: clientHeight, width: clientWidth} = await htmlElementSelector.boundingClientRect; - - const {bottom, left, right, top} = await selector.boundingClientRect; + try { + await playwrightExpect(selector.getPlaywrightLocator()).toBeInViewport({ + ratio: 1, + timeout: 1, + }); - return top >= 0 && bottom <= clientHeight && left >= 0 && right <= clientWidth; + return true; + } catch { + return false; + } }; diff --git a/src/utils/viewport/isSelectorInViewport.ts b/src/utils/viewport/isSelectorInViewport.ts index 3a5c74a4..7fb18b15 100644 --- a/src/utils/viewport/isSelectorInViewport.ts +++ b/src/utils/viewport/isSelectorInViewport.ts @@ -1,15 +1,17 @@ import type {Selector} from '../../types/internal'; +import {expect as playwrightExpect} from '@playwright/test'; + /** * Returns `true`, if the selector is in the viewport * (intersects with the viewport at least in one point), and `false` otherwise. */ export const isSelectorInViewport = async (selector: Selector): Promise => { - const htmlElementSelector = selector.createSelector('html'); - - const {height: clientHeight, width: clientWidth} = await htmlElementSelector.boundingClientRect; - - const {bottom, left, right, top} = await selector.boundingClientRect; + try { + await playwrightExpect(selector.getPlaywrightLocator()).toBeInViewport({timeout: 1}); - return top < clientHeight && bottom > 0 && left < clientWidth && right > 0; + return true; + } catch { + return false; + } };