Skip to content

Commit

Permalink
[core] Use describeTreeView for items test (partial) (#12893)
Browse files Browse the repository at this point in the history
Signed-off-by: Flavien DELANGLE <[email protected]>
Co-authored-by: Nora <[email protected]>
Co-authored-by: Lukas <[email protected]>
  • Loading branch information
3 people authored May 17, 2024
1 parent fd544b7 commit 760ea7e
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 143 deletions.
48 changes: 0 additions & 48 deletions packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,27 +166,6 @@ describe('<SimpleTreeView />', () => {
expect(getByTestId('two')).toHaveFocus();
});

it('should support conditional rendered tree items', () => {
function TestComponent() {
const [hide, setState] = React.useState(false);

return (
<React.Fragment>
<button type="button" onClick={() => setState(true)}>
Hide
</button>
<SimpleTreeView>{!hide && <TreeItem itemId="test" label="test" />}</SimpleTreeView>
</React.Fragment>
);
}

const { getByText, queryByText } = render(<TestComponent />);

expect(getByText('test')).not.to.equal(null);
fireEvent.click(getByText('Hide'));
expect(queryByText('test')).to.equal(null);
});

it('should work in a portal', () => {
const { getByTestId } = render(
<Portal>
Expand Down Expand Up @@ -215,33 +194,6 @@ describe('<SimpleTreeView />', () => {
expect(getByTestId('four')).toHaveFocus();
});

it('should update indexes when two items are swapped', () => {
const onSelectedItemsChange = spy();

function TestComponent() {
const [items, setItems] = React.useState(['1', '2', '3']);

return (
<React.Fragment>
<button type="button" onClick={() => setItems(['1', '3', '2'])}>
Swap items
</button>
<SimpleTreeView onSelectedItemsChange={onSelectedItemsChange} multiSelect>
{items.map((itemId) => (
<TreeItem key={itemId} itemId={itemId} label={itemId} />
))}
</SimpleTreeView>
</React.Fragment>
);
}

const { getByText } = render(<TestComponent />);
fireEvent.click(getByText('Swap items'));
fireEvent.click(getByText('1'));
fireEvent.click(getByText('3'), { shiftKey: true });
expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal(['1', '3']);
});

describe('Accessibility', () => {
it('(TreeView) should have the role `tree`', () => {
const { getByRole } = render(<SimpleTreeView />);
Expand Down
87 changes: 0 additions & 87 deletions packages/x-tree-view/src/TreeItem/TreeItem.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,46 +112,6 @@ describe('<TreeItem />', () => {
expect(handleClick.callCount).to.equal(1);
});

it('should allow conditional child', () => {
function TestComponent() {
const [hide, setState] = React.useState(false);

return (
<React.Fragment>
<button data-testid="button" type="button" onClick={() => setState(true)}>
Hide
</button>
<SimpleTreeView defaultExpandedItems={['1']}>
<TreeItem itemId="1" data-testid="1">
{!hide && <TreeItem itemId="2" data-testid="2" />}
</TreeItem>
</SimpleTreeView>
</React.Fragment>
);
}
const { getByTestId, queryByTestId } = render(<TestComponent />);

expect(getByTestId('1')).to.have.attribute('aria-expanded', 'true');
expect(getByTestId('2')).not.to.equal(null);
fireEvent.click(getByTestId('button'));
expect(getByTestId('1')).not.to.have.attribute('aria-expanded');
expect(queryByTestId('2')).to.equal(null);
});

it('should treat an empty array equally to no children', () => {
const { getByTestId } = render(
<SimpleTreeView defaultExpandedItems={['1']}>
<TreeItem itemId="1" label="1" data-testid="1">
<TreeItem itemId="2" label="2" data-testid="2">
{[]}
</TreeItem>
</TreeItem>
</SimpleTreeView>,
);

expect(getByTestId('2')).not.to.have.attribute('aria-expanded');
});

it('should treat multiple empty conditional arrays as empty', () => {
const { getByTestId } = render(
<SimpleTreeView defaultExpandedItems={['1']}>
Expand Down Expand Up @@ -218,16 +178,6 @@ describe('<TreeItem />', () => {
expect(handleClick.callCount).to.equal(0);
});

it('should be able to use a custom id', () => {
const { getByRole } = render(
<SimpleTreeView>
<TreeItem id="customId" itemId="one" data-testid="one" />
</SimpleTreeView>,
);

expect(getByRole('treeitem')).to.have.attribute('id', 'customId');
});

describe('Accessibility', () => {
it('should have the role `treeitem`', () => {
const { getByTestId } = render(
Expand All @@ -251,28 +201,6 @@ describe('<TreeItem />', () => {
expect(getByRole('group')).to.contain(getByText('test2'));
});

describe('aria-disabled', () => {
it('should not have the attribute `aria-disabled` if disabled is false', () => {
const { getByTestId } = render(
<SimpleTreeView>
<TreeItem itemId="one" label="one" data-testid="one" />
</SimpleTreeView>,
);

expect(getByTestId('one')).not.to.have.attribute('aria-disabled');
});

it('should have the attribute `aria-disabled={true}` if disabled', () => {
const { getByTestId } = render(
<SimpleTreeView>
<TreeItem itemId="one" label="one" disabled data-testid="one" />
</SimpleTreeView>,
);

expect(getByTestId('one')).to.have.attribute('aria-disabled', 'true');
});
});

describe('Navigation', () => {
describe('right arrow interaction', () => {
it('should open the item and not move the focus if focus is on a closed item', () => {
Expand Down Expand Up @@ -1532,21 +1460,6 @@ describe('<TreeItem />', () => {
expect(handleClick.callCount).to.equal(1);
});
});

it('should disable child items when parent item is disabled', () => {
const { getByTestId } = render(
<SimpleTreeView defaultExpandedItems={['one']}>
<TreeItem itemId="one" label="one" disabled data-testid="one">
<TreeItem itemId="two" label="two" data-testid="two" />
<TreeItem itemId="three" label="three" data-testid="three" />
</TreeItem>
</SimpleTreeView>,
);

expect(getByTestId('one')).to.have.attribute('aria-disabled', 'true');
expect(getByTestId('two')).to.have.attribute('aria-disabled', 'true');
expect(getByTestId('three')).to.have.attribute('aria-disabled', 'true');
});
});

describe('content customisation', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describeTreeView<[UseTreeViewExpansionSignature]>(
});

expect(response.isItemExpanded('1')).to.equal(false);
expect(response.getAllItemRoots()).to.have.length(2);
expect(response.getAllTreeItemIds()).to.deep.equal(['1', '2']);
});

it('should use the default state when defined', () => {
Expand All @@ -32,7 +32,7 @@ describeTreeView<[UseTreeViewExpansionSignature]>(
});

expect(response.isItemExpanded('1')).to.equal(true);
expect(response.getAllItemRoots()).to.have.length(3);
expect(response.getAllTreeItemIds()).to.deep.equal(['1', '1.1', '2']);
});

it('should use the controlled state when defined', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { expect } from 'chai';
import { spy } from 'sinon';
import { fireEvent } from '@mui-internal/test-utils';
import { describeTreeView } from 'test/utils/tree-view/describeTreeView';
import {
UseTreeViewExpansionSignature,
UseTreeViewItemsSignature,
UseTreeViewSelectionSignature,
} from '@mui/x-tree-view/internals';

describeTreeView('useTreeViewItems plugin', ({ render, treeViewComponent }) => {
describeTreeView<
[UseTreeViewItemsSignature, UseTreeViewExpansionSignature, UseTreeViewSelectionSignature]
>('useTreeViewItems plugin', ({ render, treeViewComponent }) => {
it('should throw an error when two items have the same ID', function test() {
// TODO is this fixed?
if (!/jsdom/.test(window.navigator.userAgent)) {
Expand All @@ -21,4 +30,103 @@ describeTreeView('useTreeViewItems plugin', ({ render, treeViewComponent }) => {
],
);
});

it('should be able to use a custom id attribute', function test() {
// For now, only SimpleTreeView can use custom id attributes
if (treeViewComponent.startsWith('RichTreeView')) {
this.skip();
}

const response = render({
items: [{ id: '1' }],
slotProps: {
item: {
id: 'customId',
},
},
});

expect(response.getItemRoot('1')).to.have.attribute('id', 'customId');
});

describe('items prop / JSX Tree Item', () => {
it('should support removing an item', () => {
const response = render({
items: [{ id: '1' }, { id: '2' }],
});

response.setItems([{ id: '1' }]);
expect(response.getAllTreeItemIds()).to.deep.equal(['1']);
});

it('should support adding an item at the end', () => {
const response = render({
items: [{ id: '1' }],
});

response.setItems([{ id: '1' }, { id: '2' }]);
expect(response.getAllTreeItemIds()).to.deep.equal(['1', '2']);
});

it('should support adding an item at the beginning', () => {
const response = render({
items: [{ id: '2' }],
});

response.setItems([{ id: '1' }, { id: '2' }]);
expect(response.getAllTreeItemIds()).to.deep.equal(['1', '2']);
});

it('should update indexes when two items are swapped', () => {
const onSelectedItemsChange = spy();

const response = render({
items: [{ id: '1' }, { id: '2' }, { id: '3' }],
multiSelect: true,
onSelectedItemsChange,
});

response.setItems([{ id: '1' }, { id: '3' }, { id: '2' }]);
expect(response.getAllTreeItemIds()).to.deep.equal(['1', '3', '2']);

// Check if the internal state is updated by running a range selection
fireEvent.click(response.getItemContent('1'));
fireEvent.click(response.getItemContent('3'), { shiftKey: true });
expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal(['1', '3']);
});

it('should not mark an item as expandable if its children is an empty array', () => {
const response = render({
items: [{ id: '1', children: [] }],
defaultExpandedItems: ['1'],
});

expect(response.getItemRoot('1')).not.to.have.attribute('aria-expanded');
});
});

describe('disabled prop', () => {
it('should not have the attribute `aria-disabled` if disabled is not defined', () => {
const response = render({
items: [{ id: '1' }, { id: '2', disabled: false }, { id: '3', disabled: true }],
});

expect(response.getItemRoot('1')).not.to.have.attribute('aria-disabled');
expect(response.getItemRoot('2')).not.to.have.attribute('aria-disabled');
expect(response.getItemRoot('3')).to.have.attribute('aria-disabled');
});

it('should disable all descendants of a disabled item', () => {
const response = render({
items: [
{ id: '1', disabled: true, children: [{ id: '1.1', children: [{ id: '1.1.1' }] }] },
],
defaultExpandedItems: ['1', '1.1'],
});

expect(response.getItemRoot('1')).to.have.attribute('aria-disabled', 'true');
expect(response.getItemRoot('1.1')).to.have.attribute('aria-disabled', 'true');
expect(response.getItemRoot('1.1.1')).to.have.attribute('aria-disabled', 'true');
});
});
});
5 changes: 3 additions & 2 deletions test/utils/tree-view/describeTreeView/describeTreeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ const innerDescribeTreeView = <TPlugins extends TreeViewAnyPluginSignature[]>(
): Omit<DescribeTreeViewRendererReturnValue<TPlugins>, 'setProps' | 'setItems' | 'apiRef'> => {
const getRoot = () => result.getByRole('tree');

const getAllItemRoots = () => result.queryAllByRole('treeitem');
const getAllTreeItemIds = () =>
result.queryAllByRole('treeitem').map((item) => item.dataset.testid!);

const getFocusedItemId = () => {
const activeElement = document.activeElement;
Expand Down Expand Up @@ -60,7 +61,7 @@ const innerDescribeTreeView = <TPlugins extends TreeViewAnyPluginSignature[]>(

return {
getRoot,
getAllItemRoots,
getAllTreeItemIds,
getFocusedItemId,
getItemRoot,
getItemContent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ export interface DescribeTreeViewRendererReturnValue<
*/
getFocusedItemId: () => string | null;
/**
* Returns the `root` slot of all the items.
* @returns {HTMLElement[]} List of the `root` slot of all the items.
* Returns the item id of all the items currently rendered.
* @returns {HTMLElement[]} List of the item id of all the items currently rendered.
*/
getAllItemRoots: () => HTMLElement[];
getAllTreeItemIds: () => string[];
/**
* Returns the `root` slot of the item with the given id.
* @param {string} id The id of the item to retrieve.
Expand Down

0 comments on commit 760ea7e

Please sign in to comment.