Playwright – Twój pierwszy test automatyczny

TIP: Ta lekcja jest częścią rozwijanego Programu Testy Automatyczne z Playwright 🎭
Aktualizacja: Obecnie domyślna konfiguracja posiada kosmetyczne zmiany w porównaniu do tej, którą zobaczysz w lekcji.
Wszystkie detale z tym związane znajdziesz na końcu części tekstowej lekcji.



Prezentacja

Twój pierwszy test z Playwright

Dodatkowe materiały

TIP: Pamiętaj, że Playwright to dynamicznie rozwijany framework. Pewne elementy zaprezentowane w tej lekcji mogą ulec drobnym modyfikacjom.

Jeśli takie zaobserwowałeś, oraz blokują twój postęp do daj nam znać w komentarzu.

Kod lekcji na GitHub

Cały kod do tego kursu znajdziesz w dedykowanym repozytorium na GitHub.

Kod do tej lekcji znajduje się w dokładnie w tu: Sekcja 01 Lekcja 01

Instalacja niezbędnych narzędzi

Node.js: środowisko uruchomieniowe dla JavaScript, TypeScript

VS Code: środowisko do rozwijania oprogramowania

Poznajemy stronę

Link do testowanej strony

Nowy projekt

Kroki:

  1. Tworzymy katalogi Projects/demo-bank-tests na dysku C:/
  2. W katalogu C:/Projects/demo-bank-tests tworzymy (inicjalizujemy) projekt node.js za pomocą komendy:
    npm init playwright@latest
    
  3. Usuwamy zawartość pliku example.spec.ts, który znajduje się w katalogu tests, który został utworzony po inicjalizacji projektu.
UWAGA: Pliki, które zawierające testy, muszą domyślnie posiadać w nazwie wartość spec lub test:
.spec.ts lub .test.ts dla języka TypeScript
.spec.js lub .test.js dla języka JavaScript.

Przykładowo:

  • example.spec.ts
  • login.spec.ts
  • user-account.spec.ts

My będziemy korzystać ze słów kluczowych spec oraz ts. Domyślnie Playwright rozpoznaje pliki z testami posiadające te wartości w nazwie.

Nagrywamy test

Aby nagrać test za pomocą codegen użyj polecenia:

npx playwright codegen [adres]

czyli np:

npx playwright codegen https://demo-bank.vercel.app/

Testowany scenariusz:

  1. Wpisz login (dowolne 8 znaków)
  2. Wpisz hasło (dowolne 8 znaków)
  3. Kliknij Zaloguj
  4. Kliknij w nazwę zalogowanego użytkownika: Jan Demobankowy

Aby zakończyć nagrywanie kliknij przycisk czerwonego kwadratu.

Skopiuj cały kod i wklej go w pliku example.spec.ts.

Przykładowy nagrany test:

import { test, expect } from '@playwright/test';


test('test', async ({ page }) => {
  await page.goto('https://demo-bank.vercel.app/');
  await page.getByTestId('login-input').click();
  await page.getByTestId('login-input').fill('testerLO');
  await page.getByTestId('password-input').click();
  await page.getByTestId('password-input').fill('10987654');
  await page.getByTestId('login-button').click();
  await page.getByTestId('user-name').click();
});
TIP: Pamiętaj aby zapisać zmiany w pliku skrótem klawiszowym Ctrl + s.

Możesz zamknąć okna związane z nagrywaniem poprzez zamknięcie przeglądarki do nagrywania.

Modyfikacja pliku konfiguracyjnego Playwright

Wyłączymy inne przeglądarki niż Chromium aby nasze testy przebiegały szybko i bezproblemowo.

Przejdź do pliku: playwright.config.ts. Zaznacz kod:


   {
      name: 'firefox',
      use: {
        ...devices['Desktop Firefox'],
      },
    },


    {
      name: 'webkit',
      use: {
        ...devices['Desktop Safari'],
      },
    },

Użyj skrótu klawiszowego Ctrl+/. W ten sposób kod zostanie zakomentowany i te elementy konfiguracji nie będą brane pod uwagę. Efekt:


    // {
    //   name: 'firefox',
    //   use: {
    //     ...devices['Desktop Firefox'],
    //   },
    // },


    // {
    //   name: 'webkit',
    //   use: {
    //     ...devices['Desktop Safari'],
    //   },
    // },



TIP: Pamiętaj aby zapisać zmiany skrótem klawiszowym Ctrl + s.

Uruchamiamy test

Aby uruchomić testy z katalogu test użyj polecenia:

npx playwright test

Aby uruchomić testy z katalogu test z widocznym oknem przeglądarki użyj polecenia:

npx playwright test --headed

Wyświetlenie raportu

Aby wyświetlić raport z testów użyj polecenia:

npx playwright show-report

Zakończenie wyświetlania raportu. W konsoli użyj skrótu Ctrl + c dwukrotnie.

Asercje i sprawdzenie wyników testów

Aby sprawdzić czy element (o test-id user-name) na stronie ma zadany tekst (Jan Demobankowy) możesz użyć poniższej konstrukcji:

await expect(page.getByTestId('user-name')).toHaveText('Jan Demobankowy');

Testy zakończone niepowodzeniem

Zepsuty krok w teście

Zmień wartość w teście (np. hasło) na niepoprawną i uruchom testy:

  await page.getByTestId('password-input').fill('');
TIP: Pamiętaj aby zapisać zmiany w pliku skrótem klawiszowym Ctrl + s.
TIP: W przypadku niepowodzenia testów raport jest wyświetlany automatycznie.

Zapoznaj się z raportem a następnie przywróć zmiany do poprzedniej (poprawnej) formy.

Zepsuty krok w asercji

Zmień wartość w asercji (np. oczekiwaną nazwę) na niepoprawną i uruchom testy:

await expect(page.getByTestId('user-name')).toHaveText('Jan DemobankowyX');

Zapoznaj się z raportem a następnie przywróć zmiany do poprzedniej (poprawnej) formy.

Cały kod

TIP: Cały kod z tej i innych lekcji znajdziesz w naszym repozytorium.

Opis instalacji takiego projektu znajdziesz w naszej lekcji Instalacja projektu z pliku w Node.js.

UWAGA: Zauważ, że timeout jest ustawiony na 5 sekund. Ustawienie to znajduje się w timeout: 5000.

Może się zdarzyć, że gdy strona będzie działała wolniej, to testy będą się zakończyć niepowodzeniem przez przekroczony czas czekania w asercjach. W takim przypadku spróbuj zwiększyć wartość czekania na 8 sekund (czyli na timeout: 8000), a następnie uruchom ponownie test 🙂

Zawartość pliku example.spec.ts:

import { test, expect } from '@playwright/test';


test('test', async ({ page }) => {
  await page.goto('https://demo-bank.vercel.app/');
  await page.getByTestId('login-input').click();
  await page.getByTestId('login-input').fill('testerLO');
  await page.getByTestId('password-input').click();
  await page.getByTestId('password-input').fill('10987654');
  await page.getByTestId('login-button').click();
  await page.getByTestId('user-name').click();


  await expect(page.getByTestId('user-name')).toHaveText('Jan Demobankowy');
});

Zawartość pliku package.json:

{
  "name": "demo-bank-tests",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {},
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@playwright/test": "^1.28.1"
  }
}

Zawartość pliku playwright.conf.ts:

import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';


/**
 * Read environment variables from file.
 * https://github.com/motdotla/dotenv
 */
// require('dotenv').config();


/**
 * See https://playwright.dev/docs/test-configuration.
 */
const config: PlaywrightTestConfig = {
  testDir: './tests',
  /* Maximum time one test can run for. */
  timeout: 30 * 1000,
  expect: {
    /**
     * Maximum time expect() should wait for the condition to be met.
     * For example in `await expect(locator).toHaveText();`
     */
    timeout: 5000
  },
  /* Run tests in files in parallel */
  fullyParallel: true,
  /* Fail the build on CI if you accidentally left test.only in the source code. */
  forbidOnly: !!process.env.CI,
  /* Retry on CI only */
  retries: process.env.CI ? 2 : 0,
  /* Opt out of parallel tests on CI. */
  workers: process.env.CI ? 1 : undefined,
  /* Reporter to use. See https://playwright.dev/docs/test-reporters */
  reporter: 'html',
  /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
  use: {
    /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
    actionTimeout: 0,
    /* Base URL to use in actions like `await page.goto('/')`. */
    // baseURL: 'http://localhost:3000',


    /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
    trace: 'on-first-retry',
  },


  /* Configure projects for major browsers */
  projects: [
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
      },
    },


    // {
    //   name: 'firefox',
    //   use: {
    //     ...devices['Desktop Firefox'],
    //   },
    // },


    // {
    //   name: 'webkit',
    //   use: {
    //     ...devices['Desktop Safari'],
    //   },
    // },


    /* Test against mobile viewports. */
    // {
    //   name: 'Mobile Chrome',
    //   use: {
    //     ...devices['Pixel 5'],
    //   },
    // },
    // {
    //   name: 'Mobile Safari',
    //   use: {
    //     ...devices['iPhone 12'],
    //   },
    // },


    /* Test against branded browsers. */
    // {
    //   name: 'Microsoft Edge',
    //   use: {
    //     channel: 'msedge',
    //   },
    // },
    // {
    //   name: 'Google Chrome',
    //   use: {
    //     channel: 'chrome',
    //   },
    // },
  ],


  /* Folder for test artifacts such as screenshots, videos, traces, etc. */
  // outputDir: 'test-results/',


  /* Run your local dev server before starting the tests */
  // webServer: {
  //   command: 'npm run start',
  //   port: 3000,
  // },
};


export default config;
Aktualizacja: Obecnie przy instalacji pobierany jest odrobinę inaczej wyglądający config. Główne różnice to:

Sposób importu, obecnie (Q1 2024) wygląda tak:

import { defineConfig, devices } from '@playwright/test';

Brak ustawień timeouts (możesz je śmiało dodać jak to było pokazane w kodzie starszego configa – wręcz to zalecamy). Oto kod ustawień, który już obecnie nie jest dodawany automatycznie (użyte są domyślne wartości dla Playwright taki jak w ustawieniach poniżej):

/* Maximum time one test can run for. */
  timeout: 30 * 1000,
  expect: {
    /**
     * Maximum time expect() should wait for the condition to be met.
     * For example in `await expect(locator).toHaveText();`
     */
    timeout: 5000,
  },

Usunięcie informacji o action timeout z sekcji use. Tak samo jak w przypadku poprzednich ustawień, gdy ich nie ma są używane domyślne wartości:

    /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
    actionTimeout: 0,

Pozostałe zmiany dotyczą formatowania i sposobu eksportu ale nie różnią się logiką od poprzedniej konfiguracji.

20 komentarzy

  1. Cześć,
    mam kłopot z uruchomieniem testów na Mac’u na którym chciałbym przerobić kurs. Na Windowsie wszytko śmiga ale tutaj dostaję komunikat:

    npx playwright test
    Error: Playwright Test did not expect test() to be called here.
    Most common reasons include:
    - You are calling test() in a configuration file.
    - You are calling test() in a file that is imported by the configuration file.
    - You have two different versions of @playwright/test. This usually happens
      when one of the dependencies in your package.json depends on @playwright/test.
    
       at example.spec.ts:4
    
      2 |
      3 |
    > 4 | test('test', async ({ page }) => {
        |     ^
      5 |   await page.goto('https://demo-bank.vercel.app/');
      6 |   await page.getByTestId('login-input').click();
      7 |   await page.getByTestId('login-input').fill('testerLO');
    
        at TestTypeImpl._currentSuite (/Users/adampastuszka/Projects JS:TS/demo_bank_tests_1/node_modules/playwright/lib/common/testType.js:71:13)
        at TestTypeImpl._createTest (/Users/adampastuszka/Projects JS:TS/demo_bank_tests_1/node_modules/playwright/lib/common/testType.js:80:24)
        at /Users/adampastuszka/Projects JS:TS/demo_bank_tests_1/node_modules/playwright/lib/transform/transform.js:257:12
        at Object. (/Users/adampastuszka/Projects JS:TS/demo_bank_tests_1/tests/example.spec.ts:4:5)
    Error: No tests found
    

    Ustawienia wg Waszych wskazań. Jak mogę uruchomić testy? Gdzie szukać rozwiązania?

    Avatar Adam Pastuszka
      1. Hej Adam!

        Wygląda to jak problem z nierozwiązanymi zależnościami – czyli jakby konflikt po instalacji. Muszę przyznać, że runner testów Playwright jest bardzo wrażliwy na wszelkie problemy w konfiguracji/kodzie/zależnościach.

        Ale super, że typowa informatyczna strategia ‘Have you tried turning it off and on again?’ tutaj zadziałał 😁

        Warto ją stosować w razie nieoczywistych problemów.

        Przemek Przemek
  2. czy istnieje jakaś inna opcja instalacji pakietu playwright niz z poziomu terminala z użyciem playwright@latest? z uwagi na pewne ograniczenia na komputerze służbowym node, visual studio code i niektóre rzeczy działają normalnie niestety ta komenda jest blokowana 🙁

    Avatar Marcin Bubolc
    1. Hej Marcin!
      W takim przypadku można sobie ręcznie stworzyć projekt z npm init i potem zainstalować Playwright poleceniem:

      npm install -D @playwright/test
      

      i teraz trzeba dodać samodzielnie playwright.config i folder z testami – wykorzystaj nasze repozytorium do tego https://github.com/jaktestowac/playwright_automatyzacja_wprowadzenie/tree/main/S01_wprowadzenie/L01_pierwszy_test

      Ale oczywiście nie obędzie się bez instalacji przeglądarek:

      npx playwright install
      

      i tu może pojawić się problem z blokowanymi zasobami. W dokumentacji jest kilka tricków (poleceń jakie możesz wykonać przed poleceniem npx playwright install)
      https://playwright.dev/docs/browsers/#install-behind-a-firewall-or-a-proxy

      Zobacz te instrukcje i mam nadzieję, że instalacja przebiegnie pomyślnie.

      Pozdrawiam i życzę powodzenia🫡

      Przemek Przemek
      1. częściowo udało mi się obejść problem – dzięki za porady. Niestety nie jestem w stanie korzystać z 100% dobrodziejstwa playwright, ale testy da się pisać i odpalać na chrome więc to już coś 🙂 Pojawia się u mnie często problem podczas próby uruchomienia automatycznego nagrywania testów – VS Code wyświetla komunikat “spawn UNKNOWN”. Jest na to jakieś obejście? 🙂

        Avatar Marcin Bubolc
        1. Można jeszcze próbować odpalać w niezależnym terminalu systemowym dane polecenia po za VSCode.

          Dodatkowo można coś takiego dodać przy uruchamianiu komendy UI Mode z wywołaniem przeglądarki np. npx playwright test --ui-port=0

          Ostatecznie kontakt z suportem IT bo to najczęściej reguły zabezpieczenia maszyny powodują ten błąd ;)

          Przemek Przemek
  3. Tak się zastanawiam, czy nie warto pokazać w szkoleniu opcji VSCode automatycznego zapisu zawartości pliku przy przejściu na inne okno.
    Wyeliminowałoby to pamiętanie o Ctrl + S

    Avatar Arkadiusz Jemielity
    1. Super spostrzeżenie – od listopadowej wersji w końcu możemy robić też asercje w Codegen! To dobra wiadomość dla wszystkich szybko prototypujących testy w ten sposób.
      Wymieniona funkcjonalność pokazuje, że warto czytać release notes Playwright bo zawierają ciekawe informacje.

      Przemek Przemek
  4. Hej u mnie brak jest jest tych znaczków, które umożliwiają rozwijanie poszczególnych kroków Przemkowi.

    >
    >
    >

    moment kursu to 20:48

    mój kod:

    import { test, expect } from ‘@playwright/test’;

    test(‘test’, async ({ page }) => {
    await page.goto(‘https://demo-bank.vercel.app/’);
    await page.getByTestId(‘login-input’).click();
    await page.getByTestId(‘login-input’).fill(‘testerle’);
    await page.getByTestId(‘password-input’).click();
    await page.getByTestId(‘password-input’).fill(‘haslohas’);
    await page.getByTestId(‘login-button’).click();
    await page.getByTestId(‘user-name’).click();
    });

    Avatar Franciszek Klocek
  5. Cześć, u mnie W części Test zakończony nie powodzeniem błąd pojawia sie w kroku
    9 | await page.getByTestId(‘login-button’).click();
    W lekcji jest to krok
    10 | await page.getByTestId(‘user-name’).click();
    Czy ktoś tez ma podobnie?

    Avatar Dariusz Kiwak
      1. Treść raportu z błedem:

        Test timeout of 30000ms exceeded.
        Error: locator.click: Target closed
        =========================== logs ===========================
        waiting for getByTestId('login-button')
          locator resolved to zaloguj się
        attempting click action
          waiting for element to be visible, enabled and stable
            element is not enabled - waiting...
        ============================================================
        
           7 |   await page.getByTestId("password-input").click();
           8 |   await page.getByTestId("password-input").fill("");
        >  9 |   await page.getByTestId("login-button").click();
             |                                          ^
          10 |   await page.getByTestId("user-name").click();
          11 |
          12 |   await expect(page.getByTestId("user-name")).toHaveText("Jan Demobankowy");
        
            at C:\Dysk_Darek\jaktestowac\Projects\demo-bank-tests\tests\example.spec.ts:9:42
        

        Natomiast mój kod:

        import { test, expect } from "@playwright/test";
        
        test("test", async ({ page }) => {
          await page.goto("https://demo-bank.vercel.app/");
          await page.getByTestId("login-input").click();
          await page.getByTestId("login-input").fill("testerLO");
          await page.getByTestId("password-input").click();
          await page.getByTestId("password-input").fill("");
          await page.getByTestId("login-button").click();
          await page.getByTestId("user-name").click();
        
          await expect(page.getByTestId("user-name")).toHaveText("Jan Demobankowy");
        });
        
        Avatar Dariusz Kiwak
        1. Hej,
          Powodem błędu jest puste hasło wpisywane w pole (w linii await page.getByTestId("password-input").fill("");).
          W przypadku gdy nie podamy hasła o wymaganej długości, to przycisk logowania na stronie nie będzie aktywny. To natomiast spowoduje, że Playwright nie będzie “umiał go kliknąć” (Playwright domyślnie czeka, aż element będzie klikalny i aktywny).

          Rozwiązaniem tego błędu jest podanie poprawnego hasła (albo raczej hasła o wymaganej długości), np. await page.getByTestId('password-input').fill('10987654'); 🙂

          Daj proszę znać czy ta poprawka pomogła 🙂

          Krzysiek Kijas Krzysiek Kijas
          1. Hej, hej. Dzięki za info. Jednakże nie do końca o to mi chodzi.
            25 minuta lekcji – część lekcji: “Test zakończony niepowodzeniem”. Wykonujemy test nic nie podając w polu hasło. Usuwam dane z pola hasło, zapisuję i wykonuję test. Pojawia się raport. Mój raport różni się w stosunku do raportu otrzymanego w lekcji. Mój raport wskazuje, że błąd pojawia się w kroku
            9 | await page.getByTestId(‘login-button’).click();
            W lekcji jest to krok
            10 | await page.getByTestId(‘user-name’).click();
            Mój test jak i test w lekcji zakończyły się niepowodzeniem, co jest zgodne z założeniem. Tylko zastanawia mnie skąd różnica w raportach?

            Avatar Dariusz Kiwak
            1. O widzisz! To nie zauważyłem tej różnicy 😉
              To może być spowodowane, że Przemek korzystał z innej wersji strony – a obecnie jest odrobinę inna (button login jest aktywny dopiero po wpisaniu hasła).

              Mogą pojawić się niewielkie różnice miedzy tym co na nagraniu, a tym co jest aktualnie na stronie (spowodowane przez mikro zmiany i aktualizacje) 🙂

              Krzysiek Kijas Krzysiek Kijas

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *