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 @@
+
+
+
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')}
-
+
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();
+ });
+ });
+});