import { describe, it, expect } from 'vitest' import { readFileSync } from 'fs' import { fileURLToPath } from 'url' import { dirname, join } from 'path' const here = dirname(fileURLToPath(import.meta.url)) const view = readFileSync(join(here, 'ConfigurationPoliciesList.vue'), 'utf8') // The API decides what may be edited; the buttons only reflect that decision. // These drifted once: policy_routes.py was widened to accept shipped rows while // the buttons still disabled on source !== 'local', so every shipped policy sat // behind a dead Edit button and the change looked like it had never rolled. // If the API guard changes, EDITABLE_SOURCES here changes with it. describe('the configuration policy buttons follow the API guard', () => { it('treats shipped as editable, not read-only', () => { expect(view).toContain("EDITABLE_SOURCES = ['local', 'autoapp', 'shipped']") }) it('does not gate the buttons on local alone', () => { expect(view).not.toContain(":disabled=\"p.source !== 'local'\"") }) // Counted in pairs rather than pinned to a fixed number: the list renders // shipped and site policies as two separate tables, so there is one Edit and // one Delete per table. What matters is that the two buttons always agree // with each other and both defer to iseditable - not how many tables exist. it('gates edit and delete on the same rule', () => { const disabled = view.match(/:disabled="!iseditable\(p\)"/g) || [] const edits = view.match(/@click="openModal\(p\)"/g) || [] const deletes = view.match(/@click="remove\(p\)"/g) || [] expect(disabled.length).toBe(edits.length + deletes.length) expect(edits.length).toBe(deletes.length) expect(edits.length).toBeGreaterThan(0) }) // The split is presentational. Both groups must render the same buttons // under the same rule, so a shipped row is never stranded behind a dead // button the way it was when the buttons gated on source === 'local'. it('renders both policy groups from the same editable rule', () => { expect(view).toContain('shippedPolicies') expect(view).toContain('localPolicies') expect(view).toContain("p.source === 'shipped'") expect(view).toContain("p.source !== 'shipped'") }) })