Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions frontend/__tests__/unit/components/SecondaryCard.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { faUser } from '@fortawesome/free-solid-svg-icons';
import { render, screen } from '@testing-library/react';
import SecondaryCard from 'components/SecondaryCard';

describe('SecondaryCard Component', () => {
const defaultProps = {
title: 'Test Title',
icon: faUser,
children: <p>Test children</p>,
className: 'custom-class',
};

describe('Renders successfully with minimal required props', () => {
it('renders successfully with only a title', () => {
render(<SecondaryCard title={defaultProps.title} />);
expect(screen.getByText(defaultProps.title)).toBeInTheDocument();
});

it('renders successfully with only children', () => {
render(<SecondaryCard>{defaultProps.children}</SecondaryCard>);
expect(screen.getByText('Test children')).toBeInTheDocument();
});

it('renders successfully with no props at all', () => {
const { container } = render(<SecondaryCard />);
expect(container.querySelector('.mb-8')).toBeInTheDocument();
});
});

describe('Conditional rendering logic', () => {
it('does not render the h2 title element when title prop is not provided', () => {
render(<SecondaryCard />);
const titleElement = screen.queryByRole('heading');
expect(titleElement).not.toBeInTheDocument();
});

it('renders the h2 title element when title prop is provided', () => {
render(<SecondaryCard title={defaultProps.title} />);
const titleElement = screen.getByRole('heading', { name: 'Test Title' });
expect(titleElement).toBeInTheDocument();
});

it('does not render the icon when icon prop is not provided', () => {
const { container } = render(<SecondaryCard title={defaultProps.title} />);
const iconElement = container.querySelector('svg');
expect(iconElement).not.toBeInTheDocument();
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid coupling to implementation details; assert icon absence via role, not 'svg' tag

Selecting the SVG tag tightly couples the test to FontAwesome’s DOM. Using role keeps it implementation-agnostic.

Apply this diff:

-      const { container } = render(<SecondaryCard title={defaultProps.title} />);
-      const iconElement = container.querySelector('svg');
-      expect(iconElement).not.toBeInTheDocument();
+      render(<SecondaryCard title={defaultProps.title} />);
+      expect(screen.queryByRole('img', { hidden: true })).not.toBeInTheDocument();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('does not render the icon when icon prop is not provided', () => {
const { container } = render(<SecondaryCard title={defaultProps.title} />);
const iconElement = container.querySelector('svg');
expect(iconElement).not.toBeInTheDocument();
});
it('does not render the icon when icon prop is not provided', () => {
render(<SecondaryCard title={defaultProps.title} />);
expect(screen.queryByRole('img', { hidden: true })).not.toBeInTheDocument();
});
🤖 Prompt for AI Agents
In frontend/__tests__/unit/components/SecondaryCard.test.tsx around lines 43–47,
the test currently queries the DOM for an 'svg' tag which couples it to
FontAwesome implementation; change the assertion to use an accessibility role
query instead (e.g., queryByRole('img') or queryByRole with the icon's
accessible name) so the test asserts the icon is absent via role rather than the
'svg' element, replacing the container.querySelector('svg') check with a
testing-library queryByRole and expecting it not to be in the document.

});

describe('Prop-based behavior - different props affect output', () => {
it('renders a title and an icon when both are provided', () => {
render(<SecondaryCard title={defaultProps.title} icon={defaultProps.icon} />);
expect(screen.getByText(defaultProps.title)).toBeInTheDocument();
expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument();
});

it('renders children content correctly', () => {
render(<SecondaryCard>{defaultProps.children}</SecondaryCard>);
expect(screen.getByText('Test children')).toBeInTheDocument();
});
});

describe('Text and content rendering', () => {
it('displays the correct text for the title', () => {
const customTitle = 'My Custom Title';
render(<SecondaryCard title={customTitle} />);
expect(screen.getByText(customTitle)).toBeInTheDocument();
});

it('renders complex children nodes', () => {
const complexChildren = (
<div>
<span>Nested Content</span>
<button>Click Me</button>
</div>
);
render(<SecondaryCard>{complexChildren}</SecondaryCard>);
expect(screen.getByText('Nested Content')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Click Me' })).toBeInTheDocument();
});
});

describe('Handles edge cases and invalid inputs', () => {
it('handles an empty string for the title prop by not rendering the title element', () => {
render(<SecondaryCard title="" />);
const titleElement = screen.queryByRole('heading');
expect(titleElement).not.toBeInTheDocument();
});

it('handles null children gracefully by rendering nothing for the children', () => {
const { container } = render(
<SecondaryCard title="Title">{null}</SecondaryCard>
);
const cardElement = container.firstChild;
const titleElement = screen.getByRole('heading', { name: 'Title' });
expect(titleElement).toBeInTheDocument();
expect(cardElement.childNodes.length).toBe(1);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Brittle structure assertion; avoid relying on childNodes length

Asserting exact child node counts is fragile to harmless markup changes (e.g., wrappers). Prefer content- or role-based assertions.

Apply this diff:

-    const { container } = render(
-      <SecondaryCard title="Title">{null}</SecondaryCard>
-    );
-    const cardElement = container.firstChild;
-    const titleElement = screen.getByRole('heading', { name: 'Title' });
-    expect(titleElement).toBeInTheDocument();
-    expect(cardElement.childNodes.length).toBe(1);
+    const { container } = render(
+      <SecondaryCard title="Title">{null}</SecondaryCard>
+    );
+    const heading = screen.getByRole('heading', { level: 2, name: 'Title' });
+    expect(heading).toBeInTheDocument();
+    // Ensure no extra visible text/content beyond the title
+    expect(container.textContent?.trim()).toBe('Title');
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('handles null children gracefully by rendering nothing for the children', () => {
const { container } = render(
<SecondaryCard title="Title">{null}</SecondaryCard>
);
const cardElement = container.firstChild;
const titleElement = screen.getByRole('heading', { name: 'Title' });
expect(titleElement).toBeInTheDocument();
expect(cardElement.childNodes.length).toBe(1);
});
it('handles null children gracefully by rendering nothing for the children', () => {
const { container } = render(
<SecondaryCard title="Title">{null}</SecondaryCard>
);
const heading = screen.getByRole('heading', { level: 2, name: 'Title' });
expect(heading).toBeInTheDocument();
// Ensure no extra visible text/content beyond the title
expect(container.textContent?.trim()).toBe('Title');
});
🤖 Prompt for AI Agents
frontend/__tests__/unit/components/SecondaryCard.test.tsx around lines 90 to 98:
the test currently asserts a brittle DOM structure by checking
cardElement.childNodes.length; remove that childNodes length assertion and
instead assert via content/role queries that only the title is rendered and no
children content exists (for example, use screen.queryByText or
within(cardElement).queryByRole/queryByText to assert there is no extra content
or region rendered for children when children is null). Ensure the test verifies
presence of the heading and uses queryBy* to assert absence of any child content
rather than relying on node count.

});

describe('Accessibility roles and labels', () => {
it('renders the title within an h2 tag for proper heading structure', () => {
render(<SecondaryCard title={defaultProps.title} />);
const heading = screen.getByRole('heading', { level: 2, name: defaultProps.title });
expect(heading).toBeInTheDocument();
});
});

describe('DOM structure / classNames / styles', () => {
it('applies the base and custom classNames to the root div', () => {
const { container } = render(<SecondaryCard className={defaultProps.className} />);
const cardElement = container.firstChild;
expect(cardElement).toHaveClass('mb-8', 'rounded-lg', 'bg-gray-100', 'p-6', 'shadow-md', 'dark:bg-gray-800');
expect(cardElement).toHaveClass(defaultProps.className);
});

it('has the correct classNames for the h2 title element', () => {
render(<SecondaryCard title={defaultProps.title} />);
const titleElement = screen.getByText(defaultProps.title);
expect(titleElement).toHaveClass('mb-4', 'flex', 'flex-row', 'items-center', 'gap-2', 'text-2xl', 'font-semibold');
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Target the actual h2 element when asserting classes

getByText may resolve a child node (e.g., span) inside the h2, causing false negatives. Query the heading element directly.

Apply this diff:

-      render(<SecondaryCard title={defaultProps.title} />);
-      const titleElement = screen.getByText(defaultProps.title);
-      expect(titleElement).toHaveClass('mb-4', 'flex', 'flex-row', 'items-center', 'gap-2', 'text-2xl', 'font-semibold');
+      render(<SecondaryCard title={defaultProps.title} />);
+      const heading = screen.getByRole('heading', { level: 2, name: defaultProps.title });
+      expect(heading).toHaveClass(
+        'mb-4',
+        'flex',
+        'flex-row',
+        'items-center',
+        'gap-2',
+        'text-2xl',
+        'font-semibold'
+      );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('has the correct classNames for the h2 title element', () => {
render(<SecondaryCard title={defaultProps.title} />);
const titleElement = screen.getByText(defaultProps.title);
expect(titleElement).toHaveClass('mb-4', 'flex', 'flex-row', 'items-center', 'gap-2', 'text-2xl', 'font-semibold');
});
it('has the correct classNames for the h2 title element', () => {
render(<SecondaryCard title={defaultProps.title} />);
const heading = screen.getByRole('heading', { level: 2, name: defaultProps.title });
expect(heading).toHaveClass(
'mb-4',
'flex',
'flex-row',
'items-center',
'gap-2',
'text-2xl',
'font-semibold'
);
});
🤖 Prompt for AI Agents
In frontend/__tests__/unit/components/SecondaryCard.test.tsx around lines 117 to
121, the test uses getByText which can match a child node (like a span) instead
of the h2; replace that lookup with a heading-specific query (e.g.,
getByRole('heading', { level: 2, name: defaultProps.title }) or
getByRole('heading', { name: defaultProps.title, level: 2 }) ) so the assertion
targets the actual h2 element, then run the same toHaveClass assertion against
that heading element.


it('has the correct className for the icon', () => {
render(<SecondaryCard title={defaultProps.title} icon={defaultProps.icon} />);
const iconElement = screen.getByRole('img', { hidden: true });
expect(iconElement).toHaveClass('h-5', 'w-5');
});
});
});