@zohodesk/testinglibrary 0.0.57-n20-experimental → 0.0.59-n20-experimental

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.
Files changed (29) hide show
  1. package/README.md +0 -8
  2. package/build/common/data-generator/steps/DataGenerator.spec.js +1 -1
  3. package/build/common/data-generator/steps/DataGeneratorStepsHelper.js +4 -19
  4. package/build/core/dataGenerator/DataGenerator.js +25 -93
  5. package/build/core/dataGenerator/DataGeneratorHelper.js +4 -52
  6. package/build/core/playwright/builtInFixtures/cacheLayer.js +2 -197
  7. package/build/core/playwright/helpers/auth/getUsers.js +2 -2
  8. package/build/core/playwright/readConfigFile.js +2 -2
  9. package/build/core/playwright/setup/custom-reporter.js +72 -1
  10. package/build/core/playwright/validateFeature.js +0 -11
  11. package/build/utils/logger.js +1 -3
  12. package/changelog.md +0 -27
  13. package/npm-shrinkwrap.json +70 -70
  14. package/package.json +1 -1
  15. package/.claude/worktrees/thirsty-yalow/.babelrc +0 -24
  16. package/.claude/worktrees/thirsty-yalow/.eslintrc.js +0 -31
  17. package/.claude/worktrees/thirsty-yalow/.gitlab-ci.yml +0 -208
  18. package/.claude/worktrees/thirsty-yalow/.prettierrc +0 -6
  19. package/.claude/worktrees/thirsty-yalow/README.md +0 -234
  20. package/.claude/worktrees/thirsty-yalow/bin/cli.js +0 -3
  21. package/.claude/worktrees/thirsty-yalow/bin/postinstall.js +0 -1
  22. package/.claude/worktrees/thirsty-yalow/changelog.md +0 -167
  23. package/.claude/worktrees/thirsty-yalow/jest.config.js +0 -82
  24. package/.claude/worktrees/thirsty-yalow/package.json +0 -62
  25. package/.claude/worktrees/thirsty-yalow/playwright.config.js +0 -62
  26. package/AUTO_CLEANUP_PLAN.md +0 -171
  27. package/build/core/dataGenerator/validateGenerators.js +0 -82
  28. package/build/utils/timeFormat.js +0 -41
  29. package/unit_reports/unit-report.html +0 -277
@@ -1,62 +0,0 @@
1
- {
2
- "name": "@zohodesk/testinglibrary",
3
- "version": "3.2.21",
4
- "description": "",
5
- "main": "./build/index.js",
6
- "scripts": {
7
- "postinstall": "node bin/postinstall.js",
8
- "test": "jest",
9
- "clean": "rm -rf build && mkdir build",
10
- "build-babel": "babel -d ./build ./src --copy-files",
11
- "build": "npm run clean && npm run build-babel",
12
- "prepare": "npm run build",
13
- "lint": "eslint src/* --fix"
14
- },
15
- "exports": {
16
- ".": "./build/index.js",
17
- "./helpers": "./build/core/playwright/helpers/auth/index.js",
18
- "./DataGenerator": "./build/core/dataGenerator/DataGenerator.js",
19
- "./thirdPartyHelper": "./build/core/playwright/helpers/thirdPartyLib.js"
20
- },
21
- "keywords": [],
22
- "author": "",
23
- "license": "ISC",
24
- "dependencies": {
25
- "@babel/code-frame": "7.27.1",
26
- "@babel/preset-react": "7.22.5",
27
- "@cucumber/cucumber": "11.3.0",
28
- "@playwright/test": "1.53.2",
29
- "@reportportal/agent-js-playwright": "5.1.11",
30
- "@testing-library/jest-dom": "5.11.9",
31
- "babel-jest": "29.6.2",
32
- "babel-plugin-transform-dynamic-import": "2.1.0",
33
- "fast-glob": "3.3.1",
34
- "jest": "29.6.2",
35
- "jest-environment-jsdom": "29.6.2",
36
- "msw": "1.2.3",
37
- "playwright": "1.53.2",
38
- "playwright-bdd": "8.3.1",
39
- "properties-reader": "2.3.0",
40
- "supports-color": "8.1.1",
41
- "jsonpath": "^1.1.1"
42
- },
43
- "bin": {
44
- "ZDTestingFramework": "./bin/cli.js"
45
- },
46
- "peerDependencies": {
47
- "eslint": "*",
48
- "react": "*",
49
- "react-dom": "*"
50
- },
51
- "devDependencies": {
52
- "@babel/cli": "7.22.15",
53
- "@babel/core": "7.22.17",
54
- "@babel/node": "7.22.15",
55
- "@babel/plugin-transform-runtime": "7.22.15",
56
- "@babel/polyfill": "7.12.1",
57
- "@babel/preset-env": "7.22.15",
58
- "@babel/runtime": "7.22.15",
59
- "commander": "11.0.0",
60
- "jest-html-reporter": "3.10.2"
61
- }
62
- }
@@ -1,62 +0,0 @@
1
- // @ts-check
2
- const { defineConfig, devices } = require('@playwright/test');
3
- const path = require('path');
4
- const numCPUs = require('os').cpus().length;
5
-
6
- export default defineConfig({
7
- testDir: path.join(path.resolve(process.cwd()), 'uat'),
8
- outputDir: path.join(process.cwd(), 'uat', 'test-results'),
9
- fullyParallel: true,
10
- retries: process.env.CI ? 2 : 0,
11
- reporter: [['html', { outputFolder: path.join(process.cwd(), 'uat', 'playwright-report'), open: "always" }]],
12
- timeout: 60 * 1000,
13
- expect: {
14
- timeout: 5 * 1000,
15
- },
16
- use: {
17
- trace: 'on',
18
- video: {
19
- mode: 'on',
20
- size: { width: 640, height: 480 }
21
- }
22
- },
23
- projects: [
24
- { name: 'setup', testMatch: /.*\.setup\.js/ },
25
- {
26
- name: 'chromium',
27
- use: {
28
- ...devices['Desktop Chrome'],
29
- storageState: path.resolve(process.cwd(), 'uat', 'playwright/.auth/user.json')
30
- },
31
- dependencies: ['setup'],
32
- },
33
-
34
- {
35
- name: 'firefox',
36
- timeout: 4 * 60 * 1000,
37
- expect: {
38
- timeout: 80 * 1000,
39
- },
40
- use: {
41
- ...devices['Desktop Firefox'],
42
- storageState: path.resolve(process.cwd(), 'uat', 'playwright/.auth/user.json')
43
- },
44
- dependencies: ['setup'],
45
- },
46
-
47
- {
48
- name: 'webkit',
49
- timeout: 2 * 60 * 1000,
50
- expect: {
51
- timeout: 80 * 1000,
52
- },
53
- use: {
54
- ...devices['Desktop Safari'],
55
- storageState: path.resolve(process.cwd(), 'uat', 'playwright/.auth/user.json')
56
- },
57
- dependencies: ['setup'],
58
- },
59
-
60
- ],
61
- });
62
-
@@ -1,171 +0,0 @@
1
- # Data Generator System — Design & Current State
2
-
3
- ## Data Generator Overview
4
-
5
- ### How It Works
6
- - BDD step: `Given generate a "{Type}" entity "{name}" with generator "{GeneratorName}"`
7
- - Framework builds a **global generator index** at first use — scans all `*.generators.json` files under `modules/`
8
- - Generator names must be **unique across the entire UAT suite**
9
- - `ZDTestingFramework validate` checks for duplicate generator names before test run
10
- - Data generated using **org-level OAuth credentials** (org-oauth) by default
11
- - When scenario runs under a non-admin profile (`@profile_agent`), data generation still uses org-level `data-generator` — falls back automatically if profile has no own credentials
12
-
13
- ### Generator File Convention
14
- - **Pattern:** `*.generators.json` (configurable via `generatorFilePattern` in `uat.config.js`)
15
- - **Location:** anywhere under `modules/` — discovered globally via recursive scan
16
- - **Examples:** `ticket.generators.json`, `account.generators.json`, `webhook.generators.json`
17
-
18
- ### Generator JSON Structure
19
- ```json
20
- {
21
- "generators": {
22
- "GeneratorName": [
23
- {
24
- "type": "dynamic",
25
- "name": "stepName",
26
- "generatorOperationId": "support.Module.operationName",
27
- "dataPath": "$.response.body:$",
28
- "params": { "key": "$previousStep.value" }
29
- }
30
- ]
31
- }
32
- }
33
- ```
34
-
35
- ### Discovery Mechanism
36
- - **Index-based** — built once, O(1) lookups
37
- - `#getModulesRoot()` uses `configConstants.TEST_SLICE_FOLDER + stage + 'modules'` (deterministic)
38
- - Scans recursively for files matching `generatorFilePattern` (default: `*.generators.json`)
39
- - First generator name match wins (no duplicates enforced by validator)
40
-
41
- ### Profile Resolution for Data Generation
42
- - **Default (no `using` profile):** Uses org-level `data-generator` from edition JSON. If scenario profile (e.g., `@profile_agent`) has no `data-generator`, falls back to org-level via `getListOfActors()`.
43
- - **Explicit profile (`using "agent" profile`):** Resolves that profile's credentials via `getUserForSelectedEditionAndProfile()`.
44
-
45
- ### Constraints
46
- | Constraint | Detail |
47
- |---|---|
48
- | **Unique generator names** | Generator names must be unique across all `*.generators.json` files |
49
- | **Global discovery** | Framework scans entire `modules/` tree — generators in any module are accessible from any feature file |
50
- | **File pattern** | Configurable via `generatorFilePattern` in `uat.config.js` (default: `*.generators.json`) |
51
- | **DG_API_NAME matching** | In Gherkin data tables, `DG_API_NAME` column matches generator step's `name` field to inject params |
52
- | **Chained steps** | Multi-step generators use `$previousStep.value` syntax to pass output between steps |
53
- | **dataPath extraction** | `dataPath` uses JSONPath to extract specific fields from DG service response |
54
- | **Cached response** | Generated data is cached via `cacheLayer.set(entityName, response.data)` for use in subsequent steps |
55
- | **Org-level auth fallback** | If profile has no `data-generator`, org-level config from edition JSON is used automatically |
56
-
57
- ---
58
-
59
- ## Current Generators
60
-
61
- | Generator | File | Steps | Used By |
62
- |---|---|---|---|
63
- | `TicketWithDepartment` | `Ticket/data-generators/ticket.generators.json` | getDepartments → createProduct → createTicket | 452+ scenarios |
64
- | `TicketBasic` | `Ticket/data-generators/ticket.generators.json` | getDepartments → createTicket (no product) | Express/Free editions |
65
- | `CreateAccountRecord` | `Account/data-generators/account.generators.json` | createAccount | 37 scenarios |
66
- | `CreateContactRecord` | `Contact/data-generators/contact.generators.json` | createContact | 42 scenarios |
67
- | `CreateContractRecord` | `Contract/DV/Subtabs/*/data-generators/contract.generators.json` | getDepartments → createAccount → createContract | 4 scenarios |
68
- | `CreateWebhookRecord` | `Webhooks/List/data-generators/webhook.generators.json` | createWebhook (with default subscriptions) | Webhook scenarios |
69
- | `ProductWithDepartment` | `Search/products/data-generators/products.generators.json` | getDepartments → createProduct | Product/Search scenarios |
70
-
71
- ---
72
-
73
- ## Auto-Cleanup — Design (Not Yet Implemented)
74
-
75
- ### Status
76
- - **V1 implemented then removed** in `0.0.47` — CleanupTracker + DataCleanup were too tightly coupled
77
- - **V2 planned** — proper redesign needed
78
-
79
- ### Why Cleanup Was Removed
80
- 1. `cleanupTracker` fixture added complexity to all step definitions (extra parameter)
81
- 2. Cleanup ran inside fixture teardown — failures were hard to debug
82
- 3. REST API cleanup needed auth that wasn't available in the cleanup context
83
- 4. No way to control cleanup order for cross-entity dependencies
84
-
85
- ### V2 Design Principles
86
- 1. **Cleanup config stays in generator JSON** — `cleanup` property on each step (same as V1)
87
- 2. **Separate cleanup phase** — not in fixture teardown, but as explicit post-scenario hook
88
- 3. **Auth-aware** — cleanup uses the same `actorInfo` that created the data
89
- 4. **Configurable** — cleanup can be disabled per scenario or globally
90
- 5. **Non-blocking** — cleanup failures never fail the test
91
-
92
- ### Cleanup Types (unchanged from V1 design)
93
-
94
- #### Type 1: `oas` — DG service delete
95
- ```json
96
- "cleanup": {
97
- "type": "oas",
98
- "operationId": "support.Contact.deleteContact",
99
- "idPath": "$.id"
100
- }
101
- ```
102
-
103
- #### Type 2: `api` — Direct REST API DELETE
104
- ```json
105
- "cleanup": {
106
- "type": "api",
107
- "method": "DELETE",
108
- "apiPath": "/api/v1/contracts/{id}",
109
- "idPath": "$.id"
110
- }
111
- ```
112
-
113
- #### Type 3: `api` — Disable via PATCH
114
- ```json
115
- "cleanup": {
116
- "type": "api",
117
- "method": "PATCH",
118
- "apiPath": "/api/v1/webhooks/{id}",
119
- "idPath": "$.id",
120
- "body": { "isActive": false }
121
- }
122
- ```
123
-
124
- #### No cleanup — read-only operations
125
- Steps without `cleanup` property are skipped (e.g., `getDepartments`).
126
-
127
- ### V2 Implementation Plan
128
-
129
- 1. **CleanupManager** (replaces CleanupTracker + DataCleanup)
130
- - Single class managing both tracking and execution
131
- - Runs after scenario via `testSetup` hook (not fixture teardown)
132
- - Uses `actorInfo` from the generation step for auth
133
-
134
- 2. **Config flag** in `uat.config.js`:
135
- ```javascript
136
- autoCleanup: true, // default: true
137
- cleanupTimeout: 30000 // per-entity timeout
138
- ```
139
-
140
- 3. **Per-scenario opt-out** via tag:
141
- ```gherkin
142
- @skip_cleanup
143
- Scenario: Test that needs data to persist
144
- ```
145
-
146
- 4. **Cleanup order**: reverse of creation (dependent entities first)
147
-
148
- ---
149
-
150
- ## Seed Data System — Design (Planned)
151
-
152
- See `SEED_DATA_PLAN.md` in the consumer repo for the full design.
153
-
154
- ### Summary
155
- - **Edition-scoped** — data maps to edition tiers, not portal names
156
- - **Profile-based** — support creating data as specific profiles (agent login)
157
- - **OAS + REST** — both API types supported
158
- - **Runs before scenarios** — in `page.js` fixture after login
159
- - **Idempotent** — skips existing data, creates only missing
160
-
161
- ---
162
-
163
- ## Multi-DC Portability — Design (Planned)
164
-
165
- See `MULTI_DC_STRATEGY.md` in the consumer repo for the full design.
166
-
167
- ### Summary
168
- - **`capability` field** on each portal for DC-agnostic resolution
169
- - **Department aliases** in portal config for cross-DC mapping
170
- - **Framework resolves** by `capability` first, `orgName` fallback
171
- - **Same feature files** work across all DCs
@@ -1,82 +0,0 @@
1
- "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- Object.defineProperty(exports, "__esModule", {
5
- value: true
6
- });
7
- exports.validateGenerators = validateGenerators;
8
- var _path = _interopRequireDefault(require("path"));
9
- var _fs = _interopRequireDefault(require("fs"));
10
- var _logger = require("../../utils/logger");
11
- var _readConfigFile = require("../playwright/readConfigFile");
12
- function matchesPattern(filename, pattern) {
13
- const regex = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
14
- return regex.test(filename);
15
- }
16
- function findGeneratorFiles(dir, pattern, results = []) {
17
- const entries = _fs.default.readdirSync(dir, {
18
- withFileTypes: true
19
- });
20
- for (const entry of entries) {
21
- const fullPath = _path.default.join(dir, entry.name);
22
- if (entry.isDirectory()) {
23
- findGeneratorFiles(fullPath, pattern, results);
24
- } else if (matchesPattern(entry.name, pattern)) {
25
- results.push(fullPath);
26
- }
27
- }
28
- return results;
29
- }
30
- function validateGenerators(modulesRoot) {
31
- if (!_fs.default.existsSync(modulesRoot)) {
32
- _logger.Logger.log(_logger.Logger.INFO_TYPE, `Modules directory not found: ${modulesRoot}. Skipping generator validation.`);
33
- return {
34
- valid: true,
35
- duplicates: []
36
- };
37
- }
38
- const {
39
- generatorFilePattern: pattern = '*.generators.json'
40
- } = (0, _readConfigFile.generateConfigFromFile)();
41
- const generatorMap = {};
42
- const generatorFiles = findGeneratorFiles(modulesRoot, pattern);
43
- for (const filePath of generatorFiles) {
44
- try {
45
- const data = _fs.default.readFileSync(filePath, 'utf8');
46
- const generatorObj = JSON.parse(data);
47
- if (generatorObj.generators) {
48
- for (const name of Object.keys(generatorObj.generators)) {
49
- if (!generatorMap[name]) {
50
- generatorMap[name] = [];
51
- }
52
- generatorMap[name].push(filePath);
53
- }
54
- }
55
- } catch (err) {
56
- _logger.Logger.log(_logger.Logger.FAILURE_TYPE, `Failed to parse generator file: ${filePath} - ${err.message}`);
57
- }
58
- }
59
- const duplicates = Object.entries(generatorMap).filter(([, files]) => files.length > 1).map(([name, files]) => ({
60
- name,
61
- files
62
- }));
63
- if (duplicates.length > 0) {
64
- _logger.Logger.log(_logger.Logger.FAILURE_TYPE, 'Duplicate generator names found:');
65
- for (const dup of duplicates) {
66
- _logger.Logger.log(_logger.Logger.FAILURE_TYPE, ` Generator "${dup.name}" defined in:`);
67
- for (const file of dup.files) {
68
- _logger.Logger.log(_logger.Logger.FAILURE_TYPE, ` - ${file}`);
69
- }
70
- }
71
- return {
72
- valid: false,
73
- duplicates
74
- };
75
- }
76
- const generatorCount = Object.keys(generatorMap).length;
77
- _logger.Logger.log(_logger.Logger.SUCCESS_TYPE, `Generator validation passed. ${generatorCount} unique generators found across ${generatorFiles.length} generator files.`);
78
- return {
79
- valid: true,
80
- duplicates: []
81
- };
82
- }
@@ -1,41 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.formatDuration = formatDuration;
7
- exports.formatTimestamp = formatTimestamp;
8
- /**
9
- * Format a Date (or epoch ms) as a human-readable local timestamp in 12-hour format.
10
- * Example: "2026-04-10 11:35:23.123 AM"
11
- */
12
- function formatTimestamp(input) {
13
- const d = input instanceof Date ? input : new Date(input);
14
- const pad = (n, w = 2) => String(n).padStart(w, '0');
15
- const hours24 = d.getHours();
16
- const period = hours24 >= 12 ? 'PM' : 'AM';
17
- const hours12 = hours24 % 12 || 12;
18
- return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ` + `${pad(hours12)}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)} ${period}`;
19
- }
20
-
21
- /**
22
- * Format a duration in milliseconds as a human-readable string.
23
- * Examples: "850ms", "4.32s", "1m 23.45s", "1h 02m 03.45s"
24
- */
25
- function formatDuration(ms) {
26
- if (ms < 1000) {
27
- return `${ms}ms`;
28
- }
29
- const totalSeconds = ms / 1000;
30
- if (totalSeconds < 60) {
31
- return `${totalSeconds.toFixed(2)}s`;
32
- }
33
- const hours = Math.floor(totalSeconds / 3600);
34
- const minutes = Math.floor(totalSeconds % 3600 / 60);
35
- const seconds = totalSeconds % 60;
36
- const pad = n => String(n).padStart(2, '0');
37
- if (hours > 0) {
38
- return `${hours}h ${pad(minutes)}m ${seconds.toFixed(2)}s`;
39
- }
40
- return `${minutes}m ${seconds.toFixed(2)}s`;
41
- }
@@ -1,277 +0,0 @@
1
- <html><head><meta charset="utf-8"/><title>Unit Report</title><style type="text/css">:root {
2
- --text-primary: #111;
3
- --text-secondary: #4f4f4f;
4
- --success: #006633;
5
- --success-bright: #80ffbf;
6
- --danger: #cc071e;
7
- --danger-bright: #fbdfe0;
8
- --warning: #995c00;
9
- --warning-bright: #ffeea8;
10
- --panel: #eee;
11
- --border: #949494;
12
- --disabled: #6b6b6b;
13
- }
14
-
15
- html,
16
- body {
17
- font-family: Arial, Helvetica, sans-serif;
18
- font-size: 16px;
19
- margin: 0;
20
- padding: 0;
21
- color: var(--text-primary);
22
- }
23
- body {
24
- padding: 2rem 1rem;
25
- }
26
- .jesthtml-content {
27
- margin: 0 auto;
28
- max-width: 70rem;
29
- }
30
- header {
31
- display: flex;
32
- align-items: center;
33
- }
34
- #title {
35
- margin: 0;
36
- flex-grow: 1;
37
- }
38
- #logo {
39
- height: 4rem;
40
- }
41
- #timestamp {
42
- color: var(--text-secondary);
43
- margin-top: 0.5rem;
44
- }
45
-
46
- #metadata-container {
47
- display: flex;
48
- flex-direction: column;
49
- gap: 2rem;
50
- margin-bottom: 2rem;
51
- }
52
-
53
- .additional-information-container {
54
- display: flex;
55
- flex-direction: column;
56
- gap: 0.5rem;
57
- color: var(--text-secondary);
58
- }
59
-
60
- /** SUMMARY */
61
- #summary {
62
- color: var(--text-primary);
63
- display: flex;
64
- font-family: monospace;
65
- font-size: 1rem;
66
- }
67
- #summary > div {
68
- margin-right: 0.5rem;
69
- background: var(--panel);
70
- padding: 1rem;
71
- min-width: 15rem;
72
- }
73
- #summary > div:last-child {
74
- margin-right: 0;
75
- }
76
- @media only screen and (max-width: 720px) {
77
- #summary {
78
- flex-direction: column;
79
- }
80
- #summary > div {
81
- margin-right: 0;
82
- margin-top: 1rem;
83
- }
84
- #summary > div:first-child {
85
- margin-top: 0;
86
- }
87
- }
88
-
89
- .summary-total {
90
- font-weight: bold;
91
- margin-bottom: 0.5rem;
92
- }
93
- .summary-passed {
94
- color: var(--success);
95
- border-left: 0.4rem solid var(--success);
96
- padding-left: 0.5rem;
97
- margin-bottom: 0.15rem;
98
- }
99
- .summary-failed,
100
- .summary-obsolete-snapshots {
101
- color: var(--danger);
102
- border-left: 0.4rem solid var(--danger);
103
- padding-left: 0.5rem;
104
- margin-bottom: 0.15rem;
105
- }
106
- .summary-pending {
107
- color: var(--warning);
108
- border-left: 0.4rem solid var(--warning);
109
- padding-left: 0.5rem;
110
- margin-bottom: 0.15rem;
111
- }
112
- .summary-empty {
113
- color: var(--disabled);
114
- border-left: 0.4rem solid var(--disabled);
115
- margin-bottom: 0.15rem;
116
- }
117
-
118
- .test-result {
119
- padding: 1rem;
120
- margin-bottom: 0.25rem;
121
- }
122
- .test-result:last-child {
123
- border: 0;
124
- }
125
- .test-result.passed {
126
- background-color: var(--success-bright);
127
- color: var(--success);
128
- }
129
- .test-result.failed {
130
- background-color: var(--danger-bright);
131
- color: var(--danger);
132
- }
133
- .test-result.pending {
134
- background-color: var(--warning-bright);
135
- color: var(--warning);
136
- }
137
-
138
- .test-info {
139
- display: flex;
140
- justify-content: space-between;
141
- }
142
- .test-suitename {
143
- width: 20%;
144
- text-align: left;
145
- font-weight: bold;
146
- word-break: break-word;
147
- }
148
- .test-title {
149
- width: 40%;
150
- text-align: left;
151
- font-style: italic;
152
- }
153
- .test-status {
154
- width: 20%;
155
- text-align: right;
156
- }
157
- .test-duration {
158
- width: 10%;
159
- text-align: right;
160
- font-size: 0.85rem;
161
- }
162
-
163
- .failureMessages {
164
- padding: 0 1rem;
165
- margin-top: 1rem;
166
- border-top: 1px dashed var(--danger);
167
- }
168
- .failureMessages.suiteFailure {
169
- border-top: none;
170
- }
171
- .failureMsg {
172
- white-space: pre-wrap;
173
- white-space: -moz-pre-wrap;
174
- white-space: -pre-wrap;
175
- white-space: -o-pre-wrap;
176
- word-wrap: break-word;
177
- }
178
-
179
- .suite-container {
180
- margin-bottom: 1rem;
181
- }
182
- .suite-info {
183
- padding: 1rem;
184
- background-color: var(--panel);
185
- color: var(--text-secondary);
186
- border: 0.15rem solid;
187
- border-color: var(--panel);
188
- display: flex;
189
- align-items: center;
190
- margin-bottom: 0.25rem;
191
- }
192
- .suite-info:hover {
193
- border-color: var(--border);
194
- cursor: pointer;
195
- }
196
- .suite-info .suite-path {
197
- word-break: break-all;
198
- flex-grow: 1;
199
- font-family: monospace;
200
- font-size: 1rem;
201
- }
202
- .suite-info .suite-time {
203
- margin-left: 1rem;
204
- padding: 0.2rem 0.3rem;
205
- font-size: 0.85rem;
206
- }
207
- .suite-info .suite-time.warn {
208
- background-color: var(--danger);
209
- color: #fff;
210
- }
211
- .suite-info:before {
212
- content: "\2303";
213
- display: inline-block;
214
- margin-right: 1rem;
215
- transform: rotate(180deg) translateY(0.15rem);
216
- }
217
- .suite-container[open] .suite-info:before {
218
- transform: rotate(0deg) translateY(0.15rem);
219
- }
220
-
221
- /* CONSOLE LOGS */
222
- .suite-consolelog {
223
- margin-bottom: 0.25rem;
224
- padding: 1rem;
225
- background-color: var(--panel);
226
- }
227
- .suite-consolelog-header {
228
- font-weight: bold;
229
- }
230
- .suite-consolelog-item {
231
- padding: 0.5rem;
232
- }
233
- .suite-consolelog-item pre {
234
- margin: 0.5rem 0;
235
- white-space: pre-wrap;
236
- white-space: -moz-pre-wrap;
237
- white-space: -pre-wrap;
238
- white-space: -o-pre-wrap;
239
- word-wrap: break-word;
240
- }
241
- .suite-consolelog-item-origin {
242
- color: var(--text-secondary);
243
- font-weight: bold;
244
- }
245
- .suite-consolelog-item-message {
246
- color: var(--text-primary);
247
- font-size: 1rem;
248
- padding: 0 0.5rem;
249
- }
250
-
251
- /* OBSOLETE SNAPSHOTS */
252
- .suite-obsolete-snapshots {
253
- margin-bottom: 0.25rem;
254
- padding: 1rem;
255
- background-color: var(--danger-bright);
256
- color: var(--danger);
257
- }
258
- .suite-obsolete-snapshots-header {
259
- font-weight: bold;
260
- }
261
- .suite-obsolete-snapshots-item {
262
- padding: 0.5rem;
263
- }
264
- .suite-obsolete-snapshots-item pre {
265
- margin: 0.5rem 0;
266
- white-space: pre-wrap;
267
- white-space: -moz-pre-wrap;
268
- white-space: -pre-wrap;
269
- white-space: -o-pre-wrap;
270
- word-wrap: break-word;
271
- }
272
- .suite-obsolete-snapshots-item-message {
273
- color: var(--text-primary);
274
- font-size: 1rem;
275
- padding: 0 0.5rem;
276
- }
277
- </style></head><body><main class="jesthtml-content"><header><h1 id="title">Unit Report</h1></header><section id="metadata-container"><div id="timestamp">Started: 2026-02-26 16:05:28</div><div id="summary"><div id="suite-summary"><div class="summary-total">Suites (1)</div><div class="summary-passed ">1 passed</div><div class="summary-failed summary-empty">0 failed</div><div class="summary-pending summary-empty">0 pending</div></div><div id="test-summary"><div class="summary-total">Tests (3)</div><div class="summary-passed ">3 passed</div><div class="summary-failed summary-empty">0 failed</div><div class="summary-pending summary-empty">0 pending</div></div></div></section><details id="suite-1" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/muthu-19817/git/testing-framework/src/test/core/playwright/helpers/__tests__/customFixturesHelper.test.js</div><div class="suite-time">0.176s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">getCustomAccountDetails</div><div class="test-title">returns selected user when any tag info is present</div><div class="test-status">passed</div><div class="test-duration">0.002s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getCustomAccountDetails</div><div class="test-title">logs and returns undefined if getCustomAccountDetails function throws</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">getCustomAccountDetails</div><div class="test-title">returns default actor when no tag info is not provided</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details></main></body></html>