Files
gramps-web/test/unit/enclosingPlaces.test.js
scotCW 14b4767d3a Don't delete a place that encloses other places (#1510)
* Don't delete a place that encloses other places

Like Gramps desktop, refuse to delete a place that other places are
enclosed by: Gramps 6.0 would leave those places pointing to a place
that no longer exists. The delete button shows Gramps' own "Cannot
delete place." message instead of the confirmation.

Refs #381

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Check whether deletion is blocked only in the active view

Every retained object view hears edit-mode:delete, so an inactive place
view that encloses others reported a false error whenever another
object was deleted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Check enclosed places when Delete is clicked, also in the places list

- The app bar asks the view being edited when Delete is clicked, instead
  of a value captured when edit mode began, so the two can't disagree.
- The check fetches the place's backlinks then, as the view's own data
  may be stale.
- A place enclosed by itself, and enclosed places deleted along with
  it, don't block the deletion.
- The places list applies the same check before a bulk delete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:11:51 +02:00

35 lines
1.2 KiB
JavaScript

import {describe, it, expect} from 'vitest'
import {getEnclosingPlaces} from '../../src/util.js'
// a fake appState answering GET /api/places/<handle>?backlinks=true
const appStateWith = enclosed => ({
apiGet: async url => {
const handle = url.split('/')[3].split('?')[0]
return {data: {handle, backlinks: {place: enclosed[handle] ?? []}}}
},
})
describe('getEnclosingPlaces', () => {
const appState = appStateWith({county: ['city'], city: [], farm: []})
it('returns places that other places are enclosed by', async () => {
expect(await getEnclosingPlaces(appState, ['county', 'farm'])).toEqual([
'county',
])
})
it('ignores enclosed places deleted along with them', async () => {
expect(await getEnclosingPlaces(appState, ['county', 'city'])).toEqual([])
})
it('ignores a place enclosed by itself', async () => {
const self = appStateWith({loop: ['loop']})
expect(await getEnclosingPlaces(self, ['loop'])).toEqual([])
})
it('does not block when the backlinks cannot be read', async () => {
const failing = {apiGet: async () => ({error: 'Not found'})}
expect(await getEnclosingPlaces(failing, ['county'])).toEqual([])
})
})