Refactor, add coverage

This commit is contained in:
shamoon
2024-09-14 10:31:23 -07:00
parent 3823176beb
commit a25be37c8e
6 changed files with 524 additions and 235 deletions

View File

@@ -0,0 +1,141 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { CustomFieldsQueryDropdownComponent } from './custom-fields-query-dropdown.component'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import { of } from 'rxjs'
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
import {
CUSTOM_FIELD_QUERY_OPERATORS_BY_GROUP,
CustomFieldQueryOperatorGroups,
} from 'src/app/data/custom-field-query'
import { provideHttpClientTesting } from '@angular/common/http/testing'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import {
CustomFieldQueryExpression,
CustomFieldQueryAtom,
CustomFieldQueryElement,
} from 'src/app/utils/custom-field-query-element'
const customFields = [
{
id: 1,
name: 'Test Field',
data_type: CustomFieldDataType.String,
extra_data: {},
},
{
id: 2,
name: 'Test Select Field',
data_type: CustomFieldDataType.Select,
extra_data: { select_options: ['Option 1', 'Option 2'] },
},
]
describe('CustomFieldsQueryDropdownComponent', () => {
let component: CustomFieldsQueryDropdownComponent
let fixture: ComponentFixture<CustomFieldsQueryDropdownComponent>
let customFieldsService: CustomFieldsService
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [CustomFieldsQueryDropdownComponent],
imports: [NgbDropdownModule, NgxBootstrapIconsModule.pick(allIcons)],
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
],
}).compileComponents()
customFieldsService = TestBed.inject(CustomFieldsService)
jest.spyOn(customFieldsService, 'listAll').mockReturnValue(
of({
count: customFields.length,
all: customFields.map((f) => f.id),
results: customFields,
})
)
fixture = TestBed.createComponent(CustomFieldsQueryDropdownComponent)
component = fixture.componentInstance
component.icon = 'ui-radios'
fixture.detectChanges()
})
it('should initialize custom fields on creation', () => {
expect(component.customFields).toEqual(customFields)
})
it('should add an expression when opened if queries are empty', () => {
component.selectionModel.clear()
component.onOpenChange(true)
expect(component.selectionModel.queries.length).toBe(1)
})
it('should support reset the selection model', () => {
component.selectionModel.addExpression()
component.reset()
expect(component.selectionModel.isEmpty()).toBeTruthy()
})
it('should get operators for a field', () => {
const field: CustomField = {
id: 1,
name: 'Test Field',
data_type: CustomFieldDataType.String,
extra_data: {},
}
component.customFields = [field]
const operators = component.getOperatorsForField(1)
expect(operators.length).toEqual(
[
...CUSTOM_FIELD_QUERY_OPERATORS_BY_GROUP[
CustomFieldQueryOperatorGroups.Basic
],
...CUSTOM_FIELD_QUERY_OPERATORS_BY_GROUP[
CustomFieldQueryOperatorGroups.String
],
].length
)
// Fallback to basic operators if field is not found
const operators2 = component.getOperatorsForField(2)
expect(operators2.length).toEqual(
CUSTOM_FIELD_QUERY_OPERATORS_BY_GROUP[
CustomFieldQueryOperatorGroups.Basic
].length
)
})
it('should get select options for a field', () => {
const field: CustomField = {
id: 1,
name: 'Test Field',
data_type: CustomFieldDataType.Select,
extra_data: { select_options: ['Option 1', 'Option 2'] },
}
component.customFields = [field]
const options = component.getSelectOptionsForField(1)
expect(options).toEqual(['Option 1', 'Option 2'])
// Fallback to empty array if field is not found
const options2 = component.getSelectOptionsForField(2)
expect(options2).toEqual([])
})
it('should remove an element from the selection model', () => {
const expression = new CustomFieldQueryExpression()
const atom = new CustomFieldQueryAtom()
;(expression.value as CustomFieldQueryElement[]).push(atom)
component.selectionModel.addExpression(expression)
component.removeElement(atom)
expect(component.selectionModel.isEmpty()).toBeTruthy()
})
it('should emit selectionModelChange when model changes', () => {
const nextSpy = jest.spyOn(component.selectionModelChange, 'next')
const atom = new CustomFieldQueryAtom([1, 'icontains', 'test'])
component.selectionModel.addAtom(atom)
atom.changed.next(atom)
expect(nextSpy).toHaveBeenCalled()
})
})

View File

@@ -2,19 +2,21 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'
import { Subject, first, takeUntil } from 'rxjs'
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
import {
CustomFieldQueryAtom,
CustomFieldQueryExpression,
CustomFieldQueryElementType,
CustomFieldQueryOperator,
CUSTOM_FIELD_QUERY_OPERATOR_GROUPS_BY_TYPE,
CUSTOM_FIELD_QUERY_OPERATORS_BY_GROUP,
CustomFieldQueryOperatorGroups,
CUSTOM_FIELD_QUERY_OPERATOR_LABELS,
CustomFieldQueryElement,
CUSTOM_FIELD_QUERY_MAX_DEPTH,
CUSTOM_FIELD_QUERY_MAX_ATOMS,
} from 'src/app/data/custom-field-query'
import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service'
import {
CustomFieldQueryElement,
CustomFieldQueryExpression,
CustomFieldQueryAtom,
} from 'src/app/utils/custom-field-query-element'
export class CustomFieldQueriesModel {
public queries: CustomFieldQueryElement[] = []
@@ -59,13 +61,13 @@ export class CustomFieldQueriesModel {
)
}
public addQuery(query: CustomFieldQueryAtom) {
public addAtom(atom: CustomFieldQueryAtom) {
if (this.queries.length === 0) {
this.addExpression()
}
;(this.queries[0].value as CustomFieldQueryElement[]).push(query)
query.changed.subscribe(() => {
if (query.field && query.operator && query.value) {
;(this.queries[0].value as CustomFieldQueryElement[]).push(atom)
atom.changed.subscribe(() => {
if (atom.field && atom.operator && atom.value) {
this.changed.next(this)
}
})
@@ -163,8 +165,7 @@ export class CustomFieldsQueryDropdownComponent {
@Input()
disabled: boolean = false
private _selectionModel: CustomFieldQueriesModel =
new CustomFieldQueriesModel()
private _selectionModel: CustomFieldQueriesModel
@Input()
set selectionModel(model: CustomFieldQueriesModel) {
@@ -195,6 +196,7 @@ export class CustomFieldsQueryDropdownComponent {
private unsubscribeNotifier: Subject<any> = new Subject()
constructor(protected customFieldsService: CustomFieldsService) {
this.selectionModel = new CustomFieldQueriesModel()
this.getFields()
this.reset()
}

View File

@@ -93,12 +93,14 @@ import { CustomFieldsService } from 'src/app/services/rest/custom-fields.service
import { CustomField } from 'src/app/data/custom-field'
import { SearchService } from 'src/app/services/rest/search.service'
import {
CustomFieldQueryExpression,
CustomFieldQueryAtom,
CustomFieldQueryLogicalOperator,
CustomFieldQueryOperator,
} from 'src/app/data/custom-field-query'
import { CustomFieldQueriesModel } from '../../common/custom-fields-query-dropdown/custom-fields-query-dropdown.component'
import {
CustomFieldQueryExpression,
CustomFieldQueryAtom,
} from 'src/app/utils/custom-field-query-element'
const TEXT_FILTER_TARGET_TITLE = 'title'
const TEXT_FILTER_TARGET_TITLE_CONTENT = 'title-content'
@@ -546,7 +548,7 @@ export class FilterEditorComponent
)
} else if (query.length === 3) {
// atom
this.customFieldQueriesModel.addQuery(
this.customFieldQueriesModel.addAtom(
new CustomFieldQueryAtom(query as any)
)
}