diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts
index 96b29d3a2..9245131ed 100644
--- a/src/lib/actions/analytics.ts
+++ b/src/lib/actions/analytics.ts
@@ -141,6 +141,8 @@ export enum Submit {
TeamUpdateName = 'submit_team_update_name',
AuthLimitUpdate = 'submit_auth_limit_update',
AuthStatusUpdate = 'submit_auth_status_update',
+ AuthPasswordHistoryUpdate = 'submit_auth_password_history_limit_update',
+ AuthPasswordDictionaryUpdate = 'submit_auth_password_dictionary_update',
SessionsLengthUpdate = 'submit_sessions_length_update',
SessionsLimitUpdate = 'submit_sessions_limit_update',
SessionDelete = 'submit_session_delete',
diff --git a/src/lib/elements/forms/inputDomain.svelte b/src/lib/elements/forms/inputDomain.svelte
index b261756a8..58f066199 100644
--- a/src/lib/elements/forms/inputDomain.svelte
+++ b/src/lib/elements/forms/inputDomain.svelte
@@ -15,7 +15,7 @@
export let maxlength: number = null;
// https://www.geeksforgeeks.org/how-to-validate-a-domain-name-using-regular-expression/
- const pattern = String.raw`^(?!-)[A-Za-z0-9-]+([\-\.]{1}[a-z0-9]+)*\.[A-Za-z]{2,6}$`;
+ const pattern = String.raw`^(?!-)[A-Za-z0-9-]+([\-\.]{1}[a-z0-9]+)*\.[A-Za-z]{2,18}$`;
let element: HTMLInputElement;
let error: string;
diff --git a/src/lib/layout/navigation.svelte b/src/lib/layout/navigation.svelte
index bd2a9fbae..15790203b 100644
--- a/src/lib/layout/navigation.svelte
+++ b/src/lib/layout/navigation.svelte
@@ -163,7 +163,7 @@
{#if subNavigation}
-
+
diff --git a/src/lib/layout/shell.svelte b/src/lib/layout/shell.svelte
index 2e5e7819e..f4f5fd0cf 100644
--- a/src/lib/layout/shell.svelte
+++ b/src/lib/layout/shell.svelte
@@ -1,17 +1,17 @@
+
+
diff --git a/src/routes/console/project-[project]/auth/security/updatePasswordHistory.svelte b/src/routes/console/project-[project]/auth/security/updatePasswordHistory.svelte
new file mode 100644
index 000000000..e4c48767d
--- /dev/null
+++ b/src/routes/console/project-[project]/auth/security/updatePasswordHistory.svelte
@@ -0,0 +1,76 @@
+
+
+
diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/subNavigation.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/subNavigation.svelte
index efb46a2e4..706d1177e 100644
--- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/subNavigation.svelte
+++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/subNavigation.svelte
@@ -17,7 +17,7 @@
+ href={`${base}/console/project-${project}/databases/database-${databaseId}?openNavbar=true`}>
Collections
diff --git a/tests/unit/elements/inputDomain.test.ts b/tests/unit/elements/inputDomain.test.ts
new file mode 100644
index 000000000..488c03ad5
--- /dev/null
+++ b/tests/unit/elements/inputDomain.test.ts
@@ -0,0 +1,116 @@
+import '@testing-library/jest-dom';
+import { render } from '@testing-library/svelte';
+import userEvent from '@testing-library/user-event';
+import { InputDomain } from '../../../src/lib/elements/forms';
+
+const validStrings = ['example.com', 'i-need.travelersinsurance', 'sub.domain.com'];
+const invalidStrings = ['not-a-domain', 'tld-too-short.a', 'tld-too-long.overeighteencharacters'];
+
+test('shows domain input', () => {
+ const { getByText, getByLabelText } = render(InputDomain, { id: 'input', label: 'input' });
+ const input = getByLabelText('input');
+
+ expect(getByText('input')).toBeInTheDocument();
+ expect(input).toBeInTheDocument();
+ expect(input).toHaveAttribute('type', 'text');
+});
+
+test('shows domain input - required', () => {
+ const { getByLabelText } = render(InputDomain, { id: 'input', label: 'input', required: true });
+
+ expect(getByLabelText('input')).toBeRequired();
+});
+
+test('shows domain input - disabled', () => {
+ const { getByLabelText } = render(InputDomain, { id: 'input', label: 'input', disabled: true });
+
+ expect(getByLabelText('input')).toBeDisabled();
+});
+
+test('shows domain input - autofocus', () => {
+ const { getByLabelText } = render(InputDomain, {
+ id: 'input',
+ label: 'input',
+ autofocus: true
+ });
+
+ expect(getByLabelText('input')).toHaveFocus();
+});
+
+test('shows domain input - placeholder', () => {
+ const { getByPlaceholderText } = render(InputDomain, {
+ id: 'input',
+ label: 'input',
+ placeholder: 'find me'
+ });
+
+ expect(getByPlaceholderText('find me')).toBeInTheDocument();
+});
+
+test('shows domain input - hide label', () => {
+ render(InputDomain, {
+ id: 'input',
+ label: 'label',
+ showLabel: false
+ });
+
+ const label = document.querySelector('label');
+ expect(label).toHaveClass('u-hide');
+});
+
+test('shows domain input - autocomplete', () => {
+ const { getByLabelText } = render(InputDomain, {
+ id: 'input',
+ label: 'input',
+ autocomplete: true
+ });
+
+ expect(getByLabelText('input')).toHaveAttribute('autocomplete', 'on');
+});
+
+test('shows domain input - maxlength', () => {
+ const { getByLabelText } = render(InputDomain, {
+ id: 'input',
+ label: 'input',
+ maxlength: 2
+ });
+
+ expect(getByLabelText('input')).toHaveAttribute('maxlength', '2');
+});
+
+test('state', async () => {
+ const { component, getByLabelText } = render(InputDomain, {
+ id: 'input',
+ label: 'input',
+ value: ''
+ });
+ const input = getByLabelText('input');
+
+ expect(component.value).toEqual('');
+ await userEvent.type(input, 'lorem');
+ expect(component.value).toEqual('lorem');
+});
+
+validStrings.forEach((validString) => {
+ test(`validates ${validString} as valid`, () => {
+ const { getByLabelText } = render(InputDomain, {
+ id: 'input',
+ label: 'input',
+ value: validString
+ });
+ const input = getByLabelText('input') as HTMLInputElement;
+ expect(input.checkValidity()).toBe(true);
+ });
+});
+
+invalidStrings.forEach((invalidString) => {
+ test(`validates ${invalidString} as invalid`, () => {
+ const { getByLabelText } = render(InputDomain, {
+ id: 'input',
+ label: 'input',
+ value: invalidString
+ });
+ const input = getByLabelText('input') as HTMLInputElement;
+ expect(input.checkValidity()).toBe(false);
+ });
+});