332 lines
9.3 KiB
TypeScript
332 lines
9.3 KiB
TypeScript
/*
|
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
* or more contributor license agreements. Licensed under the Elastic License
|
|
* 2.0; you may not use this file except in compliance with the Elastic License
|
|
* 2.0.
|
|
*/
|
|
|
|
import { getSortingCriteria } from './sorting';
|
|
import { FieldFormat } from 'src/plugins/data/public';
|
|
import { DatatableColumnType } from 'src/plugins/expressions';
|
|
|
|
function getMockFormatter() {
|
|
return { convert: (v: unknown) => `${v as string}` } as FieldFormat;
|
|
}
|
|
|
|
function testSorting({
|
|
input,
|
|
output,
|
|
direction,
|
|
type,
|
|
keepLast,
|
|
reverseOutput = true,
|
|
}: {
|
|
input: unknown[];
|
|
output: unknown[];
|
|
direction: 'asc' | 'desc';
|
|
type: DatatableColumnType | 'range';
|
|
keepLast?: boolean; // special flag to handle values that should always be last no matter the direction
|
|
reverseOutput?: boolean;
|
|
}) {
|
|
const datatable = input.map((v) => ({
|
|
a: v,
|
|
}));
|
|
const sorted = output.map((v) => ({ a: v }));
|
|
if (direction === 'desc' && reverseOutput) {
|
|
sorted.reverse();
|
|
if (keepLast) {
|
|
// Cycle shift of the first element
|
|
const firstEl = sorted.shift()!;
|
|
sorted.push(firstEl);
|
|
}
|
|
}
|
|
const criteria = getSortingCriteria(type, 'a', getMockFormatter(), direction);
|
|
expect(datatable.sort(criteria)).toEqual(sorted);
|
|
}
|
|
|
|
describe('Data sorting criteria', () => {
|
|
describe('Numeric values', () => {
|
|
for (const direction of ['asc', 'desc'] as const) {
|
|
it(`should provide the number criteria of numeric values (${direction})`, () => {
|
|
testSorting({
|
|
input: [7, 6, 5, -Infinity, Infinity],
|
|
output: [-Infinity, 5, 6, 7, Infinity],
|
|
direction,
|
|
type: 'number',
|
|
});
|
|
});
|
|
|
|
it(`should provide the number criteria for date values (${direction})`, () => {
|
|
const now = Date.now();
|
|
testSorting({
|
|
input: [now, 0, now - 150000],
|
|
output: [0, now - 150000, now],
|
|
direction,
|
|
type: 'date',
|
|
});
|
|
});
|
|
}
|
|
|
|
it(`should sort undefined and null to the end`, () => {
|
|
const now = Date.now();
|
|
testSorting({
|
|
input: [null, now, 0, undefined, null, now - 150000],
|
|
output: [0, now - 150000, now, null, undefined, null],
|
|
direction: 'asc',
|
|
type: 'date',
|
|
reverseOutput: false,
|
|
});
|
|
|
|
testSorting({
|
|
input: [null, now, 0, undefined, null, now - 150000],
|
|
output: [now, now - 150000, 0, null, undefined, null],
|
|
direction: 'desc',
|
|
type: 'date',
|
|
reverseOutput: false,
|
|
});
|
|
});
|
|
|
|
it(`should sort NaN to the end`, () => {
|
|
const now = Date.now();
|
|
testSorting({
|
|
input: [null, now, 0, undefined, Number.NaN, now - 150000],
|
|
output: [0, now - 150000, now, null, undefined, Number.NaN],
|
|
direction: 'asc',
|
|
type: 'number',
|
|
reverseOutput: false,
|
|
});
|
|
|
|
testSorting({
|
|
input: [null, now, 0, undefined, Number.NaN, now - 150000],
|
|
output: [now, now - 150000, 0, null, undefined, Number.NaN],
|
|
direction: 'desc',
|
|
type: 'number',
|
|
reverseOutput: false,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('String or anything else as string', () => {
|
|
for (const direction of ['asc', 'desc'] as const) {
|
|
it(`should provide the string criteria for terms values (${direction})`, () => {
|
|
testSorting({
|
|
input: ['a', 'b', 'c', 'd', '12'],
|
|
output: ['12', 'a', 'b', 'c', 'd'],
|
|
direction,
|
|
type: 'string',
|
|
});
|
|
});
|
|
|
|
it(`should provide the string criteria for other types of values (${direction})`, () => {
|
|
testSorting({
|
|
input: [true, false, false],
|
|
output: [false, false, true],
|
|
direction,
|
|
type: 'boolean',
|
|
});
|
|
});
|
|
}
|
|
|
|
it('should sort undefined and null to the end', () => {
|
|
testSorting({
|
|
input: ['a', null, 'b', 'c', undefined, 'd', '12'],
|
|
output: ['12', 'a', 'b', 'c', 'd', null, undefined],
|
|
direction: 'asc',
|
|
type: 'string',
|
|
reverseOutput: false,
|
|
});
|
|
|
|
testSorting({
|
|
input: ['a', null, 'b', 'c', undefined, 'd', '12'],
|
|
output: ['d', 'c', 'b', 'a', '12', null, undefined],
|
|
direction: 'desc',
|
|
type: 'string',
|
|
reverseOutput: false,
|
|
});
|
|
|
|
testSorting({
|
|
input: [true, null, false, undefined, false],
|
|
output: [false, false, true, null, undefined],
|
|
direction: 'asc',
|
|
type: 'boolean',
|
|
reverseOutput: false,
|
|
});
|
|
testSorting({
|
|
input: [true, null, false, undefined, false],
|
|
output: [true, false, false, null, undefined],
|
|
direction: 'desc',
|
|
type: 'boolean',
|
|
reverseOutput: false,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('IP sorting', () => {
|
|
for (const direction of ['asc', 'desc'] as const) {
|
|
it(`should provide the IP criteria for IP values (IPv4 only values) - ${direction}`, () => {
|
|
testSorting({
|
|
input: ['127.0.0.1', '192.168.1.50', '200.100.100.10', '10.0.1.76', '8.8.8.8'],
|
|
output: ['8.8.8.8', '10.0.1.76', '127.0.0.1', '192.168.1.50', '200.100.100.10'],
|
|
direction,
|
|
type: 'ip',
|
|
});
|
|
});
|
|
|
|
it(`should provide the IP criteria for IP values (IPv6 only values) - ${direction}`, () => {
|
|
testSorting({
|
|
input: [
|
|
'fc00::123',
|
|
'::1',
|
|
'2001:0db8:85a3:0000:0000:8a2e:0370:7334',
|
|
'2001:db8:1234:0000:0000:0000:0000:0000',
|
|
'2001:db8:1234::', // equivalent to the above
|
|
],
|
|
output: [
|
|
'::1',
|
|
'2001:db8:1234::',
|
|
'2001:db8:1234:0000:0000:0000:0000:0000',
|
|
'2001:0db8:85a3:0000:0000:8a2e:0370:7334',
|
|
'fc00::123',
|
|
],
|
|
direction,
|
|
type: 'ip',
|
|
});
|
|
});
|
|
|
|
it(`should provide the IP criteria for IP values (mixed values) - ${direction}`, () => {
|
|
// A mix of IPv4, IPv6, IPv4 mapped to IPv6
|
|
testSorting({
|
|
input: [
|
|
'fc00::123',
|
|
'192.168.1.50',
|
|
'::FFFF:192.168.1.50', // equivalent to the above with the IPv6 mapping
|
|
'10.0.1.76',
|
|
'8.8.8.8',
|
|
'::1',
|
|
],
|
|
output: [
|
|
'::1',
|
|
'8.8.8.8',
|
|
'10.0.1.76',
|
|
'192.168.1.50',
|
|
'::FFFF:192.168.1.50',
|
|
'fc00::123',
|
|
],
|
|
direction,
|
|
type: 'ip',
|
|
});
|
|
});
|
|
|
|
it(`should provide the IP criteria for IP values (mixed values with invalid "Other" field) - ${direction}`, () => {
|
|
testSorting({
|
|
input: ['fc00::123', '192.168.1.50', 'Other', '10.0.1.76', '8.8.8.8', '::1'],
|
|
output: ['::1', '8.8.8.8', '10.0.1.76', '192.168.1.50', 'fc00::123', 'Other'],
|
|
direction,
|
|
type: 'ip',
|
|
keepLast: true,
|
|
});
|
|
});
|
|
}
|
|
|
|
it('should sort undefined and null to the end', () => {
|
|
testSorting({
|
|
input: [
|
|
'fc00::123',
|
|
'192.168.1.50',
|
|
null,
|
|
undefined,
|
|
'Other',
|
|
'10.0.1.76',
|
|
'8.8.8.8',
|
|
'::1',
|
|
],
|
|
output: [
|
|
'::1',
|
|
'8.8.8.8',
|
|
'10.0.1.76',
|
|
'192.168.1.50',
|
|
'fc00::123',
|
|
'Other',
|
|
null,
|
|
undefined,
|
|
],
|
|
direction: 'asc',
|
|
type: 'ip',
|
|
reverseOutput: false,
|
|
});
|
|
|
|
testSorting({
|
|
input: [
|
|
'fc00::123',
|
|
'192.168.1.50',
|
|
null,
|
|
undefined,
|
|
'Other',
|
|
'10.0.1.76',
|
|
'8.8.8.8',
|
|
'::1',
|
|
],
|
|
output: [
|
|
'fc00::123',
|
|
'192.168.1.50',
|
|
'10.0.1.76',
|
|
'8.8.8.8',
|
|
'::1',
|
|
'Other',
|
|
null,
|
|
undefined,
|
|
],
|
|
direction: 'desc',
|
|
type: 'ip',
|
|
reverseOutput: false,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Range sorting', () => {
|
|
for (const direction of ['asc', 'desc'] as const) {
|
|
it(`should sort closed ranges - ${direction}`, () => {
|
|
testSorting({
|
|
input: [
|
|
{ gte: 1, lt: 5 },
|
|
{ gte: 0, lt: 5 },
|
|
{ gte: 0, lt: 1 },
|
|
],
|
|
output: [
|
|
{ gte: 0, lt: 1 },
|
|
{ gte: 0, lt: 5 },
|
|
{ gte: 1, lt: 5 },
|
|
],
|
|
direction,
|
|
type: 'range',
|
|
});
|
|
});
|
|
|
|
it(`should sort open ranges - ${direction}`, () => {
|
|
testSorting({
|
|
input: [{ gte: 1, lt: 5 }, { gte: 0, lt: 5 }, { gte: 0 }],
|
|
output: [{ gte: 0, lt: 5 }, { gte: 0 }, { gte: 1, lt: 5 }],
|
|
direction,
|
|
type: 'range',
|
|
});
|
|
});
|
|
}
|
|
|
|
it('should sort undefined and null to the end', () => {
|
|
testSorting({
|
|
input: [{ gte: 1, lt: 5 }, undefined, { gte: 0, lt: 5 }, null, { gte: 0 }],
|
|
output: [{ gte: 0, lt: 5 }, { gte: 0 }, { gte: 1, lt: 5 }, undefined, null],
|
|
direction: 'asc',
|
|
type: 'range',
|
|
reverseOutput: false,
|
|
});
|
|
testSorting({
|
|
input: [{ gte: 1, lt: 5 }, undefined, { gte: 0, lt: 5 }, null, { gte: 0 }],
|
|
output: [{ gte: 1, lt: 5 }, { gte: 0 }, { gte: 0, lt: 5 }, undefined, null],
|
|
direction: 'desc',
|
|
type: 'range',
|
|
reverseOutput: false,
|
|
});
|
|
});
|
|
});
|
|
});
|