625 lines
24 KiB
JavaScript
625 lines
24 KiB
JavaScript
// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
// ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃
|
|
// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃
|
|
// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃
|
|
// ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃
|
|
// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
|
|
// ┃ Copyright (c) 2017, the Perspective Authors. ┃
|
|
// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
|
|
// ┃ This file is part of the Perspective library, distributed under the terms ┃
|
|
// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
|
|
// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
|
|
|
|
import { test, expect } from "@perspective-dev/test";
|
|
import perspective from "./perspective_client";
|
|
|
|
const data = {
|
|
w: [1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5],
|
|
x: [1, 2, 3, 4, 4, 3, 2, 1],
|
|
y: ["a", "b", "c", "d", "a", "b", "c", "d"],
|
|
z: [true, false, true, false, true, false, true, false],
|
|
};
|
|
|
|
((perspective) => {
|
|
test.describe("group_rollup_mode", function () {
|
|
test.describe("flat", function () {
|
|
test("only emits leaves", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y"],
|
|
group_rollup_mode: "flat",
|
|
});
|
|
const json = await view.to_json();
|
|
expect(json).toStrictEqual([
|
|
{ __ROW_PATH__: ["a"], w: 7, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["b"], w: 9, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["c"], w: 11, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["d"], w: 13, x: 5, y: 2, z: 2 },
|
|
]);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("num_rows returns leaf count", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y"],
|
|
group_rollup_mode: "flat",
|
|
});
|
|
const num_rows = await view.num_rows();
|
|
expect(num_rows).toEqual(4);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("to_columns works", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y"],
|
|
group_rollup_mode: "flat",
|
|
});
|
|
const cols = await view.to_columns();
|
|
expect(cols).toStrictEqual({
|
|
__ROW_PATH__: [["a"], ["b"], ["c"], ["d"]],
|
|
w: [7, 9, 11, 13],
|
|
x: [5, 5, 5, 5],
|
|
y: [2, 2, 2, 2],
|
|
z: [2, 2, 2, 2],
|
|
});
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("sort asc", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y"],
|
|
group_rollup_mode: "flat",
|
|
sort: [["w", "asc"]],
|
|
});
|
|
const cols = await view.to_columns();
|
|
expect(cols).toStrictEqual({
|
|
__ROW_PATH__: [["a"], ["b"], ["c"], ["d"]],
|
|
w: [7, 9, 11, 13],
|
|
x: [5, 5, 5, 5],
|
|
y: [2, 2, 2, 2],
|
|
z: [2, 2, 2, 2],
|
|
});
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("sort desc", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y"],
|
|
group_rollup_mode: "flat",
|
|
sort: [["w", "desc"]],
|
|
});
|
|
const cols = await view.to_columns();
|
|
expect(cols).toStrictEqual({
|
|
__ROW_PATH__: [["d"], ["c"], ["b"], ["a"]],
|
|
w: [13, 11, 9, 7],
|
|
x: [5, 5, 5, 5],
|
|
y: [2, 2, 2, 2],
|
|
z: [2, 2, 2, 2],
|
|
});
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("sort with hidden column", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
columns: ["y"],
|
|
group_by: ["y"],
|
|
group_rollup_mode: "flat",
|
|
sort: [["x", "desc"]],
|
|
});
|
|
const cols = await view.to_columns();
|
|
expect(cols).toStrictEqual({
|
|
__ROW_PATH__: [["a"], ["b"], ["c"], ["d"]],
|
|
y: [2, 2, 2, 2],
|
|
});
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("multi-level group_by", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y", "z"],
|
|
group_rollup_mode: "flat",
|
|
});
|
|
const json = await view.to_json();
|
|
expect(json).toStrictEqual([
|
|
{
|
|
__ROW_PATH__: ["a", true],
|
|
w: 7,
|
|
x: 5,
|
|
y: 2,
|
|
z: 2,
|
|
},
|
|
{
|
|
__ROW_PATH__: ["b", false],
|
|
w: 9,
|
|
x: 5,
|
|
y: 2,
|
|
z: 2,
|
|
},
|
|
{
|
|
__ROW_PATH__: ["c", true],
|
|
w: 11,
|
|
x: 5,
|
|
y: 2,
|
|
z: 2,
|
|
},
|
|
{
|
|
__ROW_PATH__: ["d", false],
|
|
w: 13,
|
|
x: 5,
|
|
y: 2,
|
|
z: 2,
|
|
},
|
|
]);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("multi-level group_by with sort matches expanded tree order", async function () {
|
|
const table = await perspective.table(data);
|
|
const flat_view = await table.view({
|
|
group_by: ["y", "z"],
|
|
group_rollup_mode: "flat",
|
|
sort: [["w", "desc"]],
|
|
});
|
|
const flat_cols = await flat_view.to_columns();
|
|
expect(flat_cols).toStrictEqual({
|
|
__ROW_PATH__: [
|
|
["d", false],
|
|
["c", true],
|
|
["b", false],
|
|
["a", true],
|
|
],
|
|
w: [13, 11, 9, 7],
|
|
x: [5, 5, 5, 5],
|
|
y: [2, 2, 2, 2],
|
|
z: [2, 2, 2, 2],
|
|
});
|
|
flat_view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("with split_by", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y"],
|
|
split_by: ["z"],
|
|
group_rollup_mode: "flat",
|
|
});
|
|
const json = await view.to_json();
|
|
expect(json).toStrictEqual([
|
|
{
|
|
__ROW_PATH__: ["a"],
|
|
"false|w": null,
|
|
"false|x": null,
|
|
"false|y": null,
|
|
"false|z": null,
|
|
"true|w": 7,
|
|
"true|x": 5,
|
|
"true|y": 2,
|
|
"true|z": 2,
|
|
},
|
|
{
|
|
__ROW_PATH__: ["b"],
|
|
"false|w": 9,
|
|
"false|x": 5,
|
|
"false|y": 2,
|
|
"false|z": 2,
|
|
"true|w": null,
|
|
"true|x": null,
|
|
"true|y": null,
|
|
"true|z": null,
|
|
},
|
|
{
|
|
__ROW_PATH__: ["c"],
|
|
"false|w": null,
|
|
"false|x": null,
|
|
"false|y": null,
|
|
"false|z": null,
|
|
"true|w": 11,
|
|
"true|x": 5,
|
|
"true|y": 2,
|
|
"true|z": 2,
|
|
},
|
|
{
|
|
__ROW_PATH__: ["d"],
|
|
"false|w": 13,
|
|
"false|x": 5,
|
|
"false|y": 2,
|
|
"false|z": 2,
|
|
"true|w": null,
|
|
"true|x": null,
|
|
"true|y": null,
|
|
"true|z": null,
|
|
},
|
|
]);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("split_by only", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
split_by: ["z"],
|
|
group_rollup_mode: "flat",
|
|
});
|
|
const json = await view.to_json();
|
|
expect(json).toStrictEqual([
|
|
{
|
|
"false|w": null,
|
|
"false|x": null,
|
|
"false|y": null,
|
|
"false|z": null,
|
|
"true|w": 1.5,
|
|
"true|x": 1,
|
|
"true|y": "a",
|
|
"true|z": true,
|
|
},
|
|
{
|
|
"false|w": 2.5,
|
|
"false|x": 2,
|
|
"false|y": "b",
|
|
"false|z": false,
|
|
"true|w": null,
|
|
"true|x": null,
|
|
"true|y": null,
|
|
"true|z": null,
|
|
},
|
|
{
|
|
"false|w": null,
|
|
"false|x": null,
|
|
"false|y": null,
|
|
"false|z": null,
|
|
"true|w": 3.5,
|
|
"true|x": 3,
|
|
"true|y": "c",
|
|
"true|z": true,
|
|
},
|
|
{
|
|
"false|w": 4.5,
|
|
"false|x": 4,
|
|
"false|y": "d",
|
|
"false|z": false,
|
|
"true|w": null,
|
|
"true|x": null,
|
|
"true|y": null,
|
|
"true|z": null,
|
|
},
|
|
{
|
|
"false|w": null,
|
|
"false|x": null,
|
|
"false|y": null,
|
|
"false|z": null,
|
|
"true|w": 5.5,
|
|
"true|x": 4,
|
|
"true|y": "a",
|
|
"true|z": true,
|
|
},
|
|
{
|
|
"false|w": 6.5,
|
|
"false|x": 3,
|
|
"false|y": "b",
|
|
"false|z": false,
|
|
"true|w": null,
|
|
"true|x": null,
|
|
"true|y": null,
|
|
"true|z": null,
|
|
},
|
|
{
|
|
"false|w": null,
|
|
"false|x": null,
|
|
"false|y": null,
|
|
"false|z": null,
|
|
"true|w": 7.5,
|
|
"true|x": 2,
|
|
"true|y": "c",
|
|
"true|z": true,
|
|
},
|
|
{
|
|
"false|w": 8.5,
|
|
"false|x": 1,
|
|
"false|y": "d",
|
|
"false|z": false,
|
|
"true|w": null,
|
|
"true|x": null,
|
|
"true|y": null,
|
|
"true|z": null,
|
|
},
|
|
]);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("with split_by and sort", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y"],
|
|
split_by: ["z"],
|
|
group_rollup_mode: "flat",
|
|
sort: [["w", "desc"]],
|
|
});
|
|
const json = await view.to_json();
|
|
expect(json).toStrictEqual([
|
|
{
|
|
__ROW_PATH__: ["d"],
|
|
"false|w": 13,
|
|
"false|x": 5,
|
|
"false|y": 2,
|
|
"false|z": 2,
|
|
"true|w": null,
|
|
"true|x": null,
|
|
"true|y": null,
|
|
"true|z": null,
|
|
},
|
|
{
|
|
__ROW_PATH__: ["c"],
|
|
"false|w": null,
|
|
"false|x": null,
|
|
"false|y": null,
|
|
"false|z": null,
|
|
"true|w": 11,
|
|
"true|x": 5,
|
|
"true|y": 2,
|
|
"true|z": 2,
|
|
},
|
|
{
|
|
__ROW_PATH__: ["b"],
|
|
"false|w": 9,
|
|
"false|x": 5,
|
|
"false|y": 2,
|
|
"false|z": 2,
|
|
"true|w": null,
|
|
"true|x": null,
|
|
"true|y": null,
|
|
"true|z": null,
|
|
},
|
|
{
|
|
__ROW_PATH__: ["a"],
|
|
"false|w": null,
|
|
"false|x": null,
|
|
"false|y": null,
|
|
"false|z": null,
|
|
"true|w": 7,
|
|
"true|x": 5,
|
|
"true|y": 2,
|
|
"true|z": 2,
|
|
},
|
|
]);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("updates after table.update()", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y"],
|
|
group_rollup_mode: "flat",
|
|
});
|
|
const before = await view.to_json();
|
|
expect(before).toStrictEqual([
|
|
{ __ROW_PATH__: ["a"], w: 7, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["b"], w: 9, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["c"], w: 11, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["d"], w: 13, x: 5, y: 2, z: 2 },
|
|
]);
|
|
table.update([{ w: 9.5, x: 5, y: "e", z: true }]);
|
|
const after = await view.to_json();
|
|
expect(after).toStrictEqual([
|
|
{ __ROW_PATH__: ["a"], w: 7, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["b"], w: 9, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["c"], w: 11, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["d"], w: 13, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["e"], w: 9.5, x: 5, y: 1, z: 1 },
|
|
]);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("updates preserve sort order", async function () {
|
|
const table = await perspective.table(data);
|
|
const flat_view = await table.view({
|
|
group_by: ["y"],
|
|
group_rollup_mode: "flat",
|
|
sort: [["w", "desc"]],
|
|
});
|
|
table.update([{ w: 100, x: 5, y: "e", z: true }]);
|
|
const flat_cols = await flat_view.to_columns();
|
|
expect(flat_cols).toStrictEqual({
|
|
__ROW_PATH__: [["e"], ["d"], ["c"], ["b"], ["a"]],
|
|
w: [100, 13, 11, 9, 7],
|
|
x: [5, 5, 5, 5, 5],
|
|
y: [1, 2, 2, 2, 2],
|
|
z: [1, 2, 2, 2, 2],
|
|
});
|
|
flat_view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("viewport pagination", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y"],
|
|
group_rollup_mode: "flat",
|
|
});
|
|
const json = await view.to_json({
|
|
start_row: 0,
|
|
end_row: 2,
|
|
});
|
|
expect(json).toStrictEqual([
|
|
{ __ROW_PATH__: ["a"], w: 7, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["b"], w: 9, x: 5, y: 2, z: 2 },
|
|
]);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("viewport pagination with sort", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y"],
|
|
group_rollup_mode: "flat",
|
|
sort: [["w", "desc"]],
|
|
});
|
|
const json = await view.to_json({
|
|
start_row: 0,
|
|
end_row: 2,
|
|
});
|
|
expect(json).toStrictEqual([
|
|
{ __ROW_PATH__: ["d"], w: 13, x: 5, y: 2, z: 2 },
|
|
{ __ROW_PATH__: ["c"], w: 11, x: 5, y: 2, z: 2 },
|
|
]);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
});
|
|
|
|
test.describe("total", function () {
|
|
test.skip("returns only grand total with group_by", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_by: ["y"],
|
|
group_rollup_mode: "total",
|
|
});
|
|
const json = await view.to_json();
|
|
expect(json).toStrictEqual([
|
|
{ __ROW_PATH__: [], w: 40, x: 20, y: 8, z: 4 },
|
|
]);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("returns only grand total schema", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_rollup_mode: "total",
|
|
});
|
|
|
|
const json = await view.schema();
|
|
expect(json).toStrictEqual({
|
|
w: "float",
|
|
x: "integer",
|
|
y: "integer",
|
|
z: "integer",
|
|
});
|
|
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("returns only grand total", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_rollup_mode: "total",
|
|
});
|
|
|
|
const json = await view.to_json();
|
|
expect(json).toStrictEqual([
|
|
{ __ROW_PATH__: [], w: 40, x: 20, y: 8, z: 8 },
|
|
]);
|
|
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("num_rows returns 1 without group_by", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_rollup_mode: "total",
|
|
});
|
|
|
|
const num_rows = await view.num_rows();
|
|
expect(num_rows).toEqual(1);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("to_columns works", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_rollup_mode: "total",
|
|
});
|
|
|
|
const cols = await view.to_columns();
|
|
expect(cols).toStrictEqual({
|
|
w: [40],
|
|
x: [20],
|
|
y: [8],
|
|
z: [8],
|
|
});
|
|
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("with split_by", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
split_by: ["z"],
|
|
group_rollup_mode: "total",
|
|
});
|
|
const json = await view.to_json();
|
|
expect(json).toStrictEqual([
|
|
{
|
|
"false|w": 22,
|
|
"false|x": 10,
|
|
"false|y": 4,
|
|
"false|z": 4,
|
|
"true|w": 18,
|
|
"true|x": 10,
|
|
"true|y": 4,
|
|
"true|z": 4,
|
|
},
|
|
]);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("with split_by schema", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
split_by: ["z"],
|
|
group_rollup_mode: "total",
|
|
});
|
|
const schema = await view.schema();
|
|
expect(schema).toStrictEqual({
|
|
w: "float",
|
|
x: "integer",
|
|
y: "integer",
|
|
z: "integer",
|
|
});
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
|
|
test("updates after table.update()", async function () {
|
|
const table = await perspective.table(data);
|
|
const view = await table.view({
|
|
group_rollup_mode: "total",
|
|
});
|
|
const before = await view.to_json();
|
|
expect(before).toStrictEqual([
|
|
{ __ROW_PATH__: [], w: 40, x: 20, y: 8, z: 8 },
|
|
]);
|
|
table.update([{ w: 10, x: 5, y: "e", z: true }]);
|
|
const after = await view.to_json();
|
|
expect(after).toStrictEqual([
|
|
{ __ROW_PATH__: [], w: 50, x: 25, y: 9, z: 9 },
|
|
]);
|
|
view.delete();
|
|
table.delete();
|
|
});
|
|
});
|
|
});
|
|
})(perspective);
|