| <!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> |