feat: Update user authentication data and enhance routine item handling in RoutineEditView
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m16s
All checks were successful
Chore App Build, Test, and Push Docker Images / build-and-push (push) Successful in 3m16s
This commit is contained in:
@@ -2,20 +2,20 @@
|
||||
"cookies": [
|
||||
{
|
||||
"name": "refresh_token",
|
||||
"value": "vB_LRXjnl0ucrR2XdmRzT2hdBCb656qv75SB6s3yuCM",
|
||||
"value": "oxkpzGInQ4shJIvaKgzPTk-G1g-yuISm-eWLWAHCz8c",
|
||||
"domain": "localhost",
|
||||
"path": "/api/auth",
|
||||
"expires": 1786851645.664007,
|
||||
"expires": 1786910690.97027,
|
||||
"httpOnly": true,
|
||||
"secure": true,
|
||||
"sameSite": "Strict"
|
||||
},
|
||||
{
|
||||
"name": "access_token",
|
||||
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1jY0B0ZXN0LmNvbSIsInVzZXJfaWQiOiI0YjlmNGE2NS0xM2JmLTRkZDEtYTE3Mi1hODFmYzNkNjQ3ZWQiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzkwODY0NDV9.hxhTHPEnMJagMq3tUpW5qIon3qgx1u77TikMwrXfM3s",
|
||||
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1jY0B0ZXN0LmNvbSIsInVzZXJfaWQiOiIzMDgxMGE1MS0xOGRkLTRjOGEtOWU2MS0xZGRjMGM5NGQ2ODAiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzkxNDU0OTB9.wgl1nIrfwav8NhRqXaqglqbtwZ-W6M3FfR2_J00v_lw",
|
||||
"domain": "localhost",
|
||||
"path": "/",
|
||||
"expires": 1779086445.663928,
|
||||
"expires": 1779145490.969587,
|
||||
"httpOnly": true,
|
||||
"secure": true,
|
||||
"sameSite": "Lax"
|
||||
@@ -27,11 +27,11 @@
|
||||
"localStorage": [
|
||||
{
|
||||
"name": "authSyncEvent",
|
||||
"value": "{\"type\":\"logout\",\"at\":1779075645509}"
|
||||
"value": "{\"type\":\"logout\",\"at\":1779134690681}"
|
||||
},
|
||||
{
|
||||
"name": "parentAuth",
|
||||
"value": "{\"expiresAt\":1779248445817}"
|
||||
"value": "{\"expiresAt\":1779307491379}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
"cookies": [
|
||||
{
|
||||
"name": "refresh_token",
|
||||
"value": "gboIPp5ceYO1Kb0CtJoBNLLEB_aqa9hpmWeQcYU5ts0",
|
||||
"value": "n_EftQzpLANJ_pI4xwg6LQDCDds9Ufvy1GcnnYJQ00A",
|
||||
"domain": "localhost",
|
||||
"path": "/api/auth",
|
||||
"expires": 1786851645.795109,
|
||||
"expires": 1786910690.820234,
|
||||
"httpOnly": true,
|
||||
"secure": true,
|
||||
"sameSite": "Strict"
|
||||
},
|
||||
{
|
||||
"name": "access_token",
|
||||
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1kZWxldGVAdGVzdC5jb20iLCJ1c2VyX2lkIjoiYzBlNTdhNzktZTU2OS00Y2RmLTk0MDQtYzdiYjI3ZmRkMjAyIiwidG9rZW5fdmVyc2lvbiI6MCwiZXhwIjoxNzc5MDg2NDQ1fQ.WXFDSpITZGB74reIt7-NMFdS40oxrUhqhkQdqDdCrK4",
|
||||
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZS1kZWxldGVAdGVzdC5jb20iLCJ1c2VyX2lkIjoiNDE3OWYwNzItYTM3MC00NGY1LTg1YmMtZmU1ZDIwNzY3NWE3IiwidG9rZW5fdmVyc2lvbiI6MCwiZXhwIjoxNzc5MTQ1NDkwfQ.1uhXDEXNncubgx4D8XnFI6KWxXT4wvvvGrXQfHvjnhU",
|
||||
"domain": "localhost",
|
||||
"path": "/",
|
||||
"expires": 1779086445.79506,
|
||||
"expires": 1779145490.819506,
|
||||
"httpOnly": true,
|
||||
"secure": true,
|
||||
"sameSite": "Lax"
|
||||
@@ -27,11 +27,11 @@
|
||||
"localStorage": [
|
||||
{
|
||||
"name": "authSyncEvent",
|
||||
"value": "{\"type\":\"logout\",\"at\":1779075645659}"
|
||||
"value": "{\"type\":\"logout\",\"at\":1779134690558}"
|
||||
},
|
||||
{
|
||||
"name": "parentAuth",
|
||||
"value": "{\"expiresAt\":1779248445953}"
|
||||
"value": "{\"expiresAt\":1779307491309}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
"cookies": [
|
||||
{
|
||||
"name": "refresh_token",
|
||||
"value": "vp9tOOruGSijnoaSX6nKSaRCEsSzsiln0Jt1sSaaVtg",
|
||||
"value": "QCuLIgmPV2vnDfzmgvptt-mZAW1jh8UML_wwAee4OTQ",
|
||||
"domain": "localhost",
|
||||
"path": "/api/auth",
|
||||
"expires": 1786851643.974239,
|
||||
"expires": 1786912678.702645,
|
||||
"httpOnly": true,
|
||||
"secure": true,
|
||||
"sameSite": "Strict"
|
||||
},
|
||||
{
|
||||
"name": "access_token",
|
||||
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiJjYjU5ZWU1Ni1iNDJkLTQ2NDctOTQ0MC1lNzYwYmJlMjg2ZDYiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzkwODY0NDN9.8xB5EOIA_PBXC3KwajHcAfpZVC8c78OP0HaExyrYRV8",
|
||||
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImUyZUB0ZXN0LmNvbSIsInVzZXJfaWQiOiJlM2VmNGE2OC0zZWI3LTQ2MjQtYTI0Mi0yNmY1OTgyZDIwOWEiLCJ0b2tlbl92ZXJzaW9uIjowLCJleHAiOjE3NzkxNDc0Nzh9.P7CDplAG3MBXgtEpuYFtp6gOV5pi-6QeAYRqmE-Dkys",
|
||||
"domain": "localhost",
|
||||
"path": "/",
|
||||
"expires": 1779086443.974189,
|
||||
"expires": 1779147478.701971,
|
||||
"httpOnly": true,
|
||||
"secure": true,
|
||||
"sameSite": "Lax"
|
||||
@@ -27,11 +27,11 @@
|
||||
"localStorage": [
|
||||
{
|
||||
"name": "authSyncEvent",
|
||||
"value": "{\"type\":\"logout\",\"at\":1779075643795}"
|
||||
"value": "{\"type\":\"logout\",\"at\":1779136678495}"
|
||||
},
|
||||
{
|
||||
"name": "parentAuth",
|
||||
"value": "{\"expiresAt\":1779248444100}"
|
||||
"value": "{\"expiresAt\":1779309478870}"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -372,12 +372,23 @@ async function handleSubmit(form: { name: string; points: number; image_id: stri
|
||||
})
|
||||
if (!resp.ok) throw new Error('Failed to save routine')
|
||||
const routineData = await resp.json()
|
||||
const routineId = routineData.id || props.id
|
||||
const routineId =
|
||||
props.id ||
|
||||
(typeof routineData?.id === 'string' && routineData.id) ||
|
||||
(typeof routineData?.routine?.id === 'string' && routineData.routine.id) ||
|
||||
''
|
||||
|
||||
if (!routineId) {
|
||||
throw new Error('Failed to resolve routine id after save')
|
||||
}
|
||||
|
||||
for (const itemId of removedItemIds.value) {
|
||||
await fetch(`/api/routine/${routineId}/item/${itemId}`, {
|
||||
const deleteResp = await fetch(`/api/routine/${routineId}/item/${itemId}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
if (!deleteResp.ok) {
|
||||
throw new Error('Failed to delete routine item')
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of items.value) {
|
||||
@@ -385,17 +396,23 @@ async function handleSubmit(form: { name: string; points: number; image_id: stri
|
||||
const payload = { name: item.name, image_id: resolvedImageId, order: item.order }
|
||||
|
||||
if (item.id?.startsWith('temp-') || !item.id) {
|
||||
await fetch(`/api/routine/${routineId}/item/add`, {
|
||||
const addItemResp = await fetch(`/api/routine/${routineId}/item/add`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
if (!addItemResp.ok) {
|
||||
throw new Error('Failed to add routine item')
|
||||
}
|
||||
} else {
|
||||
await fetch(`/api/routine/${routineId}/item/${item.id}/edit`, {
|
||||
const editItemResp = await fetch(`/api/routine/${routineId}/item/${item.id}/edit`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
if (!editItemResp.ok) {
|
||||
throw new Error('Failed to edit routine item')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,15 @@ import { defineComponent, nextTick } from 'vue'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import RoutineEditView from '../RoutineEditView.vue'
|
||||
|
||||
const mockPush = vi.fn()
|
||||
|
||||
vi.mock('vue-router', () => ({
|
||||
useRoute: vi.fn(() => ({
|
||||
params: {},
|
||||
query: { name: 'Timmy' },
|
||||
})),
|
||||
useRouter: vi.fn(() => ({
|
||||
push: vi.fn(),
|
||||
push: mockPush,
|
||||
back: vi.fn(),
|
||||
})),
|
||||
}))
|
||||
@@ -35,7 +37,7 @@ const EntityEditFormStub = defineComponent({
|
||||
emits: ['submit', 'cancel', 'add-image'],
|
||||
template: `
|
||||
<div class="entity-edit-form-stub">
|
||||
<button type="submit" class="submit-btn" :disabled="submitDisabled">Submit</button>
|
||||
<button type="button" class="submit-btn" :disabled="submitDisabled" @click="$emit('submit', { name: 'Morning Routine', points: 5, image_id: null })">Submit</button>
|
||||
<slot name="before-actions" />
|
||||
</div>
|
||||
`,
|
||||
@@ -49,6 +51,9 @@ const ImagePickerStub = defineComponent({
|
||||
describe('RoutineEditView', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
globalThis.fetch = vi
|
||||
.fn()
|
||||
.mockResolvedValue({ ok: true, json: async () => ({}) }) as typeof fetch
|
||||
})
|
||||
|
||||
it('disables Create when there are no routine tasks', async () => {
|
||||
@@ -69,4 +74,49 @@ describe('RoutineEditView', () => {
|
||||
expect((submitButton.element as HTMLButtonElement).disabled).toBe(true)
|
||||
expect(wrapper.findComponent(EntityEditFormStub).props('submitDisabled')).toBe(true)
|
||||
})
|
||||
|
||||
it('creates routine items using nested routine id from create response', async () => {
|
||||
const fetchMock = vi.fn<typeof fetch>()
|
||||
fetchMock
|
||||
.mockResolvedValueOnce({
|
||||
ok: true,
|
||||
json: async () => ({ message: 'Routine added', routine: { id: 'routine-123' } }),
|
||||
} as Response)
|
||||
.mockResolvedValueOnce({ ok: true, json: async () => ({ id: 'item-1' }) } as Response)
|
||||
|
||||
globalThis.fetch = fetchMock
|
||||
|
||||
const wrapper = mount(RoutineEditView, {
|
||||
props: {},
|
||||
global: {
|
||||
stubs: {
|
||||
EntityEditForm: EntityEditFormStub,
|
||||
ImagePicker: ImagePickerStub,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await wrapper.find('.add-task-trigger').trigger('click')
|
||||
await nextTick()
|
||||
|
||||
const nameInput = wrapper.find('#item-name')
|
||||
await nameInput.setValue('Make bed')
|
||||
await wrapper.find('.item-form-actions .btn.btn-primary').trigger('click')
|
||||
await nextTick()
|
||||
|
||||
await wrapper.find('.submit-btn').trigger('click')
|
||||
await nextTick()
|
||||
|
||||
expect(fetchMock).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'/api/routine/add',
|
||||
expect.objectContaining({ method: 'PUT' }),
|
||||
)
|
||||
expect(fetchMock).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'/api/routine/routine-123/item/add',
|
||||
expect.objectContaining({ method: 'PUT' }),
|
||||
)
|
||||
expect(mockPush).toHaveBeenCalledWith({ name: 'RoutineView' })
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user