├── .gitignore ├── package.json ├── tests ├── pageObjectModelWithFixtures.spec.ts ├── support │ └── pageobjectmodel │ │ ├── sections │ │ ├── computerDetails.section.ts │ │ └── computerActions.section.ts │ │ └── pages │ │ ├── computers.page.ts │ │ └── addComputer.page.ts ├── fixtures │ └── basePage.ts └── pageObjectModel.spec.ts ├── README.md └── playwright.config.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playwright-test-created-by-commit-quality-youtube", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": {}, 7 | "keywords": [], 8 | "author": "", 9 | "license": "ISC", 10 | "devDependencies": { 11 | "@playwright/test": "^1.29.0" 12 | } 13 | } -------------------------------------------------------------------------------- /tests/pageObjectModelWithFixtures.spec.ts: -------------------------------------------------------------------------------- 1 | import { test } from "./fixtures/basePage"; 2 | 3 | test("basic test POM with fixtures", async ({ 4 | computersPage, 5 | addComputerPage, 6 | }) => { 7 | await computersPage.goto(); 8 | await computersPage.clickAddNewComputer(); 9 | 10 | await addComputerPage.addNewComputer(); 11 | 12 | await computersPage.assertNewComputerAdded(); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/support/pageobjectmodel/sections/computerDetails.section.ts: -------------------------------------------------------------------------------- 1 | import { Page } from "@playwright/test"; 2 | export default class ComputerDetails { 3 | page: Page; 4 | 5 | constructor(page: Page) { 6 | this.page = page; 7 | } 8 | 9 | // Locators here 10 | createThisComputerButton = () => this.page.getByText("Create this computer"); 11 | 12 | // Actions 13 | public async createComputer() { 14 | await this.createThisComputerButton().click(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Playwright-PageObjectModel-Example 2 | Repository created after a request from a viewer on the following youtube tutorial https://www.youtube.com/watch?v=Hp4QIBJO3yY&lc=UgyoIW9m44uXWP-7jqR4AaABAg 3 | 4 | 5 | ## To get test working 6 | - Run "npm install" via terminal 7 | - Run "npx playwright test" via terminal 8 | 9 | For any questions, please subscribe and leave a comment on the youtube video: https://www.youtube.com/watch?v=Hp4QIBJO3yY&lc=UgyoIW9m44uXWP-7jqR4AaABAg 10 | -------------------------------------------------------------------------------- /tests/fixtures/basePage.ts: -------------------------------------------------------------------------------- 1 | import { test as base } from "@playwright/test"; 2 | import ComputersPage from "../support/pageobjectmodel/pages/computers.page"; 3 | import AddComputerPage from "../support/pageobjectmodel/pages/addComputer.page"; 4 | 5 | // Extend basic test by providing a two new fixtures (our page object pages) 6 | export const test = base.extend<{ 7 | computersPage: ComputersPage; 8 | addComputerPage: AddComputerPage; 9 | }>({ 10 | // Define a fixture. Note that it can use built-in fixture "page" 11 | computersPage: async ({ page }, use) => { 12 | await use(new ComputersPage(page)); 13 | }, 14 | addComputerPage: async ({ page }, use) => { 15 | await use(new AddComputerPage(page)); 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /tests/support/pageobjectmodel/sections/computerActions.section.ts: -------------------------------------------------------------------------------- 1 | import { Page } from "@playwright/test"; 2 | export default class ComputerActions { 3 | page: Page; 4 | 5 | constructor(page: Page) { 6 | this.page = page; 7 | } 8 | 9 | // Locators here 10 | nameTextbox = () => this.page.locator("#name"); 11 | introducedTextbox = () => this.page.locator("#introduced"); 12 | discontinuedTextbox = () => this.page.locator("#discontinued"); 13 | companySelect = () => this.page.locator("#company"); 14 | 15 | // Actions 16 | public async enterComputerDetails() { 17 | await this.nameTextbox().fill("CommitQuality"); 18 | await this.introducedTextbox().fill("1999-12-12"); 19 | await this.introducedTextbox().fill("2000-12-12"); 20 | await this.companySelect().selectOption({ label: "Apple Inc." }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/support/pageobjectmodel/pages/computers.page.ts: -------------------------------------------------------------------------------- 1 | import { expect, Page } from "@playwright/test"; 2 | export default class ComputersPage { 3 | page: Page; 4 | 5 | constructor(page: Page) { 6 | this.page = page; 7 | } 8 | 9 | // I like to add a goto method into each page I create 10 | public async goto() { 11 | await this.page.goto("https://computer-database.gatling.io/computers"); 12 | } 13 | 14 | // Locators here 15 | addComputerButton = () => this.page.getByText("Add a new computer"); 16 | computerAddedLabel = () => 17 | this.page.getByText("Done ! Computer CommitQuality has been created"); 18 | 19 | // Actions 20 | public async clickAddNewComputer() { 21 | await this.addComputerButton().click(); 22 | } 23 | public async assertNewComputerAdded() { 24 | await expect(this.computerAddedLabel()).toBeVisible(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/support/pageobjectmodel/pages/addComputer.page.ts: -------------------------------------------------------------------------------- 1 | import { Page } from "@playwright/test"; 2 | import ComputerActions from "../sections/computerActions.section"; 3 | import ComputerDetails from "../sections/computerDetails.section"; 4 | export default class AddComputerPage { 5 | page: Page; 6 | computerDetails: ComputerDetails; 7 | computerActions: ComputerActions; 8 | 9 | constructor(page: Page) { 10 | this.page = page; 11 | this.computerActions = new ComputerActions(this.page); 12 | this.computerDetails = new ComputerDetails(this.page); 13 | } 14 | 15 | // I like to add a goto method into each page I create 16 | public async goto() { 17 | await this.page.goto("https://computer-database.gatling.io/computers"); 18 | } 19 | 20 | // Locators here 21 | addComputerButton = () => this.page.getByText("Add a new computer"); 22 | 23 | // Actions 24 | public async clickAddNewComputer() { 25 | await this.addComputerButton().click(); 26 | } 27 | 28 | public async addNewComputer() { 29 | await this.computerActions.enterComputerDetails(); 30 | await this.computerDetails.createComputer(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/pageObjectModel.spec.ts: -------------------------------------------------------------------------------- 1 | import { test, expect } from "@playwright/test"; 2 | import AddComputerPage from "./support/pageobjectmodel/pages/addComputer.page"; 3 | import ComputersPage from "./support/pageobjectmodel/pages/computers.page"; 4 | 5 | test("basic test", async ({ page }) => { 6 | await page.goto("https://computer-database.gatling.io/computers"); 7 | await page.getByText("Add a new computer").click(); 8 | await page.locator("#name").fill("CommitQuality"); 9 | await page.locator("#introduced").fill("1999-12-12"); 10 | await page.locator("#introduced").fill("1999-12-12"); 11 | await page.locator("#discontinued").fill("2000-12-12"); 12 | await page.locator("#company").selectOption({ label: "Apple Inc." }); 13 | await page.getByText("Create this computer").click(); 14 | await expect( 15 | page.getByText("Done ! Computer CommitQuality has been created") 16 | ).toBeVisible(); 17 | }); 18 | 19 | test("basic test with POM", async ({ page }) => { 20 | const computersPage = new ComputersPage(page); 21 | const addComputerPage = new AddComputerPage(page); 22 | await computersPage.goto(); 23 | await computersPage.clickAddNewComputer(); 24 | 25 | await addComputerPage.addNewComputer(); 26 | 27 | await computersPage.assertNewComputerAdded(); 28 | }); 29 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import type { PlaywrightTestConfig } from "@playwright/test"; 2 | import { devices } from "@playwright/test"; 3 | 4 | /** 5 | * Read environment variables from file. 6 | * https://github.com/motdotla/dotenv 7 | */ 8 | // require('dotenv').config(); 9 | 10 | /** 11 | * See https://playwright.dev/docs/test-configuration. 12 | */ 13 | const config: PlaywrightTestConfig = { 14 | testDir: "./tests", 15 | /* Maximum time one test can run for. */ 16 | timeout: 30 * 1000, 17 | expect: { 18 | /** 19 | * Maximum time expect() should wait for the condition to be met. 20 | * For example in `await expect(locator).toHaveText();` 21 | */ 22 | timeout: 5000, 23 | }, 24 | /* Run tests in files in parallel */ 25 | fullyParallel: true, 26 | /* Fail the build on CI if you accidentally left test.only in the source code. */ 27 | forbidOnly: !!process.env.CI, 28 | /* Retry on CI only */ 29 | retries: process.env.CI ? 2 : 0, 30 | /* Opt out of parallel tests on CI. */ 31 | workers: process.env.CI ? 1 : undefined, 32 | /* Reporter to use. See https://playwright.dev/docs/test-reporters */ 33 | reporter: "html", 34 | /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ 35 | use: { 36 | /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ 37 | actionTimeout: 0, 38 | /* Base URL to use in actions like `await page.goto('/')`. */ 39 | // baseURL: 'http://localhost:3000', 40 | 41 | /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ 42 | trace: "on-first-retry", 43 | }, 44 | 45 | /* Configure projects for major browsers */ 46 | projects: [ 47 | { 48 | name: "chromium", 49 | }, 50 | 51 | // { 52 | // name: 'firefox', 53 | // use: { 54 | // ...devices['Desktop Firefox'], 55 | // }, 56 | // }, 57 | 58 | // { 59 | // name: 'webkit', 60 | // use: { 61 | // ...devices['Desktop Safari'], 62 | // }, 63 | // }, 64 | 65 | /* Test against mobile viewports. */ 66 | // { 67 | // name: 'Mobile Chrome', 68 | // use: { 69 | // ...devices['Pixel 5'], 70 | // }, 71 | // }, 72 | // { 73 | // name: 'Mobile Safari', 74 | // use: { 75 | // ...devices['iPhone 12'], 76 | // }, 77 | // }, 78 | 79 | /* Test against branded browsers. */ 80 | // { 81 | // name: 'Microsoft Edge', 82 | // use: { 83 | // channel: 'msedge', 84 | // }, 85 | // }, 86 | // { 87 | // name: 'Google Chrome', 88 | // use: { 89 | // channel: 'chrome', 90 | // }, 91 | // }, 92 | ], 93 | 94 | /* Folder for test artifacts such as screenshots, videos, traces, etc. */ 95 | // outputDir: 'test-results/', 96 | 97 | /* Run your local dev server before starting the tests */ 98 | // webServer: { 99 | // command: 'npm run start', 100 | // port: 3000, 101 | // }, 102 | }; 103 | 104 | export default config; 105 | --------------------------------------------------------------------------------