diff --git a/client/__tests__/e2e/cellxgeneActions.js b/client/__tests__/e2e/cellxgeneActions.js index 18abcb92..fb8b12f4 100644 --- a/client/__tests__/e2e/cellxgeneActions.js +++ b/client/__tests__/e2e/cellxgeneActions.js @@ -1,7 +1,6 @@ import { strict as assert } from "assert"; export const cellxgeneActions = (page, utils) => ({ - async drag(testId, start, end, lasso = false) { const layout = await utils.waitByID(testId); const elBox = await layout.boxModel(); @@ -31,22 +30,29 @@ export const cellxgeneActions = (page, utils) => ({ }, async getAllHistograms(testclass, testIds) { - const histTestIds = testIds.map(tid => `histogram-${tid}`); + const histTestIds = testIds.map((tid) => `histogram-${tid}`); // these load asynchronously, so we need to wait for each histogram individually await utils.waitForAllByIds(histTestIds); const allHistograms = await utils.getAllByClass(testclass); - return allHistograms.map(hist => hist.replace(/^histogram-/, "")); + return allHistograms.map((hist) => hist.replace(/^histogram-/, "")); }, async getAllCategoriesAndCounts(category) { await utils.waitByClass("categorical-row"); return page.$$eval( `[data-testid="category-${category}"] [data-testclass='categorical-row']`, - rows => Object.fromEntries(rows.map(row => { - const cat = row.querySelector("[data-testclass='categorical-value']").innerText; - const count = row.querySelector("[data-testclass='categorical-value-count']").innerText; - return [cat, count]; - })) + (rows) => + Object.fromEntries( + rows.map((row) => { + const cat = row.querySelector( + "[data-testclass='categorical-value']" + ).innerText; + const count = row.querySelector( + "[data-testclass='categorical-value-count']" + ).innerText; + return [cat, count]; + }) + ) ); }, @@ -60,12 +66,14 @@ export const cellxgeneActions = (page, utils) => ({ await utils.waitByID(checkboxId); const checkedPseudoclass = await page.$eval( `[data-testid='${checkboxId}']`, - el => el.matches(":checked") + (el) => el.matches(":checked") ); if (!checkedPseudoclass) await utils.clickOn(checkboxId); try { const categoryRow = await utils.waitByID(`${category}:category-expand`); - const isExpanded = await categoryRow.$("[data-testclass='category-expand-is-expanded']"); + const isExpanded = await categoryRow.$( + "[data-testclass='category-expand-is-expanded']" + ); if (isExpanded) await utils.clickOn(`${category}:category-expand`); } catch {} }, @@ -75,14 +83,22 @@ export const cellxgeneActions = (page, utils) => ({ const size = await el.boxModel(); return { x: Math.floor(size.width * xAsPercent), - y: Math.floor(size.height * yAsPercent) - } + y: Math.floor(size.height * yAsPercent), + }; }, async calcDragCoordinates(testId, coordinateAsPercent) { return { - start: await this.calcCoordinate(testId, coordinateAsPercent.x1, coordinateAsPercent.y1), - end: await this.calcCoordinate(testId, coordinateAsPercent.x2, coordinateAsPercent.y2) + start: await this.calcCoordinate( + testId, + coordinateAsPercent.x1, + coordinateAsPercent.y1 + ), + end: await this.calcCoordinate( + testId, + coordinateAsPercent.x2, + coordinateAsPercent.y2 + ), }; }, @@ -97,7 +113,9 @@ export const cellxgeneActions = (page, utils) => ({ async expandCategory(category) { const expand = await utils.waitByID(`${category}:category-expand`); - const notExpanded = await expand.$("[data-testclass='category-expand-is-not-expanded']"); + const notExpanded = await expand.$( + "[data-testclass='category-expand-is-not-expanded']" + ); if (notExpanded) await utils.clickOn(`${category}:category-expand`); }, @@ -117,7 +135,10 @@ export const cellxgeneActions = (page, utils) => ({ async renameCategory(oldCatgoryName, newCategoryName) { await utils.clickOn(`${oldCatgoryName}:see-actions`); await utils.clickOn(`${oldCatgoryName}:edit-category-mode`); - await utils.clearInputAndTypeInto(`${oldCatgoryName}:edit-category-name-text`, newCategoryName); + await utils.clearInputAndTypeInto( + `${oldCatgoryName}:edit-category-name-text`, + newCategoryName + ); await utils.clickOn(`${oldCatgoryName}:submit-category-edit`); }, @@ -136,7 +157,7 @@ export const cellxgeneActions = (page, utils) => ({ async deleteLabel(categoryName, labelName) { await this.expandCategory(categoryName); await utils.clickOn(`${categoryName}:${labelName}:see-actions`); - await utils.clickOn( `${categoryName}:${labelName}:delete-label`); + await utils.clickOn(`${categoryName}:${labelName}:delete-label`); }, async renameLabel(categoryName, oldLabelName, newLabelName) { @@ -153,17 +174,23 @@ export const cellxgeneActions = (page, utils) => ({ async addGeneToSearch(geneName) { await utils.typeInto("gene-search", geneName); await page.keyboard.press("Enter"); - await page.waitForSelector( - `[data-testid='histogram-${geneName}']` - ); + await page.waitForSelector(`[data-testid='histogram-${geneName}']`); }, async subset(coordinatesAsPercent) { // In order to deselect the selection after the subset, make sure we have some clear part // of the scatterplot we can click on assert(coordinatesAsPercent.x2 < 0.99 || coordinatesAsPercent.y2 < 0.99); - const lassoSelection = await this.calcDragCoordinates( "layout-graph", coordinatesAsPercent); - await this.drag("layout-graph", lassoSelection.start, lassoSelection.end, true ); + const lassoSelection = await this.calcDragCoordinates( + "layout-graph", + coordinatesAsPercent + ); + await this.drag( + "layout-graph", + lassoSelection.start, + lassoSelection.end, + true + ); await utils.clickOn("subset-button"); const clearCoordinate = await this.calcCoordinate( "layout-graph", @@ -174,7 +201,9 @@ export const cellxgeneActions = (page, utils) => ({ }, async setSellSet(cellSet, cellSetNum) { - for (const selection of cellSet.filter(sel => sel.kind === "categorical")) { + for (const selection of cellSet.filter( + (sel) => sel.kind === "categorical" + )) { await this.selectCategory(selection.metadata, selection.values, true); } await this.cellSet(cellSetNum); @@ -190,7 +219,5 @@ export const cellxgeneActions = (page, utils) => ({ await utils.clickOn("section-bulk-add"); await utils.typeInto("input-bulk-add", geneNames.join(",")); await page.keyboard.press("Enter"); - } + }, }); - - diff --git a/client/__tests__/e2e/config.js b/client/__tests__/e2e/config.js index c287762d..d4748edd 100644 --- a/client/__tests__/e2e/config.js +++ b/client/__tests__/e2e/config.js @@ -1,6 +1,7 @@ export const jest_env = process.env.JEST_ENV; export const appPort = process.env.CXG_SERVER_PORT; -export const appUrlBase = process.env.CXG_URL_BASE || `http://localhost:${appPort}`; +export const appUrlBase = + process.env.CXG_URL_BASE || `http://localhost:${appPort}`; export const DEV = jest_env === "dev"; export const DEBUG = jest_env === "debug"; export const DATASET = "pbmc3k"; diff --git a/client/__tests__/e2e/data.js b/client/__tests__/e2e/data.js index ad783613..4ec285f0 100644 --- a/client/__tests__/e2e/data.js +++ b/client/__tests__/e2e/data.js @@ -4,7 +4,7 @@ export const datasets = { dataframe: { nObs: "2638", nVar: "1838", - type: "float32" + type: "float32", }, categorical: { louvain: { @@ -15,47 +15,47 @@ export const datasets = { "Dendritic cells": "37", "FCGR3A+ Monocytes": "150", Megakaryocytes: "15", - "NK cells": "154" - } + "NK cells": "154", + }, }, continuous: { n_genes: "int32", percent_mito: "float32", - n_counts: "float32" + n_counts: "float32", }, cellsets: { lasso: [ { "coordinates-as-percent": { x1: 0.1, y1: 0.25, x2: 0.7, y2: 0.75 }, - count: "1181" - } + count: "1181", + }, ], categorical: [ { metadata: "louvain", values: ["B cells", "Megakaryocytes"], - count: "357" - } + count: "357", + }, ], continuous: [ { metadata: "n_genes", "coordinates-as-percent": { x1: 0.25, y1: 0.5, x2: 0.55, y2: 0.5 }, - count: "1537" - } - ] + count: "1537", + }, + ], }, diffexp: { cellset1: [ - { kind: "categorical", metadata: "louvain", values: ["B cells"] } + { kind: "categorical", metadata: "louvain", values: ["B cells"] }, ], cellset2: [ { kind: "categorical", metadata: "louvain", - values: ["CD4 T cells", "NK cells"] - } + values: ["CD4 T cells", "NK cells"], + }, ], "gene-results": [ "HLA-DRB1", @@ -67,21 +67,21 @@ export const datasets = { "HLA-DQB1", "MS4A1", "IL32", - "CD37" - ] + "CD37", + ], }, genes: { bulkadd: ["S100A8", "FCGR3A", "LGALS2", "GSTP1"], - search: "ACD" + search: "ACD", }, subset: { cellset1: [ { kind: "categorical", metadata: "louvain", - values: ["B cells", "Megakaryocytes"] - } + values: ["B cells", "Megakaryocytes"], + }, ], count: "357", categorical: { @@ -93,27 +93,27 @@ export const datasets = { "Dendritic cells": "0", "FCGR3A+ Monocytes": "0", Megakaryocytes: "15", - "NK cells": "0" - } + "NK cells": "0", + }, }, lasso: { "coordinates-as-percent": { x1: 0.25, y1: 0.05, x2: 0.75, y2: 0.55 }, - count: "329" - } + count: "329", + }, }, scatter: { - genes: { x: "S100A8", y: "FCGR3A" } + genes: { x: "S100A8", y: "FCGR3A" }, }, pan: { - "coordinates-as-percent": { x1: 0.75, y1: 0.75, x2: 0.35, y2: 0.35 } + "coordinates-as-percent": { x1: 0.75, y1: 0.75, x2: 0.35, y2: 0.35 }, }, features: { panzoom: { lasso: { "coordinates-as-percent": { x1: 0.3, y1: 0.3, x2: 0.5, y2: 0.5 }, - count: "24" - } - } + count: "24", + }, + }, }, categoryLabel: { lasso: { @@ -122,17 +122,17 @@ export const datasets = { newCount: { bySubsetConfig: { false: "600", - true: "591" - } - } + true: "591", + }, + }, }, annotationsFromFile: { count: { bySubsetConfig: { false: "1161", - true: "856" - } - } + true: "856", + }, + }, }, clip: { min: "30", @@ -141,7 +141,7 @@ export const datasets = { gene: "S100A8", "coordinates-as-percent": { x1: 0.25, y1: 0.5, x2: 0.55, y2: 0.5 }, count: "386", - "gene-cell-count": "416" - } - } + "gene-cell-count": "416", + }, + }, }; diff --git a/client/__tests__/e2e/e2e.test.js b/client/__tests__/e2e/e2e.test.js index 5477c8d5..1710da9c 100644 --- a/client/__tests__/e2e/e2e.test.js +++ b/client/__tests__/e2e/e2e.test.js @@ -24,7 +24,9 @@ afterAll(() => { describe("did launch", () => { test("page launched", async () => { - const element = await utils.getOneElementInnerHTML("[data-testid='header']"); + const element = await utils.getOneElementInnerHTML( + "[data-testid='header']" + ); expect(element).toBe(data.title); }); }); @@ -32,7 +34,9 @@ describe("did launch", () => { describe("metadata loads", () => { test("categories and values from dataset appear", async () => { for (const label in data.categorical) { - const categoryName = await utils.getOneElementInnerText(`[data-testid="category-${label}"]`); + const categoryName = await utils.getOneElementInnerText( + `[data-testid="category-${label}"]` + ); expect(categoryName).toMatch(label); await utils.clickOn(`${label}:category-expand`); const categories = await cxgActions.getAllCategoriesAndCounts(label); @@ -50,7 +54,6 @@ describe("metadata loads", () => { await utils.waitByID(`histogram-${label}`); } }); - }); describe("cell selection", () => { @@ -81,7 +84,9 @@ describe("cell selection", () => { await utils.clickOn(`${cellset.metadata}:category-expand`); await utils.clickOn(`${cellset.metadata}:category-select`); for (const val of cellset.values) { - await utils.clickOn(`categorical-value-select-${cellset.metadata}-${val}`); + await utils.clickOn( + `categorical-value-select-${cellset.metadata}-${val}` + ); } const cellCount = await cxgActions.cellSet(1); expect(cellCount).toBe(cellset.count); @@ -103,12 +108,16 @@ describe("cell selection", () => { }); describe("gene entry", () => { - test("search for single gene", async () => cxgActions.addGeneToSearch(data.genes.search)); + test("search for single gene", async () => + cxgActions.addGeneToSearch(data.genes.search)); test("bulk add genes", async () => { const testGenes = data.genes.bulkadd; await cxgActions.bulkAddGenes(testGenes); - const allHistograms = await cxgActions.getAllHistograms("histogram-user-gene", testGenes); + const allHistograms = await cxgActions.getAllHistograms( + "histogram-user-gene", + testGenes + ); expect(allHistograms).toEqual(expect.arrayContaining(testGenes)); expect(allHistograms.length).toEqual(testGenes.length); }); @@ -172,11 +181,19 @@ describe("subset", () => { const userDefinedGenes = data.genes.bulkadd; const diffExpGenes = data.diffexp["gene-results"]; await cxgActions.bulkAddGenes(userDefinedGenes); - const userDefinedHistograms = await cxgActions.getAllHistograms("histogram-user-gene", userDefinedGenes); - expect(userDefinedHistograms).toEqual(expect.arrayContaining(userDefinedGenes)); - await cxgActions.subset({x1: 0.15, y1: 0.10, x2: 0.98, y2: 0.98}); + const userDefinedHistograms = await cxgActions.getAllHistograms( + "histogram-user-gene", + userDefinedGenes + ); + expect(userDefinedHistograms).toEqual( + expect.arrayContaining(userDefinedGenes) + ); + await cxgActions.subset({ x1: 0.15, y1: 0.1, x2: 0.98, y2: 0.98 }); await cxgActions.runDiffExp(data.diffexp.cellset1, data.diffexp.cellset2); - const diffExpHistograms = await cxgActions.getAllHistograms("histogram-diffexp", diffExpGenes); + const diffExpHistograms = await cxgActions.getAllHistograms( + "histogram-diffexp", + diffExpGenes + ); expect(diffExpHistograms).toEqual(expect.arrayContaining(diffExpGenes)); await utils.clickOn("reset-subset-button"); const expected = [].concat(userDefinedGenes, diffExpGenes); @@ -184,26 +201,38 @@ describe("subset", () => { "histogram-user-gene", expected ); - expect(userDefinedHistogramsAfterSubset).toEqual(expect.arrayContaining(expected)); + expect(userDefinedHistogramsAfterSubset).toEqual( + expect.arrayContaining(expected) + ); }); test("subset selection appends the top diff exp genes to user defined genes", async () => { const userDefinedGenes = data.genes.bulkadd; const diffExpGenes = data.diffexp["gene-results"]; await cxgActions.bulkAddGenes(userDefinedGenes); - const userDefinedHistograms = await cxgActions.getAllHistograms("histogram-user-gene", userDefinedGenes); - expect(userDefinedHistograms).toEqual(expect.arrayContaining(userDefinedGenes)); - await cxgActions.subset({x1: 0.15, y1: 0.10, x2: 0.98, y2: 0.98}); + const userDefinedHistograms = await cxgActions.getAllHistograms( + "histogram-user-gene", + userDefinedGenes + ); + expect(userDefinedHistograms).toEqual( + expect.arrayContaining(userDefinedGenes) + ); + await cxgActions.subset({ x1: 0.15, y1: 0.1, x2: 0.98, y2: 0.98 }); await cxgActions.runDiffExp(data.diffexp.cellset1, data.diffexp.cellset2); - const diffExpHistograms = await cxgActions.getAllHistograms("histogram-diffexp", diffExpGenes); + const diffExpHistograms = await cxgActions.getAllHistograms( + "histogram-diffexp", + diffExpGenes + ); expect(diffExpHistograms).toEqual(expect.arrayContaining(diffExpGenes)); - await cxgActions.subset({x1: 0.16, y1: 0.11, x2: 0.97, y2: 0.97}); + await cxgActions.subset({ x1: 0.16, y1: 0.11, x2: 0.97, y2: 0.97 }); const expected = [].concat(userDefinedGenes, diffExpGenes); const userDefinedHistogramsAfterSubset = await cxgActions.getAllHistograms( "histogram-user-gene", expected ); - expect(userDefinedHistogramsAfterSubset).toEqual(expect.arrayContaining(expected)); + expect(userDefinedHistogramsAfterSubset).toEqual( + expect.arrayContaining(expected) + ); }); }); diff --git a/client/__tests__/e2e/e2eAnnotations.test.js b/client/__tests__/e2e/e2eAnnotations.test.js index 3993dc99..6da0c42e 100644 --- a/client/__tests__/e2e/e2eAnnotations.test.js +++ b/client/__tests__/e2e/e2eAnnotations.test.js @@ -13,14 +13,13 @@ beforeAll(async () => { }); afterAll(() => { - if (browser !== undefined) browser.close() + if (browser !== undefined) browser.close(); }); describe.each([ - {withSubset: true, tag: "subset"}, - {withSubset: false, tag: "whole"} + { withSubset: true, tag: "subset" }, + { withSubset: false, tag: "whole" }, ])("annotations", (config) => { - const perTestCategoryName = "per-test-category"; const perTestLabelName = "per-test-label"; @@ -31,7 +30,8 @@ describe.each([ // setup the test fixtures await actions.createCategory(perTestCategoryName); await actions.createLabel(perTestCategoryName, perTestLabelName); - if (config.withSubset) await actions.subset({x1: 0.10, y1: 0.10, x2: 0.80, y2: 0.80}); + if (config.withSubset) + await actions.subset({ x1: 0.1, y1: 0.1, x2: 0.8, y2: 0.8 }); await utils.waitByClass("autosave-complete"); }); @@ -74,7 +74,11 @@ describe.each([ test("rename a label", async () => { const newLabelName = "my-cool-new-label"; await assertLabelDoesNotExist(perTestCategoryName, newLabelName); - await actions.renameLabel(perTestCategoryName, perTestLabelName, newLabelName); + await actions.renameLabel( + perTestCategoryName, + perTestLabelName, + newLabelName + ); await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName); await assertLabelExists(perTestCategoryName, newLabelName); }); @@ -83,8 +87,10 @@ describe.each([ const categoryName = "cluster-test"; const labelName = "four"; await actions.expandCategory(categoryName); - const result = await utils.waitByID(`categorical-value-count-${categoryName}-${labelName}`); - expect(await result.evaluate(node => node.innerText)).toBe( + const result = await utils.waitByID( + `categorical-value-count-${categoryName}-${labelName}` + ); + expect(await result.evaluate((node) => node.innerText)).toBe( data.annotationsFromFile.count.bySubsetConfig[config.withSubset] ); }); @@ -101,11 +107,17 @@ describe.each([ lassoSelection.end, true ); - await utils.waitByID("lasso-element", {visible: true}); - await utils.clickOn(`${perTestCategoryName}:${perTestLabelName}:see-actions`); - await utils.clickOn(`${perTestCategoryName}:${perTestLabelName}:add-current-selection-to-this-label`); - const result = await utils.waitByID(`categorical-value-count-${perTestCategoryName}-${perTestLabelName}`); - expect(await result.evaluate(node => node.innerText)).toBe( + await utils.waitByID("lasso-element", { visible: true }); + await utils.clickOn( + `${perTestCategoryName}:${perTestLabelName}:see-actions` + ); + await utils.clickOn( + `${perTestCategoryName}:${perTestLabelName}:add-current-selection-to-this-label` + ); + const result = await utils.waitByID( + `categorical-value-count-${perTestCategoryName}-${perTestLabelName}` + ); + expect(await result.evaluate((node) => node.innerText)).toBe( data.categoryLabel.newCount.bySubsetConfig[config.withSubset] ); }); @@ -170,7 +182,11 @@ describe.each([ test("undo/redo label rename", async () => { const newLabelName = `label-renamed-undo-${config.tag}`; await assertLabelDoesNotExist(perTestCategoryName, newLabelName); - await actions.renameLabel(perTestCategoryName, perTestLabelName, newLabelName); + await actions.renameLabel( + perTestCategoryName, + perTestLabelName, + newLabelName + ); await assertLabelExists(perTestCategoryName, newLabelName); await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName); await utils.clickOn("undo"); @@ -183,14 +199,16 @@ describe.each([ async function assertCategoryExists(categoryName) { const handle = await utils.waitByID(`${categoryName}:category-expand`); - const result = await handle.evaluate(node => node.innerText); + const result = await handle.evaluate((node) => node.innerText); // slice beginning and end of category name result to account for truncation of long names expect(result.slice(0, 10)).toBe(categoryName.slice(0, 10)); expect(result.slice(-10)).toBe(categoryName.slice(-10)); } async function assertCategoryDoesNotExist(categoryName) { - const result = await page.$(`[data-testid='${categoryName}:category-expand']`); + const result = await page.$( + `[data-testid='${categoryName}:category-expand']` + ); expect(result).toBeNull(); } @@ -198,13 +216,17 @@ describe.each([ const category = await utils.waitByID(`${categoryName}:category-expand`); expect(category).not.toBeNull(); await actions.expandCategory(categoryName); - const previous = await utils.waitByID(`categorical-value-${categoryName}-${labelName}`); - expect(await previous.evaluate(node => node.innerText)).toBe(labelName); + const previous = await utils.waitByID( + `categorical-value-${categoryName}-${labelName}` + ); + expect(await previous.evaluate((node) => node.innerText)).toBe(labelName); } async function assertLabelDoesNotExist(categoryName, labelName) { await actions.expandCategory(categoryName); - const result = await page.$(`[data-testid='categorical-value-${categoryName}-${labelName}']`); + const result = await page.$( + `[data-testid='categorical-value-${categoryName}-${labelName}']` + ); expect(result).toBeNull(); } @@ -212,10 +234,10 @@ describe.each([ try { const category = await page.waitForSelector( `[data-testid='${categoryName}:category-expand']`, - {timeout: 200} + { timeout: 200 } ); if (category !== null) return await actions.deleteCategory(categoryName); } catch {} - return null + return null; } }); diff --git a/client/__tests__/e2e/e2eJestConfig.json b/client/__tests__/e2e/e2eJestConfig.json index 04410014..585d59f0 100644 --- a/client/__tests__/e2e/e2eJestConfig.json +++ b/client/__tests__/e2e/e2eJestConfig.json @@ -1,9 +1,5 @@ { "preset": "jest-puppeteer", - "testMatch": [ - "**/__tests__/**/?(*.)(spec|test).js?(x)" - ], - "setupFiles": [ - "../setupMissingGlobals.js" - ] + "testMatch": ["**/__tests__/**/?(*.)(spec|test).js?(x)"], + "setupFiles": ["../setupMissingGlobals.js"] } diff --git a/client/__tests__/e2e/feature.test.js b/client/__tests__/e2e/feature.test.js index 8573ce5a..0fed000a 100644 --- a/client/__tests__/e2e/feature.test.js +++ b/client/__tests__/e2e/feature.test.js @@ -29,9 +29,9 @@ beforeAll(async () => { page = await browser.newPage(); await page.setViewport(browserViewport); if (DEV || DEBUG) { - page.on("console", msg => console.log(`PAGE LOG: ${msg.text()}`)); + page.on("console", (msg) => console.log(`PAGE LOG: ${msg.text()}`)); } - page.on("pageerror", err => { + page.on("pageerror", (err) => { throw new Error(`Console error: ${err}`); }); utils = puppeteerUtils(page); diff --git a/client/__tests__/e2e/puppeteerUtils.js b/client/__tests__/e2e/puppeteerUtils.js index 49b21f2b..c72a865b 100644 --- a/client/__tests__/e2e/puppeteerUtils.js +++ b/client/__tests__/e2e/puppeteerUtils.js @@ -1,4 +1,4 @@ -export const puppeteerUtils = page => ({ +export const puppeteerUtils = (page) => ({ async waitByID(testId, props = {}) { return page.waitForSelector(`[data-testid='${testId}']`, props); }, @@ -9,13 +9,13 @@ export const puppeteerUtils = page => ({ async waitForAllByIds(testIds) { await Promise.all( - testIds.map(testId => page.waitForSelector(`[data-testid='${testId}']`)) + testIds.map((testId) => page.waitForSelector(`[data-testid='${testId}']`)) ); }, async getAllByClass(testClass) { - return page.$$eval(`[data-testclass=${testClass}]`, eles => - eles.map(ele => ele.dataset.testid) + return page.$$eval(`[data-testclass=${testClass}]`, (eles) => + eles.map((ele) => ele.dataset.testid) ); }, @@ -52,18 +52,18 @@ export const puppeteerUtils = page => ({ async getOneElementInnerHTML(selector) { await page.waitForSelector(selector); - return page.$eval(selector, el => el.innerHTML); + return page.$eval(selector, (el) => el.innerHTML); }, async getOneElementInnerText(selector) { await page.waitForSelector(selector); - return page.$eval(selector, el => el.innerText); + return page.$eval(selector, (el) => el.innerText); }, async getElementCoordinates(testid) { - return page.$eval(`[data-testid='${testid}']`, elem => { + return page.$eval(`[data-testid='${testid}']`, (elem) => { const { left, top } = elem.getBoundingClientRect(); return [left, top]; }); - } + }, }); diff --git a/client/__tests__/e2e/testBrowser.js b/client/__tests__/e2e/testBrowser.js index 0ae93a4b..8aa57bca 100644 --- a/client/__tests__/e2e/testBrowser.js +++ b/client/__tests__/e2e/testBrowser.js @@ -7,41 +7,48 @@ export async function setupTestBrowser() { const browserViewport = { width: 1280, height: 960 }; const browserParams = DEV ? { - headless: false, - slowMo: 5, - args: [ `--window-size=${browserViewport.width},${browserViewport.height}`] - } + headless: false, + slowMo: 5, + args: [ + `--window-size=${browserViewport.width},${browserViewport.height}`, + ], + } : DEBUG - ? { + ? { headless: false, slowMo: 100, devtools: true, - args: [ `--window-size=${browserViewport.width + 560},${browserViewport.height}`] + args: [ + `--window-size=${browserViewport.width + 560},${ + browserViewport.height + }`, + ], } - : { - args: [ `--window-size=${browserViewport.width},${browserViewport.height}`] + : { + args: [ + `--window-size=${browserViewport.width},${browserViewport.height}`, + ], }; const browser = await puppeteer.launch(browserParams); - const page = await browser.pages().then(pages => pages[0]); + const page = await browser.pages().then((pages) => pages[0]); await page.setViewport(browserViewport); if (DEV || DEBUG) { - page.on("console", async msg => { + page.on("console", async (msg) => { // If there is a console.error but an error is not thrown, this will ensure the test fails if (msg.type() === "error") { const errorMsgText = await Promise.all( // TODO can we do this without internal properties? - msg.args().map(arg => arg._remoteObject.description) + msg.args().map((arg) => arg._remoteObject.description) ); throw new Error(`Console error: ${errorMsgText}`); } console.log(`PAGE LOG: ${msg.text()}`); }); } - page.on("pageerror", err => { + page.on("pageerror", (err) => { throw new Error(`Console error: ${err}`); }); const utils = puppeteerUtils(page); const cxgActions = cellxgeneActions(page, utils); return [browser, page, utils, cxgActions]; } - diff --git a/client/__tests__/reducers/cascade.test.js b/client/__tests__/reducers/cascade.test.js index 5395025b..a61e579e 100644 --- a/client/__tests__/reducers/cascade.test.js +++ b/client/__tests__/reducers/cascade.test.js @@ -26,7 +26,7 @@ describe("cascade", () => { expect(nextSharedState).toStrictEqual({}); expect(prevSharedState).toBe(topLevelState); return 0; - } + }, ], [ "bar", @@ -36,8 +36,8 @@ describe("cascade", () => { expect(nextSharedState).toStrictEqual({ foo: 0 }); expect(prevSharedState).toBe(topLevelState); return 99; - } - ] + }, + ], ]); const nextState = reducer(topLevelState, topLevelAction); diff --git a/client/__tests__/reducers/undoable.test.js b/client/__tests__/reducers/undoable.test.js index 40754e1e..5e27b5af 100644 --- a/client/__tests__/reducers/undoable.test.js +++ b/client/__tests__/reducers/undoable.test.js @@ -23,7 +23,7 @@ describe("create", () => { describe("undo", () => { test("expected state modifications", () => { const initialState = { a: 0, b: 1000 }; - const reducer = state => { + const reducer = (state) => { return { a: state.a + 1, b: state.b + 1 }; }; const undoableReducer = undoable(reducer, ["a"]); @@ -43,7 +43,7 @@ describe("undo", () => { describe("redo", () => { const initialState = { a: 0, b: 1000 }; - const reducer = state => { + const reducer = (state) => { return { a: state.a + 1, b: state.b + 1 }; }; let UR; @@ -58,7 +58,7 @@ describe("redo", () => { // verify undo->redo reverts state. const s2 = UR(UR(s1, { type: "@@undoable/undo" }), { - type: "@@undoable/redo" + type: "@@undoable/redo", }); expect(s2).toMatchObject({ a: 1, b: 1001 }); diff --git a/client/__tests__/util/actionHelpers.test.js b/client/__tests__/util/actionHelpers.test.js index 08d91b70..e6ab9da3 100644 --- a/client/__tests__/util/actionHelpers.test.js +++ b/client/__tests__/util/actionHelpers.test.js @@ -13,16 +13,16 @@ describe("rangeEncodeIndices", () => { expect(rangeEncodeIndices([1, 9, 432], 10, true)).toMatchObject([ 1, 9, - 432 + 432, ]); expect(rangeEncodeIndices([1, 9, 432], 10, false)).toMatchObject([ 1, 9, - 432 + 432, ]); - expect(rangeEncodeIndices([0, 1, 2, 3, 9, 10, 432], 2, true)).toMatchObject( - [[0, 3], [9, 10], 432] - ); + expect( + rangeEncodeIndices([0, 1, 2, 3, 9, 10, 432], 2, true) + ).toMatchObject([[0, 3], [9, 10], 432]); expect( rangeEncodeIndices([0, 1, 2, 3, 9, 10, 432], 2, false) ).toMatchObject([[0, 3], [9, 10], 432]); @@ -34,7 +34,10 @@ describe("rangeEncodeIndices", () => { ).toMatchObject([[3, 7], [10, 12], 99]); expect( rangeEncodeIndices([3, 4, 5, 6, 7, 10, 11, 12], 3, false) - ).toMatchObject([[3, 7], [10, 12]]); + ).toMatchObject([ + [3, 7], + [10, 12], + ]); expect( rangeEncodeIndices([0, 3, 4, 5, 6, 7, 10, 11, 12], 3, false) ).toMatchObject([0, [3, 7], [10, 12]]); diff --git a/client/__tests__/util/centroid.test.js b/client/__tests__/util/centroid.test.js index fd1588fb..28467cde 100644 --- a/client/__tests__/util/centroid.test.js +++ b/client/__tests__/util/centroid.test.js @@ -31,7 +31,7 @@ describe("centroid", () => { ...Universe.addObsLayout( universe, Universe.matrixFBSToDataframe(REST.layoutObs) - ) + ), }; world = World.createWorldFromEntireUniverse(universe); @@ -61,10 +61,10 @@ describe("centroid", () => { // This expected result assumes that all cells belong in all categorical values inside of sample response const expectedResult = [ quantile([0.5], world.obsLayout.col("umap_0").asArray())[0], - quantile([0.5], world.obsLayout.col("umap_1").asArray())[0] + quantile([0.5], world.obsLayout.col("umap_1").asArray())[0], ]; - centroidResult.forEach(coordinate => { + centroidResult.forEach((coordinate) => { expect(coordinate).toEqual(expectedResult); }); }); @@ -86,10 +86,10 @@ describe("centroid", () => { // This expected result assumes that all cells belong in all categorical values inside of sample response const expectedResult = [ quantile([0.5], world.obsLayout.col("umap_0").asArray())[0], - quantile([0.5], world.obsLayout.col("umap_1").asArray())[0] + quantile([0.5], world.obsLayout.col("umap_1").asArray())[0], ]; - centroidResult.forEach(coordinate => { + centroidResult.forEach((coordinate) => { expect(coordinate).toEqual(expectedResult); }); }); diff --git a/client/__tests__/util/dataframe/dataframe.test.js b/client/__tests__/util/dataframe/dataframe.test.js index f489f132..3ad843a9 100644 --- a/client/__tests__/util/dataframe/dataframe.test.js +++ b/client/__tests__/util/dataframe/dataframe.test.js @@ -53,7 +53,7 @@ describe("simple data access", () => { [4, 2], [ new Float64Array([0.0, Number.NaN, Number.POSITIVE_INFINITY, 3.14159]), - ["red", "blue", "green", "nan"] + ["red", "blue", "green", "nan"], ], new Dataframe.DenseInt32Index([3, 2, 1, 0]), new Dataframe.KeyIndex(["numbers", "colors"]) @@ -136,7 +136,7 @@ describe("dataframe subsetting", () => { new Int32Array([0, 1, 2]), ["A", "B", "C"], new Float32Array([4.4, 5.5, 6.6]), - ["red", "green", "blue"] + ["red", "green", "blue"], ], null, new Dataframe.KeyIndex(["int32", "string", "float32", "colors"]) @@ -258,7 +258,7 @@ describe("dataframe subsetting", () => { new Int32Array([0, 1, 2]), ["A", "B", "C"], new Float32Array([4.4, 5.5, 6.6]), - ["red", "green", "blue"] + ["red", "green", "blue"], ], new Dataframe.DenseInt32Index([2, 4, 6]), new Dataframe.KeyIndex(["int32", "string", "float32", "colors"]) @@ -283,7 +283,7 @@ describe("dataframe factories", () => { [ new Array(3).fill(0), new Int16Array(3).fill(99), - new Float64Array(3).fill(1.1) + new Float64Array(3).fill(1.1), ] ); @@ -323,7 +323,7 @@ describe("dataframe factories", () => { [2, 2], [ ["red", "blue"], - [true, false] + [true, false], ], null, new Dataframe.KeyIndex(["colors", "bools"]) @@ -346,7 +346,7 @@ describe("dataframe factories", () => { [2, 2], [ ["red", "blue"], - [true, false] + [true, false], ], null, new Dataframe.DenseInt32Index([74, 75]) @@ -371,7 +371,7 @@ describe("dataframe factories", () => { [2, 2], [ ["red", "blue"], - [true, false] + [true, false], ], null, new Dataframe.DenseInt32Index([74, 75]) @@ -396,7 +396,7 @@ describe("dataframe factories", () => { [2, 2], [ ["red", "blue"], - [true, false] + [true, false], ], null, null @@ -421,7 +421,7 @@ describe("dataframe factories", () => { [2, 2], [ ["red", "blue"], - [true, false] + [true, false], ], null, null @@ -479,7 +479,7 @@ describe("dataframe factories", () => { [ ["red", "blue"], [true, false], - [1, 0] + [1, 0], ], null, new Dataframe.KeyIndex(["colors", "bools", "numbers"]) @@ -553,7 +553,7 @@ describe("dataframe factories", () => { [ ["red", "blue"], [true, false], - [1, 0] + [1, 0], ], null, new Dataframe.KeyIndex(["colors", "bools", "numbers"]) @@ -595,7 +595,7 @@ describe("dataframe factories", () => { [ ["red", "blue"], [true, false], - [1, 0] + [1, 0], ], null, new Dataframe.KeyIndex(["colors", "bools", "numbers"]) @@ -618,7 +618,7 @@ describe("dataframe factories", () => { [ ["red", "blue"], [true, false], - [1, 0] + [1, 0], ], null, new Dataframe.KeyIndex(["colors", "bools", "numbers"]) @@ -641,7 +641,7 @@ describe("dataframe factories", () => { [ ["red", "blue"], [true, false], - [1, 0] + [1, 0], ], null, null @@ -665,7 +665,7 @@ describe("dataframe factories", () => { [ ["red", "blue"], [true, false], - [1, 0] + [1, 0], ], null, null @@ -689,7 +689,7 @@ describe("dataframe factories", () => { [ ["red", "blue"], [true, false], - [1, 0] + [1, 0], ], null, new Dataframe.DenseInt32Index([102, 101, 100]) @@ -715,7 +715,7 @@ describe("dataframe factories", () => { [ new Array(3).fill(0), new Int16Array(3).fill(99), - new Float64Array(3).fill(1.1) + new Float64Array(3).fill(1.1), ] ); const dfB = dfA.mapColumns((col, idx) => { @@ -760,7 +760,7 @@ describe("dataframe factories", () => { [2, 2], [ [true, false], - [1, 0] + [1, 0], ], null, new Dataframe.KeyIndex(["A", "B"]) @@ -781,7 +781,7 @@ describe("dataframe col", () => { [2, 2], [ [true, false], - [1, 0] + [1, 0], ], null, new Dataframe.KeyIndex(["A", "B"]) diff --git a/client/__tests__/util/dataframe/histogram.test.js b/client/__tests__/util/dataframe/histogram.test.js index 1549d27d..ce4c4943 100644 --- a/client/__tests__/util/dataframe/histogram.test.js +++ b/client/__tests__/util/dataframe/histogram.test.js @@ -14,7 +14,7 @@ describe("Dataframe column histogram", () => { new Map([ ["n1", new Map([["c1", 1]])], ["n2", new Map([["c2", 1]])], - ["n3", new Map([["c3", 1]])] + ["n3", new Map([["c3", 1]])], ]) ); // memoized? @@ -31,7 +31,11 @@ describe("Dataframe column histogram", () => { const h1 = df.col("value").histogram(3, [0, 2], df.col("name")); expect(h1).toMatchObject( - new Map([["n1", [1, 0, 0]], ["n2", [0, 1, 0]], ["n3", [0, 0, 1]]]) + new Map([ + ["n1", [1, 0, 0]], + ["n2", [0, 1, 0]], + ["n3", [0, 0, 1]], + ]) ); // memoized? expect(df.col("value").histogram(3, [0, 2], df.col("name"))).toMatchObject( @@ -48,7 +52,13 @@ describe("Dataframe column histogram", () => { ); const h1 = df.col("cat").histogram(); - expect(h1).toMatchObject(new Map([["c1", 1], ["c2", 1], ["c3", 1]])); + expect(h1).toMatchObject( + new Map([ + ["c1", 1], + ["c2", 1], + ["c3", 1], + ]) + ); // memoized? expect(df.col("value").histogram(3, [0, 2])).toMatchObject(h1); }); @@ -87,7 +97,7 @@ describe("Dataframe column histogram", () => { 0, 0, 0, - 2 + 2, ]); }); }); diff --git a/client/__tests__/util/dataframe/summarize.test.js b/client/__tests__/util/dataframe/summarize.test.js index 702d0fae..ec98d7be 100644 --- a/client/__tests__/util/dataframe/summarize.test.js +++ b/client/__tests__/util/dataframe/summarize.test.js @@ -13,7 +13,7 @@ describe("Dataframe column summary", () => { categorical: true, categories: [], categoryCounts: new Map(), - numCategories: 0 + numCategories: 0, }) ); }); @@ -27,7 +27,7 @@ describe("Dataframe column summary", () => { [true], new Float32Array([39.3]), new Int32Array([99]), - [1] + [1], ], null, new Dataframe.KeyIndex([ @@ -36,7 +36,7 @@ describe("Dataframe column summary", () => { "nameBoolean", "nameFloat32", "nameInt32", - "nameCategorical" + "nameCategorical", ]) ); @@ -45,7 +45,7 @@ describe("Dataframe column summary", () => { categorical: true, categories: ["n1"], categoryCounts: new Map([["n1", 1]]), - numCategories: 1 + numCategories: 1, }) ); expect(df.icol(1).summarize()).toEqual( @@ -53,7 +53,7 @@ describe("Dataframe column summary", () => { categorical: true, categories: ["hi"], categoryCounts: new Map([["hi", 1]]), - numCategories: 1 + numCategories: 1, }) ); expect(df.icol(2).summarize()).toEqual( @@ -61,7 +61,7 @@ describe("Dataframe column summary", () => { categorical: true, categories: [true], categoryCounts: new Map([[true, 1]]), - numCategories: 1 + numCategories: 1, }) ); expect(df.icol(3).summarize()).toEqual( @@ -71,7 +71,7 @@ describe("Dataframe column summary", () => { max: float32Conversion(39.3), nan: 0, ninf: 0, - pinf: 0 + pinf: 0, }) ); expect(df.icol(4).summarize()).toEqual( @@ -81,7 +81,7 @@ describe("Dataframe column summary", () => { max: 99, nan: 0, ninf: 0, - pinf: 0 + pinf: 0, }) ); expect(df.icol(5).summarize()).toEqual( @@ -89,7 +89,7 @@ describe("Dataframe column summary", () => { categorical: true, categories: [1], categoryCounts: new Map([[1, 1]]), - numCategories: 1 + numCategories: 1, }) ); }); @@ -103,7 +103,7 @@ describe("Dataframe column summary", () => { [false, true, true], new Float32Array([39.3, 39.3, 0]), new Int32Array([99, 99, 99]), - [1, false, "0"] + [1, false, "0"], ], null, new Dataframe.KeyIndex([ @@ -112,7 +112,7 @@ describe("Dataframe column summary", () => { "nameBoolean", "nameFloat32", "nameInt32", - "nameCategorical" + "nameCategorical", ]) ); @@ -120,24 +120,34 @@ describe("Dataframe column summary", () => { expect.objectContaining({ categorical: true, categories: expect.arrayContaining(["n0", "n1", "n2"]), - categoryCounts: new Map([["n0", 1], ["n1", 1], ["n2", 1]]), - numCategories: 3 + categoryCounts: new Map([ + ["n0", 1], + ["n1", 1], + ["n2", 1], + ]), + numCategories: 3, }) ); expect(df.icol(1).summarize()).toEqual( expect.objectContaining({ categorical: true, categories: expect.arrayContaining(["hi", "bye"]), - categoryCounts: new Map([["hi", 2], ["bye", 1]]), - numCategories: 2 + categoryCounts: new Map([ + ["hi", 2], + ["bye", 1], + ]), + numCategories: 2, }) ); expect(df.icol(2).summarize()).toEqual( expect.objectContaining({ categorical: true, categories: expect.arrayContaining([true, false]), - categoryCounts: new Map([[true, 2], [false, 1]]), - numCategories: 2 + categoryCounts: new Map([ + [true, 2], + [false, 1], + ]), + numCategories: 2, }) ); expect(df.icol(3).summarize()).toEqual( @@ -147,7 +157,7 @@ describe("Dataframe column summary", () => { max: float32Conversion(39.3), nan: 0, ninf: 0, - pinf: 0 + pinf: 0, }) ); expect(df.icol(4).summarize()).toEqual( @@ -157,15 +167,19 @@ describe("Dataframe column summary", () => { max: 99, nan: 0, ninf: 0, - pinf: 0 + pinf: 0, }) ); expect(df.icol(5).summarize()).toEqual( expect.objectContaining({ categorical: true, categories: expect.arrayContaining([1, false, "0"]), - categoryCounts: new Map([[1, 1], [false, 1], ["0", 1]]), - numCategories: 3 + categoryCounts: new Map([ + [1, 1], + [false, 1], + ["0", 1], + ]), + numCategories: 3, }) ); }); @@ -181,10 +195,10 @@ describe("Dataframe column summary", () => { 39.3, Number.NEGATIVE_INFINITY, Number.NaN, - Number.POSITIVE_INFINITY + Number.POSITIVE_INFINITY, ]), new Int32Array([99, 99, 99, 99]), - [1, false, "0", "0"] + [1, false, "0", "0"], ], null, new Dataframe.KeyIndex([ @@ -193,7 +207,7 @@ describe("Dataframe column summary", () => { "nameBoolean", "nameFloat32", "nameInt32", - "nameCategorical" + "nameCategorical", ]) ); @@ -201,24 +215,34 @@ describe("Dataframe column summary", () => { expect.objectContaining({ categorical: true, categories: expect.arrayContaining(["n0", "n1", "n2"]), - categoryCounts: new Map([["n0", 1], ["n1", 1], ["n2", 2]]), - numCategories: 3 + categoryCounts: new Map([ + ["n0", 1], + ["n1", 1], + ["n2", 2], + ]), + numCategories: 3, }) ); expect(df.icol(1).summarize()).toEqual( expect.objectContaining({ categorical: true, categories: expect.arrayContaining(["hi", "bye"]), - categoryCounts: new Map([["hi", 2], ["bye", 1]]), - numCategories: 2 + categoryCounts: new Map([ + ["hi", 2], + ["bye", 1], + ]), + numCategories: 2, }) ); expect(df.icol(2).summarize()).toEqual( expect.objectContaining({ categorical: true, categories: expect.arrayContaining([true, false]), - categoryCounts: new Map([[true, 2], [false, 1]]), - numCategories: 2 + categoryCounts: new Map([ + [true, 2], + [false, 1], + ]), + numCategories: 2, }) ); expect(df.icol(3).summarize()).toEqual( @@ -228,7 +252,7 @@ describe("Dataframe column summary", () => { max: float32Conversion(39.3), nan: 1, ninf: 1, - pinf: 1 + pinf: 1, }) ); expect(df.icol(4).summarize()).toEqual( @@ -238,15 +262,19 @@ describe("Dataframe column summary", () => { max: 99, nan: 0, ninf: 0, - pinf: 0 + pinf: 0, }) ); expect(df.icol(5).summarize()).toEqual( expect.objectContaining({ categorical: true, categories: expect.arrayContaining([1, false, "0"]), - categoryCounts: new Map([[1, 1], [false, 1], ["0", 1]]), - numCategories: 3 + categoryCounts: new Map([ + [1, 1], + [false, 1], + ["0", 1], + ]), + numCategories: 3, }) ); }); diff --git a/client/__tests__/util/nameCreators.test.js b/client/__tests__/util/nameCreators.test.js index 5c6f462c..99ea0a62 100644 --- a/client/__tests__/util/nameCreators.test.js +++ b/client/__tests__/util/nameCreators.test.js @@ -6,7 +6,7 @@ import { obsAnnoDimensionName, diffexpDimensionName, userDefinedDimensionName, - makeContinuousDimensionName + makeContinuousDimensionName, } from "../../src/util/nameCreators"; describe("nameCreators", () => { @@ -14,25 +14,25 @@ describe("nameCreators", () => { layoutDimensionName, obsAnnoDimensionName, diffexpDimensionName, - userDefinedDimensionName + userDefinedDimensionName, ]; test("check for namespace isolation", () => { const foo = "foo"; - nameCreators.forEach(fn => expect(fn(foo)).not.toBe(foo)); + nameCreators.forEach((fn) => expect(fn(foo)).not.toBe(foo)); const bar = "bar"; - nameCreators.forEach(fn => expect(fn(foo)).not.toBe(fn(bar))); + nameCreators.forEach((fn) => expect(fn(foo)).not.toBe(fn(bar))); - nameCreators.forEach(fn => { - const allOtherFn = nameCreators.filter(elmnt => elmnt !== fn); - allOtherFn.forEach(otherFn => expect(fn(foo)).not.toBe(otherFn(foo))); + nameCreators.forEach((fn) => { + const allOtherFn = nameCreators.filter((elmnt) => elmnt !== fn); + allOtherFn.forEach((otherFn) => expect(fn(foo)).not.toBe(otherFn(foo))); }); }); test("check for legal keys", () => { /* need namespace creators to return strings only */ - nameCreators.forEach(fn => expect(fn("X")).toMatch(/X/)); + nameCreators.forEach((fn) => expect(fn("X")).toMatch(/X/)); }); }); diff --git a/client/__tests__/util/promiseLimit.test.js b/client/__tests__/util/promiseLimit.test.js index 589c4acf..fc6832f6 100644 --- a/client/__tests__/util/promiseLimit.test.js +++ b/client/__tests__/util/promiseLimit.test.js @@ -1,73 +1,73 @@ import { PromiseLimit } from "../../src/util/promiseLimit"; import { range } from "../../src/util/range"; -const delay = t => new Promise((resolve, reject) => setTimeout(resolve, t)); +const delay = (t) => new Promise((resolve, reject) => setTimeout(resolve, t)); describe("PromiseLimit", () => { - test("simple evaluation, concurrency 1", async () => { - const plimit = new PromiseLimit(1); - const result = await Promise.all([ - plimit.add(() => Promise.resolve(1)), - plimit.add(() => Promise.resolve(2)), - plimit.add(() => Promise.resolve(3)), - plimit.add(() => Promise.resolve(4)) - ]); - expect(result).toEqual([1, 2, 3, 4]); - }); + test("simple evaluation, concurrency 1", async () => { + const plimit = new PromiseLimit(1); + const result = await Promise.all([ + plimit.add(() => Promise.resolve(1)), + plimit.add(() => Promise.resolve(2)), + plimit.add(() => Promise.resolve(3)), + plimit.add(() => Promise.resolve(4)), + ]); + expect(result).toEqual([1, 2, 3, 4]); + }); - test("simple evaluation, concurrency > 1", async () => { - const plimit = new PromiseLimit(100); - const result = await Promise.all([ - plimit.add(() => Promise.resolve(1)), - plimit.add(() => Promise.resolve(2)), - plimit.add(() => Promise.resolve(3)), - plimit.add(() => Promise.resolve(4)) - ]); - expect(result).toEqual([1, 2, 3, 4]); - }); + test("simple evaluation, concurrency > 1", async () => { + const plimit = new PromiseLimit(100); + const result = await Promise.all([ + plimit.add(() => Promise.resolve(1)), + plimit.add(() => Promise.resolve(2)), + plimit.add(() => Promise.resolve(3)), + plimit.add(() => Promise.resolve(4)), + ]); + expect(result).toEqual([1, 2, 3, 4]); + }); - test("eval in order of insertion", async () => { - const plimit = new PromiseLimit(100); - let counter = 0; - const result = await Promise.all([ - plimit.add(() => Promise.resolve((counter += 1))), - plimit.add(() => Promise.resolve((counter += 1))), - plimit.add(() => Promise.resolve((counter += 1))), - plimit.add(() => Promise.resolve((counter += 1))) - ]); - expect(result).toEqual([1, 2, 3, 4]); - }); + test("eval in order of insertion", async () => { + const plimit = new PromiseLimit(100); + let counter = 0; + const result = await Promise.all([ + plimit.add(() => Promise.resolve((counter += 1))), + plimit.add(() => Promise.resolve((counter += 1))), + plimit.add(() => Promise.resolve((counter += 1))), + plimit.add(() => Promise.resolve((counter += 1))), + ]); + expect(result).toEqual([1, 2, 3, 4]); + }); - test("obeys concurrency limit", async () => { - const plimit = new PromiseLimit(2); - let running = 0; - let maxRunning = 0; + test("obeys concurrency limit", async () => { + const plimit = new PromiseLimit(2); + let running = 0; + let maxRunning = 0; - const cbfn = async i => { - running = running + 1; - maxRunning = running > maxRunning ? running : maxRunning; - await delay(100); - running = running - 1; - }; + const cbfn = async (i) => { + running = running + 1; + maxRunning = running > maxRunning ? running : maxRunning; + await delay(100); + running = running - 1; + }; - const result = await Promise.all( - range(10).map(i => plimit.add(() => cbfn(i))) - ); - expect(maxRunning).toEqual(2); - }); + const result = await Promise.all( + range(10).map((i) => plimit.add(() => cbfn(i))) + ); + expect(maxRunning).toEqual(2); + }); - test("rejection", async () => { - const plimit = new PromiseLimit(2); - const result = await Promise.all([ - plimit.add(() => Promise.resolve("OK")), - plimit.add(() => Promise.reject("not OK")).catch(e => e), - plimit.add(() => Promise.resolve("OK")), - plimit - .add(() => { - throw new Error("not OK"); - }) - .catch(e => e.message) - ]); - expect(result).toEqual(["OK", "not OK", "OK", "not OK"]); - }); + test("rejection", async () => { + const plimit = new PromiseLimit(2); + const result = await Promise.all([ + plimit.add(() => Promise.resolve("OK")), + plimit.add(() => Promise.reject("not OK")).catch((e) => e), + plimit.add(() => Promise.resolve("OK")), + plimit + .add(() => { + throw new Error("not OK"); + }) + .catch((e) => e.message), + ]); + expect(result).toEqual(["OK", "not OK", "OK", "not OK"]); + }); }); diff --git a/client/__tests__/util/quantile.test.js b/client/__tests__/util/quantile.test.js index b8b49954..b316554c 100644 --- a/client/__tests__/util/quantile.test.js +++ b/client/__tests__/util/quantile.test.js @@ -1,29 +1,29 @@ import quantile from "../../src/util/quantile"; describe("quantile", () => { - test("single q", () => { - const arr = new Float32Array([9, 3, 5, 6, 0]); - expect(quantile([1.0], arr)).toMatchObject([9]); - expect(quantile([0.9], arr)).toMatchObject([9]); - expect(quantile([0.8], arr)).toMatchObject([9]); - expect(quantile([0.7], arr)).toMatchObject([6]); - expect(quantile([0.6], arr)).toMatchObject([6]); - expect(quantile([0.5], arr)).toMatchObject([5]); - expect(quantile([0.4], arr)).toMatchObject([5]); - expect(quantile([0.3], arr)).toMatchObject([3]); - expect(quantile([0.2], arr)).toMatchObject([3]); - expect(quantile([0.1], arr)).toMatchObject([0]); - expect(quantile([0], arr)).toMatchObject([0]); - }); + test("single q", () => { + const arr = new Float32Array([9, 3, 5, 6, 0]); + expect(quantile([1.0], arr)).toMatchObject([9]); + expect(quantile([0.9], arr)).toMatchObject([9]); + expect(quantile([0.8], arr)).toMatchObject([9]); + expect(quantile([0.7], arr)).toMatchObject([6]); + expect(quantile([0.6], arr)).toMatchObject([6]); + expect(quantile([0.5], arr)).toMatchObject([5]); + expect(quantile([0.4], arr)).toMatchObject([5]); + expect(quantile([0.3], arr)).toMatchObject([3]); + expect(quantile([0.2], arr)).toMatchObject([3]); + expect(quantile([0.1], arr)).toMatchObject([0]); + expect(quantile([0], arr)).toMatchObject([0]); + }); - test("multi q", () => { - const arr = new Float32Array([9, 3, 5, 6, 0]); - expect(quantile([0, 0.25, 0.5, 0.75, 1.0], arr)).toMatchObject([ - 0, - 3, - 5, - 6, - 9 - ]); - }); + test("multi q", () => { + const arr = new Float32Array([9, 3, 5, 6, 0]); + expect(quantile([0, 0.25, 0.5, 0.75, 1.0], arr)).toMatchObject([ + 0, + 3, + 5, + 6, + 9, + ]); + }); }); diff --git a/client/__tests__/util/range.test.js b/client/__tests__/util/range.test.js index fac1ade3..5da8821d 100644 --- a/client/__tests__/util/range.test.js +++ b/client/__tests__/util/range.test.js @@ -1,50 +1,48 @@ import { range, rangeFill, linspace } from "../../src/util/range"; describe("range", () => { - test("no defaults", () => { - expect(range(0, 3, 1)).toMatchObject([0, 1, 2]); - }); + test("no defaults", () => { + expect(range(0, 3, 1)).toMatchObject([0, 1, 2]); + }); - test("range(stop)", () => { - expect(range(3)).toMatchObject([0, 1, 2]); - expect(range(0)).toMatchObject([]); - expect(range(1)).toMatchObject([0]); - }); + test("range(stop)", () => { + expect(range(3)).toMatchObject([0, 1, 2]); + expect(range(0)).toMatchObject([]); + expect(range(1)).toMatchObject([0]); + }); - test("range(start,stop)", () => { - expect(range(0, 0)).toMatchObject([]); - expect(range(0, 2)).toMatchObject([0, 1]); - expect(range(4, 8)).toMatchObject([4, 5, 6, 7]); - }); + test("range(start,stop)", () => { + expect(range(0, 0)).toMatchObject([]); + expect(range(0, 2)).toMatchObject([0, 1]); + expect(range(4, 8)).toMatchObject([4, 5, 6, 7]); + }); - test("range(start, stop, step", () => { - expect(range(4, 0, -1)).toMatchObject([4, 3, 2, 1]); - expect(range(0, 4, 2)).toMatchObject([0, 2]); - }); + test("range(start, stop, step", () => { + expect(range(4, 0, -1)).toMatchObject([4, 3, 2, 1]); + expect(range(0, 4, 2)).toMatchObject([0, 2]); + }); }); describe("rangefill", () => { - test("rangeFill(arr)", () => { - expect(rangeFill(new Int32Array(3))).toMatchObject( - new Int32Array([0, 1, 2]) - ); - }); - test("rangeFill(arr, start)", () => { - expect(rangeFill(new Int32Array(2), 1)).toMatchObject( - new Int32Array([1, 2]) - ); - }); - test("rangeFill(arr, start, step)", () => { - expect(rangeFill(new Int32Array(3), 2, -1)).toMatchObject( - new Int32Array([2, 1, 0]) - ); - }); + test("rangeFill(arr)", () => { + expect(rangeFill(new Int32Array(3))).toMatchObject( + new Int32Array([0, 1, 2]) + ); + }); + test("rangeFill(arr, start)", () => { + expect(rangeFill(new Int32Array(2), 1)).toMatchObject( + new Int32Array([1, 2]) + ); + }); + test("rangeFill(arr, start, step)", () => { + expect(rangeFill(new Int32Array(3), 2, -1)).toMatchObject( + new Int32Array([2, 1, 0]) + ); + }); }); describe("linspace", () => { - test("linspace(arr, start, step)", () => { - expect(linspace(0.0, 2.0, 5)).toMatchObject( - [0.0, 0.5, 1.0, 1.5, 2.0] - ); - }); + test("linspace(arr, start, step)", () => { + expect(linspace(0.0, 2.0, 5)).toMatchObject([0.0, 0.5, 1.0, 1.5, 2.0]); + }); }); diff --git a/client/__tests__/util/stateManager/controlsHelpers.test.js b/client/__tests__/util/stateManager/controlsHelpers.test.js index 1e7b1a7a..eed02a14 100644 --- a/client/__tests__/util/stateManager/controlsHelpers.test.js +++ b/client/__tests__/util/stateManager/controlsHelpers.test.js @@ -5,20 +5,23 @@ import { subsetAndResetGeneLists } from "../../../src/util/stateManager/controls import * as globals from "../../../src/globals"; describe("controls helpers", () => { - test("subsetAndResetGeneLists", () => { - const geneList = [...Array(150).keys()].map(() => - Math.random().toString(36).substring(2, 6) // random string of 4 characters + const geneList = [...Array(150).keys()].map( + () => Math.random().toString(36).substring(2, 6) // random string of 4 characters ); const state = { userDefinedGenes: geneList.slice(0, 20), diffexpGenes: geneList.slice(20), }; - const [newUserDefinedGenes, newDiffExpGenes] = subsetAndResetGeneLists(state); + const [newUserDefinedGenes, newDiffExpGenes] = subsetAndResetGeneLists( + state + ); expect(globals.maxUserDefinedGenes).toBeLessThan(globals.maxGenes); expect(geneList.length).toBeGreaterThan(globals.maxGenes); expect(newUserDefinedGenes).toHaveLength(globals.maxGenes); - expect(newUserDefinedGenes).toStrictEqual(geneList.slice(0, globals.maxGenes)); + expect(newUserDefinedGenes).toStrictEqual( + geneList.slice(0, globals.maxGenes) + ); expect(newDiffExpGenes).toStrictEqual([]); }); }); diff --git a/client/__tests__/util/stateManager/fbs.test.js b/client/__tests__/util/stateManager/fbs.test.js index 612a2316..c7c639e7 100644 --- a/client/__tests__/util/stateManager/fbs.test.js +++ b/client/__tests__/util/stateManager/fbs.test.js @@ -3,32 +3,32 @@ test FBS encode/decode API */ import { Dataframe, KeyIndex } from "../../../src/util/dataframe"; import { - decodeMatrixFBS, - encodeMatrixFBS + decodeMatrixFBS, + encodeMatrixFBS, } from "../../../src/util/stateManager/matrix"; describe("encode/decode", () => { - test("round trip", () => { - const columns = [ - ["red", "green", "blue"], - new Int32Array(3).fill(0), - new Uint32Array(3).fill(1), - new Float32Array(3).fill(2) - ]; + test("round trip", () => { + const columns = [ + ["red", "green", "blue"], + new Int32Array(3).fill(0), + new Uint32Array(3).fill(1), + new Float32Array(3).fill(2), + ]; - const dfNoColIdx = new Dataframe([3, 4], columns); - const dfA = decodeMatrixFBS(encodeMatrixFBS(dfNoColIdx)); - expect([dfA.nRows, dfA.nCols]).toEqual(dfNoColIdx.dims); - expect(dfA.colIdx).toBeNull(); - expect(dfA.rowIdx).toBeNull(); - expect(dfA.columns).toEqual(columns); + const dfNoColIdx = new Dataframe([3, 4], columns); + const dfA = decodeMatrixFBS(encodeMatrixFBS(dfNoColIdx)); + expect([dfA.nRows, dfA.nCols]).toEqual(dfNoColIdx.dims); + expect(dfA.colIdx).toBeNull(); + expect(dfA.rowIdx).toBeNull(); + expect(dfA.columns).toEqual(columns); - const colIndex = new KeyIndex(["a", "b", "c", "d"]); - const dfWithColIdx = new Dataframe([3, 4], columns, null, colIndex); - const dfB = decodeMatrixFBS(encodeMatrixFBS(dfWithColIdx)); - expect([dfB.nRows, dfB.nCols]).toEqual(dfWithColIdx.dims); - expect(dfB.colIdx).toEqual(colIndex.keys()); - expect(dfB.rowIdx).toBeNull(); - expect(dfB.columns).toEqual(columns); - }); + const colIndex = new KeyIndex(["a", "b", "c", "d"]); + const dfWithColIdx = new Dataframe([3, 4], columns, null, colIndex); + const dfB = decodeMatrixFBS(encodeMatrixFBS(dfWithColIdx)); + expect([dfB.nRows, dfB.nCols]).toEqual(dfWithColIdx.dims); + expect(dfB.colIdx).toEqual(colIndex.keys()); + expect(dfB.rowIdx).toBeNull(); + expect(dfB.columns).toEqual(columns); + }); }); diff --git a/client/__tests__/util/stateManager/sampleResponses.js b/client/__tests__/util/stateManager/sampleResponses.js index 17fb0e8b..9e2041d0 100644 --- a/client/__tests__/util/stateManager/sampleResponses.js +++ b/client/__tests__/util/stateManager/sampleResponses.js @@ -17,13 +17,13 @@ const aConfigResponse = { { method: "POST", path: "/cluster/", available: false }, { method: "POST", path: "/layout/", available: false }, { method: "POST", path: "/diffexp/", available: false }, - { method: "POST", path: "/saveLocal/", available: false } + { method: "POST", path: "/saveLocal/", available: false }, ], displayNames: { engine: "the little engine that could", - dataset: "all your zeros are mine" - } - } + dataset: "all your zeros are mine", + }, + }, }; const aSchemaResponse = { @@ -31,7 +31,7 @@ const aSchemaResponse = { dataframe: { nObs, nVar, - type: "float32" + type: "float32", }, annotations: { obs: { @@ -44,9 +44,9 @@ const aSchemaResponse = { { name: "field4", type: "categorical", - categories: field4Categories - } - ] + categories: field4Categories, + }, + ], }, var: { index: "name", @@ -58,46 +58,46 @@ const aSchemaResponse = { { name: "fieldD", type: "categorical", - categories: fieldDCategories - } - ] - } + categories: fieldDCategories, + }, + ], + }, }, layout: { obs: [{ name: "umap", type: "float32", dims: ["umap_0", "umap_1"] }], - var: [] - } - } + var: [], + }, + }, }; const anAnnotationsObsJSONResponse = { names: ["name", "field1", "field2", "field3", "field4"], data: _() .range(nObs) - .map(idx => [ + .map((idx) => [ idx, `obs${idx}`, 2 * idx, idx + 0.0133, !!(idx & 1), - field4Categories[idx % field4Categories.length] + field4Categories[idx % field4Categories.length], ]) - .value() + .value(), }; const anAnnotationsVarJSONResponse = { names: ["fieldA", "fieldB", "fieldC", "fieldD", "name"], data: _() .range(nVar) - .map(idx => [ + .map((idx) => [ idx, 10 * idx, idx + 2.90143, !!(idx & 1), fieldDCategories[idx % fieldDCategories.length], - `var${idx}` + `var${idx}`, ]) - .value() + .value(), }; function encodeTypedArray(builder, uType, uData) { @@ -120,7 +120,7 @@ function encodeMatrix(columns, colIndex = undefined) { */ const utf8Encoder = new TextEncoder("utf-8"); const builder = new flatbuffers.Builder(1024); - const cols = _.map(columns, carr => { + const cols = _.map(columns, (carr) => { let uType; let tarr; if (_.every(carr, _.isNumber)) { @@ -178,7 +178,7 @@ const anAnnotationsVarFBSResponse = (() => { const aLayoutFBSResponse = (() => { const coords = [ new Float32Array(nObs).fill(Math.random()), - new Float32Array(nObs).fill(Math.random()) + new Float32Array(nObs).fill(Math.random()), ]; return encodeMatrix(coords, ["umap_0", "umap_1"]); })(); @@ -187,8 +187,8 @@ const aDataObsResponse = { var: [2, 4, 29], obs: _() .range(nObs) - .map(idx => [idx, Math.random(), Math.random(), Math.random()]) - .value() + .map((idx) => [idx, Math.random(), Math.random(), Math.random()]) + .value(), }; export { @@ -197,5 +197,5 @@ export { anAnnotationsVarFBSResponse as annotationsVar, anAnnotationsObsFBSResponse as annotationsObs, aSchemaResponse as schema, - aConfigResponse as config + aConfigResponse as config, }; diff --git a/client/__tests__/util/stateManager/universe.test.js b/client/__tests__/util/stateManager/universe.test.js index f647d7e7..76964723 100644 --- a/client/__tests__/util/stateManager/universe.test.js +++ b/client/__tests__/util/stateManager/universe.test.js @@ -43,7 +43,7 @@ describe("createUniverseFromResponse", () => { obsAnnotations: expect.any(Dataframe.Dataframe), varAnnotations: expect.any(Dataframe.Dataframe), obsLayout: expect.any(Dataframe.Dataframe), - varData: expect.any(Dataframe.Dataframe) + varData: expect.any(Dataframe.Dataframe), }) ); @@ -60,7 +60,7 @@ describe("createUniverseFromResponse", () => { ...Universe.addObsLayout( universe, Universe.matrixFBSToDataframe(REST.layoutObs) - ) + ), }; expect(universe).toMatchObject( @@ -71,13 +71,13 @@ describe("createUniverseFromResponse", () => { obsAnnotations: expect.any(Dataframe.Dataframe), varAnnotations: expect.any(Dataframe.Dataframe), obsLayout: expect.any(Dataframe.Dataframe), - varData: expect.any(Dataframe.Dataframe) + varData: expect.any(Dataframe.Dataframe), }) ); expect(universe.obsAnnotations.dims).toEqual([ nObs, - REST.schema.schema.annotations.obs.columns.length + REST.schema.schema.annotations.obs.columns.length, ]); expect(universe.obsLayout.dims).toEqual([nObs, 2]); expect(universe.obsLayout.colIndex.keys()).toEqual( @@ -85,7 +85,7 @@ describe("createUniverseFromResponse", () => { ); expect(universe.varAnnotations.dims).toEqual([ nVar, - REST.schema.schema.annotations.var.columns.length + REST.schema.schema.annotations.var.columns.length, ]); expect(universe.varData.isEmpty()).toBeTruthy(); }); diff --git a/client/__tests__/util/stateManager/world.test.js b/client/__tests__/util/stateManager/world.test.js index 62ea7626..6b6a5bfb 100644 --- a/client/__tests__/util/stateManager/world.test.js +++ b/client/__tests__/util/stateManager/world.test.js @@ -7,7 +7,7 @@ import { DimTypes } from "../../../src/util/typedCrossfilter/crossfilter"; import * as REST from "./sampleResponses"; import { obsAnnoDimensionName, - layoutDimensionName + layoutDimensionName, } from "../../../src/util/nameCreators"; /* @@ -35,7 +35,7 @@ const defaultBigBang = () => { ...Universe.addObsLayout( universe, Universe.matrixFBSToDataframe(REST.layoutObs) - ) + ), }; /* create world */ @@ -50,7 +50,7 @@ const defaultBigBang = () => { return { universe, world, - crossfilter + crossfilter, }; }; @@ -80,8 +80,8 @@ describe("createWorldFromEntireUniverse", () => { clipQuantiles: { min: 0, max: 1 }, unclipped: { obsAnnotations: expect.any(Dataframe.Dataframe), - varData: expect.any(Dataframe.Dataframe) - } + varData: expect.any(Dataframe.Dataframe), + }, }) ); }); @@ -92,7 +92,7 @@ describe("createWorldFromCurrentSelection", () => { const { universe, world: originalWorld, - crossfilter: originalCrossfilter + crossfilter: originalCrossfilter, } = defaultBigBang(); /* mock a selection */ @@ -100,7 +100,7 @@ describe("createWorldFromCurrentSelection", () => { .select(obsAnnoDimensionName("field1"), { mode: "range", lo: 0, hi: 5 }) .select(obsAnnoDimensionName("field3"), { mode: "exact", - values: [false] + values: [false], }); /* create the world from the selection */ @@ -124,7 +124,7 @@ describe("createWorldFromCurrentSelection", () => { }; const matchingIndices = _() .range(universe.nObs) - .filter(idx => matchFilter(universe.obsAnnotations, idx)) + .filter((idx) => matchFilter(universe.obsAnnotations, idx)) .value(); expect(world).toMatchObject( @@ -139,8 +139,8 @@ describe("createWorldFromCurrentSelection", () => { varData: expect.any(Dataframe.Dataframe), unclipped: { obsAnnotations: expect.any(Dataframe.Dataframe), - varData: expect.any(Dataframe.Dataframe) - } + varData: expect.any(Dataframe.Dataframe), + }, }) ); @@ -170,7 +170,7 @@ describe("createObsDimensionMap", () => { const { crossfilter } = defaultBigBang(); const annotationNames = _.map( REST.schema.schema.annotations.obs.columns, - c => c.name + (c) => c.name ); const obsIndexColName = REST.schema.schema.annotations.obs.index; const schemaByObsName = _.keyBy( @@ -178,7 +178,7 @@ describe("createObsDimensionMap", () => { "name" ); expect(crossfilter).toBeDefined(); - annotationNames.forEach(name => { + annotationNames.forEach((name) => { const dim = crossfilter.dimensions[obsAnnoDimensionName(name)]; if (name === obsIndexColName) { expect(dim).toBeUndefined(); diff --git a/client/__tests__/util/typedCrossfilter/bitArray.test.js b/client/__tests__/util/typedCrossfilter/bitArray.test.js index 3a712328..90ad4760 100644 --- a/client/__tests__/util/typedCrossfilter/bitArray.test.js +++ b/client/__tests__/util/typedCrossfilter/bitArray.test.js @@ -187,7 +187,7 @@ describe("fillBySelection", () => { describe("wide bitarray", () => { test.each([9, 30, 31, 32, 33, 54, 63, 64, 65, 127, 128, 129])( "more than %d dimensions", - ndim => { + (ndim) => { /* ensure we move across the uint boundary correctly */ const ba = new BitArray(defaultTestLength); diff --git a/client/__tests__/util/typedCrossfilter/crossfilter.test.js b/client/__tests__/util/typedCrossfilter/crossfilter.test.js index 9fd4e298..a65d2949 100644 --- a/client/__tests__/util/typedCrossfilter/crossfilter.test.js +++ b/client/__tests__/util/typedCrossfilter/crossfilter.test.js @@ -11,7 +11,7 @@ const someData = [ type: "tab", productIDs: ["001"], coords: [0, 0], - nonFinite: 0.0 + nonFinite: 0.0, }, { date: "2011-11-14T16:20:19Z", @@ -21,7 +21,7 @@ const someData = [ type: "tab", productIDs: ["001", "005"], coords: [0.4, 0.4], - nonFinite: Number.NaN + nonFinite: Number.NaN, }, { date: "2011-11-14T16:28:54Z", @@ -31,7 +31,7 @@ const someData = [ type: "visa", productIDs: ["004", "005"], coords: [0.3, 0.1], - nonFinite: Number.POSITIVE_INFINITY + nonFinite: Number.POSITIVE_INFINITY, }, { date: "2011-11-14T16:30:43Z", @@ -41,7 +41,7 @@ const someData = [ type: "tab", productIDs: ["001", "002"], coords: [0.392, 0.1], - nonFinite: Number.NEGATIVE_INFINITY + nonFinite: Number.NEGATIVE_INFINITY, }, { date: "2011-11-14T16:48:46Z", @@ -51,7 +51,7 @@ const someData = [ type: "tab", productIDs: ["005"], coords: [0.7, 0.0482], - nonFinite: 1.0 + nonFinite: 1.0, }, { date: "2011-11-14T16:53:41Z", @@ -61,7 +61,7 @@ const someData = [ type: "tab", productIDs: ["001", "004", "005"], coords: [0.9999, 1.0], - nonFinite: Number.NaN + nonFinite: Number.NaN, }, { date: "2011-11-14T16:54:06Z", @@ -71,7 +71,7 @@ const someData = [ type: "cash", productIDs: ["001", "002", "003", "004", "005"], coords: [0.384, 0.6938], - nonFinite: 99.0 + nonFinite: 99.0, }, { date: "2011-11-14T16:58:03Z", @@ -81,7 +81,7 @@ const someData = [ type: "tab", productIDs: ["001"], coords: [0.4822, 0.482], - nonFinite: Number.NaN + nonFinite: Number.NaN, }, { date: "2011-11-14T17:07:21Z", @@ -91,7 +91,7 @@ const someData = [ type: "tab", productIDs: ["004", "005"], coords: [0.2234, 0], - nonFinite: Number.NaN + nonFinite: Number.NaN, }, { date: "2011-11-14T17:22:59Z", @@ -101,7 +101,7 @@ const someData = [ type: "tab", productIDs: ["001", "002", "004", "005"], coords: [0.382, 0.38485], - nonFinite: -1 + nonFinite: -1, }, { date: "2011-11-14T17:25:45Z", @@ -111,7 +111,7 @@ const someData = [ type: "cash", productIDs: ["002"], coords: [0.998, 0.8472], - nonFinite: 0.0 + nonFinite: 0.0, }, { date: "2011-11-14T17:29:52Z", @@ -121,8 +121,8 @@ const someData = [ type: "visa", productIDs: ["004"], coords: [0.8273, 0.3384], - nonFinite: 0.0 - } + nonFinite: 0.0, + }, ]; let payments = null; @@ -248,16 +248,21 @@ describe("ImmutableTypedCrossfilter", () => { test("none", () => { expect(p.select("quantity", { mode: "none" }).countSelected()).toEqual(0); }); - test.each([[[]], [[2]], [[2, 1]], [[9, 82]], [[0, 1]]])("exact: %p", v => + test.each([[[]], [[2]], [[2, 1]], [[9, 82]], [[0, 1]]])("exact: %p", (v) => expect( p.select("quantity", { mode: "exact", values: v }).countSelected() - ).toEqual(_.filter(someData, d => v.includes(d.quantity)).length) + ).toEqual(_.filter(someData, (d) => v.includes(d.quantity)).length) ); - test.each([[0, 1], [1, 2], [0, 99], [99, 100000]])("range %p", (lo, hi) => + test.each([ + [0, 1], + [1, 2], + [0, 99], + [99, 100000], + ])("range %p", (lo, hi) => expect( p.select("quantity", { mode: "range", lo, hi }).countSelected() ).toEqual( - _.filter(someData, d => d.quantity >= lo && d.quantity < hi).length + _.filter(someData, (d) => d.quantity >= lo && d.quantity < hi).length ) ); test("bad mode", () => { @@ -284,11 +289,11 @@ describe("ImmutableTypedCrossfilter", () => { [["tab"]], [["visa"]], [["visa", "tab"]], - [["cash", "tab", "visa"]] - ])("exact: %p", v => + [["cash", "tab", "visa"]], + ])("exact: %p", (v) => expect( p.select("type", { mode: "exact", values: v }).countSelected() - ).toEqual(_.filter(someData, d => v.includes(d.type)).length) + ).toEqual(_.filter(someData, (d) => v.includes(d.type)).length) ); test("range", () => { expect(() => p.select("type", { mode: "range", lo: 0, hi: 9 })).toThrow( @@ -303,8 +308,8 @@ describe("ImmutableTypedCrossfilter", () => { describe("spatial dimension", () => { let p; beforeEach(() => { - const X = someData.map(r => r.coords[0]); - const Y = someData.map(r => r.coords[1]); + const X = someData.map((r) => r.coords[0]); + const Y = someData.map((r) => r.coords[1]); p = payments.addDimension("coords", "spatial", X, Y); }); @@ -316,34 +321,76 @@ describe("ImmutableTypedCrossfilter", () => { test("none", () => { expect(p.select("coords", { mode: "none" }).countSelected()).toEqual(0); }); - test.each([[0, 0, 1, 1], [0, 0, 0.5, 0.5], [0.5, 0.5, 1, 1]])( - "within-rect %d %d %d %d", - (minX, minY, maxX, maxY) => { - expect( - p - .select("coords", { mode: "within-rect", minX, minY, maxX, maxY }) - .allSelected() - ).toEqual( - _.filter(someData, d => { - const [x, y] = d.coords; - return minX <= x && x < maxX && minY <= y && y < maxY; - }) - ); - } - ); + test.each([ + [0, 0, 1, 1], + [0, 0, 0.5, 0.5], + [0.5, 0.5, 1, 1], + ])("within-rect %d %d %d %d", (minX, minY, maxX, maxY) => { + expect( + p + .select("coords", { mode: "within-rect", minX, minY, maxX, maxY }) + .allSelected() + ).toEqual( + _.filter(someData, (d) => { + const [x, y] = d.coords; + return minX <= x && x < maxX && minY <= y && y < maxY; + }) + ); + }); test.each([ [ - [[0, 0], [0, 1], [1, 1], [1, 0]], - [true, true, true, true, true, false, true, true, true, true, true, true] + [ + [0, 0], + [0, 1], + [1, 1], + [1, 0], + ], + [ + true, + true, + true, + true, + true, + false, + true, + true, + true, + true, + true, + true, + ], ], [ - [[0, 0], [0, 0.5], [0.5, 0.5], [0.5, 0]], - [true, true, true, true, false, false, false, true, true, true, false, false] - ] + [ + [0, 0], + [0, 0.5], + [0.5, 0.5], + [0.5, 0], + ], + [ + true, + true, + true, + true, + false, + false, + false, + true, + true, + true, + false, + false, + ], + ], ])("within-polygon %p", (polygon, expected) => { - expect(p.select("coords", { mode: "within-polygon", polygon }).allSelected()) - .toEqual(_.zip(someData, expected).filter(x => x[1]).map(x => x[0])); + expect( + p.select("coords", { mode: "within-polygon", polygon }).allSelected() + ).toEqual( + _.zip(someData, expected) + .filter((x) => x[1]) + .map((x) => x[0]) + ); }); }); @@ -381,7 +428,7 @@ describe("ImmutableTypedCrossfilter", () => { p .select("nonFinite", { mode: "exact", - values: [Number.POSITIVE_INFINITY] + values: [Number.POSITIVE_INFINITY], }) .countSelected() ).toEqual(1); @@ -389,7 +436,7 @@ describe("ImmutableTypedCrossfilter", () => { p .select("nonFinite", { mode: "exact", - values: [Number.NEGATIVE_INFINITY] + values: [Number.NEGATIVE_INFINITY], }) .countSelected() ).toEqual(1); @@ -402,7 +449,7 @@ describe("ImmutableTypedCrossfilter", () => { p .select("nonFinite", { mode: "exact", - values: [Number.POSITIVE_INFINITY, 0, 1, 99] + values: [Number.POSITIVE_INFINITY, 0, 1, 99], }) .countSelected() ).toEqual(6); @@ -414,7 +461,7 @@ describe("ImmutableTypedCrossfilter", () => { .select("nonFinite", { mode: "range", lo: 0, - hi: Number.POSITIVE_INFINITY + hi: Number.POSITIVE_INFINITY, }) .countSelected() ).toEqual(5); @@ -423,7 +470,7 @@ describe("ImmutableTypedCrossfilter", () => { .select("nonFinite", { mode: "range", lo: 0, - hi: Number.NaN + hi: Number.NaN, }) .countSelected() ).toEqual(6); @@ -432,7 +479,7 @@ describe("ImmutableTypedCrossfilter", () => { .select("nonFinite", { mode: "range", lo: Number.NEGATIVE_INFINITY, - hi: Number.POSITIVE_INFINITY + hi: Number.POSITIVE_INFINITY, }) .countSelected() ).toEqual(7); diff --git a/client/__tests__/util/typedCrossfilter/positiveInterval.test.js b/client/__tests__/util/typedCrossfilter/positiveInterval.test.js index 399ad9ec..1e9355fa 100644 --- a/client/__tests__/util/typedCrossfilter/positiveInterval.test.js +++ b/client/__tests__/util/typedCrossfilter/positiveInterval.test.js @@ -10,15 +10,30 @@ describe("canonicalize", () => { test("simple, already correct", () => { expect(PositiveIntervals.canonicalize([[0, 1]])).toEqual([[0, 1]]); - expect(PositiveIntervals.canonicalize([[0, 1], [2, 3]])).toEqual([ + expect( + PositiveIntervals.canonicalize([ + [0, 1], + [2, 3], + ]) + ).toEqual([ [0, 1], - [2, 3] + [2, 3], ]); }); test("non-canonical, need to be canonicalized", () => { - expect(PositiveIntervals.canonicalize([[0, 1], [1, 2]])).toEqual([[0, 2]]); - expect(PositiveIntervals.canonicalize([[1, 2], [2, 3]])).toEqual([[1, 3]]); + expect( + PositiveIntervals.canonicalize([ + [0, 1], + [1, 2], + ]) + ).toEqual([[0, 2]]); + expect( + PositiveIntervals.canonicalize([ + [1, 2], + [2, 3], + ]) + ).toEqual([[1, 3]]); }); }); @@ -26,14 +41,30 @@ describe("union", () => { test("empty range", () => { expect(PositiveIntervals.union([], [])).toEqual([]); expect(PositiveIntervals.union([], [[1, 2]])).toEqual([[1, 2]]); - expect(PositiveIntervals.union([], [[1, 2], [3, 4]])).toEqual([ + expect( + PositiveIntervals.union( + [], + [ + [1, 2], + [3, 4], + ] + ) + ).toEqual([ [1, 2], - [3, 4] + [3, 4], ]); expect(PositiveIntervals.union([[3, 4]], [])).toEqual([[3, 4]]); - expect(PositiveIntervals.union([[1, 2], [3, 4]], [])).toEqual([ + expect( + PositiveIntervals.union( + [ + [1, 2], + [3, 4], + ], + [] + ) + ).toEqual([ [1, 2], - [3, 4] + [3, 4], ]); expect(PositiveIntervals.union([[3, 3]], [])).toEqual([[3, 3]]); expect(PositiveIntervals.union([], [[3, 3]])).toEqual([[3, 3]]); @@ -44,17 +75,37 @@ describe("union", () => { expect(PositiveIntervals.union([[2, 3]], [[1, 2]])).toEqual([[1, 3]]); expect(PositiveIntervals.union([[1, 2]], [[3, 4]])).toEqual([ [1, 2], - [3, 4] + [3, 4], ]); expect( - PositiveIntervals.union([[1, 2], [3, 4]], [[6, 7], [19, 40]]) - ).toEqual([[1, 2], [3, 4], [6, 7], [19, 40]]); - expect(PositiveIntervals.union([[1, 4]], [[1, 1], [3, 4]])).toEqual([ - [1, 4] + PositiveIntervals.union( + [ + [1, 2], + [3, 4], + ], + [ + [6, 7], + [19, 40], + ] + ) + ).toEqual([ + [1, 2], + [3, 4], + [6, 7], + [19, 40], ]); + expect( + PositiveIntervals.union( + [[1, 4]], + [ + [1, 1], + [3, 4], + ] + ) + ).toEqual([[1, 4]]); expect(PositiveIntervals.union([[3, 3]], [[4, 4]])).toEqual([ [3, 3], - [4, 4] + [4, 4], ]); }); }); @@ -70,34 +121,43 @@ describe("intersection", () => { expect(PositiveIntervals.intersection([[1, 2]], [[2, 3]])).toEqual([]); expect(PositiveIntervals.intersection([[2, 3]], [[1, 2]])).toEqual([]); expect(PositiveIntervals.intersection([[1, 10]], [[1, 10]])).toEqual([ - [1, 10] + [1, 10], ]); expect(PositiveIntervals.intersection([[1, 10]], [[2, 8]])).toEqual([ - [2, 8] + [2, 8], ]); expect(PositiveIntervals.intersection([[2, 8]], [[1, 10]])).toEqual([ - [2, 8] + [2, 8], ]); expect(PositiveIntervals.intersection([[1, 10]], [[2, 12]])).toEqual([ - [2, 10] + [2, 10], ]); expect(PositiveIntervals.intersection([[2, 12]], [[1, 10]])).toEqual([ - [2, 10] + [2, 10], ]); expect(PositiveIntervals.intersection([[1, 10]], [[1, 8]])).toEqual([ - [1, 8] + [1, 8], ]); expect(PositiveIntervals.intersection([[1, 8]], [[1, 10]])).toEqual([ - [1, 8] + [1, 8], ]); - expect(PositiveIntervals.intersection([[1, 10]], [[1, 2], [6, 9]])).toEqual( - [[1, 2], [6, 9]] - ); - expect(PositiveIntervals.intersection([[0, 2638]], [[1363, 2638]])).toEqual( - [[1363, 2638]] - ); + expect( + PositiveIntervals.intersection( + [[1, 10]], + [ + [1, 2], + [6, 9], + ] + ) + ).toEqual([ + [1, 2], + [6, 9], + ]); + expect( + PositiveIntervals.intersection([[0, 2638]], [[1363, 2638]]) + ).toEqual([[1363, 2638]]); expect(PositiveIntervals.intersection([[1, 2]], [[1, 2]])).toEqual([ - [1, 2] + [1, 2], ]); }); }); @@ -110,32 +170,66 @@ describe("difference", () => { }); test("simple", () => { - expect(PositiveIntervals.difference([[1, 2], [3, 4]], [])).toEqual([ + expect( + PositiveIntervals.difference( + [ + [1, 2], + [3, 4], + ], + [] + ) + ).toEqual([ [1, 2], - [3, 4] - ]); - expect(PositiveIntervals.difference([[1, 2], [3, 10]], [[5, 10]])).toEqual([ - [1, 2], - [3, 5] - ]); - expect(PositiveIntervals.difference([[1, 2], [3, 10]], [[0, 5]])).toEqual([ - [5, 10] + [3, 4], ]); expect( - PositiveIntervals.difference([[0, 2638]], [[0, 1363], [2055, 2638]]) + PositiveIntervals.difference( + [ + [1, 2], + [3, 10], + ], + [[5, 10]] + ) + ).toEqual([ + [1, 2], + [3, 5], + ]); + expect( + PositiveIntervals.difference( + [ + [1, 2], + [3, 10], + ], + [[0, 5]] + ) + ).toEqual([[5, 10]]); + expect( + PositiveIntervals.difference( + [[0, 2638]], + [ + [0, 1363], + [2055, 2638], + ] + ) ).toEqual([[1363, 2055]]); expect( - PositiveIntervals.difference([[0, 1363], [2055, 2638]], [[0, 2638]]) + PositiveIntervals.difference( + [ + [0, 1363], + [2055, 2638], + ], + [[0, 2638]] + ) ).toEqual([]); expect(PositiveIntervals.difference([[0, 10]], [[0, 1]])).toEqual([ - [1, 10] + [1, 10], ]); expect(PositiveIntervals.difference([[0, 10]], [[1, 2]])).toEqual([ [0, 1], - [2, 10] + [2, 10], ]); expect(PositiveIntervals.difference([[0, 10]], [[9, 10]])).toEqual([ - [0, 9] + [0, 9], ]); }); }); diff --git a/client/__tests__/util/typedCrossfilter/sort.test.js b/client/__tests__/util/typedCrossfilter/sort.test.js index 2114c1b5..ea155c29 100644 --- a/client/__tests__/util/typedCrossfilter/sort.test.js +++ b/client/__tests__/util/typedCrossfilter/sort.test.js @@ -4,7 +4,7 @@ import { lowerBound, upperBound, lowerBoundIndirect, - upperBoundIndirect + upperBoundIndirect, } from "../../../src/util/typedCrossfilter/sort"; /* @@ -40,7 +40,7 @@ describe("sortArray", () => { ["a", "b", "0", "1"], [0, "a", true, null, undefined, 3.1415], fillRand(new Array(1000)), - ["a", NaN, null, pInf] + ["a", NaN, null, pInf], ].map((val, idx) => test(`JS vals ${idx}`, () => { expect(sortArray(val)).toMatchObject(val.sort()); @@ -49,7 +49,7 @@ describe("sortArray", () => { }); describe("finite numbers", () => { - [Array, Float32Array, Uint32Array, Int32Array, Float64Array].map(Type => + [Array, Float32Array, Uint32Array, Int32Array, Float64Array].map((Type) => test(Type.name, () => { expect(sortArray(Type.from([6, 5, 4, 3, 2, 1, 0]))).toMatchObject( Type.from([0, 1, 2, 3, 4, 5, 6]) @@ -131,7 +131,7 @@ describe("sortArray", () => { describe("sortIndex", () => { describe("finite numbers", () => { - [Array, Float32Array, Uint32Array, Int32Array, Float64Array].map(Type => + [Array, Float32Array, Uint32Array, Int32Array, Float64Array].map((Type) => test(Type.name, () => { const source1 = Type.from([6, 5, 4, 3, 2, 1, 0]); const index1 = fillRange(new Uint32Array(source1.length)); diff --git a/client/__tests__/util/typedCrossfilter/util.test.js b/client/__tests__/util/typedCrossfilter/util.test.js index 33b026d9..30f2212f 100644 --- a/client/__tests__/util/typedCrossfilter/util.test.js +++ b/client/__tests__/util/typedCrossfilter/util.test.js @@ -1,6 +1,6 @@ import { sliceByIndex, - makeSortIndex + makeSortIndex, } from "../../../src/util/typedCrossfilter/util"; import { rangeFill as fillRange } from "../../../src/util/range"; diff --git a/client/configuration/babel/babel.dev.js b/client/configuration/babel/babel.dev.js index 8309071b..8f32d635 100644 --- a/client/configuration/babel/babel.dev.js +++ b/client/configuration/babel/babel.dev.js @@ -3,7 +3,7 @@ module.exports = { cacheDirectory: true, presets: [ ["modern-browsers", { loose: true, modules: false }], - "@babel/preset-react" + "@babel/preset-react", ], plugins: [ "@babel/plugin-proposal-function-bind", @@ -11,6 +11,6 @@ module.exports = { ["@babel/plugin-proposal-class-properties", { loose: true }], "@babel/plugin-proposal-export-namespace-from", "@babel/plugin-proposal-optional-chaining", - "@babel/plugin-proposal-nullish-coalescing-operator" - ] + "@babel/plugin-proposal-nullish-coalescing-operator", + ], }; diff --git a/client/configuration/babel/babel.prod.js b/client/configuration/babel/babel.prod.js index 45e09551..604e2ef2 100644 --- a/client/configuration/babel/babel.prod.js +++ b/client/configuration/babel/babel.prod.js @@ -2,7 +2,7 @@ module.exports = { babelrc: false, presets: [ ["modern-browsers", { loose: true, modules: false }], - "@babel/preset-react" + "@babel/preset-react", ], plugins: [ "@babel/plugin-proposal-function-bind", @@ -12,6 +12,6 @@ module.exports = { "@babel/plugin-transform-react-constant-elements", "@babel/plugin-transform-runtime", "@babel/plugin-proposal-optional-chaining", - "@babel/plugin-proposal-nullish-coalescing-operator" - ] + "@babel/plugin-proposal-nullish-coalescing-operator", + ], }; diff --git a/client/configuration/eslint/eslint.js b/client/configuration/eslint/eslint.js index 9d2456fe..dd3c4a20 100644 --- a/client/configuration/eslint/eslint.js +++ b/client/configuration/eslint/eslint.js @@ -9,8 +9,8 @@ module.exports = { sourceType: "module", ecmaFeatures: { jsx: true, - generators: true - } + generators: true, + }, }, rules: { "no-magic-numbers": "off", @@ -26,7 +26,7 @@ module.exports = { "operator-linebreak": [ "error", "after", - { overrides: { "?": "before", ":": "before" } } + { overrides: { "?": "before", ":": "before" } }, ], "no-console": "off", "spaced-comment": ["error", "always", { exceptions: ["*"] }], @@ -35,13 +35,13 @@ module.exports = { "react/prop-types": [0], "space-before-function-paren": "off", "function-paren-newline": "off", - "prefer-destructuring": ["error", { object: true, array: false }] + "prefer-destructuring": ["error", { object: true, array: false }], }, overrides: [ { files: ["**/*.test.js"], env: { - jest: true // now **/*.test.js files' env has both es6 *and* jest + jest: true, // now **/*.test.js files' env has both es6 *and* jest }, // Can't extend in overrides: https://github.com/eslint/eslint/issues/8813 // "extends": ["plugin:jest/recommended"] @@ -51,8 +51,8 @@ module.exports = { "jest/no-focused-tests": "error", "jest/no-identical-title": "error", "jest/prefer-to-have-length": "warn", - "jest/valid-expect": "error" - } - } - ] + "jest/valid-expect": "error", + }, + }, + ], }; diff --git a/client/configuration/webpack/webpack.config.dev.js b/client/configuration/webpack/webpack.config.dev.js index 7191c812..5175a08b 100644 --- a/client/configuration/webpack/webpack.config.dev.js +++ b/client/configuration/webpack/webpack.config.dev.js @@ -18,7 +18,7 @@ module.exports = { path: path.resolve("build"), pathinfo: true, filename: "static/js/bundle.js", - publicPath: "/" + publicPath: "/", }, module: { rules: [ @@ -26,7 +26,7 @@ module.exports = { test: /\.js$/, include: src, loader: "babel-loader", - options: babelOptions + options: babelOptions, }, { test: /\.css$/, @@ -34,29 +34,29 @@ module.exports = { exclude: [path.resolve(src, "index.css")], loader: [ { - loader: "style-loader" + loader: "style-loader", }, { loader: "css-loader", options: { modules: { - localIdentName: "[name]__[local]___[hash:base64:5]" - } - } - } - ] + localIdentName: "[name]__[local]___[hash:base64:5]", + }, + }, + }, + ], }, { test: /index\.css$/, include: [path.resolve(src, "index.css")], loader: [ { - loader: "style-loader" + loader: "style-loader", }, { - loader: "css-loader" - } - ] + loader: "css-loader", + }, + ], }, { test: /\.json$/, include: [src, nodeModules], loader: "json-loader" }, @@ -64,14 +64,14 @@ module.exports = { test: /\.(jpg|png|gif|eot|svg|ttf|woff|woff2|otf)$/i, loader: "file-loader", include: [nodeModules, fonts], - query: { name: "static/assets/[name].[ext]" } - } - ] + query: { name: "static/assets/[name].[ext]" }, + }, + ], }, plugins: [ new HtmlWebpackPlugin({ inject: true, - template: path.resolve("index.html") + template: path.resolve("index.html"), }), new FaviconsWebpackPlugin({ logo: "./favicon.png", @@ -84,16 +84,18 @@ module.exports = { coast: false, firefox: false, windows: false, - yandex: false - } - } + yandex: false, + }, + }, }), new webpack.NoEmitOnErrorsPlugin(), new webpack.DefinePlugin({ - __REACT_DEVTOOLS_GLOBAL_HOOK__: "({ isDisabled: true })" + __REACT_DEVTOOLS_GLOBAL_HOOK__: "({ isDisabled: true })", }), new webpack.DefinePlugin({ - "process.env.CXG_SERVER_PORT": JSON.stringify(process.env.CXG_SERVER_PORT) - }) - ] + "process.env.CXG_SERVER_PORT": JSON.stringify( + process.env.CXG_SERVER_PORT + ), + }), + ], }; diff --git a/client/index.html b/client/index.html index 134e38c9..be954386 100644 --- a/client/index.html +++ b/client/index.html @@ -1,12 +1,27 @@
- - + +