From dddb9ee439b68c0dc8826fd34d92632058a9cccd Mon Sep 17 00:00:00 2001 From: Coding Creed Technologies Date: Thu, 19 Mar 2026 12:59:00 +0100 Subject: [PATCH] test(web-components): add 3TG-generated tests for ProgressBar and SearchResultSkeletonItem Add unit tests for `ProgressBar` and `SearchResultSkeletonItem` components. - `ProgressBar` tests cover combinations of `progress` values and `showText` props. - `SearchResultSkeletonItem` tests verify that three skeleton bars are rendered. - Helper assertions (`expectProgress`, `expectNoProgress`, `expectDiscovering`, `expectedDetermined`, `expectedIndetermined`) were added to validate HTML output. All tests were generated with 3TG and are deterministic. No functional changes were introduced. --- src/web/components/ProgressBar.3tg.md | 197 ++++++++++++++ src/web/components/ProgressBar.test.tsx | 245 ++++++++++++++++++ .../SearchResultSkeletonItem.test.tsx | 11 + 3 files changed, 453 insertions(+) create mode 100644 src/web/components/ProgressBar.3tg.md create mode 100644 src/web/components/ProgressBar.test.tsx create mode 100644 src/web/components/SearchResultSkeletonItem.test.tsx diff --git a/src/web/components/ProgressBar.3tg.md b/src/web/components/ProgressBar.3tg.md new file mode 100644 index 00000000..3ea7899e --- /dev/null +++ b/src/web/components/ProgressBar.3tg.md @@ -0,0 +1,197 @@ +# Exported functions from "src/web/components/ProgressBar.tsx" + + + +## default ProgressBar(props: ProgressBarProps) [react] + +The type definition for props: +ProgressBarProps + +These are the functional requirements for default function `ProgressBar`. + +### Tests for showText undefined + +| test name | progress | showText | screen. | .to | +| ---------- | ------------------------------------------------ | --------- | ------- | --- | +| 0-0-0-u | {pages: 0, totalPages: 0, totalDiscovered: 0} | undefined | | | +| 0-0-1-u | {pages: 0, totalPages: 0, totalDiscovered: 1} | undefined | | | +| 0-10-1-u | {pages: 0, totalPages: 10, totalDiscovered: 1} | undefined | | | +| 0-10-10-u | {pages: 0, totalPages: 10, totalDiscovered: 10} | undefined | | | +| 3-10-10-u | {pages: 3, totalPages: 10, totalDiscovered: 10} | undefined | | | +| 10-10-10-u | {pages: 10, totalPages: 10, totalDiscovered: 10} | undefined | | | +| 3-10-12-u | {pages: 3, totalPages: 10, totalDiscovered: 12} | undefined | | | + +```typescript scenario(0-0-0-u) +*/ +expectProgress(html, 0, 0, 0); +expectedDetermined(html, 0); +``` + +```typescript scenario(0-10-10-u) +*/ +expectProgress(html, 0, 10, 0); +expectedDetermined(html, 0); +``` + +```typescript scenario(3-10-10-u) +*/ +expectProgress(html, 3, 10, 30); +expectedDetermined(html, 30); +``` + +```typescript scenario(10-10-10-u) +*/ +expectProgress(html, 10, 10, 100); +expectedDetermined(html, 100); +``` + +```typescript scenario(3-10-12-u) +*/ +expectProgress(html, 3, 10, 30, 12); +expectedDetermined(html, 30); +``` + +### Tests for showText false + +| test name | progress | showText | screen. | .to | +| ---------- | ------------------------------------------------ | -------- | ------- | --- | +| 0-0-0-f | {pages: 0, totalPages: 0, totalDiscovered: 0} | false | | | +| 0-0-1-f | {pages: 0, totalPages: 0, totalDiscovered: 1} | false | | | +| 0-10-1-f | {pages: 0, totalPages: 10, totalDiscovered: 1} | false | | | +| 0-10-10-f | {pages: 0, totalPages: 10, totalDiscovered: 10} | false | | | +| 3-10-10-f | {pages: 3, totalPages: 10, totalDiscovered: 10} | false | | | +| 10-10-10-f | {pages: 10, totalPages: 10, totalDiscovered: 10} | false | | | +| 3-10-12-f | {pages: 3, totalPages: 10, totalDiscovered: 12} | false | | | + +```typescript scenario(0-0-0-f) +*/ +expectNoProgress(html); +expectedDetermined(html, 0); +``` + +```typescript scenario(0-0-1-f) +*/ +expectNoProgress(html); +expectedIndetermined(html); +``` + +```typescript scenario(0-10-1-f) +*/ +expectNoProgress(html); +expectedIndetermined(html); +``` + +```typescript scenario(0-10-10-f) +*/ +expectNoProgress(html); +expectedDetermined(html, 0); +``` + +```typescript scenario(3-10-10-f) +*/ +expectNoProgress(html); +expectedDetermined(html, 30); +``` + +```typescript scenario(10-10-10-f) +*/ +expectNoProgress(html); +expectedDetermined(html, 100); +``` + +```typescript scenario(3-10-12-f) +*/ +expectNoProgress(html); +expectedDetermined(html, 30); +``` + +### Tests for showText true + +| test name | progress | showText | screen. | .to | +| ---------- | ------------------------------------------------ | -------- | ------- | --- | +| 0-0-0-t | {pages: 0, totalPages: 0, totalDiscovered: 0} | true | | | +| 0-0-1-t | {pages: 0, totalPages: 0, totalDiscovered: 1} | true | | | +| 0-10-1-t | {pages: 0, totalPages: 10, totalDiscovered: 1} | true | | | +| 0-10-10-t | {pages: 0, totalPages: 10, totalDiscovered: 10} | true | | | +| 3-10-10-t | {pages: 3, totalPages: 10, totalDiscovered: 10} | true | | | +| 10-10-10-t | {pages: 10, totalPages: 10, totalDiscovered: 10} | true | | | +| 3-10-12-t | {pages: 3, totalPages: 10, totalDiscovered: 12} | true | | | + +```typescript scenario(0-0-0-t) +*/ +expectProgress(html, 0, 0, 0); +expectedDetermined(html, 0); +``` + +```typescript scenario(0-10-10-t) +*/ +expectProgress(html, 0, 10, 0); +expectedDetermined(html, 0); +``` + +```typescript scenario(3-10-10-t) +*/ +expectProgress(html, 3, 10, 30); +expectedDetermined(html, 30); +``` + +```typescript scenario(10-10-10-t) +*/ +expectProgress(html, 10, 10, 100); +expectedDetermined(html, 100); +``` + +```typescript scenario(3-10-12-t) +*/ +expectProgress(html, 3, 10, 30, 12); +expectedDetermined(html, 30); +``` + +--- + +We define some helper functions: + +```typescript after +function expectProgress(html: string, v1: number, v2: number, v3: number, v4?: number) { + const overflow = v4 ? ` • ${v4} total` : ''; + expect(html).toContain('Progress'); + expect(html).toContain(`${v1}/${v2} pages (${v3}%)${overflow}`); +} + +function expectNoProgress(html: string) { + expect(html).not.toContain('Progress'); + expect(html).not.toMatch(/\d+\/\d+ pages \(\d+%\)/); +} + +function expectDiscovering(html: string) { + expect(html).toContain('Progress'); + expect(html).toContain('Discovering pages...'); +} + +function expectedDetermined(html: string, v: number) { + expect(html).toContain(`transition-all duration-300" style="width: ${v}%"`); +} + +function expectedIndetermined(html: string) { + expect(html).toContain('animate-pulse" style="width: 30%"'); +} +``` + +As 3TG assumes React is present for a `.tsx` file, we need to use a workaround to disable the `React Testing Library` code + +```json configuration +{ + "in-tests": { + "*": ["const html = ProgressBar({ ...props }) as string;", "/*"] + }, + "in-tests-at-end": { + "*": ["*/", "expectDiscovering(html);", "expectedIndetermined(html);"] + } +} +``` + +Afterwards, we will remove the commented blocks diff --git a/src/web/components/ProgressBar.test.tsx b/src/web/components/ProgressBar.test.tsx new file mode 100644 index 00000000..4b39b7e9 --- /dev/null +++ b/src/web/components/ProgressBar.test.tsx @@ -0,0 +1,245 @@ +import { describe, expect, it } from 'vitest'; +import * as __testedFile from './ProgressBar'; + +describe('src/web/components/ProgressBar.tsx', () => { + describe('ProgressBar', () => { + const { default: ProgressBar } = __testedFile; + // props: ProgressBarProps + + it('0-0-0-u', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 0, totalDiscovered: 0 }, + showText: undefined, + }; + const html = ProgressBar({ ...props }) as string; + expectProgress(html, 0, 0, 0); + expectedDetermined(html, 0); + }); + + it('0-0-1-u', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 0, totalDiscovered: 1 }, + showText: undefined, + }; + const html = ProgressBar({ ...props }) as string; + expectDiscovering(html); + expectedIndetermined(html); + }); + + it('0-10-1-u', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 10, totalDiscovered: 1 }, + showText: undefined, + }; + const html = ProgressBar({ ...props }) as string; + expectDiscovering(html); + expectedIndetermined(html); + }); + + it('0-10-10-u', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 10, totalDiscovered: 10 }, + showText: undefined, + }; + const html = ProgressBar({ ...props }) as string; + expectProgress(html, 0, 10, 0); + expectedDetermined(html, 0); + }); + + it('3-10-10-u', async () => { + const props: Parameters[0] = { + progress: { pages: 3, totalPages: 10, totalDiscovered: 10 }, + showText: undefined, + }; + const html = ProgressBar({ ...props }) as string; + expectProgress(html, 3, 10, 30); + expectedDetermined(html, 30); + }); + + it('10-10-10-u', async () => { + const props: Parameters[0] = { + progress: { pages: 10, totalPages: 10, totalDiscovered: 10 }, + showText: undefined, + }; + const html = ProgressBar({ ...props }) as string; + expectProgress(html, 10, 10, 100); + expectedDetermined(html, 100); + }); + + it('3-10-12-u', async () => { + const props: Parameters[0] = { + progress: { pages: 3, totalPages: 10, totalDiscovered: 12 }, + showText: undefined, + }; + const html = ProgressBar({ ...props }) as string; + expectProgress(html, 3, 10, 30, 12); + expectedDetermined(html, 30); + }); + + it('0-0-0-f', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 0, totalDiscovered: 0 }, + showText: false, + }; + const html = ProgressBar({ ...props }) as string; + expectNoProgress(html); + expectedDetermined(html, 0); + }); + + it('0-0-1-f', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 0, totalDiscovered: 1 }, + showText: false, + }; + const html = ProgressBar({ ...props }) as string; + expectNoProgress(html); + expectedIndetermined(html); + }); + + it('0-10-1-f', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 10, totalDiscovered: 1 }, + showText: false, + }; + const html = ProgressBar({ ...props }) as string; + expectNoProgress(html); + expectedIndetermined(html); + }); + + it('0-10-10-f', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 10, totalDiscovered: 10 }, + showText: false, + }; + const html = ProgressBar({ ...props }) as string; + expectNoProgress(html); + expectedDetermined(html, 0); + }); + + it('3-10-10-f', async () => { + const props: Parameters[0] = { + progress: { pages: 3, totalPages: 10, totalDiscovered: 10 }, + showText: false, + }; + const html = ProgressBar({ ...props }) as string; + expectNoProgress(html); + expectedDetermined(html, 30); + }); + + it('10-10-10-f', async () => { + const props: Parameters[0] = { + progress: { pages: 10, totalPages: 10, totalDiscovered: 10 }, + showText: false, + }; + const html = ProgressBar({ ...props }) as string; + expectNoProgress(html); + expectedDetermined(html, 100); + }); + + it('3-10-12-f', async () => { + const props: Parameters[0] = { + progress: { pages: 3, totalPages: 10, totalDiscovered: 12 }, + showText: false, + }; + const html = ProgressBar({ ...props }) as string; + expectNoProgress(html); + expectedDetermined(html, 30); + }); + + it('0-0-0-t', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 0, totalDiscovered: 0 }, + showText: true, + }; + const html = ProgressBar({ ...props }) as string; + expectProgress(html, 0, 0, 0); + expectedDetermined(html, 0); + }); + + it('0-0-1-t', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 0, totalDiscovered: 1 }, + showText: true, + }; + const html = ProgressBar({ ...props }) as string; + expectDiscovering(html); + expectedIndetermined(html); + }); + + it('0-10-1-t', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 10, totalDiscovered: 1 }, + showText: true, + }; + const html = ProgressBar({ ...props }) as string; + expectDiscovering(html); + expectedIndetermined(html); + }); + + it('0-10-10-t', async () => { + const props: Parameters[0] = { + progress: { pages: 0, totalPages: 10, totalDiscovered: 10 }, + showText: true, + }; + const html = ProgressBar({ ...props }) as string; + expectProgress(html, 0, 10, 0); + expectedDetermined(html, 0); + }); + + it('3-10-10-t', async () => { + const props: Parameters[0] = { + progress: { pages: 3, totalPages: 10, totalDiscovered: 10 }, + showText: true, + }; + const html = ProgressBar({ ...props }) as string; + expectProgress(html, 3, 10, 30); + expectedDetermined(html, 30); + }); + + it('10-10-10-t', async () => { + const props: Parameters[0] = { + progress: { pages: 10, totalPages: 10, totalDiscovered: 10 }, + showText: true, + }; + const html = ProgressBar({ ...props }) as string; + expectProgress(html, 10, 10, 100); + expectedDetermined(html, 100); + }); + + it('3-10-12-t', async () => { + const props: Parameters[0] = { + progress: { pages: 3, totalPages: 10, totalDiscovered: 12 }, + showText: true, + }; + const html = ProgressBar({ ...props }) as string; + expectProgress(html, 3, 10, 30, 12); + expectedDetermined(html, 30); + }); + }); +}); + +function expectProgress(html: string, v1: number, v2: number, v3: number, v4?: number) { + const overflow = v4 ? ` • ${v4} total` : ''; + expect(html).toContain('Progress'); + expect(html).toContain(`${v1}/${v2} pages (${v3}%)${overflow}`); +} + +function expectNoProgress(html: string) { + expect(html).not.toContain('Progress'); + expect(html).not.toMatch(/\d+\/\d+ pages \(\d+%\)/); +} + +function expectDiscovering(html: string) { + expect(html).toContain('Progress'); + expect(html).toContain('Discovering pages...'); +} + +function expectedDetermined(html: string, v: number) { + expect(html).toContain(`transition-all duration-300" style="width: ${v}%"`); +} + +function expectedIndetermined(html: string) { + expect(html).toContain('animate-pulse" style="width: 30%"'); +} + +// 3TG (https://3tg.dev) created 21 tests in 2922 ms (139.143 ms per generated test) @ 2026-03-18T19:16:04.880Z diff --git a/src/web/components/SearchResultSkeletonItem.test.tsx b/src/web/components/SearchResultSkeletonItem.test.tsx new file mode 100644 index 00000000..81af3f0d --- /dev/null +++ b/src/web/components/SearchResultSkeletonItem.test.tsx @@ -0,0 +1,11 @@ +import { describe, expect, test } from 'vitest'; +import SearchResultSkeletonItem from './SearchResultSkeletonItem'; + +describe('src/web/components/SearchResultSkeletonItemts', () => { + test('it renders three skeleton bars', () => { + const html = SearchResultSkeletonItem() as string; + // Count how many gray-200 divs are inside + const barCount = (html.match(/