├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── playwright.config.ts └── src ├── feartures └── APIutils.ts ├── tests └── APITestSuite.ts └── utils └── fixture.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | playwright-report 3 | test-results -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Introduction:** 2 | This API testing framework serves as a well-structured and intuitive boilerplate. It not only demonstrates best practices but also embodies reliability, enabling the creation of automated test suites with confidence. 3 | Further details check out my medium blog post [here](https://medium.com/@Amr.sa/tips-tricks-intuitive-api-testing-with-playwright-648b9b8d6141) 4 | 5 | **Installation:** 6 | 7 | To set up this framework, you can use the following commands: 8 | 9 | ```markdown 10 | npm ci 11 | ``` 12 | 13 | **Execution:** 14 | 15 | To run your automated tests, use the following command: 16 | 17 | ```markdown 18 | npm run test 19 | ``` 20 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playwright-api", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "playwright-api", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@playwright/test": "^1.39.0" 13 | } 14 | }, 15 | "node_modules/@playwright/test": { 16 | "version": "1.39.0", 17 | "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", 18 | "integrity": "sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ==", 19 | "dev": true, 20 | "dependencies": { 21 | "playwright": "1.39.0" 22 | }, 23 | "bin": { 24 | "playwright": "cli.js" 25 | }, 26 | "engines": { 27 | "node": ">=16" 28 | } 29 | }, 30 | "node_modules/fsevents": { 31 | "version": "2.3.2", 32 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 33 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 34 | "dev": true, 35 | "hasInstallScript": true, 36 | "optional": true, 37 | "os": [ 38 | "darwin" 39 | ], 40 | "engines": { 41 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 42 | } 43 | }, 44 | "node_modules/playwright": { 45 | "version": "1.39.0", 46 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.39.0.tgz", 47 | "integrity": "sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw==", 48 | "dev": true, 49 | "dependencies": { 50 | "playwright-core": "1.39.0" 51 | }, 52 | "bin": { 53 | "playwright": "cli.js" 54 | }, 55 | "engines": { 56 | "node": ">=16" 57 | }, 58 | "optionalDependencies": { 59 | "fsevents": "2.3.2" 60 | } 61 | }, 62 | "node_modules/playwright-core": { 63 | "version": "1.39.0", 64 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.39.0.tgz", 65 | "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", 66 | "dev": true, 67 | "bin": { 68 | "playwright-core": "cli.js" 69 | }, 70 | "engines": { 71 | "node": ">=16" 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "playwright-api", 3 | "version": "1.0.0", 4 | "description": "Playwright Demo - API framework structure", 5 | "scripts": { 6 | "test": "npx playwright test", 7 | "report": "npx playwright show-report" 8 | }, 9 | "keywords": [ 10 | "playwright", 11 | "automation", 12 | "API", 13 | "test" 14 | ], 15 | "author": "Amrsa", 16 | "license": "ISC", 17 | "devDependencies": { 18 | "@playwright/test": "^1.39.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /playwright.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "@playwright/test"; 2 | 3 | export default defineConfig ({ 4 | use: { 5 | baseURL: 'https://restful-booker.herokuapp.com', 6 | trace: "retain-on-failure", 7 | ignoreHTTPSErrors: true, 8 | acceptDownloads: true, 9 | screenshot: 'off', 10 | video: "off", 11 | }, 12 | outputDir: "test-results", 13 | projects: [{ name: 'API test' }], 14 | testDir: 'src/tests', 15 | testMatch: ['APITestSuite.ts'], 16 | }); -------------------------------------------------------------------------------- /src/feartures/APIutils.ts: -------------------------------------------------------------------------------- 1 | import { APIRequestContext } from "playwright-core"; 2 | 3 | export default class API { 4 | private request: APIRequestContext; 5 | 6 | constructor(request: APIRequestContext) { 7 | this.request = request; 8 | } 9 | 10 | private async makeRequest(endpoint: string, method: string, reqBody?: object, token?: string) { 11 | const headers: Record = token ? { 'Cookie': `token=${token}` } : {}; 12 | 13 | const requestOptions = { 14 | headers, 15 | data: reqBody, 16 | }; 17 | 18 | const res = await this.request[method](endpoint, requestOptions); 19 | return res; 20 | } 21 | 22 | async postReq(endpoint: string, reqBody: object) { 23 | return this.makeRequest(endpoint, 'post', reqBody); 24 | } 25 | 26 | async getReq(endpoint: string) { 27 | return this.makeRequest(endpoint, 'get'); 28 | } 29 | 30 | async putReq(endpoint: string, reqBody: object, token: string) { 31 | return this.makeRequest(endpoint, 'put', reqBody, token); 32 | } 33 | 34 | async patchReq(endpoint: string, reqBody: object, token: string) { 35 | return this.makeRequest(endpoint, 'patch', reqBody, token); 36 | } 37 | 38 | async deleteReq(endpoint: string, token: string) { 39 | return this.makeRequest(endpoint, 'delete', undefined, token); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/tests/APITestSuite.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "@playwright/test"; 2 | import { fixtures as test } from "../utils/fixture"; 3 | 4 | test.describe("API Demo test suite", () => { 5 | 6 | let token: string; 7 | let bookingId: string; 8 | 9 | test.beforeAll(async ({ API }) => { 10 | const res = await API.postReq('/auth', { 11 | "username": "admin", 12 | "password": "password123" 13 | }); 14 | token = (await res.json()).token; 15 | }); 16 | 17 | test("[GET] Retrieve list of bookings and verify response", async ({ API }) => { 18 | const res = await API.getReq('/booking'); 19 | expect.soft(res.status()).toBe(200); 20 | expect((await res.json())[0]).toHaveProperty('bookingid'); 21 | }); 22 | 23 | test.describe("Creating Booking Suite", async () => { 24 | 25 | test.beforeAll(async ({ API }) => { 26 | const res = await API.postReq('/booking', { 27 | "firstname": "Amr", 28 | "lastname": "Sa", 29 | "totalprice": 100, 30 | "depositpaid": true, 31 | "bookingdates": { 32 | "checkin": "2023-10-17", 33 | "checkout": "2023-10-30" 34 | }, 35 | "additionalneeds": "launch" 36 | }); 37 | bookingId = (await res.json()).bookingid; 38 | }); 39 | 40 | test("[PUT] Update an existing booking and verify response", async ({ API }) => { 41 | const res = await API.putReq(`/booking/${bookingId}`, { 42 | "firstname": "amr", 43 | "lastname": "ka", 44 | "totalprice": 100, 45 | "depositpaid": true, 46 | "bookingdates": { 47 | "checkin": "2023-10-17", 48 | "checkout": "2023-10-30" 49 | }, 50 | "additionalneeds": "launch" 51 | }, token); 52 | expect.soft(res.status()).toBe(200); 53 | expect((await res.json()).firstname).toBe('amr'); 54 | }); 55 | 56 | test("[PATCH] Partially update an existing booking and verify response", async ({ API }) => { 57 | const res = await API.patchReq(`/booking/${bookingId}`, { 58 | "firstname": "john", 59 | "lastname": "smith" 60 | }, token); 61 | expect.soft(res.status()).toBe(200); 62 | expect.soft((await res.json()).firstname).toBe('john'); 63 | expect((await res.json()).lastname).toBe('smith'); 64 | }); 65 | 66 | test("[DELETE] Delete an existing booking and verify response", async ({ API }) => { 67 | const res = await API.deleteReq(`/booking/${bookingId}`, token); 68 | expect.soft(res.status()).toBe(201); 69 | }); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /src/utils/fixture.ts: -------------------------------------------------------------------------------- 1 | import { test as base } from "@playwright/test"; 2 | import api from "../feartures/APIutils"; 3 | 4 | type MyFixtures = { 5 | API: api; 6 | }; 7 | 8 | const fixtures = base.extend({ 9 | 10 | API: async ({ request }, use) => { 11 | const API = new api(request); 12 | await use(API); 13 | } 14 | 15 | }); 16 | 17 | export { fixtures }; 18 | --------------------------------------------------------------------------------