"^\\$lib(.*)$": "<rootDir>/src/lib$1",
"^\\$app(.*)$": [".svelte/dev/runtime/app/*", ".svelte/build/runtime/app/*"]
},
- "moduleFileExtensions": ["js", "svelte"]
+ "moduleFileExtensions": ["js", "svelte"],
+ "moduleNameMapper": {
+ "^\\$(.*)$": "<rootDir>/src$1"
+ }
}
--- /dev/null
+import '@testing-library/jest-dom/extend-expect';
+
+import { render } from '@testing-library/svelte';
+import '$/config/i18n';
+
+import ErrorBlock from './error_block.svelte';
+
+describe('Glyph component', () => {
+
+ test('Should act as an image', () => {
+
+ const results = render(ErrorBlock, { props: {
+ message: 'An error has, sadly, destroyed everything.'
+ } });
+
+ expect(results.getByText('An error has, sadly, destroyed everything.'))
+ .toBeVisible();
+ });
+});
+
<script>
- import { _ } from 'svelte-i18n';
- import { forums } from '$/stores/forums';
- import Loader from '$/components/loader/loader.svelte';
- import ErrorBlock from '$/components/error_block/error_block.svelte';
+import { _ } from 'svelte-i18n';
+export let forums;
+
+$: sortedForums = forums.sort((a, b) => a.position - b.position);
</script>
-<nav title={$_('forum_list.title')}>
- {#if $forums.loading}
- <Loader />
- {/if}
- {#if $forums.error}
- <ErrorBlock message={$_('forum_list.error.unavailable')} />
- {/if}
- <ul>
- {#each $forums.data as forum}
- <li>
- <a href="/f/{forum.id}">
- <span aria-hidden="true" class="navigation-glyph {forum.glyph}">{forum.glyph}</span>
- <span class="navigation-label">{$_(forum.label)}</span>
- </a>
- </li>
- {/each}
- </ul>
-</nav>
+<ul>
+ {#each sortedForums as forum}
+ <li>
+ <a href="/f/{forum.id}">
+ <span aria-hidden="true" class="navigation-glyph {forum.glyph}">{forum.glyph}</span>
+ <span class="navigation-label">{$_(forum.label)}</span>
+ </a>
+ </li>
+ {/each}
+</ul>
<style>
- nav {
- grid-column: col-start 1;
- grid-row: 2;
- border-right: 1px solid black;
- }
-
ul {
padding: 0;
}
--- /dev/null
+import '@testing-library/jest-dom/extend-expect';
+
+import { render } from '@testing-library/svelte';
+import '$/config/i18n';
+
+import { addMessages } from 'svelte-i18n';
+
+import ForumList from './forum_list.svelte';
+
+const internals = {
+ results: null
+};
+
+describe('Glyph component', () => {
+
+ beforeAll(() => {
+
+ addMessages('en', {
+ 'test_forums.yes': 'Absolutely yes',
+ 'test_forums.no': 'No, not at all',
+ 'test_forums.maybe': 'OK, maybe...'
+ });
+ });
+
+ beforeEach(() => {
+
+ internals.results = render(ForumList, { props: {
+ forums: [
+ {
+ id: 'yes',
+ glyph: '☆',
+ label: 'test_forums.yes',
+ position: 2
+ },
+ {
+ id: 'no',
+ glyph: '◯',
+ label: 'test_forums.no',
+ position: 0
+ },
+ {
+ id: 'maybe',
+ glyph: '⏀',
+ label: 'test_forums.maybe',
+ position: 1
+ }
+ ]
+ } });
+ });
+
+ test('It should display each forum according to their position', () => {
+
+ expect(internals.results.container)
+ .toHaveTextContent(/^◯.+⏀.+☆.+$/);
+ });
+
+ test('It should translate forum labels', () => {
+
+ expect(internals.results.getByText('Absolutely yes')).toBeVisible();
+ expect(internals.results.getByText('No, not at all')).toBeVisible();
+ expect(internals.results.getByText('OK, maybe...')).toBeVisible();
+ });
+
+ test('It should display forum glyphs', () => {
+
+ expect(internals.results.getByText('☆')).toBeVisible();
+ expect(internals.results.getByText('◯')).toBeVisible();
+ expect(internals.results.getByText('⏀')).toBeVisible();
+ });
+
+ test('Label should be a permalink to the forum', () => {
+
+ expect(internals.results.getByText('Absolutely yes').closest('a'))
+ .toHaveAttribute('href', '/f/yes');
+ expect(internals.results.getByText('No, not at all').closest('a'))
+ .toHaveAttribute('href', '/f/no');
+ expect(internals.results.getByText('OK, maybe...').closest('a'))
+ .toHaveAttribute('href', '/f/maybe');
+ });
+
+ test('Glyph should be a permalink to the forum', () => {
+
+ expect(internals.results.getByText('☆').closest('a'))
+ .toHaveAttribute('href', '/f/yes');
+ expect(internals.results.getByText('◯').closest('a'))
+ .toHaveAttribute('href', '/f/no');
+ expect(internals.results.getByText('⏀').closest('a'))
+ .toHaveAttribute('href', '/f/maybe');
+ });
+});
export let uuid;
</script>
-<div class="glyphicon" aria-hidden="true" title={$_('glyph.title')}>
+<div class="glyphicon" role="img" aria-label={$_('glyph.title')} title={$_('glyph.title')}>
{#each getGlyphHash(uuid) as fragment}
<span class={fragment.glyph} style="color: {fragment.color} ">
{fragment.glyph}
--- /dev/null
+import '@testing-library/jest-dom/extend-expect';
+
+import { render } from '@testing-library/svelte';
+import '$/config/i18n';
+
+import Glyph from './glyph.svelte';
+
+const internals = {
+ results: null
+};
+
+describe('Glyph component', () => {
+
+ beforeEach(() => {
+
+ internals.results = render(Glyph, { props: {
+ uuid: '9fb14ebc-bc64-400b-915f-d429ec44b8fe'
+ } });
+ });
+
+ test('Should act as an image', () => {
+
+ expect(internals.results.getByRole('img'))
+ .toBeVisible();
+ });
+
+ test('Should render 4 glyphs', () => {
+
+ expect(internals.results.getByRole('img'))
+ .toHaveTextContent(/^. . . .$/);
+ });
+});
{timestampToISO(post.created_at)}
</a>
</time>
- {#if post.topic}
- <span class="h-card">
- ({$_('post.topic_location')} <a href="/t/{post.topic.id}">{post.topic.title}</a>.)
- </span>
- {/if}
+ <span>
+ ({$_('post.topic_location')} <a href="/t/{post.topic.id}">{post.topic.title}</a>.)
+ </span>
</aside>
<article
class="e-content"
--- /dev/null
+import '@testing-library/jest-dom/extend-expect';
+
+import { render } from '@testing-library/svelte';
+import '$/config/i18n';
+
+import Post from './post.svelte';
+
+const internals = {
+ basicPost: {
+ id: 'e5a19d53-4c9a-4be8-afa5-00942ea3afa4',
+ text: 'This is an example post qwerty',
+ created_at: Date.UTC(2021, 3, 19, 6, 6, 6, 666).valueOf(),
+ author: {
+ handle: 'very_cool_user',
+ id: 'b01bdb48-4b5e-46a4-97f3-6db789bcd33b'
+ },
+ topic: {
+ id: '35d3c3eb-e486-42ef-994c-d8ab1f1e167a',
+ title: 'Parent topic, yes'
+ }
+ },
+
+ results: null
+};
+
+describe('Post component', () => {
+
+ beforeEach(() => {
+
+ internals.results = render(Post, { props: {
+ post: internals.basicPost
+ } });
+ });
+
+ test('Should display the text of the post', () => {
+
+ expect(internals.results.getByText('This is an example post qwerty')).toBeVisible();
+ });
+
+ test('Should display date of the post', () => {
+
+ expect(internals.results.getByText('2021-04-19T06:06:06.666Z'))
+ .toBeVisible();
+ });
+
+ test('Date of post should be a permalink to the post', () => {
+
+ expect(internals.results.getByText('2021-04-19T06:06:06.666Z').closest('a'))
+ .toHaveAttribute('href', '/p/e5a19d53-4c9a-4be8-afa5-00942ea3afa4');
+ });
+
+ test('Should display the glyph of the post author', () => {
+
+ const glyphicon = internals.results.getByRole('img');
+
+ expect(glyphicon)
+ .toBeVisible();
+ expect(glyphicon)
+ .toHaveTextContent(/^. . . .$/);
+ });
+
+ test('Should display author handle', () => {
+
+ expect(internals.results.getByText('very_cool_user'))
+ .toBeVisible();
+ });
+
+ test('Author handle should have a permalink to topic', () => {
+
+ expect(internals.results.getByText('very_cool_user').closest('a'))
+ .toHaveAttribute('href', '/a/very_cool_user');
+ });
+
+ test('Should display parent topic title', () => {
+
+ expect(internals.results.getByText('Parent topic, yes'))
+ .toBeVisible();
+ });
+
+ test('Parent topic title should have a permalink to topic', () => {
+
+ expect(internals.results.getByText('Parent topic, yes').closest('a'))
+ .toHaveAttribute('href', '/t/35d3c3eb-e486-42ef-994c-d8ab1f1e167a');
+ });
+});
+++ /dev/null
-<script>
- export let post;
- export let index = 0;
- export let count = 1;
-
- import { _ } from 'svelte-i18n';
- import Glyph from '$/components/glyph/glyph.svelte';
-
- const timestampToISO = (timestamp) => new Date(timestamp).toISOString();
-</script>
-
-<aside
- class="post-meta"
- title={$_('post.metadata_title', { values: { count: index + 1, total: count } })}
->
- <Glyph uuid={post.author.id} />
- <span class="h-card">
- {$_('post.author_credit')}
- <a href="/a/{post.author.handle}" class="p-nickname u-url">{post.author.handle}</a>.
- </span>
- <time role="presentation" class="dt-published" datetime={timestampToISO(post.created_at)}>
- <a title={$_('post.permalink_title')} href="/p/{post.id}">
- {timestampToISO(post.created_at)}
- </a>
- </time>
- {#if post.topic}
- <span class="h-card">
- ({$_('post.topic_location')} <a href="/t/{post.topic.id}">{post.topic.title}</a>.)
- </span>
- {/if}
-</aside>
-<article
- class="e-content"
- title={$_('post.title', {
- values: { count: index + 1, total: count, author: post.author.handle }
- })}
->
- {post.text}
-</article>
-<hr />
-
-<style>
- .post-meta * {
- vertical-align: top;
- }
-
- article {
- white-space: pre;
- }
-</style>
<script>
import '$/config/i18n';
+
import { isLoading } from 'svelte-i18n';
+ import { _ } from 'svelte-i18n';
+
+ import { forums } from '$/stores/forums';
+
+ import ErrorBlock from '$/components/error_block/error_block.svelte';
import ForumList from '$/components/forum_list/forum_list.svelte';
import Header from '$/components/header/header.svelte';
import Loader from '$/components/loader/loader.svelte';
<main>
<slot />
</main>
- <ForumList />
+ <nav title={$_('forum_list.title')}>
+ {#if $forums.loading}
+ <Loader />
+ {/if}
+ {#if $forums.error}
+ <ErrorBlock message={$_('forum_list.error.unavailable')} />
+ {/if}
+ {#if $forums.data}
+ <ForumList forums={$forums.data} />
+ {/if}
+ </nav>
<Footer />
{/if}
+
+<style>
+ nav {
+ grid-column: col-start 1;
+ grid-row: 2;
+ border-right: 1px solid black;
+ }
+</style>