• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

prabhuignoto / react-chrono / #92

18 Jun 2025 10:08AM UTC coverage: 90.727% (+0.9%) from 89.791%
#92

push

web-flow
Minor cleanup and expanding test coverage (#548)

* refactor: rename project to React Chrono UI and update related files

* fix: update tsconfig to reference the correct entry file for React Chrono UI

* feat: enhance styles with vendor prefixes and improve cross-browser support

* Add tests for useNewScrollPosition hook and TimelineHorizontal component

- Implement comprehensive tests for the useNewScrollPosition hook covering horizontal, vertical, and edge cases.
- Create a new test file for the TimelineHorizontal component, ensuring it renders correctly and handles various props and states.
- Update snapshots for timeline control and vertical components to reflect recent changes in class names.
- Modify vitest configuration to include all test files in the src directory.

* refactor: simplify transform handling in timeline styles

* refactor: clean up test imports and remove unused styles in timeline components

1783 of 2112 branches covered (84.42%)

Branch coverage included in aggregate %.

670 of 674 new or added lines in 12 files covered. (99.41%)

400 existing lines in 29 files now uncovered.

10564 of 11497 relevant lines covered (91.88%)

10.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.7
/src/components/timeline-horizontal/__tests__/timeline-horizontal.test.tsx
1
import { render, screen } from '@testing-library/react';
1✔
2
import { describe, expect, it, vi } from 'vitest';
3
import { GlobalContext } from '../../GlobalContext';
4
import { TimelineHorizontalModel } from '../../../models/TimelineHorizontalModel';
1✔
5
import TimelineHorizontal from '../timeline-horizontal';
1✔
6

7
const mockItems = [
1✔
8
  {
9
    id: '1',
1✔
10
    title: 'Test Item 1',
1✔
11
    cardTitle: 'Card Title 1',
1✔
12
    cardSubtitle: 'Card Subtitle 1',
1✔
13
    cardDetailedText: 'Detailed text for item 1',
1✔
14
    visible: true,
1✔
15
    active: false,
1✔
16
    wrapperId: 'timeline-wrapper',
1✔
17
  },
1✔
18
  {
1✔
19
    id: '2',
1✔
20
    title: 'Test Item 2',
1✔
21
    cardTitle: 'Card Title 2',
1✔
22
    cardSubtitle: 'Card Subtitle 2',
1✔
23
    cardDetailedText: 'Detailed text for item 2',
1✔
24
    visible: true,
1✔
25
    active: true,
1✔
26
    wrapperId: 'timeline-wrapper',
1✔
27
  },
1✔
28
  {
1✔
29
    id: '3',
1✔
30
    title: 'Test Item 3',
1✔
31
    cardTitle: 'Card Title 3',
1✔
32
    cardSubtitle: 'Card Subtitle 3',
1✔
33
    cardDetailedText: 'Detailed text for item 3',
1✔
34
    visible: false,
1✔
35
    active: false,
1✔
36
    wrapperId: 'timeline-wrapper',
1✔
37
  },
1✔
38
];
1✔
39

1✔
40
const defaultProps: TimelineHorizontalModel = {
1✔
41
  items: mockItems,
42
  handleItemClick: vi.fn(),
1✔
43
  autoScroll: vi.fn(),
1✔
44
  wrapperId: 'timeline-wrapper',
1✔
45
  slideShowRunning: false,
1✔
46
  onElapsed: vi.fn(),
1✔
47
  contentDetailsChildren: undefined,
1✔
48
  hasFocus: false,
1✔
49
  iconChildren: undefined,
1✔
50
  nestedCardHeight: 200,
1✔
51
  isNested: false,
1✔
52
};
1✔
53

1✔
54
const mockContextValue = {
1✔
55
  mode: 'HORIZONTAL' as const,
56
  itemWidth: 300,
1✔
57
  cardHeight: 200,
1✔
58
  flipLayout: false,
1✔
59
  showAllCardsHorizontal: false,
1✔
60
  theme: {
1✔
61
    primary: '#0f172a',
1✔
62
    secondary: '#64748b',
1✔
63
    cardBgColor: '#ffffff',
1✔
64
    cardForeColor: '#000000',
1✔
65
    titleColor: '#0f172a',
1✔
66
    titleColorActive: '#ffffff',
1✔
67
  },
1✔
68
  cardWidth: 400,
1✔
69
};
1✔
70

1✔
71
const renderWithContext = (
1✔
72
  props: Partial<TimelineHorizontalModel> = {},
73
  contextValue: any = mockContextValue,
1✔
74
) => {
20✔
75
  return render(
20✔
76
    <GlobalContext.Provider value={contextValue}>
20✔
77
      <TimelineHorizontal {...defaultProps} {...props} />
20✔
78
    </GlobalContext.Provider>,
20✔
79
  );
20✔
80
};
20✔
81

20✔
82
describe('TimelineHorizontal', () => {
20✔
83
  it('should render without crashing', () => {
84
    renderWithContext();
1✔
85
    expect(screen.getByTestId('timeline-collection')).toBeInTheDocument();
1✔
86
  });
1✔
87

1✔
88
  it('should render the correct number of timeline items', () => {
1✔
89
    renderWithContext();
90
    // Use querySelectorAll since getAllByRole might filter out hidden items
1✔
91
    const container = screen.getByTestId('timeline-collection');
1✔
92
    const allItems = container.querySelectorAll('li');
93
    expect(allItems).toHaveLength(mockItems.length);
1✔
94
  });
1✔
95

1✔
96
  it('should apply correct CSS classes based on mode', () => {
1✔
97
    renderWithContext();
98
    const container = screen.getByTestId('timeline-collection');
1✔
99
    expect(container).toHaveClass('horizontal');
1✔
100
    expect(container).toHaveClass('timeline-horizontal-container');
1✔
101
  });
1✔
102

1✔
103
  it('should apply show-all-cards-horizontal class when showAllCardsHorizontal is true', () => {
1✔
104
    const contextWithShowAll = {
105
      ...mockContextValue,
1✔
106
      showAllCardsHorizontal: true,
1✔
107
    };
1✔
108
    renderWithContext({}, contextWithShowAll);
1✔
109
    const container = screen.getByTestId('timeline-collection');
1✔
110
    expect(container).toHaveClass('show-all-cards-horizontal');
1✔
111
  });
1✔
112

1✔
113
  it('should render with flipped layout when flipLayout is true', () => {
1✔
114
    const contextWithFlipped = {
115
      ...mockContextValue,
1✔
116
      flipLayout: true,
1✔
117
    };
1✔
118
    renderWithContext({}, contextWithFlipped);
1✔
119
    const container = screen.getByTestId('timeline-collection');
1✔
120
    expect(container).toBeInTheDocument();
1✔
121
  });
1✔
122

1✔
123
  it('should handle item click when timeline card is clicked', async () => {
1✔
124
    const handleItemClick = vi.fn();
125
    renderWithContext({ handleItemClick });
1✔
126
    
1✔
127
    // Note: The actual click testing would depend on the TimelineCard implementation
1✔
128
    // For now, we verify that the function is passed correctly
129
    expect(handleItemClick).toHaveBeenCalledTimes(0);
130
  });
131

1✔
132
  it('should render visible items with visible class', () => {
1✔
133
    renderWithContext();
134
    const listItems = screen.getAllByRole('listitem');
1✔
135
    
1✔
136
    // First two items should be visible
1✔
137
    expect(listItems[0]).toHaveClass('visible');
138
    expect(listItems[1]).toHaveClass('visible');
139
    // Third item should not have visible class (visible: false) if it exists
1✔
140
    if (listItems[2]) {
1✔
141
      expect(listItems[2]).not.toHaveClass('visible');
142
    }
1!
NEW
143
  });
×
NEW
144

×
145
  it('should set aria-current="true" for active items', () => {
1✔
146
    renderWithContext();
147
    const listItems = screen.getAllByRole('listitem');
1✔
148
    
1✔
149
    // Only the second item is active
1✔
150
    expect(listItems[0]).not.toHaveAttribute('aria-current');
151
    expect(listItems[1]).toHaveAttribute('aria-current', 'true');
152
    if (listItems[2]) {
1✔
153
      expect(listItems[2]).not.toHaveAttribute('aria-current');
1✔
154
    }
1!
NEW
155
  });
×
NEW
156

×
157
  it('should apply correct width to timeline items', () => {
1✔
158
    const customItemWidth = 250;
159
    const contextWithCustomWidth = {
1✔
160
      ...mockContextValue,
1✔
161
      itemWidth: customItemWidth,
1✔
162
    };
1✔
163
    renderWithContext({}, contextWithCustomWidth);
1✔
164
    const listItems = screen.getAllByRole('listitem');
1✔
165
    
1✔
166
    // Check that items have the correct width style applied
1✔
167
    listItems.forEach(item => {
168
      expect(item).toHaveClass('timeline-horz-item-container');
169
    });
1✔
170
  });
2✔
171

1✔
172
  it('should handle slideshow properties correctly', () => {
1✔
173
    renderWithContext({
174
      slideShowRunning: true,
1✔
175
      onElapsed: vi.fn(),
1✔
176
    });
1✔
177
    const container = screen.getByTestId('timeline-collection');
1✔
178
    expect(container).toBeInTheDocument();
1✔
179
  });
1✔
180

1✔
181
  it('should render with nested card properties', () => {
1✔
182
    renderWithContext({
183
      isNested: true,
1✔
184
      nestedCardHeight: 150,
1✔
185
    });
1✔
186
    const container = screen.getByTestId('timeline-collection');
1✔
187
    expect(container).toBeInTheDocument();
1✔
188
  });
1✔
189

1✔
190
  it('should pass icon children to timeline cards', () => {
1✔
191
    const iconChildren = [
192
      <div key="icon1">Icon 1</div>,
1✔
193
      <div key="icon2">Icon 2</div>,
1✔
194
      <div key="icon3">Icon 3</div>,
1✔
195
    ];
1✔
196
    
1✔
197
    renderWithContext({ iconChildren });
1✔
198
    const container = screen.getByTestId('timeline-collection');
199
    expect(container).toBeInTheDocument();
1✔
200
  });
1✔
201

1✔
202
  it('should pass content details children to timeline cards', () => {
1✔
203
    const contentDetailsChildren = [
204
      <div key="content1">Content 1</div>,
1✔
205
      <div key="content2">Content 2</div>,
1✔
206
      <div key="content3">Content 3</div>,
1✔
207
    ];
1✔
208
    
1✔
209
    renderWithContext({ contentDetailsChildren });
1✔
210
    const container = screen.getByTestId('timeline-collection');
211
    expect(container).toBeInTheDocument();
1✔
212
  });
1✔
213

1✔
214
  it('should render with auto scroll enabled', () => {
1✔
215
    renderWithContext({ autoScroll: vi.fn() });
216
    const container = screen.getByTestId('timeline-collection');
1✔
217
    expect(container).toBeInTheDocument();
1✔
218
  });
1✔
219

1✔
220
  it('should render with hasFocus enabled', () => {
1✔
221
    renderWithContext({ hasFocus: true });
222
    const container = screen.getByTestId('timeline-collection');
1✔
223
    expect(container).toBeInTheDocument();
1✔
224
  });
1✔
225

1✔
226
  it('should render as semantic list with proper ARIA labels', () => {
1✔
227
    renderWithContext();
228
    const list = screen.getByRole('list', { name: 'Timeline' });
1✔
229
    expect(list).toBeInTheDocument();
1✔
230
    expect(list).toHaveAttribute('aria-label', 'Timeline');
1✔
231
  });
1✔
232

1✔
233
  it('should handle empty items array', () => {
1✔
234
    renderWithContext({ items: [] });
235
    const container = screen.getByTestId('timeline-collection');
1✔
236
    expect(container).toBeInTheDocument();
1✔
237
    const listItems = screen.queryAllByRole('listitem');
1✔
238
    expect(listItems).toHaveLength(0);
1✔
239
  });
1✔
240

1✔
241
  it('should handle different timeline modes correctly', () => {
1✔
242
    const contextVertical = {
243
      ...mockContextValue,
1✔
244
      mode: 'VERTICAL_ALTERNATING',
1✔
245
    };
1✔
246
    renderWithContext({}, contextVertical);
1✔
247
    const container = screen.getByTestId('timeline-collection');
1✔
248
    expect(container).toHaveClass('vertical_alternating');
1✔
249
  });
1✔
250

1✔
251
  it('should memoize wrapper class to prevent unnecessary re-renders', () => {
1✔
252
    const { rerender } = renderWithContext();
253
    const container = screen.getByTestId('timeline-collection');
1✔
254
    const initialClassName = container.className;
1✔
255
    
1✔
256
    // Re-render with same props
1✔
257
    rerender(
258
      <GlobalContext.Provider value={mockContextValue}>
259
        <TimelineHorizontal {...defaultProps} />
1✔
260
      </GlobalContext.Provider>,
1✔
261
    );
1✔
262
    
1✔
263
    expect(container.className).toBe(initialClassName);
1✔
264
  });
265

1✔
266
  it('should memoize timeline items to prevent unnecessary re-renders', () => {
1✔
267
    const { rerender } = renderWithContext();
268
    const initialItems = screen.getAllByRole('listitem');
1✔
269
    
1✔
270
    // Re-render with same props
1✔
271
    rerender(
272
      <GlobalContext.Provider value={mockContextValue}>
273
        <TimelineHorizontal {...defaultProps} />
1✔
274
      </GlobalContext.Provider>,
1✔
275
    );
1✔
276
    
1✔
277
    const afterRerenderItems = screen.getAllByRole('listitem');
1✔
278
    expect(afterRerenderItems).toHaveLength(initialItems.length);
279
  });
1✔
280
});
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc