Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
asimonok committed Sep 27, 2024
1 parent 5af5674 commit 6724ac3
Show file tree
Hide file tree
Showing 17 changed files with 1,466 additions and 45 deletions.
5 changes: 5 additions & 0 deletions src/__mocks__/@grafana/ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,11 @@ const StatsPickerMock = SelectMock;

const StatsPicker = jest.fn(StatsPickerMock);

/**
* Mock Card Description to prevent validateDOMNesting error
*/
(actual.Card as any).Description = ({ children }: any) => children;

beforeEach(() => {
Button.mockImplementation(ButtonMock);
Select.mockImplementation(SelectMock);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { render, screen } from '@testing-library/react';
import { getJestSelectors } from '@volkovlabs/jest-selectors';
import React from 'react';

import { ACTIONS_COLUMN_ID, TEST_IDS } from '@/constants';

import { TableHeaderCell } from './TableHeaderCell';

/**
* Props
*/
type Props = React.ComponentProps<typeof TableHeaderCell>;

describe('TableHeaderCell', () => {
/**
* Selectors
*/
const getSelectors = getJestSelectors(TEST_IDS.tableHeaderCell, ['sortIcon']);
const selectors = getSelectors(screen);

/**
* Get Component
*/
const getComponent = (props: Partial<Props>) => {
return <TableHeaderCell {...(props as any)} />;
};

it('Should render', () => {
render(
getComponent({
header: {
getContext: () =>
({
label: '123',
}) as any,
column: {
getIsSorted: jest.fn(),
getCanSort: jest.fn(),
getToggleSortingHandler: jest.fn(),
columnDef: {
header: ({ label }: any) => label,
},
} as any,
} as any,
})
);

expect(selectors.root()).toBeInTheDocument();
expect(selectors.root()).toHaveTextContent('123');
});

it('Should render nothing if actions column', () => {
render(
getComponent({
header: {
getContext: () =>
({
label: '123',
}) as any,
column: {
id: ACTIONS_COLUMN_ID,
getIsSorted: jest.fn(),
getCanSort: jest.fn(),
getToggleSortingHandler: jest.fn(),
columnDef: {
header: ({ label }: any) => label,
},
} as any,
} as any,
})
);

expect(selectors.root(true)).not.toBeInTheDocument();
});

it('Should show asc sort icon', () => {
render(
getComponent({
header: {
getContext: () =>
({
label: '123',
}) as any,
column: {
getIsSorted: jest.fn(() => 'asc'),
getCanSort: jest.fn(),
getToggleSortingHandler: jest.fn(),
columnDef: {
header: ({ label }: any) => label,
},
} as any,
} as any,
})
);

expect(selectors.root()).toBeInTheDocument();
expect(selectors.sortIcon(false, 'arrow-up')).toBeInTheDocument();
});

it('Should show desc sort icon', () => {
render(
getComponent({
header: {
getContext: () =>
({
label: '123',
}) as any,
column: {
getIsSorted: jest.fn(() => 'desc'),
getCanSort: jest.fn(),
getToggleSortingHandler: jest.fn(),
columnDef: {
header: ({ label }: any) => label,
},
} as any,
} as any,
})
);

expect(selectors.root()).toBeInTheDocument();
expect(selectors.sortIcon(false, 'arrow-down')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Icon, useStyles2 } from '@grafana/ui';
import { flexRender, Header } from '@tanstack/react-table';
import React from 'react';

import { ACTIONS_COLUMN_ID } from '@/constants';
import { ACTIONS_COLUMN_ID, TEST_IDS } from '@/constants';

import { getStyles } from './TableHeaderCell.styles';
import { TableHeaderCellFilter } from './TableHeaderCellFilter';
Expand Down Expand Up @@ -43,9 +43,15 @@ export const TableHeaderCell = <TData,>({ header }: Props<TData>) => {
className={cx({
[styles.labelSortable]: header.column.getCanSort(),
})}
{...TEST_IDS.tableHeaderCell.root.apply()}
>
{flexRender(header.column.columnDef.header, header.getContext())}
{!!sort && <Icon name={sort === 'asc' ? 'arrow-up' : 'arrow-down'} />}
{!!sort && (
<Icon
name={sort === 'asc' ? 'arrow-up' : 'arrow-down'}
{...TEST_IDS.tableHeaderCell.sortIcon.apply(sort === 'asc' ? 'arrow-up' : 'arrow-down')}
/>
)}
</div>
{header.column.columnDef.enableColumnFilter && <TableHeaderCellFilter header={header} />}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { createSelector, getJestSelectors } from '@volkovlabs/jest-selectors';
import React from 'react';

import { TEST_IDS } from '@/constants';
import { NestedObjectType } from '@/types';
import {
createNestedObjectConfig,
createNestedObjectEditorConfig,
createNestedObjectOperationConfig,
createTableRequestConfig,
} from '@/utils';

import { NestedObjectEditor } from './NestedObjectEditor';

/**
* Props
*/
type Props = React.ComponentProps<typeof NestedObjectEditor>;

/**
* In Test Ids
*/
const inTestIds = {
requestEditor: createSelector('data-testid request-editor'),
operationEditor: createSelector('data-testid operation-editor'),
configEditor: createSelector('data-testid config-editor'),
};

/**
* Mock Request Editor
*/
jest.mock('@/components/editors/RequestEditor', () => ({
RequestEditor: ({ onChange, value }: any) => (
<input {...inTestIds.requestEditor.apply()} onChange={() => onChange(value)} />
),
}));

/**
* Mock Nested Object Operation Editor
*/
jest.mock('./components/NestedObjectOperationEditor', () => ({
NestedObjectOperationEditor: ({ onChange, value }: any) => (
<input {...inTestIds.operationEditor.apply()} onChange={() => onChange(value)} />
),
}));

/**
* Mock nestedObjectEditorsRegistry
*/
jest.mock('./NestedObjectEditorsRegistry', () => ({
nestedObjectEditorsRegistry: {
get: () => ({
editor: ({ onChange, value }: any) => (
<input {...inTestIds.configEditor.apply()} onChange={() => onChange(value)} />
),
}),
},
}));

/**
* Mock Request Editor
*/
jest.mock('@/components/editors/RequestEditor');

describe('NestedObjectEditor', () => {
/**
* Change
*/
const onChange = jest.fn();

/**
* Selectors
*/
const getSelectors = getJestSelectors({ ...TEST_IDS.nestedObjectEditor, ...inTestIds });
const selectors = getSelectors(screen);

/**
* Get Component
*/
const getComponent = (props: Partial<Props>) => {
return <NestedObjectEditor value={createNestedObjectConfig({})} onChange={onChange} {...(props as any)} />;
};

it('Should allow to change type', () => {
render(
getComponent({
value: createNestedObjectConfig({
type: '' as never,
}),
})
);

expect(selectors.fieldType()).toBeInTheDocument();

fireEvent.change(selectors.fieldType(), { target: { value: NestedObjectType.CARDS } });

expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
type: NestedObjectType.CARDS,
})
);
});

it('Should allow to change get request', () => {
render(
getComponent({
value: createNestedObjectConfig({
get: createTableRequestConfig({
datasource: 'abc',
}),
}),
})
);

expect(selectors.getRequestSectionHeader()).toBeInTheDocument();
fireEvent.click(selectors.getRequestSectionHeader());

expect(selectors.getRequestSectionContent()).toBeInTheDocument();
expect(selectors.requestEditor()).toBeInTheDocument();

fireEvent.change(selectors.requestEditor(), { target: { value: '123' } });

expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
get: createTableRequestConfig({
datasource: 'abc',
}),
})
);
});

describe('operations', () => {
['add', 'update', 'delete'].forEach((operation) => {
it(`Should allow to enable ${operation} operation`, () => {
render(
getComponent({
value: createNestedObjectConfig({
[operation]: createNestedObjectOperationConfig({
enabled: false,
}),
}),
})
);

expect(selectors.operationSectionHeader(false, operation)).toBeInTheDocument();
expect(selectors.fieldOperationEnabled(false, operation)).toBeInTheDocument();

fireEvent.click(selectors.fieldOperationEnabled(false, operation));

expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
[operation]: expect.objectContaining({
enabled: true,
}),
})
);
});

it(`Should allow to enable ${operation} operation if no config`, () => {
render(
getComponent({
value: createNestedObjectConfig({
[operation]: undefined,
}),
})
);

expect(selectors.operationSectionHeader(false, operation)).toBeInTheDocument();
expect(selectors.fieldOperationEnabled(false, operation)).toBeInTheDocument();

fireEvent.click(selectors.fieldOperationEnabled(false, operation));

expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
[operation]: expect.objectContaining({
enabled: true,
}),
})
);
});

it(`Should allow to update ${operation} settings`, () => {
render(
getComponent({
value: createNestedObjectConfig({
[operation]: createNestedObjectOperationConfig({
enabled: true,
}),
}),
})
);

expect(selectors.operationSectionHeader(false, operation)).toBeInTheDocument();

fireEvent.click(selectors.operationSectionHeader(false, operation));

expect(selectors.operationEditor()).toBeInTheDocument();
fireEvent.change(selectors.operationEditor(), { target: { value: '123' } });

expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
[operation]: createNestedObjectOperationConfig({
enabled: true,
}),
})
);
});
});
});

it('Should allow to change editor config', () => {
render(
getComponent({
value: createNestedObjectConfig({
type: NestedObjectType.CARDS,
editor: createNestedObjectEditorConfig({
type: NestedObjectType.CARDS,
}),
}),
})
);

expect(selectors.configEditor()).toBeInTheDocument();

fireEvent.change(selectors.configEditor(), { target: { value: '123' } });

expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
editor: createNestedObjectEditorConfig({
type: NestedObjectType.CARDS,
}),
})
);
});
});
Loading

0 comments on commit 6724ac3

Please sign in to comment.