diff --git a/packages/ove/cypress/e2e/unit/getStructuredBases.spec.js b/packages/ove/cypress/e2e/unit/getStructuredBases.spec.js new file mode 100644 index 00000000..da69bbbf --- /dev/null +++ b/packages/ove/cypress/e2e/unit/getStructuredBases.spec.js @@ -0,0 +1,192 @@ +import { getStructuredBases } from "../../../src/RowItem/StackedAnnotations/getStructuredBases"; + +describe("getStructuredBases", () => { + it("works for fully-overlapping non origin-spanning case", () => { + const fullSequence = "ACGTCCACGT"; + let primerSeq = "ACGT"; + const start = 0; + const end = 3; + let { allBasesWithMetaData } = getStructuredBases({ + annotationRange: { start, end }, + forward: true, + bases: primerSeq, + start: start, + end: end, + fullSequence, + primerBindsOn: "3prime", + sequenceLength: fullSequence.length + }); + + let expected = [true, true, true, true]; + assert.deepEqual( + allBasesWithMetaData.map(({ isMatch }) => isMatch), + expected + ); + + primerSeq = "CCGT"; + ({ allBasesWithMetaData } = getStructuredBases({ + annotationRange: { start, end }, + forward: true, + bases: primerSeq, + start: start, + end: end, + fullSequence, + primerBindsOn: "3prime", + sequenceLength: fullSequence.length + })); + expected = [false, true, true, true]; + assert.deepEqual( + allBasesWithMetaData.map(({ isMatch }) => isMatch), + expected + ); + }); + it("works for non fully-overlapping non origin-spanning case binding on 3prime", () => { + const fullSequence = "ACGTCCACGT"; + let primerSeq = "aaaaACGT"; + const start = 0; + const end = 3; + let { allBasesWithMetaData } = getStructuredBases({ + annotationRange: { start, end }, + forward: true, + bases: primerSeq, + start: start, + end: end, + fullSequence, + primerBindsOn: "3prime", + sequenceLength: fullSequence.length + }); + + let expected = [false, false, false, false, true, true, true, true]; + assert.deepEqual( + allBasesWithMetaData.map(({ isMatch }) => isMatch), + expected + ); + expected = [ + undefined, + undefined, + undefined, + undefined, + false, + false, + false, + false + ]; + assert.deepEqual( + allBasesWithMetaData.map(({ isAmbiguousMatch }) => isAmbiguousMatch), + expected + ); + + primerSeq = "aaaaCCGT"; + ({ allBasesWithMetaData } = getStructuredBases({ + annotationRange: { start, end }, + forward: true, + bases: primerSeq, + start: start, + end: end, + fullSequence, + primerBindsOn: "3prime", + sequenceLength: fullSequence.length + })); + expected = [false, false, false, false, false, true, true, true]; + assert.deepEqual( + allBasesWithMetaData.map(({ isMatch }) => isMatch), + expected + ); + expected = [ + undefined, + undefined, + undefined, + undefined, + false, + false, + false, + false + ]; + assert.deepEqual( + allBasesWithMetaData.map(({ isAmbiguousMatch }) => isAmbiguousMatch), + expected + ); + }); + it("works for non fully-overlapping non origin-spanning case binding on 5prime", () => { + const fullSequence = "ACGTCCACGT"; + let primerSeq = "ACGTaaaa"; + const start = 0; + const end = 3; + let { allBasesWithMetaData } = getStructuredBases({ + annotationRange: { start, end }, + forward: true, + bases: primerSeq, + start: start, + end: end, + fullSequence, + primerBindsOn: "5prime", + sequenceLength: fullSequence.length + }); + + assert.deepEqual( + allBasesWithMetaData.map(({ isMatch }) => isMatch), + [true, true, true, true, false, false, false, false] + ); + assert.deepEqual( + allBasesWithMetaData.map(({ isAmbiguousMatch }) => isAmbiguousMatch), + [false, false, false, false, undefined, undefined, undefined, undefined] + ); + + primerSeq = "CCGTaaaa"; + ({ allBasesWithMetaData } = getStructuredBases({ + annotationRange: { start, end }, + forward: true, + bases: primerSeq, + start: start, + end: end, + fullSequence, + primerBindsOn: "5prime", + sequenceLength: fullSequence.length + })); + assert.deepEqual( + allBasesWithMetaData.map(({ isMatch }) => isMatch), + [false, true, true, true, false, false, false, false] + ); + assert.deepEqual( + allBasesWithMetaData.map(({ isAmbiguousMatch }) => isAmbiguousMatch), + [false, false, false, false, undefined, undefined, undefined, undefined] + ); + }); + it("works for origin-spanning case", () => { + const fullSequence = "ACGTCCACGT"; + let primerSeq = "TACGT"; + const start = 9; + const end = 3; + let { allBasesWithMetaData } = getStructuredBases({ + annotationRange: { start, end }, + forward: true, + bases: primerSeq, + start: start, + end: end, + fullSequence, + primerBindsOn: "3prime", + sequenceLength: fullSequence.length + }); + let expected = [true, true, true, true, true]; + assert.deepEqual( + allBasesWithMetaData.map(({ isMatch }) => isMatch), + expected + ); + primerSeq = "AACGT"; + expected = [false, true, true, true, true]; + ({ allBasesWithMetaData } = getStructuredBases({ + annotationRange: { start, end }, + forward: true, + bases: primerSeq, + start: start, + end: end, + fullSequence, + primerBindsOn: "3prime", + sequenceLength: fullSequence.length + })); + assert.deepEqual( + allBasesWithMetaData.map(({ isMatch }) => isMatch), + expected + ); + }); +}); diff --git a/packages/ove/demo/src/exampleData/exampleSequenceData.js b/packages/ove/demo/src/exampleData/exampleSequenceData.js index 7edb6070..d9f2b477 100644 --- a/packages/ove/demo/src/exampleData/exampleSequenceData.js +++ b/packages/ove/demo/src/exampleData/exampleSequenceData.js @@ -28,28 +28,98 @@ export default { } ], primers: [ + // { + // name: "Example Primer 1", + // start: 280, + // end: 300, + // type: "primer_bind", + // forward: true + // }, + // { + // name: "Blue primer", + // start: 1, + // end: 20, + // type: "primer_bind", + // forward: true, + // color: "blue" + // }, + // { + // name: "Red primer", + // start: 21, + // end: 30, + // type: "primer_bind", + // forward: true, + // color: "red" + // }, + // { + // name: "Normal primer", + // start: 1, + // end: 4, + // type: "primer_bind", + // forward: true, + // color: "blue", + // primerBindsOn: '3prime', + // bases: "acgt" + // }, { - name: "Example Primer 1", - start: 280, - end: 300, + name: "Origin-spanning primer", + start: 5297, + end: 4, type: "primer_bind", - forward: true + forward: true, + color: "red", + primerBindsOn: "3prime", + bases: "tcgacgt" }, { - name: "Blue primer", - start: 1, - end: 20, + name: "Origin-spanning reverse primer", + start: 5297, + end: 4, + type: "primer_bind", + forward: false, + color: "red", + primerBindsOn: "3prime", + bases: "acgtcga" + }, + { + name: "Left tail primer", + start: 7, + end: 10, type: "primer_bind", forward: true, - color: "blue" + color: "red", + primerBindsOn: "3prime", + bases: "aaaatatg" }, { - name: "Red primer", - start: 21, - end: 30, + name: "Right tail primer", + start: 11, + end: 14, type: "primer_bind", forward: true, - color: "red" + color: "red", + primerBindsOn: "5prime", + bases: "acaaaaaa" + }, + { + name: "Left tail primer - reverse", + start: 21, + end: 24, + type: "primer_bind", + forward: false, + color: "red", + primerBindsOn: "3prime", + bases: "aaaaagcc" + }, + { + name: "Right tail primer - reverse", + start: 40, + end: 43, + type: "primer_bind", + forward: false, + color: "red", + primerBindsOn: "5prime", + bases: "aagaaaaa" } ], afeatures: [ diff --git a/packages/ove/src/RowItem/StackedAnnotations/getStructuredBases.js b/packages/ove/src/RowItem/StackedAnnotations/getStructuredBases.js index 13441fa1..a1ed494d 100644 --- a/packages/ove/src/RowItem/StackedAnnotations/getStructuredBases.js +++ b/packages/ove/src/RowItem/StackedAnnotations/getStructuredBases.js @@ -4,7 +4,7 @@ import { getComplementSequenceString } from "@teselagen/sequence-utils"; import { bioData } from "@teselagen/sequence-utils"; const { ambiguous_dna_values } = bioData; export function getStructuredBases({ - annotationRange, + // annotationRange, forward, bases = "", start, @@ -29,9 +29,8 @@ export function getStructuredBases({ } } const aRange = { - //tnr: this probably needs to be changed in case annotation wraps origin - start: annotationRange.start - start, - end: annotationRange.end - start + start: 0, + end: annLen - 1 }; const r = { aRange, @@ -63,7 +62,7 @@ export function getStructuredBases({ forward ? r.basesNoInserts : r.basesNoInserts.split("").reverse().join("") ); r.basesNoInsertsWithMetaData = basesForRange.split("").map((b, i) => { - const indexOfBase = i + annotationRange.start; + const indexOfBase = (i + start) % sequenceLength; let seqForBase = (fullSequence && fullSequence[indexOfBase]) || ""; if (!forward) { seqForBase = getComplementSequenceString(seqForBase); @@ -92,6 +91,5 @@ export function getStructuredBases({ ...bases.split("").map(b => ({ b, isMatch: false })) ); }); - return r; }