@centreon/ui 26.12.1 → 26.12.2
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.
- package/package.json +6 -3
- package/public/mockServiceWorker.js +31 -8
- package/src/Dashboard/Dashboard.browser.test.tsx +107 -0
- package/src/Form/Form.browser.test.tsx +445 -0
- package/src/Form/Inputs/ExclusiveCheckboxGroup.browser.test.tsx +304 -0
- package/src/Form/Inputs/SegmentedButtons.browser.test.tsx +113 -0
- package/src/InputField/Number/Number.browser.test.tsx +109 -0
- package/src/InputField/Select/Autocomplete/Connected/Multi/MultiConnectedAutocompleteField.browser.test.tsx +250 -0
- package/src/Listing/Listing.browser.test.tsx +476 -0
- package/src/Module/Module.browser.test.tsx +145 -0
- package/src/Pagination/Pagination.browser.test.tsx +184 -0
- package/src/RichTextEditor/RichTextEditor.browser.test.tsx +567 -0
- package/src/RichTextEditor/plugins/FloatingLinkEditorPlugin.tsx +3 -1
- package/src/TimePeriods/ResolutionTimePeriod.browser.test.tsx +143 -0
- package/src/TimePeriods/TimePeriods.browser.test.tsx +468 -0
- package/src/Typography/FluidTypography/FluidTypography.browser.test.tsx +54 -0
- package/src/api/useMutationQuery/useMutationQuery.browser.test.tsx +154 -0
- package/src/components/CollapsibleItem/CollapsibleItem.browser.test.tsx +120 -0
- package/src/components/CopyCommand/CopyCommand.browser.test.tsx +287 -0
- package/src/components/CrudPage/CrudPage.browser.test.tsx +659 -0
- package/src/components/DataTable/DataTable.browser.test.tsx +257 -0
- package/src/components/Form/AccessRights/AccessRights.browser.test.tsx +527 -0
- package/src/components/ItemComposition/ItemComposition.browser.test.tsx +172 -0
- package/src/components/Layout/PageLayout.browser.test.tsx +114 -0
- package/src/components/Modal/ConfirmationModal/ConfirmationModal.browser.test.tsx +237 -0
- package/src/components/SortableEntriesList/SortableEntriesList.browser.test.tsx +981 -0
- package/src/components/Tabs/Tabs.browser.test.tsx +115 -0
- package/src/utils/useFullscreen/Fullscreen.browser.test.tsx +176 -0
- package/src/utils/useLocale/useLocale.browser.test.tsx +49 -0
- package/src/utils/useLocaleTimezoneDate/{LocaleTimezoneDate.cypress.spec.tsx → LocaleTimezoneDate.browser.test.tsx} +24 -11
- package/src/Dashboard/Dashboard.cypress.spec.tsx +0 -71
- package/src/Form/Form.cypress.spec.tsx +0 -306
- package/src/Form/Inputs/ExclusiveCheckboxGroup.cypress.spec.tsx +0 -236
- package/src/Form/Inputs/SegmentedButtons.cypress.spec.tsx +0 -101
- package/src/InputField/Number/Number.cypress.spec.tsx +0 -89
- package/src/InputField/Select/Autocomplete/Connected/Multi/MultiConnectedAutocompleteField.cypress.spec.tsx +0 -182
- package/src/Listing/Listing.cypress.spec.tsx +0 -349
- package/src/Module/Module.cypress.spec.tsx +0 -127
- package/src/Pagination/Pagination.cypress.spec.tsx +0 -140
- package/src/RichTextEditor/RichTextEditor.cypress.spec.tsx +0 -408
- package/src/TimePeriods/ResolutionTimePeriod.cypress.spec.tsx +0 -74
- package/src/TimePeriods/TimePeriods.cypress.spec.tsx +0 -488
- package/src/Typography/FluidTypography/FluidTypography.cypress.spec.tsx +0 -27
- package/src/api/useMutationQuery/useMutationQuery.cypress.spec.tsx +0 -93
- package/src/components/CollapsibleItem/CollapsibleItem.cypress.spec.tsx +0 -76
- package/src/components/CopyCommand/CopyCommand.cypress.spec.tsx +0 -167
- package/src/components/CrudPage/CrudPage.cypress.spec.tsx +0 -561
- package/src/components/DataTable/DataTable.cypress.spec.tsx +0 -174
- package/src/components/Form/AccessRights/AccessRights.cypress.spec.tsx +0 -376
- package/src/components/ItemComposition/ItemComposition.cypress.spec.tsx +0 -112
- package/src/components/Layout/PageLayout.cypress.spec.tsx +0 -64
- package/src/components/Modal/ConfirmationModal/ConfirmationModal.cypress.spec.tsx +0 -171
- package/src/components/SortableEntriesList/SortableEntriesList.cypress.spec.tsx +0 -792
- package/src/components/Tabs/Tabs.cypress.spec.tsx +0 -69
- package/src/utils/useFullscreen/Fullscreen.cypress.spec.tsx +0 -130
- package/src/utils/useLocale/useLocale.cypress.spec.tsx +0 -40
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { rs } from '@rstest/core';
|
|
2
|
+
import { interceptAPIRequest, waitForRequest } from '@test-setup/api';
|
|
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 SnackbarProvider from '../../Snackbar/SnackbarProvider';
|
|
8
|
+
import TestQueryProvider from '../TestQueryProvider';
|
|
9
|
+
import useMutationQuery, { Method, type UseMutationQueryProps } from '.';
|
|
10
|
+
|
|
11
|
+
interface Payload {
|
|
12
|
+
a: string;
|
|
13
|
+
b: number;
|
|
14
|
+
c: Array<string>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type MutationProps = Omit<
|
|
18
|
+
UseMutationQueryProps<Payload, unknown>,
|
|
19
|
+
'getEndpoint'
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
const TestComponent = (props: MutationProps): JSX.Element => {
|
|
23
|
+
const mutation = useMutationQuery<Payload, unknown>({
|
|
24
|
+
...props,
|
|
25
|
+
getEndpoint: () => '/endpoint'
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<button
|
|
30
|
+
onClick={() =>
|
|
31
|
+
mutation.mutateAsync({ payload: { a: 'a', b: 2, c: ['arr', 'ay'] } })
|
|
32
|
+
}
|
|
33
|
+
type="button"
|
|
34
|
+
>
|
|
35
|
+
Send
|
|
36
|
+
</button>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
interface InitializeProps {
|
|
41
|
+
isError?: boolean;
|
|
42
|
+
mutationProps: MutationProps & {
|
|
43
|
+
getEndpoint: () => string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const initialize = ({
|
|
48
|
+
mutationProps,
|
|
49
|
+
isError = false
|
|
50
|
+
}: InitializeProps): void => {
|
|
51
|
+
interceptAPIRequest({
|
|
52
|
+
alias: 'mutateEndpoint',
|
|
53
|
+
method: mutationProps.method,
|
|
54
|
+
path: './api/latest/endpoint',
|
|
55
|
+
response: isError
|
|
56
|
+
? {
|
|
57
|
+
message: 'custom error message'
|
|
58
|
+
}
|
|
59
|
+
: undefined,
|
|
60
|
+
statusCode: isError ? 400 : 204
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
render(
|
|
64
|
+
<SnackbarProvider>
|
|
65
|
+
<TestQueryProvider>
|
|
66
|
+
<TestComponent {...mutationProps} />
|
|
67
|
+
</TestQueryProvider>
|
|
68
|
+
</SnackbarProvider>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
describe('useMutationQuery', () => {
|
|
73
|
+
beforeEach(() => {
|
|
74
|
+
expect.hasAssertions();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
rs.restoreAllMocks();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('sends data to an endpoint', async () => {
|
|
82
|
+
// The recorded request has no headers: they are read from the call to
|
|
83
|
+
// `fetch` (the spy calls through).
|
|
84
|
+
const fetch = rs.spyOn(window, 'fetch');
|
|
85
|
+
|
|
86
|
+
initialize({
|
|
87
|
+
mutationProps: {
|
|
88
|
+
getEndpoint: () => '/endpoint',
|
|
89
|
+
method: Method.POST
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await userEvent.click(screen.getByRole('button'));
|
|
94
|
+
|
|
95
|
+
const { request } = await waitForRequest('@mutateEndpoint');
|
|
96
|
+
|
|
97
|
+
expect(request.method).toBe('POST');
|
|
98
|
+
expect(request.body).toEqual({ a: 'a', b: 2, c: ['arr', 'ay'] });
|
|
99
|
+
expect(
|
|
100
|
+
new Headers(fetch.mock.calls[0][1]?.headers).get('content-type')
|
|
101
|
+
).toBe('application/json');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("shows an error from the API via the Snackbar and inside the browser's console when posting data to an endpoint", async () => {
|
|
105
|
+
const consoleError = rs
|
|
106
|
+
.spyOn(console, 'error')
|
|
107
|
+
.mockImplementation(() => undefined);
|
|
108
|
+
|
|
109
|
+
initialize({
|
|
110
|
+
isError: true,
|
|
111
|
+
mutationProps: {
|
|
112
|
+
getEndpoint: () => '/endpoint',
|
|
113
|
+
method: Method.POST
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
await userEvent.click(screen.getByRole('button'));
|
|
118
|
+
|
|
119
|
+
expect(await screen.findByText('custom error message')).toBeVisible();
|
|
120
|
+
|
|
121
|
+
expect(consoleError).toHaveBeenCalledWith(
|
|
122
|
+
'API Request: custom error message'
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('does not show any message via the Snackbar when the httpCodesBypassErrorSnackbar is passed when posting data to an API', async () => {
|
|
127
|
+
const consoleError = rs
|
|
128
|
+
.spyOn(console, 'error')
|
|
129
|
+
.mockImplementation(() => undefined);
|
|
130
|
+
|
|
131
|
+
initialize({
|
|
132
|
+
isError: true,
|
|
133
|
+
mutationProps: {
|
|
134
|
+
getEndpoint: () => '/endpoint',
|
|
135
|
+
httpCodesBypassErrorSnackbar: [400],
|
|
136
|
+
method: Method.POST
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await userEvent.click(screen.getByRole('button'));
|
|
141
|
+
|
|
142
|
+
// The error is handled (and logged) once the answer is applied. The
|
|
143
|
+
// snackbar would show in a later render: it is given 500 ms to appear.
|
|
144
|
+
await waitFor(() => {
|
|
145
|
+
expect(consoleError).toHaveBeenCalledWith(
|
|
146
|
+
'API Request: custom error message'
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
await expect(
|
|
151
|
+
screen.findByText('custom error message', {}, { timeout: 500 })
|
|
152
|
+
).rejects.toThrow();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { render } from '@test-setup/render';
|
|
2
|
+
import { screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
|
|
5
|
+
import { CollapsibleItem, type Props } from './CollapsibleItem';
|
|
6
|
+
|
|
7
|
+
const title = 'Title';
|
|
8
|
+
|
|
9
|
+
const customizedTitle = <div>Customized title</div>;
|
|
10
|
+
|
|
11
|
+
const initialize = ({ title, ...props }: Omit<Props, 'children'>): void => {
|
|
12
|
+
// The component also takes the accordion summary props, whose `title` is
|
|
13
|
+
// a string: its own `title` accepts an element as well.
|
|
14
|
+
render(
|
|
15
|
+
<CollapsibleItem {...props} title={title as string}>
|
|
16
|
+
Content
|
|
17
|
+
</CollapsibleItem>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const getExpandButton = (expanded: boolean): HTMLElement =>
|
|
22
|
+
document.querySelector(`button[aria-expanded="${expanded}"]`) as HTMLElement;
|
|
23
|
+
|
|
24
|
+
const expectExpandIconVisible = (): void => {
|
|
25
|
+
expect(screen.getByTestId('ExpandMoreIcon')).toBeVisible();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// A string title is rendered as an h6 heading.
|
|
29
|
+
const expectStringTitleHeading = (): void => {
|
|
30
|
+
expect(screen.getByRole('heading', { level: 6, name: title })).toBeVisible();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// The details' horizontal padding: 0 when compact, 16px otherwise.
|
|
34
|
+
const getDetailsPaddingLeft = (): string =>
|
|
35
|
+
getComputedStyle(
|
|
36
|
+
screen.getByText('Content').closest('.MuiAccordionDetails-root') as Element
|
|
37
|
+
).paddingLeft;
|
|
38
|
+
|
|
39
|
+
describe('CollapsibleItem', () => {
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
expect.hasAssertions();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('displays the component collapsed by default', async () => {
|
|
45
|
+
initialize({ title });
|
|
46
|
+
|
|
47
|
+
expect(await screen.findByText(title)).toBeVisible();
|
|
48
|
+
expect(screen.getByText('Content')).not.toBeVisible();
|
|
49
|
+
expect(getExpandButton(false)).toBeInTheDocument();
|
|
50
|
+
|
|
51
|
+
expectStringTitleHeading();
|
|
52
|
+
expectExpandIconVisible();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('displays the component expanded when the corresponding prop is set to true', async () => {
|
|
56
|
+
initialize({ defaultExpanded: true, title });
|
|
57
|
+
|
|
58
|
+
expect(await screen.findByText(title)).toBeVisible();
|
|
59
|
+
expect(screen.getByText('Content')).toBeVisible();
|
|
60
|
+
expect(getExpandButton(true)).toBeInTheDocument();
|
|
61
|
+
|
|
62
|
+
expectStringTitleHeading();
|
|
63
|
+
expectExpandIconVisible();
|
|
64
|
+
expect(getDetailsPaddingLeft()).toBe('16px');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('displays a customized title', async () => {
|
|
68
|
+
initialize({ title: customizedTitle });
|
|
69
|
+
|
|
70
|
+
expect(await screen.findByText('Customized title')).toBeVisible();
|
|
71
|
+
expect(getExpandButton(false)).toBeInTheDocument();
|
|
72
|
+
|
|
73
|
+
expect(screen.queryByRole('heading', { level: 6 })).not.toBeInTheDocument();
|
|
74
|
+
expectExpandIconVisible();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('displays the component as compact', async () => {
|
|
78
|
+
initialize({ compact: true, title });
|
|
79
|
+
|
|
80
|
+
expect(await screen.findByText(title)).toBeVisible();
|
|
81
|
+
expect(getExpandButton(false)).toBeInTheDocument();
|
|
82
|
+
expect(
|
|
83
|
+
document.querySelector('div[data-compact="true"]')
|
|
84
|
+
).toBeInTheDocument();
|
|
85
|
+
|
|
86
|
+
expectStringTitleHeading();
|
|
87
|
+
expectExpandIconVisible();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('displays the component as compact and expanded when the icon is clicked', async () => {
|
|
91
|
+
initialize({ compact: true, title });
|
|
92
|
+
|
|
93
|
+
expect(await screen.findByText(title)).toBeVisible();
|
|
94
|
+
expect(getExpandButton(false)).toBeInTheDocument();
|
|
95
|
+
|
|
96
|
+
await userEvent.click(getExpandButton(false));
|
|
97
|
+
|
|
98
|
+
await waitFor(() => {
|
|
99
|
+
expect(getExpandButton(true)).toBeInTheDocument();
|
|
100
|
+
});
|
|
101
|
+
await waitFor(() => {
|
|
102
|
+
expect(screen.getByText('Content')).toBeVisible();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(getDetailsPaddingLeft()).toBe('0px');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('displays the component as compact and a customized title', async () => {
|
|
109
|
+
initialize({ compact: true, title: customizedTitle });
|
|
110
|
+
|
|
111
|
+
expect(await screen.findByText('Customized title')).toBeVisible();
|
|
112
|
+
expect(getExpandButton(false)).toBeInTheDocument();
|
|
113
|
+
expect(
|
|
114
|
+
document.querySelector('div[data-compact="true"]')
|
|
115
|
+
).toBeInTheDocument();
|
|
116
|
+
|
|
117
|
+
expect(screen.queryByRole('heading', { level: 6 })).not.toBeInTheDocument();
|
|
118
|
+
expectExpandIconVisible();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { ThemeMode, type User, userAtom } from '@centreon/ui-context';
|
|
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
|
+
|
|
9
|
+
import SnackbarProvider from '../../Snackbar/SnackbarProvider';
|
|
10
|
+
import ThemeProvider from '../../ThemeProvider';
|
|
11
|
+
import CopyCommand, { type CopyCommandProps } from './CopyCommand';
|
|
12
|
+
|
|
13
|
+
const initialize = (props: CopyCommandProps & { theme?: ThemeMode }): void => {
|
|
14
|
+
const store = createStore();
|
|
15
|
+
store.set(userAtom, { themeMode: props.theme || ThemeMode.light } as User);
|
|
16
|
+
render(
|
|
17
|
+
<Provider store={store}>
|
|
18
|
+
<ThemeProvider>
|
|
19
|
+
<SnackbarProvider>
|
|
20
|
+
<CopyCommand {...props} />
|
|
21
|
+
</SnackbarProvider>
|
|
22
|
+
</ThemeProvider>
|
|
23
|
+
</Provider>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const jsonText = `{
|
|
28
|
+
"number": 1,
|
|
29
|
+
"boolean": true,
|
|
30
|
+
"array": [
|
|
31
|
+
{
|
|
32
|
+
"string": "text"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
}`;
|
|
36
|
+
|
|
37
|
+
const getCode = (): HTMLElement => {
|
|
38
|
+
const code = document.querySelector('pre');
|
|
39
|
+
|
|
40
|
+
if (!code) {
|
|
41
|
+
throw new Error('No highlighted code block');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return code;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// The highlighted token of the given highlight.js class and exact text, with
|
|
48
|
+
// its computed colour.
|
|
49
|
+
const expectToken = ({
|
|
50
|
+
className,
|
|
51
|
+
color,
|
|
52
|
+
text
|
|
53
|
+
}: {
|
|
54
|
+
className: string;
|
|
55
|
+
color: string;
|
|
56
|
+
text: string;
|
|
57
|
+
}): void => {
|
|
58
|
+
const token = Array.from(
|
|
59
|
+
getCode().querySelectorAll<HTMLElement>(`.${className}`)
|
|
60
|
+
).find((element) => element.textContent === text);
|
|
61
|
+
|
|
62
|
+
expect(token).toBeDefined();
|
|
63
|
+
expect(token).toBeVisible();
|
|
64
|
+
expect(getComputedStyle(token as HTMLElement).color).toBe(color);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// The language chip shows the language, and only it.
|
|
68
|
+
const expectLanguageChip = (language: string): void => {
|
|
69
|
+
expect(screen.getByText(language, { selector: 'div' })).toHaveTextContent(
|
|
70
|
+
new RegExp(`^${language}$`)
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
describe('CopyCommand', () => {
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
expect.hasAssertions();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('displays bash code when props are set', async () => {
|
|
80
|
+
initialize({
|
|
81
|
+
commandToCopy: 'echo "hello" | grep "hel"',
|
|
82
|
+
language: 'bash',
|
|
83
|
+
text: `# a simple command
|
|
84
|
+
echo "hello" | grep "hel"`
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(await screen.findByText('# a simple command')).toBeVisible();
|
|
88
|
+
expect(screen.getByText('echo')).toBeVisible();
|
|
89
|
+
expect(screen.getByText('bash')).toBeVisible();
|
|
90
|
+
expect(screen.getByTestId('Copy command')).toBeVisible();
|
|
91
|
+
|
|
92
|
+
expect(getCode()).toHaveTextContent(
|
|
93
|
+
/^# a simple command echo "hello" \| grep "hel"$/
|
|
94
|
+
);
|
|
95
|
+
expectLanguageChip('bash');
|
|
96
|
+
expectToken({
|
|
97
|
+
className: 'hljs-comment',
|
|
98
|
+
color: 'rgb(106, 115, 125)',
|
|
99
|
+
text: '# a simple command'
|
|
100
|
+
});
|
|
101
|
+
expectToken({
|
|
102
|
+
className: 'hljs-built_in',
|
|
103
|
+
color: 'rgb(227, 98, 9)',
|
|
104
|
+
text: 'echo'
|
|
105
|
+
});
|
|
106
|
+
expectToken({
|
|
107
|
+
className: 'hljs-string',
|
|
108
|
+
color: 'rgb(3, 47, 98)',
|
|
109
|
+
text: '"hello"'
|
|
110
|
+
});
|
|
111
|
+
expectToken({
|
|
112
|
+
className: 'hljs-string',
|
|
113
|
+
color: 'rgb(3, 47, 98)',
|
|
114
|
+
text: '"hel"'
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('displays yaml code when props are set', async () => {
|
|
119
|
+
initialize({
|
|
120
|
+
commandToCopy: 'echo "hello" | grep "hel"',
|
|
121
|
+
language: 'yaml',
|
|
122
|
+
text: `key:
|
|
123
|
+
with:
|
|
124
|
+
input: "input"`
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
expect(await screen.findByText('key:')).toBeVisible();
|
|
128
|
+
expect(screen.getByText('"input"')).toBeVisible();
|
|
129
|
+
expect(screen.getByText('yaml')).toBeVisible();
|
|
130
|
+
|
|
131
|
+
expectLanguageChip('yaml');
|
|
132
|
+
expect(screen.getByTestId('Copy command')).toBeVisible();
|
|
133
|
+
// The indentation is kept, as non-breaking spaces.
|
|
134
|
+
expect(getCode().textContent).toMatch(/\n\u00a0+with:\n\u00a0+input:/);
|
|
135
|
+
expectToken({
|
|
136
|
+
className: 'hljs-attr',
|
|
137
|
+
color: 'rgb(0, 92, 197)',
|
|
138
|
+
text: 'key:'
|
|
139
|
+
});
|
|
140
|
+
expectToken({
|
|
141
|
+
className: 'hljs-attr',
|
|
142
|
+
color: 'rgb(0, 92, 197)',
|
|
143
|
+
text: 'input:'
|
|
144
|
+
});
|
|
145
|
+
expectToken({
|
|
146
|
+
className: 'hljs-string',
|
|
147
|
+
color: 'rgb(3, 47, 98)',
|
|
148
|
+
text: '"input"'
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('displays php code when props are set', async () => {
|
|
153
|
+
initialize({
|
|
154
|
+
commandToCopy: 'echo "hello" | grep "hel"',
|
|
155
|
+
language: 'php',
|
|
156
|
+
text: `<?php
|
|
157
|
+
echo 'Hello ' . htmlspecialchars($_POST["name"]) . '!';
|
|
158
|
+
?>`
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(await screen.findByText('echo')).toBeVisible();
|
|
162
|
+
expect(screen.getByText('"name"')).toBeVisible();
|
|
163
|
+
expect(screen.getByText('php')).toBeVisible();
|
|
164
|
+
|
|
165
|
+
expectLanguageChip('php');
|
|
166
|
+
expect(screen.getByTestId('Copy command')).toBeVisible();
|
|
167
|
+
expect(getCode()).toHaveTextContent(
|
|
168
|
+
`<?php echo 'Hello ' . htmlspecialchars($_POST["name"]) . '!'; ?>`
|
|
169
|
+
);
|
|
170
|
+
expectToken({
|
|
171
|
+
className: 'hljs-keyword',
|
|
172
|
+
color: 'rgb(215, 58, 73)',
|
|
173
|
+
text: 'echo'
|
|
174
|
+
});
|
|
175
|
+
expectToken({
|
|
176
|
+
className: 'hljs-title',
|
|
177
|
+
color: 'rgb(111, 66, 193)',
|
|
178
|
+
text: 'htmlspecialchars'
|
|
179
|
+
});
|
|
180
|
+
expectToken({
|
|
181
|
+
className: 'hljs-variable',
|
|
182
|
+
color: 'rgb(0, 92, 197)',
|
|
183
|
+
text: '$_POST'
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('displays json code when props are set', async () => {
|
|
188
|
+
initialize({
|
|
189
|
+
commandToCopy: 'echo "hello" | grep "hel"',
|
|
190
|
+
language: 'json',
|
|
191
|
+
text: jsonText
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
expect(await screen.findByText('"number"')).toBeVisible();
|
|
195
|
+
expect(screen.getByText('true')).toBeVisible();
|
|
196
|
+
expect(screen.getByText('json')).toBeVisible();
|
|
197
|
+
|
|
198
|
+
expectLanguageChip('json');
|
|
199
|
+
expect(screen.getByTestId('Copy command')).toBeVisible();
|
|
200
|
+
expectToken({
|
|
201
|
+
className: 'hljs-attr',
|
|
202
|
+
color: 'rgb(0, 92, 197)',
|
|
203
|
+
text: '"number"'
|
|
204
|
+
});
|
|
205
|
+
expectToken({
|
|
206
|
+
className: 'hljs-number',
|
|
207
|
+
color: 'rgb(0, 92, 197)',
|
|
208
|
+
text: '1'
|
|
209
|
+
});
|
|
210
|
+
expectToken({
|
|
211
|
+
className: 'hljs-keyword',
|
|
212
|
+
color: 'rgb(215, 58, 73)',
|
|
213
|
+
text: 'true'
|
|
214
|
+
});
|
|
215
|
+
expectToken({
|
|
216
|
+
className: 'hljs-string',
|
|
217
|
+
color: 'rgb(3, 47, 98)',
|
|
218
|
+
text: '"text"'
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('does not display the copy button when the corresponding prop is not passed', async () => {
|
|
223
|
+
initialize({
|
|
224
|
+
language: 'json',
|
|
225
|
+
text: jsonText
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
expect(await screen.findByText('"number"')).toBeVisible();
|
|
229
|
+
expect(screen.queryByTestId('Copy command')).not.toBeInTheDocument();
|
|
230
|
+
|
|
231
|
+
expectLanguageChip('json');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('displays the highlighted code in dark mode when the theme is changed', async () => {
|
|
235
|
+
initialize({
|
|
236
|
+
language: 'json',
|
|
237
|
+
text: jsonText,
|
|
238
|
+
theme: ThemeMode.dark
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
await waitFor(() => {
|
|
242
|
+
const keyword = document.querySelector('.hljs-keyword');
|
|
243
|
+
|
|
244
|
+
expect(keyword).not.toBeNull();
|
|
245
|
+
expect(getComputedStyle(keyword as Element).color).toBe(
|
|
246
|
+
'rgb(255, 123, 114)'
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// The rest of the dark palette.
|
|
251
|
+
expectToken({
|
|
252
|
+
className: 'hljs-attr',
|
|
253
|
+
color: 'rgb(121, 192, 255)',
|
|
254
|
+
text: '"number"'
|
|
255
|
+
});
|
|
256
|
+
expectToken({
|
|
257
|
+
className: 'hljs-string',
|
|
258
|
+
color: 'rgb(165, 214, 255)',
|
|
259
|
+
text: '"text"'
|
|
260
|
+
});
|
|
261
|
+
expectLanguageChip('json');
|
|
262
|
+
expect(screen.queryByTestId('Copy command')).not.toBeInTheDocument();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('copies the command to the clipboard when the button is clicked', async () => {
|
|
266
|
+
const writeText = rs
|
|
267
|
+
.spyOn(navigator.clipboard, 'writeText')
|
|
268
|
+
.mockResolvedValue(undefined);
|
|
269
|
+
|
|
270
|
+
initialize({
|
|
271
|
+
commandToCopy: 'echo "hello" | grep "hel"',
|
|
272
|
+
language: 'bash',
|
|
273
|
+
text: `# a simple command
|
|
274
|
+
echo "hello" | grep "hel"`
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
await userEvent.click(await screen.findByTestId('Copy command'));
|
|
278
|
+
|
|
279
|
+
await waitFor(() => {
|
|
280
|
+
expect(writeText).toHaveBeenCalledTimes(1);
|
|
281
|
+
});
|
|
282
|
+
expect(writeText).toHaveBeenCalledWith('echo "hello" | grep "hel"');
|
|
283
|
+
|
|
284
|
+
expect(screen.getByText('# a simple command')).toBeVisible();
|
|
285
|
+
expectLanguageChip('bash');
|
|
286
|
+
});
|
|
287
|
+
});
|