👨💻 Kod / Programowanie
Elementy widoczne na stronie czyli viewport w Playwright
W tym wpisie przybliżę Ci praktycznie jak zbadać czy element znajduje się w widoku testowanej strony.
Będziemy testować na stronie: 🔗https://playwright.dev, którą polecam odwiedzić, aby lepiej zrozumieć kontekst tego wpisu i testów, które zrobimy.
Kod przykladów z tego wpisu wykonasz w domyślnej instalacji Playwright Test (TypeScript).
Czym jest viewport?
Teoria mówi, że viewport to wycinek strony, który widzimy w danym czasie w oknie przeglądarki.
Niezbyt imponujące. Zróbmy praktyczny test aby lepiej to zobrazować.
Pobieramy obserwowany wycinek strony oraz całą stronę:
test("viewport screenshot vs full page", async ({page}) => { await page.goto('https://playwright.dev/'); await page.screenshot({ path: 'screenshots/page/viewport.png' }); await page.screenshot({ path: 'screenshots/page/full.png', fullPage: true }); });
Zbieramy dwa zrzuty ekranu, pierwszy zbierze widok z okna przeglądarki a drugi całą stronę.
Efekt jasno nam pokazuje różnicę między viewport (po lewej) a pełnym widokiem strony (po prawej):
Dowiedzmy się więcej o parametrach naszego viewport:
test("the viewport properties", async ({page}) => { const viewport = page.viewportSize(); console.log('Viewport:', viewport); }); // Console output: // Viewport: { width: 1280, height: 720 }
Czyli w takiej rozdzielczości działa nasza przeglądarka.
Dlaczego?
Domyślna konfiguracji w Playwright, w pliku playwright.config.ts korzysta z takich ustawień przeglądarki:
{ name: 'chromium', use: { ...devices['Desktop Chrome'], }, },
Ciekawostka - nie musisz umieszczać informacji o używanej przeglądarce w pliku konfiguracyjnym. Zostanie użyta wtedy dokładnie ta wskazana powyżej.
Pełne ustawienia związane z
'Desktop Chrome'
znajdziemy się w pliku: node_modules\playwright-core\lib\server\deviceDescriptorsSource.json"Desktop Chrome": { "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.6045.9 Safari/537.36", "screen": { "width": 1920, "height": 1080 }, "viewport": { "width": 1280, "height": 720 }, "deviceScaleFactor": 1, "isMobile": false, "hasTouch": false, "defaultBrowserType": "chromium" },
Widzimy dokładne wartości dla viewport. Świetnie - już wiemy skąd ona pochodzi!
Możesz jeszcze zaobserwować wartość
screen
. Tyczy się ona emulowanego urządzenia niż samej przeglądarki i obszaru wyświetlanego, częściej ma ona zastosowanie w widokach mobilnych.Podsumujmy:
Viewport jest to obszar wyświetlany użytkownikowi związany z zawartością strony (a nie przeglądarki i jej elementów).
Najprostsza forma weryfikacji elementu w viewport
Jeśli chcemy sprawdzić czy nasz element jest w obszarze widocznym dla użytkownika wystarczy skorzystać z asercji:
await expect(locator).toBeInViewport();
Oficjalna dokumentacja: https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-be-in-viewport
Przykładem może być weryfikacja poniższego obrazka z testowanej strony:
test("browsers image is in viewport", async ({page}) => { await page.goto('https://playwright.dev/'); const browsersImage = page.getByAltText('Browsers'); await expect(browsersImage).toBeInViewport(); });
Wynik - obrazek znaleziony i jest na ekranie:
Niby prosta sprawa - ale co to oznacza?
W ten sposób sprawdziliśmy czy cały element jest wyświetlany w przeglądarce.
Element częściowo znajdujący się viewport
Może się tak zdarzyć, że tylko część elementu znajduje się w naszym widoku.
Przykładem może być element
<main>
który zawiera nie tylko wspomniany obrazek ale i elementy znajdujące się po za testowanym widokiem:Zróbmy test na tym elemencie:
test("main element is partially in viewport", async ({page}) => { await page.goto('https://playwright.dev/'); const main = page.locator('main'); await expect(main).toBeInViewport(); });
i bez problemu przejdzie. Hmm czyli to oznacza, że metoda
toBeInViewport()
domyślnie sprawdza, że wystarczy tylko częściowa obecność elementu w viewport.Element w pełni znajdujący się viewport
Sprawdźmy teraz czy nasz element znajduje się całkowicie w viewport za pomocą ustawienia
{ratio: 1}
1
oznacza 100% elementu musi się znajdować w viewport. Zróbmy test dla naszego elementu
main
test("main element is fully in viewport", async ({page}) => { await page.goto('https://playwright.dev/'); const main = page.locator('main'); await expect(main).toBeInViewport({ratio: 1}); });
Ten test zakończy się takim niepowodzeniem
Locator: locator('main') Expected: in viewport Received: outside viewport Call log: - expect.toBeInViewport with timeout 5000ms - waiting for locator('main') - locator resolved to <main>…</main> - unexpected value "viewport ratio 0.08866515755653381"
Widzimy w ostatniej linii, że zaledwie ~9% elementu znajduje się w obszarze testowanego widoku.
Czyli gdy ustawimy ratio na 0.08 to test powinien przejść:
test("main element is at least 8% in viewport", async ({page}) => { await page.goto('https://playwright.dev/'); const main = page.locator('main'); await expect(main).toBeInViewport({ratio: 0.09}); });
Teraz możesz łatwo pobawić się zmianami ustawiania
ratio
i tym samym zadbać o to aby test przeszedł zgodnie z wymaganiami.
Element poza viewport
Na koniec zostało nam zbadanie elementu poza widokiem.
Zweryfikujmy ten oto obrazek (który, jest widoczny dopiero po skrolowaniu widoku strony):
Dodamy do naszej asercji zaprzeczenie:
.not.toBeInViewport();
test("image element is outside of viewport", async ({page}) => { await page.goto('https://playwright.dev/'); const imageVSCode = page.getByAltText('VS Code'); await expect(imageVSCode).not.toBeInViewport(); });
Test przechodzi! Czyli sprawdzenie element poza viewport działa 😁
Podsumowanie
Znając działanie viewport w Playwright z łatwością przetestujesz elementy, które powinny (lub nie), znaleźć się w obszarze prezentowanym użytkownikowi.
Tego typu testy są szczególnie ważne w przypadku treści, które np. muszą znaleźć się na pierwszej stronie i użytkownik z niej korzystający powinien mieć z nimi styczność.
W szczególności, gdy chcemy się upewnić, że cały element, np. oferta zakupu, czy baner informacyjny są poprawnie wyświetlane.
Zachęcam Ciebie do przekopiowania całego kodu i modyfikacji testów, tak aby nie przechodziły lub sprawdzić procentową widoczność danego elementu. Możesz potestować swoje umiejętności np. na stronie https://unsplash.com gdyż tam często będziesz miał styczność z przyciętymi obrazkami w viewport.
Jeszcze więcej zabawy z viewport?
To nie koniec eksperymentów z viewport, gdyż w kolejnym wpisie poczytasz jak kreatywnie wykorzystać tę wiedzę: 🔗https://playwright.info/playwright-viewport-elements
Cały kod
Poniżej znajdziesz cały kod, który rozwijałem w tym wpisie:
import { expect, test } from "@playwright/test"; test("viewport screenshot vs full page", async ({page}) => { await page.goto('https://playwright.dev/'); await page.screenshot({ path: 'screenshots/page/viewport.png' }); await page.screenshot({ path: 'screenshots/page/full.png', fullPage: true }); }); test("the viewport properties", async ({page}) => { const viewport = page.viewportSize(); console.log('Viewport size:', viewport); }); test("browsers image is in viewport", async ({page}) => { await page.goto('https://playwright.dev/'); const browsersImage = page.getByAltText('Browsers'); await expect(browsersImage).toBeInViewport(); }); test("main element is partially in viewport", async ({page}) => { await page.goto('https://playwright.dev/'); const main = page.locator('main'); await expect(main).toBeInViewport(); }); // below test will fail: element is only 8% in viewport test("main element is fully in viewport", async ({page}) => { await page.goto('https://playwright.dev/'); const main = page.locator('main'); await expect(main).toBeInViewport({ratio: 1}); }); test("main element is at least 8% in viewport", async ({page}) => { await page.goto('https://playwright.dev/'); const main = page.locator('main'); await expect(main).toBeInViewport({ratio: 0.09}); }); test("image element is outside of viewport", async ({page}) => { await page.goto('https://playwright.dev/'); const imageVSCode = page.getByAltText('VS Code'); await expect(imageVSCode).not.toBeInViewport(); });