agents-cli-automation 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,461 @@
1
+ name: Create Playwright Framework - UI Testing (JavaScript)
2
+ description: Creates a production-ready Playwright UI automation framework in JavaScript (ES Modules) with all dependencies, fixtures, and test configurations.
3
+ argument-hint: "framework requirements, e.g., 'JavaScript with UI tests'"
4
+
5
+ ---
6
+
7
+ # Playwright UI Testing Framework - JavaScript (ES Modules)
8
+
9
+ This agent creates a production-ready Playwright framework in modern JavaScript (ES Modules) optimized for UI automation.
10
+
11
+ ## Capabilities
12
+ - **JavaScript (ES Modules)** - Chromium only with latest syntax
13
+ - No build step required - direct ES modules support
14
+ - UI, component, and end-to-end testing
15
+ - **Playwright BDD** - Gherkin format with step definitions
16
+ - **Page Object Model (POM)** - Reusable page classes
17
+ - **Fixtures** - Pre-built browser & page fixtures
18
+ - **Parallel execution** - Tests run simultaneously for speed
19
+ - Test data support (JSON, CSV, YAML)
20
+ - Form helpers for complex interactions
21
+ - Scalable folder structure with best practices
22
+
23
+ ## Prerequisites
24
+ - Node.js 18+ installed
25
+ - npm or yarn
26
+
27
+ ## Setup Instructions
28
+
29
+ ### 1. Install Dependencies (Chromium Only)
30
+ ```bash
31
+ npm install
32
+ npm install --save-dev @cucumber/cucumber csv-parser js-yaml
33
+ npx playwright install chromium
34
+ ```
35
+
36
+ ### 2. Update package.json
37
+ Add `"type": "module"` for ES modules support:
38
+ ```json
39
+ {
40
+ "type": "module",
41
+ "scripts": {
42
+ "start": "node server.js",
43
+ "test": "playwright test",
44
+ "test:headed": "playwright test --headed",
45
+ "test:debug": "playwright test --debug",
46
+ "test:ui": "playwright test --ui",
47
+ "test:bdd": "npx @cucumber/cucumber-js",
48
+ "test:bdd:report": "npx @cucumber/cucumber-js --format json:cucumber-report.json"
49
+ }
50
+ }
51
+ ```
52
+
53
+ ### 3. Playwright Configuration (playwright.config.js - ES Module)
54
+ ```javascript
55
+ export default {
56
+ testDir: './tests/specs',
57
+ fullyParallel: true,
58
+ workers: process.env.CI ? 1 : 4,
59
+ retries: 1,
60
+ timeout: 30000,
61
+ expect: {
62
+ timeout: 5000,
63
+ },
64
+ use: {
65
+ browserName: 'chromium',
66
+ headless: true,
67
+ screenshot: 'only-on-failure',
68
+ video: 'retain-on-failure',
69
+ trace: 'on-first-retry',
70
+ },
71
+ webServer: {
72
+ command: 'npm start',
73
+ url: 'http://localhost:3000',
74
+ reuseExistingServer: !process.env.CI,
75
+ },
76
+ reporter: [
77
+ ['html'],
78
+ ['json', { outputFile: 'test-results/results.json' }],
79
+ ],
80
+ };
81
+ ```
82
+
83
+ ### 4. Project Structure
84
+ ```
85
+ project/
86
+ ├── tests/
87
+ │ ├── fixtures/
88
+ │ │ └── base.fixture.js # Shared fixtures
89
+ │ ├── pages/
90
+ │ │ ├── BasePage.js # Base page object
91
+ │ │ ├── LoginPage.js # Login page object
92
+ │ │ ├── InventoryPage.js # Inventory page object
93
+ │ │ └── FormPage.js # Form page object
94
+ │ ├── utils/ # Utility functions
95
+ │ │ ├── dataReader.js
96
+ │ │ ├── formHelper.js
97
+ │ │ └── testData.js
98
+ │ ├── data/ # Test data files
99
+ │ │ ├── users.json
100
+ │ │ ├── formData.csv
101
+ │ │ └── config.yaml
102
+ │ ├── features/ # BDD Gherkin scenarios
103
+ │ │ ├── login.feature
104
+ │ │ └── shopping.feature
105
+ │ ├── steps/ # BDD Step definitions
106
+ │ │ ├── loginSteps.js
107
+ │ │ └── shoppingSteps.js
108
+ │ └── specs/
109
+ │ ├── saucedemo.spec.js # Traditional tests
110
+ │ └── formTest.spec.js
111
+ ├── cucumber.js # Cucumber config
112
+ ├── playwright.config.js # Playwright config
113
+ └── package.json
114
+ ```
115
+
116
+ ### 5. Cucumber Configuration (cucumber.js)
117
+ ```javascript
118
+ export default {
119
+ default: {
120
+ paths: ['tests/features/**/*.feature'],
121
+ require: ['tests/steps/**/*.js'],
122
+ format: ['progress', 'html:cucumber-report.html'],
123
+ parallel: 4,
124
+ worldParameters: {
125
+ appUrl: 'https://www.saucedemo.com/',
126
+ }
127
+ }
128
+ };
129
+ ```
130
+
131
+ ### 6. Data Reader Utility (tests/utils/dataReader.js)
132
+ ```javascript
133
+ import fs from 'fs';
134
+ import path from 'path';
135
+ import csv from 'csv-parser';
136
+ import yaml from 'js-yaml';
137
+ import { fileURLToPath } from 'url';
138
+
139
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
140
+ const DATA_DIR = path.join(path.resolve(__dirname, '..'), 'data');
141
+
142
+ export class DataReader {
143
+ // Read JSON file
144
+ static readJSON(filename) {
145
+ const filePath = path.join(DATA_DIR, filename);
146
+ const data = fs.readFileSync(filePath, 'utf-8');
147
+ return JSON.parse(data);
148
+ }
149
+
150
+ // Read YAML file
151
+ static readYAML(filename) {
152
+ const filePath = path.join(DATA_DIR, filename);
153
+ const data = fs.readFileSync(filePath, 'utf-8');
154
+ return yaml.load(data);
155
+ }
156
+
157
+ // Read CSV file (returns array of objects)
158
+ static async readCSV(filename) {
159
+ const filePath = path.join(DATA_DIR, filename);
160
+ return new Promise((resolve, reject) => {
161
+ const records = [];
162
+ fs.createReadStream(filePath)
163
+ .pipe(csv())
164
+ .on('data', (data) => records.push(data))
165
+ .on('end', () => resolve(records))
166
+ .on('error', reject);
167
+ });
168
+ }
169
+
170
+ // Get single record by ID or key
171
+ static getRecordByKey(data, key, value) {
172
+ if (Array.isArray(data)) {
173
+ return data.find(record => record[key] === value);
174
+ }
175
+ return undefined;
176
+ }
177
+ }
178
+ ```
179
+
180
+ ### 7. Form Helper Utility (tests/utils/formHelper.js)
181
+ ```javascript
182
+ export class FormHelper {
183
+ constructor(page) {
184
+ this.page = page;
185
+ }
186
+
187
+ // Fill text input
188
+ async fillInput(selector, value) {
189
+ await this.page.fill(selector, value);
190
+ }
191
+
192
+ // Fill textarea
193
+ async fillTextArea(selector, value) {
194
+ await this.page.locator(selector).clear();
195
+ await this.page.locator(selector).type(value, { delay: 50 });
196
+ }
197
+
198
+ // Select option from dropdown (non-select element)
199
+ async selectDropdown(dropdownSelector, optionText) {
200
+ await this.page.click(dropdownSelector);
201
+ await this.page.click(`text="${optionText}"`);
202
+ }
203
+
204
+ // Select radio button by label text
205
+ async selectRadioButton(labelText) {
206
+ const label = this.page.locator(`label:has-text("${labelText}")`);
207
+ const radioId = await label.getAttribute('for');
208
+ if (radioId) {
209
+ await this.page.locator(`#${radioId}`).click();
210
+ }
211
+ }
212
+
213
+ // Check checkbox by label text
214
+ async checkCheckbox(labelText) {
215
+ const label = this.page.locator(`label:has-text("${labelText}")`);
216
+ const checkboxId = await label.getAttribute('for');
217
+ if (checkboxId) {
218
+ const checkbox = this.page.locator(`#${checkboxId}`);
219
+ if (!(await checkbox.isChecked())) {
220
+ await checkbox.click();
221
+ }
222
+ }
223
+ }
224
+
225
+ // Select multiple checkboxes
226
+ async checkMultiple(labels) {
227
+ for (const label of labels) {
228
+ await this.checkCheckbox(label);
229
+ }
230
+ }
231
+
232
+ // Verify form value
233
+ async getInputValue(selector) {
234
+ return await this.page.inputValue(selector);
235
+ }
236
+
237
+ // Verify checkbox is checked
238
+ async isCheckboxChecked(labelText) {
239
+ const label = this.page.locator(`label:has-text("${labelText}")`);
240
+ const checkboxId = await label.getAttribute('for');
241
+ return checkboxId ? await this.page.locator(`#${checkboxId}`).isChecked() : false;
242
+ }
243
+ }
244
+ ```
245
+
246
+ ### 8. Test Data Manager (tests/utils/testData.js)
247
+ ```javascript
248
+ import { DataReader } from './dataReader.js';
249
+
250
+ export class TestDataManager {
251
+ static loadUsers() {
252
+ return DataReader.readJSON('users.json');
253
+ }
254
+
255
+ static async loadFormData() {
256
+ return await DataReader.readCSV('formData.csv');
257
+ }
258
+
259
+ static loadConfig() {
260
+ return DataReader.readYAML('config.yaml');
261
+ }
262
+
263
+ static getUserByUsername(username) {
264
+ const data = this.loadUsers();
265
+ return DataReader.getRecordByKey(data.users, 'username', username);
266
+ }
267
+
268
+ static async getFormDataByScenario(scenarioName) {
269
+ const data = await this.loadFormData();
270
+ return DataReader.getRecordByKey(data, 'scenario', scenarioName);
271
+ }
272
+ }
273
+ ```
274
+
275
+ ### 9. Base Page (tests/pages/BasePage.js)
276
+ ```javascript
277
+ export class BasePage {
278
+ constructor(page) {
279
+ this.page = page;
280
+ }
281
+
282
+ async navigateTo(url) {
283
+ await this.page.goto(url);
284
+ }
285
+
286
+ async click(selector) {
287
+ await this.page.click(selector);
288
+ }
289
+
290
+ async fill(selector, text) {
291
+ await this.page.fill(selector, text);
292
+ }
293
+
294
+ async getText(selector) {
295
+ return await this.page.textContent(selector);
296
+ }
297
+
298
+ async waitForElement(selector, timeout = 5000) {
299
+ await this.page.waitForSelector(selector, { timeout });
300
+ }
301
+
302
+ async isVisible(selector) {
303
+ return await this.page.locator(selector).isVisible();
304
+ }
305
+ }
306
+ ```
307
+
308
+ ### 10. Page Object - Login (tests/pages/LoginPage.js)
309
+ ```javascript
310
+ import { BasePage } from './BasePage.js';
311
+
312
+ export class LoginPage extends BasePage {
313
+ constructor(page) {
314
+ super(page);
315
+ this.usernameField = '[data-test="username"]';
316
+ this.passwordField = '[data-test="password"]';
317
+ this.loginButton = '[data-test="login-button"]';
318
+ }
319
+
320
+ async goto() {
321
+ await this.page.goto('https://www.saucedemo.com/');
322
+ }
323
+
324
+ async login(username, password) {
325
+ await this.page.fill(this.usernameField, username);
326
+ await this.page.fill(this.passwordField, password);
327
+ await this.page.click(this.loginButton);
328
+ await this.page.waitForURL('**/inventory.html');
329
+ }
330
+
331
+ async isLoaded() {
332
+ return await this.page.locator(this.usernameField).isVisible();
333
+ }
334
+ }
335
+ ```
336
+
337
+ ### 11. Fixtures Setup (tests/fixtures/base.fixture.js)
338
+ ```javascript
339
+ import { test as base, expect } from '@playwright/test';
340
+ import { LoginPage } from '../pages/LoginPage.js';
341
+ import { InventoryPage } from '../pages/InventoryPage.js';
342
+ import { FormPage } from '../pages/FormPage.js';
343
+
344
+ export const test = base.extend({
345
+ loginPage: async ({ page }, use) => {
346
+ const loginPage = new LoginPage(page);
347
+ await use(loginPage);
348
+ },
349
+
350
+ inventoryPage: async ({ page }, use) => {
351
+ const inventoryPage = new InventoryPage(page);
352
+ await use(inventoryPage);
353
+ },
354
+
355
+ formPage: async ({ page }, use) => {
356
+ const formPage = new FormPage(page);
357
+ await use(formPage);
358
+ },
359
+
360
+ authenticatedUser: async ({ page, loginPage }, use) => {
361
+ await loginPage.goto();
362
+ await loginPage.login('standard_user', 'secret_sauce');
363
+ await use({ page, loginPage });
364
+ },
365
+ });
366
+
367
+ export { expect };
368
+ ```
369
+
370
+ ### 12. Simple Test Example (tests/specs/saucedemo.spec.js)
371
+ ```javascript
372
+ import { test, expect } from '../fixtures/base.fixture.js';
373
+
374
+ test.describe('SauceDemo E2E Tests', () => {
375
+ test('Login with valid credentials', async ({ loginPage, page }) => {
376
+ await loginPage.goto();
377
+ await loginPage.login('standard_user', 'secret_sauce');
378
+ expect(page.url()).toContain('inventory.html');
379
+ });
380
+
381
+ test('Add product to cart', async ({ authenticatedUser, inventoryPage }) => {
382
+ await inventoryPage.addProductToCart();
383
+ const cartCount = await inventoryPage.getCartCount();
384
+ expect(cartCount).toBe('1');
385
+ });
386
+ });
387
+ ```
388
+
389
+ ### 13. BDD Feature Example (tests/features/login.feature)
390
+ ```gherkin
391
+ Feature: Login to SauceDemo
392
+ As a user
393
+ I want to login to the application
394
+ So that I can access the inventory
395
+
396
+ Scenario: User logs in with valid credentials
397
+ Given I navigate to the SauceDemo application
398
+ When I login with username "standard_user" and password "secret_sauce"
399
+ Then I should see the inventory page
400
+ ```
401
+
402
+ ### 14. Test Data Files
403
+
404
+ **tests/data/users.json**
405
+ ```json
406
+ {
407
+ "users": [
408
+ {
409
+ "id": "1",
410
+ "username": "standard_user",
411
+ "password": "secret_sauce",
412
+ "email": "user@example.com",
413
+ "firstName": "John",
414
+ "lastName": "Doe"
415
+ }
416
+ ]
417
+ }
418
+ ```
419
+
420
+ **tests/data/config.yaml**
421
+ ```yaml
422
+ app:
423
+ url: https://www.saucedemo.com/
424
+ timeout: 30000
425
+ users:
426
+ standard:
427
+ username: standard_user
428
+ password: secret_sauce
429
+ ```
430
+
431
+ ## Run Tests
432
+
433
+ ```bash
434
+ # Run all tests in parallel
435
+ npm test
436
+
437
+ # Run specific test file
438
+ npm test tests/specs/saucedemo.spec.js
439
+
440
+ # Run with headed browser
441
+ npm run test:headed
442
+
443
+ # Debug mode
444
+ npm run test:debug
445
+
446
+ # Run BDD scenarios
447
+ npm run test:bdd
448
+ ```
449
+
450
+ ## Best Practices
451
+ ✅ **Page Object Model** - Encapsulate all selectors in page classes
452
+ ✅ **Fixtures** - Reusable page objects and setup/teardown
453
+ ✅ **Test Data** - Externalize data in JSON, CSV, YAML
454
+ ✅ **Form Helpers** - Handle dropdowns, radios, checkboxes
455
+ ✅ **BDD Support** - Write scenarios in Gherkin
456
+ ✅ **Parallel Execution** - 4 workers by default
457
+ ✅ **Chromium Only** - Single browser for consistency
458
+ ✅ **Modern ES Modules** - Clean import/export syntax
459
+ ✅ **No Build Step** - Node.js 18+ direct ES module support
460
+
461
+ **Note:** This framework uses JavaScript with ES Modules, Chromium only, and parallel execution. Start by copying the project structure and customizing page objects for your application.