├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── data
└── login.cred.json
├── jest-playwright.config.js
├── jest.config.js
├── package-lock.json
├── package.json
├── page
├── Header.page.ts
├── Login.page.ts
└── common.page.ts
├── playwright-ws.code-workspace
├── report.bat
├── test
├── alerts
│ └── dialogs.test.ts
├── dragdrop
│ └── drag.test.ts
├── elements
│ └── search.test.ts
├── eval
│ └── changeIMage.test.ts
├── frames
│ └── frame.test.ts
├── inputs
│ └── inputs.test.ts
├── launchBrowser.test.ts
├── login.test.ts
├── mutilple-browser
│ └── browser.test.ts
├── pageObjectsTest
│ ├── TC001_Login.test.ts
│ └── TC002.test.ts
├── select
│ └── dropdown.test.ts
├── uploadFile.test.ts
└── windowHandling
│ └── windows.test.ts
├── tsconfig.json
└── utils
├── environment.ts
└── reportUtils.ts
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | videos
9 | allure-results
10 |
11 | # Diagnostic reports (https://nodejs.org/api/report.html)
12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
13 |
14 | # Runtime data
15 | pids
16 | *.pid
17 | *.seed
18 | *.pid.lock
19 |
20 | # Directory for instrumented libs generated by jscoverage/JSCover
21 | lib-cov
22 |
23 | # Coverage directory used by tools like istanbul
24 | coverage
25 | *.lcov
26 |
27 | # nyc test coverage
28 | .nyc_output
29 |
30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
31 | .grunt
32 |
33 | # Bower dependency directory (https://bower.io/)
34 | bower_components
35 |
36 | # node-waf configuration
37 | .lock-wscript
38 |
39 | # Compiled binary addons (https://nodejs.org/api/addons.html)
40 | build/Release
41 |
42 | # Dependency directories
43 | node_modules/
44 | jspm_packages/
45 |
46 | # TypeScript v1 declaration files
47 | typings/
48 |
49 | # TypeScript cache
50 | *.tsbuildinfo
51 |
52 | # Optional npm cache directory
53 | .npm
54 |
55 | # Optional eslint cache
56 | .eslintcache
57 |
58 | # Microbundle cache
59 | .rpt2_cache/
60 | .rts2_cache_cjs/
61 | .rts2_cache_es/
62 | .rts2_cache_umd/
63 |
64 | # Optional REPL history
65 | .node_repl_history
66 |
67 | # Output of 'npm pack'
68 | *.tgz
69 |
70 | # Yarn Integrity file
71 | .yarn-integrity
72 |
73 | # dotenv environment variables file
74 | .env
75 | .env.test
76 |
77 | # parcel-bundler cache (https://parceljs.org/)
78 | .cache
79 |
80 | # Next.js build output
81 | .next
82 |
83 | # Nuxt.js build / generate output
84 | .nuxt
85 | dist
86 |
87 | # Gatsby files
88 | .cache/
89 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
90 | # https://nextjs.org/blog/next-9-1#public-directory-support
91 | # public
92 |
93 | # vuepress build output
94 | .vuepress/dist
95 |
96 | # Serverless directories
97 | .serverless/
98 |
99 | # FuseBox cache
100 | .fusebox/
101 |
102 | # DynamoDB Local files
103 | .dynamodb/
104 |
105 | # TernJS port file
106 | .tern-port
107 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Koushik
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Playwright-TypeScript-Jest
2 | ## What is Playwright?
3 | The playwright is a Node.js library to automate Chromium, Firefox, and WebKit with a single API. Playwright is built to enable cross-browser web testing.
4 |
5 | Playwright by Microsoft did start as a fork of Puppeteer
6 | Puppeteer is a node library to automate the chromium browsers with the JavaScript API
7 | ### Capabilities:
8 | * It spans multiple pages, domains, and iframes
9 | * Intercept network activity for stubbing and mocking network requests
10 | * Emulate mobile devices, geolocation, permissions
11 | * Native input events for mouse and keyboard
12 | * Upload & download support
13 |
14 | Playwright enables fast, reliable, and capable automation across all modern browsers
15 |
16 | ### Support for all browsers
17 | * Test on Chromium, Firefox, and WebKit
18 | * Test for mobile (device emulation)
19 | * Headless and headful
20 |
21 | ### Fast and reliable execution
22 | * Auto-wait APIs (clicks, types, etc)
23 | * Timeout-free automation
24 | * Lean parallelization with browser contexts
25 | * Wide variety of selectors (locators) & shadow-dom support
26 | * Can handle single page application
27 |
28 | ## Youtube Tutorial
29 | * Please follow the sequence to enhance your learning!
30 |
31 | [1. What is Playwright? | Playwright with Typescript & Jest - Part 1](https://youtu.be/zY-IoTYcbWs)
32 |
33 | [2. Playwright Jest Config & Launch Browser | Playwright - Part 2](https://youtu.be/DbdqflN3dJ4)
34 |
35 | [3. First Script - Auto Waits | Playwright - Part 3](https://youtu.be/9xEzNdG4XaQ)
36 |
37 | [4. Codeless Automation With PlayWright | Playwright - Part 4](https://youtu.be/gb43GiWwQKg)
38 |
39 | [5. Save Test Execution In Video | Playwright - Part 5](https://youtu.be/0125rwgsBP8)
40 |
41 | [6. How to upload files | Playwright - Part 6](https://youtu.be/e8jfjV71E6Q)
42 |
43 | [7. Handling different types of inputs | Playwright - Part 7](https://youtu.be/Slv5fuTrIZg)
44 |
45 | [8. Handling different types of alerts | Playwright - Part 8](https://youtu.be/RzBlwacFIl0)
46 |
47 | [9. Handling Select/DropDown | Playwright - Part 9](https://youtu.be/IubdSQFOdiU)
48 |
49 | [10. Window Handling | Playwright - Part 10](https://youtu.be/DyHQ3G442jY)
50 |
51 | [11. Frames | Playwright - Part 11](https://youtu.be/Vqm-8G81W8w)
52 |
53 | [12. Find Multiple Elements - part 12](https://youtu.be/54OwsiRa_eE)
54 |
55 | [13. How To Take Screenshot - part 13](https://youtu.be/G650JxukN1A)
56 |
57 | [14. How To Run In Local Browser | Playwright - Part 14](https://youtu.be/5LrRFHI81o4)
58 |
59 | [15. Drag and Drop | Playwright - Part 15](https://youtu.be/0wFkhkdcT8A)
60 |
61 | [16. Page Object Model | Playwright - Part 16](https://youtu.be/WSd6-X-n6P8)
62 |
63 | [17. POM Enhancement & JSON | Playwright - Part 17](https://youtu.be/00xGOpnOzds)
64 |
65 | [18. POM Enhancement & JSON | Playwright - Part 18](https://youtu.be/w05KGL8G0f4)
66 |
67 | [19. Jest Allure Report | Playwright - Part 19](https://youtu.be/tjpSkaBq9c0)
68 |
69 | [20. Jest Allure Report | Playwright - Part 20](https://youtu.be/xffrNccLIso)
70 |
71 | [21. Skip on failure | Playwright - Part 21](https://youtu.be/4-IBKtbAxlg)
72 |
--------------------------------------------------------------------------------
/data/login.cred.json:
--------------------------------------------------------------------------------
1 | {
2 | "email": "koushik1@letcode.in",
3 | "pass": "Pass123$"
4 | }
--------------------------------------------------------------------------------
/jest-playwright.config.js:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 | module.exports = {
4 | browsers: ["chromium"],
5 | exitOnPageError: false,
6 | launchOptions: {
7 | headless: true
8 | },
9 | contextOptions: {
10 | recordVideo: {
11 | dir: "/videos/"
12 | }
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('@jest/types').Config.InitialOptions} */
2 | module.exports = {
3 | verbose: true,
4 | preset: 'jest-playwright-preset',
5 | transform: {
6 | '^.+\\.ts$': 'ts-jest',
7 | },
8 | testRunner: 'jasmine2',
9 | setupFilesAfterEnv: ["jest-allure/dist/setup"],
10 | testMatch: [
11 | "/test/pageObjectsTest/TC002.test.ts"
12 | ],
13 | bail: true,
14 | bail: 1,
15 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "let-playright",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "jest -i"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "devDependencies": {
13 | "jasmine": "^3.7.0",
14 | "@types/jest": "^26.0.20",
15 | "jasmine-allure-reporter": "^1.0.2",
16 | "jest": "^26.6.3",
17 | "jest-allure": "^0.1.3",
18 | "jest-playwright-preset": "1.7.0",
19 | "playwright": "1.13.0",
20 | "ts-jest": "^26.5.0",
21 | "typescript": "^4.1.3"
22 | },
23 | "dependencies": {
24 | "ts-node": "^10.0.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/page/Header.page.ts:
--------------------------------------------------------------------------------
1 | import { Page } from "playwright";
2 |
3 | export default class HeaderPage {
4 |
5 | private page: Page;
6 |
7 | constructor(page: Page) {
8 | this.page = page;
9 | }
10 |
11 |
12 | // locators
13 |
14 | public get eleLoginBtn() {
15 | const loginBtn = this.page.$("text=Log in");
16 | if (loginBtn != null) {
17 | return loginBtn;
18 | } else throw new Error("No Element")
19 | }
20 |
21 | public get eleSignOutBtn() {
22 | const signoutEle = this.page.$("text=Sign out");
23 | if (signoutEle != null) {
24 | return signoutEle;
25 | } else throw new Error("No Element")
26 | }
27 |
28 | public async clickLoginLink() {
29 | await Promise.all([
30 | this.page.waitForNavigation({
31 | waitUntil: "domcontentloaded"
32 | }),
33 | this.page.click("text=Log in")
34 | ])
35 | // const ele = await this.eleLoginBtn;
36 | // await ele?.click();
37 | }
38 | public async clickSignOutLink() {
39 | const ele = await this.eleSignOutBtn;
40 | await ele?.click();
41 | }
42 | }
--------------------------------------------------------------------------------
/page/Login.page.ts:
--------------------------------------------------------------------------------
1 | import { Page } from "playwright";
2 |
3 | export default class LoginPage {
4 |
5 | private page: Page;
6 | constructor(page: Page) {
7 | this.page = page;
8 | }
9 |
10 | eleEmailTextField = async () => await this.page.$("input[name='email']");
11 |
12 | // public get eleEmailTextField() {
13 | // return this.page.$("input[name='email']")
14 | // // return elename;
15 | // }
16 |
17 | elePassTextField = async () => await this.page.$("input[name='password']");
18 |
19 | public get eleLoginBtn() {
20 | return this.page.$("//button[text()='LOGIN']")
21 | // return elename;
22 | }
23 |
24 | public async enterUserName(name: string) {
25 | const ele = await this.eleEmailTextField();
26 | if (ele != null)
27 | await ele.fill(name);
28 | else throw new Error("No element, hence failed")
29 | }
30 | public async enterUserPassword(pass: string) {
31 | const ele = await this.elePassTextField();
32 | await ele?.fill(pass);
33 | }
34 | public async clickLoginBtn() {
35 | const ele = await this.eleLoginBtn;
36 | await ele?.click();
37 | }
38 |
39 | public async login(username: string, pass: string) {
40 | await this.enterUserName(username);
41 | await this.enterUserPassword(pass);
42 | await this.clickLoginBtn();
43 | }
44 | }
--------------------------------------------------------------------------------
/page/common.page.ts:
--------------------------------------------------------------------------------
1 | import { Page } from "playwright";
2 |
3 | export default class CommonFunctions {
4 |
5 | private page: Page;
6 | constructor(page: Page) {
7 | this.page = page;
8 | }
9 |
10 | toaster = async () => await this.page.waitForSelector("div[role='alertdialog']");
11 |
12 | // public async verifToastMessage() {
13 |
14 | // }
15 | }
--------------------------------------------------------------------------------
/playwright-ws.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "."
5 | }
6 | ],
7 | "settings": {}
8 | }
--------------------------------------------------------------------------------
/report.bat:
--------------------------------------------------------------------------------
1 | allure serve
--------------------------------------------------------------------------------
/test/alerts/dialogs.test.ts:
--------------------------------------------------------------------------------
1 | import { Browser, BrowserContext, chromium, ElementHandle, Page } from "playwright"
2 |
3 | describe("Learn how to handle alert", () => {
4 |
5 | let browser: Browser;
6 | let context: BrowserContext;
7 | let page: Page;
8 | beforeAll(async () => {
9 | browser = await chromium.launch({
10 | headless: false
11 | });
12 | context = await browser.newContext()
13 | page = await context.newPage();
14 | await page.goto("https://letcode.in/alert")
15 | })
16 | test("Handle dialogs", async () => {
17 | const ele = await page.$("#prompt");
18 | page.on("dialog", (dialog) => {
19 | console.log('Message: ' + dialog.message());
20 | console.log('Default Value: ' + dialog.defaultValue());
21 | console.log('Type: ' + dialog.type());
22 | dialog.accept("hello koushik");
23 | // dialog.dismiss()
24 | })
25 | await ele?.click();
26 | })
27 |
28 | afterAll(async () => {
29 | await page.close()
30 | await context.close()
31 | await browser.close()
32 | })
33 | })
--------------------------------------------------------------------------------
/test/dragdrop/drag.test.ts:
--------------------------------------------------------------------------------
1 | import { Browser, BrowserContext, Page, chromium, Frame } from "playwright";
2 |
3 | describe("Drag and Drop", () => {
4 | let browser: Browser;
5 | let context: BrowserContext;
6 | let page: Page;
7 | beforeAll(async () => {
8 | browser = await chromium.launch({
9 | headless: false
10 | });
11 | context = await browser.newContext()
12 | page = await context.newPage();
13 | })
14 | xtest("my test", async () => {
15 | await page.goto("https://letcode.in/dropable")
16 | const src = await page.$("#draggable")
17 | const dst = await page.$("#droppable");
18 | if (src && dst) {
19 | const srcBound = await src.boundingBox()
20 | const dstBound = await dst.boundingBox()
21 | if (srcBound && dstBound) {
22 | await page.mouse.move(srcBound.x + srcBound.width / 2, srcBound.y + srcBound.height / 2)
23 | await page.mouse.down();
24 | await page.mouse.move(dstBound.x + dstBound.width / 2, dstBound.y + dstBound.height / 2)
25 | await page.mouse.down();
26 | } else {
27 | throw new Error("No Element")
28 | }
29 | }
30 |
31 | })
32 |
33 | test("my test", async () => {
34 | await page.goto("https://jqueryui.com/droppable/")
35 | // switch to frame
36 | const frame = page.frame({ url: "https://jqueryui.com/resources/demos/droppable/default.html" })
37 | if (frame) {
38 | const src = await frame.$("#draggable")
39 | const dst = await frame.$("#droppable");
40 | if (src && dst) {
41 | const srcBound = await src.boundingBox()
42 | const dstBound = await dst.boundingBox()
43 | if (srcBound && dstBound) {
44 | await page.mouse.move(srcBound.x + srcBound.width / 2, srcBound.y + srcBound.height / 2)
45 | await page.mouse.down();
46 | await page.mouse.move(dstBound.x + dstBound.width / 2, dstBound.y + dstBound.height / 2)
47 | await page.mouse.down();
48 | } else {
49 | throw new Error("No Element")
50 | }
51 | }
52 | }
53 | })
54 |
55 | })
--------------------------------------------------------------------------------
/test/elements/search.test.ts:
--------------------------------------------------------------------------------
1 | import { Browser, BrowserContext, Page, chromium } from "playwright";
2 |
3 | describe("Search Git Repo", () => {
4 | let browser: Browser;
5 | let context: BrowserContext;
6 | let page: Page;
7 | beforeAll(async () => {
8 | browser = await chromium.launch({
9 | headless: false
10 | });
11 | context = await browser.newContext()
12 | page = await context.newPage();
13 | await page.goto("https://letcode.in/elements")
14 | })
15 |
16 | test("Enter Git username", async () => {
17 |
18 | const header = await page.$("nav[role='navigation']")
19 | header?.screenshot({ path: "header.png" })
20 | const ele = await page.$("input[name='username']")
21 | await ele?.fill("ortonikc");
22 | await ele?.press("Enter");
23 | })
24 | test("Print all the repos", async () => {
25 | await page.waitForSelector("app-gitrepos ol li", { timeout: 5000 })
26 | const repos = await page.$$("app-gitrepos ol li");
27 | console.log(repos.length);
28 | // for await
29 | // for await (const repo of repos) {
30 | // console.log(await repo.innerText());
31 | // }
32 | // map
33 | const allUrls = await Promise.all(repos.map(async (repo, i) => {
34 | return await repo.innerText()
35 | }))
36 | console.log(allUrls);
37 | await page.screenshot({ path: "fs.png", fullPage: true })
38 | })
39 | afterEach(async () => {
40 | await page.screenshot({ path: Date.now() + 'screenshot1.png' })
41 | })
42 | afterAll(async () => {
43 | await page.close();
44 | await context.close();
45 | await browser.close()
46 | })
47 | })
--------------------------------------------------------------------------------
/test/eval/changeIMage.test.ts:
--------------------------------------------------------------------------------
1 | import { Browser, BrowserContext, chromium, ElementHandle, Page } from "playwright"
2 |
3 | describe("Learn how to handle input fields", () => {
4 |
5 | let browser: Browser;
6 | let context: BrowserContext;
7 | let page: Page;
8 | beforeAll(async () => {
9 | browser = await chromium.launch({
10 | headless: false
11 | });
12 | context = await browser.newContext()
13 | page = await context.newPage();
14 | await page.goto("https://letcode.in/")
15 | })
16 | test("change image", async () => {
17 | const ele = await page.$("img")
18 | ele?.evaluate(ele => ele.src = "https://avatars.githubusercontent.com/u/58769833?v=4")
19 | // console.log(text);
20 | })
21 | })
--------------------------------------------------------------------------------
/test/frames/frame.test.ts:
--------------------------------------------------------------------------------
1 | import { Browser, BrowserContext, Page, chromium } from "playwright";
2 |
3 | describe("Frames handling concept", () => {
4 |
5 | let browser: Browser;
6 | let context: BrowserContext;
7 | let page: Page;
8 | beforeAll(async () => {
9 | browser = await chromium.launch({
10 | headless: false
11 | });
12 | context = await browser.newContext()
13 | page = await context.newPage();
14 | await page.goto("https://letcode.in/frame")
15 | })
16 | test("Interact with frames", async () => {
17 | const frame = page.frame({ name: "firstFr" });
18 | // frame?.fill("")
19 | if (frame != null) {
20 | await frame.fill("input[name='fname']", "Koushik");
21 | await frame.fill("input[name='lname']", "Chatterjee");
22 |
23 | // inner frame
24 | const frames = frame.childFrames();
25 | console.log('No. of inner frames: ' + frames.length);
26 | if (frames != null)
27 | await frames[0].fill("input[name='email']", "koushik@mail.com")
28 | else {
29 | console.log("Wrong frame");
30 | }
31 | const parent = frames[0].parentFrame()
32 | // await frame.fill("input[name='lname']", "Letcode");
33 | await parent?.fill("input[name='lname']", "Youtube");
34 | } else throw new Error("No such frame")
35 | })
36 | })
--------------------------------------------------------------------------------
/test/inputs/inputs.test.ts:
--------------------------------------------------------------------------------
1 | import { Browser, BrowserContext, chromium, ElementHandle, Page } from "playwright"
2 |
3 | describe("Learn how to handle input fields", () => {
4 |
5 | let browser: Browser;
6 | let context: BrowserContext;
7 | let page: Page;
8 | beforeAll(async () => {
9 | browser = await chromium.launch({
10 | headless: false
11 | });
12 | context = await browser.newContext()
13 | page = await context.newPage();
14 | await page.goto("https://letcode.in/edit")
15 | })
16 |
17 | test("Enter your full Name", async () => {
18 | // await page.type("id=fullName", "Koushik Chatterjee")
19 | const name = await page.$("#fullName")
20 | // if (name != null) {
21 | // name.type("")
22 | // }
23 | await name?.type("koushik chatterjee")
24 | })
25 | test("Append a text and press keyboard tab", async () => {
26 | const join = await page.$("#join")
27 | await join?.focus();
28 | await page.keyboard.press("End")
29 | await join?.type(" Human")
30 | // await join?.fill(" Human")
31 | })
32 | test("What is inside the text box", async () => {
33 | const text = await page.getAttribute("id=getMe", "value");
34 | console.log(text);
35 | })
36 | test("Clear the text", async () => {
37 | await page.fill("//input[@value='Koushik Chatterjee']", "")
38 | })
39 | afterAll(async () => {
40 | await page.close()
41 | await context.close()
42 | await browser.close()
43 | })
44 | })
--------------------------------------------------------------------------------
/test/launchBrowser.test.ts:
--------------------------------------------------------------------------------
1 | import { chromium } from "playwright";
2 | describe('Launch Browser', () => {
3 |
4 | test('Open letcode', async () => {
5 | const browser = await chromium.launch({
6 | headless: false
7 | })
8 | const context = await browser.newContext();
9 | const page = await context.newPage();
10 | await page.goto('https://letcode.in/')
11 | await browser.close()
12 | })
13 |
14 | })
--------------------------------------------------------------------------------
/test/login.test.ts:
--------------------------------------------------------------------------------
1 | import { chromium } from "playwright";
2 | describe('Launch Browser', () => {
3 |
4 | test('Recorded script', async () => {
5 | const browser = await chromium.launch({
6 | headless: false
7 | });
8 | const context = await browser.newContext({
9 | recordVideo: {
10 | dir: "./videos/",
11 | size: {
12 | width: 800,
13 | height: 600
14 | }
15 | }
16 | });
17 |
18 | // Open new page
19 | const page = await context.newPage();
20 |
21 | // Go to https://letcode.in/
22 | await page.goto('https://letcode.in/');
23 |
24 | // Click text=/.*Log in.*/
25 | await Promise.all([
26 | page.waitForNavigation(/*{ url: 'https://letcode.in/signin' }*/),
27 | page.click('text=/.*Log in.*/')
28 | ]);
29 |
30 | // Click input[name="email"]
31 | await page.click('input[name="email"]');
32 |
33 | // Fill input[name="email"]
34 | await page.fill('input[name="email"]', 'koushik350@gmail.com');
35 |
36 | // Press Tab
37 | await page.press('input[name="email"]', 'Tab');
38 |
39 | // Fill input[name="password"]
40 | await page.fill('input[name="password"]', 'Pass123$');
41 |
42 | // Click //button[normalize-space(.)='LOGIN']
43 | await Promise.all([
44 | page.waitForNavigation(/*{ url: 'https://letcode.in/' }*/),
45 | page.click('//button[normalize-space(.)=\'LOGIN\']')
46 | ]);
47 |
48 | // Click text="Let's Go"
49 | await page.click('text="Let\'s Go"');
50 | // assert.equal(page.url(), 'https://letcode.in/test');
51 |
52 | // Click //div[5]/div/div[3]/div/div[3]/button[normalize-space(.)='Get me in!']
53 | await page.click('//div[5]/div/div[3]/div/div[3]/button[normalize-space(.)=\'Get me in!\']');
54 | // assert.equal(page.url(), 'https://letcode.in/forms');
55 |
56 | // Click input[placeholder="Text input"]
57 | await page.click('input[placeholder="Text input"]');
58 |
59 | // Fill input[placeholder="Text input"]
60 | await page.fill('input[placeholder="Text input"]', 'koushik');
61 |
62 | // Press Tab
63 | await page.press('input[placeholder="Text input"]', 'Tab');
64 |
65 | // Fill //div[normalize-space(.)='Last Name']/div/input[normalize-space(@placeholder)='Text input' and normalize-space(@type)='text']
66 | await page.fill('//div[normalize-space(.)=\'Last Name\']/div/input[normalize-space(@placeholder)=\'Text input\' and normalize-space(@type)=\'text\']', 'chatterjee');
67 |
68 | // Click //div[normalize-space(.)='Email']
69 | await page.click('//div[normalize-space(.)=\'Email\']');
70 |
71 | // Fill input[placeholder="Email input"]
72 | await page.fill('input[placeholder="Email input"]', 'koushik350@gmail.com');
73 |
74 | // Click input[placeholder="Phone Number"]
75 | await page.click('input[placeholder="Phone Number"]');
76 |
77 | // Fill input[placeholder="Phone Number"]
78 | await page.fill('input[placeholder="Phone Number"]', '9999999999');
79 |
80 | // Click input[placeholder="Address Line-1"]
81 | await page.click('input[placeholder="Address Line-1"]');
82 |
83 | // Fill input[placeholder="Address Line-1"]
84 | await page.fill('input[placeholder="Address Line-1"]', 'adyar');
85 |
86 | // Click input[placeholder="Address Line-2"]
87 | await page.click('input[placeholder="Address Line-2"]');
88 |
89 | // Fill input[placeholder="Address Line-2"]
90 | await page.fill('input[placeholder="Address Line-2"]', 'chennai');
91 |
92 | // Click input[placeholder="State"]
93 | await page.click('input[placeholder="State"]');
94 |
95 | // Fill input[placeholder="State"]
96 | await page.fill('input[placeholder="State"]', 'TN');
97 |
98 | // Click input[placeholder="Postal-Code"]
99 | await page.click('input[placeholder="Postal-Code"]');
100 |
101 | // Fill input[placeholder="Postal-Code"]
102 | await page.fill('input[placeholder="Postal-Code"]', '600113');
103 |
104 | // Fill input[name="dob"]
105 | await page.fill('input[name="dob"]', '1994-12-04');
106 |
107 | // Check input[type="checkbox"]
108 | await page.check('input[type="checkbox"]');
109 |
110 | // Fill input[name="gender"]
111 | // await page.fill('input[name="gender"]', 'on');
112 |
113 | // Click text=/.*Male.*/
114 | await page.click('text=/.*Male.*/');
115 |
116 | // Click input[type="submit"]
117 | await Promise.all([
118 | page.waitForNavigation(/*{ url: 'https://letcode.in/forms' }*/),
119 | page.click('input[type="submit"]')
120 | ]);
121 |
122 | // Click text=/.*Sign out.*/
123 | await page.click('text=/.*Sign out.*/');
124 | // assert.equal(page.url(), 'https://letcode.in/');
125 |
126 | // Close page
127 | await page.close();
128 |
129 | // ---------------------
130 | await context.close();
131 | await browser.close();
132 | })
133 |
134 |
135 | xtest('Open letcode', async () => {
136 | const browser = await chromium.launch({
137 | headless: false
138 | })
139 | const context = await browser.newContext();
140 | const page = await context.newPage();
141 | await page.goto('https://letcode.in/');
142 | await page.click("text=Log in");
143 | await page.fill("input[name='email']", 'koushik350@gmail.com');
144 | await page.fill("input[name='password']", 'Pass123$');
145 | await page.click('button:text("LOGIN")')
146 | await page.click('"Sign out"');
147 | await browser.close()
148 | })
149 |
150 | })
--------------------------------------------------------------------------------
/test/mutilple-browser/browser.test.ts:
--------------------------------------------------------------------------------
1 | import { Browser, BrowserContext, Page, chromium, firefox } from "playwright";
2 |
3 | describe("Launch local browser", () => {
4 |
5 | let browser: Browser;
6 | let context: BrowserContext;
7 | let page: Page;
8 | beforeAll(async () => {
9 | browser = await firefox.launch({
10 | headless: false,
11 | // channel: "msedge"
12 | // executablePath: "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"
13 | });
14 | context = await browser.newContext()
15 | page = await context.newPage();
16 | await page.goto("https://letcode.in/")
17 | })
18 | test("Goto letcode and verify title as LetCode with Koushik", async () => {
19 | const title = await page.title();
20 | console.log(title);
21 | expect(title).toBe("LetCode with Koushik")
22 | })
23 | })
--------------------------------------------------------------------------------
/test/pageObjectsTest/TC001_Login.test.ts:
--------------------------------------------------------------------------------
1 | import CommonFunctions from "../../page/common.page";
2 | import HeaderPage from "../../page/Header.page";
3 | import LoginPage from "../../page/Login.page";
4 | import Env from "../../utils/environment";
5 | import * as data from "../../data/login.cred.json";
6 | import { Page } from "playwright";
7 | import ReportUtils from "../../utils/reportUtils";
8 |
9 | declare const page: Page;
10 | declare const reporter: any;
11 |
12 | describe("TC001", () => {
13 | // my pages
14 | let header: HeaderPage;
15 | let login: LoginPage;
16 | let common: CommonFunctions;
17 |
18 | beforeAll(async () => {
19 | await page.goto(Env.test);
20 | header = new HeaderPage(page);
21 | login = new LoginPage(page);
22 | common = new CommonFunctions(page);
23 | })
24 |
25 | test("Login positive _ JIRA101", async () => {
26 | await reporter
27 | .description("Login into LetCode")
28 | .story("JIRA101");
29 | await reporter.startStep("Navigate to letcode");
30 | expect(page.url()).toBe("https://letcode.com/")
31 | await ReportUtils.screenshot("naviagation")
32 | await reporter.endStep();
33 | await reporter.startStep("Click login link");
34 | await header.clickLoginLink();
35 | await reporter.endStep();
36 | expect(page.url()).toBe("https://letcode.in/signin")
37 | await reporter.startStep("enter username");
38 | await login.enterUserName(data.email);
39 | await ReportUtils.screenshot("username")
40 | // const screenshotBuffer = await page.screenshot();
41 | // await reporter.addAttachment("username", screenshotBuffer, "image/png");
42 | await reporter.endStep();
43 | await reporter.startStep("enter password");
44 | await login.enterUserPassword(data.pass);
45 | await ReportUtils.screenshot();
46 | await reporter.endStep();
47 | await login.clickLoginBtn();
48 | const toaster = await common.toaster();
49 | expect(await toaster?.textContent()).toContain("Welcome");
50 | await reporter.startStep("Log out");
51 | await header.clickSignOutLink();
52 | await ReportUtils.screenshot("done")
53 | await reporter.endStep();
54 |
55 | });
56 | test("Login again", async () => {
57 | await page.goto(Env.test, {
58 | waitUntil: "domcontentloaded"
59 | });
60 | await header.clickLoginLink();
61 | await login.login("koushik350@gmail.com", "Pass123$");
62 | await page.waitForNavigation();
63 | expect(page.url()).toBe("https://letcode.in/")
64 | await ReportUtils.screenshot("done")
65 | })
66 | })
--------------------------------------------------------------------------------
/test/pageObjectsTest/TC002.test.ts:
--------------------------------------------------------------------------------
1 | import CommonFunctions from "../../page/common.page";
2 | import HeaderPage from "../../page/Header.page";
3 | import LoginPage from "../../page/Login.page";
4 | import Env from "../../utils/environment";
5 | import * as data from "../../data/login.cred.json";
6 | import { Page } from "playwright";
7 | import ReportUtils from "../../utils/reportUtils";
8 |
9 | declare const page: Page;
10 | declare const reporter: any;
11 |
12 | describe("TC002", () => {
13 | // my pages
14 | let header: HeaderPage;
15 | let login: LoginPage;
16 | let common: CommonFunctions;
17 |
18 | beforeAll(async () => {
19 | await page.goto(Env.test);
20 | header = new HeaderPage(page);
21 | login = new LoginPage(page);
22 | common = new CommonFunctions(page);
23 | })
24 |
25 | afterAll(async () => {
26 | await page.close();
27 | await context.close();
28 | await browser.close();
29 | })
30 |
31 | test("TC002 Login 1", async () => {
32 | await reporter
33 | .description("Login into LetCode")
34 | .story("JIRA101");
35 | await reporter.startStep("Navigate to letcode");
36 | expect(page.url()).toBe("https://letcode.in/")
37 | await ReportUtils.screenshot("naviagation")
38 | await reporter.endStep();
39 | await reporter.startStep("Click login link");
40 | await header.clickLoginLink();
41 | await reporter.endStep();
42 | expect(page.url()).toBe("https://letcode.in/signin")
43 | await reporter.startStep("enter username");
44 | await login.enterUserName(data.email);
45 | await ReportUtils.screenshot("username")
46 | // const screenshotBuffer = await page.screenshot();
47 | // await reporter.addAttachment("username", screenshotBuffer, "image/png");
48 | await reporter.endStep();
49 | await reporter.startStep("enter password");
50 | await login.enterUserPassword(data.pass);
51 | await ReportUtils.screenshot();
52 | await reporter.endStep();
53 | await login.clickLoginBtn();
54 | const toaster = await common.toaster();
55 | expect(await toaster?.textContent()).toContain("Welcome");
56 | await reporter.startStep("Log out");
57 | await header.clickSignOutLink();
58 | await ReportUtils.screenshot("done")
59 | await reporter.endStep();
60 |
61 | });
62 | // test("TC002 Login 2", async () => {
63 | // await page.goto(Env.test, {
64 | // waitUntil: "domcontentloaded"
65 | // });
66 | // await header.clickLoginLink();
67 | // await login.login("koushik350@gmail.com", "Pass123$");
68 | // await page.waitForNavigation();
69 | // expect(page.url()).toBe("https://letcode.in/")
70 | // await ReportUtils.screenshot("done")
71 | // })
72 | })
--------------------------------------------------------------------------------
/test/select/dropdown.test.ts:
--------------------------------------------------------------------------------
1 | import { Browser, BrowserContext, Page, chromium } from "playwright";
2 |
3 | describe("How to handle Select", () => {
4 |
5 | let browser: Browser;
6 | let context: BrowserContext;
7 | let page: Page;
8 | beforeAll(async () => {
9 | browser = await chromium.launch({
10 | headless: false
11 | });
12 | context = await browser.newContext()
13 | page = await context.newPage();
14 | await page.goto("https://letcode.in/dropdowns")
15 | })
16 | test("Select a dropdown based on value", async () => {
17 | const fruits = await page.$("#fruits");
18 | // await fruits?.selectOption({ label: "" });
19 | await fruits?.selectOption("2");
20 | const msg = await page.$("div.notification.is-success");
21 | if (msg) {
22 | // expect(await msg.textContent()).toContain("Apple");
23 | }
24 | })
25 | test("Select multiple", async () => {
26 | const heros = await page.$("#superheros");
27 | heros?.selectOption([
28 | { label: "Aquaman" }, { value: "bt" }, { index: 8 }
29 | ])
30 | })
31 | test("Count of the select", async () => {
32 | const lang = await page.$$("#lang option")
33 | console.log(lang.length);
34 | })
35 | test("get selected text", async () => {
36 | await page.selectOption("#country", "India");
37 | const text = await page.$eval("#country", ele => ele.value)
38 | console.log(text);
39 | expect(text).toBe("India");
40 | })
41 |
42 | afterAll(async () => {
43 | await page.close()
44 | await context.close()
45 | await browser.close()
46 | })
47 | })
--------------------------------------------------------------------------------
/test/uploadFile.test.ts:
--------------------------------------------------------------------------------
1 | import { chromium } from 'playwright';
2 |
3 |
4 | describe('Upload file', () => {
5 |
6 | const filePath0 = '../videos/a.webm';
7 | const filePath1 = '../videos/b.webm';
8 | xtest("upload file using set input files", async () => {
9 |
10 | const browser = await chromium.launch({
11 | headless: false
12 | })
13 | const context = await browser.newContext();
14 | const page = await context.newPage();
15 | await page.goto('https://www.sendgb.com/');
16 | await page.setInputFiles("input[name='qqfile']", [filePath0, filePath1]);
17 | })
18 |
19 | test("Upload using on function", async () => {
20 | const browser = await chromium.launch({
21 | headless: false
22 | })
23 | const context = await browser.newContext();
24 | const page = await context.newPage();
25 | await page.goto('https://the-internet.herokuapp.com/upload');
26 | page.on("filechooser", async (filechooser) => {
27 | // filechooser.isMultiple();
28 | await filechooser.setFiles([filePath0, filePath1])
29 | })
30 | await page.click(".example + div#drag-drop-upload", { force: true })
31 | })
32 |
33 | })
--------------------------------------------------------------------------------
/test/windowHandling/windows.test.ts:
--------------------------------------------------------------------------------
1 | import { Browser, BrowserContext, Page, chromium } from "playwright";
2 |
3 | describe("Window handling", () => {
4 |
5 | let browser: Browser;
6 | let context: BrowserContext;
7 | let page: Page;
8 | beforeAll(async () => {
9 | browser = await chromium.launch({
10 | headless: false
11 | });
12 | context = await browser.newContext()
13 | page = await context.newPage();
14 | await page.goto("https://letcode.in/windows")
15 | })
16 |
17 | test("Home Page", async () => {
18 | console.log(await page.title());
19 | expect(await page.title()).toBe("Window handling - LetCode");
20 | })
21 |
22 | xtest("Single page handling", async () => {
23 | const [newWindow] = await Promise.all([
24 | context.waitForEvent("page"),
25 | await page.click("#home")
26 | ])
27 | await newWindow.waitForLoadState();
28 | expect(newWindow.url()).toContain("test");
29 | await newWindow.click('"Log in"');
30 | await newWindow.waitForNavigation();
31 | expect(newWindow.url()).toContain("signin");
32 | // await newWindow.close();
33 | await page.bringToFront();
34 | await page.click('"LetXPath"');
35 | })
36 | test("Multipage handling", async () => {
37 | const [multipage] = await Promise.all([
38 | context.waitForEvent("page"),
39 | await page.click("#multi")
40 | ])
41 | await multipage.waitForLoadState();
42 | const allwindows = page.context().pages();
43 | console.log("no.of windows: " + allwindows.length);
44 | allwindows.forEach(page => {
45 | console.log(page.url());
46 | });
47 | await allwindows[1].bringToFront();
48 | allwindows[1].on("dialog", (dialog) => {
49 | console.log('Message: ' + dialog.message());
50 | dialog.accept();
51 | })
52 | await allwindows[1].click("id=accept")
53 |
54 | })
55 | afterAll(async () => {
56 | await page.close()
57 | await context.close()
58 | await browser.close()
59 | })
60 | })
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "strict": true,
5 | "module": "commonjs",
6 | "sourceMap": true,
7 | "resolveJsonModule": true,
8 | "types": [
9 | "@types/jest",
10 | "jest-playwright-preset",
11 | "expect-playwright",
12 | ]
13 | }
14 | }
--------------------------------------------------------------------------------
/utils/environment.ts:
--------------------------------------------------------------------------------
1 | export default class Env {
2 | public static test: string = "https://letcode.in/";
3 | public static integration: string = "https://int.letcode.in/";
4 | public static production: string = "https://prod.letcode.in/";
5 | public static staging: string = "https://stg.letcode.in/";
6 | }
--------------------------------------------------------------------------------
/utils/reportUtils.ts:
--------------------------------------------------------------------------------
1 | declare const reporter: any;
2 | export default class ReportUtils {
3 |
4 |
5 | public static async screenshot(description?: string) {
6 | const screenshotBuffer = await page.screenshot();
7 | description = description != undefined ? description : "screenshot";
8 | await reporter.addAttachment(description, screenshotBuffer, "image/png");
9 | }
10 |
11 | }
--------------------------------------------------------------------------------