Last active
March 22, 2026 15:33
-
-
Save up1/72f271f95c416cbaeed02527ece6d006 to your computer and use it in GitHub Desktop.
Playwright Skill for API Testing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| $npx skills add testdino-hq/playwright-skill | |
| ███████╗██╗ ██╗██╗██╗ ██╗ ███████╗ | |
| ██╔════╝██║ ██╔╝██║██║ ██║ ██╔════╝ | |
| ███████╗█████╔╝ ██║██║ ██║ ███████╗ | |
| ╚════██║██╔═██╗ ██║██║ ██║ ╚════██║ | |
| ███████║██║ ██╗██║███████╗███████╗███████║ | |
| ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚══════╝ | |
| ┌ skills | |
| │ | |
| ◇ Source: https://github.com/testdino-hq/playwright-skill.git | |
| │ | |
| ◇ Repository cloned | |
| │ | |
| ◇ Found 1 skill | |
| │ | |
| ● Skill: playwright-skill | |
| │ | |
| │ Battle-tested Playwright patterns for E2E, API, component, visual, accessibility, and security testing. Covers locators, fixtures, POM, network mocking, auth flows, debugging, CI/CD (GitHub Actions, GitLab, CircleCI, Azure, Jenkins), framework recipes (React, Next.js, Vue, Angular), and migration guides from Cypress/Selenium. TypeScript and JavaScript. | |
| │ | |
| ◇ 42 agents | |
| ◆ Which agents do you want to install to? | |
| │ | |
| │ ── Universal (.agents/skills) ── always included ──────────── | |
| │ • Amp | |
| │ • Cline | |
| │ • Codex | |
| │ • Cursor | |
| │ • Gemini CLI | |
| │ • GitHub Copilot | |
| │ • Kimi Code CLI | |
| │ • OpenCode | |
| │ • Warp | |
| │ | |
| │ ── Additional agents ───────────────────────────── | |
| │ Search: | |
| │ ↑↓ move, space select, enter confirm | |
| │ | |
| │ ❯ ○ Antigravity (.agent/skills) | |
| │ ○ Augment (.augment/skills) | |
| │ ○ Claude Code (.claude/skills) | |
| │ ○ OpenClaw (skills) | |
| │ ○ CodeBuddy (.codebuddy/skills) | |
| │ ○ Command Code (.commandcode/skills) | |
| │ ○ Continue (.continue/skills) | |
| │ ○ Cortex Code (.cortex/skills) | |
| │ ↓ 23 more | |
| │ | |
| │ Selected: Amp, Cline, Codex +6 more |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 1. ทำการสร้าง Playwrigt project ใน folder demo-api | |
| /playwright-skill create api testing in folder demo-api | |
| # 2. ทำการสร้าง test casae จาก spec ที่ต้องการ | |
| /playwright-skill add api testing from specs #api-specs/login-flow.md |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { test, expect } from '@playwright/test'; | |
| test.describe('Login Flow', () => { | |
| test.describe('GET /auth/me', () => { | |
| test('returns current user profile with valid token', async ({ request }) => { | |
| // Step 1: Login to get token | |
| const loginResponse = await request.post('/auth/login', { | |
| data: { | |
| username: 'emilys', | |
| password: 'emilyspass', | |
| }, | |
| }); | |
| expect(loginResponse.ok()).toBeTruthy(); | |
| const { accessToken } = await loginResponse.json(); | |
| // Step 2: Get profile with token | |
| const profileResponse = await request.get('/auth/me', { | |
| headers: { | |
| 'Authorization': `Bearer ${accessToken}`, | |
| }, | |
| }); | |
| expect(profileResponse.status()).toBe(200); | |
| const profile = await profileResponse.json(); | |
| expect(profile).toMatchObject({ | |
| id: expect.any(Number), | |
| username: 'emilys', | |
| email: 'emily.johnson@x.dummyjson.com', | |
| firstName: 'Emily', | |
| lastName: 'Johnson', | |
| gender: 'female', | |
| role: expect.any(String), | |
| }); | |
| expect(profile.address).toMatchObject({ | |
| city: expect.any(String), | |
| country: expect.any(String), | |
| }); | |
| expect(profile.company).toMatchObject({ | |
| name: expect.any(String), | |
| department: expect.any(String), | |
| title: expect.any(String), | |
| }); | |
| }); | |
| }); | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { test, expect } from '@playwright/test'; | |
| import { z } from 'zod'; | |
| const LoginResponseSchema = z.looseObject({ | |
| id: z.number().int().positive(), | |
| username: z.string().min(1), | |
| email: z.email(), | |
| firstName: z.string().min(1), | |
| lastName: z.string().min(1), | |
| gender: z.string().min(1), | |
| image: z.url(), | |
| accessToken: z.string().min(1), | |
| refreshToken: z.string().min(1), | |
| }); | |
| const ProfileResponseSchema = z.looseObject({ | |
| id: z.number().int().positive(), | |
| username: z.string().min(1), | |
| email: z.email(), | |
| firstName: z.string().min(1), | |
| lastName: z.string().min(1), | |
| gender: z.string().min(1), | |
| role: z.string().min(1), | |
| address: z.looseObject({ | |
| city: z.string().min(1), | |
| country: z.string().min(1), | |
| }), | |
| company: z.looseObject({ | |
| name: z.string().min(1), | |
| department: z.string().min(1), | |
| title: z.string().min(1), | |
| }), | |
| }); | |
| test.describe('Login Flow', () => { | |
| test.describe('POST /auth/login', () => { | |
| test('logs in with valid username and password', async ({ request }) => { | |
| const response = await request.post('/auth/login', { | |
| data: { | |
| username: 'emilys', | |
| password: 'emilyspass', | |
| expiresInMins: 30, | |
| }, | |
| }); | |
| expect(response.status()).toBe(200); | |
| const body = await response.json(); | |
| const result = LoginResponseSchema.safeParse(body); | |
| if (!result.success) { | |
| throw new Error(`Schema validation failed:\n${z.prettifyError(result.error)}`); | |
| } | |
| }); | |
| }); | |
| test.describe('GET /auth/me', () => { | |
| test('returns current user profile with valid token', async ({ request }) => { | |
| // Step 1: Login to get token | |
| const loginResponse = await request.post('/auth/login', { | |
| data: { | |
| username: 'emilys', | |
| password: 'emilyspass', | |
| }, | |
| }); | |
| expect(loginResponse.ok()).toBeTruthy(); | |
| const { accessToken } = await loginResponse.json(); | |
| // Step 2: Get profile with token | |
| const profileResponse = await request.get('/auth/me', { | |
| headers: { | |
| 'Authorization': `Bearer ${accessToken}`, | |
| }, | |
| }); | |
| expect(profileResponse.status()).toBe(200); | |
| const profile = await profileResponse.json(); | |
| const result = ProfileResponseSchema.safeParse(profile); | |
| if (!result.success) { | |
| throw new Error(`Schema validation failed:\n${z.prettifyError(result.error)}`); | |
| } | |
| }); | |
| }); | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Login flow | |
| 1. Login with username and password | |
| 2. Get current profile from authenticated token | |
| ## 1. Login with username and password | |
| Request: | |
| ``` | |
| fetch('https://dummyjson.com/auth/login', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| username: 'emilys', | |
| password: 'emilyspass', | |
| expiresInMins: 30, // optional, defaults to 60 | |
| }), | |
| credentials: 'include' // Include cookies (e.g., accessToken) in the request | |
| }) | |
| .then(res => res.json()) | |
| .then(console.log); | |
| ``` | |
| Response: | |
| ``` | |
| { | |
| "id": 1, | |
| "username": "emilys", | |
| "email": "emily.johnson@x.dummyjson.com", | |
| "firstName": "Emily", | |
| "lastName": "Johnson", | |
| "gender": "female", | |
| "image": "https://dummyjson.com/icon/emilys/128", | |
| "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", // JWT accessToken (for backward compatibility) in response and cookies | |
| "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." // refreshToken in response and cookies | |
| } | |
| ``` | |
| ## 2. Get current profile from authenticated token | |
| Request: | |
| ``` | |
| /* providing accessToken in bearer */ | |
| fetch('https://dummyjson.com/auth/me', { | |
| method: 'GET', | |
| headers: { | |
| 'Authorization': 'Bearer /* YOUR_ACCESS_TOKEN_HERE */', // Pass JWT via Authorization header | |
| }, | |
| credentials: 'include' // Include cookies (e.g., accessToken) in the request | |
| }) | |
| .then(res => res.json()) | |
| .then(console.log); | |
| ``` | |
| Response: | |
| ``` | |
| { | |
| "id": 1, | |
| "firstName": "Emily", | |
| "lastName": "Johnson", | |
| "maidenName": "Smith", | |
| "age": 29, | |
| "gender": "female", | |
| "email": "emily.johnson@x.dummyjson.com", | |
| "phone": "+81 965-431-3024", | |
| "username": "emilys", | |
| "password": "emilyspass", | |
| "birthDate": "1996-5-30", | |
| "image": "https://dummyjson.com/icon/emilys/128", | |
| "bloodGroup": "O-", | |
| "height": 193.24, | |
| "weight": 63.16, | |
| "eyeColor": "Green", | |
| "hair": { | |
| "color": "Brown", | |
| "type": "Curly" | |
| }, | |
| "ip": "42.48.100.32", | |
| "address": { | |
| "address": "626 Main Street", | |
| "city": "Phoenix", | |
| "state": "Mississippi", | |
| "stateCode": "MS", | |
| "postalCode": "29112", | |
| "coordinates": { | |
| "lat": -77.16213, | |
| "lng": -92.084824 | |
| }, | |
| "country": "United States" | |
| }, | |
| "macAddress": "47:fa:41:18:ec:eb", | |
| "university": "University of Wisconsin--Madison", | |
| "bank": { | |
| "cardExpire": "05/28", | |
| "cardNumber": "3693233511855044", | |
| "cardType": "Diners Club International", | |
| "currency": "GBP", | |
| "iban": "GB74MH2UZLR9TRPHYNU8F8" | |
| }, | |
| "company": { | |
| "department": "Engineering", | |
| "name": "Dooley, Kozey and Cronin", | |
| "title": "Sales Manager", | |
| "address": { | |
| "address": "263 Tenth Street", | |
| "city": "San Francisco", | |
| "state": "Wisconsin", | |
| "stateCode": "WI", | |
| "postalCode": "37657", | |
| "coordinates": { | |
| "lat": 71.814525, | |
| "lng": -161.150263 | |
| }, | |
| "country": "United States" | |
| } | |
| }, | |
| "ein": "977-175", | |
| "ssn": "900-590-289", | |
| "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36", | |
| "crypto": { | |
| "coin": "Bitcoin", | |
| "wallet": "0xb9fc2fe63b2a6c003f1c324c3bfa53259162181a", | |
| "network": "Ethereum (ERC20)" | |
| }, | |
| "role": "admin" | |
| } | |
| ``` | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment