@blueprintjs/table
Version:
Scalable interactive table component
309 lines (263 loc) • 13.1 kB
text/typescript
/*
* Copyright 2016 Palantir Technologies, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, expect, it } from "@blueprintjs/test-commons/vitest";
import { Utils } from "./utils";
describe("Utils", () => {
describe("toBase26Alpha", () => {
it("converts to spreadsheet-like base26", () => {
expect(Utils.toBase26Alpha(0)).to.equal("A");
expect(Utils.toBase26Alpha(25)).to.equal("Z");
expect(Utils.toBase26Alpha(26)).to.equal("AA");
expect(Utils.toBase26Alpha(27)).to.equal("AB");
expect(Utils.toBase26Alpha((26 + 1) * 26 - 1)).to.equal("ZZ");
expect(Utils.toBase26Alpha((26 + 1) * 26)).to.equal("AAA");
});
});
describe("toBase26CellName", () => {
it("converts to spreadsheet-like base26 cell names", () => {
expect(Utils.toBase26CellName(0, 0)).to.equal("A1");
expect(Utils.toBase26CellName(25, 0)).to.equal("A26");
expect(Utils.toBase26CellName(0, 25)).to.equal("Z1");
expect(Utils.toBase26CellName(1, 27)).to.equal("AB2");
expect(Utils.toBase26CellName(99, (26 + 1) * 26 - 1)).to.equal("ZZ100");
expect(Utils.toBase26CellName(998, (26 + 1) * 26)).to.equal("AAA999");
});
});
describe("binarySearch", () => {
it("returns 0 for empty list", () => {
const arr = [] as number[];
const lookup = (i: number) => arr[i];
expect(Utils.binarySearch(10, 0, lookup)).to.equal(0);
expect(Utils.binarySearch(0, 0, lookup)).to.equal(0);
expect(Utils.binarySearch(-10, 0, lookup)).to.equal(0);
});
it("returns max index if number is high", () => {
const arr = [10, 20, 30, 30, 40];
const lookup = (i: number) => arr[i];
expect(Utils.binarySearch(1000, arr.length, lookup)).to.equal(arr.length);
});
it("returns min index if number is low", () => {
const arr = [10, 20, 30, 30, 40];
const lookup = (i: number) => arr[i];
expect(Utils.binarySearch(-1000, arr.length, lookup)).to.equal(0);
});
it("returns index of exact match", () => {
const arr = [10, 20, 30, 30, 40];
const lookup = (i: number) => arr[i];
expect(Utils.binarySearch(20, arr.length, lookup)).to.equal(1);
expect(Utils.binarySearch(40, arr.length, lookup)).to.equal(4);
});
it("returns lowest index of multiple exact matches", () => {
expect(Utils.binarySearch(30, 5, (i: number) => [10, 20, 30, 30, 40][i])).to.equal(2);
expect(Utils.binarySearch(30, 5, (i: number) => [10, 11, 12, 30, 30][i])).to.equal(3);
expect(Utils.binarySearch(30, 5, (i: number) => [30, 30, 30, 50, 60][i])).to.equal(0);
});
it("returns insertion index if no match", () => {
const arr = [10, 20, 30, 30, 40];
const lookup = (i: number) => arr[i];
expect(Utils.binarySearch(19, arr.length, lookup)).to.equal(1);
expect(Utils.binarySearch(21, arr.length, lookup)).to.equal(2);
});
});
describe("times", () => {
it("returns empty array for 0", () => {
const arr = Utils.times(0, () => "test");
expect(arr).to.deep.equal([]);
});
it("throws error for negative numbers", () => {
expect(() => Utils.times(-5, () => "test")).to.throw();
});
it("returns array of correct length", () => {
const arr = Utils.times(4, () => "test");
expect(arr).to.deep.equal(["test", "test", "test", "test"]);
});
it("works for high numbers of elements without throwing an error", () => {
const HUGE_NUMBER = 3e6;
expect(() => Utils.times(HUGE_NUMBER, () => "test")).to.not.throw();
});
it("uses argument length", () => {
const arr = Utils.times(4, (i: number) => "test" + i);
expect(arr).to.deep.equal(["test0", "test1", "test2", "test3"]);
});
});
describe("assignSparseValues", () => {
it("compares array lengths", () => {
const defaults = Utils.times(3, () => "A");
expect(Utils.assignSparseValues(defaults, ["B"])).to.equal(defaults);
});
it("overrides with sparse values", () => {
const defaults = Utils.times(3, () => "A");
const result = Utils.assignSparseValues(defaults, [null, "B", null]);
expect(result).to.deep.equal(["A", "B", "A"]);
});
});
describe("guideIndexToReorderedIndex", () => {
describe("leaving the thing in place", () => {
runTest(0, 0, 1, 0);
runTest(1, 1, 2, 1);
runTest(10, 10, 5, 10);
});
describe("moving the thing one place to the right", () => {
runTest(0, 2, 1, 1);
runTest(0, 3, 2, 1);
runTest(0, 6, 5, 1);
});
// test moving the thing one place to the left
describe("moving the thing one place to the left", () => {
runTest(1, 0, 1, 0);
runTest(4, 3, 2, 3);
runTest(20, 19, 5, 19);
});
describe("moving the thing two places to the right", () => {
runTest(0, 3, 1, 2);
runTest(4, 8, 2, 6);
runTest(10, 17, 5, 12);
});
describe("moving the thing two places to the left", () => {
runTest(2, 0, 1, 0);
runTest(4, 2, 2, 2);
runTest(20, 18, 5, 18);
});
describe("moving the thing within itself (no-op)", () => {
runTest(2, 3, 2, 2);
runTest(10, 14, 5, 10);
});
function runTest(oldIndex: number, newIndex: number, length: number, expectedResult: number) {
it(`(oldIndex: ${oldIndex}, newIndex: ${newIndex}, length: ${length}) => ${expectedResult}`, () => {
const actualResult = Utils.guideIndexToReorderedIndex(oldIndex, newIndex, length);
expect(actualResult).to.equal(expectedResult);
});
}
});
describe("reorderedIndexToGuideIndex", () => {
describe("leaving the thing in place", () => {
runTest(0, 0, 1, 0);
runTest(1, 1, 2, 1);
runTest(10, 10, 5, 10);
});
describe("moving the thing one place to the right", () => {
runTest(0, 1, 1, 2);
runTest(0, 1, 2, 3);
runTest(0, 1, 5, 6);
});
describe("moving the thing one place to the left", () => {
runTest(1, 0, 1, 0);
runTest(4, 3, 2, 3);
runTest(20, 19, 5, 19);
});
describe("moving the thing two places to the right", () => {
runTest(0, 2, 1, 3);
runTest(4, 6, 2, 8);
runTest(10, 12, 5, 17);
});
describe("moving the thing two places to the left", () => {
runTest(2, 0, 1, 0);
runTest(4, 2, 2, 2);
runTest(20, 18, 5, 18);
});
function runTest(oldIndex: number, newIndex: number, length: number, expectedResult: number) {
it(`(oldIndex: ${oldIndex}, newIndex: ${newIndex}, length: ${length}) => ${expectedResult}`, () => {
const actualResult = Utils.reorderedIndexToGuideIndex(oldIndex, newIndex, length);
expect(actualResult).to.equal(expectedResult);
});
}
});
describe("isLeftClick", () => {
const LEFT_BUTTON_CODE = 0;
const RIGHT_BUTTON_CODE = 1;
it("returns true for left click", () => {
expect(Utils.isLeftClick({ button: LEFT_BUTTON_CODE } as any as MouseEvent)).to.be.true;
});
it("returns false for right click", () => {
expect(Utils.isLeftClick({ button: RIGHT_BUTTON_CODE } as any as MouseEvent)).to.be.false;
});
});
describe("reorderArray", () => {
const ARRAY_STRING = "ABCDEFG";
const ARRAY = ARRAY_STRING.split("");
const ARRAY_LENGTH = ARRAY.length;
const FIRST_INDEX = 0;
const LAST_INDEX = ARRAY_LENGTH - 1;
const LENGTH = 3;
describe("reorders a single element properly", () => {
it("when moved from index 0", () => {
assertArraysEqual(Utils.reorderArray(ARRAY, FIRST_INDEX, 2), "BCADEFG");
});
it("when moved from a middle index leftward", () => {
assertArraysEqual(Utils.reorderArray(ARRAY, 3, 1), "ADBCEFG");
});
it("when moved from a middle index rightward", () => {
assertArraysEqual(Utils.reorderArray(ARRAY, 3, 5), "ABCEFDG");
});
it("when moved from the end", () => {
assertArraysEqual(Utils.reorderArray(ARRAY, LAST_INDEX, LAST_INDEX - 2), "ABCDGEF");
});
});
describe("reorders multiple elements properly", () => {
it("when moved from index 0", () => {
assertArraysEqual(Utils.reorderArray(ARRAY, FIRST_INDEX, 2, LENGTH), "DEABCFG");
});
it("when moved from a middle index leftward", () => {
assertArraysEqual(Utils.reorderArray(ARRAY, 3, 1, LENGTH), "ADEFBCG");
});
it("when moved from a middle index rightward", () => {
assertArraysEqual(Utils.reorderArray(ARRAY, 2, 4, LENGTH), "ABFGCDE");
});
it("when moved from the end", () => {
const fromIndex = LAST_INDEX - LENGTH + 1; // the index that yields the last LENGTH elements
assertArraysEqual(Utils.reorderArray(ARRAY, fromIndex, fromIndex - 2, LENGTH), "ABEFGCD");
});
});
describe("edge cases", () => {
it("returns undefined if length < 0", () => {
expect(Utils.reorderArray(ARRAY, 0, 1, -1)).to.be.undefined;
});
it("returns undefined if length > array.length", () => {
expect(Utils.reorderArray(ARRAY, 0, 1, ARRAY_LENGTH + 1)).to.be.undefined;
});
it("returns undefined if from + length > array.length", () => {
const fromIndex = LAST_INDEX - LENGTH + 2; // one spot too far to the right
expect(Utils.reorderArray(ARRAY, fromIndex, fromIndex - 1, LENGTH)).to.be.undefined;
});
it("returns an unchanged copy of the array if length === 0", () => {
assertArraysEqual(Utils.reorderArray(ARRAY, 0, 1, 0), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, 0, 2, 0), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, 1, 3, 0), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, 3, 1, 0), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, LAST_INDEX, LAST_INDEX - 1, 0), ARRAY_STRING);
});
it("returns an unchanged copy of the array if length === array.length", () => {
assertArraysEqual(Utils.reorderArray(ARRAY, 0, 1, ARRAY_LENGTH), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, 0, 2, ARRAY_LENGTH), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, 1, 3, ARRAY_LENGTH), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, 3, 1, ARRAY_LENGTH), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, LAST_INDEX, LAST_INDEX - 1, ARRAY_LENGTH), ARRAY_STRING);
});
it("returns an unchanged copy of the array if from === to", () => {
assertArraysEqual(Utils.reorderArray(ARRAY, 0, 0), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, 1, 1), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, 3, 3, ARRAY_LENGTH), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, 4, 4, ARRAY_LENGTH), ARRAY_STRING);
assertArraysEqual(Utils.reorderArray(ARRAY, LAST_INDEX, LAST_INDEX), ARRAY_STRING);
});
});
function assertArraysEqual(result: string[] | undefined, expected: string) {
expect(result).not.to.be.undefined;
// use .eql to deeply compare arrays
expect(result!).to.eql(expected.split(""));
}
});
});