diff options
Diffstat (limited to 'ui')
| -rw-r--r-- | ui/lib/components/Message.svelte | 6 | ||||
| -rw-r--r-- | ui/lib/components/MessageRun.svelte | 4 | ||||
| -rw-r--r-- | ui/lib/store/messages.svelte.js | 6 | ||||
| -rw-r--r-- | ui/tests/lib/components/ActiveChannel.svelte.test.js | 22 | ||||
| -rw-r--r-- | ui/tests/lib/components/ChangePassword.svelte.test.js | 52 | ||||
| -rw-r--r-- | ui/tests/lib/components/Channel.svelte.test.js | 22 | ||||
| -rw-r--r-- | ui/tests/lib/components/ChannelList.svelte.test.js | 22 | ||||
| -rw-r--r-- | ui/tests/lib/components/CreateChannelForm.svelte.test.js | 52 | ||||
| -rw-r--r-- | ui/tests/lib/components/LogIn.svelte.test.js | 22 | ||||
| -rw-r--r-- | ui/tests/lib/components/MessageInput.svelte.test.js | 48 | ||||
| -rw-r--r-- | ui/tests/lib/components/MessageRun.svelte.test.js | 22 |
11 files changed, 270 insertions, 8 deletions
diff --git a/ui/lib/components/Message.svelte b/ui/lib/components/Message.svelte index 0c8eeec..fddeecd 100644 --- a/ui/lib/components/Message.svelte +++ b/ui/lib/components/Message.svelte @@ -1,15 +1,11 @@ <script> - import { marked } from 'marked'; - import DOMPurify from 'dompurify'; - import { deleteMessage } from '$lib/apiServer'; function scroll(message) { message.scrollIntoView(); } - let { id, at, body, editable = false } = $props(); - let renderedBody = $derived(DOMPurify.sanitize(marked.parse(body, { breaks: true }))); + let { id, at, body, renderedBody, editable = false } = $props(); let deleteArmed = $state(false); function onDelete(event) { diff --git a/ui/lib/components/MessageRun.svelte b/ui/lib/components/MessageRun.svelte index 83a82a9..39ee155 100644 --- a/ui/lib/components/MessageRun.svelte +++ b/ui/lib/components/MessageRun.svelte @@ -16,7 +16,7 @@ <span class="chip variant-soft sticky top-o left-0"> @{name}: </span> - {#each messages as { id, at, body }} - <Message {id} {at} {body} editable={ownMessage} /> + {#each messages as { id, at, body, renderedBody }} + <Message {id} {at} {body} {renderedBody} editable={ownMessage} /> {/each} </div> diff --git a/ui/lib/store/messages.svelte.js b/ui/lib/store/messages.svelte.js index c0db71b..0ceba54 100644 --- a/ui/lib/store/messages.svelte.js +++ b/ui/lib/store/messages.svelte.js @@ -1,3 +1,6 @@ +import { marked } from 'marked'; +import DOMPurify from 'dompurify'; + const RUN_COALESCE_MAX_INTERVAL = 10 /* min */ * 60 /* sec */ * 1000; /* ms */ export class Messages { @@ -9,7 +12,8 @@ export class Messages { addMessage(channel, id, { at, sender, body }) { let parsedAt = new Date(at); - const message = { id, at: parsedAt, body }; + let renderedBody = DOMPurify.sanitize(marked.parse(body, { breaks: true })); + const message = { id, at: parsedAt, body, renderedBody }; // You might be thinking, can't this be // diff --git a/ui/tests/lib/components/ActiveChannel.svelte.test.js b/ui/tests/lib/components/ActiveChannel.svelte.test.js new file mode 100644 index 0000000..183c823 --- /dev/null +++ b/ui/tests/lib/components/ActiveChannel.svelte.test.js @@ -0,0 +1,22 @@ +import { flushSync, mount, unmount } from 'svelte'; +import { afterEach, beforeEach, describe, expect, test } from 'vitest'; +import ActiveChannel from '$lib/components/ActiveChannel.svelte'; + +let component; + +describe('ActiveChannel', () => { + beforeEach(() => { + component = mount(ActiveChannel, { + target: document.body // `document` exists because of jsdom + }); + flushSync(); + }); + + afterEach(() => { + unmount(component); + }); + + test('mounts', async () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ui/tests/lib/components/ChangePassword.svelte.test.js b/ui/tests/lib/components/ChangePassword.svelte.test.js new file mode 100644 index 0000000..9eb40f5 --- /dev/null +++ b/ui/tests/lib/components/ChangePassword.svelte.test.js @@ -0,0 +1,52 @@ +import { flushSync, mount, unmount } from 'svelte'; +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; +import ChangePassword from '$lib/components/ChangePassword.svelte'; +import axios from 'axios'; + +let component; + +let mocks = vi.hoisted(() => ({ + post: vi.fn() +})); + +describe('ChangePassword', () => { + beforeEach(() => { + vi.mock('axios', async (importActual) => { + const actual = await importActual(); + + const mockAxios = { + default: { + ...actual.default, + create: vi.fn(() => ({ + ...actual.default.create(), + post: mocks.post + })) + } + }; + + return mockAxios; + }); + mocks.post.mockResolvedValue({ status: 200 }); + + component = mount(ChangePassword, { + target: document.body // `document` exists because of jsdom + }); + flushSync(); + }); + + afterEach(() => { + unmount(component); + }); + + test('onsubmit happy path', async () => { + // Set value in all three inputs at once: + const inputs = document.body.querySelectorAll('input[type=password]'); + inputs.value = 'pass'; + // Click the button, then flush the changes so you can synchronously write expectations + document.body.querySelector('button[type=submit]').click(); + flushSync(); + + // expect(axios.post).toHaveBeenCalledWith('/password', { password: 'pass', to: 'pass' }); + expect(Array.from(inputs.values()).map((i) => i.value)).toEqual(['', '', '']); + }); +}); diff --git a/ui/tests/lib/components/Channel.svelte.test.js b/ui/tests/lib/components/Channel.svelte.test.js new file mode 100644 index 0000000..a6fdab9 --- /dev/null +++ b/ui/tests/lib/components/Channel.svelte.test.js @@ -0,0 +1,22 @@ +import { flushSync, mount, unmount } from 'svelte'; +import { afterEach, beforeEach, describe, expect, test } from 'vitest'; +import Channel from '$lib/components/Channel.svelte'; + +let component; + +describe('Channel', () => { + beforeEach(() => { + component = mount(Channel, { + target: document.body // `document` exists because of jsdom + }); + flushSync(); + }); + + afterEach(() => { + unmount(component); + }); + + test('mounts', async () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ui/tests/lib/components/ChannelList.svelte.test.js b/ui/tests/lib/components/ChannelList.svelte.test.js new file mode 100644 index 0000000..7b5ae4a --- /dev/null +++ b/ui/tests/lib/components/ChannelList.svelte.test.js @@ -0,0 +1,22 @@ +import { flushSync, mount, unmount } from 'svelte'; +import { afterEach, beforeEach, describe, expect, test } from 'vitest'; +import ChannelList from '$lib/components/ChannelList.svelte'; + +let component; + +describe('ChannelList', () => { + beforeEach(() => { + component = mount(ChannelList, { + target: document.body // `document` exists because of jsdom + }); + flushSync(); + }); + + afterEach(() => { + unmount(component); + }); + + test('mounts', async () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ui/tests/lib/components/CreateChannelForm.svelte.test.js b/ui/tests/lib/components/CreateChannelForm.svelte.test.js new file mode 100644 index 0000000..a4a4695 --- /dev/null +++ b/ui/tests/lib/components/CreateChannelForm.svelte.test.js @@ -0,0 +1,52 @@ +import { flushSync, mount, unmount } from 'svelte'; +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; +import CreateChannelForm from '$lib/components/CreateChannelForm.svelte'; +import axios from 'axios'; + +let component; + +let mocks = vi.hoisted(() => ({ + post: vi.fn() +})); + +describe('CreateChannelForm', () => { + beforeEach(() => { + vi.mock('axios', async (importActual) => { + const actual = await importActual(); + + const mockAxios = { + default: { + ...actual.default, + create: vi.fn(() => ({ + ...actual.default.create(), + post: mocks.post + })) + } + }; + + return mockAxios; + }); + mocks.post.mockResolvedValue({ status: 200 }); + + component = mount(CreateChannelForm, { + target: document.body // `document` exists because of jsdom + }); + flushSync(); + }); + + afterEach(() => { + unmount(component); + }); + + test('onsubmit happy path', async () => { + // Set value on the one input this should match: + const inputs = document.body.querySelectorAll('input[type=text]'); + inputs.value = 'channel name'; + // Click the button, then flush the changes so you can synchronously write expectations + document.body.querySelector('button[type=submit]').click(); + flushSync(); + + expect(mocks.post).toHaveBeenCalled(); + expect(Array.from(inputs.values()).map((i) => i.value)).toEqual(['']); + }); +}); diff --git a/ui/tests/lib/components/LogIn.svelte.test.js b/ui/tests/lib/components/LogIn.svelte.test.js new file mode 100644 index 0000000..b64d846 --- /dev/null +++ b/ui/tests/lib/components/LogIn.svelte.test.js @@ -0,0 +1,22 @@ +import { flushSync, mount, unmount } from 'svelte'; +import { afterEach, beforeEach, describe, expect, test } from 'vitest'; +import LogIn from '$lib/components/LogIn.svelte'; + +let component; + +describe('LogIn', () => { + beforeEach(() => { + component = mount(LogIn, { + target: document.body // `document` exists because of jsdom + }); + flushSync(); + }); + + afterEach(() => { + unmount(component); + }); + + test('mounts', async () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ui/tests/lib/components/MessageInput.svelte.test.js b/ui/tests/lib/components/MessageInput.svelte.test.js new file mode 100644 index 0000000..3dde5d7 --- /dev/null +++ b/ui/tests/lib/components/MessageInput.svelte.test.js @@ -0,0 +1,48 @@ +import { flushSync, mount, unmount } from 'svelte'; +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; +import MessageInput from '$lib/components/MessageInput.svelte'; +import axios from 'axios'; + +let component; + +let mocks = vi.hoisted(() => ({ + post: vi.fn() +})); + +describe('MessageInput', () => { + beforeEach(() => { + vi.mock('axios', async (importActual) => { + const actual = await importActual(); + + const mockAxios = { + default: { + ...actual.default, + create: vi.fn(() => ({ + ...actual.default.create(), + post: mocks.post + })) + } + }; + + return mockAxios; + }); + mocks.post.mockResolvedValue({ status: 200 }); + + component = mount(MessageInput, { + target: document.body // `document` exists because of jsdom + }); + flushSync(); + }); + + afterEach(() => { + unmount(component); + }); + + test('onsubmit happy path', async () => { + // Click the button, then flush the changes so you can synchronously write expectations + document.body.querySelector('button[type=submit]').click(); + flushSync(); + + expect(mocks.post).toHaveBeenCalled(); + }); +}); diff --git a/ui/tests/lib/components/MessageRun.svelte.test.js b/ui/tests/lib/components/MessageRun.svelte.test.js new file mode 100644 index 0000000..7671c52 --- /dev/null +++ b/ui/tests/lib/components/MessageRun.svelte.test.js @@ -0,0 +1,22 @@ +import { flushSync, mount, unmount } from 'svelte'; +import { afterEach, beforeEach, describe, expect, test } from 'vitest'; +import MessageRun from '$lib/components/MessageRun.svelte'; + +let component; + +describe('MessageRun', () => { + beforeEach(() => { + component = mount(MessageRun, { + target: document.body // `document` exists because of jsdom + }); + flushSync(); + }); + + afterEach(() => { + unmount(component); + }); + + test('mounts', async () => { + expect(component).toBeTruthy(); + }); +}); |
