From 4c12123c66443b8f9c12707d5787ee2b0e429fee Mon Sep 17 00:00:00 2001 From: Abhay Bharti Date: Tue, 30 Jan 2024 20:06:37 +0530 Subject: [PATCH] added example test --- README.md | 2 +- src/helper/api/apiHelper.ts | 48 ++++--- ...todo-app.spec.js => demo-todo-app.spec.ts} | 133 ++++++++++++++---- src/tests/web/example/frame.spec.ts | 10 ++ src/tests/web/example/networkTest.spec.ts | 25 ++++ src/tests/web/example/route.spec.ts | 29 ++++ src/tests/web/example/screenshot.spec.ts | 12 ++ 7 files changed, 207 insertions(+), 52 deletions(-) rename src/tests/web/example/{demo-todo-app.spec.js => demo-todo-app.spec.ts} (85%) create mode 100644 src/tests/web/example/frame.spec.ts create mode 100644 src/tests/web/example/networkTest.spec.ts create mode 100644 src/tests/web/example/route.spec.ts create mode 100644 src/tests/web/example/screenshot.spec.ts diff --git a/README.md b/README.md index b0b27a3..7241e43 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ _☝ If you like the project, please give a ⭐ on [GitHub](https://github.com/a ### Sample Web Test -`test('Navigate to Google @smoke', async({page}) => { await page.goto('https://www.google.com/') await expect(page).toHaveTitle('Google') })` +> Note: Refer to [sample-web-test](https://github.com/abhaybharti/playwright-framework-template/tree/master/src/tests/web/example) ### Sample Web Load Test diff --git a/src/helper/api/apiHelper.ts b/src/helper/api/apiHelper.ts index cd8022f..9dd916f 100644 --- a/src/helper/api/apiHelper.ts +++ b/src/helper/api/apiHelper.ts @@ -5,32 +5,32 @@ import { StringLiteral } from "typescript"; export class ApiHelper { private apiContext: any; -/** - * The constructor function initializes a new context for the API. - * @param {any} apiContext - The `apiContext` parameter is an object that represents the context of an - * API. It is used to store and manage information related to the API, such as authentication - * credentials, request headers, and other configuration settings. - */ + /** + * The constructor function initializes a new context for the API. + * @param {any} apiContext - The `apiContext` parameter is an object that represents the context of an + * API. It is used to store and manage information related to the API, such as authentication + * credentials, request headers, and other configuration settings. + */ constructor(apiContext: any) { this.apiContext = apiContext.newContext(); } -/** - * The function `hitApiEndPoint` is an asynchronous function that takes in an operation type, an - * endpoint, a payload, and a status code, and then invokes the corresponding API method based on the - * operation type. - * @param {string} operationType - The `operationType` parameter is a string that specifies the type of - * operation to be performed on the API endpoint. It can have one of the following values: "get", - * "post", "delete", or "put". - * @param {string} endPoint - The `endPoint` parameter is a string that represents the URL or endpoint - * of the API that you want to hit. It specifies the location where the API is hosted and the specific - * resource or action you want to perform. - * @param {object} payload - The `payload` parameter is an object that contains the data to be sent in - * the request body for POST and PUT operations. It can include any relevant information that needs to - * be sent to the API endpoint. - * @param {number} statusCode - The `statusCode` parameter is the expected HTTP status code that the - * API endpoint should return. - */ + /** + * The function `hitApiEndPoint` is an asynchronous function that takes in an operation type, an + * endpoint, a payload, and a status code, and then invokes the corresponding API method based on the + * operation type. + * @param {string} operationType - The `operationType` parameter is a string that specifies the type of + * operation to be performed on the API endpoint. It can have one of the following values: "get", + * "post", "delete", or "put". + * @param {string} endPoint - The `endPoint` parameter is a string that represents the URL or endpoint + * of the API that you want to hit. It specifies the location where the API is hosted and the specific + * resource or action you want to perform. + * @param {object} payload - The `payload` parameter is an object that contains the data to be sent in + * the request body for POST and PUT operations. It can include any relevant information that needs to + * be sent to the API endpoint. + * @param {number} statusCode - The `statusCode` parameter is the expected HTTP status code that the + * API endpoint should return. + */ async hitApiEndPoint( operationType: string, endPoint: string, @@ -140,4 +140,8 @@ export class ApiHelper { `${statusCode} status code was not displayed` ).toBeOK(); } + + async getToken() { + return "tokenvalue"; + } } diff --git a/src/tests/web/example/demo-todo-app.spec.js b/src/tests/web/example/demo-todo-app.spec.ts similarity index 85% rename from src/tests/web/example/demo-todo-app.spec.js rename to src/tests/web/example/demo-todo-app.spec.ts index 699b223..b380613 100644 --- a/src/tests/web/example/demo-todo-app.spec.js +++ b/src/tests/web/example/demo-todo-app.spec.ts @@ -1,11 +1,21 @@ -// @ts-check import { test, expect } from "@playwright/test"; +import { WebHelper } from "../../../helper/web/webHelper"; test.beforeEach(async ({ page }) => { await page.goto("https://demo.playwright.dev/todomvc"); }); -const TODO_ITEMS = ["buy some cheese", "feed the cat", "book a doctors appointment"]; +test("Sample Web Test", async ({ page, browser }) => { + const browserContext = await browser.newContext(); + + const webHelper = new WebHelper(page, browserContext); +}); + +const TODO_ITEMS = [ + "buy some cheese", + "feed the cat", + "book a doctors appointment", +]; test.describe("New Todo", () => { test("should allow me to add todo items", async ({ page }) => { @@ -24,12 +34,17 @@ test.describe("New Todo", () => { await newTodo.press("Enter"); // Make sure the list now has two todo items. - await expect(page.getByTestId("todo-title")).toHaveText([TODO_ITEMS[0], TODO_ITEMS[1]]); + await expect(page.getByTestId("todo-title")).toHaveText([ + TODO_ITEMS[0], + TODO_ITEMS[1], + ]); await checkNumberOfTodosInLocalStorage(page, 2); }); - test("should clear text input field when an item is added", async ({ page }) => { + test("should clear text input field when an item is added", async ({ + page, + }) => { // create a new todo locator const newTodo = page.getByPlaceholder("What needs to be done?"); @@ -42,7 +57,9 @@ test.describe("New Todo", () => { await checkNumberOfTodosInLocalStorage(page, 1); }); - test("should append new items to the bottom of the list", async ({ page }) => { + test("should append new items to the bottom of the list", async ({ + page, + }) => { // Create 3 items. await createDefaultTodos(page); @@ -76,11 +93,17 @@ test.describe("Mark all as completed", () => { await page.getByLabel("Mark all as complete").check(); // Ensure all todos have 'completed' class. - await expect(page.getByTestId("todo-item")).toHaveClass(["completed", "completed", "completed"]); + await expect(page.getByTestId("todo-item")).toHaveClass([ + "completed", + "completed", + "completed", + ]); await checkNumberOfCompletedTodosInLocalStorage(page, 3); }); - test("should allow me to clear the complete state of all items", async ({ page }) => { + test("should allow me to clear the complete state of all items", async ({ + page, + }) => { const toggleAll = page.getByLabel("Mark all as complete"); // Check and then immediately uncheck. await toggleAll.check(); @@ -90,7 +113,9 @@ test.describe("Mark all as completed", () => { await expect(page.getByTestId("todo-item")).toHaveClass(["", "", ""]); }); - test("complete all checkbox should update state when items are completed / cleared", async ({ page }) => { + test("complete all checkbox should update state when items are completed / cleared", async ({ + page, + }) => { const toggleAll = page.getByLabel("Mark all as complete"); await toggleAll.check(); await expect(toggleAll).toBeChecked(); @@ -168,12 +193,20 @@ test.describe("Item", () => { const todoItems = page.getByTestId("todo-item"); const secondTodo = todoItems.nth(1); await secondTodo.dblclick(); - await expect(secondTodo.getByRole("textbox", { name: "Edit" })).toHaveValue(TODO_ITEMS[1]); - await secondTodo.getByRole("textbox", { name: "Edit" }).fill("buy some sausages"); + await expect(secondTodo.getByRole("textbox", { name: "Edit" })).toHaveValue( + TODO_ITEMS[1] + ); + await secondTodo + .getByRole("textbox", { name: "Edit" }) + .fill("buy some sausages"); await secondTodo.getByRole("textbox", { name: "Edit" }).press("Enter"); // Explicitly assert the new text value. - await expect(todoItems).toHaveText([TODO_ITEMS[0], "buy some sausages", TODO_ITEMS[2]]); + await expect(todoItems).toHaveText([ + TODO_ITEMS[0], + "buy some sausages", + TODO_ITEMS[2], + ]); await checkTodosInLocalStorage(page, "buy some sausages"); }); }); @@ -191,7 +224,7 @@ test.describe("Editing", () => { await expect( todoItem.locator("label", { hasText: TODO_ITEMS[1], - }), + }) ).toBeHidden(); await checkNumberOfTodosInLocalStorage(page, 3); }); @@ -199,28 +232,53 @@ test.describe("Editing", () => { test("should save edits on blur", async ({ page }) => { const todoItems = page.getByTestId("todo-item"); await todoItems.nth(1).dblclick(); - await todoItems.nth(1).getByRole("textbox", { name: "Edit" }).fill("buy some sausages"); - await todoItems.nth(1).getByRole("textbox", { name: "Edit" }).dispatchEvent("blur"); - - await expect(todoItems).toHaveText([TODO_ITEMS[0], "buy some sausages", TODO_ITEMS[2]]); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .fill("buy some sausages"); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .dispatchEvent("blur"); + + await expect(todoItems).toHaveText([ + TODO_ITEMS[0], + "buy some sausages", + TODO_ITEMS[2], + ]); await checkTodosInLocalStorage(page, "buy some sausages"); }); test("should trim entered text", async ({ page }) => { const todoItems = page.getByTestId("todo-item"); await todoItems.nth(1).dblclick(); - await todoItems.nth(1).getByRole("textbox", { name: "Edit" }).fill(" buy some sausages "); - await todoItems.nth(1).getByRole("textbox", { name: "Edit" }).press("Enter"); - - await expect(todoItems).toHaveText([TODO_ITEMS[0], "buy some sausages", TODO_ITEMS[2]]); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .fill(" buy some sausages "); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .press("Enter"); + + await expect(todoItems).toHaveText([ + TODO_ITEMS[0], + "buy some sausages", + TODO_ITEMS[2], + ]); await checkTodosInLocalStorage(page, "buy some sausages"); }); - test("should remove the item if an empty text string was entered", async ({ page }) => { + test("should remove the item if an empty text string was entered", async ({ + page, + }) => { const todoItems = page.getByTestId("todo-item"); await todoItems.nth(1).dblclick(); await todoItems.nth(1).getByRole("textbox", { name: "Edit" }).fill(""); - await todoItems.nth(1).getByRole("textbox", { name: "Edit" }).press("Enter"); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .press("Enter"); await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]); }); @@ -228,8 +286,14 @@ test.describe("Editing", () => { test("should cancel edits on escape", async ({ page }) => { const todoItems = page.getByTestId("todo-item"); await todoItems.nth(1).dblclick(); - await todoItems.nth(1).getByRole("textbox", { name: "Edit" }).fill("buy some sausages"); - await todoItems.nth(1).getByRole("textbox", { name: "Edit" }).press("Escape"); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .fill("buy some sausages"); + await todoItems + .nth(1) + .getByRole("textbox", { name: "Edit" }) + .press("Escape"); await expect(todoItems).toHaveText(TODO_ITEMS); }); }); @@ -261,7 +325,9 @@ test.describe("Clear completed button", () => { test("should display the correct text", async ({ page }) => { await page.locator(".todo-list li .toggle").first().check(); - await expect(page.getByRole("button", { name: "Clear completed" })).toBeVisible(); + await expect( + page.getByRole("button", { name: "Clear completed" }) + ).toBeVisible(); }); test("should remove completed items when clicked", async ({ page }) => { @@ -272,10 +338,14 @@ test.describe("Clear completed button", () => { await expect(todoItems).toHaveText([TODO_ITEMS[0], TODO_ITEMS[2]]); }); - test("should be hidden when there are no items that are completed", async ({ page }) => { + test("should be hidden when there are no items that are completed", async ({ + page, + }) => { await page.locator(".todo-list li .toggle").first().check(); await page.getByRole("button", { name: "Clear completed" }).click(); - await expect(page.getByRole("button", { name: "Clear completed" })).toBeHidden(); + await expect( + page.getByRole("button", { name: "Clear completed" }) + ).toBeHidden(); }); }); @@ -369,7 +439,9 @@ test.describe("Routing", () => { }); test("should highlight the currently applied filter", async ({ page }) => { - await expect(page.getByRole("link", { name: "All" })).toHaveClass("selected"); + await expect(page.getByRole("link", { name: "All" })).toHaveClass( + "selected" + ); //create locators for active and completed links const activeLink = page.getByRole("link", { name: "Active" }); @@ -411,7 +483,10 @@ async function checkNumberOfTodosInLocalStorage(page, expected) { */ async function checkNumberOfCompletedTodosInLocalStorage(page, expected) { return await page.waitForFunction((e) => { - return JSON.parse(localStorage["react-todos"]).filter((i) => i.completed).length === e; + return ( + JSON.parse(localStorage["react-todos"]).filter((i) => i.completed) + .length === e + ); }, expected); } diff --git a/src/tests/web/example/frame.spec.ts b/src/tests/web/example/frame.spec.ts new file mode 100644 index 0000000..0ffec91 --- /dev/null +++ b/src/tests/web/example/frame.spec.ts @@ -0,0 +1,10 @@ +import test from "@playwright/test"; +import { Console, log } from "console"; + +test("iframe", async ({ page }) => { + await page.goto("http://rahulshettyacademy.com/AutomationPractice/"); + const framesPage = await page.frameLocator("#courses-iframe"); + framesPage.locator("li a[href*='lifetime-access]:visible").click(); + const textCheck = await framesPage.locator(".text h2").textContent(); + console.log(textCheck); +}); diff --git a/src/tests/web/example/networkTest.spec.ts b/src/tests/web/example/networkTest.spec.ts new file mode 100644 index 0000000..1467b94 --- /dev/null +++ b/src/tests/web/example/networkTest.spec.ts @@ -0,0 +1,25 @@ +import test from "@playwright/test"; +import { ApiHelper } from "../../../helper/api/apiHelper"; + +let token: string; + +test.beforeAll(async ({ browser }) => { + const context = await browser.newContext(); + const apiContent = await context.newPage(); + const apiHelper = new ApiHelper(apiContent); + + //save token value returned from getToken() function in token variable + token = await apiHelper.getToken(); +}); + +test("Network Test -> Inject token generated through API into browser", async ({ + page, +}) => { + //executed JavaScript to inject token into browser + page.addInitScript((value) => { + window.localStorage.setItem("token", value); + }, token); + + //when script hits URL, browser will open as logged in using above generated token + await page.goto("https://www.xxxx.com"); +}); diff --git a/src/tests/web/example/route.spec.ts b/src/tests/web/example/route.spec.ts new file mode 100644 index 0000000..6b2b221 --- /dev/null +++ b/src/tests/web/example/route.spec.ts @@ -0,0 +1,29 @@ +import test from "@playwright/test"; + +let fakePayloadOrders = { data: [], message: "No Users" }; + +test("Intercept Network call and Fake API response", async ({ page }) => { + await page.goto("http://xxxx.com"); + await page.route("http://xxxx.com/abc/ird", async (route) => { + //go the response + const response = await page.request.fetch(route.request()); + let body = JSON.stringify(fakePayloadOrders); + + //send response to browser and override respond body + route.fulfill({ + status: 200, + body: body, + }); + }); +}); + +test("Intercept Network request", async ({ page }) => { + await page.goto("http://xxxx.com"); + + //intercept request send to server & respond by url value passed in route.continue + await page.route("http://xxxx.com/abc/ird", async (route) => { + route.continue({ + url: "http://xxxx.com/abc/123455", + }); + }); +}); diff --git a/src/tests/web/example/screenshot.spec.ts b/src/tests/web/example/screenshot.spec.ts new file mode 100644 index 0000000..40c5e95 --- /dev/null +++ b/src/tests/web/example/screenshot.spec.ts @@ -0,0 +1,12 @@ +import test from "@playwright/test"; + +test("Take Screenshot of Page", async ({ page }) => { + //Take screenshot of full page + await page.screenshot({ path: "screenshot.png" }); +}); + +test("Take Screenshot of Element", async ({ page }) => { + //Take screenshot of an element + await page.goto("http://xxx.com"); + await page.getByTestId("todo-item").screenshot({ path: "screenshot.png" }); +});