diff --git a/webapp/packages/core-blocks/src/Cell.test.tsx b/webapp/packages/core-blocks/src/Cell.test.tsx
new file mode 100644
index 0000000000..0aeeb2f9c2
--- /dev/null
+++ b/webapp/packages/core-blocks/src/Cell.test.tsx
@@ -0,0 +1,59 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2024 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+import { waitFor } from '@testing-library/react';
+
+import { createApp, renderInApp } from '@cloudbeaver/tests-runner';
+
+import { Cell } from './Cell';
+
+const app = createApp();
+
+describe('Cell', () => {
+ it('should render children correctly', async () => {
+ const { getByText } = renderInApp(Test Children | , app);
+ const text = await waitFor(() => getByText('Test Children'));
+
+ expect(text).toBeInTheDocument();
+ });
+
+ it('should render before element correctly', async () => {
+ const { getByText } = renderInApp(Before Element}>Test Children | , app);
+
+ const beforeText = await waitFor(() => getByText('Before Element'));
+ expect(beforeText).toBeInTheDocument();
+ });
+
+ it('should render after element correctly', async () => {
+ const { getByText } = renderInApp(After Element}>Test Children | , app);
+
+ const afterText = await waitFor(() => getByText('After Element'));
+ expect(afterText).toBeInTheDocument();
+ });
+
+ it('should render after and before elements correctly', async () => {
+ const { getByText } = renderInApp(
+ Before Element} after={After Element}>
+ Test Children
+ | ,
+ app,
+ );
+
+ const afterText = await waitFor(() => getByText('After Element'));
+ const beforeText = await waitFor(() => getByText('Before Element'));
+
+ expect(beforeText).toBeInTheDocument();
+ expect(afterText).toBeInTheDocument();
+ });
+
+ it('should render description element correctly', async () => {
+ const { getByText } = renderInApp(Description Element}>Test Children | , app);
+
+ const description = await waitFor(() => getByText('Description Element'));
+ expect(description).toBeInTheDocument();
+ });
+});
diff --git a/webapp/packages/core-blocks/src/Link.test.tsx b/webapp/packages/core-blocks/src/Link.test.tsx
new file mode 100644
index 0000000000..00547c4cc3
--- /dev/null
+++ b/webapp/packages/core-blocks/src/Link.test.tsx
@@ -0,0 +1,63 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2024 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+import { fireEvent, queryByAttribute, waitFor } from '@testing-library/react';
+
+import { createApp, renderInApp } from '@cloudbeaver/tests-runner';
+
+import { Link } from './Link';
+
+const app = createApp();
+
+describe('Link', () => {
+ it('should render link and children correctly', async () => {
+ const { getByText } = renderInApp(Test Link, app);
+ const linkElement = await waitFor(() => getByText('Test Link'));
+
+ expect(linkElement.tagName).toBe('A');
+ expect(linkElement).toBeInTheDocument();
+ });
+
+ it('should display the indicator icon when indicator is true', async () => {
+ const { container } = renderInApp(
+
+ Test Link
+ ,
+ app,
+ );
+
+ const icon = await waitFor(() => queryByAttribute('href', container, /external-link/i));
+ expect(icon).toBeInTheDocument();
+ });
+
+ it('should apply the className correctly', async () => {
+ const { getByText } = renderInApp(
+
+ Test Link
+ ,
+ app,
+ );
+
+ const linkContainer = await waitFor(() => getByText('Test Link').closest('div'));
+ expect(linkContainer).toHaveClass('custom-class');
+ });
+
+ it('should handle onClick event', async () => {
+ const handleClick = jest.fn();
+ const { getByText } = renderInApp(
+
+ Test Link
+ ,
+ app,
+ );
+
+ const linkElement = await waitFor(() => getByText('Test Link'));
+ fireEvent.click(linkElement);
+
+ expect(handleClick).toHaveBeenCalled();
+ });
+});
diff --git a/webapp/packages/core-blocks/src/StatusMessage.test.tsx b/webapp/packages/core-blocks/src/StatusMessage.test.tsx
new file mode 100644
index 0000000000..e04f56f8a3
--- /dev/null
+++ b/webapp/packages/core-blocks/src/StatusMessage.test.tsx
@@ -0,0 +1,64 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2024 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+import { fireEvent, queryByAttribute, waitFor } from '@testing-library/react';
+
+import { coreDialogsManifest } from '@cloudbeaver/core-dialogs';
+import { ENotificationType } from '@cloudbeaver/core-events';
+import { coreLocalizationManifest } from '@cloudbeaver/core-localization';
+import { createApp, renderInApp } from '@cloudbeaver/tests-runner';
+
+import { StatusMessage } from './StatusMessage';
+
+const app = createApp(coreLocalizationManifest, coreDialogsManifest);
+
+describe('StatusMessage', () => {
+ it('should display an error icon and message when type is error', async () => {
+ const message = 'test_error';
+ const { container, getByTitle } = renderInApp(, app);
+ const title = await waitFor(() => getByTitle(message));
+ const icon = await waitFor(() => queryByAttribute('src', container, /error/i));
+
+ expect(title).toBeInTheDocument();
+ expect(icon).toBeInTheDocument();
+ });
+
+ it('should display a success icon and message when type is success', async () => {
+ const message = 'test_success';
+ const { container, getByTitle } = renderInApp(, app);
+ const title = await waitFor(() => getByTitle(message));
+ const icon = await waitFor(() => queryByAttribute('src', container, /success/i));
+
+ expect(title).toBeInTheDocument();
+ expect(icon).toBeInTheDocument();
+ });
+
+ it('should display an error message when no message is provided', async () => {
+ const { getByText } = renderInApp(, app);
+ const message = await waitFor(() => getByText('Test error'));
+
+ expect(message).toBeInTheDocument();
+ });
+
+ it('should call onShowDetails when link is clicked', async () => {
+ const onShowDetails = jest.fn();
+ const message = 'test_message_with_details';
+ const { getByText } = renderInApp(, app);
+ const link = await waitFor(() => getByText(message));
+
+ fireEvent.click(link);
+ expect(onShowDetails).toHaveBeenCalled();
+ });
+
+ it('should display multiple messages joined by comma', async () => {
+ const messages = ['message_one', 'message_two'];
+ const { getByText } = renderInApp(, app);
+ const message = await waitFor(() => getByText('message_one, message_two'));
+
+ expect(message).toBeInTheDocument();
+ });
+});
diff --git a/webapp/packages/core-blocks/src/Text.test.tsx b/webapp/packages/core-blocks/src/Text.test.tsx
new file mode 100644
index 0000000000..31cf85b16e
--- /dev/null
+++ b/webapp/packages/core-blocks/src/Text.test.tsx
@@ -0,0 +1,40 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2024 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+import { waitFor } from '@testing-library/react';
+
+import { createApp, renderInApp } from '@cloudbeaver/tests-runner';
+
+import { Text } from './Text';
+
+const app = createApp();
+
+describe('Text Component', () => {
+ it('renders children correctly', async () => {
+ const { getByText } = renderInApp(Hello World, app);
+ const text = await waitFor(() => getByText('Hello World'));
+ expect(text).toBeInTheDocument();
+ });
+
+ it('applies custom className', () => {
+ const { container } = renderInApp(Hello World, app);
+ expect(container.getElementsByClassName('custom-class')).toHaveLength(1);
+ });
+
+ it('passes HTML attributes correctly', () => {
+ const { container } = renderInApp(
+
+ Hello World
+ ,
+ app,
+ );
+
+ const div = container.firstChild;
+ expect(div).toHaveAttribute('id', 'custom-id');
+ expect(div).toHaveAttribute('data-testid', 'custom-testid');
+ });
+});
diff --git a/webapp/packages/core-blocks/src/Text.tsx b/webapp/packages/core-blocks/src/Text.tsx
index 38289b0faf..83b7e8b138 100644
--- a/webapp/packages/core-blocks/src/Text.tsx
+++ b/webapp/packages/core-blocks/src/Text.tsx
@@ -7,8 +7,6 @@
*/
import { observer } from 'mobx-react-lite';
-interface Props extends React.HTMLAttributes {}
-
-export const Text: React.FC = observer(function Text({ children, ...rest }) {
+export const Text: React.FC> = observer(function Text({ children, ...rest }) {
return {children}
;
});
diff --git a/webapp/packages/core-blocks/src/TextPlaceholder.test.tsx b/webapp/packages/core-blocks/src/TextPlaceholder.test.tsx
new file mode 100644
index 0000000000..2e7a55f07c
--- /dev/null
+++ b/webapp/packages/core-blocks/src/TextPlaceholder.test.tsx
@@ -0,0 +1,27 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2024 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+import { waitFor } from '@testing-library/react';
+
+import { createApp, renderInApp } from '@cloudbeaver/tests-runner';
+
+import { TextPlaceholder } from './TextPlaceholder';
+
+const app = createApp();
+
+describe('TextPlaceholder Component', () => {
+ it('renders children correctly', async () => {
+ const { getByText } = renderInApp(Hello World, app);
+ const text = await waitFor(() => getByText('Hello World'));
+ expect(text).toBeInTheDocument();
+ });
+
+ it('applies custom className', () => {
+ const { container } = renderInApp(Hello World, app);
+ expect(container.getElementsByClassName('custom-class')).toHaveLength(1);
+ });
+});
diff --git a/webapp/packages/core-blocks/src/TimerIcon.test.tsx b/webapp/packages/core-blocks/src/TimerIcon.test.tsx
new file mode 100644
index 0000000000..a596842d04
--- /dev/null
+++ b/webapp/packages/core-blocks/src/TimerIcon.test.tsx
@@ -0,0 +1,42 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2024 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+import { queryByAttribute, waitFor } from '@testing-library/react';
+
+import { createApp, renderInApp } from '@cloudbeaver/tests-runner';
+
+import { TimerIcon } from './TimerIcon';
+
+const app = createApp();
+
+describe('TimerIcon', () => {
+ it('renders correctly with state "play" and interval 30', async () => {
+ const { getByText, container } = renderInApp(, app);
+ const text = await waitFor(() => getByText('30'));
+ const name = await waitFor(() => queryByAttribute('href', container, '/icons/timer-play_m.svg#root'));
+
+ expect(name).toBeInTheDocument();
+ expect(text).toBeInTheDocument();
+ });
+
+ it('renders correctly with state "stop" and interval 60', async () => {
+ const { getByText, container } = renderInApp(, app);
+ const text = await waitFor(() => getByText('60'));
+ const name = await waitFor(() => queryByAttribute('href', container, '/icons/timer-stop_m.svg#root'));
+
+ expect(name).toBeInTheDocument();
+ expect(text).toBeInTheDocument();
+ });
+
+ it('passes HTML attributes correctly', () => {
+ const { container } = renderInApp(, app);
+
+ const div = container.firstChild;
+ expect(div).toHaveAttribute('id', 'custom-id');
+ expect(div).toHaveAttribute('data-testid', 'custom-testid');
+ });
+});
diff --git a/webapp/packages/core-blocks/src/TimerIcon.tsx b/webapp/packages/core-blocks/src/TimerIcon.tsx
index f87620d475..0de5860e34 100644
--- a/webapp/packages/core-blocks/src/TimerIcon.tsx
+++ b/webapp/packages/core-blocks/src/TimerIcon.tsx
@@ -10,7 +10,7 @@ import type React from 'react';
import { Icon } from './Icon';
import { s } from './s';
-import style from './TimerIcon.module.css';
+import classes from './TimerIcon.module.css';
import { useS } from './useS';
interface Props {
@@ -19,7 +19,7 @@ interface Props {
}
export const TimerIcon = observer>(function TimerIcon({ state, interval, ...rest }) {
- const styles = useS(style);
+ const styles = useS(classes);
return (