jbrowse-plugin-graphgenomeviewer 7.4.2 → 7.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -24
- package/dist/chunks/{AddTrackWorkflow-LK25EC3H.js → AddTrackWorkflow-NLSZYK56.js} +2 -2
- package/dist/chunks/{GbzBaseSyntenyAdapter-BVLBK4OO.js → GbzBaseSyntenyAdapter-A5MNPCKX.js} +2 -2
- package/dist/chunks/{GbzBaseSyntenyAdapter-BVLBK4OO.js.map → GbzBaseSyntenyAdapter-A5MNPCKX.js.map} +1 -1
- package/dist/chunks/{GraphGenomeView-UCOAMRF7.js → GraphGenomeView-SEAPDRXS.js} +2 -2
- package/dist/chunks/{GraphTrackSettingsDialog-MR7X75EY.js → GraphTrackSettingsDialog-EVIIGC5G.js} +2 -2
- package/dist/chunks/{HighlightColorDialog-ZBAZDYIR.js → HighlightColorDialog-QAYWLO5I.js} +2 -2
- package/dist/chunks/{LinearGraphDisplay-AJ5OZN27.js → LinearGraphDisplay-ZC4BQ27J.js} +2 -2
- package/dist/chunks/MinigraphBubbleAdapter-3QM4XV47.js +2 -0
- package/dist/chunks/{MinigraphBubbleAdapter-PEQK5NII.js.map → MinigraphBubbleAdapter-3QM4XV47.js.map} +1 -1
- package/dist/chunks/{RgfaTabixAdapter-Q63YLPK4.js → RgfaTabixAdapter-XSOIEWBK.js} +2 -2
- package/dist/chunks/WalkFormsAdapter-FT2SW4H6.js +2 -0
- package/dist/chunks/WalkFormsAdapter-FT2SW4H6.js.map +7 -0
- package/dist/chunks/{WalkTabixSyntenyAdapter-N5PFI4LS.js → WalkTabixSyntenyAdapter-3WRVNDLB.js} +2 -2
- package/dist/chunks/{chunk-2XZYJLCE.js → chunk-2KVE7YAS.js} +1 -1
- package/dist/chunks/{chunk-2XZYJLCE.js.map → chunk-2KVE7YAS.js.map} +1 -1
- package/dist/chunks/{chunk-RCXOOBLX.js → chunk-6SWXMRAV.js} +1 -1
- package/dist/chunks/{chunk-RCXOOBLX.js.map → chunk-6SWXMRAV.js.map} +1 -1
- package/dist/chunks/{chunk-WY5W5X62.js → chunk-6VNU7SD4.js} +2 -2
- package/dist/chunks/{chunk-WY5W5X62.js.map → chunk-6VNU7SD4.js.map} +1 -1
- package/dist/chunks/{chunk-DXNE2CFX.js → chunk-6WUWQX2P.js} +2 -2
- package/dist/chunks/{chunk-DXNE2CFX.js.map → chunk-6WUWQX2P.js.map} +1 -1
- package/dist/chunks/{chunk-ZG2CQXOW.js → chunk-7RJMG6I2.js} +2 -2
- package/dist/chunks/{chunk-ZG2CQXOW.js.map → chunk-7RJMG6I2.js.map} +1 -1
- package/dist/chunks/chunk-FTXDTU27.js +3 -0
- package/dist/chunks/chunk-FTXDTU27.js.map +7 -0
- package/dist/chunks/chunk-G53BI7VK.js +2 -0
- package/dist/chunks/{chunk-MEMUEG5Z.js.map → chunk-G53BI7VK.js.map} +4 -4
- package/dist/chunks/chunk-GRTN25B6.js +3 -0
- package/dist/chunks/chunk-GRTN25B6.js.map +7 -0
- package/dist/chunks/{chunk-62AHPP6H.js → chunk-HCDKP3IP.js} +2 -2
- package/dist/chunks/{chunk-62AHPP6H.js.map → chunk-HCDKP3IP.js.map} +1 -1
- package/dist/chunks/{chunk-HI3YK2SO.js → chunk-HF4H6LQI.js} +2 -2
- package/dist/chunks/{chunk-MVVXRZXV.js → chunk-JRJHXLGP.js} +2 -2
- package/dist/chunks/{chunk-MVVXRZXV.js.map → chunk-JRJHXLGP.js.map} +2 -2
- package/dist/chunks/{chunk-LFTZNUXB.js → chunk-KIYRMZWP.js} +2 -2
- package/dist/chunks/{chunk-LFTZNUXB.js.map → chunk-KIYRMZWP.js.map} +1 -1
- package/dist/chunks/{chunk-WJGCVNGH.js → chunk-LE43I4PT.js} +2 -2
- package/dist/chunks/{chunk-U5PIXAF2.js → chunk-LE7FZM6Y.js} +2 -2
- package/dist/chunks/{chunk-U5PIXAF2.js.map → chunk-LE7FZM6Y.js.map} +1 -1
- package/dist/chunks/chunk-MUONTMYZ.js +2 -0
- package/dist/chunks/chunk-MUONTMYZ.js.map +7 -0
- package/dist/chunks/{chunk-SFYSQ52W.js → chunk-OJWBLPPH.js} +2 -2
- package/dist/chunks/{chunk-JSRGX4TC.js → chunk-P3FBI5ML.js} +1 -1
- package/dist/chunks/{chunk-JSRGX4TC.js.map → chunk-P3FBI5ML.js.map} +1 -1
- package/dist/chunks/chunk-QV7SSOMJ.js +2 -0
- package/dist/chunks/chunk-S333HOG5.js +2 -0
- package/dist/chunks/chunk-S333HOG5.js.map +7 -0
- package/dist/chunks/{chunk-QJ43NDYS.js → chunk-TYV6UXJA.js} +2 -2
- package/dist/chunks/chunk-VZQ6VOUP.js +2 -0
- package/dist/chunks/chunk-VZQ6VOUP.js.map +7 -0
- package/dist/chunks/chunk-WLRRI35A.js +3 -0
- package/dist/chunks/chunk-WLRRI35A.js.map +7 -0
- package/dist/chunks/{chunk-PUREF4BM.js → chunk-XXBE4IFZ.js} +2 -2
- package/dist/chunks/{chunk-PUREF4BM.js.map → chunk-XXBE4IFZ.js.map} +1 -1
- package/dist/chunks/{renderSvg-Z7YIXVQ5.js → renderSvg-ZIUOPWJQ.js} +2 -2
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js +3 -4
- package/dist/jbrowse-plugin-graphgenomeviewer.esm.js.map +4 -4
- package/package.json +3 -3
- package/src/GraphGenomeView/model.test.ts +2 -109
- package/src/GraphGenomeView/pane/fitViews.ts +0 -109
- package/src/GraphGenomeView/pane/graphViews.ts +0 -4
- package/src/GraphGenomeView/pane/launchActions.ts +0 -22
- package/src/GraphGenomeView/pane/loadActions.ts +1 -1
- package/src/GraphGenomeView/pane/paneBase.ts +0 -7
- package/src/GraphGenomeView/viewModel.ts +0 -3
- package/src/RgfaTabixAdapter/configSchema.ts +3 -3
- package/src/RgfaTabixAdapter/walkFiles.test.ts +6 -6
- package/src/WalkFormsAdapter/WalkFormsAdapter.test.ts +204 -0
- package/src/WalkFormsAdapter/WalkFormsAdapter.ts +232 -0
- package/src/WalkFormsAdapter/configSchema.ts +127 -0
- package/src/WalkFormsAdapter/formRows.test.ts +258 -0
- package/src/WalkFormsAdapter/formRows.ts +231 -0
- package/src/WalkFormsAdapter/formsMenu.test.ts +237 -0
- package/src/WalkFormsAdapter/formsMenu.ts +202 -0
- package/src/WalkFormsAdapter/index.ts +21 -0
- package/src/WalkFormsAdapter/pinned.ts +33 -0
- package/src/WalkTabixSyntenyAdapter/WalkTabixSyntenyAdapter.test.ts +5 -5
- package/src/WalkTabixSyntenyAdapter/configSchema.ts +2 -2
- package/src/index.ts +4 -0
- package/src/panSNAliases/panSNAliases.test.ts +2 -2
- package/src/version.ts +1 -1
- package/dist/chunks/MinigraphBubbleAdapter-PEQK5NII.js +0 -2
- package/dist/chunks/chunk-55DIX25J.js +0 -3
- package/dist/chunks/chunk-55DIX25J.js.map +0 -7
- package/dist/chunks/chunk-AWGMTI73.js +0 -3
- package/dist/chunks/chunk-AWGMTI73.js.map +0 -7
- package/dist/chunks/chunk-FWVXBJ64.js +0 -3
- package/dist/chunks/chunk-FWVXBJ64.js.map +0 -7
- package/dist/chunks/chunk-J3AXN7JQ.js +0 -2
- package/dist/chunks/chunk-MEMUEG5Z.js +0 -2
- package/dist/chunks/chunk-Q3BXNKEL.js +0 -2
- package/dist/chunks/chunk-Q3BXNKEL.js.map +0 -7
- package/dist/chunks/chunk-S75MFZDE.js +0 -2
- package/dist/chunks/chunk-S75MFZDE.js.map +0 -7
- /package/dist/chunks/{AddTrackWorkflow-LK25EC3H.js.map → AddTrackWorkflow-NLSZYK56.js.map} +0 -0
- /package/dist/chunks/{GraphGenomeView-UCOAMRF7.js.map → GraphGenomeView-SEAPDRXS.js.map} +0 -0
- /package/dist/chunks/{GraphTrackSettingsDialog-MR7X75EY.js.map → GraphTrackSettingsDialog-EVIIGC5G.js.map} +0 -0
- /package/dist/chunks/{HighlightColorDialog-ZBAZDYIR.js.map → HighlightColorDialog-QAYWLO5I.js.map} +0 -0
- /package/dist/chunks/{LinearGraphDisplay-AJ5OZN27.js.map → LinearGraphDisplay-ZC4BQ27J.js.map} +0 -0
- /package/dist/chunks/{RgfaTabixAdapter-Q63YLPK4.js.map → RgfaTabixAdapter-XSOIEWBK.js.map} +0 -0
- /package/dist/chunks/{WalkTabixSyntenyAdapter-N5PFI4LS.js.map → WalkTabixSyntenyAdapter-3WRVNDLB.js.map} +0 -0
- /package/dist/chunks/{chunk-HI3YK2SO.js.map → chunk-HF4H6LQI.js.map} +0 -0
- /package/dist/chunks/{chunk-WJGCVNGH.js.map → chunk-LE43I4PT.js.map} +0 -0
- /package/dist/chunks/{chunk-SFYSQ52W.js.map → chunk-OJWBLPPH.js.map} +0 -0
- /package/dist/chunks/{chunk-J3AXN7JQ.js.map → chunk-QV7SSOMJ.js.map} +0 -0
- /package/dist/chunks/{chunk-QJ43NDYS.js.map → chunk-TYV6UXJA.js.map} +0 -0
- /package/dist/chunks/{renderSvg-Z7YIXVQ5.js.map → renderSvg-ZIUOPWJQ.js.map} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_follow.GRCh38.walks.bed.gz → walks5_follow.GRCh38.walks.bed.gz} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_follow.GRCh38.walks.bed.gz.tbi → walks5_follow.GRCh38.walks.bed.gz.tbi} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_header.GRCh38.walks.bed.gz → walks5_header.GRCh38.walks.bed.gz} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_header.GRCh38.walks.bed.gz.tbi → walks5_header.GRCh38.walks.bed.gz.tbi} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_inversion.GRCh38.walks.bed.gz → walks5_inversion.GRCh38.walks.bed.gz} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_inversion.GRCh38.walks.bed.gz.tbi → walks5_inversion.GRCh38.walks.bed.gz.tbi} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_lanepairs.GRCh38.walks.bed.gz → walks5_lanepairs.GRCh38.walks.bed.gz} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_lanepairs.GRCh38.walks.bed.gz.tbi → walks5_lanepairs.GRCh38.walks.bed.gz.tbi} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_maxnode.GRCh38.walks.bed.gz → walks5_maxnode.GRCh38.walks.bed.gz} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_maxnode.GRCh38.walks.bed.gz.tbi → walks5_maxnode.GRCh38.walks.bed.gz.tbi} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_rejoin.GRCh38.walks.bed.gz → walks5_rejoin.GRCh38.walks.bed.gz} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_rejoin.GRCh38.walks.bed.gz.tbi → walks5_rejoin.GRCh38.walks.bed.gz.tbi} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_twopart.GRCh38.walks.bed.gz → walks5_twopart.GRCh38.walks.bed.gz} +0 -0
- /package/src/RgfaTabixAdapter/test_data/{walks4_twopart.GRCh38.walks.bed.gz.tbi → walks5_twopart.GRCh38.walks.bed.gz.tbi} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jbrowse-plugin-graphgenomeviewer",
|
|
3
|
-
"version": "7.4.
|
|
3
|
+
"version": "7.4.4",
|
|
4
4
|
"description": "JBrowse 2 graph genome (pangenome) view: rGFA reference-anchored layouts, the Bandage force-directed engine, and haplotype lanes read from a gbz-base database",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"repository": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@jbrowse/core": "5.0.0-beta.11",
|
|
33
33
|
"@jbrowse/display-kit": "5.0.0-beta.11",
|
|
34
34
|
"@jbrowse/display-ui": "5.0.0-beta.11",
|
|
35
|
-
"@jbrowse/graph-walks": "^4.
|
|
35
|
+
"@jbrowse/graph-walks": "^4.2.0",
|
|
36
36
|
"@jbrowse/render-core": "5.0.0-beta.11",
|
|
37
37
|
"@jbrowse/synteny-core": "5.0.0-beta.11"
|
|
38
38
|
},
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@emotion/react": "^11.14.0",
|
|
41
41
|
"@emotion/styled": "^11.14.1",
|
|
42
42
|
"@eslint/js": "^10.0.1",
|
|
43
|
-
"@jbrowse/bandage-core": "^11.
|
|
43
|
+
"@jbrowse/bandage-core": "^11.9.0",
|
|
44
44
|
"@jbrowse/cli": "^4.3.0",
|
|
45
45
|
"@jbrowse/mobx-state-tree": "^6.9.0",
|
|
46
46
|
"@jbrowse/plugin-linear-genome-view": "5.0.0-beta.11",
|
|
@@ -874,27 +874,7 @@ describe('loadGFAFromLocation', () => {
|
|
|
874
874
|
expect(model.graphRegion).toEqual(TEST_REGION)
|
|
875
875
|
})
|
|
876
876
|
|
|
877
|
-
test('a
|
|
878
|
-
rpcRespond()
|
|
879
|
-
mockReadFile.mockResolvedValue(SIMPLE_GFA)
|
|
880
|
-
const model = stateModelFactory().create({
|
|
881
|
-
type: 'GraphGenomeView',
|
|
882
|
-
gfaLocation: location,
|
|
883
|
-
loadedRegion: TEST_REGION,
|
|
884
|
-
layoutMode: 'auto',
|
|
885
|
-
})
|
|
886
|
-
await model.load()
|
|
887
|
-
expect(model.figureSpec()).toMatchObject({
|
|
888
|
-
gfa: location.uri,
|
|
889
|
-
region: 'chr1:1000-5000',
|
|
890
|
-
layout: 'auto',
|
|
891
|
-
})
|
|
892
|
-
expect(model.figure()).toContain(
|
|
893
|
-
'"gfa":"https://example.com/graphs/small.gfa"',
|
|
894
|
-
)
|
|
895
|
-
})
|
|
896
|
-
|
|
897
|
-
test('a gene track in tabix shorthand gives the figure spec its index', async () => {
|
|
877
|
+
test('a gene track in tabix shorthand turns genes on', async () => {
|
|
898
878
|
rpcRespond()
|
|
899
879
|
mockReadFile.mockResolvedValue(SIMPLE_GFA)
|
|
900
880
|
mockSession.tracks = [
|
|
@@ -916,11 +896,7 @@ describe('loadGFAFromLocation', () => {
|
|
|
916
896
|
})
|
|
917
897
|
await model.load()
|
|
918
898
|
expect(model.showGenes).toBe(true)
|
|
919
|
-
expect(model.
|
|
920
|
-
file: 'https://jbrowse.org/ucsc/hg38/ncbiRefSeq.gff.gz',
|
|
921
|
-
index: 'https://jbrowse.org/ucsc/hg38/ncbiRefSeq.gff.gz.csi',
|
|
922
|
-
format: 'gff3',
|
|
923
|
-
})
|
|
899
|
+
expect(model.geneTrack?.trackId).toBe('ncbiRefSeq')
|
|
924
900
|
mockSession.tracks = []
|
|
925
901
|
})
|
|
926
902
|
|
|
@@ -3413,89 +3389,6 @@ describe('walk rows', () => {
|
|
|
3413
3389
|
expect(svg).toContain("'s path")
|
|
3414
3390
|
})
|
|
3415
3391
|
|
|
3416
|
-
test("the figure spec keeps the strip's sample filter", async () => {
|
|
3417
|
-
rpcRespond()
|
|
3418
|
-
mockReadFile.mockResolvedValue(WALKS_GFA)
|
|
3419
|
-
const model = stateModelFactory().create({
|
|
3420
|
-
type: 'GraphGenomeView',
|
|
3421
|
-
gfaLocation: {
|
|
3422
|
-
uri: 'https://example.com/graphs/walks.gfa',
|
|
3423
|
-
locationType: 'UriLocation',
|
|
3424
|
-
},
|
|
3425
|
-
loadedRegion: TEST_REGION,
|
|
3426
|
-
layoutMode: 'ordered',
|
|
3427
|
-
layers: { walkStrip: true },
|
|
3428
|
-
rows: { kept: ['B'] },
|
|
3429
|
-
})
|
|
3430
|
-
await model.load()
|
|
3431
|
-
expect(model.walkStripRows!.rows.map(r => r.label)).toEqual(['B#1'])
|
|
3432
|
-
expect(model.figureSpec()).toMatchObject({
|
|
3433
|
-
walkStrip: true,
|
|
3434
|
-
walkRowSamples: ['B'],
|
|
3435
|
-
})
|
|
3436
|
-
model.setWalkStrip(false)
|
|
3437
|
-
expect(model.figureSpec()?.walkRowSamples).toBeUndefined()
|
|
3438
|
-
})
|
|
3439
|
-
|
|
3440
|
-
// bandage-figure shows deletion edges unless a spec says otherwise
|
|
3441
|
-
test('the figure spec states deletion edges only when they are off', async () => {
|
|
3442
|
-
rpcRespond()
|
|
3443
|
-
mockReadFile.mockResolvedValue(WALKS_GFA)
|
|
3444
|
-
const model = stateModelFactory().create({
|
|
3445
|
-
type: 'GraphGenomeView',
|
|
3446
|
-
gfaLocation: {
|
|
3447
|
-
uri: 'https://example.com/graphs/walks.gfa',
|
|
3448
|
-
locationType: 'UriLocation',
|
|
3449
|
-
},
|
|
3450
|
-
loadedRegion: TEST_REGION,
|
|
3451
|
-
layoutMode: 'ordered',
|
|
3452
|
-
})
|
|
3453
|
-
await model.load()
|
|
3454
|
-
expect(model.figureSpec()).not.toHaveProperty('showDeletionEdges')
|
|
3455
|
-
model.setLayer('deletions', false)
|
|
3456
|
-
expect(model.figureSpec()?.showDeletionEdges).toBe(false)
|
|
3457
|
-
})
|
|
3458
|
-
|
|
3459
|
-
// bandage-figure laid a linear layout out as an ordinary one, and drew its
|
|
3460
|
-
// own line widths, while the spec said nothing about either
|
|
3461
|
-
test('the figure spec carries the settings that change the picture', async () => {
|
|
3462
|
-
rpcRespond()
|
|
3463
|
-
mockReadFile.mockResolvedValue(WALKS_GFA)
|
|
3464
|
-
const model = stateModelFactory().create({
|
|
3465
|
-
type: 'GraphGenomeView',
|
|
3466
|
-
gfaLocation: {
|
|
3467
|
-
uri: 'https://example.com/graphs/walks.gfa',
|
|
3468
|
-
locationType: 'UriLocation',
|
|
3469
|
-
},
|
|
3470
|
-
loadedRegion: TEST_REGION,
|
|
3471
|
-
layoutMode: 'ordered',
|
|
3472
|
-
size: 9,
|
|
3473
|
-
})
|
|
3474
|
-
await model.load()
|
|
3475
|
-
expect(model.figureSpec()).toMatchObject({ contigThickness: 9 })
|
|
3476
|
-
expect(model.figureSpec()).not.toHaveProperty('connectorThickness')
|
|
3477
|
-
expect(model.figureSpec()).not.toHaveProperty('linearLayout')
|
|
3478
|
-
model.setLinearLayout(true)
|
|
3479
|
-
expect(model.figureSpec()?.linearLayout).toBe(true)
|
|
3480
|
-
})
|
|
3481
|
-
|
|
3482
|
-
test('walk rows offer no figure spec, since bandage-figure draws no walk rows', async () => {
|
|
3483
|
-
rpcRespond()
|
|
3484
|
-
mockReadFile.mockResolvedValue(WALKS_GFA)
|
|
3485
|
-
const model = stateModelFactory().create({
|
|
3486
|
-
type: 'GraphGenomeView',
|
|
3487
|
-
gfaLocation: {
|
|
3488
|
-
uri: 'https://example.com/graphs/walks.gfa',
|
|
3489
|
-
locationType: 'UriLocation',
|
|
3490
|
-
},
|
|
3491
|
-
loadedRegion: TEST_REGION,
|
|
3492
|
-
layoutMode: 'walkrows',
|
|
3493
|
-
})
|
|
3494
|
-
await model.load()
|
|
3495
|
-
expect(model.walkRowBars).toBeDefined()
|
|
3496
|
-
expect(model.figureSpecUnavailable).toMatch(/^bandage-figure draws no /)
|
|
3497
|
-
})
|
|
3498
|
-
|
|
3499
3392
|
test("a picker's set keeps lifted walks this cut does not hold", async () => {
|
|
3500
3393
|
rpcRespond()
|
|
3501
3394
|
const model = stateModelFactory().create({
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
facetGrid,
|
|
6
6
|
} from '@jbrowse/bandage-core/facetGrid'
|
|
7
7
|
import { figureSvg } from '@jbrowse/bandage-core/figure'
|
|
8
|
-
import { figureSpecSettings } from '@jbrowse/bandage-core/figureSettings'
|
|
9
8
|
import { rowLabelBox } from '@jbrowse/bandage-core/graphLabels'
|
|
10
9
|
import {
|
|
11
10
|
LEGEND_INSET_PX,
|
|
@@ -34,10 +33,6 @@ import {
|
|
|
34
33
|
} from '@jbrowse/bandage-core/tubeMap/genes'
|
|
35
34
|
import { padded } from '@jbrowse/bandage-core/viewport'
|
|
36
35
|
import { walkPosition } from '@jbrowse/bandage-core/walkKey'
|
|
37
|
-
import {
|
|
38
|
-
expandTabixShorthand,
|
|
39
|
-
readConfObject,
|
|
40
|
-
} from '@jbrowse/core/configuration'
|
|
41
36
|
import { untracked } from 'mobx'
|
|
42
37
|
|
|
43
38
|
import { SECTION_HEADER_PX, sectionPlacement } from '../walkRowGroups'
|
|
@@ -52,7 +47,6 @@ import {
|
|
|
52
47
|
VIEWPORT_PANES_BUILT,
|
|
53
48
|
dependOn,
|
|
54
49
|
paneViewportOf,
|
|
55
|
-
uriOf,
|
|
56
50
|
} from './paneBase'
|
|
57
51
|
import { trackedDpr } from '../trackedDpr'
|
|
58
52
|
|
|
@@ -63,7 +57,6 @@ import type {
|
|
|
63
57
|
} from '@jbrowse/bandage-core/layout/walkRowDraw'
|
|
64
58
|
import type { Renderer } from '@jbrowse/bandage-core/renderer/types'
|
|
65
59
|
import type { LiftedWalk, WalkLift } from '@jbrowse/bandage-core/walkHighlight'
|
|
66
|
-
import type { FileLocation } from '@jbrowse/core/util/types'
|
|
67
60
|
|
|
68
61
|
export const TOOLTIP_INSET_PX = 8
|
|
69
62
|
export const FACET_KEY_ROW_PX = 19
|
|
@@ -557,96 +550,6 @@ export const withFitViews = withHostViews
|
|
|
557
550
|
? (walkPosition(walk, node.id, node.length) ?? 'not on this walk')
|
|
558
551
|
: undefined
|
|
559
552
|
},
|
|
560
|
-
// The spec bandage-figure makes this drawing from with no browser, or
|
|
561
|
-
// undefined for a graph it cannot read again: one cut from a gbz-base
|
|
562
|
-
// track or read from a GFA url, with the genes of a GFF3 tabix track
|
|
563
|
-
figureSpec() {
|
|
564
|
-
const region = self.graphRegion
|
|
565
|
-
const adapter = self.sourceAdapter
|
|
566
|
-
const panSN = adapter?.assemblyNameToPanSN as
|
|
567
|
-
Record<string, string> | undefined
|
|
568
|
-
const window = region && `${region.refName}:${region.start}-${region.end}`
|
|
569
|
-
const gfa = uriOf(self.sourceGfaLocation)
|
|
570
|
-
const db =
|
|
571
|
-
adapter?.type === 'GbzBaseSyntenyAdapter'
|
|
572
|
-
? uriOf(adapter.gbzDbLocation as FileLocation)
|
|
573
|
-
: undefined
|
|
574
|
-
const source = gfa
|
|
575
|
-
? { gfa, region: window }
|
|
576
|
-
: db && region && window
|
|
577
|
-
? {
|
|
578
|
-
gbz: {
|
|
579
|
-
db,
|
|
580
|
-
index: uriOf(adapter!.haplotypeIndexLocation as FileLocation),
|
|
581
|
-
region: window,
|
|
582
|
-
// the adapter cuts a lane by its PanSN prefix
|
|
583
|
-
haplotypes: self.cutHaplotypes?.map(
|
|
584
|
-
lane => panSN?.[lane] ?? lane,
|
|
585
|
-
),
|
|
586
|
-
referenceSample:
|
|
587
|
-
(adapter!.referenceSample as string | undefined) ||
|
|
588
|
-
panSN?.[region.assemblyName] ||
|
|
589
|
-
region.assemblyName,
|
|
590
|
-
context: adapter!.context as number | undefined,
|
|
591
|
-
snarls: self.cutsWholeWalks
|
|
592
|
-
? 'overlapping'
|
|
593
|
-
: (adapter!.subgraphSnarls as string | undefined),
|
|
594
|
-
},
|
|
595
|
-
}
|
|
596
|
-
: undefined
|
|
597
|
-
if (!source) {
|
|
598
|
-
return undefined
|
|
599
|
-
}
|
|
600
|
-
const geneTrack = self.showGenes ? self.geneTrack : undefined
|
|
601
|
-
const geneConf = geneTrack
|
|
602
|
-
? self.sessionTrack(geneTrack.trackId)
|
|
603
|
-
: undefined
|
|
604
|
-
const geneAdapter = geneConf
|
|
605
|
-
? expandTabixShorthand(
|
|
606
|
-
readConfObject(geneConf, 'adapter') as Record<string, unknown>,
|
|
607
|
-
'gffGzLocation',
|
|
608
|
-
)
|
|
609
|
-
: undefined
|
|
610
|
-
const genes =
|
|
611
|
-
geneAdapter?.type === 'Gff3TabixAdapter'
|
|
612
|
-
? {
|
|
613
|
-
file: uriOf(geneAdapter.gffGzLocation as FileLocation),
|
|
614
|
-
index: uriOf(
|
|
615
|
-
(geneAdapter.index as { location?: FileLocation } | undefined)
|
|
616
|
-
?.location,
|
|
617
|
-
),
|
|
618
|
-
format: 'gff3',
|
|
619
|
-
}
|
|
620
|
-
: undefined
|
|
621
|
-
return JSON.parse(
|
|
622
|
-
JSON.stringify({
|
|
623
|
-
...source,
|
|
624
|
-
genes: genes?.file && genes.index ? genes : undefined,
|
|
625
|
-
referencePath: self.referencePath || undefined,
|
|
626
|
-
layout: self.chosenLayoutMode,
|
|
627
|
-
...figureSpecSettings({
|
|
628
|
-
engine: self.layoutEngine,
|
|
629
|
-
quality: self.layoutQuality,
|
|
630
|
-
linearLayout: self.linearLayout,
|
|
631
|
-
bubbleSpread: self.bubbleSpread,
|
|
632
|
-
showDeletionEdges: self.showDeletionEdges,
|
|
633
|
-
colorScheme: self.chosenColorScheme,
|
|
634
|
-
nodeWidth: self.nodeWidth,
|
|
635
|
-
contigThickness: self.contigThickness,
|
|
636
|
-
connectorThickness: self.connectorThickness,
|
|
637
|
-
colorDomain: self.statedColorDomain,
|
|
638
|
-
}),
|
|
639
|
-
walks: self.walkLayers.length
|
|
640
|
-
? self.walkLayers.map(l => (l.color ? l : l.walk))
|
|
641
|
-
: undefined,
|
|
642
|
-
facet: self.walkLayers.length > 1 ? self.facetSpec : undefined,
|
|
643
|
-
walkStrip: self.walkStripShown || undefined,
|
|
644
|
-
walkRowSamples: self.walkStripShown ? self.walkRowSamples : undefined,
|
|
645
|
-
width: self.paneWidth,
|
|
646
|
-
height: self.paneCeiling,
|
|
647
|
-
}),
|
|
648
|
-
) as Record<string, unknown>
|
|
649
|
-
},
|
|
650
553
|
// Why the drawing cannot be written as SVG, which draws the canvas's
|
|
651
554
|
// nodes, or walk rows' bars
|
|
652
555
|
get figureUnavailable() {
|
|
@@ -658,15 +561,6 @@ export const withFitViews = withHostViews
|
|
|
658
561
|
? undefined
|
|
659
562
|
: `${layoutModeByValue(self.chosenLayoutMode).label} draws a picture of its own, which the SVG export does not`
|
|
660
563
|
},
|
|
661
|
-
// Why bandage-figure can't make this drawing again: it draws only
|
|
662
|
-
// layouts with nodes, from a graph it can read for itself
|
|
663
|
-
get figureSpecUnavailable() {
|
|
664
|
-
return !self.drawsNodes
|
|
665
|
-
? `bandage-figure draws no ${layoutModeByValue(self.chosenLayoutMode).label} layout`
|
|
666
|
-
: this.figureSpec()
|
|
667
|
-
? undefined
|
|
668
|
-
: 'bandage-figure reads a graph cut from a gbz-base track or a GFA url'
|
|
669
|
-
},
|
|
670
564
|
// The drawing as a standalone SVG, fitted, with its genes, its lifted
|
|
671
565
|
// walks' keys, facet panels and the strip of walk rows; see figureSvg
|
|
672
566
|
figure() {
|
|
@@ -695,9 +589,6 @@ export const withFitViews = withHostViews
|
|
|
695
589
|
rowGenes: self.walkRowGenes,
|
|
696
590
|
rowGeneGaps: self.walkRowGeneGaps,
|
|
697
591
|
},
|
|
698
|
-
// the SVG carries the spec only where the spec draws this same
|
|
699
|
-
// picture: one of nodes (figureSpecUnavailable)
|
|
700
|
-
spec: self.drawsNodes ? this.figureSpec() : undefined,
|
|
701
592
|
})
|
|
702
593
|
: undefined
|
|
703
594
|
},
|
|
@@ -666,10 +666,6 @@ export const withGraphViews = paneBase
|
|
|
666
666
|
get cutHaplotypes(): string[] | undefined {
|
|
667
667
|
return undefined
|
|
668
668
|
},
|
|
669
|
-
// the GFA the graph on screen was read from, where a host says
|
|
670
|
-
get sourceGfaLocation(): FileLocation | undefined {
|
|
671
|
-
return undefined
|
|
672
|
-
},
|
|
673
669
|
get sourceAdapter() {
|
|
674
670
|
const track = self.sourceTrack
|
|
675
671
|
return track ? trackAdapterConfig(self, track) : undefined
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
layoutModeByValue,
|
|
6
6
|
} from '@jbrowse/bandage-core/layoutModes'
|
|
7
7
|
import { getSession } from '@jbrowse/core/util'
|
|
8
|
-
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
|
|
9
8
|
import HubIcon from '@mui/icons-material/Hub'
|
|
10
9
|
import PaletteIcon from '@mui/icons-material/Palette'
|
|
11
10
|
import PhotoCameraIcon from '@mui/icons-material/PhotoCamera'
|
|
@@ -338,27 +337,6 @@ export const withLaunchActions = withRenderingActions
|
|
|
338
337
|
}
|
|
339
338
|
},
|
|
340
339
|
},
|
|
341
|
-
{
|
|
342
|
-
label: 'Copy figure spec',
|
|
343
|
-
icon: ContentCopyIcon,
|
|
344
|
-
disabled: self.figureSpecUnavailable !== undefined,
|
|
345
|
-
disabledHelpText: self.figureSpecUnavailable,
|
|
346
|
-
onClick: () => {
|
|
347
|
-
const spec = self.figureSpec()
|
|
348
|
-
const session = getSession(self)
|
|
349
|
-
navigator.clipboard
|
|
350
|
-
.writeText(`${JSON.stringify(spec, null, 2)}\n`)
|
|
351
|
-
.then(() => {
|
|
352
|
-
session.notify(
|
|
353
|
-
'Figure spec copied. Save it as spec.json and run: npx -p @jbrowse/bandage-core bandage-figure spec.json -o figure.svg',
|
|
354
|
-
'info',
|
|
355
|
-
)
|
|
356
|
-
})
|
|
357
|
-
.catch((e: unknown) => {
|
|
358
|
-
session.notify(`Could not copy the figure spec: ${String(e)}`)
|
|
359
|
-
})
|
|
360
|
-
},
|
|
361
|
-
},
|
|
362
340
|
]
|
|
363
341
|
},
|
|
364
342
|
// `picks` false where a toolbar already picks the walks, leaving what is
|
|
@@ -349,7 +349,7 @@ export const withLoadActions = withEngineSettings.actions(self => {
|
|
|
349
349
|
: parsed
|
|
350
350
|
const folded =
|
|
351
351
|
foldBelowBp > 0 && !readsOf
|
|
352
|
-
? foldVariants(anchored, foldBelowBp)
|
|
352
|
+
? foldVariants(anchored, foldBelowBp, { cut: true })
|
|
353
353
|
: anchored
|
|
354
354
|
const graph =
|
|
355
355
|
region && folded !== anchored
|
|
@@ -177,13 +177,6 @@ export function fileName(location: FileLocation) {
|
|
|
177
177
|
return path.split(/[\\/]/).at(-1) ?? ''
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
// A url bandage-figure can read again, or undefined for a local file
|
|
181
|
-
export function uriOf(location: FileLocation | undefined) {
|
|
182
|
-
return location && 'uri' in location && location.uri
|
|
183
|
-
? new URL(location.uri, location.baseUri ?? window.location.href).href
|
|
184
|
-
: undefined
|
|
185
|
-
}
|
|
186
|
-
|
|
187
180
|
type ViewportOwner = 'fit' | 'user' | 'host'
|
|
188
181
|
|
|
189
182
|
interface BuiltViewport {
|
|
@@ -93,9 +93,6 @@ export default function stateModelFactory() {
|
|
|
93
93
|
get paneMeasured() {
|
|
94
94
|
return self.widthMeasured
|
|
95
95
|
},
|
|
96
|
-
get sourceGfaLocation() {
|
|
97
|
-
return self.gfaLocation
|
|
98
|
-
},
|
|
99
96
|
get sourceTrack(): AnyConfigurationModel | undefined {
|
|
100
97
|
return self.loadedTrackId
|
|
101
98
|
? self.sessionTrack(self.loadedTrackId)
|
|
@@ -30,8 +30,8 @@ import type { Instance } from '@jbrowse/mobx-state-tree'
|
|
|
30
30
|
* `walksUri` takes the prefix of a walk file in place of `uri`, and resolves
|
|
31
31
|
* `<walksUri>.walks.bed.gz` and its `.tbi`. `gfa-to-tabix --walks -o <prefix>`
|
|
32
32
|
* writes one per reference sample, so a track names one as
|
|
33
|
-
* `<prefix>.<sample>`. The adapter reads walk format
|
|
34
|
-
* writes from 1.
|
|
33
|
+
* `<prefix>.<sample>`. The adapter reads walk format 5, which gfa-to-tabix
|
|
34
|
+
* writes from 1.4, through @jbrowse/graph-walks. Such a graph's cut carries
|
|
35
35
|
* the haplotypes' walks, and `defaultHaplotypes` names the ones it is cut for
|
|
36
36
|
* until the user picks others.
|
|
37
37
|
*
|
|
@@ -152,7 +152,7 @@ const RgfaTabixAdapter = ConfigurationSchema(
|
|
|
152
152
|
),
|
|
153
153
|
/**
|
|
154
154
|
* #slot
|
|
155
|
-
* a `gfa-to-tabix --walks` file in walk format
|
|
155
|
+
* a `gfa-to-tabix --walks` file in walk format 5: per reference chunk,
|
|
156
156
|
* the reference's path, the ways haplotypes leave it and each haplotype's
|
|
157
157
|
* piece. Unset means the graph has no walks.
|
|
158
158
|
*/
|
|
@@ -35,27 +35,27 @@ const fixture = (name: string) =>
|
|
|
35
35
|
// W HG002 1 chr1 >1>2>7>4>5>6 7 replaces 3
|
|
36
36
|
// W HG002 2 chr1 >1>2>3>4>8>6 8 replaces 5
|
|
37
37
|
// W HG00097 1 chr1 >1>2>3>4>5>6
|
|
38
|
-
const headerPrefix = fixture('
|
|
38
|
+
const headerPrefix = fixture('walks5_header')
|
|
39
39
|
|
|
40
|
-
//
|
|
40
|
+
// walks5_header's walks with the reference named as pggb names a P-line,
|
|
41
41
|
// GRCh38#chr1
|
|
42
|
-
const twoPartPrefix = fixture('
|
|
42
|
+
const twoPartPrefix = fixture('walks5_twopart')
|
|
43
43
|
|
|
44
44
|
// An unchopped graph, node 1 spanning 0-2600 and filed under chunk 0 alone:
|
|
45
45
|
// W GRCh38 0 chr1 >1>2>4
|
|
46
46
|
// W HG002 1 chr1 >1>3>4 3 replaces 2
|
|
47
|
-
const maxNodePrefix = fixture('
|
|
47
|
+
const maxNodePrefix = fixture('walks5_maxnode')
|
|
48
48
|
|
|
49
49
|
// Twelve 500 bp reference nodes over chr1:0-6000 in one 10 kb chunk:
|
|
50
50
|
// W GRCh38 0 chr1 >1>2>3>4>5>6>7>8>9>10>11>12
|
|
51
51
|
// W HG002 1 chr1 >1>2>3>20>21>9>10>11>12 20 and 21, 2 kb each, replace 4-8
|
|
52
52
|
// W HG002 2 chr1 >1>2>3>4>5>6>7>8>9>10>11>12
|
|
53
53
|
// W HG003 1 chr1 >1>2>3>22 the contig ends on 22, 300 bp
|
|
54
|
-
const rejoinPrefix = fixture('
|
|
54
|
+
const rejoinPrefix = fixture('walks5_rejoin')
|
|
55
55
|
|
|
56
56
|
// The same graph in 1 kb chunks, whose rows name the chunks their path goes on
|
|
57
57
|
// in: HG002#1 leaves chunk 1000 for 20 and 21 and rejoins at 9, in chunk 4000
|
|
58
|
-
const followPrefix = fixture('
|
|
58
|
+
const followPrefix = fixture('walks5_follow')
|
|
59
59
|
|
|
60
60
|
function makeAdapter(prefix: string, slots = {}) {
|
|
61
61
|
const local = (path: string) => ({
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
2
|
+
import { ExpansionTooLargeError, formsCut } from '@jbrowse/graph-walks'
|
|
3
|
+
import { firstValueFrom } from 'rxjs'
|
|
4
|
+
import { toArray } from 'rxjs/operators'
|
|
5
|
+
|
|
6
|
+
import Adapter from './WalkFormsAdapter.ts'
|
|
7
|
+
import configSchema from './configSchema.ts'
|
|
8
|
+
import WalkFormsAdapterF from './index.ts'
|
|
9
|
+
|
|
10
|
+
import type * as GraphWalks from '@jbrowse/graph-walks'
|
|
11
|
+
|
|
12
|
+
vi.mock('@jbrowse/graph-walks', async importOriginal => {
|
|
13
|
+
const actual = await importOriginal<typeof GraphWalks>()
|
|
14
|
+
return { ...actual, formsCut: vi.fn(actual.formsCut) }
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
// Four walks over a 3.6 kb reference of six 600 bp nodes, in 1 kb chunks
|
|
18
|
+
// (RgfaTabixAdapter/walkFiles.test.ts builds on the same file):
|
|
19
|
+
// W GRCh38 0 chr1 >1>2>3>4>5>6
|
|
20
|
+
// W HG002 1 chr1 >1>2>7>4>5>6 7 (550 bp) replaces 3
|
|
21
|
+
// W HG002 2 chr1 >1>2>3>4>8>6 8 (20 bp) replaces 5
|
|
22
|
+
// W HG00097 1 chr1 >1>2>3>4>5>6
|
|
23
|
+
const walks =
|
|
24
|
+
require.resolve('../RgfaTabixAdapter/test_data/walks5_header.GRCh38.walks.bed.gz')
|
|
25
|
+
|
|
26
|
+
const local = (path: string) => ({
|
|
27
|
+
localPath: path,
|
|
28
|
+
locationType: 'LocalPathLocation',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// Each test pins its own window, since the cut cache outlives an adapter
|
|
32
|
+
function makeAdapter(slots: Record<string, unknown>) {
|
|
33
|
+
return new Adapter(
|
|
34
|
+
configSchema.create({
|
|
35
|
+
walksLocation: local(walks),
|
|
36
|
+
walksIndex: { location: local(`${walks}.tbi`) },
|
|
37
|
+
...slots,
|
|
38
|
+
}),
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const pinned = (start: number) => ({ refName: 'chr1', start, end: 3600 })
|
|
43
|
+
|
|
44
|
+
const region = (start: number, end: number, refName = 'chr1') => ({
|
|
45
|
+
refName,
|
|
46
|
+
assemblyName: 'hg38',
|
|
47
|
+
start,
|
|
48
|
+
end,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const featuresOf = (adapter: Adapter, query = region(0, 3600)) =>
|
|
52
|
+
firstValueFrom(adapter.getFeatures(query).pipe(toArray()))
|
|
53
|
+
|
|
54
|
+
const placeholders = (features: Awaited<ReturnType<typeof featuresOf>>) =>
|
|
55
|
+
features
|
|
56
|
+
.filter(f => f.id().startsWith('forms-reference-'))
|
|
57
|
+
.map(f => ({
|
|
58
|
+
start: f.get('start'),
|
|
59
|
+
end: f.get('end'),
|
|
60
|
+
genotypes: f.get('genotypes'),
|
|
61
|
+
}))
|
|
62
|
+
|
|
63
|
+
test('registers the adapter type', () => {
|
|
64
|
+
const pm = new PluginManager()
|
|
65
|
+
WalkFormsAdapterF(pm)
|
|
66
|
+
pm.createPluggableElements()
|
|
67
|
+
expect(pm.getAdapterType('WalkFormsAdapter').name).toBe('WalkFormsAdapter')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('names the reference sample contigs as the assembly does', async () => {
|
|
71
|
+
expect(await makeAdapter({ pinned: pinned(0) }).getRefNames()).toEqual([
|
|
72
|
+
'chr1',
|
|
73
|
+
])
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('one source per form, labelled by share and change', async () => {
|
|
77
|
+
const adapter = makeAdapter({ pinned: pinned(1) })
|
|
78
|
+
expect(await adapter.getSourcesAndWarnings()).toEqual({
|
|
79
|
+
sources: [
|
|
80
|
+
{ name: 'F1', label: '33% · ref', haplotypes: 1, forms: 1 },
|
|
81
|
+
{
|
|
82
|
+
name: 'F2',
|
|
83
|
+
label: '33% · repl 600bp->550bp',
|
|
84
|
+
haplotypes: 1,
|
|
85
|
+
forms: 1,
|
|
86
|
+
},
|
|
87
|
+
{ name: 'F3', label: '33% · del 580bp', haplotypes: 1, forms: 1 },
|
|
88
|
+
],
|
|
89
|
+
warnings: [],
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('features are the pinned window changes, with a genotype per form', async () => {
|
|
94
|
+
const features = await featuresOf(makeAdapter({ pinned: pinned(2) }))
|
|
95
|
+
expect(
|
|
96
|
+
features.map(f => ({
|
|
97
|
+
start: f.get('start'),
|
|
98
|
+
end: f.get('end'),
|
|
99
|
+
consequence: f.get('consequence'),
|
|
100
|
+
svType: f.get('svType'),
|
|
101
|
+
type: f.get('type'),
|
|
102
|
+
genotypes: f.get('genotypes'),
|
|
103
|
+
})),
|
|
104
|
+
).toEqual([
|
|
105
|
+
{
|
|
106
|
+
start: 1200,
|
|
107
|
+
end: 1800,
|
|
108
|
+
consequence: 'loss',
|
|
109
|
+
svType: 'CPX',
|
|
110
|
+
type: 'complex_substitution',
|
|
111
|
+
genotypes: { F1: '0', F2: '1', F3: '0' },
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
start: 2400,
|
|
115
|
+
end: 3000,
|
|
116
|
+
consequence: 'loss',
|
|
117
|
+
svType: 'DEL',
|
|
118
|
+
type: 'deletion',
|
|
119
|
+
genotypes: { F1: '0', F2: '0', F3: '1' },
|
|
120
|
+
},
|
|
121
|
+
])
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('a query with no change gives one reference placeholder', async () => {
|
|
125
|
+
const adapter = makeAdapter({ pinned: pinned(3) })
|
|
126
|
+
expect(placeholders(await featuresOf(adapter, region(1900, 2300)))).toEqual([
|
|
127
|
+
{ start: 1900, end: 1901, genotypes: { F1: '0', F2: '0', F3: '0' } },
|
|
128
|
+
])
|
|
129
|
+
expect(
|
|
130
|
+
placeholders(await featuresOf(adapter, region(0, 3600, 'chr2'))),
|
|
131
|
+
).toHaveLength(1)
|
|
132
|
+
expect(
|
|
133
|
+
placeholders(await featuresOf(adapter, region(2900, 3600))),
|
|
134
|
+
).toHaveLength(0)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test('a floor change regroups the cut without cutting again', async () => {
|
|
138
|
+
vi.mocked(formsCut).mockClear()
|
|
139
|
+
const fine = makeAdapter({ pinned: pinned(4), floorBp: 50 })
|
|
140
|
+
const coarse = makeAdapter({ pinned: pinned(4), floorBp: 1000 })
|
|
141
|
+
expect((await fine.getSourcesAndWarnings()).sources).toHaveLength(3)
|
|
142
|
+
expect((await coarse.getSourcesAndWarnings()).sources).toEqual([
|
|
143
|
+
{ name: 'F1', label: '100% · ref', haplotypes: 3, forms: 1 },
|
|
144
|
+
])
|
|
145
|
+
expect(placeholders(await featuresOf(coarse))).toEqual([
|
|
146
|
+
{ start: 0, end: 1, genotypes: { F1: '0' } },
|
|
147
|
+
])
|
|
148
|
+
expect(formsCut).toHaveBeenCalledTimes(1)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
test('exclude leaves samples out of every form', async () => {
|
|
152
|
+
const adapter = makeAdapter({ pinned: pinned(5), exclude: ['HG002'] })
|
|
153
|
+
expect((await adapter.getSourcesAndWarnings()).sources).toEqual([
|
|
154
|
+
{ name: 'F1', label: '100% · ref', haplotypes: 1, forms: 1 },
|
|
155
|
+
])
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test('maxForms gives the smaller forms one empty row', async () => {
|
|
159
|
+
const adapter = makeAdapter({ pinned: pinned(6), maxForms: 1 })
|
|
160
|
+
const { sources, warnings } = await adapter.getSourcesAndWarnings()
|
|
161
|
+
expect(sources.map(s => s.label)).toEqual(['33% · ref', 'other forms · 67%'])
|
|
162
|
+
expect(warnings).toEqual([
|
|
163
|
+
expect.stringMatching(/^Forms of chr1:7-3,600: 2 smaller forms/),
|
|
164
|
+
])
|
|
165
|
+
expect(placeholders(await featuresOf(adapter))).toEqual([
|
|
166
|
+
{ start: 0, end: 1, genotypes: { F1: '0' } },
|
|
167
|
+
])
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
test('a pinned window past walkByteBudget says so, without a zoom-in notice', async () => {
|
|
171
|
+
const error = await makeAdapter({ pinned: pinned(7), walkByteBudget: 1 })
|
|
172
|
+
.getSourcesAndWarnings()
|
|
173
|
+
.catch((e: unknown) => e)
|
|
174
|
+
expect(error).toBeInstanceOf(Error)
|
|
175
|
+
expect(error).not.toHaveProperty('regionTooLarge')
|
|
176
|
+
expect(`${error}`).toMatch(
|
|
177
|
+
/^Error: The pinned window chr1:8-3,600 is too large to cut for structural forms: .*walkByteBudget, 0\.0 MB\. .*Raise walkByteBudget$/,
|
|
178
|
+
)
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test('a window whose walks are too many to read in full says so', async () => {
|
|
182
|
+
vi.mocked(formsCut).mockClear()
|
|
183
|
+
vi.mocked(formsCut).mockRejectedValueOnce(
|
|
184
|
+
new ExpansionTooLargeError(24_000_000, 16_000_000, 3592),
|
|
185
|
+
)
|
|
186
|
+
const error = await makeAdapter({ pinned: pinned(9) })
|
|
187
|
+
.getSourcesAndWarnings()
|
|
188
|
+
.catch((e: unknown) => e)
|
|
189
|
+
expect(formsCut).toHaveBeenCalledWith(
|
|
190
|
+
expect.anything(),
|
|
191
|
+
expect.anything(),
|
|
192
|
+
expect.objectContaining({ partial: false }),
|
|
193
|
+
)
|
|
194
|
+
expect(error).not.toHaveProperty('regionTooLarge')
|
|
195
|
+
expect(`${error}`).toBe(
|
|
196
|
+
'Error: The pinned window chr1:10-3,600 is too large to read every walk in full for structural forms: its walks take 24.0 M steps, over the 16.0 M step budget, and about 2.39Kbp of it fits. Pin a narrower window',
|
|
197
|
+
)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test('an adapter with no pinned window says so', async () => {
|
|
201
|
+
await expect(makeAdapter({}).getSourcesAndWarnings()).rejects.toThrow(
|
|
202
|
+
/needs `pinned`/,
|
|
203
|
+
)
|
|
204
|
+
})
|