Skip to content

Commit 9fc77f9

Browse files
committed
Add new test files and update configuration
1 parent 741efbc commit 9fc77f9

8 files changed

+369
-0
lines changed

playwright.config.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { defineConfig, devices } from '@playwright/test';
2+
3+
export default defineConfig({
4+
testDir: './tests', // Folder containing test files
5+
testMatch: '**/*.spec.{ts,js}', // Match all test files with .spec.ts or .spec.js extensions
6+
timeout: 30000, // Maximum time for each test
7+
expect: {
8+
timeout: 5000, // Expiration of expectations
9+
},
10+
fullyParallel: true,
11+
retries: 1, // If the test fails, try 1 more time
12+
reporter: 'html',
13+
use: {
14+
headless: true,
15+
screenshot: 'only-on-failure',
16+
trace: 'on-first-retry',
17+
},
18+
projects: [
19+
{
20+
name: 'chromium',
21+
use: { ...devices['Desktop Chrome'] },
22+
},
23+
/* {
24+
name: 'firefox',
25+
use: { ...devices['Desktop Firefox'] },
26+
},
27+
{
28+
name: 'webkit',
29+
use: { ...devices['Desktop Safari'] },
30+
}, */
31+
],
32+
});

tests/training/05-assertions.spec.ts

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import { test, expect } from "@playwright/test";
2+
3+
test.describe("OB-04. Assertions", { tag: ["@onboarding"] }, () => {
4+
// TODO:
5+
// - [ ] Read the documentation on https://playwright.dev/docs/test-assertions
6+
//
7+
// - [ ] Goto the URL https://github.com
8+
// Write tests for AT LEAST 10 of the assertions listed below
9+
// Use different values from the documentation to test the assertions
10+
//
11+
// AUTO RETRYING ASSERTIONS
12+
// await expect(locator).toBeAttached() Element is attached
13+
14+
const url = "https://github.com";
15+
16+
test.beforeEach(async ({ page }) => {
17+
await page.goto(url);
18+
});
19+
20+
test("TC-01: should be attached", async ({ page }) => {
21+
const logo = page.locator('a[href="/"]').first();
22+
await expect(logo).toBeAttached();
23+
});
24+
25+
test("TC-02: should be visible", async ({ page }) => {
26+
const signInButton = page.getByRole("link", { name: "Sign in" });
27+
await expect(signInButton).toBeVisible();
28+
});
29+
30+
test("TC-03: should have correct title", async ({ page }) => {
31+
await expect(page).toHaveTitle(/GitHub/);
32+
});
33+
34+
test("TC-04: should have correct URL", async ({ page }) => {
35+
await expect(page).toHaveURL(url);
36+
});
37+
38+
test("TC-05: should contain text", async ({ page }) => {
39+
const heroText = page.getByRole("heading", {
40+
name: /Build and ship software on a single/i,
41+
});
42+
await expect(heroText).toContainText("Build and ship software on a single");
43+
});
44+
45+
test("TC-06: should have attribute", async ({ page }) => {
46+
const signUpButton = page.getByRole("link", { name: "Sign up" });
47+
await expect(signUpButton).toHaveAttribute("href", /\/signup/);
48+
});
49+
50+
test("TC-07: should have class", async ({ page }) => {
51+
const header = page.locator("header").first();
52+
await expect(header).toHaveClass(/header-logged-out/);
53+
});
54+
55+
test("TC-08: should have sign-in button enabled", async ({ page }) => {
56+
const signInButton = page.getByRole("link", { name: "Sign in" });
57+
await expect(signInButton).toBeEnabled();
58+
});
59+
60+
test("TC-09: should have an empty search input in explore page", async ({
61+
page,
62+
}) => {
63+
await page.goto("https://github.com/search");
64+
const searchBox = page.getByRole("textbox", { name: "Search GitHub" });
65+
const isSearchBoxVisible = await searchBox.isVisible();
66+
await expect(searchBox).toBeEmpty();
67+
});
68+
69+
test("TC-10: should have accessible name", async ({ page }) => {
70+
const signInButton = page.getByRole("link", { name: "Sign in" });
71+
await expect(signInButton).toHaveAccessibleName("Sign in");
72+
});
73+
74+
test("TC-11: should hide the mobile menu by default", async ({ page }) => {
75+
const mobileMenu = page.locator(".mobile-menu");
76+
await expect(mobileMenu).toBeHidden();
77+
});
78+
79+
test("TC-12: should verify if Sign in link is truthy", async ({ page }) => {
80+
const signInButton = await page
81+
.getByRole("link", { name: "Sign in" })
82+
.getAttribute("href");
83+
expect(signInButton).toBeTruthy();
84+
});
85+
86+
test("TC-13: should match Sign up link with regex", async ({ page }) => {
87+
const signUpButton = await page
88+
.getByRole("link", { name: "Sign up" })
89+
.getAttribute("href");
90+
expect(signUpButton).toMatch(/^\/signup/);
91+
});
92+
93+
// await expect(locator).toBeChecked() Checkbox is checked
94+
// await expect(locator).toBeDisabled() Element is disabled
95+
// await expect(locator).toBeEditable() Element is editable
96+
// await expect(locator).toBeEmpty() Container is empty
97+
// await expect(locator).toBeEnabled() Element is enabled
98+
// await expect(locator).toBeFocused() Element is focused
99+
// await expect(locator).toBeHidden() Element is not visible
100+
// await expect(locator).toBeInViewport() Element intersects viewport
101+
// await expect(locator).toBeVisible() Element is visible
102+
// await expect(locator).toContainText() Element contains text
103+
// await expect(locator).toHaveAccessibleDescription() Element has a matching accessible description
104+
// await expect(locator).toHaveAccessibleName() Element has a matching accessible name
105+
// await expect(locator).toHaveAttribute() Element has a DOM attribute
106+
// await expect(locator).toHaveClass() Element has a class property
107+
// await expect(locator).toHaveCount() List has exact number of children
108+
// await expect(locator).toHaveCSS() Element has CSS property
109+
// await expect(locator).toHaveId() Element has an ID
110+
// await expect(locator).toHaveJSProperty() Element has a JavaScript property
111+
// await expect(locator).toHaveRole() Element has a specific ARIA role
112+
// await expect(locator).toHaveScreenshot() Element has a screenshot
113+
// await expect(locator).toHaveText() Element matches text
114+
// await expect(locator).toHaveValue() Input has a value
115+
// await expect(locator).toHaveValues() Select has options selected
116+
// await expect(page).toHaveScreenshot() Page has a screenshot
117+
// await expect(page).toHaveTitle() Page has a title
118+
// await expect(page).toHaveURL() Page has a URL
119+
// await expect(response).toBeOK() Response has an OK status
120+
//
121+
// NON-RETRYING ASSERTIONS
122+
// expect(value).toBe() Value is the same
123+
// expect(value).toBeCloseTo() Number is approximately equal
124+
// expect(value).toBeDefined() Value is not undefined
125+
// expect(value).toBeFalsy() Value is falsy, e.g. false, 0, null, etc.
126+
// expect(value).toBeGreaterThan() Number is more than
127+
// expect(value).toBeGreaterThanOrEqual() Number is more than or equal
128+
// expect(value).toBeInstanceOf() Object is an instance of a class
129+
// expect(value).toBeLessThan() Number is less than
130+
// expect(value).toBeLessThanOrEqual() Number is less than or equal
131+
// expect(value).toBeNaN() Value is NaN
132+
// expect(value).toBeNull() Value is null
133+
// expect(value).toBeTruthy() Value is truthy, i.e. not false, 0, null, etc.
134+
// expect(value).toBeUndefined() Value is undefined
135+
// expect(value).toContain() String contains a substring
136+
// expect(value).toContain() Array or set contains an element
137+
// expect(value).toContainEqual() Array or set contains a similar element
138+
// expect(value).toEqual() Value is similar - deep equality and pattern matching
139+
// expect(value).toHaveLength() Array or string has length
140+
// expect(value).toHaveProperty() Object has a property
141+
// expect(value).toMatch() String matches a regular expression
142+
// expect(value).toMatchObject() Object contains specified properties
143+
// expect(value).toStrictEqual() Value is similar, including property types
144+
// expect(value).toThrow() Function throws an error
145+
// expect(value).any() Matches any instance of a class/primitive
146+
// expect(value).anything() Matches anything
147+
// expect(value).arrayContaining() Array contains specific elements
148+
// expect(value).closeTo() Number is approximately equal
149+
// expect(value).objectContaining() Object contains specific properties
150+
// expect(value).stringContaining() String contains a substring
151+
// expect(value).stringMatching() String matches a regular expression
152+
});

tests/training/06-github-home.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Page, Locator } from '@playwright/test';
2+
3+
export class GithubHomePage {
4+
private page: Page;
5+
readonly logo: Locator;
6+
readonly signInButton: Locator;
7+
readonly signUpButton: Locator;
8+
readonly heroText: Locator;
9+
readonly header: Locator;
10+
readonly mobileMenu: Locator;
11+
readonly searchBox: Locator;
12+
13+
constructor(page: Page) {
14+
this.page = page;
15+
this.logo = page.locator('a[href="/"]').first();
16+
this.signInButton = page.getByRole('link', { name: 'Sign in' });
17+
this.signUpButton = page.getByRole('link', { name: 'Sign up' });
18+
this.heroText = page.getByRole('heading', {
19+
name: /Build and ship software on a single/i,
20+
});
21+
this.header = page.locator('header').first();
22+
this.mobileMenu = page.locator('.mobile-menu');
23+
this.searchBox = page.getByRole('textbox', { name: 'Search GitHub' });
24+
}
25+
26+
// Navigate to GitHub home page
27+
async goto() {
28+
await this.page.goto('https://github.com');
29+
}
30+
31+
// Navigate to the GitHub search page
32+
async gotoSearchPage() {
33+
await this.page.goto('https://github.com/search');
34+
}
35+
36+
// Navigate to GitHub Pricing page
37+
async goToPricing() {
38+
await this.page.getByLabel('Search or jump to…').click();
39+
await this.page.getByText('Pricing Learn More').click();
40+
}
41+
42+
// Get the text content of the main header
43+
async getHeaderText() {
44+
return await this.page.textContent('h1#hero-section-brand-heading');
45+
}
46+
47+
// Get href attribute of the sign-in button
48+
async getSignInHref() {
49+
return await this.signInButton.getAttribute('href');
50+
}
51+
52+
// Get href attribute of the sign-up button
53+
async getSignUpHref() {
54+
return await this.signUpButton.getAttribute('href');
55+
}
56+
}

tests/training/06-pom.spec.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { test, expect } from '@playwright/test'
2+
// https://playwright.dev/docs/pom
3+
import { GithubHomePage } from './06-github-home';
4+
test.describe('OB-05. POM', { tag: ['@onboarding'] }, () => {
5+
// TODO:
6+
// Create a Page Object Model for the github.com website (04-GithubHome.ts)
7+
// - [ ] Create a page object for the home page
8+
// - [ ] Define some locators and methods for the home page
9+
// - [ ] Write the same test as you did in the previous exercises using the POM
10+
11+
let githubHome: GithubHomePage
12+
13+
test.beforeEach(async ({ page }) => {
14+
githubHome = new GithubHomePage(page)
15+
await githubHome.goto()
16+
})
17+
18+
test('TC-01: Logo should be attached', async () => {
19+
await expect(githubHome.logo).toBeAttached()
20+
})
21+
22+
test('TC-02: Sign in button should be visible', async () => {
23+
await expect(githubHome.signInButton).toBeVisible()
24+
})
25+
26+
test('TC-03: Page should have correct title', async ({ page }) => {
27+
await expect(page).toHaveTitle(/GitHub/)
28+
})
29+
30+
test('TC-04: Page should have correct URL', async ({ page }) => {
31+
await expect(page).toHaveURL('https://github.com')
32+
})
33+
34+
test('TC-05: Hero section should contain text', async () => {
35+
await expect(githubHome.heroText).toContainText(
36+
'Build and ship software on a single',
37+
)
38+
})
39+
40+
test('TC-06: Sign up button should have correct href', async () => {
41+
const signUpHref = await githubHome.getSignUpHref()
42+
expect(signUpHref).toMatch(/^\/signup/)
43+
})
44+
45+
test('TC-07: Header should have correct class', async () => {
46+
await expect(githubHome.header).toHaveClass(/header-logged-out/)
47+
})
48+
49+
test('TC-08: Sign in button should be enabled', async () => {
50+
await expect(githubHome.signInButton).toBeEnabled()
51+
})
52+
53+
test('TC-09: Mobile menu should be hidden by default', async () => {
54+
await expect(githubHome.mobileMenu).toBeHidden()
55+
})
56+
57+
test('TC-10: Search input should be empty in search page', async () => {
58+
await githubHome.gotoSearchPage();
59+
await expect(githubHome.searchBox).toBeEmpty();
60+
});
61+
62+
test('TC-11: Sign in button should have accessible name', async () => {
63+
await expect(githubHome.signInButton).toHaveAccessibleName('Sign in');
64+
});
65+
66+
test('TC-12: Sign in link should be truthy', async () => {
67+
const href = await githubHome.getSignInHref();
68+
expect(href).toBeTruthy();
69+
});
70+
71+
test('TC-13: Sign up link should match regex', async () => {
72+
const href = await githubHome.getSignUpHref();
73+
expect(href).toMatch(/^\/signup/);
74+
});
75+
})

tests/training/07-fixture.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { test as base } from '@playwright/test'
2+
import { GithubHomePage } from './06-github-home'
3+
4+
// TODO: Import GithubHome page object
5+
// Extend basic test by providing a "githubHomePage" fixture.
6+
7+
export const test = base.extend({
8+
githubHomePage: async ({ page }, use) => {
9+
const githubHomePage = new GithubHomePage(page)
10+
await use(githubHomePage)
11+
},
12+
})

tests/training/07-fixtures.spec.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Import the fixture from the 07-fixture file not from @playwright/test
2+
import { test } from './07-fixture'
3+
import { expect } from '@playwright/test'
4+
5+
test.describe('OB-06. Fixtures', { tag: ['@onboarding'] }, () => {
6+
7+
// TODO:
8+
// - [ ] Use the fixture and write the same test as in 03-write-test.spec.ts
9+
10+
// 06-01. Test for Playwright page title
11+
test('should verify page title contains Playwright', async ({ page }) => {
12+
await page.goto('https://playwright.dev')
13+
const title = await page.title()
14+
console.log(title)
15+
expect(title).toContain('Playwright')
16+
})
17+
18+
// 06-02. Group of Tests for GitHub
19+
test.describe('https://github.com', { tag: ['@onboarding'] }, () => {
20+
const url = 'https://github.com'
21+
22+
// GitHub homepage title test
23+
test('should verify page title contains GitHub', async ({ page }) => {
24+
await page.goto(url)
25+
const header = await page.locator('h1#hero-section-brand-heading')
26+
console.log(await header.textContent())
27+
await expect(header).toHaveText(
28+
'Build and ship software on a single, collaborative platform',
29+
)
30+
})
31+
32+
// Going to pricing page test
33+
test('should navigate to the Pricing page and verify the page title', async ({ page }) => {
34+
await page.goto(url)
35+
await page.getByLabel('Search or jump to…').click()
36+
await page.getByText('Pricing Learn More').click()
37+
const title = await page.title()
38+
console.log(title)
39+
expect(title).toContain('Pricing')
40+
})
41+
})
42+
})

0 commit comments

Comments
 (0)