diff --git a/src/lib/components/pages/Login.svelte b/src/lib/components/pages/Login.svelte new file mode 100644 index 0000000..e21eaac --- /dev/null +++ b/src/lib/components/pages/Login.svelte @@ -0,0 +1,10 @@ + + + diff --git a/src/lib/components/templates/Login.svelte b/src/lib/components/templates/Login.svelte new file mode 100644 index 0000000..d60b436 --- /dev/null +++ b/src/lib/components/templates/Login.svelte @@ -0,0 +1,47 @@ + + +
+
+

{$_('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 f387107..a8d0451 100644 --- a/src/lib/i18n/locales/de.json +++ b/src/lib/i18n/locales/de.json @@ -1,60 +1,83 @@ { - "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." - }, - "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": { + "validation": { + "incorrect": "", + "missing": "" + }, + "fields": { + "account": { + "label": "" + }, + "passphrase": { + "label": "" + }, + "server": { + "label": "" + } + }, + "reset": "", + "submit": "" + }, + "heading": "", + "intro": "" + }, + "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 2a00138..9477cdc 100644 --- a/src/lib/i18n/locales/en.json +++ b/src/lib/i18n/locales/en.json @@ -34,6 +34,29 @@ "heading": "Create project", "intro": "Add a new F2 project to Anvil." }, + "login": { + "form": { + "validation": { + "incorrect": "Account or password wrong.", + "missing": "The account field is required." + }, + "fields": { + "account": { + "label": "Account name" + }, + "passphrase": { + "label": "Passphrase" + }, + "server": { + "label": "F2 server" + } + }, + "reset": "Reset passphrase", + "submit": "Log in" + }, + "heading": "Log in", + "intro": "To use Anvil with your F2 account, fill in your credentials." + }, "profile": { "activities": { "block_or_report": "{blockElementOpen}block{blockElementClose} or {reportElementOpen}report{reportElementClose}", diff --git a/src/lib/i18n/locales/he.json b/src/lib/i18n/locales/he.json index af7d16e..e7cc773 100644 --- a/src/lib/i18n/locales/he.json +++ b/src/lib/i18n/locales/he.json @@ -34,6 +34,29 @@ "heading": "", "intro": "" }, + "login": { + "form": { + "validation": { + "incorrect": "", + "missing": "" + }, + "fields": { + "account": { + "label": "" + }, + "passphrase": { + "label": "" + }, + "server": { + "label": "" + } + }, + "reset": "", + "submit": "" + }, + "heading": "", + "intro": "" + }, "profile": { "activities": { "block_or_report": "", diff --git a/src/routes/account/login/+page.svelte b/src/routes/account/login/+page.svelte index 082e6aa..f332ffd 100644 --- a/src/routes/account/login/+page.svelte +++ b/src/routes/account/login/+page.svelte @@ -1,35 +1,8 @@ -
-
-

Log in

-

To use Anvil with your F2 account, fill in your credentials.

-
- {#if form?.missing}

The account field is required.

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

Account or password wrong.

{/if} - - - - - -
-
-
+ diff --git a/tests/components/pages/Login.test.ts b/tests/components/pages/Login.test.ts new file mode 100644 index 0000000..0604aa4 --- /dev/null +++ b/tests/components/pages/Login.test.ts @@ -0,0 +1,168 @@ +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/svelte'; +import { init, locale, register } from 'svelte-i18n'; + +import Login from '../../../src/lib/components/pages/Login.svelte'; +import enMessages from '../../../src/lib/i18n/locales/en.json'; + +describe('Login.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 = {}; + + // Act + const { container } = render(Login, { form }); + + // Assert + expect(container).toBeTruthy(); + }); + + it('should have a h1', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + const h1 = screen.getByRole('heading', { level: 1 }); + + // Assert + expect(h1).toBeInTheDocument(); + expect(h1).toHaveTextContent(enMessages.page.login.heading); + }); + + it('should have an intro', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + const intro = screen.getByText(enMessages.page.login.intro); + + // Assert + expect(intro).toBeInTheDocument(); + }); + + it('should have a form', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + const formElement = screen.getByRole('form'); + + // Assert + expect(formElement).toBeInTheDocument(); + }); + + it('should have a server select control', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + 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 = {}; + + // Act + render(Login, { form }); + 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 = {}; + + // Act + render(Login, { form }); + 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 = {}; + + // Act + render(Login, { form }); + const reset = screen.getByRole('link', { name: enMessages.page.login.form.reset }); + + // Assert + expect(reset).toBeInTheDocument(); + }); + + it('should have a submit button', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + const submit = screen.getByRole('button', { name: enMessages.page.login.form.submit }); + + // Assert + expect(submit).toBeInTheDocument(); + }); + + it('should have no validation errors', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + + // 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 + }; + + // Act + render(Login, { form }); + 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 + }; + + // Act + render(Login, { form }); + const intro = screen.getByText(enMessages.page.login.form.validation.incorrect); + + // Assert + expect(intro).toBeInTheDocument(); + }); + }); +}); diff --git a/tests/components/templates/Login.test.ts b/tests/components/templates/Login.test.ts new file mode 100644 index 0000000..d79cde6 --- /dev/null +++ b/tests/components/templates/Login.test.ts @@ -0,0 +1,168 @@ +import '@testing-library/jest-dom'; +import { render, screen } from '@testing-library/svelte'; +import { init, locale, register } from 'svelte-i18n'; + +import Login from '../../../src/lib/components/templates/Login.svelte'; +import enMessages from '../../../src/lib/i18n/locales/en.json'; + +describe('Login.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 = {}; + + // Act + const { container } = render(Login, { form }); + + // Assert + expect(container).toBeTruthy(); + }); + + it('should have a h1', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + const h1 = screen.getByRole('heading', { level: 1 }); + + // Assert + expect(h1).toBeInTheDocument(); + expect(h1).toHaveTextContent(enMessages.page.login.heading); + }); + + it('should have an intro', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + const intro = screen.getByText(enMessages.page.login.intro); + + // Assert + expect(intro).toBeInTheDocument(); + }); + + it('should have a form', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + const formElement = screen.getByRole('form'); + + // Assert + expect(formElement).toBeInTheDocument(); + }); + + it('should have a server select control', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + 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 = {}; + + // Act + render(Login, { form }); + 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 = {}; + + // Act + render(Login, { form }); + 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 = {}; + + // Act + render(Login, { form }); + const reset = screen.getByRole('link', { name: enMessages.page.login.form.reset }); + + // Assert + expect(reset).toBeInTheDocument(); + }); + + it('should have a submit button', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + const submit = screen.getByRole('button', { name: enMessages.page.login.form.submit }); + + // Assert + expect(submit).toBeInTheDocument(); + }); + + it('should have no validation errors', () => { + // Arrange + const form = {}; + + // Act + render(Login, { form }); + + // 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 + }; + + // Act + render(Login, { form }); + 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 + }; + + // Act + render(Login, { form }); + const intro = screen.getByText(enMessages.page.login.form.validation.incorrect); + + // Assert + expect(intro).toBeInTheDocument(); + }); + }); +});