diff --git a/package-lock.json b/package-lock.json index fe44f24..07f7f32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "anvil", - "version": "0.0.6", + "version": "0.0.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "anvil", - "version": "0.0.6", + "version": "0.0.7", "dependencies": { "axios": "1.7.2", "cheerio": "1.0.0-rc.12" diff --git a/package.json b/package.json index ec313f4..623953d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "anvil", - "version": "0.0.6", + "version": "0.0.7", "private": true, "scripts": { "dev": "vite dev", diff --git a/src/lib/components/atoms/AnvilLogo.svelte b/src/lib/components/atoms/AnvilLogo.svelte index 05e385a..d801361 100644 --- a/src/lib/components/atoms/AnvilLogo.svelte +++ b/src/lib/components/atoms/AnvilLogo.svelte @@ -12,6 +12,8 @@ You should have received a copy of the GNU Affero General Public License along w --> -Anvil logo +{$_(i18n.alt)} diff --git a/src/lib/components/molecules/Welcome.svelte b/src/lib/components/molecules/Welcome.svelte index 682d109..1dd4e81 100644 --- a/src/lib/components/molecules/Welcome.svelte +++ b/src/lib/components/molecules/Welcome.svelte @@ -15,27 +15,61 @@ You should have received a copy of the GNU Affero General Public License along w import { _ } from 'svelte-i18n'; import AnvilLogo from '../atoms/AnvilLogo.svelte'; + + /** + * Translation keys. + */ + export let i18n = { + create: '', + headline: '', + intro: '', + login: '', + logo: { + alt: '' + }, + reset: '' + }; + + /** + * Allow for linking stories in Storybook. + */ + export let sbCreate = ''; + + /** + * Allow for linking stories in Storybook. + */ + export let sbLogin = ''; + + /** + * Allow for linking stories in Storybook. + */ + export let sbReset = '';
- -

{$_('page.welcome.headline')}

-

{$_('page.welcome.intro')}

+ +

{$_(i18n.headline)}

+

{$_(i18n.intro)}

diff --git a/src/lib/components/pages/Welcome.svelte b/src/lib/components/pages/Welcome.svelte new file mode 100644 index 0000000..0531998 --- /dev/null +++ b/src/lib/components/pages/Welcome.svelte @@ -0,0 +1,18 @@ + + + + + diff --git a/src/lib/components/templates/Welcome.svelte b/src/lib/components/templates/Welcome.svelte new file mode 100644 index 0000000..245b8a4 --- /dev/null +++ b/src/lib/components/templates/Welcome.svelte @@ -0,0 +1,32 @@ + + + + + diff --git a/src/lib/i18n/locales/bg.json b/src/lib/i18n/locales/bg.json index 57fb854..9267dc3 100644 --- a/src/lib/i18n/locales/bg.json +++ b/src/lib/i18n/locales/bg.json @@ -128,8 +128,14 @@ } }, "welcome": { + "create": "", + "intro": "", "headline": "", - "intro": "" + "login": "", + "logo": { + "alt": "" + }, + "reset": "" } } } diff --git a/src/lib/i18n/locales/de.json b/src/lib/i18n/locales/de.json index c63ee57..0f7880f 100644 --- a/src/lib/i18n/locales/de.json +++ b/src/lib/i18n/locales/de.json @@ -128,8 +128,14 @@ } }, "welcome": { + "create": "", + "intro": "", "headline": "Willkommen bei Anvil", - "intro": "" + "login": "", + "logo": { + "alt": "" + }, + "reset": "" } } } diff --git a/src/lib/i18n/locales/en.json b/src/lib/i18n/locales/en.json index 4ca1a5f..b78cbb8 100644 --- a/src/lib/i18n/locales/en.json +++ b/src/lib/i18n/locales/en.json @@ -128,8 +128,14 @@ } }, "welcome": { + "create": "Create F2 account", "headline": "Welcome to Anvil", - "intro": "Anvil is a F2 („ForgeFed”) client. If you don't have an F2 account you need to create one on an F2 server." + "intro": "Anvil is a F2 („ForgeFed”) client. If you don't have an F2 account you need to create one on an F2 server.", + "login": "I have an account", + "logo": { + "alt": "Anvil logo" + }, + "reset": "Reset passphrase" } } } diff --git a/src/lib/i18n/locales/he.json b/src/lib/i18n/locales/he.json index a01b511..d64b747 100644 --- a/src/lib/i18n/locales/he.json +++ b/src/lib/i18n/locales/he.json @@ -128,8 +128,14 @@ } }, "welcome": { + "create": "", "headline": "", - "intro": "" + "intro": "", + "login": "", + "logo": { + "alt": "" + }, + "reset": "" } } } diff --git a/src/lib/i18n/locales/pl.json b/src/lib/i18n/locales/pl.json index 81ff289..48beaac 100644 --- a/src/lib/i18n/locales/pl.json +++ b/src/lib/i18n/locales/pl.json @@ -128,8 +128,14 @@ } }, "welcome": { + "create": "", "headline": "Witamy w Anvil", - "intro": "" + "intro": "", + "login": "", + "logo": { + "alt": "" + }, + "reset": "" } } } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 28d7511..192a4e6 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/stories/molecules/Welcome.stories.ts b/stories/molecules/Welcome.stories.ts index 692fc6c..74b07ca 100644 --- a/stories/molecules/Welcome.stories.ts +++ b/stories/molecules/Welcome.stories.ts @@ -17,10 +17,33 @@ import Welcome from '$lib/components/molecules/Welcome.svelte'; const meta = { title: 'Molecules/Welcome', component: Welcome, - tags: ['autodocs'] + tags: ['autodocs'], + args: { + sbCreate: 'Templates/Login', + sbLogin: 'Templates/Login', + sbReset: 'Templates/Login' + }, + argTypes: { + sbCreate: { control: 'radio', options: ['Templates/Login'] }, + sbLogin: { control: 'radio', options: ['Templates/Login'] }, + sbReset: { control: 'radio', options: ['Templates/Login'] } + } } satisfies Meta; export default meta; type Story = StoryObj; -export const Plain: Story = {}; +export const Plain: Story = { + args: { + i18n: { + create: 'page.welcome.create', + headline: 'page.welcome.headline', + intro: 'page.welcome.intro', + login: 'page.welcome.login', + logo: { + alt: 'page.welcome.logo.alt' + }, + reset: 'page.welcome.reset' + } + } +}; diff --git a/stories/pages/Welcome.stories.ts b/stories/pages/Welcome.stories.ts new file mode 100644 index 0000000..560cdc9 --- /dev/null +++ b/stories/pages/Welcome.stories.ts @@ -0,0 +1,26 @@ +/* Stories for Welcome page. + * Copyright (C) 2024 André Jaenisch + * SPDX-FileCopyrightText: 2024 André Jaenisch + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . + */ + +import type { Meta, StoryObj } from '@storybook/svelte'; + +import Welcome from '$lib/components/pages/Welcome.svelte'; + +const meta = { + title: 'Pages/Welcome', + component: Welcome, + tags: ['autodocs'] +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Plain: Story = {}; diff --git a/stories/templates/Welcome.stories.ts b/stories/templates/Welcome.stories.ts new file mode 100644 index 0000000..031bf0e --- /dev/null +++ b/stories/templates/Welcome.stories.ts @@ -0,0 +1,26 @@ +/* Stories for Welcome template. + * Copyright (C) 2024 André Jaenisch + * SPDX-FileCopyrightText: 2024 André Jaenisch + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . + */ + +import type { Meta, StoryObj } from '@storybook/svelte'; + +import Welcome from '$lib/components/templates/Welcome.svelte'; + +const meta = { + title: 'Templates/Welcome', + component: Welcome, + tags: ['autodocs'] +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Plain: Story = {}; diff --git a/tests/components/atoms/AnvilLogo.test.ts b/tests/components/atoms/AnvilLogo.test.ts new file mode 100644 index 0000000..4f092c4 --- /dev/null +++ b/tests/components/atoms/AnvilLogo.test.ts @@ -0,0 +1,74 @@ +/* Component test for AnvilLogo atom. + * Copyright (C) 2024 André Jaenisch + * SPDX-FileCopyrightText: 2024 André Jaenisch + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . + */ + +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/svelte'; +import { init, locale, register } from 'svelte-i18n'; + +import AnvilLogo from '../../../src/lib/components/atoms/AnvilLogo.svelte'; +import enMessages from '../../../src/lib/i18n/locales/en.json'; + +describe('AnvilLogo.svelte', () => { + beforeEach(() => { + register('en', () => import('../../../src/lib/i18n/locales/en.json')); + init({ fallbackLocale: 'en', initialLocale: 'en' }); + locale.set('en'); + }); + + it('should mount', () => { + // Arrange + // Nothing to prepare + + // Act + const { container } = render(AnvilLogo); + + // Assert + expect(container).toBeTruthy(); + }); + + it('should have a logo', () => { + // Arrange + // Nothing to prepare + + // Act + render(AnvilLogo); + + // Assert + // No alt text and therefore no accessible name. Turns role img into presentation + expect(screen.getByRole('presentation')).toBeInTheDocument(); + }); + + it('should have a logo', () => { + // Arrange + const i18n = { + alt: 'Testing Anvil' + }; + + // Act + render(AnvilLogo, { i18n }); + + // Assert + expect(screen.getByRole('img')).toBeInTheDocument(); + }); + + it('should accept arbitrary classes', () => { + // Arrange + const klass = 'w-full'; + + // Act + render(AnvilLogo, { class: klass }); + + // Assert + expect(screen.getByRole('presentation')).toBeInTheDocument(); + expect(screen.getByRole('presentation')).toHaveClass('w-full'); + }); +}); diff --git a/tests/components/molecules/Welcome.test.ts b/tests/components/molecules/Welcome.test.ts new file mode 100644 index 0000000..a04b7d0 --- /dev/null +++ b/tests/components/molecules/Welcome.test.ts @@ -0,0 +1,73 @@ +/* Component test for Welcome molecule. + * Copyright (C) 2024 André Jaenisch + * SPDX-FileCopyrightText: 2024 André Jaenisch + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . + */ + +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/svelte'; +import { init, locale, register } from 'svelte-i18n'; + +import Welcome from '../../../src/lib/components/molecules/Welcome.svelte'; +import enMessages from '../../../src/lib/i18n/locales/en.json'; + +describe('Welcome.svelte', () => { + beforeEach(() => { + register('en', () => import('../../../src/lib/i18n/locales/en.json')); + init({ fallbackLocale: 'en', initialLocale: 'en' }); + locale.set('en'); + }); + + it('should mount', () => { + // Arrange + // Nothing to prepare + + // Act + const { container } = render(Welcome); + + // Assert + expect(container).toBeTruthy(); + }); + + it('should have a logo', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + + // Assert + // No alt text and therefore no accessible name. Turns role img into presentation + expect(screen.getByRole('presentation')).toBeInTheDocument(); + }); + + it('should have a h1', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + const h1 = screen.getByRole('heading', { level: 1 }); + + // Assert + expect(h1).toBeInTheDocument(); + }); + + it('should have three links', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + const buttons = screen.getAllByRole('link'); + + // Assert + expect(buttons).toHaveLength(3); + }); +}); diff --git a/tests/components/pages/Welcome.test.ts b/tests/components/pages/Welcome.test.ts new file mode 100644 index 0000000..58d516f --- /dev/null +++ b/tests/components/pages/Welcome.test.ts @@ -0,0 +1,109 @@ +/* Component test for Welcome page. + * Copyright (C) 2024 André Jaenisch + * SPDX-FileCopyrightText: 2024 André Jaenisch + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . + */ + +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/svelte'; +import { init, locale, register } from 'svelte-i18n'; + +import Welcome from '../../../src/lib/components/pages/Welcome.svelte'; +import enMessages from '../../../src/lib/i18n/locales/en.json'; + +describe('Welcome.svelte', () => { + beforeEach(() => { + register('en', () => import('../../../src/lib/i18n/locales/en.json')); + init({ fallbackLocale: 'en', initialLocale: 'en' }); + locale.set('en'); + }); + + it('should mount', () => { + // Arrange + // Nothing to prepare + + // Act + const { container } = render(Welcome); + + // Assert + expect(container).toBeTruthy(); + }); + + it('should have a logo', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + + // Assert + expect(screen.getByRole('img')).toBeInTheDocument(); + expect(screen.getByRole('img')).toHaveAccessibleName(enMessages.page.welcome.logo.alt); + }); + + it('should have a h1', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + const h1 = screen.getByRole('heading', { level: 1 }); + + // Assert + expect(h1).toBeInTheDocument(); + expect(h1).toHaveTextContent(enMessages.page.welcome.headline); + }); + + it('should have an intro text', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + + // Assert + expect(screen.getByText(enMessages.page.welcome.intro)).toBeInTheDocument(); + }); + + it('should have a signup link', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + const button = screen.getByRole('link', { name: enMessages.page.welcome.create }); + + // Assert + expect(button).toBeInTheDocument(); + }); + + it('should have a signin link', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + const button = screen.getByRole('link', { name: enMessages.page.welcome.login }); + + // Assert + expect(button).toBeInTheDocument(); + }); + + it('should have a reset link', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + const button = screen.getByRole('link', { name: enMessages.page.welcome.reset }); + + // Assert + expect(button).toBeInTheDocument(); + }); +}); diff --git a/tests/components/templates/Welcome.test.ts b/tests/components/templates/Welcome.test.ts new file mode 100644 index 0000000..faf01af --- /dev/null +++ b/tests/components/templates/Welcome.test.ts @@ -0,0 +1,109 @@ +/* Component test for Welcome template. + * Copyright (C) 2024 André Jaenisch + * SPDX-FileCopyrightText: 2024 André Jaenisch + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . + */ + +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/svelte'; +import { init, locale, register } from 'svelte-i18n'; + +import Welcome from '../../../src/lib/components/templates/Welcome.svelte'; +import enMessages from '../../../src/lib/i18n/locales/en.json'; + +describe('Welcome.svelte', () => { + beforeEach(() => { + register('en', () => import('../../../src/lib/i18n/locales/en.json')); + init({ fallbackLocale: 'en', initialLocale: 'en' }); + locale.set('en'); + }); + + it('should mount', () => { + // Arrange + // Nothing to prepare + + // Act + const { container } = render(Welcome); + + // Assert + expect(container).toBeTruthy(); + }); + + it('should have a logo', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + + // Assert + expect(screen.getByRole('img')).toBeInTheDocument(); + expect(screen.getByRole('img')).toHaveAccessibleName(enMessages.page.welcome.logo.alt); + }); + + it('should have a h1', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + const h1 = screen.getByRole('heading', { level: 1 }); + + // Assert + expect(h1).toBeInTheDocument(); + expect(h1).toHaveTextContent(enMessages.page.welcome.headline); + }); + + it('should have an intro text', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + + // Assert + expect(screen.getByText(enMessages.page.welcome.intro)).toBeInTheDocument(); + }); + + it('should have a signup link', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + const button = screen.getByRole('link', { name: enMessages.page.welcome.create }); + + // Assert + expect(button).toBeInTheDocument(); + }); + + it('should have a signin link', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + const button = screen.getByRole('link', { name: enMessages.page.welcome.login }); + + // Assert + expect(button).toBeInTheDocument(); + }); + + it('should have a reset link', () => { + // Arrange + // Nothing to prepare + + // Act + render(Welcome); + const button = screen.getByRole('link', { name: enMessages.page.welcome.reset }); + + // Assert + expect(button).toBeInTheDocument(); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index 1bd5d0b..ee3f64d 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -10,14 +10,22 @@ * You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ +import { resolve } from 'node:path'; + import { svelte } from '@sveltejs/vite-plugin-svelte'; import { defineConfig } from 'vitest/config'; export default defineConfig({ + resolve: { + alias: { + // Help vitest find imported images + $lib: resolve('./src/lib') + } + }, plugins: [svelte({ hot: !process.env.VITEST })], test: { coverage: { - include: ['src/**/*.svelte'] + include: ['src'] }, include: ['tests/**/*.test.ts'], environment: 'jsdom',