Full coverage, almost lost it

This commit is contained in:
shamoon
2024-09-14 23:43:31 -07:00
parent a25be37c8e
commit b0970adaca
3 changed files with 168 additions and 72 deletions

View File

@@ -8,6 +8,7 @@ import {
CustomFieldQueryLogicalOperator,
CustomFieldQueryOperator,
} from '../data/custom-field-query'
import { fakeAsync, tick } from '@angular/core/testing'
describe('CustomFieldQueryElement', () => {
it('should initialize with correct type and id', () => {
@@ -86,23 +87,95 @@ describe('CustomFieldQueryAtom', () => {
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', () => {
const atom = new CustomFieldQueryAtom([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', () => {
it('should initialize with correct operator and value', () => {
const expression = new CustomFieldQueryExpression([
CustomFieldQueryLogicalOperator.And,
[],
])
expect(expression.operator).toBe(CustomFieldQueryLogicalOperator.And)
it('should initialize with default operator and empty value', () => {
const expression = new CustomFieldQueryExpression()
expect(expression.operator).toBe(CustomFieldQueryLogicalOperator.Or)
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 atom = new CustomFieldQueryAtom([
1,
@@ -111,9 +184,14 @@ describe('CustomFieldQueryExpression', () => {
])
expression.addAtom(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 subExpression = new CustomFieldQueryExpression([
CustomFieldQueryLogicalOperator.Or,
@@ -121,19 +199,42 @@ describe('CustomFieldQueryExpression', () => {
])
expression.addExpression(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', () => {
const expression = new CustomFieldQueryExpression([
CustomFieldQueryLogicalOperator.And,
[[1, 'operator', 'value']],
[[1, 'exists', 'true']],
])
expect(expression.serialize()).toEqual([
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', () => {
const expression = new CustomFieldQueryExpression([
CustomFieldQueryLogicalOperator.Not,

View File

@@ -73,31 +73,20 @@ export class CustomFieldQueryAtom extends CustomFieldQueryElement {
this.value = null
} else {
if (!newTypes.includes(typeof this.value)) {
if (newTypes.length === 1) {
switch (newTypes[0]) {
case 'string':
this.value = ''
break
case 'boolean':
this.value = 'true'
break
case 'array':
this.value = []
break
default:
this.value = null
break
}
} else {
if (newTypes.includes('number')) {
try {
this.value = parseFloat(this.value as string).toString()
} catch (e) {
this.value = null
}
} else {
this.value = null
}
switch (newTypes[0]) {
case 'string':
this.value = ''
break
case 'boolean':
this.value = 'true'
break
case 'array':
this.value = []
break
case 'number':
const num = parseFloat(this.value as string)
this.value = isNaN(num) ? null : num.toString()
break
}
} else if (
['true', 'false'].includes(this.value as string) &&
@@ -133,6 +122,8 @@ export class CustomFieldQueryAtom extends CustomFieldQueryElement {
}
export class CustomFieldQueryExpression extends CustomFieldQueryElement {
protected _value: string[] | number[] | CustomFieldQueryElement[]
constructor(
expressionArray: [CustomFieldQueryLogicalOperator, any[]] = [
CustomFieldQueryLogicalOperator.Or,
@@ -174,17 +165,13 @@ export class CustomFieldQueryExpression extends CustomFieldQueryElement {
public override serialize() {
let value
if (this._value instanceof Array) {
value = this._value.map((atom) => atom.serialize())
// If the expression is negated it should have only one child which is an expression
if (
this._operator === CustomFieldQueryLogicalOperator.Not &&
value.length === 1
) {
value = value[0]
}
} else {
value = value.serialize()
value = this._value.map((element) => element.serialize())
// If the expression is negated it should have only one child which is an expression
if (
this._operator === CustomFieldQueryLogicalOperator.Not &&
value.length === 1
) {
value = value[0]
}
return [this._operator, value]
}