Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Macro: #2414 - macro undo and redo tool #3613

Merged
merged 24 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
13f940e
Menu buttons undo/redo, EditorHistory class added, execute-invert com…
SashaGraves Nov 7, 2023
7c85adb
added to history, undo/redo for: Add Bond, Delete Bond, Add Monomer
SashaGraves Nov 8, 2023
2c7c7cb
Merge branch 'master' into 2414-Macro-Undo-and-Redo-tool
SashaGraves Nov 8, 2023
5c06197
monomers add - delete: undo/redo; add Erase and Clear to histore
SashaGraves Nov 9, 2023
93959e9
add-delete bond: undo redo (with issues)
SashaGraves Nov 9, 2023
54b3669
fix finishBondCreation
SashaGraves Nov 10, 2023
267c9e8
Merge commit '80bc156cbf330bfd2765d4c73cefb47f963c163f' into 2414-Mac…
SashaGraves Nov 13, 2023
6d49cf3
Revert "fix finishBondCreation"
SashaGraves Nov 13, 2023
66337a0
Revert "add-delete bond: undo redo (with issues)"
SashaGraves Nov 13, 2023
9492ce0
Revert "monomers add - delete: undo/redo; add Erase and Clear to hist…
SashaGraves Nov 13, 2023
38227e4
Merge commit 'c973292764bdfb92c014afe05ef1320a89128c38' into 2414-Mac…
SashaGraves Nov 16, 2023
8d7eced
Revert "Revert "monomers add - delete: undo/redo; add Erase and Clear…
rrodionov91 Nov 20, 2023
36fcb4b
Revert "Revert "add-delete bond: undo redo (with issues)""
rrodionov91 Nov 20, 2023
f957277
Revert "Revert "fix finishBondCreation""
rrodionov91 Nov 20, 2023
4c25477
- fixed/added undo/redo for snake mode layout, rna/peptides/bonds ope…
rrodionov91 Nov 21, 2023
99d03a6
Merge remote-tracking branch 'origin/master' into 2414-Macro-Undo-and…
rrodionov91 Nov 22, 2023
60f48ff
- fixed bugs
rrodionov91 Nov 22, 2023
cd880dd
- fixed bugs
rrodionov91 Nov 22, 2023
855f27f
- fixed bug with selection overlapping the bond
rrodionov91 Nov 23, 2023
d7ed61e
Merge remote-tracking branch 'origin/master' into 2414-Macro-Undo-and…
rrodionov91 Nov 23, 2023
8a2801d
- fixes after self-review
rrodionov91 Nov 23, 2023
6d917a8
- fixes after review
rrodionov91 Nov 24, 2023
bd9e22d
Merge remote-tracking branch 'origin/master' into 2414-Macro-Undo-and…
rrodionov91 Nov 24, 2023
55f8379
- updated screenshots
rrodionov91 Nov 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { test } from '@playwright/test';
import {
addMonomerToCanvas,
dragMouseTo,
selectEraseTool,
selectRectangleArea,
selectRectangleSelectionTool,
Expand All @@ -11,6 +10,7 @@ import {
} from '@utils';
import { turnOnMacromoleculesEditor } from '@utils/macromolecules';
import { bondTwoMonomers } from '@utils/macromolecules/polymerBond';
import { moveMonomer } from '@utils/macromolecules/monomer';
/* eslint-disable no-magic-numbers */

test.describe('Rectangle Selection Tool', () => {
Expand Down Expand Up @@ -129,10 +129,7 @@ test.describe('Rectangle Selection Tool', () => {

await takeEditorScreenshot(page);

// Move selected monomer
await selectRectangleSelectionTool(page);
await page.mouse.click(400, 400);
await dragMouseTo(200, 400, page);
await moveMonomer(page, peptide2, 200, 400);

await takeEditorScreenshot(page);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { Locator, test } from '@playwright/test';
import {
addMonomerToCanvas,
clickRedo,
clickUndo,
selectSingleBondTool,
selectSnakeBondTool,
takeEditorScreenshot,
waitForPageInit,
} from '@utils';
import {
hideMonomerPreview,
turnOnMacromoleculesEditor,
} from '@utils/macromolecules';
import { bondTwoMonomers } from '@utils/macromolecules/polymerBond';
import { moveMonomer } from '@utils/macromolecules/monomer';
/* eslint-disable no-magic-numbers */

test.describe('Undo Redo', () => {
let peptide1: Locator;
let peptide2: Locator;
test.beforeEach(async ({ page }) => {
await waitForPageInit(page);
await turnOnMacromoleculesEditor(page);
const MONOMER_NAME = 'Tza___3-thiazolylalanine';
const MONOMER_ALIAS = 'Tza';

peptide1 = await addMonomerToCanvas(
page,
MONOMER_NAME,
MONOMER_ALIAS,
300,
300,
0,
);
peptide2 = await addMonomerToCanvas(
page,
MONOMER_NAME,
MONOMER_ALIAS,
400,
300,
1,
);
const peptide3 = await addMonomerToCanvas(
page,
MONOMER_NAME,
MONOMER_ALIAS,
500,
300,
2,
);

// Select bond tool
await selectSingleBondTool(page);

// Create bonds between peptides
await bondTwoMonomers(page, peptide1, peptide2);
await bondTwoMonomers(page, peptide3, peptide2);

await hideMonomerPreview(page);
});

test('Undo redo for monomers and bonds addition', async ({ page }) => {
/*
Description: Add monomers and bonds and do undo redo
*/

// check that history pointer stops on last operation
await clickRedo(page);
await clickRedo(page);

// check undo
await clickUndo(page);
await clickUndo(page);
await takeEditorScreenshot(page);

// check that history pointer stops on first operation
await clickUndo(page);
await clickUndo(page);
await clickUndo(page);
await clickUndo(page);
await clickUndo(page);

// check redo
await clickRedo(page);
await takeEditorScreenshot(page);
});

test('Undo redo for snake mode layout', async ({ page }) => {
/*
Description: Add monomers and bonds, activate snake mode and do undo redo
*/

await selectSnakeBondTool(page);
await clickUndo(page);
await takeEditorScreenshot(page);
});

test('Undo redo for monomers movement', async ({ page }) => {
/*
Description: Move monomers and do undo redo
*/

await moveMonomer(page, peptide1, 500, 500);
await moveMonomer(page, peptide2, 600, 600);
await moveMonomer(page, peptide2, 400, 400);
await clickUndo(page);
await clickUndo(page);
await takeEditorScreenshot(page);
await clickRedo(page);
await takeEditorScreenshot(page);
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions ketcher-autotests/tests/utils/canvas/tools/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ export async function selectRectangleSelectionTool(page: Page) {
await bondToolButton.click();
}

// undo/redo heplers currently used for macromolecules editor because buttons are in different panel
export async function clickUndo(page: Page) {
const undoButton = page.getByTestId('undo-button');
await undoButton.click();
}

export async function clickRedo(page: Page) {
const redoButton = page.getByTestId('redo-button');
await redoButton.click();
}

export async function selectRectangleArea(
page: Page,
startX: number,
Expand Down
13 changes: 13 additions & 0 deletions ketcher-autotests/tests/utils/macromolecules/monomer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Locator, Page } from '@playwright/test';
import { dragMouseTo, selectRectangleSelectionTool } from '@utils';

export async function moveMonomer(
page: Page,
monomer: Locator,
x: number,
y: number,
) {
await selectRectangleSelectionTool(page);
await monomer.click();
await dragMouseTo(x, y, page);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { CoreEditor, EditorHistory } from 'application/editor';
import { createPolymerEditorCanvas } from '../../helpers/dom';
import { Command } from 'domain/entities/Command';

describe('EditorHistory', () => {
let canvas;
let editor: CoreEditor;
let history: EditorHistory;
beforeEach(() => {
canvas = createPolymerEditorCanvas();
editor = new CoreEditor({ theme: {}, canvas });
history = new EditorHistory(editor);
});

afterEach(() => {
history.destroy();
});

it('should be a singletone', () => {
const historyInstance2 = new EditorHistory(editor);
expect(history).toBe(historyInstance2);
});

it('should create another instance after destroy', () => {
history.destroy();
const historyInstance2 = new EditorHistory(editor);
expect(history).not.toBe(historyInstance2);
});

it('should add commands into history stack', () => {
history.update(new Command());
history.update(new Command());
expect(history.historyStack.length).toEqual(2);
});

it('should move pointer when undo/redo methods called', () => {
history.update(new Command());
history.update(new Command());
expect(history.historyPointer).toEqual(2);
history.redo();
expect(history.historyPointer).toEqual(2);
history.undo();
expect(history.historyPointer).toEqual(1);
history.undo();
expect(history.historyPointer).toEqual(0);
history.undo();
expect(history.historyPointer).toEqual(0);
history.redo();
expect(history.historyPointer).toEqual(1);
});

it('should have stack maximum size equal 32 commands', () => {
for (let i = 0; i < 40; i++) {
history.update(new Command());
}
expect(history.historyStack.length).toEqual(32);
expect(history.historyPointer).toEqual(32);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,28 @@ jest.mock('d3', () => {
style() {
return this;
},
on() {},
on() {
return this;
},
append() {
return this;
},
data() {
return this;
},
text() {
return this;
},
node() {
return {
getBBox() {
return {};
},
getBoundingClientRect() {
return {};
},
};
},
};
},
ZoomTransform: jest.fn().mockImplementation(() => {
Expand Down Expand Up @@ -67,6 +88,10 @@ global.ResizeObserver = jest.fn().mockImplementation(() => ({
}));

describe('Select Rectangle Tool', () => {
afterEach(() => {
jest.clearAllMocks();
});

it('should select drawing entity on mousedown', () => {
const polymerBond = getFinishedPolymerBond(0, 0, 10, 10);
const event = {
Expand Down Expand Up @@ -111,11 +136,15 @@ describe('Select Rectangle Tool', () => {
new Vec2(0, 0),
);
editor.renderersContainer.update(modelChanges);

const peptide = Array.from(editor.drawingEntitiesManager.monomers)[0][1];
const onMove = jest.fn();
jest
.spyOn(BaseMonomerRenderer.prototype, 'moveSelection')
.mockImplementation(onMove);
jest
.spyOn(PeptideRenderer.prototype, 'drawSelection')
.mockImplementation(() => {});

const selectRectangleTool = new SelectRectangle(editor);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import { PolymerBond } from 'domain/entities/PolymerBond';
import { DrawingEntity } from 'domain/entities/DrawingEntity';
import {
DrawingEntityHoverOperation,
DrawingEntityMoveOperation,
DrawingEntitySelectOperation,
} from 'application/editor/operations/drawingEntity';
import {
MonomerAddOperation,
MonomerDeleteOperation,
MonomerHoverOperation,
MonomerMoveOperation,
} from 'application/editor/operations/monomer';
import { RenderersManager } from 'application/render/renderers/RenderersManager';

describe('Drawing Entities Manager', () => {
it('should create monomer', () => {
Expand All @@ -33,11 +34,12 @@ describe('Drawing Entities Manager', () => {

it('should create polymer bond', () => {
const drawingEntitiesManager = new DrawingEntitiesManager();
const { command, polymerBond } = drawingEntitiesManager.addPolymerBond(
new Peptide(peptideMonomerItem),
new Vec2(0, 0),
new Vec2(10, 10),
);
const { command, polymerBond } =
drawingEntitiesManager.startPolymerBondCreation(
new Peptide(peptideMonomerItem),
new Vec2(0, 0),
new Vec2(10, 10),
);
expect(command.operations.length).toEqual(1);
expect(command.operations[0]).toBeInstanceOf(PolymerBondAddOperation);
expect(polymerBond).toBeInstanceOf(PolymerBond);
Expand All @@ -55,7 +57,7 @@ describe('Drawing Entities Manager', () => {
secondPeptide.attachmentPointsToBonds = { R2: null };
secondPeptide.potentialAttachmentPointsToBonds = { R2: null };

const { polymerBond } = drawingEntitiesManager.addPolymerBond(
const { polymerBond } = drawingEntitiesManager.startPolymerBondCreation(
firstPeptide,
new Vec2(0, 0),
new Vec2(10, 10),
Expand Down Expand Up @@ -85,18 +87,21 @@ describe('Drawing Entities Manager', () => {

it('should delete peptide', () => {
const drawingEntitiesManager = new DrawingEntitiesManager();
const renderersManager = new RenderersManager({ theme: {} });
drawingEntitiesManager.addMonomer(peptideMonomerItem, new Vec2(0, 0));
const peptide = Array.from(drawingEntitiesManager.monomers)[0][1];
expect(peptide).toBeInstanceOf(Peptide);
const command = drawingEntitiesManager.deleteMonomer(peptide);
renderersManager.update(command);
expect(command.operations.length).toEqual(1);
expect(command.operations[0]).toBeInstanceOf(MonomerDeleteOperation);
expect(drawingEntitiesManager.monomers.size).toEqual(0);
});

it('should delete polymer bond', () => {
const drawingEntitiesManager = new DrawingEntitiesManager();
const { polymerBond } = drawingEntitiesManager.addPolymerBond(
const renderersManager = new RenderersManager({ theme: {} });
const { polymerBond } = drawingEntitiesManager.startPolymerBondCreation(
new Peptide(peptideMonomerItem),
new Vec2(0, 0),
new Vec2(10, 10),
Expand All @@ -105,6 +110,7 @@ describe('Drawing Entities Manager', () => {
Array.from(drawingEntitiesManager.polymerBonds)[0][1],
).toBeInstanceOf(PolymerBond);
const command = drawingEntitiesManager.deletePolymerBond(polymerBond);
renderersManager.update(command);
expect(command.operations.length).toEqual(1);
expect(command.operations[0]).toBeInstanceOf(PolymerBondDeleteOperation);
expect(drawingEntitiesManager.polymerBonds.size).toEqual(0);
Expand All @@ -121,15 +127,19 @@ describe('Drawing Entities Manager', () => {

it('should move peptide', () => {
const drawingEntitiesManager = new DrawingEntitiesManager();
const peptide = new Peptide(peptideMonomerItem);
const command = drawingEntitiesManager.moveMonomer(
peptide,
const renderersManager = new RenderersManager({ theme: {} });
jest.spyOn(renderersManager, 'moveDrawingEntity').mockImplementation();
drawingEntitiesManager.addMonomer(peptideMonomerItem, new Vec2(0, 0));
const peptide = Array.from(drawingEntitiesManager.monomers)[0][1];
peptide.turnOnSelection();
const command = drawingEntitiesManager.moveSelectedDrawingEntities(
new Vec2(100, 200),
);
renderersManager.update(command);
expect(peptide.position.x).toEqual(100);
expect(peptide.position.y).toEqual(200);
expect(command.operations.length).toEqual(1);
expect(command.operations[0]).toBeInstanceOf(MonomerMoveOperation);
expect(command.operations[0]).toBeInstanceOf(DrawingEntityMoveOperation);
});

it('should hover drawing entity', () => {
Expand Down
Loading
Loading