-
Notifications
You must be signed in to change notification settings - Fork 90
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
base: main
Are you sure you want to change the base?
Changes from all 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 |
---|---|---|
@@ -0,0 +1 @@ | ||
export const numberOfItemsToAdd = 100; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { BenchmarkConnector } from "./speedometer-utils/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(); |
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 { | ||
constructor(name, run) { | ||
this.name = name; | ||
this.run = run; | ||
} | ||
|
||
async runAndRecord(params, suite, test, callback) { | ||
flashdesignory marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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; | ||
flashdesignory marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
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; | ||
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)); | ||
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. So we're gonna propagate location.search from the main frame to iframe? 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. what's the benefit of doing it this way? 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. So that we don't have to propagate / forward the query string from the main document to the iframe's document. 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. I think it would be nicer to explicitly forward this for debugging purposes. One would easily see the full URL including params for the frame in the network log and could open that separately. Either approach would be fine. |
||
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. | ||
flashdesignory marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
export function getParent(lookupStartNode, path) { | ||
flashdesignory marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.