|
| 1 | +/* eslint-disable no-case-declarations */ |
| 2 | +import { TestRunner } from "./test-runner.mjs"; |
| 3 | +import { Params } from "./params.mjs"; |
| 4 | + |
| 5 | +/** |
| 6 | + * BenchmarkStep |
| 7 | + * |
| 8 | + * A single test step, with a common interface to interact with. |
| 9 | + */ |
| 10 | +export class BenchmarkStep { |
| 11 | + constructor(name, run) { |
| 12 | + this.name = name; |
| 13 | + this.run = run; |
| 14 | + } |
| 15 | + |
| 16 | + async runAndRecord(params, suite, test, callback) { |
| 17 | + const testRunner = new TestRunner(null, null, params, suite, test, callback); |
| 18 | + const result = await testRunner.runTest(); |
| 19 | + return result; |
| 20 | + } |
| 21 | +} |
| 22 | + |
| 23 | +/** |
| 24 | + * BenchmarkSuite |
| 25 | + * |
| 26 | + * A single test suite that contains one or more test steps. |
| 27 | + */ |
| 28 | +export class BenchmarkSuite { |
| 29 | + constructor(name, tests) { |
| 30 | + this.name = name; |
| 31 | + this.tests = tests; |
| 32 | + } |
| 33 | + |
| 34 | + record(_test, syncTime, asyncTime) { |
| 35 | + const total = syncTime + asyncTime; |
| 36 | + const results = { |
| 37 | + tests: { Sync: syncTime, Async: asyncTime }, |
| 38 | + total: total, |
| 39 | + }; |
| 40 | + |
| 41 | + return results; |
| 42 | + } |
| 43 | + |
| 44 | + async runAndRecord(params, onProgress) { |
| 45 | + const measuredValues = { |
| 46 | + tests: {}, |
| 47 | + total: 0, |
| 48 | + }; |
| 49 | + const suiteStartLabel = `suite-${this.name}-start`; |
| 50 | + const suiteEndLabel = `suite-${this.name}-end`; |
| 51 | + |
| 52 | + performance.mark(suiteStartLabel); |
| 53 | + |
| 54 | + for (const test of this.tests) { |
| 55 | + const result = await test.runAndRecord(params, this, test, this.record); |
| 56 | + measuredValues.tests[test.name] = result; |
| 57 | + measuredValues.total += result.total; |
| 58 | + onProgress?.(test.name); |
| 59 | + } |
| 60 | + |
| 61 | + performance.mark(suiteEndLabel); |
| 62 | + performance.measure(`suite-${this.name}`, suiteStartLabel, suiteEndLabel); |
| 63 | + |
| 64 | + return { |
| 65 | + type: "suite-tests-complete", |
| 66 | + status: "success", |
| 67 | + result: measuredValues, |
| 68 | + suitename: this.name, |
| 69 | + }; |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +/** ********************************************************************** |
| 74 | + * BenchmarkConnector |
| 75 | + * |
| 76 | + * postMessage is used to communicate between app and benchmark. |
| 77 | + * When the app is ready, an 'app-ready' message is sent to signal that the app can receive instructions. |
| 78 | + * |
| 79 | + * A prepare script within the apps appends window.name and window.version from the package.json file. |
| 80 | + * The appId is build by appending name-version |
| 81 | + * It's used as an additional safe-guard to ensure the correct app responds to a message. |
| 82 | + *************************************************************************/ |
| 83 | +export class BenchmarkConnector { |
| 84 | + constructor(suites, name, version) { |
| 85 | + this.suites = suites; |
| 86 | + this.name = name; |
| 87 | + this.version = version; |
| 88 | + |
| 89 | + if (!name || !version) |
| 90 | + console.warn("No name or version supplied, to create a unique appId"); |
| 91 | + |
| 92 | + this.appId = name && version ? `${name}-${version}` : -1; |
| 93 | + this.onMessage = this.onMessage.bind(this); |
| 94 | + } |
| 95 | + |
| 96 | + async onMessage(event) { |
| 97 | + if (event.data.id !== this.appId || event.data.key !== "benchmark-connector") |
| 98 | + return; |
| 99 | + |
| 100 | + switch (event.data.type) { |
| 101 | + case "benchmark-suite": |
| 102 | + const params = new Params(new URLSearchParams(window.location.search)); |
| 103 | + const suite = this.suites[event.data.name]; |
| 104 | + if (!suite) |
| 105 | + console.error(`Suite with the name of "${event.data.name}" not found!`); |
| 106 | + const { result } = await suite.runAndRecord(params, (test) => this.sendMessage({ type: "step-complete", status: "success", appId: this.appId, name: this.name, test })); |
| 107 | + this.sendMessage({ type: "suite-complete", status: "success", appId: this.appId, result }); |
| 108 | + this.disconnect(); |
| 109 | + break; |
| 110 | + default: |
| 111 | + console.error(`Message data type not supported: ${event.data.type}`); |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + sendMessage(message) { |
| 116 | + window.top.postMessage(message, "*"); |
| 117 | + } |
| 118 | + |
| 119 | + connect() { |
| 120 | + window.addEventListener("message", this.onMessage); |
| 121 | + this.sendMessage({ type: "app-ready", status: "success", appId: this.appId }); |
| 122 | + } |
| 123 | + |
| 124 | + disconnect() { |
| 125 | + window.removeEventListener("message", this.onMessage); |
| 126 | + } |
| 127 | +} |
0 commit comments