Merge branch 'main' into tooltip

This commit is contained in:
Torsten Dittmann
2022-05-14 18:58:22 +02:00
committed by GitHub
36 changed files with 2955 additions and 3005 deletions
+2145 -2952
View File
File diff suppressed because it is too large Load Diff
+16 -15
View File
@@ -23,32 +23,33 @@
"chart.js": "^3.7.1"
},
"devDependencies": {
"@playwright/test": "^1.21.1",
"@sveltejs/adapter-auto": "1.0.0-next.36",
"@sveltejs/kit": "1.0.0-next.318",
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.41",
"@playwright/test": "^1.22.0",
"@sveltejs/adapter-auto": "1.0.0-next.40",
"@sveltejs/kit": "1.0.0-next.328",
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.44",
"@testing-library/dom": "^8.13.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/svelte": "^3.1.1",
"@testing-library/user-event": "^14.1.1",
"@typescript-eslint/eslint-plugin": "^5.20.0",
"@typescript-eslint/parser": "^5.20.0",
"eslint": "^8.13.0",
"@testing-library/user-event": "^14.2.0",
"@typescript-eslint/eslint-plugin": "^5.23.0",
"@typescript-eslint/parser": "^5.23.0",
"eslint": "^8.15.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte3": "^3.4.1",
"jest": "^27.5.1",
"eslint-plugin-svelte3": "^4.0.0",
"jest": "^28.1.0",
"jest-environment-jsdom": "^28.1.0",
"pre-commit": "^1.2.2",
"prettier": "^2.6.2",
"prettier-plugin-svelte": "^2.7.0",
"sass": "^1.50.1",
"svelte": "^3.47.0",
"sass": "^1.51.0",
"svelte": "^3.48.0",
"svelte-check": "^2.7.0",
"svelte-jester": "^2.3.2",
"svelte-preprocess": "^4.10.6",
"ts-jest": "^27.1.4",
"ts-jest": "^28.0.2",
"tslib": "^2.4.0",
"typescript": "^4.6.3",
"vite": "^2.9.5"
"typescript": "^4.6.4",
"vite": "^2.9.9"
},
"type": "module",
"jest": {
+14
View File
@@ -0,0 +1,14 @@
<script lang="ts">
export let size: number;
export let src: string;
export let name: string;
</script>
<img
width={size}
height={size}
class="avatar"
style="--size: {size}px"
{src}
title={name}
alt={name} />
-4
View File
@@ -5,10 +5,6 @@
const copy = async () => {
try {
if (navigator.clipboard === undefined) {
//TODO: add fallback to old methods
throw new Error('Clipboard API only available to SSL');
}
await navigator.clipboard.writeText(value);
addNotification({
message: 'Copied to clipboard.',
+4
View File
@@ -14,3 +14,7 @@ export { default as DropListItem } from './dropListItem.svelte';
export { default as DropListLink } from './dropListLink.svelte';
export { default as Collapsible } from './collapsible.svelte';
export { default as Tooltip } from './tooltip.svelte';
export { default as Avatar } from './avatar.svelte';
export { default as SwitchBox } from './switchBox.svelte';
export { default as SwitchBoxes } from './switchBoxes.svelte';
export { default as InfoSection } from './infoSection.svelte';
+4
View File
@@ -0,0 +1,4 @@
<div>
<i class="icon-info-circled" />
<slot />
</div>
+48
View File
@@ -0,0 +1,48 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
type SwitchBox = {
label: string;
id: string;
src: string;
alt: string;
href: string;
linkText: string;
value: boolean;
required: boolean;
disabled: boolean;
wip: boolean;
};
export let box: SwitchBox;
let { label, id, src, alt, href, linkText, disabled, required, value, wip } = box;
const dispatch = createEventDispatcher();
//TODO: move SwitchBox type outside component
</script>
<li class="card">
<label class="switch-box" for={id}>
<div class="switch-box-image">
<img height="50" width="50" src={src || 'https://via.placeholder.com/50'} {alt} />
</div>
<span class="switch-box-title">{label}</span>
{#if !wip}
<a {href} class="link" target="_blank">
<span class="text">{linkText || 'Docs'} </span>
<span class="icon-link-ext" aria-hidden="true" />
</a>
<input
{id}
{disabled}
{required}
type="checkbox"
class="switch"
role="switch"
bind:checked={value}
on:change={() => dispatch('updated', { value, id })} />
{/if}
</label>
</li>
+13
View File
@@ -0,0 +1,13 @@
<script>
import SwitchBox from './switchBox.svelte';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
export let boxes;
</script>
<ul class="grid-box">
{#each boxes as box}
<SwitchBox {box} on:updated={(e) => dispatch('updated', e.detail)} />
{/each}
</ul>
+12 -9
View File
@@ -9,6 +9,7 @@
export let required = false;
export let disabled = false;
export let autofocus = false;
export let meter = true;
let element: HTMLInputElement;
@@ -33,14 +34,16 @@
class="input-text"
bind:value
bind:this={element} />
<meter
value={strength > 100 ? 100 : strength}
min="0"
max="100"
class="password-meter"
class:is-weak={strength !== 0 && strength <= 33}
class:is-medium={strength > 33 && strength <= 66}
class:is-strong={strength > 66 && strength <= 100}
aria-label="Password strength week" />
{#if meter}
<meter
value={strength > 100 ? 100 : strength}
min="0"
max="100"
class="password-meter"
class:is-weak={strength !== 0 && strength <= 33}
class:is-medium={strength > 33 && strength <= 66}
class:is-strong={strength > 66 && strength <= 100}
aria-label="Password strength weak" />
{/if}
</div>
</FormItem>
+2 -2
View File
@@ -17,7 +17,7 @@
}
});
const onKeyUp = (event: KeyboardEvent) => {
const valueChange = (event: Event) => {
clearTimeout(timer);
timer = setTimeout(() => {
const target = event.target as HTMLInputElement;
@@ -32,5 +32,5 @@
{required}
type="search"
class="input-text"
on:keyup={onKeyUp}
on:input={valueChange}
bind:this={element} />
+1 -1
View File
@@ -15,7 +15,7 @@
<FormItem>
<label class="label" for={id}>{label}</label>
<div class="select">
<select bind:value {required} {disabled}>
<select bind:value {id} {required} {disabled}>
{#each options as option}
<option value={option.value} selected={option.value === value}>
{option.label}
+2
View File
@@ -9,6 +9,7 @@
export let required = false;
export let disabled = false;
export let autofocus = false;
export let autocomplete = true;
export let maxlength: number = null;
let element: HTMLInputElement;
@@ -29,6 +30,7 @@
{disabled}
{required}
{maxlength}
autocomplete={autocomplete ? 'on' : 'off'}
type="text"
class="input-text"
bind:value
+1 -1
View File
@@ -4,7 +4,7 @@
<li class="inline-links-item">
<a class="link" href="https://github.com/appwrite/appwrite" target="_blank">
<span class="icon-github-circled" aria-hidden="true" />
<span class="text">GitHun</span>
<span class="text">GitHub</span>
</a>
</li>
<li class="inline-links-item">
+5 -7
View File
@@ -2,7 +2,7 @@
import { goto } from '$app/navigation';
import { base } from '$app/paths';
import { page } from '$app/stores';
import { DropList, DropListItem, DropListLink } from '$lib/components';
import { DropList, DropListItem, DropListLink, Avatar } from '$lib/components';
import { app } from '$lib/stores/app';
import { sdkForConsole } from '$lib/stores/sdk';
import { user } from '$lib/stores/user';
@@ -46,12 +46,10 @@
<DropList bind:show={showDropdown} position="bottom" horizontal="left">
<button class="transparent-button" on:click={() => (showDropdown = !showDropdown)}>
<span class="is-only-desktop">{$user.name}</span>
<img
width="40"
height="40"
class="user-image"
src={sdkForConsole.avatars.getInitials($user.name, 40, 40).toString()}
alt="" />
<Avatar
size={50}
name={$user.name}
src={sdkForConsole.avatars.getInitials($user.name, 50, 50).toString()} />
</button>
<svelte:fragment slot="list">
<DropListLink href="/console/$me" icon="user">Your Account</DropListLink>
@@ -1,6 +1,5 @@
<script>
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Back } from '$lib/components';
import { Cover } from '$lib/layout';
@@ -22,4 +21,5 @@
<Tabs />
</Cover>
{/if}
<slot />
@@ -36,6 +36,7 @@
<div aria-busy="true" />
{:then response}
{#if response.total}
<p>{response.total} documents found</p>
<Table>
<TableHeader>
{#each columns as column}
@@ -1,6 +1,5 @@
<script>
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Back } from '$lib/components';
import { Cover } from '$lib/layout';
@@ -28,6 +28,7 @@
<div aria-busy="true" />
{:then response}
{#if response.total}
<p>{response.total} functions found</p>
<Tiles>
{#each response.functions as func}
<Tile
@@ -1,8 +1,6 @@
<script>
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Back } from '$lib/components';
import { Cover } from '$lib/layout';
const projectId = $page.params.project;
@@ -1,6 +1,5 @@
<script>
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Back } from '$lib/components';
import { Cover } from '$lib/layout';
@@ -20,5 +19,4 @@
<svelte:fragment slot="title">Settings</svelte:fragment>
<Tabs />
</Cover>
<slot />
@@ -47,6 +47,7 @@
<div aria-busy="true" />
{:then response}
{#if response.total}
<p>{response.total} files found</p>
<Table>
<TableHeader>
<TableCellHead width={30} />
@@ -1,6 +1,5 @@
<script lang="ts">
import { base } from '$app/paths';
import { page } from '$app/stores';
import { Back } from '$lib/components';
import { Cover } from '$lib/layout';
@@ -21,5 +20,4 @@
<Tabs />
</Cover>
{/if}
<slot />
@@ -0,0 +1,50 @@
<script lang="ts">
import { Modal, InfoSection } from '$lib/components';
import { Button, InputNumber, Form } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdkForConsole } from '$lib/stores/sdk';
import { project } from '../store';
export let showUserLimitModal = false;
export let authLimit: number;
const projectId = $project.$id;
let userLimit: number = $project.authLimit;
const update = async () => {
try {
const oauth = await sdkForConsole.projects.updateAuthLimit(projectId, userLimit);
console.log(oauth);
authLimit = userLimit;
showUserLimitModal = false;
addNotification({
type: 'success',
message: 'Updated project users limit successfully'
});
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
}
};
</script>
<Form on:submit={update}>
<Modal bind:show={showUserLimitModal}>
<svelte:fragment slot="header">Max Allowed Users</svelte:fragment>
<InputNumber id="userLimit" label="User Limit" autofocus={true} bind:value={userLimit} />
<InfoSection>
<p>
This limit will prevent new users from signing up for your project, no matter what
auth method has been used. You will still be able to create users and team
memberships from your Appwrite console. For an unlimited amount of users, set the
limit to 0. Max limit is 10,000.
</p>
</InfoSection>
<svelte:fragment slot="footer">
<Button submit>Update</Button>
<Button secondary on:click={() => (showUserLimitModal = false)}>Cancel</Button>
</svelte:fragment>
</Modal>
</Form>
@@ -0,0 +1,56 @@
<script lang="ts">
import { page } from '$app/stores';
import { Modal, Copy, InfoSection } from '$lib/components';
import { Button, InputPassword, InputText, Form } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdkForConsole } from '$lib/stores/sdk';
export let showModal = false;
export let provider: string;
let appId: string, secret: string;
const projectId = $page.params.project;
let redirectURI = `${
sdkForConsole.config.endpoint
}/account/session/oauth2/callback/${provider.toLocaleLowerCase()}/${projectId}`;
const update = async () => {
try {
const oauth = await sdkForConsole.projects.updateOAuth2(
projectId,
provider,
appId,
secret
);
console.log(oauth);
showModal = false;
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
}
};
</script>
<Form on:submit={update}>
<Modal bind:show={showModal}>
<svelte:fragment slot="header">{provider} OAuth2 Settings</svelte:fragment>
<InputText
id="appId"
label="App Id"
autofocus={true}
autocomplete={false}
bind:value={appId} />
<InputPassword id="secret" label="App Secret" meter={false} bind:value={secret} />
<InfoSection>
<p>
To complete set up, add this OAuth2 redirect URI to your {provider} app configuration.
</p>
<Copy bind:value={redirectURI} />
</InfoSection>
<svelte:fragment slot="footer">
<Button submit>Update</Button>
<Button secondary on:click={() => (showModal = false)}>Cancel</Button>
</svelte:fragment>
</Modal>
</Form>
@@ -44,6 +44,7 @@
<div aria-busy="true" />
{:then response}
{#if response.total}
<p>{response.total} users found</p>
<Table>
<TableHeader>
<TableCellHead width={30} />
@@ -1,10 +1,125 @@
<script>
import { Card } from '$lib/components';
<script lang="ts">
import { SwitchBoxes } from '$lib/components';
import { Container } from '$lib/layout';
import Toggle from './_toggleOAuth.svelte';
import SetUserLimit from './_setUserLimit.svelte';
import { addNotification } from '$lib/stores/notifications';
import { sdkForConsole } from '$lib/stores/sdk';
import { project } from '../store';
let showModal = false;
let showUserLimitModal = false;
const projectId = $project.$id;
let provider: string;
let authLimit = $project.authLimit;
const authUpdate = async (event) => {
try {
await sdkForConsole.projects.updateAuthStatus(
projectId,
event.detail.id,
event.detail.value
);
addNotification({
type: 'success',
message: 'Updated project auth status successfully'
});
} catch (error) {
addNotification({
type: 'error',
message: error.message
});
}
};
const providerUpdate = async (event) => {
provider = event.detail.id;
showModal = true;
};
let authBoxes = [
{
label: 'Email/Password',
id: 'email-password',
href: 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateSession',
value: $project.authEmailPassword
},
{
label: 'Magic URL',
id: 'magic-url',
href: 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateMagicURLSession',
value: $project.authUsersAuthMagicURL
},
{
label: 'Anonymous',
id: 'anonymous',
href: 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateAnonymousSession',
value: $project.authAnonymous
},
{
label: 'Invites',
id: 'invites',
href: 'https://appwrite.io/docs/client/teams?sdk=web-default#teamsCreateMembership',
value: $project.authInvites
},
{
label: 'JWT',
id: 'jwt',
href: 'https://appwrite.io/docs/client/account?sdk=web-default#accountCreateJWT',
value: $project.authJWT
},
{
label: 'Phone (soon)',
id: 'phone',
value: false,
wip: true
}
];
let providersBoxes = [
{
label: 'Amazon',
id: 'amazon',
href: 'https://developer.amazon.com/apps-and-games/services-and-apis',
linkText: 'OAuth2 Docs'
},
{
label: 'Apple',
id: 'apple',
href: 'https://developer.amazon.com/apps-and-games/services-and-apis',
linkText: 'OAuth2 Docs'
},
{
label: 'BitBucket',
id: 'bitbucket',
href: 'https://developer.amazon.com/apps-and-games/services-and-apis',
linkText: 'OAuth2 Docs'
}
];
//TODO: move authBoxes and providersBoxes to a store
//TODO: if operation not successful revert switchbox value
</script>
<Container>
<h1>Settings</h1>
<Card />
<p>
{authLimit ? `${authLimit} Users allowed` : 'Unlimited Users '}
<button
on:click={() => {
showUserLimitModal = true;
}}
class=" is-text link"
><span class="text">{authLimit ? 'Change Limit' : 'Set Limit'}</span></button>
</p>
<h2>Settings</h2>
<p>Choose auth methods you wish to use.</p>
<SwitchBoxes boxes={authBoxes} on:updated={authUpdate} />
<h3>OAuth2 Providers</h3>
<SwitchBoxes boxes={providersBoxes} on:updated={providerUpdate} />
</Container>
{#if provider && showModal}
<Toggle {provider} bind:showModal />
{/if}
<SetUserLimit bind:authLimit bind:showUserLimitModal />
@@ -44,6 +44,7 @@
<div aria-busy="true" />
{:then response}
{#if response.total}
<p>{response.total} teams found</p>
<Table>
<TableHeader>
<TableCellHead width={30} />
+50 -1
View File
@@ -1,9 +1,58 @@
import { expect, test } from '@playwright/test';
test('login page has inputs', async ({ page }) => {
/*TODO: Things to test in login:
- presence of forgot password link
- validation message on wrong input
- correct response and redirect after login
- logout works
- back button does not log out user
- forward button does not log in user after logout
- limit to total number of login attempts
-
*/
test('login page has inputs and button', async ({ page }) => {
await page.goto('/login');
const mail = page.locator('id=email');
const pass = page.locator('id=password');
const button = page.locator('button:has-text("Login")');
expect(await mail.isVisible());
expect(await pass.isVisible());
expect(await button.isVisible());
});
test('login page has a working sign up link', async ({ page }) => {
await page.goto('/login');
const signup = page.locator('a[href="/register"]');
expect(await signup.isVisible());
await signup.click();
await page.waitForNavigation();
expect(page.url()).toContain('/register');
expect(await page.locator('Register').isVisible());
});
test('login page inputs are navigable by keyboard', async ({ page }) => {
await page.goto('/login');
const mail = page.locator('id=email');
await mail.focus();
await page.keyboard.type('wrongemail@apppwrite.io');
await page.keyboard.press('Tab');
await page.keyboard.type('password');
await page.keyboard.press('Tab');
await page.keyboard.press('Enter');
expect(await page.locator('.toaster-item').isVisible());
expect(await page.locator('text=Invalid credentials').isVisible());
});
test('login page shows error & response is 401 with wrong inputs', async ({ page }) => {
await page.goto('/login');
await page.fill('id=email', 'wrongemail@apppwrite.io');
await page.fill('id=password', 'wrongpassword');
await page.click('button:has-text("Login")');
page.on('response', (response) => {
expect(response.status()).toBe(401);
});
expect(await page.locator('.toaster-item').isVisible());
expect(await page.locator('text=Invalid credentials').isVisible());
});
+31
View File
@@ -0,0 +1,31 @@
import '@testing-library/jest-dom';
import { render, fireEvent } from '@testing-library/svelte';
import { Copy } from '../../../src/lib/components';
const value = 'This is a test';
test('shows copy component', () => {
const { getByRole } = render(Copy, { value });
const input = document.querySelector('input');
const button = getByRole('button');
expect(input).toBeInTheDocument();
expect(button).toBeInTheDocument();
expect(input).toHaveAttribute('type', 'text');
expect(input).toBeDisabled();
});
test('copy to clipboard function called on click', async () => {
const { getByRole } = render(Copy, { value });
Object.assign(window.navigator, {
clipboard: {
writeText: jest.fn().mockImplementation(() => Promise.resolve())
}
});
const button = getByRole('button');
await fireEvent.click(button);
expect(window.navigator.clipboard.writeText).toHaveBeenCalledWith('This is a test');
});
+44
View File
@@ -0,0 +1,44 @@
import '@testing-library/jest-dom';
import { render, fireEvent } from '@testing-library/svelte';
import { DropList } from '../../../src/lib/components';
const data = {
show: true,
position: 'top',
horizontal: 'right'
};
test('shows drop list component', () => {
render(DropList, { ...data });
const wrapper = document.querySelector('div');
const dropList = document.querySelector('ul');
expect(wrapper).toBeInTheDocument();
expect(dropList).toBeInTheDocument();
});
test('hide drop list on body click', async () => {
render(DropList, { ...data });
const body = document.querySelector('body');
const dropList = document.querySelector('ul');
await fireEvent.click(body);
expect(dropList).not.toBeInTheDocument();
});
test('hide drop list on wrapper click', async () => {
render(DropList, { ...data });
const wrapper = document.querySelector('div');
const dropList = document.querySelector('ul');
await fireEvent.click(wrapper);
expect(dropList).not.toBeInTheDocument();
});
test('drop list visible on list click', async () => {
render(DropList, { ...data });
const dropList = document.querySelector('ul');
await fireEvent.click(dropList);
expect(dropList).toBeInTheDocument();
});
+74
View File
@@ -0,0 +1,74 @@
import '@testing-library/jest-dom';
import { render, fireEvent } from '@testing-library/svelte';
import { SwitchBox } from '../../../src/lib/components';
const box = {
id: 'input',
src: 'https://via.placeholder.com/50',
label: 'Bool',
alt: 'image',
required: false,
disabled: false,
value: false
};
test('shows boolean input', () => {
const { getByText, getByRole, getByAltText } = render(SwitchBox, {
box
});
const checkbox = getByRole('switch');
const img = getByAltText('image');
expect(getByText('Bool')).toBeInTheDocument();
expect(img).toBeInTheDocument();
expect(checkbox).toBeInTheDocument();
expect(checkbox).toHaveAttribute('type', 'checkbox');
});
test('shows boolean input - required', () => {
box.required = true;
const { getByRole } = render(SwitchBox, { box });
expect(getByRole('switch')).toBeRequired();
});
test('shows boolean input - disabled', () => {
box.disabled = true;
const { getByRole } = render(SwitchBox, { box });
expect(getByRole('switch')).toBeDisabled();
});
test('state', async () => {
const { getByRole, component } = render(SwitchBox, { box });
const checkbox = getByRole('switch');
setTimeout(() => {
expect(checkbox).not.toBeChecked();
expect(component.box.value).toStrictEqual(false);
}, 1);
await fireEvent.click(checkbox);
setTimeout(() => {
expect(checkbox).toBeChecked();
expect(component.box.value).toStrictEqual(true);
}, 1);
await fireEvent.click(checkbox);
setTimeout(() => {
expect(checkbox).not.toBeChecked();
expect(component.box.value).toStrictEqual(false);
}, 1);
component.box.value = true;
setTimeout(() => {
expect(checkbox).toBeChecked();
expect(component.box.value).toStrictEqual(true);
}, 1);
component.box.value = false;
setTimeout(() => {
expect(checkbox).not.toBeChecked();
expect(component.box.value).toStrictEqual(false);
}, 1);
});
+65
View File
@@ -0,0 +1,65 @@
import '@testing-library/jest-dom';
import { render, fireEvent } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import { InputCheckbox } from '../../../src/lib/elements/forms';
test('shows checkbox input', () => {
const { getByText, getByLabelText } = render(InputCheckbox, { id: 'input', label: 'checkbox' });
const checkbox = getByLabelText('checkbox');
expect(getByText('checkbox')).toBeInTheDocument();
expect(checkbox).toBeInTheDocument();
});
test('shows checkbox input - required', () => {
const { getByLabelText } = render(InputCheckbox, {
id: 'input',
label: 'checkbox',
required: true
});
expect(getByLabelText('checkbox')).toBeRequired();
});
test('shows checkbox input - disabled', () => {
const { getByLabelText } = render(InputCheckbox, {
id: 'input',
label: 'checkbox',
disabled: true
});
expect(getByLabelText('checkbox')).toBeDisabled();
});
test('state', async () => {
const { component, getByLabelText } = render(InputCheckbox, { id: 'input', label: 'checkbox' });
const checkbox = getByLabelText('checkbox');
expect(component.value).toEqual(false);
await userEvent.click(checkbox);
expect(component.value).toEqual(true);
});
test('state', async () => {
const { getByLabelText, component } = render(InputCheckbox, { id: 'input', label: 'checkbox' });
const checkbox = getByLabelText('checkbox');
expect(checkbox).not.toBeChecked();
expect(component.value).toStrictEqual(false);
await fireEvent.click(checkbox);
expect(checkbox).toBeChecked();
expect(component.value).toStrictEqual(true);
await fireEvent.click(checkbox);
expect(checkbox).not.toBeChecked();
expect(component.value).toStrictEqual(false);
component.value = true;
expect(checkbox).toBeChecked();
expect(component.value).toStrictEqual(true);
component.value = false;
expect(checkbox).not.toBeChecked();
expect(component.value).toStrictEqual(false);
});
+53
View File
@@ -0,0 +1,53 @@
import '@testing-library/jest-dom';
import { render, fireEvent } from '@testing-library/svelte';
import { InputRadio } from '../../../src/lib/elements/forms';
import radioGroup from './radioGroup.test.svelte';
const data = {
id: 'radio',
label: 'radio',
group: 'radio',
value: 'radio',
name: 'radio'
};
test('shows label', () => {
const { getByText } = render(InputRadio, { ...data });
expect(getByText('radio')).toBeInTheDocument();
});
test('shows boolean input - required', () => {
const { getByRole } = render(InputRadio, { ...data, required: true });
expect(getByRole('radio')).toBeRequired();
});
test('shows boolean input - disabled', () => {
const { getByRole } = render(InputRadio, { ...data, disabled: true });
expect(getByRole('radio')).toBeDisabled();
});
test('state', async () => {
const { getByLabelText } = render(radioGroup);
const one = getByLabelText('one');
const two = getByLabelText('two');
const three = getByLabelText('three');
await fireEvent.click(one);
expect(one).toBeChecked();
expect(two).not.toBeChecked();
expect(three).not.toBeChecked();
await fireEvent.click(two);
expect(one).not.toBeChecked();
expect(two).toBeChecked();
expect(three).not.toBeChecked();
await fireEvent.click(three);
expect(one).not.toBeChecked();
expect(two).not.toBeChecked();
expect(three).toBeChecked();
});
+57
View File
@@ -0,0 +1,57 @@
import '@testing-library/jest-dom';
import { render } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import { InputSearch } from '../../../src/lib/elements/forms';
test('shows search input', () => {
const { getByPlaceholderText } = render(InputSearch, { placeholder: 'search' });
expect(getByPlaceholderText('search')).toBeInTheDocument();
expect(getByPlaceholderText('search')).toHaveAttribute('type', 'search');
});
test('shows input search - required', () => {
const { getByPlaceholderText } = render(InputSearch, { placeholder: 'search', required: true });
expect(getByPlaceholderText('search')).toBeRequired();
});
test('shows input search - disabled', () => {
const { getByPlaceholderText } = render(InputSearch, { placeholder: 'search', disabled: true });
expect(getByPlaceholderText('search')).toBeDisabled();
});
test('shows input search - autofocus', () => {
const { getByPlaceholderText } = render(InputSearch, {
placeholder: 'search',
autofocus: true
});
expect(getByPlaceholderText('search')).toHaveFocus();
});
test('shows input search - placeholder', () => {
const { getByPlaceholderText } = render(InputSearch, {
placeholder: 'find me'
});
expect(getByPlaceholderText('find me')).toBeInTheDocument();
});
test('state change after debounce', async () => {
const debounce = 250;
const { component, getByPlaceholderText } = render(InputSearch, {
placeholder: 'search',
value: '',
debounce
});
const input = getByPlaceholderText('search');
expect(component.value).toEqual('');
await userEvent.type(input, 'lorem');
expect(component.value).not.toEqual('lorem');
setTimeout(() => {
expect(component.value).toEqual('lorem');
}, debounce);
});
+75
View File
@@ -0,0 +1,75 @@
import '@testing-library/jest-dom';
import { render } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import { InputSelect } from '../../../src/lib/elements/forms';
const options = [
{ value: '', label: 'None' },
{ value: '1', label: 'One' },
{ value: '2', label: 'Two' },
{ value: '3', label: 'Three' }
];
test('shows select input', () => {
const { getByText, getByLabelText } = render(InputSelect, {
id: 'select',
label: 'select',
options
});
const select = getByLabelText('select');
expect(getByText('select')).toBeInTheDocument();
expect(select).toBeInTheDocument();
});
test('shows options', () => {
const { getByText } = render(InputSelect, {
id: 'select',
label: 'select',
options
});
options.forEach((option) => {
expect(getByText(option.label)).toBeInTheDocument();
});
});
test('shows select input - required', () => {
const { getByLabelText } = render(InputSelect, {
id: 'select',
label: 'select',
options,
required: true
});
expect(getByLabelText('select')).toBeRequired();
});
test('shows select input - disabled', () => {
const { getByLabelText } = render(InputSelect, {
id: 'select',
label: 'select',
options,
disabled: true
});
expect(getByLabelText('select')).toBeDisabled();
});
test('state', async () => {
const { component, getByLabelText } = render(InputSelect, {
id: 'select',
label: 'select',
options,
value: ''
});
const select = getByLabelText('select');
expect(component.value).toEqual('');
await userEvent.selectOptions(select, '1');
expect(component.value).toEqual(options[1].value);
await userEvent.selectOptions(select, '2');
expect(component.value).toEqual(options[2].value);
});
@@ -0,0 +1,7 @@
<script>
import { InputRadio } from '../../../src/lib/elements/forms';
</script>
<InputRadio label="one" id="one" group="radio" value="1" name="radio" />
<InputRadio label="two" id="two" group="radio" value="2" name="radio" />
<InputRadio label="three" id="three" group="radio" value="3" name="radio" />