W tym artykule kontynuujemy naszą przygodę z budową frameworka testowego opartego na Playwright i TypeScript. W poprzednim wpisie przedstawiłem kilka kluczowych koncepcji, które będą niezbędne w dalszej pracy nad kodem.
Dziś skupimy się na fragmencie kodu, który posłuży jako fundament całego rozwiązania. Omówimy sposób, w jaki można łączyć operacje API z testami UI oraz jak wykorzystać dobre praktyki projektowe, takie jak wzorce factory, page object pattern oraz dedykowane klasy asercji, by stworzyć skalowalne i łatwe w utrzymaniu testy automatyczne.
Do stworzenia przykładowego kodu, który zilustruje działanie naszego frameworka, posłużymy się aplikacją Trello – tak jak wspomniałem w pierwszej części. Trello to narzędzie do zarządzania projektami w oparciu o tablice kanban. Posiada również rozbudowane API, co czyni je świetnym wyborem do nauki automatyzacji testów.
Zachęcam, by podczas nauki korzystać z bardziej zaawansowanych aplikacji – takich, które odzwierciedlają realne scenariusze z codziennej pracy testera automatyzującego. Dzięki temu nasze testy będą bardziej realistyczne i lepiej przygotują nas do projektów komercyjnych.
Co będzie robił nasz test?
Nasz test będzie:
- Tworzył nową tablicę (board) w Trello za pomocą API.
- Tworzył listy przypisane do tej tablicy.
- Tworzył karty przypisane do konkretnej listy.
- Dodawał nową (dodatkową) listę.
- Przenosił karty do nowo utworzonej listy.
- Weryfikował, czy karty faktycznie znalazły się w odpowiedniej liście (asercje).
Przegląd architektury frameworka: Playwright + TypeScript

Diagram przedstawia architekturę naszego frameworka testowego opartego na Playwright i TypeScript, z naciskiem na testy integrujące warstwę API z UI:
- Test Runner (
Playwright)- Odpowiada za uruchamianie testów (
spec files) oraz za zarządzanie cyklem życiafixtures.
- Odpowiada za uruchamianie testów (
- Fixtures (
boardFactory,cardFactory,trelloApi)- Wstrzykiwane zależności do testów – odpowiadają za tworzenie i usuwanie danych testowych.
- Są centralnym punktem integracji pomiędzy testami a logiką API.
- Pozwalają na zmniejszoną ilość duplikacji kodu.
- Fabryki (
BoardFactory,CardFactory)- Opakowują wywołania API w ustrukturyzowaną, łatwą do testowania formę.
- Przykład dobrego zastosowania wzorca Factory Pattern.
- TrelloAPI
- Klasa komunikująca się z Trello REST API przez
APIRequestContextz Playwrighta. - Zawiera metody takie jak
createBoard,createCard,getLists,deleteBoard.
- Klasa komunikująca się z Trello REST API przez
- APIRequestContext → Trello REST API
- Bezpośrednia warstwa realizująca zapytania HTTP.
- Autoryzacja oparta o
apiKeyitoken.
- Page Objecty (
LoginPage,BoardPage) + Asercje- Klasy reprezentujące interfejs użytkownika Trello.
- Rozdzielają logikę interakcji od testów.
Klasa TrelloBoardAssertionsto osobna warstwa do sprawdzania stanu UI (np. kart w kolumnach).
- Konfiguracja (
TrelloSettings) + zmienne środowiskowe- Ustawienia frameworka: URL-e, klucze, dane logowania.
- Możliwość nadpisywania wartości przez
process.envw CI/CD.
Całość pozwala na:
- Spójne tworzenie testów E2E z wykorzystaniem danych tworzonych przez API.
- Łatwe utrzymanie, testowanie i rozbudowę frameworka.
- Reużywalność dzięki tej strukturze i czystemu rozdzieleniu warstw.
Na co warto zwrócić uwagę?
- Dla każdego testu powinniśmy tworzyć świeże dane testowe, aby zapewnić ich niezależność.
- Po zakończeniu testu należy te dane usunąć, aby nie zaśmiecały środowiska testowego.
- W przypadku Trello ma to szczególne znaczenie – darmowe konto pozwala na utworzenie maksymalnie 10 tablic. Jeżeli o tym zapomnimy, testy mogą kończyć się błędami z powodu przekroczenia limitu.
Klasa TrelloBoardPage

Klasa TrelloBoardPage zawiera zestaw metod odpowiedzialnych za interakcje z tablicą w aplikacji Trello przy użyciu Playwrighta. Odpowiada za obsługę działań typowych dla użytkownika na poziomie tablicy, takich jak nawigacja, przenoszenie kart czy weryfikacja ich obecności w kolumnach.
Ponieważ Trello jest aplikacją typu SPA (Single Page Application), liczba dostępnych akcji w obrębie jednej podstrony (np. boardu) może być bardzo duża. W takiej sytuacji warto rozdzielać logikę na mniejsze klasy Page Object, zgodnie z zasadą single responsibility. Dzięki temu kod będzie bardziej czytelny i łatwiejszy w utrzymaniu.
Czym charakteryzuje się SPA (Single Page Application)?
Do cech charakterystycznych dla Single Page Application należy:
- Dynamiczne przeładowywanie treści – elementy strony (np. kolumny, karty) aktualizują się bez pełnego przeładowania.
- Wykorzystanie JavaScriptu – Trello używało m.in. Backbone.js, ale w każdej SPA JavaScript zarządza dynamicznymi interakcjami, animacjami i zdarzeniami (np. drag-and-drop).
- Asynchroniczne pobieranie danych – za pomocą AJAX lub WebSocket, dane są pobierane bez odświeżania strony, co zapewnia płynność działania.
- Zarządzanie stanem aplikacji po stronie klienta – np. aktualny board, filtr czy otwarta karta są trzymane lokalnie i synchronizowane z serwerem w tle.
- Zmienny URL bez przeładowania strony – adres URL zmienia się w kontekście konkretnego boarda lub karty, ale strona pozostaje aktywna.
Metoda navigateToBoard
Jedną z kluczowych metod w klasie TrelloBoardPage jest navigateToBoard(url). Otwiera ona podaną stronę i oczekuje, aż pojawi się element reprezentujący tytuł tablicy, świadczący o tym, że interfejs jest gotowy do dalszych akcji testowych.

W tej klasie przekazujemy obiekt page (dostarczany przez Playwright) jako parametr konstruktora, co umożliwia nam wykonywanie akcji w kontekście konkretnej przeglądarki.
Na początku definiujemy prywatne pola typu string, które zawierają selektory do elementów na stronie:
usernameField– pole na nazwę użytkownika,passwordField– pole na hasło,loginSubmitButton– przycisk logowania,allBoardsContent– kontener z tablicami widoczny po zalogowaniu.
Metoda go(url: string)
Metoda go umożliwia przejście do określonego adresu URL. Obecnie przyjmuje go jako parametr, ale można ją łatwo rozbudować np. o pobieranie wartości z zmiennej środowiskowej, co przydałoby się w przypadku pracy na różnych środowiskach (dev, staging, prod).
Metoda getAllBoardsVisibleStatus()
Ta metoda sprawdza, czy element odpowiadający za kontener tablic (allBoardsContent) jest widoczny. Dzięki temu możemy weryfikować, czy użytkownik został pomyślnie zalogowany.
Metoda login(username: string, password: string)
Tutaj widzimy kompletny flow logowania:
- Kliknięcie przycisku „Log in” w nagłówku strony (menu).
- Wprowadzenie nazwy użytkownika.
- Kliknięcie przycisku
continue. - Wprowadzenie hasła.
- Kliknięcie przycisku
login. - Oczekiwanie na załadowanie elementu tablic (
allBoardsContent).
Można by rozważyć dodanie dodatkowej metody do nawigacji bezpośrednio do strony logowania, np. poprzez przekazanie URL jako parametru lub skonfigurowanie osobnych endpointów w zależności od środowiska.
Omówienie typu trelloFixtures


Na powyższym fragmencie widać type definiujący fixtures, z których korzystają wszystkie testy. To miejsce, w którym inicjalizowane są wszystkie zależności: obiekty typu PageObject, Factory, API, Assertions, a także konfiguracja (TrelloSettings).
Warto podkreślić, że:
- fixtures są rejestrowane poprzez test.extend<TrelloFixtures>(),
- każda zależność jest tworzona tylko wtedy, gdy dana fixture jest potrzebna w danym teście (lazy-loaded),
- dzięki temu unika się zbędnego kodu w testach i można łatwo konfigurować i dzielić stan między przypadkami testowymi.
Wskazówka projektowa
W tym przykładzie wszystkie PageObjecty, klasy pomocnicze (Factory, Assertions) i konfiguracja zostały umieszczone w jednej klasie TrelloFixtures. Dla celów edukacyjnych i małego projektu to bardzo wygodne, ale w środowisku produkcyjnym warto rozważyć bardziej granularne podejście:
- Podziel
fixturesna mniejsze grupy (np.apiFixtures,uiFixtures,authFixtures). - Ułatwi to utrzymanie, testowanie i refaktoryzację poszczególnych warstw frameworka.
- Mniejsze pliki są też znacznie łatwiejsze do przeglądania i debugowania, zwłaszcza w większych projektach.
Interfejsy – po co z nich korzystamy?
Na podstawie odpowiedzi z API możemy tworzyć własne interfejsy, np. Board, List, Card. Dzięki TypeScriptowi, jeśli API Trello zwraca dane zgodne z tymi interfejsami, możemy je bezpiecznie wykorzystywać w kodzie. Interfejsy te można oczywiście rozbudowywać w zależności od naszych potrzeb projektowych.
Przykład interfejsu Board:

Podobnie wyglądają List i Card,


co pozwala na typowanie danych zwracanych z API i daje lepszą kontrolę w kodzie testów oraz implementacjach factory.
Metody, które korzystając z tych interfejsów np.

Metody, takie jak createBoard, zwracają odpowiedź zgodną z interfejsem Board. Jeśli zdefiniujemy w interfejsie pola odpowiadające strukturze zwracanej przez API Trello, TypeScript umożliwi nam bezpieczny dostęp do tych danych. Dzięki temu możemy z łatwością korzystać z takich właściwości jak id, name czy url bez dodatkowego rzutowania typów.
Oczywiście interfejsy takie jak Board, List czy Card można swobodnie rozszerzać w zależności od potrzeb projektu – na przykład o dodatkowe pola opcjonalne. Taka elastyczność ułatwia utrzymanie i rozwój frameworku testowego.
Przechowywanie ustawień w frameworku testowym – klasa Settings.ts

Zarządzanie konfiguracją we frameworkach automatyzacji testów
W każdym większym frameworku testowym pojawia się potrzeba zarządzania konfiguracją – taką jak klucze API, adresy URL środowisk, dane logowania itp.
Istnieje kilka sposobów na organizację takich ustawień. Najpopularniejsze podejścia to:
- Przechowywanie ustawień w plikach
.json,.ymllub.env. - Wykorzystywanie zmiennych środowiskowych przez
process.env. - Łączenie obu podejść z warstwą konfiguracyjną w kodzie (np. klasą
Settings).
Podejście z plikiem .json
W przedstawionym przykładzie konfiguracja przechowywana jest w pliku TrelloSettings.json. To podejście ma wiele zalet:
- Pozwala łatwo modyfikować dane konfiguracyjne bez rekompilacji projektu.
- Umożliwia posiadanie osobnych plików dla różnych środowisk (np.
settings.dev.json,settings.staging.json). - Dane wrażliwe (takie jak tokeny API) można nadpisać zmiennymi środowiskowymi w CI/CD, zamiast trzymać je w repozytorium.
Klasa TrelloSettings
W kodzie widzimy klasę TrelloSettings, która:
- Wczytuje konfigurację z pliku o rozszerzeniu typu .json, którego ścieżka przekazywana jest do konstruktora.
- Sprawdza istnienie pliku i wyrzuca błąd, jeśli plik nie istnieje.
- Parsuje plik JSON i przypisuje poszczególne wartości do pól klasy (takich jak
apiKey,token,baseUrlitd.).
To prosty i skuteczny sposób na enkapsulację konfiguracji w jednej klasie.
Pro tip! Warto dodać logikę, która umożliwi pobieranie niektórych wartości z process.env, jeśli nie są one ustawione w pliku. Dzięki temu możesz trzymać „bezpieczne” wartości w pliku lokalnie, a wrażliwe dane ładować dynamicznie w pipeline.
Klasa BoardFactory – tworzenie i usuwanie boardów (tablic)

Klasa BoardFactory odpowiada za operacje tworzenia i usuwania tablic (boardów) w aplikacji Trello, wykorzystując do tego metody dostępne w warstwie API (TrelloAPI).
Zastosowanie Factory Pattern w tym miejscu to świetne rozwiązanie – pozwala nam oddzielić logikę tworzenia danych testowych od samego testu. Dzięki temu testy stają się bardziej przejrzyste, a zarządzanie danymi prostsze i bardziej kontrolowane.
Co robią metody?
createBoard(name: string)– tworzy nową tablicę w Trello z podaną nazwą. Zwraca obiekt typuBoard.deleteBoard(boardId: string)– usuwa tablicę o wskazanymboardId. Warto wykorzystywać tę metodę np. wafterEachlubfinally, aby usuwać dane testowe po zakończeniu testu.
Dlaczego to dobre podejście?
- Rozdziela odpowiedzialności – test skupia się na sprawdzaniu zachowań, a factory zajmuje się tworzeniem danych.
- Ułatwia ponowne wykorzystanie kodu (reusability).
- Umożliwia łatwą rozbudowę (np. o dodatkowe parametry, tworzenie tablic z domyślnymi listami itp.).
Klasa CardFactory – tworzenie kart dla tablicy

W naszym przykładzie wykorzystujemy dwie klasy odpowiedzialne za tworzenie danych testowych: BoardFactory oraz CardFactory.
Skupmy się na CardFactory. Klasa ta zawiera metodę createCards, która przyjmuje dwa parametry:
titles– tablica zawierająca tytuły kart, które mają zostać utworzone.boardId– identyfikator tablicy, do której karty mają zostać przypisane.
Wewnątrz metody wykonujemy następujące kroki:
- Pobieramy listy dostępne na wskazanej tablicy (
getListsz API Trello). - Tworzymy pustą tablicę
cards, w której będą przechowywane utworzone obiekty kart. - Iterujemy po tytułach i dla każdej wartości wywołujemy metodę
createCard, przekazując tytuł oraz ID pierwszej listy (przyjmujemy, że tworzymy wszystkie karty w jednej domyślnej liście). - Dodajemy każdą nowo utworzoną kartę do tablicy
cards. - Zwracamy tablicę wszystkich utworzonych kart.
Takie podejście pozwala nam tworzyć zestawy testowych danych w sposób modularny i wielokrotnego użytku, a sama metoda może być łatwo rozbudowana np. o obsługę wielu list lub losowe przypisywanie kart.
Klasa TrelloBoardAssertions – weryfikacja kart w kolumnie

Klasa TrelloBoardAssertions odpowiada za warstwę asercji w testach – sprawdzanie, czy oczekiwany stan faktycznie występuje w UI.
Na początku przekazujemy do konstruktora obiekt TrelloBoardPage, dzięki czemu w dalszej części klasy możemy korzystać z metod UI zdefiniowanych w tej klasie – w tym przypadku z metody getAllCardsInColumn, która zwraca wszystkie karty znajdujące się w danej kolumnie.
Metoda verifyCardsInColumn przyjmuje dwa parametry:
cardTitles– tablicę z tytułami kart, które chcemy zweryfikować,columnName– nazwę kolumny, w której te karty powinny się znajdować.
Jak działa ta metoda?
- Pobieramy wszystkie tytuły kart z określonej kolumny, używając metody
getAllCardsInColumn. - Iterujemy po wszystkich oczekiwanych tytułach (
cardTitles), sprawdzając:- czy dana karta faktycznie znajduje się w kolumnie (
.toBeVisible()), - czy jej tekst odpowiada oczekiwanemu tytułowi.
- czy dana karta faktycznie znajduje się w kolumnie (
- Na końcusprawdzamy czy:
- wszystkie oczekiwane karty są zawarte w kolumnie (
toEqual(expect.arrayContaining(...))), - liczba znalezionych kart odpowiada liczbie oczekiwanych (
toBe(cardTitles.length)).
- wszystkie oczekiwane karty są zawarte w kolumnie (
To podejście pozwala upewnić się, że zarówno zawartość, jak i liczba kart w kolumnie są zgodne z oczekiwaniami.
Finalny kod testu – trelloBoardTests.spec.ts

Na załączonym fragmencie widzimy kod testu end-to-end, który tworzy tablicę w Trello, dodaje karty, przenosi je do innej kolumny, a na końcu usuwa testowe dane. Test korzysta z wcześniej zdefiniowanych fixture’ów takich jak boardFactory, cardFactory czy trelloApi, które są dostarczone jako zależności.
Pierwszym krokiem jest wygenerowanie unikalnej nazwy tablicy przy użyciu Date.now(), co minimalizuje ryzyko konfliktu nazw między testami. Następnie tworzona jest tablica (board) za pomocą API.x
W dalszej części definiujemy tablicę cardTitles, zawierającą nazwy kart, które mają zostać dodane do tablicy. Te karty są tworzone za pomocą cardFactory.createCards, z przypisaniem ich do tablicy przez przekazanie board.id.
Kolejny krok to utworzenie nowej kolumny (newColumnName), do której zostaną przeniesione wcześniej utworzone karty.
Po zakończeniu operacji na API przechodzimy do działań w UI. Metoda trelloLoginPage.go przechodzi na stronę logowania, po czym następuje logowanie z użyciem danych użytkownika pobranych z pliku konfiguracyjnego (settings). Po zalogowaniu test otwiera bezpośrednio nowo utworzoną tablicę na podstawie board.url.
W interfejsie użytkownika wykonywana jest operacja przenoszenia kart do nowej kolumny (moveCardsToColumn) oraz ich weryfikacja (verifyCardsInColumn), co pozwala sprawdzić, czy karty faktycznie zostały przesunięte. Na końcu testu wywoływana jest metoda deleteBoard, która usuwa stworzoną tablicę. Warto jednak zauważyć, że jeśli test zakończy się błędem wcześniej, tablica może nie zostać usunięta. Dobrym pomysłem byłoby dodanie metody afterAll lub finally, która zapewni sprzątanie niezależnie od wyniku testu. Zachęcam do samodzielnego zaimplementowania takiego rozwiązania jako ćwiczenia.

Podsumowanie
W tym artykule zaprezentowałem, jak można zbudować bardziej zaawansowane rozwiązanie testowe w Playwright. Pokazałem, jak zintegrować podejście do tworzenia danych testowych za pomocą API, a następnie wykorzystać je w warstwie interfejsu użytkownika (UI).
Przykładowy kod demonstruje kilka dobrych praktyk i wzorców projektowych stosowanych w Playwright z TypeScriptem, takich jak:
- factory pattern do generowania danych testowych (np. boardFactory, cardFactory),
- page object pattern do organizacji interakcji z UI,
- dedykowana klasa asercji, oddzielająca warstwę sprawdzania stanu od logiki testowej,
- fixtures, które ułatwiają współdzielenie i konfigurowanie zależności między testami.
Takie podejście stanowi solidną bazę do dalszego rozwoju architektury testów automatycznych – zarówno w kontekście testów UI, jak i integracyjnych.
Zachęcam do eksperymentowania, rozbudowy tego szkieletu oraz do wprowadzenia np. globalnego mechanizmu czyszczenia danych po testach (afterAll lub finally), by zapewnić większą niezawodność i porządek w środowisku testowym.
***
Pierwszą część artykułu znajdziesz tutaj: Playwright w praktyce: 5 kroków do skutecznego frameworka do automatyzacji testów UI & Web. Część I
Zostaw komentarz