Skip to content

Commit

Permalink
707: Added unit tests for ContextMenu Component and fixed code to get…
Browse files Browse the repository at this point in the history
… them to pass
  • Loading branch information
tombogle committed Feb 27, 2024
1 parent dd7b4dd commit 5ec68c6
Show file tree
Hide file tree
Showing 9 changed files with 928 additions and 805 deletions.
222 changes: 111 additions & 111 deletions lib/platform-bible-react/dist/index.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/platform-bible-react/dist/index.cjs.map

Large diffs are not rendered by default.

1,375 changes: 690 additions & 685 deletions lib/platform-bible-react/dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lib/platform-bible-react/dist/index.js.map

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default function ContextMenu(menuProps: PropsWithChildren<ContextMenuProp
};

// If no menu items or children, we don't want to display the context menu at all.
return (menuDefinition.items?.length ?? 0) === 0 || !children ? (
return (menuDefinition?.items?.length ?? 0) === 0 || !children ? (
children
) : (
<div
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { PlatformMenus } from 'platform-bible-utils';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import ContextMenu from './context-menu.component';
import NonValidatingDocumentCombiner from '../test-utils/non-validating-document-combiner';
import * as jsonMenu from './sample.composed.full.menu.json';

describe('ContextMenu', () => {
const topMenuCombiner = new NonValidatingDocumentCombiner(jsonMenu, {
copyDocuments: false,
ignoreDuplicateProperties: true,
});

// Assert the type that schema validation should have already sorted out
// eslint-disable-next-line no-type-assertion/no-type-assertion
const menuData = topMenuCombiner.output as PlatformMenus;
render(<ContextMenu
menuDefinition={menuData.defaultWebViewContextMenu}
commandHandler={() => {}} >
click me
</ContextMenu>);

it('renders no menu items before context menu is displayed', () => {
console.log(screen.debug());
expect(screen.queryAllByRole('menuitem').length).toBe(0);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { PlatformMenus } from 'platform-bible-utils';
import { fireEvent, render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { PropsWithChildren } from 'react';
import ContextMenu from './context-menu.component';
import NonValidatingDocumentCombiner from '../test-utils/non-validating-document-combiner';
import * as jsonMenu from './sample.composed.full.menu.json';

jest.mock('@mui/material', () => {
const mui = jest.requireActual('@mui/material'); // Import the actual MUI components

return {
...mui, // Spread the actual MUI exports

// Mocked components
MenuItem: ({
divider,
className,
children,
}: {
divider: boolean | undefined;
className: string | undefined;
} & PropsWithChildren) => {
const dividerStyle = divider ? ' hasDivider' : '';
return (
<li className={`${className || ''}${dividerStyle}`} role="menuitem">
{children}
</li>
);
},
};
});

describe('ContextMenu renders', () => {
const topMenuCombiner = new NonValidatingDocumentCombiner(jsonMenu, {
copyDocuments: false,
ignoreDuplicateProperties: true,
});

// Assert the type that schema validation should have already sorted out
// eslint-disable-next-line no-type-assertion/no-type-assertion
const menuData = topMenuCombiner.output as PlatformMenus;
render(<ContextMenu
menuDefinition={menuData.defaultWebViewContextMenu}
commandHandler={() => {}} >
click me
</ContextMenu>);

const contextMenuTarget = screen.getByText('click me');
fireEvent.contextMenu(contextMenuTarget);

console.log(screen.debug());

const allMenuItems = screen.queryAllByRole('menuitem');

it('the correct total number of items', () => {
expect(allMenuItems.length).toBe(2);
});

it('the correct total number of groups with dividers', () => {
let cItemsWithDividers = 0;
allMenuItems.forEach((m) => {
if (m.outerHTML?.match('hasDivider')) cItemsWithDividers += 1;
});

// In the test data, there are two groups in the context menu, so there should be a divider
// between them.
expect(cItemsWithDividers).toBe(1);
});

it('the last group in a column without a final separator', () => {
const htmlForWordListItem = allMenuItems
.map((i) => i.outerHTML)
.find((html) => html && /%wordList%/.test(html));

expect(htmlForWordListItem).toBeDefined();
expect(htmlForWordListItem).not.toMatch('hasDivider');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
MenuItemContainingCommand,
MenuItemContainingSubmenu,
ReferencedItem,
SingleColumnMenu,
} from 'platform-bible-utils';
import MenuItem, { MenuProps } from './menu-item.component';

Expand All @@ -23,6 +24,12 @@ type SubMenuProps = MenuProps & {
parentMenuItem: MenuItemContainingSubmenu;
};

function getAllGroups(menuDefinition : SingleColumnMenu) {
const groupEntries = Object.entries(menuDefinition.groups);
// Convert array of entries to array of objects with id and group properties
return groupEntries.map(([key, value]) => ({ id: key, group: value }));
};

function SubMenu(props: SubMenuProps) {
const [anchorEl, setAnchorEl] = useState<undefined | HTMLElement>(undefined);

Expand All @@ -37,10 +44,7 @@ function SubMenu(props: SubMenuProps) {
};

const renderSubMenuItems = () => {
const groupEntries = Object.entries(menuDefinition.groups);
// Convert array of entries to array of objects with id and group properties
const groups = groupEntries.map(([key, value]) => ({ id: key, group: value }));
let includedGroups = groups.filter((g) => 'menuItem' in g.group);
let includedGroups = getAllGroups(menuDefinition).filter((g) => 'menuItem' in g.group);

// Ensure valid parent menu was provided. (If not, submenu will contain all groups!)
if (parentMenuItem?.id) {
Expand Down Expand Up @@ -100,6 +104,12 @@ export default function GroupedMenuItemList(
) {
const { menuDefinition, onClick, commandHandler } = menuProps;

if (includedGroups?.length ?? 0 === 0) {
// We're apparently laying out a single-column menu (presumably a context menu). In this case,
// all groups should be included expect ones that belong to a submenu.
includedGroups = getAllGroups(menuDefinition).filter((g) => !('menuItem' in g.group));
}

const sortedGroups = Object.values(includedGroups).sort(
(a, b) => (a.group.order || 0) - (b.group.order || 0),
);
Expand Down
6 changes: 4 additions & 2 deletions src/renderer/components/platform-bible-menu.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,12 @@ const supportAndDevelopmentMenuLayout: LocalizedMainMenu = {
group: 'paratext.resourceTools',
order: 3,
command: 'paratextBibleWordList.open',
hasDivider: true,
},
{
name: 'Open Hello World Project...',
label: 'Open Hello World Project...',
localizeNotes: 'Main application menu > Paratext column > Open Word List',
group: 'paratext.resourceTools',
order: 4,
command: 'helloWorld.openProject',
},
{
Expand Down

0 comments on commit 5ec68c6

Please sign in to comment.