refactor: break out routes into components

For now I'm following the Atomic Design philosophy with respect to
organising the components. I started with Avatar which wraps the
SvelteKit library with some defaults. I test it using vitest which is
now configured accordingly. All dependencies were updated to their
latest version.

Signed-off-by: André Jaenisch <andre.jaenisch@posteo.de>
This commit is contained in:
André Jaenisch 2024-02-09 11:19:08 +01:00
parent 9bb86e84b1
commit 9c4c8f04f4
No known key found for this signature in database
GPG key ID: 5A668E771F1ED854
11 changed files with 2020 additions and 225 deletions

1
.gitignore vendored
View file

@ -6,5 +6,6 @@ node_modules
.env .env
.env.* .env.*
!.env.example !.env.example
coverage
vite.config.js.timestamp-* vite.config.js.timestamp-*
vite.config.ts.timestamp-* vite.config.ts.timestamp-*

2074
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -10,36 +10,41 @@
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test:unit": "vitest", "test:unit": "vitest",
"test:unit:coverage": "vitest --coverage",
"lint": "prettier --check . && eslint .", "lint": "prettier --check . && eslint .",
"format": "prettier --write ." "format": "prettier --write ."
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "1.40.1", "@playwright/test": "1.41.2",
"@skeletonlabs/skeleton": "2.6.1", "@skeletonlabs/skeleton": "2.8.0",
"@skeletonlabs/tw-plugin": "0.3.0", "@skeletonlabs/tw-plugin": "0.3.1",
"@sveltejs/adapter-auto": "3.0.1", "@sveltejs/adapter-auto": "3.1.1",
"@sveltejs/kit": "2.0.4", "@sveltejs/kit": "2.5.0",
"@sveltejs/vite-plugin-svelte": "3.0.1", "@sveltejs/vite-plugin-svelte": "3.0.2",
"@tailwindcss/forms": "0.5.7", "@tailwindcss/forms": "0.5.7",
"@tailwindcss/typography": "0.5.10", "@tailwindcss/typography": "0.5.10",
"@types/node": "20.10.5", "@testing-library/jest-dom": "6.4.2",
"@typescript-eslint/eslint-plugin": "6.15.0", "@testing-library/svelte": "4.1.0",
"@typescript-eslint/parser": "6.15.0", "@types/node": "20.11.17",
"autoprefixer": "10.4.16", "@typescript-eslint/eslint-plugin": "6.21.0",
"@typescript-eslint/parser": "6.21.0",
"@vitest/coverage-v8": "1.2.2",
"autoprefixer": "10.4.17",
"eslint": "8.56.0", "eslint": "8.56.0",
"eslint-config-prettier": "9.1.0", "eslint-config-prettier": "9.1.0",
"eslint-plugin-svelte": "2.35.1", "eslint-plugin-svelte": "2.35.1",
"postcss": "8.4.32", "jsdom": "24.0.0",
"prettier": "3.1.1", "postcss": "8.4.35",
"prettier": "3.2.5",
"prettier-plugin-svelte": "3.1.2", "prettier-plugin-svelte": "3.1.2",
"svelte": "4.2.8", "svelte": "4.2.10",
"svelte-check": "3.6.2", "svelte-check": "3.6.4",
"svelte-octicons": "18.6.0", "svelte-octicons": "18.7.1",
"tailwindcss": "3.4.0", "tailwindcss": "3.4.1",
"tslib": "2.6.2", "tslib": "2.6.2",
"typescript": "5.3.3", "typescript": "5.3.3",
"vite": "5.0.10", "vite": "5.1.1",
"vitest": "1.1.0" "vitest": "1.2.2"
}, },
"type": "module" "type": "module"
} }

View file

@ -1,7 +0,0 @@
import { describe, it, expect } from 'vitest';
describe('sum test', () => {
it('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});
});

View file

@ -0,0 +1,21 @@
<script lang="ts">
import { Avatar } from '@skeletonlabs/skeleton';
/**
* URL to Avatar image.
*/
export let avatar: string | undefined = '';
/**
* Name to derive initials from if no avatar is given.
*/
export let displayName: string | undefined = '';
</script>
<Avatar
src={avatar}
alt={displayName}
width="w-32"
rounded="rounded-full"
initials={displayName}
/>

View file

@ -19,8 +19,8 @@ export const actions = {
} }
try { try {
// TODO: Consumre response? // TODO: Consume response?
await requests.post('/login', { account, passphrase }); await requests.post('/login', { account, passphrase, server });
} catch (exc) { } catch (exc) {
console.error(exc); console.error(exc);
return fail(400, { account, incorrect: true }); return fail(400, { account, incorrect: true });

View file

@ -1,5 +1,5 @@
<script> <script>
import { Avatar } from '@skeletonlabs/skeleton'; import Avatar from '$lib/components/atoms/Avatar.svelte';
/** @type {import('./$types').PageData} */ /** @type {import('./$types').PageData} */
export let data; export let data;
@ -9,10 +9,8 @@
<!-- Profile header --> <!-- Profile header -->
<div> <div>
<Avatar <Avatar
src={data.user.avatar} avatar={data.user.avatar}
width="w-32" displayName={data.user.display_name}
rounded="rounded-full"
initials={data.user.display_name}
/> />
</div> </div>
<!-- Board --> <!-- Board -->

View file

@ -0,0 +1,60 @@
import '@testing-library/jest-dom'
import { render, screen } from '@testing-library/svelte'
import Avatar from '../../../src/lib/components/atoms/Avatar.svelte'
describe('Avatar.svelte', () => {
it('should mount', () => {
// Arrange
// Nothing to prepare
// Act
const { container } = render(Avatar);
// Assert
expect(container).toBeTruthy()
})
it('should pick up the passed image', () => {
// Arrange
const avatar = 'https://example.com/avatar.png'
// Act
render(Avatar, { avatar })
const figure = screen.getByTestId('avatar')
const image = figure.querySelector('img')
// Assert
expect(figure).toBeInTheDocument()
expect(image).not.toBeNull()
expect(image).toHaveAttribute('src', avatar)
})
it('should use an image with alt text', () => {
// Arrange
const avatar = 'https://example.com/avatar.png'
const displayName = 'Jane Doe'
// Act
render(Avatar, { avatar, displayName })
const figure = screen.getByTestId('avatar')
// Assert
expect(figure).toBeInTheDocument()
expect(screen.getByAltText(displayName)).toBeDefined()
})
it('should use the initials as text', () => {
// Arrange
const displayName = 'Jane Doe'
const initials = displayName.substring(0, 2).toUpperCase()
// Act
render(Avatar, { displayName })
const figure = screen.getByTestId('avatar')
// Assert
expect(figure).toBeInTheDocument()
expect(screen.getByText(initials)).toBeDefined()
})
})

View file

@ -8,10 +8,7 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"skipLibCheck": true, "skipLibCheck": true,
"sourceMap": true, "sourceMap": true,
"strict": true "strict": true,
"types": ["vitest/globals"]
} }
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
} }

View file

@ -2,8 +2,5 @@ import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config'; import { defineConfig } from 'vitest/config';
export default defineConfig({ export default defineConfig({
plugins: [sveltekit()], plugins: [sveltekit()]
test: {
include: ['src/**/*.{test,spec}.{js,ts}']
}
}); });

17
vitest.config.ts Normal file
View file

@ -0,0 +1,17 @@
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [svelte({ hot: !process.env.VITEST })],
test: {
coverage: {
include: [
'src/**/*.svelte'
]
},
include: ['tests/**/*.test.ts'],
environment: 'jsdom',
globals: true
}
});