diff --git a/backend/test/fixtures/pbmc3k-genesets.csv b/backend/test/fixtures/pbmc3k-genesets.csv
index da62da69..83340d4c 100644
--- a/backend/test/fixtures/pbmc3k-genesets.csv
+++ b/backend/test/fixtures/pbmc3k-genesets.csv
@@ -5,8 +5,8 @@ first gene set name,a description,NO_SUCH_GENE, non-existent gene
first gene set name,a description,F5, duplicate gene
first gene set name, a description,SUMO3,
first gene set name,,SRM,
-second gene set,,RER1
-second gene set,,SIK1
+second_gene_set,,RER1
+second_gene_set,,SIK1
third gene set,,NO_SUCH_GENE
fourth_gene_set,fourth description,,gene intentionally missing
fifth_dataset,,,
@@ -14,3 +14,8 @@ summary test,,ACD,
summary test,,AATF,
summary test,,F5,
summary test,,PIGU,
+geneset_to_delete,,,
+geneset_to_edit,,,
+fill_this_geneset,,RER1,
+empty_this_geneset,,SIK1,
+brush_this_gene,,SIK1,
\ No newline at end of file
diff --git a/backend/test/test_czi_hosted/unit/common/test_api.py b/backend/test/test_czi_hosted/unit/common/test_api.py
index 37da2f57..3bd021bf 100644
--- a/backend/test/test_czi_hosted/unit/common/test_api.py
+++ b/backend/test/test_czi_hosted/unit/common/test_api.py
@@ -453,7 +453,7 @@ class EndPointsCxg(EndPoints):
{"gene_description": "", "gene_symbol": "SIK1"},
],
"geneset_description": "",
- "geneset_name": "second gene set",
+ "geneset_name": "second_gene_set",
},
{"genes": [], "geneset_description": "", "geneset_name": "third gene set"},
{"genes": [], "geneset_description": "fourth description", "geneset_name": "fourth_gene_set"},
@@ -468,6 +468,23 @@ class EndPointsCxg(EndPoints):
"geneset_description": "",
"geneset_name": "summary test",
},
+ {'genes': [], 'geneset_description': '', 'geneset_name': 'geneset_to_delete'},
+ {'genes': [], 'geneset_description': '', 'geneset_name': 'geneset_to_edit'},
+ {
+ 'genes': [{'gene_description': '', 'gene_symbol': 'RER1'}],
+ 'geneset_description': '',
+ 'geneset_name': 'fill_this_geneset'
+ },
+ {
+ 'genes': [{'gene_description': '', 'gene_symbol': 'SIK1'}],
+ 'geneset_description': '',
+ 'geneset_name': 'empty_this_geneset'
+ },
+ {
+ 'genes': [{'gene_description': '', 'gene_symbol': 'SIK1'}],
+ 'geneset_description': '',
+ 'geneset_name': 'brush_this_gene'
+ }
],
"tid": 0,
},
@@ -484,8 +501,8 @@ class EndPointsCxg(EndPoints):
first gene set name,a description,F5, a gene_description\r
first gene set name,a description,SUMO3,\r
first gene set name,a description,SRM,\r
-second gene set,,RER1,\r
-second gene set,,SIK1,\r
+second_gene_set,,RER1,\r
+second_gene_set,,SIK1,\r
third gene set,,,\r
fourth_gene_set,fourth description,,\r
fifth_dataset,,,\r
@@ -493,6 +510,11 @@ summary test,,ACD,\r
summary test,,AATF,\r
summary test,,F5,\r
summary test,,PIGU,\r
+geneset_to_delete,,,\r
+geneset_to_edit,,,\r
+fill_this_geneset,,RER1,\r
+empty_this_geneset,,SIK1,\r
+brush_this_gene,,SIK1,\r
"""
self.assertEqual(result.data.decode("utf-8"), expected_data)
diff --git a/backend/test/test_server/unit/common/test_api.py b/backend/test/test_server/unit/common/test_api.py
index 9c8984e4..8633d46b 100644
--- a/backend/test/test_server/unit/common/test_api.py
+++ b/backend/test/test_server/unit/common/test_api.py
@@ -575,7 +575,7 @@ class EndPointsAnnDataGenesets(unittest.TestCase, EndPoints):
{"gene_description": "", "gene_symbol": "SIK1"},
],
"geneset_description": "",
- "geneset_name": "second gene set",
+ "geneset_name": "second_gene_set",
},
{"genes": [], "geneset_description": "", "geneset_name": "third gene set"},
{"genes": [], "geneset_description": "fourth description", "geneset_name": "fourth_gene_set"},
@@ -590,6 +590,23 @@ class EndPointsAnnDataGenesets(unittest.TestCase, EndPoints):
"geneset_description": "",
"geneset_name": "summary test",
},
+ {'genes': [], 'geneset_description': '', 'geneset_name': 'geneset_to_delete'},
+ {'genes': [], 'geneset_description': '', 'geneset_name': 'geneset_to_edit'},
+ {
+ 'genes': [{'gene_description': '', 'gene_symbol': 'RER1'}],
+ 'geneset_description': '',
+ 'geneset_name': 'fill_this_geneset'
+ },
+ {
+ 'genes': [{'gene_description': '', 'gene_symbol': 'SIK1'}],
+ 'geneset_description': '',
+ 'geneset_name': 'empty_this_geneset'
+ },
+ {
+ 'genes': [{'gene_description': '', 'gene_symbol': 'SIK1'}],
+ 'geneset_description': '',
+ 'geneset_name': 'brush_this_gene'
+ }
],
"tid": 0,
},
@@ -607,8 +624,8 @@ class EndPointsAnnDataGenesets(unittest.TestCase, EndPoints):
first gene set name,a description,F5, a gene_description\r
first gene set name,a description,SUMO3,\r
first gene set name,a description,SRM,\r
-second gene set,,RER1,\r
-second gene set,,SIK1,\r
+second_gene_set,,RER1,\r
+second_gene_set,,SIK1,\r
third gene set,,,\r
fourth_gene_set,fourth description,,\r
fifth_dataset,,,\r
@@ -616,6 +633,11 @@ summary test,,ACD,\r
summary test,,AATF,\r
summary test,,F5,\r
summary test,,PIGU,\r
+geneset_to_delete,,,\r
+geneset_to_edit,,,\r
+fill_this_geneset,,RER1,\r
+empty_this_geneset,,SIK1,\r
+brush_this_gene,,SIK1,\r
""",
)
diff --git a/client/Makefile b/client/Makefile
index 33b02144..fbf7dab4 100644
--- a/client/Makefile
+++ b/client/Makefile
@@ -1,10 +1,13 @@
include ../common.mk
ANNOTATIONS := $(if $(ANNOTATIONS),$(ANNOTATIONS),../backend/test/fixtures/pbmc3k-annotations.csv)
+GENE_SETS := $(if $(GENE_SETS),$(GENE_SETS),../backend/test/fixtures/pbmc3k-genesets.csv)
ANNOTATIONS_FILENAME := $(shell basename $(ANNOTATIONS))
+GENE_SETS_FILENAME := $(shell basename $(GENE_SETS))
CXG_CONFIG := $(if $(CXG_CONFIG), $(CXG_CONFIG), ./__tests__/e2e/test_config.yaml)
+
# Packaging
.PHONY: clean
clean:
@@ -42,8 +45,9 @@ smoke-test:
smoke-test-annotations:
$(eval TMP_DIR := $(shell mktemp -d /tmp/cellxgene_XXXXXX))
cp $(ANNOTATIONS) $(TMP_DIR)/ && \
+ cp $(GENE_SETS) $(TMP_DIR)/ && \
start_server_and_test \
- 'CXG_OPTIONS="--annotations-file $(TMP_DIR)/$(ANNOTATIONS_FILENAME)" $(MAKE) start-server' \
+ 'CXG_OPTIONS="--annotations-file $(TMP_DIR)/$(ANNOTATIONS_FILENAME) --gene-sets-file $(TMP_DIR)/$(GENE_SETS_FILENAME)" $(MAKE) start-server' \
$(CXG_SERVER_PORT) \
'CXG_URL_BASE="http://localhost:$(CXG_SERVER_PORT)" npm run e2e-annotations -- --verbose false'
rm -rf $(TMP_DIR)
diff --git a/client/__tests__/e2e/__snapshots__/e2eAnnotations.test.js.snap b/client/__tests__/e2e/__snapshots__/e2eAnnotations.test.js.snap
index b2c6342d..ca759215 100644
--- a/client/__tests__/e2e/__snapshots__/e2eAnnotations.test.js.snap
+++ b/client/__tests__/e2e/__snapshots__/e2eAnnotations.test.js.snap
@@ -21,3 +21,7 @@ exports[`annotations truncate midpoint whitespace 2`] = `"T"`;
exports[`annotations truncate single character 2`] = `"T"`;
+
+exports[`geneSET crud operations and interactions diffexp 1`] = `"
CD79A79A HLA-DRB1DRB1 HLA-DQA1DQA1 HLA-DPB1DPB1 HLA-DQB1DQB1 HLA-DPA1DPA1 MS4A14A1 LTBTB CD79B79B CD3737 HLA-DMA-DMA TCL1AL1A LINC0092600926 HLA-DMB-DMB HVCN1CN1 EAF2F2 FCRLARLA IRF8F8 PKIGIG P2RX5RX5 HLA-DOB-DOB SPIBIB BLNKNK SWAP70P70 PNOCOC CD1919 SMIM14M14 CD7272 KIAA01250125 IGLL5LL5 ARHGAP24AP24 COTL1TL1 C16orf74rf74 BTKTK SNX29P229P2 ADAM28M28 FCGR2BR2B PLD4D4 PPP1R14AR14A MZB1B1 KIAA00400040 PHACTR1CTR1 FCRL2RL2 RIC3C3 P2RY10Y10 SCPEP1EP1 DRAM2AM2 RP5-887A10.17A10.1 CD8282 GPX1X1 "`;
+
+exports[`geneSET crud operations and interactions diffexp 2`] = `"CD79A79A HLA-DRB1DRB1 HLA-DQA1DQA1 HLA-DPB1DPB1 HLA-DQB1DQB1 HLA-DPA1DPA1 MS4A14A1 LTBTB CD79B79B CD3737 HLA-DMA-DMA TCL1AL1A LINC0092600926 HLA-DMB-DMB HVCN1CN1 EAF2F2 FCRLARLA IRF8F8 PKIGIG P2RX5RX5 HLA-DOB-DOB SPIBIB BLNKNK SWAP70P70 PNOCOC CD1919 SMIM14M14 CD7272 KIAA01250125 IGLL5LL5 ARHGAP24AP24 COTL1TL1 C16orf74rf74 BTKTK SNX29P229P2 ADAM28M28 FCGR2BR2B PLD4D4 PPP1R14AR14A MZB1B1 KIAA00400040 PHACTR1CTR1 FCRL2RL2 RIC3C3 P2RY10Y10 SCPEP1EP1 DRAM2AM2 RP5-887A10.17A10.1 CD8282 GPX1X1 "`;
diff --git a/client/__tests__/e2e/cellxgeneActions.js b/client/__tests__/e2e/cellxgeneActions.js
index ba1f4f08..0f5bf77c 100644
--- a/client/__tests__/e2e/cellxgeneActions.js
+++ b/client/__tests__/e2e/cellxgeneActions.js
@@ -176,6 +176,137 @@ export async function createCategory(categoryName) {
await clickOn("submit-category");
}
+/*
+
+ GENESET
+
+*/
+
+export async function colorByGeneset(genesetName) {
+ await clickOn(`${genesetName}:colorby-entire-geneset`);
+}
+
+export async function colorByGene(gene) {
+ await clickOn(`colorby-${gene}`);
+}
+
+export async function assertColorLegendLabel(label) {
+ const handle = await waitByID("continuous_legend_color_by_label");
+
+ const result = await handle.evaluate((node) => {
+ return node.getAttribute("aria-label");
+ });
+
+ return expect(result).toBe(label);
+}
+
+export async function expandGeneset(genesetName) {
+ const expand = await waitByID(`${genesetName}:geneset-expand`);
+ const notExpanded = await expand.$(
+ "[data-testclass='geneset-expand-is-not-expanded']"
+ );
+ if (notExpanded) await clickOn(`${genesetName}:geneset-expand`);
+}
+
+export async function createGeneset(genesetName) {
+ await clickOnUntil("open-create-geneset-dialog", async () => {
+ await expect(page).toMatchElement(getTestId("create-geneset-input"));
+ });
+
+ await typeInto("create-geneset-input", genesetName);
+ await clickOn("submit-geneset");
+ await waitByClass("autosave-complete");
+}
+
+export async function editGenesetName(genesetName, editText) {
+ const editButton = `${genesetName}:edit-genesetName-mode`;
+ const submitButton = `${genesetName}:submit-geneset`;
+ await clickOnUntil(`${genesetName}:see-actions`, async () => {
+ await expect(page).toMatchElement(getTestId(editButton));
+ });
+ await clickOn(editButton);
+ await typeInto("rename-geneset-modal", editText);
+ await clickOn(submitButton);
+}
+
+export async function deleteGeneset(genesetName) {
+ const targetId = `${genesetName}:delete-geneset`;
+
+ await clickOnUntil(`${genesetName}:see-actions`, async () => {
+ await expect(page).toMatchElement(getTestId(targetId));
+ });
+
+ await clickOn(targetId);
+
+ await assertGenesetDoesNotExist(genesetName);
+ await waitByClass("autosave-complete");
+}
+
+export async function assertGenesetDoesNotExist(genesetName) {
+ const result = await isElementPresent(
+ getTestId(`${genesetName}:geneset-name`)
+ );
+ await expect(result).toBe(false);
+}
+
+export async function assertGenesetExists(genesetName) {
+ const handle = await waitByID(`${genesetName}:geneset-name`);
+
+ const result = await handle.evaluate((node) => {
+ return node.getAttribute("aria-label");
+ });
+
+ return expect(result).toBe(genesetName);
+}
+
+/*
+
+ GENE
+
+*/
+
+export async function addGeneToSet(genesetName, geneToAddToSet) {
+ const submitButton = `${genesetName}:submit-gene`;
+
+ await clickOn(`${genesetName}:add-new-gene-to-geneset`);
+ await typeInto("add-genes", geneToAddToSet);
+ await clickOn(submitButton);
+}
+
+export async function removeGene(geneSymbol) {
+ const targetId = `delete-from-geneset:${geneSymbol}`;
+
+ await clickOn(targetId);
+
+ await waitByClass("autosave-complete");
+}
+
+export async function assertGeneExistsInGeneset(geneSymbol) {
+ const handle = await waitByID(`${geneSymbol}:gene-label`);
+
+ const result = await handle.evaluate((node) => {
+ return node.getAttribute("aria-label");
+ });
+
+ return expect(result).toBe(geneSymbol);
+}
+
+export async function assertGeneDoesNotExist(geneSymbol) {
+ const result = await isElementPresent(getTestId(`${geneSymbol}:gene-label`));
+
+ await expect(result).toBe(false);
+}
+
+export async function expandGene(geneSymbol) {
+ await clickOn(`maximize-${geneSymbol}`);
+}
+
+/*
+
+ CATEGORY
+
+*/
+
export async function duplicateCategory(categoryName) {
await clickOn("open-annotation-dialog");
diff --git a/client/__tests__/e2e/diffexpGeneSets.js b/client/__tests__/e2e/diffexpGeneSets.js
new file mode 100644
index 00000000..627861c4
--- /dev/null
+++ b/client/__tests__/e2e/diffexpGeneSets.js
@@ -0,0 +1,105 @@
+export const diffexpPop1Genes = [
+ "CD79A",
+ "HLA-DRB1",
+ "HLA-DQA1",
+ "HLA-DPB1",
+ "HLA-DQB1",
+ "HLA-DPA1",
+ "MS4A1",
+ "LTB",
+ "CD79B",
+ "CD37",
+ "HLA-DMA",
+ "TCL1A",
+ "LINC00926",
+ "HLA-DMB",
+ "HVCN1",
+ "EAF2",
+ "FCRLA",
+ "IRF8",
+ "PKIG",
+ "P2RX5",
+ "HLA-DOB",
+ "SPIB",
+ "BLNK",
+ "SWAP70",
+ "PNOC",
+ "CD19",
+ "SMIM14",
+ "CD72",
+ "KIAA0125",
+ "IGLL5",
+ "ARHGAP24",
+ "COTL1",
+ "C16orf74",
+ "BTK",
+ "SNX29P2",
+ "ADAM28",
+ "FCGR2B",
+ "PLD4",
+ "PPP1R14A",
+ "MZB1",
+ "KIAA0040",
+ "PHACTR1",
+ "FCRL2",
+ "RIC3",
+ "P2RY10",
+ "SCPEP1",
+ "DRAM2",
+ "RP5-887A10.1",
+ "CD82",
+ "GPX1",
+];
+
+export const diffexpPop2Genes = [
+ "NKG7",
+ "GZMB",
+ "CTSW",
+ "PRF1",
+ "GNLY",
+ "GZMA",
+ "CST7",
+ "FGFBP2",
+ "SRGN",
+ "CD247",
+ "FCGR3A",
+ "TYROBP",
+ "FCER1G",
+ "ID2",
+ "SPON2",
+ "CCL4",
+ "CCL5",
+ "GZMH",
+ "GIMAP7",
+ "CLIC3",
+ "HOPX",
+ "XCL2",
+ "LGALS1",
+ "IGFBP7",
+ "AKR1C3",
+ "IL32",
+ "EFHD2",
+ "PRSS23",
+ "TTC38",
+ "ZAP70",
+ "S1PR5",
+ "SAMD3",
+ "GIMAP4",
+ "CCL3",
+ "ABI3",
+ "XCL1",
+ "S100A6",
+ "UBB",
+ "GPR56",
+ "PDIA3",
+ "S100A11",
+ "APOBEC3G",
+ "HAVCR2",
+ "PLEKHF1",
+ "LITAF",
+ "ARPC5L",
+ "PTGDR",
+ "PRMT2",
+ "GSTP1",
+ "FCRL6",
+];
diff --git a/client/__tests__/e2e/e2eAnnotations.test.js b/client/__tests__/e2e/e2eAnnotations.test.js
index e998da6b..1430600c 100644
--- a/client/__tests__/e2e/e2eAnnotations.test.js
+++ b/client/__tests__/e2e/e2eAnnotations.test.js
@@ -12,6 +12,7 @@ import {
getTestId,
getTestClass,
getAllByClass,
+ getOneElementInnerHTML,
} from "./puppeteerUtils";
import {
@@ -27,6 +28,21 @@ import {
renameLabel,
subset,
duplicateCategory,
+ createGeneset,
+ deleteGeneset,
+ assertGenesetExists,
+ assertGenesetDoesNotExist,
+ getCellSetCount,
+ expandGeneset,
+ editGenesetName,
+ addGeneToSet,
+ assertGeneExistsInGeneset,
+ removeGene,
+ assertGeneDoesNotExist,
+ expandGene,
+ colorByGeneset,
+ assertColorLegendLabel,
+ colorByGene,
} from "./cellxgeneActions";
const data = datasets[DATASET];
@@ -34,12 +50,40 @@ 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";
+
async function setup(config) {
await goToPage(appUrlBase);
- // setup the test fixtures
- await createCategory(perTestCategoryName);
- await createLabel(perTestCategoryName, perTestLabelName);
+ 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 });
@@ -51,6 +95,166 @@ async function setup(config) {
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", 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);
+ });
+ test("edit geneset name", async () => {
+ await setup(config);
+
+ await editGenesetName(editableGenesetName, editText);
+ await assertGenesetExists(newGenesetName);
+ });
+ test("delete a geneset", async () => {
+ if (config.withSubset) return;
+
+ await setup(config);
+
+ await deleteGeneset(genesetToDeleteName);
+ });
+});
+
+describe.each([
+ { withSubset: true, tag: "subset" },
+ { withSubset: false, tag: "whole" },
+])("GENE crud operations and interactions", (config) => {
+ test("add a gene to geneset", async () => {
+ await setup(config);
+
+ await addGeneToSet(setToAddGeneTo, geneToAddToSet);
+ await expandGeneset(setToAddGeneTo);
+ 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", async () => {
+ // We've already deleted the gene
+ if (config.withSubset) return;
+
+ await setup(config);
+
+ await expandGeneset(setToRemoveFrom);
+ await removeGene(geneToRemove);
+ 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);
diff --git a/client/__tests__/e2e/puppeteerUtils.js b/client/__tests__/e2e/puppeteerUtils.js
index 099b6065..8d3a497f 100644
--- a/client/__tests__/e2e/puppeteerUtils.js
+++ b/client/__tests__/e2e/puppeteerUtils.js
@@ -12,7 +12,7 @@ export async function waitByID(testId, props = {}) {
}
export async function waitByClass(testClass, props = {}) {
- await page.waitForSelector(`[data-testclass='${testClass}']`, props);
+ return page.waitForSelector(`[data-testclass='${testClass}']`, props);
}
export async function waitForAllByIds(testIds) {
diff --git a/client/src/components/brushableHistogram/index.js b/client/src/components/brushableHistogram/index.js
index 89773fa8..f56faed1 100644
--- a/client/src/components/brushableHistogram/index.js
+++ b/client/src/components/brushableHistogram/index.js
@@ -366,6 +366,10 @@ class HistogramBrush extends React.PureComponent {
const fieldForId = field.replace(/\s/g, "_");
const showScatterPlot = isUserDefined;
+ let testClass = "histogram-continuous-metadata";
+ if (isUserDefined) testClass = "histogram-user-gene";
+ else if (isGeneSetSummary) testClass = "histogram-gene-set-summary";
+
return (
{
*/
return (
{
.attr("y", 2)
.attr("x", 0 - legendHeight / 2)
.attr("dy", "1em")
+ .attr("data-testid", "continuous_legend_color_by_label")
+ .attr("aria-label", colorAccessor)
.style("text-anchor", "middle")
.style("fill", "white")
.text(colorAccessor);
diff --git a/client/src/components/geneExpression/gene.js b/client/src/components/geneExpression/gene.js
index 7d0834eb..47efc6cd 100644
--- a/client/src/components/geneExpression/gene.js
+++ b/client/src/components/geneExpression/gene.js
@@ -135,7 +135,7 @@ class Gene extends React.Component {