diff --git a/frontend/src/stores/projects.test.ts b/frontend/src/stores/projects.test.ts index 26c2b92c2..a3ef547c2 100644 --- a/frontend/src/stores/projects.test.ts +++ b/frontend/src/stores/projects.test.ts @@ -30,6 +30,22 @@ vi.mock('@/stores/base', () => ({ }), })) +const projectServiceMock = { + update: vi.fn(), + getAll: vi.fn(), + totalPages: 1, +} + +vi.mock('@/services/project', () => ({ + default: class { + update = (project: IProject) => projectServiceMock.update(project) + getAll = (...args: unknown[]) => projectServiceMock.getAll(...args) + get totalPages() { + return projectServiceMock.totalPages + } + }, +})) + function createMockProject(overrides: Partial): IProject { return { id: 1, @@ -211,4 +227,41 @@ describe('project store', () => { expect(store.getEffectiveParentProjectId(orphanedProject, 5)).toBe(5) }) }) + + describe('updateProject', () => { + beforeEach(() => { + projectServiceMock.update.mockReset() + projectServiceMock.getAll.mockReset() + projectServiceMock.totalPages = 1 + }) + + it('should not reload all projects when the api kept the position', async () => { + const store = useProjectStore() + const project = createMockProject({id: 1, position: 100}) + store.setProject(project) + projectServiceMock.update.mockResolvedValue(createMockProject({id: 1, position: 100})) + + await store.updateProject({...project, title: 'Renamed'}) + + expect(projectServiceMock.getAll).not.toHaveBeenCalled() + }) + + it('should reload all projects when the api recalculated the position', async () => { + const store = useProjectStore() + store.setProject(createMockProject({id: 1, position: 0.0001})) + store.setProject(createMockProject({id: 2, position: 100})) + projectServiceMock.update.mockResolvedValue(createMockProject({id: 1, position: 2147483648})) + projectServiceMock.getAll.mockResolvedValue([ + createMockProject({id: 1, position: 2147483648}), + createMockProject({id: 2, position: 4294967296}), + ]) + + await store.updateProject(createMockProject({id: 1, position: 0.0001})) + + expect(projectServiceMock.getAll).toHaveBeenCalled() + // Project 2 was never updated - only the reload can fix its stale position. + expect(store.projectsArray.map(p => p.id)).toEqual([1, 2]) + expect(store.projects[2].position).toBe(4294967296) + }) + }) }) diff --git a/frontend/src/stores/projects.ts b/frontend/src/stores/projects.ts index 18a3dfad2..037fc9f79 100644 --- a/frontend/src/stores/projects.ts +++ b/frontend/src/stores/projects.ts @@ -224,6 +224,12 @@ export const useProjectStore = defineStore('project', () => { const updatedProject = await projectService.update(project) setProject(project) + // The api renumbers all sibling positions once they drift too close together but + // only returns the updated project, so the rest would keep stale positions. + if (updatedProject.position !== project.position) { + await loadAllProjects() + } + // the returned project from projectService.update is the same! // in order to not create a manipulation in pinia store we have to create a new copy return updatedProject