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',