Files
cellxgene/client/__tests__/e2e/e2eAnnotations.test.js
Bruce Martin eaae6df5e3 TS Revert (1) (#2402)
* revert all commits to before Typescript migration

* update compat workflow to match latest deps (#2335)

* update compat workflow to match latest deps

* attempt to debug

* attempt to debug

* remove debugging code

* typo

* update deps to match desktop (#2340)

* fix: don't run lint with `--fix` on push tests (#2273)

* fix: don't run lint with `--fix` on push tests

* npx

Co-authored-by: maniarathi <mani.arathi@gmail.com>
Co-authored-by: Madison Dunitz <madison.dunitz@chanzuckerberg.com>

* rename X_approx_distribution to X_approximate_distribution (#2337)

* Correctly handle non-finite numbers in heuristic determination of X distribution (#2342)

* handle non-finites explicitly

* improve and test edge case handling for distribution estimation

* revert debugging changes

* code readability

* clean up type inferencing (#2332)

* unit tests for 64 bit conversion

* clean up type handling

* type inference tests

* more type inference fixes

* use schema to determine user intent for data typing

* stop using deprecated API

* fbs type encoding test

* add missing test

* add more tests

* correctly infer X type for CXG adaptor

* lint

* fix typo

* ts migration

* cleanup from PR review

* lint

* PR review changes

* remove unused packages from client (#2359)

* remove unused packages from client

* add missing peer dep

* fix: disable FE auth testing on compatibility tests (#2377)

* update: release process (#2277)

Co-authored-by: maniarathi <mani.arathi@gmail.com>

* fix: remove spaces in param setup (#2380)

* delete deploy workflow (#2396)

* undo reformatting which now does not pass lint

* fix snapshots which changed due to npm dep changes

* add missing quoting to snapshot

* another snapshot typo fix

* TS Revert (2) - replay PR #2347 and #2354 (#2403)

* replay edits from PR 2347

* TS Revert (3) - replay edits in PR #2327 (#2404)

* replay edits in PR 2327

* TS Revert (4) - replay PR #2355 (#2405)

* replay edits in PR 2355

* add additional babel config

* reformat with new prettier config

Co-authored-by: Severiano Badajoz <sbadajoz@chanzuckerberg.com>
Co-authored-by: maniarathi <mani.arathi@gmail.com>
Co-authored-by: Madison Dunitz <madison.dunitz@chanzuckerberg.com>
2021-08-23 15:01:36 -07:00

596 lines
18 KiB
JavaScript

/*
Tests included in this file are specific to annotation features
*/
import { appUrlBase, DATASET } from "./config";
import { datasets } from "./data";
import {
clickOn,
goToPage,
waitByClass,
waitByID,
getTestId,
getTestClass,
getAllByClass,
clickOnUntil,
getOneElementInnerHTML,
} from "./puppeteerUtils";
import {
assertCategoryDoesNotExist,
calcDragCoordinates,
createCategory,
createLabel,
deleteCategory,
deleteLabel,
drag,
expandCategory,
renameCategory,
renameLabel,
subset,
duplicateCategory,
createGeneset,
deleteGeneset,
assertGenesetExists,
assertGenesetDoesNotExist,
getCellSetCount,
expandGeneset,
editGenesetName,
addGeneToSet,
assertGeneExistsInGeneset,
removeGene,
assertGeneDoesNotExist,
expandGene,
colorByGeneset,
assertColorLegendLabel,
colorByGene,
} from "./cellxgeneActions";
const data = datasets[DATASET];
const perTestCategoryName = "TEST-CATEGORY";
const perTestLabelName = "TEST-LABEL";
// geneset CRUD
const genesetToDeleteName = "geneset_to_delete";
const preExistingGenesetName = "fifth_dataset";
const meanExpressionBrushGenesetName = "second_gene_set";
const meanExpressionBrushCellsSelected = "557";
const subsetMeanExpressionBrushCellsSelected = "452";
// initial text, the text we type in, the result
const editableGenesetName = "geneset_to_edit";
const editText = "_111";
const newGenesetName = "geneset_to_edit_111";
// add gene to set
const geneToAddToSet = "RER1";
const setToAddGeneTo = "fill_this_geneset";
// remove gene from set
const geneToRemove = "SIK1";
const setToRemoveFrom = "empty_this_geneset";
// brush a gene
const geneToBrushAndColorBy = "SIK1";
const brushThisGeneGeneset = "brush_this_gene";
const geneBrushedCellCount = "109";
const subsetGeneBrushedCellCount = "96";
const genesetDescriptionID =
"geneset-description-tooltip-fourth_gene_set: fourth description";
const genesetDescriptionString = "fourth_gene_set: fourth description";
const genesetToCheckForDescription = "fourth_gene_set";
async function setup(config) {
await goToPage(appUrlBase);
if (config.categoricalAnno) {
// setup the test fixtures
await createCategory(perTestCategoryName);
await createLabel(perTestCategoryName, perTestLabelName);
}
if (config.withSubset) {
await subset({ x1: 0.1, y1: 0.1, x2: 0.8, y2: 0.8 });
}
await waitByClass("autosave-complete");
}
describe.each([
{ withSubset: true, tag: "subset" },
{ withSubset: false, tag: "whole" },
])("geneSET crud operations and interactions", (config) => {
test("genesets load from csv", async () => {
await setup(config);
await assertGenesetExists(preExistingGenesetName);
});
test("brush on geneset mean", async () => {
await setup(config);
await expandGeneset(meanExpressionBrushGenesetName);
const histBrushableAreaId = `histogram-${meanExpressionBrushGenesetName}-plot-brushable-area`;
const coords = await calcDragCoordinates(histBrushableAreaId, {
x1: 0.25,
y1: 0.5,
x2: 0.55,
y2: 0.5,
});
await drag(histBrushableAreaId, coords.start, coords.end);
const cellCount = await getCellSetCount(1);
if (config.withSubset) {
expect(cellCount).toBe(subsetMeanExpressionBrushCellsSelected);
} else {
expect(cellCount).toBe(meanExpressionBrushCellsSelected);
}
});
test("color by mean expression", async () => {
await setup(config);
await colorByGeneset(meanExpressionBrushGenesetName);
await assertColorLegendLabel(meanExpressionBrushGenesetName);
});
test("diffexp", async () => {
if (config.withSubset) return;
await setup(config);
// set the two cell sets to b cells vs nk cells
await expandCategory(`louvain`);
await clickOn(`louvain:category-select`);
await clickOn(`categorical-value-select-louvain-B cells`);
await clickOn(`cellset-button-1`);
await clickOn(`categorical-value-select-louvain-B cells`);
await clickOn(`categorical-value-select-louvain-NK cells`);
await clickOn(`cellset-button-2`);
// run diffexp
await clickOn(`diffexp-button`);
await waitByClass("pop-1-geneset-expand");
await expect(page).toClick(getTestClass("pop-1-geneset-expand"));
await page.waitForFunction(
(selector) => !document.querySelector(selector),
{},
getTestClass("gene-loading-spinner")
);
let genesHTML = await getOneElementInnerHTML(
getTestClass("gene-set-genes")
);
expect(genesHTML).toMatchSnapshot();
await expect(page).toClick(getTestClass("pop-1-geneset-expand"));
await expect(page).toClick(getTestClass("pop-2-geneset-expand"));
await page.waitForFunction(
(selector) => !document.querySelector(selector),
{},
getTestClass("gene-loading-spinner")
);
genesHTML = await getOneElementInnerHTML(getTestClass("gene-set-genes"));
expect(genesHTML).toMatchSnapshot();
});
test("create a new geneset and undo/redo", async () => {
if (config.withSubset) return;
await setup(config);
const genesetName = `test-geneset-foo-123`;
await assertGenesetDoesNotExist(genesetName);
await createGeneset(genesetName);
/* note: as of June 2021, the aria label is in the truncate component which clones the element */
await assertGenesetExists(genesetName);
await clickOn("undo");
await assertGenesetDoesNotExist(genesetName);
await clickOn("redo");
await assertGenesetExists(genesetName);
});
test("edit geneset name and undo/redo", async () => {
await setup(config);
await editGenesetName(editableGenesetName, editText);
await assertGenesetExists(newGenesetName);
await clickOn("undo");
await assertGenesetExists(editableGenesetName);
await clickOn("redo");
await assertGenesetExists(newGenesetName);
});
test("delete a geneset and undo/redo", async () => {
if (config.withSubset) return;
await setup(config);
await deleteGeneset(genesetToDeleteName);
await clickOn("undo");
await assertGenesetExists(genesetToDeleteName);
await clickOn("redo");
await assertGenesetDoesNotExist(genesetToDeleteName);
});
test("geneset description", async () => {
if (config.withSubset) return;
await setup(config);
await clickOnUntil(
`${genesetToCheckForDescription}:geneset-expand`,
async () => {
expect(page).toMatchElement(getTestId(genesetDescriptionID), {
text: genesetDescriptionString,
});
}
);
});
});
describe.each([
{ withSubset: true, tag: "subset" },
{ withSubset: false, tag: "whole" },
])("GENE crud operations and interactions", (config) => {
test("add a gene to geneset and undo/redo", async () => {
await setup(config);
await addGeneToSet(setToAddGeneTo, geneToAddToSet);
await expandGeneset(setToAddGeneTo);
await assertGeneExistsInGeneset(geneToAddToSet);
await clickOn("undo");
await assertGeneDoesNotExist(geneToAddToSet);
await clickOn("redo");
await assertGeneExistsInGeneset(geneToAddToSet);
});
test("expand gene and brush", async () => {
await setup(config);
await expandGeneset(brushThisGeneGeneset);
await expandGene(geneToBrushAndColorBy);
const histBrushableAreaId = `histogram-${geneToBrushAndColorBy}-plot-brushable-area`;
const coords = await calcDragCoordinates(histBrushableAreaId, {
x1: 0.25,
y1: 0.5,
x2: 0.55,
y2: 0.5,
});
await drag(histBrushableAreaId, coords.start, coords.end);
const cellCount = await getCellSetCount(1);
if (config.withSubset) {
expect(cellCount).toBe(subsetGeneBrushedCellCount);
} else {
expect(cellCount).toBe(geneBrushedCellCount);
}
});
test("color by gene in geneset", async () => {
await setup(config);
await expandGeneset(meanExpressionBrushGenesetName);
await colorByGene(geneToBrushAndColorBy);
await assertColorLegendLabel(geneToBrushAndColorBy);
});
test("delete gene from geneset and undo/redo", async () => {
// We've already deleted the gene
if (config.withSubset) return;
await setup(config);
await expandGeneset(setToRemoveFrom);
await removeGene(geneToRemove);
await assertGeneDoesNotExist(geneToRemove);
await clickOn("undo");
await assertGeneExistsInGeneset(geneToRemove);
await clickOn("redo");
await assertGeneDoesNotExist(geneToRemove);
});
});
describe.each([
{ withSubset: true, tag: "subset", categoricalAnno: true },
{ withSubset: false, tag: "whole", categoricalAnno: true },
])("annotations", (config) => {
test("create a category", async () => {
await setup(config);
const categoryName = `category-created-${config.tag}`;
await assertCategoryDoesNotExist(categoryName);
await createCategory(categoryName);
await assertCategoryExists(categoryName);
});
test("delete a category", async () => {
await setup(config);
await deleteCategory(perTestCategoryName);
await assertCategoryDoesNotExist(perTestCategoryName);
});
test("rename a category", async () => {
await setup(config);
const newCategoryName = `NEW-${config.tag}`;
await renameCategory(perTestCategoryName, newCategoryName);
await assertCategoryDoesNotExist(perTestCategoryName);
await assertCategoryExists(newCategoryName);
});
test("create a label", async () => {
await setup(config);
const labelName = `new-label-${config.tag}`;
await assertLabelDoesNotExist(perTestCategoryName, labelName);
await createLabel(perTestCategoryName, labelName);
await assertLabelExists(perTestCategoryName, labelName);
});
test("delete a label", async () => {
await setup(config);
await deleteLabel(perTestCategoryName, perTestLabelName);
await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName);
});
test("rename a label", async () => {
await setup(config);
const newLabelName = "my-cool-new-label";
await assertLabelDoesNotExist(perTestCategoryName, newLabelName);
await renameLabel(perTestCategoryName, perTestLabelName, newLabelName);
await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName);
await assertLabelExists(perTestCategoryName, newLabelName);
});
test("check cell count for a label loaded from file", async () => {
await setup(config);
const duplicateCategoryName = "duplicate";
await duplicateCategory(duplicateCategoryName);
await page.reload({ waitUntil: ["networkidle0", "domcontentloaded"] });
const firstCategoryExpandIcon = await expect(page).toMatchElement(
getTestClass("category-expand")
);
await firstCategoryExpandIcon.click();
const expectedCategoryRow = await expect(page).toMatchElement(
getTestClass("categorical-row")
);
const expectedLabelName = await getInnerText(
expectedCategoryRow,
"categorical-value"
);
const expectedLabelCount = await getInnerText(
expectedCategoryRow,
"categorical-value-count"
);
await expandCategory(duplicateCategoryName);
const expectedCategory = await expect(page).toMatchElement(
getTestClass("category")
);
const actualCategoryRow = await expect(expectedCategory).toMatchElement(
getTestClass("categorical-row")
);
const actualLabelName = await getInnerText(
actualCategoryRow,
"categorical-value"
);
const actualLabelCount = await getInnerText(
actualCategoryRow,
"categorical-value-count"
);
expect(actualLabelName).toBe(expectedLabelName);
expect(actualLabelCount).toBe(expectedLabelCount);
async function getInnerText(element, className) {
return element.$eval(getTestClass(className), (node) => node?.innerText);
}
});
test("assign cells to a label", async () => {
await setup(config);
await expandCategory(perTestCategoryName);
const lassoSelection = await calcDragCoordinates(
"layout-graph",
data.categoryLabel.lasso["coordinates-as-percent"]
);
await drag("layout-graph", lassoSelection.start, lassoSelection.end, true);
await waitByID("lasso-element", { visible: true });
await clickOn(`${perTestCategoryName}:${perTestLabelName}:see-actions`);
await clickOn(
`${perTestCategoryName}:${perTestLabelName}:add-current-selection-to-this-label`
);
const result = await waitByID(
`categorical-value-count-${perTestCategoryName}-${perTestLabelName}`
);
expect(await result.evaluate((node) => node.innerText)).toBe(
data.categoryLabel.newCount.bySubsetConfig[config.withSubset]
);
});
test("undo/redo category creation", async () => {
await setup(config);
const categoryName = `category-created-undo-${config.tag}`;
await assertCategoryDoesNotExist(categoryName);
await createCategory(categoryName);
await assertCategoryExists(categoryName);
await clickOn("undo");
await assertCategoryDoesNotExist(categoryName);
await clickOn("redo");
await assertCategoryExists(categoryName);
});
test("undo/redo category deletion", async () => {
await setup(config);
const categoryName = `category-deleted-undo-${config.tag}`;
await createCategory(categoryName);
await assertCategoryExists(categoryName);
await deleteCategory(categoryName);
await assertCategoryDoesNotExist(categoryName);
await clickOn("undo");
await assertCategoryExists(categoryName);
await clickOn("redo");
await assertCategoryDoesNotExist(categoryName);
});
test("undo/redo category rename", async () => {
await setup(config);
const newCategoryName = `category-renamed-undo-${config.tag}`;
await assertCategoryDoesNotExist(newCategoryName);
await renameCategory(perTestCategoryName, newCategoryName);
await assertCategoryExists(newCategoryName);
await assertCategoryDoesNotExist(perTestCategoryName);
await clickOn("undo");
await assertCategoryExists(perTestCategoryName);
await assertCategoryDoesNotExist(newCategoryName);
await clickOn("redo");
await assertCategoryExists(newCategoryName);
await assertCategoryDoesNotExist(perTestCategoryName);
});
test("undo/redo label creation", async () => {
await setup(config);
const labelName = `label-created-undo-${config.tag}`;
await assertLabelDoesNotExist(perTestCategoryName, labelName);
await createLabel(perTestCategoryName, labelName);
await assertLabelExists(perTestCategoryName, labelName);
await clickOn("undo");
await assertLabelDoesNotExist(perTestCategoryName);
await clickOn("redo");
await assertLabelExists(perTestCategoryName, labelName);
});
test("undo/redo label deletion", async () => {
await setup(config);
await deleteLabel(perTestCategoryName, perTestLabelName);
await assertLabelDoesNotExist(perTestCategoryName);
await clickOn("undo");
await assertLabelExists(perTestCategoryName, perTestLabelName);
await clickOn("redo");
await assertLabelDoesNotExist(perTestCategoryName);
});
test("undo/redo label rename", async () => {
await setup(config);
const newLabelName = `label-renamed-undo-${config.tag}`;
await assertLabelDoesNotExist(perTestCategoryName, newLabelName);
await renameLabel(perTestCategoryName, perTestLabelName, newLabelName);
await assertLabelExists(perTestCategoryName, newLabelName);
await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName);
await clickOn("undo");
await assertLabelExists(perTestCategoryName, perTestLabelName);
await assertLabelDoesNotExist(perTestCategoryName, newLabelName);
await clickOn("redo");
await assertLabelExists(perTestCategoryName, newLabelName);
await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName);
});
test("stacked bar graph renders", async () => {
await setup(config);
await expandCategory(perTestCategoryName);
await clickOn(`colorby-louvain`);
const labels = await getAllByClass("categorical-row");
const result = await Promise.all(
labels.map((label) => page.evaluate((element) => element.outerHTML, label))
);
expect(result).toMatchSnapshot();
});
test("truncate midpoint whitespace", async () => {
await setup(config);
const newLabelName = "123 456";
await renameLabel(perTestCategoryName, perTestLabelName, newLabelName);
const value = await waitByID(
`categorical-value-${perTestCategoryName}-${newLabelName}`
);
const result = await page.evaluate((elem) => elem.outerHTML, value);
expect(result).toMatchSnapshot();
});
test("truncate single character", async () => {
await setup(config);
const newLabelName = "T";
await renameLabel(perTestCategoryName, perTestLabelName, newLabelName);
const value = await waitByID(
`categorical-value-${perTestCategoryName}-${newLabelName}`
);
const result = await page.evaluate((elem) => elem.outerHTML, value);
expect(result).toMatchSnapshot();
});
async function assertCategoryExists(categoryName) {
const handle = await waitByID(`${categoryName}:category-label`);
const result = await handle.evaluate((node) =>
node.getAttribute("aria-label")
);
return expect(result).toBe(categoryName);
}
async function assertLabelExists(categoryName, labelName) {
await expect(page).toMatchElement(
getTestId(`${categoryName}:category-expand`)
);
await expandCategory(categoryName);
const previous = await waitByID(
`categorical-value-${categoryName}-${labelName}`
);
expect(
await previous.evaluate((node) => node.getAttribute("aria-label"))
).toBe(labelName);
}
async function assertLabelDoesNotExist(categoryName, labelName) {
await expandCategory(categoryName);
const result = await page.$(
`[data-testid='categorical-value-${categoryName}-${labelName}']`
);
expect(result).toBeNull();
}
});