425 lines
13 KiB
TypeScript
425 lines
13 KiB
TypeScript
import { getDisplayProcessor, getRawDisplayProcessor } from './displayProcessor';
|
|
import { DisplayProcessor, DisplayValue } from '../types/displayValue';
|
|
import { MappingType, ValueMapping } from '../types/valueMapping';
|
|
import { FieldConfig, FieldType, ThresholdsMode } from '../types';
|
|
import { systemDateFormats } from '../datetime';
|
|
import { createTheme } from '../themes';
|
|
|
|
function getDisplayProcessorFromConfig(config: FieldConfig) {
|
|
return getDisplayProcessor({
|
|
field: {
|
|
config,
|
|
type: FieldType.number,
|
|
},
|
|
theme: createTheme(),
|
|
});
|
|
}
|
|
|
|
function assertSame(input: any, processors: DisplayProcessor[], match: DisplayValue) {
|
|
processors.forEach((processor) => {
|
|
const value = processor(input);
|
|
for (const key of Object.keys(match)) {
|
|
expect((value as any)[key]).toEqual((match as any)[key]);
|
|
}
|
|
});
|
|
}
|
|
|
|
describe('Process simple display values', () => {
|
|
// Don't test float values here since the decimal formatting changes
|
|
const processors = [
|
|
// Without options, this shortcuts to a much easier implementation
|
|
getDisplayProcessor({ field: { config: {} }, theme: createTheme() }),
|
|
|
|
// Add a simple option that is not used (uses a different base class)
|
|
getDisplayProcessorFromConfig({ min: 0, max: 100 }),
|
|
|
|
// Add a simple option that is not used (uses a different base class)
|
|
getDisplayProcessorFromConfig({ unit: 'locale' }),
|
|
];
|
|
|
|
it('support null', () => {
|
|
assertSame(null, processors, { text: '', numeric: NaN });
|
|
});
|
|
|
|
it('support undefined', () => {
|
|
assertSame(undefined, processors, { text: '', numeric: NaN });
|
|
});
|
|
|
|
it('support NaN', () => {
|
|
assertSame(NaN, processors, { text: 'NaN', numeric: NaN });
|
|
});
|
|
|
|
it('Integer', () => {
|
|
assertSame(3, processors, { text: '3', numeric: 3 });
|
|
});
|
|
|
|
it('Text to number', () => {
|
|
assertSame('3', processors, { text: '3', numeric: 3 });
|
|
});
|
|
|
|
it('Empty string is NaN', () => {
|
|
assertSame('', processors, { text: '', numeric: NaN });
|
|
});
|
|
|
|
it('Simple String', () => {
|
|
assertSame('hello', processors, { text: 'hello', numeric: NaN });
|
|
});
|
|
|
|
it('empty array', () => {
|
|
assertSame([], processors, { text: '', numeric: NaN });
|
|
});
|
|
|
|
it('array of text', () => {
|
|
assertSame(['a', 'b', 'c'], processors, { text: 'a,b,c', numeric: NaN });
|
|
});
|
|
|
|
it('array of numbers', () => {
|
|
assertSame([1, 2, 3], processors, { text: '1,2,3', numeric: NaN });
|
|
});
|
|
|
|
it('empty object', () => {
|
|
assertSame({}, processors, { text: '[object Object]', numeric: NaN });
|
|
});
|
|
|
|
it('boolean true', () => {
|
|
assertSame(true, processors, { text: 'true', numeric: 1 });
|
|
});
|
|
|
|
it('boolean false', () => {
|
|
assertSame(false, processors, { text: 'false', numeric: 0 });
|
|
});
|
|
});
|
|
|
|
describe('Process null values', () => {
|
|
const processors = [
|
|
getDisplayProcessorFromConfig({
|
|
min: 0,
|
|
max: 100,
|
|
thresholds: {
|
|
mode: ThresholdsMode.Absolute,
|
|
steps: [
|
|
{ value: -Infinity, color: '#000' },
|
|
{ value: 0, color: '#100' },
|
|
{ value: 100, color: '#200' },
|
|
],
|
|
},
|
|
}),
|
|
];
|
|
|
|
it('Null should get -Infinity (base) color', () => {
|
|
assertSame(null, processors, { text: '', numeric: NaN, color: '#000' });
|
|
});
|
|
});
|
|
|
|
describe('Format value', () => {
|
|
it('should return if value isNaN', () => {
|
|
const valueMappings: ValueMapping[] = [];
|
|
const value = 'N/A';
|
|
const instance = getDisplayProcessorFromConfig({ mappings: valueMappings });
|
|
|
|
const result = instance(value);
|
|
|
|
expect(result.text).toEqual('N/A');
|
|
});
|
|
|
|
it('should return formatted value if there are no value mappings', () => {
|
|
const valueMappings: ValueMapping[] = [];
|
|
const value = '6';
|
|
|
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
|
|
|
const result = instance(value);
|
|
|
|
expect(result.text).toEqual('6.0');
|
|
});
|
|
|
|
it('should return formatted value if there are no matching value mappings', () => {
|
|
const valueMappings: ValueMapping[] = [
|
|
{ type: MappingType.ValueToText, options: { '11': { text: 'elva' } } },
|
|
{ type: MappingType.RangeToText, options: { from: 1, to: 9, result: { text: '1-9' } } },
|
|
];
|
|
|
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
|
const result = instance('10');
|
|
|
|
expect(result.text).toEqual('10.0');
|
|
});
|
|
|
|
it('should return mapped value if there are matching value mappings', () => {
|
|
const valueMappings: ValueMapping[] = [
|
|
{ type: MappingType.ValueToText, options: { '11': { text: 'elva' } } },
|
|
{ type: MappingType.RangeToText, options: { from: 1, to: 9, result: { text: '1-9' } } },
|
|
];
|
|
|
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
|
const result = instance('11');
|
|
|
|
expect(result.text).toEqual('elva');
|
|
});
|
|
|
|
it('should replace a matching regex', () => {
|
|
const valueMappings: ValueMapping[] = [
|
|
{ type: MappingType.RegexToText, options: { pattern: '([^.]*).example.com', result: { text: '$1' } } },
|
|
];
|
|
|
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
|
const result = instance('hostname.example.com');
|
|
|
|
expect(result.text).toEqual('hostname');
|
|
});
|
|
|
|
it('should not replace a non-matching regex', () => {
|
|
const valueMappings: ValueMapping[] = [
|
|
{ type: MappingType.RegexToText, options: { pattern: '([^.]*).example.com', result: { text: '$1' } } },
|
|
];
|
|
|
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
|
const result = instance('hostname.acme.com');
|
|
|
|
expect(result.text).toEqual('hostname.acme.com');
|
|
});
|
|
|
|
it('should empty a matching regex without replacement', () => {
|
|
const valueMappings: ValueMapping[] = [
|
|
{ type: MappingType.RegexToText, options: { pattern: '([^.]*).example.com', result: { text: '' } } },
|
|
];
|
|
|
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
|
const result = instance('hostname.example.com');
|
|
|
|
expect(result.text).toEqual('');
|
|
});
|
|
|
|
it('should not empty a non-matching regex', () => {
|
|
const valueMappings: ValueMapping[] = [
|
|
{ type: MappingType.RegexToText, options: { pattern: '([^.]*).example.com', result: { text: '' } } },
|
|
];
|
|
|
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
|
const result = instance('hostname.acme.com');
|
|
|
|
expect(result.text).toEqual('hostname.acme.com');
|
|
});
|
|
|
|
it('should return value with color if mapping has color', () => {
|
|
const valueMappings: ValueMapping[] = [{ type: MappingType.ValueToText, options: { Low: { color: 'red' } } }];
|
|
|
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
|
const result = instance('Low');
|
|
|
|
expect(result.text).toEqual('Low');
|
|
expect(result.color).toEqual('#F2495C');
|
|
});
|
|
|
|
it('should return mapped value and leave numeric value in tact if value mapping maps to empty string', () => {
|
|
const valueMappings: ValueMapping[] = [{ type: MappingType.ValueToText, options: { '1': { text: '' } } }];
|
|
const value = '1';
|
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings });
|
|
|
|
expect(instance(value).text).toEqual('');
|
|
expect(instance(value).numeric).toEqual(1);
|
|
});
|
|
|
|
it('should not map 1kW to the value for 1W', () => {
|
|
const valueMappings: ValueMapping[] = [{ type: MappingType.ValueToText, options: { '1': { text: 'mapped' } } }];
|
|
const value = '1000';
|
|
const instance = getDisplayProcessorFromConfig({ decimals: 1, mappings: valueMappings, unit: 'watt' });
|
|
|
|
const result = instance(value);
|
|
|
|
expect(result.text).toEqual('1.0');
|
|
});
|
|
|
|
it('With null value and thresholds should use base color', () => {
|
|
const instance = getDisplayProcessorFromConfig({
|
|
thresholds: {
|
|
mode: ThresholdsMode.Absolute,
|
|
steps: [{ value: -Infinity, color: '#AAA' }],
|
|
},
|
|
});
|
|
const disp = instance(null);
|
|
expect(disp.text).toEqual('');
|
|
expect(disp.color).toEqual('#AAA');
|
|
});
|
|
|
|
//
|
|
// Below is current behavior but it's clearly not working great
|
|
//
|
|
|
|
it('with value 1000 and unit short', () => {
|
|
const value = 1000;
|
|
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' });
|
|
const disp = instance(value);
|
|
expect(disp.text).toEqual('1');
|
|
expect(disp.suffix).toEqual(' K');
|
|
});
|
|
|
|
it('with value 1200 and unit short', () => {
|
|
const value = 1200;
|
|
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' });
|
|
const disp = instance(value);
|
|
expect(disp.text).toEqual('1.20');
|
|
expect(disp.suffix).toEqual(' K');
|
|
});
|
|
|
|
it('with value 1250 and unit short', () => {
|
|
const value = 1250;
|
|
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' });
|
|
const disp = instance(value);
|
|
expect(disp.text).toEqual('1.25');
|
|
expect(disp.suffix).toEqual(' K');
|
|
});
|
|
|
|
it('with value 10000000 and unit short', () => {
|
|
const value = 1000000;
|
|
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' });
|
|
const disp = instance(value);
|
|
expect(disp.text).toEqual('1');
|
|
expect(disp.suffix).toEqual(' Mil');
|
|
});
|
|
|
|
it('with value 15000000 and unit short', () => {
|
|
const value = 1500000;
|
|
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'short' });
|
|
const disp = instance(value);
|
|
expect(disp.text).toEqual('1.50');
|
|
expect(disp.suffix).toEqual(' Mil');
|
|
});
|
|
|
|
it('with value 128000000 and unit bytes', () => {
|
|
const value = 1280000125;
|
|
const instance = getDisplayProcessorFromConfig({ decimals: null, unit: 'bytes' });
|
|
const disp = instance(value);
|
|
expect(disp.text).toEqual('1.19');
|
|
expect(disp.suffix).toEqual(' GiB');
|
|
});
|
|
});
|
|
|
|
describe('Date display options', () => {
|
|
it('should format UTC dates', () => {
|
|
const processor = getDisplayProcessor({
|
|
timeZone: 'utc',
|
|
field: {
|
|
type: FieldType.time,
|
|
config: {
|
|
unit: 'xyz', // ignore non-date formats
|
|
},
|
|
},
|
|
theme: createTheme(),
|
|
});
|
|
expect(processor(0).text).toEqual('1970-01-01 00:00:00');
|
|
});
|
|
|
|
it('should pick configured time format', () => {
|
|
const processor = getDisplayProcessor({
|
|
timeZone: 'utc',
|
|
field: {
|
|
type: FieldType.time,
|
|
config: {
|
|
unit: 'dateTimeAsUS', // ignore non-date formats
|
|
},
|
|
},
|
|
theme: createTheme(),
|
|
});
|
|
expect(processor(0).text).toEqual('01/01/1970 12:00:00 am');
|
|
});
|
|
|
|
it('respect the configured date format', () => {
|
|
const processor = getDisplayProcessor({
|
|
timeZone: 'utc',
|
|
field: {
|
|
type: FieldType.time,
|
|
config: {
|
|
unit: 'time:YYYY', // ignore non-date formats
|
|
},
|
|
},
|
|
theme: createTheme(),
|
|
});
|
|
expect(processor(0).text).toEqual('1970');
|
|
});
|
|
|
|
it('Should use system date format by default', () => {
|
|
const currentFormat = systemDateFormats.fullDate;
|
|
systemDateFormats.fullDate = 'YYYY-MM';
|
|
|
|
const processor = getDisplayProcessor({
|
|
timeZone: 'utc',
|
|
field: {
|
|
type: FieldType.time,
|
|
config: {},
|
|
},
|
|
theme: createTheme(),
|
|
});
|
|
|
|
expect(processor(0).text).toEqual('1970-01');
|
|
|
|
systemDateFormats.fullDate = currentFormat;
|
|
});
|
|
|
|
it('should handle ISO string dates', () => {
|
|
const processor = getDisplayProcessor({
|
|
timeZone: 'utc',
|
|
field: {
|
|
type: FieldType.time,
|
|
config: {},
|
|
},
|
|
theme: createTheme(),
|
|
});
|
|
|
|
expect(processor('2020-08-01T08:48:43.783337Z').text).toEqual('2020-08-01 08:48:43');
|
|
});
|
|
|
|
describe('number formatting for string values', () => {
|
|
it('should preserve string unchanged if unit is strings', () => {
|
|
const processor = getDisplayProcessor({
|
|
field: {
|
|
type: FieldType.string,
|
|
config: { unit: 'string' },
|
|
},
|
|
theme: createTheme(),
|
|
});
|
|
expect(processor('22.1122334455').text).toEqual('22.1122334455');
|
|
});
|
|
|
|
it('should format string as number if no unit', () => {
|
|
const processor = getDisplayProcessor({
|
|
field: {
|
|
type: FieldType.string,
|
|
config: { decimals: 2 },
|
|
},
|
|
theme: createTheme(),
|
|
});
|
|
expect(processor('22.1122334455').text).toEqual('22.11');
|
|
|
|
// Support empty/missing strings
|
|
expect(processor(undefined).text).toEqual('');
|
|
expect(processor(null).text).toEqual('');
|
|
expect(processor('').text).toEqual('');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('getRawDisplayProcessor', () => {
|
|
const processor = getRawDisplayProcessor();
|
|
const date = new Date('2020-01-01T00:00:00.000Z');
|
|
const timestamp = date.valueOf();
|
|
|
|
it.each`
|
|
value | expected
|
|
${0} | ${'0'}
|
|
${13.37} | ${'13.37'}
|
|
${true} | ${'true'}
|
|
${false} | ${'false'}
|
|
${date} | ${`${date}`}
|
|
${timestamp} | ${'1577836800000'}
|
|
${'a string'} | ${'a string'}
|
|
${null} | ${'null'}
|
|
${undefined} | ${'undefined'}
|
|
${{ value: 0, label: 'a label' }} | ${'[object Object]'}
|
|
`('when called with value:{$value}', ({ value, expected }) => {
|
|
const result = processor(value);
|
|
|
|
expect(result).toEqual({ text: expected, numeric: null });
|
|
});
|
|
});
|