snapshot of https://github.com/frappe/crm.git (develop, 2026-09-18; blobs > 131072B omitted)
Some checks failed
Frontend / Unit Tests & Coverage (push) Has been skipped
Regenerate POT file (translatable strings) / Regenerate POT file (develop) (push) Failing after 1m13s
Regenerate POT file (translatable strings) / Regenerate POT file (main-hotfix) (push) Failing after 6s
Server / Tests (frappe ${{ matrix.frappe-branch }}) (push) Has been skipped
Server / Resolve frappe matrix (push) Failing after 1s
Frontend / Lint (push) Failing after 1s
Frontend / Production Build (push) Failing after 1s
Server / Coverage Wrap Up (push) Has been skipped
Frontend / Coverage Wrap Up (push) Has been skipped
Some checks failed
Frontend / Unit Tests & Coverage (push) Has been skipped
Regenerate POT file (translatable strings) / Regenerate POT file (develop) (push) Failing after 1m13s
Regenerate POT file (translatable strings) / Regenerate POT file (main-hotfix) (push) Failing after 6s
Server / Tests (frappe ${{ matrix.frappe-branch }}) (push) Has been skipped
Server / Resolve frappe matrix (push) Failing after 1s
Frontend / Lint (push) Failing after 1s
Frontend / Production Build (push) Failing after 1s
Server / Coverage Wrap Up (push) Has been skipped
Frontend / Coverage Wrap Up (push) Has been skipped
This commit is contained in:
230
frontend/tests/unit/fieldPropertyOverrides.test.js
Normal file
230
frontend/tests/unit/fieldPropertyOverrides.test.js
Normal file
@@ -0,0 +1,230 @@
|
||||
import { reactive } from 'vue'
|
||||
|
||||
/**
|
||||
* Mirrors the actual setFieldProperty / removeFieldProperty / setFieldProperties
|
||||
* logic that will be added to script.js prototype methods.
|
||||
* Tests the override map manipulation in isolation.
|
||||
*/
|
||||
|
||||
function setFieldProperty(ctx, target, property, value, rowName) {
|
||||
if (!ctx.fieldPropertyOverrides) ctx.fieldPropertyOverrides = {}
|
||||
const key = rowName ? `${target}:${rowName}` : target
|
||||
if (!ctx.fieldPropertyOverrides[key]) ctx.fieldPropertyOverrides[key] = {}
|
||||
ctx.fieldPropertyOverrides[key][property] = value
|
||||
}
|
||||
|
||||
function setFieldProperties(ctx, target, properties, rowName) {
|
||||
for (const [key, value] of Object.entries(properties)) {
|
||||
setFieldProperty(ctx, target, key, value, rowName)
|
||||
}
|
||||
}
|
||||
|
||||
function removeFieldProperty(ctx, target, property, rowName) {
|
||||
const key = rowName ? `${target}:${rowName}` : target
|
||||
if (!ctx.fieldPropertyOverrides?.[key]) return
|
||||
delete ctx.fieldPropertyOverrides[key][property]
|
||||
if (Object.keys(ctx.fieldPropertyOverrides[key]).length === 0) {
|
||||
delete ctx.fieldPropertyOverrides[key]
|
||||
}
|
||||
}
|
||||
|
||||
function createCtx() {
|
||||
return reactive({ fieldPropertyOverrides: {} })
|
||||
}
|
||||
|
||||
describe('fieldPropertyOverrides map operations', () => {
|
||||
// ─── setFieldProperty ─────────────────────────────────────────
|
||||
|
||||
it('sets a single property on a field', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'email', 'hidden', true)
|
||||
expect(ctx.fieldPropertyOverrides.email.hidden).toBe(true)
|
||||
})
|
||||
|
||||
it('sets multiple properties on the same field', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'email', 'hidden', true)
|
||||
setFieldProperty(ctx, 'email', 'label', 'Work Email')
|
||||
expect(ctx.fieldPropertyOverrides.email).toEqual({
|
||||
hidden: true,
|
||||
label: 'Work Email',
|
||||
})
|
||||
})
|
||||
|
||||
it('overwrites a previously set property', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'email', 'hidden', true)
|
||||
setFieldProperty(ctx, 'email', 'hidden', false)
|
||||
expect(ctx.fieldPropertyOverrides.email.hidden).toBe(false)
|
||||
})
|
||||
|
||||
it('sets properties on different fields independently', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'email', 'hidden', true)
|
||||
setFieldProperty(ctx, 'phone', 'read_only', true)
|
||||
expect(ctx.fieldPropertyOverrides.email).toEqual({ hidden: true })
|
||||
expect(ctx.fieldPropertyOverrides.phone).toEqual({ read_only: true })
|
||||
})
|
||||
|
||||
it('handles string values', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'status', 'options', 'New\nOpen\nClosed')
|
||||
expect(ctx.fieldPropertyOverrides.status.options).toBe('New\nOpen\nClosed')
|
||||
})
|
||||
|
||||
it('handles object values (link_filters)', () => {
|
||||
const ctx = createCtx()
|
||||
const filters = { company: 'ACME', enabled: 1 }
|
||||
setFieldProperty(ctx, 'contact', 'link_filters', filters)
|
||||
expect(ctx.fieldPropertyOverrides.contact.link_filters).toEqual(filters)
|
||||
})
|
||||
|
||||
it('initializes fieldPropertyOverrides if missing', () => {
|
||||
const ctx = reactive({})
|
||||
setFieldProperty(ctx, 'x', 'hidden', true)
|
||||
expect(ctx.fieldPropertyOverrides.x.hidden).toBe(true)
|
||||
})
|
||||
|
||||
// ─── setFieldProperties (batch) ────────────────────────────────
|
||||
|
||||
it('sets multiple properties at once', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperties(ctx, 'revenue', {
|
||||
hidden: false,
|
||||
read_only: true,
|
||||
label: 'Revenue (USD)',
|
||||
})
|
||||
expect(ctx.fieldPropertyOverrides.revenue).toEqual({
|
||||
hidden: false,
|
||||
read_only: true,
|
||||
label: 'Revenue (USD)',
|
||||
})
|
||||
})
|
||||
|
||||
it('batch set merges with existing properties', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'x', 'hidden', true)
|
||||
setFieldProperties(ctx, 'x', { label: 'New', description: 'Help' })
|
||||
expect(ctx.fieldPropertyOverrides.x).toEqual({
|
||||
hidden: true,
|
||||
label: 'New',
|
||||
description: 'Help',
|
||||
})
|
||||
})
|
||||
|
||||
// ─── removeFieldProperty ──────────────────────────────────────
|
||||
|
||||
it('removes a single property', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'email', 'hidden', true)
|
||||
setFieldProperty(ctx, 'email', 'label', 'X')
|
||||
removeFieldProperty(ctx, 'email', 'hidden')
|
||||
expect(ctx.fieldPropertyOverrides.email).toEqual({ label: 'X' })
|
||||
})
|
||||
|
||||
it('cleans up empty field key after last property removed', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'email', 'hidden', true)
|
||||
removeFieldProperty(ctx, 'email', 'hidden')
|
||||
expect(ctx.fieldPropertyOverrides.email).toBeUndefined()
|
||||
})
|
||||
|
||||
it('is safe to call on non-existent field', () => {
|
||||
const ctx = createCtx()
|
||||
expect(() => removeFieldProperty(ctx, 'nope', 'hidden')).not.toThrow()
|
||||
})
|
||||
|
||||
it('is safe to call on non-existent property', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'email', 'hidden', true)
|
||||
expect(() => removeFieldProperty(ctx, 'email', 'nope')).not.toThrow()
|
||||
// original property still there
|
||||
expect(ctx.fieldPropertyOverrides.email.hidden).toBe(true)
|
||||
})
|
||||
|
||||
// ─── Dot notation (child table fields) ────────────────────────
|
||||
|
||||
it('handles dot notation for child table fields', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'products.qty', 'read_only', true)
|
||||
expect(ctx.fieldPropertyOverrides['products.qty'].read_only).toBe(true)
|
||||
})
|
||||
|
||||
it('removes dot notation overrides', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'products.qty', 'read_only', true)
|
||||
removeFieldProperty(ctx, 'products.qty', 'read_only')
|
||||
expect(ctx.fieldPropertyOverrides['products.qty']).toBeUndefined()
|
||||
})
|
||||
|
||||
// ─── Section and tab names ────────────────────────────────────
|
||||
|
||||
it('handles section names', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'details_section', 'hidden', true)
|
||||
expect(ctx.fieldPropertyOverrides.details_section.hidden).toBe(true)
|
||||
})
|
||||
|
||||
it('handles tab names', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'advanced_tab', 'hidden', true)
|
||||
expect(ctx.fieldPropertyOverrides.advanced_tab.hidden).toBe(true)
|
||||
})
|
||||
|
||||
it('section override with multiple properties', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperties(ctx, 'notes_section', {
|
||||
collapsible: true,
|
||||
opened: false,
|
||||
label: 'Internal Notes',
|
||||
})
|
||||
expect(ctx.fieldPropertyOverrides.notes_section).toEqual({
|
||||
collapsible: true,
|
||||
opened: false,
|
||||
label: 'Internal Notes',
|
||||
})
|
||||
})
|
||||
|
||||
// ─── Per-row overrides (4th param: rowName) ─────────────────
|
||||
|
||||
it('sets per-row override with rowName', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'products.qty', 'read_only', true, 'row_abc')
|
||||
expect(ctx.fieldPropertyOverrides['products.qty:row_abc'].read_only).toBe(
|
||||
true,
|
||||
)
|
||||
})
|
||||
|
||||
it('per-row and column-level overrides coexist', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'products.qty', 'read_only', true)
|
||||
setFieldProperty(ctx, 'products.qty', 'read_only', false, 'row_abc')
|
||||
expect(ctx.fieldPropertyOverrides['products.qty'].read_only).toBe(true)
|
||||
expect(ctx.fieldPropertyOverrides['products.qty:row_abc'].read_only).toBe(
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
it('removes per-row override independently', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperty(ctx, 'products.qty', 'read_only', true)
|
||||
setFieldProperty(ctx, 'products.qty', 'read_only', false, 'row_abc')
|
||||
removeFieldProperty(ctx, 'products.qty', 'read_only', 'row_abc')
|
||||
expect(ctx.fieldPropertyOverrides['products.qty:row_abc']).toBeUndefined()
|
||||
expect(ctx.fieldPropertyOverrides['products.qty'].read_only).toBe(true)
|
||||
})
|
||||
|
||||
it('batch set with per-row', () => {
|
||||
const ctx = createCtx()
|
||||
setFieldProperties(
|
||||
ctx,
|
||||
'products.rate',
|
||||
{ read_only: true, label: 'Fixed Rate' },
|
||||
'row_xyz',
|
||||
)
|
||||
expect(ctx.fieldPropertyOverrides['products.rate:row_xyz']).toEqual({
|
||||
read_only: true,
|
||||
label: 'Fixed Rate',
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user