mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[PUI] Quick commands pallet (#6987)
* add spotlight * [PUI] Quick commands pallet Fixes #5888 * add testing for new commands * add text input testing * only test backend if code changed * add trans files * fix testing text * always push coverage * add nav state to manage navigation state * add navigation action and test * make test faster * fix typo * use texts instead * fix tests for linux * use var to determine action key * Revert "use texts instead" This reverts commit7771189556
. * add wait for input * split out keyboard based tests * split ou test * add upload * revert assert change * adjust reporting settings * ignore error code * fix reporter config * add full info suit (+tests) * make tests more accurate * license modal fixes * unify icons * add custom actions registering with removal on page refresh * only upload report data if the tests failed * Revert "add trans files" This reverts commit28d96e058f
. * adjust url that iw waited for * try an await and body locator for keypresses * test registering addition actions * extend testing for actions * add doclink and test * merge tests
This commit is contained in:
parent
ff8eeca8c0
commit
cbbdb70762
@ -27,6 +27,7 @@
|
||||
"@mantine/hooks": "<7",
|
||||
"@mantine/modals": "<7",
|
||||
"@mantine/notifications": "<7",
|
||||
"@mantine/spotlight": "<7",
|
||||
"@naisutech/react-tree": "^3.1.0",
|
||||
"@sentry/react": "^7.109.0",
|
||||
"@tabler/icons-react": "^3.1.0",
|
||||
|
15
src/frontend/src/components/buttons/SpotlightButton.tsx
Normal file
15
src/frontend/src/components/buttons/SpotlightButton.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { ActionIcon } from '@mantine/core';
|
||||
import { spotlight } from '@mantine/spotlight';
|
||||
import { IconCommand } from '@tabler/icons-react';
|
||||
|
||||
/**
|
||||
* A button which opens the quick command modal
|
||||
*/
|
||||
export function SpotlightButton() {
|
||||
return (
|
||||
<ActionIcon onClick={() => spotlight.open()} title={t`Open spotlight`}>
|
||||
<IconCommand />
|
||||
</ActionIcon>
|
||||
);
|
||||
}
|
@ -16,6 +16,10 @@ export interface MenuLinkItem {
|
||||
docchildren?: React.ReactNode;
|
||||
}
|
||||
|
||||
export type menuItemsCollection = {
|
||||
[key: string]: MenuLinkItem;
|
||||
};
|
||||
|
||||
function ConditionalDocTooltip({
|
||||
item,
|
||||
children
|
||||
|
@ -2,7 +2,7 @@ import { ActionIcon, Container, Group, Indicator, Tabs } from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { IconBell, IconSearch } from '@tabler/icons-react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useMatch, useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { api } from '../../App';
|
||||
@ -10,7 +10,9 @@ import { navTabs as mainNavTabs } from '../../defaults/links';
|
||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||
import { InvenTreeStyle } from '../../globalStyle';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
import { useLocalState } from '../../states/LocalState';
|
||||
import { ScanButton } from '../buttons/ScanButton';
|
||||
import { SpotlightButton } from '../buttons/SpotlightButton';
|
||||
import { MainMenu } from './MainMenu';
|
||||
import { NavHoverMenu } from './NavHoverMenu';
|
||||
import { NavigationDrawer } from './NavigationDrawer';
|
||||
@ -19,8 +21,12 @@ import { SearchDrawer } from './SearchDrawer';
|
||||
|
||||
export function Header() {
|
||||
const { classes } = InvenTreeStyle();
|
||||
const [setNavigationOpen, navigationOpen] = useLocalState((state) => [
|
||||
state.setNavigationOpen,
|
||||
state.navigationOpen
|
||||
]);
|
||||
const [navDrawerOpened, { open: openNavDrawer, close: closeNavDrawer }] =
|
||||
useDisclosure(false);
|
||||
useDisclosure(navigationOpen);
|
||||
const [
|
||||
searchDrawerOpened,
|
||||
{ open: openSearchDrawer, close: closeSearchDrawer }
|
||||
@ -59,6 +65,18 @@ export function Header() {
|
||||
refetchOnWindowFocus: false
|
||||
});
|
||||
|
||||
// Sync Navigation Drawer state with zustand
|
||||
useEffect(() => {
|
||||
if (navigationOpen === navDrawerOpened) return;
|
||||
setNavigationOpen(navDrawerOpened);
|
||||
}, [navDrawerOpened]);
|
||||
|
||||
useEffect(() => {
|
||||
if (navigationOpen === navDrawerOpened) return;
|
||||
if (navigationOpen) openNavDrawer();
|
||||
else closeNavDrawer();
|
||||
}, [navigationOpen]);
|
||||
|
||||
return (
|
||||
<div className={classes.layoutHeader}>
|
||||
<SearchDrawer opened={searchDrawerOpened} onClose={closeSearchDrawer} />
|
||||
@ -80,6 +98,7 @@ export function Header() {
|
||||
<ActionIcon onClick={openSearchDrawer}>
|
||||
<IconSearch />
|
||||
</ActionIcon>
|
||||
<SpotlightButton />
|
||||
<ScanButton />
|
||||
<ActionIcon onClick={openNotificationDrawer}>
|
||||
<Indicator
|
||||
|
@ -1,6 +1,11 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import { Container, Flex, Space } from '@mantine/core';
|
||||
import { Navigate, Outlet, useLocation } from 'react-router-dom';
|
||||
import { SpotlightProvider } from '@mantine/spotlight';
|
||||
import { IconSearch } from '@tabler/icons-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom';
|
||||
|
||||
import { getActions } from '../../defaults/actions';
|
||||
import { InvenTreeStyle } from '../../globalStyle';
|
||||
import { useSessionState } from '../../states/SessionState';
|
||||
import { Footer } from './Footer';
|
||||
@ -22,17 +27,43 @@ export const ProtectedRoute = ({ children }: { children: JSX.Element }) => {
|
||||
|
||||
export default function LayoutComponent() {
|
||||
const { classes } = InvenTreeStyle();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const defaultactions = getActions(navigate);
|
||||
const [actions, setActions] = useState(defaultactions);
|
||||
const [customActions, setCustomActions] = useState<boolean>(false);
|
||||
|
||||
function actionsAreChanging(change: []) {
|
||||
if (change.length > defaultactions.length) setCustomActions(true);
|
||||
setActions(change);
|
||||
}
|
||||
useEffect(() => {
|
||||
if (customActions) {
|
||||
setActions(defaultactions);
|
||||
setCustomActions(false);
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
return (
|
||||
<ProtectedRoute>
|
||||
<Flex direction="column" mih="100vh">
|
||||
<Header />
|
||||
<Container className={classes.layoutContent} size="100%">
|
||||
<Outlet />
|
||||
</Container>
|
||||
<Space h="xl" />
|
||||
<Footer />
|
||||
</Flex>
|
||||
<SpotlightProvider
|
||||
actions={actions}
|
||||
onActionsChange={actionsAreChanging}
|
||||
searchIcon={<IconSearch size="1.2rem" />}
|
||||
searchPlaceholder={t`Search...`}
|
||||
shortcut={['mod + K', '/']}
|
||||
nothingFoundMessage={t`Nothing found...`}
|
||||
>
|
||||
<Flex direction="column" mih="100vh">
|
||||
<Header />
|
||||
<Container className={classes.layoutContent} size="100%">
|
||||
<Outlet />
|
||||
</Container>
|
||||
<Space h="xl" />
|
||||
<Footer />
|
||||
</Flex>
|
||||
</SpotlightProvider>
|
||||
</ProtectedRoute>
|
||||
);
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ import { useLocalState } from '../../states/LocalState';
|
||||
import { InvenTreeLogo } from '../items/InvenTreeLogo';
|
||||
import { MenuLinks } from '../items/MenuLinks';
|
||||
|
||||
const onlyItems = Object.values(menuItems);
|
||||
|
||||
export function NavHoverMenu({
|
||||
openDrawer: openDrawer
|
||||
}: {
|
||||
@ -85,7 +87,7 @@ export function NavHoverMenu({
|
||||
mx="-md"
|
||||
color={theme.colorScheme === 'dark' ? 'dark.5' : 'gray.1'}
|
||||
/>
|
||||
<MenuLinks links={menuItems} highlighted={true} />
|
||||
<MenuLinks links={onlyItems} highlighted={true} />
|
||||
<div className={classes.headerDropdownFooter}>
|
||||
<Group position="apart">
|
||||
<div>
|
||||
|
@ -18,6 +18,7 @@ import { MenuLinkItem, MenuLinks } from '../items/MenuLinks';
|
||||
|
||||
// TODO @matmair #1: implement plugin loading and menu item generation see #5269
|
||||
const plugins: MenuLinkItem[] = [];
|
||||
const onlyItems = Object.values(menuItems);
|
||||
|
||||
export function NavigationDrawer({
|
||||
opened,
|
||||
@ -60,7 +61,7 @@ function DrawerContent() {
|
||||
<Container className={classes.layoutContent} p={0}>
|
||||
<ScrollArea h={scrollHeight} type="always" offsetScrollbars>
|
||||
<Title order={5}>{t`Pages`}</Title>
|
||||
<MenuLinks links={menuItems} />
|
||||
<MenuLinks links={onlyItems} />
|
||||
<Space h="md" />
|
||||
{plugins.length > 0 ? (
|
||||
<>
|
||||
|
@ -101,6 +101,7 @@ export function LanguageContext({ children }: { children: JSX.Element }) {
|
||||
// Clear out cached table column names
|
||||
useLocalState.getState().clearTableColumnNames();
|
||||
})
|
||||
/* istanbul ignore next */
|
||||
.catch((err) => {
|
||||
console.error('Failed loading translations', err);
|
||||
if (isMounted.current) setLoadedState('error');
|
||||
@ -115,6 +116,7 @@ export function LanguageContext({ children }: { children: JSX.Element }) {
|
||||
return <LoadingOverlay visible={true} />;
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (loadedState === 'error') {
|
||||
return (
|
||||
<Text>
|
||||
|
59
src/frontend/src/defaults/actions.tsx
Normal file
59
src/frontend/src/defaults/actions.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import type { SpotlightAction } from '@mantine/spotlight';
|
||||
import { IconHome, IconLink, IconPointer } from '@tabler/icons-react';
|
||||
import { NavigateFunction } from 'react-router-dom';
|
||||
|
||||
import { useLocalState } from '../states/LocalState';
|
||||
import { aboutInvenTree, docLinks, licenseInfo, serverInfo } from './links';
|
||||
import { menuItems } from './menuItems';
|
||||
|
||||
export function getActions(navigate: NavigateFunction) {
|
||||
const setNavigationOpen = useLocalState((state) => state.setNavigationOpen);
|
||||
|
||||
const actions: SpotlightAction[] = [
|
||||
{
|
||||
title: t`Home`,
|
||||
description: `Go to the home page`,
|
||||
onTrigger: () => navigate(menuItems.home.link),
|
||||
icon: <IconHome size="1.2rem" />
|
||||
},
|
||||
{
|
||||
title: t`Dashboard`,
|
||||
description: t`Go to the InvenTree dashboard`,
|
||||
onTrigger: () => navigate(menuItems.dashboard.link),
|
||||
icon: <IconLink size="1.2rem" />
|
||||
},
|
||||
{
|
||||
title: t`Documentation`,
|
||||
description: t`Visit the documentation to learn more about InvenTree`,
|
||||
onTrigger: () => (window.location.href = docLinks.faq),
|
||||
icon: <IconLink size="1.2rem" />
|
||||
},
|
||||
{
|
||||
title: t`About InvenTree`,
|
||||
description: t`About the InvenTree org`,
|
||||
onTrigger: () => aboutInvenTree(),
|
||||
icon: <IconLink size="1.2rem" />
|
||||
},
|
||||
{
|
||||
title: t`Server Information`,
|
||||
description: t`About this Inventree instance`,
|
||||
onTrigger: () => serverInfo(),
|
||||
icon: <IconLink size="1.2rem" />
|
||||
},
|
||||
{
|
||||
title: t`License Information`,
|
||||
description: t`Licenses for dependencies of the service`,
|
||||
onTrigger: () => licenseInfo(),
|
||||
icon: <IconLink size="1.2rem" />
|
||||
},
|
||||
{
|
||||
title: t`Open Navigation`,
|
||||
description: t`Open the main navigation menu`,
|
||||
onTrigger: () => setNavigationOpen(true),
|
||||
icon: <IconPointer size="1.2rem" />
|
||||
}
|
||||
];
|
||||
|
||||
return actions;
|
||||
}
|
@ -71,7 +71,7 @@ export const navDocLinks: DocumentationLinkItem[] = [
|
||||
}
|
||||
];
|
||||
|
||||
function serverInfo() {
|
||||
export function serverInfo() {
|
||||
return openContextModal({
|
||||
modal: 'info',
|
||||
title: (
|
||||
@ -84,7 +84,7 @@ function serverInfo() {
|
||||
});
|
||||
}
|
||||
|
||||
function aboutInvenTree() {
|
||||
export function aboutInvenTree() {
|
||||
return openContextModal({
|
||||
modal: 'about',
|
||||
title: (
|
||||
@ -96,7 +96,8 @@ function aboutInvenTree() {
|
||||
innerProps: {}
|
||||
});
|
||||
}
|
||||
function licenseInfo() {
|
||||
|
||||
export function licenseInfo() {
|
||||
return openContextModal({
|
||||
modal: 'license',
|
||||
title: (
|
||||
|
@ -1,75 +1,75 @@
|
||||
import { Trans } from '@lingui/macro';
|
||||
|
||||
import { MenuLinkItem } from '../components/items/MenuLinks';
|
||||
import { menuItemsCollection } from '../components/items/MenuLinks';
|
||||
import { IS_DEV_OR_DEMO } from '../main';
|
||||
|
||||
export const menuItems: MenuLinkItem[] = [
|
||||
{
|
||||
export const menuItems: menuItemsCollection = {
|
||||
home: {
|
||||
id: 'home',
|
||||
text: <Trans>Home</Trans>,
|
||||
link: '/',
|
||||
highlight: true
|
||||
},
|
||||
{
|
||||
profile: {
|
||||
id: 'profile',
|
||||
text: <Trans>Account settings</Trans>,
|
||||
link: '/settings/user',
|
||||
doctext: <Trans>User attributes and design settings.</Trans>
|
||||
},
|
||||
{
|
||||
scan: {
|
||||
id: 'scan',
|
||||
text: <Trans>Scanning</Trans>,
|
||||
link: '/scan',
|
||||
doctext: <Trans>View for interactive scanning and multiple actions.</Trans>,
|
||||
highlight: true
|
||||
},
|
||||
{
|
||||
dashboard: {
|
||||
id: 'dashboard',
|
||||
text: <Trans>Dashboard</Trans>,
|
||||
link: '/dashboard'
|
||||
},
|
||||
{
|
||||
parts: {
|
||||
id: 'parts',
|
||||
text: <Trans>Parts</Trans>,
|
||||
link: '/part/'
|
||||
},
|
||||
{
|
||||
stock: {
|
||||
id: 'stock',
|
||||
text: <Trans>Stock</Trans>,
|
||||
link: '/stock'
|
||||
},
|
||||
{
|
||||
build: {
|
||||
id: 'build',
|
||||
text: <Trans>Build</Trans>,
|
||||
link: '/build/'
|
||||
},
|
||||
{
|
||||
purchasing: {
|
||||
id: 'purchasing',
|
||||
text: <Trans>Purchasing</Trans>,
|
||||
link: '/purchasing/'
|
||||
},
|
||||
{
|
||||
sales: {
|
||||
id: 'sales',
|
||||
text: <Trans>Sales</Trans>,
|
||||
link: '/sales/'
|
||||
},
|
||||
{
|
||||
'settings-system': {
|
||||
id: 'settings-system',
|
||||
text: <Trans>System Settings</Trans>,
|
||||
link: '/settings/system'
|
||||
},
|
||||
{
|
||||
'settings-admin': {
|
||||
id: 'settings-admin',
|
||||
text: <Trans>Admin Center</Trans>,
|
||||
link: '/settings/admin'
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
if (IS_DEV_OR_DEMO) {
|
||||
menuItems.push({
|
||||
menuItems['playground'] = {
|
||||
id: 'playground',
|
||||
text: <Trans>Playground</Trans>,
|
||||
link: '/playground',
|
||||
highlight: true
|
||||
});
|
||||
};
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ import { Trans } from '@lingui/macro';
|
||||
import { Button, Card, Stack, TextInput } from '@mantine/core';
|
||||
import { Group, Text } from '@mantine/core';
|
||||
import { Accordion } from '@mantine/core';
|
||||
import { spotlight } from '@mantine/spotlight';
|
||||
import { IconAlien } from '@tabler/icons-react';
|
||||
import { ReactNode, useMemo, useState } from 'react';
|
||||
|
||||
import { OptionsApiForm } from '../../components/forms/ApiForm';
|
||||
@ -167,6 +169,38 @@ function StatusLabelPlayground() {
|
||||
);
|
||||
}
|
||||
|
||||
// Sample for spotlight actions
|
||||
function SpotlighPlayground() {
|
||||
return (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
spotlight.registerActions([
|
||||
{
|
||||
id: 'secret-action-1',
|
||||
title: 'Secret action',
|
||||
description: 'It was registered with a button click',
|
||||
icon: <IconAlien size="1.2rem" />,
|
||||
onTrigger: () => console.log('Secret')
|
||||
},
|
||||
{
|
||||
id: 'secret-action-2',
|
||||
title: 'Another secret action',
|
||||
description:
|
||||
'You can register multiple actions with just one command',
|
||||
icon: <IconAlien size="1.2rem" />,
|
||||
onTrigger: () => console.log('Secret')
|
||||
}
|
||||
]);
|
||||
console.log('registed');
|
||||
spotlight.open();
|
||||
}}
|
||||
>
|
||||
Register extra actions
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
/** Construct a simple accordion group with title and content */
|
||||
function PlaygroundArea({
|
||||
title,
|
||||
@ -207,6 +241,10 @@ export default function Playground() {
|
||||
title="Status labels"
|
||||
content={<StatusLabelPlayground />}
|
||||
/>
|
||||
<PlaygroundArea
|
||||
title="Spotlight actions"
|
||||
content={<SpotlighPlayground />}
|
||||
/>
|
||||
</Accordion>
|
||||
</>
|
||||
);
|
||||
|
@ -31,6 +31,8 @@ interface LocalStateProps {
|
||||
clearTableColumnNames: () => void;
|
||||
detailDrawerStack: number;
|
||||
addDetailDrawer: (value: number | false) => void;
|
||||
navigationOpen: boolean;
|
||||
setNavigationOpen: (value: boolean) => void;
|
||||
}
|
||||
|
||||
export const useLocalState = create<LocalStateProps>()(
|
||||
@ -87,6 +89,11 @@ export const useLocalState = create<LocalStateProps>()(
|
||||
detailDrawerStack:
|
||||
value === false ? 0 : get().detailDrawerStack + value
|
||||
});
|
||||
},
|
||||
// navigation
|
||||
navigationOpen: false,
|
||||
setNavigationOpen: (value) => {
|
||||
set({ navigationOpen: value });
|
||||
}
|
||||
}),
|
||||
{
|
||||
|
@ -1,11 +1,22 @@
|
||||
import { test as baseTest } from '@playwright/test';
|
||||
import * as crypto from 'crypto';
|
||||
import * as fs from 'fs';
|
||||
import os from 'os';
|
||||
import * as path from 'path';
|
||||
|
||||
const istanbulCLIOutput = path.join(process.cwd(), '.nyc_output');
|
||||
export const classicUrl = 'http://127.0.0.1:8000';
|
||||
|
||||
let platform = os.platform();
|
||||
let systemKeyVar;
|
||||
if (platform === 'darwin') {
|
||||
systemKeyVar = 'Meta';
|
||||
} else {
|
||||
systemKeyVar = 'Control';
|
||||
}
|
||||
/* metaKey is the local action key (used for spotlight for example) */
|
||||
export const systemKey = systemKeyVar;
|
||||
|
||||
export function generateUUID(): string {
|
||||
return crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
|
147
src/frontend/tests/pui_command.spec.ts
Normal file
147
src/frontend/tests/pui_command.spec.ts
Normal file
@ -0,0 +1,147 @@
|
||||
import { expect, systemKey, test } from './baseFixtures.js';
|
||||
|
||||
test('PUI - Quick Command', async ({ page }) => {
|
||||
await page.goto('./platform/');
|
||||
await expect(page).toHaveTitle('InvenTree');
|
||||
await page.waitForURL('**/platform/');
|
||||
await page.getByLabel('username').fill('allaccess');
|
||||
await page.getByLabel('password').fill('nolimits');
|
||||
await page.getByRole('button', { name: 'Log in' }).click();
|
||||
await page.waitForURL('**/platform');
|
||||
await page.goto('./platform/');
|
||||
|
||||
await expect(page).toHaveTitle('InvenTree');
|
||||
await page.waitForURL('**/platform/');
|
||||
await page
|
||||
.getByRole('heading', { name: 'Welcome to your Dashboard,' })
|
||||
.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Open Spotlight with Keyboard Shortcut
|
||||
await page.locator('body').press(`${systemKey}+k`);
|
||||
await page.waitForTimeout(200);
|
||||
await page
|
||||
.getByRole('button', { name: 'Dashboard Go to the InvenTree dashboard' })
|
||||
.click();
|
||||
await page
|
||||
.locator('div')
|
||||
.filter({ hasText: /^Dashboard$/ })
|
||||
.click();
|
||||
await page.waitForURL('**/platform/dashboard');
|
||||
|
||||
// Open Spotlight with Button
|
||||
await page.getByRole('button', { name: 'Open spotlight' }).click();
|
||||
await page.getByRole('button', { name: 'Home Go to the home page' }).click();
|
||||
await page
|
||||
.getByRole('heading', { name: 'Welcome to your Dashboard,' })
|
||||
.click();
|
||||
await page.waitForURL('**/platform');
|
||||
|
||||
// Open Spotlight with Keyboard Shortcut and Search
|
||||
await page.locator('body').press(`${systemKey}+k`);
|
||||
await page.waitForTimeout(200);
|
||||
await page.getByPlaceholder('Search...').fill('Dashboard');
|
||||
await page.getByPlaceholder('Search...').press('Tab');
|
||||
await page.getByPlaceholder('Search...').press('Enter');
|
||||
await page.waitForURL('**/platform/dashboard');
|
||||
});
|
||||
|
||||
test('PUI - Quick Command - no keys', async ({ page }) => {
|
||||
await page.goto('./platform/');
|
||||
await expect(page).toHaveTitle('InvenTree');
|
||||
await page.waitForURL('**/platform/');
|
||||
await page.getByLabel('username').fill('allaccess');
|
||||
await page.getByLabel('password').fill('nolimits');
|
||||
await page.getByRole('button', { name: 'Log in' }).click();
|
||||
await page.waitForURL('**/platform');
|
||||
|
||||
await expect(page).toHaveTitle('InvenTree');
|
||||
await page.waitForURL('**/platform');
|
||||
// wait for the page to load - 0.5s
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Open Spotlight with Button
|
||||
await page.getByRole('button', { name: 'Open spotlight' }).click();
|
||||
await page.getByRole('button', { name: 'Home Go to the home page' }).click();
|
||||
await page
|
||||
.getByRole('heading', { name: 'Welcome to your Dashboard,' })
|
||||
.click();
|
||||
await page.waitForURL('**/platform');
|
||||
|
||||
// Use navigation menu
|
||||
await page.getByRole('button', { name: 'Open spotlight' }).click();
|
||||
await page
|
||||
.getByRole('button', { name: 'Open Navigation Open the main' })
|
||||
.click();
|
||||
// assert the nav headers are visible
|
||||
await page.getByRole('heading', { name: 'Navigation' }).waitFor();
|
||||
await page.getByRole('heading', { name: 'Pages' }).waitFor();
|
||||
await page.getByRole('heading', { name: 'Documentation' }).waitFor();
|
||||
await page.getByRole('heading', { name: 'About' }).waitFor();
|
||||
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
// use server info
|
||||
await page.getByRole('button', { name: 'Open spotlight' }).click();
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Server Information About this Inventree instance'
|
||||
})
|
||||
.click();
|
||||
await page.getByRole('cell', { name: 'Instance Name' }).waitFor();
|
||||
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||
|
||||
await page.waitForURL('**/platform');
|
||||
|
||||
// use license info
|
||||
await page.getByRole('button', { name: 'Open spotlight' }).click();
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'License Information Licenses for dependencies of the service'
|
||||
})
|
||||
.click();
|
||||
await page.getByText('License Information').first().waitFor();
|
||||
await page.getByRole('tab', { name: 'backend Packages' }).waitFor();
|
||||
|
||||
await page.getByLabel('License Information').getByRole('button').click();
|
||||
|
||||
// use about
|
||||
await page.getByRole('button', { name: 'Open spotlight' }).click();
|
||||
await page
|
||||
.getByRole('button', { name: 'About InvenTree About the InvenTree org' })
|
||||
.click();
|
||||
await page.getByText('This information is only').waitFor();
|
||||
|
||||
await page.getByLabel('About InvenTree').getByRole('button').click();
|
||||
|
||||
// use documentation
|
||||
await page.getByRole('button', { name: 'Open spotlight' }).click();
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Documentation Visit the documentation to learn more about InvenTree'
|
||||
})
|
||||
.click();
|
||||
await page.waitForURL('https://docs.inventree.org/**');
|
||||
|
||||
// Test addition of new actions
|
||||
await page.goto('./platform/playground');
|
||||
await page
|
||||
.locator('div')
|
||||
.filter({ hasText: /^Playground$/ })
|
||||
.waitFor();
|
||||
await page.getByRole('button', { name: 'Spotlight actions' }).click();
|
||||
await page.getByRole('button', { name: 'Register extra actions' }).click();
|
||||
await page.getByPlaceholder('Search...').fill('secret');
|
||||
await page.getByRole('button', { name: 'Secret action It was' }).click();
|
||||
await page.getByRole('button', { name: 'Open spotlight' }).click();
|
||||
await page.getByPlaceholder('Search...').fill('Another secret action');
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'Another secret action You can register multiple actions with just one command'
|
||||
})
|
||||
.click();
|
||||
await page.getByRole('tab', { name: 'Home' }).click();
|
||||
await page.getByRole('button', { name: 'Open spotlight' }).click();
|
||||
await page.getByPlaceholder('Search...').fill('secret');
|
||||
await page.getByText('Nothing found...').click();
|
||||
});
|
@ -1156,6 +1156,13 @@
|
||||
"@mantine/utils" "6.0.21"
|
||||
react-transition-group "4.4.2"
|
||||
|
||||
"@mantine/spotlight@<7":
|
||||
version "6.0.21"
|
||||
resolved "https://registry.yarnpkg.com/@mantine/spotlight/-/spotlight-6.0.21.tgz#98f507bd3429fee1f2b57ad5ef9f88d1d8d8ff32"
|
||||
integrity sha512-xJqF2Vpn8s6I4mSF+iCi7IzqL8iaqbvq0RcYlF1usLZYW2HrArX31s1r11DmzqM1PIuBQUhquW8jUXx/MZy3oA==
|
||||
dependencies:
|
||||
"@mantine/utils" "6.0.21"
|
||||
|
||||
"@mantine/styles@6.0.21":
|
||||
version "6.0.21"
|
||||
resolved "https://registry.yarnpkg.com/@mantine/styles/-/styles-6.0.21.tgz#8ea097fc76cbb3ed55f5cfd719d2f910aff5031b"
|
||||
|
Loading…
Reference in New Issue
Block a user