@syngrisi/wdio-sdk 2.6.1 → 3.1.0
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 +1 -0
- package/dist/WDIODriver.js +5 -1
- package/dist/lib/getDomDump.js +134 -56
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -256,6 +256,7 @@ macOS/Linux: `export SYNGRISI_LOG_LEVEL=debug`
|
|
|
256
256
|
`ENV_POSTFIX` - will add to platform property, you can use this to set some unique platform name for particular
|
|
257
257
|
environment
|
|
258
258
|
`SYNGRISI_LOG_LEVEL` - logging level (`"trace" | "debug" | "info" | "warn" | "error"`)
|
|
259
|
+
`SYNGRISI_DISABLE_DOM_DATA` - disable DOM data collection for RCA (`"true" | "false"`), default: `"true"`
|
|
259
260
|
|
|
260
261
|
## Documentation
|
|
261
262
|
|
package/dist/WDIODriver.js
CHANGED
|
@@ -209,6 +209,9 @@ class WDIODriver {
|
|
|
209
209
|
if (!Buffer.isBuffer(imageBuffer))
|
|
210
210
|
throw new Error('check - wrong imageBuffer');
|
|
211
211
|
let opts = null;
|
|
212
|
+
// Check if DOM data should be skipped (via env var or option)
|
|
213
|
+
// Default: skipDomData is true (disabled) unless explicitly enabled via env var or params
|
|
214
|
+
const skipDomData = params?.skipDomData ?? (process.env.SYNGRISI_DISABLE_DOM_DATA !== 'false');
|
|
212
215
|
try {
|
|
213
216
|
opts = {
|
|
214
217
|
// ident: ['name', 'viewport', 'browserName', 'os', 'app', 'branch'];
|
|
@@ -224,7 +227,8 @@ class WDIODriver {
|
|
|
224
227
|
browserVersion: this.params.test.browserVersion,
|
|
225
228
|
browserFullVersion: this.params.test.browserFullVersion,
|
|
226
229
|
hashCode: (0, node_crypto_1.createHash)('sha512').update(imageBuffer).digest('hex'),
|
|
227
|
-
domDump: domDump,
|
|
230
|
+
domDump: skipDomData ? undefined : domDump,
|
|
231
|
+
skipDomData: skipDomData || undefined,
|
|
228
232
|
};
|
|
229
233
|
(0, paramsGuard_1.paramsGuard)(opts, 'check, opts', core_api_1.CheckParamsSchema);
|
|
230
234
|
// Remove autoAccept from params before sending to API (it's SDK-only)
|
package/dist/lib/getDomDump.js
CHANGED
|
@@ -1,78 +1,156 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getDomDump = getDomDump;
|
|
4
|
+
/**
|
|
5
|
+
* Maximum depth for DOM tree traversal to prevent stack overflow
|
|
6
|
+
*/
|
|
7
|
+
const MAX_DEPTH = 15;
|
|
8
|
+
/**
|
|
9
|
+
* Maximum text length to capture for each element
|
|
10
|
+
*/
|
|
11
|
+
const MAX_TEXT_LENGTH = 200;
|
|
12
|
+
/**
|
|
13
|
+
* Tags to skip during DOM collection (non-visual elements)
|
|
14
|
+
*/
|
|
15
|
+
const SKIP_TAGS = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'META', 'LINK', 'HEAD', 'SVG', 'PATH'];
|
|
16
|
+
/**
|
|
17
|
+
* CSS properties to capture for RCA analysis
|
|
18
|
+
*/
|
|
19
|
+
const STYLES_TO_CAPTURE = [
|
|
20
|
+
// Display & Visibility
|
|
21
|
+
'display', 'visibility', 'opacity',
|
|
22
|
+
// Position
|
|
23
|
+
'position', 'top', 'right', 'bottom', 'left',
|
|
24
|
+
// Dimensions
|
|
25
|
+
'width', 'height', 'min-width', 'min-height', 'max-width', 'max-height',
|
|
26
|
+
// Box Model
|
|
27
|
+
'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
|
|
28
|
+
'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left',
|
|
29
|
+
'border', 'border-width', 'border-style', 'border-color',
|
|
30
|
+
'border-radius',
|
|
31
|
+
// Colors
|
|
32
|
+
'background-color', 'color',
|
|
33
|
+
// Typography
|
|
34
|
+
'font-family', 'font-size', 'font-weight', 'line-height', 'text-align',
|
|
35
|
+
'text-decoration', 'text-transform', 'letter-spacing',
|
|
36
|
+
// Layout
|
|
37
|
+
'overflow', 'overflow-x', 'overflow-y',
|
|
38
|
+
'z-index',
|
|
39
|
+
'transform',
|
|
40
|
+
// Flexbox
|
|
41
|
+
'flex', 'flex-direction', 'flex-wrap', 'justify-content', 'align-items', 'align-content', 'gap',
|
|
42
|
+
// Grid
|
|
43
|
+
'grid-template-columns', 'grid-template-rows', 'grid-gap',
|
|
44
|
+
// Box Shadow
|
|
45
|
+
'box-shadow',
|
|
46
|
+
];
|
|
47
|
+
/**
|
|
48
|
+
* Check if element is visible (has dimensions and not hidden)
|
|
49
|
+
*/
|
|
4
50
|
function isVisible(el) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const style = window.getComputedStyle(el, null);
|
|
11
|
-
if (!el || !style) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
if (style.display === 'none' || style.visibility === 'hidden' || +style.opacity === 0) {
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
if ((style.display === 'block' || style.display === 'inline-block') &&
|
|
18
|
-
style.height === '0px' && style.overflow === 'hidden') {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
return style.position === 'fixed' || isVisible(el.parentNode);
|
|
51
|
+
const style = window.getComputedStyle(el);
|
|
52
|
+
if (style.display === 'none' ||
|
|
53
|
+
style.visibility === 'hidden' ||
|
|
54
|
+
parseFloat(style.opacity) === 0) {
|
|
55
|
+
return false;
|
|
22
56
|
}
|
|
23
|
-
|
|
57
|
+
const rect = el.getBoundingClientRect();
|
|
58
|
+
return rect.width > 0 && rect.height > 0;
|
|
24
59
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
sibCount++;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (el.hasAttribute('id') && el.id !== '') {
|
|
40
|
-
stack.unshift(`${el.nodeName.toLowerCase()}#${el.id}`);
|
|
60
|
+
/**
|
|
61
|
+
* Extract relevant attributes from element
|
|
62
|
+
* Skips most data-* attributes to reduce payload size
|
|
63
|
+
*/
|
|
64
|
+
function getAttributes(el) {
|
|
65
|
+
const attrs = {};
|
|
66
|
+
for (const attr of Array.from(el.attributes)) {
|
|
67
|
+
// Include data-testid but skip other data-* attributes
|
|
68
|
+
if (!attr.name.startsWith('data-') || attr.name === 'data-testid') {
|
|
69
|
+
attrs[attr.name] = attr.value;
|
|
41
70
|
}
|
|
42
|
-
|
|
43
|
-
|
|
71
|
+
}
|
|
72
|
+
return attrs;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Extract computed styles that affect visual appearance
|
|
76
|
+
*/
|
|
77
|
+
function getComputedStyles(el) {
|
|
78
|
+
const computed = window.getComputedStyle(el);
|
|
79
|
+
const styles = {};
|
|
80
|
+
for (const prop of STYLES_TO_CAPTURE) {
|
|
81
|
+
const value = computed.getPropertyValue(prop);
|
|
82
|
+
// Skip default/empty values to reduce payload
|
|
83
|
+
if (value && value !== 'initial' && value !== 'none' && value !== 'normal' && value !== 'auto' && value !== '0px' && value !== 'rgba(0, 0, 0, 0)') {
|
|
84
|
+
// Convert kebab-case to camelCase for consistency
|
|
85
|
+
const camelProp = prop.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
86
|
+
styles[camelProp] = value;
|
|
44
87
|
}
|
|
45
|
-
|
|
46
|
-
|
|
88
|
+
}
|
|
89
|
+
return styles;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get direct text content (not from children)
|
|
93
|
+
*/
|
|
94
|
+
function getDirectText(el) {
|
|
95
|
+
let text = '';
|
|
96
|
+
for (const child of Array.from(el.childNodes)) {
|
|
97
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
98
|
+
text += child.textContent?.trim() || '';
|
|
47
99
|
}
|
|
48
|
-
el = el.parentNode;
|
|
49
100
|
}
|
|
50
|
-
|
|
101
|
+
// Truncate long text
|
|
102
|
+
const trimmed = text.trim();
|
|
103
|
+
if (!trimmed)
|
|
104
|
+
return undefined;
|
|
105
|
+
return trimmed.length > MAX_TEXT_LENGTH ? trimmed.substring(0, MAX_TEXT_LENGTH) + '...' : trimmed;
|
|
51
106
|
}
|
|
52
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Recursively collect DOM node and its children
|
|
109
|
+
*/
|
|
110
|
+
function collectNode(el, depth = 0) {
|
|
111
|
+
// Limit depth to prevent stack overflow
|
|
112
|
+
if (depth > MAX_DEPTH)
|
|
113
|
+
return null;
|
|
114
|
+
// Skip invisible elements
|
|
115
|
+
if (!isVisible(el))
|
|
116
|
+
return null;
|
|
117
|
+
// Skip non-visual elements
|
|
118
|
+
if (SKIP_TAGS.includes(el.tagName))
|
|
119
|
+
return null;
|
|
53
120
|
const rect = el.getBoundingClientRect();
|
|
121
|
+
// Collect children
|
|
122
|
+
const children = [];
|
|
123
|
+
for (const child of Array.from(el.children)) {
|
|
124
|
+
const childNode = collectNode(child, depth + 1);
|
|
125
|
+
if (childNode) {
|
|
126
|
+
children.push(childNode);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
54
129
|
return {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
130
|
+
tagName: el.tagName.toLowerCase(),
|
|
131
|
+
attributes: getAttributes(el),
|
|
132
|
+
rect: {
|
|
133
|
+
x: Math.round(rect.x),
|
|
134
|
+
y: Math.round(rect.y),
|
|
135
|
+
width: Math.round(rect.width),
|
|
136
|
+
height: Math.round(rect.height),
|
|
137
|
+
},
|
|
138
|
+
computedStyles: getComputedStyles(el),
|
|
139
|
+
children,
|
|
140
|
+
text: getDirectText(el),
|
|
64
141
|
};
|
|
65
142
|
}
|
|
66
143
|
/**
|
|
144
|
+
* Collects DOM tree with computed styles for RCA (Root Cause Analysis)
|
|
145
|
+
* This function runs in browser context via WebdriverIO
|
|
146
|
+
*
|
|
147
|
+
* @param done - Callback function for WebdriverIO async compatibility
|
|
148
|
+
* @returns Serialized DomNode tree as JSON string
|
|
67
149
|
* @hidden - hidden for typedoc
|
|
68
150
|
*/
|
|
69
151
|
function getDomDump(done) {
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
for (const el of elements) {
|
|
73
|
-
dumpElements.push(dumpElement(el));
|
|
74
|
-
}
|
|
75
|
-
const result = JSON.stringify(dumpElements, null, '\t');
|
|
152
|
+
const root = collectNode(document.body);
|
|
153
|
+
const result = JSON.stringify(root);
|
|
76
154
|
done(result);
|
|
77
155
|
return result;
|
|
78
156
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syngrisi/wdio-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Syngrisi WebdriverIO SDK",
|
|
5
5
|
"main": "dist/WDIODriver.js",
|
|
6
6
|
"types": "dist/WDIODriver.d.ts",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://github.com/syngrisi/syngrisi/tree/main/packages/wdio-sdk#readme",
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@syngrisi/core-api": "^
|
|
41
|
+
"@syngrisi/core-api": "^3.1.0",
|
|
42
42
|
"@wdio/globals": "^9.17.0",
|
|
43
43
|
"@wdio/types": "^9.20.0",
|
|
44
44
|
"form-data": "^4.0.5",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"typescript": "^5.9.3",
|
|
60
60
|
"vitest": "^4.0.15"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "f88990c7fc709ffd5007f45c31535f1edb5fafb4"
|
|
63
63
|
}
|