-
Notifications
You must be signed in to change notification settings - Fork 89
PostMessage Cleanup - TodoMVC example (experimental) #509
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
0a0d3df
c7d51ba
57b6245
5f4489c
129317b
c70043e
d9c4022
4c91e59
be6fa5f
cb3e998
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export * from "./helpers.mjs"; | ||
export * from "./benchmark.mjs"; | ||
export * from "./translations.mjs"; | ||
flashdesignory marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/* eslint-disable no-case-declarations */ | ||
import { TestRunner } from "./test-runner.mjs"; | ||
import { Params } from "./params.mjs"; | ||
|
||
/** | ||
* BenchmarkStep | ||
* | ||
* A single test step, with a common interface to interact with. | ||
*/ | ||
export class BenchmarkStep { | ||
flashdesignory marked this conversation as resolved.
Show resolved
Hide resolved
|
||
constructor(name, run) { | ||
this.name = name; | ||
this.run = run; | ||
} | ||
|
||
async runAndRecord(params, suite, test, callback) { | ||
const testRunner = new TestRunner(null, null, params, suite, test, callback); | ||
const result = await testRunner.runTest(); | ||
return result; | ||
} | ||
} | ||
|
||
/** | ||
* BenchmarkSuite | ||
* | ||
* A single test suite that contains one or more test steps. | ||
*/ | ||
export class BenchmarkSuite { | ||
constructor(name, tests) { | ||
this.name = name; | ||
this.tests = tests; | ||
} | ||
|
||
record(_test, syncTime, asyncTime) { | ||
const total = syncTime + asyncTime; | ||
const results = { | ||
tests: { Sync: syncTime, Async: asyncTime }, | ||
total: total, | ||
}; | ||
|
||
return results; | ||
} | ||
|
||
async runAndRecord(params, onProgress) { | ||
const measuredValues = { | ||
tests: {}, | ||
total: 0, | ||
}; | ||
const suiteStartLabel = `suite-${this.name}-start`; | ||
const suiteEndLabel = `suite-${this.name}-end`; | ||
|
||
performance.mark(suiteStartLabel); | ||
|
||
for (const test of this.tests) { | ||
const result = await test.runAndRecord(params, this, test, this.record); | ||
measuredValues.tests[test.name] = result; | ||
measuredValues.total += result.total; | ||
onProgress?.(test.name); | ||
} | ||
|
||
performance.mark(suiteEndLabel); | ||
performance.measure(`suite-${this.name}`, suiteStartLabel, suiteEndLabel); | ||
|
||
return { | ||
type: "suite-tests-complete", | ||
status: "success", | ||
result: measuredValues, | ||
suitename: this.name, | ||
}; | ||
} | ||
} | ||
|
||
/** ********************************************************************** | ||
* BenchmarkConnector | ||
* | ||
* postMessage is used to communicate between app and benchmark. | ||
* When the app is ready, an 'app-ready' message is sent to signal that the app can receive instructions. | ||
* | ||
* A prepare script within the apps appends window.name and window.version from the package.json file. | ||
* The appId is build by appending name-version | ||
* It's used as an additional safe-guard to ensure the correct app responds to a message. | ||
*************************************************************************/ | ||
export class BenchmarkConnector { | ||
constructor(suites, name, version) { | ||
this.suites = suites; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use private variables? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a benefit in refactoring? Maybe that's a larger conversation if we want to prefer private variables in our code and might be better to tackle in a separate discussion. This is consistent with the previous example that uses the postMessage api and I rather want to avoid larger changes in a single pr. |
||
this.name = name; | ||
this.version = version; | ||
|
||
if (!name || !version) | ||
console.warn("No name or version supplied, to create a unique appId"); | ||
|
||
this.appId = name && version ? `${name}-${version}` : -1; | ||
this.onMessage = this.onMessage.bind(this); | ||
} | ||
|
||
async onMessage(event) { | ||
if (event.data.id !== this.appId || event.data.key !== "benchmark-connector") | ||
return; | ||
|
||
switch (event.data.type) { | ||
case "benchmark-suite": | ||
const params = new Params(new URLSearchParams(window.location.search)); | ||
const suite = this.suites[event.data.name]; | ||
if (!suite) | ||
console.error(`Suite with the name of "${event.data.name}" not found!`); | ||
const { result } = await suite.runAndRecord(params, (test) => this.sendMessage({ type: "step-complete", status: "success", appId: this.appId, name: this.name, test })); | ||
this.sendMessage({ type: "suite-complete", status: "success", appId: this.appId, result }); | ||
this.disconnect(); | ||
break; | ||
default: | ||
console.error(`Message data type not supported: ${event.data.type}`); | ||
} | ||
} | ||
|
||
sendMessage(message) { | ||
window.top.postMessage(message, "*"); | ||
} | ||
|
||
connect() { | ||
window.addEventListener("message", this.onMessage); | ||
this.sendMessage({ type: "app-ready", status: "success", appId: this.appId }); | ||
} | ||
|
||
disconnect() { | ||
window.removeEventListener("message", this.onMessage); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/** | ||
* Helper Methods | ||
* | ||
* Various methods that are extracted from the Page class. | ||
*/ | ||
export function getParent(lookupStartNode, path) { | ||
lookupStartNode = lookupStartNode.shadowRoot ?? lookupStartNode; | ||
const parent = path.reduce((root, selector) => { | ||
const node = root.querySelector(selector); | ||
return node.shadowRoot ?? node; | ||
}, lookupStartNode); | ||
|
||
return parent; | ||
} | ||
|
||
export function getElement(selector, path = [], lookupStartNode = document) { | ||
const element = getParent(lookupStartNode, path).querySelector(selector); | ||
return element; | ||
} | ||
|
||
export function getAllElements(selector, path = [], lookupStartNode = document) { | ||
const elements = Array.from(getParent(lookupStartNode, path).querySelectorAll(selector)); | ||
return elements; | ||
} | ||
|
||
export function forceLayout() { | ||
const rect = document.body.getBoundingClientRect(); | ||
const e = document.elementFromPoint((rect.width / 2) | 0, (rect.height / 2) | 0); | ||
return e; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { BenchmarkConnector } from "./benchmark.mjs"; | ||
import suites, { appName, appVersion } from "./workload-test.mjs"; | ||
|
||
/* | ||
Paste below into dev console for manual testing: | ||
window.addEventListener("message", (event) => console.log(event.data)); | ||
window.postMessage({ id: "todomvc-postmessage-1.0.0", key: "benchmark-connector", type: "benchmark-suite", name: "default" }, "*"); | ||
*/ | ||
const benchmarkConnector = new BenchmarkConnector(suites, appName, appVersion); | ||
benchmarkConnector.connect(); |
Uh oh!
There was an error while loading. Please reload this page.