Files
react-native-devtools-frontend/test/interactions/data_grid_controller/data_grid_controller_test.ts
T
Tim van der Lippe 23283e110a Instrument code with Istanbul for interactions tests
All interaction tests are now lazily instrumented with Istanbul
to obtain code coverage. The interactions tests can be started
with `COVERAGE=1` to obtain coverage. For that, the Mocha hooks
perform the eventual reporting and gathering of data. The instrumentation
is performed in the components server itself.

To make sure that we perform the minimal amount of work required
(since code coverage instrumentation is computationally expensive),
we preload pages to populate the instrumentation cache. Every
interactions tests should preload an example (most likely basic.html)
to populate the cache. Every subsequent test will then use the
already-instrumented code, rather than computing the code over
and over again.

The eventual code coverage is written to /interactions-coverage.
The results will eventually be merged with /karma-coverage
to obtain the union of both unit and interaction tests coverage.

R=aerotwist@chromium.org,jacktfranklin@chromium.org

Bug: 1206705
Change-Id: I5e19b1ecef23d21107210699cb29800556e0415e
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2879986
Commit-Queue: Tim van der Lippe <tvanderlippe@chromium.org>
Reviewed-by: Jack Franklin <jacktfranklin@chromium.org>
2021-05-10 14:44:42 +00:00

195 lines
7.6 KiB
TypeScript

// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {assert} from 'chai';
import type * as puppeteer from 'puppeteer';
import {getDataGrid, getDataGridController, getInnerTextOfDataGridCells} from '../../e2e/helpers/datagrid-helpers.js';
import {$, $$, $textContent, click, waitFor, waitForFunction} from '../../shared/helper.js';
import {describe, it} from '../../shared/mocha-extensions.js';
import {loadComponentDocExample, preloadForCodeCoverage} from '../helpers/shared.js';
import {platform} from '../../shared/helper.js';
function platformSpecificTextForSubMenuEntryItem(text: string): string {
/**
* On Mac the context menu adds the ▶ icon to the sub menu entry points in the
* context menu, but on Linux/Windows it uses an image. So if we're running on
* Mac, we append the search text with the icon, else we do not.
*/
return platform === 'mac' ? `${text}▶` : text;
}
async function assertTopLevelContextMenuItemsText(expectedOptions: string[]): Promise<void> {
const contextMenu = await $('.soft-context-menu');
if (!contextMenu) {
assert.fail('Could not find context menu.');
}
const allItems = await $$('.soft-context-menu > .soft-context-menu-item');
const allItemsText = await Promise.all(allItems.map(item => item.evaluate(div => div.textContent)));
assert.deepEqual(allItemsText, expectedOptions);
}
async function assertSubMenuItemsText(subMenuText: string, expectedOptions: string[]): Promise<void> {
const subMenuEntryItem = await findSubMenuEntryItem(subMenuText);
if (!subMenuEntryItem) {
const allItems = await $$('.soft-context-menu > .soft-context-menu-item');
const allItemsText = await Promise.all(allItems.map(item => item.evaluate(div => div.textContent)));
assert.fail(`Could not find "${subMenuText}" option on context menu. Found items: ${allItemsText.join(' | ')}`);
return;
}
await subMenuEntryItem.hover();
await waitForFunction(async () => {
const menus = await $$('.soft-context-menu');
// Wait for the main menu + the sub menu to be in the DOM
return menus.length === 2;
});
const allMenus = await $$('.soft-context-menu');
// Each submenu is rendered as a separate context menu and is appended to
// the DOM after the main context menu, hence the array index.
const subMenuElement = allMenus[1];
if (!subMenuElement) {
assert.fail(`Could not find sub menu for ${subMenuText}`);
}
const subMenuItems = await $$('.soft-context-menu-item', subMenuElement);
const subMenuItemsText = await Promise.all(subMenuItems.map(item => item.evaluate(div => div.textContent)));
assert.deepEqual(subMenuItemsText, expectedOptions);
}
async function activateContextMenuOnColumnHeader(headerText: string) {
const dataGridController = await getDataGridController();
const dataGrid = await getDataGrid(dataGridController);
const headerCell = await $textContent(headerText, dataGrid);
if (!headerCell) {
assert.fail(`Could not find header cell with text ${headerText}`);
}
await click(headerCell, {
clickOptions: {
button: 'right',
},
});
return headerCell;
}
async function findSubMenuEntryItem(text: string): Promise<puppeteer.ElementHandle<Element>> {
const textToSearchFor = platformSpecificTextForSubMenuEntryItem(text);
const matchingElement = await $textContent(textToSearchFor);
if (!matchingElement) {
const allItems = await $$('.soft-context-menu > .soft-context-menu-item');
const allItemsText = await Promise.all(allItems.map(item => item.evaluate(div => div.textContent)));
assert.fail(`Could not find "${text}" option on context menu. Found items: ${allItemsText.join(' | ')}`);
}
return matchingElement;
}
async function activateContextMenuOnBodyCell(cellText: string) {
const dataGridController = await getDataGridController();
const dataGrid = await getDataGrid(dataGridController);
const headerCell = await $textContent(cellText, dataGrid);
if (!headerCell) {
assert.fail(`Could not find body cell with text ${cellText}`);
}
await click(headerCell, {
clickOptions: {
button: 'right',
},
});
return headerCell;
}
describe('data grid controller', () => {
preloadForCodeCoverage('data_grid_controller/basic.html');
it('lets the user right click on a header to show the context menu', async () => {
await loadComponentDocExample('data_grid_controller/basic.html');
await activateContextMenuOnColumnHeader('Key');
const contextMenu = await $('.soft-context-menu');
assert.isNotNull(contextMenu);
await assertTopLevelContextMenuItemsText(
['Value', platformSpecificTextForSubMenuEntryItem('Sort By'), 'Reset Columns']);
});
it('lists the hideable columns in the context menu and lets the user click to toggle the visibility', async () => {
await loadComponentDocExample('data_grid_controller/basic.html');
await activateContextMenuOnColumnHeader('Key');
const contextMenu = await $('.soft-context-menu');
assert.isNotNull(contextMenu);
const valueColumnOption = await $('[aria-label="Value, checked"]');
if (!valueColumnOption) {
assert.fail('Could not find Value column in context menu.');
}
await click(valueColumnOption);
const dataGrid = await getDataGrid();
await waitForFunction(async () => {
const hiddenCells = await $$('tbody td.hidden', dataGrid);
return hiddenCells.length === 3;
});
const renderedText = await getInnerTextOfDataGridCells(dataGrid, 3);
assert.deepEqual(
[
['Bravo'],
['Alpha'],
['Charlie'],
],
renderedText);
});
it('lists sortable columns in a sub-menu and lets the user click to sort', async () => {
await loadComponentDocExample('data_grid_controller/basic.html');
await activateContextMenuOnColumnHeader('Key');
const contextMenu = await $('.soft-context-menu');
if (!contextMenu) {
assert.fail('Could not find context menu.');
}
const sortBy = await findSubMenuEntryItem('Sort By');
await sortBy.hover();
const keyColumnSort = await waitFor('[aria-label="Key"]');
await keyColumnSort.click();
await waitForFunction(async () => {
const dataGrid = await getDataGrid();
const firstBodyCell = await $('tbody td', dataGrid);
const text = firstBodyCell && await firstBodyCell.evaluate(cell => (cell as HTMLElement).innerText);
return text === 'Alpha';
});
const dataGrid = await getDataGrid();
const renderedText = await getInnerTextOfDataGridCells(dataGrid, 3);
assert.deepEqual(
[
['Alpha', 'Letter A'],
['Bravo', 'Letter B'],
['Charlie', 'Letter C'],
],
renderedText);
});
it('lists sort by and header options when right clicking on a body row', async () => {
await loadComponentDocExample('data_grid_controller/basic.html');
await activateContextMenuOnBodyCell('Bravo');
await assertTopLevelContextMenuItemsText([
platformSpecificTextForSubMenuEntryItem('Sort By'),
platformSpecificTextForSubMenuEntryItem('Header Options'),
]);
await assertSubMenuItemsText('Header Options', ['Value', 'Reset Columns']);
await assertSubMenuItemsText('Sort By', ['Key', 'Value']);
});
it('allows the parent to add custom context menu items', async () => {
await loadComponentDocExample('data_grid_controller/custom-context-menu-items.html');
await activateContextMenuOnBodyCell('Bravo');
await assertTopLevelContextMenuItemsText([
platformSpecificTextForSubMenuEntryItem('Sort By'),
platformSpecificTextForSubMenuEntryItem('Header Options'),
'Hello World',
]);
});
});