403 lines
15 KiB
HTML
403 lines
15 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>In-Browser Tests for Performance Dashboard</title>
|
|
<link rel="stylesheet" href="../node_modules/mocha/mocha.css">
|
|
<script src="../node_modules/mocha/mocha.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/expect.js/0.2.0/expect.min.js"></script>
|
|
<script>
|
|
|
|
mocha.setup('bdd');
|
|
|
|
</script>
|
|
<script src="../unit-tests/resources/mock-remote-api.js"></script>
|
|
</head>
|
|
<body>
|
|
<div id="mocha"></div>
|
|
<script src="async-task-tests.js"></script>
|
|
<script src="component-base-tests.js"></script>
|
|
<script src="page-tests.js"></script>
|
|
<script src="page-router-tests.js"></script>
|
|
<script src="close-button-tests.js"></script>
|
|
<script src="editable-text-tests.js"></script>
|
|
<script src="time-series-chart-tests.js"></script>
|
|
<script src="interactive-time-series-chart-tests.js"></script>
|
|
<script src="chart-status-evaluator-tests.js"></script>
|
|
<script src="chart-revision-range-tests.js"></script>
|
|
<script src="commit-log-viewer-tests.js"></script>
|
|
<script src="test-group-form-tests.js"></script>
|
|
<script src="custom-analysis-task-configurator-tests.js"></script>
|
|
<script src="customizable-test-group-form-tests.js"></script>
|
|
<script src="markup-page-tests.js"></script>
|
|
<script src="test-group-result-page-tests.js"></script>
|
|
<script>
|
|
|
|
afterEach(() => {
|
|
BrowsingContext.cleanup();
|
|
});
|
|
|
|
class BrowsingContext {
|
|
|
|
constructor()
|
|
{
|
|
let iframe = document.createElement('iframe');
|
|
document.body.appendChild(iframe);
|
|
iframe.style.position = 'absolute';
|
|
iframe.style.left = '0px';
|
|
iframe.style.top = '0px';
|
|
BrowsingContext._iframes.push(iframe);
|
|
|
|
// Expedite calls to callbacks to make tests go faster.
|
|
iframe.contentWindow.requestAnimationFrame = (callback) => setTimeout(callback, 0);
|
|
|
|
// Allow markup-component.js to function.
|
|
iframe.contentWindow.require = (module) => {
|
|
expect(module).to.be('assert');
|
|
return {
|
|
ok: (condition) => expect(!!condition).to.be(true)
|
|
};
|
|
}
|
|
|
|
this.iframe = iframe;
|
|
this.symbols = {};
|
|
this.global = this.iframe.contentWindow;
|
|
this.document = this.iframe.contentDocument;
|
|
this._didLoadMockRemote = false;
|
|
}
|
|
|
|
importScripts(pathList, ...symbolList)
|
|
{
|
|
const doc = this.iframe.contentDocument;
|
|
const global = this.iframe.contentWindow;
|
|
|
|
pathList = pathList.map((path) => `../public/v3/${path}`);
|
|
if (!this._didLoadMockRemote) {
|
|
this._didLoadMockRemote = true;
|
|
pathList.unshift('../unit-tests/resources/mock-remote-api.js');
|
|
}
|
|
|
|
return Promise.all(pathList.map((path) => {
|
|
return new Promise((resolve, reject) => {
|
|
let script = doc.createElement('script');
|
|
script.addEventListener('load', resolve);
|
|
script.addEventListener('error', reject);
|
|
script.src = path;
|
|
script.async = false;
|
|
doc.body.appendChild(script);
|
|
});
|
|
})).then(() => {
|
|
const script = doc.createElement('script');
|
|
script.textContent = `window.importedSymbols = [${symbolList.join(', ')}];`;
|
|
global.RemoteAPI = global.MockRemoteAPI;
|
|
doc.body.appendChild(script);
|
|
|
|
const importedSymbols = global.importedSymbols;
|
|
for (let i = 0; i < symbolList.length; i++)
|
|
this.symbols[symbolList[i]] = importedSymbols[i];
|
|
|
|
return symbolList.length == 1 ? importedSymbols[0] : importedSymbols;
|
|
});
|
|
}
|
|
|
|
importScript(path, ...symbols)
|
|
{
|
|
return this.importScripts([path], ...symbols);
|
|
}
|
|
|
|
static cleanup()
|
|
{
|
|
BrowsingContext._iframes.forEach((iframe) => { iframe.remove(); });
|
|
BrowsingContext._iframes = [];
|
|
}
|
|
}
|
|
BrowsingContext._iframes = [];
|
|
|
|
function waitForComponentsToRender(context)
|
|
{
|
|
if (!context._dummyComponent) {
|
|
const ComponentBase = context.symbols.ComponentBase;
|
|
context._dummyComponent = class SomeComponent extends ComponentBase {
|
|
constructor(resolve)
|
|
{
|
|
super();
|
|
this._resolve = resolve;
|
|
}
|
|
render() { setTimeout(this._resolve, 0); }
|
|
}
|
|
ComponentBase.defineElement('dummy-component', context._dummyComponent);
|
|
}
|
|
return new Promise((resolve) => {
|
|
const instance = new context._dummyComponent(resolve);
|
|
context.document.body.appendChild(instance.element());
|
|
setTimeout(() => {
|
|
instance.enqueueToRender();
|
|
}, 0);
|
|
});
|
|
}
|
|
|
|
function waitForElementResize(element)
|
|
{
|
|
return new Promise((resolve) => {
|
|
const observer = new ResizeObserver(() => {
|
|
observer.disconnect();
|
|
resolve();
|
|
});
|
|
observer.observe(element);
|
|
})
|
|
}
|
|
|
|
function wait(milliseconds)
|
|
{
|
|
return new Promise((resolve) => {
|
|
setTimeout(resolve, milliseconds);
|
|
});
|
|
}
|
|
|
|
function canvasImageData(canvas)
|
|
{
|
|
return canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
|
|
}
|
|
|
|
function canvasRefTest(canvas1, canvas2, shouldMatch)
|
|
{
|
|
expect(canvas1.offsetWidth).to.be(canvas2.offsetWidth);
|
|
expect(canvas2.offsetHeight).to.be(canvas2.offsetHeight);
|
|
const data1 = canvasImageData(canvas1).data;
|
|
const data2 = canvasImageData(canvas2).data;
|
|
expect(data1.length).to.be.a('number');
|
|
expect(data1.length).to.be(data2.length);
|
|
|
|
let match = true;
|
|
for (let i = 0; i < data1.length; i++) {
|
|
if (data1[i] != data2[i]) {
|
|
match = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (match == shouldMatch)
|
|
return;
|
|
|
|
[canvas1, canvas2].forEach((canvas) => {
|
|
let image = document.createElement('img');
|
|
image.src = canvas.toDataURL();
|
|
image.style.display = 'block';
|
|
document.body.appendChild(image);
|
|
});
|
|
|
|
throw new Error(shouldMatch ? 'Canvas contents were different' : 'Canvas contents were identical');
|
|
}
|
|
|
|
const CanvasTest = {
|
|
fillCanvasBeforeRedrawCheck(canvas)
|
|
{
|
|
const canvasContext = canvas.getContext('2d');
|
|
canvasContext.fillStyle = 'white';
|
|
canvasContext.fillRect(0, 0, canvas.width, canvas.height);
|
|
},
|
|
|
|
hasCanvasBeenRedrawn(canvas)
|
|
{
|
|
return canvasImageData(canvas).data.some((value) => value != 255);
|
|
},
|
|
|
|
canvasImageData(canvas) { return canvasImageData(canvas); },
|
|
|
|
canvasContainsColor(canvas, color, rect = {})
|
|
{
|
|
const content = canvas.getContext('2d').getImageData(rect.x || 0, rect.y || 0, rect.width || canvas.width, rect.height || canvas.height);
|
|
let found = false;
|
|
const data = content.data;
|
|
for (let startOfPixel = 0; startOfPixel < data.length; startOfPixel += 4) {
|
|
let r = data[startOfPixel];
|
|
let g = data[startOfPixel + 1];
|
|
let b = data[startOfPixel + 2];
|
|
let a = data[startOfPixel + 3];
|
|
if (r == color.r && g == color.g && b == color.b && (color.a == undefined || a == color.a))
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
expectCanvasesMatch(canvas1, canvas2) { return canvasRefTest(canvas1, canvas2, true); },
|
|
expectCanvasesMismatch(canvas1, canvas2) { return canvasRefTest(canvas1, canvas2, false); },
|
|
}
|
|
|
|
const dayInMilliseconds = 24 * 3600 * 1000;
|
|
|
|
function posixTime(string) { return +new Date(string); }
|
|
|
|
const ChartTest = {
|
|
importChartScripts(context)
|
|
{
|
|
return context.importScripts([
|
|
'async-task.js',
|
|
'../shared/statistics.js',
|
|
'lazily-evaluated-function.js',
|
|
'instrumentation.js',
|
|
'models/data-model.js',
|
|
'models/time-series.js',
|
|
'models/measurement-set.js',
|
|
'models/measurement-cluster.js',
|
|
'models/measurement-adaptor.js',
|
|
'models/repository.js',
|
|
'models/platform.js',
|
|
'models/test.js',
|
|
'models/metric.js',
|
|
'models/commit-set.js',
|
|
'models/commit-log.js',
|
|
'../shared/common-component-base.js',
|
|
'components/base.js',
|
|
'components/time-series-chart.js',
|
|
'components/interactive-time-series-chart.js'],
|
|
'CommonComponentBase', 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart',
|
|
'Platform', 'Metric', 'Test', 'Repository', 'MeasurementSet', 'MockRemoteAPI', 'AsyncTask').then(() => {
|
|
return context.symbols.TimeSeriesChart;
|
|
})
|
|
},
|
|
|
|
posixTime: posixTime,
|
|
|
|
get sampleCluster() { return this.makeSampleCluster(); },
|
|
|
|
makeModelObjectsForSampleCluster(context)
|
|
{
|
|
const test = context.symbols.Test.ensureSingleton(2, {name: 'Test'});
|
|
const metric = context.symbols.Metric.ensureSingleton(1, {name: 'Time', test})
|
|
const platform = context.symbols.Platform.ensureSingleton(1,
|
|
{name: 'SomePlatform', metrics: [metric], lastModifiedByMetric: [posixTime('2016-01-18T00:00:00Z')]});
|
|
metric.addPlatform(platform);
|
|
context.symbols.Repository.ensureSingleton(1, {name: 'SomeApp'});
|
|
context.symbols.Repository.ensureSingleton(2, {name: 'macOS'});
|
|
},
|
|
|
|
makeSampleCluster(options = {})
|
|
{
|
|
const baselineStart = options.baselineIsSmaller ? 30 : 130;
|
|
const targetStart = options.targetIsBigger ? 120 : 90;
|
|
return {
|
|
"clusterStart": posixTime('2016-01-01T00:00:00Z'),
|
|
"clusterSize": 7 * dayInMilliseconds,
|
|
"startTime": posixTime('2016-01-01T00:00:00Z'),
|
|
"endTime": posixTime('2016-01-08T00:00:00Z'),
|
|
"lastModified": posixTime('2016-01-18T00:00:00Z'),
|
|
"clusterCount": 1,
|
|
"status": "OK",
|
|
"formatMap": [
|
|
"id", "mean", "iterationCount", "sum", "squareSum", "markedOutlier",
|
|
"revisions",
|
|
"commitTime", "build", "buildTime", "buildTag", "builder"
|
|
],
|
|
"configurations": {
|
|
"current": [
|
|
[
|
|
1000, 100, 1, 100, 100 * 100, false,
|
|
[ [2000, 1, "4000", null, posixTime('2016-01-05T17:35:00Z')], [3000, 2, "15B42", null, 0, 0] ],
|
|
posixTime('2016-01-05T17:35:00Z'), 5000, posixTime('2016-01-05T19:23:00Z'), "10", 7
|
|
],
|
|
[
|
|
1001, 131, 1, 131, 131 * 131, true,
|
|
[ [2001, 1, "4001", null, posixTime('2016-01-05T18:43:01Z')], [3000, 2, "15B42", null, 0, 0] ],
|
|
posixTime('2016-01-05T18:43:01Z'), 5001, posixTime('2016-01-05T20:58:01Z'), "11", 7
|
|
],
|
|
[
|
|
1002, 122, 1, 122, 122 * 122, false,
|
|
[ [2002, 1, "4002", null, posixTime('2016-01-05T20:01:02Z')], [3000, 2, "15B42", null, 0, 0] ],
|
|
posixTime('2016-01-05T20:01:02Z'), 5002, posixTime('2016-01-05T22:37:02Z'), "12", 7
|
|
],
|
|
[
|
|
1003, 113, 1, 113, 113 * 113, false,
|
|
[ [2003, 1, "4003", null, posixTime('2016-01-05T23:19:03Z')], [3000, 2, "15B42", null, 0, 0] ],
|
|
posixTime('2016-01-05T23:19:03Z'), 5003, posixTime('2016-01-06T23:19:03Z'), "13", 7
|
|
],
|
|
[
|
|
1004, 124, 1, 124, 124 * 124, false,
|
|
[ [2004, 1, "4004", null, posixTime('2016-01-06T01:52:04Z')], [3001, 2, "15C50", null, 0, 0] ],
|
|
posixTime('2016-01-06T01:52:04Z'), 5004, posixTime('2016-01-06T02:42:04Z'), "14", 7
|
|
],
|
|
[
|
|
1005, 115, 1, 115, 115 * 115, true,
|
|
[ [2005, 1, "4005", null, posixTime('2016-01-06T03:22:05Z')], [3001, 2, "15C50", null, 0, 0] ],
|
|
posixTime('2016-01-06T03:22:05Z'), 5005, posixTime('2016-01-06T06:01:05Z'), "15", 7
|
|
],
|
|
[
|
|
1006, 116, 1, 116, 116 * 116, false,
|
|
[ [2006, 1, "4006", null, posixTime('2016-01-06T05:59:06Z')], [3001, 2, "15C50", null, 0, 0] ],
|
|
posixTime('2016-01-06T05:59:06Z'), 5006, posixTime('2016-01-06T08:34:06Z'), "16", 7
|
|
]
|
|
],
|
|
"baseline": [
|
|
[
|
|
7000, baselineStart, 1, baselineStart, baselineStart * baselineStart, false,
|
|
[ ],
|
|
posixTime('2016-01-05T12:00:30Z'), 5030, posixTime('2016-01-05T12:00:30Z'), "30", 7
|
|
],
|
|
[
|
|
7001, baselineStart + 1, 1, baselineStart + 1, Math.pow(baselineStart + 1, 2), false,
|
|
[ ],
|
|
posixTime('2016-01-06T00:00:31Z'), 5031, posixTime('2016-01-06T00:00:31Z'), "31", 7
|
|
],
|
|
],
|
|
"target": [
|
|
[
|
|
8000, targetStart, 1, targetStart, targetStart * targetStart, false,
|
|
[ ],
|
|
posixTime('2016-01-05T12:00:30Z'), 5030, posixTime('2016-01-05T12:00:30Z'), "90", 7
|
|
],
|
|
[
|
|
8001, targetStart + 1, 1, targetStart + 1, Math.pow(targetStart + 1, 2), false,
|
|
[ ],
|
|
posixTime('2016-01-06T00:00:31Z'), 5031, posixTime('2016-01-06T00:00:31Z'), "91", 7
|
|
],
|
|
]
|
|
}};
|
|
},
|
|
|
|
createChartWithSampleCluster(context, sourceList = null, chartOptions = {}, className = 'TimeSeriesChart')
|
|
{
|
|
const TimeSeriesChart = context.symbols[className];
|
|
const MeasurementSet = context.symbols.MeasurementSet;
|
|
|
|
if (sourceList == null)
|
|
sourceList = [{type: 'current'}];
|
|
|
|
const sampleCluster = MeasurementSet.findSet(1, 1, 0);
|
|
for (let source of sourceList) {
|
|
if (!source.type)
|
|
source.type = 'current';
|
|
source.measurementSet = sampleCluster;
|
|
}
|
|
|
|
const chart = new TimeSeriesChart(sourceList, chartOptions);
|
|
const element = chart.element();
|
|
element.style.width = chartOptions.width || '300px';
|
|
element.style.height = chartOptions.height || '100px';
|
|
context.document.body.appendChild(element);
|
|
|
|
return chart;
|
|
},
|
|
|
|
createInteractiveChartWithSampleCluster(context, sourceList = null, chartOptions = {})
|
|
{
|
|
if (sourceList == null)
|
|
sourceList = [{type: 'current', interactive: true}];
|
|
return this.createChartWithSampleCluster(context, sourceList, chartOptions, 'InteractiveTimeSeriesChart');
|
|
},
|
|
|
|
respondWithSampleCluster(request, options)
|
|
{
|
|
expect(request.url).to.be('/data/measurement-set-1-1.json');
|
|
expect(request.method).to.be('GET');
|
|
request.resolve(this.makeSampleCluster(options));
|
|
},
|
|
};
|
|
|
|
mocha.checkLeaks();
|
|
mocha.globals(['expect', 'BrowsingContext', 'CanvasTest', 'ChartTest', 'wait', 'waitForComponentsToRender', 'waitForElementResize']);
|
|
mocha.run();
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|