Modified: trunk/Websites/perf.webkit.org/public/v3/components/results-table.js (196707 => 196708)
--- trunk/Websites/perf.webkit.org/public/v3/components/results-table.js 2016-02-17 20:11:05 UTC (rev 196707)
+++ trunk/Websites/perf.webkit.org/public/v3/components/results-table.js 2016-02-17 20:18:05 UTC (rev 196708)
@@ -4,10 +4,23 @@
super(name);
this._repositoryList = [];
this._valueFormatter = null;
+ this._rangeSelectorLabels = null;
+ this._rangeSelectorCallback = null;
+ this._selectedRange = {};
}
setValueFormatter(valueFormatter) { this._valueFormatter = valueFormatter; }
+ setRangeSelectorLabels(labels) { this._rangeSelectorLabels = labels; }
+ setRangeSelectorCallback(callback) { this._rangeSelectorCallback = callback; }
+ selectedRange() { return this._selectedRange; }
+ _rangeSelectorClicked(label, row)
+ {
+ this._selectedRange[label] = row;
+ if (this._rangeSelectorCallback)
+ this._rangeSelectorCallback();
+ }
+
render()
{
if (!this._valueFormatter)
@@ -23,13 +36,22 @@
var barGraphGroup = new BarGraphGroup(this._valueFormatter);
var element = ComponentBase.createElement;
var self = this;
+ var hasGroupHeading = false;
var tableBodies = rowGroups.map(function (group) {
var groupHeading = group.heading;
var revisionSupressionCount = {};
+ hasGroupHeading = !!groupHeading;
return element('tbody', group.rows.map(function (row, rowIndex) {
var cells = [];
+ if (self._rangeSelectorLabels) {
+ self._selectedRange = {};
+ for (var label of self._rangeSelectorLabels)
+ cells.push(element('td', element('input',
+ {type: 'radio', name: label, onchange: self._rangeSelectorClicked.bind(self, label, row)})));
+ }
+
if (groupHeading !== undefined && !rowIndex)
cells.push(element('th', {rowspan: group.rows.length}, groupHeading));
cells.push(element('td', row.heading()));
@@ -47,6 +69,7 @@
this.renderReplace(this.content().querySelector('table'), [
element('thead', [
+ this._rangeSelectorLabels ? this._rangeSelectorLabels.map(function (label) { return element('th', label) }) : [],
this.heading(),
element('th', 'Result'),
repositoryList.map(function (repository) { return element('th', repository.label()); }),
Added: trunk/Websites/perf.webkit.org/public/v3/components/test-group-form.js (0 => 196708)
--- trunk/Websites/perf.webkit.org/public/v3/components/test-group-form.js (rev 0)
+++ trunk/Websites/perf.webkit.org/public/v3/components/test-group-form.js 2016-02-17 20:18:05 UTC (rev 196708)
@@ -0,0 +1,55 @@
+
+class TestGroupForm extends ComponentBase {
+
+ constructor()
+ {
+ super('test-group-form');
+ this._startCallback = null;
+ this._repetitionCountControl = this.content().querySelector('.repetition-count');
+ this._repetitionCountControl.value = 4;
+ this._buttonControl = this.content().querySelector('button');
+ this._nameControl = this.content().querySelector('.name');
+ this.content().querySelector('form')._onsubmit_ = this._submitted.bind(this);
+ }
+
+ setStartCallback(callback) { this._startCallback = callback; }
+ setNeedsName(needsName) { this._nameControl.style.display = needsName ? null : 'none'; }
+ setDisabled(disabled) { this._buttonControl.disabled = disabled; }
+
+ setLabel(label) { this._buttonControl.textContent = label; }
+ setRepetitionCount(count) { this._repetitionCountControl.value = count; }
+
+ _submitted(event)
+ {
+ event.preventDefault();
+ if (this._startCallback)
+ this._startCallback(this._nameControl.value, this._repetitionCountControl.value);
+ }
+
+ static htmlTemplate()
+ {
+ return `
+ <form>
+ <button type="submit">Start A/B testing</button>
+ <input class="name" type="text">
+ with
+ <select class="repetition-count">
+ <option>1</option>
+ <option>2</option>
+ <option>3</option>
+ <option>4</option>
+ <option>5</option>
+ <option>6</option>
+ <option>7</option>
+ <option>8</option>
+ <option>9</option>
+ <option>10</option>
+ </select>
+ iterations per set
+ </form>
+ `;
+ }
+
+}
+
+ComponentBase.defineElement('test-group-form', TestGroupForm);
Modified: trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js (196707 => 196708)
--- trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js 2016-02-17 20:11:05 UTC (rev 196707)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js 2016-02-17 20:18:05 UTC (rev 196708)
@@ -8,6 +8,22 @@
setPage(page) { this._page = page; }
router() { return this._page.router(); }
+
+ _mainSelectionDidChange(selection, didEndDrag)
+ {
+ super._mainSelectionDidChange(selection);
+ if (didEndDrag)
+ this._page._chartSelectionDidChange();
+ }
+
+ selectedPoints()
+ {
+ var selection = this._mainChart ? this._mainChart.currentSelection() : null;
+ if (!selection)
+ return null;
+
+ return this._mainChart.sampledDataBetween('current', selection[0], selection[1]);
+ }
}
ComponentBase.defineElement('analysis-task-chart-pane', AnalysisTaskChartPane);
@@ -33,6 +49,8 @@
this._chartPane.setPage(this);
this._analysisResultsViewer = this.content().querySelector('analysis-results-viewer').component();
this._analysisResultsViewer.setTestGroupCallback(this._showTestGroup.bind(this));
+ this._analysisResultsViewer.setRangeSelectorLabels(['A', 'B']);
+ this._analysisResultsViewer.setRangeSelectorCallback(this._selectedRowInAnalysisResultsViewer.bind(this));
this._testGroupResultsTable = this.content().querySelector('test-group-results-table').component();
this._taskNameLabel = this.content().querySelector('.analysis-task-name editable-text').component();
this._taskNameLabel.setStartedEditingCallback(this._didStartEditingTaskName.bind(this));
@@ -45,7 +63,16 @@
this._bugTrackerControl = this.content().querySelector('.bug-tracker-control');
this._bugNumberControl = this.content().querySelector('.bug-number-control');
- this.content().querySelector('.test-group-retry-form')._onsubmit_ = this._retryCurrentTestGroup.bind(this);
+ this._newTestGroupFormForChart = this.content().querySelector('.overview-chart test-group-form').component();
+ this._newTestGroupFormForChart.setStartCallback(this._createNewTestGroupFromChart.bind(this));
+
+ this._newTestGroupFormForViewer = this.content().querySelector('.analysis-results-view test-group-form').component();
+ this._newTestGroupFormForViewer.setStartCallback(this._createNewTestGroupFromViewer.bind(this));
+ this._selectedRowInAnalysisResultsViewer();
+
+ this._retryForm = this.content().querySelector('.test-group-retry-form').firstChild.component();
+ this._retryForm.setStartCallback(this._retryCurrentTestGroup.bind(this));
+ this._retryForm.setNeedsName(false);
}
title() { return this._task ? this._task.label() : 'Analysis Task'; }
@@ -228,6 +255,8 @@
}));
}
+ this._newTestGroupFormForChart.render();
+
this._analysisResultsViewer.setCurrentTestGroup(this._currentTestGroup);
this._analysisResultsViewer.render();
@@ -297,16 +326,14 @@
this._chartPane.setMainSelection([startTime, endTime]);
}
- this.content().querySelector('.test-group-retry-button').textContent = this._currentTestGroup ? 'Retry' : 'Confirm the change';
+ this._retryForm.setLabel('Retry');
+ if (this._currentTestGroup)
+ this._retryForm.setRepetitionCount(this._currentTestGroup.repetitionCount());
+ this._retryForm.element().style.display = this._currentTestGroup ? null : 'none';
- var repetitionCount = this._currentTestGroup ? this._currentTestGroup.repetitionCount() : 4;
- var repetitionCountController = this.content().querySelector('.test-group-retry-repetition-count');
- repetitionCountController.value = repetitionCount;
-
this._renderedCurrentTestGroup = this._currentTestGroup;
}
-
- this.content().querySelector('.test-group-retry-button').disabled = !(this._currentTestGroup || this._startPoint);
+ this._retryForm.render();
}
_showTestGroup(testGroup)
@@ -390,26 +417,49 @@
});
}
- _retryCurrentTestGroup(event)
+ _retryCurrentTestGroup(unusedName, repetitionCount)
{
- event.preventDefault();
- console.assert(this._currentTestGroup || this._startPoint);
+ console.assert(this._currentTestGroup);
+ var testGroup = this._currentTestGroup;
+ var newName = this._createRetryNameForTestGroup(testGroup.name());
+ var rootSetList = testGroup.requestedRootSets();
+ var rootSetLabels = rootSetList.map(function (rootSet) { return testGroup.labelForRootSet(rootSet); });
+ return this._createTestGroupAfterVerifyingRootSetList(newName, repetitionCount, rootSetList, rootSetLabels);
+ }
- var testGroupName;
- var rootSetList;
- var rootSetLabels;
+ _chartSelectionDidChange()
+ {
+ var points = this._chartPane.selectedPoints();
+ this._newTestGroupFormForChart.setDisabled(!points || points.length < 2);
+ }
- if (this._currentTestGroup) {
- var testGroup = this._currentTestGroup;
- testGroupName = this._createRetryNameForTestGroup(testGroup.name());
- rootSetList = testGroup.requestedRootSets();
- rootSetLabels = rootSetList.map(function (rootSet) { return testGroup.labelForRootSet(rootSet); });
- } else {
- testGroupName = 'Confirming the change';
- rootSetList = [this._startPoint.rootSet(), this._endPoint.rootSet()];
- rootSetLabels = ['Point 0', `Point ${this._endPoint.seriesIndex - this._startPoint.seriesIndex}`];
- }
+ _createNewTestGroupFromChart(name, repetitionCount)
+ {
+ var points = this._chartPane.selectedPoints();
+ console.assert(points && points.length >= 2);
+ return this._createTestGroupAfterVerifyingRootSetList(name, repetitionCount,
+ [points[0].rootSet(), points[points.length - 1].rootSet()], ['A', 'B']);
+ }
+ _selectedRowInAnalysisResultsViewer()
+ {
+ var selectedRange = this._analysisResultsViewer.selectedRange();
+ this._newTestGroupFormForViewer.setDisabled(!selectedRange['A'] || !selectedRange['B']);
+ }
+
+ _createNewTestGroupFromViewer(name, repetitionCount)
+ {
+ var selectedRange = this._analysisResultsViewer.selectedRange();
+ console.assert(selectedRange && selectedRange['A'] && selectedRange['B']);
+ return this._createTestGroupAfterVerifyingRootSetList(name, repetitionCount,
+ [selectedRange['A'].rootSet(), selectedRange['B'].rootSet()], ['A', 'B']);
+ }
+
+ _createTestGroupAfterVerifyingRootSetList(testGroupName, repetitionCount, rootSetList, rootSetLabels)
+ {
+ if (this._hasDuplicateTestGroupName(testGroupName))
+ alert(`There is already a test group named "${testGroupName}"`);
+
var rootSetsByName = {};
for (var repository of rootSetList[0].repositories())
rootSetsByName[repository.name()] = [];
@@ -434,8 +484,6 @@
}
}
- var repetitionCount = this.content().querySelector('.test-group-retry-repetition-count').value;
-
TestGroup.createAndRefetchTestGroups(this._task, testGroupName, repetitionCount, rootSetsByName)
.then(this._didFetchTestGroups.bind(this), function (error) {
alert('Failed to create a new test group: ' + error);
@@ -507,31 +555,17 @@
</div>
<section class="overview-chart">
<analysis-task-chart-pane></analysis-task-chart-pane>
+ <div class="new-test-group-form"><test-group-form></test-group-form></div>
</section>
<section class="analysis-results-view">
<analysis-results-viewer></analysis-results-viewer>
+ <div class="new-test-group-form"><test-group-form></test-group-form></div>
</section>
<section class="test-group-view">
<ul class="test-group-list"></ul>
<div class="test-group-details">
<test-group-results-table></test-group-results-table>
- <form class="test-group-retry-form">
- <button class="test-group-retry-button" type="submit">Retry</button>
- with
- <select class="test-group-retry-repetition-count">
- <option>1</option>
- <option>2</option>
- <option>3</option>
- <option>4</option>
- <option>5</option>
- <option>6</option>
- <option>7</option>
- <option>8</option>
- <option>9</option>
- <option>10</option>
- </select>
- iterations per set
- </form>
+ <div class="test-group-retry-form"><test-group-form></test-group-form></div>
</div>
</section>
</div>
@@ -617,7 +651,7 @@
}
.analysis-results-view {
- margin: 1rem;
+ margin: 2.5rem 1rem;
}
.test-configuration h3 {
@@ -641,6 +675,7 @@
margin: 0;
}
+ .new-test-group-form,
.test-group-retry-form {
padding: 0;
margin: 0.5rem;