import { test, expect } from "@playwright/test";
test.describe("Multiple locators", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/practice/simple-multiple-elements-no-ids.html");
});
test("action on multiple checkboxes (advanced, with assertion)", async ({
page,
}) => {
// Arrange:
const elementRole = "checkbox";
const resultsTestId = "dti-results";
const expectedMessages = {
0: "Checkbox is checked! (Opt 1!)",
1: "Checkbox is checked! (Opt 2!)",
2: "Checkbox is checked! (Opt 3!)",
3: "Checkbox is checked! (Opt 4!)",
4: "Checkbox is checked! (Opt 5!)",
};
const expectedNumberOfElements = 5;
const checkboxLocator = page.getByRole(elementRole);
const resultsLocator = page.getByTestId(resultsTestId);
// Assert:
await expect(checkboxLocator).toHaveCount(expectedNumberOfElements);
// Act & Assert:
const numberOfFoundCheckboxes = await checkboxLocator.count();
for (let i = 0; i < numberOfFoundCheckboxes; i++) {
// Act:
await checkboxLocator.nth(i).check();
console.log(await resultsLocator.innerText());
// Assert:
await expect.soft(resultsLocator).toHaveText(expectedMessages[i]);
}
});
});
Tu ciekawym dodatkowym zadaniem może być dodatkowa asercja w pętli na zmieniającą się zawartość kontenera Results History – to tak jakby ktoś szukał inspiracji do dalszych ćwiczeń 🙂
Tez zachęcam o eksperymentów i wprowadzania różnych usprawnień 😉
Moje trzy grosze w dyskusji:
******************************************************************* import { expect, test as it, type Locator } from "@playwright/test"; it.describe("Multiple checkboxes", () => { it.beforeEach(async ({ page }) => { await page.goto("/practice/simple-multiple-elements-no-ids.html"); }); it("action on multiple checkboxes", async ({ page }) => { // Arrange const getMessageAboutOperation = async ( checkboxElement: Locator, ): Promise => { const isChecked = await checkboxElement.isChecked(); const textContent = await checkboxElement.evaluate((element) => { const nextSibling = element.nextSibling; return nextSibling ? nextSibling.textContent?.trim() : null; }); return `Checkbox is ${isChecked ? "checked" : "unchecked"}! (${textContent}!)`; }; const checkBox = page.locator('input[type="checkbox"]'); const expectedCheckboxCount: number = 5; const resultsElement = page.getByTestId("dti-results"); // Act for (const checkboxElement of await checkBox.all()) { await checkboxElement.check(); const textContent = await getMessageAboutOperation(checkboxElement); console.log(`The text content is: "${textContent}"`); const resultsTextContent = await resultsElement.textContent(); console.log( `The results element text content is: "${resultsTextContent}"`, ); await expect(resultsElement).toContainText(textContent || ""); } // Assert await expect(checkBox).toHaveCount(expectedCheckboxCount); }); });Cześć
Miałam problem z użyciem obiektu expectedMessages:
Element implicitly has an ‘any’ type because expression of type ‘number’ can’t be used to index type ‘{ 0: string; 1: string; 2: string; 3: string; 4: string; }’.
No index signature with a parameter of type ‘number’ was found on type ‘{ 0: string; 1: string; 2: string; 3: string; 4: string; }’.
Musiałam z tego obiektu robić array.
Hej,
Jak wyglądał Twój kod dla którego pojawił się ten błąd? 🙂
Tak samo jak i u was w tej sekcji (Rozwiązanie trudniejsze, z asercją). Próbowałam nawet robić kopię waszego kodu żeby. I miałam ten problem z powrotem.
Hmm błąd o którym wspominasz prawdopodobnie wynikał z konfiguracji projektu i ze TypeScript traktował klucze w obiekcie jako “0”, “1” itd.
Gdy próbujesz użyć liczby do indeksowania (np. expectedMessages[2]), TypeScript nie znajduje dopasowania do indeksu typu number – oczekuje, że obiekt będzie miał sygnaturę indeksu zdefiniowaną np. jako
[key: number]: string.Rozwiązania są 2 – jedno to zamiana na listę (czyli Twoje rozwiązanie).
Drugie to dodanie sygnatury indeksu do obiektu, czyli jawne określienie, że obiekt ten ma sygnaturę indeksu dla number np:
const expectedMessages: { [key: number]: string } = { 0: 'wiadomość0', 1: 'wiadomość1', 2: 'wiadomość2', 3: 'wiadomość3', 4: 'wiadomość4', };A dla mnie nie przemawia ani tablica ani lista 😉
await page.goto('/practice/simple-multiple-elements-no-ids.html'); const checkboxLocator = page.getByRole('checkbox'); console.log(await checkboxLocator.count()); await expect(checkboxLocator).toHaveCount(5); const result = page.getByTestId('dti-results-container'); for (const button of await checkboxLocator.all()) { await button.check(); console.log(await result.textContent()); } console.log('===================================='); for (let i = 0; i < (await checkboxLocator.count()); i++) { await checkboxLocator.nth(i).check(); await expect(result).toHaveText(`Checkbox is checked! (Opt ${i + 1}!)`); }wystarczy spojrzeć, że ten wpis się powtarza "Checkbox is checked! (Opt)"
Gdyby to pojawiło się dwa razy to jeszcze ok, ale w przypadku 5 razy to już jest DRY
Można prościej 😉
Zgadzam się, ze, ze to może być kolejny krok 🙂
W tym przypadku rozwiązanie z listą pokazuje nam duplikacje, a tym samym możemy zrefaktoryzować rozwiązanie i wydzielić część wspólną.
Rozwiązanie z listą ma na celu pokazanie jak można projektować struktury danych się przydać, gdy będziemy potrzebować odnosić się do oczekiwanych danych za pomocą klucza 😉
To ja bym tutaj wgl poszedł inaczej, nie robił żadnej struktury w kodzie testowym tylko plik enums i tam wrzucił enuma z tym 🙂
W sumie ciekawy pomysł 😀
Możesz podrzucić taki typ rozwiązania tutaj – może być inspirujący dla osób, które będą przechodziły przez tą lekcję 😀
export enum VerifyMessage { Option1 = "Checkbox is checked! (Opt 1!)", Option2 = "Checkbox is checked! (Opt 2!)", Option3 = "Checkbox is checked! (Opt 3!)", Option4 = "Checkbox is checked! (Opt 4!)", Option5 = "Checkbox is checked! (Opt 5!)" } import w teście: import { VerifyMessage } from './verifyMessages'; test: await page.goto('/practice/simple-multiple-elements-no-ids.html'); const checkboxLocator = page.getByRole('checkbox'); console.log(await checkboxLocator.count()); await expect(checkboxLocator).toHaveCount(5); const result = page.getByTestId('dti-results-container'); for (let i = 0; i < (await checkboxLocator.count()); i++) { await checkboxLocator.nth(i).check(); // Pobranie oczekiwanej wartości z enum const expectedMessage = VerifyMessage[`Option${i + 1}` as keyof typeof VerifyMessage]; await expect(result).toHaveText(expectedMessage); } }Dzięki! 😀
hej ja w dodatkowym zadaniu użyłem takiego zapisu
const verifyMessage = [ 'Checkbox is checked! (Opt 1!)', 'Checkbox is checked! (Opt 2!)', 'Checkbox is checked! (Opt 3!)', 'Checkbox is checked! (Opt 4!)', 'Checkbox is checked! (Opt 5!)']choć pokazany zapis obiektu bardziej do mnie przemawia 🙂 świetna lekcja i zadania które faktycznie wnoszą praktyczne umiejętności 😉
Dzięki wielkie!🙇♂️
W tym przypadku tka lista też się sprawdzi, a lista obiektów daje więcej możliwości.
A to moze się przydać w bardziej skomplikowanych przypadkach lub gdy potrzebujemy bardziej skomplikowanych danych testowych 🙂