PHP 8.2.30
Preview: property-decorator.spec.ts Size: 6.75 KB
//proc/thread-self/root/home/byroehnu/.trash/node_modules11/adminjs/src/backend/decorators/property/property-decorator.spec.ts

import { expect } from 'chai'
import sinon, { SinonStubbedInstance } from 'sinon'

import PropertyDecorator from './property-decorator.js'
import BaseProperty from '../../adapters/property/base-property.js'
import AdminJS from '../../../adminjs.js'
import ResourceDecorator from '../resource/resource-decorator.js'
import { BaseResource } from '../../adapters/resource/index.js'

describe('PropertyDecorator', () => {
  const normalName = 'normalName'
  let stubbedAdmin: SinonStubbedInstance<AdminJS> & AdminJS
  let property: BaseProperty
  let args: {
    property: BaseProperty;
    admin: typeof stubbedAdmin;
    resource: ResourceDecorator;
  }

  beforeEach(() => {
    property = new BaseProperty({ path: 'name', type: 'string' })
    stubbedAdmin = sinon.createStubInstance(AdminJS)
    // stubbedAdmin.translateProperty = sinon.stub().returns(normalName) as any
    args = { property, admin: stubbedAdmin, resource: { id: () => 'someId' } as ResourceDecorator }
  })

  describe('#isSortable', () => {
    it('passes the execution to the base property', () => {
      sinon.stub(BaseProperty.prototype, 'isSortable').returns(false)
      expect(new PropertyDecorator(args).isSortable()).to.equal(false)
    })
  })

  describe('#isVisible', () => {
    it('passes execution to BaseProperty.isVisible for list when no options are specified', () => {
      expect(new PropertyDecorator(args).isVisible('list')).to.equal(property.isVisible())
    })

    it('passes execution to BaseProperty.isEditable for edit when no options are specified', () => {
      sinon.stub(BaseProperty.prototype, 'isVisible').returns(false)
      expect(new PropertyDecorator(args).isVisible('edit')).to.equal(property.isEditable())
    })

    it('sets new value when it is changed for all views by isVisible option', () => {
      const decorator = new PropertyDecorator({ ...args, options: { isVisible: false } })
      expect(decorator.isVisible('list')).to.equal(false)
      expect(decorator.isVisible('edit')).to.equal(false)
      expect(decorator.isVisible('show')).to.equal(false)
    })
  })

  describe('#label', () => {
    it('returns translated label', () => {
      sinon.stub(BaseProperty.prototype, 'name').returns('normalName')
      expect(new PropertyDecorator(args).label()).to.equal(normalName)
    })
  })

  describe('#reference', () => {
    const rawReferenceValue = 'Article'
    const optionsReferenceValue = 'BlogPost'
    const ReferenceResource = 'OtherResource' as unknown as BaseResource

    beforeEach(() => {
      property = new BaseProperty({ path: 'externalId', type: 'reference' })
      sinon.stub(property, 'reference').returns(rawReferenceValue)
      args.admin.findResource.returns(ReferenceResource)
    })

    it('returns model from AdminJS for reference name in properties', () => {
      new PropertyDecorator({ ...args, property }).reference()

      expect(args.admin.findResource).to.have.been.calledWith(rawReferenceValue)
    })

    it('returns model from options when they are given', () => {
      new PropertyDecorator({
        ...args,
        property,
        options: {
          reference: optionsReferenceValue,
        },
      }).reference()

      expect(args.admin.findResource).to.have.been.calledWith(optionsReferenceValue)
    })
  })

  describe('#type', () => {
    const propertyType = 'boolean'

    beforeEach(() => {
      property = new BaseProperty({ path: 'externalId', type: propertyType })
    })

    it('returns `reference` type if reference is set in options', () => {
      const decorator = new PropertyDecorator({
        ...args,
        property,
        options: {
          reference: 'SomeReference',
        },
      })

      expect(decorator.type()).to.equal('reference')
    })

    it('returns property reference when no options are given', () => {
      const decorator = new PropertyDecorator({ ...args, property })

      expect(decorator.type()).to.equal(propertyType)
    })
  })

  describe('#availableValues', () => {
    it('map default value to { value, label } object and uses translations', () => {
      sinon.stub(BaseProperty.prototype, 'availableValues').returns(['val'])
      expect(new PropertyDecorator(args).availableValues()).to.deep.equal([{
        value: 'val',
        label: 'val',
      }])
    })
  })

  describe('#position', () => {
    it('returns -1 for title field', () => {
      sinon.stub(BaseProperty.prototype, 'isTitle').returns(true)
      expect(new PropertyDecorator(args).position()).to.equal(-1)
    })

    it('returns 101 for second field', () => {
      sinon.stub(BaseProperty.prototype, 'isTitle').returns(false)
      expect(new PropertyDecorator(args).position()).to.equal(101)
    })

    it('returns 0 for an id field', () => {
      sinon.stub(BaseProperty.prototype, 'isTitle').returns(false)
      sinon.stub(BaseProperty.prototype, 'isId').returns(true)
      expect(new PropertyDecorator(args).position()).to.equal(0)
    })
  })

  describe('#subProperties', () => {
    let propertyDecorator: PropertyDecorator
    const propertyName = 'super'
    const subPropertyName = 'nested'
    const subPropertyLabel = 'nestedLabel'

    beforeEach(() => {
      property = new BaseProperty({ path: propertyName, type: 'string' })
      sinon.stub(property, 'subProperties').returns([
        new BaseProperty({ path: subPropertyName, type: 'string' }),
      ])

      propertyDecorator = new PropertyDecorator({
        ...args,
        property,
        resource: {
          id: () => 'resourceId',
          options: {
            properties: {
              [`${propertyName}.${subPropertyName}`]: { label: subPropertyLabel },
            },
          },
        } as unknown as ResourceDecorator,
      })
    })

    it('returns the array of decorated properties', () => {
      expect(propertyDecorator.subProperties()).to.have.lengthOf(1)
      expect(propertyDecorator.subProperties()[0]).to.be.instanceOf(PropertyDecorator)
    })

    it('changes label of the nested property to what was given in PropertyOptions', () => {
      const subProperty = propertyDecorator.subProperties()[0]

      expect(subProperty.label()).to.eq(`${propertyName}.${subPropertyName}`)
    })
  })

  describe('#toJSON', () => {
    it('returns JSON representation of a property', () => {
      expect(new PropertyDecorator(args).toJSON()).to.have.keys(
        'isTitle',
        'isId',
        'position',
        'isSortable',
        'availableValues',
        'name',
        'label',
        'type',
        'reference',
        'components',
        'isDisabled',
        'subProperties',
        'isArray',
        'isDraggable',
        'custom',
        'resourceId',
        'propertyPath',
        'isRequired',
        'isVirtual',
        'props',
        'hideLabel',
        'description',
      )
    })
  })

  afterEach(() => {
    sinon.restore()
  })
})

Directory Contents

Dirs: 1 × Files: 4

Name Size Perms Modified Actions
utils DIR
- drwxr-xr-x 2026-02-28 00:32:38
Edit Download
190 B lrw-r--r-- 2026-02-28 00:32:28
Edit Download
6.75 KB lrw-r--r-- 2026-02-28 00:32:40
Edit Download
8.31 KB lrw-r--r-- 2026-02-28 00:32:40
Edit Download
3.37 KB lrw-r--r-- 2026-02-28 00:32:40
Edit Download

If ZipArchive is unavailable, a .tar will be created (no compression).