Skip to content

Commit

Permalink
test: datagrid tests [INFENG-687] (#9400)
Browse files Browse the repository at this point in the history
  • Loading branch information
JComins000 committed May 31, 2024
1 parent 8a9839a commit c49eeea
Show file tree
Hide file tree
Showing 19 changed files with 343 additions and 92 deletions.
3 changes: 2 additions & 1 deletion webui/react/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module.exports = {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
},
ignorePatterns: ['**/src/services/stream/wire.ts', '**/src/e2e/playwright-report/**'],
overrides: [
{
files: ['**/*.ts', '**/*.tsx'],
Expand All @@ -42,11 +43,11 @@ module.exports = {
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 2018,
project: true,
sourceType: 'module',
},
plugins: ['import', 'jsdoc', 'react', 'react-hooks', 'sort-keys-fix'],
root: true,
ignorePatterns: ['**/src/services/stream/wire.ts', '**/src/e2e/playwright-report/**'],
rules: {
// Can disagree with @typescript-eslint/member-ordering.
'@typescript-eslint/adjacent-overload-signatures': 'off',
Expand Down
13 changes: 5 additions & 8 deletions webui/react/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
import { defineConfig, devices } from '@playwright/test';
import * as dotenv from 'dotenv';

import {
defineConfig,
devices,
} from '@playwright/test';

dotenv.config();

const serverAddess = process.env.PW_SERVER_ADDRESS;
Expand All @@ -28,7 +24,7 @@ export default defineConfig({
fullyParallel: !!process.env.CI,

/* https://playwright.dev/docs/test-timeouts#global-timeout */
globalTimeout: process.env.PWDEBUG ? 0 : 900_000,
globalTimeout: process.env.PWDEBUG ? 0 : 1_800_000,
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
outputDir: './src/e2e/test-results',
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
Expand All @@ -42,7 +38,7 @@ export default defineConfig({

{
name: 'firefox',
use: { ...devices['Desktop Firefox']},
use: { ...devices['Desktop Firefox'] },
},

{
Expand Down Expand Up @@ -103,5 +99,6 @@ export default defineConfig({
reuseExistingServer: !process.env.CI,
},

workers: process.env.CI ? 4 : 1,
// workers: process.env.CI ? 4 : 1,
workers: 1,
});
5 changes: 4 additions & 1 deletion webui/react/src/components/FilterForm/TableFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ const TableFilter = ({
}
open={isOpenFilter}
onOpenChange={handleIsOpenFilterChange}>
<Button hideChildren={isMobile} icon={<Icon decorative name="filter" />}>
<Button
data-test-component="tableFilter"
hideChildren={isMobile}
icon={<Icon decorative name="filter" />}>
Filter {fieldCount > 0 && `(${fieldCount})`}
</Button>
</Dropdown>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ const FilterField = ({
);

return (
<div className={css.base} ref={(node) => drop(node)}>
<div className={css.base} data-test-component="FilterField" ref={(node) => drop(node)}>
<ConjunctionContainer
conjunction={conjunction}
index={index}
Expand Down
12 changes: 10 additions & 2 deletions webui/react/src/components/TableActionBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -424,17 +424,24 @@ const TableActionBar: React.FC<Props> = ({
<OptionsMenu rowHeight={rowHeight} onRowHeightChange={onRowHeightChange} />
{selectedExperimentIds.length > 0 && (
<Dropdown menu={editMenuItems} onClick={handleAction}>
<Button hideChildren={isMobile}>Actions</Button>
<Button data-test="actionsDropdown" hideChildren={isMobile}>
Actions
</Button>
</Dropdown>
)}
{!isMobile && <span className={css.expNum}>{selectionLabel}</span>}
{!isMobile && (
<span className={css.expNum} data-test="expNum">
{selectionLabel}
</span>
)}
</Row>
</Column>
<Column align="right">
<Row>
{heatmapBtnVisible && (
<Tooltip content={'Toggle Metric Heatmap'}>
<Button
data-test="heatmapToggle"
icon={<Icon name="heatmap" title="heatmap" />}
type={heatmapOn ? 'primary' : 'default'}
onClick={() => onHeatmapToggle?.(heatmapOn ?? false)}
Expand All @@ -443,6 +450,7 @@ const TableActionBar: React.FC<Props> = ({
)}
{!!onComparisonViewToggle && (
<Button
data-test="compare"
hideChildren={isMobile}
icon={<Icon name={compareViewOn ? 'panel-on' : 'panel'} title="compare" />}
onClick={onComparisonViewToggle}>
Expand Down
2 changes: 1 addition & 1 deletion webui/react/src/e2e/models/BaseComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export class BaseComponent implements ComponentBasics {
* The playwright Locator that represents this model
*/
get pwLocator(): Locator {
// Treat the locator as a readonly, but only after we've created it
if (this._locator === undefined) {
// Treat the locator as a readonly, but only after we've created it
this._locator = this._parent.pwLocator.locator(this.selector);
}
return this._locator;
Expand Down
11 changes: 11 additions & 0 deletions webui/react/src/e2e/models/BasePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,15 @@ export abstract class BasePage implements ModelBasics {
}
return this;
}

/**
* Logs a string to the browser console. This string will show in playwright's trace.
* @param {string} s - the string to log to the browser console
*/
async browserLog(s: string): Promise<void> {
await this._page.evaluate((s: string) => {
// eslint-disable-next-line no-console
console.log(s);
}, s);
}
}
36 changes: 22 additions & 14 deletions webui/react/src/e2e/models/ant/Dropdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,26 +64,34 @@ export class Dropdown extends BaseComponent {
* Returns a representation of a dropdown menu item with the specified id.
* @param {string} id - the id of the menu item
*/
menuItem(id: string): BaseComponent {
return new BaseComponent({
menuItem(id: string): menuItem {
return new menuItem({
parent: this,
selector: `li.ant-dropdown-menu-item[data-menu-id$="${id}"]`,
});
}

/**
* Returns a representation of a dropdown menu item. Since order is not
* guaranteed, make sure to verify the contents of the menu item.
*
* It's better to prefer the menuItem method and to fall back on this.
* For example, there are some dropdowns which populate with dynamic data. Or
* maybe we could enter some sort of search filter and select the first item.
* @param {number} n - the number of the menu item
* Selects a menu item with the specified id.
* @param {string} id - id of the item to select
*/
nthMenuItem(n: number): BaseComponent {
return new BaseComponent({
parent: this,
selector: `li.ant-dropdown-menu-item:nth-of-type(${n})`,
});
async selectMenuOption(id: string): Promise<void> {
await this.open();
await this.menuItem(id).pwLocator.click();
await this.pwLocator.waitFor({ state: 'hidden' });
}
}

class menuItem extends BaseComponent {
override readonly _parent: Dropdown;
constructor({ parent, selector }: { parent: Dropdown; selector: string }) {
super({ parent, selector });
this._parent = parent;
}

async select(clickArgs = {}): Promise<void> {
await this._parent.open();
await this.pwLocator.click(clickArgs);
await this._parent.pwLocator.waitFor({ state: 'hidden' });
}
}
11 changes: 11 additions & 0 deletions webui/react/src/e2e/models/ant/Popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,15 @@ export class Popover extends BaseComponent {
await this.openMethod();
return this;
}

/**
* Closes the popover.
*/
async close(): Promise<void> {
// [ET-284] Popover click handle doesn't work unless we wait
await this.root._page.waitForTimeout(500);
// [ET-something] Popover has no close button and doesn't respect Escape key
await this.root.nav.sidebar.header.pwLocator.click();
await this.pwLocator.waitFor({ state: 'hidden' });
}
}
15 changes: 2 additions & 13 deletions webui/react/src/e2e/models/ant/Select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,7 @@ export class Select extends BaseComponent {
menuItem(title: string): BaseComponent {
return new BaseComponent({
parent: this._menu,
selector: `div.ant-select-item[title$="${title}"]`,
});
}

/**
* Returns a representation of a select dropdown menu item. Since order is not
* guaranteed, make sure to verify the contents of the menu item.
* @param {number} n - the number of the menu item
*/
nthMenuItem(n: number): BaseComponent {
return new BaseComponent({
parent: this._menu,
selector: `div.ant-select-item:nth-of-type(${n})`,
selector: `div.ant-select-item[title="${title}"]`,
});
}

Expand All @@ -98,6 +86,7 @@ export class Select extends BaseComponent {
async selectMenuOption(title: string): Promise<void> {
await this.openMenu();
await this.menuItem(title).pwLocator.click();
await this._menu.pwLocator.waitFor({ state: 'hidden' });
}
}

Expand Down
24 changes: 21 additions & 3 deletions webui/react/src/e2e/models/components/ColumnPickerMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class ColumnPickerMenu extends DropdownContent {
}

class ColumnPickerTab extends NamedComponent {
readonly defaultSelector = '[data-test-component="columnPickerTab"]';
readonly defaultSelector = '[data-test-component="columnPickerTab"]:visible';
readonly search = new BaseComponent({ parent: this, selector: '[data-test="search"]' });
readonly columns = new List({ parent: this });
readonly noResults = new Message({ parent: this.columns });
Expand All @@ -39,20 +39,38 @@ class ColumnPickerTab extends NamedComponent {

class List extends NamedComponent {
readonly defaultSelector = '[data-test="columns"]';
readonly rows = new Row({ parent: this, selector: '[data-test="row"]' });
readonly rows = new Row({ parent: this });

/**
* Returns a representation of a list row with the specified testid.
* @param {string} [testid] - the testid of the tab, generally the name
*/
public listItem(testid: string): Row {
return new Row({
attachment: `[data-test-id="${testid}"]`,
attachment: `[${this.rows.keyAttribute}="${testid}"]`,
parent: this,
});
}

/**
* Returns a list of keys associated with attributes from rows from the entire table.
*/
async allRowKeys(): Promise<string[]> {
const { pwLocator, keyAttribute } = this.rows;
const rows = await pwLocator.all();
return Promise.all(
rows.map(async (row) => {
return (
(await row.getAttribute(keyAttribute)) ||
Promise.reject(new Error(`All rows should have the attribute ${keyAttribute}`))
);
}),
);
}
}

class Row extends NamedComponent {
readonly defaultSelector = '[data-test="row"]';
readonly keyAttribute = 'data-test-id';
readonly checkbox = new BaseComponent({ parent: this, selector: '[data-test="checkbox"]' });
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BaseComponent, NamedComponent } from 'e2e/models/BaseComponent';
import { BaseComponent, BaseReactFragment } from 'e2e/models/BaseComponent';
import { Select } from 'e2e/models/hew/Select';

/**
Expand All @@ -8,8 +8,7 @@ import { Select } from 'e2e/models/hew/Select';
* @param {CanBeParent} obj.parent - The parent used to locate this ConjunctionContainer
* @param {string} obj.selector - Used instead of `defaultSelector`
*/
export class ConjunctionContainer extends NamedComponent {
readonly defaultSelector = '[data-test-component="ConjunctionContainer"]';
export class ConjunctionContainer extends BaseReactFragment {
readonly where = new BaseComponent({ parent: this, selector: '[data-test="where"]' });
readonly conjunctionSelect = new ConjunctionSelect({
parent: this,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class FilterGroup extends NamedComponent {
this.filterGroups = new FilterGroup({
attachment: this.#notNestedSelector,
level: level + 1,
parent: this.children,
parent: this.#children,
});
}
}
Expand All @@ -36,41 +36,41 @@ export class FilterGroup extends NamedComponent {
*/
private selectorTemplate = (selector: string) => `${selector}${this.#notNestedSelector}`;
readonly conjunctionContainer = new ConjunctionContainer({ parent: this });
readonly groupCard = new BaseComponent({
readonly #groupCard = new BaseComponent({
parent: this,
selector: this.selectorTemplate('[data-test="groupCard"]'),
});
readonly header = new BaseComponent({
parent: this.groupCard,
readonly #header = new BaseComponent({
parent: this.#groupCard,
selector: this.selectorTemplate('[data-test="header"]'),
});
readonly explanation = new BaseComponent({
parent: this.header,
parent: this.#header,
selector: this.selectorTemplate('[data-test="explanation"]'),
});
readonly addDropdown = new AddDropdown({
childNode: new BaseComponent({
parent: this.header,
parent: this.#header,
selector: this.selectorTemplate('[data-test="add"]'),
}),
root: this.root,
});
readonly remove = new BaseComponent({
parent: this.header,
parent: this.#header,
selector: this.selectorTemplate('[data-test="remove"]'),
});
readonly move = new BaseComponent({
parent: this.header,
parent: this.#header,
selector: this.selectorTemplate('[data-test="move"]'),
});
readonly children = new BaseComponent({
parent: this.groupCard,
readonly #children = new BaseComponent({
parent: this.#groupCard,
selector: this.#childrenSelector,
});
readonly filterGroups: FilterGroup | undefined;
readonly filterFields = new FilterField({
attachment: this.#notNestedSelector,
parent: this.children,
parent: this.#children,
});
}

Expand Down
2 changes: 1 addition & 1 deletion webui/react/src/e2e/models/components/MultiSortMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Select } from 'e2e/models/hew/Select';
export class MultiSortMenu extends DropdownContent {
constructor({ parent, root }: { parent: CanBeParent; root: BasePage }) {
super({
childNode: new BaseComponent({ parent, selector: '[data-test-component="multiSortMenu"]' }),
childNode: new BaseComponent({ parent, selector: '[data-testid="sort-menu-button"]' }),
root,
});
}
Expand Down
Loading

0 comments on commit c49eeea

Please sign in to comment.