test work in progress

This commit is contained in:
2024-03-12 20:58:13 +01:00
parent 4b298fa332
commit 6dfd99dc88
11 changed files with 865 additions and 61 deletions

View File

@@ -11,7 +11,9 @@
"build:sw": "workbox generateSW workbox-config.js"
},
"dependencies": {
"@faker-js/faker": "^8.4.1",
"@vee-validate/zod": "^4.12.5",
"@vue/test-utils": "^2.4.4",
"@vueuse/core": "^10.9.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
@@ -30,12 +32,15 @@
"devDependencies": {
"@types/node": "^20.11.24",
"@vitejs/plugin-vue": "^5.0.4",
"@vitest/ui": "^1.3.1",
"autoprefixer": "^10.4.18",
"jsdom": "^24.0.0",
"shadcn-vue": "^0.9.0",
"tailwindcss": "^3.4.1",
"typescript": "^5.2.2",
"vite": "^5.1.4",
"vite-plugin-pwa": "^0.19.0",
"vitest": "^1.3.1",
"vue-tsc": "^1.8.27"
}
}

714
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
import { faker } from "@faker-js/faker";
import { describe, expect, it } from "vitest";
import { mount } from '@vue/test-utils'
import EntryForm from './EntryForm.vue'
describe('Entry Form tests', () => {
it('takes in values for name and title and displays them in the respected fields', () => {
const name = faker.word.noun()
const text = faker.lorem.paragraph()
const component = mount(EntryForm, {
props: {
name, text, action: 'edit'
}
})
const nameField = component.find('input')
const textField = component.find('textarea')
console.log(nameField.element.classList, textField.text())
})
})

View File

@@ -1,14 +1,13 @@
<script setup lang="ts">
import * as z from 'zod'
import { toTypedSchema } from '@vee-validate/zod'
import { Input } from '@/components/ui/input';
import { Form, FormItem, FormLabel, FormField, FormControl, FormMessage, FormDescription } from '@/components/ui/form';
import { Button } from './ui/button';
import { Textarea } from '@/components/ui/textarea';
import { Entry } from '@/data/entries';
import { ref } from 'vue';
const emit = defineEmits<{
submit: [value: CreateEntrySchema]
submit: [value: CreateEntrySchema]
}>()
const props = defineProps<{
@@ -16,41 +15,38 @@ const props = defineProps<{
inputEntry?: Entry | undefined
}>()
const nameField = ref(props.inputEntry ? props.inputEntry.name : '')
const textField = ref(props.inputEntry ? props.inputEntry.text : '')
const createEntryZodSchema = z.object({
const createEntrySchema = z.object({
name: z.string(),
text: z.string().optional()
})
export type CreateEntrySchema = z.infer<typeof createEntryZodSchema>
export type CreateEntrySchema = z.infer<typeof createEntrySchema>
function submit() {
const result = createEntrySchema.safeParse({
name: nameField.value,
text: textField.value
})
if (result.success) {
emit('submit', result.data)
}
}
const createEntrySchema = toTypedSchema(createEntryZodSchema)
</script>
<template>
<Form :validation-schema="createEntrySchema" @submit="(val) => emit('submit', val as CreateEntrySchema)">
<FormField v-slot="{ componentField }" name="name">
<FormItem>
<FormControl>
<Input placeholder="Name" v-bind="componentField" :model-value="inputEntry ? inputEntry.name : ''" />
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<FormField v-slot="{ componentField }" name="text">
<FormItem>
<FormLabel></FormLabel>
<FormControl>
<Textarea placeholder="Text" v-bind="componentField" :model-value="inputEntry ? inputEntry.text : ''" />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
</FormField>
<Button type="submit" class="w-full" v-if="props.action === 'create'">Create</Button>
<Button type="submit" class="w-full" v-else>Edit</Button>
</Form>
<form class="flex gap-3 flex-col">
<Input placeholder="Name" v-model:model-value="nameField" />
<Textarea placeholder="Text" v-model:model-value="textField" />
<Button class="w-full" v-if="props.action === 'create'" @click="submit()">Create</Button>
<Button class="w-full" v-else @click="submit()">Edit</Button>
</form>
</template>
<style scoped></style>

56
src/data/entries.test.ts Normal file
View File

@@ -0,0 +1,56 @@
import { expect, it } from "vitest"
import { describe } from "vitest"
import { faker } from '@faker-js/faker'
import { parseFromPossibleString } from "./entries"
import moment from "moment"
describe('function for managing entries data entity', () => {
function generateList<E>(length: number, generate: () => E) {
const arr: Array<E> = []
for (let i = 0; i < length; i++) {
arr.push(generate());
}
return arr;
}
function generateListWithWrongDate() {
return generateList(10, () => <any>{
name: faker.word.noun(),
text: faker.lorem.paragraph(),
last_reset: faker.word.sample()
})
}
function generateListWithNoName() {
return generateList(10, () => <any>{
text: faker.lorem.paragraph(),
last_reset: moment()
})
}
function generateListWithCorrectData() {
return generateList(10, () => <any>{
name: faker.word.noun(),
text: faker.lorem.paragraph(),
last_reset: moment()
})
}
it('fails to parse localstorage string because of wrong date', () => {
const data = generateListWithWrongDate()
expect(parseFromPossibleString(JSON.stringify(data)).length).toBe(0)
})
it('fails to parse localstorage string because no name was parsed', () => {
const data = generateListWithNoName()
expect(parseFromPossibleString(JSON.stringify(data)).length).toBe(0)
})
it('succeeds to parse localstorage string witht he correct data', () => {
const data = generateListWithCorrectData()
expect(parseFromPossibleString(JSON.stringify(data)).length).toBe(10)
})
})

View File

@@ -1,39 +1,46 @@
import moment, { Moment } from "moment";
import { Ref, ref } from "vue";
const localStorageKey = 'entries'
export const localStorageKey = 'entries'
export function getDifferenceToToday(date: Moment) {
return Math.abs(date.diff(moment(), 'days'))
return Math.abs(date.diff(moment(), 'days'))
}
export const entries: Ref<Entry[]> = ref(parseFromPossibleString(localStorage.getItem(localStorageKey)))
export interface Entry {
name: string
text: string | undefined
last_reset: Moment
name: string
text: string | undefined
last_reset: Moment
}
export function parseFromPossibleString(input: string | null): Entry[] {
if (input === null) {
return []
}
if (input === null) {
return []
}
const entries: Entry[] = []
const rawObjects: any[] = JSON.parse(input)
const entries: Entry[] = []
const rawObjects: any[] = JSON.parse(input)
for (const rawObject of rawObjects) {
const { name, text, last_reset } = rawObject
for (const rawObject of rawObjects) {
const { name, text, last_reset } = rawObject
const date = parseDate(last_reset)
if (name && last_reset) {
entries.push({ name, text, last_reset: moment(last_reset) })
}
}
if (date && name) {
entries.push({ name, text, last_reset: moment(last_reset) })
}
return entries
}
return entries
}
export function save() {
localStorage.setItem(localStorageKey, JSON.stringify(entries.value))
export function parseDate(dateString: string) {
const date = moment(dateString)
if (!date.isValid()) {
return undefined
}
return date
}
export function save(entries: Entry[]) {
localStorage.setItem(localStorageKey, JSON.stringify(entries))
}

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { Card, CardTitle, CardDescription } from '@/components/ui/card';
import { entries, getDifferenceToToday, save } from '@/data/entries';
import { entries } from '@/state/entry';
import { getDifferenceToToday, save } from '@/data/entries';
import { ArrowBigLeft, MenuIcon, TrashIcon } from 'lucide-vue-next';
import {
DropdownMenu,
@@ -40,7 +41,7 @@ function deleteEntry() {
}
entries.value.splice(index, 1)
save()
save(entries.value)
router.back()
}
@@ -52,7 +53,7 @@ function resetDate() {
entry.value.last_reset = moment()
confirmDialogState.value = false
save()
save(entries.value)
}
function editEntry(val: CreateEntrySchema) {
@@ -65,7 +66,7 @@ function editEntry(val: CreateEntrySchema) {
entry.value.name = val.name
entry.value.text = val.text
editEntryDialog.value = false
save()
save(entries.value)
}
</script>

View File

@@ -1,7 +1,8 @@
<script setup lang="ts">
import { Button } from '@/components/ui/button';
import { Plus } from 'lucide-vue-next';
import { entries, save } from '@/data/entries';
import { entries } from '@/state/entry'
import { save } from '@/data/entries';
import { Drawer, DrawerHeader, DrawerTitle, DrawerContent } from '@/components/ui/drawer';
import { Dialog, DialogHeader, DialogTitle, DialogContent } from '@/components/ui/dialog';
import { Badge } from '@/components/ui/badge';
@@ -36,7 +37,7 @@ async function createEntry(value: CreateEntrySchema) {
? value.text : undefined
})
save()
save(entries.value)
createDrawerState.value = false
}

1
src/shims-vue.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare module '*.vue'

4
src/state/entry.ts Normal file
View File

@@ -0,0 +1,4 @@
import { Entry, localStorageKey, parseFromPossibleString } from "@/data/entries";
import { Ref, ref } from "vue";
export const entries: Ref<Entry[]> = ref(parseFromPossibleString(localStorage.getItem(localStorageKey)))

View File

@@ -1,4 +1,4 @@
import { defineConfig } from 'vite'
import { defineConfig } from 'vitest/config'
import { VitePWA } from 'vite-plugin-pwa'
import vue from '@vitejs/plugin-vue'
import tailwind from 'tailwindcss'
@@ -7,6 +7,10 @@ import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
test: {
environment: 'jsdom',
globals: true
},
plugins: [vue(), VitePWA({
registerType: 'autoUpdate',
manifest: {
@@ -27,4 +31,4 @@ export default defineConfig({
"@": path.resolve(__dirname, "./src"),
},
},
})
})