Full coverage, almost lost it
This commit is contained in:
parent
a25be37c8e
commit
b0970adaca
@ -98,10 +98,10 @@ import { SearchService } from 'src/app/services/rest/search.service'
|
|||||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
|
||||||
import { CustomFieldsQueryDropdownComponent } from '../../common/custom-fields-query-dropdown/custom-fields-query-dropdown.component'
|
import { CustomFieldsQueryDropdownComponent } from '../../common/custom-fields-query-dropdown/custom-fields-query-dropdown.component'
|
||||||
import {
|
import {
|
||||||
CustomFieldQueryAtom,
|
|
||||||
CustomFieldQueryLogicalOperator,
|
CustomFieldQueryLogicalOperator,
|
||||||
CustomFieldQueryOperator,
|
CustomFieldQueryOperator,
|
||||||
} from 'src/app/data/custom-field-query'
|
} from 'src/app/data/custom-field-query'
|
||||||
|
import { CustomFieldQueryAtom } from 'src/app/utils/custom-field-query-element'
|
||||||
|
|
||||||
const tags: Tag[] = [
|
const tags: Tag[] = [
|
||||||
{
|
{
|
||||||
@ -886,31 +886,39 @@ describe('FilterEditorComponent', () => {
|
|||||||
).toEqual(['42', CustomFieldQueryOperator.Exists, 'true'])
|
).toEqual(['42', CustomFieldQueryOperator.Exists, 'true'])
|
||||||
}))
|
}))
|
||||||
|
|
||||||
it('should ingest filter rules for exclude tag(s)', fakeAsync(() => {
|
it('should ingest filter rules for custom field lookup', fakeAsync(() => {
|
||||||
// expect(component.customFieldQueriesModel.getExcludedItems()).toHaveLength(0)
|
expect(component.customFieldQueriesModel.isEmpty()).toBeTruthy()
|
||||||
// component.filterRules = [
|
component.filterRules = [
|
||||||
// {
|
{
|
||||||
// rule_type: FILTER_DOES_NOT_HAVE_CUSTOM_FIELDS,
|
rule_type: FILTER_CUSTOM_FIELDS_LOOKUP,
|
||||||
// value: '42',
|
value: '["AND", [[42, "exists", "true"],[43, "exists", "true"]]]',
|
||||||
// },
|
},
|
||||||
// {
|
]
|
||||||
// rule_type: FILTER_DOES_NOT_HAVE_CUSTOM_FIELDS,
|
expect(component.customFieldQueriesModel.queries[0].operator).toEqual(
|
||||||
// value: '43',
|
CustomFieldQueryLogicalOperator.And
|
||||||
// },
|
)
|
||||||
// ]
|
expect(component.customFieldQueriesModel.queries[0].value.length).toEqual(2)
|
||||||
// expect(component.customFieldQueriesModel.logicalOperator).toEqual(
|
expect(
|
||||||
// LogicalOperator.And
|
(
|
||||||
// )
|
component.customFieldQueriesModel.queries[0]
|
||||||
// expect(component.customFieldQueriesModel.getExcludedItems()).toEqual(
|
.value[0] as CustomFieldQueryAtom
|
||||||
// custom_fields
|
).serialize()
|
||||||
// )
|
).toEqual([42, CustomFieldQueryOperator.Exists, 'true'])
|
||||||
// // coverage
|
|
||||||
// component.filterRules = [
|
// atom
|
||||||
// {
|
component.filterRules = [
|
||||||
// rule_type: FILTER_DOES_NOT_HAVE_CUSTOM_FIELDS,
|
{
|
||||||
// value: null,
|
rule_type: FILTER_CUSTOM_FIELDS_LOOKUP,
|
||||||
// },
|
value: '[42, "exists", "true"]',
|
||||||
// ]
|
},
|
||||||
|
]
|
||||||
|
expect(component.customFieldQueriesModel.queries[0].value.length).toEqual(1)
|
||||||
|
expect(
|
||||||
|
(
|
||||||
|
component.customFieldQueriesModel.queries[0]
|
||||||
|
.value[0] as CustomFieldQueryAtom
|
||||||
|
).serialize()
|
||||||
|
).toEqual([42, CustomFieldQueryOperator.Exists, 'true'])
|
||||||
}))
|
}))
|
||||||
|
|
||||||
it('should ingest filter rules for owner', fakeAsync(() => {
|
it('should ingest filter rules for owner', fakeAsync(() => {
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
CustomFieldQueryLogicalOperator,
|
CustomFieldQueryLogicalOperator,
|
||||||
CustomFieldQueryOperator,
|
CustomFieldQueryOperator,
|
||||||
} from '../data/custom-field-query'
|
} from '../data/custom-field-query'
|
||||||
|
import { fakeAsync, tick } from '@angular/core/testing'
|
||||||
|
|
||||||
describe('CustomFieldQueryElement', () => {
|
describe('CustomFieldQueryElement', () => {
|
||||||
it('should initialize with correct type and id', () => {
|
it('should initialize with correct type and id', () => {
|
||||||
@ -86,23 +87,95 @@ describe('CustomFieldQueryAtom', () => {
|
|||||||
expect(atom.value).toEqual([])
|
expect(atom.value).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should try to set existing value to number if new type is number', () => {
|
||||||
|
const atom = new CustomFieldQueryAtom()
|
||||||
|
atom.value = '42'
|
||||||
|
atom.operator = CustomFieldQueryOperator.GreaterThan
|
||||||
|
expect(atom.value).toBe('42')
|
||||||
|
|
||||||
|
// fallback to null if value is not parseable
|
||||||
|
atom.value = 'not_a_number'
|
||||||
|
atom.operator = CustomFieldQueryOperator.GreaterThan
|
||||||
|
expect(atom.value).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should change boolean values to empty string if operator is not boolean', () => {
|
||||||
|
const atom = new CustomFieldQueryAtom()
|
||||||
|
atom.value = 'true'
|
||||||
|
atom.operator = CustomFieldQueryOperator.Exact
|
||||||
|
expect(atom.value).toBe('')
|
||||||
|
})
|
||||||
|
|
||||||
it('should serialize correctly', () => {
|
it('should serialize correctly', () => {
|
||||||
const atom = new CustomFieldQueryAtom([1, 'operator', 'value'])
|
const atom = new CustomFieldQueryAtom([1, 'operator', 'value'])
|
||||||
expect(atom.serialize()).toEqual([1, 'operator', 'value'])
|
expect(atom.serialize()).toEqual([1, 'operator', 'value'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should emit changed on value change after debounce', fakeAsync(() => {
|
||||||
|
const atom = new CustomFieldQueryAtom()
|
||||||
|
const changeSpy = jest.spyOn(atom.changed, 'next')
|
||||||
|
atom.value = 'new value'
|
||||||
|
tick(1000)
|
||||||
|
expect(changeSpy).toHaveBeenCalled()
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('CustomFieldQueryExpression', () => {
|
describe('CustomFieldQueryExpression', () => {
|
||||||
it('should initialize with correct operator and value', () => {
|
it('should initialize with default operator and empty value', () => {
|
||||||
const expression = new CustomFieldQueryExpression([
|
const expression = new CustomFieldQueryExpression()
|
||||||
CustomFieldQueryLogicalOperator.And,
|
expect(expression.operator).toBe(CustomFieldQueryLogicalOperator.Or)
|
||||||
[],
|
|
||||||
])
|
|
||||||
expect(expression.operator).toBe(CustomFieldQueryLogicalOperator.And)
|
|
||||||
expect(expression.value).toEqual([])
|
expect(expression.value).toEqual([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should add atom correctly', () => {
|
it('should initialize with correct operator and value, propagate changes', () => {
|
||||||
|
const expression = new CustomFieldQueryExpression([
|
||||||
|
CustomFieldQueryLogicalOperator.And,
|
||||||
|
[
|
||||||
|
[1, 'exists', 'true'],
|
||||||
|
[2, 'exists', 'true'],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
expect(expression.operator).toBe(CustomFieldQueryLogicalOperator.And)
|
||||||
|
expect(expression.value.length).toBe(2)
|
||||||
|
|
||||||
|
// propagate changes
|
||||||
|
const expressionChangeSpy = jest.spyOn(expression.changed, 'next')
|
||||||
|
;(expression.value[0] as CustomFieldQueryAtom).changed.next(
|
||||||
|
expression.value[0] as any
|
||||||
|
)
|
||||||
|
expect(expressionChangeSpy).toHaveBeenCalled()
|
||||||
|
|
||||||
|
const expression2 = new CustomFieldQueryExpression([
|
||||||
|
CustomFieldQueryLogicalOperator.Not,
|
||||||
|
[[CustomFieldQueryLogicalOperator.Or, []]],
|
||||||
|
])
|
||||||
|
const expressionChangeSpy2 = jest.spyOn(expression2.changed, 'next')
|
||||||
|
;(expression2.value[0] as CustomFieldQueryExpression).changed.next(
|
||||||
|
expression2.value[0] as any
|
||||||
|
)
|
||||||
|
expect(expressionChangeSpy2).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should initialize with a sub-expression i.e. NOT', () => {
|
||||||
|
const expression = new CustomFieldQueryExpression([
|
||||||
|
CustomFieldQueryLogicalOperator.Not,
|
||||||
|
[
|
||||||
|
'AND',
|
||||||
|
[
|
||||||
|
[1, 'exists', 'true'],
|
||||||
|
[2, 'exists', 'true'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
expect(expression.value).toHaveLength(1)
|
||||||
|
const changedSpy = jest.spyOn(expression.changed, 'next')
|
||||||
|
;(expression.value[0] as CustomFieldQueryExpression).changed.next(
|
||||||
|
expression.value[0] as any
|
||||||
|
)
|
||||||
|
expect(changedSpy).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should add atom correctly, propagate changes', () => {
|
||||||
const expression = new CustomFieldQueryExpression()
|
const expression = new CustomFieldQueryExpression()
|
||||||
const atom = new CustomFieldQueryAtom([
|
const atom = new CustomFieldQueryAtom([
|
||||||
1,
|
1,
|
||||||
@ -111,9 +184,14 @@ describe('CustomFieldQueryExpression', () => {
|
|||||||
])
|
])
|
||||||
expression.addAtom(atom)
|
expression.addAtom(atom)
|
||||||
expect(expression.value).toContain(atom)
|
expect(expression.value).toContain(atom)
|
||||||
|
const changeSpy = jest.spyOn(expression.changed, 'next')
|
||||||
|
atom.changed.next(atom)
|
||||||
|
expect(changeSpy).toHaveBeenCalled()
|
||||||
|
// coverage
|
||||||
|
expression.addAtom()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should add expression correctly', () => {
|
it('should add expression correctly, propagate changes', () => {
|
||||||
const expression = new CustomFieldQueryExpression()
|
const expression = new CustomFieldQueryExpression()
|
||||||
const subExpression = new CustomFieldQueryExpression([
|
const subExpression = new CustomFieldQueryExpression([
|
||||||
CustomFieldQueryLogicalOperator.Or,
|
CustomFieldQueryLogicalOperator.Or,
|
||||||
@ -121,19 +199,42 @@ describe('CustomFieldQueryExpression', () => {
|
|||||||
])
|
])
|
||||||
expression.addExpression(subExpression)
|
expression.addExpression(subExpression)
|
||||||
expect(expression.value).toContain(subExpression)
|
expect(expression.value).toContain(subExpression)
|
||||||
|
const changeSpy = jest.spyOn(expression.changed, 'next')
|
||||||
|
subExpression.changed.next(subExpression)
|
||||||
|
expect(changeSpy).toHaveBeenCalled()
|
||||||
|
// coverage
|
||||||
|
expression.addExpression()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should serialize correctly', () => {
|
it('should serialize correctly', () => {
|
||||||
const expression = new CustomFieldQueryExpression([
|
const expression = new CustomFieldQueryExpression([
|
||||||
CustomFieldQueryLogicalOperator.And,
|
CustomFieldQueryLogicalOperator.And,
|
||||||
[[1, 'operator', 'value']],
|
[[1, 'exists', 'true']],
|
||||||
])
|
])
|
||||||
expect(expression.serialize()).toEqual([
|
expect(expression.serialize()).toEqual([
|
||||||
CustomFieldQueryLogicalOperator.And,
|
CustomFieldQueryLogicalOperator.And,
|
||||||
[[1, 'operator', 'value']],
|
[[1, 'exists', 'true']],
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should serialize NOT expressions correctly', () => {
|
||||||
|
const expression = new CustomFieldQueryExpression()
|
||||||
|
expression.addExpression(
|
||||||
|
new CustomFieldQueryExpression([
|
||||||
|
CustomFieldQueryLogicalOperator.And,
|
||||||
|
[
|
||||||
|
[1, 'exists', 'true'],
|
||||||
|
[2, 'exists', 'true'],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
)
|
||||||
|
expression.operator = CustomFieldQueryLogicalOperator.Not
|
||||||
|
const serialized = expression.serialize()
|
||||||
|
expect(serialized[0]).toBe(CustomFieldQueryLogicalOperator.Not)
|
||||||
|
expect(serialized[1][0]).toBe(CustomFieldQueryLogicalOperator.And)
|
||||||
|
expect(serialized[1][1].length).toBe(2)
|
||||||
|
})
|
||||||
|
|
||||||
it('should be negatable if it has one child which is an expression', () => {
|
it('should be negatable if it has one child which is an expression', () => {
|
||||||
const expression = new CustomFieldQueryExpression([
|
const expression = new CustomFieldQueryExpression([
|
||||||
CustomFieldQueryLogicalOperator.Not,
|
CustomFieldQueryLogicalOperator.Not,
|
||||||
|
@ -73,31 +73,20 @@ export class CustomFieldQueryAtom extends CustomFieldQueryElement {
|
|||||||
this.value = null
|
this.value = null
|
||||||
} else {
|
} else {
|
||||||
if (!newTypes.includes(typeof this.value)) {
|
if (!newTypes.includes(typeof this.value)) {
|
||||||
if (newTypes.length === 1) {
|
switch (newTypes[0]) {
|
||||||
switch (newTypes[0]) {
|
case 'string':
|
||||||
case 'string':
|
this.value = ''
|
||||||
this.value = ''
|
break
|
||||||
break
|
case 'boolean':
|
||||||
case 'boolean':
|
this.value = 'true'
|
||||||
this.value = 'true'
|
break
|
||||||
break
|
case 'array':
|
||||||
case 'array':
|
this.value = []
|
||||||
this.value = []
|
break
|
||||||
break
|
case 'number':
|
||||||
default:
|
const num = parseFloat(this.value as string)
|
||||||
this.value = null
|
this.value = isNaN(num) ? null : num.toString()
|
||||||
break
|
break
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (newTypes.includes('number')) {
|
|
||||||
try {
|
|
||||||
this.value = parseFloat(this.value as string).toString()
|
|
||||||
} catch (e) {
|
|
||||||
this.value = null
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.value = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
['true', 'false'].includes(this.value as string) &&
|
['true', 'false'].includes(this.value as string) &&
|
||||||
@ -133,6 +122,8 @@ export class CustomFieldQueryAtom extends CustomFieldQueryElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class CustomFieldQueryExpression extends CustomFieldQueryElement {
|
export class CustomFieldQueryExpression extends CustomFieldQueryElement {
|
||||||
|
protected _value: string[] | number[] | CustomFieldQueryElement[]
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
expressionArray: [CustomFieldQueryLogicalOperator, any[]] = [
|
expressionArray: [CustomFieldQueryLogicalOperator, any[]] = [
|
||||||
CustomFieldQueryLogicalOperator.Or,
|
CustomFieldQueryLogicalOperator.Or,
|
||||||
@ -174,17 +165,13 @@ export class CustomFieldQueryExpression extends CustomFieldQueryElement {
|
|||||||
|
|
||||||
public override serialize() {
|
public override serialize() {
|
||||||
let value
|
let value
|
||||||
if (this._value instanceof Array) {
|
value = this._value.map((element) => element.serialize())
|
||||||
value = this._value.map((atom) => atom.serialize())
|
// If the expression is negated it should have only one child which is an expression
|
||||||
// If the expression is negated it should have only one child which is an expression
|
if (
|
||||||
if (
|
this._operator === CustomFieldQueryLogicalOperator.Not &&
|
||||||
this._operator === CustomFieldQueryLogicalOperator.Not &&
|
value.length === 1
|
||||||
value.length === 1
|
) {
|
||||||
) {
|
value = value[0]
|
||||||
value = value[0]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
value = value.serialize()
|
|
||||||
}
|
}
|
||||||
return [this._operator, value]
|
return [this._operator, value]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user