diff --git a/package.json b/package.json index 7ebfef7..1bf5713 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "@types/react-addons-linked-state-mixin": "^0.14.22", "css-loader": "^6.7.1", "jest": "^29.2.0", + "jest-canvas-mock": "^2.5.2", "mkdirp": "^1.0.3", "npm-run-all2": "^7.0.1", "rimraf": "^5.0.1", diff --git a/src/__tests__/arbalister.spec.ts b/src/__tests__/arbalister.spec.ts deleted file mode 100644 index 8e99419..0000000 --- a/src/__tests__/arbalister.spec.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Example of [Jest](https://jestjs.io/docs/getting-started) unit tests - */ - -describe("arbalister", () => { - it("should be tested", () => { - expect(1 + 1).toEqual(2); - }); -}); diff --git a/src/__tests__/collection.spec.ts b/src/__tests__/collection.spec.ts new file mode 100644 index 0000000..2f1da77 --- /dev/null +++ b/src/__tests__/collection.spec.ts @@ -0,0 +1,51 @@ +import { PairMap } from "../collection"; + +describe("PairMap", () => { + it("sets and gets values with primitive keys", () => { + const map = new PairMap(); + map.set([1, "a"], "value1"); + expect(map.get([1, "a"])).toBe("value1"); + expect(map.get([2, "a"])).toBeUndefined(); + }); + + it("checks has, delete, and clear", () => { + const map = new PairMap(); + map.set([1, 2], "foo"); + expect(map.has([1, 2])).toBe(true); + expect(map.delete([1, 2])).toBe(true); + expect(map.has([1, 2])).toBe(false); + map.set([3, 4], "bar"); + map.clear(); + expect(map.size).toBe(0); + }); + + it("returns correct size", () => { + const map = new PairMap(); + map.set(["a", "b"], 1); + map.set(["c", "d"], 2); + expect(map.size).toBe(2); + map.delete(["a", "b"]); + expect(map.size).toBe(1); + }); + + it("overwrites values for same key", () => { + const map = new PairMap(); + map.set([1, 2], "first"); + map.set([1, 2], "second"); + expect(map.get([1, 2])).toBe("second"); + expect(map.size).toBe(1); + }); + + it("iterates with forEach", () => { + const map = new PairMap(); + map.set([1, 2], "a"); + map.set([3, 4], "b"); + const entries: Array<[[number, number], string]> = []; + map.forEach((value, key) => { + entries.push([key, value]); + }); + expect(entries).toContainEqual([[1, 2], "a"]); + expect(entries).toContainEqual([[3, 4], "b"]); + expect(entries.length).toBe(2); + }); +}); diff --git a/src/__tests__/model.spec.ts b/src/__tests__/model.spec.ts new file mode 100644 index 0000000..ea3c71c --- /dev/null +++ b/src/__tests__/model.spec.ts @@ -0,0 +1,72 @@ +import "jest-canvas-mock"; + +import { tableFromArrays } from "apache-arrow"; +import type * as Arrow from "apache-arrow"; + +import { ArrowModel } from "../model"; +import { fetchStats, fetchTable } from "../requests"; +import type * as Req from "../requests"; + +const MOCK_TABLE = tableFromArrays({ + id: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + name: ["Alice", "Bob", "Charlie", "David", "Eve", "Frank", "Grace", "Hank", "Ivy", "Jack"], + age: [25, 30, 35, 40, 45, 50, 55, 60, 65, 70], + city: ["Paris", "London", "Ur", "Turin", "Rome", "Tokyo", "Boston", "Sydney", "Lima", "Cairo"], + score: [85, 90, 78, 92, 88, 76, 95, 81, 89, 93], +}); + +async function fetchStatsMocked(_params: Req.StatsParams): Promise { + return { + num_rows: MOCK_TABLE.numRows, + num_cols: MOCK_TABLE.numCols, + }; +} + +async function fetchTableMocked(params: Req.TableParams): Promise { + let table: Arrow.Table = MOCK_TABLE; + + if (params.row_chunk !== undefined && params.row_chunk_size !== undefined) { + const start = params.row_chunk * params.row_chunk_size; + const end = start + params.row_chunk_size; + table = table.slice(start, end); + } + + if (params.col_chunk !== undefined && params.col_chunk_size !== undefined) { + const colNames = table.schema.fields.map((field) => field.name); + const start = params.col_chunk * params.col_chunk_size; + const end = start + params.col_chunk_size; + const selectedCols = colNames.slice(start, end); + table = table.select(selectedCols); + } + + return table; +} + +jest.mock("../requests", () => ({ + fetchTable: jest.fn(), + fetchStats: jest.fn(), +})); + +describe("ArrowModel", () => { + (fetchTable as jest.Mock).mockImplementation(fetchTableMocked); + (fetchStats as jest.Mock).mockImplementation(fetchStatsMocked); + + const model = new ArrowModel({ path: "test/path.parquet" }); + + it("should initialize data", async () => { + await model.ready; + + expect(fetchStats).toHaveBeenCalledTimes(1); + // One for schema and once for data + expect(fetchTable).toHaveBeenCalledTimes(2); + + expect(model.schema).toEqual(MOCK_TABLE.schema); + expect(model.columnCount("body")).toEqual(MOCK_TABLE.numCols); + expect(model.columnCount("row-header")).toEqual(1); + expect(model.rowCount("body")).toEqual(MOCK_TABLE.numRows); + expect(model.rowCount("column-header")).toEqual(1); + + // First chunk is initialized + expect(model.data("body", 0, 0)).toEqual(MOCK_TABLE.getChildAt(0)?.get(0).toString()); + }); +}); diff --git a/src/model.ts b/src/model.ts index ca90afe..1630c7d 100644 --- a/src/model.ts +++ b/src/model.ts @@ -43,7 +43,7 @@ export class ArrowModel extends DataModel { return this._ready; } - private get schema(): Arrow.Schema { + get schema(): Arrow.Schema { return this._schema; } diff --git a/yarn.lock b/yarn.lock index bdeb3da..e1d0188 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3705,6 +3705,7 @@ __metadata: apache-arrow: ^21.1.0 css-loader: ^6.7.1 jest: ^29.2.0 + jest-canvas-mock: ^2.5.2 mkdirp: ^1.0.3 npm-run-all2: ^7.0.1 rimraf: ^5.0.1 @@ -4141,7 +4142,7 @@ __metadata: languageName: node linkType: hard -"color-name@npm:~1.1.4": +"color-name@npm:^1.1.4, color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 @@ -4328,6 +4329,13 @@ __metadata: languageName: node linkType: hard +"cssfontparser@npm:^1.2.1": + version: 1.2.1 + resolution: "cssfontparser@npm:1.2.1" + checksum: 952d487cddab591fb944f2a4c326a7736bc963784a6d92b6ad4051f3bf5ee49a732eff62e29a52ff085197cb07f5bd66525a2245ded7fd356113ac81be9238b9 + languageName: node + linkType: hard + "cssom@npm:^0.5.0": version: 0.5.0 resolution: "cssom@npm:0.5.0" @@ -5537,6 +5545,16 @@ __metadata: languageName: node linkType: hard +"jest-canvas-mock@npm:^2.5.2": + version: 2.5.2 + resolution: "jest-canvas-mock@npm:2.5.2" + dependencies: + cssfontparser: ^1.2.1 + moo-color: ^1.0.2 + checksum: a3004d2e96473049045e49dcf98e5ea6011494048ab42b5422b3089d9ff406aaca8353e79587055d840fa145541668eb8f78613765f252ad5901a8217e91ea5d + languageName: node + linkType: hard + "jest-changed-files@npm:^29.7.0": version: 29.7.0 resolution: "jest-changed-files@npm:29.7.0" @@ -6585,6 +6603,15 @@ __metadata: languageName: node linkType: hard +"moo-color@npm:^1.0.2": + version: 1.0.3 + resolution: "moo-color@npm:1.0.3" + dependencies: + color-name: ^1.1.4 + checksum: 02bf59b6bbd5e86641bc062e2dc0843e6e579e18ef67e1c8e93bfc01945df578f20e66ce16aa9632db2aa0e16806e0914a26eb345a804f45fff1ae12a8906a29 + languageName: node + linkType: hard + "ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3"