Skip to content

Commit

Permalink
add more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewazores committed Feb 8, 2023
1 parent b354770 commit c870469
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 11 deletions.
4 changes: 2 additions & 2 deletions src/app/Dashboard/Charts/ChartCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export interface ChartCardProps extends DashboardCardProps {
}

// TODO these need to be localized
enum ChartKind {
export enum ChartKind {
'Core Count' = 1,
'Thread Count' = 2,
'CPU Load' = 3,
Expand All @@ -96,7 +96,7 @@ enum ChartKind {
'Object Allocation Sample' = 38,
}

function kindToId(kind: string): number {
export function kindToId(kind: string): number {
return ChartKind[kind];
}

Expand Down
139 changes: 131 additions & 8 deletions src/test/Dashboard/Charts/ChartCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,30 @@
*/

jest.mock('@app/Dashboard/Charts/ChartController');
import { ChartCard } from '@app/Dashboard/Charts/ChartCard';

import { createMemoryHistory } from 'history';
const history = createMemoryHistory({ initialEntries: ['/'] });
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useRouteMatch: () => ({ url: history.location.pathname }),
useHistory: () => history,
}));
import { ChartCard, ChartKind, kindToId } from '@app/Dashboard/Charts/ChartCard';
import { ChartContext } from '@app/Dashboard/Charts/ChartContext';
import { ChartController } from '@app/Dashboard/Charts/ChartController';
import { NotificationsContext, NotificationsInstance } from '@app/Notifications/Notifications';
import { store } from '@app/Shared/Redux/ReduxStore';
import { setupStore, store } from '@app/Shared/Redux/ReduxStore';
import { defaultServices, ServiceContext } from '@app/Shared/Services/Services';
import React from 'react';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React, { PropsWithChildren } from 'react';
import { Provider } from 'react-redux';
import renderer, { act } from 'react-test-renderer';
import { of } from 'rxjs';
import { Observable, of } from 'rxjs';
import { cleanup, screen, waitFor } from '@testing-library/react';

const mockGrafanaDashboardUrlResponse = 'http://localhost:3000';
jest.spyOn(defaultServices.api, 'grafanaDashboardUrl').mockReturnValueOnce(of(mockGrafanaDashboardUrlResponse));
const mockDashboardUrl = 'http://localhost:3000';
jest.spyOn(defaultServices.api, 'grafanaDashboardUrl').mockReturnValue(of(mockDashboardUrl));

const mockTarget = { connectUrl: 'service:jmx:rmi://someUrl', alias: 'fooTarget' };
jest.spyOn(defaultServices.target, 'target').mockReturnValue(of(mockTarget));
Expand All @@ -64,10 +75,11 @@ const mockChartContext = {
controller: mockController,
};

jest.spyOn(mockController, 'attach').mockReturnValue(of(0));

describe('<ChartCard />', () => {
it('renders correctly', async () => {
jest.spyOn(mockController, 'hasActiveRecording').mockReturnValue(of(true));
jest.spyOn(mockController, 'attach').mockReturnValue(of(0));

let tree;
await act(async () => {
Expand All @@ -83,6 +95,117 @@ describe('<ChartCard />', () => {
</ServiceContext.Provider>
);
});
expect(tree.toJSON()).toMatchSnapshot();
expect(tree.toJSON()).toMatchSnapshot('with-content');
});

it('renders empty state correctly', async () => {
jest.spyOn(mockController, 'hasActiveRecording').mockReturnValue(of(false));

let tree;
await act(async () => {
tree = renderer.create(
<ServiceContext.Provider value={defaultServices}>
<NotificationsContext.Provider value={NotificationsInstance}>
<ChartContext.Provider value={mockChartContext}>
<Provider store={store}>
<ChartCard theme={'light'} chartKind={'CPU Load'} duration={120} period={10} span={6} dashboardId={0} />
</Provider>
</ChartContext.Provider>
</NotificationsContext.Provider>
</ServiceContext.Provider>
);
});
expect(tree.toJSON()).toMatchSnapshot('empty-state');
});

it('renders empty state with information and action button', async () => {
jest.spyOn(mockController, 'hasActiveRecording').mockReturnValue(of(false));

renderChartCard(
<ChartCard theme={'light'} chartKind={'CPU Load'} duration={120} period={10} span={6} dashboardId={0} />
);

expect(screen.getByText('CPU Load')).toBeInTheDocument();
expect(screen.getByText('No source recording')).toBeInTheDocument();
expect(screen.getByRole('button', { name: /create/i })).toBeInTheDocument();
});

it('navigates to recording creation with prefilled state when empty state button clicked', async () => {
jest.spyOn(mockController, 'hasActiveRecording').mockReturnValue(of(false));

const { user } = renderChartCard(
<ChartCard theme={'light'} chartKind={'CPU Load'} duration={120} period={10} span={6} dashboardId={0} />
);

expect(history.location.pathname).toBe('/');
await user.click(screen.getByRole('button', { name: /create/i }));
expect(history.location.pathname).toBe('/recordings/create');
expect(history.location.state).toEqual({
duration: -1,
labels: [
{
key: 'origin',
value: 'dashboard_metrics',
},
],
maxAge: 120,
maxSize: 100 * 1024 * 1024,
name: 'dashboard_metrics',
restartExisting: true,
templateName: 'Profiling',
templateType: 'TARGET',
});
});

it.each([
['CPU Load', 120, 5],
['Heap Usage', 90, 10],
['Network Utilization', 60, 15],
['File I/O', 30, 20],
])('renders iframe', async (chartKind: string, duration: number, period: number) => {
jest.spyOn(mockController, 'hasActiveRecording').mockReturnValue(of(true));

const { container } = renderChartCard(
<ChartCard theme={'light'} chartKind={chartKind} duration={duration} period={period} span={6} dashboardId={0} />
);

const iframe = container.querySelector('iframe');
expect(iframe).toBeTruthy();
const u = new URL(iframe?.src || '');
expect(u.host).toBe('localhost:3000');
expect(u.protocol).toBe('http:');
const params = new URLSearchParams();
params.set('theme', 'light');
params.set('panelId', String(kindToId(chartKind)));
params.set('to', 'now');
params.set('from', `now-${duration}s`);
params.set('refresh', `${period}s`);
expect(u.searchParams.toString()).toEqual(params.toString());
});
});

const renderChartCard = (
ui: React.ReactElement,
{
services = defaultServices,
notifications = NotificationsInstance,
chartContext = mockChartContext,
preloadState = {},
store = setupStore(preloadState),
user = userEvent.setup(),
...renderOptions
} = {}
) => {
const Wrapper = ({ children }: PropsWithChildren<unknown>) => {
return (
<ChartContext.Provider value={chartContext}>
<ServiceContext.Provider value={services}>
<NotificationsContext.Provider value={notifications}>
<Provider store={store}>{children}</Provider>
</NotificationsContext.Provider>
</ServiceContext.Provider>
</ChartContext.Provider>
);
};
return { store, user, ...render(ui, { wrapper: Wrapper, ...renderOptions }) };
};
126 changes: 125 additions & 1 deletion src/test/Dashboard/Charts/__snapshots__/ChartCard.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<ChartCard /> renders correctly 1`] = `
exports[`<ChartCard /> renders correctly: with-content 1`] = `
<div
className="draggable-ref-wrapper"
onDragStart={[Function]}
Expand Down Expand Up @@ -125,3 +125,127 @@ exports[`<ChartCard /> renders correctly 1`] = `
</div>
</div>
`;

exports[`<ChartCard /> renders empty state correctly: empty-state 1`] = `
<div
className="draggable-ref-wrapper"
onDragStart={[Function]}
onTransitionEnd={[Function]}
style={Object {}}
>
<div
className="dashboard-card-resizable-wrapper"
>
<article
className="pf-c-card pf-m-compact pf-m-rounded dashboard-card"
data-ouia-component-id="OUIA-Generated-Card-2"
data-ouia-component-type="PF4/Card"
data-ouia-safe={true}
id="CPU Load-chart-card"
style={
Object {
"height": 380,
}
}
>
<div
className="draggable-ref__grip"
draggable={true}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<div
className="pf-c-card__header"
style={
Object {
"marginBottom": "-2.8em",
}
}
>
<div
className="pf-c-card__title"
id="CPU Load-chart-card-title"
>
CPU Load
</div>
<div
className="pf-c-card__actions"
/>
</div>
</div>
<div
className="pf-c-card__body"
>
<div
className="pf-l-bullseye"
>
<div
className="pf-c-empty-state pf-m-lg"
>
<div
className="pf-c-empty-state__content"
>
<svg
aria-hidden="true"
aria-labelledby={null}
className="pf-c-empty-state__icon"
fill="currentColor"
height="1em"
role="img"
style={
Object {
"verticalAlign": "-0.125em",
}
}
viewBox="0 0 896 1024"
width="1em"
>
<path
d="M750.6252,63 L896.0002,223 L750.6252,383 L704.0002,339.594 L780.807,255.06 L179.0074,254.909 L178.9774,254.909 C147.0174,254.909 117.0004,268.239 94.4554,292.446 C71.9454,316.615 59.6154,348.704 59.7374,382.802 C59.9914,453.334 113.4824,511 178.9784,511 L717.0204,511 C764.8314,511 809.7404,530.399 843.4754,566.14 C877.3464,602.026 896.0004,649.93 896.0004,701.029 L896.0004,701.689 C896.0004,752.688 877.3844,800.818 843.5834,837.211 C809.9954,873.375 765.2134,893.895 717.4864,894.992 L717.1364,895 L276.310151,895.001486 C261.798404,956.828303 206.189494,1003 140,1003 C62.804,1003 0,940.196 0,863 C0,785.804 62.804,723 140,723 C206.189852,723 261.799004,769.172196 276.310386,830.999515 L716.5394,831 C748.5984,830.173 778.6564,816.448 801.2074,792.166 C823.8134,767.826 836.2624,735.694 836.2624,701.689 L836.2624,701.029 C836.2624,667.119 823.8964,635.341 801.4404,611.55 C778.9654,587.739 748.9844,575 717.0204,575 L178.9784,575 C80.6694,575 0.0014,489 0.0014,383.047 C0.0014,332 18.3334,283.634 52.1394,247.337 C85.9744,211.008 131.0224,191 178.9774,191 L179.0224,191 L781.009,191.162 L704.0002,106.406 L750.6252,63 Z M140,787 C98.093,787 64,821.094 64,863 C64,904.906 98.093,939 140,939 C181.907,939 216,904.906 216,863 C216,821.094 181.907,787 140,787 Z"
/>
</svg>
<h2
className="pf-c-title pf-m-md"
data-ouia-component-id="OUIA-Generated-Title-2"
data-ouia-component-type="PF4/Title"
data-ouia-safe={true}
>
No source recording
</h2>
<div
className="pf-c-empty-state__body"
>
Metrics cards display data taken from running flight recordings with the label
<code>
origin=
dashboard_metrics
</code>
. No such recordings are currently available.
</div>
<button
aria-disabled={false}
aria-label={null}
className="pf-c-button pf-m-primary"
data-ouia-component-id="OUIA-Generated-Button-primary-2"
data-ouia-component-type="PF4/Button"
data-ouia-safe={true}
disabled={false}
onClick={[Function]}
role={null}
type="button"
>
Create
</button>
</div>
</div>
</div>
</div>
</article>
<div
className="resizable-ref"
onMouseDown={[Function]}
/>
</div>
</div>
`;

0 comments on commit c870469

Please sign in to comment.