@testivai/witness-playwright 0.1.5 → 0.1.6
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/dist/snapshot.js +78 -10
- package/package.json +1 -1
- package/progress.md +36 -1
- package/src/snapshot.ts +78 -10
package/dist/snapshot.js
CHANGED
|
@@ -79,36 +79,104 @@ async function snapshot(page, testInfo, name, config) {
|
|
|
79
79
|
const baseFilename = `${timestamp}_${safeName}`;
|
|
80
80
|
// 1. Capture full-page screenshot
|
|
81
81
|
const screenshotPath = path.join(outputDir, `${baseFilename}.png`);
|
|
82
|
-
// Temporarily disable overflow constraints to enable full-page capture
|
|
82
|
+
// Temporarily disable overflow constraints and fixed heights to enable full-page capture
|
|
83
|
+
// This handles common SPA patterns like h-screen overflow-hidden with internal scrolling
|
|
84
|
+
/* eslint-disable no-eval */
|
|
83
85
|
await page.evaluate(`
|
|
84
86
|
window.__testivaiOriginalStyles = [];
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
|
|
88
|
+
// Process all elements to find and fix viewport-constraining styles
|
|
89
|
+
document.querySelectorAll('*').forEach(function(el) {
|
|
90
|
+
var computed = window.getComputedStyle(el);
|
|
91
|
+
|
|
92
|
+
// Check for overflow constraints
|
|
93
|
+
var hasOverflowConstraint =
|
|
94
|
+
computed.overflow === 'auto' ||
|
|
95
|
+
computed.overflow === 'scroll' ||
|
|
96
|
+
computed.overflow === 'hidden' ||
|
|
97
|
+
computed.overflowY === 'auto' ||
|
|
98
|
+
computed.overflowY === 'scroll' ||
|
|
99
|
+
computed.overflowY === 'hidden';
|
|
100
|
+
|
|
101
|
+
// Check for fixed height constraints (100vh, 100%, or specific pixel values on containers)
|
|
102
|
+
var hasHeightConstraint =
|
|
103
|
+
computed.height === '100vh' ||
|
|
104
|
+
(computed.height.endsWith('px') && el.scrollHeight > el.clientHeight) ||
|
|
105
|
+
(computed.maxHeight && computed.maxHeight !== 'none');
|
|
106
|
+
|
|
107
|
+
if (hasOverflowConstraint || hasHeightConstraint) {
|
|
89
108
|
window.__testivaiOriginalStyles.push({
|
|
90
109
|
element: el,
|
|
91
110
|
overflow: el.style.overflow,
|
|
92
|
-
|
|
111
|
+
overflowY: el.style.overflowY,
|
|
112
|
+
height: el.style.height,
|
|
113
|
+
maxHeight: el.style.maxHeight,
|
|
114
|
+
minHeight: el.style.minHeight
|
|
93
115
|
});
|
|
116
|
+
|
|
117
|
+
// For scrollable containers, expand to full scroll height
|
|
118
|
+
if (hasOverflowConstraint && el.scrollHeight > el.clientHeight) {
|
|
119
|
+
el.style.height = el.scrollHeight + 'px';
|
|
120
|
+
el.style.minHeight = el.scrollHeight + 'px';
|
|
121
|
+
} else if (hasHeightConstraint) {
|
|
122
|
+
el.style.height = 'auto';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Remove overflow constraints
|
|
94
126
|
el.style.overflow = 'visible';
|
|
95
|
-
el.style.
|
|
127
|
+
el.style.overflowY = 'visible';
|
|
128
|
+
el.style.maxHeight = 'none';
|
|
96
129
|
}
|
|
97
130
|
});
|
|
131
|
+
|
|
132
|
+
// Also handle html and body elements specifically
|
|
133
|
+
var html = document.documentElement;
|
|
134
|
+
var body = document.body;
|
|
135
|
+
|
|
136
|
+
window.__testivaiRootStyles = {
|
|
137
|
+
html: {
|
|
138
|
+
overflow: html.style.overflow,
|
|
139
|
+
height: html.style.height
|
|
140
|
+
},
|
|
141
|
+
body: {
|
|
142
|
+
overflow: body.style.overflow,
|
|
143
|
+
height: body.style.height
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
html.style.overflow = 'visible';
|
|
148
|
+
html.style.height = 'auto';
|
|
149
|
+
body.style.overflow = 'visible';
|
|
150
|
+
body.style.height = 'auto';
|
|
98
151
|
`);
|
|
99
|
-
// Wait for layout to stabilize
|
|
100
|
-
await page.waitForTimeout(
|
|
152
|
+
// Wait for layout to stabilize after style changes
|
|
153
|
+
await page.waitForTimeout(300);
|
|
101
154
|
// Take full-page screenshot
|
|
102
155
|
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
103
156
|
// Restore original styles
|
|
104
157
|
await page.evaluate(`
|
|
158
|
+
// Restore element styles
|
|
105
159
|
if (window.__testivaiOriginalStyles) {
|
|
106
|
-
window.__testivaiOriginalStyles.forEach((item)
|
|
160
|
+
window.__testivaiOriginalStyles.forEach(function(item) {
|
|
107
161
|
item.element.style.overflow = item.overflow;
|
|
162
|
+
item.element.style.overflowY = item.overflowY;
|
|
108
163
|
item.element.style.height = item.height;
|
|
164
|
+
item.element.style.maxHeight = item.maxHeight;
|
|
165
|
+
item.element.style.minHeight = item.minHeight;
|
|
109
166
|
});
|
|
110
167
|
delete window.__testivaiOriginalStyles;
|
|
111
168
|
}
|
|
169
|
+
|
|
170
|
+
// Restore root element styles
|
|
171
|
+
if (window.__testivaiRootStyles) {
|
|
172
|
+
var html = document.documentElement;
|
|
173
|
+
var body = document.body;
|
|
174
|
+
html.style.overflow = window.__testivaiRootStyles.html.overflow;
|
|
175
|
+
html.style.height = window.__testivaiRootStyles.html.height;
|
|
176
|
+
body.style.overflow = window.__testivaiRootStyles.body.overflow;
|
|
177
|
+
body.style.height = window.__testivaiRootStyles.body.height;
|
|
178
|
+
delete window.__testivaiRootStyles;
|
|
179
|
+
}
|
|
112
180
|
`);
|
|
113
181
|
// 2. Dump full-page DOM
|
|
114
182
|
const domPath = path.join(outputDir, `${baseFilename}.html`);
|
package/package.json
CHANGED
package/progress.md
CHANGED
|
@@ -745,12 +745,47 @@ The Playwright SDK provides two main components that are production-ready:
|
|
|
745
745
|
|
|
746
746
|
---
|
|
747
747
|
|
|
748
|
+
## Full-Page Screenshot Fix (January 10, 2026)
|
|
749
|
+
|
|
750
|
+
### Issue: Windowed Screenshots Instead of Full-Page
|
|
751
|
+
|
|
752
|
+
**Problem**: Screenshots were capturing only the visible viewport instead of the full scrollable page content.
|
|
753
|
+
|
|
754
|
+
**Root Cause**: Modern SPA frameworks (React, Vue, Angular) commonly use fixed viewport layouts like:
|
|
755
|
+
```css
|
|
756
|
+
.container {
|
|
757
|
+
height: 100vh; /* Fixed to viewport height */
|
|
758
|
+
overflow: hidden; /* Prevents document scrolling */
|
|
759
|
+
}
|
|
760
|
+
.content {
|
|
761
|
+
overflow-y: auto; /* Internal scrolling container */
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
This pattern (e.g., TailwindCSS `h-screen overflow-hidden`) creates a scrollable container **inside** a fixed-height viewport. Playwright's `fullPage: true` captures the document height, but when the document is constrained to viewport height with internal scrolling, it only captures what's visible.
|
|
766
|
+
|
|
767
|
+
**Solution**: Enhanced the snapshot function to:
|
|
768
|
+
1. Detect and temporarily remove `overflow: hidden` (not just `auto`/`scroll`)
|
|
769
|
+
2. Detect and remove `height: 100vh` constraints
|
|
770
|
+
3. Remove `maxHeight` constraints that limit container expansion
|
|
771
|
+
4. Explicitly handle `html` and `body` element styles
|
|
772
|
+
5. Wait for layout to stabilize (300ms) before capturing
|
|
773
|
+
6. Restore all original styles after capture
|
|
774
|
+
|
|
775
|
+
**Files Modified**:
|
|
776
|
+
- `src/snapshot.ts` - Enhanced full-page capture logic
|
|
777
|
+
|
|
778
|
+
**Testing**: Run visual tests to verify full-page screenshots are now captured correctly.
|
|
779
|
+
|
|
780
|
+
---
|
|
781
|
+
|
|
748
782
|
**Last Updated**: January 10, 2026
|
|
749
783
|
**Status**: 🎉 PUBLISHED TO NPM ✅ - Publicly available
|
|
750
|
-
**NPM Package**: @testivai/witness-playwright@0.1.
|
|
784
|
+
**NPM Package**: @testivai/witness-playwright@0.1.5
|
|
751
785
|
**Core Features**: Evidence capture, batch upload, and performance monitoring fully functional
|
|
752
786
|
**Configuration**: ✅ COMPLETE - End-to-end flow working
|
|
753
787
|
**Performance Metrics**: ✅ COMPLETE - Basic timing + optional Lighthouse
|
|
788
|
+
**Full-Page Screenshots**: ✅ FIXED - Handles SPA fixed viewport layouts
|
|
754
789
|
**API Key Format**: tstvai-{secure-random-string}
|
|
755
790
|
**Known Issues**: Minor UX improvements (retry logic, progress reporting)
|
|
756
791
|
**Blocker**: None - All critical features implemented
|
package/src/snapshot.ts
CHANGED
|
@@ -56,39 +56,107 @@ export async function snapshot(
|
|
|
56
56
|
// 1. Capture full-page screenshot
|
|
57
57
|
const screenshotPath = path.join(outputDir, `${baseFilename}.png`);
|
|
58
58
|
|
|
59
|
-
// Temporarily disable overflow constraints to enable full-page capture
|
|
59
|
+
// Temporarily disable overflow constraints and fixed heights to enable full-page capture
|
|
60
|
+
// This handles common SPA patterns like h-screen overflow-hidden with internal scrolling
|
|
61
|
+
/* eslint-disable no-eval */
|
|
60
62
|
await page.evaluate(`
|
|
61
63
|
window.__testivaiOriginalStyles = [];
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
|
|
65
|
+
// Process all elements to find and fix viewport-constraining styles
|
|
66
|
+
document.querySelectorAll('*').forEach(function(el) {
|
|
67
|
+
var computed = window.getComputedStyle(el);
|
|
68
|
+
|
|
69
|
+
// Check for overflow constraints
|
|
70
|
+
var hasOverflowConstraint =
|
|
71
|
+
computed.overflow === 'auto' ||
|
|
72
|
+
computed.overflow === 'scroll' ||
|
|
73
|
+
computed.overflow === 'hidden' ||
|
|
74
|
+
computed.overflowY === 'auto' ||
|
|
75
|
+
computed.overflowY === 'scroll' ||
|
|
76
|
+
computed.overflowY === 'hidden';
|
|
77
|
+
|
|
78
|
+
// Check for fixed height constraints (100vh, 100%, or specific pixel values on containers)
|
|
79
|
+
var hasHeightConstraint =
|
|
80
|
+
computed.height === '100vh' ||
|
|
81
|
+
(computed.height.endsWith('px') && el.scrollHeight > el.clientHeight) ||
|
|
82
|
+
(computed.maxHeight && computed.maxHeight !== 'none');
|
|
83
|
+
|
|
84
|
+
if (hasOverflowConstraint || hasHeightConstraint) {
|
|
66
85
|
window.__testivaiOriginalStyles.push({
|
|
67
86
|
element: el,
|
|
68
87
|
overflow: el.style.overflow,
|
|
69
|
-
|
|
88
|
+
overflowY: el.style.overflowY,
|
|
89
|
+
height: el.style.height,
|
|
90
|
+
maxHeight: el.style.maxHeight,
|
|
91
|
+
minHeight: el.style.minHeight
|
|
70
92
|
});
|
|
93
|
+
|
|
94
|
+
// For scrollable containers, expand to full scroll height
|
|
95
|
+
if (hasOverflowConstraint && el.scrollHeight > el.clientHeight) {
|
|
96
|
+
el.style.height = el.scrollHeight + 'px';
|
|
97
|
+
el.style.minHeight = el.scrollHeight + 'px';
|
|
98
|
+
} else if (hasHeightConstraint) {
|
|
99
|
+
el.style.height = 'auto';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Remove overflow constraints
|
|
71
103
|
el.style.overflow = 'visible';
|
|
72
|
-
el.style.
|
|
104
|
+
el.style.overflowY = 'visible';
|
|
105
|
+
el.style.maxHeight = 'none';
|
|
73
106
|
}
|
|
74
107
|
});
|
|
108
|
+
|
|
109
|
+
// Also handle html and body elements specifically
|
|
110
|
+
var html = document.documentElement;
|
|
111
|
+
var body = document.body;
|
|
112
|
+
|
|
113
|
+
window.__testivaiRootStyles = {
|
|
114
|
+
html: {
|
|
115
|
+
overflow: html.style.overflow,
|
|
116
|
+
height: html.style.height
|
|
117
|
+
},
|
|
118
|
+
body: {
|
|
119
|
+
overflow: body.style.overflow,
|
|
120
|
+
height: body.style.height
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
html.style.overflow = 'visible';
|
|
125
|
+
html.style.height = 'auto';
|
|
126
|
+
body.style.overflow = 'visible';
|
|
127
|
+
body.style.height = 'auto';
|
|
75
128
|
`);
|
|
76
129
|
|
|
77
|
-
// Wait for layout to stabilize
|
|
78
|
-
await page.waitForTimeout(
|
|
130
|
+
// Wait for layout to stabilize after style changes
|
|
131
|
+
await page.waitForTimeout(300);
|
|
79
132
|
|
|
80
133
|
// Take full-page screenshot
|
|
81
134
|
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
82
135
|
|
|
83
136
|
// Restore original styles
|
|
84
137
|
await page.evaluate(`
|
|
138
|
+
// Restore element styles
|
|
85
139
|
if (window.__testivaiOriginalStyles) {
|
|
86
|
-
window.__testivaiOriginalStyles.forEach((item)
|
|
140
|
+
window.__testivaiOriginalStyles.forEach(function(item) {
|
|
87
141
|
item.element.style.overflow = item.overflow;
|
|
142
|
+
item.element.style.overflowY = item.overflowY;
|
|
88
143
|
item.element.style.height = item.height;
|
|
144
|
+
item.element.style.maxHeight = item.maxHeight;
|
|
145
|
+
item.element.style.minHeight = item.minHeight;
|
|
89
146
|
});
|
|
90
147
|
delete window.__testivaiOriginalStyles;
|
|
91
148
|
}
|
|
149
|
+
|
|
150
|
+
// Restore root element styles
|
|
151
|
+
if (window.__testivaiRootStyles) {
|
|
152
|
+
var html = document.documentElement;
|
|
153
|
+
var body = document.body;
|
|
154
|
+
html.style.overflow = window.__testivaiRootStyles.html.overflow;
|
|
155
|
+
html.style.height = window.__testivaiRootStyles.html.height;
|
|
156
|
+
body.style.overflow = window.__testivaiRootStyles.body.overflow;
|
|
157
|
+
body.style.height = window.__testivaiRootStyles.body.height;
|
|
158
|
+
delete window.__testivaiRootStyles;
|
|
159
|
+
}
|
|
92
160
|
`);
|
|
93
161
|
|
|
94
162
|
// 2. Dump full-page DOM
|