@windstream/react-shared-components 0.1.93 → 0.1.95

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.
Files changed (112) hide show
  1. package/dist/contentful/index.esm.js +2 -2
  2. package/dist/contentful/index.esm.js.map +1 -1
  3. package/dist/contentful/index.js +3 -3
  4. package/dist/contentful/index.js.map +1 -1
  5. package/dist/core.d.ts +2 -2
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.esm.js +1 -1
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/index.js +3 -3
  10. package/dist/index.js.map +1 -1
  11. package/dist/styles.css +1 -1
  12. package/dist/utils/index.esm.js +1 -1
  13. package/dist/utils/index.js +1 -1
  14. package/package.json +14 -8
  15. package/src/components/accordion/index.test.tsx +270 -0
  16. package/src/components/alert-card/index.test.tsx +152 -0
  17. package/src/components/animation-wrapper/index.test.tsx +424 -0
  18. package/src/components/brand-button/index.test.tsx +292 -0
  19. package/src/components/button/index.test.tsx +91 -0
  20. package/src/components/call-button/index.test.tsx +260 -0
  21. package/src/components/checkbox/index.test.tsx +252 -0
  22. package/src/components/checklist/index.test.tsx +231 -0
  23. package/src/components/checklist/index.tsx +64 -29
  24. package/src/components/checklist/types.ts +7 -1
  25. package/src/components/collapse/index.test.tsx +277 -0
  26. package/src/components/collapse/index.tsx +1 -0
  27. package/src/components/divider/index.test.tsx +53 -0
  28. package/src/components/image/index.test.tsx +174 -0
  29. package/src/components/input/index.test.tsx +348 -0
  30. package/src/components/link/index.test.tsx +199 -0
  31. package/src/components/list/index.test.tsx +166 -0
  32. package/src/components/material-icon/index.test.tsx +130 -0
  33. package/src/components/modal/index.test.tsx +310 -0
  34. package/src/components/next-image/index.test.tsx +406 -0
  35. package/src/components/pagination/index.test.tsx +521 -0
  36. package/src/components/radio-button/index.test.tsx +151 -0
  37. package/src/components/see-more/index.test.tsx +96 -0
  38. package/src/components/select/index.test.tsx +256 -0
  39. package/src/components/select-plan-button/index.test.tsx +173 -0
  40. package/src/components/skeleton/index.test.tsx +74 -0
  41. package/src/components/spinner/index.test.tsx +76 -0
  42. package/src/components/text/index.test.tsx +65 -0
  43. package/src/components/tooltip/index.test.tsx +50 -0
  44. package/src/components/view-cart-button/index.test.tsx +57 -0
  45. package/src/contentful/blocks/accordion/index.test.tsx +218 -0
  46. package/src/contentful/blocks/accordion/index.tsx +3 -1
  47. package/src/contentful/blocks/address-input-banner/index.test.tsx +132 -0
  48. package/src/contentful/blocks/anchored-bottom-banner/index.test.tsx +287 -0
  49. package/src/contentful/blocks/blogs-grid/BlogGrid.stories.tsx +5 -4
  50. package/src/contentful/blocks/blogs-grid/index.test.tsx +355 -0
  51. package/src/contentful/blocks/blogs-grid-base/index.test.tsx +274 -0
  52. package/src/contentful/blocks/breadcrumbs/index.test.tsx +281 -0
  53. package/src/contentful/blocks/button/index.test.tsx +339 -0
  54. package/src/contentful/blocks/callout/index.test.tsx +539 -0
  55. package/src/contentful/blocks/cards/blog-card/index.test.tsx +218 -0
  56. package/src/contentful/blocks/cards/floating-image-card/index.test.tsx +201 -0
  57. package/src/contentful/blocks/cards/full-image-card/index.test.tsx +216 -0
  58. package/src/contentful/blocks/cards/index.test.tsx +39 -0
  59. package/src/contentful/blocks/cards/product-card/index.test.tsx +263 -0
  60. package/src/contentful/blocks/cards/simple-card/index.test.tsx +364 -0
  61. package/src/contentful/blocks/cards/simple-card/index.tsx +1 -1
  62. package/src/contentful/blocks/cards/testimonial-card/index.test.tsx +180 -0
  63. package/src/contentful/blocks/carousel/helper.test.tsx +539 -0
  64. package/src/contentful/blocks/carousel/index.test.tsx +308 -0
  65. package/src/contentful/blocks/carousel/types.test.ts +16 -0
  66. package/src/contentful/blocks/cart-retention-banner/index.test.tsx +409 -0
  67. package/src/contentful/blocks/cart-retention-banner/index.tsx +4 -4
  68. package/src/contentful/blocks/comparison-table/index.test.tsx +114 -0
  69. package/src/contentful/blocks/cookiebanner/index.test.tsx +277 -0
  70. package/src/contentful/blocks/cta-callout/index.test.tsx +244 -0
  71. package/src/contentful/blocks/dynamic-tabs/index.test.tsx +240 -0
  72. package/src/contentful/blocks/email-input-block/index.test.tsx +213 -0
  73. package/src/contentful/blocks/email-input-block/index.tsx +40 -35
  74. package/src/contentful/blocks/find-kinetic/index.test.tsx +269 -0
  75. package/src/contentful/blocks/floating-banner/index.test.tsx +246 -0
  76. package/src/contentful/blocks/footer/index.test.tsx +302 -0
  77. package/src/contentful/blocks/image-promo-bar/helper.test.tsx +61 -0
  78. package/src/contentful/blocks/image-promo-bar/index.test.tsx +467 -0
  79. package/src/contentful/blocks/image-promo-bar/index.tsx +248 -246
  80. package/src/contentful/blocks/image-promo-bar/vimeo-embed.test.tsx +142 -0
  81. package/src/contentful/blocks/image-promo-bar/youtube-embed.test.tsx +104 -0
  82. package/src/contentful/blocks/modal/index.test.tsx +209 -0
  83. package/src/contentful/blocks/navigation/desktop-link-groups.tsx/index.test.tsx +208 -0
  84. package/src/contentful/blocks/navigation/index.test.tsx +924 -0
  85. package/src/contentful/blocks/navigation/mobile-link-groups.tsx/index.test.tsx +131 -0
  86. package/src/contentful/blocks/primary-hero/index.test.tsx +286 -0
  87. package/src/contentful/blocks/primary-hero/index.tsx +7 -4
  88. package/src/contentful/blocks/search-block/index.test.tsx +268 -0
  89. package/src/contentful/blocks/shape-background-wrapper/index.test.tsx +284 -0
  90. package/src/contentful/blocks/text/index.test.tsx +36 -0
  91. package/src/contentful/index.test.ts +45 -0
  92. package/src/global-mocks/contentful/to-document.ts +25 -0
  93. package/src/global-mocks/cookie.ts +48 -0
  94. package/src/global-mocks/cx.ts +37 -0
  95. package/src/global-mocks/index.ts +89 -0
  96. package/src/global-mocks/speed-card-bg.ts +27 -0
  97. package/src/global-mocks/utm.ts +49 -0
  98. package/src/hooks/contentful/use-contentful-rich-text.test.tsx +1758 -0
  99. package/src/hooks/contentful/use-contentful-rich-text.tsx +1 -1
  100. package/src/hooks/contentful/use-processed-check-list.test.tsx +277 -0
  101. package/src/hooks/use-body-scroll-lock.test.ts +134 -0
  102. package/src/hooks/use-carousel-swipe.test.ts +393 -0
  103. package/src/hooks/use-outside-click.test.ts +142 -0
  104. package/src/index.ts +1 -1
  105. package/src/next/index.test.ts +7 -0
  106. package/src/setupTests.ts +17 -11
  107. package/src/utils/contentful/to-document.test.ts +85 -0
  108. package/src/utils/cookie.test.ts +180 -0
  109. package/src/utils/cx.test.ts +90 -0
  110. package/src/utils/index.test.ts +115 -0
  111. package/src/utils/speed-card-bg.test.ts +46 -0
  112. package/src/utils/utm.test.ts +359 -0
@@ -0,0 +1,409 @@
1
+ import "@testing-library/jest-dom";
2
+
3
+ import CartRetentionBanner from "./index";
4
+ import { TypeCartRetentionBannerFields } from "./types";
5
+
6
+ import { act, fireEvent, render, screen } from "@testing-library/react";
7
+
8
+ // Mock dependencies
9
+ jest.mock("@shared/components/text", () => ({
10
+ Text: ({ children, ...props }: any) => <span {...props}>{children}</span>,
11
+ }));
12
+
13
+ jest.mock("@shared/contentful/blocks/button", () => ({
14
+ Button: ({ children, onClick, buttonLabel, ...props }: any) => (
15
+ <button onClick={onClick} data-testid="banner-button" {...props}>
16
+ {buttonLabel || children}
17
+ </button>
18
+ ),
19
+ }));
20
+
21
+ jest.mock("@shared/contentful/blocks/modal", () => ({
22
+ Modal: ({ isOpen, onRequestClose, children, ...props }: any) => (
23
+ <div data-testid="modal" data-open={isOpen} data-type={props.type}>
24
+ {isOpen && (
25
+ <>
26
+ <button data-testid="modal-close" onClick={onRequestClose}>
27
+ Close
28
+ </button>
29
+ {children}
30
+ </>
31
+ )}
32
+ </div>
33
+ ),
34
+ }));
35
+
36
+ jest.mock("@shared/hooks/contentful/use-contentful-rich-text", () => ({
37
+ renderContentfulRichText: (doc: any, _: any, className: string) => (
38
+ <div data-testid="rich-text" className={className}>
39
+ Rich Text Content
40
+ </div>
41
+ ),
42
+ }));
43
+
44
+ jest.mock("@shared/utils/contentful/to-document", () => ({
45
+ toDocument: (json: any) => json,
46
+ }));
47
+
48
+ describe("CartRetentionBanner", () => {
49
+ const defaultFields: TypeCartRetentionBannerFields = {
50
+ entryTitle: "Cart Banner",
51
+ anchorId: "cart-banner",
52
+ mainHeading: "Welcome back!",
53
+ description: { json: { content: [{ nodeType: "paragraph" }] } },
54
+ cta: { buttonLabel: "Continue checkout" },
55
+ };
56
+
57
+ beforeEach(() => {
58
+ jest.useFakeTimers();
59
+ });
60
+
61
+ afterEach(() => {
62
+ jest.useRealTimers();
63
+ });
64
+
65
+ describe("modal behavior", () => {
66
+ it("renders a section with anchorId", () => {
67
+ render(<CartRetentionBanner fields={defaultFields} />);
68
+ const section = document.querySelector("section#cart-banner");
69
+ expect(section).toBeInTheDocument();
70
+ });
71
+
72
+ it("renders with data-testid cart-retention-banner", () => {
73
+ render(<CartRetentionBanner fields={defaultFields} />);
74
+ expect(screen.getByTestId("cart-retention-banner")).toBeInTheDocument();
75
+ });
76
+
77
+ it("opens modal when checkShouldShowBanner returns true", async () => {
78
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
79
+ render(
80
+ <CartRetentionBanner
81
+ fields={defaultFields}
82
+ checkShouldShowBanner={checkShouldShowBanner}
83
+ />
84
+ );
85
+
86
+ // Allow useEffect to fire
87
+ act(() => {
88
+ jest.advanceTimersByTime(200);
89
+ });
90
+
91
+ const modal = screen.getByTestId("modal");
92
+ expect(modal).toHaveAttribute("data-open", "true");
93
+ });
94
+
95
+ it("does not open modal when checkShouldShowBanner returns false", () => {
96
+ const checkShouldShowBanner = jest.fn().mockReturnValue(false);
97
+ render(
98
+ <CartRetentionBanner
99
+ fields={defaultFields}
100
+ checkShouldShowBanner={checkShouldShowBanner}
101
+ />
102
+ );
103
+
104
+ act(() => {
105
+ jest.advanceTimersByTime(200);
106
+ });
107
+
108
+ const modal = screen.getByTestId("modal");
109
+ expect(modal).toHaveAttribute("data-open", "false");
110
+ });
111
+
112
+ it("calls onBannerShown when modal opens", () => {
113
+ const onBannerShown = jest.fn();
114
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
115
+ render(
116
+ <CartRetentionBanner
117
+ fields={defaultFields}
118
+ checkShouldShowBanner={checkShouldShowBanner}
119
+ onBannerShown={onBannerShown}
120
+ />
121
+ );
122
+
123
+ act(() => {
124
+ jest.advanceTimersByTime(200);
125
+ });
126
+
127
+ expect(onBannerShown).toHaveBeenCalled();
128
+ });
129
+
130
+ it("closes modal and calls onClose", () => {
131
+ const onClose = jest.fn();
132
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
133
+ render(
134
+ <CartRetentionBanner
135
+ fields={defaultFields}
136
+ checkShouldShowBanner={checkShouldShowBanner}
137
+ onClose={onClose}
138
+ />
139
+ );
140
+
141
+ act(() => {
142
+ jest.advanceTimersByTime(200);
143
+ });
144
+
145
+ fireEvent.click(screen.getByTestId("modal-close"));
146
+ expect(onClose).toHaveBeenCalled();
147
+ });
148
+
149
+ it("calls onContinueCheckout when CTA button is clicked in modal", () => {
150
+ const onContinueCheckout = jest.fn();
151
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
152
+ render(
153
+ <CartRetentionBanner
154
+ fields={defaultFields}
155
+ checkShouldShowBanner={checkShouldShowBanner}
156
+ onContinueCheckout={onContinueCheckout}
157
+ />
158
+ );
159
+
160
+ act(() => {
161
+ jest.advanceTimersByTime(200);
162
+ });
163
+
164
+ fireEvent.click(screen.getByTestId("banner-button"));
165
+ expect(onContinueCheckout).toHaveBeenCalled();
166
+ });
167
+
168
+ it("uses default heading when mainHeading is not provided", () => {
169
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
170
+ const fields = { ...defaultFields, mainHeading: undefined };
171
+ render(
172
+ <CartRetentionBanner
173
+ fields={fields}
174
+ checkShouldShowBanner={checkShouldShowBanner}
175
+ />
176
+ );
177
+
178
+ act(() => {
179
+ jest.advanceTimersByTime(200);
180
+ });
181
+
182
+ expect(screen.getByText(/Welcome back/i)).toBeInTheDocument();
183
+ });
184
+
185
+ it("uses default CTA label when cta.buttonLabel is not provided", () => {
186
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
187
+ const fields = { ...defaultFields, cta: {} };
188
+ render(
189
+ <CartRetentionBanner
190
+ fields={fields}
191
+ checkShouldShowBanner={checkShouldShowBanner}
192
+ />
193
+ );
194
+
195
+ act(() => {
196
+ jest.advanceTimersByTime(200);
197
+ });
198
+
199
+ expect(screen.getByTestId("banner-button")).toHaveTextContent(
200
+ "Continue checkout"
201
+ );
202
+ });
203
+
204
+ it("renders rich text description in modal", () => {
205
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
206
+ render(
207
+ <CartRetentionBanner
208
+ fields={defaultFields}
209
+ checkShouldShowBanner={checkShouldShowBanner}
210
+ />
211
+ );
212
+
213
+ act(() => {
214
+ jest.advanceTimersByTime(200);
215
+ });
216
+
217
+ expect(screen.getByTestId("rich-text")).toBeInTheDocument();
218
+ });
219
+
220
+ it("does not render rich text when description is undefined", () => {
221
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
222
+ const fields = { ...defaultFields, description: undefined };
223
+ render(
224
+ <CartRetentionBanner
225
+ fields={fields}
226
+ checkShouldShowBanner={checkShouldShowBanner}
227
+ />
228
+ );
229
+
230
+ act(() => {
231
+ jest.advanceTimersByTime(200);
232
+ });
233
+
234
+ expect(screen.queryByTestId("rich-text")).not.toBeInTheDocument();
235
+ });
236
+
237
+ it("passes type=cart-retention-banner to Modal", () => {
238
+ render(<CartRetentionBanner fields={defaultFields} />);
239
+ const modal = screen.getByTestId("modal");
240
+ expect(modal).toHaveAttribute("data-type", "cart-retention-banner");
241
+ });
242
+ });
243
+
244
+ describe("popup container mode", () => {
245
+ it("renders inline content when isInPopupContainer is true", () => {
246
+ render(
247
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
248
+ );
249
+ // Should render the pop-up-body div directly
250
+ const popupBody = document.querySelector(".pop-up-body");
251
+ expect(popupBody).toBeInTheDocument();
252
+ });
253
+
254
+ it("renders data-section-type attribute in popup mode", () => {
255
+ render(
256
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
257
+ );
258
+ const div = document.querySelector(
259
+ '[data-section-type="cart-retention-banner"]'
260
+ );
261
+ expect(div).toBeInTheDocument();
262
+ });
263
+
264
+ it("renders heading in popup mode", () => {
265
+ render(
266
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
267
+ );
268
+ expect(screen.getByText("Welcome back!")).toBeInTheDocument();
269
+ });
270
+
271
+ it("renders default heading in popup mode when mainHeading is undefined", () => {
272
+ const fields = { ...defaultFields, mainHeading: undefined };
273
+ render(<CartRetentionBanner fields={fields} isInPopupContainer={true} />);
274
+ expect(screen.getByText(/Welcome back/)).toBeInTheDocument();
275
+ });
276
+
277
+ it("renders CTA in popup mode", () => {
278
+ render(
279
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
280
+ );
281
+ expect(screen.getByTestId("banner-button")).toHaveTextContent(
282
+ "Continue checkout"
283
+ );
284
+ });
285
+
286
+ it("calls onContinueCheckout in popup mode", () => {
287
+ const onContinueCheckout = jest.fn();
288
+ render(
289
+ <CartRetentionBanner
290
+ fields={defaultFields}
291
+ isInPopupContainer={true}
292
+ onContinueCheckout={onContinueCheckout}
293
+ />
294
+ );
295
+ fireEvent.click(screen.getByTestId("banner-button"));
296
+ expect(onContinueCheckout).toHaveBeenCalled();
297
+ });
298
+
299
+ it("does not render Modal in popup mode", () => {
300
+ render(
301
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
302
+ );
303
+ expect(screen.queryByTestId("modal")).not.toBeInTheDocument();
304
+ });
305
+
306
+ it("renders rich text in popup mode", () => {
307
+ render(
308
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
309
+ );
310
+ expect(screen.getByTestId("rich-text")).toBeInTheDocument();
311
+ });
312
+ });
313
+
314
+ describe("timer cleanup", () => {
315
+ it("cleans up timeout on unmount", () => {
316
+ const checkShouldShowBanner = jest.fn().mockReturnValue(false);
317
+ const { unmount } = render(
318
+ <CartRetentionBanner
319
+ fields={defaultFields}
320
+ checkShouldShowBanner={checkShouldShowBanner}
321
+ />
322
+ );
323
+ unmount();
324
+ // No error should occur after unmount
325
+ act(() => {
326
+ jest.advanceTimersByTime(200);
327
+ });
328
+ });
329
+ });
330
+
331
+ describe("optional callback branches", () => {
332
+ it("handles close without onClose callback", () => {
333
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
334
+ render(
335
+ <CartRetentionBanner
336
+ fields={defaultFields}
337
+ checkShouldShowBanner={checkShouldShowBanner}
338
+ />
339
+ );
340
+
341
+ act(() => {
342
+ jest.advanceTimersByTime(200);
343
+ });
344
+
345
+ // Should not throw when clicking close without onClose
346
+ fireEvent.click(screen.getByTestId("modal-close"));
347
+ expect(screen.getByTestId("modal")).toHaveAttribute("data-open", "false");
348
+ });
349
+
350
+ it("handles continue checkout without onContinueCheckout callback", () => {
351
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
352
+ render(
353
+ <CartRetentionBanner
354
+ fields={defaultFields}
355
+ checkShouldShowBanner={checkShouldShowBanner}
356
+ />
357
+ );
358
+
359
+ act(() => {
360
+ jest.advanceTimersByTime(200);
361
+ });
362
+
363
+ // Should not throw when clicking CTA without onContinueCheckout
364
+ fireEvent.click(screen.getByTestId("banner-button"));
365
+ expect(screen.getByTestId("banner-button")).toBeInTheDocument();
366
+ });
367
+
368
+ it("handles continue checkout in popup mode without callback", () => {
369
+ render(
370
+ <CartRetentionBanner fields={defaultFields} isInPopupContainer={true} />
371
+ );
372
+ // Should not throw
373
+ fireEvent.click(screen.getByTestId("banner-button"));
374
+ expect(screen.getByTestId("banner-button")).toBeInTheDocument();
375
+ });
376
+
377
+ it("opens modal without onBannerShown callback", () => {
378
+ const checkShouldShowBanner = jest.fn().mockReturnValue(true);
379
+ render(
380
+ <CartRetentionBanner
381
+ fields={defaultFields}
382
+ checkShouldShowBanner={checkShouldShowBanner}
383
+ />
384
+ );
385
+
386
+ act(() => {
387
+ jest.advanceTimersByTime(200);
388
+ });
389
+
390
+ expect(screen.getByTestId("modal")).toHaveAttribute("data-open", "true");
391
+ });
392
+
393
+ it("does not open if checkShouldShowBanner is not provided", () => {
394
+ render(<CartRetentionBanner fields={defaultFields} />);
395
+
396
+ act(() => {
397
+ jest.advanceTimersByTime(200);
398
+ });
399
+
400
+ expect(screen.getByTestId("modal")).toHaveAttribute("data-open", "false");
401
+ });
402
+
403
+ it("renders popup without description", () => {
404
+ const fields = { ...defaultFields, description: undefined };
405
+ render(<CartRetentionBanner fields={fields} isInPopupContainer={true} />);
406
+ expect(screen.queryByTestId("rich-text")).not.toBeInTheDocument();
407
+ });
408
+ });
409
+ });
@@ -95,10 +95,10 @@ const CartRetentionBanner: React.FC<{
95
95
  "body1 my-4 text-center"
96
96
  )}
97
97
  <div className="flex w-full justify-center">
98
- <Button
99
- onClick={continueCheckout}
100
- buttonLabel={cta?.buttonLabel ?? "Continue checkout"}
101
- />
98
+ <Button
99
+ onClick={continueCheckout}
100
+ buttonLabel={cta?.buttonLabel ?? "Continue checkout"}
101
+ />
102
102
  </div>
103
103
  </div>
104
104
  </Modal>
@@ -0,0 +1,114 @@
1
+ import "@testing-library/jest-dom";
2
+
3
+ import React from "react";
4
+ import { ComparisonTable } from "./index";
5
+
6
+ import { render, screen } from "@testing-library/react";
7
+
8
+ jest.mock("@shared/components/text", () => ({
9
+ Text: ({ children, as, ...props }: any) => {
10
+ const Tag = as || "span";
11
+ return <Tag {...props}>{children}</Tag>;
12
+ },
13
+ }));
14
+
15
+ describe("ComparisonTable", () => {
16
+ const defaultProps = {
17
+ title: "Compare Plans",
18
+ disclaimer: <span>* Terms and conditions apply</span>,
19
+ table: (
20
+ <table data-testid="comparison-table-content">
21
+ <thead>
22
+ <tr>
23
+ <th>Feature</th>
24
+ <th>Basic</th>
25
+ <th>Pro</th>
26
+ </tr>
27
+ </thead>
28
+ <tbody>
29
+ <tr>
30
+ <td>Speed</td>
31
+ <td>100 Mbps</td>
32
+ <td>500 Mbps</td>
33
+ </tr>
34
+ </tbody>
35
+ </table>
36
+ ),
37
+ };
38
+
39
+ it("renders the component container", () => {
40
+ const { container } = render(<ComparisonTable {...defaultProps} />);
41
+ expect(container.querySelector(".component-container")).toBeInTheDocument();
42
+ });
43
+
44
+ it("renders the title as h2", () => {
45
+ render(<ComparisonTable {...defaultProps} />);
46
+ const title = screen.getByText("Compare Plans");
47
+ expect(title.tagName).toBe("H2");
48
+ });
49
+
50
+ it("renders the table content", () => {
51
+ render(<ComparisonTable {...defaultProps} />);
52
+ expect(screen.getByTestId("comparison-table-content")).toBeInTheDocument();
53
+ });
54
+
55
+ it("renders the disclaimer text", () => {
56
+ render(<ComparisonTable {...defaultProps} />);
57
+ expect(
58
+ screen.getByText("* Terms and conditions apply")
59
+ ).toBeInTheDocument();
60
+ });
61
+
62
+ it("applies max-w-120 class when maxWidth is true (default)", () => {
63
+ const { container } = render(<ComparisonTable {...defaultProps} />);
64
+ const inner = container.querySelector(".component-container > div");
65
+ expect(inner?.className).toContain("max-w-120");
66
+ expect(inner?.className).toContain("xl:mx-auto");
67
+ });
68
+
69
+ it("does not apply max-w-120 class when maxWidth is false", () => {
70
+ const { container } = render(
71
+ <ComparisonTable {...defaultProps} maxWidth={false} />
72
+ );
73
+ const inner = container.querySelector(".component-container > div");
74
+ expect(inner?.className).not.toContain("max-w-120");
75
+ expect(inner?.className).not.toContain("xl:mx-auto");
76
+ });
77
+
78
+ it("renders comparison-table-container", () => {
79
+ const { container } = render(<ComparisonTable {...defaultProps} />);
80
+ expect(
81
+ container.querySelector(".comparison-table-container")
82
+ ).toBeInTheDocument();
83
+ });
84
+
85
+ it("renders without title", () => {
86
+ const { container } = render(
87
+ <ComparisonTable {...defaultProps} title={undefined} />
88
+ );
89
+ expect(container.querySelector(".component-container")).toBeInTheDocument();
90
+ expect(screen.queryByText("Compare Plans")).not.toBeInTheDocument();
91
+ });
92
+
93
+ it("renders without disclaimer", () => {
94
+ const { container } = render(
95
+ <ComparisonTable {...defaultProps} disclaimer={undefined} />
96
+ );
97
+ expect(container.querySelector(".component-container")).toBeInTheDocument();
98
+ });
99
+
100
+ it("renders table as a ReactNode", () => {
101
+ const customTable = <div data-testid="custom-table">Custom content</div>;
102
+ render(<ComparisonTable {...defaultProps} table={customTable} />);
103
+ expect(screen.getByTestId("custom-table")).toBeInTheDocument();
104
+ });
105
+
106
+ it("renders with correct CSS structure", () => {
107
+ const { container } = render(<ComparisonTable {...defaultProps} />);
108
+ const tableContainer = container.querySelector(
109
+ ".comparison-table-container"
110
+ );
111
+ expect(tableContainer?.className).toContain("mt-5");
112
+ expect(tableContainer?.className).toContain("p-5");
113
+ });
114
+ });