@happyvertical/smrt-chat 0.53.0 → 0.53.2
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.
- package/dist/index.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/smrt-knowledge.json +3 -3
- package/package.json +13 -13
- package/dist/__tests__/chat-test-db.d.ts +0 -8
- package/dist/__tests__/chat-test-db.d.ts.map +0 -1
- package/dist/__tests__/raw-collections.d.ts +0 -35
- package/dist/__tests__/raw-collections.d.ts.map +0 -1
- package/dist/svelte/components/agent/__tests__/ToolCallDisplay.test.js +0 -135
- package/dist/svelte/components/agent/message-blocks.test.js +0 -31
- package/dist/svelte/components/assistant/__tests__/AssistantComposer.test.js +0 -75
- package/dist/svelte/components/assistant/__tests__/AssistantDock.test.js +0 -515
- package/dist/svelte/components/assistant/__tests__/AssistantThreadList.test.js +0 -45
- package/dist/svelte/components/assistant/__tests__/action-status.test.js +0 -25
- package/dist/svelte/components/assistant/__tests__/assistant-composer-dictation.test.js +0 -197
- package/dist/svelte/components/assistant/__tests__/assistant-dock-action-outcome.test.js +0 -320
- package/dist/svelte/components/assistant/__tests__/assistant-dock-choices-pending.test.js +0 -224
- package/dist/svelte/components/assistant/__tests__/assistant-dock-choices-preview.test.js +0 -312
- package/dist/svelte/components/assistant/__tests__/assistant-dock-choices.test.js +0 -338
- package/dist/svelte/components/assistant/__tests__/assistant-dock-client-tools.test.js +0 -442
- package/dist/svelte/components/assistant/__tests__/assistant-dock-draft-settled.test.js +0 -448
- package/dist/svelte/components/assistant/__tests__/assistant-dock-host-actions.test.js +0 -185
- package/dist/svelte/components/assistant/__tests__/assistant-dock-run.test.js +0 -303
- package/dist/svelte/components/assistant/__tests__/assistant-dock-settle.test.js +0 -146
- package/dist/svelte/components/assistant/__tests__/assistant-transport.test.js +0 -253
- package/dist/svelte/components/assistant/__tests__/attachment-href.test.js +0 -31
- package/dist/svelte/components/assistant/__tests__/create-assistant-dock-controller.test.js +0 -2491
- package/dist/svelte/components/layout/__tests__/ChatLayout.test.js +0 -67
- package/dist/svelte/components/messages/__tests__/MessageInput.test.js +0 -38
- package/dist/svelte/components/messages/__tests__/MessageItem.test.js +0 -108
- package/dist/svelte/components/shared/__tests__/Avatar.test.js +0 -20
- package/dist/svelte/components/shared/__tests__/LinkPreview.test.js +0 -29
- package/dist/svelte/components/shared/__tests__/MentionAutocomplete.test.js +0 -66
- package/dist/svelte/components/shared/__tests__/ModelPicker.test.js +0 -36
- package/dist/svelte/components/shared/__tests__/ReactionPicker.test.js +0 -35
- package/dist/svelte/components/shared/__tests__/ReadReceipts.test.js +0 -28
- package/dist/svelte/components/shared/__tests__/UserPresence.test.js +0 -23
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
/**
|
|
3
|
-
* Component coverage for ChatLayout via the shared S11 harness (#1416).
|
|
4
|
-
*
|
|
5
|
-
* Regression for T2 #1391: ChatLayout rendered RoomList but never forwarded
|
|
6
|
-
* `oncreateroom`, so the bundled layout's create-room affordance was dead.
|
|
7
|
-
* ChatLayout must now accept and forward `oncreateroom` to RoomList.
|
|
8
|
-
*/
|
|
9
|
-
import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
|
|
10
|
-
import { createRawSnippet } from 'svelte';
|
|
11
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
12
|
-
import ChatLayout from '../ChatLayout.svelte';
|
|
13
|
-
const ROOMS = [
|
|
14
|
-
{
|
|
15
|
-
id: 'room-1',
|
|
16
|
-
name: 'General',
|
|
17
|
-
description: '',
|
|
18
|
-
roomType: 'public',
|
|
19
|
-
topic: '',
|
|
20
|
-
participantCount: 1,
|
|
21
|
-
unreadCount: 0,
|
|
22
|
-
},
|
|
23
|
-
];
|
|
24
|
-
// Minimal main-content snippet for the required `children` prop.
|
|
25
|
-
const content = createRawSnippet(() => ({
|
|
26
|
-
render: () => '<div data-testid="main">Main content</div>',
|
|
27
|
-
}));
|
|
28
|
-
describe('ChatLayout', () => {
|
|
29
|
-
it('forwards oncreateroom to RoomList so the create affordance is wired', async () => {
|
|
30
|
-
const oncreateroom = vi.fn();
|
|
31
|
-
render(ChatLayout, {
|
|
32
|
-
props: {
|
|
33
|
-
rooms: ROOMS,
|
|
34
|
-
currentProfileId: 'p1',
|
|
35
|
-
onselectroom: vi.fn(),
|
|
36
|
-
oncreateroom,
|
|
37
|
-
children: content,
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
const createBtn = screen.getByRole('button', { name: 'Create new room' });
|
|
41
|
-
await userEvent.click(createBtn);
|
|
42
|
-
expect(oncreateroom).toHaveBeenCalledTimes(1);
|
|
43
|
-
});
|
|
44
|
-
it('omits the create affordance when no oncreateroom is supplied', () => {
|
|
45
|
-
render(ChatLayout, {
|
|
46
|
-
props: {
|
|
47
|
-
rooms: ROOMS,
|
|
48
|
-
currentProfileId: 'p1',
|
|
49
|
-
onselectroom: vi.fn(),
|
|
50
|
-
children: content,
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
expect(screen.queryByRole('button', { name: 'Create new room' })).toBeNull();
|
|
54
|
-
});
|
|
55
|
-
it('is axe-clean', async () => {
|
|
56
|
-
const { container } = render(ChatLayout, {
|
|
57
|
-
props: {
|
|
58
|
-
rooms: ROOMS,
|
|
59
|
-
currentProfileId: 'p1',
|
|
60
|
-
onselectroom: vi.fn(),
|
|
61
|
-
oncreateroom: vi.fn(),
|
|
62
|
-
children: content,
|
|
63
|
-
},
|
|
64
|
-
});
|
|
65
|
-
await expectNoA11yViolations(container);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
/**
|
|
3
|
-
* Component coverage for MessageInput via the shared S11 harness (#1416).
|
|
4
|
-
*/
|
|
5
|
-
import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
|
|
6
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
7
|
-
import MessageInput from '../MessageInput.svelte';
|
|
8
|
-
describe('MessageInput', () => {
|
|
9
|
-
it('sends the trimmed content on Enter', async () => {
|
|
10
|
-
const onsend = vi.fn();
|
|
11
|
-
render(MessageInput, { props: { onsend } });
|
|
12
|
-
await userEvent.type(screen.getByLabelText('Message input'), 'Hello{Enter}');
|
|
13
|
-
expect(onsend).toHaveBeenCalledWith('Hello');
|
|
14
|
-
});
|
|
15
|
-
it('does not send empty content', async () => {
|
|
16
|
-
const onsend = vi.fn();
|
|
17
|
-
render(MessageInput, { props: { onsend } });
|
|
18
|
-
await userEvent.type(screen.getByLabelText('Message input'), '{Enter}');
|
|
19
|
-
expect(onsend).not.toHaveBeenCalled();
|
|
20
|
-
});
|
|
21
|
-
it('shows a reply banner and cancels it', async () => {
|
|
22
|
-
const oncancelreply = vi.fn();
|
|
23
|
-
render(MessageInput, {
|
|
24
|
-
props: {
|
|
25
|
-
onsend: vi.fn(),
|
|
26
|
-
replyTo: { id: 'm1', senderName: 'Ada', content: 'earlier message' },
|
|
27
|
-
oncancelreply,
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
expect(screen.getByText('Ada')).toBeInTheDocument();
|
|
31
|
-
await userEvent.click(screen.getByRole('button', { name: 'Cancel reply' }));
|
|
32
|
-
expect(oncancelreply).toHaveBeenCalledTimes(1);
|
|
33
|
-
});
|
|
34
|
-
it('is axe-clean', async () => {
|
|
35
|
-
const { container } = render(MessageInput, { props: { onsend: vi.fn() } });
|
|
36
|
-
await expectNoA11yViolations(container);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
/**
|
|
3
|
-
* Component coverage for MessageItem via the shared S11 harness (#1416).
|
|
4
|
-
*
|
|
5
|
-
* Regression for the T2 #1391 a11y blocker: the per-message actions
|
|
6
|
-
* (reply/edit/delete/react) used to be revealed ONLY by mouseenter/mouseleave,
|
|
7
|
-
* so keyboard- and touch-only users could never reach them. There must now be a
|
|
8
|
-
* keyboard-reachable "more actions" button that toggles the action bar, and
|
|
9
|
-
* focusing into the row must reveal the actions.
|
|
10
|
-
*/
|
|
11
|
-
import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
|
|
12
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
13
|
-
import MessageItem from '../MessageItem.svelte';
|
|
14
|
-
function makeMessage(overrides = {}) {
|
|
15
|
-
return {
|
|
16
|
-
id: 'm1',
|
|
17
|
-
roomId: 'room-1',
|
|
18
|
-
threadId: null,
|
|
19
|
-
senderProfileId: 'p1',
|
|
20
|
-
senderName: 'Ada',
|
|
21
|
-
senderAvatarUrl: undefined,
|
|
22
|
-
agentSessionId: null,
|
|
23
|
-
content: 'Hello there',
|
|
24
|
-
messageType: 'text',
|
|
25
|
-
role: 'user',
|
|
26
|
-
isEdited: false,
|
|
27
|
-
isDeleted: false,
|
|
28
|
-
replyTo: null,
|
|
29
|
-
reactions: [],
|
|
30
|
-
attachments: [],
|
|
31
|
-
toolCallData: null,
|
|
32
|
-
createdAt: new Date('2026-01-01T00:00:00Z'),
|
|
33
|
-
...overrides,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
describe('MessageItem', () => {
|
|
37
|
-
it('renders a keyboard-reachable "more actions" button when actions are available', () => {
|
|
38
|
-
render(MessageItem, {
|
|
39
|
-
props: {
|
|
40
|
-
message: makeMessage(),
|
|
41
|
-
isOwn: false,
|
|
42
|
-
onreply: vi.fn(),
|
|
43
|
-
onreact: vi.fn(),
|
|
44
|
-
},
|
|
45
|
-
});
|
|
46
|
-
const moreBtn = screen.getByRole('button', { name: 'Message actions' });
|
|
47
|
-
expect(moreBtn).toBeInTheDocument();
|
|
48
|
-
// A real <button> is inherently in the tab order (no tabindex="-1").
|
|
49
|
-
expect(moreBtn.getAttribute('tabindex')).not.toBe('-1');
|
|
50
|
-
});
|
|
51
|
-
it('reveals the action bar via the more-actions button without any mouse hover', async () => {
|
|
52
|
-
const onreply = vi.fn();
|
|
53
|
-
render(MessageItem, {
|
|
54
|
-
props: {
|
|
55
|
-
message: makeMessage(),
|
|
56
|
-
isOwn: false,
|
|
57
|
-
onreply,
|
|
58
|
-
onreact: vi.fn(),
|
|
59
|
-
},
|
|
60
|
-
});
|
|
61
|
-
// Actions are hidden until requested — no hover, no focus yet.
|
|
62
|
-
expect(screen.queryByRole('button', { name: 'Reply' })).toBeNull();
|
|
63
|
-
await userEvent.click(screen.getByRole('button', { name: 'Message actions' }));
|
|
64
|
-
// The action bar is now reachable; clicking Reply invokes the callback.
|
|
65
|
-
const replyBtn = await screen.findByRole('button', { name: 'Reply' });
|
|
66
|
-
await userEvent.click(replyBtn);
|
|
67
|
-
expect(onreply).toHaveBeenCalledWith('m1');
|
|
68
|
-
});
|
|
69
|
-
it("exposes edit/delete actions for the user's own message", async () => {
|
|
70
|
-
const onedit = vi.fn();
|
|
71
|
-
const ondelete = vi.fn();
|
|
72
|
-
render(MessageItem, {
|
|
73
|
-
props: {
|
|
74
|
-
message: makeMessage(),
|
|
75
|
-
isOwn: true,
|
|
76
|
-
onedit,
|
|
77
|
-
ondelete,
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
await userEvent.click(screen.getByRole('button', { name: 'Message actions' }));
|
|
81
|
-
await userEvent.click(await screen.findByRole('button', { name: 'Edit' }));
|
|
82
|
-
expect(onedit).toHaveBeenCalledWith('m1');
|
|
83
|
-
// The bar stays open while focus remains inside the row, so Delete is
|
|
84
|
-
// reachable in the same interaction.
|
|
85
|
-
await userEvent.click(screen.getByRole('button', { name: 'Delete' }));
|
|
86
|
-
expect(ondelete).toHaveBeenCalledWith('m1');
|
|
87
|
-
});
|
|
88
|
-
it('does not render the more-actions affordance when no action callbacks are provided', () => {
|
|
89
|
-
render(MessageItem, {
|
|
90
|
-
props: {
|
|
91
|
-
message: makeMessage(),
|
|
92
|
-
isOwn: false,
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
expect(screen.queryByRole('button', { name: 'Message actions' })).toBeNull();
|
|
96
|
-
});
|
|
97
|
-
it('is axe-clean with the action affordance present', async () => {
|
|
98
|
-
const { container } = render(MessageItem, {
|
|
99
|
-
props: {
|
|
100
|
-
message: makeMessage(),
|
|
101
|
-
isOwn: false,
|
|
102
|
-
onreply: vi.fn(),
|
|
103
|
-
onreact: vi.fn(),
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
await expectNoA11yViolations(container);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
/**
|
|
3
|
-
* First component test in smrt-chat via the shared S11 harness (#1416). Avatar is
|
|
4
|
-
* the thin adapter over the library Avatar (maps avatarUrl→src, dnd→busy).
|
|
5
|
-
*/
|
|
6
|
-
import { expectNoA11yViolations, render, screen, } from '@happyvertical/smrt-vitest/svelte';
|
|
7
|
-
import { describe, expect, it } from 'vitest';
|
|
8
|
-
import Avatar from '../Avatar.svelte';
|
|
9
|
-
describe('Avatar', () => {
|
|
10
|
-
it('renders the initials derived from the name', () => {
|
|
11
|
-
render(Avatar, { props: { name: 'Ada Lovelace' } });
|
|
12
|
-
expect(screen.getByText('AL')).toBeInTheDocument();
|
|
13
|
-
});
|
|
14
|
-
it('is axe-clean', async () => {
|
|
15
|
-
const { container } = render(Avatar, {
|
|
16
|
-
props: { name: 'Ada Lovelace', onlineStatus: 'online' },
|
|
17
|
-
});
|
|
18
|
-
await expectNoA11yViolations(container);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
/**
|
|
3
|
-
* Component coverage for LinkPreview via the shared S11 harness (#1416).
|
|
4
|
-
*/
|
|
5
|
-
import { expectNoA11yViolations, render, screen, } from '@happyvertical/smrt-vitest/svelte';
|
|
6
|
-
import { describe, expect, it } from 'vitest';
|
|
7
|
-
import LinkPreview from '../LinkPreview.svelte';
|
|
8
|
-
describe('LinkPreview', () => {
|
|
9
|
-
it('renders a link with title, description, and hostname', () => {
|
|
10
|
-
render(LinkPreview, {
|
|
11
|
-
props: {
|
|
12
|
-
url: 'https://example.com/article',
|
|
13
|
-
title: 'An Article',
|
|
14
|
-
description: 'A short summary',
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
const link = screen.getByRole('link');
|
|
18
|
-
expect(link).toHaveAttribute('href', 'https://example.com/article');
|
|
19
|
-
expect(screen.getByText('An Article')).toBeInTheDocument();
|
|
20
|
-
expect(screen.getByText('A short summary')).toBeInTheDocument();
|
|
21
|
-
expect(screen.getByText('example.com')).toBeInTheDocument();
|
|
22
|
-
});
|
|
23
|
-
it('is axe-clean', async () => {
|
|
24
|
-
const { container } = render(LinkPreview, {
|
|
25
|
-
props: { url: 'https://example.com', title: 'Example' },
|
|
26
|
-
});
|
|
27
|
-
await expectNoA11yViolations(container);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
/**
|
|
3
|
-
* Component coverage for MentionAutocomplete via the shared S11 harness (#1416).
|
|
4
|
-
*
|
|
5
|
-
* Regression for T2 #1391: `case 'Escape': break;` was a no-op, so the
|
|
6
|
-
* suggestion popup could not be dismissed from the keyboard. Escape must now
|
|
7
|
-
* invoke the `oncancel` callback so a parent can close the popup.
|
|
8
|
-
*/
|
|
9
|
-
import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
|
|
10
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
11
|
-
import MentionAutocomplete from '../MentionAutocomplete.svelte';
|
|
12
|
-
const SUGGESTIONS = [
|
|
13
|
-
{ id: 'p1', name: 'Ada Lovelace' },
|
|
14
|
-
{ id: 'p2', name: 'Alan Turing' },
|
|
15
|
-
];
|
|
16
|
-
describe('MentionAutocomplete', () => {
|
|
17
|
-
it('invokes oncancel when Escape is pressed while visible', async () => {
|
|
18
|
-
const oncancel = vi.fn();
|
|
19
|
-
render(MentionAutocomplete, {
|
|
20
|
-
props: {
|
|
21
|
-
query: 'a',
|
|
22
|
-
suggestions: SUGGESTIONS,
|
|
23
|
-
onselect: vi.fn(),
|
|
24
|
-
isVisible: true,
|
|
25
|
-
oncancel,
|
|
26
|
-
},
|
|
27
|
-
});
|
|
28
|
-
await userEvent.keyboard('{Escape}');
|
|
29
|
-
expect(oncancel).toHaveBeenCalledTimes(1);
|
|
30
|
-
});
|
|
31
|
-
it('does not throw on Escape when no oncancel is provided', async () => {
|
|
32
|
-
render(MentionAutocomplete, {
|
|
33
|
-
props: {
|
|
34
|
-
query: 'a',
|
|
35
|
-
suggestions: SUGGESTIONS,
|
|
36
|
-
onselect: vi.fn(),
|
|
37
|
-
isVisible: true,
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
await expect(userEvent.keyboard('{Escape}')).resolves.not.toThrow();
|
|
41
|
-
});
|
|
42
|
-
it('selects the active suggestion on Enter', async () => {
|
|
43
|
-
const onselect = vi.fn();
|
|
44
|
-
render(MentionAutocomplete, {
|
|
45
|
-
props: {
|
|
46
|
-
query: 'a',
|
|
47
|
-
suggestions: SUGGESTIONS,
|
|
48
|
-
onselect,
|
|
49
|
-
isVisible: true,
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
await userEvent.keyboard('{Enter}');
|
|
53
|
-
expect(onselect).toHaveBeenCalledWith('p1');
|
|
54
|
-
});
|
|
55
|
-
it('is axe-clean', async () => {
|
|
56
|
-
const { container } = render(MentionAutocomplete, {
|
|
57
|
-
props: {
|
|
58
|
-
query: 'a',
|
|
59
|
-
suggestions: SUGGESTIONS,
|
|
60
|
-
onselect: vi.fn(),
|
|
61
|
-
isVisible: true,
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
await expectNoA11yViolations(container);
|
|
65
|
-
});
|
|
66
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
/**
|
|
3
|
-
* Component-level coverage for ModelPicker (#2904 review, cycle-3 second
|
|
4
|
-
* final F2 — the underlying `<select>` had no accessible name, no test in
|
|
5
|
-
* this suite ever supplied `models` to any component, and this file had no
|
|
6
|
-
* test at all).
|
|
7
|
-
*/
|
|
8
|
-
import { expectNoA11yViolations, render, screen, userEvent, } from '@happyvertical/smrt-vitest/svelte';
|
|
9
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
10
|
-
import ModelPicker, {} from '../ModelPicker.svelte';
|
|
11
|
-
const models = [
|
|
12
|
-
{ id: 'model-a', label: 'Model A' },
|
|
13
|
-
{ id: 'model-b', label: 'Model B' },
|
|
14
|
-
];
|
|
15
|
-
describe('ModelPicker', () => {
|
|
16
|
-
it('has an accessible name by default and lets the user pick a model', async () => {
|
|
17
|
-
const onchange = vi.fn();
|
|
18
|
-
render(ModelPicker, { props: { models, value: 'model-a', onchange } });
|
|
19
|
-
const select = screen.getByLabelText('Model');
|
|
20
|
-
expect(select).toBeInTheDocument();
|
|
21
|
-
await userEvent.selectOptions(select, 'model-b');
|
|
22
|
-
expect(onchange).toHaveBeenCalledWith('model-b');
|
|
23
|
-
});
|
|
24
|
-
it('accepts an override accessible name via ariaLabel', () => {
|
|
25
|
-
render(ModelPicker, {
|
|
26
|
-
props: { models, value: 'model-a', ariaLabel: 'Reply model' },
|
|
27
|
-
});
|
|
28
|
-
expect(screen.getByLabelText('Reply model')).toBeInTheDocument();
|
|
29
|
-
});
|
|
30
|
-
it('is axe-clean', async () => {
|
|
31
|
-
const { container } = render(ModelPicker, {
|
|
32
|
-
props: { models, value: 'model-a' },
|
|
33
|
-
});
|
|
34
|
-
await expectNoA11yViolations(container);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
/**
|
|
3
|
-
* Component coverage for ReactionPicker via the shared S11 harness (#1416).
|
|
4
|
-
*/
|
|
5
|
-
import { expectNoA11yViolations, render, screen, userEvent, within, } from '@happyvertical/smrt-vitest/svelte';
|
|
6
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
7
|
-
import ReactionPicker from '../ReactionPicker.svelte';
|
|
8
|
-
// S12 a11y remediation (#1417): the picker is now a labelled `role="group"` of
|
|
9
|
-
// buttons (was an invalid `role="grid"` with button children, which tripped axe's
|
|
10
|
-
// `aria-required-children`), so axe is asserted on the open state.
|
|
11
|
-
describe('ReactionPicker', () => {
|
|
12
|
-
it('renders a labelled group of emoji buttons when open', () => {
|
|
13
|
-
render(ReactionPicker, { props: { onreact: vi.fn(), isOpen: true } });
|
|
14
|
-
const group = screen.getByRole('group', { name: 'Emoji reactions' });
|
|
15
|
-
expect(within(group).getAllByRole('button').length).toBeGreaterThan(0);
|
|
16
|
-
});
|
|
17
|
-
it('reports the chosen emoji through onreact', async () => {
|
|
18
|
-
const onreact = vi.fn();
|
|
19
|
-
render(ReactionPicker, { props: { onreact, isOpen: true } });
|
|
20
|
-
const [first] = within(screen.getByRole('group', { name: 'Emoji reactions' })).getAllByRole('button');
|
|
21
|
-
await userEvent.click(first);
|
|
22
|
-
expect(onreact).toHaveBeenCalledTimes(1);
|
|
23
|
-
expect(onreact.mock.calls[0][0]).toBeTruthy();
|
|
24
|
-
});
|
|
25
|
-
it('renders nothing when closed', () => {
|
|
26
|
-
render(ReactionPicker, { props: { onreact: vi.fn(), isOpen: false } });
|
|
27
|
-
expect(screen.queryByRole('group')).toBeNull();
|
|
28
|
-
});
|
|
29
|
-
it('is axe-clean', async () => {
|
|
30
|
-
const { container } = render(ReactionPicker, {
|
|
31
|
-
props: { onreact: vi.fn(), isOpen: true },
|
|
32
|
-
});
|
|
33
|
-
await expectNoA11yViolations(container);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
/**
|
|
3
|
-
* Component coverage for ReadReceipts via the shared S11 harness (#1416).
|
|
4
|
-
*/
|
|
5
|
-
import { expectNoA11yViolations, render, screen, } from '@happyvertical/smrt-vitest/svelte';
|
|
6
|
-
import { describe, expect, it } from 'vitest';
|
|
7
|
-
import ReadReceipts from '../ReadReceipts.svelte';
|
|
8
|
-
describe('ReadReceipts', () => {
|
|
9
|
-
it('summarises how many participants have read', () => {
|
|
10
|
-
render(ReadReceipts, {
|
|
11
|
-
props: {
|
|
12
|
-
readBy: [{ name: 'Ada' }, { name: 'Bob' }],
|
|
13
|
-
totalParticipants: 3,
|
|
14
|
-
},
|
|
15
|
-
});
|
|
16
|
-
expect(screen.getByLabelText('2 of 3 read')).toBeInTheDocument();
|
|
17
|
-
});
|
|
18
|
-
it('renders the sent (unread) state with no readers', () => {
|
|
19
|
-
render(ReadReceipts, { props: { readBy: [], totalParticipants: 3 } });
|
|
20
|
-
expect(screen.getByLabelText('0 of 3 read')).toBeInTheDocument();
|
|
21
|
-
});
|
|
22
|
-
it('is axe-clean', async () => {
|
|
23
|
-
const { container } = render(ReadReceipts, {
|
|
24
|
-
props: { readBy: [{ name: 'Ada' }], totalParticipants: 2 },
|
|
25
|
-
});
|
|
26
|
-
await expectNoA11yViolations(container);
|
|
27
|
-
});
|
|
28
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
/**
|
|
3
|
-
* Component coverage for UserPresence via the shared S11 harness (#1416).
|
|
4
|
-
*/
|
|
5
|
-
import { expectNoA11yViolations, render, screen, } from '@happyvertical/smrt-vitest/svelte';
|
|
6
|
-
import { describe, expect, it } from 'vitest';
|
|
7
|
-
import UserPresence from '../UserPresence.svelte';
|
|
8
|
-
describe('UserPresence', () => {
|
|
9
|
-
it('exposes the status as an accessible label', () => {
|
|
10
|
-
render(UserPresence, { props: { status: 'online' } });
|
|
11
|
-
expect(screen.getByLabelText('Online')).toBeInTheDocument();
|
|
12
|
-
});
|
|
13
|
-
it('shows a visible label when requested', () => {
|
|
14
|
-
render(UserPresence, { props: { status: 'dnd', showLabel: true } });
|
|
15
|
-
expect(screen.getByText('Do not disturb')).toBeInTheDocument();
|
|
16
|
-
});
|
|
17
|
-
it('is axe-clean', async () => {
|
|
18
|
-
const { container } = render(UserPresence, {
|
|
19
|
-
props: { status: 'away', showLabel: true },
|
|
20
|
-
});
|
|
21
|
-
await expectNoA11yViolations(container);
|
|
22
|
-
});
|
|
23
|
-
});
|