diff options
| author | Ruben Beltran del Rio <ruben@unlimited.pizza> | 2022-05-01 01:02:58 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <ruben@unlimited.pizza> | 2022-05-01 01:02:58 +0200 |
| commit | cac85db02ff00732cf75d473dc3411332f33d845 (patch) | |
| tree | 5f244968a905eb3888434b3f60cbf05d2b652207 /src | |
| parent | a7cf03c192470cbab13edeb1aec99e0c66dede10 (diff) | |
Apply formatting
Diffstat (limited to 'src')
47 files changed, 2029 insertions, 2244 deletions
diff --git a/src/lib/animations/blink.test.ts b/src/lib/animations/blink.test.ts index 32dc872..b4d9f33 100644 --- a/src/lib/animations/blink.test.ts +++ b/src/lib/animations/blink.test.ts @@ -6,102 +6,88 @@ import { blink } from './blink'; import { sineOut } from 'svelte/easing'; const internals = { - response: null + response: null }; describe('blink', () => { + test('it has a default delay of 0ms', () => { + const response = blink(document.createElement('div'), {}); - test('it has a default delay of 0ms', () => { + expect(response.delay).toBe(0); + }); - const response = blink(document.createElement('div'), {}); + test('it allows delay to be overridden', () => { + const response = blink(document.createElement('div'), { + delay: 300 + }); - expect(response.delay).toBe(0); - }); + expect(response.delay).toBe(300); + }); - test('it allows delay to be overridden', () => { + test('it has a default duration of 400ms', () => { + const response = blink(document.createElement('div'), {}); - const response = blink(document.createElement('div'), { - delay: 300 - }); + expect(response.duration).toBe(400); + }); - expect(response.delay).toBe(300); - }); + test('it allows delay to be overridden', () => { + const response = blink(document.createElement('div'), { + duration: 999 + }); - test('it has a default duration of 400ms', () => { + expect(response.duration).toBe(999); + }); - const response = blink(document.createElement('div'), {}); + test('it uses sineOut as the default easing function', () => { + const response = blink(document.createElement('div'), {}); - expect(response.duration).toBe(400); - }); + expect(response.easing).toBe(sineOut); + }); - test('it allows delay to be overridden', () => { + test('it allows easing function to be overridden', () => { + const response = blink(document.createElement('div'), { + easing: () => 666 + }); - const response = blink(document.createElement('div'), { - duration: 999 - }); + expect(response.easing(0)).toBe(666); + }); - expect(response.duration).toBe(999); - }); + describe('css animation function', () => { + beforeEach(() => { + const div = document.createElement('div'); + div.style.width = '100px'; + div.style.height = '200px'; + internals.response = blink(div, {}); + }); - test('it uses sineOut as the default easing function', () => { + test('It starts with with zeroed width and height', () => { + const css = internals.response.css(0, 1); + expect(css).toContain('width: 0px'); + expect(css).toContain('height: 0px'); + }); - const response = blink(document.createElement('div'), {}); + test('It grows to full height and 0 width in first 20%', () => { + const css = internals.response.css(0.2, 0.8); + expect(css).toContain('width: 0px'); + expect(css).toContain('height: 200px'); + }); - expect(response.easing).toBe(sineOut); - }); + test('It expands to full height by the end', () => { + const css = internals.response.css(1, 0); + expect(css).toContain('width: 100px'); + expect(css).toContain('height: 200px'); + }); - test('it allows easing function to be overridden', () => { + test('It keeps element vertically centered by adjusting the margin', () => { + const css = internals.response.css(0.1, 0.9); + expect(css).toContain('margin: 50px 50px'); + expect(css).toContain('height: 100px'); + }); - const response = blink(document.createElement('div'), { - easing: () => 666 - }); - - expect(response.easing(0)).toBe(666); - }); - - describe('css animation function', () => { - - beforeEach(() => { - - const div = document.createElement('div'); - div.style.width = '100px'; - div.style.height = '200px'; - internals.response = blink(div, {}); - }); - - test('It starts with with zeroed width and height', () => { - - const css = internals.response.css(0, 1); - expect(css).toContain('width: 0px'); - expect(css).toContain('height: 0px'); - }); - - test('It grows to full height and 0 width in first 20%', () => { - - const css = internals.response.css(0.2, 0.8); - expect(css).toContain('width: 0px'); - expect(css).toContain('height: 200px'); - }); - - test('It expands to full height by the end', () => { - - const css = internals.response.css(1, 0); - expect(css).toContain('width: 100px'); - expect(css).toContain('height: 200px'); - }); - - test('It keeps element vertically centered by adjusting the margin', () => { - - const css = internals.response.css(0.1, 0.9); - expect(css).toContain('margin: 50px 50px'); - expect(css).toContain('height: 100px'); - }); - - test('It keeps element horizontally centered by adjusting the margin', () => { - - const css = internals.response.css(0.6, 0.4); - expect(css).toContain('margin: 0px 25px'); - expect(css).toContain('width: 50px'); - }); - }); + test('It keeps element horizontally centered by adjusting the margin', () => { + const css = internals.response.css(0.6, 0.4); + expect(css).toContain('margin: 0px 25px'); + expect(css).toContain('width: 50px'); + }); + }); }); diff --git a/src/lib/animations/blink.ts b/src/lib/animations/blink.ts index c291b06..623489a 100644 --- a/src/lib/animations/blink.ts +++ b/src/lib/animations/blink.ts @@ -1,25 +1,23 @@ import { sineOut } from 'svelte/easing'; import type { AnimationConfig } from 'svelte/animate'; -export const blink = function blink(node: HTMLElement, params: AnimationConfig): AnimationConfig{ +export const blink = function blink(node: HTMLElement, params: AnimationConfig): AnimationConfig { + const originalWidth = parseFloat(getComputedStyle(node).width); + const originalHeight = parseFloat(getComputedStyle(node).height); - const originalWidth = parseFloat(getComputedStyle(node).width); - const originalHeight = parseFloat(getComputedStyle(node).height); + return { + delay: params.delay || 0, + duration: params.duration || 400, + easing: params.easing || sineOut, + css: (t: number): string => { + const halfWidth = originalWidth / 2; + const halfHeight = originalHeight / 2; + const height = Math.round(t <= 0.2 ? (originalHeight * t) / 0.2 : originalHeight); + const marginY = Math.round(t <= 0.2 ? halfHeight * (1 - t / 0.2) : 0); + const width = Math.round(t > 0.2 ? ((t - 0.2) / 0.8) * originalWidth : 0); + const marginX = Math.round(t > 0.2 ? (1 - (t - 0.2) / 0.8) * halfWidth : halfWidth); - return { - delay: params.delay || 0, - duration: params.duration || 400, - easing: params.easing || sineOut, - css: (t: number): string => { - - const halfWidth = originalWidth / 2; - const halfHeight = originalHeight / 2; - const height = Math.round(t <= 0.2 ? (originalHeight * t) / 0.2 : originalHeight); - const marginY = Math.round(t <= 0.2 ? halfHeight * (1 - t / 0.2) : 0); - const width = Math.round(t > 0.2 ? ((t - 0.2) / 0.8) * originalWidth : 0); - const marginX = Math.round(t > 0.2 ? (1 - (t - 0.2) / 0.8) * halfWidth : halfWidth); - - return `width: ${width}px; height: ${height}px; margin: ${marginY}px ${marginX}px`; - } - }; + return `width: ${width}px; height: ${height}px; margin: ${marginY}px ${marginX}px`; + } + }; }; diff --git a/src/lib/components/actions/topic.svelte b/src/lib/components/actions/topic.svelte index 1311d06..a3cba3b 100644 --- a/src/lib/components/actions/topic.svelte +++ b/src/lib/components/actions/topic.svelte @@ -1,14 +1,14 @@ <script lang="ts"> - import type { TopicAction } from '$lib/stores/action'; + import type { TopicAction } from '$lib/stores/action'; export let actions: TopicAction; import { _ } from 'svelte-i18n'; </script> <li> - <a href="/reply/{actions.id}" title={$_('header.action.reply.title')}> - {@html $_('header.action.reply.display')} - </a> + <a href="/reply/{actions.id}" title={$_('header.action.reply.title')}> + {@html $_('header.action.reply.display')} + </a> </li> <style> diff --git a/src/lib/components/actions/topic.test.ts b/src/lib/components/actions/topic.test.ts index 0037a7f..9a17d38 100644 --- a/src/lib/components/actions/topic.test.ts +++ b/src/lib/components/actions/topic.test.ts @@ -9,21 +9,19 @@ import '$lib/i18n'; import Topic from './topic.svelte'; -const internals = { - results: null -}; - describe('Topic Actions component', () => { + test('Should link to reply page', () => { + const results = render(Topic, { + props: { + actions: { + id: '8ebaa211-fd9b-423a-8b4f-b57622007fde' + } + } + }); - test('Should link to reply page', () => { - - const results = render(Topic, { props: { - actions: { - id: '8ebaa211-fd9b-423a-8b4f-b57622007fde' - } - } }); - - expect(results.getByTitle('Reply').closest('a')) - .toHaveAttribute('href', '/reply/8ebaa211-fd9b-423a-8b4f-b57622007fde'); - }); + expect(results.getByTitle('Reply').closest('a')).toHaveAttribute( + 'href', + '/reply/8ebaa211-fd9b-423a-8b4f-b57622007fde' + ); + }); }); diff --git a/src/lib/components/error_block/error_block.test.ts b/src/lib/components/error_block/error_block.test.ts index d2ac458..401874f 100644 --- a/src/lib/components/error_block/error_block.test.ts +++ b/src/lib/components/error_block/error_block.test.ts @@ -10,23 +10,19 @@ import '$lib/i18n'; import ErrorBlock from './error_block.svelte'; describe('Error Block component', () => { + test('Should display error message sent', () => { + const results = render(ErrorBlock, { + props: { + message: 'An error has, sadly, destroyed everything.' + } + }); - test('Should display error message sent', () => { + expect(results.getByText('An error has, sadly, destroyed everything.')).toBeVisible(); + }); - const results = render(ErrorBlock, { props: { - message: 'An error has, sadly, destroyed everything.' - } }); + test('Should display default error message', () => { + const results = render(ErrorBlock); - expect(results.getByText('An error has, sadly, destroyed everything.')) - .toBeVisible(); - }); - - test('Should display default error message', () => { - - const results = render(ErrorBlock); - - expect(results.getByText('Unknown error has occurred. Panic!')) - .toBeVisible(); - }); + expect(results.getByText('Unknown error has occurred. Panic!')).toBeVisible(); + }); }); - diff --git a/src/lib/components/forum/forum.test.ts b/src/lib/components/forum/forum.test.ts index b3ce167..61a70e1 100644 --- a/src/lib/components/forum/forum.test.ts +++ b/src/lib/components/forum/forum.test.ts @@ -12,62 +12,59 @@ import '$lib/i18n'; import Forum from './forum.svelte'; const internals = { - results: null + results: null }; describe('Forum component', () => { + beforeAll(() => { + addMessages('en', { + 'test_forums.oleo': 'Oleo' + }); + }); - beforeAll(() => { + beforeEach(() => { + internals.results = render(Forum, { + props: { + forum: { + id: 'oleo', + glyph: '☽', + label: 'test_forums.oleo', + topics: [ + { + id: '0575d375-5bea-44df-a597-bee3adda624d', + title: 'Very forumy topic', + ttl: 160 * 1000, + updated_at: Date.now() + }, + { + id: 'aeeb56e4-751d-4400-8aa7-d0f3a20d4e25', + title: 'Only mildly forum-like', + ttl: 160 * 1000, + updated_at: Date.now() + } + ] + } + } + }); + }); - addMessages('en', { - 'test_forums.oleo': 'Oleo' - }); - }); + test('It should display the forum glyph and label', () => { + expect(internals.results.getByText(/^\s*☽\s*Oleo\s*$/)).toBeVisible(); + }); - beforeEach(() => { + test('It should display the topics', () => { + expect(internals.results.getByText('Very forumy topic')).toBeVisible(); + expect(internals.results.getByText('Only mildly forum-like')).toBeVisible(); + }); - internals.results = render(Forum, { props: { - forum: { - id: 'oleo', - glyph: '☽', - label: 'test_forums.oleo', - topics: [ - { - id: '0575d375-5bea-44df-a597-bee3adda624d', - title: 'Very forumy topic', - ttl: 160 * 1000, - updated_at: Date.now() - }, - { - id: 'aeeb56e4-751d-4400-8aa7-d0f3a20d4e25', - title: 'Only mildly forum-like', - ttl: 160 * 1000, - updated_at: Date.now() - } - ] - } - } }); - }); - - test('It should display the forum glyph and label', () => { - - expect(internals.results.getByText(/^\s*☽\s*Oleo\s*$/)) - .toBeVisible(); - }); - - test('It should display the topics', () => { - - expect(internals.results.getByText('Very forumy topic')) - .toBeVisible(); - expect(internals.results.getByText('Only mildly forum-like')) - .toBeVisible(); - }); - - test('It should link to the topics', () => { - - expect(internals.results.getByText('Very forumy topic').closest('a')) - .toHaveAttribute('href', '/t/0575d375-5bea-44df-a597-bee3adda624d'); - expect(internals.results.getByText('Only mildly forum-like').closest('a')) - .toHaveAttribute('href', '/t/aeeb56e4-751d-4400-8aa7-d0f3a20d4e25'); - }); + test('It should link to the topics', () => { + expect(internals.results.getByText('Very forumy topic').closest('a')).toHaveAttribute( + 'href', + '/t/0575d375-5bea-44df-a597-bee3adda624d' + ); + expect(internals.results.getByText('Only mildly forum-like').closest('a')).toHaveAttribute( + 'href', + '/t/aeeb56e4-751d-4400-8aa7-d0f3a20d4e25' + ); + }); }); diff --git a/src/lib/components/forum_list/forum_list.svelte b/src/lib/components/forum_list/forum_list.svelte index 3529de8..81a5e4f 100644 --- a/src/lib/components/forum_list/forum_list.svelte +++ b/src/lib/components/forum_list/forum_list.svelte @@ -1,19 +1,19 @@ <script lang="ts"> - import { _ } from 'svelte-i18n'; - export let forums; + import { _ } from 'svelte-i18n'; + export let forums; - $: sortedForums = forums.slice().sort((a, b) => a.position - b.position); + $: sortedForums = forums.slice().sort((a, b) => a.position - b.position); </script> <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} + {#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> diff --git a/src/lib/components/forum_list/forum_list.test.ts b/src/lib/components/forum_list/forum_list.test.ts index be7d449..1670b5c 100644 --- a/src/lib/components/forum_list/forum_list.test.ts +++ b/src/lib/components/forum_list/forum_list.test.ts @@ -12,83 +12,79 @@ import { addMessages } from 'svelte-i18n'; import ForumList from './forum_list.svelte'; const internals = { - results: null + results: null }; describe('Forum List component', () => { + beforeAll(() => { + addMessages('en', { + 'test_forums.yes': 'Absolutely yes', + 'test_forums.no': 'No, not at all', + 'test_forums.maybe': 'OK, maybe...' + }); + }); - beforeAll(() => { + 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 + } + ] + } + }); + }); - addMessages('en', { - 'test_forums.yes': 'Absolutely yes', - 'test_forums.no': 'No, not at all', - 'test_forums.maybe': 'OK, maybe...' - }); - }); + test('It should display each forum according to their position', () => { + expect(internals.results.container).toHaveTextContent(/^◯.+⏀.+☆.+$/); + }); - beforeEach(() => { + 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(); + }); - 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 forum glyphs', () => { + expect(internals.results.getByText('☆')).toBeVisible(); + expect(internals.results.getByText('◯')).toBeVisible(); + expect(internals.results.getByText('⏀')).toBeVisible(); + }); - test('It should display each forum according to their position', () => { + 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' + ); + }); - 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'); - }); + 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'); + }); }); diff --git a/src/lib/components/glyph/glyph.test.ts b/src/lib/components/glyph/glyph.test.ts index 778353f..a4a7470 100644 --- a/src/lib/components/glyph/glyph.test.ts +++ b/src/lib/components/glyph/glyph.test.ts @@ -10,27 +10,23 @@ import '$lib/i18n'; import Glyph from './glyph.svelte'; const internals = { - results: null + results: null }; describe('Glyph component', () => { + beforeEach(() => { + internals.results = render(Glyph, { + props: { + uuid: '9fb14ebc-bc64-400b-915f-d429ec44b8fe' + } + }); + }); - beforeEach(() => { + test('Should act as an image', () => { + expect(internals.results.getByRole('img')).toBeVisible(); + }); - 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(/^. . . .$/); - }); + test('Should render 4 glyphs', () => { + expect(internals.results.getByRole('img')).toHaveTextContent(/^. . . .$/); + }); }); diff --git a/src/lib/components/header/header.svelte b/src/lib/components/header/header.svelte index e6c9348..51df1c2 100644 --- a/src/lib/components/header/header.svelte +++ b/src/lib/components/header/header.svelte @@ -20,7 +20,7 @@ > </li> {#if $topicActions} - <TopicActions actions={$topicActions} /> + <TopicActions actions={$topicActions} /> {/if} <li> <a href="/search" title={$_('header.action.search.title')} diff --git a/src/lib/components/header/header.test.ts b/src/lib/components/header/header.test.ts index 4107b2d..d7a7801 100644 --- a/src/lib/components/header/header.test.ts +++ b/src/lib/components/header/header.test.ts @@ -13,21 +13,16 @@ jest.mock('$lib/config/config.ts'); import Header from './header.svelte'; describe('Header component', () => { + test('Should not display topic if action is not set', () => { + const results = render(Header); - test('Should not display topic if action is not set', () => { + expect(results.queryByTitle('Reply')).toBe(null); + }); - const results = render(Header); + test('Should display topic if action is set', () => { + enableTopicActions('d138d6d8-e669-42e7-995d-20a7fcc176f5'); + const results = render(Header); - expect(results.queryByTitle('Reply')) - .toBe(null); - }); - - test('Should display topic if action is set', () => { - - enableTopicActions('d138d6d8-e669-42e7-995d-20a7fcc176f5'); - const results = render(Header); - - expect(results.getByTitle('Reply')) - .toBeVisible(); - }); + expect(results.getByTitle('Reply')).toBeVisible(); + }); }); diff --git a/src/lib/components/language_selector/language_selector.svelte b/src/lib/components/language_selector/language_selector.svelte index 64a4ef9..4e245f8 100644 --- a/src/lib/components/language_selector/language_selector.svelte +++ b/src/lib/components/language_selector/language_selector.svelte @@ -3,16 +3,16 @@ import { getLangNameFromCode } from 'language-name-map'; $: namedLocales = $locales - .map((code) => ({ - code, - ...getLangNameFromCode(code) - })) - .sort((a, b) => a.native - b.native); + .map((code) => ({ + code, + ...getLangNameFromCode(code) + })) + .sort((a, b) => a.native - b.native); - let selected = $locale + let selected = $locale; $: { - locale.set(selected); + locale.set(selected); } </script> diff --git a/src/lib/components/language_selector/language_selector.test.ts b/src/lib/components/language_selector/language_selector.test.ts index 74156bc..673e1e6 100644 --- a/src/lib/components/language_selector/language_selector.test.ts +++ b/src/lib/components/language_selector/language_selector.test.ts @@ -12,37 +12,28 @@ import '$lib/i18n'; import LanguageSelector from './language_selector.svelte'; const internals = { - results: null + results: null }; describe('Language Selector component', () => { + beforeEach(() => { + internals.results = render(LanguageSelector); + }); - beforeEach(() => { + test('Should display languages in their own language', () => { + expect(internals.results.getByText('English')).toBeVisible(); + expect(internals.results.getByText('Español')).toBeVisible(); + }); - internals.results = render(LanguageSelector); - }); - - test('Should display languages in their own language', () => { - - expect(internals.results.getByText('English')) - .toBeVisible(); - expect(internals.results.getByText('Español')) - .toBeVisible(); - }); - - test('Should change locale when a language is selected', async () => { - - locale.subscribe((localeValue) => { - - expect(localeValue).toBe('en'); - })(); - const spanish = internals.results.getByText('Español'); - await userEvent.selectOptions(spanish.closest('select'), spanish); - await act(); - locale.subscribe((localeValue) => { - - expect(localeValue).toBe('es'); - })(); - }); + test('Should change locale when a language is selected', async () => { + locale.subscribe((localeValue) => { + expect(localeValue).toBe('en'); + })(); + const spanish = internals.results.getByText('Español'); + await userEvent.selectOptions(spanish.closest('select'), spanish); + await act(); + locale.subscribe((localeValue) => { + expect(localeValue).toBe('es'); + })(); + }); }); - diff --git a/src/lib/components/post/post.svelte b/src/lib/components/post/post.svelte index 4e6c28f..d909be1 100644 --- a/src/lib/components/post/post.svelte +++ b/src/lib/components/post/post.svelte @@ -23,11 +23,11 @@ {timestampToISO(post.created_at)} </a> </time> - {#if post.topic} - <span> - ({$_('post.topic_location')} <a href="/t/{post.topic.id}">{post.topic.title}</a>.) - </span> - {/if} + {#if post.topic} + <span> + ({$_('post.topic_location')} <a href="/t/{post.topic.id}">{post.topic.title}</a>.) + </span> + {/if} </aside> <article class="e-content" diff --git a/src/lib/components/post/post.test.ts b/src/lib/components/post/post.test.ts index d43583e..4351f3f 100644 --- a/src/lib/components/post/post.test.ts +++ b/src/lib/components/post/post.test.ts @@ -10,119 +10,110 @@ import '$lib/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' - } - }, - postWithoutTopic: { - id: '9e52e38e-9007-4a20-bbf1-cea4e2f950f3', - text: 'This is a post without a topic', - created_at: Date.UTC(2022, 8, 21, 4, 3, 1, 340).valueOf(), - author: { - handle: 'my_normal_user', - id: '121f8f97-de02-4102-b25d-f34fd619009b' - } - }, + 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' + } + }, + postWithoutTopic: { + id: '9e52e38e-9007-4a20-bbf1-cea4e2f950f3', + text: 'This is a post without a topic', + created_at: Date.UTC(2022, 8, 21, 4, 3, 1, 340).valueOf(), + author: { + handle: 'my_normal_user', + id: '121f8f97-de02-4102-b25d-f34fd619009b' + } + }, - results: null + results: null }; describe('Post component', () => { + beforeEach(() => { + internals.results = render(Post, { + props: { + post: internals.basicPost + } + }); + }); - beforeEach(() => { + test('Should display the text of the post', () => { + expect(internals.results.getByText('This is an example post qwerty')).toBeVisible(); + }); - internals.results = render(Post, { props: { - post: internals.basicPost - } }); - }); + test('Should display date of the post', () => { + expect(internals.results.getByText('2021-04-19T06:06:06.666Z')).toBeVisible(); + }); - test('Should display the text of the post', () => { + 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' + ); + }); - expect(internals.results.getByText('This is an example post qwerty')).toBeVisible(); - }); + test('Should display the glyph of the post author', () => { + const glyphicon = internals.results.getByRole('img'); - test('Should display date of the post', () => { + expect(glyphicon).toBeVisible(); + expect(glyphicon).toHaveTextContent(/^. . . .$/); + }); - expect(internals.results.getByText('2021-04-19T06:06:06.666Z')) - .toBeVisible(); - }); + test('Should display author handle', () => { + expect(internals.results.getByText('very_cool_user')).toBeVisible(); + }); - test('Date of post should be a permalink to the post', () => { + test('Author handle should have a permalink to topic', () => { + expect(internals.results.getByText('very_cool_user').closest('a')).toHaveAttribute( + 'href', + '/a/very_cool_user' + ); + }); - expect(internals.results.getByText('2021-04-19T06:06:06.666Z').closest('a')) - .toHaveAttribute('href', '/p/e5a19d53-4c9a-4be8-afa5-00942ea3afa4'); - }); + test('Should display parent topic title', () => { + expect(internals.results.getByText('Parent topic, yes')).toBeVisible(); + }); - test('Should display the glyph of the post author', () => { + 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' + ); + }); - const glyphicon = internals.results.getByRole('img'); + test('Parent topic title should have a permalink to topic', () => { + cleanup(); + internals.results = render(Post, { + props: { + post: internals.postWithoutTopic + } + }); - expect(glyphicon) - .toBeVisible(); - expect(glyphicon) - .toHaveTextContent(/^. . . .$/); - }); + expect(internals.results.queryByText('Parent topic, yes')).toBe(null); + }); - test('Should display author handle', () => { + test('It should default to 1/1 when no index or count is passed', () => { + expect(internals.results.getByTitle('Post 1 of 1 by very_cool_user')).toBeVisible(); + }); - expect(internals.results.getByText('very_cool_user')) - .toBeVisible(); - }); + test('Parent topic title should have a permalink to topic', () => { + cleanup(); + internals.results = render(Post, { + props: { + index: 2, + count: 5, + post: internals.postWithoutTopic + } + }); - 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'); - }); - - test('Parent topic title should have a permalink to topic', () => { - - cleanup(); - internals.results = render(Post, { props: { - post: internals.postWithoutTopic - } }); - - expect(internals.results.queryByText('Parent topic, yes')) - .toBe(null); - }); - - test('It should default to 1/1 when no index or count is passed', () => { - - expect(internals.results.getByTitle('Post 1 of 1 by very_cool_user')) - .toBeVisible(); - }); - - test('Parent topic title should have a permalink to topic', () => { - - cleanup(); - internals.results = render(Post, { props: { - index: 2, - count: 5, - post: internals.postWithoutTopic - } }); - - expect(internals.results.getByTitle('Post 3 of 5 by my_normal_user')) - .toBeVisible(); - }); + expect(internals.results.getByTitle('Post 3 of 5 by my_normal_user')).toBeVisible(); + }); }); diff --git a/src/lib/components/tag/tag.test.ts b/src/lib/components/tag/tag.test.ts index 76304e6..9e4564c 100644 --- a/src/lib/components/tag/tag.test.ts +++ b/src/lib/components/tag/tag.test.ts @@ -10,53 +10,51 @@ import '$lib/i18n'; import Tag from './tag.svelte'; const internals = { - results: null + results: null }; describe('Tag component', () => { + beforeEach(() => { + internals.results = render(Tag, { + props: { + tag: { + id: 'avocado', + topics: [ + { + id: 'eb751e7a-5777-46c3-b81b-cc66546d5157', + title: 'A single topic', + ttl: 160 * 1000, + updated_at: Date.now() + }, + { + id: 'b4a5613c-237b-4147-a867-9c105d51e365', + title: 'And its companion', + ttl: 160 * 1000, + updated_at: Date.now() + } + ] + } + } + }); + }); - beforeEach(() => { + test('It should display the tag title', () => { + expect(internals.results.getByText('Tag: avocado')).toBeVisible(); + }); - internals.results = render(Tag, { props: { - tag: { - id: 'avocado', - topics: [ - { - id: 'eb751e7a-5777-46c3-b81b-cc66546d5157', - title: 'A single topic', - ttl: 160 * 1000, - updated_at: Date.now() - }, - { - id: 'b4a5613c-237b-4147-a867-9c105d51e365', - title: 'And its companion', - ttl: 160 * 1000, - updated_at: Date.now() - } - ] - } - } }); - }); + test('It should display the topics', () => { + expect(internals.results.getByText('A single topic')).toBeVisible(); + expect(internals.results.getByText('And its companion')).toBeVisible(); + }); - test('It should display the tag title', () => { - - expect(internals.results.getByText('Tag: avocado')) - .toBeVisible(); - }); - - test('It should display the topics', () => { - - expect(internals.results.getByText('A single topic')) - .toBeVisible(); - expect(internals.results.getByText('And its companion')) - .toBeVisible(); - }); - - test('It should link to the topics', () => { - - expect(internals.results.getByText('A single topic').closest('a')) - .toHaveAttribute('href', '/t/eb751e7a-5777-46c3-b81b-cc66546d5157'); - expect(internals.results.getByText('And its companion').closest('a')) - .toHaveAttribute('href', '/t/b4a5613c-237b-4147-a867-9c105d51e365'); - }); + test('It should link to the topics', () => { + expect(internals.results.getByText('A single topic').closest('a')).toHaveAttribute( + 'href', + '/t/eb751e7a-5777-46c3-b81b-cc66546d5157' + ); + expect(internals.results.getByText('And its companion').closest('a')).toHaveAttribute( + 'href', + '/t/b4a5613c-237b-4147-a867-9c105d51e365' + ); + }); }); diff --git a/src/lib/components/topic/topic.svelte b/src/lib/components/topic/topic.svelte index 06f2aeb..5b5ed0b 100644 --- a/src/lib/components/topic/topic.svelte +++ b/src/lib/components/topic/topic.svelte @@ -14,27 +14,28 @@ <aside class="topic-meta" title={$_('topic.metadata_title')}> {#if topic.forum} <span class="topic-location"> - {$_('topic.category_location')} + {$_('topic.category_location')} <a href="/f/{topic.forum.id}" class="p-category"> - {topic.forum.glyph} {$_(topic.forum.label)} - </a>. - </span> + {topic.forum.glyph} + {$_(topic.forum.label)} + </a>. + </span> {/if} <span class="topic-ttl"> - <a class="u-url u-uid" title={$_('topic.permalink_title')} href="/t/{topic.id}"> - ({$_('topic.remaining_time', { + <a class="u-url u-uid" title={$_('topic.permalink_title')} href="/t/{topic.id}"> + ({$_('topic.remaining_time', { values: { remaining: $_(remaining.label, { values: { count: remaining.count } }) } - })}) - </a>. - </span> + })}) + </a>. + </span> </aside> {#if topic.tags.length > 0} <aside class="topic-tags" title={$_('topic.tags_title')}> {$_('topic.tags_location')} {#each topic.tags as tag} <a href="/g/{tag.id}" class="p-category"> - {tag.id}<span class="tag-weight">({tag.weight})</span> - </a>{' '} + {tag.id}<span class="tag-weight">({tag.weight})</span> + </a>{' '} {/each} </aside> {/if} diff --git a/src/lib/components/topic/topic.test.ts b/src/lib/components/topic/topic.test.ts index 985ca72..e4c3bad 100644 --- a/src/lib/components/topic/topic.test.ts +++ b/src/lib/components/topic/topic.test.ts @@ -12,157 +12,139 @@ import '$lib/i18n'; import Topic from './topic.svelte'; const internals = { - results: null, - basicTopic: { - id: 'b1a4f8d1-4d16-4872-b391-fda6a0e9012d', - title: 'I sure am a test topic', - ttl: 160 * 1000, - updated_at: Date.now(), - forum: { - id: 'diversion', - glyph: '⏃', - label: 'test_forums.diversion' - }, - tags: [ - { - id: 'fish', - weight: 40 - }, - { - id: 'statue', - weight: 5 - } - ], - posts: [ - { - id: '413a74db-9473-4bac-8698-da9452c05854', - text: 'This is the first post', - created_at: Date.UTC(1999, 7, 1, 8, 8, 2, 111).valueOf(), - author: { - handle: 'past_user', - id: 'c76d3e51-76ac-4e84-a1b2-2eee9abd68b3' - } - }, - { - id: '821ff177-5250-406f-9431-1a8097b35430', - text: 'This response came later', - created_at: Date.UTC(2038, 1, 2, 3, 4, 6, 789).valueOf(), - author: { - handle: 'future_user', - id: 'cb9307cb-77e9-4c55-bbe7-dbbf88737358' - } - } - ] - }, - topicWithoutForum: { - id: '9715e9ee-0d63-4b50-b613-826ef2791728', - title: 'This topic, no forums', - ttl: 160 * 1000, - updated_at: Date.now(), - tags: [ - { - id: 'cauliflower', - weight: 33 - } - ], - posts: [] - } + results: null, + basicTopic: { + id: 'b1a4f8d1-4d16-4872-b391-fda6a0e9012d', + title: 'I sure am a test topic', + ttl: 160 * 1000, + updated_at: Date.now(), + forum: { + id: 'diversion', + glyph: '⏃', + label: 'test_forums.diversion' + }, + tags: [ + { + id: 'fish', + weight: 40 + }, + { + id: 'statue', + weight: 5 + } + ], + posts: [ + { + id: '413a74db-9473-4bac-8698-da9452c05854', + text: 'This is the first post', + created_at: Date.UTC(1999, 7, 1, 8, 8, 2, 111).valueOf(), + author: { + handle: 'past_user', + id: 'c76d3e51-76ac-4e84-a1b2-2eee9abd68b3' + } + }, + { + id: '821ff177-5250-406f-9431-1a8097b35430', + text: 'This response came later', + created_at: Date.UTC(2038, 1, 2, 3, 4, 6, 789).valueOf(), + author: { + handle: 'future_user', + id: 'cb9307cb-77e9-4c55-bbe7-dbbf88737358' + } + } + ] + }, + topicWithoutForum: { + id: '9715e9ee-0d63-4b50-b613-826ef2791728', + title: 'This topic, no forums', + ttl: 160 * 1000, + updated_at: Date.now(), + tags: [ + { + id: 'cauliflower', + weight: 33 + } + ], + posts: [] + } }; describe('Topic component', () => { + beforeAll(() => { + addMessages('en', { + 'test_forums.diversion': 'Diversion' + }); + }); - beforeAll(() => { + beforeEach(() => { + internals.results = render(Topic, { + props: { + topic: internals.basicTopic + } + }); + }); - addMessages('en', { - 'test_forums.diversion': 'Diversion' - }); - }); + test('Should show the topic title', () => { + expect(internals.results.getByText('I sure am a test topic')).toBeVisible(); + }); + test('Should display remaining time in readable format', () => { + expect(internals.results.getByText(/2 minutes remaining/)).toBeVisible(); + }); + test('Remaining time should be a permalink to the topic', () => { + expect(internals.results.getByText(/2 minutes remaining/).closest('a')).toHaveAttribute( + 'href', + '/t/b1a4f8d1-4d16-4872-b391-fda6a0e9012d' + ); + }); - beforeEach(() => { + test('Should show text for all posts', () => { + expect(internals.results.getByText('This is the first post')).toBeVisible(); + expect(internals.results.getByText('This response came later')).toBeVisible(); + }); - internals.results = render(Topic, { props: { - topic: internals.basicTopic - } }); - }); + test('Should send index and count to posts', () => { + expect(internals.results.getByTitle('Post 1 of 2 by past_user')).toBeVisible(); + expect(internals.results.getByTitle('Post 2 of 2 by future_user')).toBeVisible(); + }); - test('Should show the topic title', () => { + describe('Forum link', () => { + test('Should show forum if the post has one', () => { + expect(internals.results.getByText(/^\s*⏃\s*Diversion\s*$/)).toBeVisible(); + }); - expect(internals.results.getByText('I sure am a test topic')) - .toBeVisible(); - }); - test('Should display remaining time in readable format', () => { + test('Forum text should be a permalink to the forum', () => { + expect(internals.results.getByText(/^\s*⏃\s*Diversion\s*$/).closest('a')).toHaveAttribute( + 'href', + '/f/diversion' + ); + }); - expect(internals.results.getByText(/2 minutes remaining/)) - .toBeVisible(); - }); - test('Remaining time should be a permalink to the topic', () => { + test("Should not show forum if the post doesn't have one", () => { + cleanup(); + internals.results = render(Topic, { + props: { + topic: internals.topicWithoutForum + } + }); - expect(internals.results.getByText(/2 minutes remaining/).closest('a')) - .toHaveAttribute('href', '/t/b1a4f8d1-4d16-4872-b391-fda6a0e9012d'); - }); + expect(internals.results.queryByText(/^\s*⏃\s*Diversion\s*$/)).toBe(null); + }); + }); - test('Should show text for all posts', () => { + describe('Tag listing', () => { + test('Should show topic tags', () => { + expect(internals.results.getByText('fish')).toBeVisible(); + expect(internals.results.getByText('fish')).toHaveTextContent('fish(40)'); + expect(internals.results.getByText('statue')).toBeVisible(); + expect(internals.results.getByText('statue')).toHaveTextContent('statue(5)'); + }); - expect(internals.results.getByText('This is the first post')) - .toBeVisible(); - expect(internals.results.getByText('This response came later')) - .toBeVisible(); - }); - - test('Should send index and count to posts', () => { - - expect(internals.results.getByTitle('Post 1 of 2 by past_user')) - .toBeVisible(); - expect(internals.results.getByTitle('Post 2 of 2 by future_user')) - .toBeVisible(); - }); - - describe('Forum link', () => { - - test('Should show forum if the post has one', () => { - - expect(internals.results.getByText(/^\s*⏃\s*Diversion\s*$/)) - .toBeVisible(); - }); - - test('Forum text should be a permalink to the forum', () => { - - expect(internals.results.getByText(/^\s*⏃\s*Diversion\s*$/).closest('a')) - .toHaveAttribute('href', '/f/diversion'); - }); - - test('Should not show forum if the post doesn\'t have one', () => { - - cleanup(); - internals.results = render(Topic, { props: { - topic: internals.topicWithoutForum - } }); - - expect(internals.results.queryByText(/^\s*⏃\s*Diversion\s*$/)) - .toBe(null); - }); - }); - - describe('Tag listing', () => { - - test('Should show topic tags', () => { - - expect(internals.results.getByText('fish')) - .toBeVisible(); - expect(internals.results.getByText('fish')) - .toHaveTextContent('fish(40)'); - expect(internals.results.getByText('statue')) - .toBeVisible(); - expect(internals.results.getByText('statue')) - .toHaveTextContent('statue(5)'); - }); - - test('Tag text should be a permalink to the tag', () => { - - expect(internals.results.getByText('fish').closest('a')) - .toHaveAttribute('href', '/g/fish'); - expect(internals.results.getByText('statue').closest('a')) - .toHaveAttribute('href', '/g/statue'); - }); - }); + test('Tag text should be a permalink to the tag', () => { + expect(internals.results.getByText('fish').closest('a')).toHaveAttribute('href', '/g/fish'); + expect(internals.results.getByText('statue').closest('a')).toHaveAttribute( + 'href', + '/g/statue' + ); + }); + }); }); diff --git a/src/lib/components/topic_summary/topic_summary.svelte b/src/lib/components/topic_summary/topic_summary.svelte index bfcf8ab..39dad08 100644 --- a/src/lib/components/topic_summary/topic_summary.svelte +++ b/src/lib/components/topic_summary/topic_summary.svelte @@ -9,14 +9,16 @@ </script> <li class="h-entry" title={$_('topic.title')}> - <span class="p-name"> - <a class="u-url u-uid" title={$_('topic.permalink_title')} href="/t/{topic.id}"> - {topic.title} - </a></span> - <span class="topic-ttl">({$_('topic.remaining_time', { + <span class="p-name"> + <a class="u-url u-uid" title={$_('topic.permalink_title')} href="/t/{topic.id}"> + {topic.title} + </a></span + > + <span class="topic-ttl" + >({$_('topic.remaining_time', { values: { remaining: $_(remaining.label, { values: { count: remaining.count } }) } - })}) - </span> + })}) + </span> </li> <style> diff --git a/src/lib/components/topic_summary/topic_summary.test.ts b/src/lib/components/topic_summary/topic_summary.test.ts index a4c46bf..f178c89 100644 --- a/src/lib/components/topic_summary/topic_summary.test.ts +++ b/src/lib/components/topic_summary/topic_summary.test.ts @@ -10,38 +10,35 @@ import '$lib/i18n'; import TopicSummary from './topic_summary.svelte'; const internals = { - results: null + results: null }; describe('Topic Summary component', () => { + beforeEach(() => { + internals.results = render(TopicSummary, { + props: { + topic: { + id: 'ea2431c8-5c1c-4ed0-907a-45e012696ab8', + title: 'I sure am a test topic', + ttl: 160 * 1000, + updated_at: Date.now() + } + } + }); + }); - beforeEach(() => { + test('It should display the title', () => { + expect(internals.results.getByText('I sure am a test topic')).toBeVisible(); + }); - internals.results = render(TopicSummary, { props: { - topic: { - id: 'ea2431c8-5c1c-4ed0-907a-45e012696ab8', - title: 'I sure am a test topic', - ttl: 160 * 1000, - updated_at: Date.now() - } - } }); - }); + test('Topic title should be a permalink', () => { + expect(internals.results.getByText('I sure am a test topic').closest('a')).toHaveAttribute( + 'href', + '/t/ea2431c8-5c1c-4ed0-907a-45e012696ab8' + ); + }); - test('It should display the title', () => { - - expect(internals.results.getByText('I sure am a test topic')) - .toBeVisible(); - }); - - test('Topic title should be a permalink', () => { - - expect(internals.results.getByText('I sure am a test topic').closest('a')) - .toHaveAttribute('href', '/t/ea2431c8-5c1c-4ed0-907a-45e012696ab8'); - }); - - test('It should display remaining time in readable format', () => { - - expect(internals.results.getByText(/2 minutes remaining/)) - .toBeVisible(); - }); + test('It should display remaining time in readable format', () => { + expect(internals.results.getByText(/2 minutes remaining/)).toBeVisible(); + }); }); diff --git a/src/lib/config/__mocks__/config.ts b/src/lib/config/__mocks__/config.ts index 448e1db..7752dd7 100644 --- a/src/lib/config/__mocks__/config.ts +++ b/src/lib/config/__mocks__/config.ts @@ -1,15 +1,15 @@ export const apollo = { - uri: 'http://127.0.0.1:1234/graphql', - name: 'COOL_APP', - version: '9.9.9', - defaultOptions: { - watchQuery: { - fetchPolicy: 'no-cache' - }, - query: { - fetchPolicy: 'no-cache' - } - } + uri: 'http://127.0.0.1:1234/graphql', + name: 'COOL_APP', + version: '9.9.9', + defaultOptions: { + watchQuery: { + fetchPolicy: 'no-cache' + }, + query: { + fetchPolicy: 'no-cache' + } + } }; export const version = '9.9.9'; diff --git a/src/lib/config/apollo.ts b/src/lib/config/apollo.ts index a3820ed..826fd28 100644 --- a/src/lib/config/apollo.ts +++ b/src/lib/config/apollo.ts @@ -5,10 +5,10 @@ import { apollo as apolloConfig } from './config'; const cache = new InMemoryCache(); export const client = new ApolloClient({ - cache, - link: new HttpLink({ - uri: apolloConfig.uri, - fetch - }), - ...apolloConfig + cache, + link: new HttpLink({ + uri: apolloConfig.uri, + fetch + }), + ...apolloConfig }); diff --git a/src/lib/config/config.ts b/src/lib/config/config.ts index 1ec5ab2..efb7dea 100644 --- a/src/lib/config/config.ts +++ b/src/lib/config/config.ts @@ -1,5 +1,5 @@ const internals = { - version: '1.0.0' + version: '1.0.0' }; /* @@ -9,9 +9,9 @@ const internals = { */ export const apollo = { - uri: import.meta.env.VITE_APOLLO_SERVER, - name: 'forum', - version: internals.version + uri: import.meta.env.VITE_APOLLO_SERVER, + name: 'forum', + version: internals.version }; export const version = internals.version; diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts index 5dd533a..2f6edb8 100644 --- a/src/lib/i18n.ts +++ b/src/lib/i18n.ts @@ -7,6 +7,6 @@ addMessages('en', en); addMessages('es', es); init({ - fallbackLocale: 'en', - initialLocale: getLocaleFromNavigator().replace(/-[A-Z]{2}$/, '') + fallbackLocale: 'en', + initialLocale: getLocaleFromNavigator().replace(/-[A-Z]{2}$/, '') }); diff --git a/src/lib/stores/actions.test.ts b/src/lib/stores/actions.test.ts index c650536..39e1f6e 100644 --- a/src/lib/stores/actions.test.ts +++ b/src/lib/stores/actions.test.ts @@ -1,32 +1,25 @@ import { enableTopicActions, disableTopicActions, topicActions } from './actions'; describe('Topic actions and state', () => { + test('There should be no topic actions by default', () => { + topicActions.subscribe((actions) => { + expect(actions).toBe(undefined); + })(); + }); - test('There should be no topic actions by default', () => { + test('enableTopicActions should set the topic id', () => { + enableTopicActions('free_hat'); + topicActions.subscribe((actions) => { + expect(actions).toEqual({ + id: 'free_hat' + }); + })(); + }); - topicActions.subscribe((actions) => { - - expect(actions).toBe(undefined); - })(); - }); - - test('enableTopicActions should set the topic id', () => { - - enableTopicActions('free_hat'); - topicActions.subscribe((actions) => { - - expect(actions).toEqual({ - id: 'free_hat' - }); - })(); - }); - - test('disableTopicActions should unset the topic id', () => { - - disableTopicActions(); - topicActions.subscribe((actions) => { - - expect(actions).toEqual(undefined); - })(); - }); + test('disableTopicActions should unset the topic id', () => { + disableTopicActions(); + topicActions.subscribe((actions) => { + expect(actions).toEqual(undefined); + })(); + }); }); diff --git a/src/lib/stores/actions.ts b/src/lib/stores/actions.ts index 95702dc..19e9a69 100644 --- a/src/lib/stores/actions.ts +++ b/src/lib/stores/actions.ts @@ -2,11 +2,11 @@ import { derived, writable } from 'svelte/store'; import type { Readable, Writable } from 'svelte/store'; export type Actions = { - topic?: TopicAction + topic?: TopicAction; }; export type TopicAction = { - id: string + id: string; }; /* @@ -16,26 +16,19 @@ export type TopicAction = { const actions: Writable<Actions> = writable({}); export const enableTopicActions = (id: string) => { - - actions.update((actionsValue: Actions): Actions => { - - actionsValue.topic = { - id - }; - return actionsValue; - }); + actions.update((actionsValue: Actions): Actions => { + actionsValue.topic = { + id + }; + return actionsValue; + }); }; export const disableTopicActions = () => { - - actions.update((actionsValue): Actions => { - - delete actionsValue.topic; - return actionsValue; - }); + actions.update((actionsValue): Actions => { + delete actionsValue.topic; + return actionsValue; + }); }; -export const topicActions: Readable<TopicAction> = derived( - actions, - ($actions) => $actions.topic -); +export const topicActions: Readable<TopicAction> = derived(actions, ($actions) => $actions.topic); diff --git a/src/lib/stores/apollo.ts b/src/lib/stores/apollo.ts index 12463c3..4ef1986 100644 --- a/src/lib/stores/apollo.ts +++ b/src/lib/stores/apollo.ts @@ -10,55 +10,53 @@ import type { Readable } from 'svelte/store'; */ type ApolloStoreConfiguration<Type> = { - key: string, - query: DocumentNode, - initialValue?: Type | void - variables?: object + key: string; + query: DocumentNode; + initialValue?: Type | void; + variables?: object; }; type ApolloStoreState<Type> = { - loading: boolean, - data: Type | void, - error: Error | void + loading: boolean; + data: Type | void; + error: Error | void; }; -export const store = function store<Type>({ key, query, initialValue = null, variables = {} }: ApolloStoreConfiguration<Type>): Readable<ApolloStoreState<Type>> { +export const store = function store<Type>({ + key, + query, + initialValue = null, + variables = {} +}: ApolloStoreConfiguration<Type>): Readable<ApolloStoreState<Type>> { + const initialState: ApolloStoreState<Type> = { + loading: true, + data: initialValue, + error: undefined + }; - const initialState: ApolloStoreState<Type> = { - loading: true, - data: initialValue, - error: undefined - }; + return readable(initialState, (set) => { + const handleError = function (error: Error) { + return set({ + loading: false, + data: initialValue, + error + }); + }; - return readable( - initialState, - (set) => { + client.watchQuery({ query, variables }).subscribe( + (result: ApolloQueryResult<Type>) => { + if (result.errors) { + const error = new ApolloError({ graphQLErrors: result.errors }); + return handleError(error); + } - const handleError = function (error: Error) { - - return set({ - loading: false, - data: initialValue, - error - }); - }; - - client.watchQuery({ query, variables }).subscribe( - (result: ApolloQueryResult<Type>) => { - - if (result.errors) { - const error = new ApolloError({ graphQLErrors: result.errors }); - return handleError(error); - } - - set({ - loading: false, - data: result.data[key], - error: undefined - }); - }, - (error: Error) => handleError(error) - ); - } - ); + set({ + loading: false, + data: result.data[key], + error: undefined + }); + }, + (error: Error) => handleError(error) + ); + }); }; diff --git a/src/lib/stores/forums.test.ts b/src/lib/stores/forums.test.ts index 5a8c723..1a33290 100644 --- a/src/lib/stores/forums.test.ts +++ b/src/lib/stores/forums.test.ts @@ -10,43 +10,38 @@ jest.mock('$lib/config/config.ts'); import { getForum, getForums } from './forums'; const internals = { - provider: null + provider: null }; describe('Forums store pact', () => { + beforeAll(async () => { + internals.provider = new Pact({ + port: 1234, + dir: resolve(process.cwd(), 'pacts'), + consumer: 'ForumClient', + provider: 'ForumServer', + pactfileWriteMode: 'update' + }); - beforeAll(async () => { + await internals.provider.setup(); + }); - internals.provider = new Pact({ - port: 1234, - dir: resolve(process.cwd(), 'pacts'), - consumer: 'ForumClient', - provider: 'ForumServer', - pactfileWriteMode: 'update' - }); + afterEach(() => internals.provider.verify()); + afterAll(() => internals.provider.finalize()); - await internals.provider.setup(); - }); - - afterEach(() => internals.provider.verify()); - afterAll(() => internals.provider.finalize()); - - describe('When there\'s data', () => { - - describe('GetForums', () => { - - beforeAll(async () => { - - const forumQuery = new GraphQLInteraction() - .given('there\'s data') - .uponReceiving('a request to list the forums') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetForums') - .withQuery( - `query GetForums { + describe("When there's data", () => { + describe('GetForums', () => { + beforeAll(async () => { + const forumQuery = new GraphQLInteraction() + .given("there's data") + .uponReceiving('a request to list the forums') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetForums') + .withQuery( + `query GetForums { forums { id glyph @@ -55,67 +50,67 @@ describe('Forums store pact', () => { __typename } }` - ) - .withVariables({}) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - data: { - forums: eachLike({ - id: like('butter'), - glyph: like('⌘'), - label: like('test_forums.butter'), - position: like(1) - }) - } - } - }); - return await internals.provider.addInteraction(forumQuery); - }); + ) + .withVariables({}) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + data: { + forums: eachLike({ + id: like('butter'), + glyph: like('⌘'), + label: like('test_forums.butter'), + position: like(1) + }) + } + } + }); + return await internals.provider.addInteraction(forumQuery); + }); - test('it returns the forums', async () => { + test('it returns the forums', async () => { + const forums = getForums(); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + forums.subscribe((forumsValue) => { + response = forumsValue; + counter(); + }); + expect(response.data).toBeInstanceOf(Array); + expect(response.data.length).toBe(0); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toEqual( + expect.arrayContaining([ + { + id: 'butter', + glyph: '⌘', + label: 'test_forums.butter', + position: 1 + } + ]) + ); + expect(response.loading).toBe(false); + expect(response.error).toBe(undefined); + }); + }); - const forums = getForums(); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - forums.subscribe((forumsValue) => { - - response = forumsValue; - counter(); - }); - expect(response.data).toBeInstanceOf(Array); - expect(response.data.length).toBe(0); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toEqual(expect.arrayContaining([{ - id: 'butter', - glyph: '⌘', - label: 'test_forums.butter', - position: 1 - }])); - expect(response.loading).toBe(false); - expect(response.error).toBe(undefined); - }); - }); - - describe('GetForum', () => { - - beforeAll(async () => { - - const forumQuery = new GraphQLInteraction() - .given('there\'s data') - .uponReceiving('a request to get a single forum') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetForum') - .withQuery( - `query GetForum($id: ID!) { + describe('GetForum', () => { + beforeAll(async () => { + const forumQuery = new GraphQLInteraction() + .given("there's data") + .uponReceiving('a request to get a single forum') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetForum') + .withQuery( + `query GetForum($id: ID!) { forum(id: $id) { id glyph @@ -131,81 +126,80 @@ describe('Forums store pact', () => { __typename } }` - ) - .withVariables({ - id: 'freezer' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - data: { - forum: like({ - id: 'freezer', - glyph: like('✭'), - label: like('test_forums.freezer'), - position: like(3), - topics: eachLike({ - id: like('629de02c-151a-4db7-bb86-43b2add8a15a'), - title: like('Very pacty topic'), - updated_at: like(1619954611616), - ttl: like(3601) - }) - }) - } - } - }); - return await internals.provider.addInteraction(forumQuery); - }); + ) + .withVariables({ + id: 'freezer' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + data: { + forum: like({ + id: 'freezer', + glyph: like('✭'), + label: like('test_forums.freezer'), + position: like(3), + topics: eachLike({ + id: like('629de02c-151a-4db7-bb86-43b2add8a15a'), + title: like('Very pacty topic'), + updated_at: like(1619954611616), + ttl: like(3601) + }) + }) + } + } + }); + return await internals.provider.addInteraction(forumQuery); + }); - test('it returns the forum', async () => { + test('it returns the forum', async () => { + const forum = getForum('freezer'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + forum.subscribe((forumsValue) => { + response = forumsValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data.id).toBe('freezer'); + expect(response.data.glyph).toBe('✭'); + expect(response.data.label).toBe('test_forums.freezer'); + expect(response.data.position).toBe(3); + expect(response.data.topics).toEqual( + expect.arrayContaining([ + { + id: '629de02c-151a-4db7-bb86-43b2add8a15a', + title: 'Very pacty topic', + updated_at: 1619954611616, + ttl: 3601 + } + ]) + ); + expect(response.loading).toBe(false); + expect(response.error).toBe(undefined); + }); + }); + }); - const forum = getForum('freezer'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - forum.subscribe((forumsValue) => { - - response = forumsValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data.id).toBe('freezer'); - expect(response.data.glyph).toBe('✭'); - expect(response.data.label).toBe('test_forums.freezer'); - expect(response.data.position).toBe(3); - expect(response.data.topics).toEqual(expect.arrayContaining([{ - id: '629de02c-151a-4db7-bb86-43b2add8a15a', - title: 'Very pacty topic', - updated_at: 1619954611616, - ttl: 3601 - }])); - expect(response.loading).toBe(false); - expect(response.error).toBe(undefined); - }); - }); - }); - - describe('When there\'s no data', () => { - - describe('GetForums', () => { - - beforeAll(async () => { - - const forumQuery = new GraphQLInteraction() - .given('there\'s no data') - .uponReceiving('a request to list the forums') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetForums') - .withQuery( - `query GetForums { + describe("When there's no data", () => { + describe('GetForums', () => { + beforeAll(async () => { + const forumQuery = new GraphQLInteraction() + .given("there's no data") + .uponReceiving('a request to list the forums') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetForums') + .withQuery( + `query GetForums { forums { id glyph @@ -214,58 +208,54 @@ describe('Forums store pact', () => { __typename } }` - ) - .withVariables({}) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - data: { - forums: [] - } - } - }); - return await internals.provider.addInteraction(forumQuery); - }); - - test('it returns the forums', async () => { - - const forums = getForums(); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - forums.subscribe((forumsValue) => { - - response = forumsValue; - counter(); - }); - expect(response.data).toBeInstanceOf(Array); - expect(response.data.length).toBe(0); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBeInstanceOf(Array); - expect(response.data.length).toBe(0); - expect(response.loading).toBe(false); - expect(response.error).toBe(undefined); - }); - }); - - describe('GetForum', () => { + ) + .withVariables({}) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + data: { + forums: [] + } + } + }); + return await internals.provider.addInteraction(forumQuery); + }); - beforeAll(async () => { + test('it returns the forums', async () => { + const forums = getForums(); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + forums.subscribe((forumsValue) => { + response = forumsValue; + counter(); + }); + expect(response.data).toBeInstanceOf(Array); + expect(response.data.length).toBe(0); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBeInstanceOf(Array); + expect(response.data.length).toBe(0); + expect(response.loading).toBe(false); + expect(response.error).toBe(undefined); + }); + }); - const forumQuery = new GraphQLInteraction() - .given('there\'s no data') - .uponReceiving('a request to get a single forum') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetForum') - .withQuery( - `query GetForum($id: ID!) { + describe('GetForum', () => { + beforeAll(async () => { + const forumQuery = new GraphQLInteraction() + .given("there's no data") + .uponReceiving('a request to get a single forum') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetForum') + .withQuery( + `query GetForum($id: ID!) { forum(id: $id) { id glyph @@ -281,61 +271,56 @@ describe('Forums store pact', () => { __typename } }` - ) - .withVariables({ - id: 'freezer' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - data: { - forum: null - } - } - }); - return await internals.provider.addInteraction(forumQuery); - }); - - test('it returns the forum', async () => { - - const forum = getForum('freezer'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - forum.subscribe((forumsValue) => { - - response = forumsValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error).toBe(undefined); - }); - }); - }); - - describe('When there\'s a server error', () => { - - describe('GetForums', () => { + ) + .withVariables({ + id: 'freezer' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + data: { + forum: null + } + } + }); + return await internals.provider.addInteraction(forumQuery); + }); - beforeAll(async () => { + test('it returns the forum', async () => { + const forum = getForum('freezer'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + forum.subscribe((forumsValue) => { + response = forumsValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error).toBe(undefined); + }); + }); + }); - const forumQuery = new GraphQLInteraction() - .given('there\'s a server error') - .uponReceiving('a request to list the forums') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetForums') - .withQuery( - `query GetForums { + describe("When there's a server error", () => { + describe('GetForums', () => { + beforeAll(async () => { + const forumQuery = new GraphQLInteraction() + .given("there's a server error") + .uponReceiving('a request to list the forums') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetForums') + .withQuery( + `query GetForums { forums { id glyph @@ -344,50 +329,46 @@ describe('Forums store pact', () => { __typename } }` - ) - .withVariables({}) - .willRespondWith({ - status: 500 - }); - return await internals.provider.addInteraction(forumQuery); - }); + ) + .withVariables({}) + .willRespondWith({ + status: 500 + }); + return await internals.provider.addInteraction(forumQuery); + }); - test('it returns the error', async () => { + test('it returns the error', async () => { + const forums = getForums(); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + forums.subscribe((forumsValue) => { + response = forumsValue; + counter(); + }); + expect(response.data).toBeInstanceOf(Array); + expect(response.data.length).toBe(0); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBeInstanceOf(Array); + expect(response.data.length).toBe(0); + expect(response.loading).toBe(false); + expect(response.error).toBeInstanceOf(Error); + }); + }); - const forums = getForums(); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - forums.subscribe((forumsValue) => { - - response = forumsValue; - counter(); - }); - expect(response.data).toBeInstanceOf(Array); - expect(response.data.length).toBe(0); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBeInstanceOf(Array); - expect(response.data.length).toBe(0); - expect(response.loading).toBe(false); - expect(response.error).toBeInstanceOf(Error); - }); - }); - - describe('GetForum', () => { - - beforeAll(async () => { - - const forumQuery = new GraphQLInteraction() - .given('there\'s a server error') - .uponReceiving('a request to get a single forum') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetForum') - .withQuery( - `query GetForum($id: ID!) { + describe('GetForum', () => { + beforeAll(async () => { + const forumQuery = new GraphQLInteraction() + .given("there's a server error") + .uponReceiving('a request to get a single forum') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetForum') + .withQuery( + `query GetForum($id: ID!) { forum(id: $id) { id glyph @@ -403,53 +384,48 @@ describe('Forums store pact', () => { __typename } }` - ) - .withVariables({ - id: 'freezer' - }) - .willRespondWith({ - status: 500 - }); - return await internals.provider.addInteraction(forumQuery); - }); - - test('it returns the error', async () => { + ) + .withVariables({ + id: 'freezer' + }) + .willRespondWith({ + status: 500 + }); + return await internals.provider.addInteraction(forumQuery); + }); - const forum = getForum('freezer'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - forum.subscribe((forumsValue) => { + test('it returns the error', async () => { + const forum = getForum('freezer'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + forum.subscribe((forumsValue) => { + response = forumsValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error).toBeInstanceOf(Error); + }); + }); + }); - response = forumsValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error).toBeInstanceOf(Error); - }); - }); - }); - - describe('When there\'s an error in the response', () => { - - describe('GetForums', () => { - - beforeAll(async () => { - - const forumQuery = new GraphQLInteraction() - .given('there\'s an error in the response') - .uponReceiving('a request to list the forums') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetForums') - .withQuery( - `query GetForums { + describe("When there's an error in the response", () => { + describe('GetForums', () => { + beforeAll(async () => { + const forumQuery = new GraphQLInteraction() + .given("there's an error in the response") + .uponReceiving('a request to list the forums') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetForums') + .withQuery( + `query GetForums { forums { id glyph @@ -458,60 +434,60 @@ describe('Forums store pact', () => { __typename } }` - ) - .withVariables({}) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - errors: eachLike({ - message: like('An error occurred when fetching forums') - }) - } - }); - return await internals.provider.addInteraction(forumQuery); - }); - - test('it returns the error', async () => { + ) + .withVariables({}) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + errors: eachLike({ + message: like('An error occurred when fetching forums') + }) + } + }); + return await internals.provider.addInteraction(forumQuery); + }); - const forums = getForums(); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - forums.subscribe((forumsValue) => { + test('it returns the error', async () => { + const forums = getForums(); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + forums.subscribe((forumsValue) => { + response = forumsValue; + counter(); + }); + expect(response.data).toBeInstanceOf(Array); + expect(response.data.length).toBe(0); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBeInstanceOf(Array); + expect(response.data.length).toBe(0); + expect(response.loading).toBe(false); + expect(response.error.graphQLErrors).toEqual( + expect.arrayContaining([ + { + message: 'An error occurred when fetching forums' + } + ]) + ); + }); + }); - response = forumsValue; - counter(); - }); - expect(response.data).toBeInstanceOf(Array); - expect(response.data.length).toBe(0); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBeInstanceOf(Array); - expect(response.data.length).toBe(0); - expect(response.loading).toBe(false); - expect(response.error.graphQLErrors).toEqual(expect.arrayContaining([{ - message: 'An error occurred when fetching forums' - }])); - }); - }); - - describe('GetForum', () => { - - beforeAll(async () => { - - const forumQuery = new GraphQLInteraction() - .given('there\'s an error in the response') - .uponReceiving('a request to get a single forum') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetForum') - .withQuery( - `query GetForum($id: ID!) { + describe('GetForum', () => { + beforeAll(async () => { + const forumQuery = new GraphQLInteraction() + .given("there's an error in the response") + .uponReceiving('a request to get a single forum') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetForum') + .withQuery( + `query GetForum($id: ID!) { forum(id: $id) { id glyph @@ -527,44 +503,46 @@ describe('Forums store pact', () => { __typename } }` - ) - .withVariables({ - id: 'freezer' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - errors: eachLike({ - message: like('An error occurred when fetching the forum') - }) - } - }); - return await internals.provider.addInteraction(forumQuery); - }); - - test('it returns the error', async () => { - - const forum = getForum('freezer'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - forum.subscribe((forumsValue) => { + ) + .withVariables({ + id: 'freezer' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + errors: eachLike({ + message: like('An error occurred when fetching the forum') + }) + } + }); + return await internals.provider.addInteraction(forumQuery); + }); - response = forumsValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error.graphQLErrors).toEqual(expect.arrayContaining([{ - message: 'An error occurred when fetching the forum' - }])); - }); - }); - }); + test('it returns the error', async () => { + const forum = getForum('freezer'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + forum.subscribe((forumsValue) => { + response = forumsValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error.graphQLErrors).toEqual( + expect.arrayContaining([ + { + message: 'An error occurred when fetching the forum' + } + ]) + ); + }); + }); + }); }); diff --git a/src/lib/stores/forums.ts b/src/lib/stores/forums.ts index cb62b5a..ab02066 100644 --- a/src/lib/stores/forums.ts +++ b/src/lib/stores/forums.ts @@ -1,5 +1,6 @@ import { store } from './apollo'; import { GET_FORUM, GET_FORUMS } from '$lib/data/queries'; -export const getForum = (id: string) => store({ key: 'forum', query: GET_FORUM, variables: { id } }); +export const getForum = (id: string) => + store({ key: 'forum', query: GET_FORUM, variables: { id } }); export const getForums = () => store({ key: 'forums', query: GET_FORUMS, initialValue: [] }); diff --git a/src/lib/stores/posts.test.ts b/src/lib/stores/posts.test.ts index afc22f3..ef1835d 100644 --- a/src/lib/stores/posts.test.ts +++ b/src/lib/stores/posts.test.ts @@ -10,43 +10,38 @@ jest.mock('$lib/config/config.ts'); import { getPost } from './posts'; const internals = { - provider: null + provider: null }; describe('Posts store pact', () => { + beforeAll(async () => { + internals.provider = new Pact({ + port: 1234, + dir: resolve(process.cwd(), 'pacts'), + consumer: 'ForumClient', + provider: 'ForumServer', + pactfileWriteMode: 'update' + }); - beforeAll(async () => { + await internals.provider.setup(); + }); - internals.provider = new Pact({ - port: 1234, - dir: resolve(process.cwd(), 'pacts'), - consumer: 'ForumClient', - provider: 'ForumServer', - pactfileWriteMode: 'update' - }); + afterEach(() => internals.provider.verify()); + afterAll(() => internals.provider.finalize()); - await internals.provider.setup(); - }); - - afterEach(() => internals.provider.verify()); - afterAll(() => internals.provider.finalize()); - - describe('When there\'s data', () => { - - describe('GetPost', () => { - - beforeAll(async () => { - - const postQuery = new GraphQLInteraction() - .given('there\'s data') - .uponReceiving('a request to get a single post') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetPost') - .withQuery( - `query GetPost($id: ID!) { + describe("When there's data", () => { + describe('GetPost', () => { + beforeAll(async () => { + const postQuery = new GraphQLInteraction() + .given("there's data") + .uponReceiving('a request to get a single post') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetPost') + .withQuery( + `query GetPost($id: ID!) { post(id: $id) { id text @@ -64,85 +59,80 @@ describe('Posts store pact', () => { __typename } }` - ) - .withVariables({ - id: '8f75eba5-6989-4dd3-b466-e464546ce374' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - data: { - post: like({ - id: like('8f75eba5-6989-4dd3-b466-e464546ce374'), - text: like('This is a very pacty post'), - created_at: like(1619976194937), - author: like({ - id: like('a805b3de-cac4-451c-a1e6-f078869c9db9'), - handle: like('pacts_person') - }), - topic: like({ - id: like('5c283ce1-0470-4b98-86f5-1fec9a22c9ac'), - title: like('The parent pacts topic') - }) - }) - } - } - }); - return await internals.provider.addInteraction(postQuery); - }); + ) + .withVariables({ + id: '8f75eba5-6989-4dd3-b466-e464546ce374' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + data: { + post: like({ + id: like('8f75eba5-6989-4dd3-b466-e464546ce374'), + text: like('This is a very pacty post'), + created_at: like(1619976194937), + author: like({ + id: like('a805b3de-cac4-451c-a1e6-f078869c9db9'), + handle: like('pacts_person') + }), + topic: like({ + id: like('5c283ce1-0470-4b98-86f5-1fec9a22c9ac'), + title: like('The parent pacts topic') + }) + }) + } + } + }); + return await internals.provider.addInteraction(postQuery); + }); - test('it returns the post', async () => { + test('it returns the post', async () => { + const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + post.subscribe((postValue) => { + response = postValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toEqual({ + id: '8f75eba5-6989-4dd3-b466-e464546ce374', + text: 'This is a very pacty post', + created_at: 1619976194937, + author: { + id: 'a805b3de-cac4-451c-a1e6-f078869c9db9', + handle: 'pacts_person' + }, + topic: { + id: '5c283ce1-0470-4b98-86f5-1fec9a22c9ac', + title: 'The parent pacts topic' + } + }); + expect(response.loading).toBe(false); + expect(response.error).toBe(undefined); + }); + }); + }); - const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - post.subscribe((postValue) => { - - response = postValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toEqual({ - id: '8f75eba5-6989-4dd3-b466-e464546ce374', - text: 'This is a very pacty post', - created_at: 1619976194937, - author: { - id: 'a805b3de-cac4-451c-a1e6-f078869c9db9', - handle: 'pacts_person' - }, - topic: { - id: '5c283ce1-0470-4b98-86f5-1fec9a22c9ac', - title: 'The parent pacts topic' - } - }); - expect(response.loading).toBe(false); - expect(response.error).toBe(undefined); - }); - }); - }); - - describe('When there\'s no data', () => { - - describe('GetPost', () => { - - beforeAll(async () => { - - const postQuery = new GraphQLInteraction() - .given('there\'s no data') - .uponReceiving('a request to get a single post') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetPost') - .withQuery( - `query GetPost($id: ID!) { + describe("When there's no data", () => { + describe('GetPost', () => { + beforeAll(async () => { + const postQuery = new GraphQLInteraction() + .given("there's no data") + .uponReceiving('a request to get a single post') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetPost') + .withQuery( + `query GetPost($id: ID!) { post(id: $id) { id text @@ -160,61 +150,56 @@ describe('Posts store pact', () => { __typename } }` - ) - .withVariables({ - id: '8f75eba5-6989-4dd3-b466-e464546ce374' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - data: { - post: null - } - } - }); - return await internals.provider.addInteraction(postQuery); - }); + ) + .withVariables({ + id: '8f75eba5-6989-4dd3-b466-e464546ce374' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + data: { + post: null + } + } + }); + return await internals.provider.addInteraction(postQuery); + }); - test('it returns the post', async () => { + test('it returns the post', async () => { + const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + post.subscribe((postValue) => { + response = postValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error).toBe(undefined); + }); + }); + }); - const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - post.subscribe((postValue) => { - - response = postValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error).toBe(undefined); - }); - }); - }); - - describe('When there\'s a server error', () => { - - describe('GetPost', () => { - - beforeAll(async () => { - - const postQuery = new GraphQLInteraction() - .given('there\'s a server error') - .uponReceiving('a request to get a single post') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetPost') - .withQuery( - `query GetPost($id: ID!) { + describe("When there's a server error", () => { + describe('GetPost', () => { + beforeAll(async () => { + const postQuery = new GraphQLInteraction() + .given("there's a server error") + .uponReceiving('a request to get a single post') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetPost') + .withQuery( + `query GetPost($id: ID!) { post(id: $id) { id text @@ -232,53 +217,48 @@ describe('Posts store pact', () => { __typename } }` - ) - .withVariables({ - id: '8f75eba5-6989-4dd3-b466-e464546ce374' - }) - .willRespondWith({ - status: 500 - }); - return await internals.provider.addInteraction(postQuery); - }); - - test('it returns the error', async () => { + ) + .withVariables({ + id: '8f75eba5-6989-4dd3-b466-e464546ce374' + }) + .willRespondWith({ + status: 500 + }); + return await internals.provider.addInteraction(postQuery); + }); - const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - post.subscribe((postValue) => { + test('it returns the error', async () => { + const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + post.subscribe((postValue) => { + response = postValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error).toBeInstanceOf(Error); + }); + }); + }); - response = postValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error).toBeInstanceOf(Error); - }); - }); - }); - - describe('When there\'s an error in the response', () => { - - describe('GetPost', () => { - - beforeAll(async () => { - - const postQuery = new GraphQLInteraction() - .given('there\'s an error in the response') - .uponReceiving('a request to get a single post') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetPost') - .withQuery( - `query GetPost($id: ID!) { + describe("When there's an error in the response", () => { + describe('GetPost', () => { + beforeAll(async () => { + const postQuery = new GraphQLInteraction() + .given("there's an error in the response") + .uponReceiving('a request to get a single post') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetPost') + .withQuery( + `query GetPost($id: ID!) { post(id: $id) { id text @@ -296,44 +276,46 @@ describe('Posts store pact', () => { __typename } }` - ) - .withVariables({ - id: '8f75eba5-6989-4dd3-b466-e464546ce374' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - errors: eachLike({ - message: like('An error occurred when fetching the post') - }) - } - }); - return await internals.provider.addInteraction(postQuery); - }); - - test('it returns the error', async () => { - - const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - post.subscribe((postValue) => { + ) + .withVariables({ + id: '8f75eba5-6989-4dd3-b466-e464546ce374' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + errors: eachLike({ + message: like('An error occurred when fetching the post') + }) + } + }); + return await internals.provider.addInteraction(postQuery); + }); - response = postValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error.graphQLErrors).toEqual(expect.arrayContaining([{ - message: 'An error occurred when fetching the post' - }])); - }); - }); - }); + test('it returns the error', async () => { + const post = getPost('8f75eba5-6989-4dd3-b466-e464546ce374'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + post.subscribe((postValue) => { + response = postValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error.graphQLErrors).toEqual( + expect.arrayContaining([ + { + message: 'An error occurred when fetching the post' + } + ]) + ); + }); + }); + }); }); diff --git a/src/lib/stores/tags.test.ts b/src/lib/stores/tags.test.ts index 9d3eb50..8c9effe 100644 --- a/src/lib/stores/tags.test.ts +++ b/src/lib/stores/tags.test.ts @@ -10,43 +10,38 @@ jest.mock('$lib/config/config.ts'); import { getTag } from './tags'; const internals = { - provider: null + provider: null }; describe('Tags store pact', () => { + beforeAll(async () => { + internals.provider = new Pact({ + port: 1234, + dir: resolve(process.cwd(), 'pacts'), + consumer: 'ForumClient', + provider: 'ForumServer', + pactfileWriteMode: 'update' + }); - beforeAll(async () => { + await internals.provider.setup(); + }); - internals.provider = new Pact({ - port: 1234, - dir: resolve(process.cwd(), 'pacts'), - consumer: 'ForumClient', - provider: 'ForumServer', - pactfileWriteMode: 'update' - }); + afterEach(() => internals.provider.verify()); + afterAll(() => internals.provider.finalize()); - await internals.provider.setup(); - }); - - afterEach(() => internals.provider.verify()); - afterAll(() => internals.provider.finalize()); - - describe('When there\'s data', () => { - - describe('GetTag', () => { - - beforeAll(async () => { - - const tagQuery = new GraphQLInteraction() - .given('there\'s data') - .uponReceiving('a request to get a single tag') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetTag') - .withQuery( - `query GetTag($id: ID!) { + describe("When there's data", () => { + describe('GetTag', () => { + beforeAll(async () => { + const tagQuery = new GraphQLInteraction() + .given("there's data") + .uponReceiving('a request to get a single tag') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetTag') + .withQuery( + `query GetTag($id: ID!) { tag(id: $id) { id topics { @@ -59,77 +54,74 @@ describe('Tags store pact', () => { __typename } }` - ) - .withVariables({ - id: 'pineapple' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - data: { - tag: { - id: like('pineapple'), - topics: eachLike({ - id: like('cd038ae7-e8b4-4e38-9543-3d697e69ac34'), - title: like('This topic is about pineapples'), - updated_at: like(1619978944077), - ttl: like(3555) - }) - } - } - } - }); - return await internals.provider.addInteraction(tagQuery); - }); + ) + .withVariables({ + id: 'pineapple' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + data: { + tag: { + id: like('pineapple'), + topics: eachLike({ + id: like('cd038ae7-e8b4-4e38-9543-3d697e69ac34'), + title: like('This topic is about pineapples'), + updated_at: like(1619978944077), + ttl: like(3555) + }) + } + } + } + }); + return await internals.provider.addInteraction(tagQuery); + }); - test('it returns the tag', async () => { + test('it returns the tag', async () => { + const tag = getTag('pineapple'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + tag.subscribe((tagValue) => { + response = tagValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toEqual({ + id: 'pineapple', + topics: [ + { + id: 'cd038ae7-e8b4-4e38-9543-3d697e69ac34', + title: 'This topic is about pineapples', + updated_at: 1619978944077, + ttl: 3555 + } + ] + }); + expect(response.loading).toBe(false); + expect(response.error).toBe(undefined); + }); + }); + }); - const tag = getTag('pineapple'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - tag.subscribe((tagValue) => { - - response = tagValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toEqual({ - id: 'pineapple', - topics: [{ - id: 'cd038ae7-e8b4-4e38-9543-3d697e69ac34', - title: 'This topic is about pineapples', - updated_at: 1619978944077, - ttl: 3555 - }] - }); - expect(response.loading).toBe(false); - expect(response.error).toBe(undefined); - }); - }); - }); - - describe('When there\'s no data', () => { - - describe('GetTag', () => { - - beforeAll(async () => { - - const tagQuery = new GraphQLInteraction() - .given('there\'s no data') - .uponReceiving('a request to get a single tag') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetTag') - .withQuery( - `query GetTag($id: ID!) { + describe("When there's no data", () => { + describe('GetTag', () => { + beforeAll(async () => { + const tagQuery = new GraphQLInteraction() + .given("there's no data") + .uponReceiving('a request to get a single tag') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetTag') + .withQuery( + `query GetTag($id: ID!) { tag(id: $id) { id topics { @@ -142,61 +134,56 @@ describe('Tags store pact', () => { __typename } }` - ) - .withVariables({ - id: 'pineapple' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - data: { - tag: null - } - } - }); - return await internals.provider.addInteraction(tagQuery); - }); + ) + .withVariables({ + id: 'pineapple' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + data: { + tag: null + } + } + }); + return await internals.provider.addInteraction(tagQuery); + }); - test('it returns the tag', async () => { + test('it returns the tag', async () => { + const tag = getTag('pineapple'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + tag.subscribe((tagValue) => { + response = tagValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error).toBe(undefined); + }); + }); + }); - const tag = getTag('pineapple'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - tag.subscribe((tagValue) => { - - response = tagValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error).toBe(undefined); - }); - }); - }); - - describe('When there\'s a server error', () => { - - describe('GetTag', () => { - - beforeAll(async () => { - - const tagQuery = new GraphQLInteraction() - .given('there\'s a server error') - .uponReceiving('a request to get a single tag') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetTag') - .withQuery( - `query GetTag($id: ID!) { + describe("When there's a server error", () => { + describe('GetTag', () => { + beforeAll(async () => { + const tagQuery = new GraphQLInteraction() + .given("there's a server error") + .uponReceiving('a request to get a single tag') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetTag') + .withQuery( + `query GetTag($id: ID!) { tag(id: $id) { id topics { @@ -209,53 +196,48 @@ describe('Tags store pact', () => { __typename } }` - ) - .withVariables({ - id: 'pineapple' - }) - .willRespondWith({ - status: 500 - }); - return await internals.provider.addInteraction(tagQuery); - }); - - test('it returns the error', async () => { + ) + .withVariables({ + id: 'pineapple' + }) + .willRespondWith({ + status: 500 + }); + return await internals.provider.addInteraction(tagQuery); + }); - const tag = getTag('pineapple'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - tag.subscribe((tagValue) => { + test('it returns the error', async () => { + const tag = getTag('pineapple'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + tag.subscribe((tagValue) => { + response = tagValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error).toBeInstanceOf(Error); + }); + }); + }); - response = tagValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error).toBeInstanceOf(Error); - }); - }); - }); - - describe('When there\'s an error in the response', () => { - - describe('GetTag', () => { - - beforeAll(async () => { - - const tagQuery = new GraphQLInteraction() - .given('there\'s an error in the response') - .uponReceiving('a request to get a single tag') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetTag') - .withQuery( - `query GetTag($id: ID!) { + describe("When there's an error in the response", () => { + describe('GetTag', () => { + beforeAll(async () => { + const tagQuery = new GraphQLInteraction() + .given("there's an error in the response") + .uponReceiving('a request to get a single tag') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetTag') + .withQuery( + `query GetTag($id: ID!) { tag(id: $id) { id topics { @@ -268,44 +250,46 @@ describe('Tags store pact', () => { __typename } }` - ) - .withVariables({ - id: 'pineapple' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - errors: eachLike({ - message: like('An error occurred when fetching the tag') - }) - } - }); - return await internals.provider.addInteraction(tagQuery); - }); - - test('it returns the error', async () => { - - const tag = getTag('pineapple'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - tag.subscribe((tagValue) => { + ) + .withVariables({ + id: 'pineapple' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + errors: eachLike({ + message: like('An error occurred when fetching the tag') + }) + } + }); + return await internals.provider.addInteraction(tagQuery); + }); - response = tagValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error.graphQLErrors).toEqual(expect.arrayContaining([{ - message: 'An error occurred when fetching the tag' - }])); - }); - }); - }); + test('it returns the error', async () => { + const tag = getTag('pineapple'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + tag.subscribe((tagValue) => { + response = tagValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error.graphQLErrors).toEqual( + expect.arrayContaining([ + { + message: 'An error occurred when fetching the tag' + } + ]) + ); + }); + }); + }); }); diff --git a/src/lib/stores/topics.test.ts b/src/lib/stores/topics.test.ts index 2d2a2e7..ad53054 100644 --- a/src/lib/stores/topics.test.ts +++ b/src/lib/stores/topics.test.ts @@ -10,43 +10,38 @@ jest.mock('$lib/config/config.ts'); import { getTopic } from './topics'; const internals = { - provider: null + provider: null }; describe('Topics store pact', () => { + beforeAll(async () => { + internals.provider = new Pact({ + port: 1234, + dir: resolve(process.cwd(), 'pacts'), + consumer: 'ForumClient', + provider: 'ForumServer', + pactfileWriteMode: 'update' + }); - beforeAll(async () => { + await internals.provider.setup(); + }); - internals.provider = new Pact({ - port: 1234, - dir: resolve(process.cwd(), 'pacts'), - consumer: 'ForumClient', - provider: 'ForumServer', - pactfileWriteMode: 'update' - }); + afterEach(() => internals.provider.verify()); + afterAll(() => internals.provider.finalize()); - await internals.provider.setup(); - }); - - afterEach(() => internals.provider.verify()); - afterAll(() => internals.provider.finalize()); - - describe('When there\'s data', () => { - - describe('GetTopic', () => { - - beforeAll(async () => { - - const topicQuery = new GraphQLInteraction() - .given('there\'s data') - .uponReceiving('a request to get a single topic') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetTopic') - .withQuery( - `query GetTopic($id: ID!) { + describe("When there's data", () => { + describe('GetTopic', () => { + beforeAll(async () => { + const topicQuery = new GraphQLInteraction() + .given("there's data") + .uponReceiving('a request to get a single topic') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetTopic') + .withQuery( + `query GetTopic($id: ID!) { topic(id: $id) { id title @@ -77,107 +72,106 @@ describe('Topics store pact', () => { __typename } }` - ) - .withVariables({ - id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - data: { - topic: { - id: like('0b58959d-d448-4a4e-84b6-35e5ac0028d1'), - title: like('The pacty topic of the day'), - updated_at: like(1619979888906), - ttl: like(3399), - forum: { - id: like('cucumber'), - glyph: like('✽'), - label: like('test_forums.cucumber') - }, - tags: eachLike({ - id: like('skunk'), - weight: like(44) - }), - posts: eachLike({ - id: like('ed93530e-6f9c-4701-91ef-14f9e0ed3e26'), - text: like('The content of this post is very relevant'), - created_at: like(1619979889798), - author: like({ - id: like('07fb2ba0-0945-464a-b215-873296710c8c'), - handle: like('cucumber_fan92') - }) - }) - } - } - } - }); - return await internals.provider.addInteraction(topicQuery); - }); + ) + .withVariables({ + id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + data: { + topic: { + id: like('0b58959d-d448-4a4e-84b6-35e5ac0028d1'), + title: like('The pacty topic of the day'), + updated_at: like(1619979888906), + ttl: like(3399), + forum: { + id: like('cucumber'), + glyph: like('✽'), + label: like('test_forums.cucumber') + }, + tags: eachLike({ + id: like('skunk'), + weight: like(44) + }), + posts: eachLike({ + id: like('ed93530e-6f9c-4701-91ef-14f9e0ed3e26'), + text: like('The content of this post is very relevant'), + created_at: like(1619979889798), + author: like({ + id: like('07fb2ba0-0945-464a-b215-873296710c8c'), + handle: like('cucumber_fan92') + }) + }) + } + } + } + }); + return await internals.provider.addInteraction(topicQuery); + }); - test('it returns the topic', async () => { + test('it returns the topic', async () => { + const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + topic.subscribe((topicValue) => { + response = topicValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toEqual({ + id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1', + title: 'The pacty topic of the day', + updated_at: 1619979888906, + ttl: 3399, + forum: { + id: 'cucumber', + glyph: '✽', + label: 'test_forums.cucumber' + }, + tags: [ + { + id: 'skunk', + weight: 44 + } + ], + posts: [ + { + id: 'ed93530e-6f9c-4701-91ef-14f9e0ed3e26', + text: 'The content of this post is very relevant', + created_at: 1619979889798, + author: { + id: '07fb2ba0-0945-464a-b215-873296710c8c', + handle: 'cucumber_fan92' + } + } + ] + }); + expect(response.loading).toBe(false); + expect(response.error).toBe(undefined); + }); + }); + }); - const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - topic.subscribe((topicValue) => { - - response = topicValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toEqual({ - id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1', - title: 'The pacty topic of the day', - updated_at: 1619979888906, - ttl: 3399, - forum: { - id: 'cucumber', - glyph: '✽', - label: 'test_forums.cucumber' - }, - tags: [{ - id: 'skunk', - weight: 44 - }], - posts: [{ - id: 'ed93530e-6f9c-4701-91ef-14f9e0ed3e26', - text: 'The content of this post is very relevant', - created_at: 1619979889798, - author: { - id: '07fb2ba0-0945-464a-b215-873296710c8c', - handle: 'cucumber_fan92' - } - }] - }); - expect(response.loading).toBe(false); - expect(response.error).toBe(undefined); - }); - }); - }); - - describe('When there\'s no data', () => { - - describe('GetTopic', () => { - - beforeAll(async () => { - - const topicQuery = new GraphQLInteraction() - .given('there\'s no data') - .uponReceiving('a request to get a single topic') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetTopic') - .withQuery( - `query GetTopic($id: ID!) { + describe("When there's no data", () => { + describe('GetTopic', () => { + beforeAll(async () => { + const topicQuery = new GraphQLInteraction() + .given("there's no data") + .uponReceiving('a request to get a single topic') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetTopic') + .withQuery( + `query GetTopic($id: ID!) { topic(id: $id) { id title @@ -208,61 +202,56 @@ describe('Topics store pact', () => { __typename } }` - ) - .withVariables({ - id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - data: { - topic: null - } - } - }); - return await internals.provider.addInteraction(topicQuery); - }); + ) + .withVariables({ + id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + data: { + topic: null + } + } + }); + return await internals.provider.addInteraction(topicQuery); + }); - test('it returns the topic', async () => { + test('it returns the topic', async () => { + const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + topic.subscribe((topicValue) => { + response = topicValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error).toBe(undefined); + }); + }); + }); - const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - topic.subscribe((topicValue) => { - - response = topicValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error).toBe(undefined); - }); - }); - }); - - describe('When there\'s a server error', () => { - - describe('GetTopic', () => { - - beforeAll(async () => { - - const topicQuery = new GraphQLInteraction() - .given('there\'s a server error') - .uponReceiving('a request to get a single topic') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetTopic') - .withQuery( - `query GetTopic($id: ID!) { + describe("When there's a server error", () => { + describe('GetTopic', () => { + beforeAll(async () => { + const topicQuery = new GraphQLInteraction() + .given("there's a server error") + .uponReceiving('a request to get a single topic') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetTopic') + .withQuery( + `query GetTopic($id: ID!) { topic(id: $id) { id title @@ -293,53 +282,48 @@ describe('Topics store pact', () => { __typename } }` - ) - .withVariables({ - id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' - }) - .willRespondWith({ - status: 500 - }); - return await internals.provider.addInteraction(topicQuery); - }); - - test('it returns the error', async () => { + ) + .withVariables({ + id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' + }) + .willRespondWith({ + status: 500 + }); + return await internals.provider.addInteraction(topicQuery); + }); - const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - topic.subscribe((topicValue) => { + test('it returns the error', async () => { + const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + topic.subscribe((topicValue) => { + response = topicValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error).toBeInstanceOf(Error); + }); + }); + }); - response = topicValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error).toBeInstanceOf(Error); - }); - }); - }); - - describe('When there\'s an error in the response', () => { - - describe('GetTopic', () => { - - beforeAll(async () => { - - const topicQuery = new GraphQLInteraction() - .given('there\'s an error in the response') - .uponReceiving('a request to get a single topic') - .withRequest({ - path: '/graphql', - method: 'POST' - }) - .withOperation('GetTopic') - .withQuery( - `query GetTopic($id: ID!) { + describe("When there's an error in the response", () => { + describe('GetTopic', () => { + beforeAll(async () => { + const topicQuery = new GraphQLInteraction() + .given("there's an error in the response") + .uponReceiving('a request to get a single topic') + .withRequest({ + path: '/graphql', + method: 'POST' + }) + .withOperation('GetTopic') + .withQuery( + `query GetTopic($id: ID!) { topic(id: $id) { id title @@ -370,44 +354,46 @@ describe('Topics store pact', () => { __typename } }` - ) - .withVariables({ - id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' - }) - .willRespondWith({ - status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8' - }, - body: { - errors: eachLike({ - message: like('An error occurred when fetching the topic') - }) - } - }); - return await internals.provider.addInteraction(topicQuery); - }); - - test('it returns the error', async () => { - - const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); - const { counter, promise: resolveAfterTwo } = resolveAfter(2); - let response = null; - topic.subscribe((topicValue) => { + ) + .withVariables({ + id: '0b58959d-d448-4a4e-84b6-35e5ac0028d1' + }) + .willRespondWith({ + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8' + }, + body: { + errors: eachLike({ + message: like('An error occurred when fetching the topic') + }) + } + }); + return await internals.provider.addInteraction(topicQuery); + }); - response = topicValue; - counter(); - }); - expect(response.data).toBe(null); - expect(response.loading).toBe(true); - expect(response.error).toBe(undefined); - await resolveAfterTwo; - expect(response.data).toBe(null); - expect(response.loading).toBe(false); - expect(response.error.graphQLErrors).toEqual(expect.arrayContaining([{ - message: 'An error occurred when fetching the topic' - }])); - }); - }); - }); + test('it returns the error', async () => { + const topic = getTopic('0b58959d-d448-4a4e-84b6-35e5ac0028d1'); + const { counter, promise: resolveAfterTwo } = resolveAfter(2); + let response = null; + topic.subscribe((topicValue) => { + response = topicValue; + counter(); + }); + expect(response.data).toBe(null); + expect(response.loading).toBe(true); + expect(response.error).toBe(undefined); + await resolveAfterTwo; + expect(response.data).toBe(null); + expect(response.loading).toBe(false); + expect(response.error.graphQLErrors).toEqual( + expect.arrayContaining([ + { + message: 'An error occurred when fetching the topic' + } + ]) + ); + }); + }); + }); }); diff --git a/src/lib/stores/topics.ts b/src/lib/stores/topics.ts index 84228cd..8d69d80 100644 --- a/src/lib/stores/topics.ts +++ b/src/lib/stores/topics.ts @@ -1,4 +1,5 @@ import { store } from './apollo'; import { GET_TOPIC } from '$lib/data/queries'; -export const getTopic = (id: string) => store({ key: 'topic', query: GET_TOPIC, variables: { id } }); +export const getTopic = (id: string) => + store({ key: 'topic', query: GET_TOPIC, variables: { id } }); diff --git a/src/lib/utils/glyph_hash.test.ts b/src/lib/utils/glyph_hash.test.ts index 5f57a2a..e791bab 100644 --- a/src/lib/utils/glyph_hash.test.ts +++ b/src/lib/utils/glyph_hash.test.ts @@ -2,67 +2,55 @@ import { getGlyphHash } from './glyph_hash'; import type { GlyphHash } from './glyph_hash'; type TestState = { - glyphHash?: GlyphHash + glyphHash?: GlyphHash; }; - describe('Glyph Hash utility', () => { + test('it throws an exception if the string is too short', () => { + expect(() => { + getGlyphHash('short'); + }).toThrow(); - test('it throws an exception if the string is too short', () => { - - expect(() => { - - getGlyphHash('short'); - }).toThrow(); - - expect(() => { - - getGlyphHash('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'); - }).toThrow(); - - expect(() => { - - getGlyphHash('abcdefghijklmnopqrstuvwxyzABCDEF'); - }).not.toThrow(); - }); - - test('it treats UUIDs with hyphens the same as those without', () => { - - const uuidWithHyphens = 'f7722355-2285-46c0-a55f-3483a826f3a6'; - const uuidWithoutHyphens = 'f7722355228546c0a55f3483a826f3a6'; - - expect(getGlyphHash(uuidWithHyphens)).toEqual(getGlyphHash(uuidWithoutHyphens)); - }); - - describe('it generates four sets of glyphs and colors', () => { - - const state: TestState = {}; + expect(() => { + getGlyphHash('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'); + }).toThrow(); - beforeEach(() => { + expect(() => { + getGlyphHash('abcdefghijklmnopqrstuvwxyzABCDEF'); + }).not.toThrow(); + }); - state.glyphHash = getGlyphHash('f7722355-2285-46c0-a55f-3483a826f3a6'); - }); + test('it treats UUIDs with hyphens the same as those without', () => { + const uuidWithHyphens = 'f7722355-2285-46c0-a55f-3483a826f3a6'; + const uuidWithoutHyphens = 'f7722355228546c0a55f3483a826f3a6'; - test('there should be four glyph fragments', () => { + expect(getGlyphHash(uuidWithHyphens)).toEqual(getGlyphHash(uuidWithoutHyphens)); + }); - expect(state.glyphHash.length).toBe(4); - }); + describe('it generates four sets of glyphs and colors', () => { + const state: TestState = {}; - test('each fragment should have a single character glyph', () => { + beforeEach(() => { + state.glyphHash = getGlyphHash('f7722355-2285-46c0-a55f-3483a826f3a6'); + }); - for (const glyphHashFragment of state.glyphHash) { - expect(typeof glyphHashFragment.glyph).toBe('string'); - expect(glyphHashFragment.glyph.length).toBe(1); - } - }); + test('there should be four glyph fragments', () => { + expect(state.glyphHash.length).toBe(4); + }); - test('each fragment should have a hexadecimal color', () => { + test('each fragment should have a single character glyph', () => { + for (const glyphHashFragment of state.glyphHash) { + expect(typeof glyphHashFragment.glyph).toBe('string'); + expect(glyphHashFragment.glyph.length).toBe(1); + } + }); - for (const glyphHashFragment of state.glyphHash) { - expect(typeof glyphHashFragment.color).toBe('string'); - expect(glyphHashFragment.color.length).toBe(7); - expect(glyphHashFragment.color).toEqual(expect.stringMatching(/#[0-9a-f]{6}/)); - } - }); - }); + test('each fragment should have a hexadecimal color', () => { + for (const glyphHashFragment of state.glyphHash) { + expect(typeof glyphHashFragment.color).toBe('string'); + expect(glyphHashFragment.color.length).toBe(7); + expect(glyphHashFragment.color).toEqual(expect.stringMatching(/#[0-9a-f]{6}/)); + } + }); + }); }); diff --git a/src/lib/utils/glyph_hash.ts b/src/lib/utils/glyph_hash.ts index b704569..7ac6448 100644 --- a/src/lib/utils/glyph_hash.ts +++ b/src/lib/utils/glyph_hash.ts @@ -1,55 +1,37 @@ export type GlyphHash = GlyphHashFragment[]; type GlyphHashFragment = { - glyph: string, - color: string + glyph: string; + color: string; }; const internals = { - kDehyphenRegex: /[-]/g, - kSplitterRegex: /.{1,8}/g, - kGlyphs: [ - '☽', - '☆', - '♢', - '♡', - '╱', - '╲', - '╳', - '〰', - '▷', - '⏊', - '〒', - '▢', - '◯', - '⏃', - '⏀', - '⏆' - ], - unexpectedUUIDLength: class UnexpectedUUIDLength extends Error { - name = 'UnexpectedUUIDLength'; - message = 'The provided string was not a valid UUIDv4, please provide a 32 character long string' - } + kDehyphenRegex: /[-]/g, + kSplitterRegex: /.{1,8}/g, + kGlyphs: ['☽', '☆', '♢', '♡', '╱', '╲', '╳', '〰', '▷', '⏊', '〒', '▢', '◯', '⏃', '⏀', '⏆'], + unexpectedUUIDLength: class UnexpectedUUIDLength extends Error { + name = 'UnexpectedUUIDLength'; + message = + 'The provided string was not a valid UUIDv4, please provide a 32 character long string'; + } }; // Return a glyph with color based on a 4 byte fragment of a UUIDv4 const getGlyphHashFragment = function (uuidFragment: string): GlyphHashFragment { - - const glyphIndex = parseInt(uuidFragment.substring(0, 2), 16) % 16; - return { - glyph: internals.kGlyphs[glyphIndex], - color: `#${uuidFragment.substring(2, 8)}` - }; + const glyphIndex = parseInt(uuidFragment.substring(0, 2), 16) % 16; + return { + glyph: internals.kGlyphs[glyphIndex], + color: `#${uuidFragment.substring(2, 8)}` + }; }; // Return an array of glyphs based on a UUIDv4 export const getGlyphHash = function (uuid: string): GlyphHash { + const dehyphenedUuid = uuid.replace(/[-]/g, ''); - const dehyphenedUuid = uuid.replace(/[-]/g, ''); - - if (dehyphenedUuid.length !== 32) { - throw new internals.unexpectedUUIDLength(); - } + if (dehyphenedUuid.length !== 32) { + throw new internals.unexpectedUUIDLength(); + } - const hashFragments = dehyphenedUuid.match(internals.kSplitterRegex); - return hashFragments.map(getGlyphHashFragment); + const hashFragments = dehyphenedUuid.match(internals.kSplitterRegex); + return hashFragments.map(getGlyphHashFragment); }; diff --git a/src/lib/utils/readable_time.test.ts b/src/lib/utils/readable_time.test.ts index 5d8ba27..6c4b4d4 100644 --- a/src/lib/utils/readable_time.test.ts +++ b/src/lib/utils/readable_time.test.ts @@ -1,83 +1,71 @@ import { readableTime } from './readable_time'; describe('readableTime', () => { + test('it shows negative time as 0', () => { + const response = readableTime(-1000); - test('it shows negative time as 0', () => { + expect(response.count).toBe(0); + expect(response.label).toContain('seconds'); + }); - const response = readableTime(-1000); + test('uses seconds as the smallest unit', () => { + const response = readableTime(10); - expect(response.count).toBe(0); - expect(response.label).toContain('seconds'); - }); + expect(response.count).toBe(0); + expect(response.label).toContain('seconds'); + }); - test('uses seconds as the smallest unit', () => { + test('correctly divides miliseconds into seconds', () => { + const response = readableTime(4000); - const response = readableTime(10); + expect(response.count).toBe(4); + }); - expect(response.count).toBe(0); - expect(response.label).toContain('seconds'); - }); + test('uses seconds if the time is < 1 minute', () => { + const response = readableTime(59 * 1000); - test('correctly divides miliseconds into seconds', () => { + expect(response.label).toContain('seconds'); + }); - const response = readableTime(4000); + test('correctly divides miliseconds into minutes', () => { + const response = readableTime(2 * 60 * 1000); - expect(response.count).toBe(4); - }); + expect(response.count).toBe(2); + }); - test('uses seconds if the time is < 1 minute', () => { + test('uses minutes if the time is < 1 hour', () => { + const response = readableTime(59 * 60 * 1000); - const response = readableTime(59 * 1000); + expect(response.label).toContain('minutes'); + }); - expect(response.label).toContain('seconds'); - }); + test('correctly divides miliseconds into hours', () => { + const response = readableTime(2 * 60 * 60 * 1000); - test('correctly divides miliseconds into minutes', () => { + expect(response.count).toBe(2); + }); - const response = readableTime(2 * 60 * 1000); + test('uses hours if the time is < 1 days', () => { + const response = readableTime(23 * 60 * 60 * 1000); - expect(response.count).toBe(2); - }); + expect(response.label).toContain('hours'); + }); - test('uses minutes if the time is < 1 hour', () => { + test('correctly divides miliseconds into days', () => { + const response = readableTime(2 * 24 * 60 * 60 * 1000); - const response = readableTime(59 * 60 * 1000); + expect(response.count).toBe(2); + }); - expect(response.label).toContain('minutes'); - }); + test('uses days if the time is >= 1 day', () => { + const response = readableTime(364 * 24 * 60 * 60 * 1000); - test('correctly divides miliseconds into hours', () => { + expect(response.label).toContain('days'); + }); - const response = readableTime(2 * 60 * 60 * 1000); + test('uses days as the maximum unit', () => { + const response = readableTime(Number.MAX_VALUE); - expect(response.count).toBe(2); - }); - - test('uses hours if the time is < 1 days', () => { - - const response = readableTime(23 * 60 * 60 * 1000); - - expect(response.label).toContain('hours'); - }); - - test('correctly divides miliseconds into days', () => { - - const response = readableTime(2 * 24 * 60 * 60 * 1000); - - expect(response.count).toBe(2); - }); - - test('uses days if the time is >= 1 day', () => { - - const response = readableTime(364 * 24 * 60 * 60 * 1000); - - expect(response.label).toContain('days'); - }); - - test('uses days as the maximum unit', () => { - - const response = readableTime(Number.MAX_VALUE); - - expect(response.label).toContain('days'); - }); + expect(response.label).toContain('days'); + }); }); diff --git a/src/lib/utils/readable_time.ts b/src/lib/utils/readable_time.ts index 86ba044..c949981 100644 --- a/src/lib/utils/readable_time.ts +++ b/src/lib/utils/readable_time.ts @@ -1,46 +1,43 @@ type DateMagnitude = 'day' | 'hour' | 'minute' | 'second'; type ReadableTime = { - count: number, - label: string + count: number; + label: string; }; - const internals = { - magnitudes: { - day: 86400000, - hour: 3600000, - minute: 60000, - second: 1000 - }, - labels: { - day: 'time.days', - hour: 'time.hours', - minute: 'time.minutes', - second: 'time.seconds' - }, - - makeTimeReadable(time: number, magnitude: DateMagnitude): ReadableTime { + magnitudes: { + day: 86400000, + hour: 3600000, + minute: 60000, + second: 1000 + }, + labels: { + day: 'time.days', + hour: 'time.hours', + minute: 'time.minutes', + second: 'time.seconds' + }, - return { - count: Math.floor(time / internals.magnitudes[magnitude]), - label: internals.labels[magnitude] - }; - } + makeTimeReadable(time: number, magnitude: DateMagnitude): ReadableTime { + return { + count: Math.floor(time / internals.magnitudes[magnitude]), + label: internals.labels[magnitude] + }; + } }; export const readableTime = function readableTime(time: number): ReadableTime { - - switch (true) { - case time >= internals.magnitudes.day: - return internals.makeTimeReadable(time, 'day'); - case time >= internals.magnitudes.hour: - return internals.makeTimeReadable(time, 'hour'); - case time >= internals.magnitudes.minute: - return internals.makeTimeReadable(time, 'minute'); - case time < 0: - return internals.makeTimeReadable(0, 'second'); - default: - return internals.makeTimeReadable(time, 'second'); - } + switch (true) { + case time >= internals.magnitudes.day: + return internals.makeTimeReadable(time, 'day'); + case time >= internals.magnitudes.hour: + return internals.makeTimeReadable(time, 'hour'); + case time >= internals.magnitudes.minute: + return internals.makeTimeReadable(time, 'minute'); + case time < 0: + return internals.makeTimeReadable(0, 'second'); + default: + return internals.makeTimeReadable(time, 'second'); + } }; diff --git a/src/lib/utils/resolve_after.test.ts b/src/lib/utils/resolve_after.test.ts index 7e0cc3c..4b30d3c 100644 --- a/src/lib/utils/resolve_after.test.ts +++ b/src/lib/utils/resolve_after.test.ts @@ -1,37 +1,30 @@ import { resolveAfter } from './resolve_after'; describe('Resolve After', () => { + test('it should throw if given 0', () => { + expect(() => { + resolveAfter(0); + }).toThrow(); + }); - test('it should throw if given 0', () => { + test('it should throw if given a negative number', () => { + expect(() => { + resolveAfter(-1); + }).toThrow(); + }); - expect(() => { + test('it should resolve after the specified number of times', () => { + expect(() => { + const { counter, promise: resolveAfterThree } = resolveAfter(3); + let resolved = false; - resolveAfter(0); - }).toThrow(); - }); - - test('it should throw if given a negative number', () => { - - expect(() => { - - resolveAfter(-1); - }).toThrow(); - }); - - test('it should resolve after the specified number of times', () => { - - expect(() => { - - const { counter, promise: resolveAfterThree } = resolveAfter(3); - let resolved = false; - - resolveAfterThree.then(() => (resolved = true)); - counter(); - expect(resolved).toBe(false); - counter(); - expect(resolved).toBe(false); - counter(); - expect(resolved).toBe(true); - }).toThrow(); - }); + resolveAfterThree.then(() => (resolved = true)); + counter(); + expect(resolved).toBe(false); + counter(); + expect(resolved).toBe(false); + counter(); + expect(resolved).toBe(true); + }).toThrow(); + }); }); diff --git a/src/lib/utils/resolve_after.ts b/src/lib/utils/resolve_after.ts index 95a477e..5d35f1c 100644 --- a/src/lib/utils/resolve_after.ts +++ b/src/lib/utils/resolve_after.ts @@ -1,29 +1,26 @@ export type ResolveAfterPromise = { - counter: () => void, - promise: Promise<void> + counter: () => void; + promise: Promise<void>; }; export const resolveAfter = function (timesUntilResolve: number): ResolveAfterPromise { + let counter = null; + let currentValue = 0; - let counter = null; - let currentValue = 0; + if (timesUntilResolve <= 0) { + throw new Error('Resolve after requires a positive integer'); + } - if (timesUntilResolve <= 0) { - throw new Error('Resolve after requires a positive integer'); - } + const promise: Promise<void> = new Promise((resolvePromise) => { + counter = () => { + if (++currentValue === timesUntilResolve) { + resolvePromise(); + } + }; + }); - const promise: Promise<void> = new Promise((resolvePromise) => { - - counter = () => { - - if (++currentValue === timesUntilResolve) { - resolvePromise(); - } - }; - }); - - return { - counter, - promise - }; + return { + counter, + promise + }; }; diff --git a/src/routes/__error.svelte b/src/routes/__error.svelte index 737fc1e..f0620d3 100644 --- a/src/routes/__error.svelte +++ b/src/routes/__error.svelte @@ -1,4 +1,4 @@ -<script> +<script lang="ts"> import InvalidRoute from '$lib/components/invalid_route/invalid_route.svelte'; </script> diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte index b856e21..62881f2 100644 --- a/src/routes/__layout.svelte +++ b/src/routes/__layout.svelte @@ -1,5 +1,5 @@ -<script> - import "../app.css"; +<script lang="ts"> + import '../app.css'; import '$lib/i18n'; import { isLoading } from 'svelte-i18n'; @@ -24,17 +24,17 @@ <main> <slot /> </main> - <nav title={$_('forum_list.title')}> - {#if $store.loading} - <Loader /> - {/if} - {#if $store.error} - <ErrorBlock message={$_('forum_list.error.unavailable')} /> - {/if} - {#if forums} - <ForumList forums={forums} /> - {/if} - </nav> + <nav title={$_('forum_list.title')}> + {#if $store.loading} + <Loader /> + {/if} + {#if $store.error} + <ErrorBlock message={$_('forum_list.error.unavailable')} /> + {/if} + {#if forums} + <ForumList {forums} /> + {/if} + </nav> <Footer /> {/if} diff --git a/src/routes/a/[id].svelte b/src/routes/a/[id].svelte index 1eef1dc..aeca142 100644 --- a/src/routes/a/[id].svelte +++ b/src/routes/a/[id].svelte @@ -1,12 +1,12 @@ -<script context="module"> +<script lang="ts" context="module"> export const load = ({ - page: { - params: { id } - } + page: { + params: { id } + } }) => ({ props: { id } }); </script> -<script> +<script lang="ts"> import Author from '$lib/components/author/author.svelte'; </script> diff --git a/src/routes/f/[id].svelte b/src/routes/f/[id].svelte index eb65c22..b235e21 100644 --- a/src/routes/f/[id].svelte +++ b/src/routes/f/[id].svelte @@ -1,12 +1,12 @@ -<script context="module"> +<script lang="ts" context="module"> export const load = ({ - page: { - params: { id } - } + page: { + params: { id } + } }) => ({ props: { id } }); </script> -<script> +<script lang="ts"> import { _ } from 'svelte-i18n'; import Forum from '$lib/components/forum/forum.svelte'; import ErrorBlock from '$lib/components/error_block/error_block.svelte'; diff --git a/src/routes/g/[id].svelte b/src/routes/g/[id].svelte index a822738..a82e488 100644 --- a/src/routes/g/[id].svelte +++ b/src/routes/g/[id].svelte @@ -1,12 +1,12 @@ -<script context="module"> +<script lang="ts" context="module"> export const load = ({ - page: { - params: { id } - } + page: { + params: { id } + } }) => ({ props: { id } }); </script> -<script> +<script lang="ts"> import { _ } from 'svelte-i18n'; import { getTag } from '$lib/stores/tags'; import ErrorBlock from '$lib/components/error_block/error_block.svelte'; diff --git a/src/routes/index.svelte b/src/routes/index.svelte index f5fa06f..dad9253 100644 --- a/src/routes/index.svelte +++ b/src/routes/index.svelte @@ -1,4 +1,4 @@ -<script> +<script lang="ts"> import { _ } from 'svelte-i18n'; import Loader from '$lib/components/loader/loader.svelte'; </script> diff --git a/src/routes/p/[id].svelte b/src/routes/p/[id].svelte index 38bdb49..82d8045 100644 --- a/src/routes/p/[id].svelte +++ b/src/routes/p/[id].svelte @@ -1,12 +1,12 @@ -<script context="module"> +<script lang="ts" context="module"> export const load = ({ - page: { - params: { id } - } + page: { + params: { id } + } }) => ({ props: { id } }); </script> -<script> +<script lang="ts"> import { _ } from 'svelte-i18n'; import { getPost } from '$lib/stores/posts'; import Post from '$lib/components/post/post.svelte'; diff --git a/src/routes/t/[id].svelte b/src/routes/t/[id].svelte index 510ced1..e04f473 100644 --- a/src/routes/t/[id].svelte +++ b/src/routes/t/[id].svelte @@ -1,12 +1,12 @@ -<script context="module"> +<script lang="ts" context="module"> export const load = ({ - page: { - params: { id } - } + page: { + params: { id } + } }) => ({ props: { id } }); </script> -<script> +<script lang="ts"> import { onDestroy } from 'svelte'; import { _ } from 'svelte-i18n'; import { getTopic } from '$lib/stores/topics'; |