test work in progress
This commit is contained in:
@@ -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
714
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
21
src/components/EntryForm.test.ts
Normal file
21
src/components/EntryForm.test.ts
Normal 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())
|
||||
})
|
||||
})
|
||||
@@ -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
56
src/data/entries.test.ts
Normal 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)
|
||||
})
|
||||
})
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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
1
src/shims-vue.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module '*.vue'
|
||||
4
src/state/entry.ts
Normal file
4
src/state/entry.ts
Normal 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)))
|
||||
@@ -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"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user