Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: datagrid tests [INFENG-687] #9400

Merged
merged 18 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/**'],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a formatting change that came from precommit

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
Loading