@centreon/ui 26.10.7 → 26.10.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/package.json +6 -3
  2. package/public/mockServiceWorker.js +31 -8
  3. package/src/Dashboard/Dashboard.browser.test.tsx +107 -0
  4. package/src/Form/Form.browser.test.tsx +445 -0
  5. package/src/Form/Inputs/ConnectedAutocomplete.tsx +12 -4
  6. package/src/Form/Inputs/ExclusiveCheckboxGroup.browser.test.tsx +304 -0
  7. package/src/Form/Inputs/ExclusiveCheckboxGroup.tsx +60 -15
  8. package/src/Form/Inputs/LoadingSkeleton.tsx +4 -0
  9. package/src/Form/Inputs/SegmentedButtons.browser.test.tsx +113 -0
  10. package/src/Form/Inputs/SegmentedButtons.tsx +71 -0
  11. package/src/Form/Inputs/index.tsx +5 -0
  12. package/src/Form/Inputs/models.ts +16 -1
  13. package/src/InputField/Number/Number.browser.test.tsx +109 -0
  14. package/src/InputField/Select/Autocomplete/Connected/Multi/MultiConnectedAutocompleteField.browser.test.tsx +250 -0
  15. package/src/InputField/Select/Autocomplete/Connected/index.test.tsx +37 -0
  16. package/src/InputField/Select/Autocomplete/Connected/index.tsx +59 -45
  17. package/src/Listing/Cell/DataCell.tsx +0 -2
  18. package/src/Listing/Listing.browser.test.tsx +476 -0
  19. package/src/Module/Module.browser.test.tsx +145 -0
  20. package/src/Pagination/Pagination.browser.test.tsx +184 -0
  21. package/src/RichTextEditor/RichTextEditor.browser.test.tsx +567 -0
  22. package/src/RichTextEditor/plugins/FloatingLinkEditorPlugin.tsx +3 -1
  23. package/src/TimePeriods/ResolutionTimePeriod.browser.test.tsx +143 -0
  24. package/src/TimePeriods/TimePeriods.browser.test.tsx +468 -0
  25. package/src/Typography/FluidTypography/FluidTypography.browser.test.tsx +54 -0
  26. package/src/api/useMutationQuery/useMutationQuery.browser.test.tsx +154 -0
  27. package/src/components/CollapsibleItem/CollapsibleItem.browser.test.tsx +120 -0
  28. package/src/components/CopyCommand/CopyCommand.browser.test.tsx +287 -0
  29. package/src/components/CrudPage/CrudPage.browser.test.tsx +659 -0
  30. package/src/components/DataTable/DataTable.browser.test.tsx +257 -0
  31. package/src/components/Form/AccessRights/AccessRights.browser.test.tsx +527 -0
  32. package/src/components/ItemComposition/ItemComposition.browser.test.tsx +172 -0
  33. package/src/components/Layout/PageLayout.browser.test.tsx +114 -0
  34. package/src/components/Modal/ConfirmationModal/ConfirmationModal.browser.test.tsx +237 -0
  35. package/src/components/SortableEntriesList/SortableEntriesList.browser.test.tsx +981 -0
  36. package/src/components/Tabs/Tabs.browser.test.tsx +115 -0
  37. package/src/utils/useFullscreen/Fullscreen.browser.test.tsx +176 -0
  38. package/src/utils/useLocale/useLocale.browser.test.tsx +49 -0
  39. package/src/utils/useLocaleTimezoneDate/{LocaleTimezoneDate.cypress.spec.tsx → LocaleTimezoneDate.browser.test.tsx} +24 -11
  40. package/src/Dashboard/Dashboard.cypress.spec.tsx +0 -71
  41. package/src/Form/Form.cypress.spec.tsx +0 -306
  42. package/src/Form/Inputs/ExclusiveCheckboxGroup.cypress.spec.tsx +0 -174
  43. package/src/InputField/Number/Number.cypress.spec.tsx +0 -89
  44. package/src/InputField/Select/Autocomplete/Connected/Multi/MultiConnectedAutocompleteField.cypress.spec.tsx +0 -182
  45. package/src/Listing/Listing.cypress.spec.tsx +0 -304
  46. package/src/Module/Module.cypress.spec.tsx +0 -127
  47. package/src/Pagination/Pagination.cypress.spec.tsx +0 -140
  48. package/src/RichTextEditor/RichTextEditor.cypress.spec.tsx +0 -408
  49. package/src/TimePeriods/ResolutionTimePeriod.cypress.spec.tsx +0 -74
  50. package/src/TimePeriods/TimePeriods.cypress.spec.tsx +0 -488
  51. package/src/Typography/FluidTypography/FluidTypography.cypress.spec.tsx +0 -27
  52. package/src/api/useMutationQuery/useMutationQuery.cypress.spec.tsx +0 -93
  53. package/src/components/CollapsibleItem/CollapsibleItem.cypress.spec.tsx +0 -76
  54. package/src/components/CopyCommand/CopyCommand.cypress.spec.tsx +0 -167
  55. package/src/components/CrudPage/CrudPage.cypress.spec.tsx +0 -561
  56. package/src/components/DataTable/DataTable.cypress.spec.tsx +0 -174
  57. package/src/components/Form/AccessRights/AccessRights.cypress.spec.tsx +0 -376
  58. package/src/components/ItemComposition/ItemComposition.cypress.spec.tsx +0 -112
  59. package/src/components/Layout/PageLayout.cypress.spec.tsx +0 -64
  60. package/src/components/Modal/ConfirmationModal/ConfirmationModal.cypress.spec.tsx +0 -171
  61. package/src/components/SortableEntriesList/SortableEntriesList.cypress.spec.tsx +0 -792
  62. package/src/components/Tabs/Tabs.cypress.spec.tsx +0 -69
  63. package/src/utils/useFullscreen/Fullscreen.cypress.spec.tsx +0 -130
  64. package/src/utils/useLocale/useLocale.cypress.spec.tsx +0 -40
@@ -0,0 +1,115 @@
1
+ import { Typography } from '@mui/material';
2
+
3
+ import { render } from '@test-setup/render';
4
+ import { screen, waitFor } from '@testing-library/react';
5
+ import userEvent from '@testing-library/user-event';
6
+
7
+ import { Tabs } from '.';
8
+ import { TabPanel } from './TabPanel';
9
+
10
+ const initialize = (withTabListProps = false): void => {
11
+ render(
12
+ <Tabs
13
+ defaultTab="tab 0"
14
+ tabList={
15
+ withTabListProps
16
+ ? {
17
+ variant: 'fullWidth'
18
+ }
19
+ : undefined
20
+ }
21
+ tabs={[
22
+ { label: 'Tab 0', value: 'tab 0' },
23
+ { label: 'Tab 1', value: 'tab 1' }
24
+ ]}
25
+ >
26
+ <TabPanel value="tab 0">
27
+ <Typography>Tab 0</Typography>
28
+ </TabPanel>
29
+ <TabPanel value="tab 1">
30
+ <Typography>Tab 1</Typography>
31
+ </TabPanel>
32
+ </Tabs>
33
+ );
34
+ };
35
+
36
+ const getTabPanel = (value: string): HTMLElement =>
37
+ document.querySelector(`[data-TabPanel="${value}"]`) as HTMLElement;
38
+
39
+ const getFontWeight = (element: HTMLElement): string =>
40
+ getComputedStyle(element).fontWeight;
41
+
42
+ describe('Tabs', () => {
43
+ beforeEach(() => {
44
+ expect.hasAssertions();
45
+ });
46
+
47
+ it('displays tabs and their content when a tab is selected', async () => {
48
+ initialize();
49
+
50
+ await waitFor(() => {
51
+ expect(getTabPanel('tab 0')).not.toHaveAttribute('hidden');
52
+ });
53
+ expect(getTabPanel('tab 1')).toHaveAttribute('hidden');
54
+ expect(screen.getByLabelText('Tab 0')).toHaveAttribute(
55
+ 'aria-selected',
56
+ 'true'
57
+ );
58
+ expect(screen.getByLabelText('Tab 1')).toHaveAttribute(
59
+ 'aria-selected',
60
+ 'false'
61
+ );
62
+
63
+ await userEvent.click(screen.getByRole('tab', { name: 'Tab 1' }));
64
+
65
+ await waitFor(() => {
66
+ expect(getTabPanel('tab 0')).toHaveAttribute('hidden');
67
+ });
68
+ expect(getTabPanel('tab 1')).not.toHaveAttribute('hidden');
69
+ expect(screen.getByLabelText('Tab 0')).toHaveAttribute(
70
+ 'aria-selected',
71
+ 'false'
72
+ );
73
+ expect(screen.getByLabelText('Tab 1')).toHaveAttribute(
74
+ 'aria-selected',
75
+ 'true'
76
+ );
77
+
78
+ // The selected tab in bold, the other one not, and the content of the
79
+ // selected panel only.
80
+ expect(getFontWeight(screen.getByLabelText('Tab 1'))).toBe('700');
81
+ expect(getFontWeight(screen.getByLabelText('Tab 0'))).toBe('400');
82
+ expect(getTabPanel('tab 1')).toHaveTextContent(/^Tab 1$/);
83
+ expect(getTabPanel('tab 1')).toBeVisible();
84
+ expect(getTabPanel('tab 0')).not.toBeVisible();
85
+ });
86
+
87
+ it('displays tabs when tabList props are set', async () => {
88
+ initialize(true);
89
+
90
+ await waitFor(() => {
91
+ expect(getTabPanel('tab 0')).not.toHaveAttribute('hidden');
92
+ });
93
+ expect(getTabPanel('tab 1')).toHaveAttribute('hidden');
94
+
95
+ // The first tab selected, in bold, and the two tabs sharing the full
96
+ // width of the page.
97
+ const firstTab = screen.getByLabelText('Tab 0');
98
+ const secondTab = screen.getByLabelText('Tab 1');
99
+
100
+ expect(firstTab).toHaveAttribute('aria-selected', 'true');
101
+ expect(getFontWeight(firstTab)).toBe('700');
102
+ expect(getFontWeight(secondTab)).toBe('400');
103
+ expect(getTabPanel('tab 0')).toHaveTextContent(/^Tab 0$/);
104
+
105
+ const tabList = screen.getByRole('tablist');
106
+ const tabListWidth = tabList.getBoundingClientRect().width;
107
+ const firstTabWidth = firstTab.getBoundingClientRect().width;
108
+
109
+ expect(firstTabWidth).toBeGreaterThan(tabListWidth / 2 - 32);
110
+ expect(secondTab.getBoundingClientRect().width).toBeCloseTo(
111
+ firstTabWidth,
112
+ 0
113
+ );
114
+ });
115
+ });
@@ -0,0 +1,176 @@
1
+ import { Button } from '@mui/material';
2
+
3
+ import { rs } from '@rstest/core';
4
+ import { render } from '@test-setup/render';
5
+ import { screen, waitFor } from '@testing-library/react';
6
+ import userEvent from '@testing-library/user-event';
7
+ import { createStore, Provider } from 'jotai';
8
+ import { BrowserRouter } from 'react-router';
9
+
10
+ import { useFullscreen } from './useFullscreen';
11
+ import { router, useFullscreenListener } from './useFullscreenListener';
12
+
13
+ const labelEnterFullscreen = 'Enter fullscreen';
14
+ const labelExitFullscreen = 'Exit fullscreen';
15
+
16
+ const ChildComponent = (): JSX.Element => {
17
+ const { toggleFullscreen, fullscreenEnabled, isFullscreenActivated } =
18
+ useFullscreen();
19
+
20
+ return (
21
+ <div
22
+ data-fullscreenActivated={isFullscreenActivated}
23
+ data-fullscreenEnabled={fullscreenEnabled}
24
+ id="test"
25
+ >
26
+ <Button onClick={() => toggleFullscreen(document.body)}>
27
+ {isFullscreenActivated ? labelExitFullscreen : labelEnterFullscreen}
28
+ </Button>
29
+ <input id="input" />
30
+ <textarea id="textarea" />
31
+ </div>
32
+ );
33
+ };
34
+
35
+ const TestComponent = (): JSX.Element => {
36
+ useFullscreenListener();
37
+
38
+ return <ChildComponent />;
39
+ };
40
+
41
+ // The real Fullscreen API is shared by the whole browser: with test files
42
+ // running in parallel pages, a request can be refused or exited by another
43
+ // page. It is faked here, with the same contract: the promises resolve, the
44
+ // fullscreen element is set and cleared, and `fullscreenchange` is fired.
45
+ const fakeFullscreenApi = (): void => {
46
+ let fullscreenElement: Element | null = null;
47
+
48
+ const changeFullscreenElement = (element: Element | null): Promise<void> => {
49
+ fullscreenElement = element;
50
+ document.dispatchEvent(new Event('fullscreenchange'));
51
+
52
+ return Promise.resolve();
53
+ };
54
+
55
+ rs.spyOn(Document.prototype, 'fullscreenElement', 'get').mockImplementation(
56
+ () => fullscreenElement
57
+ );
58
+ rs.spyOn(Document.prototype, 'fullscreenEnabled', 'get').mockReturnValue(
59
+ true
60
+ );
61
+ rs.spyOn(Element.prototype, 'requestFullscreen').mockImplementation(function (
62
+ this: Element
63
+ ) {
64
+ return changeFullscreenElement(this);
65
+ });
66
+ rs.spyOn(Document.prototype, 'exitFullscreen').mockImplementation(() =>
67
+ changeFullscreenElement(null)
68
+ );
69
+ };
70
+
71
+ const initialize = (): void => {
72
+ fakeFullscreenApi();
73
+
74
+ const store = createStore();
75
+
76
+ const queryParameters = new Map();
77
+
78
+ rs.spyOn(router, 'useSearchParams').mockImplementation(
79
+ () =>
80
+ queryParameters as unknown as ReturnType<typeof router.useSearchParams>
81
+ );
82
+
83
+ render(
84
+ <BrowserRouter>
85
+ <Provider store={store}>
86
+ <TestComponent />
87
+ </Provider>
88
+ </BrowserRouter>
89
+ );
90
+ };
91
+
92
+ const getTestElement = (): HTMLElement =>
93
+ document.querySelector('#test') as HTMLElement;
94
+
95
+ const expectFullscreen = async (activated: boolean): Promise<void> => {
96
+ await waitFor(() => {
97
+ expect(getTestElement()).toHaveAttribute(
98
+ 'data-fullscreenActivated',
99
+ `${activated}`
100
+ );
101
+ });
102
+ expect(getTestElement()).toHaveAttribute('data-fullscreenEnabled', 'true');
103
+ };
104
+
105
+ describe('Fullscreen', () => {
106
+ beforeEach(() => {
107
+ expect.hasAssertions();
108
+ });
109
+
110
+ afterEach(() => {
111
+ rs.restoreAllMocks();
112
+ });
113
+
114
+ it('enters fullscreen mode when the button is clicked', async () => {
115
+ initialize();
116
+
117
+ await expectFullscreen(false);
118
+
119
+ await userEvent.click(screen.getByText(labelEnterFullscreen));
120
+
121
+ await expectFullscreen(true);
122
+
123
+ await userEvent.click(screen.getByText(labelExitFullscreen));
124
+ });
125
+
126
+ it('exits fullscreen mode when the button is clicked', async () => {
127
+ initialize();
128
+
129
+ await expectFullscreen(false);
130
+
131
+ await userEvent.click(screen.getByText(labelEnterFullscreen));
132
+
133
+ await expectFullscreen(true);
134
+
135
+ await userEvent.click(screen.getByText(labelExitFullscreen));
136
+
137
+ await expectFullscreen(false);
138
+ });
139
+
140
+ it('toggles fullscreen mode when the corresponding shortcut is clicked', async () => {
141
+ initialize();
142
+
143
+ await expectFullscreen(false);
144
+
145
+ await userEvent.keyboard('F');
146
+
147
+ await expectFullscreen(true);
148
+
149
+ await userEvent.keyboard('F');
150
+
151
+ await expectFullscreen(false);
152
+ });
153
+
154
+ ['input', 'textarea'].forEach((tag) => {
155
+ it(`cannot toggle fullscreen feature using the shortcut when ${tag === 'input' ? 'an' : 'a'} ${tag} is focused`, async () => {
156
+ initialize();
157
+
158
+ await expectFullscreen(false);
159
+
160
+ // The spies (which call through) tell that the shortcut did not even
161
+ // ask for a change.
162
+ const requestFullscreen = rs.spyOn(document.body, 'requestFullscreen');
163
+ const exitFullscreen = rs.spyOn(document, 'exitFullscreen');
164
+ const field = document.querySelector(`#${tag}`) as HTMLInputElement;
165
+
166
+ field.focus();
167
+
168
+ await userEvent.keyboard('F');
169
+
170
+ await expectFullscreen(false);
171
+ expect(field).toHaveValue('F');
172
+ expect(requestFullscreen).not.toHaveBeenCalled();
173
+ expect(exitFullscreen).not.toHaveBeenCalled();
174
+ });
175
+ });
176
+ });
@@ -0,0 +1,49 @@
1
+ import { browserLocaleAtom, type User, userAtom } from '@centreon/ui-context';
2
+
3
+ import { render } from '@test-setup/render';
4
+ import { screen } from '@testing-library/react';
5
+ import { createStore, Provider } from 'jotai';
6
+
7
+ import { useLocale } from '.';
8
+
9
+ const TestComponent = () => {
10
+ const locale = useLocale();
11
+
12
+ return <p>{locale}</p>;
13
+ };
14
+
15
+ interface InitializeProps {
16
+ browserLocale: string;
17
+ userLocale: string | null;
18
+ }
19
+
20
+ const initialize = ({ userLocale, browserLocale }: InitializeProps): void => {
21
+ const store = createStore();
22
+
23
+ store.set(userAtom, { locale: userLocale } as User);
24
+ store.set(browserLocaleAtom, browserLocale);
25
+
26
+ render(
27
+ <Provider store={store}>
28
+ <TestComponent />
29
+ </Provider>
30
+ );
31
+ };
32
+
33
+ describe('useLocale', () => {
34
+ beforeEach(() => {
35
+ expect.hasAssertions();
36
+ });
37
+
38
+ it('displays the user locale when the corresponding atom is set', async () => {
39
+ initialize({ browserLocale: 'en', userLocale: 'fi' });
40
+
41
+ expect(await screen.findByText('fi')).toBeVisible();
42
+ });
43
+
44
+ it('displays the browser locale when the user locale is not set', async () => {
45
+ initialize({ browserLocale: 'de', userLocale: null });
46
+
47
+ expect(await screen.findByText('de')).toBeVisible();
48
+ });
49
+ });
@@ -9,6 +9,9 @@ import 'dayjs/locale/en';
9
9
  import 'dayjs/locale/es';
10
10
  import 'dayjs/locale/pt';
11
11
 
12
+ import { render } from '@test-setup/render';
13
+ import { screen } from '@testing-library/react';
14
+
12
15
  import { type Props, useLocaleTimezoneDate } from './useLocaleTimezoneDate';
13
16
 
14
17
  dayjs.extend(localizedFormat);
@@ -27,10 +30,12 @@ const Test = ({
27
30
  return <p>{formatDate({ date, format, locale, timezone })}</p>;
28
31
  };
29
32
 
30
- const initialize = ({ timezone, locale, format }): void => {
31
- cy.mount({
32
- Component: <Test format={format} locale={locale} timezone={timezone} />
33
- });
33
+ const initialize = ({
34
+ timezone,
35
+ locale,
36
+ format
37
+ }: Omit<Props, 'date'>): void => {
38
+ render(<Test format={format} locale={locale} timezone={timezone} />);
34
39
  };
35
40
 
36
41
  const locales = [undefined, 'en-GB', 'fr-FR', 'de-DE'];
@@ -83,16 +88,24 @@ const resultsDE = [
83
88
  ];
84
89
 
85
90
  const results = [...resultsEN, ...resultsEN, ...resultsFR, ...resultsDE];
86
- let index = 0;
87
91
 
88
- locales.forEach((locale) => {
89
- timezones.forEach((timezone) => {
90
- formats.forEach((format) => {
91
- it(`displays the date with the locale ${locale}, the timezone ${timezone} and the format ${format}`, () => {
92
+ beforeEach(() => {
93
+ expect.hasAssertions();
94
+ });
95
+
96
+ locales.forEach((locale, localeIndex) => {
97
+ timezones.forEach((timezone, timezoneIndex) => {
98
+ formats.forEach((format, formatIndex) => {
99
+ // The expected result of this combination, independent of the order the
100
+ // tests run in.
101
+ const index =
102
+ (localeIndex * timezones.length + timezoneIndex) * formats.length +
103
+ formatIndex;
104
+
105
+ it(`displays the date with the locale ${locale}, the timezone ${timezone} and the format ${format}`, async () => {
92
106
  initialize({ format, locale, timezone });
93
107
 
94
- cy.contains(results[index]).should('be.visible');
95
- index += 1;
108
+ expect(await screen.findByText(results[index])).toBeVisible();
96
109
  });
97
110
  });
98
111
  });
@@ -1,71 +0,0 @@
1
- import { Typography } from '@mui/material';
2
-
3
- import { DashboardLayout } from '.';
4
-
5
- const dashboardLayout: Array<CustomLayout> = [
6
- {
7
- content: 'Panel 1',
8
- h: 4,
9
- i: 'a',
10
- shouldUseFluidTypography: false,
11
- w: 6,
12
- x: 0,
13
- y: 0
14
- },
15
- {
16
- content: 'Panel 2',
17
- h: 3,
18
- i: 'b',
19
- minW: 2,
20
- w: 7,
21
- x: 1,
22
- y: 7
23
- },
24
- {
25
- content: 'Panel 3',
26
- h: 7,
27
- i: 'c',
28
- w: 6,
29
- x: 6,
30
- y: 6
31
- },
32
- {
33
- content: 'Panel 4',
34
- h: 3,
35
- i: 'd',
36
- minW: 2,
37
- w: 5,
38
- x: 4,
39
- y: 10
40
- }
41
- ];
42
-
43
- const initialize = (): void => {
44
- cy.adjustViewport();
45
-
46
- cy.mount({
47
- Component: (
48
- <DashboardLayout.Layout layout={dashboardLayout}>
49
- {dashboardLayout.map(({ i, content }) => (
50
- <DashboardLayout.Item header={<div>header</div>} id={i} key={i}>
51
- <Typography>{content}</Typography>
52
- </DashboardLayout.Item>
53
- ))}
54
- </DashboardLayout.Layout>
55
- )
56
- });
57
-
58
- cy.viewport('macbook-13');
59
- };
60
-
61
- describe('Dashboard', () => {
62
- it('displays placeholder when panels is out of viewport', () => {
63
- initialize();
64
-
65
- cy.get('[data-widget-skeleton="a"]').should('not.exist');
66
- cy.get('[data-widget-skeleton="b"]').should('not.exist');
67
- cy.get('[data-widget-skeleton="c"]').should('not.exist');
68
-
69
- cy.makeSnapshot();
70
- });
71
- });