Skip to content

Commit

Permalink
Merge pull request elastic#26 from Elastic-AWP-Platform/process_tree_…
Browse files Browse the repository at this point in the history
…node_tests
  • Loading branch information
opauloh authored Dec 16, 2021
2 parents 85c9d90 + fd5b405 commit 5596d33
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const searchProcessTree = (processMap: ProcessMap, searchQuery: string |

// TODO: the text we search is the same as what we render.
// should this be customizable??
const text = `${workingDirectory} ${args.join(' ')}`;
const text = `${workingDirectory} ${args?.join(' ')}`;

process.searchMatched = text.includes(searchQuery) ? searchQuery : null;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import userEvent from '@testing-library/user-event';
import {
processMock,
sessionViewAlertProcessMock,
} from '../../../common/mocks/constants/session_view_process.mock';
import { AppContextTestRender, createAppRootMockRenderer } from '../../test';
import { ProcessTreeNode } from './index';

describe('ProcessTreeNode component', () => {
let render: () => ReturnType<AppContextTestRender['render']>;
let renderResult: ReturnType<typeof render>;
let mockedContext: AppContextTestRender;

beforeEach(() => {
mockedContext = createAppRootMockRenderer();
});

describe('When ProcessTreeNode is mounted', () => {
it('should render given a valid process', async () => {
renderResult = mockedContext.render(<ProcessTreeNode process={processMock} />);

expect(renderResult.queryByTestId('processTreeNode')).toBeTruthy();
});
it('renders orphaned node', async () => {
renderResult = mockedContext.render(<ProcessTreeNode process={processMock} isOrphan />);
expect(renderResult.queryByText(/orphaned/i)).toBeTruthy();
});

it('renders user icon for user entered process', async () => {
const userEnteredProcessMock: typeof processMock = {
...processMock,
isUserEntered: () => true,
};

renderResult = mockedContext.render(<ProcessTreeNode process={userEnteredProcessMock} />);

expect(renderResult.queryByTestId('processTreeNodeUserIcon')).toBeTruthy();
});

it('renders Exec icon for executed process', async () => {
const executedProcessMock: typeof processMock = {
...processMock,
hasExec: () => true,
};

renderResult = mockedContext.render(<ProcessTreeNode process={executedProcessMock} />);

expect(renderResult.queryByTestId('processTreeNodeExecIcon')).toBeTruthy();
});

it('renders Root Escalation flag properly', async () => {
const rootEscalationProcessMock: typeof processMock = {
...processMock,
getDetails: () => ({
...processMock.getDetails(),
process: {
...processMock.getDetails().process,
// @ts-ignore
parent: {
user: {
name: 'test',
id: '1000',
},
},
user: {
id: '-1',
name: 'root',
},
},
}),
};

renderResult = mockedContext.render(<ProcessTreeNode process={rootEscalationProcessMock} />);

expect(renderResult.queryByTestId('processTreeNodeRootEscalationFlag')).toBeTruthy();
});

it('executes callback function when user Clicks', async () => {
const onProcessSelected = jest.fn();

renderResult = mockedContext.render(
<ProcessTreeNode process={processMock} onProcessSelected={onProcessSelected} />
);

userEvent.click(renderResult.getByTestId('processTreeNodeRow'));
expect(onProcessSelected).toHaveBeenCalled();
});

it('does not executes callback function when user is Clicking to copy text', async () => {
const windowGetSelectionSpy = jest.spyOn(window, 'getSelection');

const onProcessSelected = jest.fn();

renderResult = mockedContext.render(
<ProcessTreeNode process={processMock} onProcessSelected={onProcessSelected} />
);

// @ts-ignore
windowGetSelectionSpy.mockImplementation(() => ({ type: 'Range' }));

userEvent.click(renderResult.getByTestId('processTreeNodeRow'));
expect(onProcessSelected).not.toHaveBeenCalled();

// cleanup
windowGetSelectionSpy.mockRestore();
});
describe('Alerts', () => {
it('renders Alert button when process has alerts', async () => {
renderResult = mockedContext.render(
<ProcessTreeNode process={sessionViewAlertProcessMock} />
);

expect(renderResult.queryByTestId('processTreeNodeAlertButton')).toBeTruthy();
});
it('toggle Alert Details button when Alert button is clicked', async () => {
renderResult = mockedContext.render(
<ProcessTreeNode process={sessionViewAlertProcessMock} />
);
userEvent.click(renderResult.getByTestId('processTreeNodeAlertButton'));
expect(renderResult.queryByTestId('sessionViewAlertDetails')).toBeTruthy();
userEvent.click(renderResult.getByTestId('processTreeNodeAlertButton'));
expect(renderResult.queryByTestId('sessionViewAlertDetails')).toBeFalsy();
});
});
describe('Child processes', () => {
it('renders Child processes button when process has Child processes', async () => {
const processMockWithChildren: typeof processMock = {
...processMock,
children: [processMock],
};

renderResult = mockedContext.render(<ProcessTreeNode process={processMockWithChildren} />);

expect(renderResult.queryByTestId('processTreeNodeChildProcessesButton')).toBeTruthy();
});
it('toggle Child processes nodes when Child processes button is clicked', async () => {
const processMockWithChildren: typeof processMock = {
...processMock,
children: [processMock],
};

renderResult = mockedContext.render(<ProcessTreeNode process={processMockWithChildren} />);

expect(renderResult.getAllByTestId('processTreeNode')).toHaveLength(1);

userEvent.click(renderResult.getByTestId('processTreeNodeChildProcessesButton'));
expect(renderResult.getAllByTestId('processTreeNode')).toHaveLength(2);

userEvent.click(renderResult.getByTestId('processTreeNodeChildProcessesButton'));
expect(renderResult.getAllByTestId('processTreeNode')).toHaveLength(1);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ interface ProcessDeps {
isSessionLeader?: boolean;
isOrphan?: boolean;
depth?: number;
onProcessSelected(process: Process): void;
onProcessSelected?: (process: Process) => void;
}

/**
Expand Down Expand Up @@ -119,8 +119,10 @@ export function ProcessTreeNode({
if (!isSessionLeader && process.children.length > 0) {
buttons.push(
<EuiButton
key="child-processes-button"
css={styles.getButtonStyle(ButtonType.children)}
onClick={() => setChildrenExpanded(!childrenExpanded)}
data-test-subj="processTreeNodeChildProcessesButton"
>
<FormattedMessage
id="xpack.sessionView.childProcesses"
Expand All @@ -134,6 +136,7 @@ export function ProcessTreeNode({
if (alerts.length) {
buttons.push(
<EuiButton
key="alert-button"
css={styles.getButtonStyle(ButtonType.alerts)}
onClick={() => setAlertsExpanded(!alertsExpanded)}
data-test-subj="processTreeNodeAlertButton"
Expand Down Expand Up @@ -191,8 +194,19 @@ export function ProcessTreeNode({
const renderProcess = () => {
return (
<span>
{process.isUserEntered() && <EuiIcon css={styles.userEnteredIcon} type="user" />}
<EuiIcon type={hasExec ? 'console' : 'branch'} /> {template()}
{process.isUserEntered() && (
<EuiIcon
data-test-subj="processTreeNodeUserIcon"
css={styles.userEnteredIcon}
type="user"
/>
)}
{hasExec ? (
<EuiIcon data-test-subj="processTreeNodeExecIcon" type="console" />
) : (
<EuiIcon type="branch" />
)}
{template()}
{isOrphan ? '(orphaned)' : ''}
</span>
);
Expand All @@ -203,7 +217,10 @@ export function ProcessTreeNode({

if (user.name === 'root' && user.id !== parent.user.id) {
return (
<EuiButton css={styles.getButtonStyle(ButtonType.userChanged)}>
<EuiButton
data-test-subj="processTreeNodeRootEscalationFlag"
css={styles.getButtonStyle(ButtonType.userChanged)}
>
<FormattedMessage
id="xpack.sessionView.execUserChange"
defaultMessage="Root escalation"
Expand All @@ -223,7 +240,7 @@ export function ProcessTreeNode({
return;
}

onProcessSelected(process);
onProcessSelected?.(process);
};

const id = process.id;
Expand All @@ -237,7 +254,7 @@ export function ProcessTreeNode({
data-test-subj="processTreeNode"
>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
<div css={styles.wrapper} onClick={onProcessClicked}>
<div data-test-subj="processTreeNodeRow" css={styles.wrapper} onClick={onProcessClicked}>
{isSessionLeader ? renderSessionLeader() : renderProcess()}
{renderRootEscalation()}
{renderButtons()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ export const useStyles = ({ depth, hasAlerts }: StylesDeps) => {
color: colors.text,
};

const searchHighlight: CSSObject = {
backgroundColor: colors.highlight,
color: colors.text,
borderRadius: border.radius.medium,
};
const searchHighlight = `
background-color: ${colors.highlight};
color: ${colors.text};
border-radius: ${border.radius.medium};
`;

const children: CSSObject = {
position: 'relative',
Expand Down

0 comments on commit 5596d33

Please sign in to comment.