| import './index'; |
| |
| import { $, $$ } from 'common-sk/modules/dom'; |
| import fetchMock from 'fetch-mock'; |
| import { |
| eventPromise, |
| eventSequencePromise, |
| expectQueryStringToEqual, |
| setQueryString, |
| setUpElementUnderTest, |
| } from '../../../infra-sk/modules/test_util'; |
| import { IgnoresPageSk } from './ignores-page-sk'; |
| import { ParamSet } from '../rpc_types'; |
| import { fakeNow, ignoreRules_10 } from './test_data'; |
| import { CheckOrRadio } from 'elements-sk/checkbox-sk/checkbox-sk'; |
| import { EditIgnoreRuleSk } from '../edit-ignore-rule-sk/edit-ignore-rule-sk'; |
| import { expect } from 'chai'; |
| |
| describe('ignores-page-sk', () => { |
| const newInstance = setUpElementUnderTest<IgnoresPageSk>('ignores-page-sk'); |
| |
| const regularNow = Date.now; |
| let ignoresPageSk: IgnoresPageSk; |
| |
| beforeEach(async () => { |
| // Clear out any query params we might have to not mess with our current state. |
| setQueryString(''); |
| // These will get called on page load. |
| fetchMock.get('/json/v1/ignores?counts=1', ignoreRules_10); |
| // We only need a few params to make sure the edit-ignore-rule-dialog works properly and it |
| // does not matter really what they are, so we use a small subset of actual params. |
| const someParams: ParamSet = { |
| alpha_type: ['Opaque', 'Premul'], |
| arch: ['arm', 'arm64', 'x86', 'x86_64'], |
| }; |
| fetchMock.get('/json/v1/paramset', someParams); |
| // set the time to our mocked Now |
| Date.now = () => fakeNow; |
| |
| const event = eventPromise('end-task'); |
| ignoresPageSk = newInstance(); |
| await event; |
| }); |
| |
| afterEach(() => { |
| expect(fetchMock.done()).to.be.true; // All mock RPCs called at least once. |
| |
| // Completely remove the mocking which allows each test |
| // to be able to mess with the mocked routes w/o impacting other tests. |
| fetchMock.reset(); |
| // reset the time |
| Date.now = regularNow; |
| }); |
| |
| describe('html layout', () => { |
| it('should make a table with 10 rows in the body', () => { |
| const rows = $('table tbody tr', ignoresPageSk); |
| expect(rows).to.have.length(10); |
| }); |
| |
| it('creates links to test the filter', () => { |
| const rows = $('table tbody tr', ignoresPageSk); |
| const queryLink = $$<HTMLAnchorElement>('.query a', rows[9])!; |
| 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', |
| ); |
| }); |
| |
| it('has some expired and some not expired rules', () => { |
| const rows = $('table tbody tr', ignoresPageSk); |
| const firstRow = rows[0]; |
| expect(firstRow.className).to.contain('expired'); |
| let timeBox = $$<HTMLElement>('.expired', firstRow)!; |
| expect(timeBox.innerText).to.contain('Expired'); |
| |
| const fourthRow = rows[4]; |
| expect(fourthRow.className).to.not.contain('expired'); |
| timeBox = $$<HTMLElement>('.expired', fourthRow)!; |
| expect(timeBox).to.be.null; |
| }); |
| }); // end describe('html layout') |
| |
| describe('interaction', () => { |
| it('toggles between counting traces with untriaged digests and all traces', () => { |
| let checkbox = findUntriagedDigestsCheckbox(ignoresPageSk); |
| expect(checkbox.checked).to.be.true; |
| expect(findMatchesTextForRow(ignoresPageSk, 2)).to.contain('0 / 4'); |
| expectQueryStringToEqual(''); |
| |
| clickUntriagedDigestsCheckbox(ignoresPageSk); |
| |
| checkbox = findUntriagedDigestsCheckbox(ignoresPageSk); |
| expect(checkbox.checked).to.be.false; |
| expect(findMatchesTextForRow(ignoresPageSk, 2)).to.contain('6 / 10'); |
| expectQueryStringToEqual('?count_all=true'); |
| |
| clickUntriagedDigestsCheckbox(ignoresPageSk); |
| |
| checkbox = findUntriagedDigestsCheckbox(ignoresPageSk); |
| expect(checkbox.checked).to.be.true; |
| expect(findMatchesTextForRow(ignoresPageSk, 2)).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(ignoresPageSk); |
| expect(checkbox.checked).to.be.false; |
| |
| // And now return to the default view. |
| await goForward(); |
| checkbox = findUntriagedDigestsCheckbox(ignoresPageSk); |
| expect(checkbox.checked).to.be.true; |
| }); |
| |
| it('prompts "are you sure" before deleting an ignore rule', () => { |
| const dialog = findConfirmDeleteDialog(ignoresPageSk); |
| expect(dialog.hasAttribute('open')).to.be.false; |
| |
| const del = findDeleteForRow(ignoresPageSk, 2); |
| del.click(); |
| |
| expect(dialog.hasAttribute('open')).to.be.true; |
| const msg = $$<HTMLElement>('.message', dialog)!; |
| expect(msg.innerText).to.contain('Are you sure you want to delete'); |
| }); |
| |
| it('deletes an existing ignore rule', async () => { |
| const idOfThirdRule = '7589748925671328782'; |
| const del = findDeleteForRow(ignoresPageSk, 2); |
| del.click(); |
| |
| fetchMock.post(`/json/v1/ignores/del/${idOfThirdRule}`, '{"deleted": "true"}'); |
| const p = eventPromise('end-task'); |
| clickConfirmDeleteButton(ignoresPageSk); |
| await p; |
| }); |
| |
| it('adds a new ignore rule', async () => { |
| const dialog = findCreateEditIgnoreRuleDialog(ignoresPageSk); |
| expect(dialog.hasAttribute('open')).to.be.false; |
| |
| clickCreateIgnoreRuleButton(ignoresPageSk); |
| expect(dialog.hasAttribute('open')).to.be.true; |
| |
| setIgnoreRuleProperties(ignoresPageSk, 'alpha=beta&gamma=delta', |
| '2020-02-01T00:00:00Z', 'see skia:9525'); |
| fetchMock.post('/json/v1/ignores/add/', (url, opts) => { |
| expect(opts.body).to.equal( |
| '{"duration":"5w","filter":"alpha=beta&gamma=delta","note":"see skia:9525"}', |
| ); |
| return '{"created": "true"}'; |
| }); |
| |
| const createBtn = findConfirmSaveIgnoreRuleButton(ignoresPageSk); |
| expect(createBtn.innerText).to.equal('Create'); |
| |
| const p = eventPromise('end-task'); |
| createBtn.click(); |
| await p; |
| |
| expect(dialog.hasAttribute('open')).to.be.false; |
| }); |
| |
| it('updates an existing ignore rule', async () => { |
| const idOfThirdRule = '7589748925671328782'; |
| const edit = findUpdateForRow(ignoresPageSk, 2); |
| edit.click(); |
| |
| const dialog = findCreateEditIgnoreRuleDialog(ignoresPageSk); |
| expect(dialog.hasAttribute('open')).to.be.true; |
| |
| setIgnoreRuleProperties(ignoresPageSk, 'alpha=beta&gamma=delta', |
| '2020-02-01T00:00:00Z', 'see skia:9525'); |
| fetchMock.post(`/json/v1/ignores/save/${idOfThirdRule}`, (url, opts) => { |
| expect(opts.body).to.equal( |
| '{"duration":"5w","filter":"alpha=beta&gamma=delta","note":"see skia:9525"}', |
| ); |
| return '{"created": "true"}'; |
| }); |
| |
| const updateBtn = findConfirmSaveIgnoreRuleButton(ignoresPageSk); |
| expect(updateBtn.innerText).to.equal('Update'); |
| |
| const p = eventPromise('end-task'); |
| updateBtn.click(); |
| await p; |
| }); |
| |
| it('does not create a new ignore rule if form is empty', async () => { |
| clickCreateIgnoreRuleButton(ignoresPageSk); |
| |
| // The rule at this point has no fields set, so this should show an error. |
| findConfirmSaveIgnoreRuleButton(ignoresPageSk).click(); |
| const dialog = findCreateEditIgnoreRuleDialog(ignoresPageSk); |
| expect(dialog.hasAttribute('open')).to.be.true; |
| }); |
| }); |
| }); |
| |
| function findUntriagedDigestsCheckbox(ele: IgnoresPageSk): CheckOrRadio { |
| return $$<CheckOrRadio>('.controls checkbox-sk', ele)!; |
| } |
| |
| function findMatchesTextForRow(ele: IgnoresPageSk, n: number): string { |
| const row = $<HTMLTableRowElement>('table tbody tr', ele)[n]; |
| const cell = $$<HTMLTableDataCellElement>('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(ele: IgnoresPageSk, n: number): HTMLElement { |
| const row = $('table tbody tr', ele)[n]; |
| return $$<HTMLElement>('.mutate-icons delete-icon-sk', row)!; |
| } |
| |
| function findUpdateForRow(ele: IgnoresPageSk, n: number): HTMLElement { |
| const row = $('table tbody tr', ele)[n]; |
| return $$<HTMLElement>('.mutate-icons mode-edit-icon-sk', row)!; |
| } |
| |
| function findConfirmDeleteDialog(ele: IgnoresPageSk): HTMLDialogElement { |
| return $$<HTMLDialogElement>('confirm-dialog-sk dialog', ele)!; |
| } |
| |
| function findCreateEditIgnoreRuleDialog(ele: IgnoresPageSk): HTMLDialogElement { |
| return $$<HTMLDialogElement>('dialog#edit-ignore-rule-dialog', ele)!; |
| } |
| |
| function findConfirmSaveIgnoreRuleButton(ele: IgnoresPageSk): HTMLButtonElement { |
| return $$<HTMLButtonElement>('#edit-ignore-rule-dialog button#ok', ele)!; |
| } |
| |
| function clickUntriagedDigestsCheckbox(ele: IgnoresPageSk) { |
| // 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 = $$<HTMLInputElement>('input[type="checkbox"]', findUntriagedDigestsCheckbox(ele))!; |
| input.click(); |
| } |
| |
| function clickConfirmDeleteButton(ele: IgnoresPageSk) { |
| $$<HTMLButtonElement>('button.confirm', findConfirmDeleteDialog(ele))!.click(); |
| } |
| |
| function clickCreateIgnoreRuleButton(ele: IgnoresPageSk) { |
| $$<HTMLButtonElement>('.controls button.create', ele)!.click(); |
| } |
| |
| function setIgnoreRuleProperties(ele: IgnoresPageSk, query: string, expires: string, note: string) { |
| const editor = $$<EditIgnoreRuleSk>('edit-ignore-rule-sk', findCreateEditIgnoreRuleDialog(ele))!; |
| editor.query = query; |
| editor.expires = expires; |
| editor.note = note; |
| } |
| |
| async function goBack() { |
| // Wait for /json/v1/ignores and /json/v1/paramset RPCs to complete. |
| const events = eventSequencePromise(['end-task', 'end-task']); |
| history.back(); |
| await events; |
| } |
| |
| async function goForward() { |
| // Wait for /json/v1/ignores and /json/v1/paramset RPCs to complete. |
| const events = eventSequencePromise(['end-task', 'end-task']); |
| history.forward(); |
| await events; |
| } |