agents-cli-automation 1.0.19 → 1.0.21
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.
|
@@ -1,404 +0,0 @@
|
|
|
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.
|