mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-16 21:37:59 +08:00
* refactor categorical controls state * lint * fix race condition in tests * fix typo * add missing update on subset * remove obsolete code * update jest and puppeteer major version; update all minors * update when label changes * remove lint from tests; increase timeouts in e2e tests * initial refactoring to new async annomatrix * refine error handling * fix bad merge * add continuous legend * lint * fix memoization in color table creators * partial implementation of user defined annotations * add new annotations action creator file * first pass at user annotations * additional user annotation bug fixes * user annotation auto-save * unit test cleanup * lint * refactor into multiple files * cleanup * add column GC * fix several bugs in user annotations * remove debug code * no anonymous functions * undo redo cleanup * file cleanup * scatterplot * performance * cleanup * remove old code * render in parallel with load * fix race condition * simply graph rendering * render throttle DRY * fix category label order * fix typo in e2e test setup * re-fix the e2e test setup * be more tolerant of races * anno matrix unit tests * temp disable reembedding * pilot port continuous histo to react-async * name change * lint * fix repaint bug * typo fix * update snap to match new ids * world/universe name cleanup * move annoMatrix to src dir * use private underscore naming convention * fix corner case in all selected * name cleanup * add layout control * init edge case * lint * port scatterplot * fix label indexing bug and improve tests * port category to react-async * fix user annotation labelling while subset * select all of prev layout on layout switch * fix race with crossfilter update * prettier lint * fix misleading comment * fix url composition in loader * first pass at crossfilter tests * lint * lint * fix typo * improved error handling for network errors * fix memoization bug * add memo * refactor for performnce * add missing single-value handling in select exact parser * small bugs discovered by tests * lint * additional crossfilter unit tests * remove extraneous comment * add support for automatic category determination * lint * fix render bug in category * take advantage of schema categories guarantee * lint * do not clear history when resetting * enhanced annomatrix gc * lint * finish renaming to follow conventions; fix clone race bug * lint * add priority based loading to improve initial data load UX * crossfilter cache perf * perf tuning * remove timers * documentation * PR review changes * PR review changes * more PR review edits * improve clarity of comment * more PR review fixes * port centroidLabels to use react-async * remove dead code * pr review updates * oops, remove logging
334 lines
9.5 KiB
JavaScript
334 lines
9.5 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,
|
|
} from "./puppeteerUtils";
|
|
|
|
import {
|
|
assertCategoryDoesNotExist,
|
|
calcDragCoordinates,
|
|
createCategory,
|
|
createLabel,
|
|
deleteCategory,
|
|
deleteLabel,
|
|
drag,
|
|
expandCategory,
|
|
renameCategory,
|
|
renameLabel,
|
|
subset,
|
|
duplicateCategory,
|
|
} from "./cellxgeneActions";
|
|
|
|
const data = datasets[DATASET];
|
|
|
|
const perTestCategoryName = "TEST-CATEGORY";
|
|
const perTestLabelName = "TEST-LABEL";
|
|
|
|
async function setup(config) {
|
|
await goToPage(appUrlBase);
|
|
|
|
// 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" },
|
|
])("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) => {
|
|
return page.evaluate((element) => {
|
|
return element.outerHTML;
|
|
}, label);
|
|
})
|
|
);
|
|
|
|
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();
|
|
}
|
|
});
|