[gold] Add checkbox and delete rule interaction to ignore page.
Change-Id: I1f239d44c56e57a766402e08ab0c679ba6e322f2
Bug: skia:9525
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/263710
Commit-Queue: Kevin Lubick <kjlubick@google.com>
Reviewed-by: Leandro Lovisolo <lovisolo@google.com>
diff --git a/golden/modules/ignores-page-sk/ignores-page-sk-demo.js b/golden/modules/ignores-page-sk/ignores-page-sk-demo.js
index 5f7224d..ed63be6 100644
--- a/golden/modules/ignores-page-sk/ignores-page-sk-demo.js
+++ b/golden/modules/ignores-page-sk/ignores-page-sk-demo.js
@@ -9,4 +9,5 @@
Date.now = () => fakeNow;
fetchMock.get('/json/ignores?counts=1', delay(ignoreRules_10, 300));
+fetchMock.post('glob:/json/ignores/del/*', delay({}, 600));
fetchMock.catch(404);
diff --git a/golden/modules/ignores-page-sk/ignores-page-sk.js b/golden/modules/ignores-page-sk/ignores-page-sk.js
index 52b824f..b98bbae 100644
--- a/golden/modules/ignores-page-sk/ignores-page-sk.js
+++ b/golden/modules/ignores-page-sk/ignores-page-sk.js
@@ -5,19 +5,22 @@
* Page to view/edit/delete ignore rules.
*/
-import * as human from 'common-sk/modules/human'
+import * as human from 'common-sk/modules/human';
+import { $$ } from 'common-sk/modules/dom';
import { classMap } from 'lit-html/directives/class-map.js';
-import { define } from 'elements-sk/define'
-import { ElementSk } from '../../../infra-sk/modules/ElementSk'
-import { html } from 'lit-html'
-import { jsonOrThrow } from "../../../common-sk/modules/jsonOrThrow";
+import { define } from 'elements-sk/define';
+import { ElementSk } from '../../../infra-sk/modules/ElementSk';
+import { html } from 'lit-html';
+import { jsonOrThrow } from '../../../common-sk/modules/jsonOrThrow';
+import { stateReflector } from 'common-sk/modules/stateReflector';
-import 'elements-sk/checkbox-sk'
-import 'elements-sk/icon/delete-icon-sk'
-import 'elements-sk/icon/info-outline-icon-sk'
-import 'elements-sk/icon/mode-edit-icon-sk'
-import 'elements-sk/styles/buttons'
+import 'elements-sk/checkbox-sk';
+import 'elements-sk/icon/delete-icon-sk';
+import 'elements-sk/icon/info-outline-icon-sk';
+import 'elements-sk/icon/mode-edit-icon-sk';
+import 'elements-sk/styles/buttons';
+import '../../../infra-sk/modules/confirm-dialog-sk';
const template = (ele) => html`
<div class=controls>
@@ -27,6 +30,8 @@
<button @click=${ele._newIgnoreRule}>Create new ignore rule</button>
</div>
+<confirm-dialog-sk></confirm-dialog-sk>
+
<table>
<thead>
<tr>
@@ -61,8 +66,10 @@
<td class=query><a href=${'/list?include=true&query=' + encodeURIComponent(r.query)}
>${humanReadableQuery(r.query)}</a></td>
<td>${r.note || '--'}</td>
- <!--TODO(kjlubick) change this to use the All varients with the checkbox -->
- <td title="these counts are recomputed every few minutes">${r.exclusiveCount} / ${r.count}</td>
+ <td class=matches title="these counts are recomputed every few minutes">
+ ${ele._countAllTraces ? r.exclusiveCountAll : r.exclusiveCount} /
+ ${ele._countAllTraces ? r.countAll: r.count}
+ </td>
<td class=${classMap({expired: isExpired})}>
${isExpired ? 'Expired': human.diffDate(r.expires)}
</td>
@@ -90,8 +97,23 @@
this._rules = [];
this._countAllTraces = false;
- // TODO(kjlubick) state reflector
+ this._stateChanged = stateReflector(
+ /*getState*/() => {
+ return {
+ // provide empty values
+ 'count_all': this._countAllTraces,
+ }
+ }, /*setState*/(newState) => {
+ if (!this._connected) {
+ return;
+ }
+
+ // default values if not specified.
+ this._countAllTraces = newState.count_all || false;
+ this._fetch();
+ this._render();
+ });
// Allows us to abort fetches if we fetch again.
this._fetchController = null;
}
@@ -99,17 +121,20 @@
connectedCallback() {
super.connectedCallback();
this._render();
-
- // Run this on the next microtask to allow for the demo.js to be set up.
- // TODO(kjlubick) remove after state reflector is added.
- setTimeout(()=> {
- this._fetch()
- });
}
_delete(rule) {
- // TODO(kjlubick)
- console.log('delete', rule);
+ const dialog = $$('confirm-dialog-sk', this);
+ dialog.open('Are you sure you want to delete '+
+ 'this ignore rule?').then(() => {
+ this._sendBusy();
+ fetch('/json/ignores/del/'+rule.id, {
+ method: 'POST',
+ }).then(jsonOrThrow).then(() => {
+ this._fetch();
+ this._sendDone();
+ }).catch((e) => this._sendFetchError(e, 'deleting ignore'));
+ });
}
_edit(rule) {
@@ -166,6 +191,7 @@
_toggleCountAll(e) {
e.preventDefault();
this._countAllTraces = !this._countAllTraces;
+ this._stateChanged();
this._render();
}
-});
\ No newline at end of file
+});
diff --git a/golden/modules/ignores-page-sk/ignores-page-sk_test.js b/golden/modules/ignores-page-sk/ignores-page-sk_test.js
index 7ae7055..3099068 100644
--- a/golden/modules/ignores-page-sk/ignores-page-sk_test.js
+++ b/golden/modules/ignores-page-sk/ignores-page-sk_test.js
@@ -1,15 +1,10 @@
import './index.js';
import { $, $$ } from 'common-sk/modules/dom';
-import { eventPromise, expectNoUnmatchedCalls } from "../test_util";
+import { eventPromise, expectNoUnmatchedCalls, expectQueryStringToEqual } from '../test_util';
import { fakeNow, ignoreRules_10 } from './test_data';
import { fetchMock } from 'fetch-mock';
-function setQueryString(q) {
- history.pushState(
- null, '', window.location.origin + window.location.pathname + q);
-}
-
describe('ignores-page-sk', () => {
const regularNow = Date.now;
let ignoresSk;
@@ -55,7 +50,6 @@
it('creates links to test the filter', () => {
const rows = $('table tbody tr', ignoresSk);
const queryLink = $$('.query a', rows[9]);
- expect(queryLink).to.not.be.null;
expect(queryLink.href).to.contain('include=true&query=config%3Dglmsaa4%26cpu_or_gpu_value%3DTegraX1%26name%3Drg1024_green_grapes.svg');
expect(queryLink.textContent).to.equal(`config=glmsaa4\ncpu_or_gpu_value=TegraX1\nname=rg1024_green_grapes.svg`);
});
@@ -65,7 +59,7 @@
const firstRow = rows[0];
expect(firstRow.className).to.contain('expired');
let timeBox = $$('.expired', firstRow);
- expect(timeBox.textContent).to.contain('Expired');
+ expect(timeBox.innerText).to.contain('Expired');
const fourthRow = rows[4];
expect(fourthRow.className).to.not.contain('expired');
@@ -73,4 +67,117 @@
expect(timeBox).to.be.null;
});
}); // end describe('html layout')
-});
\ No newline at end of file
+
+ describe('interaction', () => {
+ it('toggles between counting traces with untriaged digests and all traces', () => {
+ let checkbox = findUntriagedDigestsCheckbox(ignoresSk);
+ expect(checkbox.checked).to.be.true;
+ expect(findMatchesTextForRow(2, ignoresSk)).to.contain('0 / 4');
+ expectQueryStringToEqual('');
+
+ clickUntriagedDigestsCheckbox(ignoresSk);
+
+ checkbox = findUntriagedDigestsCheckbox(ignoresSk);
+ expect(checkbox.checked).to.be.false;
+ expect(findMatchesTextForRow(2, ignoresSk)).to.contain('6 / 10');
+ expectQueryStringToEqual('?count_all=true');
+
+ clickUntriagedDigestsCheckbox(ignoresSk);
+
+ checkbox = findUntriagedDigestsCheckbox(ignoresSk);
+ expect(checkbox.checked).to.be.true;
+ expect(findMatchesTextForRow(2, ignoresSk)).to.contain('0 / 4');
+ expectQueryStringToEqual('');
+ });
+
+ it('responds to back and forward browser buttons', async () => {
+ // Create some mock history so we can use the back button.
+ setQueryString('?count_all=true');
+ setQueryString('');
+
+ // We should go back to the count_all=true setting
+ await goBack();
+ let checkbox = findUntriagedDigestsCheckbox(ignoresSk);
+ expect(checkbox.checked).to.be.false;
+
+ // And now return to the default view.
+ await goForward();
+ checkbox = findUntriagedDigestsCheckbox(ignoresSk);
+ expect(checkbox.checked).to.be.true;
+ });
+
+ it('prompts "are you sure" before deleting an ignore rule', () => {
+ let dialog = findDialog(ignoresSk);
+ expect(dialog.hasAttribute('open')).to.be.false;
+
+ const del = findDeleteForRow(2);
+ del.click();
+
+ expect(dialog.hasAttribute('open')).to.be.true;
+ const msg = $$('.message', dialog);
+ expect(msg.innerText).to.contain('Are you sure you want to delete');
+ });
+
+ it('fires a post request to delete an ignore rule', async () => {
+ const idOfThirdRule = '7589748925671328782';
+ const del = findDeleteForRow(2);
+ del.click();
+
+ fetchMock.post(`/json/ignores/del/${idOfThirdRule}`, '{"deleted": "true"}');
+ const p = eventPromise('end-task');
+ clickOkDialogButton(ignoresSk);
+ await p;
+ });
+ });
+});
+
+function setQueryString(q) {
+ history.pushState(
+ null, '', window.location.origin + window.location.pathname + q);
+}
+
+function findUntriagedDigestsCheckbox(ele) {
+ return $$('.controls checkbox-sk', ele);
+}
+
+function findMatchesTextForRow(n, ele) {
+ const row = $('table tbody tr', ele)[n];
+ const cell = $$('td.matches', row);
+ // condense all whitespace and then trim to avoid the formatting of
+ // the html from impacting the tests too much (e.g. extraneous \n)
+ return cell.innerText;
+}
+
+function findDeleteForRow(n, ele) {
+ const row = $('table tbody tr', ele)[n];
+ return $$('.mutate-icons delete-icon-sk', row);
+}
+
+function findDialog(ele) {
+ return $$('confirm-dialog-sk dialog', ele);
+}
+
+function clickUntriagedDigestsCheckbox(ele) {
+ // We need to click on the input element to accurately mimic a user event. This is
+ // because the checkbox-sk element listens for the click event created by the
+ // internal input event.
+ const input = $$('input[type="checkbox"]', findUntriagedDigestsCheckbox(ele));
+ input.click();
+}
+
+function clickOkDialogButton(ele) {
+ const ok = $$('button.confirm', findDialog(ele));
+ ok.click();
+}
+
+function goBack() {
+ const event = eventPromise('end-task');
+ history.back();
+ return event;
+}
+
+function goForward() {
+ const event = eventPromise('end-task');
+ history.forward();
+ return event;
+}
\ No newline at end of file