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: add e2e tests to context menu #1571

Merged
merged 26 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7af3ee8
wip
ethanalvizo Oct 11, 2023
ff92198
generated all linux snapshots
ethanalvizo Oct 12, 2023
4c4de6e
test: add e2e test to context menu
ethanalvizo Oct 12, 2023
5bccaec
scroll to top of Iris Grid
ethanalvizo Oct 12, 2023
d10e121
fixed tests
ethanalvizo Oct 13, 2023
df52f75
Merge branch 'main' into e2e-context-menu
ethanalvizo Oct 13, 2023
ea002fe
PR feedback
ethanalvizo Oct 13, 2023
976add3
screenshot icon fix
ethanalvizo Oct 13, 2023
ae2d664
wip
ethanalvizo Oct 16, 2023
762b61e
wip
ethanalvizo Oct 17, 2023
baea73d
Merge branch '1477-stuck-to-bottom' into e2e-context-menu
ethanalvizo Oct 17, 2023
0cc5db1
remove scroll
ethanalvizo Oct 17, 2023
5fd344b
fix: stuck to bottom on filter clear
ethanalvizo Oct 18, 2023
c0aca30
remove row hover
ethanalvizo Oct 18, 2023
9f224ef
PR feedback
ethanalvizo Oct 20, 2023
6c2b017
update JSDocs
ethanalvizo Oct 20, 2023
f4d8672
Merge branch '1477-stuck-to-bottom' into e2e-context-menu
ethanalvizo Oct 20, 2023
03ca9c7
fix failure
ethanalvizo Oct 20, 2023
4674639
Merge branch 'main' into e2e-context-menu
ethanalvizo Oct 22, 2023
cec3046
update snapshots for new theme
ethanalvizo Oct 22, 2023
9799e9f
removed key check
ethanalvizo Oct 24, 2023
9395443
Merge branch '1477-stuck-to-bottom' into e2e-context-menu
ethanalvizo Oct 25, 2023
94af812
remove commented code
ethanalvizo Oct 26, 2023
e42f72b
remove commented code
ethanalvizo Oct 27, 2023
390ddfe
Merge branch '1477-stuck-to-bottom' into e2e-context-menu
ethanalvizo Oct 27, 2023
f6b2ceb
Merge branch 'main' into e2e-context-menu
ethanalvizo Oct 27, 2023
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
26 changes: 23 additions & 3 deletions packages/grid/src/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1323,14 +1323,34 @@ class Grid extends PureComponent<GridProps, GridState> {
* @param viewState New state properties to set.
* @param forceUpdate Whether to force an update.
ethanalvizo marked this conversation as resolved.
Show resolved Hide resolved
*/
setViewState(viewState: Partial<GridState>, forceUpdate = false): void {

setViewState(
viewState: Partial<GridState>,
forceUpdate = false,
eventType: WheelEvent | GridKeyboardEvent | null = null
mofojed marked this conversation as resolved.
Show resolved Hide resolved
): void {
if (!this.metrics) throw new Error('metrics not set');

const { isStickyBottom, isStickyRight } = this.props;
const { top, left } = viewState;
const { lastTop, lastLeft } = this.metrics;
let isUserInputDown = false;

if (eventType instanceof WheelEvent) {
isUserInputDown = eventType.deltaY > 0;
} else if (
eventType instanceof KeyboardEvent ||
(eventType && 'nativeEvent' in eventType) // used to catch the case that a synthetic react keyboard event is passed
) {
isUserInputDown =
eventType.key === 'ArrowDown' ||
eventType.key === 'End' ||
eventType.key === 'PageDown';
}
if (top != null) {
this.setState({ isStuckToBottom: isStickyBottom && top >= lastTop });
this.setState({
isStuckToBottom: isStickyBottom && top >= lastTop && isUserInputDown,
});
}
if (left != null) {
this.setState({ isStuckToRight: isStickyRight && left >= lastLeft });
ethanalvizo marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -1978,7 +1998,7 @@ class Grid extends PureComponent<GridProps, GridState> {
}
}

this.setViewState({ top, left, leftOffset, topOffset });
this.setViewState({ top, left, leftOffset, topOffset }, false, event);

event.stopPropagation();
event.preventDefault();
Expand Down
9 changes: 6 additions & 3 deletions packages/grid/src/key-handlers/SelectionKeyHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ class SelectionKeyHandler extends KeyHandler {

grid.moveCursorToPosition(left, cursorRow, isShiftKey, false);

grid.setViewState({ left });
grid.setViewState({ left }, false, event);
} else if (autoSelectColumn && deltaRow !== 0) {
const { lastTop } = grid.metrics;
let { top } = grid.state;
Expand All @@ -194,9 +194,12 @@ class SelectionKeyHandler extends KeyHandler {

grid.moveCursorToPosition(top, cursorColumn, isShiftKey, false);

grid.setViewState({ top });
grid.setViewState({ top }, false, event);
} else {
const { lastLeft, lastTop } = grid.metrics;

grid.moveCursor(deltaColumn, deltaRow, isShiftKey);
grid.setViewState({ left: lastLeft, top: lastTop }, false, event); // need to call setViewState again since moveCursor calls a series of functions that sets view state
ethanalvizo marked this conversation as resolved.
Show resolved Hide resolved
}
}
return true;
Expand Down Expand Up @@ -279,7 +282,7 @@ class SelectionKeyHandler extends KeyHandler {
isShiftKey,
false
);
grid.setViewState({ top: viewportPosition });
grid.setViewState({ top: viewportPosition }, false, e);

return true;
}
Expand Down
12 changes: 6 additions & 6 deletions packages/iris-grid/src/GotoRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -385,23 +385,23 @@ const GotoRow = forwardRef<GotoRowElement, GotoRowProps>(

<div>
<Button
tooltip="Next match"
icon={vsArrowUp}
kind="ghost"
disabled={gotoValue === ''}
onClick={() => {
onGotoValueSubmit(true);
}}
>
<FontAwesomeIcon icon={vsArrowUp} />
</Button>
/>
<Button
tooltip="Previous match"
icon={vsArrowDown}
kind="ghost"
disabled={gotoValue === ''}
onClick={() => {
onGotoValueSubmit(false);
}}
>
<FontAwesomeIcon icon={vsArrowDown} />
</Button>
/>
</div>
{gotoValueError && (
<div className="text-danger">{gotoValueError}</div>
Expand Down
254 changes: 253 additions & 1 deletion tests/context-menu.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,254 @@
import { test, expect } from '@playwright/test';
import { test, expect, Page } from '@playwright/test';
import {
makeTableCommand,
pasteInMonaco,
TableTypes,
waitForLoadingDone,
generateVarName,
} from './utils';

test.describe.configure({ mode: 'serial' });

async function openAdvancedFilters(page: Page) {
await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 20 } });
await page.getByRole('button', { name: 'Advanced Filters' }).click();
}

Comment on lines +12 to +17
Copy link
Member

Choose a reason for hiding this comment

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

This method does open advanced filters, but it just seems to be doing just the first column of the first grid (e.g. you can't pass in a specific column or even a specific grid to open the advanced filter on). I'd rather be more specific with the name of the function (e.g. openFirstAdvancedFilters), or allow passing in the grid locator and an optional offset, e.g.:

/**
 * Open advanced filters for a grid.
 * @param gridLocator Locator to specify which grid to open the advanced filters on
 * @param xOffset Offset of the column to open advanced filters for. Defaults to the first column.
 */
async function openAdvancedFilters(gridLocator: Locator, xOffset = 20) 
  ...
}

const tableName = generateVarName('t');
test.beforeEach(async ({ page }) => {
await page.goto('');

const consoleInput = page.locator('.console-input');

const command = makeTableCommand(tableName, TableTypes.AllTypes);

await pasteInMonaco(consoleInput, command);
await page.keyboard.press('Enter');

// Wait for the panel to show
await expect(page.locator('.iris-grid-panel')).toHaveCount(1);

// Wait until it's done loading
await expect(page.locator('.iris-grid-panel .loading-spinner')).toHaveCount(
0
);

// Model is loaded, need to make sure table data is also loaded
await waitForLoadingDone(page);

const tableOperationsMenu = page.locator(
'data-testid=btn-iris-grid-settings-button-table'
);
await tableOperationsMenu.click();

// Wait for Table Options menu to show
await expect(page.locator('.table-sidebar')).toHaveCount(1);
});
Comment on lines +26 to +52
Copy link
Member

Choose a reason for hiding this comment

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

This is exactly the same setup as in table-operations.spec.ts. We could refactor this into a utility method used by both, but I think in this case I would rather just move these table specific context menu tests to table-operations.spec.ts (since we're really testing the table operations here, we're just accessing them from the context menu).


test.afterEach(async ({ page }) => {
const consoleInput = page.locator('.console-input');
await consoleInput.click();

const command = `del ${tableName}`;
await pasteInMonaco(consoleInput, command);
await page.keyboard.press('Enter');
});

test('toggle column visibility', async ({ page }) => {
await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 20 } });

await page.getByRole('button', { name: 'Hide Column' }).click();
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 20 } });

await page.getByRole('button', { name: 'Show All Columns' }).click();
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();
});

test('quick filter and clear', async ({ page }) => {
await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 20 } });

await page.getByRole('button', { name: 'Quick Filters' }).click();
await page.keyboard.type('a999');

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 20 } });

await page.getByRole('button', { name: 'Clear Column Filter' }).click();

await page
.locator('.iris-grid .grid-wrapper')
.hover({ position: { x: 20, y: 20 } });
await page.mouse.wheel(0, -100);

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();
});

test('advanced filters', async ({ page }) => {
await openAdvancedFilters(page);
await page.getByLabel('Sort String Ascending').click();

await page.getByRole('button', { name: 'Done' }).click();
await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await openAdvancedFilters(page);
await page.getByLabel('Sort String Descending').click();

await page.getByRole('button', { name: 'Done' }).click();
await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await openAdvancedFilters(page);
await page.getByPlaceholder('Enter value').click();
await page.keyboard.type('a999');

await page.getByRole('button', { name: 'Done' }).click();
await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await openAdvancedFilters(page);
await page.getByLabel('Remove Filter').click();

await page.getByRole('button', { name: 'Done' }).click();
await waitForLoadingDone(page);

await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await openAdvancedFilters(page);
await page.getByRole('button', { name: 'Clear', exact: true }).click();

await page.getByRole('button', { name: 'Done' }).click();
await waitForLoadingDone(page);

await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await openAdvancedFilters(page);
await page.getByRole('button', { name: 'Select All' }).click();

await page.getByRole('button', { name: 'Done' }).click();

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();
});

test('sort by', async ({ page }) => {
await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 20 } });

await page.getByRole('button', { name: 'Sort By' }).hover();

await page.getByRole('button', { name: 'String Ascending' }).click();

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 20 } });

await page.getByRole('button', { name: 'Sort By' }).hover();
await page.getByRole('button', { name: 'String Descending' }).click();

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 20 } });

await page.getByRole('button', { name: 'Sort By' }).hover();
await page.getByRole('button', { name: 'Remove Sort' }).click();

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();
});

test('freeze column', async ({ page }) => {
await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 20 } });

await page.getByRole('button', { name: 'Freeze Column' }).click();

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 20 } });

await page.getByRole('button', { name: 'Unfreeze Column' }).click();

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();
});

test('reverse table', async ({ page }) => {
await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 20 } });

await page.getByRole('button', { name: 'Reverse Table' }).click();

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();
});

test('filter by value', async ({ page }) => {
await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 60 } });

await page.getByRole('button', { name: 'Filter by Value' }).hover();
await page.getByRole('button', { name: 'text is exactly' }).click();

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 60 } });
await page.getByRole('button', { name: 'Clear Column Filter' }).click();
});

test('go to', async ({ page }) => {
await page
.locator('.iris-grid .grid-wrapper')
.click({ button: 'right', position: { x: 20, y: 60 } });

await page.getByRole('button', { name: 'Go to' }).click();
await page.getByLabel('filter-type-select').selectOption('Equals');

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await page.locator('[aria-label="Previous match"]').first().click();
await page.locator('[aria-label="Previous match"]').first().click();

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();

await page.locator('[aria-label="Next match"]').first().click();

await waitForLoadingDone(page);
await expect(page.locator('.iris-grid-column')).toHaveScreenshot();
});

test('open custom context menu with another custom context menu open', async ({
page,
Expand All @@ -12,4 +262,6 @@ test('open custom context menu with another custom context menu open', async ({
.getByText('Command History')
.click({ button: 'right', force: true });
await expect(page.getByText('Close', { exact: true })).toHaveCount(1);

await page.getByText('Console').click({ force: true });
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
ethanalvizo marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
ethanalvizo marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading