{"id":31299,"date":"2025-06-06T05:00:00","date_gmt":"2025-06-06T03:00:00","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=31299"},"modified":"2025-06-06T09:25:12","modified_gmt":"2025-06-06T07:25:12","slug":"playwright-w-praktyce-integracja-api-i-ui-architektura-frameworka-testowego-czesc-ii","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/playwright-w-praktyce-integracja-api-i-ui-architektura-frameworka-testowego-czesc-ii\/","title":{"rendered":"Playwright w praktyce: integracja API i UI, architektura frameworka testowego. Cz\u0119\u015b\u0107 II"},"content":{"rendered":"\n<p>W tym artykule kontynuujemy nasz\u0105 przygod\u0119 z budow\u0105 frameworka testowego opartego na Playwright i TypeScript. <a href=\"https:\/\/sii.pl\/blog\/playwright-w-praktyce-5-krokow-do-skutecznego-frameworka-do-automatyzacji-testow-ui-web-czesc-i\/\" target=\"_blank\" rel=\"noopener\" title=\"\">W poprzednim wpisie<\/a> przedstawi\u0142em kilka kluczowych koncepcji, kt\u00f3re b\u0119d\u0105 niezb\u0119dne w dalszej pracy nad kodem.<\/p>\n\n\n\n<p>Dzi\u015b skupimy si\u0119 na fragmencie kodu, kt\u00f3ry pos\u0142u\u017cy jako fundament ca\u0142ego rozwi\u0105zania. Om\u00f3wimy spos\u00f3b, w jaki mo\u017cna \u0142\u0105czy\u0107 operacje API z testami UI oraz jak wykorzysta\u0107 dobre praktyki projektowe, takie jak wzorce factory, page object pattern oraz dedykowane klasy asercji, by stworzy\u0107 skalowalne i \u0142atwe w utrzymaniu testy automatyczne.<\/p>\n\n\n\n<p>Do stworzenia przyk\u0142adowego kodu, kt\u00f3ry zilustruje dzia\u0142anie naszego frameworka, pos\u0142u\u017cymy si\u0119 aplikacj\u0105 <strong>Trello<\/strong> \u2013 tak jak wspomnia\u0142em <a href=\"https:\/\/sii.pl\/blog\/playwright-w-praktyce-5-krokow-do-skutecznego-frameworka-do-automatyzacji-testow-ui-web-czesc-i\/\" target=\"_blank\" rel=\"noopener\" title=\"\">w pierwszej cz\u0119\u015bci<\/a>. Trello to narz\u0119dzie do zarz\u0105dzania projektami w oparciu o tablice kanban. Posiada r\u00f3wnie\u017c rozbudowane API, co czyni je \u015bwietnym wyborem do nauki automatyzacji test\u00f3w.<\/p>\n\n\n\n<p>Zach\u0119cam, by podczas nauki korzysta\u0107 z bardziej zaawansowanych aplikacji \u2013 takich, kt\u00f3re odzwierciedlaj\u0105 realne scenariusze z codziennej pracy testera automatyzuj\u0105cego. Dzi\u0119ki temu nasze testy b\u0119d\u0105 bardziej realistyczne i lepiej przygotuj\u0105 nas do projekt\u00f3w komercyjnych.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Co b\u0119dzie robi\u0142 nasz test?<\/strong><\/h2>\n\n\n\n<p>Nasz test b\u0119dzie:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Tworzy\u0142 now\u0105 tablic\u0119 (board) w Trello za pomoc\u0105 API.<\/li>\n\n\n\n<li>Tworzy\u0142 listy przypisane do tej tablicy.<\/li>\n\n\n\n<li>Tworzy\u0142 karty przypisane do konkretnej listy.<\/li>\n\n\n\n<li>Dodawa\u0142 now\u0105 (dodatkow\u0105) list\u0119.<\/li>\n\n\n\n<li>Przenosi\u0142 karty do nowo utworzonej listy.<\/li>\n\n\n\n<li>Weryfikowa\u0142, czy karty faktycznie znalaz\u0142y si\u0119 w odpowiedniej li\u015bcie (asercje).<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Przegl\u0105d architektury frameworka: Playwright + TypeScript<\/strong><\/h2>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image2-6.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image aligncenter size-large&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31302&quot;,&quot;imgStyles&quot;:null,&quot;targetWidth&quot;:1907,&quot;targetHeight&quot;:1071,&quot;scaleAttr&quot;:false,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: Diagram rozwi\\u0105zania&quot;,&quot;alt&quot;:&quot;Diagram rozwi\\u0105zania&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" width=\"1024\" height=\"575\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image2-6-1024x575.png\" alt=\"Diagram rozwi\u0105zania\" class=\"wp-image-31302\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image2-6-1024x575.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image2-6-300x168.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image2-6-768x431.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image2-6-1536x863.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image2-6-555x312.png 555w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image2-6.png 1907w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: Diagram rozwi\u0105zania\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><figcaption class=\"wp-element-caption\">Ryc. 1 Diagram rozwi\u0105zania<\/figcaption><\/figure>\n\n\n\n<p>Diagram przedstawia architektur\u0119 naszego frameworka testowego opartego na Playwright i TypeScript, z naciskiem na testy integruj\u0105ce warstw\u0119 <strong>API<\/strong> z <strong>UI<\/strong>:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Test Runner (<\/strong><code>Playwright<\/code><strong>)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Odpowiada za uruchamianie test\u00f3w (<code>spec files<\/code>) oraz za zarz\u0105dzanie cyklem \u017cycia <code>fixtures<\/code>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Fixtures (<\/strong><code>boardFactory<\/code><strong>, <\/strong><code>cardFactory<\/code><strong>, <\/strong><code>trelloApi<\/code><strong>)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Wstrzykiwane zale\u017cno\u015bci do test\u00f3w \u2013 odpowiadaj\u0105 za tworzenie i usuwanie danych testowych.<\/li>\n\n\n\n<li>S\u0105 centralnym punktem integracji pomi\u0119dzy testami a logik\u0105 API.<\/li>\n\n\n\n<li>Pozwalaj\u0105 na zmniejszon\u0105 ilo\u015b\u0107 duplikacji kodu.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Fabryki&nbsp; (<\/strong><code>BoardFactory<\/code><strong>, <\/strong><code>CardFactory<\/code><strong>)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Opakowuj\u0105 wywo\u0142ania API w ustrukturyzowan\u0105, \u0142atw\u0105 do testowania form\u0119.<\/li>\n\n\n\n<li>Przyk\u0142ad dobrego zastosowania wzorca <strong>Factory Pattern<\/strong>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>TrelloAPI<\/strong>\n<ul class=\"wp-block-list\">\n<li>Klasa komunikuj\u0105ca si\u0119 z <strong>Trello REST API<\/strong> przez <code>APIRequestContext<\/code> z Playwrighta.<\/li>\n\n\n\n<li>Zawiera metody takie jak <code>createBoard<\/code>, <code>createCard<\/code>, <code>getLists<\/code>, <code>deleteBoard<\/code>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>APIRequestContext \u2192 Trello REST API<\/strong>\n<ul class=\"wp-block-list\">\n<li>Bezpo\u015brednia warstwa realizuj\u0105ca zapytania HTTP.<\/li>\n\n\n\n<li>Autoryzacja oparta o <code>apiKey<\/code> i <code>token<\/code>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Page Objecty (<\/strong><code>LoginPage<\/code><strong>, <\/strong><code>BoardPage<\/code><strong>) + Asercje<\/strong>\n<ul class=\"wp-block-list\">\n<li>Klasy reprezentuj\u0105ce interfejs u\u017cytkownika Trello.<\/li>\n\n\n\n<li>Rozdzielaj\u0105 logik\u0119 interakcji od test\u00f3w.<\/li>\n\n\n\n<li><code>Klasa TrelloBoardAssertions<\/code> to osobna warstwa do sprawdzania stanu UI (np. kart w kolumnach).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Konfiguracja (<\/strong><code>TrelloSettings<\/code><strong>) + zmienne \u015brodowiskowe<\/strong>\n<ul class=\"wp-block-list\">\n<li>Ustawienia frameworka: URL-e, klucze, dane logowania.<\/li>\n\n\n\n<li>Mo\u017cliwo\u015b\u0107 nadpisywania warto\u015bci przez <code>process.env<\/code> w CI\/CD.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p><strong>Ca\u0142o\u015b\u0107 pozwala na:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Sp\u00f3jne tworzenie test\u00f3w E2E z wykorzystaniem danych tworzonych przez API.<\/li>\n\n\n\n<li>\u0141atwe utrzymanie, testowanie i rozbudow\u0119 frameworka.<\/li>\n\n\n\n<li>Reu\u017cywalno\u015b\u0107 dzi\u0119ki tej strukturze i czystemu rozdzieleniu warstw.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Na co warto zwr\u00f3ci\u0107 uwag\u0119?<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Dla ka\u017cdego testu powinni\u015bmy tworzy\u0107 <strong>\u015bwie\u017ce dane testowe<\/strong>, aby zapewni\u0107 ich niezale\u017cno\u015b\u0107.<\/li>\n\n\n\n<li>Po zako\u0144czeniu testu nale\u017cy te dane <strong>usun\u0105\u0107<\/strong>, aby nie za\u015bmieca\u0142y \u015brodowiska testowego.<\/li>\n\n\n\n<li>W przypadku Trello ma to szczeg\u00f3lne znaczenie \u2013 darmowe konto pozwala na utworzenie maksymalnie 10 tablic. Je\u017celi o tym zapomnimy, testy mog\u0105 ko\u0144czy\u0107 si\u0119 b\u0142\u0119dami z powodu przekroczenia limitu.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Klasa TrelloBoardPage<\/strong> <\/h2>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image3-4.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image size-large&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31305&quot;,&quot;imgStyles&quot;:null,&quot;targetWidth&quot;:1460,&quot;targetHeight&quot;:564,&quot;scaleAttr&quot;:false,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image size-large wp-lightbox-container\"><img decoding=\"async\" width=\"1024\" height=\"396\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image3-4-1024x396.png\" alt=\"kod\" class=\"wp-image-31305\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image3-4-1024x396.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image3-4-300x116.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image3-4-768x297.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image3-4.png 1460w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p>Klasa <code>TrelloBoardPage<\/code> zawiera zestaw metod odpowiedzialnych za interakcje z tablic\u0105 w aplikacji Trello przy u\u017cyciu Playwrighta. Odpowiada za obs\u0142ug\u0119 dzia\u0142a\u0144 typowych dla u\u017cytkownika na poziomie tablicy, takich jak nawigacja, przenoszenie kart czy weryfikacja ich obecno\u015bci w kolumnach. <\/p>\n\n\n\n<p>Poniewa\u017c Trello jest aplikacj\u0105 typu <strong>SPA (Single Page Application)<\/strong>, liczba dost\u0119pnych akcji w obr\u0119bie jednej podstrony (np. boardu) mo\u017ce by\u0107 bardzo du\u017ca. W takiej sytuacji warto rozdziela\u0107 logik\u0119 na mniejsze klasy Page Object, zgodnie z zasad\u0105 <strong>single responsibility<\/strong>. Dzi\u0119ki temu kod b\u0119dzie bardziej czytelny i \u0142atwiejszy w utrzymaniu.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Czym charakteryzuje si\u0119 SPA (Single Page Application)?<\/strong><\/h2>\n\n\n\n<p>Do cech charakterystycznych dla Single Page Application nale\u017cy:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Dynamiczne prze\u0142adowywanie tre\u015bci<\/strong> \u2013 elementy strony (np. kolumny, karty) aktualizuj\u0105 si\u0119 bez pe\u0142nego prze\u0142adowania.<\/li>\n\n\n\n<li><strong>Wykorzystanie JavaScriptu<\/strong> \u2013 Trello u\u017cywa\u0142o m.in. Backbone.js, ale w ka\u017cdej SPA JavaScript zarz\u0105dza dynamicznymi interakcjami, animacjami i zdarzeniami (np. drag-and-drop).<\/li>\n\n\n\n<li><strong>Asynchroniczne pobieranie danych<\/strong> \u2013 za pomoc\u0105 AJAX lub WebSocket, dane s\u0105 pobierane bez od\u015bwie\u017cania strony, co zapewnia p\u0142ynno\u015b\u0107 dzia\u0142ania.<\/li>\n\n\n\n<li><strong>Zarz\u0105dzanie stanem aplikacji po stronie klienta<\/strong> \u2013 np. aktualny board, filtr czy otwarta karta s\u0105 trzymane lokalnie i synchronizowane z serwerem w tle.<\/li>\n\n\n\n<li><strong>Zmienny URL bez prze\u0142adowania strony<\/strong> \u2013 adres URL zmienia si\u0119 w kontek\u015bcie konkretnego boarda lub karty, ale strona pozostaje aktywna.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Metoda <\/strong><code><strong>navigateToBoard<\/strong><\/code><\/h2>\n\n\n\n<p>Jedn\u0105 z kluczowych metod w klasie <code>TrelloBoardPage<\/code> jest <code>navigateToBoard(url)<\/code>. Otwiera ona podan\u0105 stron\u0119 i oczekuje, a\u017c pojawi si\u0119 element reprezentuj\u0105cy tytu\u0142 tablicy, \u015bwiadcz\u0105cy o tym, \u017ce interfejs jest gotowy do dalszych akcji testowych.<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image4-4.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image aligncenter size-large is-resized&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31307&quot;,&quot;imgStyles&quot;:&quot;width:840px;height:auto&quot;,&quot;targetWidth&quot;:1686,&quot;targetHeight&quot;:1012,&quot;scaleAttr&quot;:false,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image aligncenter size-large is-resized wp-lightbox-container\"><img decoding=\"async\" width=\"1024\" height=\"615\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image4-4-1024x615.png\" alt=\"kod\" class=\"wp-image-31307\" style=\"width:840px;height:auto\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image4-4-1024x615.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image4-4-300x180.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image4-4-768x461.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image4-4-1536x922.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image4-4.png 1686w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p>W tej klasie przekazujemy obiekt <code>page<\/code> (dostarczany przez Playwright) jako parametr konstruktora, co umo\u017cliwia nam wykonywanie akcji w kontek\u015bcie konkretnej przegl\u0105darki.<\/p>\n\n\n\n<p>Na pocz\u0105tku definiujemy prywatne pola typu <code>string<\/code>, kt\u00f3re zawieraj\u0105 selektory do element\u00f3w na stronie:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>usernameField<\/code> \u2013 pole na nazw\u0119 u\u017cytkownika,<\/li>\n\n\n\n<li><code>passwordField<\/code> \u2013 pole na has\u0142o,<\/li>\n\n\n\n<li><code>loginSubmitButton<\/code> \u2013 przycisk logowania,<\/li>\n\n\n\n<li><code>allBoardsContent<\/code> \u2013 kontener z tablicami widoczny po zalogowaniu.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Metoda <\/strong><code><strong>go(url: string)<\/strong><\/code><\/h3>\n\n\n\n<p>Metoda <code>go<\/code> umo\u017cliwia przej\u015bcie do okre\u015blonego adresu URL. Obecnie przyjmuje go jako parametr, ale mo\u017cna j\u0105 \u0142atwo rozbudowa\u0107 np. o pobieranie warto\u015bci z <strong>zmiennej \u015brodowiskowej<\/strong>, co przyda\u0142oby si\u0119 w przypadku pracy na r\u00f3\u017cnych \u015brodowiskach (dev, staging, prod).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Metoda <\/strong><code><strong>getAllBoardsVisibleStatus()<\/strong><\/code><\/h3>\n\n\n\n<p>Ta metoda sprawdza, czy element odpowiadaj\u0105cy za kontener tablic (<code>allBoardsContent<\/code>) jest widoczny. Dzi\u0119ki temu mo\u017cemy weryfikowa\u0107, czy u\u017cytkownik zosta\u0142 pomy\u015blnie zalogowany.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Metoda <\/strong><code><strong>login(username: string, password: string)<\/strong><\/code><\/h3>\n\n\n\n<p>Tutaj widzimy kompletny flow logowania:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Klikni\u0119cie przycisku <strong>\u201eLog in\u201d<\/strong> w nag\u0142\u00f3wku strony (menu).<\/li>\n\n\n\n<li>Wprowadzenie nazwy u\u017cytkownika.<\/li>\n\n\n\n<li>Klikni\u0119cie przycisku <code>continue<\/code>.<\/li>\n\n\n\n<li>Wprowadzenie has\u0142a.<\/li>\n\n\n\n<li>Klikni\u0119cie przycisku <code>login<\/code>.<\/li>\n\n\n\n<li>Oczekiwanie na za\u0142adowanie elementu tablic (<code>allBoardsContent<\/code>).<\/li>\n<\/ol>\n\n\n\n<p>Mo\u017cna by rozwa\u017cy\u0107 dodanie dodatkowej metody do nawigacji bezpo\u015brednio do strony logowania, np. poprzez przekazanie URL jako parametru lub skonfigurowanie osobnych endpoint\u00f3w w zale\u017cno\u015bci od \u015brodowiska.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Om\u00f3wienie typu trelloFixtures<\/strong><\/h2>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image5-4.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image aligncenter size-full&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31309&quot;,&quot;imgStyles&quot;:&quot;object-fit:cover&quot;,&quot;targetWidth&quot;:796,&quot;targetHeight&quot;:773,&quot;scaleAttr&quot;:&quot;cover&quot;,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image aligncenter size-full wp-lightbox-container\"><img decoding=\"async\" width=\"796\" height=\"773\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image5-4.png\" alt=\"kod\" class=\"wp-image-31309\" style=\"object-fit:cover\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image5-4.png 796w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image5-4-300x291.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image5-4-768x746.png 768w\" sizes=\"(max-width: 796px) 100vw, 796px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image6-1.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image aligncenter size-full&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31311&quot;,&quot;imgStyles&quot;:null,&quot;targetWidth&quot;:824,&quot;targetHeight&quot;:557,&quot;scaleAttr&quot;:false,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image aligncenter size-full wp-lightbox-container\"><img decoding=\"async\" width=\"824\" height=\"557\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image6-1.png\" alt=\"kod\" class=\"wp-image-31311\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image6-1.png 824w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image6-1-300x203.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image6-1-768x519.png 768w\" sizes=\"(max-width: 824px) 100vw, 824px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p>Na powy\u017cszym fragmencie wida\u0107 type definiuj\u0105cy fixtures, z kt\u00f3rych korzystaj\u0105 wszystkie testy. To miejsce, w kt\u00f3rym <strong>inicjalizowane s\u0105 wszystkie zale\u017cno\u015bci<\/strong>: obiekty typu PageObject, Factory, API, Assertions, a tak\u017ce konfiguracja (TrelloSettings).<\/p>\n\n\n\n<p>Warto podkre\u015bli\u0107, \u017ce:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>fixtures s\u0105 rejestrowane poprzez test.extend&lt;TrelloFixtures&gt;(),<\/li>\n\n\n\n<li>ka\u017cda zale\u017cno\u015b\u0107 jest tworzona tylko wtedy, gdy dana fixture jest potrzebna w danym te\u015bcie (lazy-loaded),<\/li>\n\n\n\n<li>dzi\u0119ki temu unika si\u0119 zb\u0119dnego kodu w testach i mo\u017cna \u0142atwo konfigurowa\u0107 i dzieli\u0107 stan mi\u0119dzy przypadkami testowymi.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Wskaz\u00f3wka projektowa<\/strong><\/h3>\n\n\n\n<p>W tym przyk\u0142adzie wszystkie <code>PageObjecty<\/code>, klasy pomocnicze (<code>Factory<\/code>, <code>Assertions<\/code>) i konfiguracja zosta\u0142y umieszczone w jednej klasie <code>TrelloFixtures<\/code>. Dla cel\u00f3w edukacyjnych i ma\u0142ego projektu to bardzo wygodne, ale <strong>w \u015brodowisku produkcyjnym<\/strong> warto rozwa\u017cy\u0107 bardziej granularne podej\u015bcie:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Podziel <code>fixtures<\/code> na mniejsze grupy (np. <code>apiFixtures<\/code>, <code>uiFixtures<\/code>, <code>authFixtures<\/code>).<\/li>\n\n\n\n<li>U\u0142atwi to <strong>utrzymanie, testowanie i refaktoryzacj\u0119<\/strong> poszczeg\u00f3lnych warstw frameworka.<\/li>\n\n\n\n<li>Mniejsze pliki s\u0105 te\u017c znacznie \u0142atwiejsze do przegl\u0105dania i debugowania, zw\u0142aszcza w wi\u0119kszych projektach.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Interfejsy \u2013 po co z nich korzystamy?<\/strong><\/h2>\n\n\n\n<p>Na podstawie odpowiedzi z API mo\u017cemy tworzy\u0107 w\u0142asne interfejsy, np. Board, List, Card. Dzi\u0119ki TypeScriptowi, je\u015bli API Trello zwraca dane zgodne z tymi interfejsami, mo\u017cemy je bezpiecznie wykorzystywa\u0107 w kodzie. Interfejsy te mo\u017cna oczywi\u015bcie rozbudowywa\u0107 w zale\u017cno\u015bci od naszych potrzeb projektowych.<\/p>\n\n\n\n<p>Przyk\u0142ad interfejsu Board:<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image7-1.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image aligncenter size-full&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31313&quot;,&quot;imgStyles&quot;:&quot;object-fit:cover&quot;,&quot;targetWidth&quot;:484,&quot;targetHeight&quot;:212,&quot;scaleAttr&quot;:&quot;cover&quot;,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image aligncenter size-full wp-lightbox-container\"><img decoding=\"async\" width=\"484\" height=\"212\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image7-1.png\" alt=\"kod\" class=\"wp-image-31313\" style=\"object-fit:cover\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image7-1.png 484w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image7-1-300x131.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image7-1-370x162.png 370w\" sizes=\"(max-width: 484px) 100vw, 484px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p>Podobnie wygl\u0105daj\u0105 List i Card,<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image8.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image aligncenter size-full&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31315&quot;,&quot;imgStyles&quot;:&quot;object-fit:cover&quot;,&quot;targetWidth&quot;:516,&quot;targetHeight&quot;:206,&quot;scaleAttr&quot;:&quot;cover&quot;,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image aligncenter size-full wp-lightbox-container\"><img decoding=\"async\" width=\"516\" height=\"206\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image8.png\" alt=\"kod\" class=\"wp-image-31315\" style=\"object-fit:cover\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image8.png 516w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image8-300x120.png 300w\" sizes=\"(max-width: 516px) 100vw, 516px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image9.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image aligncenter size-full&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31317&quot;,&quot;imgStyles&quot;:&quot;object-fit:cover&quot;,&quot;targetWidth&quot;:424,&quot;targetHeight&quot;:204,&quot;scaleAttr&quot;:&quot;cover&quot;,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image aligncenter size-full wp-lightbox-container\"><img decoding=\"async\" width=\"424\" height=\"204\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image9.png\" alt=\"kod\" class=\"wp-image-31317\" style=\"object-fit:cover\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image9.png 424w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image9-300x144.png 300w\" sizes=\"(max-width: 424px) 100vw, 424px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p>co pozwala na typowanie danych zwracanych z API i daje lepsz\u0105 kontrol\u0119 w kodzie test\u00f3w oraz implementacjach factory.<\/p>\n\n\n\n<p>Metody, kt\u00f3re korzystaj\u0105c z tych interfejs\u00f3w np.<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image10.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image size-large&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31319&quot;,&quot;imgStyles&quot;:null,&quot;targetWidth&quot;:1032,&quot;targetHeight&quot;:383,&quot;scaleAttr&quot;:false,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image size-large wp-lightbox-container\"><img decoding=\"async\" width=\"1024\" height=\"380\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image10-1024x380.png\" alt=\"kod\" class=\"wp-image-31319\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image10-1024x380.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image10-300x111.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image10-768x285.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image10.png 1032w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p>Metody, takie jak createBoard, zwracaj\u0105 odpowied\u017a zgodn\u0105 z interfejsem Board. Je\u015bli zdefiniujemy w interfejsie pola odpowiadaj\u0105ce strukturze zwracanej przez API Trello, TypeScript umo\u017cliwi nam bezpieczny dost\u0119p do tych danych. Dzi\u0119ki temu mo\u017cemy z \u0142atwo\u015bci\u0105 korzysta\u0107 z takich w\u0142a\u015bciwo\u015bci jak id, name czy url bez dodatkowego rzutowania typ\u00f3w.<\/p>\n\n\n\n<p>Oczywi\u015bcie interfejsy takie jak Board, List czy Card mo\u017cna swobodnie rozszerza\u0107 w zale\u017cno\u015bci od potrzeb projektu \u2013 na przyk\u0142ad o dodatkowe pola opcjonalne. Taka elastyczno\u015b\u0107 u\u0142atwia utrzymanie i rozw\u00f3j frameworku testowego.<\/p>\n\n\n\n<p>Przechowywanie ustawie\u0144 w frameworku testowym \u2013 klasa Settings.ts<\/p>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image11.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image aligncenter size-large&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31321&quot;,&quot;imgStyles&quot;:null,&quot;targetWidth&quot;:1434,&quot;targetHeight&quot;:1006,&quot;scaleAttr&quot;:false,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" width=\"1024\" height=\"718\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image11-1024x718.png\" alt=\"kod\" class=\"wp-image-31321\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image11-1024x718.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image11-300x210.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image11-768x539.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image11.png 1434w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Zarz\u0105dzanie konfiguracj\u0105 we frameworkach automatyzacji test\u00f3w<\/strong><\/h2>\n\n\n\n<p>W ka\u017cdym wi\u0119kszym frameworku testowym pojawia si\u0119 potrzeba zarz\u0105dzania konfiguracj\u0105 \u2013 tak\u0105 jak klucze API, adresy URL \u015brodowisk, dane logowania itp.<\/p>\n\n\n\n<p>Istnieje kilka sposob\u00f3w na organizacj\u0119 takich ustawie\u0144. Najpopularniejsze podej\u015bcia to:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Przechowywanie ustawie\u0144 w plikach <\/strong><code><strong>.json<\/strong><\/code><strong>, <\/strong><code><strong>.yml<\/strong><\/code><strong> lub <\/strong><code><strong>.env<\/strong><\/code>.<\/li>\n\n\n\n<li><strong>Wykorzystywanie zmiennych \u015brodowiskowych przez <\/strong><code><strong>process.env<\/strong><\/code>.<\/li>\n\n\n\n<li><strong>\u0141\u0105czenie obu podej\u015b\u0107 z warstw\u0105 konfiguracyjn\u0105 w kodzie (np. klas\u0105 <\/strong><code><strong>Settings<\/strong><\/code><strong>)<\/strong>.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Podej\u015bcie z plikiem <\/strong><code><strong>.json<\/strong><\/code><\/h3>\n\n\n\n<p>W przedstawionym przyk\u0142adzie konfiguracja przechowywana jest w pliku <code>TrelloSettings.json<\/code>. To podej\u015bcie ma wiele zalet:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Pozwala \u0142atwo modyfikowa\u0107 dane konfiguracyjne bez rekompilacji projektu.<\/li>\n\n\n\n<li>Umo\u017cliwia posiadanie osobnych plik\u00f3w dla r\u00f3\u017cnych \u015brodowisk (np. <code>settings.dev.json<\/code>, <code>settings.staging.json<\/code>).<\/li>\n\n\n\n<li>Dane wra\u017cliwe (takie jak tokeny API) mo\u017cna nadpisa\u0107 zmiennymi \u015brodowiskowymi w CI\/CD, zamiast trzyma\u0107 je w repozytorium.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Klasa <\/strong><code><strong>TrelloSettings<\/strong><\/code><\/h3>\n\n\n\n<p>W kodzie widzimy klas\u0119 <code>TrelloSettings<\/code>, kt\u00f3ra:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Wczytuje konfiguracj\u0119 z pliku o rozszerzeniu typu .json, kt\u00f3rego \u015bcie\u017cka przekazywana jest do konstruktora.<\/li>\n\n\n\n<li>Sprawdza istnienie pliku i wyrzuca b\u0142\u0105d, je\u015bli plik nie istnieje.<\/li>\n\n\n\n<li>Parsuje plik JSON i przypisuje poszczeg\u00f3lne warto\u015bci do p\u00f3l klasy (takich jak <code>apiKey<\/code>, <code>token<\/code>, <code>baseUrl<\/code> itd.).<\/li>\n<\/ol>\n\n\n\n<p>To prosty i skuteczny spos\u00f3b na enkapsulacj\u0119 konfiguracji w jednej klasie.<\/p>\n\n\n\n<p><strong>Pro tip<\/strong>! Warto doda\u0107 logik\u0119, kt\u00f3ra umo\u017cliwi pobieranie niekt\u00f3rych warto\u015bci z <code>process.env<\/code>, je\u015bli nie s\u0105 one ustawione w pliku. Dzi\u0119ki temu mo\u017cesz trzyma\u0107 \u201ebezpieczne\u201d warto\u015bci w pliku lokalnie, a wra\u017cliwe dane \u0142adowa\u0107 dynamicznie w pipeline.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Klasa BoardFactory \u2013 tworzenie i usuwanie board\u00f3w (tablic<\/strong>)<\/h3>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image12.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image aligncenter size-full&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31325&quot;,&quot;imgStyles&quot;:null,&quot;targetWidth&quot;:854,&quot;targetHeight&quot;:490,&quot;scaleAttr&quot;:false,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image aligncenter size-full wp-lightbox-container\"><img decoding=\"async\" width=\"854\" height=\"490\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image12.png\" alt=\"kod\" class=\"wp-image-31325\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image12.png 854w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image12-300x172.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image12-768x441.png 768w\" sizes=\"(max-width: 854px) 100vw, 854px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p>Klasa <code>BoardFactory<\/code> odpowiada za operacje tworzenia i usuwania tablic (<code>board\u00f3w<\/code>) w aplikacji Trello, wykorzystuj\u0105c do tego metody dost\u0119pne w warstwie API (<code>TrelloAPI<\/code>).<\/p>\n\n\n\n<p>Zastosowanie <strong>Factory Pattern<\/strong> w tym miejscu to \u015bwietne rozwi\u0105zanie \u2013 pozwala nam oddzieli\u0107 logik\u0119 tworzenia danych testowych od samego testu. Dzi\u0119ki temu testy staj\u0105 si\u0119 bardziej przejrzyste, a zarz\u0105dzanie danymi prostsze i bardziej kontrolowane.<\/p>\n\n\n\n<p><strong>Co robi\u0105 metody?<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>createBoard(name: string)<\/code> \u2013 tworzy now\u0105 tablic\u0119 w Trello z podan\u0105 nazw\u0105. Zwraca obiekt typu <code>Board<\/code>.<\/li>\n\n\n\n<li><code>deleteBoard(boardId: string)<\/code> \u2013 usuwa tablic\u0119 o wskazanym <code>boardId<\/code>. Warto wykorzystywa\u0107 t\u0119 metod\u0119 np. w <code>afterEach<\/code> lub <code>finally<\/code>, aby usuwa\u0107 dane testowe po zako\u0144czeniu testu.<\/li>\n<\/ul>\n\n\n\n<p><strong>Dlaczego to dobre podej\u015bcie?<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Rozdziela odpowiedzialno\u015bci \u2013 test skupia si\u0119 na <strong>sprawdzaniu zachowa\u0144<\/strong>, a factory zajmuje si\u0119 <strong>tworzeniem danych<\/strong>.<\/li>\n\n\n\n<li>U\u0142atwia <strong>ponowne wykorzystanie<\/strong> kodu (reusability).<\/li>\n\n\n\n<li>Umo\u017cliwia \u0142atw\u0105 rozbudow\u0119 (np. o dodatkowe parametry, tworzenie tablic z domy\u015blnymi listami itp.).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Klasa <\/strong><code><strong>CardFactory<\/strong><\/code><strong> \u2013 tworzenie kart dla tablicy<\/strong><\/h3>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image13.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image size-large&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31327&quot;,&quot;imgStyles&quot;:null,&quot;targetWidth&quot;:1262,&quot;targetHeight&quot;:574,&quot;scaleAttr&quot;:false,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image size-large wp-lightbox-container\"><img decoding=\"async\" width=\"1024\" height=\"466\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image13-1024x466.png\" alt=\"kod\" class=\"wp-image-31327\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image13-1024x466.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image13-300x136.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image13-768x349.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image13.png 1262w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p>W naszym przyk\u0142adzie wykorzystujemy dwie klasy odpowiedzialne za tworzenie danych testowych: <code>BoardFactory<\/code> oraz <code>CardFactory<\/code>.<\/p>\n\n\n\n<p>Skupmy si\u0119 na <code>CardFactory<\/code>. Klasa ta zawiera metod\u0119 <code>createCards<\/code>, kt\u00f3ra przyjmuje dwa parametry:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>titles<\/code> \u2013 tablica zawieraj\u0105ca tytu\u0142y kart, kt\u00f3re maj\u0105 zosta\u0107 utworzone.<\/li>\n\n\n\n<li><code>boardId<\/code> \u2013 identyfikator tablicy, do kt\u00f3rej karty maj\u0105 zosta\u0107 przypisane.<\/li>\n<\/ul>\n\n\n\n<p>Wewn\u0105trz metody wykonujemy nast\u0119puj\u0105ce kroki:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Pobieramy listy dost\u0119pne na wskazanej tablicy (<code>getLists<\/code> z API Trello).<\/li>\n\n\n\n<li>Tworzymy pust\u0105 tablic\u0119 <code>cards<\/code>, w kt\u00f3rej b\u0119d\u0105 przechowywane utworzone obiekty kart.<\/li>\n\n\n\n<li>Iterujemy po tytu\u0142ach i dla ka\u017cdej warto\u015bci wywo\u0142ujemy metod\u0119 <code>createCard<\/code>, przekazuj\u0105c tytu\u0142 oraz ID pierwszej listy (przyjmujemy, \u017ce tworzymy wszystkie karty w jednej domy\u015blnej li\u015bcie).<\/li>\n\n\n\n<li>Dodajemy ka\u017cd\u0105 nowo utworzon\u0105 kart\u0119 do tablicy <code>cards<\/code>.<\/li>\n\n\n\n<li>Zwracamy tablic\u0119 wszystkich utworzonych kart.<\/li>\n<\/ol>\n\n\n\n<p>Takie podej\u015bcie pozwala nam tworzy\u0107 zestawy testowych danych w spos\u00f3b modularny i wielokrotnego u\u017cytku, a sama metoda mo\u017ce by\u0107 \u0142atwo rozbudowana np. o obs\u0142ug\u0119 wielu list lub losowe przypisywanie kart.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Klasa <code><strong>TrelloBoardAssertions<\/strong><\/code> \u2013 weryfikacja kart w kolumnie<\/h3>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image14.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image size-large&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31329&quot;,&quot;imgStyles&quot;:null,&quot;targetWidth&quot;:1726,&quot;targetHeight&quot;:766,&quot;scaleAttr&quot;:false,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image size-large wp-lightbox-container\"><img decoding=\"async\" width=\"1024\" height=\"454\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image14-1024x454.png\" alt=\"kod\" class=\"wp-image-31329\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image14-1024x454.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image14-300x133.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image14-768x341.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image14-1536x682.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image14.png 1726w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p>Klasa <code>TrelloBoardAssertions<\/code> odpowiada za warstw\u0119 asercji w testach \u2013 sprawdzanie, czy oczekiwany stan faktycznie wyst\u0119puje w UI.<\/p>\n\n\n\n<p>Na pocz\u0105tku przekazujemy do konstruktora obiekt <code>TrelloBoardPage<\/code>, dzi\u0119ki czemu w dalszej cz\u0119\u015bci klasy mo\u017cemy korzysta\u0107 z metod UI zdefiniowanych w tej klasie \u2013 w tym przypadku z metody <code>getAllCardsInColumn<\/code>, kt\u00f3ra zwraca wszystkie karty znajduj\u0105ce si\u0119 w danej kolumnie.<\/p>\n\n\n\n<p>Metoda <code>verifyCardsInColumn<\/code> przyjmuje dwa parametry:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>cardTitles<\/code> \u2013 tablic\u0119 z tytu\u0142ami kart, kt\u00f3re chcemy zweryfikowa\u0107,<\/li>\n\n\n\n<li><code>columnName<\/code> \u2013 nazw\u0119 kolumny, w kt\u00f3rej te karty powinny si\u0119 znajdowa\u0107.<\/li>\n<\/ul>\n\n\n\n<p><strong>Jak dzia\u0142a ta metoda?<\/strong><\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Pobieramy wszystkie tytu\u0142y kart z okre\u015blonej kolumny, u\u017cywaj\u0105c metody <code>getAllCardsInColumn<\/code>.<\/li>\n\n\n\n<li>Iterujemy po wszystkich oczekiwanych tytu\u0142ach (<code>cardTitles<\/code>), sprawdzaj\u0105c:\n<ul class=\"wp-block-list\">\n<li>czy dana karta faktycznie znajduje si\u0119 w kolumnie (<code>.toBeVisible()<\/code>),<\/li>\n\n\n\n<li>czy jej tekst odpowiada oczekiwanemu tytu\u0142owi.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Na ko\u0144cusprawdzamy czy:\n<ul class=\"wp-block-list\">\n<li>wszystkie oczekiwane karty s\u0105 zawarte w kolumnie (<code>toEqual(expect.arrayContaining(...))<\/code>),<\/li>\n\n\n\n<li>liczba znalezionych kart odpowiada liczbie oczekiwanych (<code>toBe(cardTitles.length)<\/code>).<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>To podej\u015bcie pozwala upewni\u0107 si\u0119, \u017ce zar\u00f3wno zawarto\u015b\u0107, jak i liczba kart w kolumnie s\u0105 zgodne z oczekiwaniami.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Finalny kod testu \u2013 trelloBoardTests.spec.ts<\/strong><\/h2>\n\n\n\n<figure data-wp-context=\"{&quot;uploadedSrc&quot;:&quot;https:\\\/\\\/sii.pl\\\/blog\\\/wp-content\\\/uploads\\\/2025\\\/05\\\/image15.png&quot;,&quot;figureClassNames&quot;:&quot;wp-block-image size-large&quot;,&quot;figureStyles&quot;:null,&quot;imgClassNames&quot;:&quot;wp-image-31331&quot;,&quot;imgStyles&quot;:null,&quot;targetWidth&quot;:1810,&quot;targetHeight&quot;:1170,&quot;scaleAttr&quot;:false,&quot;ariaLabel&quot;:&quot;Powi\\u0119ksz obrazek: kod&quot;,&quot;alt&quot;:&quot;kod&quot;}\" data-wp-interactive=\"core\/image\" class=\"wp-block-image size-large wp-lightbox-container\"><img decoding=\"async\" width=\"1024\" height=\"662\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image15-1024x662.png\" alt=\"kod\" class=\"wp-image-31331\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image15-1024x662.png 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image15-300x194.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image15-768x496.png 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image15-1536x993.png 1536w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/image15.png 1810w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><button\n\t\t\tclass=\"lightbox-trigger\"\n\t\t\ttype=\"button\"\n\t\t\taria-haspopup=\"dialog\"\n\t\t\taria-label=\"Powi\u0119ksz obrazek: kod\"\n\t\t\tdata-wp-init=\"callbacks.initTriggerButton\"\n\t\t\tdata-wp-on-async--click=\"actions.showLightbox\"\n\t\t\tdata-wp-style--right=\"context.imageButtonRight\"\n\t\t\tdata-wp-style--top=\"context.imageButtonTop\"\n\t\t>\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewBox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\" \/>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure>\n\n\n\n<p>Na za\u0142\u0105czonym fragmencie widzimy kod testu end-to-end, kt\u00f3ry tworzy tablic\u0119 w Trello, dodaje karty, przenosi je do innej kolumny, a na ko\u0144cu usuwa testowe dane. Test korzysta z wcze\u015bniej zdefiniowanych fixture&#8217;\u00f3w takich jak boardFactory, cardFactory czy trelloApi, kt\u00f3re s\u0105 dostarczone jako zale\u017cno\u015bci.<\/p>\n\n\n\n<p>Pierwszym krokiem jest wygenerowanie unikalnej nazwy tablicy przy u\u017cyciu Date.now(), co minimalizuje ryzyko konfliktu nazw mi\u0119dzy testami. Nast\u0119pnie tworzona jest tablica (board) za pomoc\u0105 API.x<\/p>\n\n\n\n<p>W dalszej cz\u0119\u015bci definiujemy tablic\u0119 cardTitles, zawieraj\u0105c\u0105 nazwy kart, kt\u00f3re maj\u0105 zosta\u0107 dodane do tablicy. Te karty s\u0105 tworzone za pomoc\u0105 cardFactory.createCards, z przypisaniem ich do tablicy przez przekazanie board.id.<\/p>\n\n\n\n<p>Kolejny krok to utworzenie nowej kolumny (newColumnName), do kt\u00f3rej zostan\u0105 przeniesione wcze\u015bniej utworzone karty.<\/p>\n\n\n\n<p>Po zako\u0144czeniu operacji na API przechodzimy do dzia\u0142a\u0144 w UI. Metoda trelloLoginPage.go przechodzi na stron\u0119 logowania, po czym nast\u0119puje logowanie z u\u017cyciem danych u\u017cytkownika pobranych z pliku konfiguracyjnego (settings). Po zalogowaniu test otwiera bezpo\u015brednio nowo utworzon\u0105 tablic\u0119 na podstawie board.url.<\/p>\n\n\n\n<p>W interfejsie u\u017cytkownika wykonywana jest operacja przenoszenia kart do nowej kolumny (moveCardsToColumn) oraz ich weryfikacja (verifyCardsInColumn), co pozwala sprawdzi\u0107, czy karty faktycznie zosta\u0142y przesuni\u0119te. Na ko\u0144cu testu wywo\u0142ywana jest metoda deleteBoard, kt\u00f3ra usuwa stworzon\u0105 tablic\u0119. Warto jednak zauwa\u017cy\u0107, \u017ce je\u015bli test zako\u0144czy si\u0119 b\u0142\u0119dem wcze\u015bniej, tablica mo\u017ce nie zosta\u0107 usuni\u0119ta. Dobrym pomys\u0142em by\u0142oby dodanie metody afterAll lub finally, kt\u00f3ra zapewni sprz\u0105tanie niezale\u017cnie od wyniku testu. Zach\u0119cam do samodzielnego zaimplementowania takiego rozwi\u0105zania jako \u0107wiczenia.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.pl\/oferty-pracy\/\" target=\"_blank\" rel=\"noreferrer noopener\"><img decoding=\"async\" width=\"737\" height=\"170\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/praca-PL-k-9.jpg\" alt=\"oferty pracy\" class=\"wp-image-31336\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/praca-PL-k-9.jpg 737w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/praca-PL-k-9-300x69.jpg 300w\" sizes=\"(max-width: 737px) 100vw, 737px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Podsumowanie<\/strong><\/h2>\n\n\n\n<p>W tym artykule zaprezentowa\u0142em, jak mo\u017cna zbudowa\u0107 bardziej zaawansowane rozwi\u0105zanie testowe w Playwright. Pokaza\u0142em, jak zintegrowa\u0107 podej\u015bcie do tworzenia danych testowych za pomoc\u0105 API, a nast\u0119pnie wykorzysta\u0107 je w warstwie interfejsu u\u017cytkownika (UI).<\/p>\n\n\n\n<p>Przyk\u0142adowy kod demonstruje kilka dobrych praktyk i wzorc\u00f3w projektowych stosowanych w Playwright z TypeScriptem, takich jak:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>factory pattern<\/strong> do generowania danych testowych (np. boardFactory, cardFactory),<\/li>\n\n\n\n<li><strong>page object pattern<\/strong> do organizacji interakcji z UI,<\/li>\n\n\n\n<li><strong>dedykowana klasa asercji<\/strong>, oddzielaj\u0105ca warstw\u0119 sprawdzania stanu od logiki testowej,<\/li>\n\n\n\n<li><strong>fixtures<\/strong>, kt\u00f3re u\u0142atwiaj\u0105 wsp\u00f3\u0142dzielenie i konfigurowanie zale\u017cno\u015bci mi\u0119dzy testami.<\/li>\n<\/ul>\n\n\n\n<p>Takie podej\u015bcie stanowi solidn\u0105 baz\u0119 do dalszego rozwoju architektury test\u00f3w automatycznych \u2013 zar\u00f3wno w kontek\u015bcie test\u00f3w UI, jak i integracyjnych.<\/p>\n\n\n\n<p>Zach\u0119cam do eksperymentowania, rozbudowy tego szkieletu oraz do wprowadzenia np. globalnego mechanizmu czyszczenia danych po testach (afterAll lub finally), by zapewni\u0107 wi\u0119ksz\u0105 niezawodno\u015b\u0107 i porz\u0105dek w \u015brodowisku testowym.<\/p>\n\n\n\n<p>***<\/p>\n\n\n\n<p>Pierwsz\u0105 cz\u0119\u015b\u0107 artyku\u0142u znajdziesz tutaj: Playwright w praktyce: <a href=\"https:\/\/sii.pl\/blog\/playwright-w-praktyce-5-krokow-do-skutecznego-frameworka-do-automatyzacji-testow-ui-web-czesc-i\/\" target=\"_blank\" rel=\"noopener\" title=\"\">5 krok\u00f3w do skutecznego frameworka do automatyzacji test\u00f3w UI &amp; Web. Cz\u0119\u015b\u0107 I<\/a><\/p>\n\n\n\n<p><br><\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;31299&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;3&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 3)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Playwright w praktyce: integracja API i UI, architektura frameworka testowego. Cz\u0119\u015b\u0107 II&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 3)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>W tym artykule kontynuujemy nasz\u0105 przygod\u0119 z budow\u0105 frameworka testowego opartego na Playwright i TypeScript. W poprzednim wpisie przedstawi\u0142em kilka &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/playwright-w-praktyce-integracja-api-i-ui-architektura-frameworka-testowego-czesc-ii\/\">Continued<\/a><\/p>\n","protected":false},"author":215,"featured_media":31333,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1317],"tags":[1579,1546,1512,807,276,333],"class_list":["post-31299","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-testowanie","tag-playwright","tag-przeglad-narzedzi","tag-poradnik","tag-testowanie","tag-framework","tag-typescript"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/05\/Coding_2.jpg","category_names":["Testowanie"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/31299"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/215"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=31299"}],"version-history":[{"count":2,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/31299\/revisions"}],"predecessor-version":[{"id":31404,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/31299\/revisions\/31404"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/31333"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=31299"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=31299"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=31299"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}