Skip to content

Commit

Permalink
Backmerge: #4109 - Macro: UI support export sequence (#4294)
Browse files Browse the repository at this point in the history
  • Loading branch information
rrodionov91 authored Mar 21, 2024
1 parent 960d557 commit cbcc91c
Show file tree
Hide file tree
Showing 34 changed files with 245 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import { test, expect } from '@playwright/test';
import {
TopPanelButton,
openFileAndAddToCanvasMacro,
selectTopPanelButton,
takeEditorScreenshot,
waitForPageInit,
saveToFile,
openFile,
receiveFileComparisonData,
selectOptionInDropdown,
pressButton,
selectSnakeLayoutModeTool,
chooseFileFormat,
readFileContents,
getSequence,
moveMouseAway,
} from '@utils';
import { turnOnMacromoleculesEditor } from '@utils/macromolecules';

test.describe('Import-Saving .seq Files', () => {
test.beforeEach(async ({ page }) => {
await waitForPageInit(page);
await turnOnMacromoleculesEditor(page);
});

const sequenceFileTypes = ['DNA', 'RNA', 'Peptide'] as const;

for (const fileType of sequenceFileTypes) {
test(`Import .seq ${fileType} file`, async ({ page }) => {
await openFileAndAddToCanvasMacro(
`Sequence/sequence-${fileType.toLowerCase()}.seq`,
page,
fileType,
);
await moveMouseAway(page);
await takeEditorScreenshot(page);
});
}

test('Import incorrect data', async ({ page }) => {
const randomText = 'asjfnsalkfl';
await selectTopPanelButton(TopPanelButton.Open, page);
await page.getByTestId('paste-from-clipboard-button').click();
await page.getByTestId('open-structure-textarea').fill(randomText);
await chooseFileFormat(page, 'Sequence');
await page.getByTestId('add-to-canvas-button').click();
await takeEditorScreenshot(page);
});

test('Check import of .ket file and save in .seq format', async ({
page,
}) => {
await openFileAndAddToCanvasMacro('KET/rna-a.ket', page);
const expectedFile = await getSequence(page);
await saveToFile('Sequence/sequence-rna-a-expected.seq', expectedFile);

const METADATA_STRING_INDEX = [1];

const { fileExpected: sequenceFileExpected, file: sequenceFile } =
await receiveFileComparisonData({
page,
expectedFileName:
'tests/test-data/Sequence/sequence-rna-a-expected.seq',
metaDataIndexes: METADATA_STRING_INDEX,
});

expect(sequenceFile).toEqual(sequenceFileExpected);

await takeEditorScreenshot(page);
});

test('Check that empty file can be saved in .seq format', async ({
page,
}) => {
const expectedFile = await getSequence(page);
await saveToFile('Sequence/sequence-empty.seq', expectedFile);

const METADATA_STRING_INDEX = [1];

const { fileExpected: sequenceFileExpected, file: sequenceFile } =
await receiveFileComparisonData({
page,
expectedFileName: 'tests/test-data/Sequence/sequence-empty.seq',
metaDataIndexes: METADATA_STRING_INDEX,
});

expect(sequenceFile).toEqual(sequenceFileExpected);
});

test('Check that system does not let importing empty .seq file', async ({
page,
}) => {
await selectTopPanelButton(TopPanelButton.Open, page);
await openFile('Sequence/sequence-empty.seq', page);
await page.getByText('Add to Canvas').isDisabled();
});

test('Check that system does not let uploading corrupted .seq file', async ({
page,
}) => {
await selectTopPanelButton(TopPanelButton.Open, page);

const filename = 'Sequence/sequence-corrupted.seq';
await openFile(filename, page);
await selectOptionInDropdown(filename, page);
await pressButton(page, 'Add to Canvas');
await takeEditorScreenshot(page);
});

test('Validate correct displaying of snake viewed RNA chain loaded from .seq file format', async ({
page,
}) => {
await openFileAndAddToCanvasMacro(
'Sequence/sequence-snake-mode-rna.seq',
page,
);
await selectSnakeLayoutModeTool(page);
await takeEditorScreenshot(page);
});

test('Check that you can save snake viewed chain of peptides in a .seq file', async ({
page,
}) => {
await openFileAndAddToCanvasMacro(
'Sequence/sequence-snake-mode-rna.seq',
page,
);
await selectSnakeLayoutModeTool(page);
const expectedFile = await getSequence(page);
await saveToFile(
'Sequence/sequence-snake-mode-rna-expected.seq',
expectedFile,
);

const METADATA_STRING_INDEX = [1];

const { fileExpected: sequenceFileExpected, file: sequenceFile } =
await receiveFileComparisonData({
page,
expectedFileName:
'tests/test-data/Sequence/sequence-snake-mode-rna-expected.seq',
metaDataIndexes: METADATA_STRING_INDEX,
});

expect(sequenceFile).toEqual(sequenceFileExpected);
});

test('Should open .ket file and modify to .seq format in save modal textarea', async ({
page,
}) => {
await openFileAndAddToCanvasMacro('KET/rna-a.ket', page);
await selectTopPanelButton(TopPanelButton.Save, page);
await chooseFileFormat(page, 'Sequence');
await page
.getByTestId('dropdown-select')
.getByRole('combobox')
.allInnerTexts();

const textArea = page.getByTestId('preview-area-text');
const file = await readFileContents(
'tests/test-data/Sequence/sequence-rna-a.seq',
);
const expectedData = file;
const valueInTextarea = await textArea.inputValue();
expect(valueInTextarea).toBe(expectedData);
});

// Should not convert to Sequence type in case of there are more than one monomer type
test('Should not convert .ket file with RNA and Peptide to .seq format in save modal', async ({
page,
}) => {
await openFileAndAddToCanvasMacro('KET/rna-and-peptide.ket', page);
await selectTopPanelButton(TopPanelButton.Save, page);
await chooseFileFormat(page, 'Sequence');

await takeEditorScreenshot(page);
});

// Should not convert to Sequence type in case of there is any CHEM
test('Should not convert .ket file with CHEMs to .seq format in save modal', async ({
page,
}) => {
await openFileAndAddToCanvasMacro('KET/chems-not-connected.ket', page);
await selectTopPanelButton(TopPanelButton.Save, page);
await chooseFileFormat(page, 'Sequence');

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.
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
@@ -0,0 +1 @@
12w12r23e32e33
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TCCAGCCATTGTGATTGCATGTTCCCAGGTTGAGGTAGTAGGTTGTATAGTTTAGAATTACATCAAGGGAGATAACTGTACAGCCTCCTAGCTTTCCTTGGGTCTTGCACAAAGCAACATGGCGAGA
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MIIGYVIGQATTQEALILAERPVRLGTYVVLEYDNVKALGLITNVTRGSPLLDDNMNDIEIVQRLKQFNNSIPVYTKAKVKLLCDMNNHFLMPDIPPFAGTPAREAEDEELKSIYSQDGQIRIGSLIGKNVEVKLNINSFARHLAILAATGSGKSNTVAVLSQRISELGGSVLIFDYHGEYYDSDIKNLNRIEPKLNPLYMTPREFSTLLEIRENAIIQYRILRRAFIKVTNGIRAALAAGQIPFSTLNSQFYELMADALETQGNSDKKSSAKDEVLNKFEEFMDRYSNVIDLTSSDIIEKVKRGKVNVVSLTQLDEDSMDAVVSHYLRRILDSRKDFKRSKNSGLKFPIIAVIEEAHVFLSKNENTLTKYWASRIAREGRKFGVGLTIVSQRPKGLDENILSQMTNKIILKIIEPTDKKYILESSDNLSEDLAEQLSSLDVGEAIIIGKIVKLPAVVKIDMFEGKLLGSDPDMIGEWKKVAASEKIAKGFADFGTEIGD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
UCCAGCCAUUGUGAUUGCAUGUUCCCAGGUUGAGGUAGUAGGUUGUAUAGUUUAGAAUUACAUCAAGGGAGAUAACUGUACAGCCUCCUAGCUUUCCUUGGGUCUUGCACAAAGCAACAUGGCGAGA
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CCCCCCCCCCCCCCCCCCCCCCCCCCC
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CCCCCCCCCCCCCCCCCCCCCCCCCCC
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const GetFileMethod: Record<string, keyof Ketcher> = {
inchi: 'getInchi',
sdf: 'getSdf',
fasta: 'getFasta',
seq: 'getSequence',
} as const;

type KetcherApiFunction = (format?: string) => Promise<string>;
Expand Down
4 changes: 4 additions & 0 deletions ketcher-autotests/tests/utils/formats/formats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export async function getFasta(page: Page): Promise<string> {
return await page.evaluate(() => window.ketcher.getFasta());
}

export async function getSequence(page: Page): Promise<string> {
return await page.evaluate(() => window.ketcher.getSequence());
}

export async function getCml(page: Page): Promise<string> {
return await page.evaluate(() => window.ketcher.getCml());
}
Expand Down
2 changes: 1 addition & 1 deletion ketcher-autotests/tests/utils/macromolecules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export async function scrollDown(page: Page, scrollDelta: number) {

export async function chooseFileFormat(
page: Page,
fileFomat: 'Ket' | 'MDL Molfile V3000' | 'FASTA',
fileFomat: 'Ket' | 'MDL Molfile V3000' | 'FASTA' | 'Sequence',
) {
await page.getByTestId('dropdown-select').click();
await waitForSpinnerFinishedWork(page, async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ const formatProperties: FormatPropertiesMap = {
['.fasta'],
true,
),
sequence: new SupportedFormatProperties(
'SEQUENCE',
ChemicalMimeType.SEQUENCE,
['.seq'],
false,
{},
),
unknown: new SupportedFormatProperties(
'Unknown',
ChemicalMimeType.UNKNOWN,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export enum SupportedFormat {
sdf = 'sdf',
sdfV3000 = 'sdfV3000',
fasta = 'fasta',
sequence = 'sequence',
unknown = 'unknown',
}

Expand Down
9 changes: 9 additions & 0 deletions packages/ketcher-core/src/application/ketcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,15 @@ export class Ketcher {
);
}

getSequence(): Promise<string> {
return getStructure(
SupportedFormat.sequence,
this.#formatterFactory,
this.#editor.struct(),
CoreEditor.provideEditorInstance()?.drawingEntitiesManager,
);
}

getSmarts(): Promise<string> {
if (window.isPolymerEditorTurnedOn) {
throw new Error('SMARTS format is not available in macro mode');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export enum ChemicalMimeType {
UNKNOWN = 'chemical/x-unknown',
SDF = 'chemical/x-sdf',
FASTA = 'fasta',
SEQUENCE = 'chemical/x-sequence',
}

export interface WithStruct {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const LoadContainer = styled.div`
}
`;
export const LoadingCircles = () => (
<LoadContainer>
<LoadContainer className="loading-spinner">
<span />
<span />
<span />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Object {
"baseElement": <body>
<div>
<div
class="css-rmxyg7"
class="loading-spinner css-rmxyg7"
>
<span />
<span />
Expand All @@ -16,7 +16,7 @@ Object {
</body>,
"container": <div>
<div
class="css-rmxyg7"
class="loading-spinner css-rmxyg7"
>
<span />
<span />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Object {
class="css-1ro7qq4"
>
<div
class="css-rmxyg7"
class="loading-spinner css-rmxyg7"
>
<span />
<span />
Expand All @@ -23,7 +23,7 @@ Object {
class="css-1ro7qq4"
>
<div
class="css-rmxyg7"
class="loading-spinner css-rmxyg7"
>
<span />
<span />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ import { openErrorModal } from 'state/modal';
const options: Array<Option> = [
{ id: 'ket', label: 'Ket' },
{ id: 'mol', label: 'MDL Molfile V3000' },
{ id: 'sequence', label: 'Sequence' },
{ id: 'fasta', label: 'FASTA' },
];

const formatDetector = {
mol: ChemicalMimeType.Mol,
fasta: ChemicalMimeType.FASTA,
sequence: ChemicalMimeType.SEQUENCE,
};

const StyledModal = styled(Modal)({
Expand Down Expand Up @@ -95,7 +97,7 @@ export const Save = ({

try {
setIsLoading(true);
if (fileFormat === 'fasta') {
if (fileFormat === 'fasta' || fileFormat === 'sequence') {
const isValid =
editor.drawingEntitiesManager.validateIfApplicableForFasta();
if (!isValid) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
SupportedFormatProperties,
} from './supportedFormatProperties';

export type SupportedFormats = 'mol' | 'ket' | 'fasta';
export type SupportedFormats = 'mol' | 'ket' | 'fasta' | 'sequence';

type FormatProperties = {
[key in SupportedFormats]: SupportedFormatProperties;
Expand All @@ -44,7 +44,14 @@ const formatProperties: FormatProperties = {
'FASTA',
ChemicalMimeType.Fasta,
['.fasta'],
true,
false,
{},
),
sequence: new SupportedFormatProperties(
'SEQUENCE',
ChemicalMimeType.Sequence,
['.seq'],
false,
{},
),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export enum ChemicalMimeType {
Mol = 'chemical/x-mdl-molfile',
Helm = 'chemical/x-helm',
Fasta = 'fasta',
Sequence = 'sequence',
}

interface SupportedFormatPropertiesOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export enum SupportedFormat {
CDXML = 'cdxml',
SDF = 'sdf',
FASTA = 'fasta',
SEQUENCE = 'sequence',
}

export interface WithStruct {
Expand Down
Loading

0 comments on commit cbcc91c

Please sign in to comment.