💻 Frontend
Testy automatyczne działania pseudo-elementów z Playwright
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:
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: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:
- Odszukujemy element z likiem, który chcemy zbadać:
.locator("#html-output > p:nth-child(1) > a")
- Następnie wykonujemy kod JavaScript na naszym elemencie (jako
e
):.evaluate((e) => {
- Zwracamy wynik operacji wykonanych na obiekcie reprezentującym przeglądarkę
window
:return window
- Po pierwsze aplikujemy style CSS na elemencie:
.getComputedStyle(e, "::after")
- Po drugie pobieramy jego zawartość czyli
content
:.getPropertyValue("content");
- 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/::afterI 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:
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!😉