Merge branch 'develop' into update_212_Dec_merge_with_21125_chamges
[jalview.git] / test / jalview / gui / PopupMenuTest.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import static jalview.util.UrlConstants.DB_ACCESSION;
24 import static jalview.util.UrlConstants.SEQUENCE_ID;
25 import static org.testng.AssertJUnit.assertEquals;
26 import static org.testng.AssertJUnit.assertFalse;
27 import static org.testng.AssertJUnit.assertNotNull;
28 import static org.testng.AssertJUnit.assertNull;
29 import static org.testng.AssertJUnit.assertTrue;
30
31 import java.awt.Component;
32 import java.awt.Container;
33 import java.io.IOException;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.Iterator;
37 import java.util.List;
38
39 import javax.swing.JMenu;
40 import javax.swing.JMenuItem;
41 import javax.swing.JPopupMenu;
42 import javax.swing.JSeparator;
43
44 import org.testng.annotations.BeforeClass;
45 import org.testng.annotations.BeforeMethod;
46 import org.testng.annotations.Test;
47
48 import jalview.bin.Cache;
49 import jalview.bin.Console;
50 import jalview.datamodel.AlignmentAnnotation;
51 import jalview.datamodel.AlignmentI;
52 import jalview.datamodel.Annotation;
53 import jalview.datamodel.ColumnSelection;
54 import jalview.datamodel.DBRefEntry;
55 import jalview.datamodel.DBRefSource;
56 import jalview.datamodel.HiddenColumns;
57 import jalview.datamodel.Sequence;
58 import jalview.datamodel.SequenceFeature;
59 import jalview.datamodel.SequenceGroup;
60 import jalview.datamodel.SequenceI;
61 import jalview.io.DataSourceType;
62 import jalview.io.FileFormat;
63 import jalview.io.FormatAdapter;
64 import jalview.urls.api.UrlProviderFactoryI;
65 import jalview.urls.desktop.DesktopUrlProviderFactory;
66 import jalview.util.MessageManager;
67 import jalview.util.UrlConstants;
68
69 public class PopupMenuTest
70 {
71
72   @BeforeClass(alwaysRun = true)
73   public void setUpJvOptionPane()
74   {
75     JvOptionPane.setInteractiveMode(false);
76     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
77   }
78
79   // 4 sequences x 13 positions
80   final static String TEST_DATA = ">FER_CAPAA Ferredoxin\n"
81           + "TIETHKEAELVG-\n"
82           + ">FER_CAPAN Ferredoxin, chloroplast precursor\n"
83           + "TIETHKEAELVG-\n"
84           + ">FER1_SOLLC Ferredoxin-1, chloroplast precursor\n"
85           + "TIETHKEEELTA-\n" + ">Q93XJ9_SOLTU Ferredoxin I precursor\n"
86           + "TIETHKEEELTA-\n";
87
88   AlignmentI alignment;
89
90   AlignmentPanel parentPanel;
91
92   PopupMenu testee = null;
93
94   @BeforeMethod(alwaysRun = true)
95   public void setUp() throws IOException
96   {
97     Cache.loadProperties("test/jalview/io/testProps.jvprops");
98     Console.initLogger();
99
100     String inMenuString = ("EMBL-EBI Search | http://www.ebi.ac.uk/ebisearch/search.ebi?db=allebi&query=$"
101             + SEQUENCE_ID + "$" + "|"
102             + "UNIPROT | http://www.uniprot.org/uniprot/$" + DB_ACCESSION
103             + "$") + "|"
104             + ("INTERPRO | http://www.ebi.ac.uk/interpro/entry/$"
105                     + DB_ACCESSION + "$")
106             + "|" +
107             // Gene3D entry tests for case (in)sensitivity
108             ("Gene3D | http://gene3d.biochem.ucl.ac.uk/Gene3D/search?sterm=$"
109                     + DB_ACCESSION + "$&mode=protein");
110
111     UrlProviderFactoryI factory = new DesktopUrlProviderFactory(
112             UrlConstants.DEFAULT_LABEL, inMenuString, "");
113     Preferences.sequenceUrlLinks = factory.createUrlProvider();
114
115     alignment = new FormatAdapter().readFile(TEST_DATA,
116             DataSourceType.PASTE, FileFormat.Fasta);
117     AlignFrame af = new AlignFrame(alignment, 700, 500);
118     parentPanel = new AlignmentPanel(af, af.getViewport());
119     testee = new PopupMenu(parentPanel, alignment.getSequenceAt(0), null);
120     int i = 0;
121     for (SequenceI seq : alignment.getSequences())
122     {
123       final AlignmentAnnotation annotation = new AlignmentAnnotation(
124               "label" + i, "desc" + i, i);
125       annotation.setCalcId("calcId" + i);
126       seq.addAlignmentAnnotation(annotation);
127       annotation.setSequenceRef(seq);
128     }
129   }
130
131   @Test(groups = { "Functional" })
132   public void testConfigureReferenceAnnotationsMenu_noSequenceSelected()
133   {
134     JMenuItem menu = new JMenuItem();
135     List<SequenceI> seqs = new ArrayList<>();
136     testee.configureReferenceAnnotationsMenu(menu, seqs);
137     assertFalse(menu.isEnabled());
138     // now try null list
139     menu.setEnabled(true);
140     testee.configureReferenceAnnotationsMenu(menu, null);
141     assertFalse(menu.isEnabled());
142   }
143
144   /**
145    * Test building the 'add reference annotations' menu for the case where there
146    * are no reference annotations to add to the alignment. The menu item should
147    * be disabled.
148    */
149   @Test(groups = { "Functional" })
150   public void testConfigureReferenceAnnotationsMenu_noReferenceAnnotations()
151   {
152     JMenuItem menu = new JMenuItem();
153
154     /*
155      * Initial state is that sequences have annotations, and have dataset
156      * sequences, but the dataset sequences have no annotations. Hence nothing
157      * to add.
158      */
159     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
160
161     testee.configureReferenceAnnotationsMenu(menu, seqs);
162     assertFalse(menu.isEnabled());
163   }
164
165   /**
166    * Test building the 'add reference annotations' menu for the case where all
167    * reference annotations are already on the alignment. The menu item should be
168    * disabled.
169    */
170   @Test(groups = { "Functional" })
171   public void testConfigureReferenceAnnotationsMenu_alreadyAdded()
172   {
173     JMenuItem menu = new JMenuItem();
174     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
175
176     // make up new annotations and add to dataset sequences, sequences and
177     // alignment
178     attachReferenceAnnotations(seqs, true, true);
179
180     testee.configureReferenceAnnotationsMenu(menu, seqs);
181     assertFalse(menu.isEnabled());
182   }
183
184   /**
185    * Test building the 'add reference annotations' menu for the case where
186    * several reference annotations are on the dataset but not on the sequences.
187    * The menu item should be enabled, and acquire a tooltip which lists the
188    * annotation sources (calcIds) and type (labels).
189    */
190   @Test(groups = { "Functional" })
191   public void testConfigureReferenceAnnotationsMenu()
192   {
193     JMenuItem menu = new JMenuItem();
194     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
195
196     // make up new annotations and add to dataset sequences
197     attachReferenceAnnotations(seqs, false, false);
198
199     testee.configureReferenceAnnotationsMenu(menu, seqs);
200     assertTrue(menu.isEnabled());
201     String s = MessageManager.getString("label.add_annotations_for");
202 //    String expected = "<html><style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
203 //            + "<div class=\"ttip\">" + s
204 //            + "<br/>Jmol/secondary structure<br/>PDB/Temp </div></html>";
205     String expected = "<html>" + s + "<br>Jmol/secondary structure<br>PDB/Temp</html>";
206     assertEquals(expected, menu.getToolTipText());
207   }
208
209   /**
210    * Test building the 'add reference annotations' menu for the case where
211    * several reference annotations are on the dataset and the sequences but not
212    * on the alignment. The menu item should be enabled, and acquire a tooltip
213    * which lists the annotation sources (calcIds) and type (labels).
214    */
215   @Test(groups = { "Functional" })
216   public void testConfigureReferenceAnnotationsMenu_notOnAlignment()
217   {
218     JMenuItem menu = new JMenuItem();
219     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
220
221     // make up new annotations and add to dataset sequences and sequences
222     attachReferenceAnnotations(seqs, true, false);
223
224     testee.configureReferenceAnnotationsMenu(menu, seqs);
225     assertTrue(menu.isEnabled());
226     String s = MessageManager.getString("label.add_annotations_for");
227 //    String expected = "<html><style> div.ttip {width:350px;white-space:pre-wrap;padding:2px;overflow-wrap:break-word;}</style>"
228 //            + "<div class=\"ttip\">" + s
229 //            + "<br/>Jmol/secondary structure<br/>PDB/Temp</html>";
230     String expected = "<html>" + s
231             + "<br>Jmol/secondary structure<br>PDB/Temp</html>";
232     s = menu.getToolTipText();
233     assertEquals(expected, s);
234   }
235
236   /**
237    * Generate annotations and add to dataset sequences and (optionally)
238    * sequences and/or alignment
239    * 
240    * @param seqs
241    * @param addToSequence
242    * @param addToAlignment
243    */
244   private void attachReferenceAnnotations(List<SequenceI> seqs,
245           boolean addToSequence, boolean addToAlignment)
246   {
247     // PDB.secondary structure on Sequence0
248     AlignmentAnnotation annotation = new AlignmentAnnotation(
249             "secondary structure", "", 0);
250     annotation.annotations = new Annotation[] { new Annotation(2f) };
251     annotation.setCalcId("PDB");
252     seqs.get(0).getDatasetSequence().addAlignmentAnnotation(annotation);
253     if (addToSequence)
254     {
255       seqs.get(0).addAlignmentAnnotation(annotation);
256     }
257     if (addToAlignment)
258     {
259       this.alignment.addAnnotation(annotation);
260     }
261
262     // PDB.Temp on Sequence1
263     annotation = new AlignmentAnnotation("Temp", "", 0);
264     annotation.setCalcId("PDB");
265     annotation.annotations = new Annotation[] { new Annotation(2f) };
266     seqs.get(1).getDatasetSequence().addAlignmentAnnotation(annotation);
267     if (addToSequence)
268     {
269       seqs.get(1).addAlignmentAnnotation(annotation);
270     }
271     if (addToAlignment)
272     {
273       this.alignment.addAnnotation(annotation);
274     }
275
276     // JMOL.secondary structure on Sequence0
277     annotation = new AlignmentAnnotation("secondary structure", "", 0);
278     annotation.setCalcId("Jmol");
279     annotation.annotations = new Annotation[] { new Annotation(2f) };
280     seqs.get(0).getDatasetSequence().addAlignmentAnnotation(annotation);
281     if (addToSequence)
282     {
283       seqs.get(0).addAlignmentAnnotation(annotation);
284     }
285     if (addToAlignment)
286     {
287       this.alignment.addAnnotation(annotation);
288     }
289   }
290
291   /**
292    * Test building the 'add reference annotations' menu for the case where there
293    * are two alignment views:
294    * <ul>
295    * <li>in one view, reference annotations have been added (are on the
296    * datasets, sequences and alignment)</li>
297    * <li>in the current view, reference annotations are on the dataset and
298    * sequence, but not the alignment</li>
299    * </ul>
300    * The menu item should be enabled, and acquire a tooltip which lists the
301    * annotation sources (calcIds) and type (labels).
302    */
303   @Test(groups = { "Functional" })
304   public void testConfigureReferenceAnnotationsMenu_twoViews()
305   {
306   }
307
308   /**
309    * Test for building menu options including 'show' and 'hide' annotation
310    * types.
311    */
312   @Test(groups = { "Functional" })
313   public void testBuildAnnotationTypesMenus()
314   {
315     JMenu showMenu = new JMenu();
316     JMenu hideMenu = new JMenu();
317     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
318
319     // make up new annotations and add to sequences and to the alignment
320
321     // PDB.secondary structure on Sequence0
322     AlignmentAnnotation annotation = new AlignmentAnnotation(
323             "secondary structure", "", new Annotation[] {});
324     annotation.setCalcId("PDB");
325     annotation.visible = true;
326     seqs.get(0).addAlignmentAnnotation(annotation);
327     parentPanel.getAlignment().addAnnotation(annotation);
328
329     // JMOL.secondary structure on Sequence0 - hidden
330     annotation = new AlignmentAnnotation("secondary structure", "",
331             new Annotation[] {});
332     annotation.setCalcId("JMOL");
333     annotation.visible = false;
334     seqs.get(0).addAlignmentAnnotation(annotation);
335     parentPanel.getAlignment().addAnnotation(annotation);
336
337     // Jpred.SSP on Sequence0 - hidden
338     annotation = new AlignmentAnnotation("SSP", "", new Annotation[] {});
339     annotation.setCalcId("JPred");
340     annotation.visible = false;
341     seqs.get(0).addAlignmentAnnotation(annotation);
342     parentPanel.getAlignment().addAnnotation(annotation);
343
344     // PDB.Temp on Sequence1
345     annotation = new AlignmentAnnotation("Temp", "", new Annotation[] {});
346     annotation.setCalcId("PDB");
347     annotation.visible = true;
348     seqs.get(1).addAlignmentAnnotation(annotation);
349     parentPanel.getAlignment().addAnnotation(annotation);
350
351     /*
352      * Expect menu options to show "secondary structure" and "SSP", and to hide
353      * "secondary structure" and "Temp". Tooltip should be calcId.
354      */
355     testee.buildAnnotationTypesMenus(showMenu, hideMenu, seqs);
356
357     assertTrue(showMenu.isEnabled());
358     assertTrue(hideMenu.isEnabled());
359
360     Component[] showOptions = showMenu.getMenuComponents();
361     Component[] hideOptions = hideMenu.getMenuComponents();
362
363     assertEquals(4, showOptions.length); // includes 'All' and separator
364     assertEquals(4, hideOptions.length);
365     String all = MessageManager.getString("label.all");
366     assertEquals(all, ((JMenuItem) showOptions[0]).getText());
367     assertTrue(showOptions[1] instanceof JPopupMenu.Separator);
368     assertEquals(JSeparator.HORIZONTAL,
369             ((JSeparator) showOptions[1]).getOrientation());
370     assertEquals("secondary structure",
371             ((JMenuItem) showOptions[2]).getText());
372     assertEquals("JMOL", ((JMenuItem) showOptions[2]).getToolTipText());
373     assertEquals("SSP", ((JMenuItem) showOptions[3]).getText());
374     assertEquals("JPred", ((JMenuItem) showOptions[3]).getToolTipText());
375
376     assertEquals(all, ((JMenuItem) hideOptions[0]).getText());
377     assertTrue(hideOptions[1] instanceof JPopupMenu.Separator);
378     assertEquals(JSeparator.HORIZONTAL,
379             ((JSeparator) hideOptions[1]).getOrientation());
380     assertEquals("secondary structure",
381             ((JMenuItem) hideOptions[2]).getText());
382     assertEquals("PDB", ((JMenuItem) hideOptions[2]).getToolTipText());
383     assertEquals("Temp", ((JMenuItem) hideOptions[3]).getText());
384     assertEquals("PDB", ((JMenuItem) hideOptions[3]).getToolTipText());
385   }
386
387   /**
388    * Test for building menu options with only 'hide' annotation types enabled.
389    */
390   @Test(groups = { "Functional" })
391   public void testBuildAnnotationTypesMenus_showDisabled()
392   {
393     JMenu showMenu = new JMenu();
394     JMenu hideMenu = new JMenu();
395     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
396
397     // make up new annotations and add to sequences and to the alignment
398
399     // PDB.secondary structure on Sequence0
400     AlignmentAnnotation annotation = new AlignmentAnnotation(
401             "secondary structure", "", new Annotation[] {});
402     annotation.setCalcId("PDB");
403     annotation.visible = true;
404     seqs.get(0).addAlignmentAnnotation(annotation);
405     parentPanel.getAlignment().addAnnotation(annotation);
406
407     // PDB.Temp on Sequence1
408     annotation = new AlignmentAnnotation("Temp", "", new Annotation[] {});
409     annotation.setCalcId("PDB");
410     annotation.visible = true;
411     seqs.get(1).addAlignmentAnnotation(annotation);
412     parentPanel.getAlignment().addAnnotation(annotation);
413
414     /*
415      * Expect menu options to hide "secondary structure" and "Temp". Tooltip
416      * should be calcId. 'Show' menu should be disabled.
417      */
418     testee.buildAnnotationTypesMenus(showMenu, hideMenu, seqs);
419
420     assertFalse(showMenu.isEnabled());
421     assertTrue(hideMenu.isEnabled());
422
423     Component[] showOptions = showMenu.getMenuComponents();
424     Component[] hideOptions = hideMenu.getMenuComponents();
425
426     assertEquals(2, showOptions.length); // includes 'All' and separator
427     assertEquals(4, hideOptions.length);
428     String all = MessageManager.getString("label.all");
429     assertEquals(all, ((JMenuItem) showOptions[0]).getText());
430     assertTrue(showOptions[1] instanceof JPopupMenu.Separator);
431     assertEquals(JSeparator.HORIZONTAL,
432             ((JSeparator) showOptions[1]).getOrientation());
433
434     assertEquals(all, ((JMenuItem) hideOptions[0]).getText());
435     assertTrue(hideOptions[1] instanceof JPopupMenu.Separator);
436     assertEquals(JSeparator.HORIZONTAL,
437             ((JSeparator) hideOptions[1]).getOrientation());
438     assertEquals("secondary structure",
439             ((JMenuItem) hideOptions[2]).getText());
440     assertEquals("PDB", ((JMenuItem) hideOptions[2]).getToolTipText());
441     assertEquals("Temp", ((JMenuItem) hideOptions[3]).getText());
442     assertEquals("PDB", ((JMenuItem) hideOptions[3]).getToolTipText());
443   }
444
445   /**
446    * Test for building menu options with only 'show' annotation types enabled.
447    */
448   @Test(groups = { "Functional" })
449   public void testBuildAnnotationTypesMenus_hideDisabled()
450   {
451     JMenu showMenu = new JMenu();
452     JMenu hideMenu = new JMenu();
453     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
454
455     // make up new annotations and add to sequences and to the alignment
456
457     // PDB.secondary structure on Sequence0
458     AlignmentAnnotation annotation = new AlignmentAnnotation(
459             "secondary structure", "", new Annotation[] {});
460     annotation.setCalcId("PDB");
461     annotation.visible = false;
462     seqs.get(0).addAlignmentAnnotation(annotation);
463     parentPanel.getAlignment().addAnnotation(annotation);
464
465     // PDB.Temp on Sequence1
466     annotation = new AlignmentAnnotation("Temp", "", new Annotation[] {});
467     annotation.setCalcId("PDB2");
468     annotation.visible = false;
469     seqs.get(1).addAlignmentAnnotation(annotation);
470     parentPanel.getAlignment().addAnnotation(annotation);
471
472     /*
473      * Expect menu options to show "secondary structure" and "Temp". Tooltip
474      * should be calcId. 'hide' menu should be disabled.
475      */
476     testee.buildAnnotationTypesMenus(showMenu, hideMenu, seqs);
477
478     assertTrue(showMenu.isEnabled());
479     assertFalse(hideMenu.isEnabled());
480
481     Component[] showOptions = showMenu.getMenuComponents();
482     Component[] hideOptions = hideMenu.getMenuComponents();
483
484     assertEquals(4, showOptions.length); // includes 'All' and separator
485     assertEquals(2, hideOptions.length);
486     String all = MessageManager.getString("label.all");
487     assertEquals(all, ((JMenuItem) showOptions[0]).getText());
488     assertTrue(showOptions[1] instanceof JPopupMenu.Separator);
489     assertEquals(JSeparator.HORIZONTAL,
490             ((JSeparator) showOptions[1]).getOrientation());
491     assertEquals("secondary structure",
492             ((JMenuItem) showOptions[2]).getText());
493     assertEquals("PDB", ((JMenuItem) showOptions[2]).getToolTipText());
494     assertEquals("Temp", ((JMenuItem) showOptions[3]).getText());
495     assertEquals("PDB2", ((JMenuItem) showOptions[3]).getToolTipText());
496
497     assertEquals(all, ((JMenuItem) hideOptions[0]).getText());
498     assertTrue(hideOptions[1] instanceof JPopupMenu.Separator);
499     assertEquals(JSeparator.HORIZONTAL,
500             ((JSeparator) hideOptions[1]).getOrientation());
501   }
502
503   /**
504    * Test for adding sequence id, dbref and feature links
505    */
506   @Test(groups = { "Functional" })
507   public void testBuildLinkMenu()
508   {
509     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
510     final SequenceI seq0 = seqs.get(0);
511     final SequenceI seq1 = seqs.get(1);
512     final List<SequenceFeature> noFeatures = Collections
513             .<SequenceFeature> emptyList();
514     final String linkText = MessageManager.getString("action.link");
515
516     seq0.addDBRef(new DBRefEntry(DBRefSource.UNIPROT, "1", "P83527"));
517     seq0.addDBRef(new DBRefEntry("INTERPRO", "1", "IPR001041"));
518     seq0.addDBRef(new DBRefEntry("INTERPRO", "1", "IPR012675"));
519     seq0.addDBRef(new DBRefEntry("INTERPRO", "1", "IPR006058"));
520     seq1.addDBRef(new DBRefEntry(DBRefSource.UNIPROT, "1", "Q9ZTS2"));
521     seq1.addDBRef(new DBRefEntry("GENE3D", "1", "3.10.20.30"));
522
523     /*
524      * check the Link Menu for the first sequence
525      */
526     JMenu linkMenu = PopupMenu.buildLinkMenu(seq0, noFeatures);
527     assertEquals(linkText, linkMenu.getText());
528     Component[] linkItems = linkMenu.getMenuComponents();
529
530     /*
531      * menu items are ordered: SEQUENCE_ID search first, then dbrefs in order
532      * of database name (and within that by order of dbref addition)
533      */
534     assertEquals(5, linkItems.length);
535     assertEquals("EMBL-EBI Search", ((JMenuItem) linkItems[0]).getText());
536     assertEquals("INTERPRO|IPR001041",
537             ((JMenuItem) linkItems[1]).getText());
538     assertEquals("INTERPRO|IPR012675",
539             ((JMenuItem) linkItems[2]).getText());
540     assertEquals("INTERPRO|IPR006058",
541             ((JMenuItem) linkItems[3]).getText());
542     assertEquals("UNIPROT|P83527", ((JMenuItem) linkItems[4]).getText());
543
544     /*
545      * check the Link Menu for the second sequence
546      * note dbref GENE3D is matched to link Gene3D, the latter is displayed
547      */
548     linkMenu = PopupMenu.buildLinkMenu(seq1, noFeatures);
549     linkItems = linkMenu.getMenuComponents();
550     assertEquals(3, linkItems.length);
551     assertEquals("EMBL-EBI Search", ((JMenuItem) linkItems[0]).getText());
552     assertEquals("Gene3D|3.10.20.30", ((JMenuItem) linkItems[1]).getText());
553     assertEquals("UNIPROT|Q9ZTS2", ((JMenuItem) linkItems[2]).getText());
554
555     /*
556      * if there are no valid links the Links submenu is still shown, but
557      * reduced to the EMBL-EBI lookup only (inserted by 
558      * CustomUrlProvider.choosePrimaryUrl())
559      */
560     String unmatched = "NOMATCH|http://www.uniprot.org/uniprot/$"
561             + DB_ACCESSION + "$";
562     UrlProviderFactoryI factory = new DesktopUrlProviderFactory(null,
563             unmatched, "");
564     Preferences.sequenceUrlLinks = factory.createUrlProvider();
565
566     linkMenu = PopupMenu.buildLinkMenu(seq1, noFeatures);
567     linkItems = linkMenu.getMenuComponents();
568     assertEquals(1, linkItems.length);
569     assertEquals("EMBL-EBI Search", ((JMenuItem) linkItems[0]).getText());
570
571     /*
572      * if sequence is null, only feature links are shown (alignment popup submenu)
573      */
574     linkMenu = PopupMenu.buildLinkMenu(null, noFeatures);
575     linkItems = linkMenu.getMenuComponents();
576     assertEquals(0, linkItems.length);
577
578     List<SequenceFeature> features = new ArrayList<>();
579     SequenceFeature sf = new SequenceFeature("type", "desc", 1, 20, null);
580     features.add(sf);
581     linkMenu = PopupMenu.buildLinkMenu(null, features);
582     linkItems = linkMenu.getMenuComponents();
583     assertEquals(0, linkItems.length); // feature has no links
584
585     sf.addLink("Pfam family|http://pfam.xfam.org/family/PF00111");
586     linkMenu = PopupMenu.buildLinkMenu(null, features);
587     linkItems = linkMenu.getMenuComponents();
588     assertEquals(1, linkItems.length);
589     JMenuItem item = (JMenuItem) linkItems[0];
590     assertEquals("Pfam family", item.getText());
591     // ? no way to verify URL, compiled into link's actionListener
592   }
593
594   @Test(groups = { "Functional" })
595   public void testHideInsertions()
596   {
597     // get sequences from the alignment
598     List<SequenceI> seqs = parentPanel.getAlignment().getSequences();
599
600     // add our own seqs to avoid problems with changes to existing sequences
601     // (gap at end of sequences varies depending on how tests are run!)
602     Sequence seqGap1 = new Sequence("GappySeq",
603             "AAAA----AA-AAAAAAA---AAA-----------AAAAAAAAAA--");
604     seqGap1.createDatasetSequence();
605     seqs.add(seqGap1);
606     Sequence seqGap2 = new Sequence("LessGappySeq",
607             "AAAAAA-AAAAA---AAA--AAAAA--AAAAAAA-AAAAAA");
608     seqGap2.createDatasetSequence();
609     seqs.add(seqGap2);
610     Sequence seqGap3 = new Sequence("AnotherGapSeq",
611             "AAAAAA-AAAAAA--AAAAAA-AAAAAAAAAAA---AAAAAAAA");
612     seqGap3.createDatasetSequence();
613     seqs.add(seqGap3);
614     Sequence seqGap4 = new Sequence("NoGaps",
615             "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
616     seqGap4.createDatasetSequence();
617     seqs.add(seqGap4);
618
619     ColumnSelection sel = new ColumnSelection();
620     parentPanel.av.getAlignment().getHiddenColumns()
621             .revealAllHiddenColumns(sel);
622
623     // get the Popup Menu for 7th sequence - no insertions
624     testee = new PopupMenu(parentPanel, seqs.get(7), null);
625     testee.hideInsertions_actionPerformed(null);
626
627     HiddenColumns hidden = parentPanel.av.getAlignment().getHiddenColumns();
628     Iterator<int[]> it = hidden.iterator();
629     assertFalse(it.hasNext());
630
631     // get the Popup Menu for GappySeq - this time we have insertions
632     testee = new PopupMenu(parentPanel, seqs.get(4), null);
633     testee.hideInsertions_actionPerformed(null);
634     hidden = parentPanel.av.getAlignment().getHiddenColumns();
635     it = hidden.iterator();
636
637     assertTrue(it.hasNext());
638     int[] region = it.next();
639     assertEquals(region[0], 4);
640     assertEquals(region[1], 7);
641
642     assertTrue(it.hasNext());
643     region = it.next();
644     assertEquals(region[0], 10);
645     assertEquals(region[1], 10);
646
647     assertTrue(it.hasNext());
648     region = it.next();
649     assertEquals(region[0], 18);
650     assertEquals(region[1], 20);
651
652     assertTrue(it.hasNext());
653     region = it.next();
654     assertEquals(region[0], 24);
655     assertEquals(region[1], 34);
656
657     assertTrue(it.hasNext());
658     region = it.next();
659     assertEquals(region[0], 45);
660     assertEquals(region[1], 46);
661
662     assertFalse(it.hasNext());
663
664     sel = new ColumnSelection();
665     hidden.revealAllHiddenColumns(sel);
666
667     // make a sequence group and hide insertions within the group
668     SequenceGroup sg = new SequenceGroup();
669     sg.setStartRes(8);
670     sg.setEndRes(42);
671     sg.addSequence(seqGap2, false);
672     sg.addSequence(seqGap3, false);
673     parentPanel.av.setSelectionGroup(sg);
674
675     // hide columns outside and within selection
676     // only hidden columns outside the collection will be retained (unless also
677     // gaps in the selection)
678     hidden.hideColumns(1, 10);
679     hidden.hideColumns(31, 40);
680
681     // get the Popup Menu for LessGappySeq in the sequence group
682     testee = new PopupMenu(parentPanel, seqs.get(5), null);
683     testee.hideInsertions_actionPerformed(null);
684     hidden = parentPanel.av.getAlignment().getHiddenColumns();
685     it = hidden.iterator();
686
687     assertTrue(it.hasNext());
688     region = it.next();
689     assertEquals(region[0], 1);
690     assertEquals(region[1], 7);
691
692     assertTrue(it.hasNext());
693     region = it.next();
694     assertEquals(region[0], 13);
695     assertEquals(region[1], 14);
696
697     assertTrue(it.hasNext());
698     region = it.next();
699     assertEquals(region[0], 34);
700     assertEquals(region[1], 34);
701   }
702
703   @Test(groups = { "Functional" })
704   public void testAddFeatureDetails()
705   {
706     String menuText = MessageManager.getString("label.feature_details");
707
708     /*
709      * with no features, sub-menu should not be created
710      */
711     List<SequenceFeature> features = new ArrayList<>();
712     SequenceI seq = this.alignment.getSequenceAt(0); // FER_CAPAA/1-12
713     testee.addFeatureDetails(features, seq, 10);
714     JMenu menu = findMenu(testee, menuText);
715     assertNull(menu);
716
717     /*
718      * add some features; the menu item text is wrapped in html, and includes
719      * feature type, position, description, group (if not null)
720      */
721     SequenceFeature sf1 = new SequenceFeature("helix", "curly", 2, 6, null);
722     SequenceFeature sf2 = new SequenceFeature("chain", "straight", 1, 1,
723             "uniprot");
724     features.add(sf1);
725     features.add(sf2);
726     testee.addFeatureDetails(features, seq, 10);
727     menu = findMenu(testee, menuText);
728     assertNotNull(menu);
729     assertEquals(2, menu.getItemCount());
730     JMenuItem item = menu.getItem(0);
731     assertEquals("<html>helix 2-6 curly</html>", item.getText());
732     item = menu.getItem(1);
733     assertEquals("<html>chain 1 straight (uniprot)</html>", item.getText());
734
735     /*
736      * long feature descriptions are truncated to 40 characters
737      */
738     sf1.setDescription("this is a quite extraordinarily long description");
739     testee.remove(menu); // don't create the sub-menu twice
740     testee.addFeatureDetails(features, seq, 10);
741     menu = findMenu(testee, menuText);
742     item = menu.getItem(0);
743     assertEquals(
744             "<html>helix 2-6 this is a quite extraordinarily long des...</html>",
745             item.getText());
746   }
747
748   /**
749    * Returns the first component which is a JMenu with the given text
750    * 
751    * @param c
752    * @param text
753    * @return
754    */
755   private JMenu findMenu(Container c, String text)
756   {
757     for (int i = 0; i < c.getComponentCount(); i++)
758     {
759       Component comp = c.getComponent(i);
760       if ((comp instanceof JMenu) && ((JMenu) comp).getText().equals(text))
761       {
762         return (JMenu) comp;
763       }
764     }
765     return null;
766   }
767
768   @Test(groups = { "Functional" })
769   public void testAddFeatureDetails_linkedFeatures()
770   {
771     // todo tests that verify menu items for complement features
772   }
773 }