jbrowse-plugin-msaview 3.5.0 → 3.7.0

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.
Files changed (242) hide show
  1. package/README.md +8 -0
  2. package/dist/AddHighlightModel/index.js +3 -3
  3. package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +8 -2
  4. package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +3 -3
  5. package/dist/BgzipFastaMsaAdapter/configSchema.js +1 -1
  6. package/dist/LaunchMsaView/cleanProteinSequence.test.d.ts +1 -0
  7. package/dist/LaunchMsaView/cleanProteinSequence.test.js +77 -0
  8. package/dist/LaunchMsaView/codingFeature.d.ts +14 -0
  9. package/dist/LaunchMsaView/codingFeature.js +38 -0
  10. package/dist/LaunchMsaView/codingFeature.test.d.ts +1 -0
  11. package/dist/LaunchMsaView/codingFeature.test.js +75 -0
  12. package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.d.ts +3 -1
  13. package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.js +47 -55
  14. package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.d.ts +3 -1
  15. package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.js +16 -17
  16. package/dist/LaunchMsaView/components/BlastQuery/BlastPanel.d.ts +7 -3
  17. package/dist/LaunchMsaView/components/BlastQuery/BlastPanel.js +8 -7
  18. package/dist/LaunchMsaView/components/BlastQuery/BlastSettingsDialog.js +2 -2
  19. package/dist/LaunchMsaView/components/BlastQuery/CachedBlastResults.js +14 -13
  20. package/dist/LaunchMsaView/components/BlastQuery/consts.d.ts +2 -0
  21. package/dist/LaunchMsaView/components/BlastQuery/consts.js +28 -4
  22. package/dist/LaunchMsaView/components/HelpButton.d.ts +2 -0
  23. package/dist/LaunchMsaView/components/HelpButton.js +17 -0
  24. package/dist/LaunchMsaView/components/HelpDialog.d.ts +4 -0
  25. package/dist/LaunchMsaView/components/HelpDialog.js +18 -0
  26. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.d.ts +3 -1
  27. package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +21 -16
  28. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.d.ts +3 -1
  29. package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +34 -26
  30. package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.d.ts +3 -1
  31. package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.js +30 -26
  32. package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.d.ts +2 -1
  33. package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.js +18 -2
  34. package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.d.ts +1 -0
  35. package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.js +13 -0
  36. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.d.ts +3 -1
  37. package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +50 -27
  38. package/dist/LaunchMsaView/components/SequenceStatus.d.ts +14 -0
  39. package/dist/LaunchMsaView/components/SequenceStatus.js +15 -0
  40. package/dist/LaunchMsaView/components/SubmitCancelActions.d.ts +6 -2
  41. package/dist/LaunchMsaView/components/SubmitCancelActions.js +28 -13
  42. package/dist/LaunchMsaView/components/SubmitCancelActions.test.js +52 -4
  43. package/dist/LaunchMsaView/components/TabPanel.js +15 -3
  44. package/dist/LaunchMsaView/components/TabPanel.test.d.ts +1 -0
  45. package/dist/LaunchMsaView/components/TabPanel.test.js +36 -0
  46. package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +3 -1
  47. package/dist/LaunchMsaView/components/TranscriptSelector.js +4 -2
  48. package/dist/LaunchMsaView/components/calculateProteinSequence.d.ts +12 -2
  49. package/dist/LaunchMsaView/components/calculateProteinSequence.js +38 -23
  50. package/dist/LaunchMsaView/components/fetchSeq.d.ts +4 -1
  51. package/dist/LaunchMsaView/components/fetchSeq.js +14 -4
  52. package/dist/LaunchMsaView/components/launchConnectedView.d.ts +23 -0
  53. package/dist/LaunchMsaView/components/launchConnectedView.js +39 -0
  54. package/dist/LaunchMsaView/components/launchPlacement.d.ts +8 -0
  55. package/dist/LaunchMsaView/components/launchPlacement.js +25 -0
  56. package/dist/LaunchMsaView/components/useFeatureSequence.d.ts +2 -0
  57. package/dist/LaunchMsaView/components/useFeatureSequence.js +14 -11
  58. package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +10 -1
  59. package/dist/LaunchMsaView/components/useTranscriptSelection.js +21 -3
  60. package/dist/LaunchMsaView/components/useTranscriptSelection.test.d.ts +1 -0
  61. package/dist/LaunchMsaView/components/useTranscriptSelection.test.js +57 -0
  62. package/dist/LaunchMsaView/detectQueryRow.js +4 -7
  63. package/dist/LaunchMsaView/index.js +28 -9
  64. package/dist/LaunchMsaView/launchTarget.d.ts +36 -8
  65. package/dist/LaunchMsaView/launchTarget.js +23 -13
  66. package/dist/LaunchMsaView/launchTarget.test.js +80 -15
  67. package/dist/LaunchMsaView/useQueryRowName.d.ts +12 -0
  68. package/dist/LaunchMsaView/useQueryRowName.js +11 -0
  69. package/dist/LaunchMsaView/useQueryRowName.test.js +18 -1
  70. package/dist/LaunchMsaView/util.d.ts +6 -0
  71. package/dist/LaunchMsaView/util.js +12 -3
  72. package/dist/LaunchMsaViewExtensionPoint/index.test.js +15 -0
  73. package/dist/MsaViewPanel/afterCreateAutoruns.d.ts +6 -4
  74. package/dist/MsaViewPanel/afterCreateAutoruns.js +38 -14
  75. package/dist/MsaViewPanel/applyRegion.d.ts +23 -0
  76. package/dist/MsaViewPanel/applyRegion.js +19 -0
  77. package/dist/MsaViewPanel/applyRegion.test.d.ts +1 -0
  78. package/dist/MsaViewPanel/applyRegion.test.js +45 -0
  79. package/dist/MsaViewPanel/components/LaunchProgress.d.ts +4 -3
  80. package/dist/MsaViewPanel/components/LaunchProgress.js +10 -7
  81. package/dist/MsaViewPanel/components/MsaViewPanel.js +7 -3
  82. package/dist/MsaViewPanel/components/MsaViewPanel.test.js +24 -0
  83. package/dist/MsaViewPanel/doLaunchBlast.js +2 -1
  84. package/dist/MsaViewPanel/doLaunchBlast.test.js +12 -0
  85. package/dist/MsaViewPanel/doLaunchOrthologs.test.js +1 -1
  86. package/dist/MsaViewPanel/genomeToMSA.js +24 -6
  87. package/dist/MsaViewPanel/genomeToMSA.test.js +218 -44
  88. package/dist/MsaViewPanel/launchAutoruns.test.d.ts +1 -0
  89. package/dist/MsaViewPanel/launchAutoruns.test.js +36 -0
  90. package/dist/MsaViewPanel/model.d.ts +156 -61
  91. package/dist/MsaViewPanel/model.js +69 -3
  92. package/dist/MsaViewPanel/model.test.js +66 -0
  93. package/dist/MsaViewPanel/msaDataStore.d.ts +5 -3
  94. package/dist/MsaViewPanel/msaDataStore.js +3 -12
  95. package/dist/MsaViewPanel/msaDataStore.test.js +8 -1
  96. package/dist/MsaViewPanel/observeProteinHighlights.test.js +4 -4
  97. package/dist/MsaViewPanel/regionSnapshot.test.d.ts +1 -0
  98. package/dist/MsaViewPanel/regionSnapshot.test.js +29 -0
  99. package/dist/MsaViewPanel/resolveConnectedTranscript.d.ts +41 -4
  100. package/dist/MsaViewPanel/resolveConnectedTranscript.js +109 -68
  101. package/dist/MsaViewPanel/resolveConnectedTranscript.test.d.ts +1 -0
  102. package/dist/MsaViewPanel/resolveConnectedTranscript.test.js +149 -0
  103. package/dist/MsaViewPanel/runLaunch.d.ts +8 -4
  104. package/dist/MsaViewPanel/runLaunch.js +15 -4
  105. package/dist/MsaViewPanel/runLaunch.test.js +16 -0
  106. package/dist/MsaViewPanel/storedData.test.js +43 -0
  107. package/dist/MsaViewPanel/syncGenomeHoverToMsaColumn.test.js +15 -7
  108. package/dist/MsaViewPanel/util.d.ts +23 -23
  109. package/dist/MsaViewPanel/util.js +23 -24
  110. package/dist/index.d.ts +11 -11
  111. package/dist/index.js +17 -10
  112. package/dist/jbrowse-plugin-msaview.umd.production.min.js +54 -67
  113. package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
  114. package/dist/utils/blastCache.d.ts +26 -2
  115. package/dist/utils/blastCache.js +51 -39
  116. package/dist/utils/blastCache.test.js +46 -19
  117. package/dist/utils/domainCache.d.ts +1 -1
  118. package/dist/utils/domainCache.js +18 -14
  119. package/dist/utils/ebiJobDispatcher.d.ts +11 -0
  120. package/dist/utils/ebiJobDispatcher.js +12 -13
  121. package/dist/utils/ebiJobDispatcher.test.js +17 -3
  122. package/dist/utils/eutils.d.ts +14 -0
  123. package/dist/utils/eutils.js +48 -14
  124. package/dist/utils/eutils.test.d.ts +1 -0
  125. package/dist/utils/eutils.test.js +31 -0
  126. package/dist/utils/idb.d.ts +16 -3
  127. package/dist/utils/idb.js +51 -5
  128. package/dist/utils/idb.test.d.ts +1 -0
  129. package/dist/utils/idb.test.js +78 -0
  130. package/dist/utils/ncbiDomains.js +4 -4
  131. package/dist/utils/ncbiOrthologs.d.ts +1 -1
  132. package/dist/utils/ncbiOrthologs.js +18 -17
  133. package/dist/utils/ncbiOrthologs.test.js +17 -1
  134. package/dist/utils/ncbiTaxonomy.js +14 -15
  135. package/dist/utils/taxonomyNames.js +23 -19
  136. package/dist/utils/taxonomyNames.test.d.ts +1 -0
  137. package/dist/utils/taxonomyNames.test.js +33 -0
  138. package/dist/utils/unirefHomologs.js +2 -2
  139. package/dist/utils/unirefHomologs.test.js +8 -1
  140. package/dist/version.d.ts +1 -1
  141. package/dist/version.js +1 -1
  142. package/package.json +23 -23
  143. package/src/AddHighlightModel/index.tsx +1 -1
  144. package/src/BgzipFastaMsaAdapter/configSchema.ts +1 -1
  145. package/src/LaunchMsaView/cleanProteinSequence.test.ts +91 -0
  146. package/src/LaunchMsaView/codingFeature.test.ts +96 -0
  147. package/src/LaunchMsaView/codingFeature.ts +49 -0
  148. package/src/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.tsx +60 -56
  149. package/src/LaunchMsaView/components/BlastQuery/BlastManualPanel.tsx +18 -15
  150. package/src/LaunchMsaView/components/BlastQuery/BlastPanel.tsx +24 -11
  151. package/src/LaunchMsaView/components/BlastQuery/BlastSettingsDialog.tsx +4 -4
  152. package/src/LaunchMsaView/components/BlastQuery/CachedBlastResults.tsx +16 -15
  153. package/src/LaunchMsaView/components/BlastQuery/consts.ts +31 -4
  154. package/src/LaunchMsaView/components/HelpButton.tsx +33 -0
  155. package/src/LaunchMsaView/components/HelpDialog.tsx +78 -0
  156. package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +54 -34
  157. package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +46 -23
  158. package/src/LaunchMsaView/components/OrthologQuery/OrthologPanel.tsx +41 -28
  159. package/src/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.test.ts +16 -0
  160. package/src/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.tsx +24 -2
  161. package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +81 -25
  162. package/src/LaunchMsaView/components/SequenceStatus.tsx +29 -0
  163. package/src/LaunchMsaView/components/SubmitCancelActions.test.tsx +88 -4
  164. package/src/LaunchMsaView/components/SubmitCancelActions.tsx +43 -16
  165. package/src/LaunchMsaView/components/TabPanel.test.tsx +51 -0
  166. package/src/LaunchMsaView/components/TabPanel.tsx +16 -4
  167. package/src/LaunchMsaView/components/TranscriptSelector.tsx +6 -1
  168. package/src/LaunchMsaView/components/calculateProteinSequence.ts +51 -22
  169. package/src/LaunchMsaView/components/fetchSeq.ts +25 -5
  170. package/src/LaunchMsaView/components/launchConnectedView.ts +56 -0
  171. package/src/LaunchMsaView/components/launchPlacement.tsx +41 -0
  172. package/src/LaunchMsaView/components/useFeatureSequence.ts +18 -11
  173. package/src/LaunchMsaView/components/useTranscriptSelection.test.tsx +66 -0
  174. package/src/LaunchMsaView/components/useTranscriptSelection.ts +41 -2
  175. package/src/LaunchMsaView/detectQueryRow.ts +5 -7
  176. package/src/LaunchMsaView/index.ts +32 -13
  177. package/src/LaunchMsaView/launchTarget.test.ts +88 -15
  178. package/src/LaunchMsaView/launchTarget.ts +49 -14
  179. package/src/LaunchMsaView/useQueryRowName.test.ts +28 -1
  180. package/src/LaunchMsaView/useQueryRowName.ts +17 -0
  181. package/src/LaunchMsaView/util.ts +15 -5
  182. package/src/LaunchMsaViewExtensionPoint/index.test.ts +16 -0
  183. package/src/LaunchMsaViewExtensionPoint/index.ts +28 -1
  184. package/src/MsaViewPanel/afterCreateAutoruns.ts +46 -14
  185. package/src/MsaViewPanel/applyRegion.test.ts +55 -0
  186. package/src/MsaViewPanel/applyRegion.ts +33 -0
  187. package/src/MsaViewPanel/components/LaunchProgress.tsx +12 -5
  188. package/src/MsaViewPanel/components/MsaViewPanel.test.tsx +26 -0
  189. package/src/MsaViewPanel/components/MsaViewPanel.tsx +7 -5
  190. package/src/MsaViewPanel/doLaunchBlast.test.ts +20 -1
  191. package/src/MsaViewPanel/doLaunchBlast.ts +2 -1
  192. package/src/MsaViewPanel/doLaunchOrthologs.test.ts +1 -1
  193. package/src/MsaViewPanel/genomeToMSA.test.ts +255 -59
  194. package/src/MsaViewPanel/genomeToMSA.ts +30 -5
  195. package/src/MsaViewPanel/launchAutoruns.test.ts +45 -0
  196. package/src/MsaViewPanel/model.test.ts +75 -0
  197. package/src/MsaViewPanel/model.ts +76 -3
  198. package/src/MsaViewPanel/msaDataStore.test.ts +14 -2
  199. package/src/MsaViewPanel/msaDataStore.ts +4 -12
  200. package/src/MsaViewPanel/observeProteinHighlights.test.ts +4 -4
  201. package/src/MsaViewPanel/regionSnapshot.test.ts +34 -0
  202. package/src/MsaViewPanel/resolveConnectedTranscript.test.ts +183 -0
  203. package/src/MsaViewPanel/resolveConnectedTranscript.ts +160 -67
  204. package/src/MsaViewPanel/runLaunch.test.ts +19 -0
  205. package/src/MsaViewPanel/runLaunch.ts +21 -4
  206. package/src/MsaViewPanel/storedData.test.ts +58 -0
  207. package/src/MsaViewPanel/syncGenomeHoverToMsaColumn.test.ts +17 -7
  208. package/src/MsaViewPanel/util.ts +38 -29
  209. package/src/index.ts +19 -10
  210. package/src/utils/blastCache.test.ts +59 -20
  211. package/src/utils/blastCache.ts +62 -39
  212. package/src/utils/domainCache.ts +27 -15
  213. package/src/utils/ebiJobDispatcher.test.ts +18 -3
  214. package/src/utils/ebiJobDispatcher.ts +12 -16
  215. package/src/utils/eutils.test.ts +39 -0
  216. package/src/utils/eutils.ts +64 -14
  217. package/src/utils/idb.test.ts +106 -0
  218. package/src/utils/idb.ts +57 -5
  219. package/src/utils/ncbiDomains.ts +4 -4
  220. package/src/utils/ncbiOrthologs.test.ts +20 -0
  221. package/src/utils/ncbiOrthologs.ts +26 -28
  222. package/src/utils/ncbiTaxonomy.ts +17 -20
  223. package/src/utils/taxonomyNames.test.ts +41 -0
  224. package/src/utils/taxonomyNames.ts +33 -19
  225. package/src/utils/unirefHomologs.test.ts +11 -1
  226. package/src/utils/unirefHomologs.ts +2 -2
  227. package/src/version.ts +1 -1
  228. package/dist/LaunchMsaView/components/BlastQuery/blastLaunchView.d.ts +0 -16
  229. package/dist/LaunchMsaView/components/BlastQuery/blastLaunchView.js +0 -31
  230. package/dist/LaunchMsaView/components/ManualMSALoader/launchView.d.ts +0 -16
  231. package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js +0 -16
  232. package/dist/LaunchMsaView/components/OrthologQuery/orthologLaunchView.d.ts +0 -9
  233. package/dist/LaunchMsaView/components/OrthologQuery/orthologLaunchView.js +0 -15
  234. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.d.ts +0 -11
  235. package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js +0 -21
  236. package/dist/LaunchMsaView/components/geneticCodes.d.ts +0 -15
  237. package/dist/LaunchMsaView/components/geneticCodes.js +0 -227
  238. package/src/LaunchMsaView/components/BlastQuery/blastLaunchView.ts +0 -59
  239. package/src/LaunchMsaView/components/ManualMSALoader/launchView.ts +0 -43
  240. package/src/LaunchMsaView/components/OrthologQuery/orthologLaunchView.ts +0 -31
  241. package/src/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.ts +0 -38
  242. package/src/LaunchMsaView/components/geneticCodes.ts +0 -298
@@ -6,12 +6,40 @@ vi.mock('@jbrowse/core/util', () => ({
6
6
  getSession: vi.fn(),
7
7
  }));
8
8
  const mockGetSession = vi.mocked(getSession);
9
+ // What the assembly calls each chromosome. Empty means every name is already
10
+ // canonical, which is the config where the raw comparison this replaced
11
+ // happened to work.
12
+ let aliases = {};
13
+ // core's assembly throws from getCanonicalRefName until its aliases load, and
14
+ // answers `initialized` false until then -- so the stand-in throws too, or the
15
+ // guard is asserted against a method that never had the failure mode
16
+ let assemblyInitialized = true;
17
+ function mockSession(session) {
18
+ mockGetSession.mockReturnValue({
19
+ assemblyManager: {
20
+ get: () => ({
21
+ get initialized() {
22
+ return assemblyInitialized;
23
+ },
24
+ getCanonicalRefName: (refName) => {
25
+ if (!assemblyInitialized) {
26
+ throw new Error('aliases not loaded');
27
+ }
28
+ return aliases[refName] ?? refName;
29
+ },
30
+ }),
31
+ },
32
+ ...session,
33
+ });
34
+ }
9
35
  describe('genomeToMSA', () => {
10
36
  beforeEach(() => {
11
37
  vi.clearAllMocks();
38
+ aliases = {};
39
+ assemblyInitialized = true;
12
40
  });
13
41
  test('returns undefined when connectedView is not initialized', () => {
14
- mockGetSession.mockReturnValue({
42
+ mockSession({
15
43
  hovered: {
16
44
  hoverFeature: {},
17
45
  hoverPosition: { coord: 1005, refName: 'chr1' },
@@ -19,7 +47,7 @@ describe('genomeToMSA', () => {
19
47
  });
20
48
  const model = {
21
49
  querySeqName: 'hg38.chr1',
22
- rows: [['hg38.chr1', 'ACGTACGTAC']],
50
+ rowMap: new Map([['hg38.chr1', 'ACGTACGTAC']]),
23
51
  transcriptToMsaMap: undefined,
24
52
  mafRegion: {
25
53
  refName: 'chr1',
@@ -27,19 +55,19 @@ describe('genomeToMSA', () => {
27
55
  end: 1010,
28
56
  assemblyName: 'hg38',
29
57
  },
30
- connectedView: { initialized: false },
58
+ connectedView: { initialized: false, assemblyNames: ['hg38'] },
31
59
  seqPosToVisibleCol: vi.fn(),
32
60
  };
33
61
  const result = genomeToMSA({ model });
34
62
  expect(result).toBeUndefined();
35
63
  });
36
64
  test('returns undefined when hovered is not valid', () => {
37
- mockGetSession.mockReturnValue({
65
+ mockSession({
38
66
  hovered: null,
39
67
  });
40
68
  const model = {
41
69
  querySeqName: 'hg38.chr1',
42
- rows: [['hg38.chr1', 'ACGTACGTAC']],
70
+ rowMap: new Map([['hg38.chr1', 'ACGTACGTAC']]),
43
71
  transcriptToMsaMap: undefined,
44
72
  mafRegion: {
45
73
  refName: 'chr1',
@@ -47,7 +75,7 @@ describe('genomeToMSA', () => {
47
75
  end: 1010,
48
76
  assemblyName: 'hg38',
49
77
  },
50
- connectedView: { initialized: true },
78
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
51
79
  seqPosToVisibleCol: vi.fn(),
52
80
  };
53
81
  const result = genomeToMSA({ model });
@@ -55,7 +83,7 @@ describe('genomeToMSA', () => {
55
83
  });
56
84
  describe('mafRegion mapping', () => {
57
85
  test('returns visible column for valid hover within mafRegion', () => {
58
- mockGetSession.mockReturnValue({
86
+ mockSession({
59
87
  hovered: {
60
88
  hoverFeature: {},
61
89
  hoverPosition: { coord: 1005, refName: 'chr1' },
@@ -64,7 +92,7 @@ describe('genomeToMSA', () => {
64
92
  const mockSeqPosToVisibleCol = vi.fn().mockReturnValue(5);
65
93
  const model = {
66
94
  querySeqName: 'hg38.chr1',
67
- rows: [['hg38.chr1', 'ACGTACGTAC']],
95
+ rowMap: new Map([['hg38.chr1', 'ACGTACGTAC']]),
68
96
  transcriptToMsaMap: undefined,
69
97
  mafRegion: {
70
98
  refName: 'chr1',
@@ -77,6 +105,7 @@ describe('genomeToMSA', () => {
77
105
  assemblyNames: ['hg38'],
78
106
  },
79
107
  seqPosToVisibleCol: mockSeqPosToVisibleCol,
108
+ visibleColToSeqPos: () => 4,
80
109
  };
81
110
  const result = genomeToMSA({ model });
82
111
  // hover coord 1005 is 1-based, so the 0-based genome position is 1004,
@@ -84,8 +113,36 @@ describe('genomeToMSA', () => {
84
113
  expect(mockSeqPosToVisibleCol).toHaveBeenCalledWith('hg38.chr1', 4);
85
114
  expect(result).toBe(5);
86
115
  });
116
+ test('a position past the end of the row lights nothing', () => {
117
+ mockSession({
118
+ hovered: {
119
+ hoverFeature: {},
120
+ hoverPosition: { coord: 1009, refName: 'chr1' },
121
+ },
122
+ });
123
+ // react-msaview answers one column past the end for a residue the row
124
+ // does not have, and that column maps back to no residue
125
+ const model = {
126
+ querySeqName: 'hg38.chr1',
127
+ rowMap: new Map([['hg38.chr1', 'ACGTACG']]),
128
+ transcriptToMsaMap: undefined,
129
+ mafRegion: {
130
+ refName: 'chr1',
131
+ start: 1000,
132
+ end: 1010,
133
+ assemblyName: 'hg38',
134
+ },
135
+ connectedView: {
136
+ initialized: true,
137
+ assemblyNames: ['hg38'],
138
+ },
139
+ seqPosToVisibleCol: () => 7,
140
+ visibleColToSeqPos: () => undefined,
141
+ };
142
+ expect(genomeToMSA({ model })).toBeUndefined();
143
+ });
87
144
  test('returns undefined when hover refName does not match mafRegion', () => {
88
- mockGetSession.mockReturnValue({
145
+ mockSession({
89
146
  hovered: {
90
147
  hoverFeature: {},
91
148
  hoverPosition: { coord: 1005, refName: 'chr2' },
@@ -93,7 +150,7 @@ describe('genomeToMSA', () => {
93
150
  });
94
151
  const model = {
95
152
  querySeqName: 'hg38.chr1',
96
- rows: [['hg38.chr1', 'ACGTACGTAC']],
153
+ rowMap: new Map([['hg38.chr1', 'ACGTACGTAC']]),
97
154
  transcriptToMsaMap: undefined,
98
155
  mafRegion: {
99
156
  refName: 'chr1',
@@ -112,7 +169,7 @@ describe('genomeToMSA', () => {
112
169
  });
113
170
  test('returns undefined when hover coord is before mafRegion start', () => {
114
171
  // 1-based coord 1000 is the 0-based base 999, one before the region
115
- mockGetSession.mockReturnValue({
172
+ mockSession({
116
173
  hovered: {
117
174
  hoverFeature: {},
118
175
  hoverPosition: { coord: 1000, refName: 'chr1' },
@@ -120,7 +177,7 @@ describe('genomeToMSA', () => {
120
177
  });
121
178
  const model = {
122
179
  querySeqName: 'hg38.chr1',
123
- rows: [['hg38.chr1', 'ACGTACGTAC']],
180
+ rowMap: new Map([['hg38.chr1', 'ACGTACGTAC']]),
124
181
  transcriptToMsaMap: undefined,
125
182
  mafRegion: {
126
183
  refName: 'chr1',
@@ -139,7 +196,7 @@ describe('genomeToMSA', () => {
139
196
  });
140
197
  test('returns undefined when hover coord is at or after mafRegion end', () => {
141
198
  // 1-based coord 1011 is the 0-based base 1010, one past the region
142
- mockGetSession.mockReturnValue({
199
+ mockSession({
143
200
  hovered: {
144
201
  hoverFeature: {},
145
202
  hoverPosition: { coord: 1011, refName: 'chr1' },
@@ -147,7 +204,7 @@ describe('genomeToMSA', () => {
147
204
  });
148
205
  const model = {
149
206
  querySeqName: 'hg38.chr1',
150
- rows: [['hg38.chr1', 'ACGTACGTAC']],
207
+ rowMap: new Map([['hg38.chr1', 'ACGTACGTAC']]),
151
208
  transcriptToMsaMap: undefined,
152
209
  mafRegion: {
153
210
  refName: 'chr1',
@@ -165,7 +222,7 @@ describe('genomeToMSA', () => {
165
222
  expect(result).toBeUndefined();
166
223
  });
167
224
  test('returns undefined when assembly does not match', () => {
168
- mockGetSession.mockReturnValue({
225
+ mockSession({
169
226
  hovered: {
170
227
  hoverFeature: {},
171
228
  hoverPosition: { coord: 1005, refName: 'chr1' },
@@ -173,7 +230,7 @@ describe('genomeToMSA', () => {
173
230
  });
174
231
  const model = {
175
232
  querySeqName: 'hg38.chr1',
176
- rows: [['hg38.chr1', 'ACGTACGTAC']],
233
+ rowMap: new Map([['hg38.chr1', 'ACGTACGTAC']]),
177
234
  transcriptToMsaMap: undefined,
178
235
  mafRegion: {
179
236
  refName: 'chr1',
@@ -193,7 +250,7 @@ describe('genomeToMSA', () => {
193
250
  });
194
251
  describe('transcriptToMsaMap mapping (original behavior)', () => {
195
252
  test('returns visible column using g2p mapping', () => {
196
- mockGetSession.mockReturnValue({
253
+ mockSession({
197
254
  hovered: {
198
255
  hoverFeature: {},
199
256
  hoverPosition: { coord: 1005, refName: 'chr1' },
@@ -203,14 +260,14 @@ describe('genomeToMSA', () => {
203
260
  const model = {
204
261
  querySeqName: 'QUERY',
205
262
  querySeqOffset: 0,
206
- rows: [['QUERY', 'MKVLTAEEK']],
263
+ rowMap: new Map([['QUERY', 'MKVLTAEEK']]),
207
264
  transcriptToMsaMap: {
208
265
  refName: 'chr1',
209
266
  // g2p is keyed by 0-based genome position, the hover coord is 1-based
210
267
  g2p: { 1004: 10 },
211
268
  },
212
269
  mafRegion: undefined,
213
- connectedView: { initialized: true },
270
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
214
271
  seqPosToVisibleCol: mockSeqPosToVisibleCol,
215
272
  visibleColToSeqPos: (_name, col) => col,
216
273
  };
@@ -221,7 +278,7 @@ describe('genomeToMSA', () => {
221
278
  test('returns undefined when the hover is on another refName', () => {
222
279
  // session.hovered is global, so a hover on an unrelated chromosome can
223
280
  // carry a coordinate that happens to be a g2p key
224
- mockGetSession.mockReturnValue({
281
+ mockSession({
225
282
  hovered: {
226
283
  hoverFeature: {},
227
284
  hoverPosition: { coord: 1005, refName: 'chr2' },
@@ -230,13 +287,13 @@ describe('genomeToMSA', () => {
230
287
  const mockSeqPosToVisibleCol = vi.fn();
231
288
  const model = {
232
289
  querySeqName: 'QUERY',
233
- rows: [['QUERY', 'MKVLTAEEK']],
290
+ rowMap: new Map([['QUERY', 'MKVLTAEEK']]),
234
291
  transcriptToMsaMap: {
235
292
  refName: 'chr1',
236
293
  g2p: { 1004: 10 },
237
294
  },
238
295
  mafRegion: undefined,
239
- connectedView: { initialized: true },
296
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
240
297
  seqPosToVisibleCol: mockSeqPosToVisibleCol,
241
298
  };
242
299
  expect(genomeToMSA({ model })).toBeUndefined();
@@ -245,7 +302,7 @@ describe('genomeToMSA', () => {
245
302
  // a pasted alignment often carries only the region BLAST aligned, and
246
303
  // querySeqOffset says how much of the protein comes before it
247
304
  test('shifts a trimmed query row by querySeqOffset', () => {
248
- mockGetSession.mockReturnValue({
305
+ mockSession({
249
306
  hovered: {
250
307
  hoverFeature: {},
251
308
  hoverPosition: { coord: 1005, refName: 'chr1' },
@@ -255,10 +312,10 @@ describe('genomeToMSA', () => {
255
312
  const model = {
256
313
  querySeqName: 'QUERY',
257
314
  querySeqOffset: 4,
258
- rows: [['QUERY', 'MKVLTAEEK']],
315
+ rowMap: new Map([['QUERY', 'MKVLTAEEK']]),
259
316
  transcriptToMsaMap: { refName: 'chr1', g2p: { 1004: 10 } },
260
317
  mafRegion: undefined,
261
- connectedView: { initialized: true },
318
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
262
319
  seqPosToVisibleCol,
263
320
  visibleColToSeqPos: (_name, col) => col,
264
321
  };
@@ -266,7 +323,7 @@ describe('genomeToMSA', () => {
266
323
  expect(seqPosToVisibleCol).toHaveBeenCalledWith('QUERY', 6);
267
324
  });
268
325
  test('maps nothing for a residue before the trimmed row starts', () => {
269
- mockGetSession.mockReturnValue({
326
+ mockSession({
270
327
  hovered: {
271
328
  hoverFeature: {},
272
329
  hoverPosition: { coord: 1005, refName: 'chr1' },
@@ -276,10 +333,10 @@ describe('genomeToMSA', () => {
276
333
  const model = {
277
334
  querySeqName: 'QUERY',
278
335
  querySeqOffset: 20,
279
- rows: [['QUERY', 'MKVLTAEEK']],
336
+ rowMap: new Map([['QUERY', 'MKVLTAEEK']]),
280
337
  transcriptToMsaMap: { refName: 'chr1', g2p: { 1004: 10 } },
281
338
  mafRegion: undefined,
282
- connectedView: { initialized: true },
339
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
283
340
  seqPosToVisibleCol,
284
341
  visibleColToSeqPos: (_name, col) => col,
285
342
  };
@@ -289,7 +346,7 @@ describe('genomeToMSA', () => {
289
346
  // react-msaview answers one column past the end for a residue the row does
290
347
  // not have, which would light the last column for everything beyond it
291
348
  test('maps nothing for a residue the row stops short of', () => {
292
- mockGetSession.mockReturnValue({
349
+ mockSession({
293
350
  hovered: {
294
351
  hoverFeature: {},
295
352
  hoverPosition: { coord: 1005, refName: 'chr1' },
@@ -298,17 +355,17 @@ describe('genomeToMSA', () => {
298
355
  const model = {
299
356
  querySeqName: 'QUERY',
300
357
  querySeqOffset: 0,
301
- rows: [['QUERY', 'MKV']],
358
+ rowMap: new Map([['QUERY', 'MKV']]),
302
359
  transcriptToMsaMap: { refName: 'chr1', g2p: { 1004: 10 } },
303
360
  mafRegion: undefined,
304
- connectedView: { initialized: true },
361
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
305
362
  seqPosToVisibleCol: () => 3,
306
363
  visibleColToSeqPos: () => undefined,
307
364
  };
308
365
  expect(genomeToMSA({ model })).toBeUndefined();
309
366
  });
310
367
  test('returns undefined when g2p has no mapping for coord', () => {
311
- mockGetSession.mockReturnValue({
368
+ mockSession({
312
369
  hovered: {
313
370
  hoverFeature: {},
314
371
  hoverPosition: { coord: 1005, refName: 'chr1' },
@@ -316,13 +373,13 @@ describe('genomeToMSA', () => {
316
373
  });
317
374
  const model = {
318
375
  querySeqName: 'QUERY',
319
- rows: [['QUERY', 'MKVLTAEEK']],
376
+ rowMap: new Map([['QUERY', 'MKVLTAEEK']]),
320
377
  transcriptToMsaMap: {
321
378
  refName: 'chr1',
322
379
  g2p: { 1000: 0 }, // No entry for 1004
323
380
  },
324
381
  mafRegion: undefined,
325
- connectedView: { initialized: true },
382
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
326
383
  seqPosToVisibleCol: vi.fn(),
327
384
  };
328
385
  const result = genomeToMSA({ model });
@@ -330,7 +387,7 @@ describe('genomeToMSA', () => {
330
387
  });
331
388
  });
332
389
  test('returns undefined when neither mafRegion nor transcriptToMsaMap is set', () => {
333
- mockGetSession.mockReturnValue({
390
+ mockSession({
334
391
  hovered: {
335
392
  hoverFeature: {},
336
393
  hoverPosition: { coord: 1005, refName: 'chr1' },
@@ -338,21 +395,17 @@ describe('genomeToMSA', () => {
338
395
  });
339
396
  const model = {
340
397
  querySeqName: 'QUERY',
341
- rows: [['QUERY', 'MKVLTAEEK']],
398
+ rowMap: new Map([['QUERY', 'MKVLTAEEK']]),
342
399
  transcriptToMsaMap: undefined,
343
400
  mafRegion: undefined,
344
- connectedView: { initialized: true },
401
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
345
402
  seqPosToVisibleCol: vi.fn(),
346
403
  };
347
404
  const result = genomeToMSA({ model });
348
405
  expect(result).toBeUndefined();
349
406
  });
350
- // seqPosToVisibleCol answers 0 for a row name it does not know, so without a
351
- // guard an alignment whose query row is missing -- the default 'QUERY' on an
352
- // uploaded file, or the empty name the manual panel leaves when it matches
353
- // nothing -- lights column 0 on every genome hover
354
407
  test('returns undefined when querySeqName names no row here', () => {
355
- mockGetSession.mockReturnValue({
408
+ mockSession({
356
409
  hovered: {
357
410
  hoverFeature: {},
358
411
  hoverPosition: { coord: 1005, refName: 'chr1' },
@@ -361,13 +414,134 @@ describe('genomeToMSA', () => {
361
414
  const seqPosToVisibleCol = vi.fn(() => 0);
362
415
  const model = {
363
416
  querySeqName: 'QUERY',
364
- rows: [['some_other_row', 'MKVLTAEEK']],
417
+ rowMap: new Map([['some_other_row', 'MKVLTAEEK']]),
365
418
  transcriptToMsaMap: { refName: 'chr1', g2p: { 1004: 3 } },
366
419
  mafRegion: undefined,
367
- connectedView: { initialized: true },
420
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
368
421
  seqPosToVisibleCol,
369
422
  };
370
423
  expect(genomeToMSA({ model })).toBeUndefined();
371
424
  expect(seqPosToVisibleCol).not.toHaveBeenCalled();
372
425
  });
426
+ test('a query row folded into a collapsed clade still maps', () => {
427
+ mockSession({
428
+ hovered: {
429
+ hoverFeature: {},
430
+ hoverPosition: { coord: 1005, refName: 'chr1' },
431
+ },
432
+ });
433
+ const model = {
434
+ querySeqName: 'QUERY',
435
+ querySeqOffset: 0,
436
+ rows: [['collapsed_clade', 'MKVLTAEEK']],
437
+ rowMap: new Map([['QUERY', 'MKVLTAEEK']]),
438
+ transcriptToMsaMap: { refName: 'chr1', g2p: { 1004: 3 } },
439
+ mafRegion: undefined,
440
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
441
+ seqPosToVisibleCol: (_name, pos) => pos,
442
+ visibleColToSeqPos: (_name, col) => col,
443
+ };
444
+ expect(genomeToMSA({ model })).toBe(3);
445
+ });
446
+ // A hover names the chromosome the assembly's way and the transcript names it
447
+ // the annotation file's way, so on GENCODE-on-hg38 -- jbrowse.org's own
448
+ // config -- the two are '1' and 'chr1' and pointing at a codon lit nothing.
449
+ describe('a config whose assembly and annotation spell the refName apart', () => {
450
+ beforeEach(() => {
451
+ aliases = { chr1: '1', chr2: '2' };
452
+ });
453
+ test('maps a hover named 1 onto a transcript named chr1', () => {
454
+ mockSession({
455
+ hovered: {
456
+ hoverFeature: {},
457
+ hoverPosition: { coord: 1005, refName: '1' },
458
+ },
459
+ });
460
+ const seqPosToVisibleCol = vi.fn().mockReturnValue(10);
461
+ const model = {
462
+ querySeqName: 'QUERY',
463
+ querySeqOffset: 0,
464
+ rowMap: new Map([['QUERY', 'MKVLTAEEK']]),
465
+ transcriptToMsaMap: { refName: 'chr1', g2p: { 1004: 10 } },
466
+ mafRegion: undefined,
467
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
468
+ seqPosToVisibleCol,
469
+ visibleColToSeqPos: (_name, col) => col,
470
+ };
471
+ expect(genomeToMSA({ model })).toBe(10);
472
+ expect(seqPosToVisibleCol).toHaveBeenCalledWith('QUERY', 10);
473
+ });
474
+ // the gate still has to reject another chromosome, which is the whole
475
+ // reason it is there
476
+ test('still rejects a hover on another chromosome', () => {
477
+ mockSession({
478
+ hovered: {
479
+ hoverFeature: {},
480
+ hoverPosition: { coord: 1005, refName: '2' },
481
+ },
482
+ });
483
+ const seqPosToVisibleCol = vi.fn();
484
+ const model = {
485
+ querySeqName: 'QUERY',
486
+ rowMap: new Map([['QUERY', 'MKVLTAEEK']]),
487
+ transcriptToMsaMap: { refName: 'chr1', g2p: { 1004: 10 } },
488
+ mafRegion: undefined,
489
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
490
+ seqPosToVisibleCol,
491
+ };
492
+ expect(genomeToMSA({ model })).toBeUndefined();
493
+ expect(seqPosToVisibleCol).not.toHaveBeenCalled();
494
+ });
495
+ // this runs from a hover autorun, and mafRegion names an assembly off a
496
+ // frozen spec that nothing here waited for -- so the aliases may not be
497
+ // loaded, and asking for them throws
498
+ test('falls back to the raw name while the aliases are still loading', () => {
499
+ assemblyInitialized = false;
500
+ mockSession({
501
+ hovered: {
502
+ hoverFeature: {},
503
+ hoverPosition: { coord: 1005, refName: 'chr1' },
504
+ },
505
+ });
506
+ const seqPosToVisibleCol = vi.fn().mockReturnValue(10);
507
+ const model = {
508
+ querySeqName: 'QUERY',
509
+ querySeqOffset: 0,
510
+ rowMap: new Map([['QUERY', 'MKVLTAEEK']]),
511
+ transcriptToMsaMap: { refName: 'chr1', g2p: { 1004: 10 } },
512
+ mafRegion: undefined,
513
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
514
+ seqPosToVisibleCol,
515
+ visibleColToSeqPos: (_name, col) => col,
516
+ };
517
+ expect(() => genomeToMSA({ model })).not.toThrow();
518
+ // both sides spell it 'chr1' here, so the raw comparison still answers
519
+ expect(genomeToMSA({ model })).toBe(10);
520
+ });
521
+ test('maps a hover named 1 into a mafRegion named chr1', () => {
522
+ mockSession({
523
+ hovered: {
524
+ hoverFeature: {},
525
+ hoverPosition: { coord: 1005, refName: '1' },
526
+ },
527
+ });
528
+ const seqPosToVisibleCol = vi.fn().mockReturnValue(5);
529
+ const model = {
530
+ querySeqName: 'hg38.chr1',
531
+ rowMap: new Map([['hg38.chr1', 'ACGTACGTAC']]),
532
+ transcriptToMsaMap: undefined,
533
+ mafRegion: {
534
+ refName: 'chr1',
535
+ start: 1000,
536
+ end: 1010,
537
+ assemblyName: 'hg38',
538
+ },
539
+ connectedView: { initialized: true, assemblyNames: ['hg38'] },
540
+ seqPosToVisibleCol,
541
+ visibleColToSeqPos: () => 4,
542
+ };
543
+ expect(genomeToMSA({ model })).toBe(5);
544
+ expect(seqPosToVisibleCol).toHaveBeenCalledWith('hg38.chr1', 4);
545
+ });
546
+ });
373
547
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ import { expect, test, vi } from 'vitest';
2
+ import { doLaunchBlast } from './doLaunchBlast';
3
+ import stateModelFactory from './model';
4
+ vi.mock('@jbrowse/core/util', async (importOriginal) => ({
5
+ ...(await importOriginal()),
6
+ getSession: () => ({ views: [], hovered: undefined }),
7
+ }));
8
+ vi.mock('./doLaunchBlast', () => ({ doLaunchBlast: vi.fn() }));
9
+ vi.mock('./msaDataStore', () => ({
10
+ cleanupOldData: vi.fn(async () => { }),
11
+ deleteMsaData: vi.fn(async () => { }),
12
+ generateDataStoreId: vi.fn(),
13
+ retrieveMsaData: vi.fn(),
14
+ storeMsaData: vi.fn(),
15
+ }));
16
+ // the real launch reads querySeqName before its first await, as resolveQuery
17
+ // does, which used to make that name a reason to launch again
18
+ test('a write the launch body read does not start a second launch', () => {
19
+ vi.mocked(doLaunchBlast).mockImplementation(({ self }) => {
20
+ void self.querySeqName;
21
+ return new Promise(() => { });
22
+ });
23
+ const model = stateModelFactory().create({
24
+ type: 'MsaView',
25
+ id: 'msa1',
26
+ blastParams: {
27
+ searchProgram: 'blastp',
28
+ blastDatabase: 'uniprotkb_swissprot',
29
+ msaAlgorithm: 'clustalo',
30
+ proteinSequence: 'MKV',
31
+ },
32
+ });
33
+ expect(doLaunchBlast).toHaveBeenCalledTimes(1);
34
+ model.setQuerySeqName('P53_HUMAN_query');
35
+ expect(doLaunchBlast).toHaveBeenCalledTimes(1);
36
+ });