diff --git a/tests/Unit/ChatScroll.test.tsx b/tests/Unit/ChatScroll.test.tsx index a6654dc9f..542c2b8a2 100644 --- a/tests/Unit/ChatScroll.test.tsx +++ b/tests/Unit/ChatScroll.test.tsx @@ -1,3 +1,4 @@ +/// import assert from 'node:assert/strict'; import * as React from 'react'; import type { ReactElement, ReactNode } from 'react'; @@ -28,6 +29,7 @@ vi.doMock('react', () => ({ }, useMemo: (compute: () => unknown) => compute(), useCallback: (callback: unknown) => callback, + useId: () => 'id', useEffect: (effect: () => unknown) => { effects.push(effect); }, @@ -41,7 +43,7 @@ vi.doMock('react', () => ({ })); vi.doMock('../../resources/js/hooks/use-chat', () => ({ useThread: () => thread, - useChat: () => ({}), + useChat: () => ({ mutePreferences: {} }), })); vi.doMock('../../resources/js/hooks/use-stash', () => ({ useStash: () => ({}), diff --git a/tests/Unit/CommitImages.test.tsx b/tests/Unit/CommitImages.test.tsx index cc26a3d0a..8f00ae38c 100644 --- a/tests/Unit/CommitImages.test.tsx +++ b/tests/Unit/CommitImages.test.tsx @@ -1,7 +1,7 @@ /// import assert from 'node:assert/strict'; -import { test } from 'node:test'; import { renderToStaticMarkup } from 'react-dom/server'; +import { test } from 'vite-plus/test'; import { CommitImages, gridImageObjectFit, @@ -36,6 +36,8 @@ const commit: Commit = { website: null, company: null, has_sponsors_listing: false, + timezone: null, + show_local_time: false, has_github_account: true, lane: 0, created_at: '2026-10-16T10:00:00Z', diff --git a/tests/Unit/CommitQuoteButton.test.tsx b/tests/Unit/CommitQuoteButton.test.tsx index c57f4aa96..69001241c 100644 --- a/tests/Unit/CommitQuoteButton.test.tsx +++ b/tests/Unit/CommitQuoteButton.test.tsx @@ -1,9 +1,9 @@ /// import assert from 'node:assert/strict'; -import { test } from 'node:test'; import { createInertiaApp } from '@inertiajs/react'; import { renderToString } from 'react-dom/server'; +import { test } from 'vite-plus/test'; import { CommitQuoteButton } from '../../resources/js/components/commit-quote-button'; import { TooltipProvider } from '../../resources/js/components/ui/tooltip'; import type { Commit } from '../../resources/js/types'; diff --git a/tests/Unit/CommitSecondaryActions.test.tsx b/tests/Unit/CommitSecondaryActions.test.tsx index b064250ff..532f15e78 100644 --- a/tests/Unit/CommitSecondaryActions.test.tsx +++ b/tests/Unit/CommitSecondaryActions.test.tsx @@ -1,9 +1,9 @@ /// import assert from 'node:assert/strict'; -import { test } from 'node:test'; import { createInertiaApp } from '@inertiajs/react'; import { renderToString } from 'react-dom/server'; +import { test } from 'vite-plus/test'; import { CommitSecondaryActions } from '../../resources/js/components/commit-secondary-actions'; import { TooltipProvider } from '../../resources/js/components/ui/tooltip'; import type { Commit } from '../../resources/js/types'; diff --git a/tests/Unit/CommitStarButton.test.tsx b/tests/Unit/CommitStarButton.test.tsx index 4807a7669..cec154fdc 100644 --- a/tests/Unit/CommitStarButton.test.tsx +++ b/tests/Unit/CommitStarButton.test.tsx @@ -1,9 +1,9 @@ /// import assert from 'node:assert/strict'; -import { test } from 'node:test'; import { createInertiaApp } from '@inertiajs/react'; import { renderToString } from 'react-dom/server'; +import { test } from 'vite-plus/test'; import { CommitStarButton } from '../../resources/js/components/commit-star-button'; import { TooltipProvider } from '../../resources/js/components/ui/tooltip'; import type { Commit } from '../../resources/js/types'; diff --git a/tests/Unit/FollowersYouKnow.test.tsx b/tests/Unit/FollowersYouKnow.test.tsx index b81f7c731..ac37ae846 100644 --- a/tests/Unit/FollowersYouKnow.test.tsx +++ b/tests/Unit/FollowersYouKnow.test.tsx @@ -1,9 +1,9 @@ /// import assert from 'node:assert/strict'; -import { test } from 'node:test'; import { createInertiaApp } from '@inertiajs/react'; import { renderToString } from 'react-dom/server'; +import { test } from 'vite-plus/test'; import { FollowersYouKnow } from '../../resources/js/components/followers-you-know'; import type { User } from '../../resources/js/types'; diff --git a/tests/Unit/LinkifiedText.test.tsx b/tests/Unit/LinkifiedText.test.tsx index 0ec93e3e2..0d2c461ec 100644 --- a/tests/Unit/LinkifiedText.test.tsx +++ b/tests/Unit/LinkifiedText.test.tsx @@ -814,6 +814,10 @@ for (const kind of ['direct', 'org'] as const) { partner_read_through_id: 0, partner_read_at: null, is_archived: false, + last_incoming_message_id: null, + last_incoming_sender: null, + pinned_at: null, + muted_until_at: null, }, ]} />, diff --git a/tests/Unit/LocalTime.test.tsx b/tests/Unit/LocalTime.test.tsx index fff15ec46..39efb6056 100644 --- a/tests/Unit/LocalTime.test.tsx +++ b/tests/Unit/LocalTime.test.tsx @@ -19,14 +19,9 @@ test('shows the time in the given timezone with its offset', () => { const html = renderToStaticMarkup( , ); - const expected = new Date('2026-07-01T10:06:00Z').toLocaleTimeString( - undefined, - { hour: 'numeric', minute: '2-digit', timeZone: 'Europe/Berlin' }, - ); - assert.ok(html.startsWith('

(UTC +02:00)`)); + assert.match(html, />12:06(?: PM)?<\/time> \(UTC \+02:00\)/); }); test('renders as a list item by default, for the profile details', () => { diff --git a/tests/Unit/NotificationInbox.test.tsx b/tests/Unit/NotificationInbox.test.tsx index 16619e4d9..0e566601f 100644 --- a/tests/Unit/NotificationInbox.test.tsx +++ b/tests/Unit/NotificationInbox.test.tsx @@ -1,12 +1,12 @@ /// import assert from 'node:assert/strict'; -import { test } from 'node:test'; import { createInertiaApp } from '@inertiajs/react'; import { renderToString } from 'react-dom/server'; +import { test } from 'vite-plus/test'; import { TooltipProvider } from '../../resources/js/components/ui/tooltip'; import Index from '../../resources/js/pages/notification/index'; -import type { NotificationGroup, User } from '../../resources/js/types'; +import type { Commit, NotificationGroup, User } from '../../resources/js/types'; const timestamp = '2026-10-16T12:00:00Z'; const actor: User = { @@ -26,6 +26,23 @@ const actor: User = { created_at: timestamp, updated_at: timestamp, }; +const quoteCommit: Omit = { + id: 'quote-commit', + user_id: actor.id, + parent_id: null, + hot_score: 0, + body: 'My quote commentary', + mentions: {}, + body_segments: [{ type: 'text', text: 'My quote commentary' }], + preview_segments: null, + status: 'approved', + created_at: timestamp, + updated_at: timestamp, + stars_count: 0, + pasteis_count: 0, + starred: false, + quoted_commit_id: 'quoted-commit', +}; const quote: NotificationGroup = { id: 'notice', recipient_id: 'recipient', @@ -43,20 +60,7 @@ const quote: NotificationGroup = { unread_count: 0, created_at: timestamp, updated_at: timestamp, - commit: { - id: 'quote-commit', - user_id: actor.id, - parent_id: null, - body: 'My quote commentary', - body_segments: [{ type: 'text', text: 'My quote commentary' }], - preview_segments: null, - status: 'approved', - created_at: timestamp, - updated_at: timestamp, - stars_count: 0, - starred: false, - quoted_commit_id: 'quoted-commit', - }, + commit: quoteCommit, }; async function renderInbox(notifications: NotificationGroup[], type = 'all') { @@ -101,6 +105,21 @@ async function renderInbox(notifications: NotificationGroup[], type = 'all') { return result.body.replaceAll('', ''); } +// The filters render once for mobile and once for desktop, so count each category once. +function categories(html: string, pressed: boolean): Set { + return new Set( + Array.from( + html.matchAll( + new RegExp( + `aria-pressed="${pressed}"[^>]*>([^<]+)`, + 'g', + ), + ), + (match) => match[1], + ), + ); +} + void test('renders quote notifications with their label and quoting commit destination', async () => { const html = await renderInbox([quote]); @@ -115,10 +134,11 @@ void test('shows the quoted commit under a quote, or a tombstone once it is dele { ...quote, commit: { - ...quote.commit, + ...quoteCommit, quoted_commit: { id: 'quoted-commit', body_segments: [{ type: 'text', text: 'My quoted commit' }], + deleted_at: null, }, }, }, @@ -131,7 +151,7 @@ void test('shows the quoted commit under a quote, or a tombstone once it is dele { ...quote, commit: { - ...quote.commit, + ...quoteCommit, quoted_commit: { id: 'quoted-commit', parent_id: null, @@ -162,7 +182,7 @@ void test('renders a follow-back control outside the follower profile link', asy html, /Quoter<\/a>.*@quoter<\/a>(?:<\/span>)? ]*data-notification-link[^>]*>followed you<\/a>/s, ); - assert.match(html, /]*aria-label="Follow @quoter"/); + assert.match(html, /]*aria-label="Follow @quoter back"/); assert.doesNotMatch(html, /]*>(?:(?!<\/a>).)* and 1 other ]*data-notification-link[^>]*>followed you<\/a>/, + /Quoter<\/a> ]*>and 1 other <\/span>]*data-notification-link[^>]*>followed you<\/a>/, ); assert.match(html, /aria-expanded="false"[^>]*>.*2 followers/s); assert.doesNotMatch(html, /aria-label="(Follow|Unfollow) @/); @@ -198,7 +218,7 @@ void test('renders notification code without altering emoji shortcodes or nestin { ...quote, commit: { - ...quote.commit, + ...quoteCommit, body: '`echo ":fire:"`', body_segments: [{ type: 'code', code: 'echo ":fire:"' }], }, @@ -214,7 +234,7 @@ void test('renders highlighted notification blocks without a nested copy button' { ...quote, commit: { - ...quote.commit, + ...quoteCommit, body: '```php\nreturn ":fire:";\n```', body_segments: [ { @@ -236,16 +256,16 @@ void test('renders highlighted notification blocks without a nested copy button' void test('renders all notification categories as enabled toggle buttons by default', async () => { const body = await renderInbox([]); - assert.equal((body.match(/aria-pressed="true"/g) ?? []).length, 6); + assert.equal(categories(body, true).size, 6); assert.doesNotMatch(body, /aria-current="page"|role="tab"/); }); void test('renders independent category selections and the all-off empty state', async () => { const selected = await renderInbox([], 'replies,mentions'); - assert.equal((selected.match(/aria-pressed="true"/g) ?? []).length, 2); - assert.equal((selected.match(/aria-pressed="false"/g) ?? []).length, 4); + assert.deepEqual([...categories(selected, true)], ['Replies', 'Mentions']); + assert.equal(categories(selected, false).size, 4); const none = await renderInbox([], 'none'); - assert.equal((none.match(/aria-pressed="false"/g) ?? []).length, 6); + assert.equal(categories(none, false).size, 6); assert.match(none, /All categories are turned off/); assert.match(none, /Show all notifications/); }); diff --git a/tests/Unit/NotificationIndex.test.tsx b/tests/Unit/NotificationIndex.test.tsx index b9be50c0f..8b041596a 100644 --- a/tests/Unit/NotificationIndex.test.tsx +++ b/tests/Unit/NotificationIndex.test.tsx @@ -1,10 +1,10 @@ /// import assert from 'node:assert/strict'; -import { test } from 'node:test'; import { createInertiaApp } from '@inertiajs/react'; import type { ReactElement } from 'react'; import { renderToString } from 'react-dom/server'; +import { test } from 'vite-plus/test'; import { TooltipProvider } from '../../resources/js/components/ui/tooltip'; import { laneColor } from '../../resources/js/lib/utils'; import Index, { @@ -136,7 +136,7 @@ void test('separates last-visit activity without a border under the preceding no }); void test('colors each notification rail by actor and ends it in a dot', async () => { - const nextActor = { ...other, lane: 5 }; + const nextActor = { ...other, lane: 5, is_followed: false }; const body = await render([ notification, { @@ -186,7 +186,7 @@ for (const isFollowing of [false, true]) { assert.match( body, new RegExp( - `aria-label="${isFollowing ? 'Unfollow' : 'Follow'} @mnapoli"`, + `aria-label="${isFollowing ? 'Unfollow @mnapoli' : 'Follow @mnapoli back'}"`, ), ); assert.match(body, /followed you/); @@ -218,7 +218,7 @@ void test('shows the image of a starred image-only commit beside who starred it' assert.match( body, - /and 2 others ]*data-notification-link[^>]*>starred your image<\/a>/, + /and 2 others <\/span>]*data-notification-link[^>]*>starred your image<\/a>/, ); assert.match(body, /]*src="\/commit-images\/image"/); assert.doesNotMatch(body, /]*>(?:]*>)*(?:<\/div>)+<\/a>/); diff --git a/tests/Unit/QuotedCommit.test.tsx b/tests/Unit/QuotedCommit.test.tsx index 45f086bf7..482194ce3 100644 --- a/tests/Unit/QuotedCommit.test.tsx +++ b/tests/Unit/QuotedCommit.test.tsx @@ -1,9 +1,9 @@ /// import assert from 'node:assert/strict'; -import { test } from 'node:test'; import { createInertiaApp } from '@inertiajs/react'; import { renderToString } from 'react-dom/server'; +import { test } from 'vite-plus/test'; import { QuotedCommit } from '../../resources/js/components/quoted-commit'; import type { LinkPreview, diff --git a/tests/draft-images.config.ts b/tests/draft-images.config.ts index 527f7ef17..4a56c462f 100644 --- a/tests/draft-images.config.ts +++ b/tests/draft-images.config.ts @@ -11,57 +11,6 @@ export default defineConfig({ env: { VITE_APP_NAME: '/dev/null', }, - include: [ - 'tests/Unit/CommitPhoto.test.tsx', - 'tests/Unit/CommitSize.test.tsx', - 'tests/Unit/ThreadHistory.test.tsx', - 'tests/Unit/ThreadMutationHistory.test.tsx', - 'tests/Unit/ActivityCells.test.tsx', - 'tests/Unit/AuthorHover.test.tsx', - 'tests/Unit/FollowState.test.tsx', - 'tests/Unit/FollowRequests.test.tsx', - 'tests/Unit/GuestFollow.test.tsx', - 'tests/Unit/DraftStash.test.tsx', - 'tests/Unit/DraftImageReads.test.tsx', - 'tests/Unit/DraftImagePreview.test.tsx', - 'tests/Unit/ImageAttachments.test.tsx', - 'tests/Unit/ComposerDraft.test.tsx', - 'tests/Unit/QuoteDraft.test.tsx', - 'tests/Unit/EmojiSearch.test.tsx', - 'tests/Unit/IssueReference.test.tsx', - 'tests/Unit/ReactionTray.test.tsx', - 'tests/Unit/NotificationFilters.test.tsx', - 'tests/Unit/EmojiShortcodes.test.tsx', - 'tests/Unit/QuoteReply.test.tsx', - 'tests/Unit/CommitThread.test.tsx', - 'tests/Unit/SearchDialogComposition.test.tsx', - 'tests/Unit/TabSlide.test.tsx', - 'tests/Unit/ReportResolution.test.tsx', - 'tests/Unit/LinkifiedText.test.tsx', - 'tests/Unit/CommitPoll.test.tsx', - 'tests/Unit/GraphCanvas.test.tsx', - 'tests/Unit/RailEntrance.test.tsx', - 'tests/Unit/LazyTooltip.test.tsx', - 'tests/Unit/ReportDialog.test.tsx', - 'tests/Unit/StandaloneCommitGraphs.test.tsx', - 'tests/Unit/LogInsertion.test.tsx', - 'tests/Unit/StagedRail.test.tsx', - 'tests/Unit/ComposerPreview.test.tsx', - 'tests/Unit/UseNow.test.tsx', - 'tests/Unit/LinkEmbedHeight.test.tsx', - 'tests/Unit/CommitRules.test.tsx', - 'tests/Unit/TimeoutRefusals.test.tsx', - 'tests/Unit/VoiceVolume.test.tsx', - 'tests/Unit/VoiceVolumeHover.test.tsx', - 'tests/Unit/VoiceSpeed.test.tsx', - 'tests/Unit/VoiceNoteSpeed.test.tsx', - 'tests/Unit/OrgMentionSuggestions.test.tsx', - 'tests/Unit/RemoteNotifications.test.tsx', - 'tests/Unit/RemotePush.test.tsx', - 'tests/Unit/RemotePins.test.tsx', - 'tests/Unit/RemotePreferences.test.tsx', - 'tests/Unit/ChatNavigation.test.tsx', - 'tests/Unit/CommittedToast.test.tsx', - ], + include: ['tests/Unit/*.test.tsx'], }, }); diff --git a/tests/tsconfig.json b/tests/tsconfig.json index 8ce2749f6..2c64c4ad6 100644 --- a/tests/tsconfig.json +++ b/tests/tsconfig.json @@ -6,32 +6,7 @@ }, "include": [ "Unit/**/*.test.ts", - "Unit/CommitPhoto.test.tsx", - "Unit/DraftStash.test.tsx", - "Unit/ComposerDraft.test.tsx", - "Unit/SearchDialogComposition.test.tsx", - "Unit/ReactionTray.test.tsx", - "Unit/NotificationFilters.test.tsx", - "Unit/DraftImageReads.test.tsx", - "Unit/DraftImagePreview.test.tsx", - "Unit/ImageAttachments.test.tsx", - "Unit/StagedRail.test.tsx", - "Unit/StandaloneCommitGraphs.test.tsx", - "Unit/UseNow.test.tsx", - "Unit/FollowRequests.test.tsx", - "Unit/FollowState.test.tsx", - "Unit/LinkEmbedHeight.test.tsx", - "Unit/LogInsertion.test.tsx", - "Unit/VoiceVolume.test.tsx", - "Unit/VoiceVolumeHover.test.tsx", - "Unit/VoiceSpeed.test.tsx", - "Unit/VoiceNoteSpeed.test.tsx", - "Unit/OrgMentionSuggestions.test.tsx", - "Unit/IssueReference.test.tsx", - "Unit/RemoteNotifications.test.tsx", - "Unit/RemotePush.test.tsx", - "Unit/RemotePreferences.test.tsx", - "Unit/ChatNavigation.test.tsx", + "Unit/**/*.test.tsx", "../resources/js/types/*.d.ts", "draft-images.config.ts" ]