Powrót do: Praktyczne wprowadzenie do testów automatycznych z Playwright
Rozwiązanie – agregujemy akcje
TIP: Ta lekcja jest częścią rozwijanego Programu Testy Automatyczne z Playwright 🎭
Dodatkowe materiały
Rozwiązanie prezentowane w tej lekcji zajdziesz w naszym repozytorium: L11_pom_solution
Zawartość pulpit.page.ts:
import { Page } from '@playwright/test';
import { SideMenuComponent } from '../component/side-menu.component';
export class PulpitPage {
constructor(private page: Page) {}
sideMenuComponent = new SideMenuComponent(this.page);
transferReceiverInput = this.page.locator('#widget_1_transfer_receiver');
transferAmountInput = this.page.locator('#widget_1_transfer_amount');
transferTitleInput = this.page.locator('#widget_1_transfer_title');
transferButton = this.page.getByRole('button', { name: 'wykonaj' });
actionCloseButton = this.page.getByTestId('close-button');
messageText = this.page.locator('#show_messages');
topUpReceiverInput = this.page.locator('#widget_1_topup_receiver');
topUpAmountInput = this.page.locator('#widget_1_topup_amount');
topUpAgreementCheckbox = this.page.locator(
'#uniform-widget_1_topup_agreement span'
);
topUpExecuteButton = this.page.getByRole('button', {
name: 'doładuj telefon',
});
moneyValueText = this.page.locator('#money_value');
userNameText = this.page.getByTestId('user-name');
async executeQuickPayment(
receiverId: string,
transferAmount: string,
transferTitle: string
): Promise<void> {
await this.transferReceiverInput.selectOption(receiverId);
await this.transferAmountInput.fill(transferAmount);
await this.transferTitleInput.fill(transferTitle);
await this.transferButton.click();
await this.actionCloseButton.click();
}
async executeMobileTopUp(
topUpReceiver: string,
topUpAmount: string
): Promise<void> {
await this.topUpReceiverInput.selectOption(topUpReceiver);
await this.topUpAmountInput.fill(topUpAmount);
await this.topUpAgreementCheckbox.click();
await this.topUpExecuteButton.click();
await this.actionCloseButton.click();
}
}
Zawartość pulpit.spec.ts:
import { test, expect } from '@playwright/test';
import { loginData } from '../test-data/login.data';
import { LoginPage } from '../pages/login.page';
import { PulpitPage } from '../pages/pulpit.page';
test.describe('Pulpit tests', () => {
let pulpitPage: PulpitPage;
test.beforeEach(async ({ page }) => {
const userId = loginData.userId;
const userPassword = loginData.userPassword;
await page.goto('/');
const loginPage = new LoginPage(page);
await loginPage.login(userId, userPassword);
pulpitPage = new PulpitPage(page);
});
test('quick payment with correct data', async ({ page }) => {
// Arrange
const receiverId = '2';
const transferAmount = '150';
const transferTitle = 'pizza';
const expectedTransferReceiver = 'Chuck Demobankowy';
// Act
await pulpitPage.executeQuickPayment(
receiverId,
transferAmount,
transferTitle
);
// Assert
await expect(pulpitPage.messageText).toHaveText(
`Przelew wykonany! ${expectedTransferReceiver} - ${transferAmount},00PLN - ${transferTitle}`
);
});
test('successful mobile top-up', async ({ page }) => {
// Arrange
const topUpReceiver = '500 xxx xxx';
const topUpAmount = '50';
const expectedMessage = `Doładowanie wykonane! ${topUpAmount},00PLN na numer ${topUpReceiver}`;
// Act
await pulpitPage.executeMobileTopUp(topUpReceiver, topUpAmount);
// Assert
await expect(pulpitPage.messageText).toHaveText(expectedMessage);
});
test('correct balance after successful mobile top-up', async ({ page }) => {
// Arrange
const topUpReceiver = '500 xxx xxx';
const topUpAmount = '50';
const initialBalance = await pulpitPage.moneyValueText.innerText();
const expectedBalance = Number(initialBalance) - Number(topUpAmount);
// Act
await pulpitPage.executeMobileTopUp(topUpReceiver, topUpAmount);
// Assert
await expect(pulpitPage.moneyValueText).toHaveText(`${expectedBalance}`);
});
});

Oddolna inicjatywa taka: w pliku
pulpit.page.tsmożna wprowadzić drobną modyfikację w nazewnictwie- która notabene jest prezentowana na videoW repo jest:
sideMenuComponent: SideMenuComponent;a jeżeli zmodyfikujemy na
sideMenu: SideMenuComponent;temat może być ciut łatwiejszy do zrozumienia / rozróżnienia
Ale to tylko luźna suegestia
Hej,
Słuszne spostrzeżenie!
W tym podejściu idziemy w strone skrócenia i uproszenia nazw – wtedy wywołania stają sie krótsze 😉
Warto tutaj pamiętac o 2 rzeczach – spójności w całym projekcie i aby nie iśc za bardzo w “skracanie” aby nadal zachować sens i czytelność 😉
Hej mam problem,
Poniższy kod działa poprawnie
test('quick payment with correct data', async ({ page }) => { // Arrange const expectedUserName = 'Jan Demobankowy'; const reciverId = '2'; const transferAmount = '120'; const transferTitle = 'Zwrot środków'; const expectedTransferReceiver = 'Chuck Demobankowy'; // Act await pulpit.transferReceiver.selectOption(reciverId); await pulpit.transferAmount.fill(transferAmount); await pulpit.transferTitle.fill(transferTitle); await pulpit.transferButton.click(); await pulpit.closeButton.click(); // Assert await expect(pulpit.confirmationMessage).toHaveText( `Przelew wykonany! ${expectedTransferReceiver} - ${transferAmount},00PLN - ${transferTitle}`, ); });Natomiast jak dodałem metodę
pulpit.executeQuickPayment(reciverId,transferAmount,transferTitle)
Która wygląda tak samo jak u Was
async executeQuickPayment( receiverId: string, transferAmount: string, transferTitle: string ): Promise { await this.transferReceiver.selectOption(receiverId); await this.transferAmount.fill(transferAmount); await this.transferTitle.fill(transferTitle); await this.transferButton.click(); await this.closeButton.click(); }To nie chce kliknąć na closeButton i rzuca błąd:
Error: locator.click: Target page, context or browser has been closed
Głowie się co może to powodować, Z góry dzięki za pomoc!
Hej,
Wszystko wskazuje na to, ze brakuje
awaitprzed wywołaniem metody (która jest asynchroniczna (słówkoasync)) 😉czyli:
Faktycznie ! Dzięki za pomoc bo jakoś to mi umknęło 🙂
Ciesze się, że śmiga 😀
PS. Z doświadczenia powiem, że
await(albo jego brak) jest częstym powodem rożnych błędów w testach 😉 A że metoda/funkcja asynchroniczna jest poprawna (ale niekoniecznie w danym miejscu) bez await, to po naszej stronie jest jej przypilnowanie 😀A jak najlepiej uruchamiać jedną serię testów dla różnych userów (różne uprawnienia) tak aby nie dublować kodu?
Hej,
Dobre pytanie 🙂
Tu sporo zależy od konkretnych scenariuszów oraz testowanej aplikacji.
Tu się pojawiają dwie kwestie – uruchomienie testów z różnymi użytkownikami i implementacja testów, logiki i POM 🙂
Implementacja POM – tutaj można pójść prosto, czyli zaimplementować reprezentacje stron z punktu widzenia użytkownika, który może wykonać każdą operacje.
Plusem tego rozwiązani jest – prostota implementacji i brak dodatkowej logiki i duplikacji kodu w obiektach stron.
Minusem – my z poziomu testów musimy wiedzieć co dany użytkownik o danych uprawnieniach może wykonać, a czego nie może.
Bardziej skomplikowaną wersją jest dodanie dziedziczenia, ze stronami pochodnymi implementującymi akcje,w zależności od roli. To rozwiązanie zdejmuje z nas potrzebę pamiętania w testach co dany użytkownik może wykonać, natomiast może bardzo skomplikować implementacje samych page objectów.
Kwestia testów – inne testy będą dla użytkowników, którzy moga wejść na stronę X i zrobić Y, niz dla użytkowników, którzy nie mają dostępu do strony X.
Mozna je pogrupować w katalogi, albo tagi, a później możemy uruchamiać nasze testy z różnymi zestawami danych 🙂
Temat ten planujemy poruszyć w zaawansowanej części Programu o Playwright 🙂
Natomiast testy na różnych środowiskach (z wykorzystaniem różnych danych) opisujemy ogólnie w naszym poście: https://playwright.info/playwright-testy-na-roznych-srodowiskach – pokazujemy tam wykorzystanie projects w Playwright i biblioteki dotenv (którą też dokładnie omawiamy w części zaawansowanej).
Super artykuł dzięki. To było moje kolejne pytanie o różne środowiska. A jak w takim wypadku ustawia się różne dane logowania dla poszczególnych środowisk?
To dokładnie pokrywa punkt Sposób 2 (rozszerzony) z tego posta 😀
W dużym skrócie – możemy przygotować klika plików ze zmiennymi środowiskowymi, z których korzystamy w testach.
Każdy plik będzie odpowiadał danemu środowisku.
W kodzie dodajemy możliwość wyboru dowolnego pliku ze zmiennymi środowiskowymi 🙂
Później możemy np. podczas uruchamiania testów podac parametr, który mówi z jakim plikiem/na jakim środowisku chcemy puszczać testy np.:
a zmienne w testach (adresy, dane użytkowników etc) będą automatycznie pobierane z podanego przez nas pliku 🙂
Dzięki za odpowiedź Krzysiek, teraz wiem z której strony to ugryźć.
w kilku miejscach w kodzie na stronie macie:
): Promise$lt;void> {
Dzięki za zgłoszenie – wkradła mi się literówka/błąd w formatowaniu – poprawione! 🙂