agents-cli-automation 1.0.18 → 1.0.20

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-cli-automation",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "Agents CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -32,7 +32,8 @@ export default async function initCommand() {
32
32
  "TypeScript (Recommended - Latest & Type Safe)",
33
33
  "JavaScript (ES Modules)",
34
34
  "Java (Maven + JUnit)",
35
- "C# (NUnit + .NET)"
35
+ "C# (NUnit + .NET)",
36
+ "API+DB+UI Full Stack (All-in-One Template)"
36
37
  ]
37
38
  }
38
39
  ]);
@@ -52,6 +53,9 @@ export default async function initCommand() {
52
53
  } else if (language.includes("C#")) {
53
54
  templateFile = "playwright-agent-csharp.md";
54
55
  templateName = "C#";
56
+ }else if (language.includes("API+DB+UI")) {
57
+ templateFile = "playwright-agent-api-db-ui.md";
58
+ templateName = "API+DB+UI Full Stack";
55
59
  }
56
60
 
57
61
  const templatePath = path.resolve(__dirname, `../templates/${templateFile}`);
@@ -0,0 +1,404 @@
1
+ # Playwright Test Automation Framework
2
+
3
+ **Name:** Playwright Agent - TypeScript Test Automation Framework
4
+
5
+ **Description:** A comprehensive test automation framework built with Playwright and TypeScript for testing UI, API, and database layers. This framework demonstrates best practices for organizing test code, managing test data, implementing step definitions with Playwright BDD, and maintaining database state during testing. It provides examples of test-harness clients for database access, API calls, correlation tracking, and fixture management.
6
+
7
+ ---
8
+
9
+ ## Folder Structure
10
+
11
+ ```
12
+ automation-framework/
13
+ ├─ package.json
14
+ ├─ tsconfig.json
15
+ ├─ playwright.config.ts
16
+ ├─ .env
17
+
18
+ ├─ configs/
19
+ │ ├─ projects/
20
+ │ │ ├─ ui.project.ts
21
+ │ │ ├─ api.project.ts
22
+ │ │ ├─ db.project.ts
23
+ │ │ └─ integrated.project.ts
24
+ │ ├─ environments/
25
+ │ └─ global-setup.ts
26
+
27
+ ├─ scripts/
28
+ │ ├─ seed-db.ts
29
+ │ ├─ reset-db.ts
30
+ │ └─ run-wrapper.ts
31
+
32
+ ├─ src/
33
+ │ ├─ core/
34
+ │ │ ├─ test-harness/
35
+ │ │ │ ├─ dbClient.ts
36
+ │ │ │ └─ wrapperClient.ts
37
+ │ │ ├─ fixtures/
38
+ │ │ │ └─ context.fixture.ts
39
+ │ │ └─ utils/
40
+ │ │ ├─ logger.ts
41
+ │ │ └─ correlation.ts
42
+ │ │
43
+ │ ├─ modules/
44
+ │ │ ├─ scheduling-group/
45
+ │ │ │ ├─ features/
46
+ │ │ │ │ └─ create-scheduling-group.feature
47
+ │ │ │ ├─ steps/
48
+ │ │ │ │ └─ createSchedulingGroup.steps.ts
49
+ │ │ │ ├─ read-models/
50
+ │ │ │ │ └─ schedule.read.ts
51
+ │ │ │ ├─ queries/
52
+ │ │ │ │ └─ schedule.mutations.ts
53
+ │ │ │ ├─ contracts/
54
+ │ │ │ │ └─ schedule.wrapper.contract.ts
55
+ │ │ │ └─ test-data/
56
+ │ │ │ └─ schedule.seed.ts
57
+ │ │ └─ <future-module>/
58
+ │ │
59
+ │ └─ shared/
60
+ │ ├─ constants/
61
+ │ ├─ types/
62
+ │ └─ data-factory/
63
+
64
+ ├─ reports/
65
+ └─ README.md
66
+ ```
67
+
68
+ ## Environment Configuration
69
+
70
+ ### .env
71
+
72
+ ```bash
73
+ # Database
74
+ DB_HOST=localhost
75
+ DB_PORT=1433
76
+ DB_USER=sa
77
+ DB_PASSWORD=YourStrong@Passw0rd
78
+ DB_NAME=AutomationTestDB
79
+
80
+ # Wrapper / API
81
+ WRAPPER_URL=http://localhost:4000/test-wrapper
82
+
83
+ # Test Config
84
+ NODE_ENV=test
85
+ ```
86
+
87
+ ## Core Components
88
+
89
+ ### DB Client – `core/test-harness/dbClient.ts`
90
+
91
+ ```typescript
92
+ import { ConnectionPool } from "mssql";
93
+ import dotenv from "dotenv";
94
+ dotenv.config();
95
+
96
+ const pool = new ConnectionPool({
97
+ user: process.env.DB_USER,
98
+ password: process.env.DB_PASSWORD,
99
+ server: process.env.DB_HOST,
100
+ database: process.env.DB_NAME,
101
+ options: { encrypt: true, trustServerCertificate: true }
102
+ });
103
+
104
+ export const dbClient = {
105
+ query: async (query: string, params?: Record<string, any>) => {
106
+ const connection = await pool.connect();
107
+ const request = connection.request();
108
+ if (params) {
109
+ for (const key in params) {
110
+ request.input(key, params[key]);
111
+ }
112
+ }
113
+ const result = await request.query(query);
114
+ return result.recordset;
115
+ }
116
+ };
117
+ ```
118
+
119
+ ### Wrapper Client / API Layer – `core/test-harness/wrapperClient.ts`
120
+
121
+ ```typescript
122
+ import axios from "axios";
123
+ import { utils } from "../utils/correlation";
124
+
125
+ export const wrapperClient = {
126
+ createSchedulingGroup: async (payload: any, user: any) => {
127
+ const correlationId = utils.getCorrelationId();
128
+
129
+ const headers = {
130
+ "x-correlation-id": correlationId,
131
+ "x-test-user-id": user.id
132
+ };
133
+
134
+ utils.log("Calling wrapper API", { payload, user });
135
+
136
+ const response = await axios.post(
137
+ `${process.env.WRAPPER_URL}/scheduling-groups`,
138
+ payload,
139
+ { headers }
140
+ );
141
+
142
+ utils.log("Wrapper API response", response.data);
143
+
144
+ return { ...response.data, correlationId };
145
+ }
146
+ };
147
+ ```
148
+
149
+ ### Utilities – `core/utils/correlation.ts`
150
+
151
+ ```typescript
152
+ import { v4 as uuidv4 } from "uuid";
153
+
154
+ export const utils = {
155
+ getCorrelationId: (): string => uuidv4(),
156
+
157
+ normalizeTimestamps: (record: any) => {
158
+ if (record.createdAt) record.createdAt = "<timestamp>";
159
+ if (record.updatedAt) record.updatedAt = "<timestamp>";
160
+ return record;
161
+ },
162
+
163
+ log: (message: string, data?: any) => {
164
+ console.log(`[${new Date().toISOString()}] ${message}`, data || "");
165
+ }
166
+ };
167
+ ```
168
+
169
+ ### Fixtures – `core/fixtures/context.fixture.ts`
170
+
171
+ ```typescript
172
+ export const contextProvider = {
173
+ getCurrentUser: async () => {
174
+ return { id: "default-system-admin", role: "System Admin" };
175
+ },
176
+
177
+ createTestContext: () => {
178
+ return {
179
+ user: null,
180
+ payload: null,
181
+ response: null,
182
+ correlationId: null
183
+ };
184
+ }
185
+ };
186
+ ```
187
+
188
+ ## Test Data & Database
189
+
190
+ ### Test Data Builder – `modules/scheduling-group/test-data/schedule.seed.ts`
191
+
192
+ ```typescript
193
+ export const schedulingGroupBuilder = {
194
+ createValid: () => ({
195
+ name: `MorningNews_${Date.now()}`,
196
+ areaId: 101,
197
+ allocationsMenu: true,
198
+ notes: "Used for weekday planning",
199
+ teams: [201, 202]
200
+ })
201
+ };
202
+ ```
203
+
204
+ ### Read-Models – `modules/scheduling-group/read-models/schedule.read.ts`
205
+
206
+ ```typescript
207
+ import { dbClient } from "../../core/test-harness/dbClient";
208
+
209
+ export const schedulingGroupRead = {
210
+ getByName: async (name: string) => {
211
+ const result = await dbClient.query(`SELECT * FROM SchedulingGroup WHERE Name = @name`, { name });
212
+ return result[0] || null;
213
+ },
214
+
215
+ getTeamsByGroupId: async (groupId: number) => {
216
+ return dbClient.query(`SELECT * FROM SchedulingGroupTeam WHERE SchedulingGroupId = @groupId`, { groupId });
217
+ },
218
+
219
+ getHistoryByGroupId: async (groupId: number) => {
220
+ return dbClient.query(
221
+ `SELECT * FROM SchedulingGroupHistory WHERE SchedulingGroupId = @groupId ORDER BY LastAmendedDate DESC`,
222
+ { groupId }
223
+ );
224
+ }
225
+ };
226
+ ```
227
+
228
+ ### DB Stub / Invariants – `modules/scheduling-group/queries/schedule.mutations.ts`
229
+
230
+ ```typescript
231
+ import { dbClient } from "../../core/test-harness/dbClient";
232
+
233
+ export const schedulingGroupMutations = {
234
+ createSchedulingGroupStub: async (payload: any, userId: string) => {
235
+ const result = await dbClient.query(
236
+ `INSERT INTO SchedulingGroup (Name, AreaId, AllocationsMenu, Notes, LastAmendedBy, LastAmendedDate)
237
+ VALUES (@name, @areaId, @allocationsMenu, @notes, @userId, GETDATE());
238
+ SELECT SCOPE_IDENTITY() AS Id;`,
239
+ {
240
+ name: payload.name,
241
+ areaId: payload.areaId,
242
+ allocationsMenu: payload.allocationsMenu,
243
+ notes: payload.notes,
244
+ userId
245
+ }
246
+ );
247
+
248
+ const groupId = result[0].Id;
249
+
250
+ for (const teamId of payload.teams) {
251
+ await dbClient.query(
252
+ `INSERT INTO SchedulingGroupTeam (SchedulingGroupId, TeamId) VALUES (@groupId, @teamId)`,
253
+ { groupId, teamId }
254
+ );
255
+ }
256
+
257
+ await dbClient.query(
258
+ `INSERT INTO SchedulingGroupHistory (SchedulingGroupId, Name, AreaId, AllocationsMenu, Notes, LastAmendedBy, LastAmendedDate)
259
+ VALUES (@groupId, @name, @areaId, @allocationsMenu, @notes, @userId, GETDATE())`,
260
+ {
261
+ groupId,
262
+ name: payload.name,
263
+ areaId: payload.areaId,
264
+ allocationsMenu: payload.allocationsMenu,
265
+ notes: payload.notes,
266
+ userId
267
+ }
268
+ );
269
+
270
+ return groupId;
271
+ }
272
+ };
273
+ ```
274
+
275
+ ## Test Implementation
276
+
277
+ ### Step Definitions – `modules/scheduling-group/steps/createSchedulingGroup.steps.ts`
278
+
279
+ ```typescript
280
+ import { Given, When, Then } from "@cucumber/cucumber";
281
+ import { wrapperClient } from "../../core/test-harness/wrapperClient";
282
+ import { schedulingGroupMutations } from "../queries/schedule.mutations";
283
+ import { schedulingGroupRead } from "../read-models/schedule.read";
284
+ import { schedulingGroupBuilder } from "../test-data/schedule.seed";
285
+ import { contextProvider } from "../../core/fixtures/context.fixture";
286
+ import { utils } from "../../core/utils/correlation";
287
+ import { expect } from "@playwright/test";
288
+
289
+ Given("an Area Admin user is available", async function () {
290
+ this.context = contextProvider.createTestContext();
291
+ this.context.user = await contextProvider.getCurrentUser();
292
+ });
293
+
294
+ Given("a valid Scheduling Group payload is prepared", async function () {
295
+ this.context.payload = schedulingGroupBuilder.createValid();
296
+ utils.log("Payload prepared", this.context.payload);
297
+ });
298
+
299
+ When("the user creates the Scheduling Group via DB stub", async function () {
300
+ this.context.groupId = await schedulingGroupMutations.createSchedulingGroupStub(
301
+ this.context.payload,
302
+ this.context.user.id
303
+ );
304
+ });
305
+
306
+ Then("the Scheduling Group should exist in the database with correct invariants", async function () {
307
+ const record = await schedulingGroupRead.getByName(this.context.payload.name);
308
+ const normalized = utils.normalizeTimestamps(record);
309
+
310
+ expect(normalized).not.toBeNull();
311
+ expect(normalized.areaId).toBe(this.context.payload.areaId);
312
+ expect(normalized.allocationsMenu).toBe(this.context.payload.allocationsMenu);
313
+ expect(normalized.notes).toBe(this.context.payload.notes);
314
+
315
+ const teams = await schedulingGroupRead.getTeamsByGroupId(this.context.groupId);
316
+ expect(teams.map(t => t.TeamId)).toEqual(this.context.payload.teams);
317
+
318
+ const history = await schedulingGroupRead.getHistoryByGroupId(this.context.groupId);
319
+ expect(history.length).toBeGreaterThan(0);
320
+ expect(history[0].LastAmendedBy).toBe(this.context.user.id);
321
+ });
322
+ ```
323
+
324
+ ### Feature File – `modules/scheduling-group/features/create-scheduling-group.feature`
325
+
326
+ ```gherkin
327
+ Feature: Create Scheduling Group
328
+
329
+ Scenario: Area Admin creates a new Scheduling Group
330
+ Given an Area Admin user is available
331
+ And a valid Scheduling Group payload is prepared
332
+ When the user creates the Scheduling Group via DB stub
333
+ Then the Scheduling Group should exist in the database with correct invariants
334
+ ```
335
+
336
+ ## Database Scripts
337
+
338
+ ### Seed Script – `scripts/seed-db.ts`
339
+
340
+ ```typescript
341
+ import { dbClient } from "../src/core/test-harness/dbClient";
342
+
343
+ async function seed() {
344
+ await dbClient.query(`INSERT INTO Area(Id, Name) VALUES (101, 'London')`);
345
+ await dbClient.query(`
346
+ INSERT INTO SchedulingTeam(Id, Name, AreaId)
347
+ VALUES (201,'TeamA',101), (202,'TeamB',101)
348
+ `);
349
+ console.log("DB seeded successfully");
350
+ }
351
+
352
+ seed();
353
+ ```
354
+
355
+ ### Reset Script – `scripts/reset-db.ts`
356
+
357
+ ```typescript
358
+ import { dbClient } from "../src/core/test-harness/dbClient";
359
+
360
+ async function reset() {
361
+ await dbClient.query(`TRUNCATE TABLE SchedulingGroup`);
362
+ await dbClient.query(`TRUNCATE TABLE SchedulingGroupTeam`);
363
+ await dbClient.query(`TRUNCATE TABLE SchedulingGroupHistory`);
364
+ console.log("DB reset successfully");
365
+ }
366
+
367
+ reset();
368
+ ```
369
+
370
+ ## Package Configuration
371
+
372
+ ### package.json Scripts
373
+
374
+ ```json
375
+ {
376
+ "scripts": {
377
+ "seed-db": "ts-node scripts/seed-db.ts",
378
+ "reset-db": "ts-node scripts/reset-db.ts",
379
+ "apitest": "playwright test --project=api",
380
+ "uitest": "playwright test --project=ui",
381
+ "test": "playwright test --project=integrated"
382
+ }
383
+ }
384
+ ```
385
+
386
+ ## Running Tests
387
+
388
+ 1. **Reset the database:**
389
+ ```bash
390
+ npm run reset-db
391
+ ```
392
+
393
+ 2. **Seed the database:**
394
+ ```bash
395
+ npm run seed-db
396
+ ```
397
+
398
+ 3. **Run API/DB tests:**
399
+ ```bash
400
+ npm run apitest
401
+ ```
402
+
403
+ 4. **View reports and logs:**
404
+ Check the `reports/` directory for HTML reports and detailed logs.