mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-08 23:08:12 +08:00
Add undo/redo smoke tests for annotations (#1175)
* Add undo/redo tests for annotations Fixes https://github.com/chanzuckerberg/cellxgene/issues/969 ... also refactor the tests for DRY. * Add done() * Make e2e annotations tests safer to concurrency * Add data-testclass for save state. * Simplify tests and make them dependent on save state
This commit is contained in:
@@ -117,13 +117,20 @@ export const datasets = {
|
|||||||
},
|
},
|
||||||
categoryLabel: {
|
categoryLabel: {
|
||||||
lasso: {
|
lasso: {
|
||||||
"coordinates-as-percent": { x1: 0.3, y1: 0.3, x2: 0.5, y2: 0.5 },
|
"coordinates-as-percent": { x1: 0.05, y1: 0.3, x2: 0.5, y2: 0.5 },
|
||||||
count: "38"
|
|
||||||
},
|
},
|
||||||
newCount: {
|
newCount: {
|
||||||
bySubsetConfig: {
|
bySubsetConfig: {
|
||||||
false: "199",
|
false: "600",
|
||||||
true: "193"
|
true: "591"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
annotationsFromFile: {
|
||||||
|
count: {
|
||||||
|
bySubsetConfig: {
|
||||||
|
false: "1161",
|
||||||
|
true: "856"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,31 +13,19 @@ beforeAll(async () => {
|
|||||||
[browser, page, utils, cxgActions] = await setupTestBrowser(browserViewport);
|
[browser, page, utils, cxgActions] = await setupTestBrowser(browserViewport);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await page.goto(appUrlBase);
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
if (!DEBUG) browser.close();
|
if (!DEBUG) browser.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("did launch", () => {
|
|
||||||
test("page launched", async () => {
|
|
||||||
let el = await utils.getOneElementInnerHTML("[data-testid='header']");
|
|
||||||
expect(el).toBe(data.title);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
describe.each([
|
describe.each([
|
||||||
{withSubset: true},
|
{withSubset: true, tag: "subset"},
|
||||||
{withSubset: false}
|
{withSubset: false, tag: "whole"}
|
||||||
])("annotations", (config) => {
|
])("annotations", (config) => {
|
||||||
|
|
||||||
async function subset() {
|
async function subset() {
|
||||||
const lassoSelection = await cxgActions.calcDragCoordinates(
|
const lassoSelection = await cxgActions.calcDragCoordinates(
|
||||||
"layout-graph",
|
"layout-graph",
|
||||||
{ x1: 0.10, y1: 0.10, x2: 0.80, y2: 0.80 }
|
{x1: 0.10, y1: 0.10, x2: 0.80, y2: 0.80}
|
||||||
);
|
);
|
||||||
await cxgActions.drag(
|
await cxgActions.drag(
|
||||||
"layout-graph",
|
"layout-graph",
|
||||||
@@ -50,73 +38,76 @@ describe.each([
|
|||||||
await cxgActions.clickOnCoordinate("layout-graph", coordinate);
|
await cxgActions.clickOnCoordinate("layout-graph", coordinate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const perTestCategoryName = "per-test-category";
|
||||||
|
const perTestLabelName = "per-test-label";
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
await page.goto(appUrlBase);
|
||||||
|
// wait for the page to load
|
||||||
|
await utils.waitByClass("autosave-complete");
|
||||||
|
// setup the test fixtures
|
||||||
|
await cxgActions.createCategory(perTestCategoryName);
|
||||||
|
await cxgActions.createLabel(perTestCategoryName, perTestLabelName);
|
||||||
if (config.withSubset) await subset();
|
if (config.withSubset) await subset();
|
||||||
|
await utils.waitByClass("autosave-complete");
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await deleteCategoryIfExists(perTestCategoryName);
|
||||||
|
await utils.waitByClass("autosave-complete");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("create a category", async () => {
|
test("create a category", async () => {
|
||||||
await utils.clickOn("open-annotation-dialog");
|
const categoryName = `category-created-${config.tag}`;
|
||||||
await utils.typeInto("new-category-name", "test-category-name");
|
await assertCategoryDoesNotExist(categoryName);
|
||||||
await utils.clickOn("submit-category");
|
await cxgActions.createCategory(categoryName);
|
||||||
const result = await utils.waitByID("test-category-name:category-expand");
|
await assertCategoryExists(categoryName);
|
||||||
expect(await result.evaluate(node => node.innerText)).toBe("test-category-name");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("delete a category", async () => {
|
test("delete a category", async () => {
|
||||||
const previous = await utils.waitByID("cluster-test:category-expand");
|
await cxgActions.deleteCategory(perTestCategoryName);
|
||||||
expect(await previous.evaluate(node => node.innerText)).toBe("cluster-test");
|
await assertCategoryDoesNotExist(perTestCategoryName);
|
||||||
await utils.hoverOn("cluster-test:see-actions");
|
|
||||||
await utils.clickOn("cluster-test:delete-category");
|
|
||||||
const result = await page.$("[data-testid='cluster-test:category-expand']");
|
|
||||||
expect(result).toBeNull();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("rename a category", async () => {
|
test("rename a category", async () => {
|
||||||
const previous = await utils.waitByID("cluster-test:category-expand");
|
const newCategoryName = `cluster-for-real-${config.tag}`;
|
||||||
expect(await previous.evaluate(node => node.innerText)).toBe("cluster-test");
|
await cxgActions.renameCategory(perTestCategoryName, newCategoryName);
|
||||||
await utils.hoverOn("cluster-test:see-actions");
|
await assertCategoryDoesNotExist(perTestCategoryName);
|
||||||
await utils.clickOn("cluster-test:edit-category-mode");
|
await assertCategoryExists(newCategoryName);
|
||||||
await utils.typeInto("cluster-test:edit-category-name-text", "-renamed");
|
|
||||||
await utils.clickOn("cluster-test:submit-category-edit");
|
|
||||||
const result = await utils.waitByID("cluster-test-renamed:category-expand");
|
|
||||||
expect(await result.evaluate(node => node.innerText)).toBe("cluster-test-renamed");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("create a label", async () => {
|
test("create a label", async () => {
|
||||||
await utils.hoverOn("cluster-test:see-actions");
|
const labelName = `new-label-${config.tag}`;
|
||||||
await utils.clickOn("cluster-test:add-new-label-to-category");
|
await assertLabelDoesNotExist(perTestCategoryName, labelName);
|
||||||
await utils.typeInto("cluster-test:new-label-name", "test-label-name");
|
await cxgActions.createLabel(perTestCategoryName, labelName);
|
||||||
await utils.clickOn("cluster-test:submit-label");
|
await assertLabelExists(perTestCategoryName, labelName);
|
||||||
await cxgActions.expandCategory("cluster-test");
|
|
||||||
const result = await utils.waitByID("categorical-value-cluster-test-test-label-name");
|
|
||||||
expect(await result.evaluate(node => node.innerText)).toBe("test-label-name");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("delete a label", async () => {
|
test("delete a label", async () => {
|
||||||
await cxgActions.expandCategory("cluster-test");
|
await cxgActions.deleteLabel(perTestCategoryName, perTestLabelName);
|
||||||
const previous = await utils.waitByID("categorical-value-cluster-test-three");
|
await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName);
|
||||||
expect(await previous.evaluate(node => node.innerText)).toBe("three");
|
|
||||||
await utils.hoverOn("cluster-test:three:see-actions");
|
|
||||||
await utils.clickOn("cluster-test:three:delete-label");
|
|
||||||
const result = await page.$("[data-testid='categorical-value-cluster-test-three']");
|
|
||||||
expect(result).toBeNull();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("rename a label", async () => {
|
test("rename a label", async () => {
|
||||||
await cxgActions.expandCategory("cluster-test");
|
const newLabelName = "my-cool-new-label";
|
||||||
const previous = await utils.waitByID("categorical-value-cluster-test-four");
|
await assertLabelDoesNotExist(perTestCategoryName, newLabelName);
|
||||||
expect(await previous.evaluate(node => node.innerText)).toBe("four");
|
await cxgActions.renameLabel(perTestCategoryName, perTestLabelName, newLabelName);
|
||||||
await utils.hoverOn("cluster-test:four:see-actions");
|
await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName);
|
||||||
await utils.clickOn("cluster-test:four:edit-label");
|
await assertLabelExists(perTestCategoryName, newLabelName);
|
||||||
await utils.typeInto("cluster-test:four:edit-label-name", ".");
|
});
|
||||||
await utils.clickOn("cluster-test:four:submit-label-edit");
|
|
||||||
const result = await utils.waitByID("categorical-value-cluster-test-four.");
|
test("check cell count for a label loaded from file", async () => {
|
||||||
expect(await result.evaluate(node => node.innerText)).toBe("four.");
|
const categoryName = "cluster-test";
|
||||||
|
const labelName = "four";
|
||||||
|
await cxgActions.expandCategory(categoryName);
|
||||||
|
const result = await utils.waitByID(`categorical-value-count-${categoryName}-${labelName}`);
|
||||||
|
expect(await result.evaluate(node => node.innerText)).toBe(
|
||||||
|
data.annotationsFromFile.count.bySubsetConfig[config.withSubset]
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("assign cells to a label", async () => {
|
test("assign cells to a label", async () => {
|
||||||
await cxgActions.expandCategory("cluster-test");
|
await cxgActions.expandCategory(perTestCategoryName);
|
||||||
|
|
||||||
const lassoSelection = await cxgActions.calcDragCoordinates(
|
const lassoSelection = await cxgActions.calcDragCoordinates(
|
||||||
"layout-graph",
|
"layout-graph",
|
||||||
data.categoryLabel.lasso["coordinates-as-percent"]
|
data.categoryLabel.lasso["coordinates-as-percent"]
|
||||||
@@ -128,13 +119,118 @@ describe.each([
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
await utils.waitByID("lasso-element", {visible: true});
|
await utils.waitByID("lasso-element", {visible: true});
|
||||||
const initialCount = await cxgActions.cellSet(1);
|
await utils.clickOn(`${perTestCategoryName}:${perTestLabelName}:see-actions`);
|
||||||
expect(initialCount).toBe(data.categoryLabel.lasso.count);
|
await utils.clickOn(`${perTestCategoryName}:${perTestLabelName}:add-current-selection-to-this-label`);
|
||||||
await utils.hoverOn("cluster-test:one:see-actions");
|
const result = await utils.waitByID(`categorical-value-count-${perTestCategoryName}-${perTestLabelName}`);
|
||||||
await utils.clickOn("cluster-test:one:add-current-selection-to-this-label");
|
|
||||||
const result = await utils.waitByID("categorical-value-count-cluster-test-one");
|
|
||||||
expect(await result.evaluate(node => node.innerText)).toBe(
|
expect(await result.evaluate(node => node.innerText)).toBe(
|
||||||
data.categoryLabel.newCount.bySubsetConfig[config.withSubset]
|
data.categoryLabel.newCount.bySubsetConfig[config.withSubset]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("undo/redo category creation", async () => {
|
||||||
|
const categoryName = `category-created-undo-${config.tag}`;
|
||||||
|
await assertCategoryDoesNotExist(categoryName);
|
||||||
|
await cxgActions.createCategory(categoryName);
|
||||||
|
await assertCategoryExists(categoryName);
|
||||||
|
await utils.clickOn("undo");
|
||||||
|
await assertCategoryDoesNotExist(categoryName);
|
||||||
|
await utils.clickOn("redo");
|
||||||
|
await assertCategoryExists(categoryName);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("undo/redo category deletion", async () => {
|
||||||
|
const categoryName = `category-deleted-undo-${config.tag}`;
|
||||||
|
await cxgActions.createCategory(categoryName);
|
||||||
|
await assertCategoryExists(categoryName);
|
||||||
|
await cxgActions.deleteCategory(categoryName);
|
||||||
|
await assertCategoryDoesNotExist(categoryName);
|
||||||
|
await utils.clickOn("undo");
|
||||||
|
await assertCategoryExists(categoryName);
|
||||||
|
await utils.clickOn("redo");
|
||||||
|
await assertCategoryDoesNotExist(categoryName);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("undo/redo category rename", async () => {
|
||||||
|
const newCategoryName = `category-renamed-undo-${config.tag}`;
|
||||||
|
await assertCategoryDoesNotExist(newCategoryName);
|
||||||
|
await cxgActions.renameCategory(perTestCategoryName, newCategoryName);
|
||||||
|
await assertCategoryExists(newCategoryName);
|
||||||
|
await assertCategoryDoesNotExist(perTestCategoryName);
|
||||||
|
await utils.clickOn("undo");
|
||||||
|
await assertCategoryExists(perTestCategoryName);
|
||||||
|
await assertCategoryDoesNotExist(newCategoryName);
|
||||||
|
await utils.clickOn("redo");
|
||||||
|
await assertCategoryExists(newCategoryName);
|
||||||
|
await assertCategoryDoesNotExist(perTestCategoryName);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("undo/redo label creation", async () => {
|
||||||
|
const labelName = `label-created-undo-${config.tag}`;
|
||||||
|
await assertLabelDoesNotExist(perTestCategoryName, labelName);
|
||||||
|
await cxgActions.createLabel(perTestCategoryName, labelName);
|
||||||
|
await assertLabelExists(perTestCategoryName, labelName);
|
||||||
|
await utils.clickOn("undo");
|
||||||
|
await assertLabelDoesNotExist(perTestCategoryName);
|
||||||
|
await utils.clickOn("redo");
|
||||||
|
await assertLabelExists(perTestCategoryName, labelName);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("undo/redo label deletion", async () => {
|
||||||
|
await cxgActions.deleteLabel(perTestCategoryName, perTestLabelName);
|
||||||
|
await assertLabelDoesNotExist(perTestCategoryName);
|
||||||
|
await utils.clickOn("undo");
|
||||||
|
await assertLabelExists(perTestCategoryName, perTestLabelName);
|
||||||
|
await utils.clickOn("redo");
|
||||||
|
await assertLabelDoesNotExist(perTestCategoryName);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("undo/redo label rename", async () => {
|
||||||
|
const newLabelName = `label-renamed-undo-${config.tag}`;
|
||||||
|
await assertLabelDoesNotExist(perTestCategoryName, newLabelName);
|
||||||
|
await cxgActions.renameLabel(perTestCategoryName, perTestLabelName, newLabelName);
|
||||||
|
await assertLabelExists(perTestCategoryName, newLabelName);
|
||||||
|
await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName);
|
||||||
|
await utils.clickOn("undo");
|
||||||
|
await assertLabelExists(perTestCategoryName, perTestLabelName);
|
||||||
|
await assertLabelDoesNotExist(perTestCategoryName, newLabelName);
|
||||||
|
await utils.clickOn("redo");
|
||||||
|
await assertLabelExists(perTestCategoryName, newLabelName);
|
||||||
|
await assertLabelDoesNotExist(perTestCategoryName, perTestLabelName);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function assertCategoryExists(categoryName) {
|
||||||
|
const result = await utils.waitByID(`${categoryName}:category-expand`);
|
||||||
|
expect(await result.evaluate(node => node.innerText)).toBe(categoryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assertCategoryDoesNotExist(categoryName) {
|
||||||
|
const result = await page.$(`[data-testid='${categoryName}:category-expand']`);
|
||||||
|
expect(result).toBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assertLabelExists(categoryName, labelName) {
|
||||||
|
const category = await utils.waitByID(`${categoryName}:category-expand`);
|
||||||
|
expect(category).not.toBeNull();
|
||||||
|
await cxgActions.expandCategory(categoryName);
|
||||||
|
const previous = await utils.waitByID(`categorical-value-${categoryName}-${labelName}`);
|
||||||
|
expect(await previous.evaluate(node => node.innerText)).toBe(labelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function assertLabelDoesNotExist(categoryName, labelName) {
|
||||||
|
await cxgActions.expandCategory(categoryName);
|
||||||
|
const result = await page.$(`[data-testid='categorical-value-${categoryName}-${labelName}']`);
|
||||||
|
expect(result).toBeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteCategoryIfExists(categoryName) {
|
||||||
|
try {
|
||||||
|
const category = await page.waitForSelector(
|
||||||
|
`[data-testid='${categoryName}:category-expand']`,
|
||||||
|
{timeout: 200}
|
||||||
|
);
|
||||||
|
if (category !== null) return await cxgActions.deleteCategory(categoryName);
|
||||||
|
} catch (error) {
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -56,19 +56,13 @@ export const puppeteerUtils = puppeteerPage => ({
|
|||||||
// select all
|
// select all
|
||||||
|
|
||||||
await puppeteerPage.click(selector, { clickCount: 3 });
|
await puppeteerPage.click(selector, { clickCount: 3 });
|
||||||
await puppeteerPage.keyboard.type("Backspace");
|
await puppeteerPage.keyboard.press("Backspace");
|
||||||
await puppeteerPage.type(selector, text);
|
await puppeteerPage.type(selector, text);
|
||||||
},
|
},
|
||||||
|
|
||||||
async clickOn(testid) {
|
async clickOn(testid, options={}) {
|
||||||
await this.waitByID(testid);
|
await this.waitByID(testid);
|
||||||
await puppeteerPage.click(`[data-testid='${testid}']`);
|
await puppeteerPage.click(`[data-testid='${testid}']`, options);
|
||||||
await puppeteerPage.waitFor(50);
|
|
||||||
},
|
|
||||||
|
|
||||||
async hoverOn(testid) {
|
|
||||||
await this.waitByID(testid);
|
|
||||||
await puppeteerPage.hover(`[data-testid='${testid}']`);
|
|
||||||
await puppeteerPage.waitFor(50);
|
await puppeteerPage.waitFor(50);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -210,8 +204,10 @@ export const cellxgeneActions = puppeteerPage => ({
|
|||||||
|
|
||||||
async expandCategory(category) {
|
async expandCategory(category) {
|
||||||
const expand = await puppeteerUtils(puppeteerPage).waitByID(`${category}:category-expand`);
|
const expand = await puppeteerUtils(puppeteerPage).waitByID(`${category}:category-expand`);
|
||||||
const expandArrow = await expand.$("[data-testclass='category-expand-is-not-expanded']");
|
const notExpanded = await expand.$("[data-testclass='category-expand-is-not-expanded']");
|
||||||
await expandArrow.click();
|
if (notExpanded) {
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${category}:category-expand`);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async reset() {
|
async reset() {
|
||||||
@@ -231,6 +227,48 @@ export const cellxgeneActions = puppeteerPage => ({
|
|||||||
max
|
max
|
||||||
);
|
);
|
||||||
await puppeteerUtils(puppeteerPage).clickOn("clip-commit");
|
await puppeteerUtils(puppeteerPage).clickOn("clip-commit");
|
||||||
|
},
|
||||||
|
|
||||||
|
async createCategory(categoryName) {
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn("open-annotation-dialog");
|
||||||
|
await puppeteerUtils(puppeteerPage).typeInto("new-category-name", categoryName);
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn("submit-category");
|
||||||
|
},
|
||||||
|
|
||||||
|
async renameCategory(oldCatgoryName, newCategoryName) {
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${oldCatgoryName}:see-actions`);
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${oldCatgoryName}:edit-category-mode`);
|
||||||
|
await puppeteerUtils(puppeteerPage).clearInputAndTypeInto(`${oldCatgoryName}:edit-category-name-text`, newCategoryName);
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${oldCatgoryName}:submit-category-edit`);
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteCategory(categoryName) {
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${categoryName}:see-actions`);
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${categoryName}:delete-category`);
|
||||||
|
},
|
||||||
|
|
||||||
|
async createLabel(categoryName, labelName) {
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${categoryName}:see-actions`);
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${categoryName}:add-new-label-to-category`);
|
||||||
|
await puppeteerUtils(puppeteerPage).typeInto(`${categoryName}:new-label-name`, labelName);
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${categoryName}:submit-label`);
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteLabel(categoryName, labelName) {
|
||||||
|
await this.expandCategory(categoryName);
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${categoryName}:${labelName}:see-actions`);
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn( `${categoryName}:${labelName}:delete-label`);
|
||||||
|
},
|
||||||
|
|
||||||
|
async renameLabel(categoryName, oldLabelName, newLabelName) {
|
||||||
|
await this.expandCategory(categoryName);
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${categoryName}:${oldLabelName}:see-actions`);
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${categoryName}:${oldLabelName}:edit-label`);
|
||||||
|
await puppeteerUtils(puppeteerPage).clearInputAndTypeInto(
|
||||||
|
`${categoryName}:${oldLabelName}:edit-label-name`,
|
||||||
|
newLabelName
|
||||||
|
);
|
||||||
|
await puppeteerUtils(puppeteerPage).clickOn(`${categoryName}:${oldLabelName}:submit-label-edit`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import FilenameDialog from "./filenameDialog";
|
|||||||
saveInProgress: state.autosave?.saveInProgress ?? false,
|
saveInProgress: state.autosave?.saveInProgress ?? false,
|
||||||
lastSavedObsAnnotations: state.autosave?.lastSavedObsAnnotations,
|
lastSavedObsAnnotations: state.autosave?.lastSavedObsAnnotations,
|
||||||
error: state.autosave?.error,
|
error: state.autosave?.error,
|
||||||
writableCategoriesEnabled: state.config?.parameters?.["annotations"] ?? false
|
writableCategoriesEnabled: state.config?.parameters?.["annotations"] ?? false,
|
||||||
|
initialDataLoadComplete: state.autosave?.initialDataLoadComplete
|
||||||
}))
|
}))
|
||||||
class Autosave extends React.Component {
|
class Autosave extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
@@ -63,10 +64,17 @@ class Autosave extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { writableCategoriesEnabled } = this.props;
|
const { writableCategoriesEnabled, saveInProgress, initialDataLoadComplete } = this.props;
|
||||||
return writableCategoriesEnabled ? (
|
return writableCategoriesEnabled ? (
|
||||||
<div
|
<div
|
||||||
id="autosave"
|
id="autosave"
|
||||||
|
data-testclass={
|
||||||
|
!initialDataLoadComplete
|
||||||
|
? "autosave-init"
|
||||||
|
: (this.needToSave() || saveInProgress)
|
||||||
|
? "autosave-incomplete"
|
||||||
|
: "autosave-complete"
|
||||||
|
}
|
||||||
style={{
|
style={{
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
display: "inherit",
|
display: "inherit",
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ function InformationMenu(props) {
|
|||||||
style={{
|
style={{
|
||||||
cursor: "pointer"
|
cursor: "pointer"
|
||||||
}}
|
}}
|
||||||
|
data-testid="undo"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
@@ -46,6 +47,7 @@ function InformationMenu(props) {
|
|||||||
style={{
|
style={{
|
||||||
cursor: "pointer"
|
cursor: "pointer"
|
||||||
}}
|
}}
|
||||||
|
data-testid="redo"
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ const Autosave = (
|
|||||||
state = {
|
state = {
|
||||||
saveInProgress: false,
|
saveInProgress: false,
|
||||||
error: false,
|
error: false,
|
||||||
lastSavedObsAnnotations: null
|
lastSavedObsAnnotations: null,
|
||||||
|
initialDataLoadComplete: false
|
||||||
},
|
},
|
||||||
action,
|
action,
|
||||||
nextSharedState
|
nextSharedState
|
||||||
@@ -15,7 +16,8 @@ const Autosave = (
|
|||||||
...state,
|
...state,
|
||||||
error: false,
|
error: false,
|
||||||
saveInProgress: false,
|
saveInProgress: false,
|
||||||
lastSavedObsAnnotations: universe.obsAnnotations
|
lastSavedObsAnnotations: universe.obsAnnotations,
|
||||||
|
initialDataLoadComplete: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ class EndPointsAnnotations(EndPoints):
|
|||||||
|
|
||||||
def test_get_user_annotations_existing_obs_keys_fbs(self):
|
def test_get_user_annotations_existing_obs_keys_fbs(self):
|
||||||
self._test_get_user_annotations_obs_keys_fbs(
|
self._test_get_user_annotations_obs_keys_fbs(
|
||||||
"cluster-test", {"unassigned", "one", "two", "three", "four", "five"},
|
"cluster-test", {"unassigned", "one", "two", "three", "four", "five", "six", "seven"},
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_put_user_annotations_obs_fbs(self):
|
def test_put_user_annotations_obs_fbs(self):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Annotations generated on 2020-02-07T16:48:13 using cellxgene version 0.14.0
|
# Annotations generated on 2020-02-24T23:19:31 using cellxgene version 0.14.0
|
||||||
# Input data file was ../example-dataset/pbmc3k.h5ad, which was last modified on 2019-12-17T17:13:17
|
# Input data file was example-dataset/pbmc3k.h5ad, which was last modified on 2019-12-17T17:13:17
|
||||||
index,cluster-test
|
index,cluster-test
|
||||||
AAACATACAACCAC-1,four
|
AAACATACAACCAC-1,four
|
||||||
AAACATTGAGCTAC-1,three
|
AAACATTGAGCTAC-1,three
|
||||||
@@ -27,7 +27,7 @@ AAAGTTTGTAGCGT-1,two
|
|||||||
AAATCAACAATGCC-1,three
|
AAATCAACAATGCC-1,three
|
||||||
AAATCAACACCAGT-1,four
|
AAATCAACACCAGT-1,four
|
||||||
AAATCAACCAGGAG-1,four
|
AAATCAACCAGGAG-1,four
|
||||||
AAATCAACCCTATT-1,unassigned
|
AAATCAACCCTATT-1,seven
|
||||||
AAATCAACGGAAGC-1,four
|
AAATCAACGGAAGC-1,four
|
||||||
AAATCAACTCGCAA-1,four
|
AAATCAACTCGCAA-1,four
|
||||||
AAATCATGACCACA-1,one
|
AAATCATGACCACA-1,one
|
||||||
@@ -245,7 +245,7 @@ ACCCAAGAGGACAG-1,four
|
|||||||
ACCCAAGATTCACT-1,three
|
ACCCAAGATTCACT-1,three
|
||||||
ACCCACTGCGCCTT-1,two
|
ACCCACTGCGCCTT-1,two
|
||||||
ACCCACTGGACAGG-1,four
|
ACCCACTGGACAGG-1,four
|
||||||
ACCCACTGGTTCAG-1,one
|
ACCCACTGGTTCAG-1,six
|
||||||
ACCCACTGTCGTAG-1,two
|
ACCCACTGTCGTAG-1,two
|
||||||
ACCCAGCTCAGAAA-1,four
|
ACCCAGCTCAGAAA-1,four
|
||||||
ACCCAGCTGTTAGC-1,two
|
ACCCAGCTGTTAGC-1,two
|
||||||
@@ -271,7 +271,7 @@ ACCTTTGACTCCCA-1,two
|
|||||||
ACCTTTGAGGAACG-1,two
|
ACCTTTGAGGAACG-1,two
|
||||||
ACCTTTGAGGAAGC-1,one
|
ACCTTTGAGGAAGC-1,one
|
||||||
ACGAACACCTTGTT-1,five
|
ACGAACACCTTGTT-1,five
|
||||||
ACGAACTGGCTATG-1,one
|
ACGAACTGGCTATG-1,six
|
||||||
ACGAAGCTCTCCAC-1,four
|
ACGAAGCTCTCCAC-1,four
|
||||||
ACGAAGCTCTGAGT-1,three
|
ACGAAGCTCTGAGT-1,three
|
||||||
ACGACCCTATCTCT-1,one
|
ACGACCCTATCTCT-1,one
|
||||||
@@ -545,7 +545,7 @@ AGTCTACTAGGGTG-1,two
|
|||||||
AGTCTACTTGCATG-1,one
|
AGTCTACTTGCATG-1,one
|
||||||
AGTCTTACACCACA-1,four
|
AGTCTTACACCACA-1,four
|
||||||
AGTCTTACTTCGCC-1,five
|
AGTCTTACTTCGCC-1,five
|
||||||
AGTCTTACTTCGGA-1,one
|
AGTCTTACTTCGGA-1,six
|
||||||
AGTGACTGCAACTG-1,one
|
AGTGACTGCAACTG-1,one
|
||||||
AGTGTTCTAACCTG-1,four
|
AGTGTTCTAACCTG-1,four
|
||||||
AGTGTTCTATAAGG-1,four
|
AGTGTTCTATAAGG-1,four
|
||||||
@@ -630,7 +630,7 @@ ATCAACCTTCTCTA-1,five
|
|||||||
ATCAACCTTTGTCT-1,five
|
ATCAACCTTTGTCT-1,five
|
||||||
ATCACACTTTGTCT-1,four
|
ATCACACTTTGTCT-1,four
|
||||||
ATCACGGATTTCGT-1,three
|
ATCACGGATTTCGT-1,three
|
||||||
ATCATCTGACACCA-1,one
|
ATCATCTGACACCA-1,six
|
||||||
ATCATGCTAGAGTA-1,five
|
ATCATGCTAGAGTA-1,five
|
||||||
ATCATGCTGAACCT-1,three
|
ATCATGCTGAACCT-1,three
|
||||||
ATCCAGGACGCTAA-1,one
|
ATCCAGGACGCTAA-1,one
|
||||||
@@ -745,7 +745,7 @@ ATTAGATGTTTCAC-1,four
|
|||||||
ATTATGGAATCTCT-1,four
|
ATTATGGAATCTCT-1,four
|
||||||
ATTCAAGAACGGGA-1,four
|
ATTCAAGAACGGGA-1,four
|
||||||
ATTCAAGACCTTTA-1,four
|
ATTCAAGACCTTTA-1,four
|
||||||
ATTCAGCTCATTGG-1,one
|
ATTCAGCTCATTGG-1,six
|
||||||
ATTCCAACCATTGG-1,two
|
ATTCCAACCATTGG-1,two
|
||||||
ATTCCAACTTAGGC-1,five
|
ATTCCAACTTAGGC-1,five
|
||||||
ATTCGACTCACTAG-1,four
|
ATTCGACTCACTAG-1,four
|
||||||
@@ -946,7 +946,7 @@ CATAACCTTCTCCG-1,four
|
|||||||
CATACTACCTCGAA-1,four
|
CATACTACCTCGAA-1,four
|
||||||
CATACTACCTGAAC-1,four
|
CATACTACCTGAAC-1,four
|
||||||
CATACTACGTACCA-1,two
|
CATACTACGTACCA-1,two
|
||||||
CATACTTGGGTTAC-1,four
|
CATACTTGGGTTAC-1,seven
|
||||||
CATAGTCTAATCGC-1,four
|
CATAGTCTAATCGC-1,four
|
||||||
CATAGTCTCACTTT-1,four
|
CATAGTCTCACTTT-1,four
|
||||||
CATATAGACTAAGC-1,unassigned
|
CATATAGACTAAGC-1,unassigned
|
||||||
@@ -1523,7 +1523,7 @@ GACCTCTGCATCAG-1,four
|
|||||||
GACGAACTCCCACT-1,four
|
GACGAACTCCCACT-1,four
|
||||||
GACGATTGCCAATG-1,three
|
GACGATTGCCAATG-1,three
|
||||||
GACGCCGACCTTCG-1,four
|
GACGCCGACCTTCG-1,four
|
||||||
GACGCTCTCTCTCG-1,one
|
GACGCTCTCTCTCG-1,six
|
||||||
GACGGCACACGGGA-1,five
|
GACGGCACACGGGA-1,five
|
||||||
GACGGCACGAGATA-1,two
|
GACGGCACGAGATA-1,two
|
||||||
GACGTAACCTATGG-1,four
|
GACGTAACCTATGG-1,four
|
||||||
@@ -1589,7 +1589,7 @@ GAGTGTTGTGGTCA-1,four
|
|||||||
GAGTTGTGCATGGT-1,unassigned
|
GAGTTGTGCATGGT-1,unassigned
|
||||||
GAGTTGTGCTGAGT-1,five
|
GAGTTGTGCTGAGT-1,five
|
||||||
GAGTTGTGGCGAGA-1,two
|
GAGTTGTGGCGAGA-1,two
|
||||||
GAGTTGTGGTAGCT-1,one
|
GAGTTGTGGTAGCT-1,six
|
||||||
GAGTTGTGTATGCG-1,one
|
GAGTTGTGTATGCG-1,one
|
||||||
GATAAGGAGAAACA-1,two
|
GATAAGGAGAAACA-1,two
|
||||||
GATAAGGATTCACT-1,two
|
GATAAGGATTCACT-1,two
|
||||||
@@ -1735,7 +1735,7 @@ GCGATATGGTGTTG-1,four
|
|||||||
GCGCACGAAGTCGT-1,four
|
GCGCACGAAGTCGT-1,four
|
||||||
GCGCACGACTTTAC-1,two
|
GCGCACGACTTTAC-1,two
|
||||||
GCGCATCTAGGTCT-1,four
|
GCGCATCTAGGTCT-1,four
|
||||||
GCGCATCTGGTTAC-1,one
|
GCGCATCTGGTTAC-1,six
|
||||||
GCGCATCTTCGATG-1,four
|
GCGCATCTTCGATG-1,four
|
||||||
GCGCATCTTGCTCC-1,five
|
GCGCATCTTGCTCC-1,five
|
||||||
GCGCATCTTTCTAC-1,four
|
GCGCATCTTTCTAC-1,four
|
||||||
@@ -1779,7 +1779,7 @@ GCTTAACTGCTGAT-1,four
|
|||||||
GCTTAACTTAGACC-1,two
|
GCTTAACTTAGACC-1,two
|
||||||
GCTTAACTTCAGTG-1,four
|
GCTTAACTTCAGTG-1,four
|
||||||
GGAACACTCACTTT-1,two
|
GGAACACTCACTTT-1,two
|
||||||
GGAACACTTCAGAC-1,one
|
GGAACACTTCAGAC-1,six
|
||||||
GGAACTACTACTTC-1,four
|
GGAACTACTACTTC-1,four
|
||||||
GGAACTTGAAGGTA-1,two
|
GGAACTTGAAGGTA-1,two
|
||||||
GGAACTTGAGAATG-1,four
|
GGAACTTGAGAATG-1,four
|
||||||
@@ -1841,7 +1841,7 @@ GGCAATACGTTTCT-1,three
|
|||||||
GGCACGTGGCTTAG-1,one
|
GGCACGTGGCTTAG-1,one
|
||||||
GGCACTCTTTTGTC-1,four
|
GGCACTCTTTTGTC-1,four
|
||||||
GGCATATGCTTATC-1,five
|
GGCATATGCTTATC-1,five
|
||||||
GGCATATGGGGAGT-1,one
|
GGCATATGGGGAGT-1,six
|
||||||
GGCATATGTGTGAC-1,unassigned
|
GGCATATGTGTGAC-1,unassigned
|
||||||
GGCCACGACAGAGG-1,four
|
GGCCACGACAGAGG-1,four
|
||||||
GGCCAGACTGGTTG-1,four
|
GGCCAGACTGGTTG-1,four
|
||||||
@@ -1977,7 +1977,7 @@ GTCACCTGCCTCCA-1,four
|
|||||||
GTCACCTGTCCCGT-1,four
|
GTCACCTGTCCCGT-1,four
|
||||||
GTCATACTAATCGC-1,three
|
GTCATACTAATCGC-1,three
|
||||||
GTCATACTGCGATT-1,five
|
GTCATACTGCGATT-1,five
|
||||||
GTCATACTTCGCCT-1,one
|
GTCATACTTCGCCT-1,six
|
||||||
GTCATACTTTACCT-1,five
|
GTCATACTTTACCT-1,five
|
||||||
GTCATACTTTGACG-1,four
|
GTCATACTTTGACG-1,four
|
||||||
GTCCAAGAAAAACG-1,four
|
GTCCAAGAAAAACG-1,four
|
||||||
@@ -2044,7 +2044,7 @@ TAAATCGATGAGGG-1,four
|
|||||||
TAACAATGTGCCCT-1,three
|
TAACAATGTGCCCT-1,three
|
||||||
TAACACCTTCGCTC-1,two
|
TAACACCTTCGCTC-1,two
|
||||||
TAACACCTTCGTAG-1,two
|
TAACACCTTCGTAG-1,two
|
||||||
TAACACCTTGTTTC-1,one
|
TAACACCTTGTTTC-1,six
|
||||||
TAACATGACACTAG-1,three
|
TAACATGACACTAG-1,three
|
||||||
TAACCGGACTTACT-1,four
|
TAACCGGACTTACT-1,four
|
||||||
TAACGTCTCAACCA-1,two
|
TAACGTCTCAACCA-1,two
|
||||||
@@ -2507,7 +2507,7 @@ TTACCATGTGTCTT-1,four
|
|||||||
TTACCATGTTGTGG-1,three
|
TTACCATGTTGTGG-1,three
|
||||||
TTACGACTGAGAGC-1,four
|
TTACGACTGAGAGC-1,four
|
||||||
TTACGACTTGACAC-1,two
|
TTACGACTTGACAC-1,two
|
||||||
TTACGTACGTTCAG-1,one
|
TTACGTACGTTCAG-1,six
|
||||||
TTACTCGAAGAATG-1,two
|
TTACTCGAAGAATG-1,two
|
||||||
TTACTCGACGCAAT-1,unassigned
|
TTACTCGACGCAAT-1,unassigned
|
||||||
TTACTCGAGGGTGA-1,four
|
TTACTCGAGGGTGA-1,four
|
||||||
|
|||||||
|
Reference in New Issue
Block a user