blob: ff62988e7780f261e9389524dc8a90b9ef040451 [file] [log] [blame]
/**
* @module modules/bisect-dialog-sk
* @description <h2><code>bisect-dialog-sk</code></h2>
*
* Dialog to show when user wants to bisect with the corresponding plot data from chart-tooltip.
* The bisect logic is only specific to Chrome.
*
* Takes the following inputs:
* Request parameters:
* test path: the full string of trace name from chart-tooltip
* bug_id: Bug ID number, as a string (when submitting the form).
* start_revision: start revision time from input
* end_revision: end revision time from input
* story: the last sub_test name from test path
* Once a validated user submits this dialog, there'll be an attempt to post a bisect
* request. If succesful, user will see the alert dialog popup at the bottom left of the page
* and close the dialog. If unsuccesful, an error message toast will appear.
*
*/
import '../../../elements-sk/modules/select-sk';
import { html } from 'lit/html.js';
import { define } from '../../../elements-sk/modules/define';
import { jsonOrThrow } from '../../../infra-sk/modules/jsonOrThrow';
import { CreateBisectRequest } from '../json';
import { ElementSk } from '../../../infra-sk/modules/ElementSk';
import { upgradeProperty } from '../../../elements-sk/modules/upgradeProperty';
import { errorMessage } from '../../../elements-sk/modules/errorMessage';
import { SpinnerSk } from '../../../elements-sk/modules/spinner-sk/spinner-sk';
import '../../../elements-sk/modules/icons/close-icon-sk';
import '../../../elements-sk/modules/toast-sk';
import '../../../elements-sk/modules/spinner-sk';
import { LoggedIn } from '../../../infra-sk/modules/alogin-sk/alogin-sk';
import { Status as LoginStatus } from '../../../infra-sk/modules/json';
import '../../../infra-sk/modules/alogin-sk/alogin-sk';
import { ToastSk } from '../../../elements-sk/modules/toast-sk/toast-sk';
const STATISTIC_VALUES = ['avg', 'count', 'max', 'min', 'std', 'sum'];
export interface BisectPreloadParams {
testPath?: string | '';
startCommit?: string | '';
endCommit?: string | '';
bugId?: string | '';
story?: string | '';
anomalyId: string | '';
}
export class BisectDialogSk extends ElementSk {
private bugId: string = '';
private startCommit: string = '';
private endCommit: string = '';
private story: string = '';
private user: string = '';
private testPath: string = '';
private anomalyId: string = '';
private _dialog: HTMLDialogElement | null = null;
private spinner: SpinnerSk | null = null;
private _form: HTMLFormElement | null = null;
private bisectButton: HTMLButtonElement | null = null;
private jobId: string = '';
private jobUrl: string = '';
private closeBisectToastButton: HTMLButtonElement | null = null;
private bisectJobToast: ToastSk | null = null;
private static template = (ele: BisectDialogSk) => html`
<dialog id='bisect-dialog'>
<h2>Bisect</h2>
<button id="bisectCloseIcon" @click=${ele.closeBisectDialog}>
<close-icon-sk></close-icon-sk>
</button>
<form id="bisect-form">
<h3>Test Path</h3>
<input id="testpath" type="text" value=${ele.testPath}></input>
<h3>Bug ID</h3>
<input id="bug-id" type="text" value=${ele.bugId}></input>
<h3>Start Commit</h3>
<input id="start-commit" type="text" value=${ele.startCommit}></input>
<h3>End Commit</h3>
<input id="end-commit" type="text" value=${ele.endCommit}></input>
<h3>Story</h3>
<input id="story" type="text" value=${ele.story}></input>
<h3>Patch to apply to the entire job(optional)</h3>
<input id="patch" type="text"></input>
<div class=footer>
<spinner-sk id="dialog-spinner"></spinner-sk>
<button id="submit-button" type="submit" @click=${ele.postBisect}>Bisect</button>
<button id="close-btn" @click=${ele.closeBisectDialog} type="button">Close</button>
</div>
</form>
</dialog>
<toast-sk id="bisect_toast" duration="8000">
<div id="bisect-url">
<a href=${ele.jobUrl} target="_blank">Bisect job created: ${ele.jobId}</a>
</div>
<div class="close-bisect">
<button id="hide-bisect-toast" class="action" type="button">Close</button>
</div>
</toast-sk>
`;
constructor() {
super(BisectDialogSk.template);
}
connectedCallback() {
super.connectedCallback();
upgradeProperty(this, 'preloadInputParameters');
upgradeProperty(this, 'testPath');
upgradeProperty(this, 'startCommit');
upgradeProperty(this, 'endCommit');
upgradeProperty(this, 'bugId');
upgradeProperty(this, 'story');
this._render();
this._dialog = this.querySelector('#bisect-dialog');
this.spinner = this.querySelector('#dialog-spinner');
this.bisectButton = this.querySelector('#submit-button');
this._form = this.querySelector('#bisect-form');
this.closeBisectToastButton = this.querySelector('#hide-bisect-toast');
this.bisectJobToast = this.querySelector('#bisect_toast');
this.closeBisectToastButton!.addEventListener('click', () => this.bisectJobToast?.hide());
LoggedIn()
.then((status: LoginStatus) => {
this.user = status.email;
})
.catch(errorMessage);
}
setBisectInputParams(preloadBisectInputs: BisectPreloadParams): void {
this.testPath = preloadBisectInputs.testPath!;
this.startCommit = preloadBisectInputs.startCommit!;
this.endCommit = preloadBisectInputs.endCommit!;
this.bugId = preloadBisectInputs.bugId!;
this.story = preloadBisectInputs.story!;
this.anomalyId = preloadBisectInputs.anomalyId!;
this._form!.reset();
this._render();
}
open(): void {
this._render();
this._dialog!.showModal();
this.bisectButton!.disabled = false;
}
private closeBisectDialog(): void {
this._dialog!.close();
}
private postBisect(): void {
this.spinner!.active = true;
this.bisectButton!.disabled = true;
if (this.testPath === '') {
this._render();
return;
}
const parameters = this.testPath.split('/');
const test = parameters!.at(3);
const parts: string[] = test!.split(':');
// Pop up the last element from the array if exists
const tail = parts.pop();
let chart: string = test!;
let statistic: string = '';
if (tail !== undefined) {
chart = STATISTIC_VALUES.includes(tail) ? parts!.join('_') : test!;
statistic = STATISTIC_VALUES.includes(tail) ? tail : '';
}
const bugId = document.getElementById('bug-id')! as HTMLInputElement;
const startCommit = document.getElementById('start-commit')! as HTMLInputElement;
const endCommit = document.getElementById('end-commit')! as HTMLInputElement;
this.story = parameters.pop()!;
const patch = document.getElementById('patch')! as HTMLInputElement;
const req: CreateBisectRequest = {
comparison_mode: 'performance',
start_git_hash: startCommit.value,
end_git_hash: endCommit.value,
configuration: this.testPath.split('/')[1],
benchmark: this.testPath.split('/')[2],
story: this.story,
chart: chart,
statistic: statistic,
comparison_magnitude: '',
pin: patch.value,
project: 'chromium',
bug_id: bugId.value,
user: this.user,
alert_ids: '[' + this.anomalyId + ']',
};
fetch('/_/bisect/create', {
method: 'POST',
body: JSON.stringify(req),
headers: {
'Content-Type': 'application/json',
},
})
.then(jsonOrThrow)
.then(async (json) => {
this.bisectButton!.disabled = false;
this.spinner!.active = false;
this.jobId = json.jobId;
this.jobUrl = json.jobUrl;
this.closeBisectDialog();
this.bisectJobToast?.show();
this._render();
})
.catch((msg: any) => {
errorMessage(msg);
this.spinner!.active = false;
this._render();
});
}
/** Clear Bisect Dialog fields */
reset(): void {
this.startCommit = '';
this.endCommit = '';
this.story = '';
this.user = '';
this.testPath = '';
this.anomalyId = '';
this._render();
}
}
define('bisect-dialog-sk', BisectDialogSk);