flexysnap 0.5.2 → 0.5.4
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/package.json +1 -1
- package/src/testUtils.js +108 -33
package/package.json
CHANGED
package/src/testUtils.js
CHANGED
|
@@ -7,43 +7,92 @@ async function callClosePopups(page) {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
async function
|
|
10
|
+
async function attemptNavigation(page, url, timeout) {
|
|
11
|
+
logTimestamp('Loading ' + url);
|
|
12
|
+
let response = null;
|
|
13
|
+
try {
|
|
14
|
+
response = await page.goto(url, {timeout: timeout, waitUntil: "commit"});
|
|
15
|
+
} catch (e) {
|
|
16
|
+
logTimestamp('Error navigating to page');
|
|
17
|
+
throw e
|
|
18
|
+
}
|
|
19
|
+
if (response && response.status() >= 400) {
|
|
20
|
+
logTimestamp(`HTTP error ${response.status()} ${response.statusText()} while loading ${url}`);
|
|
21
|
+
throw new Error('Could not load page.')
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
await page.waitForLoadState('domcontentloaded', {timeout: timeout});
|
|
25
|
+
} catch (e) {
|
|
26
|
+
logTimestamp('Error while waiting for DOMContentLoaded event');
|
|
27
|
+
throw e
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
await page.waitForLoadState('load', {timeout: timeout});
|
|
31
|
+
} catch (e) {
|
|
32
|
+
logTimestamp('Error while waiting for load event');
|
|
33
|
+
throw e
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const CLOSE_STUCK_TARGET_TIMEOUT_MS = 5000;
|
|
38
|
+
const replacementContexts = [];
|
|
39
|
+
|
|
40
|
+
function closeWithinTimeout(closeable, timeoutMs) {
|
|
41
|
+
let timeoutHandle;
|
|
42
|
+
const timeoutPromise = new Promise(resolve => {
|
|
43
|
+
timeoutHandle = setTimeout(resolve, timeoutMs);
|
|
44
|
+
});
|
|
45
|
+
const closePromise = closeable.close().catch(() => {});
|
|
46
|
+
return Promise.race([closePromise, timeoutPromise]).finally(() => clearTimeout(timeoutHandle));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function openReplacementPage(stuckPage, contextOptions) {
|
|
50
|
+
const stuckContext = stuckPage.context();
|
|
51
|
+
const browser = stuckContext.browser();
|
|
52
|
+
|
|
53
|
+
if (!browser) {
|
|
54
|
+
await closeWithinTimeout(stuckPage, CLOSE_STUCK_TARGET_TIMEOUT_MS);
|
|
55
|
+
return stuckContext.newPage();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const viewport = stuckPage.viewportSize();
|
|
59
|
+
await closeWithinTimeout(stuckContext, CLOSE_STUCK_TARGET_TIMEOUT_MS);
|
|
60
|
+
|
|
61
|
+
const replacementContext = await browser.newContext({ viewport, ...contextOptions });
|
|
62
|
+
replacementContexts.push(replacementContext);
|
|
63
|
+
await configureContext(replacementContext);
|
|
64
|
+
|
|
65
|
+
return replacementContext.newPage();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function prepareRetry(stuckPage, waitMs, contextOptions) {
|
|
69
|
+
logTimestamp('Discarding stuck page and opening a new browser context');
|
|
70
|
+
const replacementPage = await openReplacementPage(stuckPage, contextOptions);
|
|
71
|
+
logTimestamp(`Waiting ${waitMs}ms before retrying`);
|
|
72
|
+
await replacementPage.waitForTimeout(waitMs);
|
|
73
|
+
return replacementPage;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function gotoWithRetry(page, url, options = {}) {
|
|
77
|
+
const { baseTimeout = 20000, backoffMs = 3000, contextOptions = {} } = options;
|
|
78
|
+
const maxAttempts = 3;
|
|
11
79
|
let error = null;
|
|
12
|
-
|
|
80
|
+
let currentPage = page;
|
|
81
|
+
for (let i = 1; i <= maxAttempts; ++i) {
|
|
13
82
|
error = null;
|
|
14
|
-
|
|
15
|
-
logTimestamp('Loading ' + url);
|
|
16
|
-
let response = null;
|
|
17
|
-
try {
|
|
18
|
-
response = await page.goto(url, {timeout: timeout, waitUntil: "commit"});
|
|
19
|
-
} catch (e) {
|
|
20
|
-
logTimestamp('Error navigating to page');
|
|
21
|
-
error = e
|
|
22
|
-
continue
|
|
23
|
-
}
|
|
24
|
-
if (response && response.status() >= 400) {
|
|
25
|
-
logTimestamp(`HTTP error ${response.status()} ${response.statusText()} while loading ${url}`);
|
|
26
|
-
error = new Error('Could not load page.')
|
|
27
|
-
continue
|
|
28
|
-
}
|
|
83
|
+
const timeout = baseTimeout * i;
|
|
29
84
|
try {
|
|
30
|
-
await
|
|
85
|
+
await attemptNavigation(currentPage, url, timeout);
|
|
86
|
+
break
|
|
31
87
|
} catch (e) {
|
|
32
|
-
logTimestamp('Error while waiting for DOMContentLoaded event');
|
|
33
88
|
error = e
|
|
34
|
-
|
|
89
|
+
if (i < maxAttempts)
|
|
90
|
+
currentPage = await prepareRetry(currentPage, backoffMs * i, contextOptions);
|
|
35
91
|
}
|
|
36
|
-
try {
|
|
37
|
-
await page.waitForLoadState('load', {timeout: timeout});
|
|
38
|
-
} catch (e) {
|
|
39
|
-
logTimestamp('Error while waiting for load event');
|
|
40
|
-
error = e
|
|
41
|
-
continue
|
|
42
|
-
}
|
|
43
|
-
break
|
|
44
92
|
}
|
|
45
93
|
if (error)
|
|
46
94
|
throw error
|
|
95
|
+
return currentPage;
|
|
47
96
|
}
|
|
48
97
|
|
|
49
98
|
|
|
@@ -62,11 +111,24 @@ function setCacheableUrlPattern(pattern) {
|
|
|
62
111
|
cacheableUrlPatterns.push(pattern)
|
|
63
112
|
}
|
|
64
113
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
114
|
+
const cacheBustedResourceTypes = ['document', 'xhr', 'fetch'];
|
|
115
|
+
|
|
116
|
+
function isTestedSiteUrl(url) {
|
|
117
|
+
return baseUrl !== '' && url.href.includes(baseUrl);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function configureContext(context) {
|
|
121
|
+
context.on('requestfailed', request => {
|
|
122
|
+
const errorText = request.failure()?.errorText;
|
|
123
|
+
logTimestamp(`Request failed: ${request.method()} ${request.url()} - ${errorText}`);
|
|
124
|
+
});
|
|
125
|
+
await context.route(isTestedSiteUrl, route => {
|
|
69
126
|
const req = route.request();
|
|
127
|
+
|
|
128
|
+
if (!cacheBustedResourceTypes.includes(req.resourceType())) {
|
|
129
|
+
return route.continue();
|
|
130
|
+
}
|
|
131
|
+
|
|
70
132
|
const url = new URL(req.url());
|
|
71
133
|
const path = url.pathname.toLowerCase();
|
|
72
134
|
|
|
@@ -75,7 +137,7 @@ test.beforeEach(async ({ context }) => {
|
|
|
75
137
|
return route.continue();
|
|
76
138
|
}
|
|
77
139
|
|
|
78
|
-
if (req.method() !== 'GET'
|
|
140
|
+
if (req.method() !== 'GET') {
|
|
79
141
|
return route.continue();
|
|
80
142
|
}
|
|
81
143
|
url.searchParams.set('_cb', Date.now().toString());
|
|
@@ -83,6 +145,19 @@ test.beforeEach(async ({ context }) => {
|
|
|
83
145
|
logTimestamp('Requesting ' + urlString);
|
|
84
146
|
route.continue({ url: urlString });
|
|
85
147
|
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
test.beforeEach(async ({ context }) => {
|
|
151
|
+
startTimer();
|
|
152
|
+
logTimestamp('Test execution started');
|
|
153
|
+
await configureContext(context);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test.afterEach(async () => {
|
|
157
|
+
const contextsToClose = replacementContexts.splice(0, replacementContexts.length);
|
|
158
|
+
for (const replacementContext of contextsToClose) {
|
|
159
|
+
await closeWithinTimeout(replacementContext, CLOSE_STUCK_TARGET_TIMEOUT_MS);
|
|
160
|
+
}
|
|
86
161
|
});
|
|
87
162
|
|
|
88
163
|
let testStartTime = null;
|