<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[213119] trunk/Websites/perf.webkit.org</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/213119">213119</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2017-02-27 20:48:36 -0800 (Mon, 27 Feb 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Split tests for InteractiveTimeSeriesChart into a separate test file.
https://bugs.webkit.org/show_bug.cgi?id=168960

Reviewed by Joseph Pecoraro.

Extracted the test cases for InteractiveTimeSeriesChart charts from time-series-chart-tests.js
into interactive-time-series-chart-tests.js now that the former file has gotten really big over time.

Also extracted a bunch of helper functions time-series-chart-tests.js as ChartTest in index.html.
Any test which instantiates a time series chart can use this helper class.

* browser-tests/index.html:
(ChartTest.importChartScripts): Ditto.
(ChartTest.posixTime): Moved from time-series-chart-tests.js.
(ChartTest.sampleCluster): Ditto.
(ChartTest.createChartWithSampleCluster): Ditto.
(ChartTest.createInteractiveChartWithSampleCluster): Ditto.
(ChartTest.respondWithSampleCluster):
* browser-tests/interactive-time-series-chart-tests.js: Extracted from time-series-chart-tests.js.
* browser-tests/time-series-chart-tests.js:
(posixTime): Moved.
(dayInMilliseconds): Moved.
(sampleCluster): Moved.
(createChartWithSampleCluster): Moved.
(createInteractiveChartWithSampleCluster): Moved.
(respondWithSampleCluster): Moved.
* unit-tests/analysis-task-tests.js: Fixed a typo. s/adopter/adapter/.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgChangeLog">trunk/Websites/perf.webkit.org/ChangeLog</a></li>
<li><a href="#trunkWebsitesperfwebkitorgbrowsertestsindexhtml">trunk/Websites/perf.webkit.org/browser-tests/index.html</a></li>
<li><a href="#trunkWebsitesperfwebkitorgbrowserteststimeseriescharttestsjs">trunk/Websites/perf.webkit.org/browser-tests/time-series-chart-tests.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgunittestsanalysistasktestsjs">trunk/Websites/perf.webkit.org/unit-tests/analysis-task-tests.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgbrowsertestsinteractivetimeseriescharttestsjs">trunk/Websites/perf.webkit.org/browser-tests/interactive-time-series-chart-tests.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkWebsitesperfwebkitorgChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/ChangeLog (213118 => 213119)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/ChangeLog        2017-02-28 04:47:44 UTC (rev 213118)
+++ trunk/Websites/perf.webkit.org/ChangeLog        2017-02-28 04:48:36 UTC (rev 213119)
</span><span class="lines">@@ -1,5 +1,35 @@
</span><span class="cx"> 2017-02-27  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><ins>+        Split tests for InteractiveTimeSeriesChart into a separate test file.
+        https://bugs.webkit.org/show_bug.cgi?id=168960
+
+        Reviewed by Joseph Pecoraro.
+
+        Extracted the test cases for InteractiveTimeSeriesChart charts from time-series-chart-tests.js
+        into interactive-time-series-chart-tests.js now that the former file has gotten really big over time.
+
+        Also extracted a bunch of helper functions time-series-chart-tests.js as ChartTest in index.html.
+        Any test which instantiates a time series chart can use this helper class.
+
+        * browser-tests/index.html:
+        (ChartTest.importChartScripts): Ditto.
+        (ChartTest.posixTime): Moved from time-series-chart-tests.js.
+        (ChartTest.sampleCluster): Ditto.
+        (ChartTest.createChartWithSampleCluster): Ditto.
+        (ChartTest.createInteractiveChartWithSampleCluster): Ditto.
+        (ChartTest.respondWithSampleCluster):
+        * browser-tests/interactive-time-series-chart-tests.js: Extracted from time-series-chart-tests.js.
+        * browser-tests/time-series-chart-tests.js:
+        (posixTime): Moved.
+        (dayInMilliseconds): Moved.
+        (sampleCluster): Moved.
+        (createChartWithSampleCluster): Moved.
+        (createInteractiveChartWithSampleCluster): Moved.
+        (respondWithSampleCluster): Moved.
+        * unit-tests/analysis-task-tests.js: Fixed a typo. s/adopter/adapter/.
+
+2017-02-27  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
</ins><span class="cx">         Calling build() on a baseline point results in an exception
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=168959
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgbrowsertestsindexhtml"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/browser-tests/index.html (213118 => 213119)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/browser-tests/index.html        2017-02-28 04:47:44 UTC (rev 213118)
+++ trunk/Websites/perf.webkit.org/browser-tests/index.html        2017-02-28 04:48:36 UTC (rev 213119)
</span><span class="lines">@@ -18,6 +18,7 @@
</span><span class="cx"> &lt;script src=&quot;close-button-tests.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> &lt;script src=&quot;editable-text-tests.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> &lt;script src=&quot;time-series-chart-tests.js&quot;&gt;&lt;/script&gt;
</span><ins>+&lt;script src=&quot;interactive-time-series-chart-tests.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx"> &lt;script&gt;
</span><span class="cx"> 
</span><span class="cx"> afterEach(() =&gt; {
</span><span class="lines">@@ -184,8 +185,127 @@
</span><span class="cx">     expectCanvasesMismatch(canvas1, canvas2) { return canvasRefTest(canvas1, canvas2, false); },
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+const dayInMilliseconds = 24 * 3600 * 1000;
+
+function posixTime(string) { return +new Date(string); }
+
+const ChartTest = {
+    importChartScripts(context)
+    {
+        return context.importScripts([
+            '../shared/statistics.js',
+            'instrumentation.js',
+            'models/data-model.js',
+            'models/metric.js',
+            'models/time-series.js',
+            'models/measurement-set.js',
+            'models/measurement-cluster.js',
+            'models/measurement-adaptor.js',
+            'components/base.js',
+            'components/time-series-chart.js',
+            'components/interactive-time-series-chart.js'],
+            'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'Metric', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
+                return context.symbols.TimeSeriesChart;
+            })
+    },
+
+    posixTime: posixTime,
+
+    sampleCluster: {
+        &quot;clusterStart&quot;: posixTime('2016-01-01T00:00:00Z'),
+        &quot;clusterSize&quot;: 7 * dayInMilliseconds,
+        &quot;startTime&quot;: posixTime('2016-01-01T00:00:00Z'),
+        &quot;endTime&quot;: posixTime('2016-01-08T00:00:00Z'),
+        &quot;lastModified&quot;: posixTime('2016-01-18T00:00:00Z'),
+        &quot;clusterCount&quot;: 1,
+        &quot;status&quot;: &quot;OK&quot;,
+        &quot;formatMap&quot;: [
+            &quot;id&quot;, &quot;mean&quot;, &quot;iterationCount&quot;, &quot;sum&quot;, &quot;squareSum&quot;, &quot;markedOutlier&quot;,
+            &quot;revisions&quot;,
+            &quot;commitTime&quot;, &quot;build&quot;, &quot;buildTime&quot;, &quot;buildNumber&quot;, &quot;builder&quot;
+        ],
+        &quot;configurations&quot;: {
+            &quot;current&quot;: [
+                [
+                    1000, 100, 1, 100, 100 * 100, false,
+                    [ [ 2000, 1, &quot;4000&quot;, posixTime('2016-01-05T17:35:00Z')] ],
+                    posixTime('2016-01-05T17:35:00Z'), 5000, posixTime('2016-01-05T19:23:00Z'), &quot;10&quot;, 7
+                ],
+                [
+                    1001, 131, 1, 131, 131 * 131, true,
+                    [ [ 2001, 1, &quot;4001&quot;, posixTime('2016-01-05T18:43:01Z')] ],
+                    posixTime('2016-01-05T18:43:01Z'), 5001, posixTime('2016-01-05T20:58:01Z'), &quot;11&quot;, 7
+                ],
+                [
+                    1002, 122, 1, 122, 122 * 122, false,
+                    [ [ 2002, 1, &quot;4002&quot;, posixTime('2016-01-05T20:01:02Z') ] ],
+                    posixTime('2016-01-05T20:01:02Z'), 5002, posixTime('2016-01-05T22:37:02Z'), &quot;12&quot;, 7
+                ],
+                [
+                    1003, 113, 1, 113, 113 * 113, false,
+                    [ [ 2003, 1, &quot;4003&quot;, posixTime('2016-01-05T23:19:03Z') ] ],
+                    posixTime('2016-01-05T23:19:03Z'), 5003, posixTime('2016-01-06T23:19:03Z'), &quot;13&quot;, 7
+                ],
+                [
+                    1004, 124, 1, 124, 124 * 124, false,
+                    [ [ 2004, 1, &quot;4004&quot;, posixTime('2016-01-06T01:52:04Z') ] ],
+                    posixTime('2016-01-06T01:52:04Z'), 5004, posixTime('2016-01-06T02:42:04Z'), &quot;14&quot;, 7
+                ],
+                [
+                    1005, 115, 1, 115, 115 * 115, true,
+                    [ [ 2005, 1, &quot;4005&quot;, posixTime('2016-01-06T03:22:05Z') ] ],
+                    posixTime('2016-01-06T03:22:05Z'), 5005, posixTime('2016-01-06T06:01:05Z'), &quot;15&quot;, 7
+                ],
+                [
+                    1006, 116, 1, 116, 116 * 116, false,
+                    [ [ 2006, 1, &quot;4006&quot;, posixTime('2016-01-06T05:59:06Z') ] ],
+                    posixTime('2016-01-06T05:59:06Z'), 5006, posixTime('2016-01-06T08:34:06Z'), &quot;16&quot;, 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)
+    {
+        expect(request.url).to.be('../data/measurement-set-1-1.json');
+        expect(request.method).to.be('GET');
+        request.resolve(this.sampleCluster);
+    },
+};
+
</ins><span class="cx"> mocha.checkLeaks();
</span><del>-mocha.globals(['expect', 'BrowsingContext', 'CanvasTest', 'wait', 'waitForComponentsToRender']);
</del><ins>+mocha.globals(['expect', 'BrowsingContext', 'CanvasTest', 'ChartTest', 'wait', 'waitForComponentsToRender']);
</ins><span class="cx"> mocha.run();
</span><span class="cx"> 
</span><span class="cx"> &lt;/script&gt;
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgbrowsertestsinteractivetimeseriescharttestsjs"></a>
<div class="addfile"><h4>Added: trunk/Websites/perf.webkit.org/browser-tests/interactive-time-series-chart-tests.js (0 => 213119)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/browser-tests/interactive-time-series-chart-tests.js                                (rev 0)
+++ trunk/Websites/perf.webkit.org/browser-tests/interactive-time-series-chart-tests.js        2017-02-28 04:48:36 UTC (rev 213119)
</span><span class="lines">@@ -0,0 +1,844 @@
</span><ins>+
+describe('InteractiveTimeSeriesChart', () =&gt; {
+
+    it('should change the unlocked indicator to the point closest to the last mouse move position', () =&gt; {
+        const context = new BrowsingContext();
+        return ChartTest.importChartScripts(context).then(() =&gt; {
+            const chart = ChartTest.createInteractiveChartWithSampleCluster(context);
+
+            chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+            chart.fetchMeasurementSets();
+            ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+            const indicatorChangeCalls = [];
+            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
+
+            let selectionChangeCount = 0;
+            chart.listenToAction('selectionChange', () =&gt; selectionChangeCount++);
+
+            let canvas;
+            return waitForComponentsToRender(context).then(() =&gt; {
+                expect(chart.currentSelection()).to.be(null);
+                expect(chart.currentIndicator()).to.be(null);
+                expect(indicatorChangeCalls).to.be.eql([]);
+
+                canvas = chart.content().querySelector('canvas');
+                const rect = canvas.getBoundingClientRect();
+                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 1, clientY: rect.top + rect.height / 2, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                expect(chart.currentSelection()).to.be(null);
+                const indicator = chart.currentIndicator();
+                expect(indicator).to.not.be(null);
+                const currentView = chart.sampledTimeSeriesData('current');
+                const lastPoint = currentView.lastPoint();
+                expect(indicator.view).to.be(currentView);
+                expect(indicator.point).to.be(lastPoint);
+                expect(indicator.isLocked).to.be(false);
+                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false]]);
+
+                expect(selectionChangeCount).to.be(0);
+            });
+        });
+    });
+
+    it('should lock the indicator to the point closest to the clicked position', () =&gt; {
+        const context = new BrowsingContext();
+        return ChartTest.importChartScripts(context).then(() =&gt; {
+            const chart = ChartTest.createInteractiveChartWithSampleCluster(context);
+
+            chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+            chart.fetchMeasurementSets();
+            ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+            const indicatorChangeCalls = [];
+            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
+
+            let selectionChangeCount = 0;
+            chart.listenToAction('selectionChange', () =&gt; selectionChangeCount++);
+
+            let canvas;
+            return waitForComponentsToRender(context).then(() =&gt; {
+                expect(chart.currentSelection()).to.be(null);
+                expect(chart.currentIndicator()).to.be(null);
+                expect(indicatorChangeCalls).to.be.eql([]);
+                canvas = chart.content().querySelector('canvas');
+                const rect = canvas.getBoundingClientRect();
+
+                const x = rect.right - 1;
+                const y = rect.top + rect.height / 2;
+                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: x - 0.5, clientY: y + 0.5, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: x - 0.5, clientY: y + 0.5, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: x - 0.5, clientY: y + 0.5, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                const currentView = chart.sampledTimeSeriesData('current');
+                const lastPoint = currentView.lastPoint();
+                expect(chart.currentSelection()).to.be(null);
+                const indicator = chart.currentIndicator();
+                expect(indicator.view).to.be(currentView);
+                expect(indicator.point).to.be(lastPoint);
+                expect(indicator.isLocked).to.be(true);
+                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false], [lastPoint.id, true]]);
+
+                expect(selectionChangeCount).to.be(0);
+            });
+        });
+    });
+
+    it('should clear the unlocked indicator when the mouse cursor exits the chart', () =&gt; {
+        const context = new BrowsingContext();
+        return ChartTest.importChartScripts(context).then(() =&gt; {
+            const chart = ChartTest.createInteractiveChartWithSampleCluster(context);
+
+            chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+            chart.fetchMeasurementSets();
+            ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+            const indicatorChangeCalls = [];
+            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
+
+            let selectionChangeCount = 0;
+            chart.listenToAction('selectionChange', () =&gt; selectionChangeCount++);
+
+            let canvas;
+            let rect;
+            let lastPoint;
+            return waitForComponentsToRender(context).then(() =&gt; {
+                expect(chart.currentSelection()).to.be(null);
+                expect(chart.currentIndicator()).to.be(null);
+                expect(indicatorChangeCalls).to.be.eql([]);
+
+                canvas = chart.content().querySelector('canvas');
+                rect = canvas.getBoundingClientRect();
+                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 1, clientY: rect.top + rect.height / 2, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                const currentView = chart.sampledTimeSeriesData('current');
+                lastPoint = currentView.lastPoint();
+                expect(chart.currentSelection()).to.be(null);
+                const indicator = chart.currentIndicator();
+                expect(indicator.view).to.be(currentView);
+                expect(indicator.point).to.be(lastPoint);
+                expect(indicator.isLocked).to.be(false);
+                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false]]);
+
+                canvas.parentNode.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('mouseleave', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                expect(chart.currentSelection()).to.be(null);
+                expect(chart.currentIndicator()).to.be(null);
+                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false], [null, false]]);
+
+                expect(selectionChangeCount).to.be(0);
+            });
+        });
+    });
+
+    it('should not clear the locked indicator when the mouse cursor exits the chart', () =&gt; {
+        const context = new BrowsingContext();
+        return ChartTest.importChartScripts(context).then(() =&gt; {
+            const chart = ChartTest.createInteractiveChartWithSampleCluster(context);
+
+            chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+            chart.fetchMeasurementSets();
+            ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+            const indicatorChangeCalls = [];
+            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
+
+            let selectionChangeCount = 0;
+            chart.listenToAction('selectionChange', () =&gt; selectionChangeCount++);
+
+            let canvas;
+            let rect;
+            let currentView;
+            let lastPoint;
+            return waitForComponentsToRender(context).then(() =&gt; {
+                expect(chart.currentSelection()).to.be(null);
+                expect(chart.currentIndicator()).to.be(null);
+                expect(indicatorChangeCalls).to.be.eql([]);
+
+                canvas = chart.content().querySelector('canvas');
+                rect = canvas.getBoundingClientRect();
+                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 1, clientY: rect.top + rect.height / 2, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                currentView = chart.sampledTimeSeriesData('current');
+                lastPoint = currentView.lastPoint();
+                expect(chart.currentSelection()).to.be(null);
+                const indicator = chart.currentIndicator();
+                expect(indicator.view).to.be(currentView);
+                expect(indicator.point).to.be(lastPoint);
+                expect(indicator.isLocked).to.be(true);
+                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true]]);
+
+                canvas.parentNode.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('mouseleave', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
+
+                expect(chart.currentSelection()).to.be(null);
+                const indicator = chart.currentIndicator();
+                expect(indicator.view).to.be(currentView);
+                expect(indicator.point).to.be(lastPoint);
+                expect(indicator.isLocked).to.be(true);
+                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true]]);
+
+                expect(selectionChangeCount).to.be(0);
+            })
+        });
+    });
+
+    it('should clear the locked indicator when clicked', () =&gt; {
+        const context = new BrowsingContext();
+        return ChartTest.importChartScripts(context).then(() =&gt; {
+            const chart = ChartTest.createInteractiveChartWithSampleCluster(context);
+
+            chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+            chart.fetchMeasurementSets();
+            ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+            const indicatorChangeCalls = [];
+            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
+
+            let selectionChangeCount = 0;
+            chart.listenToAction('selectionChange', () =&gt; selectionChangeCount++);
+
+            let canvas;
+            let rect;
+            let y;
+            let currentView;
+            let lastPoint;
+            return waitForComponentsToRender(context).then(() =&gt; {
+                expect(chart.currentSelection()).to.be(null);
+                expect(chart.currentIndicator()).to.be(null);
+                expect(indicatorChangeCalls).to.be.eql([]);
+
+                canvas = chart.content().querySelector('canvas');
+                rect = canvas.getBoundingClientRect();
+                y = rect.top + rect.height / 2;
+                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 1, clientY: y, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                currentView = chart.sampledTimeSeriesData('current');
+                lastPoint = currentView.lastPoint();
+                expect(chart.currentSelection()).to.be(null);
+                const indicator = chart.currentIndicator();
+                expect(indicator.view).to.be(currentView);
+                expect(indicator.point).to.be(lastPoint);
+                expect(indicator.isLocked).to.be(true);
+                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true]]);
+
+                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.left + 1, clientY: y, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                expect(chart.currentSelection()).to.be(null);
+                const firstPoint = currentView.firstPoint();
+                const indicator = chart.currentIndicator();
+                expect(indicator.view).to.be(currentView);
+                expect(indicator.point).to.be(firstPoint);
+                expect(indicator.isLocked).to.be(false);
+                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true], [firstPoint.id, false]]);
+
+                expect(selectionChangeCount).to.be(0);
+            })
+        });
+    });
+
+    it('should change the selection when the mouse cursor is dragged', () =&gt; {
+        const context = new BrowsingContext();
+        return ChartTest.importChartScripts(context).then(() =&gt; {
+            const chart = ChartTest.createInteractiveChartWithSampleCluster(context, null, {selection: {lineStyle: '#f93', lineWidth: 2, fillStyle: '#ccc'}});
+
+            chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+            chart.fetchMeasurementSets();
+            ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+            const indicatorChangeCalls = [];
+            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
+
+            const selectionChangeCalls = [];
+            chart.listenToAction('selectionChange', (...args) =&gt; selectionChangeCalls.push(args));
+
+            const zoomButton = chart.content('zoom-button');
+
+            let canvas;
+            let rect;
+            let y;
+            let currentView;
+            let firstPoint;
+            let oldRange;
+            let newRange;
+            return waitForComponentsToRender(context).then(() =&gt; {
+                expect(chart.currentSelection()).to.be(null);
+                expect(chart.currentIndicator()).to.be(null);
+                expect(selectionChangeCalls).to.be.eql([]);
+
+                canvas = chart.content().querySelector('canvas');
+                rect = canvas.getBoundingClientRect();
+                y = rect.top + rect.height / 2;
+                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                currentView = chart.sampledTimeSeriesData('current');
+                firstPoint = currentView.firstPoint();
+                expect(chart.currentSelection()).to.be(null);
+                let indicator = chart.currentIndicator();
+                expect(indicator.view).to.be(currentView);
+                expect(indicator.point).to.be(firstPoint);
+                expect(indicator.isLocked).to.be(false);
+                expect(indicatorChangeCalls).to.be.eql([[firstPoint.id, false]]);
+                expect(zoomButton.offsetHeight).to.be(0);
+
+                canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
+
+                expect(chart.currentSelection()).to.be(null);
+                let indicator = chart.currentIndicator();
+                expect(indicator.view).to.be(currentView);
+                expect(indicator.point).to.be(firstPoint);
+                expect(indicator.isLocked).to.be(false);
+                expect(selectionChangeCalls).to.be.eql([]);
+                expect(indicatorChangeCalls).to.be.eql([[firstPoint.id, false]]);
+                expect(zoomButton.offsetHeight).to.be(0);
+
+                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 15, clientY: y + 5, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                expect(chart.currentSelection()).to.not.be(null);
+                expect(chart.currentIndicator()).to.be(null);
+                expect(selectionChangeCalls.length).to.be(1);
+                oldRange = selectionChangeCalls[0][0];
+                expect(oldRange).to.be.eql(chart.currentSelection());
+                expect(selectionChangeCalls[0][1]).to.be(false);
+                expect(indicatorChangeCalls).to.be.eql([[firstPoint.id, false], [null, false]]);
+                expect(zoomButton.offsetHeight).to.be(0);
+
+                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 5, clientY: y + 5, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                expect(chart.currentSelection()).to.not.be(null);
+                expect(chart.currentIndicator()).to.be(null);
+                expect(selectionChangeCalls.length).to.be(2);
+                newRange = selectionChangeCalls[1][0];
+                expect(newRange).to.be.eql(chart.currentSelection());
+                expect(newRange[0]).to.be(oldRange[0]);
+                expect(newRange[1]).to.be.greaterThan(oldRange[1]);
+                expect(selectionChangeCalls[1][1]).to.be(false);
+                expect(zoomButton.offsetHeight).to.be(0);
+
+                canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: rect.right - 5, clientY: y + 5, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 5, clientY: y + 5, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                expect(chart.currentSelection()).to.be.eql(newRange);
+                expect(chart.currentIndicator()).to.be(null);
+                expect(selectionChangeCalls.length).to.be(3);
+                expect(selectionChangeCalls[2][0]).to.be.eql(newRange);
+                expect(selectionChangeCalls[2][1]).to.be(true);
+                expect(zoomButton.offsetHeight).to.be(0);
+            });
+        });
+    });
+
+    it('should dispatch the &quot;zoom&quot; action when the zoom button is clicked', () =&gt; {
+        const context = new BrowsingContext();
+        return ChartTest.importChartScripts(context).then(() =&gt; {
+            const chart = ChartTest.createInteractiveChartWithSampleCluster(context, null, {selection: {lineStyle: '#f93', lineWidth: 2, fillStyle: '#ccc'}, zoomButton: true});
+
+            chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+            chart.fetchMeasurementSets();
+            ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+            const zoomCalls = [];
+            chart.listenToAction('zoom', (...args) =&gt; zoomCalls.push(args));
+            const zoomButton = chart.content('zoom-button');
+
+            let selection;
+            let canvas;
+            return waitForComponentsToRender(context).then(() =&gt; {
+                expect(zoomButton.offsetHeight).to.be(0);
+                canvas = chart.content().querySelector('canvas');
+                const rect = canvas.getBoundingClientRect();
+                const y = rect.top + rect.height / 2;
+                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                selection = chart.currentSelection();
+                expect(selection).to.not.be(null);
+                expect(chart.currentIndicator()).to.be(null);
+                expect(zoomButton.offsetHeight).to.not.be(0);
+                expect(zoomCalls).to.be.eql([]);
+                zoomButton.click();
+            }).then(() =&gt; {
+                expect(zoomCalls).to.be.eql([[selection]]);
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
+            });
+        });
+    });
+
+    it('should clear the selection when clicked', () =&gt; {
+        const context = new BrowsingContext();
+        return ChartTest.importChartScripts(context).then(() =&gt; {
+            const chart = ChartTest.createInteractiveChartWithSampleCluster(context, null, {selection: {lineStyle: '#f93', lineWidth: 2, fillStyle: '#ccc'}});
+
+            chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+            chart.fetchMeasurementSets();
+            ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+            let canvas;
+            let rect;
+            let y;
+            return waitForComponentsToRender(context).then(() =&gt; {
+                canvas = chart.content().querySelector('canvas');
+                rect = canvas.getBoundingClientRect();
+                y = rect.top + rect.height / 2;
+                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
+                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                expect(chart.currentSelection()).to.not.be(null);
+                expect(chart.currentIndicator()).to.be(null);
+
+                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.left + 1, clientY: y + 5, composed: true, bubbles: true}));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                expect(chart.currentSelection()).to.be(null);
+                const currentView = chart.sampledTimeSeriesData('current');
+                const indicator = chart.currentIndicator();
+                expect(indicator.view).to.be(currentView);
+                expect(indicator.point).to.be(currentView.firstPoint());
+                expect(indicator.isLocked).to.be(false);
+            });
+        });
+    });
+
+    it('should dispatch &quot;annotationClick&quot; action when an annotation is clicked', () =&gt; {
+        const context = new BrowsingContext();
+        return ChartTest.importChartScripts(context).then(() =&gt; {
+            const chart = ChartTest.createInteractiveChartWithSampleCluster(context, null,
+                {annotations: { textStyle: '#000', textBackground: '#fff', minWidth: 3, barHeight: 10, barSpacing: 1}});
+
+            chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+            chart.fetchMeasurementSets();
+            ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+            const diff = ChartTest.sampleCluster.endTime - ChartTest.sampleCluster.startTime;
+            const annotations = [{
+                startTime: ChartTest.sampleCluster.startTime + diff / 2,
+                endTime: ChartTest.sampleCluster.endTime - diff / 4,
+                label: 'hello, world',
+                fillStyle: 'rgb(0, 0, 255)',
+            }]
+            chart.setAnnotations(annotations);
+
+            const annotationClickCalls = [];
+            chart.listenToAction('annotationClick', (...args) =&gt; annotationClickCalls.push(args));
+
+            let canvas;
+            let init;
+            return waitForComponentsToRender(context).then(() =&gt; {
+                expect(annotationClickCalls).to.be.eql([]);
+                expect(chart.content('annotation-label').textContent).to.not.contain('hello, world');
+
+                canvas = chart.content().querySelector('canvas');
+                const rect = canvas.getBoundingClientRect();
+                init = {target: canvas, clientX: rect.right - rect.width / 4, clientY: rect.bottom - 5, composed: true, bubbles: true};
+                canvas.dispatchEvent(new MouseEvent('mousemove', init));
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                expect(chart.content('annotation-label').textContent).to.contain('hello, world');
+                expect(annotationClickCalls).to.be.eql([]);
+                canvas.dispatchEvent(new MouseEvent('mousedown', init));
+                canvas.dispatchEvent(new MouseEvent('mouseup', init));
+                canvas.dispatchEvent(new MouseEvent('click', init));
+
+                expect(annotationClickCalls).to.be.eql([[annotations[0]]]);
+
+                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
+            });
+        });
+    });
+
+    describe('render', () =&gt; {
+        it('should render the unlocked indicator when options.indicator is specified', () =&gt; {
+            const context = new BrowsingContext();
+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chartWithoutIndicator = ChartTest.createInteractiveChartWithSampleCluster(context);
+                const chartWithIndicator = ChartTest.createInteractiveChartWithSampleCluster(context, null,
+                    {indicator: {lineStyle: 'rgb(51, 204, 255)', lineWidth: 2, pointRadius: 2}, interactiveChart: true});
+                const indicatorColor = {r: 51, g: 204, b: 255};
+
+                chartWithoutIndicator.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+                chartWithoutIndicator.fetchMeasurementSets();
+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+                chartWithIndicator.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+                chartWithIndicator.fetchMeasurementSets();
+
+                let canvasWithoutIndicator;
+                let canvasWithIndicator;
+                return waitForComponentsToRender(context).then(() =&gt; {
+                    canvasWithoutIndicator = chartWithoutIndicator.content().querySelector('canvas');
+                    canvasWithIndicator = chartWithIndicator.content().querySelector('canvas');
+
+                    const rect = canvasWithIndicator.getBoundingClientRect();
+                    const x = rect.right - 1;
+                    const y = rect.top + rect.height / 2;
+                    canvasWithIndicator.dispatchEvent(new MouseEvent('mousemove', {target: canvasWithIndicator, clientX: x, clientY: y, composed: true, bubbles: true}));
+
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithIndicator);
+                    return waitForComponentsToRender(context);
+                }).then(() =&gt; {
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithIndicator)).to.be(true);
+
+                    const indicator = chartWithIndicator.currentIndicator();
+                    const currentView = chartWithIndicator.sampledTimeSeriesData('current');
+                    expect(indicator.view).to.be(currentView);
+                    expect(indicator.point).to.be(currentView.lastPoint());
+                    expect(indicator.isLocked).to.be(false);
+
+                    CanvasTest.expectCanvasesMismatch(canvasWithoutIndicator, canvasWithIndicator);
+                    expect(CanvasTest.canvasContainsColor(canvasWithoutIndicator, indicatorColor)).to.be(false);
+                    expect(CanvasTest.canvasContainsColor(canvasWithIndicator, indicatorColor)).to.be(true);
+                });
+            });
+        });
+
+        it('should render the locked indicator differently from the unlocked indicator when options.lockedIndicator is specified', () =&gt; {
+            const context = new BrowsingContext();
+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chartOptions = {
+                    indicator: {lineStyle: 'rgb(51, 204, 255)', lineWidth: 2, pointRadius: 3},
+                    lockedIndicator: {lineStyle: 'rgb(51, 102, 204)', fillStyle: 'rgb(250, 250, 250)', lineWidth: 2, pointRadius: 3}
+                };
+                const unlockedColor = {r: 51, g: 204, b: 255};
+                const lockedColor = {r: 51, g: 102, b: 204};
+                const lockedFillColor = {r: 250, g: 250, b: 250};
+                const chartWithUnlockedIndicator = ChartTest.createInteractiveChartWithSampleCluster(context, null, chartOptions);
+                const chartWithLockedIndicator = ChartTest.createInteractiveChartWithSampleCluster(context, null, chartOptions);
+
+                chartWithUnlockedIndicator.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+                chartWithUnlockedIndicator.fetchMeasurementSets();
+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+                chartWithLockedIndicator.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+                chartWithLockedIndicator.fetchMeasurementSets();
+
+                let canvasWithUnlockedIndicator;
+                let canvasWithLockedIndicator;
+                return waitForComponentsToRender(context).then(() =&gt; {
+                    canvasWithUnlockedIndicator = chartWithUnlockedIndicator.content().querySelector('canvas');
+                    canvasWithLockedIndicator = chartWithLockedIndicator.content().querySelector('canvas');
+
+                    const rect = canvasWithUnlockedIndicator.getBoundingClientRect();
+                    const x = rect.right - 1;
+                    const y = rect.top + rect.height / 2;
+                    canvasWithUnlockedIndicator.dispatchEvent(new MouseEvent('mousemove', {target: canvasWithUnlockedIndicator, clientX: x, clientY: y, composed: true, bubbles: true}));
+                    canvasWithLockedIndicator.dispatchEvent(new MouseEvent('click', {target: canvasWithLockedIndicator, clientX: x, clientY: y, composed: true, bubbles: true}));
+
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithUnlockedIndicator);
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithLockedIndicator);
+                    return waitForComponentsToRender(context);
+                }).then(() =&gt; {
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithUnlockedIndicator)).to.be(true);
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithLockedIndicator)).to.be(true);
+
+                    let indicator = chartWithUnlockedIndicator.currentIndicator();
+                    let currentView = chartWithUnlockedIndicator.sampledTimeSeriesData('current');
+                    expect(indicator.view).to.be(currentView);
+                    expect(indicator.point).to.be(currentView.lastPoint());
+                    expect(indicator.isLocked).to.be(false);
+
+                    indicator = chartWithLockedIndicator.currentIndicator();
+                    currentView = chartWithLockedIndicator.sampledTimeSeriesData('current');
+                    expect(indicator.view).to.be(currentView);
+                    expect(indicator.point).to.be(currentView.lastPoint());
+                    expect(indicator.isLocked).to.be(true);
+
+                    CanvasTest.expectCanvasesMismatch(canvasWithUnlockedIndicator, canvasWithLockedIndicator);
+                    expect(CanvasTest.canvasContainsColor(canvasWithUnlockedIndicator, unlockedColor)).to.be(true);
+                    expect(CanvasTest.canvasContainsColor(canvasWithUnlockedIndicator, lockedFillColor)).to.be(false);
+                    expect(CanvasTest.canvasContainsColor(canvasWithLockedIndicator, lockedColor)).to.be(true);
+                    expect(CanvasTest.canvasContainsColor(canvasWithLockedIndicator, lockedFillColor)).to.be(true);
+                });
+            });
+        });
+    });
+
+    describe('moveLockedIndicatorWithNotification', () =&gt; {
+        it('should move the locked indicator to the right when forward boolean is true', () =&gt; {
+            const context = new BrowsingContext();
+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createInteractiveChartWithSampleCluster(context);
+
+                chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+                chart.fetchMeasurementSets();
+                let indicatorChangeCount = 0;
+                chart.listenToAction('indicatorChange', () =&gt; indicatorChangeCount++);
+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+                let canvas;
+                return waitForComponentsToRender(context).then(() =&gt; {
+                    expect(indicatorChangeCount).to.be(0);
+
+                    canvas = chart.content().querySelector('canvas');
+
+                    const rect = canvas.getBoundingClientRect();
+                    const x = rect.left + 1;
+                    const y = rect.top + rect.height / 2;
+                    canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
+
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                    return waitForComponentsToRender(context);
+                }).then(() =&gt; {
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+                    expect(indicatorChangeCount).to.be(1);
+
+                    let indicator = chart.currentIndicator();
+                    let currentView = chart.sampledTimeSeriesData('current');
+                    expect(indicator.view).to.be(currentView);
+                    expect(indicator.point).to.be(currentView.firstPoint());
+                    expect(indicator.isLocked).to.be(true);
+
+                    chart.moveLockedIndicatorWithNotification(true);
+
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                    return waitForComponentsToRender(context);
+                }).then(() =&gt; {
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+                    expect(indicatorChangeCount).to.be(2);
+
+                    let indicator = chart.currentIndicator();
+                    let currentView = chart.sampledTimeSeriesData('current');
+                    expect(indicator.view).to.be(currentView);
+                    expect(indicator.point).to.not.be(currentView.firstPoint());
+                    expect(indicator.point).to.be(currentView.nextPoint(currentView.firstPoint()));
+                    expect(currentView.previousPoint(indicator.point)).to.be(currentView.firstPoint());
+                    expect(indicator.isLocked).to.be(true);
+                    expect(indicatorChangeCount).to.be(2);
+                });
+            });
+        });
+
+        it('should move the locked indicator to the left when forward boolean is false', () =&gt; {
+            const context = new BrowsingContext();
+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createInteractiveChartWithSampleCluster(context);
+
+                chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
+                chart.fetchMeasurementSets();
+                let indicatorChangeCount = 0;
+                chart.listenToAction('indicatorChange', () =&gt; indicatorChangeCount++);
+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+                let canvas;
+                return waitForComponentsToRender(context).then(() =&gt; {
+                    expect(indicatorChangeCount).to.be(0);
+
+                    canvas = chart.content().querySelector('canvas');
+
+                    const rect = canvas.getBoundingClientRect();
+                    const x = rect.right - 1;
+                    const y = rect.top + rect.height / 2;
+                    canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
+
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                    return waitForComponentsToRender(context);
+                }).then(() =&gt; {
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+                    expect(indicatorChangeCount).to.be(1);
+
+                    let indicator = chart.currentIndicator();
+                    let currentView = chart.sampledTimeSeriesData('current');
+                    expect(indicator.view).to.be(currentView);
+                    expect(indicator.point).to.be(currentView.lastPoint());
+                    expect(indicator.isLocked).to.be(true);
+
+                    chart.moveLockedIndicatorWithNotification(false);
+
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                    return waitForComponentsToRender(context);
+                }).then(() =&gt; {
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+                    expect(indicatorChangeCount).to.be(2);
+
+                    let indicator = chart.currentIndicator();
+                    let currentView = chart.sampledTimeSeriesData('current');
+                    expect(indicator.view).to.be(currentView);
+                    expect(indicator.point).to.not.be(currentView.firstPoint());
+                    expect(indicator.point).to.be(currentView.previousPoint(currentView.lastPoint()));
+                    expect(currentView.nextPoint(indicator.point)).to.be(currentView.lastPoint());
+                    expect(indicator.isLocked).to.be(true);
+                    expect(indicatorChangeCount).to.be(2);
+                });
+            });
+        });
+
+        it('should not move the locked indicator when there are no points within the domain', () =&gt; {
+            const context = new BrowsingContext();
+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createInteractiveChartWithSampleCluster(context);
+
+                // The domain inclues points 2, 3
+                chart.setDomain(posixTime('2016-01-05T20:00:00Z'), posixTime('2016-01-06T00:00:00Z'));
+                chart.fetchMeasurementSets();
+                let indicatorChangeCount = 0;
+                chart.listenToAction('indicatorChange', () =&gt; indicatorChangeCount++);
+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
+
+                let canvas;
+                let currentView;
+                return waitForComponentsToRender(context).then(() =&gt; {
+                    expect(indicatorChangeCount).to.be(0);
+
+                    canvas = chart.content().querySelector('canvas');
+
+                    const rect = canvas.getBoundingClientRect();
+                    const x = rect.right - 1;
+                    const y = rect.top + rect.height / 2;
+                    canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
+
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                    return waitForComponentsToRender(context);
+                }).then(() =&gt; {
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+                    expect(indicatorChangeCount).to.be(1);
+
+                    currentView = chart.sampledTimeSeriesData('current');
+                    expect(currentView.length()).to.be(4); // points 0 and 4 are added to draw lines extending beyond the domain.
+                    expect([...currentView].map((point) =&gt; point.id)).to.be.eql([1000, 1002, 1003, 1004]);
+
+                    const indicator = chart.currentIndicator();
+                    expect(indicator.view).to.be(currentView);
+                    expect(indicator.point.id).to.be(1003);
+                    expect(indicator.isLocked).to.be(true);
+
+                    chart.moveLockedIndicatorWithNotification(true);
+
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                    return waitForComponentsToRender(context);
+                }).then(() =&gt; {
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
+
+                    expect(indicatorChangeCount).to.be(1);
+                    const indicator = chart.currentIndicator();
+                    expect(indicator.view).to.be(currentView);
+                    expect(indicator.point.id).to.be(1003);
+                    expect(indicator.isLocked).to.be(true);
+
+                    chart.moveLockedIndicatorWithNotification(false);
+
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                    return waitForComponentsToRender(context);
+                }).then(() =&gt; {
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
+
+                    expect(indicatorChangeCount).to.be(2);
+                    const indicator = chart.currentIndicator();
+                    expect(indicator.view).to.be(currentView);
+                    expect(indicator.point.id).to.be(1002);
+                    expect(indicator.isLocked).to.be(true);
+
+                    chart.moveLockedIndicatorWithNotification(false);
+
+                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
+                    return waitForComponentsToRender(context);
+                }).then(() =&gt; {
+                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
+
+                    expect(indicatorChangeCount).to.be(2);
+                    const indicator = chart.currentIndicator();
+                    expect(indicator.view).to.be(currentView);
+                    expect(indicator.point.id).to.be(1002);
+                    expect(indicator.isLocked).to.be(true);
+                });
+            });
+        });
+
+    });
+});
</ins></span></pre></div>
<a id="trunkWebsitesperfwebkitorgbrowserteststimeseriescharttestsjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/browser-tests/time-series-chart-tests.js (213118 => 213119)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/browser-tests/time-series-chart-tests.js        2017-02-28 04:47:44 UTC (rev 213118)
+++ trunk/Websites/perf.webkit.org/browser-tests/time-series-chart-tests.js        2017-02-28 04:48:36 UTC (rev 213119)
</span><span class="lines">@@ -1,4 +1,3 @@
</span><del>-(() =&gt; {
</del><span class="cx"> 
</span><span class="cx"> const scripts = [
</span><span class="cx">     '../shared/statistics.js',
</span><span class="lines">@@ -13,106 +12,10 @@
</span><span class="cx">     'components/time-series-chart.js',
</span><span class="cx">     'components/interactive-time-series-chart.js'];
</span><span class="cx"> 
</span><del>-function posixTime(string) { return +new Date(string); }
-
-const dayInMilliseconds = 24 * 3600 * 1000;
-
-const sampleCluster = {
-    &quot;clusterStart&quot;: posixTime('2016-01-01T00:00:00Z'),
-    &quot;clusterSize&quot;: 7 * dayInMilliseconds,
-    &quot;startTime&quot;: posixTime('2016-01-01T00:00:00Z'),
-    &quot;endTime&quot;: posixTime('2016-01-08T00:00:00Z'),
-    &quot;lastModified&quot;: posixTime('2016-01-18T00:00:00Z'),
-    &quot;clusterCount&quot;: 1,
-    &quot;status&quot;: &quot;OK&quot;,
-    &quot;formatMap&quot;: [
-        &quot;id&quot;, &quot;mean&quot;, &quot;iterationCount&quot;, &quot;sum&quot;, &quot;squareSum&quot;, &quot;markedOutlier&quot;,
-        &quot;revisions&quot;,
-        &quot;commitTime&quot;, &quot;build&quot;, &quot;buildTime&quot;, &quot;buildNumber&quot;, &quot;builder&quot;
-    ],
-    &quot;configurations&quot;: {
-        &quot;current&quot;: [
-            [
-                1000, 100, 1, 100, 100 * 100, false,
-                [ [ 2000, 1, &quot;4000&quot;, posixTime('2016-01-05T17:35:00Z')] ],
-                posixTime('2016-01-05T17:35:00Z'), 5000, posixTime('2016-01-05T19:23:00Z'), &quot;10&quot;, 7
-            ],
-            [
-                1001, 131, 1, 131, 131 * 131, true,
-                [ [ 2001, 1, &quot;4001&quot;, posixTime('2016-01-05T18:43:01Z')] ],
-                posixTime('2016-01-05T18:43:01Z'), 5001, posixTime('2016-01-05T20:58:01Z'), &quot;11&quot;, 7
-            ],
-            [
-                1002, 122, 1, 122, 122 * 122, false,
-                [ [ 2002, 1, &quot;4002&quot;, posixTime('2016-01-05T20:01:02Z') ] ],
-                posixTime('2016-01-05T20:01:02Z'), 5002, posixTime('2016-01-05T22:37:02Z'), &quot;12&quot;, 7
-            ],
-            [
-                1003, 113, 1, 113, 113 * 113, false,
-                [ [ 2003, 1, &quot;4003&quot;, posixTime('2016-01-05T23:19:03Z') ] ],
-                posixTime('2016-01-05T23:19:03Z'), 5003, posixTime('2016-01-06T23:19:03Z'), &quot;13&quot;, 7
-            ],
-            [
-                1004, 124, 1, 124, 124 * 124, false,
-                [ [ 2004, 1, &quot;4004&quot;, posixTime('2016-01-06T01:52:04Z') ] ],
-                posixTime('2016-01-06T01:52:04Z'), 5004, posixTime('2016-01-06T02:42:04Z'), &quot;14&quot;, 7
-            ],
-            [
-                1005, 115, 1, 115, 115 * 115, true,
-                [ [ 2005, 1, &quot;4005&quot;, posixTime('2016-01-06T03:22:05Z') ] ],
-                posixTime('2016-01-06T03:22:05Z'), 5005, posixTime('2016-01-06T06:01:05Z'), &quot;15&quot;, 7
-            ],
-            [
-                1006, 116, 1, 116, 116 * 116, false,
-                [ [ 2006, 1, &quot;4006&quot;, posixTime('2016-01-06T05:59:06Z') ] ],
-                posixTime('2016-01-06T05:59:06Z'), 5006, posixTime('2016-01-06T08:34:06Z'), &quot;16&quot;, 7
-            ]
-        ]
-    },
-};
-
-function 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;
-}
-
-function createInteractiveChartWithSampleCluster(context, sourceList = null, chartOptions = {})
-{
-    if (sourceList == null)
-        sourceList = [{type: 'current', interactive: true}];
-    return createChartWithSampleCluster(context, sourceList, chartOptions, 'InteractiveTimeSeriesChart');
-}
-
-function respondWithSampleCluster(request)
-{
-    expect(request.url).to.be('../data/measurement-set-1-1.json');
-    expect(request.method).to.be('GET');
-    request.resolve(sampleCluster);
-}
-
</del><span class="cx"> describe('TimeSeriesChart', () =&gt; {
</span><span class="cx"> 
</span><span class="cx">     it('should be constructible with an empty sourec list and an empty options', () =&gt; {
</span><del>-        return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+        return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">             new TimeSeriesChart([], {});
</span><span class="cx">         });
</span><span class="cx">     });
</span><span class="lines">@@ -119,7 +22,7 @@
</span><span class="cx"> 
</span><span class="cx">     describe('computeTimeGrid', () =&gt; {
</span><span class="cx">         it('should return an empty array when the start and the end times are identical', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const someTime = Date.now();
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(someTime, someTime, 0);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -131,7 +34,7 @@
</span><span class="cx">         const millisecondsPerDay = 24 * millisecondsPerHour;
</span><span class="cx"> 
</span><span class="cx">         it('should return an empty array when maxLabels is 0', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = Date.now();
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, endTime, 0);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -140,7 +43,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should return an empty array when maxLabels is 0 even when the interval spans multiple months', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = Date.now();
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(endTime - 120 * millisecondsPerDay, endTime, 0);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -156,7 +59,7 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         it('should generate one hour label with just day for two hour interval when maxLabels is 1', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T07:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(endTime - 2 * millisecondsPerHour, +endTime, 1);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -166,7 +69,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate two two-hour labels for four hour interval when maxLabels is 2', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T07:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(endTime - 4 * millisecondsPerHour, +endTime, 2);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -177,7 +80,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate six two-hour labels for twelve hour interval when maxLabels is 6', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T07:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 12 * millisecondsPerHour, 6);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -192,7 +95,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate six two-hour labels with one date label for twelve hour interval that cross a day when maxLabels is 6', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T16:12:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 12 * millisecondsPerHour, 6);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -207,7 +110,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate three two-hour labels for six hour interval that cross a year when maxLabels is 5', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2016-12-31T21:37:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 6 * millisecondsPerHour, 5);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -219,7 +122,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate one one-day label for one day interval when maxLabels is 1', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T07:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, +endTime, 1);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -229,7 +132,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate two one-day labels for one day interval when maxLabels is 2', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T07:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(endTime - millisecondsPerDay, +endTime, 2);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -240,7 +143,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate four half-day labels for two day interval when maxLabels is 5', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T16:12:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 2 * millisecondsPerDay, 5);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -253,7 +156,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate four half-day labels for two day interval that cross a year when maxLabels is 5', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2016-12-31T09:12:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 2 * millisecondsPerDay, 5);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -266,7 +169,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate seven per-day labels for one week interval when maxLabels is 10', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T07:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(endTime - 7 * millisecondsPerDay, endTime, 10);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -282,7 +185,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate three two-day labels for one week interval when maxLabels is 4', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T07:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(endTime - 7 * millisecondsPerDay, endTime, 4);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -294,7 +197,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate seven one-day labels for two week interval when maxLabels is 8', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T18:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 14 * millisecondsPerDay, 8);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -310,7 +213,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate two one-week labels for two week interval when maxLabels is 3', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T18:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(+endTime, +endTime + 14 * millisecondsPerDay, 3);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -321,7 +224,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate seven one-month labels for six and half months interval starting before 15th when maxLabels is 7', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T18:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-12T18:53:00Z'), new Date('2017-01-18T08:17:53Z'), 7);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -337,7 +240,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate seven one-month labels for six months interval staring after 15th when maxLabels is 7', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T18:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-18T18:53:00Z'), new Date('2017-01-18T08:17:53Z'), 7);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -351,7 +254,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate six two-months labels for one year interval when maxLabels is 7', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T18:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-11T18:53:00Z'), new Date('2017-07-27T08:17:53Z'), 7);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -366,7 +269,7 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should generate four three-months labels for one year interval when maxLabels is 5', () =&gt; {
</span><del>-            return new BrowsingContext().importScripts(scripts, 'TimeSeriesChart').then((TimeSeriesChart) =&gt; {
</del><ins>+            return ChartTest.importChartScripts(new BrowsingContext).then((TimeSeriesChart) =&gt; {
</ins><span class="cx">                 const endTime = new Date('2017-01-15T18:53:00Z');
</span><span class="cx">                 const labels = TimeSeriesChart.computeTimeGrid(new Date('2016-07-11T18:53:00Z'), new Date('2017-07-27T08:17:53Z'), 5);
</span><span class="cx">                 expect(labels).to.be.a('array');
</span><span class="lines">@@ -542,15 +445,15 @@
</span><span class="cx"> 
</span><span class="cx">         it('should fetch the measurement set and create a canvas element upon receiving the data', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createChartWithSampleCluster(context);
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createChartWithSampleCluster(context);
</ins><span class="cx"> 
</span><del>-                chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chart.fetchMeasurementSets();
</span><span class="cx"> 
</span><span class="cx">                 const requests = context.symbols.MockRemoteAPI.requests;
</span><span class="cx">                 expect(requests.length).to.be(1);
</span><del>-                respondWithSampleCluster(requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(requests[0]);
</ins><span class="cx"> 
</span><span class="cx">                 expect(chart.content().querySelector('canvas')).to.be(null);
</span><span class="cx">                 return waitForComponentsToRender(context).then(() =&gt; {
</span><span class="lines">@@ -561,19 +464,19 @@
</span><span class="cx"> 
</span><span class="cx">         it('should immediately enqueue to render when the measurement set had already been fetched', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createChartWithSampleCluster(context);
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createChartWithSampleCluster(context);
</ins><span class="cx"> 
</span><span class="cx">                 let set = context.symbols.MeasurementSet.findSet(1, 1, 0);
</span><del>-                let promise = set.fetchBetween(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                let promise = set.fetchBetween(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx"> 
</span><span class="cx">                 const requests = context.symbols.MockRemoteAPI.requests;
</span><span class="cx">                 expect(requests.length).to.be(1);
</span><del>-                respondWithSampleCluster(requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(requests[0]);
</ins><span class="cx"> 
</span><span class="cx">                 return promise.then(() =&gt; {
</span><span class="cx">                     expect(chart.content().querySelector('canvas')).to.be(null);
</span><del>-                    chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                    chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                     chart.fetchMeasurementSets();
</span><span class="cx">                     return waitForComponentsToRender(context);
</span><span class="cx">                 }).then(() =&gt; {
</span><span class="lines">@@ -585,18 +488,18 @@
</span><span class="cx"> 
</span><span class="cx">         it('should dispatch &quot;dataChange&quot; action once the fetched data becomes available', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createChartWithSampleCluster(context);
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createChartWithSampleCluster(context);
</ins><span class="cx"> 
</span><span class="cx">                 let dataChangeCount = 0;
</span><span class="cx">                 chart.listenToAction('dataChange', () =&gt; dataChangeCount++);
</span><span class="cx"> 
</span><del>-                chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chart.fetchMeasurementSets();
</span><span class="cx"> 
</span><span class="cx">                 const requests = context.symbols.MockRemoteAPI.requests;
</span><span class="cx">                 expect(requests.length).to.be(1);
</span><del>-                respondWithSampleCluster(requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(requests[0]);
</ins><span class="cx"> 
</span><span class="cx">                 expect(dataChangeCount).to.be(0);
</span><span class="cx">                 expect(chart.sampledTimeSeriesData('current')).to.be(null);
</span><span class="lines">@@ -613,12 +516,12 @@
</span><span class="cx">     describe('sampledTimeSeriesData', () =&gt; {
</span><span class="cx">         it('should not contain an outlier when includeOutliers is false', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createChartWithSampleCluster(context, [{includeOutliers: false}])
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: false}])
</ins><span class="cx"> 
</span><del>-                chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chart.fetchMeasurementSets();
</span><del>-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</ins><span class="cx"> 
</span><span class="cx">                 return waitForComponentsToRender(context).then(() =&gt; {
</span><span class="cx">                     const view = chart.sampledTimeSeriesData('current');
</span><span class="lines">@@ -631,12 +534,12 @@
</span><span class="cx"> 
</span><span class="cx">         it('should contain every outlier when includeOutliers is true', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createChartWithSampleCluster(context, [{includeOutliers: true}])
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}])
</ins><span class="cx"> 
</span><del>-                chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chart.fetchMeasurementSets();
</span><del>-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</ins><span class="cx"> 
</span><span class="cx">                 return waitForComponentsToRender(context).then(() =&gt; {
</span><span class="cx">                     const view = chart.sampledTimeSeriesData('current');
</span><span class="lines">@@ -649,12 +552,12 @@
</span><span class="cx"> 
</span><span class="cx">         it('should only contain data points in the domain and one preceding point when there are no succeeding points', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createChartWithSampleCluster(context, [{includeOutliers: true}])
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}])
</ins><span class="cx"> 
</span><span class="cx">                 chart.setDomain(posixTime('2016-01-06T00:00:00Z'), posixTime('2016-01-07T00:00:00Z'));
</span><span class="cx">                 chart.fetchMeasurementSets();
</span><del>-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</ins><span class="cx"> 
</span><span class="cx">                 return waitForComponentsToRender(context).then(() =&gt; {
</span><span class="cx">                     const view = chart.sampledTimeSeriesData('current');
</span><span class="lines">@@ -665,13 +568,13 @@
</span><span class="cx"> 
</span><span class="cx">         it('should only contain data points in the domain and one succeeding point when there are no preceding points', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createChartWithSampleCluster(context, [{includeOutliers: true}])
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}])
</ins><span class="cx"> 
</span><span class="cx">                 chart.setDomain(posixTime('2016-01-05T00:00:00Z'), posixTime('2016-01-06T00:00:00Z'));
</span><span class="cx">                 chart.fetchMeasurementSets();
</span><span class="cx">                 chart.fetchMeasurementSets();
</span><del>-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</ins><span class="cx"> 
</span><span class="cx">                 return waitForComponentsToRender(context).then(() =&gt; {
</span><span class="cx">                     const view = chart.sampledTimeSeriesData('current');
</span><span class="lines">@@ -682,12 +585,12 @@
</span><span class="cx"> 
</span><span class="cx">         it('should only contain data points in the domain and one preceding point and one succeeding point', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createChartWithSampleCluster(context, [{includeOutliers: true}])
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createChartWithSampleCluster(context, [{includeOutliers: true}])
</ins><span class="cx"> 
</span><span class="cx">                 chart.setDomain(posixTime('2016-01-05T21:00:00Z'), posixTime('2016-01-06T02:00:00Z'));
</span><span class="cx">                 chart.fetchMeasurementSets();
</span><del>-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</ins><span class="cx"> 
</span><span class="cx">                 return waitForComponentsToRender(context).then(() =&gt; {
</span><span class="cx">                     const view = chart.sampledTimeSeriesData('current');
</span><span class="lines">@@ -700,18 +603,18 @@
</span><span class="cx">     describe('render', () =&gt; {
</span><span class="cx">         it('should update the canvas size and its content after the window has been resized', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createChartWithSampleCluster(context, null, {width: '100%', height: '100%'});
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createChartWithSampleCluster(context, null, {width: '100%', height: '100%'});
</ins><span class="cx"> 
</span><span class="cx">                 let dataChangeCount = 0;
</span><span class="cx">                 chart.listenToAction('dataChange', () =&gt; dataChangeCount++);
</span><span class="cx"> 
</span><del>-                chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chart.fetchMeasurementSets();
</span><span class="cx"> 
</span><span class="cx">                 const requests = context.symbols.MockRemoteAPI.requests;
</span><span class="cx">                 expect(requests.length).to.be(1);
</span><del>-                respondWithSampleCluster(requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(requests[0]);
</ins><span class="cx"> 
</span><span class="cx">                 expect(dataChangeCount).to.be(0);
</span><span class="cx">                 expect(chart.sampledTimeSeriesData('current')).to.be(null);
</span><span class="lines">@@ -750,18 +653,18 @@
</span><span class="cx"> 
</span><span class="cx">         it('should not update update the canvas when the window has been resized but its dimensions stays the same', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createChartWithSampleCluster(context, null, {width: '100px', height: '100px'});
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chart = ChartTest.createChartWithSampleCluster(context, null, {width: '100px', height: '100px'});
</ins><span class="cx"> 
</span><span class="cx">                 let dataChangeCount = 0;
</span><span class="cx">                 chart.listenToAction('dataChange', () =&gt; dataChangeCount++);
</span><span class="cx"> 
</span><del>-                chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chart.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chart.fetchMeasurementSets();
</span><span class="cx"> 
</span><span class="cx">                 const requests = context.symbols.MockRemoteAPI.requests;
</span><span class="cx">                 expect(requests.length).to.be(1);
</span><del>-                respondWithSampleCluster(requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(requests[0]);
</ins><span class="cx">                 expect(dataChangeCount).to.be(0);
</span><span class="cx"> 
</span><span class="cx">                 let canvas;
</span><span class="lines">@@ -796,8 +699,8 @@
</span><span class="cx"> 
</span><span class="cx">         it('should render Y-axis', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI', 'Metric').then(() =&gt; {
-                const chartWithoutYAxis = createChartWithSampleCluster(context, null, {axis:
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
+                const chartWithoutYAxis = ChartTest.createChartWithSampleCluster(context, null, {axis:
</ins><span class="cx">                     {
</span><span class="cx">                         gridStyle: '#ccc',
</span><span class="cx">                         fontSize: 1,
</span><span class="lines">@@ -804,7 +707,7 @@
</span><span class="cx">                         valueFormatter: context.symbols.Metric.makeFormatter('ms', 3),
</span><span class="cx">                     }
</span><span class="cx">                 });
</span><del>-                const chartWithYAxis1 = createChartWithSampleCluster(context, null, {axis:
</del><ins>+                const chartWithYAxis1 = ChartTest.createChartWithSampleCluster(context, null, {axis:
</ins><span class="cx">                     {
</span><span class="cx">                         yAxisWidth: 4,
</span><span class="cx">                         gridStyle: '#ccc',
</span><span class="lines">@@ -812,7 +715,7 @@
</span><span class="cx">                         valueFormatter: context.symbols.Metric.makeFormatter('ms', 3),
</span><span class="cx">                     }
</span><span class="cx">                 });
</span><del>-                const chartWithYAxis2 = createChartWithSampleCluster(context, null, {axis:
</del><ins>+                const chartWithYAxis2 = ChartTest.createChartWithSampleCluster(context, null, {axis:
</ins><span class="cx">                     {
</span><span class="cx">                         yAxisWidth: 4,
</span><span class="cx">                         gridStyle: '#ccc',
</span><span class="lines">@@ -821,16 +724,16 @@
</span><span class="cx">                     }
</span><span class="cx">                 });
</span><span class="cx"> 
</span><del>-                chartWithoutYAxis.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chartWithoutYAxis.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chartWithoutYAxis.fetchMeasurementSets();
</span><del>-                chartWithYAxis1.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chartWithYAxis1.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chartWithYAxis1.fetchMeasurementSets();
</span><del>-                chartWithYAxis2.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chartWithYAxis2.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chartWithYAxis2.fetchMeasurementSets();
</span><span class="cx"> 
</span><span class="cx">                 const requests = context.symbols.MockRemoteAPI.requests;
</span><span class="cx">                 expect(requests.length).to.be(1);
</span><del>-                respondWithSampleCluster(requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(requests[0]);
</ins><span class="cx"> 
</span><span class="cx">                 return waitForComponentsToRender(context).then(() =&gt; {
</span><span class="cx">                     let canvasWithoutYAxis = chartWithoutYAxis.content().querySelector('canvas');
</span><span class="lines">@@ -848,18 +751,18 @@
</span><span class="cx"> 
</span><span class="cx">         it('should render the sampled time series', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
</ins><span class="cx">                 const lineStyle = 'rgb(0, 128, 255)';
</span><span class="cx">                 const lineColor = {r: 0, g: 128, b: 255};
</span><span class="cx">                 const chartOptions = {width: '100px', height: '100px'};
</span><del>-                const chartWithoutSampling = createChartWithSampleCluster(context, [{lineStyle, sampleData: false}], chartOptions);
-                const chartWithSampling = createChartWithSampleCluster(context, [{lineStyle, sampleData: true}], chartOptions);
</del><ins>+                const chartWithoutSampling = ChartTest.createChartWithSampleCluster(context, [{lineStyle, sampleData: false}], chartOptions);
+                const chartWithSampling = ChartTest.createChartWithSampleCluster(context, [{lineStyle, sampleData: true}], chartOptions);
</ins><span class="cx"> 
</span><del>-                chartWithoutSampling.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chartWithoutSampling.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chartWithoutSampling.fetchMeasurementSets();
</span><del>-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</ins><span class="cx"> 
</span><del>-                chartWithSampling.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chartWithSampling.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chartWithSampling.fetchMeasurementSets();
</span><span class="cx"> 
</span><span class="cx">                 let canvasWithSampling;
</span><span class="lines">@@ -872,9 +775,9 @@
</span><span class="cx">                     expect(CanvasTest.canvasContainsColor(canvasWithoutSampling, lineColor)).to.be(true);
</span><span class="cx">                     expect(CanvasTest.canvasContainsColor(canvasWithSampling, lineColor)).to.be(true);
</span><span class="cx"> 
</span><del>-                    const diff = sampleCluster.endTime - sampleCluster.startTime;
-                    chartWithoutSampling.setDomain(sampleCluster.startTime - 2 * diff, sampleCluster.endTime);
-                    chartWithSampling.setDomain(sampleCluster.startTime - 2 * diff, sampleCluster.endTime);
</del><ins>+                    const diff = ChartTest.sampleCluster.endTime - ChartTest.sampleCluster.startTime;
+                    chartWithoutSampling.setDomain(ChartTest.sampleCluster.startTime - 2 * diff, ChartTest.sampleCluster.endTime);
+                    chartWithSampling.setDomain(ChartTest.sampleCluster.startTime - 2 * diff, ChartTest.sampleCluster.endTime);
</ins><span class="cx"> 
</span><span class="cx">                     CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithoutSampling);
</span><span class="cx">                     CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithSampling);
</span><span class="lines">@@ -893,24 +796,24 @@
</span><span class="cx"> 
</span><span class="cx">         it('should render annotations', () =&gt; {
</span><span class="cx">             const context = new BrowsingContext();
</span><del>-            return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
</del><ins>+            return ChartTest.importChartScripts(context).then(() =&gt; {
</ins><span class="cx">                 const options = {annotations: { textStyle: '#000', textBackground: '#fff', minWidth: 3, barHeight: 7, barSpacing: 2}};
</span><del>-                const chartWithoutAnnotations = createChartWithSampleCluster(context, null, options);
-                const chartWithAnnotations = createChartWithSampleCluster(context, null, options);
</del><ins>+                const chartWithoutAnnotations = ChartTest.createChartWithSampleCluster(context, null, options);
+                const chartWithAnnotations = ChartTest.createChartWithSampleCluster(context, null, options);
</ins><span class="cx"> 
</span><del>-                chartWithoutAnnotations.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chartWithoutAnnotations.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chartWithoutAnnotations.fetchMeasurementSets();
</span><del>-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</del><ins>+                ChartTest.respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
</ins><span class="cx"> 
</span><del>-                chartWithAnnotations.setDomain(sampleCluster.startTime, sampleCluster.endTime);
</del><ins>+                chartWithAnnotations.setDomain(ChartTest.sampleCluster.startTime, ChartTest.sampleCluster.endTime);
</ins><span class="cx">                 chartWithAnnotations.fetchMeasurementSets();
</span><span class="cx"> 
</span><span class="cx">                 let canvasWithAnnotations;
</span><span class="cx">                 return waitForComponentsToRender(context).then(() =&gt; {
</span><del>-                    const diff = sampleCluster.endTime - sampleCluster.startTime;
</del><ins>+                    const diff = ChartTest.sampleCluster.endTime - ChartTest.sampleCluster.startTime;
</ins><span class="cx">                     chartWithAnnotations.setAnnotations([{
</span><del>-                        startTime: sampleCluster.startTime + diff / 4,
-                        endTime: sampleCluster.startTime + diff / 2,
</del><ins>+                        startTime: ChartTest.sampleCluster.startTime + diff / 4,
+                        endTime: ChartTest.sampleCluster.startTime + diff / 2,
</ins><span class="cx">                         label: 'hello, world',
</span><span class="cx">                         fillStyle: 'rgb(0, 0, 255)',
</span><span class="cx">                     }]);
</span><span class="lines">@@ -929,849 +832,3 @@
</span><span class="cx">     });
</span><span class="cx"> 
</span><span class="cx"> });
</span><del>-
-describe('InteractiveTimeSeriesChart', () =&gt; {
-
-    it('should change the unlocked indicator to the point closest to the last mouse move position', () =&gt; {
-        const context = new BrowsingContext();
-        return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-            const chart = createInteractiveChartWithSampleCluster(context);
-
-            chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-            chart.fetchMeasurementSets();
-            respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-            const indicatorChangeCalls = [];
-            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
-
-            let selectionChangeCount = 0;
-            chart.listenToAction('selectionChange', () =&gt; selectionChangeCount++);
-
-            let canvas;
-            return waitForComponentsToRender(context).then(() =&gt; {
-                expect(chart.currentSelection()).to.be(null);
-                expect(chart.currentIndicator()).to.be(null);
-                expect(indicatorChangeCalls).to.be.eql([]);
-
-                canvas = chart.content().querySelector('canvas');
-                const rect = canvas.getBoundingClientRect();
-                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 1, clientY: rect.top + rect.height / 2, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                expect(chart.currentSelection()).to.be(null);
-                const indicator = chart.currentIndicator();
-                expect(indicator).to.not.be(null);
-                const currentView = chart.sampledTimeSeriesData('current');
-                const lastPoint = currentView.lastPoint();
-                expect(indicator.view).to.be(currentView);
-                expect(indicator.point).to.be(lastPoint);
-                expect(indicator.isLocked).to.be(false);
-                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false]]);
-
-                expect(selectionChangeCount).to.be(0);
-            });
-        });
-    });
-
-    it('should lock the indicator to the point closest to the clicked position', () =&gt; {
-        const context = new BrowsingContext();
-        return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-            const chart = createInteractiveChartWithSampleCluster(context);
-
-            chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-            chart.fetchMeasurementSets();
-            respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-            const indicatorChangeCalls = [];
-            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
-
-            let selectionChangeCount = 0;
-            chart.listenToAction('selectionChange', () =&gt; selectionChangeCount++);
-
-            let canvas;
-            return waitForComponentsToRender(context).then(() =&gt; {
-                expect(chart.currentSelection()).to.be(null);
-                expect(chart.currentIndicator()).to.be(null);
-                expect(indicatorChangeCalls).to.be.eql([]);
-                canvas = chart.content().querySelector('canvas');
-                const rect = canvas.getBoundingClientRect();
-
-                const x = rect.right - 1;
-                const y = rect.top + rect.height / 2;
-                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: x - 0.5, clientY: y + 0.5, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: x - 0.5, clientY: y + 0.5, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: x - 0.5, clientY: y + 0.5, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                const currentView = chart.sampledTimeSeriesData('current');
-                const lastPoint = currentView.lastPoint();
-                expect(chart.currentSelection()).to.be(null);
-                const indicator = chart.currentIndicator();
-                expect(indicator.view).to.be(currentView);
-                expect(indicator.point).to.be(lastPoint);
-                expect(indicator.isLocked).to.be(true);
-                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false], [lastPoint.id, true]]);
-
-                expect(selectionChangeCount).to.be(0);
-            });
-        });
-    });
-
-    it('should clear the unlocked indicator when the mouse cursor exits the chart', () =&gt; {
-        const context = new BrowsingContext();
-        return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-            const chart = createInteractiveChartWithSampleCluster(context);
-
-            chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-            chart.fetchMeasurementSets();
-            respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-            const indicatorChangeCalls = [];
-            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
-
-            let selectionChangeCount = 0;
-            chart.listenToAction('selectionChange', () =&gt; selectionChangeCount++);
-
-            let canvas;
-            let rect;
-            let lastPoint;
-            return waitForComponentsToRender(context).then(() =&gt; {
-                expect(chart.currentSelection()).to.be(null);
-                expect(chart.currentIndicator()).to.be(null);
-                expect(indicatorChangeCalls).to.be.eql([]);
-
-                canvas = chart.content().querySelector('canvas');
-                rect = canvas.getBoundingClientRect();
-                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 1, clientY: rect.top + rect.height / 2, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                const currentView = chart.sampledTimeSeriesData('current');
-                lastPoint = currentView.lastPoint();
-                expect(chart.currentSelection()).to.be(null);
-                const indicator = chart.currentIndicator();
-                expect(indicator.view).to.be(currentView);
-                expect(indicator.point).to.be(lastPoint);
-                expect(indicator.isLocked).to.be(false);
-                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false]]);
-
-                canvas.parentNode.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('mouseleave', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                expect(chart.currentSelection()).to.be(null);
-                expect(chart.currentIndicator()).to.be(null);
-                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, false], [null, false]]);
-
-                expect(selectionChangeCount).to.be(0);
-            });
-        });
-    });
-
-    it('should not clear the locked indicator when the mouse cursor exits the chart', () =&gt; {
-        const context = new BrowsingContext();
-        return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-            const chart = createInteractiveChartWithSampleCluster(context);
-
-            chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-            chart.fetchMeasurementSets();
-            respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-            const indicatorChangeCalls = [];
-            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
-
-            let selectionChangeCount = 0;
-            chart.listenToAction('selectionChange', () =&gt; selectionChangeCount++);
-
-            let canvas;
-            let rect;
-            let currentView;
-            let lastPoint;
-            return waitForComponentsToRender(context).then(() =&gt; {
-                expect(chart.currentSelection()).to.be(null);
-                expect(chart.currentIndicator()).to.be(null);
-                expect(indicatorChangeCalls).to.be.eql([]);
-
-                canvas = chart.content().querySelector('canvas');
-                rect = canvas.getBoundingClientRect();
-                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 1, clientY: rect.top + rect.height / 2, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                currentView = chart.sampledTimeSeriesData('current');
-                lastPoint = currentView.lastPoint();
-                expect(chart.currentSelection()).to.be(null);
-                const indicator = chart.currentIndicator();
-                expect(indicator.view).to.be(currentView);
-                expect(indicator.point).to.be(lastPoint);
-                expect(indicator.isLocked).to.be(true);
-                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true]]);
-
-                canvas.parentNode.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('mouseleave', {target: canvas, clientX: rect.right + 50, clientY: rect.bottom + 50, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
-
-                expect(chart.currentSelection()).to.be(null);
-                const indicator = chart.currentIndicator();
-                expect(indicator.view).to.be(currentView);
-                expect(indicator.point).to.be(lastPoint);
-                expect(indicator.isLocked).to.be(true);
-                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true]]);
-
-                expect(selectionChangeCount).to.be(0);
-            })
-        });
-    });
-
-    it('should clear the locked indicator when clicked', () =&gt; {
-        const context = new BrowsingContext();
-        return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-            const chart = createInteractiveChartWithSampleCluster(context);
-
-            chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-            chart.fetchMeasurementSets();
-            respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-            const indicatorChangeCalls = [];
-            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
-
-            let selectionChangeCount = 0;
-            chart.listenToAction('selectionChange', () =&gt; selectionChangeCount++);
-
-            let canvas;
-            let rect;
-            let y;
-            let currentView;
-            let lastPoint;
-            return waitForComponentsToRender(context).then(() =&gt; {
-                expect(chart.currentSelection()).to.be(null);
-                expect(chart.currentIndicator()).to.be(null);
-                expect(indicatorChangeCalls).to.be.eql([]);
-
-                canvas = chart.content().querySelector('canvas');
-                rect = canvas.getBoundingClientRect();
-                y = rect.top + rect.height / 2;
-                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 1, clientY: y, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                currentView = chart.sampledTimeSeriesData('current');
-                lastPoint = currentView.lastPoint();
-                expect(chart.currentSelection()).to.be(null);
-                const indicator = chart.currentIndicator();
-                expect(indicator.view).to.be(currentView);
-                expect(indicator.point).to.be(lastPoint);
-                expect(indicator.isLocked).to.be(true);
-                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true]]);
-
-                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.left + 1, clientY: y, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                expect(chart.currentSelection()).to.be(null);
-                const firstPoint = currentView.firstPoint();
-                const indicator = chart.currentIndicator();
-                expect(indicator.view).to.be(currentView);
-                expect(indicator.point).to.be(firstPoint);
-                expect(indicator.isLocked).to.be(false);
-                expect(indicatorChangeCalls).to.be.eql([[lastPoint.id, true], [firstPoint.id, false]]);
-
-                expect(selectionChangeCount).to.be(0);
-            })
-        });
-    });
-
-    it('should change the selection when the mouse cursor is dragged', () =&gt; {
-        const context = new BrowsingContext();
-        return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-            const chart = createInteractiveChartWithSampleCluster(context, null, {selection: {lineStyle: '#f93', lineWidth: 2, fillStyle: '#ccc'}});
-
-            chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-            chart.fetchMeasurementSets();
-            respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-            const indicatorChangeCalls = [];
-            chart.listenToAction('indicatorChange', (...args) =&gt; indicatorChangeCalls.push(args));
-
-            const selectionChangeCalls = [];
-            chart.listenToAction('selectionChange', (...args) =&gt; selectionChangeCalls.push(args));
-
-            const zoomButton = chart.content('zoom-button');
-
-            let canvas;
-            let rect;
-            let y;
-            let currentView;
-            let firstPoint;
-            let oldRange;
-            let newRange;
-            return waitForComponentsToRender(context).then(() =&gt; {
-                expect(chart.currentSelection()).to.be(null);
-                expect(chart.currentIndicator()).to.be(null);
-                expect(selectionChangeCalls).to.be.eql([]);
-
-                canvas = chart.content().querySelector('canvas');
-                rect = canvas.getBoundingClientRect();
-                y = rect.top + rect.height / 2;
-                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                currentView = chart.sampledTimeSeriesData('current');
-                firstPoint = currentView.firstPoint();
-                expect(chart.currentSelection()).to.be(null);
-                let indicator = chart.currentIndicator();
-                expect(indicator.view).to.be(currentView);
-                expect(indicator.point).to.be(firstPoint);
-                expect(indicator.isLocked).to.be(false);
-                expect(indicatorChangeCalls).to.be.eql([[firstPoint.id, false]]);
-                expect(zoomButton.offsetHeight).to.be(0);
-
-                canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
-
-                expect(chart.currentSelection()).to.be(null);
-                let indicator = chart.currentIndicator();
-                expect(indicator.view).to.be(currentView);
-                expect(indicator.point).to.be(firstPoint);
-                expect(indicator.isLocked).to.be(false);
-                expect(selectionChangeCalls).to.be.eql([]);
-                expect(indicatorChangeCalls).to.be.eql([[firstPoint.id, false]]);
-                expect(zoomButton.offsetHeight).to.be(0);
-
-                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 15, clientY: y + 5, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                expect(chart.currentSelection()).to.not.be(null);
-                expect(chart.currentIndicator()).to.be(null);
-                expect(selectionChangeCalls.length).to.be(1);
-                oldRange = selectionChangeCalls[0][0];
-                expect(oldRange).to.be.eql(chart.currentSelection());
-                expect(selectionChangeCalls[0][1]).to.be(false);
-                expect(indicatorChangeCalls).to.be.eql([[firstPoint.id, false], [null, false]]);
-                expect(zoomButton.offsetHeight).to.be(0);
-
-                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 5, clientY: y + 5, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                expect(chart.currentSelection()).to.not.be(null);
-                expect(chart.currentIndicator()).to.be(null);
-                expect(selectionChangeCalls.length).to.be(2);
-                newRange = selectionChangeCalls[1][0];
-                expect(newRange).to.be.eql(chart.currentSelection());
-                expect(newRange[0]).to.be(oldRange[0]);
-                expect(newRange[1]).to.be.greaterThan(oldRange[1]);
-                expect(selectionChangeCalls[1][1]).to.be(false);
-                expect(zoomButton.offsetHeight).to.be(0);
-
-                canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: rect.right - 5, clientY: y + 5, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 5, clientY: y + 5, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                expect(chart.currentSelection()).to.be.eql(newRange);
-                expect(chart.currentIndicator()).to.be(null);
-                expect(selectionChangeCalls.length).to.be(3);
-                expect(selectionChangeCalls[2][0]).to.be.eql(newRange);
-                expect(selectionChangeCalls[2][1]).to.be(true);
-                expect(zoomButton.offsetHeight).to.be(0);
-            });
-        });
-    });
-
-    it('should dispatch the &quot;zoom&quot; action when the zoom button is clicked', () =&gt; {
-        const context = new BrowsingContext();
-        return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-            const chart = createInteractiveChartWithSampleCluster(context, null, {selection: {lineStyle: '#f93', lineWidth: 2, fillStyle: '#ccc'}, zoomButton: true});
-
-            chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-            chart.fetchMeasurementSets();
-            respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-            const zoomCalls = [];
-            chart.listenToAction('zoom', (...args) =&gt; zoomCalls.push(args));
-            const zoomButton = chart.content('zoom-button');
-
-            let selection;
-            let canvas;
-            return waitForComponentsToRender(context).then(() =&gt; {
-                expect(zoomButton.offsetHeight).to.be(0);
-                canvas = chart.content().querySelector('canvas');
-                const rect = canvas.getBoundingClientRect();
-                const y = rect.top + rect.height / 2;
-                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                selection = chart.currentSelection();
-                expect(selection).to.not.be(null);
-                expect(chart.currentIndicator()).to.be(null);
-                expect(zoomButton.offsetHeight).to.not.be(0);
-                expect(zoomCalls).to.be.eql([]);
-                zoomButton.click();
-            }).then(() =&gt; {
-                expect(zoomCalls).to.be.eql([[selection]]);
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
-            });
-        });
-    });
-
-    it('should clear the selection when clicked', () =&gt; {
-        const context = new BrowsingContext();
-        return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-            const chart = createInteractiveChartWithSampleCluster(context, null, {selection: {lineStyle: '#f93', lineWidth: 2, fillStyle: '#ccc'}});
-
-            chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-            chart.fetchMeasurementSets();
-            respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-            let canvas;
-            let rect;
-            let y;
-            return waitForComponentsToRender(context).then(() =&gt; {
-                canvas = chart.content().querySelector('canvas');
-                rect = canvas.getBoundingClientRect();
-                y = rect.top + rect.height / 2;
-                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('mousedown', {target: canvas, clientX: rect.left + 5, clientY: y, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('mousemove', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('mouseup', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
-                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.right - 10, clientY: y + 5, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                expect(chart.currentSelection()).to.not.be(null);
-                expect(chart.currentIndicator()).to.be(null);
-
-                canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: rect.left + 1, clientY: y + 5, composed: true, bubbles: true}));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                expect(chart.currentSelection()).to.be(null);
-                const currentView = chart.sampledTimeSeriesData('current');
-                const indicator = chart.currentIndicator();
-                expect(indicator.view).to.be(currentView);
-                expect(indicator.point).to.be(currentView.firstPoint());
-                expect(indicator.isLocked).to.be(false);
-            });
-        });
-    });
-
-    it('should dispatch &quot;annotationClick&quot; action when an annotation is clicked', () =&gt; {
-        const context = new BrowsingContext();
-        return context.importScripts(scripts, 'ComponentBase', 'TimeSeriesChart', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-            const chart = createInteractiveChartWithSampleCluster(context, null,
-                {annotations: { textStyle: '#000', textBackground: '#fff', minWidth: 3, barHeight: 10, barSpacing: 1}});
-
-            chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-            chart.fetchMeasurementSets();
-            respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-            const diff = sampleCluster.endTime - sampleCluster.startTime;
-            const annotations = [{
-                startTime: sampleCluster.startTime + diff / 2,
-                endTime: sampleCluster.endTime - diff / 4,
-                label: 'hello, world',
-                fillStyle: 'rgb(0, 0, 255)',
-            }]
-            chart.setAnnotations(annotations);
-
-            const annotationClickCalls = [];
-            chart.listenToAction('annotationClick', (...args) =&gt; annotationClickCalls.push(args));
-
-            let canvas;
-            let init;
-            return waitForComponentsToRender(context).then(() =&gt; {
-                expect(annotationClickCalls).to.be.eql([]);
-                expect(chart.content('annotation-label').textContent).to.not.contain('hello, world');
-
-                canvas = chart.content().querySelector('canvas');
-                const rect = canvas.getBoundingClientRect();
-                init = {target: canvas, clientX: rect.right - rect.width / 4, clientY: rect.bottom - 5, composed: true, bubbles: true};
-                canvas.dispatchEvent(new MouseEvent('mousemove', init));
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                expect(chart.content('annotation-label').textContent).to.contain('hello, world');
-                expect(annotationClickCalls).to.be.eql([]);
-                canvas.dispatchEvent(new MouseEvent('mousedown', init));
-                canvas.dispatchEvent(new MouseEvent('mouseup', init));
-                canvas.dispatchEvent(new MouseEvent('click', init));
-
-                expect(annotationClickCalls).to.be.eql([[annotations[0]]]);
-
-                CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                return waitForComponentsToRender(context);
-            }).then(() =&gt; {
-                expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
-            });
-        });
-    });
-
-    describe('render', () =&gt; {
-        it('should render the unlocked indicator when options.indicator is specified', () =&gt; {
-            const context = new BrowsingContext();
-            return context.importScripts(scripts, 'ComponentBase', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chartWithoutIndicator = createInteractiveChartWithSampleCluster(context);
-                const chartWithIndicator = createInteractiveChartWithSampleCluster(context, null,
-                    {indicator: {lineStyle: 'rgb(51, 204, 255)', lineWidth: 2, pointRadius: 2}, interactiveChart: true});
-                const indicatorColor = {r: 51, g: 204, b: 255};
-
-                chartWithoutIndicator.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-                chartWithoutIndicator.fetchMeasurementSets();
-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-                chartWithIndicator.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-                chartWithIndicator.fetchMeasurementSets();
-
-                let canvasWithoutIndicator;
-                let canvasWithIndicator;
-                return waitForComponentsToRender(context).then(() =&gt; {
-                    canvasWithoutIndicator = chartWithoutIndicator.content().querySelector('canvas');
-                    canvasWithIndicator = chartWithIndicator.content().querySelector('canvas');
-
-                    const rect = canvasWithIndicator.getBoundingClientRect();
-                    const x = rect.right - 1;
-                    const y = rect.top + rect.height / 2;
-                    canvasWithIndicator.dispatchEvent(new MouseEvent('mousemove', {target: canvasWithIndicator, clientX: x, clientY: y, composed: true, bubbles: true}));
-
-                    CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithIndicator);
-                    return waitForComponentsToRender(context);
-                }).then(() =&gt; {
-                    expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithIndicator)).to.be(true);
-
-                    const indicator = chartWithIndicator.currentIndicator();
-                    const currentView = chartWithIndicator.sampledTimeSeriesData('current');
-                    expect(indicator.view).to.be(currentView);
-                    expect(indicator.point).to.be(currentView.lastPoint());
-                    expect(indicator.isLocked).to.be(false);
-
-                    CanvasTest.expectCanvasesMismatch(canvasWithoutIndicator, canvasWithIndicator);
-                    expect(CanvasTest.canvasContainsColor(canvasWithoutIndicator, indicatorColor)).to.be(false);
-                    expect(CanvasTest.canvasContainsColor(canvasWithIndicator, indicatorColor)).to.be(true);
-                });
-            });
-        });
-
-        it('should render the locked indicator differently from the unlocked indicator when options.lockedIndicator is specified', () =&gt; {
-            const context = new BrowsingContext();
-            return context.importScripts(scripts, 'ComponentBase', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chartOptions = {
-                    indicator: {lineStyle: 'rgb(51, 204, 255)', lineWidth: 2, pointRadius: 3},
-                    lockedIndicator: {lineStyle: 'rgb(51, 102, 204)', fillStyle: 'rgb(250, 250, 250)', lineWidth: 2, pointRadius: 3}
-                };
-                const unlockedColor = {r: 51, g: 204, b: 255};
-                const lockedColor = {r: 51, g: 102, b: 204};
-                const lockedFillColor = {r: 250, g: 250, b: 250};
-                const chartWithUnlockedIndicator = createInteractiveChartWithSampleCluster(context, null, chartOptions);
-                const chartWithLockedIndicator = createInteractiveChartWithSampleCluster(context, null, chartOptions);
-
-                chartWithUnlockedIndicator.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-                chartWithUnlockedIndicator.fetchMeasurementSets();
-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-                chartWithLockedIndicator.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-                chartWithLockedIndicator.fetchMeasurementSets();
-
-                let canvasWithUnlockedIndicator;
-                let canvasWithLockedIndicator;
-                return waitForComponentsToRender(context).then(() =&gt; {
-                    canvasWithUnlockedIndicator = chartWithUnlockedIndicator.content().querySelector('canvas');
-                    canvasWithLockedIndicator = chartWithLockedIndicator.content().querySelector('canvas');
-
-                    const rect = canvasWithUnlockedIndicator.getBoundingClientRect();
-                    const x = rect.right - 1;
-                    const y = rect.top + rect.height / 2;
-                    canvasWithUnlockedIndicator.dispatchEvent(new MouseEvent('mousemove', {target: canvasWithUnlockedIndicator, clientX: x, clientY: y, composed: true, bubbles: true}));
-                    canvasWithLockedIndicator.dispatchEvent(new MouseEvent('click', {target: canvasWithLockedIndicator, clientX: x, clientY: y, composed: true, bubbles: true}));
-
-                    CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithUnlockedIndicator);
-                    CanvasTest.fillCanvasBeforeRedrawCheck(canvasWithLockedIndicator);
-                    return waitForComponentsToRender(context);
-                }).then(() =&gt; {
-                    expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithUnlockedIndicator)).to.be(true);
-                    expect(CanvasTest.hasCanvasBeenRedrawn(canvasWithLockedIndicator)).to.be(true);
-
-                    let indicator = chartWithUnlockedIndicator.currentIndicator();
-                    let currentView = chartWithUnlockedIndicator.sampledTimeSeriesData('current');
-                    expect(indicator.view).to.be(currentView);
-                    expect(indicator.point).to.be(currentView.lastPoint());
-                    expect(indicator.isLocked).to.be(false);
-
-                    indicator = chartWithLockedIndicator.currentIndicator();
-                    currentView = chartWithLockedIndicator.sampledTimeSeriesData('current');
-                    expect(indicator.view).to.be(currentView);
-                    expect(indicator.point).to.be(currentView.lastPoint());
-                    expect(indicator.isLocked).to.be(true);
-
-                    CanvasTest.expectCanvasesMismatch(canvasWithUnlockedIndicator, canvasWithLockedIndicator);
-                    expect(CanvasTest.canvasContainsColor(canvasWithUnlockedIndicator, unlockedColor)).to.be(true);
-                    expect(CanvasTest.canvasContainsColor(canvasWithUnlockedIndicator, lockedFillColor)).to.be(false);
-                    expect(CanvasTest.canvasContainsColor(canvasWithLockedIndicator, lockedColor)).to.be(true);
-                    expect(CanvasTest.canvasContainsColor(canvasWithLockedIndicator, lockedFillColor)).to.be(true);
-                });
-            });
-        });
-    });
-
-    describe('moveLockedIndicatorWithNotification', () =&gt; {
-        it('should move the locked indicator to the right when forward boolean is true', () =&gt; {
-            const context = new BrowsingContext();
-            return context.importScripts(scripts, 'ComponentBase', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createInteractiveChartWithSampleCluster(context);
-
-                chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-                chart.fetchMeasurementSets();
-                let indicatorChangeCount = 0;
-                chart.listenToAction('indicatorChange', () =&gt; indicatorChangeCount++);
-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-                let canvas;
-                return waitForComponentsToRender(context).then(() =&gt; {
-                    expect(indicatorChangeCount).to.be(0);
-
-                    canvas = chart.content().querySelector('canvas');
-
-                    const rect = canvas.getBoundingClientRect();
-                    const x = rect.left + 1;
-                    const y = rect.top + rect.height / 2;
-                    canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
-
-                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                    return waitForComponentsToRender(context);
-                }).then(() =&gt; {
-                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-                    expect(indicatorChangeCount).to.be(1);
-
-                    let indicator = chart.currentIndicator();
-                    let currentView = chart.sampledTimeSeriesData('current');
-                    expect(indicator.view).to.be(currentView);
-                    expect(indicator.point).to.be(currentView.firstPoint());
-                    expect(indicator.isLocked).to.be(true);
-
-                    chart.moveLockedIndicatorWithNotification(true);
-
-                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                    return waitForComponentsToRender(context);
-                }).then(() =&gt; {
-                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-                    expect(indicatorChangeCount).to.be(2);
-
-                    let indicator = chart.currentIndicator();
-                    let currentView = chart.sampledTimeSeriesData('current');
-                    expect(indicator.view).to.be(currentView);
-                    expect(indicator.point).to.not.be(currentView.firstPoint());
-                    expect(indicator.point).to.be(currentView.nextPoint(currentView.firstPoint()));
-                    expect(currentView.previousPoint(indicator.point)).to.be(currentView.firstPoint());
-                    expect(indicator.isLocked).to.be(true);
-                    expect(indicatorChangeCount).to.be(2);
-                });
-            });
-        });
-
-        it('should move the locked indicator to the left when forward boolean is false', () =&gt; {
-            const context = new BrowsingContext();
-            return context.importScripts(scripts, 'ComponentBase', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createInteractiveChartWithSampleCluster(context);
-
-                chart.setDomain(sampleCluster.startTime, sampleCluster.endTime);
-                chart.fetchMeasurementSets();
-                let indicatorChangeCount = 0;
-                chart.listenToAction('indicatorChange', () =&gt; indicatorChangeCount++);
-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-                let canvas;
-                return waitForComponentsToRender(context).then(() =&gt; {
-                    expect(indicatorChangeCount).to.be(0);
-
-                    canvas = chart.content().querySelector('canvas');
-
-                    const rect = canvas.getBoundingClientRect();
-                    const x = rect.right - 1;
-                    const y = rect.top + rect.height / 2;
-                    canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
-
-                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                    return waitForComponentsToRender(context);
-                }).then(() =&gt; {
-                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-                    expect(indicatorChangeCount).to.be(1);
-
-                    let indicator = chart.currentIndicator();
-                    let currentView = chart.sampledTimeSeriesData('current');
-                    expect(indicator.view).to.be(currentView);
-                    expect(indicator.point).to.be(currentView.lastPoint());
-                    expect(indicator.isLocked).to.be(true);
-
-                    chart.moveLockedIndicatorWithNotification(false);
-
-                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                    return waitForComponentsToRender(context);
-                }).then(() =&gt; {
-                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-                    expect(indicatorChangeCount).to.be(2);
-
-                    let indicator = chart.currentIndicator();
-                    let currentView = chart.sampledTimeSeriesData('current');
-                    expect(indicator.view).to.be(currentView);
-                    expect(indicator.point).to.not.be(currentView.firstPoint());
-                    expect(indicator.point).to.be(currentView.previousPoint(currentView.lastPoint()));
-                    expect(currentView.nextPoint(indicator.point)).to.be(currentView.lastPoint());
-                    expect(indicator.isLocked).to.be(true);
-                    expect(indicatorChangeCount).to.be(2);
-                });
-            });
-        });
-
-        it('should not move the locked indicator when there are no points within the domain', () =&gt; {
-            const context = new BrowsingContext();
-            return context.importScripts(scripts, 'ComponentBase', 'InteractiveTimeSeriesChart', 'MeasurementSet', 'MockRemoteAPI').then(() =&gt; {
-                const chart = createInteractiveChartWithSampleCluster(context);
-
-                // The domain inclues points 2, 3
-                chart.setDomain(posixTime('2016-01-05T20:00:00Z'), posixTime('2016-01-06T00:00:00Z'));
-                chart.fetchMeasurementSets();
-                let indicatorChangeCount = 0;
-                chart.listenToAction('indicatorChange', () =&gt; indicatorChangeCount++);
-                respondWithSampleCluster(context.symbols.MockRemoteAPI.requests[0]);
-
-                let canvas;
-                let currentView;
-                return waitForComponentsToRender(context).then(() =&gt; {
-                    expect(indicatorChangeCount).to.be(0);
-
-                    canvas = chart.content().querySelector('canvas');
-
-                    const rect = canvas.getBoundingClientRect();
-                    const x = rect.right - 1;
-                    const y = rect.top + rect.height / 2;
-                    canvas.dispatchEvent(new MouseEvent('click', {target: canvas, clientX: x, clientY: y, composed: true, bubbles: true}));
-
-                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                    return waitForComponentsToRender(context);
-                }).then(() =&gt; {
-                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-                    expect(indicatorChangeCount).to.be(1);
-
-                    currentView = chart.sampledTimeSeriesData('current');
-                    expect(currentView.length()).to.be(4); // points 0 and 4 are added to draw lines extending beyond the domain.
-                    expect([...currentView].map((point) =&gt; point.id)).to.be.eql([1000, 1002, 1003, 1004]);
-
-                    const indicator = chart.currentIndicator();
-                    expect(indicator.view).to.be(currentView);
-                    expect(indicator.point.id).to.be(1003);
-                    expect(indicator.isLocked).to.be(true);
-
-                    chart.moveLockedIndicatorWithNotification(true);
-
-                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                    return waitForComponentsToRender(context);
-                }).then(() =&gt; {
-                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
-
-                    expect(indicatorChangeCount).to.be(1);
-                    const indicator = chart.currentIndicator();
-                    expect(indicator.view).to.be(currentView);
-                    expect(indicator.point.id).to.be(1003);
-                    expect(indicator.isLocked).to.be(true);
-
-                    chart.moveLockedIndicatorWithNotification(false);
-
-                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                    return waitForComponentsToRender(context);
-                }).then(() =&gt; {
-                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(true);
-
-                    expect(indicatorChangeCount).to.be(2);
-                    const indicator = chart.currentIndicator();
-                    expect(indicator.view).to.be(currentView);
-                    expect(indicator.point.id).to.be(1002);
-                    expect(indicator.isLocked).to.be(true);
-
-                    chart.moveLockedIndicatorWithNotification(false);
-
-                    CanvasTest.fillCanvasBeforeRedrawCheck(canvas);
-                    return waitForComponentsToRender(context);
-                }).then(() =&gt; {
-                    expect(CanvasTest.hasCanvasBeenRedrawn(canvas)).to.be(false);
-
-                    expect(indicatorChangeCount).to.be(2);
-                    const indicator = chart.currentIndicator();
-                    expect(indicator.view).to.be(currentView);
-                    expect(indicator.point.id).to.be(1002);
-                    expect(indicator.isLocked).to.be(true);
-                });
-            });
-        });
-
-    });
-});
-
-})();
</del><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgunittestsanalysistasktestsjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/unit-tests/analysis-task-tests.js (213118 => 213119)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/unit-tests/analysis-task-tests.js        2017-02-28 04:47:44 UTC (rev 213118)
+++ trunk/Websites/perf.webkit.org/unit-tests/analysis-task-tests.js        2017-02-28 04:48:36 UTC (rev 213119)
</span><span class="lines">@@ -204,10 +204,10 @@
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="cx">         it('should find CommitLog objects for `causes` when MeasurementAdaptor created matching objects', function (done) {
</span><del>-            var adoptor = new MeasurementAdaptor(measurementCluster().formatMap);
-            var adoptedMeasurement = adoptor.applyTo(measurementCluster().configurations.current[0]);
-            assert.equal(adoptedMeasurement.id, 37188161);
-            assert.equal(adoptedMeasurement.rootSet().commitForRepository(MockModels.webkit).revision(), '196051');
</del><ins>+            var adaptor = new MeasurementAdaptor(measurementCluster().formatMap);
+            var adaptedMeasurement = adaptor.applyTo(measurementCluster().configurations.current[0]);
+            assert.equal(adaptedMeasurement.id, 37188161);
+            assert.equal(adaptedMeasurement.rootSet().commitForRepository(MockModels.webkit).revision(), '196051');
</ins><span class="cx"> 
</span><span class="cx">             var promise = AnalysisTask.fetchAll();
</span><span class="cx">             requests[0].resolve(sampleAnalysisTask());
</span></span></pre>
</div>
</div>

</body>
</html>