blob: 6b25815f7e52a2d3eabebca8b910a9a3d463aaa2 [file] [log] [blame]
<!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="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>
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);
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 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([
'../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',
'components/base.js',
'components/time-series-chart.js',
'components/interactive-time-series-chart.js'],
'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart',
'Platform', 'Metric', 'Test', 'Repository', 'MeasurementSet', 'MockRemoteAPI').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", "buildNumber", "builder"
],
"configurations": {
"current": [
[
1000, 100, 1, 100, 100 * 100, false,
[ [2000, 1, "4000", posixTime('2016-01-05T17:35:00Z')], [3000, 2, "15B42", 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", posixTime('2016-01-05T18:43:01Z')], [3000, 2, "15B42", 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", posixTime('2016-01-05T20:01:02Z')], [3000, 2, "15B42", 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", posixTime('2016-01-05T23:19:03Z')], [3000, 2, "15B42", 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", posixTime('2016-01-06T01:52:04Z')], [3001, 2, "15C50", 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", posixTime('2016-01-06T03:22:05Z')], [3001, 2, "15C50", 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", posixTime('2016-01-06T05:59:06Z')], [3001, 2, "15C50", 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']);
mocha.run();
</script>
</body>
</html>