diff --git a/.env.testing b/.env.testing new file mode 100644 index 0000000..bcaacc3 --- /dev/null +++ b/.env.testing @@ -0,0 +1,3 @@ +BASE_URL=https://team8-2022brno.herokuapp.com +ADMIN_USERNAME=da-app.admin@czechitas.cz +ADMIN_PASSWORD=${ADMIN_PASSWORD} \ No newline at end of file diff --git a/.github/workflows/playwright-tests.yml b/.github/workflows/playwright-tests.yml new file mode 100644 index 0000000..9af1232 --- /dev/null +++ b/.github/workflows/playwright-tests.yml @@ -0,0 +1,42 @@ +name: Playwright Tests and Deploy Report to GitHub Pages + +on: + push: + branches: [main] + +jobs: + test-and-deploy-report: + timeout-minutes: 60 + runs-on: ubuntu-latest + container: + image: mcr.microsoft.com/playwright:v1.49.1-noble + options: --user 1001 + permissions: + contents: read + pages: write + id-token: write + environment: github-pages + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + + - name: Install dependencies + run: npm ci + - name: Run Playwright tests + run: npx playwright test --config=playwright.ci.config.js --grep @ci + env: + ADMIN_PASSWORD: ${{ secrets.ADMIN_PASSWORD }} + continue-on-error: true + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + if: always() + with: + path: playwright-report + + - name: Deploy to GitHub Pages + uses: actions/deploy-pages@v4 + if: always() diff --git a/package-lock.json b/package-lock.json index 2716b9e..93fbb25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "automation-playwright", "version": "1.0.0", "devDependencies": { - "@playwright/test": "^1.47.2", + "@playwright/test": "^1.49.1", "@types/node": "^20.14.11", "dotenv": "^16.4.5", "eslint": "^9.9.1", @@ -382,12 +382,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", - "integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", + "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", "dev": true, "dependencies": { - "playwright": "1.47.2" + "playwright": "1.49.1" }, "bin": { "playwright": "cli.js" @@ -1312,12 +1312,12 @@ } }, "node_modules/playwright": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", - "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", + "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", "dev": true, "dependencies": { - "playwright-core": "1.47.2" + "playwright-core": "1.49.1" }, "bin": { "playwright": "cli.js" @@ -1363,9 +1363,9 @@ } }, "node_modules/playwright-core": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", - "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", + "version": "1.49.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", + "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", "dev": true, "bin": { "playwright-core": "cli.js" diff --git a/package.json b/package.json index d2254fe..0135ed6 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,11 @@ "name": "automation-playwright", "version": "1.0.0", "devDependencies": { - "@playwright/test": "^1.47.2", + "@playwright/test": "^1.49.1", "@types/node": "^20.14.11", "dotenv": "^16.4.5", "eslint": "^9.9.1", "playwright-bdd": "^7.5.0" }, "scripts": {} -} +} \ No newline at end of file diff --git a/playwright.ci.config.js b/playwright.ci.config.js new file mode 100644 index 0000000..9fb5373 --- /dev/null +++ b/playwright.ci.config.js @@ -0,0 +1,74 @@ +// @ts-check +require("dotenv").config({ path: ".env.testing" }); +const { defineConfig, devices } = require("@playwright/test"); +const { BASE_URL } = process.env; + +/** + * @see https://playwright.dev/docs/test-configuration + */ +module.exports = defineConfig({ + testDir: "./src/examples", + /* Run tests in files in parallel */ + fullyParallel: false, + /* 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 ? 1 : 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", { outputFolder: "playwright-report", open: "never" }]], + /* 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", + }, + + /* 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: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/playwright.config.js b/playwright.config.js index 87d4f31..7f6fe10 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -73,5 +73,4 @@ module.exports = defineConfig({ // url: 'http://127.0.0.1:3000', // reuseExistingServer: !process.env.CI, // }, -}); - +}); \ No newline at end of file diff --git a/src/examples/08-page-objects/login.spec.js b/src/examples/08-page-objects/login.spec.js index 21af283..6ba48d5 100644 --- a/src/examples/08-page-objects/login.spec.js +++ b/src/examples/08-page-objects/login.spec.js @@ -1,78 +1,114 @@ /** * Lesson 8: Code organization: Page Object Model - Exercise 1 */ -import {username, password, userFullName, ApplicationTexts} from '../../fixtures/fixtures.js' -import {expect, test} from "@playwright/test"; -import {LoginPage} from "./pages/login.page"; - -test.describe('Login Page', async () => { - - test.beforeEach(async ({ page }) => { - const loginPage = new LoginPage(page); - await loginPage.open(); - await test.expect(page).toHaveTitle(ApplicationTexts.loginPage.title); - }); - - test('should show login form', async ({ page }) => { - const loginPage = new LoginPage(page); - - await expect(loginPage.emailField, 'email field should be visible').toBeVisible(); - await expect(loginPage.emailField, 'email field should be enabled').toBeEnabled(); - - await expect(loginPage.passwordField, 'password field should be visible').toBeVisible(); - await expect(loginPage.passwordField, 'password field should be enabled').toBeEnabled(); - - await expect(loginPage.loginButton, 'login button should be visible').toBeVisible(); - await expect(loginPage.loginButton, 'login button text should have text').toHaveText('Přihlásit'); - }); - - test('should login with valid credentials', async ({page}) => { - const loginPage = new LoginPage(page); - - // await loginPage.emailField.fill(username); - // await loginPage.passwordField.fill(password); - // await loginPage.loginButton.click(); - - await loginPage.login(username, password); - - await expect(loginPage.usernameDropdown).toHaveText(userFullName); - }); - - test('should not login with invalid credentials', async ({ page }) => { - const loginPage = new LoginPage(page); - // const emailField = getEmailField(page); - // const passwordField = getPasswordField(page); - // const loginButton = getLoginButton(page); - - // await loginPage.emailField.fill(username); - // await loginPage.passwordField.fill('invalid'); - // await loginPage.loginButton.click(); - - await loginPage.login(username, 'invalid'); - - await expect(loginPage.toast).toHaveText("Některé pole obsahuje špatně zadanou hodnotu"); - await expect(loginPage.fieldError).toHaveText("Tyto přihlašovací údaje neodpovídají žadnému záznamu."); - - await expect(loginPage.emailField).toBeVisible(); - await expect(loginPage.passwordField).toBeVisible(); - await expect(loginPage.loginButton).toBeVisible(); - }); - - test('should logout', async ({ page }) => { - const loginPage = new LoginPage(page); - - // await loginPage.emailField.fill(username); - // await loginPage.passwordField.fill(password); - // await loginPage.loginButton.click(); - - await loginPage.login(username, password); - - await expect(await loginPage.usernameDropdown).toHaveText(userFullName); - - await loginPage.usernameDropdown.click(); - await loginPage.logoutLink.click(); - - await expect(await loginPage.usernameDropdown).toBeVisible({ visible: false }); - await expect(await loginPage.navbarRight).toHaveText('Přihlásit'); +import { expect, test } from "@playwright/test"; +import { + ApplicationTexts, + password, + userFullName, + username, +} from "../../fixtures/fixtures.js"; +import { LoginPage } from "./pages/login.page"; + +test.describe("Login Page", async () => { + test.beforeEach(async ({ page }) => { + const loginPage = new LoginPage(page); + await loginPage.open(); + await test.expect(page).toHaveTitle(ApplicationTexts.loginPage.title); + }); + + test("should show login form", { tag: "@ci" }, async ({ page }) => { + const loginPage = new LoginPage(page); + + await expect( + loginPage.emailField, + "email field should be visible" + ).toBeVisible(); + await expect( + loginPage.emailField, + "email field should be enabled" + ).toBeEnabled(); + + await expect( + loginPage.passwordField, + "password field should be visible" + ).toBeVisible(); + await expect( + loginPage.passwordField, + "password field should be enabled" + ).toBeEnabled(); + + await expect( + loginPage.loginButton, + "login button should be visible" + ).toBeVisible(); + await expect( + loginPage.loginButton, + "login button text should have text" + ).toHaveText("Přihlásit"); + }); + + test( + "should login with valid credentials", + { tag: "@ci" }, + async ({ page }) => { + const loginPage = new LoginPage(page); + + // await loginPage.emailField.fill(username); + // await loginPage.passwordField.fill(password); + // await loginPage.loginButton.click(); + + await loginPage.login(username, password); + + await expect(loginPage.usernameDropdown).toHaveText(userFullName); + } + ); + + test( + "should not login with invalid credentials", + { tag: "@ci" }, + async ({ page }) => { + const loginPage = new LoginPage(page); + // const emailField = getEmailField(page); + // const passwordField = getPasswordField(page); + // const loginButton = getLoginButton(page); + + // await loginPage.emailField.fill(username); + // await loginPage.passwordField.fill('invalid'); + // await loginPage.loginButton.click(); + + await loginPage.login(username, "invalid"); + + await expect(loginPage.toast).toHaveText( + "Některé pole obsahuje špatně zadanou hodnotu" + ); + await expect(loginPage.fieldError).toHaveText( + "Tyto přihlašovací údaje neodpovídají žadnému záznamu." + ); + + await expect(loginPage.emailField).toBeVisible(); + await expect(loginPage.passwordField).toBeVisible(); + await expect(loginPage.loginButton).toBeVisible(); + } + ); + + test("should logout", { tag: "@ci" }, async ({ page }) => { + const loginPage = new LoginPage(page); + + // await loginPage.emailField.fill(username); + // await loginPage.passwordField.fill(password); + // await loginPage.loginButton.click(); + + await loginPage.login(username, password); + + await expect(await loginPage.usernameDropdown).toHaveText(userFullName); + + await loginPage.usernameDropdown.click(); + await loginPage.logoutLink.click(); + + await expect(await loginPage.usernameDropdown).toBeVisible({ + visible: false, }); + await expect(await loginPage.navbarRight).toHaveText("Přihlásit"); + }); });