diff --git a/src/lib/components/organisms/LoginForm.svelte b/src/lib/components/organisms/LoginForm.svelte new file mode 100644 index 0000000..cfb5257 --- /dev/null +++ b/src/lib/components/organisms/LoginForm.svelte @@ -0,0 +1,56 @@ + + +
+ {#if form?.missing} +

{$_(i18n.validation.missing)}

+ {/if} + {#if form?.incorrect} +

{$_(i18n.validation.incorrect)}

+ {/if} + + + +
+ + {$_(i18n.reset)} + +
+ +
diff --git a/src/lib/components/templates/Login.svelte b/src/lib/components/templates/Login.svelte index d60b436..d19f1a4 100644 --- a/src/lib/components/templates/Login.svelte +++ b/src/lib/components/templates/Login.svelte @@ -1,47 +1,43 @@

{$_('page.login.heading')}

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

-
- {#if form?.missing} -

{$_('page.login.form.validation.missing')}

- {/if} - {#if form?.incorrect} -

{$_('page.login.form.validation.incorrect')}

- {/if} - - - - - -
+
diff --git a/src/lib/i18n/locales/de.json b/src/lib/i18n/locales/de.json index 505339b..417e113 100644 --- a/src/lib/i18n/locales/de.json +++ b/src/lib/i18n/locales/de.json @@ -1,83 +1,84 @@ { - "page": { - "import_project": { - "form": { - "avatar": "Avatar", - "components": "Komponenten", - "fields": { - "avatar": { - "label": "Bild hochladen" - }, - "description": { - "label": "Beschreibung", - "placeholder": "Beschreibe das Projekt in unter 100 Zeichen..." - }, - "issues": { - "label": "Issues" - }, - "name": { - "error": "Dieses Feld ist erforderlich", - "label": "Name", - "placeholder": "Name des Projekts..." - }, - "pr": { - "label": "Pullrequest" - }, - "repository": { - "hint": "optional: von Git-Repository importieren", - "label": "Repository", - "placeholder": "URL des Git-Repositorys" - } - }, - "submit": "Projekt erstellen" - }, - "heading": "Projekt erstellen", - "intro": "Neues F2-Projekt zu Anvil hinzufügen." - }, - "login": { - "form": { - "validation": { - "incorrect": "Accountname oder Kennwort sind falsch.", - "missing": "Das Account-Feld ist verbindlich." - }, - "fields": { - "account": { - "label": "Accountname" - }, - "passphrase": { - "label": "Passwort" - }, - "server": { - "label": "F2-Server" - } - }, - "reset": "Passwort zurücksetzen", - "submit": "Einloggen" - }, - "heading": "Einloggen", - "intro": "Die Einwahldaten ausfüllen um Anvil mit deinem F2-Account zu benutzen." - }, - "profile": { - "activities": { - "block_or_report": "{blockElementOpen}blockieren{blockElementClose} oder {reportElementOpen}melden{reportElementClose}", - "like": "Gefällt mir" - }, - "heading": "Profil für", - "history": { - "activities": { - "setup": { - "description": "Das F2-Konto @{username}@{instance} wurde erfolgreich innerhalb von {created_with} erstellt", - "summary": "Kontoeinstellungen" - } - }, - "heading": "Aktivitäten" - }, - "projects": { - "add_or_import": "{addElementOpen}Ein Projekt hinzufügen{addElementClose} oder {importElementOpen}Ein Projekt importieren{importElementClose}.", - "empty": "Bisher keine Projekte hinzugefügt.", - "heading": "Projekte" - } - }, - "welcome": "Willkommen bei Anvil!" - } + "page": { + "import_project": { + "form": { + "avatar": "Avatar", + "components": "Komponenten", + "fields": { + "avatar": { + "label": "Bild hochladen" + }, + "description": { + "label": "Beschreibung", + "placeholder": "Beschreibe das Projekt in unter 100 Zeichen..." + }, + "issues": { + "label": "Issues" + }, + "name": { + "error": "Dieses Feld ist erforderlich", + "label": "Name", + "placeholder": "Name des Projekts..." + }, + "pr": { + "label": "Pullrequest" + }, + "repository": { + "hint": "optional: von Git-Repository importieren", + "label": "Repository", + "placeholder": "URL des Git-Repositorys" + } + }, + "submit": "Projekt erstellen" + }, + "heading": "Projekt erstellen", + "intro": "Neues F2-Projekt zu Anvil hinzufügen." + }, + "login": { + "form": { + "fields": { + "account": { + "label": "Accountname" + }, + "passphrase": { + "label": "Passwort", + "placeholder": "" + }, + "server": { + "label": "F2-Server" + } + }, + "reset": "Passwort zurücksetzen", + "submit": "Einloggen", + "validation": { + "incorrect": "Accountname oder Kennwort sind falsch.", + "missing": "Das Account-Feld ist verbindlich." + } + }, + "heading": "Einloggen", + "intro": "Die Einwahldaten ausfüllen um Anvil mit deinem F2-Account zu benutzen." + }, + "profile": { + "activities": { + "block_or_report": "{blockElementOpen}blockieren{blockElementClose} oder {reportElementOpen}melden{reportElementClose}", + "like": "Gefällt mir" + }, + "heading": "Profil für", + "history": { + "activities": { + "setup": { + "description": "Das F2-Konto @{username}@{instance} wurde erfolgreich innerhalb von {created_with} erstellt", + "summary": "Kontoeinstellungen" + } + }, + "heading": "Aktivitäten" + }, + "projects": { + "add_or_import": "{addElementOpen}Ein Projekt hinzufügen{addElementClose} oder {importElementOpen}Ein Projekt importieren{importElementClose}.", + "empty": "Bisher keine Projekte hinzugefügt.", + "heading": "Projekte" + } + }, + "welcome": "Willkommen bei Anvil!" + } } diff --git a/src/lib/i18n/locales/en.json b/src/lib/i18n/locales/en.json index 9477cdc..2ae8919 100644 --- a/src/lib/i18n/locales/en.json +++ b/src/lib/i18n/locales/en.json @@ -36,23 +36,24 @@ }, "login": { "form": { - "validation": { - "incorrect": "Account or password wrong.", - "missing": "The account field is required." - }, "fields": { "account": { "label": "Account name" }, "passphrase": { - "label": "Passphrase" + "label": "Passphrase", + "placeholder": "password" }, "server": { "label": "F2 server" } }, "reset": "Reset passphrase", - "submit": "Log in" + "submit": "Log in", + "validation": { + "incorrect": "Account or password wrong.", + "missing": "The account field is required." + } }, "heading": "Log in", "intro": "To use Anvil with your F2 account, fill in your credentials." diff --git a/src/lib/i18n/locales/he.json b/src/lib/i18n/locales/he.json index b511436..4463811 100644 --- a/src/lib/i18n/locales/he.json +++ b/src/lib/i18n/locales/he.json @@ -1,83 +1,84 @@ { - "page": { - "import_project": { - "form": { - "avatar": "תמונה", - "components": "רכיבים", - "fields": { - "avatar": { - "label": "להעלות תמונה?" - }, - "description": { - "label": "תיאור", - "placeholder": "על הפרויקט בפחות מ-100 אותיות…" - }, - "issues": { - "label": "סוגיות" - }, - "name": { - "error": "חובה", - "label": "שם", - "placeholder": "שם הפרויקט…" - }, - "pr": { - "label": "פרים" - }, - "repository": { - "hint": "אפשר לייבא קוד מקופסת גיט", - "label": "קופסת קוד", - "placeholder": "לינק לקופסת קוד של גיט" - } - }, - "submit": "ליצור פרויקט" - }, - "heading": "יצירת פרויקט", - "intro": "" - }, - "login": { - "form": { - "validation": { - "incorrect": "שם המשתמש או הסיסמה שגויים.", - "missing": "שם המשתמש חובה." - }, - "fields": { - "account": { - "label": "שם משתמש" - }, - "passphrase": { - "label": "סיסמה" - }, - "server": { - "label": "" - } - }, - "reset": "לשנות סיסמה?", - "submit": "להיכנס" - }, - "heading": "כניסה", - "intro": "" - }, - "profile": { - "activities": { - "block_or_report": "{blockElementOpen}לחסום{blockElementClose} או {reportElementOpen}לדווח{reportElementClose}", - "like": "אהבתי" - }, - "heading": "הפרופיל של", - "history": { - "activities": { - "setup": { - "description": "", - "summary": "" - } - }, - "heading": "פעילות" - }, - "projects": { - "add_or_import": "", - "empty": "עדיין אין כאן פרוייקטים.", - "heading": "פרויקטים" - } - }, - "welcome": "" - } + "page": { + "import_project": { + "form": { + "avatar": "תמונה", + "components": "רכיבים", + "fields": { + "avatar": { + "label": "להעלות תמונה?" + }, + "description": { + "label": "תיאור", + "placeholder": "על הפרויקט בפחות מ-100 אותיות…" + }, + "issues": { + "label": "סוגיות" + }, + "name": { + "error": "חובה", + "label": "שם", + "placeholder": "שם הפרויקט…" + }, + "pr": { + "label": "פרים" + }, + "repository": { + "hint": "אפשר לייבא קוד מקופסת גיט", + "label": "קופסת קוד", + "placeholder": "לינק לקופסת קוד של גיט" + } + }, + "submit": "ליצור פרויקט" + }, + "heading": "יצירת פרויקט", + "intro": "" + }, + "login": { + "form": { + "fields": { + "account": { + "label": "שם משתמש" + }, + "passphrase": { + "label": "סיסמה", + "placeholder": "" + }, + "server": { + "label": "" + } + }, + "reset": "לשנות סיסמה?", + "submit": "להיכנס", + "validation": { + "incorrect": "שם המשתמש או הסיסמה שגויים.", + "missing": "שם המשתמש חובה." + } + }, + "heading": "כניסה", + "intro": "" + }, + "profile": { + "activities": { + "block_or_report": "{blockElementOpen}לחסום{blockElementClose} או {reportElementOpen}לדווח{reportElementClose}", + "like": "אהבתי" + }, + "heading": "הפרופיל של", + "history": { + "activities": { + "setup": { + "description": "", + "summary": "" + } + }, + "heading": "פעילות" + }, + "projects": { + "add_or_import": "", + "empty": "עדיין אין כאן פרוייקטים.", + "heading": "פרויקטים" + } + }, + "welcome": "" + } } diff --git a/tests/components/organisms/LoginForm.test.ts b/tests/components/organisms/LoginForm.test.ts new file mode 100644 index 0000000..3f1a7d0 --- /dev/null +++ b/tests/components/organisms/LoginForm.test.ts @@ -0,0 +1,291 @@ +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/svelte'; +import { init, locale, register } from 'svelte-i18n'; + +import LoginForm from '../../../src/lib/components/organisms/LoginForm.svelte'; +import enMessages from '../../../src/lib/i18n/locales/en.json'; + +describe('LoginForm.svelte', () => { + beforeEach(() => { + register('en', () => import('../../../src/lib/i18n/locales/en.json')); + init({ fallbackLocale: 'en', initialLocale: 'en' }); + locale.set('en'); + }); + + it('should mount', () => { + // Arrange + const form = {}; + const i18n = { + fields: { + account: { + label: enMessages.page.login.form.fields.account.label + }, + passphrase: { + label: enMessages.page.login.form.fields.passphrase.label + }, + server: { + label: enMessages.page.login.form.fields.server.label + } + } + }; + const servers = []; + + // Act + const { container } = render(LoginForm, { form, i18n, servers }); + + // Assert + expect(container).toBeTruthy(); + }); + + it('should have a form', () => { + // Arrange + const form = {}; + const i18n = { + fields: { + account: { + label: enMessages.page.login.form.fields.account.label + }, + passphrase: { + label: enMessages.page.login.form.fields.passphrase.label + }, + server: { + label: enMessages.page.login.form.fields.server.label + } + } + }; + const servers = []; + + // Act + render(LoginForm, { form, i18n, servers }); + const formElement = screen.getByRole('form'); + + // Assert + expect(formElement).toBeInTheDocument(); + }); + + it('should have a server select control', () => { + // Arrange + const form = {}; + const i18n = { + fields: { + account: { + label: enMessages.page.login.form.fields.account.label + }, + passphrase: { + label: enMessages.page.login.form.fields.passphrase.label + }, + server: { + label: enMessages.page.login.form.fields.server.label + } + } + }; + const servers = []; + + // Act + render(LoginForm, { form, i18n, servers }); + const server = screen.getByLabelText(enMessages.page.login.form.fields.server.label); + + // Assert + expect(server).toBeInTheDocument(); + }); + + it('should have a account input control', () => { + // Arrange + const form = {}; + const i18n = { + fields: { + account: { + label: enMessages.page.login.form.fields.account.label + }, + passphrase: { + label: enMessages.page.login.form.fields.passphrase.label + }, + server: { + label: enMessages.page.login.form.fields.server.label + } + } + }; + const servers = []; + + // Act + render(LoginForm, { form, i18n, servers }); + const account = screen.getByLabelText(enMessages.page.login.form.fields.account.label); + + // Assert + expect(account).toBeInTheDocument(); + }); + + it('should have a passphrase input control', () => { + // Arrange + const form = {}; + const i18n = { + fields: { + account: { + label: enMessages.page.login.form.fields.account.label + }, + passphrase: { + label: enMessages.page.login.form.fields.passphrase.label + }, + server: { + label: enMessages.page.login.form.fields.server.label + } + } + }; + const servers = []; + + // Act + render(LoginForm, { form, i18n, servers }); + const passphrase = screen.getByLabelText(enMessages.page.login.form.fields.passphrase.label); + + // Assert + expect(passphrase).toBeInTheDocument(); + }); + + it('should have a reset passphrase link', () => { + // Arrange + const form = {}; + const i18n = { + fields: { + account: { + label: enMessages.page.login.form.fields.account.label + }, + passphrase: { + label: enMessages.page.login.form.fields.passphrase.label + }, + server: { + label: enMessages.page.login.form.fields.server.label + } + }, + reset: enMessages.page.login.form.reset + }; + const servers = []; + + // Act + render(LoginForm, { form, i18n, servers }); + const reset = screen.getByRole('link', { name: enMessages.page.login.form.reset }); + + // Assert + expect(reset).toBeInTheDocument(); + }); + + it('should have a submit button', () => { + // Arrange + const form = {}; + const i18n = { + fields: { + account: { + label: enMessages.page.login.form.fields.account.label + }, + passphrase: { + label: enMessages.page.login.form.fields.passphrase.label + }, + server: { + label: enMessages.page.login.form.fields.server.label + } + }, + submit: enMessages.page.login.form.submit + }; + const servers = []; + + // Act + render(LoginForm, { form, i18n, servers }); + const submit = screen.getByRole('button', { name: enMessages.page.login.form.submit }); + + // Assert + expect(submit).toBeInTheDocument(); + }); + + it('should have no validation errors', () => { + // Arrange + const form = {}; + const i18n = { + fields: { + account: { + label: enMessages.page.login.form.fields.account.label + }, + passphrase: { + label: enMessages.page.login.form.fields.passphrase.label + }, + server: { + label: enMessages.page.login.form.fields.server.label + } + } + }; + const servers = []; + + // Act + render(LoginForm, { form, i18n, servers }); + + // Assert + expect( + screen.queryByText(enMessages.page.login.form.validation.incorrect) + ).not.toBeInTheDocument(); + expect( + screen.queryByText(enMessages.page.login.form.validation.missing) + ).not.toBeInTheDocument(); + }); + + describe('when validation failed', () => { + it('should show an error on required inputs', () => { + // Arrange + const form = { + missing: true + }; + const i18n = { + fields: { + account: { + label: enMessages.page.login.form.fields.account.label + }, + passphrase: { + label: enMessages.page.login.form.fields.passphrase.label + }, + server: { + label: enMessages.page.login.form.fields.server.label + } + }, + validation: { + missing: enMessages.page.login.form.validation.missing + } + }; + const servers = []; + + // Act + render(LoginForm, { form, i18n, servers }); + const intro = screen.getByText(enMessages.page.login.form.validation.missing); + + // Assert + expect(intro).toBeInTheDocument(); + }); + + it('should show an error on invalid inputs', () => { + // Arrange + const form = { + incorrect: true + }; + const i18n = { + fields: { + account: { + label: enMessages.page.login.form.fields.account.label + }, + passphrase: { + label: enMessages.page.login.form.fields.passphrase.label + }, + server: { + label: enMessages.page.login.form.fields.server.label + } + }, + validation: { + incorrect: enMessages.page.login.form.validation.incorrect + } + }; + const servers = []; + + // Act + render(LoginForm, { form, i18n, servers }); + const intro = screen.getByText(enMessages.page.login.form.validation.incorrect); + + // Assert + expect(intro).toBeInTheDocument(); + }); + }); +});