browsertime 14.15.0 → 14.18.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/CHANGELOG.md +32 -0
- package/browsertime/visualmetrics.py +59 -11
- package/lib/core/engine/command/click.js +17 -0
- package/lib/core/engine/command/select.js +155 -0
- package/lib/core/engine/iteration.js +3 -1
- package/lib/core/seleniumRunner.js +18 -4
- package/lib/support/cli.js +1 -1
- package/lib/support/util.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# Browsertime changelog (we do [semantic versioning](https://semver.org))
|
|
2
2
|
|
|
3
|
+
## 14.18.1 - 2022-01-24
|
|
4
|
+
### Fixed
|
|
5
|
+
* If loading a URL failed and we retry and we logged that as an info message, but you as a user only need to know if it fails after X retries. The log message now logs at debug level [#1701](https://github.com/sitespeedio/browsertime/pull/1701).
|
|
6
|
+
* The summary log message `[2022-01-24 16:12:38] INFO: https://www.sitespeed.io 27 requests, TTFB: 962ms (σ917.00ms), firstPaint: 2.92s (σ3.07s), firstVisualChange: 2.94s (σ3.07s), FCP: 2.92s (σ3.07s), DOMContentLoaded: 3.22s (σ2.98s), LCP: 2.92s (σ3.07s), CLS: 0.0389 (σ0.03), TBT: 769ms (σ53.00ms), Load: 3.77s (σ3.12s), speedIndex: 2.96s (σ3.07s), visualComplete85: 2.95s (σ3.06s), lastVisualChange: 4.54s (σ3.30s) (21 runs)` that summaries all the runs used mean instead of median metric. That sucks when you do many runs and want to compare them. That is now fixed to show median number instead [#1700](https://github.com/sitespeedio/browsertime/pull/1700).
|
|
7
|
+
|
|
8
|
+
## 14.18.0 - 2022-01-24
|
|
9
|
+
### Added
|
|
10
|
+
* Updated to Edge stable in the Docker container.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
* A more safe way to get dockumentURI for Firefox. See PR [#1699](https://github.com/sitespeedio/browsertime/pull/1699) and bug [#1698](https://github.com/sitespeedio/browsertime/issues/1698).
|
|
14
|
+
|
|
15
|
+
## 14.17.0 - 2022-01-23
|
|
16
|
+
### Added
|
|
17
|
+
* New Select command [#1696](https://github.com/sitespeedio/browsertime/pull/1696):
|
|
18
|
+
* `select.selectByIdValue(selectId, value)`
|
|
19
|
+
* `select.selectByNameAndValue(selectName, value)`
|
|
20
|
+
* `select.selectByIdAndIndex(selectId, index)`
|
|
21
|
+
* `select.selectByNameAndIndex(selectName, index)`
|
|
22
|
+
* `select.deselectById(selectId)`
|
|
23
|
+
* `select.getValuesById(selectId)`
|
|
24
|
+
* `select.getSelectedValueById(selectId)`
|
|
25
|
+
|
|
26
|
+
* New click by name command `click.byName(name)` [#1697](https://github.com/sitespeedio/browsertime/pull/1697).
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
* Remove the top 10 rows of the image to handle progress bars on some mobile browser recordings. Thank you [Gregory Mierzwinski](https://github.com/gmierz) for PR [#1687](https://github.com/sitespeedio/browsertime/pull/1687).
|
|
30
|
+
|
|
31
|
+
## 14.16.0 - 2022-01-14
|
|
32
|
+
### Added
|
|
33
|
+
* Upgraded Geckodriver downloader that downloads a pre-built Geckodriver on Raspberry Pis.
|
|
34
|
+
|
|
3
35
|
## 14.15.0 - 2022-01-12
|
|
4
36
|
### Added
|
|
5
37
|
* Added so you can append a String to Firefox User Agent [#1694](https://github.com/sitespeedio/browsertime/pull/1694), thank you [Henrik Skupin](https://github.com/whimboo) for pointing me in the right direction. With [#1695](https://github.com/sitespeedio/browsertime/pull/1695) we now have `--appendToUserAgent` that works in Chrome/Edge and Firefox.
|
|
@@ -89,6 +89,7 @@ def video_to_frames(
|
|
|
89
89
|
if os.path.isfile(video):
|
|
90
90
|
video = os.path.realpath(video)
|
|
91
91
|
logging.info("Processing frames from video " + video + " to " + directory)
|
|
92
|
+
is_mobile = find_recording_platform(video)
|
|
92
93
|
if os.path.isdir(directory):
|
|
93
94
|
shutil.rmtree(directory, True)
|
|
94
95
|
if not os.path.isdir(directory):
|
|
@@ -103,13 +104,15 @@ def video_to_frames(
|
|
|
103
104
|
viewport_retries,
|
|
104
105
|
viewport_min_height,
|
|
105
106
|
viewport_min_width,
|
|
107
|
+
is_mobile,
|
|
106
108
|
)
|
|
107
109
|
gc.collect()
|
|
108
110
|
if extract_frames(video, directory, full_resolution, viewport):
|
|
109
111
|
client_viewport = None
|
|
110
112
|
if find_viewport and options.notification:
|
|
111
113
|
client_viewport = find_image_viewport(
|
|
112
|
-
os.path.join(directory, "video-000000.png")
|
|
114
|
+
os.path.join(directory, "video-000000.png"),
|
|
115
|
+
is_mobile
|
|
113
116
|
)
|
|
114
117
|
if multiple and orange_file is not None:
|
|
115
118
|
directories = split_videos(directory, orange_file)
|
|
@@ -122,12 +125,12 @@ def video_to_frames(
|
|
|
122
125
|
remove_orange_frames(dir, orange_file)
|
|
123
126
|
find_first_frame(dir, white_file)
|
|
124
127
|
blank_first_frame(dir)
|
|
125
|
-
find_render_start(dir, orange_file, gray_file, cropped)
|
|
128
|
+
find_render_start(dir, orange_file, gray_file, cropped, is_mobile)
|
|
126
129
|
find_last_frame(dir, white_file)
|
|
127
130
|
adjust_frame_times(dir)
|
|
128
131
|
if timeline_file is not None and not multiple:
|
|
129
132
|
synchronize_to_timeline(dir, timeline_file)
|
|
130
|
-
eliminate_duplicate_frames(dir, cropped)
|
|
133
|
+
eliminate_duplicate_frames(dir, cropped, is_mobile)
|
|
131
134
|
eliminate_similar_frames(dir)
|
|
132
135
|
# See if we are limiting the number of frames to keep
|
|
133
136
|
# (before processing them to save processing time)
|
|
@@ -202,6 +205,33 @@ def extract_frames(video, directory, full_resolution, viewport):
|
|
|
202
205
|
return ret
|
|
203
206
|
|
|
204
207
|
|
|
208
|
+
def find_recording_platform(video):
|
|
209
|
+
"""Find the platform that this video was recorded on.
|
|
210
|
+
|
|
211
|
+
We can make use of a field called `com.android.version` to
|
|
212
|
+
determine if we've recorded on mobile or not.
|
|
213
|
+
"""
|
|
214
|
+
command = ["ffprobe", video]
|
|
215
|
+
logging.debug(command)
|
|
216
|
+
|
|
217
|
+
lines = []
|
|
218
|
+
if sys.version_info > (3, 0):
|
|
219
|
+
proc = subprocess.Popen(command, stderr=subprocess.PIPE, encoding="UTF-8")
|
|
220
|
+
else:
|
|
221
|
+
proc = subprocess.Popen(command, stderr=subprocess.PIPE)
|
|
222
|
+
|
|
223
|
+
while proc.poll() is None:
|
|
224
|
+
lines.extend(iter(proc.stderr.readline, ""))
|
|
225
|
+
|
|
226
|
+
is_mobile = False
|
|
227
|
+
matcher = re.compile(".*com\.android\.version.*")
|
|
228
|
+
for line in lines:
|
|
229
|
+
if matcher.search(line):
|
|
230
|
+
is_mobile = True
|
|
231
|
+
|
|
232
|
+
return is_mobile
|
|
233
|
+
|
|
234
|
+
|
|
205
235
|
def split_videos(directory, orange_file):
|
|
206
236
|
"""Split multiple videos on orange frame separators"""
|
|
207
237
|
logging.debug("Splitting video on orange frames (this may take a while)...")
|
|
@@ -291,7 +321,7 @@ def remove_orange_frames(directory, orange_file):
|
|
|
291
321
|
break
|
|
292
322
|
|
|
293
323
|
|
|
294
|
-
def find_image_viewport(file):
|
|
324
|
+
def find_image_viewport(file, is_mobile):
|
|
295
325
|
logging.debug("Finding the viewport for %s", file)
|
|
296
326
|
try:
|
|
297
327
|
from PIL import Image
|
|
@@ -357,6 +387,12 @@ def find_image_viewport(file):
|
|
|
357
387
|
"height": (bottom - top),
|
|
358
388
|
}
|
|
359
389
|
|
|
390
|
+
if is_mobile:
|
|
391
|
+
# On mobile we need to ignore the top ~10 pixels because
|
|
392
|
+
# there is a visible progress bar there on some browsers.
|
|
393
|
+
viewport["y"] += 10
|
|
394
|
+
viewport["height"] -= 10
|
|
395
|
+
|
|
360
396
|
except Exception:
|
|
361
397
|
viewport = None
|
|
362
398
|
|
|
@@ -371,6 +407,7 @@ def find_video_viewport(
|
|
|
371
407
|
viewport_retries,
|
|
372
408
|
viewport_min_height,
|
|
373
409
|
viewport_min_width,
|
|
410
|
+
is_mobile,
|
|
374
411
|
):
|
|
375
412
|
logging.debug("Finding Video Viewport...")
|
|
376
413
|
viewport = None
|
|
@@ -463,7 +500,7 @@ def find_video_viewport(
|
|
|
463
500
|
}
|
|
464
501
|
|
|
465
502
|
elif find_viewport:
|
|
466
|
-
viewport = find_image_viewport(frame)
|
|
503
|
+
viewport = find_image_viewport(frame, is_mobile)
|
|
467
504
|
else:
|
|
468
505
|
viewport = {"x": 0, "y": 0, "width": width, "height": height}
|
|
469
506
|
|
|
@@ -629,7 +666,7 @@ def find_last_frame(directory, white_file):
|
|
|
629
666
|
logging.exception("Error finding last frame")
|
|
630
667
|
|
|
631
668
|
|
|
632
|
-
def find_render_start(directory, orange_file, gray_file, cropped):
|
|
669
|
+
def find_render_start(directory, orange_file, gray_file, cropped, is_mobile):
|
|
633
670
|
logging.debug("Finding Render Start...")
|
|
634
671
|
try:
|
|
635
672
|
if (
|
|
@@ -680,13 +717,19 @@ def find_render_start(directory, orange_file, gray_file, cropped):
|
|
|
680
717
|
top += client_viewport["y"]
|
|
681
718
|
elif cropped:
|
|
682
719
|
# The image was already cropped, so only cutout the bottom
|
|
683
|
-
# to get rid of the network request/etc. information
|
|
720
|
+
# to get rid of the network request/etc. information for
|
|
721
|
+
# desktop videos, and nothing extra on mobile.
|
|
684
722
|
top = 0
|
|
685
723
|
left = 0
|
|
686
724
|
width = im_width
|
|
687
|
-
|
|
725
|
+
|
|
726
|
+
if is_mobile:
|
|
727
|
+
height = im_height
|
|
728
|
+
else:
|
|
729
|
+
height = im_height - bottom_margin
|
|
688
730
|
|
|
689
731
|
crop = "{0:d}x{1:d}+{2:d}+{3:d}".format(width, height, left, top)
|
|
732
|
+
|
|
690
733
|
for i in range(1, count):
|
|
691
734
|
if frames_match(first, files[i], 10, 0, crop, mask):
|
|
692
735
|
logging.debug("Removing pre-render frame %s", files[i])
|
|
@@ -705,7 +748,7 @@ def find_render_start(directory, orange_file, gray_file, cropped):
|
|
|
705
748
|
logging.exception("Error getting render start")
|
|
706
749
|
|
|
707
750
|
|
|
708
|
-
def eliminate_duplicate_frames(directory, cropped):
|
|
751
|
+
def eliminate_duplicate_frames(directory, cropped, is_mobile):
|
|
709
752
|
logging.debug("Eliminating Duplicate Frames...")
|
|
710
753
|
global client_viewport
|
|
711
754
|
try:
|
|
@@ -745,11 +788,16 @@ def eliminate_duplicate_frames(directory, cropped):
|
|
|
745
788
|
top += client_viewport["y"]
|
|
746
789
|
elif cropped:
|
|
747
790
|
# The image was already cropped, so only cutout the bottom
|
|
748
|
-
# to get rid of the network request/etc. information
|
|
791
|
+
# to get rid of the network request/etc. information for
|
|
792
|
+
# desktop videos, and nothing extra on mobile.
|
|
749
793
|
top = 0
|
|
750
794
|
left = 0
|
|
751
795
|
width = im_width
|
|
752
|
-
|
|
796
|
+
|
|
797
|
+
if is_mobile:
|
|
798
|
+
height = im_height
|
|
799
|
+
else:
|
|
800
|
+
height = im_height - bottom_margin
|
|
753
801
|
|
|
754
802
|
crop = "{0:d}x{1:d}+{2:d}+{3:d}".format(width, height, left, top)
|
|
755
803
|
logging.debug("Viewport cropping set to " + crop)
|
|
@@ -212,6 +212,23 @@ class Click {
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
/**
|
|
216
|
+
* Click on element located by the name, Uses document.querySelector.
|
|
217
|
+
* @param {string} name the name of the element
|
|
218
|
+
* @returns {Promise} Promise object represents when the element has been clicked
|
|
219
|
+
* @throws Will throw an error if the element is not found
|
|
220
|
+
*/
|
|
221
|
+
async byName(name) {
|
|
222
|
+
try {
|
|
223
|
+
const script = `document.querySelector("[name='${name}']").click()`;
|
|
224
|
+
await this.browser.runScript(script, 'CUSTOM');
|
|
225
|
+
} catch (e) {
|
|
226
|
+
log.error('Could not find element by name %s', name);
|
|
227
|
+
log.verbose(e);
|
|
228
|
+
throw Error('Could not find element by name ' + name);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
215
232
|
/**
|
|
216
233
|
* Click on link located by the ID attribute. Uses document.getElementById() to find the element. And wait for page complete check to finish.
|
|
217
234
|
* @param {string} id
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const log = require('intel').getLogger('browsertime.command.select');
|
|
4
|
+
|
|
5
|
+
class Select {
|
|
6
|
+
constructor(browser) {
|
|
7
|
+
this.browser = browser;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Select value of a select by the selects id
|
|
12
|
+
* @param {string} selectId The id of the select
|
|
13
|
+
* @param {string} value The value of the option you want to set
|
|
14
|
+
* @returns {Promise} Promise object represents when the option has been
|
|
15
|
+
* set to the element
|
|
16
|
+
* @throws Will throw an error if the select is not found
|
|
17
|
+
*/
|
|
18
|
+
async selectByIdAndValue(selectId, value) {
|
|
19
|
+
try {
|
|
20
|
+
const script = `const select = document.getElementById('${selectId}'); select.value = '${value}'; select.dispatchEvent(new Event('change'));`;
|
|
21
|
+
await this.browser.runScript(script, 'CUSTOM');
|
|
22
|
+
} catch (e) {
|
|
23
|
+
log.error('Could not select value for select with id %s', selectId);
|
|
24
|
+
log.verbose(e);
|
|
25
|
+
throw Error(`Could not select value for select with id ${selectId}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Select value of a select by the selects name
|
|
31
|
+
* @param {string} selectName The name of the select
|
|
32
|
+
* @param {string} value The value of the option you want to set
|
|
33
|
+
* @returns {Promise} Promise object represents when the option has been
|
|
34
|
+
* set to the element
|
|
35
|
+
* @throws Will throw an error if the select is not found
|
|
36
|
+
*/
|
|
37
|
+
async selectByNameAndValue(selectName, value) {
|
|
38
|
+
try {
|
|
39
|
+
const script = `const select = document.querySelector("select[name='${selectName}']"); select.value = '${value}'; select.dispatchEvent(new Event('change'));`;
|
|
40
|
+
await this.browser.runScript(script, 'CUSTOM');
|
|
41
|
+
} catch (e) {
|
|
42
|
+
log.error('Could not select value for select with name %s', selectName);
|
|
43
|
+
log.verbose(e);
|
|
44
|
+
throw Error(`Could not select value for select with name ${selectName}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Select value of a select index and by the selects id
|
|
50
|
+
* @param {string} selectId The id of the select
|
|
51
|
+
* @param {number} index the index of the option you want to set
|
|
52
|
+
* @returns {Promise} Promise object represents when the option has been
|
|
53
|
+
* set to the element
|
|
54
|
+
* @throws Will throw an error if the select is not found
|
|
55
|
+
*/
|
|
56
|
+
async selectByIdAndIndex(selectId, index) {
|
|
57
|
+
try {
|
|
58
|
+
const script = `const select = document.getElementById('${selectId}'); select.selectedIndex = ${index};select.dispatchEvent(new Event('change'));`;
|
|
59
|
+
const value = await this.browser.runScript(script, 'CUSTOM');
|
|
60
|
+
return value;
|
|
61
|
+
} catch (e) {
|
|
62
|
+
log.error(
|
|
63
|
+
'Could not select value for select with id %s by index',
|
|
64
|
+
selectId
|
|
65
|
+
);
|
|
66
|
+
log.verbose(e);
|
|
67
|
+
throw Error(`Could not select value for select with id ${selectId}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Select value of a select index and by the selects name
|
|
73
|
+
* @param {string} selectName - the name of the select
|
|
74
|
+
* @param {number} index - the index of the option you want to set
|
|
75
|
+
* @returns {Promise} Promise object represents when the option has been
|
|
76
|
+
* set to the element
|
|
77
|
+
* @throws Will throw an error if the select is not found
|
|
78
|
+
*/
|
|
79
|
+
async selectByNameAndIndex(selectName, index) {
|
|
80
|
+
try {
|
|
81
|
+
const script = `const select = document.querySelector("select[name='${selectName}']"); select.selectedIndex = ${index};select.dispatchEvent(new Event('change'));`;
|
|
82
|
+
const value = await this.browser.runScript(script, 'CUSTOM');
|
|
83
|
+
return value;
|
|
84
|
+
} catch (e) {
|
|
85
|
+
log.error(
|
|
86
|
+
'Could not select value for select with name %s by index',
|
|
87
|
+
selectName
|
|
88
|
+
);
|
|
89
|
+
log.verbose(e);
|
|
90
|
+
throw Error(`Could not select value for select name ${selectName}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Deselect all options in a select.
|
|
96
|
+
* @param {string} selectId
|
|
97
|
+
* @returns {Promise} Promise object represents when options been deselected
|
|
98
|
+
* @throws Will throw an error if the select is not found
|
|
99
|
+
*/
|
|
100
|
+
async deselectById(selectId) {
|
|
101
|
+
try {
|
|
102
|
+
const script = `const select = document.getElementById('${selectId}'); select.selectedIndex = -1;select.dispatchEvent(new Event('change'));`;
|
|
103
|
+
const value = await this.browser.runScript(script, 'CUSTOM');
|
|
104
|
+
return value;
|
|
105
|
+
} catch (e) {
|
|
106
|
+
log.error('Could not deleselect by select id %s', selectId);
|
|
107
|
+
log.verbose(e);
|
|
108
|
+
throw Error(`Could not deleselect by select id ${selectId}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get all option values in a select.
|
|
114
|
+
* @param {string} selectId - the id of the select.
|
|
115
|
+
* @returns {Promise} Promise object tha will return an array with the values of the select
|
|
116
|
+
* @throws Will throw an error if the select is not found
|
|
117
|
+
*/
|
|
118
|
+
async getValuesById(selectId) {
|
|
119
|
+
const script = `const select = document.getElementById('${selectId}');
|
|
120
|
+
const values = [];
|
|
121
|
+
for (let option of select.options) {
|
|
122
|
+
values.push(option.value);
|
|
123
|
+
}
|
|
124
|
+
return values;
|
|
125
|
+
`;
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const value = await this.browser.runScript(script, 'CUSTOM');
|
|
129
|
+
return value;
|
|
130
|
+
} catch (e) {
|
|
131
|
+
log.error('Could not get select options for id %s', selectId);
|
|
132
|
+
log.verbose(e);
|
|
133
|
+
throw Error(`Could not get select options for id ${selectId}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get the selected option value in a select.
|
|
139
|
+
* @param {select} selectId the id of the select.
|
|
140
|
+
* @returns {Promise} Promise object tha will return the value of the selected option.
|
|
141
|
+
* @throws Will throw an error if the select is not found.
|
|
142
|
+
*/
|
|
143
|
+
async getSelectedValueById(selectId) {
|
|
144
|
+
try {
|
|
145
|
+
const script = `const select = document.getElementById('${selectId}'); return select.options[select.selectedIndex].value;`;
|
|
146
|
+
const value = await this.browser.runScript(script, 'CUSTOM');
|
|
147
|
+
return value;
|
|
148
|
+
} catch (e) {
|
|
149
|
+
log.error('Could not select value for select with id %s', selectId);
|
|
150
|
+
log.verbose(e);
|
|
151
|
+
throw Error(`Could not select value for select with id ${selectId}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
module.exports = Select;
|
|
@@ -21,6 +21,7 @@ const Set = require('./command/set.js');
|
|
|
21
21
|
const Cache = require('./command/cache.js');
|
|
22
22
|
const Meta = require('./command/meta.js');
|
|
23
23
|
const StopWatch = require('./command/stopWatch.js');
|
|
24
|
+
const Select = require('./command/select.js');
|
|
24
25
|
const AndroidCommand = require('./command/android.js');
|
|
25
26
|
const ChromeDevToolsProtocol = require('./command/chromeDevToolsProtocol.js');
|
|
26
27
|
const { addConnectivity, removeConnectivity } = require('../../connectivity');
|
|
@@ -172,7 +173,8 @@ class Iteration {
|
|
|
172
173
|
singleClick: new SingleClick(browser, this.pageCompleteCheck),
|
|
173
174
|
doubleClick: new DoubleClick(browser, this.pageCompleteCheck),
|
|
174
175
|
clickAndHold: new ClickAndHold(browser)
|
|
175
|
-
}
|
|
176
|
+
},
|
|
177
|
+
select: new Select(browser)
|
|
176
178
|
};
|
|
177
179
|
|
|
178
180
|
// Safari can't load data:text URLs at startup, so you can either choose
|
|
@@ -222,9 +222,23 @@ class SeleniumRunner {
|
|
|
222
222
|
// query parameters, order fragments, etc. We don't want to change url itself 'cuz it can be
|
|
223
223
|
// used for indexing collected data and it is possible that normalization could change a key.
|
|
224
224
|
const normalizedURI = new URL(url).toString();
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
225
|
+
|
|
226
|
+
// See https://github.com/sitespeedio/browsertime/issues/1698
|
|
227
|
+
const retries = 5;
|
|
228
|
+
let documentURI;
|
|
229
|
+
for (var i = 0; i < retries; i++) {
|
|
230
|
+
documentURI = await driver.executeScript('return document.documentURI;');
|
|
231
|
+
if (documentURI != null) {
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
await delay(1000);
|
|
235
|
+
}
|
|
236
|
+
let startURI;
|
|
237
|
+
try {
|
|
238
|
+
startURI = new URL(documentURI).toString();
|
|
239
|
+
} catch (e) {
|
|
240
|
+
log.error(`Failed to get documentURI ${documentURI}`, e);
|
|
241
|
+
}
|
|
228
242
|
if (this.options.webdriverPageload) {
|
|
229
243
|
const clearOrange = `(function() {
|
|
230
244
|
const orange = document.getElementById('browsertime-orange');
|
|
@@ -297,7 +311,7 @@ class SeleniumRunner {
|
|
|
297
311
|
} else {
|
|
298
312
|
const waitTime = (this.options.retryWaitTime || 10000) * (i + 1);
|
|
299
313
|
totalWaitTime += waitTime;
|
|
300
|
-
log.
|
|
314
|
+
log.debug(
|
|
301
315
|
`URL ${url} failed to load, the ${
|
|
302
316
|
this.options.browser
|
|
303
317
|
} are still on ${startURI} , trying ${
|
package/lib/support/cli.js
CHANGED
|
@@ -982,7 +982,7 @@ module.exports.parseCommandLine = function parseCommandLine() {
|
|
|
982
982
|
type: 'array',
|
|
983
983
|
default: [0, 10, 90, 99, 100],
|
|
984
984
|
describe:
|
|
985
|
-
'The percentile values within the data browsertime will calculate and report.'
|
|
985
|
+
'The percentile values within the data browsertime will calculate and report. This argument uses Yargs arrays and you you to set them correctly it is recommended to use a configuraration file instead.'
|
|
986
986
|
})
|
|
987
987
|
.option('decimals', {
|
|
988
988
|
type: 'number',
|
package/lib/support/util.js
CHANGED
|
@@ -16,7 +16,7 @@ module.exports = {
|
|
|
16
16
|
} else return value;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
let formatted = `${name}: ${fmt(multiple ? metric.
|
|
19
|
+
let formatted = `${name}: ${fmt(multiple ? metric.median : metric)}`;
|
|
20
20
|
if (extras) {
|
|
21
21
|
formatted += ` (σ${fmt(metric.stddev.toFixed(2))})`;
|
|
22
22
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"description": "Browsertime",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.18.1",
|
|
4
4
|
"bin": "./bin/browsertime.js",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@sitespeed.io/throttle": "3.0.0",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"lodash.set": "4.3.2",
|
|
25
25
|
"selenium-webdriver": "4.1.0",
|
|
26
26
|
"@sitespeed.io/edgedriver": "95.0.1020-30",
|
|
27
|
-
"@sitespeed.io/geckodriver": "0.29.1-
|
|
27
|
+
"@sitespeed.io/geckodriver": "0.29.1-3",
|
|
28
28
|
"@sitespeed.io/tracium": "0.3.3",
|
|
29
29
|
"speedline-core": "1.4.3",
|
|
30
30
|
"@cypress/xvfb": "1.2.4",
|