1class Params { 2 viewport = { 3 width: 800, 4 height: 600, 5 }; 6 // Enable a detailed developer menu to change the current Params. 7 developerMode = false; 8 startAutomatically = false; 9 iterationCount = 10; 10 suites = []; 11 // A list of tags to filter suites 12 tags = []; 13 // Toggle running a dummy suite once before the normal test suites. 14 useWarmupSuite = false; 15 // Change how a test measurement is triggered and async time is measured: 16 // "timer": The classic (as in Speedometer 2.x) way using setTimeout 17 // "raf": Using rAF callbacks, both for triggering the sync part and for measuring async time. 18 measurementMethod = "raf"; // or "timer" 19 // Wait time before the sync step in ms. 20 waitBeforeSync = 0; 21 // Warmup time before the sync step in ms. 22 warmupBeforeSync = 0; 23 // Seed for shuffling the execution order of suites. 24 // "off": do not shuffle 25 // "generate": generate a random seed 26 // <integer>: use the provided integer as a seed 27 shuffleSeed = "off"; 28 29 constructor(searchParams = undefined) { 30 if (searchParams) 31 this._copyFromSearchParams(searchParams); 32 if (!this.developerMode) { 33 Object.freeze(this.viewport); 34 Object.freeze(this); 35 } 36 } 37 38 _parseInt(value, errorMessage) { 39 const number = Number(value); 40 if (!Number.isInteger(number) && errorMessage) 41 throw new Error(`Invalid ${errorMessage} param: '${value}', expected int.`); 42 return parseInt(number); 43 } 44 45 _copyFromSearchParams(searchParams) { 46 if (searchParams.has("viewport")) { 47 const viewportParam = searchParams.get("viewport"); 48 const [width, height] = viewportParam.split("x"); 49 this.viewport.width = this._parseInt(width, "viewport.width"); 50 this.viewport.height = this._parseInt(height, "viewport.height"); 51 if (this.viewport.width < 800 || this.viewport.height < 600) 52 throw new Error(`Invalid viewport param: ${viewportParam}`); 53 searchParams.delete("viewport"); 54 } 55 this.startAutomatically = searchParams.has("startAutomatically"); 56 searchParams.delete("startAutomatically"); 57 if (searchParams.has("iterationCount")) { 58 this.iterationCount = this._parseInt(searchParams.get("iterationCount"), "iterationCount"); 59 if (this.iterationCount < 1) 60 throw new Error(`Invalid iterationCount param: '${this.iterationCount}', must be > 1.`); 61 searchParams.delete("iterationCount"); 62 } 63 if (searchParams.has("suite") || searchParams.has("suites")) { 64 if (searchParams.has("suite") && searchParams.has("suites"))
65 throw new Error("Params 'suite' and 'suites' can not be used together."); 66 const value = searchParams.get("suite") || searchParams.get("suites"); 67 this.suites = value.split(","); 68 if (this.suites.length === 0) 69 throw new Error("No suites selected"); 70 searchParams.delete("suite"); 71 searchParams.delete("suites"); 72 } 73 74 if (searchParams.has("tags")) { 75 if (this.suites.length) 76 throw new Error("'suites' and 'tags' cannot be used together."); 77 this.tags = searchParams.get("tags").split(","); 78 searchParams.delete("tags"); 79 } 80 81 this.developerMode = searchParams.has("developerMode"); 82 searchParams.delete("developerMode"); 83 84 if (searchParams.has("useWarmupSuite")) { 85 this.useWarmupSuite = true; 86 searchParams.delete("useWarmupSuite"); 87 } 88 89 if (searchParams.has("waitBeforeSync")) { 90 this.waitBeforeSync = this._parseInt(searchParams.get("waitBeforeSync"), "waitBeforeSync"); 91 if (this.waitBeforeSync < 0) 92 throw new Error(`Invalid waitBeforeSync param: '${this.waitBeforeSync}', must be >= 0.`); 93 searchParams.delete("waitBeforeSync"); 94 } 95 96 if (searchParams.has("warmupBeforeSync")) { 97 this.warmupBeforeSync = this._parseInt(searchParams.get("warmupBeforeSync"), "warmupBeforeSync"); 98 if (this.warmupBeforeSync < 0) 99 throw new Error(`Invalid warmupBeforeSync param: '${this.warmupBeforeSync}', must be >= 0.`); 100 searchParams.delete("warmupBeforeSync"); 101 } 102 103 if (searchParams.has("measurementMethod")) { 104 this.measurementMethod = searchParams.get("measurementMethod"); 105 if (this.measurementMethod !== "timer" && this.measurementMethod !== "raf") 106 throw new Error(`Invalid measurement method: '${this.measurementMethod}', must be either 'raf' or 'timer'.`); 107 searchParams.delete("measurementMethod"); 108 } 109 110 if (searchParams.has("shuffleSeed")) { 111 this.shuffleSeed = searchParams.get("shuffleSeed"); 112 if (this.shuffleSeed !== "off") { 113 if (this.shuffleSeed === "generate") { 114 this.shuffleSeed = Math.floor((Math.random() * 1) << 16); 115 console.log(`Generated a random suite order seed: ${this.shuffleSeed}`); 116 } else { 117 this.shuffleSeed = parseInt(this.shuffleSeed); 118 } 119 if (!Number.isInteger(this.shuffleSeed)) 120 throw new Error(`Invalid shuffle seed: '${this.shuffleSeed}', must be either 'off', 'generate' or an integer.`); 121 } 122 searchParams.delete("shuffleSeed"); 123 } 124 125 const unused = Array.from(searchParams.keys()); 126 if (unused.length > 0) 127 console.error("Got unused search params", unused); 128 } 129 130 toSearchParams() { 131 const rawParams = { ...this }; 132 rawParams["viewport"] = `${this.viewport.width}x${this.viewport.height}`; 133 return new URLSearchParams(rawParams).toString(); 134 } 135} 136 137export const defaultParams = new Params(); 138 139const searchParams = new URLSearchParams(window.location.search); 140let maybeCustomParams = new Params(); 141try { 142 maybeCustomParams = new Params(searchParams); 143} catch (e) { 144 console.error("Invalid URL Param", e, "\nUsing defaults as fallback:", maybeCustomParams); 145 alert(`Invalid URL Param: ${e}`); 146} 147export const params = maybeCustomParams;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.