Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core] Use describeTreeView for items test (partial) #12893

Merged
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 @@ -119,46 +119,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 @@ -225,16 +185,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 @@ -258,28 +208,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 @@ -1694,21 +1622,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.getAllItemId()).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.getAllItemId()).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,117 @@ 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.getAllItemId()).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.getAllItemId()).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.getAllItemId()).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.getAllItemId()).to.deep.equal(['1', '3', '2']);

// Check if the internal state is updates by running a range selection
flaviendelangle marked this conversation as resolved.
Show resolved Hide resolved
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 its children are an empty array', () => {
flaviendelangle marked this conversation as resolved.
Show resolved Hide resolved
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' }],
});

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

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

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

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

expect(response.getItemRoot('1')).to.have.attribute('aria-disabled');
});
flaviendelangle marked this conversation as resolved.
Show resolved Hide resolved

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 getAllItemId = () =>
result.queryAllByRole('treeitem').map((item) => item.dataset.testid!);

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

return {
getRoot,
getAllItemRoots,
getAllItemId,
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[];
getAllItemId: () => string[];
flaviendelangle marked this conversation as resolved.
Show resolved Hide resolved
/**
* Returns the `root` slot of the item with the given id.
* @param {string} id The id of the item to retrieve.
Expand Down
Loading