💻 Frontend

Testy automatyczne działania pseudo-elementów z Playwright

Language
date
Sep 13, 2023
slug
pseudo-elementy-testy-automatyczne-playwright
author
status
Public
tags
CSS
Playwright
TypeScript
Porady
Automatyzacja
summary
Jak z Playwright przetestować pseudo-element niewidoczny w kodzie strony
type
Post
thumbnail
pseudo-elementy-i-testy-automatyczne-playwright.jpg
updatedAt
Oct 5, 2023 10:46 AM
category
💻 Frontend
Pseudo-elementy (pseudo-klasy) CSS wpływają na wygląd treści na stronach, ale mogą być trudnym przypadkiem do testowania. Poćwiczmy razem testy takiego pseudo-elementu jakim np. jest ::after a następnie zajmiemy się innym typem elementu wstrzykiwanego przez przeglądarkę.

Czym jest pseudo-element?

 
::after to jeden z pseudo-elementów CSS, który jest używany do dodawania zawartości do elementu HTML bez konieczności dodawania dodatkowego kodu do elementu. Aby wykorzystać działanie ::after należy utworzyć selektor CSS. Na przykład:
p::after { content: " - To jest dodatkowy tekst"; color: red; }
Gdy mamy powyższy kod CSS przy każdym użyciu elementu p w kodzie strony zostanie dodany czerwony tekst.
 
Czyli mając taki kod: <p class="example">To jest przykładowy tekst.</p> Zawartość na stronie będzie wyglądać tak:
To jest przykładowy tekst. - To jest dodatkowy tekst
 

Przykład praktyczny

 
Przeanalizujmy stronę z takimi elementami:
Widzimy taki oto tekst:
notion image
Ale w pliku HTML nie znajdziemy części z linkiem w nawiasie, doklejonej do tekstu (jak powyżej) .
Gdy w przeglądarce wykonamy akcję inspect (albo zbadaj element) na tym elemencie, to otrzymamy:
notion image
A dokładniej interesuje nas ten fragment kodu HTML:
<div id="html-output" class="output editor-tabbed"> <p> " The sailfish is named for its sail-like dorsal fin and is widely considered the fastest fish in the ocean." <a href="https://en.wikipedia.org/wiki/Sailfish"> "You can read more about it here" ::after </a> ". " </p>
Jest to sprawka pseudo elementu zapisanego w pliku CSS:
a::after { content: ' (' attr(href) ')'; }
Jak widzisz, każdy element a otrzyma dodatkowy tekst w nawiasie składający się z linku, jaki jest zdefiniowany w tym elemencie (w atrybucie href)
 

Załapanie takiego elementu w Playwright

 
Skoro takiego elementu nie ma bezpośrednio w kodzie HTML strony, to jak sprawdzić jego działanie?
 
Jak złapać ten tekst? 🤔
 
Tradycyjne pobranie elementu i sprawdzenie tekstu nie przejdzie, gdyż Playwright nie będzie w stanie go pobrać. Dlatego musimy zastosować inne rozwiązanie…
 

Sposób na weryfikację wartości z ::after

 
Możemy zastosować strategię pobrania wyrenderowanego stylu.
Przyjrzyjmy się poniższemu testowi:
import { expect, test } from "@playwright/test"; test.describe("jaktestowac.pl concepts", async () => { test("check rendering with ::after pseudo-class", async ({ page }) => { await page.goto( "https://interactive-examples.mdn.mozilla.net/pages/tabbed/pseudo-element-after.html" ); const frame = page.frameLocator("#output-iframe"); const contentOfPseudoElement = await frame .locator("#html-output > p:nth-child(1) > a") .evaluate((e) => { return window .getComputedStyle(e, "::after") .getPropertyValue("content"); }); expect(contentOfPseudoElement).toContain(" (https://en.wikipedia.org/wiki/Sailfish)"); }); });
 

Omówmy kod:

 
Przechodzimy do testowanej strony:
await page.goto( "https://interactive-examples.mdn.mozilla.net/pages/tabbed/pseudo-element-after.html" );
następnie warto złapać iframe, w którym znajduje się nasz kod:
const frame = page.frameLocator("#output-iframe");
 
Potem możemy wyciągnąć wytworzoną przez CSS wartość:
const contentOfPseudoElement = await frame .locator("#html-output > p:nth-child(1) > a") .evaluate((e) => { return window .getComputedStyle(e, "::after") .getPropertyValue("content"); });
Omówmy składniki tego wyrażenia:
  1. Odszukujemy element z likiem, który chcemy zbadać: .locator("#html-output > p:nth-child(1) > a")
  1. Następnie wykonujemy kod JavaScript na naszym elemencie (jako e): .evaluate((e) => {
  1. Zwracamy wynik operacji wykonanych na obiekcie reprezentującym przeglądarkę window: return window
  1. Po pierwsze aplikujemy style CSS na elemencie: .getComputedStyle(e, "::after")
  1. Po drugie pobieramy jego zawartość czyli content: .getPropertyValue("content");
  1. i to finalnie jest zwracane do zmiennej: contentOfPseudoElement
 
Na koniec pozostaje już tylko porównać, czy oczekiwana wartość zgadza się z wartością otrzymaną ze strony:
expect(contentOfPseudoElement).toContain(" (https://en.wikipedia.org/wiki/Sailfish)");
 
Warto uzupełnić swoją wiedzę na temat pseudo-elementu ::after ze strony 🔗https://developer.mozilla.org/en-US/docs/Web/CSS/::after
I podglądnąć na niej przykłady 🔗https://developer.mozilla.org/en-US/docs/Web/CSS/::after

Kolejny wirtualny element: ValidationMessage

Przeglądarki mogą automatycznie obsłużyć niepoprawne wartości w elementach typu input i zaprezentować nam takie ostrzeżenie:
notion image
 
Kod takiego input będzie wyglądał w przybliżeniu tak:
<div id="root"> <form> <label for="email">Email</label> <input name="email" type="email"> <input type="submit"> </form> </div>
 
Nigdzie nie znajdziemy wiadomości ostrzeżenia. W kodzie stylów CSS też jej nie ma gdyż tego typu element jest generowany przez przeglądarkę i dodawany w locie do naszej strony.
 
Ale jesteśmy w stanie tę wiadomość złapać:
import { expect, test} from '@playwright/test'; test('has warning message presented', async ({ page }) => { await page.goto('https://q94u5.csb.app/'); const email = page.getByRole('textbox') const submit = page.getByRole('button', { name: 'Submit' }); await email.fill('x'); await submit.click(); const validationMessage = await email.evaluate((element) => { const input = element as HTMLInputElement return input.validationMessage }) expect(validationMessage).toContain("Please include an '@' in the email address. 'x' is missing an '@'.") });
 
Na początek warto zwrócić uwagę na wyszukany element.
Tutaj, tak samo jak w poprzednim przykładzie, jesteśmy w stanie za pomocą evaluate dostać się do elementu i jego property validationMessage.
 
Dodatkowo musimy jawnie określić typ nasz element:HTMLInputElement
const input = element as HTMLInputElement
Zmiana jego typu wynika z tego, że domyślnie element jest oczekiwany w dwóch typach SVGElement | HTMLElement - a my mamy tu HTMLInputElement.
 
Możemy się dostać do wielu innych własności elementu o których możesz poczytać na: 🔗https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#the_constraint_validation_api
Podstawowe informacje na temat walidacji pól znajdziesz 🔗https://www.w3.org/WAI/tutorials/forms/validation/

Podsumowanie

 
Testy ::after czy validationMessage to dość kontrowersyjny temat, ze względu na to, że testujemy w pewnym sensie działanie klasy CSS czy samej przeglądarki. Warto jednak zapoznać się z tego typu testami, gdy przyjdzie nam sprawdzić wartość, dla której standardowe pobranie z lokatora nie jest możliwe.
Jeśli znasz inny sposób na obsługę tego typu przypadków w Playwright napisz do mnie!😉