[Response Ops] Fix flaky maintenance window E2E tests (#188487)

## Summary

Issue: https://github.com/elastic/kibana/issues/187818

Fix flaky test where it's not waiting for the page to load before
searching maintenance window. Also slightly improve the maintenance
window table to not flicker when loading. Instead it should just show
the spinner.

### Checklist
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
This commit is contained in:
Jiawei Wu 2024-09-03 18:31:31 -07:00 committed by GitHub
parent 37d6545699
commit 8b2cb75a57
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 67 additions and 29 deletions

View file

@ -39,6 +39,7 @@ export const useFindMaintenanceWindows = (props?: UseFindMaintenanceWindowsProps
const {
isLoading,
isFetching,
isInitialLoading,
data = [],
refetch,
} = useQuery({
@ -54,6 +55,7 @@ export const useFindMaintenanceWindows = (props?: UseFindMaintenanceWindowsProps
return {
maintenanceWindows: data,
isLoading: enabled && (isLoading || isFetching),
isInitialLoading,
refetch,
};
};

View file

@ -92,7 +92,7 @@ describe('MaintenanceWindowsList', () => {
const result = appMockRenderer.render(
<MaintenanceWindowsList
refreshData={() => {}}
loading={false}
isLoading={false}
items={items}
readOnly={false}
/>
@ -125,7 +125,7 @@ describe('MaintenanceWindowsList', () => {
const result = appMockRenderer.render(
<MaintenanceWindowsList
refreshData={() => {}}
loading={false}
isLoading={false}
items={items}
readOnly={true}
/>
@ -142,7 +142,7 @@ describe('MaintenanceWindowsList', () => {
const result = appMockRenderer.render(
<MaintenanceWindowsList
refreshData={refreshData}
loading={false}
isLoading={false}
items={items}
readOnly={false}
/>

View file

@ -5,17 +5,17 @@
* 2.0.
*/
import React, { ReactElement, useCallback, useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import {
formatDate,
EuiInMemoryTable,
EuiBasicTableColumn,
EuiFlexGroup,
EuiFlexItem,
SearchFilterConfig,
EuiBadge,
useEuiTheme,
EuiButton,
EuiSearchBarProps,
} from '@elastic/eui';
import { css } from '@emotion/react';
import { SortDirection } from '../types';
@ -35,7 +35,7 @@ import { useArchiveMaintenanceWindow } from '../../../hooks/use_archive_maintena
import { useFinishAndArchiveMaintenanceWindow } from '../../../hooks/use_finish_and_archive_maintenance_window';
interface MaintenanceWindowsListProps {
loading: boolean;
isLoading: boolean;
items: MaintenanceWindow[];
readOnly: boolean;
refreshData: () => void;
@ -99,21 +99,9 @@ const rowProps = (item: MaintenanceWindow) => ({
});
export const MaintenanceWindowsList = React.memo<MaintenanceWindowsListProps>(
({ loading, items, readOnly, refreshData }) => {
const search: { filters: SearchFilterConfig[]; toolsRight: ReactElement } = {
filters: [
{
type: 'custom_component',
component: StatusFilter,
},
],
toolsRight: (
<EuiButton data-test-subj="refresh-button" iconType="refresh" onClick={refreshData}>
{i18n.REFRESH}
</EuiButton>
),
};
({ isLoading, items, readOnly, refreshData }) => {
const { euiTheme } = useEuiTheme();
const { navigateToEditMaintenanceWindows } = useEditMaintenanceWindowsNavigation();
const onEdit = useCallback<TableActionsPopoverProps['onEdit']>(
(id) => navigateToEditMaintenanceWindows(id),
@ -127,6 +115,7 @@ export const MaintenanceWindowsList = React.memo<MaintenanceWindowsListProps>(
);
const { mutate: archiveMaintenanceWindow, isLoading: isLoadingArchive } =
useArchiveMaintenanceWindow();
const onArchive = useCallback(
(id: string, archive: boolean) =>
archiveMaintenanceWindow(
@ -137,11 +126,16 @@ export const MaintenanceWindowsList = React.memo<MaintenanceWindowsListProps>(
);
const { mutate: finishAndArchiveMaintenanceWindow, isLoading: isLoadingFinishAndArchive } =
useFinishAndArchiveMaintenanceWindow();
const onCancelAndArchive = useCallback(
(id: string) => finishAndArchiveMaintenanceWindow(id, { onSuccess: () => refreshData() }),
[finishAndArchiveMaintenanceWindow, refreshData]
);
const isMutatingOrLoading = useMemo(() => {
return isLoadingFinish || isLoadingArchive || isLoadingFinishAndArchive || isLoading;
}, [isLoadingFinish, isLoadingArchive, isLoadingFinishAndArchive, isLoading]);
const tableCss = useMemo(() => {
return css`
.euiTableRow {
@ -160,6 +154,7 @@ export const MaintenanceWindowsList = React.memo<MaintenanceWindowsListProps>(
return (
<TableActionsPopover
id={id}
isLoading={isMutatingOrLoading}
status={status}
onEdit={onEdit}
onCancel={onCancel}
@ -170,7 +165,7 @@ export const MaintenanceWindowsList = React.memo<MaintenanceWindowsListProps>(
},
},
],
[onArchive, onCancel, onCancelAndArchive, onEdit]
[isMutatingOrLoading, onArchive, onCancel, onCancelAndArchive, onEdit]
);
const columns = useMemo(
@ -178,12 +173,35 @@ export const MaintenanceWindowsList = React.memo<MaintenanceWindowsListProps>(
[actions, readOnly]
);
const search: EuiSearchBarProps = useMemo(
() => ({
filters: [
{
type: 'custom_component',
component: StatusFilter,
},
],
toolsRight: (
<EuiButton
data-test-subj="refresh-button"
iconType="refresh"
onClick={refreshData}
isLoading={isMutatingOrLoading}
isDisabled={isMutatingOrLoading}
>
{i18n.REFRESH}
</EuiButton>
),
}),
[isMutatingOrLoading, refreshData]
);
return (
<EuiInMemoryTable
data-test-subj="maintenance-windows-table"
css={tableCss}
itemId="id"
loading={loading || isLoadingFinish || isLoadingArchive || isLoadingFinishAndArchive}
loading={isMutatingOrLoading}
tableCaption="Maintenance Windows List"
items={items}
columns={columns}

View file

@ -24,6 +24,7 @@ describe('TableActionsPopover', () => {
const result = appMockRenderer.render(
<TableActionsPopover
id={'123'}
isLoading={false}
status={MaintenanceWindowStatus.Running}
onEdit={() => {}}
onCancel={() => {}}
@ -39,6 +40,7 @@ describe('TableActionsPopover', () => {
const result = appMockRenderer.render(
<TableActionsPopover
id={'123'}
isLoading={false}
status={MaintenanceWindowStatus.Running}
onEdit={() => {}}
onCancel={() => {}}
@ -56,6 +58,7 @@ describe('TableActionsPopover', () => {
const result = appMockRenderer.render(
<TableActionsPopover
id={'123'}
isLoading={false}
status={MaintenanceWindowStatus.Upcoming}
onEdit={() => {}}
onCancel={() => {}}
@ -72,6 +75,7 @@ describe('TableActionsPopover', () => {
const result = appMockRenderer.render(
<TableActionsPopover
id={'123'}
isLoading={false}
status={MaintenanceWindowStatus.Finished}
onEdit={() => {}}
onCancel={() => {}}
@ -88,6 +92,7 @@ describe('TableActionsPopover', () => {
const result = appMockRenderer.render(
<TableActionsPopover
id={'123'}
isLoading={false}
status={MaintenanceWindowStatus.Archived}
onEdit={() => {}}
onCancel={() => {}}

View file

@ -21,6 +21,7 @@ import { MaintenanceWindowStatus } from '../../../../common';
export interface TableActionsPopoverProps {
id: string;
status: MaintenanceWindowStatus;
isLoading: boolean;
onEdit: (id: string) => void;
onCancel: (id: string) => void;
onArchive: (id: string, archive: boolean) => void;
@ -30,7 +31,7 @@ type ModalType = 'cancel' | 'cancelAndArchive' | 'archive' | 'unarchive';
type ActionType = ModalType | 'edit';
export const TableActionsPopover: React.FC<TableActionsPopoverProps> = React.memo(
({ id, status, onEdit, onCancel, onArchive, onCancelAndArchive }) => {
({ id, status, isLoading, onEdit, onCancel, onArchive, onCancelAndArchive }) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const [isModalVisible, setIsModalVisible] = useState(false);
const [modalType, setModalType] = useState<ModalType>();
@ -197,6 +198,7 @@ export const TableActionsPopover: React.FC<TableActionsPopoverProps> = React.mem
const button = useMemo(
() => (
<EuiButtonIcon
isDisabled={isLoading}
data-test-subj="table-actions-icon-button"
iconType="boxesHorizontal"
size="s"
@ -204,7 +206,7 @@ export const TableActionsPopover: React.FC<TableActionsPopoverProps> = React.mem
onClick={onButtonClick}
/>
),
[onButtonClick]
[isLoading, onButtonClick]
);
return (

View file

@ -40,7 +40,7 @@ export const MaintenanceWindowsPage = React.memo(() => {
const { navigateToCreateMaintenanceWindow } = useCreateMaintenanceWindowNavigation();
const { isLoading, maintenanceWindows, refetch } = useFindMaintenanceWindows({
const { isLoading, isInitialLoading, maintenanceWindows, refetch } = useFindMaintenanceWindows({
enabled: hasLicense,
});
@ -81,7 +81,7 @@ export const MaintenanceWindowsPage = React.memo(() => {
};
}, [setBadge, chrome]);
if (isLoading) {
if (isInitialLoading) {
return <CenterJustifiedSpinner />;
}
@ -127,7 +127,7 @@ export const MaintenanceWindowsPage = React.memo(() => {
<MaintenanceWindowsList
readOnly={readOnly}
refreshData={refreshData}
loading={isLoading}
isLoading={isLoading}
items={maintenanceWindows}
/>
</>

View file

@ -20,8 +20,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
let objectRemover: ObjectRemover;
const browser = getService('browser');
// Failing: See https://github.com/elastic/kibana/issues/187818
describe.skip('Maintenance windows table', function () {
describe('Maintenance windows table', function () {
before(async () => {
objectRemover = await createObjectRemover({ getService });
});

View file

@ -34,6 +34,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await security.testUser.setRoles(['alerts_and_actions_role']);
});
after(async () => {
await security.testUser.restoreDefaults();
});
it('Loads the page', async () => {
await pageObjects.common.navigateToUrl(
'management',
@ -77,6 +81,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
await security.testUser.setRoles(['only_actions_role']);
});
after(async () => {
await security.testUser.restoreDefaults();
});
it('Loads the page but shows missing permission prompt', async () => {
await pageObjects.common.navigateToUrl(
'management',
@ -103,6 +111,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
);
});
after(async () => {
await security.testUser.restoreDefaults();
});
it('Loads the page', async () => {
log.debug('Checking for section heading to say Alerts.');