@bernierllc/content-management-tests 0.1.1
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/README.md +522 -0
- package/dist/index.js +1104 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1068 -0
- package/dist/index.mjs.map +1 -0
- package/jest.config.js +33 -0
- package/package.json +125 -0
- package/playwright.config.ts +48 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
10
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
|
+
|
|
34
|
+
// jest.config.js
|
|
35
|
+
var require_jest_config = __commonJS({
|
|
36
|
+
"jest.config.js"(exports2, module2) {
|
|
37
|
+
"use strict";
|
|
38
|
+
module2.exports = {
|
|
39
|
+
testEnvironment: "jsdom",
|
|
40
|
+
setupFilesAfterEnv: ["<rootDir>/src/setup/jest.setup.ts"],
|
|
41
|
+
testMatch: [
|
|
42
|
+
"<rootDir>/src/**/*.test.ts",
|
|
43
|
+
"<rootDir>/src/**/*.test.tsx"
|
|
44
|
+
],
|
|
45
|
+
collectCoverageFrom: [
|
|
46
|
+
"src/**/*.{ts,tsx}",
|
|
47
|
+
"!src/**/*.test.{ts,tsx}",
|
|
48
|
+
"!src/**/*.stories.{ts,tsx}",
|
|
49
|
+
"!src/setup/**",
|
|
50
|
+
"!src/fixtures/**",
|
|
51
|
+
"!src/mocks/**"
|
|
52
|
+
],
|
|
53
|
+
coverageThreshold: {
|
|
54
|
+
global: {
|
|
55
|
+
branches: 80,
|
|
56
|
+
functions: 80,
|
|
57
|
+
lines: 80,
|
|
58
|
+
statements: 80
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
moduleNameMapper: {
|
|
62
|
+
"^@/(.*)$": "<rootDir>/src/$1",
|
|
63
|
+
"^@test/(.*)$": "<rootDir>/src/test-utils/$1",
|
|
64
|
+
"^@fixtures/(.*)$": "<rootDir>/src/fixtures/$1",
|
|
65
|
+
"^@mocks/(.*)$": "<rootDir>/src/mocks/$1"
|
|
66
|
+
},
|
|
67
|
+
transform: {
|
|
68
|
+
"^.+\\.(ts|tsx)$": "ts-jest"
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// src/index.ts
|
|
75
|
+
var src_exports = {};
|
|
76
|
+
__export(src_exports, {
|
|
77
|
+
TestAssertions: () => TestAssertions,
|
|
78
|
+
TestDataGenerator: () => TestDataGenerator,
|
|
79
|
+
TestHelpers: () => TestHelpers,
|
|
80
|
+
TestRunner: () => TestRunner,
|
|
81
|
+
TestUtils: () => TestUtils,
|
|
82
|
+
apiResponseFixtures: () => apiResponseFixtures,
|
|
83
|
+
configFixtures: () => configFixtures,
|
|
84
|
+
contentFixtures: () => contentFixtures,
|
|
85
|
+
contentTypeFixtures: () => contentTypeFixtures,
|
|
86
|
+
createTestUtils: () => createTestUtils,
|
|
87
|
+
dateFixtures: () => dateFixtures,
|
|
88
|
+
defaultTestConfig: () => defaultTestConfig,
|
|
89
|
+
expect: () => import_test3.expect,
|
|
90
|
+
fireEvent: () => import_react2.fireEvent,
|
|
91
|
+
jestConfig: () => import_jest.default,
|
|
92
|
+
playwrightConfig: () => playwright_config_default,
|
|
93
|
+
render: () => import_react2.render,
|
|
94
|
+
screen: () => import_react2.screen,
|
|
95
|
+
test: () => import_test3.test,
|
|
96
|
+
testDataGenerators: () => testDataGenerators,
|
|
97
|
+
userEvent: () => import_user_event.userEvent,
|
|
98
|
+
userFixtures: () => userFixtures,
|
|
99
|
+
waitFor: () => import_react2.waitFor,
|
|
100
|
+
workflowFixtures: () => workflowFixtures
|
|
101
|
+
});
|
|
102
|
+
module.exports = __toCommonJS(src_exports);
|
|
103
|
+
|
|
104
|
+
// src/test-utils/test-utils.ts
|
|
105
|
+
var import_test = require("@playwright/test");
|
|
106
|
+
var TestUtils = class {
|
|
107
|
+
constructor(page, context) {
|
|
108
|
+
this.page = page;
|
|
109
|
+
this.context = context;
|
|
110
|
+
}
|
|
111
|
+
// Navigation helpers
|
|
112
|
+
async navigateTo(path) {
|
|
113
|
+
await this.page.goto(path);
|
|
114
|
+
await this.page.waitForLoadState("networkidle");
|
|
115
|
+
}
|
|
116
|
+
async waitForElement(selector, timeout = 5e3) {
|
|
117
|
+
await this.page.waitForSelector(selector, { timeout });
|
|
118
|
+
}
|
|
119
|
+
async waitForText(text, timeout = 5e3) {
|
|
120
|
+
await this.page.waitForSelector(`text=${text}`, { timeout });
|
|
121
|
+
}
|
|
122
|
+
// Form helpers
|
|
123
|
+
async fillForm(formData) {
|
|
124
|
+
for (const [field, value] of Object.entries(formData)) {
|
|
125
|
+
await this.page.fill(`[name="${field}"]`, value);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async clickButton(text) {
|
|
129
|
+
await this.page.click(`button:has-text("${text}")`);
|
|
130
|
+
}
|
|
131
|
+
async clickLink(text) {
|
|
132
|
+
await this.page.click(`a:has-text("${text}")`);
|
|
133
|
+
}
|
|
134
|
+
// Content management helpers
|
|
135
|
+
async createContent(type, data) {
|
|
136
|
+
await this.navigateTo("/admin/content/new");
|
|
137
|
+
await this.page.selectOption('[name="contentType"]', type);
|
|
138
|
+
await this.fillForm(data);
|
|
139
|
+
await this.clickButton("Save as Draft");
|
|
140
|
+
await this.waitForText("Content saved successfully");
|
|
141
|
+
}
|
|
142
|
+
async editContent(contentId, updates) {
|
|
143
|
+
await this.navigateTo(`/admin/content/${contentId}/edit`);
|
|
144
|
+
await this.fillForm(updates);
|
|
145
|
+
await this.clickButton("Save Changes");
|
|
146
|
+
await this.waitForText("Content updated successfully");
|
|
147
|
+
}
|
|
148
|
+
async publishContent(contentId) {
|
|
149
|
+
await this.navigateTo(`/admin/content/${contentId}/edit`);
|
|
150
|
+
await this.clickButton("Publish");
|
|
151
|
+
await this.waitForText("Content published successfully");
|
|
152
|
+
}
|
|
153
|
+
async scheduleContent(contentId, date) {
|
|
154
|
+
await this.navigateTo(`/admin/content/${contentId}/edit`);
|
|
155
|
+
await this.page.fill('[name="publishDate"]', date);
|
|
156
|
+
await this.clickButton("Schedule");
|
|
157
|
+
await this.waitForText("Content scheduled successfully");
|
|
158
|
+
}
|
|
159
|
+
async deleteContent(contentId, soft = true) {
|
|
160
|
+
await this.navigateTo(`/admin/content/${contentId}/edit`);
|
|
161
|
+
await this.clickButton("Delete");
|
|
162
|
+
if (!soft) {
|
|
163
|
+
await this.page.click('[data-testid="confirm-delete"]');
|
|
164
|
+
}
|
|
165
|
+
await this.waitForText("Content deleted successfully");
|
|
166
|
+
}
|
|
167
|
+
// Workflow helpers
|
|
168
|
+
async createWorkflow(workflowData) {
|
|
169
|
+
await this.navigateTo("/admin/workflows/new");
|
|
170
|
+
await this.fillForm(workflowData);
|
|
171
|
+
await this.clickButton("Create Workflow");
|
|
172
|
+
await this.waitForText("Workflow created successfully");
|
|
173
|
+
}
|
|
174
|
+
async updateWorkflow(workflowId, updates) {
|
|
175
|
+
await this.navigateTo(`/admin/workflows/${workflowId}/edit`);
|
|
176
|
+
await this.fillForm(updates);
|
|
177
|
+
await this.clickButton("Update Workflow");
|
|
178
|
+
await this.waitForText("Workflow updated successfully");
|
|
179
|
+
}
|
|
180
|
+
async deleteWorkflow(workflowId) {
|
|
181
|
+
await this.navigateTo(`/admin/workflows/${workflowId}/edit`);
|
|
182
|
+
await this.clickButton("Delete Workflow");
|
|
183
|
+
await this.page.click('[data-testid="confirm-delete"]');
|
|
184
|
+
await this.waitForText("Workflow deleted successfully");
|
|
185
|
+
}
|
|
186
|
+
// Content type helpers
|
|
187
|
+
async createContentType(contentTypeData) {
|
|
188
|
+
await this.navigateTo("/admin/content-types/new");
|
|
189
|
+
await this.fillForm(contentTypeData);
|
|
190
|
+
await this.clickButton("Create Content Type");
|
|
191
|
+
await this.waitForText("Content type created successfully");
|
|
192
|
+
}
|
|
193
|
+
async updateContentType(contentTypeId, updates) {
|
|
194
|
+
await this.navigateTo(`/admin/content-types/${contentTypeId}/edit`);
|
|
195
|
+
await this.fillForm(updates);
|
|
196
|
+
await this.clickButton("Update Content Type");
|
|
197
|
+
await this.waitForText("Content type updated successfully");
|
|
198
|
+
}
|
|
199
|
+
async deleteContentType(contentTypeId) {
|
|
200
|
+
await this.navigateTo(`/admin/content-types/${contentTypeId}/edit`);
|
|
201
|
+
await this.clickButton("Delete Content Type");
|
|
202
|
+
await this.page.click('[data-testid="confirm-delete"]');
|
|
203
|
+
await this.waitForText("Content type deleted successfully");
|
|
204
|
+
}
|
|
205
|
+
// User helpers
|
|
206
|
+
async createUser(userData) {
|
|
207
|
+
await this.navigateTo("/admin/users/new");
|
|
208
|
+
await this.fillForm(userData);
|
|
209
|
+
await this.clickButton("Create User");
|
|
210
|
+
await this.waitForText("User created successfully");
|
|
211
|
+
}
|
|
212
|
+
async updateUser(userId, updates) {
|
|
213
|
+
await this.navigateTo(`/admin/users/${userId}/edit`);
|
|
214
|
+
await this.fillForm(updates);
|
|
215
|
+
await this.clickButton("Update User");
|
|
216
|
+
await this.waitForText("User updated successfully");
|
|
217
|
+
}
|
|
218
|
+
async deleteUser(userId) {
|
|
219
|
+
await this.navigateTo(`/admin/users/${userId}/edit`);
|
|
220
|
+
await this.clickButton("Delete User");
|
|
221
|
+
await this.page.click('[data-testid="confirm-delete"]');
|
|
222
|
+
await this.waitForText("User deleted successfully");
|
|
223
|
+
}
|
|
224
|
+
// Authentication helpers
|
|
225
|
+
async login(email, password) {
|
|
226
|
+
await this.navigateTo("/login");
|
|
227
|
+
await this.fillForm({ email, password });
|
|
228
|
+
await this.clickButton("Login");
|
|
229
|
+
await this.waitForText("Welcome");
|
|
230
|
+
}
|
|
231
|
+
async logout() {
|
|
232
|
+
await this.clickButton("Logout");
|
|
233
|
+
await this.waitForText("Logged out successfully");
|
|
234
|
+
}
|
|
235
|
+
// API helpers
|
|
236
|
+
async apiRequest(method, endpoint, data) {
|
|
237
|
+
const response = await this.page.request.fetch(`/api${endpoint}`, {
|
|
238
|
+
method,
|
|
239
|
+
headers: {
|
|
240
|
+
"Content-Type": "application/json"
|
|
241
|
+
},
|
|
242
|
+
data: data ? JSON.stringify(data) : void 0
|
|
243
|
+
});
|
|
244
|
+
return response;
|
|
245
|
+
}
|
|
246
|
+
async getContent(contentId) {
|
|
247
|
+
const response = await this.apiRequest("GET", `/content/${contentId}`);
|
|
248
|
+
return response.json();
|
|
249
|
+
}
|
|
250
|
+
async createContentViaAPI(type, data) {
|
|
251
|
+
const response = await this.apiRequest("POST", "/content", { type, data });
|
|
252
|
+
return response.json();
|
|
253
|
+
}
|
|
254
|
+
async updateContentViaAPI(contentId, data) {
|
|
255
|
+
const response = await this.apiRequest("PUT", `/content/${contentId}`, { data });
|
|
256
|
+
return response.json();
|
|
257
|
+
}
|
|
258
|
+
async deleteContentViaAPI(contentId, soft = true) {
|
|
259
|
+
const response = await this.apiRequest("DELETE", `/content/${contentId}?soft=${soft}`);
|
|
260
|
+
return response;
|
|
261
|
+
}
|
|
262
|
+
// Assertion helpers
|
|
263
|
+
async expectElementVisible(selector) {
|
|
264
|
+
await (0, import_test.expect)(this.page.locator(selector)).toBeVisible();
|
|
265
|
+
}
|
|
266
|
+
async expectElementHidden(selector) {
|
|
267
|
+
await (0, import_test.expect)(this.page.locator(selector)).toBeHidden();
|
|
268
|
+
}
|
|
269
|
+
async expectTextVisible(text) {
|
|
270
|
+
await (0, import_test.expect)(this.page.locator(`text=${text}`)).toBeVisible();
|
|
271
|
+
}
|
|
272
|
+
async expectTextHidden(text) {
|
|
273
|
+
await (0, import_test.expect)(this.page.locator(`text=${text}`)).toBeHidden();
|
|
274
|
+
}
|
|
275
|
+
async expectElementCount(selector, count) {
|
|
276
|
+
await (0, import_test.expect)(this.page.locator(selector)).toHaveCount(count);
|
|
277
|
+
}
|
|
278
|
+
async expectElementText(selector, text) {
|
|
279
|
+
await (0, import_test.expect)(this.page.locator(selector)).toHaveText(text);
|
|
280
|
+
}
|
|
281
|
+
async expectElementValue(selector, value) {
|
|
282
|
+
await (0, import_test.expect)(this.page.locator(selector)).toHaveValue(value);
|
|
283
|
+
}
|
|
284
|
+
// Screenshot helpers
|
|
285
|
+
async takeScreenshot(name) {
|
|
286
|
+
await this.page.screenshot({ path: `test-results/screenshots/${name}.png` });
|
|
287
|
+
}
|
|
288
|
+
async takeElementScreenshot(selector, name) {
|
|
289
|
+
await this.page.locator(selector).screenshot({ path: `test-results/screenshots/${name}.png` });
|
|
290
|
+
}
|
|
291
|
+
// Performance helpers
|
|
292
|
+
async measurePerformance(name, fn) {
|
|
293
|
+
const start = Date.now();
|
|
294
|
+
await fn();
|
|
295
|
+
const end = Date.now();
|
|
296
|
+
console.log(`Performance: ${name} took ${end - start}ms`);
|
|
297
|
+
return end - start;
|
|
298
|
+
}
|
|
299
|
+
// Accessibility helpers
|
|
300
|
+
async checkAccessibility() {
|
|
301
|
+
const elements = await this.page.locator("[role]").all();
|
|
302
|
+
(0, import_test.expect)(elements.length).toBeGreaterThan(0);
|
|
303
|
+
}
|
|
304
|
+
// Mobile helpers
|
|
305
|
+
async switchToMobile() {
|
|
306
|
+
await this.page.setViewportSize({ width: 375, height: 667 });
|
|
307
|
+
}
|
|
308
|
+
async switchToTablet() {
|
|
309
|
+
await this.page.setViewportSize({ width: 768, height: 1024 });
|
|
310
|
+
}
|
|
311
|
+
async switchToDesktop() {
|
|
312
|
+
await this.page.setViewportSize({ width: 1920, height: 1080 });
|
|
313
|
+
}
|
|
314
|
+
// Cleanup helpers
|
|
315
|
+
async cleanup() {
|
|
316
|
+
await this.page.evaluate(() => {
|
|
317
|
+
localStorage.clear();
|
|
318
|
+
sessionStorage.clear();
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
function createTestUtils(page, context) {
|
|
323
|
+
return new TestUtils(page, context);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/fixtures/index.ts
|
|
327
|
+
var import_faker = require("@faker-js/faker");
|
|
328
|
+
var import_uuid = require("uuid");
|
|
329
|
+
var import_date_fns = require("date-fns");
|
|
330
|
+
var contentFixtures = {
|
|
331
|
+
text: {
|
|
332
|
+
id: (0, import_uuid.v4)(),
|
|
333
|
+
type: "text",
|
|
334
|
+
title: import_faker.faker.lorem.sentence(),
|
|
335
|
+
slug: import_faker.faker.lorem.slug(),
|
|
336
|
+
body: import_faker.faker.lorem.paragraphs(3),
|
|
337
|
+
excerpt: import_faker.faker.lorem.sentence(),
|
|
338
|
+
tags: import_faker.faker.lorem.words(3).split(" "),
|
|
339
|
+
categories: ["general"],
|
|
340
|
+
author: {
|
|
341
|
+
id: (0, import_uuid.v4)(),
|
|
342
|
+
name: import_faker.faker.person.fullName(),
|
|
343
|
+
email: import_faker.faker.internet.email()
|
|
344
|
+
},
|
|
345
|
+
status: "draft",
|
|
346
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
347
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
348
|
+
publishedAt: null
|
|
349
|
+
},
|
|
350
|
+
image: {
|
|
351
|
+
id: (0, import_uuid.v4)(),
|
|
352
|
+
type: "image",
|
|
353
|
+
title: import_faker.faker.lorem.sentence(),
|
|
354
|
+
slug: import_faker.faker.lorem.slug(),
|
|
355
|
+
description: import_faker.faker.lorem.sentence(),
|
|
356
|
+
altText: import_faker.faker.lorem.sentence(),
|
|
357
|
+
imageUrl: import_faker.faker.image.url(),
|
|
358
|
+
tags: import_faker.faker.lorem.words(3).split(" "),
|
|
359
|
+
categories: ["photography"],
|
|
360
|
+
author: {
|
|
361
|
+
id: (0, import_uuid.v4)(),
|
|
362
|
+
name: import_faker.faker.person.fullName(),
|
|
363
|
+
email: import_faker.faker.internet.email()
|
|
364
|
+
},
|
|
365
|
+
status: "draft",
|
|
366
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
367
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
368
|
+
publishedAt: null
|
|
369
|
+
},
|
|
370
|
+
audio: {
|
|
371
|
+
id: (0, import_uuid.v4)(),
|
|
372
|
+
type: "audio",
|
|
373
|
+
title: import_faker.faker.lorem.sentence(),
|
|
374
|
+
slug: import_faker.faker.lorem.slug(),
|
|
375
|
+
description: import_faker.faker.lorem.sentence(),
|
|
376
|
+
audioUrl: import_faker.faker.internet.url(),
|
|
377
|
+
duration: import_faker.faker.number.int({ min: 60, max: 3600 }),
|
|
378
|
+
metadata: {
|
|
379
|
+
artist: import_faker.faker.person.fullName(),
|
|
380
|
+
album: import_faker.faker.lorem.words(2),
|
|
381
|
+
year: import_faker.faker.date.past().getFullYear()
|
|
382
|
+
},
|
|
383
|
+
tags: import_faker.faker.lorem.words(3).split(" "),
|
|
384
|
+
categories: ["podcast"],
|
|
385
|
+
author: {
|
|
386
|
+
id: (0, import_uuid.v4)(),
|
|
387
|
+
name: import_faker.faker.person.fullName(),
|
|
388
|
+
email: import_faker.faker.internet.email()
|
|
389
|
+
},
|
|
390
|
+
status: "draft",
|
|
391
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
392
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
393
|
+
publishedAt: null
|
|
394
|
+
},
|
|
395
|
+
video: {
|
|
396
|
+
id: (0, import_uuid.v4)(),
|
|
397
|
+
type: "video",
|
|
398
|
+
title: import_faker.faker.lorem.sentence(),
|
|
399
|
+
slug: import_faker.faker.lorem.slug(),
|
|
400
|
+
description: import_faker.faker.lorem.sentence(),
|
|
401
|
+
videoUrl: import_faker.faker.internet.url(),
|
|
402
|
+
duration: import_faker.faker.number.int({ min: 60, max: 3600 }),
|
|
403
|
+
dimensions: {
|
|
404
|
+
width: 1920,
|
|
405
|
+
height: 1080
|
|
406
|
+
},
|
|
407
|
+
metadata: {
|
|
408
|
+
director: import_faker.faker.person.fullName(),
|
|
409
|
+
genre: import_faker.faker.lorem.word(),
|
|
410
|
+
year: import_faker.faker.date.past().getFullYear()
|
|
411
|
+
},
|
|
412
|
+
tags: import_faker.faker.lorem.words(3).split(" "),
|
|
413
|
+
categories: ["tutorial"],
|
|
414
|
+
author: {
|
|
415
|
+
id: (0, import_uuid.v4)(),
|
|
416
|
+
name: import_faker.faker.person.fullName(),
|
|
417
|
+
email: import_faker.faker.internet.email()
|
|
418
|
+
},
|
|
419
|
+
status: "draft",
|
|
420
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
421
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
422
|
+
publishedAt: null
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
var workflowFixtures = {
|
|
426
|
+
simple: {
|
|
427
|
+
id: "simple",
|
|
428
|
+
name: "Simple Workflow",
|
|
429
|
+
description: "A simple two-stage workflow",
|
|
430
|
+
stages: [
|
|
431
|
+
{
|
|
432
|
+
id: "draft",
|
|
433
|
+
name: "Draft",
|
|
434
|
+
order: 1,
|
|
435
|
+
isPublishStage: false,
|
|
436
|
+
allowsScheduling: false,
|
|
437
|
+
permissions: ["content.edit"]
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
id: "published",
|
|
441
|
+
name: "Published",
|
|
442
|
+
order: 2,
|
|
443
|
+
isPublishStage: true,
|
|
444
|
+
allowsScheduling: true,
|
|
445
|
+
permissions: ["content.publish"]
|
|
446
|
+
}
|
|
447
|
+
],
|
|
448
|
+
transitions: [
|
|
449
|
+
{
|
|
450
|
+
id: "draft-to-published",
|
|
451
|
+
from: "draft",
|
|
452
|
+
to: "published",
|
|
453
|
+
permissions: ["content.publish"]
|
|
454
|
+
}
|
|
455
|
+
]
|
|
456
|
+
},
|
|
457
|
+
editorial: {
|
|
458
|
+
id: "editorial",
|
|
459
|
+
name: "Editorial Workflow",
|
|
460
|
+
description: "A comprehensive editorial workflow",
|
|
461
|
+
stages: [
|
|
462
|
+
{
|
|
463
|
+
id: "write",
|
|
464
|
+
name: "Write",
|
|
465
|
+
order: 1,
|
|
466
|
+
isPublishStage: false,
|
|
467
|
+
allowsScheduling: false,
|
|
468
|
+
permissions: ["content.edit"]
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
id: "edit",
|
|
472
|
+
name: "Edit",
|
|
473
|
+
order: 2,
|
|
474
|
+
isPublishStage: false,
|
|
475
|
+
allowsScheduling: false,
|
|
476
|
+
permissions: ["content.edit"]
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
id: "review",
|
|
480
|
+
name: "Review",
|
|
481
|
+
order: 3,
|
|
482
|
+
isPublishStage: false,
|
|
483
|
+
allowsScheduling: false,
|
|
484
|
+
permissions: ["content.review"]
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
id: "publish",
|
|
488
|
+
name: "Publish",
|
|
489
|
+
order: 4,
|
|
490
|
+
isPublishStage: true,
|
|
491
|
+
allowsScheduling: true,
|
|
492
|
+
permissions: ["content.publish"]
|
|
493
|
+
}
|
|
494
|
+
],
|
|
495
|
+
transitions: [
|
|
496
|
+
{
|
|
497
|
+
id: "write-to-edit",
|
|
498
|
+
from: "write",
|
|
499
|
+
to: "edit",
|
|
500
|
+
permissions: ["content.edit"]
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
id: "edit-to-review",
|
|
504
|
+
from: "edit",
|
|
505
|
+
to: "review",
|
|
506
|
+
permissions: ["content.review"]
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
id: "review-to-publish",
|
|
510
|
+
from: "review",
|
|
511
|
+
to: "publish",
|
|
512
|
+
permissions: ["content.publish"]
|
|
513
|
+
}
|
|
514
|
+
]
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
var userFixtures = {
|
|
518
|
+
admin: {
|
|
519
|
+
id: (0, import_uuid.v4)(),
|
|
520
|
+
name: "Admin User",
|
|
521
|
+
email: "admin@example.com",
|
|
522
|
+
role: "admin",
|
|
523
|
+
permissions: ["*"],
|
|
524
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
525
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
526
|
+
},
|
|
527
|
+
editor: {
|
|
528
|
+
id: (0, import_uuid.v4)(),
|
|
529
|
+
name: "Editor User",
|
|
530
|
+
email: "editor@example.com",
|
|
531
|
+
role: "editor",
|
|
532
|
+
permissions: ["content.edit", "content.publish", "content.schedule"],
|
|
533
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
534
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
535
|
+
},
|
|
536
|
+
author: {
|
|
537
|
+
id: (0, import_uuid.v4)(),
|
|
538
|
+
name: "Author User",
|
|
539
|
+
email: "author@example.com",
|
|
540
|
+
role: "author",
|
|
541
|
+
permissions: ["content.edit"],
|
|
542
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
543
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
544
|
+
},
|
|
545
|
+
user: {
|
|
546
|
+
id: (0, import_uuid.v4)(),
|
|
547
|
+
name: "Regular User",
|
|
548
|
+
email: "user@example.com",
|
|
549
|
+
role: "user",
|
|
550
|
+
permissions: ["content.view"],
|
|
551
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
552
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
var contentTypeFixtures = {
|
|
556
|
+
text: {
|
|
557
|
+
id: "text",
|
|
558
|
+
name: "Text Content",
|
|
559
|
+
description: "Rich text content with formatting",
|
|
560
|
+
schema: {
|
|
561
|
+
title: { type: "string", required: true },
|
|
562
|
+
slug: { type: "string", required: true },
|
|
563
|
+
body: { type: "string", required: true },
|
|
564
|
+
excerpt: { type: "string", required: false },
|
|
565
|
+
tags: { type: "array", required: false },
|
|
566
|
+
categories: { type: "array", required: false }
|
|
567
|
+
},
|
|
568
|
+
editorConfig: {
|
|
569
|
+
component: "TextEditor",
|
|
570
|
+
fields: [
|
|
571
|
+
{ name: "title", type: "text", label: "Title", required: true },
|
|
572
|
+
{ name: "slug", type: "text", label: "Slug", required: true },
|
|
573
|
+
{ name: "body", type: "textarea", label: "Body", required: true },
|
|
574
|
+
{ name: "excerpt", type: "textarea", label: "Excerpt", required: false },
|
|
575
|
+
{ name: "tags", type: "tags", label: "Tags", required: false },
|
|
576
|
+
{ name: "categories", type: "select", label: "Categories", required: false }
|
|
577
|
+
]
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
image: {
|
|
581
|
+
id: "image",
|
|
582
|
+
name: "Image Content",
|
|
583
|
+
description: "Image content with metadata",
|
|
584
|
+
schema: {
|
|
585
|
+
title: { type: "string", required: true },
|
|
586
|
+
slug: { type: "string", required: true },
|
|
587
|
+
description: { type: "string", required: false },
|
|
588
|
+
altText: { type: "string", required: true },
|
|
589
|
+
imageUrl: { type: "string", required: true },
|
|
590
|
+
tags: { type: "array", required: false },
|
|
591
|
+
categories: { type: "array", required: false }
|
|
592
|
+
},
|
|
593
|
+
editorConfig: {
|
|
594
|
+
component: "ImageEditor",
|
|
595
|
+
fields: [
|
|
596
|
+
{ name: "title", type: "text", label: "Title", required: true },
|
|
597
|
+
{ name: "slug", type: "text", label: "Slug", required: true },
|
|
598
|
+
{ name: "description", type: "textarea", label: "Description", required: false },
|
|
599
|
+
{ name: "altText", type: "text", label: "Alt Text", required: true },
|
|
600
|
+
{ name: "imageUrl", type: "url", label: "Image URL", required: true },
|
|
601
|
+
{ name: "tags", type: "tags", label: "Tags", required: false },
|
|
602
|
+
{ name: "categories", type: "select", label: "Categories", required: false }
|
|
603
|
+
]
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
var configFixtures = {
|
|
608
|
+
development: {
|
|
609
|
+
server: {
|
|
610
|
+
port: 3e3,
|
|
611
|
+
host: "localhost",
|
|
612
|
+
cors: {
|
|
613
|
+
origin: "*",
|
|
614
|
+
credentials: true
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
database: {
|
|
618
|
+
type: "sqlite",
|
|
619
|
+
name: "content_management_dev"
|
|
620
|
+
},
|
|
621
|
+
logging: {
|
|
622
|
+
level: "debug",
|
|
623
|
+
format: "text",
|
|
624
|
+
console: {
|
|
625
|
+
enabled: true,
|
|
626
|
+
colorize: true
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
production: {
|
|
631
|
+
server: {
|
|
632
|
+
port: 80,
|
|
633
|
+
host: "0.0.0.0",
|
|
634
|
+
cors: {
|
|
635
|
+
origin: ["https://myapp.com"],
|
|
636
|
+
credentials: true
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
database: {
|
|
640
|
+
type: "postgresql",
|
|
641
|
+
url: "postgresql://user:password@localhost:5432/content_management"
|
|
642
|
+
},
|
|
643
|
+
logging: {
|
|
644
|
+
level: "info",
|
|
645
|
+
format: "json",
|
|
646
|
+
file: {
|
|
647
|
+
enabled: true,
|
|
648
|
+
path: "./logs",
|
|
649
|
+
maxSize: "10MB",
|
|
650
|
+
maxFiles: 5
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
var testDataGenerators = {
|
|
656
|
+
generateContent: (type, overrides = {}) => {
|
|
657
|
+
const base = contentFixtures[type];
|
|
658
|
+
return {
|
|
659
|
+
...base,
|
|
660
|
+
id: (0, import_uuid.v4)(),
|
|
661
|
+
...overrides
|
|
662
|
+
};
|
|
663
|
+
},
|
|
664
|
+
generateWorkflow: (overrides = {}) => {
|
|
665
|
+
return {
|
|
666
|
+
...workflowFixtures.simple,
|
|
667
|
+
id: (0, import_uuid.v4)(),
|
|
668
|
+
...overrides
|
|
669
|
+
};
|
|
670
|
+
},
|
|
671
|
+
generateUser: (role = "user", overrides = {}) => {
|
|
672
|
+
const base = userFixtures[role];
|
|
673
|
+
return {
|
|
674
|
+
...base,
|
|
675
|
+
id: (0, import_uuid.v4)(),
|
|
676
|
+
...overrides
|
|
677
|
+
};
|
|
678
|
+
},
|
|
679
|
+
generateContentType: (overrides = {}) => {
|
|
680
|
+
return {
|
|
681
|
+
...contentTypeFixtures.text,
|
|
682
|
+
id: (0, import_uuid.v4)(),
|
|
683
|
+
...overrides
|
|
684
|
+
};
|
|
685
|
+
},
|
|
686
|
+
generateConfig: (environment = "development", overrides = {}) => {
|
|
687
|
+
const base = configFixtures[environment];
|
|
688
|
+
return {
|
|
689
|
+
...base,
|
|
690
|
+
...overrides
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
};
|
|
694
|
+
var dateFixtures = {
|
|
695
|
+
now: /* @__PURE__ */ new Date(),
|
|
696
|
+
tomorrow: (0, import_date_fns.addDays)(/* @__PURE__ */ new Date(), 1),
|
|
697
|
+
yesterday: (0, import_date_fns.subDays)(/* @__PURE__ */ new Date(), 1),
|
|
698
|
+
nextWeek: (0, import_date_fns.addDays)(/* @__PURE__ */ new Date(), 7),
|
|
699
|
+
lastWeek: (0, import_date_fns.subDays)(/* @__PURE__ */ new Date(), 7),
|
|
700
|
+
nextMonth: (0, import_date_fns.addDays)(/* @__PURE__ */ new Date(), 30),
|
|
701
|
+
lastMonth: (0, import_date_fns.subDays)(/* @__PURE__ */ new Date(), 30)
|
|
702
|
+
};
|
|
703
|
+
var apiResponseFixtures = {
|
|
704
|
+
success: {
|
|
705
|
+
success: true,
|
|
706
|
+
message: "Operation completed successfully",
|
|
707
|
+
data: null
|
|
708
|
+
},
|
|
709
|
+
error: {
|
|
710
|
+
success: false,
|
|
711
|
+
message: "An error occurred",
|
|
712
|
+
error: {
|
|
713
|
+
code: "INTERNAL_ERROR",
|
|
714
|
+
details: "Something went wrong"
|
|
715
|
+
}
|
|
716
|
+
},
|
|
717
|
+
validationError: {
|
|
718
|
+
success: false,
|
|
719
|
+
message: "Validation failed",
|
|
720
|
+
error: {
|
|
721
|
+
code: "VALIDATION_ERROR",
|
|
722
|
+
details: {
|
|
723
|
+
field: "title",
|
|
724
|
+
message: "Title is required"
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
// src/setup/jest.setup.ts
|
|
731
|
+
var import_jest_dom = require("@testing-library/jest-dom");
|
|
732
|
+
var import_react = require("@testing-library/react");
|
|
733
|
+
var import_util = require("util");
|
|
734
|
+
(0, import_react.configure)({ testIdAttribute: "data-testid" });
|
|
735
|
+
global.TextEncoder = import_util.TextEncoder;
|
|
736
|
+
global.TextDecoder = import_util.TextDecoder;
|
|
737
|
+
global.IntersectionObserver = class IntersectionObserver {
|
|
738
|
+
constructor() {
|
|
739
|
+
}
|
|
740
|
+
disconnect() {
|
|
741
|
+
}
|
|
742
|
+
observe() {
|
|
743
|
+
}
|
|
744
|
+
unobserve() {
|
|
745
|
+
}
|
|
746
|
+
};
|
|
747
|
+
global.ResizeObserver = class ResizeObserver {
|
|
748
|
+
constructor() {
|
|
749
|
+
}
|
|
750
|
+
disconnect() {
|
|
751
|
+
}
|
|
752
|
+
observe() {
|
|
753
|
+
}
|
|
754
|
+
unobserve() {
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
Object.defineProperty(window, "matchMedia", {
|
|
758
|
+
writable: true,
|
|
759
|
+
value: jest.fn().mockImplementation((query) => ({
|
|
760
|
+
matches: false,
|
|
761
|
+
media: query,
|
|
762
|
+
onchange: null,
|
|
763
|
+
addListener: jest.fn(),
|
|
764
|
+
// deprecated
|
|
765
|
+
removeListener: jest.fn(),
|
|
766
|
+
// deprecated
|
|
767
|
+
addEventListener: jest.fn(),
|
|
768
|
+
removeEventListener: jest.fn(),
|
|
769
|
+
dispatchEvent: jest.fn()
|
|
770
|
+
}))
|
|
771
|
+
});
|
|
772
|
+
var localStorageMock = {
|
|
773
|
+
getItem: jest.fn(),
|
|
774
|
+
setItem: jest.fn(),
|
|
775
|
+
removeItem: jest.fn(),
|
|
776
|
+
clear: jest.fn()
|
|
777
|
+
};
|
|
778
|
+
global.localStorage = localStorageMock;
|
|
779
|
+
var sessionStorageMock = {
|
|
780
|
+
getItem: jest.fn(),
|
|
781
|
+
setItem: jest.fn(),
|
|
782
|
+
removeItem: jest.fn(),
|
|
783
|
+
clear: jest.fn()
|
|
784
|
+
};
|
|
785
|
+
global.sessionStorage = sessionStorageMock;
|
|
786
|
+
global.fetch = jest.fn();
|
|
787
|
+
var originalError = console.error;
|
|
788
|
+
var originalWarn = console.warn;
|
|
789
|
+
beforeAll(() => {
|
|
790
|
+
console.error = (...args) => {
|
|
791
|
+
if (typeof args[0] === "string" && args[0].includes("Warning: ReactDOM.render is no longer supported")) {
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
originalError.call(console, ...args);
|
|
795
|
+
};
|
|
796
|
+
console.warn = (...args) => {
|
|
797
|
+
if (typeof args[0] === "string" && args[0].includes("Warning: ReactDOM.render is no longer supported")) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
originalWarn.call(console, ...args);
|
|
801
|
+
};
|
|
802
|
+
});
|
|
803
|
+
afterAll(() => {
|
|
804
|
+
console.error = originalError;
|
|
805
|
+
console.warn = originalWarn;
|
|
806
|
+
});
|
|
807
|
+
afterEach(() => {
|
|
808
|
+
jest.clearAllMocks();
|
|
809
|
+
localStorageMock.clear();
|
|
810
|
+
sessionStorageMock.clear();
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
// src/index.ts
|
|
814
|
+
var import_react2 = require("@testing-library/react");
|
|
815
|
+
var import_user_event = require("@testing-library/user-event");
|
|
816
|
+
var import_test3 = require("@playwright/test");
|
|
817
|
+
|
|
818
|
+
// playwright.config.ts
|
|
819
|
+
var import_test2 = require("@playwright/test");
|
|
820
|
+
var playwright_config_default = (0, import_test2.defineConfig)({
|
|
821
|
+
testDir: "./src/playwright",
|
|
822
|
+
fullyParallel: true,
|
|
823
|
+
forbidOnly: !!process.env.CI,
|
|
824
|
+
retries: process.env.CI ? 2 : 0,
|
|
825
|
+
workers: process.env.CI ? 1 : void 0,
|
|
826
|
+
reporter: [
|
|
827
|
+
["html"],
|
|
828
|
+
["json", { outputFile: "test-results/results.json" }],
|
|
829
|
+
["junit", { outputFile: "test-results/results.xml" }]
|
|
830
|
+
],
|
|
831
|
+
use: {
|
|
832
|
+
baseURL: "http://localhost:3000",
|
|
833
|
+
trace: "on-first-retry",
|
|
834
|
+
screenshot: "only-on-failure",
|
|
835
|
+
video: "retain-on-failure"
|
|
836
|
+
},
|
|
837
|
+
projects: [
|
|
838
|
+
{
|
|
839
|
+
name: "chromium",
|
|
840
|
+
use: { ...import_test2.devices["Desktop Chrome"] }
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
name: "firefox",
|
|
844
|
+
use: { ...import_test2.devices["Desktop Firefox"] }
|
|
845
|
+
},
|
|
846
|
+
{
|
|
847
|
+
name: "webkit",
|
|
848
|
+
use: { ...import_test2.devices["Desktop Safari"] }
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
name: "Mobile Chrome",
|
|
852
|
+
use: { ...import_test2.devices["Pixel 5"] }
|
|
853
|
+
},
|
|
854
|
+
{
|
|
855
|
+
name: "Mobile Safari",
|
|
856
|
+
use: { ...import_test2.devices["iPhone 12"] }
|
|
857
|
+
}
|
|
858
|
+
],
|
|
859
|
+
webServer: {
|
|
860
|
+
command: "npm run start:test",
|
|
861
|
+
url: "http://localhost:3000",
|
|
862
|
+
reuseExistingServer: !process.env.CI,
|
|
863
|
+
timeout: 120 * 1e3
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
// src/index.ts
|
|
868
|
+
var import_jest = __toESM(require_jest_config());
|
|
869
|
+
var TestRunner = class {
|
|
870
|
+
constructor(config) {
|
|
871
|
+
this.suites = [];
|
|
872
|
+
this.config = config;
|
|
873
|
+
}
|
|
874
|
+
addSuite(suite) {
|
|
875
|
+
this.suites.push(suite);
|
|
876
|
+
}
|
|
877
|
+
async runSuite(suiteName) {
|
|
878
|
+
const suite = this.suites.find((s) => s.name === suiteName);
|
|
879
|
+
if (!suite) {
|
|
880
|
+
throw new Error(`Suite ${suiteName} not found`);
|
|
881
|
+
}
|
|
882
|
+
const results = [];
|
|
883
|
+
const startTime = Date.now();
|
|
884
|
+
try {
|
|
885
|
+
if (suite.setup) {
|
|
886
|
+
await suite.setup();
|
|
887
|
+
}
|
|
888
|
+
for (const testCase of suite.tests) {
|
|
889
|
+
if (testCase.skip) {
|
|
890
|
+
results.push({
|
|
891
|
+
name: testCase.name,
|
|
892
|
+
status: "skipped",
|
|
893
|
+
duration: 0
|
|
894
|
+
});
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
const testStartTime = Date.now();
|
|
898
|
+
try {
|
|
899
|
+
await testCase.test();
|
|
900
|
+
results.push({
|
|
901
|
+
name: testCase.name,
|
|
902
|
+
status: "passed",
|
|
903
|
+
duration: Date.now() - testStartTime
|
|
904
|
+
});
|
|
905
|
+
} catch (error) {
|
|
906
|
+
results.push({
|
|
907
|
+
name: testCase.name,
|
|
908
|
+
status: "failed",
|
|
909
|
+
duration: Date.now() - testStartTime,
|
|
910
|
+
error
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
} finally {
|
|
915
|
+
if (suite.teardown) {
|
|
916
|
+
await suite.teardown();
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
const summary = {
|
|
920
|
+
total: results.length,
|
|
921
|
+
passed: results.filter((r) => r.status === "passed").length,
|
|
922
|
+
failed: results.filter((r) => r.status === "failed").length,
|
|
923
|
+
skipped: results.filter((r) => r.status === "skipped").length,
|
|
924
|
+
duration: Date.now() - startTime
|
|
925
|
+
};
|
|
926
|
+
return {
|
|
927
|
+
suite: suiteName,
|
|
928
|
+
results,
|
|
929
|
+
summary
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
async runAllSuites() {
|
|
933
|
+
const reports = [];
|
|
934
|
+
for (const suite of this.suites) {
|
|
935
|
+
const report = await this.runSuite(suite.name);
|
|
936
|
+
reports.push(report);
|
|
937
|
+
}
|
|
938
|
+
return reports;
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
var TestDataGenerator = class {
|
|
942
|
+
static generateContent(type, overrides = {}) {
|
|
943
|
+
const base = {
|
|
944
|
+
id: `test-${Date.now()}`,
|
|
945
|
+
type,
|
|
946
|
+
title: `Test ${type} Content`,
|
|
947
|
+
slug: `test-${type}-content`,
|
|
948
|
+
body: `Test content for ${type}`,
|
|
949
|
+
author: {
|
|
950
|
+
id: "test-user",
|
|
951
|
+
name: "Test User",
|
|
952
|
+
email: "test@example.com"
|
|
953
|
+
},
|
|
954
|
+
status: "draft",
|
|
955
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
956
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
957
|
+
};
|
|
958
|
+
return { ...base, ...overrides };
|
|
959
|
+
}
|
|
960
|
+
static generateWorkflow(overrides = {}) {
|
|
961
|
+
const base = {
|
|
962
|
+
id: `test-workflow-${Date.now()}`,
|
|
963
|
+
name: "Test Workflow",
|
|
964
|
+
description: "Test workflow for testing",
|
|
965
|
+
stages: [
|
|
966
|
+
{
|
|
967
|
+
id: "draft",
|
|
968
|
+
name: "Draft",
|
|
969
|
+
order: 1,
|
|
970
|
+
isPublishStage: false,
|
|
971
|
+
allowsScheduling: false,
|
|
972
|
+
permissions: ["content.edit"]
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
id: "published",
|
|
976
|
+
name: "Published",
|
|
977
|
+
order: 2,
|
|
978
|
+
isPublishStage: true,
|
|
979
|
+
allowsScheduling: true,
|
|
980
|
+
permissions: ["content.publish"]
|
|
981
|
+
}
|
|
982
|
+
],
|
|
983
|
+
transitions: [
|
|
984
|
+
{
|
|
985
|
+
id: "draft-to-published",
|
|
986
|
+
from: "draft",
|
|
987
|
+
to: "published",
|
|
988
|
+
permissions: ["content.publish"]
|
|
989
|
+
}
|
|
990
|
+
]
|
|
991
|
+
};
|
|
992
|
+
return { ...base, ...overrides };
|
|
993
|
+
}
|
|
994
|
+
static generateUser(role = "user", overrides = {}) {
|
|
995
|
+
const base = {
|
|
996
|
+
id: `test-user-${Date.now()}`,
|
|
997
|
+
name: "Test User",
|
|
998
|
+
email: "test@example.com",
|
|
999
|
+
role,
|
|
1000
|
+
permissions: role === "admin" ? ["*"] : ["content.view"],
|
|
1001
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
1002
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
1003
|
+
};
|
|
1004
|
+
return { ...base, ...overrides };
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
var TestAssertions = class {
|
|
1008
|
+
static async expectElementVisible(page, selector) {
|
|
1009
|
+
const element = await page.locator(selector);
|
|
1010
|
+
await expect(element).toBeVisible();
|
|
1011
|
+
}
|
|
1012
|
+
static async expectElementHidden(page, selector) {
|
|
1013
|
+
const element = await page.locator(selector);
|
|
1014
|
+
await expect(element).toBeHidden();
|
|
1015
|
+
}
|
|
1016
|
+
static async expectTextVisible(page, text) {
|
|
1017
|
+
const element = await page.locator(`text=${text}`);
|
|
1018
|
+
await expect(element).toBeVisible();
|
|
1019
|
+
}
|
|
1020
|
+
static async expectTextHidden(page, text) {
|
|
1021
|
+
const element = await page.locator(`text=${text}`);
|
|
1022
|
+
await expect(element).toBeHidden();
|
|
1023
|
+
}
|
|
1024
|
+
static async expectElementCount(page, selector, count) {
|
|
1025
|
+
const elements = await page.locator(selector);
|
|
1026
|
+
await expect(elements).toHaveCount(count);
|
|
1027
|
+
}
|
|
1028
|
+
static async expectElementText(page, selector, text) {
|
|
1029
|
+
const element = await page.locator(selector);
|
|
1030
|
+
await expect(element).toHaveText(text);
|
|
1031
|
+
}
|
|
1032
|
+
static async expectElementValue(page, selector, value) {
|
|
1033
|
+
const element = await page.locator(selector);
|
|
1034
|
+
await expect(element).toHaveValue(value);
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1037
|
+
var TestHelpers = class {
|
|
1038
|
+
static async waitForElement(page, selector, timeout = 5e3) {
|
|
1039
|
+
await page.waitForSelector(selector, { timeout });
|
|
1040
|
+
}
|
|
1041
|
+
static async waitForText(page, text, timeout = 5e3) {
|
|
1042
|
+
await page.waitForSelector(`text=${text}`, { timeout });
|
|
1043
|
+
}
|
|
1044
|
+
static async fillForm(page, formData) {
|
|
1045
|
+
for (const [field, value] of Object.entries(formData)) {
|
|
1046
|
+
await page.fill(`[name="${field}"]`, value);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
static async clickButton(page, text) {
|
|
1050
|
+
await page.click(`button:has-text("${text}")`);
|
|
1051
|
+
}
|
|
1052
|
+
static async clickLink(page, text) {
|
|
1053
|
+
await page.click(`a:has-text("${text}")`);
|
|
1054
|
+
}
|
|
1055
|
+
static async takeScreenshot(page, name) {
|
|
1056
|
+
await page.screenshot({ path: `test-results/screenshots/${name}.png` });
|
|
1057
|
+
}
|
|
1058
|
+
static async measurePerformance(name, fn) {
|
|
1059
|
+
const start = Date.now();
|
|
1060
|
+
await fn();
|
|
1061
|
+
const end = Date.now();
|
|
1062
|
+
console.log(`Performance: ${name} took ${end - start}ms`);
|
|
1063
|
+
return end - start;
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
var defaultTestConfig = {
|
|
1067
|
+
baseURL: "http://localhost:3000",
|
|
1068
|
+
timeout: 3e4,
|
|
1069
|
+
retries: 2,
|
|
1070
|
+
workers: 1,
|
|
1071
|
+
reporter: ["html", "json"],
|
|
1072
|
+
browser: ["chromium", "firefox", "webkit"],
|
|
1073
|
+
mobile: true,
|
|
1074
|
+
tablet: true,
|
|
1075
|
+
desktop: true
|
|
1076
|
+
};
|
|
1077
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1078
|
+
0 && (module.exports = {
|
|
1079
|
+
TestAssertions,
|
|
1080
|
+
TestDataGenerator,
|
|
1081
|
+
TestHelpers,
|
|
1082
|
+
TestRunner,
|
|
1083
|
+
TestUtils,
|
|
1084
|
+
apiResponseFixtures,
|
|
1085
|
+
configFixtures,
|
|
1086
|
+
contentFixtures,
|
|
1087
|
+
contentTypeFixtures,
|
|
1088
|
+
createTestUtils,
|
|
1089
|
+
dateFixtures,
|
|
1090
|
+
defaultTestConfig,
|
|
1091
|
+
expect,
|
|
1092
|
+
fireEvent,
|
|
1093
|
+
jestConfig,
|
|
1094
|
+
playwrightConfig,
|
|
1095
|
+
render,
|
|
1096
|
+
screen,
|
|
1097
|
+
test,
|
|
1098
|
+
testDataGenerators,
|
|
1099
|
+
userEvent,
|
|
1100
|
+
userFixtures,
|
|
1101
|
+
waitFor,
|
|
1102
|
+
workflowFixtures
|
|
1103
|
+
});
|
|
1104
|
+
//# sourceMappingURL=index.js.map
|