Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
38 changes: 38 additions & 0 deletions src/renderer/__helpers__/jest.setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,41 @@ Object.defineProperty(navigator, 'clipboard', {
},
configurable: true,
});

// Simple IntersectionObserver mock for test environments (jsdom)
class MockIntersectionObserver {
readonly root: Element | Document | null;
readonly rootMargin: string;
readonly thresholds: number | number[];

constructor(
// callback unused in this mock
_callback: IntersectionObserverCallback,
options?: IntersectionObserverInit,
) {
this.root = (options?.root as Element | Document) ?? null;
this.rootMargin = options?.rootMargin ?? '';
this.thresholds = options?.threshold ?? 0;
}

observe() {
return null;
}

unobserve() {
return null;
}

disconnect() {
return null;
}

takeRecords(): IntersectionObserverEntry[] {
return [];
}
}

// Attach to global if not present
if (typeof (globalThis as any).IntersectionObserver === 'undefined') {
(globalThis as any).IntersectionObserver = MockIntersectionObserver;
}
28 changes: 11 additions & 17 deletions src/renderer/components/metrics/CommentsPill.test.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
import { renderWithAppContext } from '../../__helpers__/test-utils';
import { mockGitifyNotification } from '../../__mocks__/notifications-mocks';

import { CommentsPill } from './CommentsPill';
import { CommentsPill, type CommentsPillProps } from './CommentsPill';

describe('renderer/components/metrics/CommentsPill.tsx', () => {
it('renders with no comments (null)', () => {
const mockNotification = { ...mockGitifyNotification };
mockNotification.subject.commentCount = null;
const props: CommentsPillProps = null;

const tree = renderWithAppContext(
<CommentsPill commentCount={mockNotification.subject.commentCount} />,
);
const tree = renderWithAppContext(<CommentsPill {...props} />);

expect(tree).toMatchSnapshot();
});

it('renders with 1 comment', () => {
const mockNotification = { ...mockGitifyNotification };
mockNotification.subject.commentCount = 1;
const props: CommentsPillProps = {
commentCount: 1,
};

const tree = renderWithAppContext(
<CommentsPill commentCount={mockNotification.subject.commentCount} />,
);
const tree = renderWithAppContext(<CommentsPill {...props} />);

expect(tree).toMatchSnapshot();
});

it('renders with multiple comments', () => {
const mockNotification = { ...mockGitifyNotification };
mockNotification.subject.commentCount = 2;
const props: CommentsPillProps = {
commentCount: 2,
};

const tree = renderWithAppContext(
<CommentsPill commentCount={mockNotification.subject.commentCount} />,
);
const tree = renderWithAppContext(<CommentsPill {...props} />);

expect(tree).toMatchSnapshot();
});
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/components/metrics/CommentsPill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ export const CommentsPill: FC<CommentsPillProps> = ({ commentCount }) => {
return (
<MetricPill
color={IconColor.GRAY}
contents={description}
icon={CommentIcon}
metric={commentCount}
title={description}
/>
);
};
25 changes: 17 additions & 8 deletions src/renderer/components/metrics/LabelsPill.test.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import { renderWithAppContext } from '../../__helpers__/test-utils';
import { mockGitifyNotification } from '../../__mocks__/notifications-mocks';

import { LabelsPill } from './LabelsPill';
import { LabelsPill, type LabelsPillProps } from './LabelsPill';

describe('renderer/components/metrics/LabelsPill.tsx', () => {
it('renders labels pill', () => {
const mockNotification = { ...mockGitifyNotification };
mockNotification.subject.labels = ['enhancement', 'good-first-issue'];
it('renders without labels', () => {
const props: LabelsPillProps = { labels: [] };

const tree = renderWithAppContext(
<LabelsPill labels={mockNotification.subject.labels} />,
);
const tree = renderWithAppContext(<LabelsPill {...props} />);

expect(tree).toMatchSnapshot();
});

it('renders with labels', () => {
const props: LabelsPillProps = {
labels: [
{ name: 'enhancement', color: 'a2eeef' },
{ name: 'good-first-issue', color: '7057ff' },
],
};

const tree = renderWithAppContext(<LabelsPill {...props} />);

expect(tree).toMatchSnapshot();
});
Expand Down
29 changes: 17 additions & 12 deletions src/renderer/components/metrics/LabelsPill.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,42 @@
import type { FC } from 'react';

import { TagIcon } from '@primer/octicons-react';
import { IssueLabelToken, LabelGroup } from '@primer/react';

import { IconColor } from '../../types';
import { type GitifyLabels, IconColor } from '../../types';

import { formatMetricDescription } from '../../utils/notifications/formatters';
import { MetricPill } from './MetricPill';

export interface LabelsPillProps {
labels: string[];
labels: GitifyLabels[];
}

export const LabelsPill: FC<LabelsPillProps> = ({ labels }) => {
if (!labels?.length) {
return null;
}

const description = formatMetricDescription(
labels.length,
'label',
(count, noun) => {
const formatted = labels.map((label) => `🏷️ ${label}`).join(', ');

return `${count} ${noun}: ${formatted}`;
},
const labelsContent = (
<LabelGroup>
{labels.map((label) => {
return (
<IssueLabelToken
fillColor={label.color ? `#${label.color}` : undefined}
key={label.name}
size="small"
text={label.name}
/>
);
})}
</LabelGroup>
);

return (
<MetricPill
color={IconColor.GRAY}
contents={labelsContent}
icon={TagIcon}
metric={labels.length}
title={description}
/>
);
};
28 changes: 16 additions & 12 deletions src/renderer/components/metrics/LinkedIssuesPill.test.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import { renderWithAppContext } from '../../__helpers__/test-utils';
import { mockGitifyNotification } from '../../__mocks__/notifications-mocks';

import { LinkedIssuesPill } from './LinkedIssuesPill';
import {
LinkedIssuesPill,
type LinkedIssuesPillProps,
} from './LinkedIssuesPill';

describe('renderer/components/metrics/LinkedIssuesPill.tsx', () => {
it('renders when no linked issues/prs', () => {
const props: LinkedIssuesPillProps = { linkedIssues: [] };

const tree = renderWithAppContext(<LinkedIssuesPill {...props} />);

expect(tree).toMatchSnapshot();
});

it('renders when linked to one issue/pr', () => {
const mockNotification = { ...mockGitifyNotification };
mockNotification.subject.linkedIssues = ['#1'];
const props: LinkedIssuesPillProps = { linkedIssues: ['#1'] };

const tree = renderWithAppContext(
<LinkedIssuesPill linkedIssues={mockNotification.subject.linkedIssues} />,
);
const tree = renderWithAppContext(<LinkedIssuesPill {...props} />);

expect(tree).toMatchSnapshot();
});

it('renders when linked to multiple issues/prs', () => {
const mockNotification = { ...mockGitifyNotification };
mockNotification.subject.linkedIssues = ['#1', '#2'];
const props: LinkedIssuesPillProps = { linkedIssues: ['#1', '#2'] };

const tree = renderWithAppContext(
<LinkedIssuesPill linkedIssues={mockNotification.subject.linkedIssues} />,
);
const tree = renderWithAppContext(<LinkedIssuesPill {...props} />);

expect(tree).toMatchSnapshot();
});
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/components/metrics/LinkedIssuesPill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ export const LinkedIssuesPill: FC<LinkedIssuesPillProps> = ({
const description = formatMetricDescription(
linkedIssues.length,
'issue',
(count, noun) => `Linked to ${count} ${noun}: ${linkedIssues.join(', ')}`,
(noun) => `Linked to ${noun}: ${linkedIssues.join(', ')}`,
);

return (
<MetricPill
color={IconColor.GRAY}
contents={description}
icon={IssueOpenedIcon}
metric={linkedIssues.length}
title={description}
/>
);
};
31 changes: 21 additions & 10 deletions src/renderer/components/metrics/MetricGroup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,29 @@ import { mockSettings } from '../../__mocks__/state-mocks';
import { MetricGroup, type MetricGroupProps } from './MetricGroup';

describe('renderer/components/metrics/MetricGroup.tsx', () => {
describe('showPills disabled', () => {
it('should not render any pills when showPills is disabled', async () => {
const mockNotification = mockGitifyNotification;
const props: MetricGroupProps = {
notification: mockNotification,
};
it('should not render any pills when showPills is disabled', async () => {
const mockNotification = mockGitifyNotification;
const props: MetricGroupProps = {
notification: mockNotification,
};

const tree = renderWithAppContext(<MetricGroup {...props} />, {
settings: { ...mockSettings, showPills: false },
});
const tree = renderWithAppContext(<MetricGroup {...props} />, {
settings: { ...mockSettings, showPills: false },
});

expect(tree).toMatchSnapshot();
});

expect(tree).toMatchSnapshot();
it('should render pills when showPills is enabled', async () => {
const mockNotification = mockGitifyNotification;
const props: MetricGroupProps = {
notification: mockNotification,
};

const tree = renderWithAppContext(<MetricGroup {...props} />, {
settings: { ...mockSettings, showPills: true },
});

expect(tree).toMatchSnapshot();
});
});
4 changes: 2 additions & 2 deletions src/renderer/components/metrics/MetricGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ export const MetricGroup: FC<MetricGroupProps> = ({ notification }) => {

<CommentsPill commentCount={notification.subject.commentCount ?? 0} />

<LabelsPill labels={notification.subject.labels ?? []} />

<MilestonePill milestone={notification.subject.milestone} />

<LabelsPill labels={notification.subject.labels ?? []} />
</div>
);
};
4 changes: 2 additions & 2 deletions src/renderer/components/metrics/MetricPill.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { MetricPill, type MetricPillProps } from './MetricPill';
describe('renderer/components/metrics/MetricPill.tsx', () => {
it('should render with metric', () => {
const props: MetricPillProps = {
title: 'Mock Pill',
contents: 'Mock Pill',
metric: 1,
icon: MarkGithubIcon,
color: IconColor.GREEN,
Expand All @@ -22,7 +22,7 @@ describe('renderer/components/metrics/MetricPill.tsx', () => {

it('should render without metric', () => {
const props: MetricPillProps = {
title: 'Mock Pill',
contents: 'Mock Pill',
icon: MarkGithubIcon,
color: IconColor.GREEN,
};
Expand Down
7 changes: 4 additions & 3 deletions src/renderer/components/metrics/MetricPill.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { FC } from 'react';
import type { FC, ReactNode } from 'react';

import type { Icon } from '@primer/octicons-react';
import { Label, Stack, Text, Tooltip } from '@primer/react';

import { type IconColor, Size } from '../../types';

export interface MetricPillProps {
title: string;
contents: string | ReactNode;
metric?: number;
icon: Icon;
color: IconColor;
Expand All @@ -16,7 +16,8 @@ export const MetricPill: FC<MetricPillProps> = (props: MetricPillProps) => {
const Icon = props.icon;

return (
<Tooltip direction="s" text={props.title}>
// @ts-expect-error: We overload text with a ReactNode
<Tooltip direction="s" text={props.contents}>
<button type="button">
<Label
className="hover:bg-gitify-notification-pill-hover"
Expand Down
Loading