Skip to content

Add new test for open a page and screenshot function #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 16 additions & 17 deletions playwright.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// @ts-check
require('dotenv').config();
require("dotenv").config();

const { defineConfig, devices } = require('@playwright/test');
const { defineConfig, devices } = require("@playwright/test");
const { BASE_URL } = process.env;

/**
* @see https://playwright.dev/docs/test-configuration
*/
module.exports = defineConfig({
testDir: './src/tests',
testDir: "./src/tests",
// testDir: './src/examples',
/* Run tests in files in parallel */
fullyParallel: false,
Expand All @@ -19,32 +19,32 @@ module.exports = defineConfig({
/* 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',
reporter: "html",
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: BASE_URL,

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

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

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

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

/* Test against mobile viewports. */
// {
Expand Down Expand Up @@ -74,4 +74,3 @@ module.exports = defineConfig({
// reuseExistingServer: !process.env.CI,
// },
});

26 changes: 11 additions & 15 deletions src/fixtures/fixtures.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,16 @@ const { ADMIN_USERNAME, ADMIN_PASSWORD } = process.env;

export const username = ADMIN_USERNAME;
export const password = ADMIN_PASSWORD;
export const userFullName = "Lišák Admin";
export const applicationsPageSize = 10;
export const applicationsSearchText = "Eli";
export const applicationsSearchResultText = /.*[Ee]l[ií].*/;
export const userFullName = "Konečná Hana";
export const invalidPassword = "123456789";

export const ApplicationTexts = {
loginPage: {
title:"Přihlášení - Czechitas",
emailFieldLabel: "Email",
passwordFieldLabel: "Heslo",
loginButtonLabel: "Přihlásit",
},
applicationsPage: {
title: "Přihlášky - Czechitas",
applicationsSectionName: "Přihlášky"
}
}
loginPage: {
title: "Přihlášení - Czechitas",
loginButtonLabel: "Přihlásit",
},
registrationPage: {
title: "Registrace - Czechitas",
registrationButtonLabel: "Zaregistrovat",
},
};
6 changes: 0 additions & 6 deletions src/tests/example.spec.js

This file was deleted.

125 changes: 125 additions & 0 deletions src/tests/homework.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { expect, test } from "@playwright/test";

import {
ApplicationTexts,
invalidPassword,
password,
userFullName,
} from "../fixtures/fixtures.js";

import { LoginPage } from "./pages/login.page.js";
import { RegistrationPage } from "./pages/registration.page.js";

test.describe("Registration Page is Correctly Displayed", async () => {
test.beforeEach(async ({ page }) => {
const loginPage = new LoginPage(page);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pro účely tohoto zadání není potřeba více page object modelů než jeden, a to RegistrationPage. Do něj se dá vložit metoda pro otevření stránky open()

await loginPage.open();
await loginPage.registrationButton.click();
await test
.expect(page)
.toHaveTitle(ApplicationTexts.registrationPage.title);
});

test("verify registration form is displayed correctly ", async ({ page }) => {
const registrationPage = new RegistrationPage(page);

await expect(
registrationPage.fullNameInput,
"name input field should be visible"
).toBeVisible();
await expect(
registrationPage.fullNameInput,
"name input field should be enabled"
).toBeEnabled();

await expect(
registrationPage.emailInput,
"email input field should be visible"
).toBeVisible();
await expect(
registrationPage.emailInput,
"email input field should be enabled"
).toBeEnabled();

await expect(
registrationPage.passwordInput,
"password input field should be visible"
).toBeVisible();
await expect(
registrationPage.passwordInput,
"password input field should be enabled"
).toBeEnabled();

await expect(
registrationPage.confirmPasswordInput,
"confirm password input field should be visible"
).toBeVisible();
await expect(
registrationPage.confirmPasswordInput,
"confirm password input field should be enabled"
).toBeEnabled();

await expect(
registrationPage.registrationButton,
"registration button should be visible"
).toBeVisible();
await expect(
registrationPage.registrationButton,
"login button text should have text"
).toHaveText(ApplicationTexts.registrationPage.registrationButtonLabel);
});

test("verify registration form using screenshot", async ({ page }) => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Samostatný screenshot by neměl být test. Můžeš ho ale klidně dát do testu verify registration form is displayed correctly

await page.screenshot({ path: "registrationForm.png" });
});
});

//Temporarily force a single worker //
test.describe("User Registration - Valid and Invalid Credentials", () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Myslím že není potřeba mít dva test.describe(). Jeden test.describe() by stačil.

let testEmail;

test.beforeAll(async () => {
testEmail = `testuserCzechitas+${Date.now()}@gmail.com`;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

testEmail generuje vždy unikátní e-mail, protože Date.now() negeneruje jen datum, ale i čas. To znamená, že test should not register with existing email vždy failne.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pro účely testu should not register with existing email by stačil statický, již existující e-mail (třeba [email protected]), generováním dynamického e-mailu se vystavujeme flakiness. V takovém případě by ani nemusel být v test.beforeAll(), ale cením snahu beforeAll() nějak zakomponovat.

console.log("Generated Email in beforeAll:", testEmail);
});

test.beforeEach(async ({ page }) => {
const registrationPage = new RegistrationPage(page);
await registrationPage.open();
await test
.expect(page)
.toHaveTitle(ApplicationTexts.registrationPage.title);
});

test("should register with valid credentials", async ({ page }) => {
const registrationPage = new RegistrationPage(page);
await registrationPage.register(userFullName, testEmail, password);

await expect(registrationPage.usernameDropdown).toHaveText(userFullName);
});

test("should not register with existing email", async ({ page }) => {
const registrationPage = new RegistrationPage(page);
await registrationPage.register(userFullName, testEmail, password);
await expect(registrationPage.toast).toHaveText(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert toHaveText() je fajn, ale mnohem důležitější assert je .isVisible() - zajišťuje totiž, že se element skutečně zobrazuje v DOMu a není třeba hidden nebo překrytý jinou komponentu. Bylo by fajn assertnout i text i visibilitu.

"Některé pole obsahuje špatně zadanou hodnotu"
);
await expect(registrationPage.fieldError).toHaveText(
"Účet s tímto emailem již existuje"
);
});

test("should not register with invalid password - only digits", async ({

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formátování:

test("should not register with invalid password - only digits", async ({ page }) => {

page,
}) => {
const testEmail2 = `testuserCzechitas+${Date.now()}@gmail.com`;
const registrationPage = new RegistrationPage(page);
await registrationPage.register(userFullName, testEmail2, invalidPassword);
await expect(registrationPage.toast).toHaveText(
"Některé pole obsahuje špatně zadanou hodnotu"
);
await expect(registrationPage.fieldError).toHaveText(
"Heslo musí obsahovat minimálně 6 znaků, velké i malé písmeno a číslici"
);
});
});
30 changes: 30 additions & 0 deletions src/tests/pages/app.page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export class AppPage {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stejně jako LoginPage, ani AppPage object model tu úplně nemusí být. Vzniká totiž tzv. dead code - nevyužitý kód - funkce getToastMessage(), logout() a getCurrentUser() se nikde v rámci /test/homework.spec.js nepoužívají.

constructor(page, url) {
this.url = url;
this.page = page;
this.toast = this.page.locator(".toast-message");
this.navbarRight = this.page.locator(".navbar-right");
this.usernameDropdown = this.navbarRight.locator(
"[data-toggle='dropdown']"
);
this.logoutLink = this.page.locator("#logout-link");
this.fieldError = this.page.locator(".invalid-feedback");
}

async open() {
await this.page.goto("/" + this.url);
}

async getToastMessage() {
return await this.toast.textContent();
}

async logout() {
await this.usernameDropdown.click();
await this.logoutLink.click();
}

async getCurrentUser() {
return await this.usernameDropdown.textContent();
}
}
21 changes: 21 additions & 0 deletions src/tests/pages/login.page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//Page object describing the login page//

const { AppPage } = require("./app.page");

export class LoginPage extends AppPage {
constructor(page) {
super(page, "prihlaseni");
this.emailField = this.page.getByLabel("Email");
this.passwordField = this.page.getByLabel("Heslo");
this.loginButton = this.page.getByRole("button", { name: "Přihlásit" });
this.registrationButton = this.page.getByRole("link", {
name: "Zaregistrujte se",
});
}

async login(username, password) {
await this.emailField.fill(username);
await this.passwordField.fill(password);
await this.loginButton.click();
}
}
24 changes: 24 additions & 0 deletions src/tests/pages/registration.page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//Page object describing the registration page//

import { AppPage } from "./app.page";

export class RegistrationPage extends AppPage {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jak jsem zmínila: RegistrationPage jako page object model dokáže pobrat všechny elementy, lokátory i funkce potřebné k pokrytí stránky registrace. LoginPage a AppPage nejsou potřeba. Struktura s víc page object modely dává smysl u testů, kde testujeme více stránek - tento úkol je ale pouze o testování stránky registrace.

constructor(page) {
super(page, "registrace");
this.fullNameInput = this.page.getByLabel("Jméno a příjmení");
this.emailInput = this.page.getByLabel("Email");
this.passwordInput = this.page.getByLabel("Heslo");
this.confirmPasswordInput = this.page.getByLabel("Kontrola hesla");
this.registrationButton = this.page.getByRole("button", {
name: "Zaregistrovat",
});
}

async register(userFullName, username, password) {
await this.fullNameInput.fill(userFullName);
await this.emailInput.fill(username);
await this.passwordInput.fill(password);
await this.confirmPasswordInput.fill(password);
await this.registrationButton.click();
}
}