sequenceserver 2.1.0 → 3.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.
Potentially problematic release.
This version of sequenceserver might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/COPYRIGHT.txt +1 -1
 - data/bin/sequenceserver +10 -3
 - data/lib/sequenceserver/blast/error.rb +53 -0
 - data/lib/sequenceserver/blast/formatter.rb +13 -4
 - data/lib/sequenceserver/blast/job.rb +2 -43
 - data/lib/sequenceserver/blast/report.rb +33 -3
 - data/lib/sequenceserver/config.rb +4 -1
 - data/lib/sequenceserver/job.rb +21 -11
 - data/lib/sequenceserver/makeblastdb-modified-with-cache.rb +345 -0
 - data/lib/sequenceserver/makeblastdb.rb +97 -75
 - data/lib/sequenceserver/pool.rb +1 -1
 - data/lib/sequenceserver/report.rb +1 -5
 - data/lib/sequenceserver/routes.rb +52 -5
 - data/lib/sequenceserver/server.rb +1 -1
 - data/lib/sequenceserver/sys.rb +1 -1
 - data/lib/sequenceserver/version.rb +1 -1
 - data/lib/sequenceserver.rb +11 -2
 - data/public/404.html +27 -0
 - data/public/config.js +0 -6
 - data/public/css/grapher.css +1 -1
 - data/public/css/sequenceserver.css +22 -11
 - data/public/css/sequenceserver.min.css +2 -2
 - data/public/js/circos.js +7 -3
 - data/public/js/dnd.js +3 -3
 - data/public/js/fastq_to_fasta.js +35 -0
 - data/public/js/form.js +30 -11
 - data/public/js/grapher.js +123 -113
 - data/public/js/hit.js +8 -2
 - data/public/js/hits_overview.js +4 -1
 - data/public/js/jquery_world.js +0 -1
 - data/public/js/kablammo.js +4 -0
 - data/public/js/length_distribution.js +5 -1
 - data/public/js/null_plugins/download_links.js +7 -0
 - data/public/js/null_plugins/hit_buttons.js +11 -0
 - data/public/js/null_plugins/report_plugins.js +18 -0
 - data/public/js/query.js +26 -6
 - data/public/js/report.js +92 -22
 - data/public/js/search.js +0 -8
 - data/public/js/sidebar.js +11 -1
 - data/public/js/tests/advanced_parameters.spec.js +36 -0
 - data/public/js/tests/mock_data/sequences.js +49 -0
 - data/public/js/tests/report.spec.js +62 -6
 - data/public/js/tests/search_query.spec.js +45 -19
 - data/public/js/visualisation_helpers.js +1 -1
 - data/public/sequenceserver-report.min.js +76 -42
 - data/public/sequenceserver-search.min.js +34 -33
 - data/views/layout.erb +9 -12
 - metadata +34 -23
 
    
        data/public/js/report.js
    CHANGED
    
    | 
         @@ -8,15 +8,14 @@ import { ReportQuery } from './query'; 
     | 
|
| 
       8 
8 
     | 
    
         
             
            import Hit from './hit';
         
     | 
| 
       9 
9 
     | 
    
         
             
            import HSP from './hsp';
         
     | 
| 
       10 
10 
     | 
    
         
             
            import AlignmentExporter from './alignment_exporter';
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 11 
     | 
    
         
            +
            import ReportPlugins from 'report_plugins';
         
     | 
| 
       14 
12 
     | 
    
         | 
| 
       15 
13 
     | 
    
         
             
            /**
         
     | 
| 
       16 
14 
     | 
    
         
             
             * Renders entire report.
         
     | 
| 
       17 
15 
     | 
    
         
             
             *
         
     | 
| 
       18 
16 
     | 
    
         
             
             * Composed of Query and Sidebar components.
         
     | 
| 
       19 
17 
     | 
    
         
             
             */
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
       20 
19 
     | 
    
         
             
            class Report extends Component {
         
     | 
| 
       21 
20 
     | 
    
         
             
                constructor(props) {
         
     | 
| 
       22 
21 
     | 
    
         
             
                    super(props);
         
     | 
| 
         @@ -28,6 +27,8 @@ class Report extends Component { 
     | 
|
| 
       28 
27 
     | 
    
         
             
                    this.nextHSP = 0;
         
     | 
| 
       29 
28 
     | 
    
         
             
                    this.maxHSPs = 3; // max HSPs to render in a cycle
         
     | 
| 
       30 
29 
     | 
    
         
             
                    this.state = {
         
     | 
| 
      
 30 
     | 
    
         
            +
                        user_warning: null,
         
     | 
| 
      
 31 
     | 
    
         
            +
                        download_links: [],
         
     | 
| 
       31 
32 
     | 
    
         
             
                        search_id: '',
         
     | 
| 
       32 
33 
     | 
    
         
             
                        seqserv_version: '',
         
     | 
| 
       33 
34 
     | 
    
         
             
                        program: '',
         
     | 
| 
         @@ -45,16 +46,21 @@ class Report extends Component { 
     | 
|
| 
       45 
46 
     | 
    
         
             
                    this.prepareAlignmentOfSelectedHits = this.prepareAlignmentOfSelectedHits.bind(this);
         
     | 
| 
       46 
47 
     | 
    
         
             
                    this.prepareAlignmentOfAllHits = this.prepareAlignmentOfAllHits.bind(this);
         
     | 
| 
       47 
48 
     | 
    
         
             
                    this.setStateFromJSON = this.setStateFromJSON.bind(this);
         
     | 
| 
      
 49 
     | 
    
         
            +
                    this.plugins = new ReportPlugins(this);
         
     | 
| 
       48 
50 
     | 
    
         
             
                }
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
       49 
52 
     | 
    
         
             
                /**
         
     | 
| 
       50 
53 
     | 
    
         
             
               * Fetch results.
         
     | 
| 
       51 
54 
     | 
    
         
             
               */
         
     | 
| 
       52 
55 
     | 
    
         
             
                fetchResults() {
         
     | 
| 
       53 
     | 
    
         
            -
                     
     | 
| 
       54 
     | 
    
         
            -
                     
     | 
| 
      
 56 
     | 
    
         
            +
                    const path = location.pathname + '.json' + location.search;
         
     | 
| 
      
 57 
     | 
    
         
            +
                    this.pollPeriodically(path, this.setStateFromJSON, this.props.showErrorModal);
         
     | 
| 
      
 58 
     | 
    
         
            +
                }
         
     | 
| 
       55 
59 
     | 
    
         | 
| 
      
 60 
     | 
    
         
            +
                pollPeriodically(path, callback, errCallback) {
         
     | 
| 
      
 61 
     | 
    
         
            +
                    var intervals = [200, 400, 800, 1200, 2000, 3000, 5000];
         
     | 
| 
       56 
62 
     | 
    
         
             
                    function poll() {
         
     | 
| 
       57 
     | 
    
         
            -
                        $.getJSON( 
     | 
| 
      
 63 
     | 
    
         
            +
                        $.getJSON(path).complete(function (jqXHR) {
         
     | 
| 
       58 
64 
     | 
    
         
             
                            switch (jqXHR.status) {
         
     | 
| 
       59 
65 
     | 
    
         
             
                            case 202:
         
     | 
| 
       60 
66 
     | 
    
         
             
                                var interval;
         
     | 
| 
         @@ -66,12 +72,12 @@ class Report extends Component { 
     | 
|
| 
       66 
72 
     | 
    
         
             
                                setTimeout(poll, interval);
         
     | 
| 
       67 
73 
     | 
    
         
             
                                break;
         
     | 
| 
       68 
74 
     | 
    
         
             
                            case 200:
         
     | 
| 
       69 
     | 
    
         
            -
                                 
     | 
| 
      
 75 
     | 
    
         
            +
                                callback(jqXHR.responseJSON);
         
     | 
| 
       70 
76 
     | 
    
         
             
                                break;
         
     | 
| 
       71 
     | 
    
         
            -
                            case 404:
         
     | 
| 
       72 
77 
     | 
    
         
             
                            case 400:
         
     | 
| 
      
 78 
     | 
    
         
            +
                            case 422:
         
     | 
| 
       73 
79 
     | 
    
         
             
                            case 500:
         
     | 
| 
       74 
     | 
    
         
            -
                                 
     | 
| 
      
 80 
     | 
    
         
            +
                                errCallback(jqXHR.responseJSON);
         
     | 
| 
       75 
81 
     | 
    
         
             
                                break;
         
     | 
| 
       76 
82 
     | 
    
         
             
                            }
         
     | 
| 
       77 
83 
     | 
    
         
             
                        });
         
     | 
| 
         @@ -86,8 +92,13 @@ class Report extends Component { 
     | 
|
| 
       86 
92 
     | 
    
         
             
                setStateFromJSON(responseJSON) {
         
     | 
| 
       87 
93 
     | 
    
         
             
                    this.lastTimeStamp = Date.now();
         
     | 
| 
       88 
94 
     | 
    
         
             
                    // the callback prepares the download link for all alignments
         
     | 
| 
       89 
     | 
    
         
            -
                     
     | 
| 
      
 95 
     | 
    
         
            +
                    if (responseJSON.user_warning == 'LARGE_RESULT') {
         
     | 
| 
      
 96 
     | 
    
         
            +
                        this.setState({user_warning: responseJSON.user_warning, download_links: responseJSON.download_links});
         
     | 
| 
      
 97 
     | 
    
         
            +
                    } else {
         
     | 
| 
      
 98 
     | 
    
         
            +
                        this.setState(responseJSON, this.prepareAlignmentOfAllHits);
         
     | 
| 
      
 99 
     | 
    
         
            +
                    }
         
     | 
| 
       90 
100 
     | 
    
         
             
                }
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
       91 
102 
     | 
    
         
             
                /**
         
     | 
| 
       92 
103 
     | 
    
         
             
               * Called as soon as the page has loaded and the user sees the loading spinner.
         
     | 
| 
       93 
104 
     | 
    
         
             
               * We use this opportunity to setup services that make use of delegated events
         
     | 
| 
         @@ -95,6 +106,7 @@ class Report extends Component { 
     | 
|
| 
       95 
106 
     | 
    
         
             
               */
         
     | 
| 
       96 
107 
     | 
    
         
             
                componentDidMount() {
         
     | 
| 
       97 
108 
     | 
    
         
             
                    this.fetchResults();
         
     | 
| 
      
 109 
     | 
    
         
            +
                    this.plugins.init();
         
     | 
| 
       98 
110 
     | 
    
         
             
                    // This sets up an event handler which enables users to select text from
         
     | 
| 
       99 
111 
     | 
    
         
             
                    // hit header without collapsing the hit.
         
     | 
| 
       100 
112 
     | 
    
         
             
                    this.preventCollapseOnSelection();
         
     | 
| 
         @@ -107,9 +119,9 @@ class Report extends Component { 
     | 
|
| 
       107 
119 
     | 
    
         
             
               * and circos would have been rendered at this point. At this stage we kick
         
     | 
| 
       108 
120 
     | 
    
         
             
               * start iteratively adding 1 HSP to the page every 25 milli-seconds.
         
     | 
| 
       109 
121 
     | 
    
         
             
               */
         
     | 
| 
       110 
     | 
    
         
            -
                componentDidUpdate() {
         
     | 
| 
       111 
     | 
    
         
            -
             
     | 
| 
       112 
     | 
    
         
            -
                    console.log((Date.now() - this.lastTimeStamp) / 1000);
         
     | 
| 
      
 122 
     | 
    
         
            +
                componentDidUpdate(prevProps, prevState) {
         
     | 
| 
      
 123 
     | 
    
         
            +
                    // Log to console how long the last update take?
         
     | 
| 
      
 124 
     | 
    
         
            +
                    // console.log((Date.now() - this.lastTimeStamp) / 1000);
         
     | 
| 
       113 
125 
     | 
    
         | 
| 
       114 
126 
     | 
    
         
             
                    // Lock sidebar in its position on the first update.
         
     | 
| 
       115 
127 
     | 
    
         
             
                    if (this.nextQuery == 0 && this.nextHit == 0 && this.nextHSP == 0) {
         
     | 
| 
         @@ -125,6 +137,8 @@ class Report extends Component { 
     | 
|
| 
       125 
137 
     | 
    
         
             
                    } else {
         
     | 
| 
       126 
138 
     | 
    
         
             
                        this.componentFinishedUpdating();
         
     | 
| 
       127 
139 
     | 
    
         
             
                    }
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                    this.plugins.componentDidUpdate(prevProps, prevState);
         
     | 
| 
       128 
142 
     | 
    
         
             
                }
         
     | 
| 
       129 
143 
     | 
    
         | 
| 
       130 
144 
     | 
    
         
             
                /**
         
     | 
| 
         @@ -135,13 +149,14 @@ class Report extends Component { 
     | 
|
| 
       135 
149 
     | 
    
         
             
                    var numHSPsProcessed = 0;
         
     | 
| 
       136 
150 
     | 
    
         
             
                    while (this.nextQuery < this.state.queries.length) {
         
     | 
| 
       137 
151 
     | 
    
         
             
                        var query = this.state.queries[this.nextQuery];
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
       138 
153 
     | 
    
         
             
                        // We may see a query multiple times during rendering because only
         
     | 
| 
       139 
     | 
    
         
            -
                        // 3 hsps  
     | 
| 
      
 154 
     | 
    
         
            +
                        // 3 hsps are rendered in each cycle, but we want to create the
         
     | 
| 
       140 
155 
     | 
    
         
             
                        // corresponding Query component only the first time we see it.
         
     | 
| 
       141 
156 
     | 
    
         
             
                        if (this.nextHit == 0 && this.nextHSP == 0) {
         
     | 
| 
       142 
157 
     | 
    
         
             
                            results.push(
         
     | 
| 
       143 
158 
     | 
    
         
             
                                <ReportQuery
         
     | 
| 
       144 
     | 
    
         
            -
                                    key={'Query_' + query. 
     | 
| 
      
 159 
     | 
    
         
            +
                                    key={'Query_' + query.id}
         
     | 
| 
       145 
160 
     | 
    
         
             
                                    query={query}
         
     | 
| 
       146 
161 
     | 
    
         
             
                                    program={this.state.program}
         
     | 
| 
       147 
162 
     | 
    
         
             
                                    querydb={this.state.querydb}
         
     | 
| 
         @@ -151,6 +166,8 @@ class Report extends Component { 
     | 
|
| 
       151 
166 
     | 
    
         
             
                                    veryBig={this.state.veryBig}
         
     | 
| 
       152 
167 
     | 
    
         
             
                                />
         
     | 
| 
       153 
168 
     | 
    
         
             
                            );
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                            results.push(...this.plugins.queryResults(query));
         
     | 
| 
       154 
171 
     | 
    
         
             
                        }
         
     | 
| 
       155 
172 
     | 
    
         | 
| 
       156 
173 
     | 
    
         
             
                        while (this.nextHit < query.hits.length) {
         
     | 
| 
         @@ -185,11 +202,11 @@ class Report extends Component { 
     | 
|
| 
       185 
202 
     | 
    
         
             
                                    <HSP
         
     | 
| 
       186 
203 
     | 
    
         
             
                                        key={
         
     | 
| 
       187 
204 
     | 
    
         
             
                                            'Query_' +
         
     | 
| 
       188 
     | 
    
         
            -
             
     | 
| 
       189 
     | 
    
         
            -
             
     | 
| 
       190 
     | 
    
         
            -
             
     | 
| 
       191 
     | 
    
         
            -
             
     | 
| 
       192 
     | 
    
         
            -
             
     | 
| 
      
 205 
     | 
    
         
            +
                                            query.number +
         
     | 
| 
      
 206 
     | 
    
         
            +
                                            '_Hit_' +
         
     | 
| 
      
 207 
     | 
    
         
            +
                                            hit.number +
         
     | 
| 
      
 208 
     | 
    
         
            +
                                            '_HSP_' +
         
     | 
| 
      
 209 
     | 
    
         
            +
                                            hsp.number
         
     | 
| 
       193 
210 
     | 
    
         
             
                                        }
         
     | 
| 
       194 
211 
     | 
    
         
             
                                        query={query}
         
     | 
| 
       195 
212 
     | 
    
         
             
                                        hit={hit}
         
     | 
| 
         @@ -256,6 +273,9 @@ class Report extends Component { 
     | 
|
| 
       256 
273 
     | 
    
         
             
                                    <br />
         
     | 
| 
       257 
274 
     | 
    
         
             
                        You can bookmark the page and come back to it later or share the
         
     | 
| 
       258 
275 
     | 
    
         
             
                        link with someone.
         
     | 
| 
      
 276 
     | 
    
         
            +
                                    <br />
         
     | 
| 
      
 277 
     | 
    
         
            +
                                    <br />
         
     | 
| 
      
 278 
     | 
    
         
            +
                                    { process.env.targetEnv === 'cloud' && <b>If the job takes more than 10 minutes to complete, we will send you an email upon completion.</b> }
         
     | 
| 
       259 
279 
     | 
    
         
             
                                </p>
         
     | 
| 
       260 
280 
     | 
    
         
             
                            </div>
         
     | 
| 
       261 
281 
     | 
    
         
             
                        </div>
         
     | 
| 
         @@ -286,6 +306,40 @@ class Report extends Component { 
     | 
|
| 
       286 
306 
     | 
    
         
             
                    );
         
     | 
| 
       287 
307 
     | 
    
         
             
                }
         
     | 
| 
       288 
308 
     | 
    
         | 
| 
      
 309 
     | 
    
         
            +
             
     | 
| 
      
 310 
     | 
    
         
            +
                warningJSX() {
         
     | 
| 
      
 311 
     | 
    
         
            +
                    return(
         
     | 
| 
      
 312 
     | 
    
         
            +
                        <div className="container">
         
     | 
| 
      
 313 
     | 
    
         
            +
                            <div className="row">
         
     | 
| 
      
 314 
     | 
    
         
            +
                                <div className="col-md-6 col-md-offset-3 text-center">
         
     | 
| 
      
 315 
     | 
    
         
            +
                                    <h1>
         
     | 
| 
      
 316 
     | 
    
         
            +
                                        <i className="fa fa-exclamation-triangle"></i>  Warning
         
     | 
| 
      
 317 
     | 
    
         
            +
                                    </h1>
         
     | 
| 
      
 318 
     | 
    
         
            +
                                    <p>
         
     | 
| 
      
 319 
     | 
    
         
            +
                                        <br />
         
     | 
| 
      
 320 
     | 
    
         
            +
                                        The BLAST result might be too large to load in the browser. If you have a powerful machine you can try loading the results anyway. Otherwise, you can download the results and view them locally.
         
     | 
| 
      
 321 
     | 
    
         
            +
                                    </p>
         
     | 
| 
      
 322 
     | 
    
         
            +
                                    <br />
         
     | 
| 
      
 323 
     | 
    
         
            +
                                    <p>
         
     | 
| 
      
 324 
     | 
    
         
            +
                                        {this.state.download_links.map((link, index) => {
         
     | 
| 
      
 325 
     | 
    
         
            +
                                            return (
         
     | 
| 
      
 326 
     | 
    
         
            +
                                                <a href={link.url} className="btn btn-secondary" key={'download_link_' + index} >
         
     | 
| 
      
 327 
     | 
    
         
            +
                                                    {link.name}
         
     | 
| 
      
 328 
     | 
    
         
            +
                                                </a>
         
     | 
| 
      
 329 
     | 
    
         
            +
                                            );
         
     | 
| 
      
 330 
     | 
    
         
            +
                                        })}
         
     | 
| 
      
 331 
     | 
    
         
            +
                                    </p>
         
     | 
| 
      
 332 
     | 
    
         
            +
                                    <br />
         
     | 
| 
      
 333 
     | 
    
         
            +
                                    <p>
         
     | 
| 
      
 334 
     | 
    
         
            +
                                        <a href={location.pathname + '?bypass_file_size_warning=true'} className="btn btn-primary">
         
     | 
| 
      
 335 
     | 
    
         
            +
                                            View results in browser anyway
         
     | 
| 
      
 336 
     | 
    
         
            +
                                        </a>
         
     | 
| 
      
 337 
     | 
    
         
            +
                                    </p>
         
     | 
| 
      
 338 
     | 
    
         
            +
                                </div>
         
     | 
| 
      
 339 
     | 
    
         
            +
                            </div>
         
     | 
| 
      
 340 
     | 
    
         
            +
                        </div>
         
     | 
| 
      
 341 
     | 
    
         
            +
                    );
         
     | 
| 
      
 342 
     | 
    
         
            +
                }
         
     | 
| 
       289 
343 
     | 
    
         
             
                /**
         
     | 
| 
       290 
344 
     | 
    
         
             
               * Renders report overview.
         
     | 
| 
       291 
345 
     | 
    
         
             
               */
         
     | 
| 
         @@ -350,6 +404,15 @@ class Report extends Component { 
     | 
|
| 
       350 
404 
     | 
    
         
             
                    return this.state.queries.length >= 1;
         
     | 
| 
       351 
405 
     | 
    
         
             
                }
         
     | 
| 
       352 
406 
     | 
    
         | 
| 
      
 407 
     | 
    
         
            +
                /**
         
     | 
| 
      
 408 
     | 
    
         
            +
                 * Indicates the response contains a warning message for the user
         
     | 
| 
      
 409 
     | 
    
         
            +
                 * in which case we should not render the results and render the
         
     | 
| 
      
 410 
     | 
    
         
            +
                 * warning instead.
         
     | 
| 
      
 411 
     | 
    
         
            +
                 **/
         
     | 
| 
      
 412 
     | 
    
         
            +
                isUserWarningPresent() {
         
     | 
| 
      
 413 
     | 
    
         
            +
                    return this.state.user_warning;
         
     | 
| 
      
 414 
     | 
    
         
            +
                }
         
     | 
| 
      
 415 
     | 
    
         
            +
             
     | 
| 
       353 
416 
     | 
    
         
             
                /**
         
     | 
| 
       354 
417 
     | 
    
         
             
               * Returns true if we have at least one hit.
         
     | 
| 
       355 
418 
     | 
    
         
             
               */
         
     | 
| 
         @@ -403,7 +466,7 @@ class Report extends Component { 
     | 
|
| 
       403 
466 
     | 
    
         
             
                toggleTable() {
         
     | 
| 
       404 
467 
     | 
    
         
             
                    $('body').on(
         
     | 
| 
       405 
468 
     | 
    
         
             
                        'mousedown',
         
     | 
| 
       406 
     | 
    
         
            -
                        '.resultn  
     | 
| 
      
 469 
     | 
    
         
            +
                        '.resultn .caption[data-toggle="collapse"]',
         
     | 
| 
       407 
470 
     | 
    
         
             
                        function (event) {
         
     | 
| 
       408 
471 
     | 
    
         
             
                            var $this = $(this);
         
     | 
| 
       409 
472 
     | 
    
         
             
                            $this.on('mouseup mousemove', function handler(event) {
         
     | 
| 
         @@ -461,6 +524,7 @@ class Report extends Component { 
     | 
|
| 
       461 
524 
     | 
    
         
             
                    } else {
         
     | 
| 
       462 
525 
     | 
    
         
             
                        $hit.removeClass('glow');
         
     | 
| 
       463 
526 
     | 
    
         
             
                        $hit.next('.hsp').removeClass('glow');
         
     | 
| 
      
 527 
     | 
    
         
            +
                        $('.download-fasta-of-selected').attr('href', '#').removeAttr('download');
         
     | 
| 
       464 
528 
     | 
    
         
             
                    }
         
     | 
| 
       465 
529 
     | 
    
         | 
| 
       466 
530 
     | 
    
         
             
                    var $a = $('.download-fasta-of-selected');
         
     | 
| 
         @@ -539,7 +603,13 @@ class Report extends Component { 
     | 
|
| 
       539 
603 
     | 
    
         
             
                }
         
     | 
| 
       540 
604 
     | 
    
         | 
| 
       541 
605 
     | 
    
         
             
                render() {
         
     | 
| 
       542 
     | 
    
         
            -
                     
     | 
| 
      
 606 
     | 
    
         
            +
                    if (this.isUserWarningPresent()) {
         
     | 
| 
      
 607 
     | 
    
         
            +
                        return this.warningJSX();
         
     | 
| 
      
 608 
     | 
    
         
            +
                    } else if (this.isResultAvailable()) {
         
     | 
| 
      
 609 
     | 
    
         
            +
                        return this.resultsJSX();
         
     | 
| 
      
 610 
     | 
    
         
            +
                    } else {
         
     | 
| 
      
 611 
     | 
    
         
            +
                        return this.loadingJSX();
         
     | 
| 
      
 612 
     | 
    
         
            +
                    }
         
     | 
| 
       543 
613 
     | 
    
         
             
                }
         
     | 
| 
       544 
614 
     | 
    
         
             
            }
         
     | 
| 
       545 
615 
     | 
    
         | 
    
        data/public/js/search.js
    CHANGED
    
    | 
         @@ -3,14 +3,6 @@ import React, { Component } from "react"; 
     | 
|
| 
       3 
3 
     | 
    
         
             
            import { createRoot } from "react-dom/client";
         
     | 
| 
       4 
4 
     | 
    
         
             
            import { DnD } from "./dnd";
         
     | 
| 
       5 
5 
     | 
    
         
             
            import { Form } from "./form";
         
     | 
| 
       6 
     | 
    
         
            -
            /**
         
     | 
| 
       7 
     | 
    
         
            -
             * Load necessary polyfills.
         
     | 
| 
       8 
     | 
    
         
            -
             */
         
     | 
| 
       9 
     | 
    
         
            -
            $.webshims.setOptions(
         
     | 
| 
       10 
     | 
    
         
            -
              "basePath",
         
     | 
| 
       11 
     | 
    
         
            -
              "/vendor/npm/webshim@1.15.8/js-webshim/minified/shims/"
         
     | 
| 
       12 
     | 
    
         
            -
            );
         
     | 
| 
       13 
     | 
    
         
            -
            $.webshims.polyfill("forms");
         
     | 
| 
       14 
6 
     | 
    
         | 
| 
       15 
7 
     | 
    
         
             
            /**
         
     | 
| 
       16 
8 
     | 
    
         
             
             * Clear sessionStorage on reload.
         
     | 
    
        data/public/js/sidebar.js
    CHANGED
    
    | 
         @@ -4,7 +4,7 @@ import _ from 'underscore'; 
     | 
|
| 
       4 
4 
     | 
    
         
             
            import downloadFASTA from './download_fasta';
         
     | 
| 
       5 
5 
     | 
    
         
             
            import asMailtoHref from './mailto';
         
     | 
| 
       6 
6 
     | 
    
         
             
            import CloudShareModal from './cloud_share_modal';
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
      
 7 
     | 
    
         
            +
            import DownloadLinks from 'download_links';
         
     | 
| 
       8 
8 
     | 
    
         
             
            /**
         
     | 
| 
       9 
9 
     | 
    
         
             
             * checks whether code is being run by jest
         
     | 
| 
       10 
10 
     | 
    
         
             
             */
         
     | 
| 
         @@ -172,6 +172,9 @@ export default class extends Component { 
     | 
|
| 
       172 
172 
     | 
    
         
             
                    var sequence_ids = $('.hit-links :checkbox:checked').map(function () {
         
     | 
| 
       173 
173 
     | 
    
         
             
                        return this.value;
         
     | 
| 
       174 
174 
     | 
    
         
             
                    }).get();
         
     | 
| 
      
 175 
     | 
    
         
            +
                    if (sequence_ids.length === 0) {
         
     | 
| 
      
 176 
     | 
    
         
            +
                        return false;
         
     | 
| 
      
 177 
     | 
    
         
            +
                    }
         
     | 
| 
       175 
178 
     | 
    
         
             
                    var database_ids = _.map(this.props.data.querydb, _.iteratee('id'));
         
     | 
| 
       176 
179 
     | 
    
         
             
                    downloadFASTA(sequence_ids, database_ids);
         
     | 
| 
       177 
180 
     | 
    
         
             
                    return false;
         
     | 
| 
         @@ -347,6 +350,7 @@ export default class extends Component { 
     | 
|
| 
       347 
350 
     | 
    
         
             
                                        </a>
         
     | 
| 
       348 
351 
     | 
    
         
             
                                    </li>
         
     | 
| 
       349 
352 
     | 
    
         
             
                                }
         
     | 
| 
      
 353 
     | 
    
         
            +
                                <DownloadLinks imported_xml={this.props.data.imported_xml} search_id={this.props.data.search_id} />
         
     | 
| 
       350 
354 
     | 
    
         
             
                            </ul>
         
     | 
| 
       351 
355 
     | 
    
         
             
                        </div>
         
     | 
| 
       352 
356 
     | 
    
         
             
                    );
         
     | 
| 
         @@ -406,6 +410,12 @@ export default class extends Component { 
     | 
|
| 
       406 
410 
     | 
    
         
             
                            {this.topPanelJSX()}
         
     | 
| 
       407 
411 
     | 
    
         
             
                            {this.downloadsPanelJSX()}
         
     | 
| 
       408 
412 
     | 
    
         
             
                            {this.sharingPanelJSX()}
         
     | 
| 
      
 413 
     | 
    
         
            +
                            <div className="referral-panel">
         
     | 
| 
      
 414 
     | 
    
         
            +
                                <div className="section-header-sidebar">
         
     | 
| 
      
 415 
     | 
    
         
            +
                                    <h4>Recommend SequenceServer</h4>
         
     | 
| 
      
 416 
     | 
    
         
            +
                                    <p><a href="https://sequenceserver.com/referral-program" target="_blank">Earn up to $100 per signup</a></p>
         
     | 
| 
      
 417 
     | 
    
         
            +
                                </div>
         
     | 
| 
      
 418 
     | 
    
         
            +
                            </div>
         
     | 
| 
       409 
419 
     | 
    
         
             
                        </div>
         
     | 
| 
       410 
420 
     | 
    
         
             
                    );
         
     | 
| 
       411 
421 
     | 
    
         
             
                }
         
     | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            /* eslint-disable no-unused-vars */
         
     | 
| 
      
 2 
     | 
    
         
            +
            /* eslint-disable no-undef */
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { render, screen, fireEvent } from '@testing-library/react';
         
     | 
| 
      
 4 
     | 
    
         
            +
            import { Form } from '../form';
         
     | 
| 
      
 5 
     | 
    
         
            +
            import { AMINO_ACID_SEQUENCE } from './mock_data/sequences';
         
     | 
| 
      
 6 
     | 
    
         
            +
            import data from './mock_data/databases.json';
         
     | 
| 
      
 7 
     | 
    
         
            +
            import userEvent from '@testing-library/user-event';
         
     | 
| 
      
 8 
     | 
    
         
            +
            import '@testing-library/jest-dom/extend-expect';
         
     | 
| 
      
 9 
     | 
    
         
            +
            import '@testing-library/react/dont-cleanup-after-each';
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            export const setMockJSONResult = (result) => {
         
     | 
| 
      
 12 
     | 
    
         
            +
                global.$.getJSON = (_, cb) => cb(result);
         
     | 
| 
      
 13 
     | 
    
         
            +
            };
         
     | 
| 
      
 14 
     | 
    
         
            +
            describe('ADVANCED PARAMETERS', () => {
         
     | 
| 
      
 15 
     | 
    
         
            +
                const getInputElement = () => screen.getByRole('textbox', { name: '' });
         
     | 
| 
      
 16 
     | 
    
         
            +
                test('should not render the link to advanced parameters modal if blast algorithm is unknown', () => {
         
     | 
| 
      
 17 
     | 
    
         
            +
                    setMockJSONResult(data);
         
     | 
| 
      
 18 
     | 
    
         
            +
                    const {container } =render(<Form onSequenceTypeChanged={() => { }
         
     | 
| 
      
 19 
     | 
    
         
            +
                    } />);
         
     | 
| 
      
 20 
     | 
    
         
            +
                    const modalButton = container.querySelector('[data-target="#help"]');
         
     | 
| 
      
 21 
     | 
    
         
            +
                    expect(modalButton).toBeNull();
         
     | 
| 
      
 22 
     | 
    
         
            +
                });
         
     | 
| 
      
 23 
     | 
    
         
            +
                test('should render the link to advanced parameters modal if blast algorithm is known', () => {
         
     | 
| 
      
 24 
     | 
    
         
            +
                    setMockJSONResult(data);
         
     | 
| 
      
 25 
     | 
    
         
            +
                    const {container } =render(<Form onSequenceTypeChanged={() => { }
         
     | 
| 
      
 26 
     | 
    
         
            +
                    } />);
         
     | 
| 
      
 27 
     | 
    
         
            +
                    
         
     | 
| 
      
 28 
     | 
    
         
            +
                    const inputEl = getInputElement();
         
     | 
| 
      
 29 
     | 
    
         
            +
                    // populate search and select dbs to determine blast algorithm
         
     | 
| 
      
 30 
     | 
    
         
            +
                    fireEvent.change(inputEl, { target: { value: AMINO_ACID_SEQUENCE } });
         
     | 
| 
      
 31 
     | 
    
         
            +
                    const proteinSelectAllBtn = screen.getByRole('heading', { name: /protein databases/i }).parentElement.querySelector('button');
         
     | 
| 
      
 32 
     | 
    
         
            +
                    fireEvent.click(proteinSelectAllBtn);
         
     | 
| 
      
 33 
     | 
    
         
            +
                    const modalButton = container.querySelector('[data-target="#help"]');
         
     | 
| 
      
 34 
     | 
    
         
            +
                    expect(modalButton).not.toBeNull();
         
     | 
| 
      
 35 
     | 
    
         
            +
                });
         
     | 
| 
      
 36 
     | 
    
         
            +
            });
         
     | 
| 
         @@ -0,0 +1,49 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            export const AMINO_ACID_SEQUENCE = `MNTLWLSLWDYPGKLPLNFMVFDTKDDLQAAYWRDPYSIPLAVIFEDPQPISQRLIYEIR
         
     | 
| 
      
 2 
     | 
    
         
            +
            TNPSYTLPPPPTKLYSAPISCRKNKTGHWMDDILSIKTGESCPVNNYLHSGFLALQMITD
         
     | 
| 
      
 3 
     | 
    
         
            +
            ITKIKLENSDVTIPDIKLIMFPKEPYTADWMLAFRVVIPLYMVLALSQFITYLLILIVGE
         
     | 
| 
      
 4 
     | 
    
         
            +
            KENKIKEGMKMMGLNDSVF
         
     | 
| 
      
 5 
     | 
    
         
            +
            >SI2.2.0_13722 locus=Si_gnF.scaffold06207[1925625..1928536].pep_1 quality=100.00
         
     | 
| 
      
 6 
     | 
    
         
            +
            MSANRLNVLVTLMLAVALLVTESGNAQVDGYLQFNPKRSAVSSPQKYCGKKLSNALQIIC
         
     | 
| 
      
 7 
     | 
    
         
            +
            DGVYNSMFKKSGQDFPPQNKRHIAHRINGNEEESFTTLKSNFLNWCVEVYHRHYRFVFVS
         
     | 
| 
      
 8 
     | 
    
         
            +
            EMEMADYPLAYDISPYLPPFLSRARARGMLDGRFAGRRYRRESRGIHEECCINGCTINEL
         
     | 
| 
      
 9 
     | 
    
         
            +
            TSYCGP
         
     | 
| 
      
 10 
     | 
    
         
            +
            `;
         
     | 
| 
      
 11 
     | 
    
         
            +
            export const NUCLEOTIDE_SEQUENCE = `ATGAATACCCTCTGGCTCTCTTTATGGGATTATCCCGGTAAGCTTCCCTTAAACTTCATG
         
     | 
| 
      
 12 
     | 
    
         
            +
            GTGTTTGACACGAAGGATGATCTGCAAGCAGCGTATTGGAGAGATCCTTACAGCATACCT
         
     | 
| 
      
 13 
     | 
    
         
            +
            CTGGCAGTTATCTTCGAGGACCCCCAACCGATATCACAGCGACTTATATATGAAATTAGG
         
     | 
| 
      
 14 
     | 
    
         
            +
            ACGAATCCTTCATACACTTTGCCGCCACCGCCAACCAAATTGTATTCTGCTCCGATCAGT
         
     | 
| 
      
 15 
     | 
    
         
            +
            TGTCGAAAGAATAAAACTGGTCACTGGATGGACGACATTTTATCGATAAAAACCGGTGAA
         
     | 
| 
      
 16 
     | 
    
         
            +
            TCTTGTCCCGTTAACAATTACTTGCATTCTGGCTTCTTGGCTCTGCAAATGATAACGGAT
         
     | 
| 
      
 17 
     | 
    
         
            +
            ATCACAAAGATAAAATTGGAAAATTCTGACGTGACAATACCGGATATTAAACTCATAATG
         
     | 
| 
      
 18 
     | 
    
         
            +
            TTTCCTAAAGAGCCGTATACCGCTGACTGGATGCTGGCCTTCAGAGTTGTTATTCCGCTT
         
     | 
| 
      
 19 
     | 
    
         
            +
            TACATGGTCTTGGCTCTCTCGCAATTTATCACTTATCTTCTGATCCTAATAGTTGGCGAG
         
     | 
| 
      
 20 
     | 
    
         
            +
            AAGGAAAATAAGATTAAAGAGGGAATGAAGATGATGGGCTTAAATGATTCTGTGTTT
         
     | 
| 
      
 21 
     | 
    
         
            +
            >SI2.2.0_13722 Si_gnF.scaffold06207[1925625..1928536].pep_1
         
     | 
| 
      
 22 
     | 
    
         
            +
            ATGTCCGCGAATCGATTGAACGTGCTGGTGACCCTGATGCTCGCCGTCGCGCTTCTTGTG
         
     | 
| 
      
 23 
     | 
    
         
            +
            ACGGAATCAGGAAATGCACAGGTGGATGGCTATCTCCAATTCAACCCAAAGCGATCCGCC
         
     | 
| 
      
 24 
     | 
    
         
            +
            GTGAGCTCGCCGCAGAAGTATTGCGGCAAAAAGCTTTCTAATGCTCTACAGATAATCTGT
         
     | 
| 
      
 25 
     | 
    
         
            +
            GATGGCGTGTACAATTCCATGTTTAAGAAGAGTGGTCAAGATTTTCCCCCGCAAAATAAG
         
     | 
| 
      
 26 
     | 
    
         
            +
            AGACACATAGCACACAGAATAAATGGGAATGAGGAAGAGAGCTTTACTACGTTAAAGTCG
         
     | 
| 
      
 27 
     | 
    
         
            +
            AATTTTTTAAACTGGTGTGTTGAAGTTTATCATCGTCACTACAGATTCGTTTTTGTTTCA
         
     | 
| 
      
 28 
     | 
    
         
            +
            GAGATGGAAATGGCCGATTACCCGCTCGCCTATGATATTTCCCCGTATCTTCCGCCGTTC
         
     | 
| 
      
 29 
     | 
    
         
            +
            CTGTCGCGAGCGAGGGCACGGGGAATGTTAGACGGTCGCTTCGCCGGCAGACGCTACCGA
         
     | 
| 
      
 30 
     | 
    
         
            +
            AGGGAGTCGCGGGGCATTCACGAGGAGTGTTGCATCAACGGATGTACGATAAACGAATTG
         
     | 
| 
      
 31 
     | 
    
         
            +
            ACCAGCTACTGCGGCCCC
         
     | 
| 
      
 32 
     | 
    
         
            +
            `;
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            export const FASTQ_SEQUENCE =
         
     | 
| 
      
 35 
     | 
    
         
            +
            `@SRR001666.1 071112_SLXA-EAS1_s_7:5:1:817:345 length=72
         
     | 
| 
      
 36 
     | 
    
         
            +
            GGGTGATGGCCGCTGCCGATGGCGTCAAATCCCACCAAGTTACCCTTAACAACTTAAGGGTTTTCAAATAGA
         
     | 
| 
      
 37 
     | 
    
         
            +
            +SRR001666.1 071112_SLXA-EAS1_s_7:5:1:817:345 length=72
         
     | 
| 
      
 38 
     | 
    
         
            +
            IIIIIIIIIIIIIIIIIIIIIIIIIIIIII9IG9ICIIIIIIIIIIIIIIIIIIIIDIIIIIII>IIIIII/
         
     | 
| 
      
 39 
     | 
    
         
            +
            @SRR001666.2 071112_SLXA-EAS1_s_7:5:1:801:338 length=72
         
     | 
| 
      
 40 
     | 
    
         
            +
            GTTCAGGGATACGACGTTTGTATTTTAAGAATCTGAAGCAGAAGTCGATGATAATACGCGTCGTTTTATCAT
         
     | 
| 
      
 41 
     | 
    
         
            +
            +SRR001666.2 071112_SLXA-EAS1_s_7:5:1:801:338 length=72
         
     | 
| 
      
 42 
     | 
    
         
            +
            IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII6IBIIIIIIIIIIIIIIIIIIIIIIIGII>IIIII-I)8I
         
     | 
| 
      
 43 
     | 
    
         
            +
            `;
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
            export const FASTA_OF_FASTQ_SEQUENCE =
         
     | 
| 
      
 46 
     | 
    
         
            +
            `>SRR001666.1 071112_SLXA-EAS1_s_7:5:1:817:345 length=72
         
     | 
| 
      
 47 
     | 
    
         
            +
            GGGTGATGGCCGCTGCCGATGGCGTCAAATCCCACCAAGTTACCCTTAACAACTTAAGGGTTTTCAAATAGA
         
     | 
| 
      
 48 
     | 
    
         
            +
            >SRR001666.2 071112_SLXA-EAS1_s_7:5:1:801:338 length=72
         
     | 
| 
      
 49 
     | 
    
         
            +
            GTTCAGGGATACGACGTTTGTATTTTAAGAATCTGAAGCAGAAGTCGATGATAATACGCGTCGTTTTATCAT`;
         
     | 
| 
         @@ -25,7 +25,14 @@ const TestSidebar = ({ long }) => { 
     | 
|
| 
       25 
25 
     | 
    
         
             
                />;
         
     | 
| 
       26 
26 
     | 
    
         
             
            };
         
     | 
| 
       27 
27 
     | 
    
         | 
| 
      
 28 
     | 
    
         
            +
            const clickCheckboxes = (checkboxes, count) => {
         
     | 
| 
      
 29 
     | 
    
         
            +
                Array.from(checkboxes).slice(0, count).forEach((checkbox) => {
         
     | 
| 
      
 30 
     | 
    
         
            +
                    fireEvent.click(checkbox);
         
     | 
| 
      
 31 
     | 
    
         
            +
                });
         
     | 
| 
      
 32 
     | 
    
         
            +
            };
         
     | 
| 
       28 
33 
     | 
    
         
             
            describe('REPORT PAGE', () => {
         
     | 
| 
      
 34 
     | 
    
         
            +
                global.URL.createObjectURL = jest.fn();//.mockReturnValue('xyz.test');
         
     | 
| 
      
 35 
     | 
    
         
            +
                global.setTimeout = (cb) => cb();
         
     | 
| 
       29 
36 
     | 
    
         
             
                it('should render the report component with initial loading state', () => {
         
     | 
| 
       30 
37 
     | 
    
         
             
                    render(<Report />);
         
     | 
| 
       31 
38 
     | 
    
         
             
                    expect(screen.getByRole('heading', { name: 'BLAST-ing' })).toBeInTheDocument();
         
     | 
| 
         @@ -39,7 +46,7 @@ describe('REPORT PAGE', () => { 
     | 
|
| 
       39 
46 
     | 
    
         
             
                });
         
     | 
| 
       40 
47 
     | 
    
         
             
                it('it should render the report page correctly if there\'s a response provided', () => {
         
     | 
| 
       41 
48 
     | 
    
         
             
                    setMockJSONResult({ status: 200, responseJSON: shortResponseJSON });
         
     | 
| 
       42 
     | 
    
         
            -
                    const { container } = render(<Report />);
         
     | 
| 
      
 49 
     | 
    
         
            +
                    const { container } = render(<Report getCharacterWidth={jest.fn()} />);
         
     | 
| 
       43 
50 
     | 
    
         
             
                    expect(container.querySelector('#results')).toBeInTheDocument();
         
     | 
| 
       44 
51 
     | 
    
         | 
| 
       45 
52 
     | 
    
         
             
                });
         
     | 
| 
         @@ -63,18 +70,18 @@ describe('REPORT PAGE', () => { 
     | 
|
| 
       63 
70 
     | 
    
         
             
                    });
         
     | 
| 
       64 
71 
     | 
    
         | 
| 
       65 
72 
     | 
    
         
             
                    describe('LONG QUERIES (>12)', () => {
         
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
      
 73 
     | 
    
         
            +
                        let container;
         
     | 
| 
      
 74 
     | 
    
         
            +
                        beforeEach(() => {
         
     | 
| 
      
 75 
     | 
    
         
            +
                            container = render(<TestSidebar long />).container;
         
     | 
| 
      
 76 
     | 
    
         
            +
                        });
         
     | 
| 
       67 
77 
     | 
    
         
             
                        it('should not show navigation links for long queries', () => {
         
     | 
| 
       68 
     | 
    
         
            -
                            const { container } = render(<TestSidebar long />);
         
     | 
| 
       69 
78 
     | 
    
         
             
                            expect(container.querySelectorAll('a[href^="#Query_"]').length).toBe(0);
         
     | 
| 
       70 
79 
     | 
    
         
             
                        });
         
     | 
| 
       71 
80 
     | 
    
         
             
                        it('should show only next button if on first query ', () => {
         
     | 
| 
       72 
     | 
    
         
            -
                            render(<TestSidebar long />);
         
     | 
| 
       73 
81 
     | 
    
         
             
                            expect(nextQueryButton()).toBeInTheDocument();
         
     | 
| 
       74 
82 
     | 
    
         
             
                            expect(previousQueryButton()).not.toBeInTheDocument();
         
     | 
| 
       75 
83 
     | 
    
         
             
                        });
         
     | 
| 
       76 
84 
     | 
    
         
             
                        it('should show both previous and next buttons if not on first query', () => {
         
     | 
| 
       77 
     | 
    
         
            -
                            render(<TestSidebar long />);
         
     | 
| 
       78 
85 
     | 
    
         
             
                            const nextBtn = nextQueryButton();
         
     | 
| 
       79 
86 
     | 
    
         
             
                            expect(nextBtn).toBeInTheDocument();
         
     | 
| 
       80 
87 
     | 
    
         
             
                            fireEvent.click(nextBtn);
         
     | 
| 
         @@ -84,7 +91,6 @@ describe('REPORT PAGE', () => { 
     | 
|
| 
       84 
91 
     | 
    
         
             
                        });
         
     | 
| 
       85 
92 
     | 
    
         
             
                        it('should show only previous button if on last query', () => {
         
     | 
| 
       86 
93 
     | 
    
         
             
                            const { queries } = longResponseJSON;
         
     | 
| 
       87 
     | 
    
         
            -
                            render(<TestSidebar long />);
         
     | 
| 
       88 
94 
     | 
    
         
             
                            expect(nextQueryButton()).toBeInTheDocument();
         
     | 
| 
       89 
95 
     | 
    
         
             
                            expect(previousQueryButton()).not.toBeInTheDocument();
         
     | 
| 
       90 
96 
     | 
    
         | 
| 
         @@ -95,5 +101,55 @@ describe('REPORT PAGE', () => { 
     | 
|
| 
       95 
101 
     | 
    
         
             
                            expect(previousQueryButton()).toBeInTheDocument();
         
     | 
| 
       96 
102 
     | 
    
         
             
                        });
         
     | 
| 
       97 
103 
     | 
    
         
             
                    });
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                    describe('DOWNLOAD LINKS', () => {
         
     | 
| 
      
 106 
     | 
    
         
            +
                        let container;
         
     | 
| 
      
 107 
     | 
    
         
            +
                        beforeEach(() => {
         
     | 
| 
      
 108 
     | 
    
         
            +
                            setMockJSONResult({ status: 200, responseJSON: shortResponseJSON });
         
     | 
| 
      
 109 
     | 
    
         
            +
                            container = render(<Report getCharacterWidth={jest.fn()}  />).container;
         
     | 
| 
      
 110 
     | 
    
         
            +
                        });
         
     | 
| 
      
 111 
     | 
    
         
            +
                        describe('ALIGNMENT DOWNLOAD', () => {
         
     | 
| 
      
 112 
     | 
    
         
            +
                            it('should generate a blob url and filename for downloading alignment of all hits on render', () => {
         
     | 
| 
      
 113 
     | 
    
         
            +
                                const alignment_download_link = container.querySelector('.download-alignment-of-all');
         
     | 
| 
      
 114 
     | 
    
         
            +
                                const expected_num_hits = container.querySelectorAll('.hit-links input[type="checkbox"]').length;
         
     | 
| 
      
 115 
     | 
    
         
            +
                                const file_name = `alignment-${expected_num_hits}_hits.txt`;
         
     | 
| 
      
 116 
     | 
    
         
            +
                                expect(alignment_download_link.download).toEqual(file_name);
         
     | 
| 
      
 117 
     | 
    
         
            +
                                expect(alignment_download_link.hred).not.toEqual('#');
         
     | 
| 
      
 118 
     | 
    
         
            +
                            });
         
     | 
| 
      
 119 
     | 
    
         
            +
                            it('link for downloading alignment of specific number of selected hits should be disabled on initial load', () => {
         
     | 
| 
      
 120 
     | 
    
         
            +
                                const alignment_download_link = container.querySelector('.download-alignment-of-selected');
         
     | 
| 
      
 121 
     | 
    
         
            +
                                expect(alignment_download_link.classList.contains('disabled')).toBeTruthy();
         
     | 
| 
      
 122 
     | 
    
         
            +
                
         
     | 
| 
      
 123 
     | 
    
         
            +
                            });
         
     | 
| 
      
 124 
     | 
    
         
            +
                            it('should generate a blob url and filename for downloading alignment of specific number of selected hits', () => {
         
     | 
| 
      
 125 
     | 
    
         
            +
                                const alignment_download_link = container.querySelector('.download-alignment-of-selected');
         
     | 
| 
      
 126 
     | 
    
         
            +
                                // QUERY ALL HIT LINKS CHECKBOXES
         
     | 
| 
      
 127 
     | 
    
         
            +
                                const checkboxes = container.querySelectorAll('.hit-links input[type="checkbox"]');
         
     | 
| 
      
 128 
     | 
    
         
            +
                                // SELECT 4 CHECKBOXES
         
     | 
| 
      
 129 
     | 
    
         
            +
                                clickCheckboxes(checkboxes, 4);
         
     | 
| 
      
 130 
     | 
    
         
            +
                                const file_name = 'alignment-4_hits.txt';
         
     | 
| 
      
 131 
     | 
    
         
            +
                                expect(alignment_download_link.textContent).toEqual('Alignment of 4 selected hit(s)');
         
     | 
| 
      
 132 
     | 
    
         
            +
                                expect(alignment_download_link.download).toEqual(file_name);
         
     | 
| 
      
 133 
     | 
    
         
            +
                            });
         
     | 
| 
      
 134 
     | 
    
         
            +
                        });
         
     | 
| 
      
 135 
     | 
    
         
            +
                
         
     | 
| 
      
 136 
     | 
    
         
            +
                        describe('FASTA DOWNLOAD', () => {
         
     | 
| 
      
 137 
     | 
    
         
            +
                            let fasta_download_link;
         
     | 
| 
      
 138 
     | 
    
         
            +
                            beforeEach(() => {
         
     | 
| 
      
 139 
     | 
    
         
            +
                                fasta_download_link = container.querySelector('.download-fasta-of-selected');
         
     | 
| 
      
 140 
     | 
    
         
            +
                            });
         
     | 
| 
      
 141 
     | 
    
         
            +
                            it('link for downloading fasta of selected number of hits should be disabled on initial load', () => {
         
     | 
| 
      
 142 
     | 
    
         
            +
                                expect(fasta_download_link.classList.contains('disabled')).toBeTruthy();
         
     | 
| 
      
 143 
     | 
    
         
            +
                            });
         
     | 
| 
      
 144 
     | 
    
         
            +
                
         
     | 
| 
      
 145 
     | 
    
         
            +
                            it('link for downloading fasta of specific number of selected hits should be active after selection', () => {
         
     | 
| 
      
 146 
     | 
    
         
            +
                                const checkboxes = container.querySelectorAll('.hit-links input[type="checkbox"]');
         
     | 
| 
      
 147 
     | 
    
         
            +
                                // SELECT 5 CHECKBOXES
         
     | 
| 
      
 148 
     | 
    
         
            +
                                clickCheckboxes(checkboxes, 5);
         
     | 
| 
      
 149 
     | 
    
         
            +
                                expect(fasta_download_link.textContent).toEqual('FASTA of 5 selected hit(s)');
         
     | 
| 
      
 150 
     | 
    
         
            +
                            });
         
     | 
| 
      
 151 
     | 
    
         
            +
                        });
         
     | 
| 
      
 152 
     | 
    
         
            +
                    });
         
     | 
| 
       98 
153 
     | 
    
         
             
                });
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
       99 
155 
     | 
    
         
             
            });
         
     | 
| 
         @@ -3,39 +3,65 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            import { render, screen, fireEvent } from '@testing-library/react';
         
     | 
| 
       4 
4 
     | 
    
         
             
            import { SearchQueryWidget } from '../query';
         
     | 
| 
       5 
5 
     | 
    
         
             
            import { Form } from '../form';
         
     | 
| 
       6 
     | 
    
         
            -
            import  
     | 
| 
      
 6 
     | 
    
         
            +
            import { AMINO_ACID_SEQUENCE, NUCLEOTIDE_SEQUENCE, FASTQ_SEQUENCE, FASTA_OF_FASTQ_SEQUENCE } from './mock_data/sequences';
         
     | 
| 
       7 
7 
     | 
    
         
             
            import '@testing-library/jest-dom/extend-expect';
         
     | 
| 
       8 
8 
     | 
    
         
             
            import '@testing-library/react/dont-cleanup-after-each';
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
            KENKIKEGMKMMGLNDSVF
         
     | 
| 
       14 
     | 
    
         
            -
            >SI2.2.0_13722 locus=Si_gnF.scaffold06207[1925625..1928536].pep_1 quality=100.00
         
     | 
| 
       15 
     | 
    
         
            -
            MSANRLNVLVTLMLAVALLVTESGNAQVDGYLQFNPKRSAVSSPQKYCGKKLSNALQIIC
         
     | 
| 
       16 
     | 
    
         
            -
            DGVYNSMFKKSGQDFPPQNKRHIAHRINGNEEESFTTLKSNFLNWCVEVYHRHYRFVFVS
         
     | 
| 
       17 
     | 
    
         
            -
            EMEMADYPLAYDISPYLPPFLSRARARGMLDGRFAGRRYRRESRGIHEECCINGCTINEL
         
     | 
| 
       18 
     | 
    
         
            -
            TSYCGP
         
     | 
| 
       19 
     | 
    
         
            -
            `;
         
     | 
| 
      
 10 
     | 
    
         
            +
            let container;
         
     | 
| 
      
 11 
     | 
    
         
            +
            let inputEl;
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
       20 
13 
     | 
    
         
             
            describe('SEARCH COMPONENT', () => {
         
     | 
| 
       21 
     | 
    
         
            -
                 
     | 
| 
      
 14 
     | 
    
         
            +
                beforeEach(() => {
         
     | 
| 
      
 15 
     | 
    
         
            +
                    container  = render(<Form onSequenceTypeChanged={() => { }
         
     | 
| 
      
 16 
     | 
    
         
            +
                    } />).container;
         
     | 
| 
      
 17 
     | 
    
         
            +
                    inputEl = screen.getByRole('textbox', { name: '' });
         
     | 
| 
      
 18 
     | 
    
         
            +
                });
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
       22 
20 
     | 
    
         
             
                test('should render the search component textarea', () => {
         
     | 
| 
       23 
     | 
    
         
            -
                     
     | 
| 
       24 
     | 
    
         
            -
                    } />);
         
     | 
| 
       25 
     | 
    
         
            -
                    const el = getInputElement();
         
     | 
| 
       26 
     | 
    
         
            -
                    expect(el).toHaveClass('form-control');
         
     | 
| 
      
 21 
     | 
    
         
            +
                    expect(inputEl).toHaveClass('form-control');
         
     | 
| 
       27 
22 
     | 
    
         
             
                });
         
     | 
| 
       28 
23 
     | 
    
         | 
| 
       29 
24 
     | 
    
         
             
                test('clear button should only become visible if textarea is not empty', () => {
         
     | 
| 
       30 
     | 
    
         
            -
                    render(<SearchQueryWidget onSequenceTypeChanged={() => { }
         
     | 
| 
       31 
     | 
    
         
            -
                    } />);
         
     | 
| 
       32 
25 
     | 
    
         
             
                    const getButtonWrapper = () => screen.getByRole('button', { name: /clear query sequence\(s\)\./i }).parentElement;
         
     | 
| 
       33 
26 
     | 
    
         
             
                    expect(getButtonWrapper()).toHaveClass('hidden');
         
     | 
| 
       34 
     | 
    
         
            -
                    const inputEl = getInputElement();
         
     | 
| 
       35 
27 
     | 
    
         
             
                    fireEvent.change(inputEl, { target: { value: AMINO_ACID_SEQUENCE } });
         
     | 
| 
       36 
28 
     | 
    
         
             
                    expect(getButtonWrapper()).not.toHaveClass('hidden');
         
     | 
| 
       37 
29 
     | 
    
         
             
                    fireEvent.change(inputEl, { target: { value: '' } });
         
     | 
| 
       38 
30 
     | 
    
         
             
                    expect(getButtonWrapper()).toHaveClass('hidden');
         
     | 
| 
      
 31 
     | 
    
         
            +
                });
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                test('should correctly detect the amino-acid sequence type and show notification', () => {
         
     | 
| 
      
 34 
     | 
    
         
            +
                    // populate search
         
     | 
| 
      
 35 
     | 
    
         
            +
                    fireEvent.change(inputEl, { target: { value: AMINO_ACID_SEQUENCE } });
         
     | 
| 
      
 36 
     | 
    
         
            +
                    const activeNotification = container.querySelector('.notification.active');
         
     | 
| 
      
 37 
     | 
    
         
            +
                    expect(activeNotification.id).toBe('protein-sequence-notification');
         
     | 
| 
      
 38 
     | 
    
         
            +
                    const alertWrapper = activeNotification.children[0];
         
     | 
| 
      
 39 
     | 
    
         
            +
                    expect(alertWrapper).toHaveTextContent('Detected: amino-acid sequence(s).');
         
     | 
| 
      
 40 
     | 
    
         
            +
                });
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                test('should correctly detect the nucleotide sequence type and show notification', () => {
         
     | 
| 
      
 43 
     | 
    
         
            +
                    // populate search
         
     | 
| 
      
 44 
     | 
    
         
            +
                    fireEvent.change(inputEl, { target: { value: NUCLEOTIDE_SEQUENCE } });
         
     | 
| 
      
 45 
     | 
    
         
            +
                    const activeNotification = container.querySelector('.notification.active');
         
     | 
| 
      
 46 
     | 
    
         
            +
                    const alertWrapper = activeNotification.children[0];
         
     | 
| 
      
 47 
     | 
    
         
            +
                    expect(activeNotification.id).toBe('nucleotide-sequence-notification');
         
     | 
| 
      
 48 
     | 
    
         
            +
                    expect(alertWrapper).toHaveTextContent('Detected: nucleotide sequence(s).');
         
     | 
| 
      
 49 
     | 
    
         
            +
                });
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                test('should correctly detect the mixed sequences and show error notification', () => {
         
     | 
| 
      
 52 
     | 
    
         
            +
                    fireEvent.change(inputEl, { target: { value: `${NUCLEOTIDE_SEQUENCE}${AMINO_ACID_SEQUENCE}` } });
         
     | 
| 
      
 53 
     | 
    
         
            +
                    const activeNotification = container.querySelector('.notification.active');
         
     | 
| 
      
 54 
     | 
    
         
            +
                    expect(activeNotification.id).toBe('mixed-sequence-notification');
         
     | 
| 
      
 55 
     | 
    
         
            +
                    const alertWrapper = activeNotification.children[0];
         
     | 
| 
      
 56 
     | 
    
         
            +
                    expect(alertWrapper).toHaveTextContent('Error: mixed nucleotide and amino-acid sequences detected.');
         
     | 
| 
      
 57 
     | 
    
         
            +
                });
         
     | 
| 
       39 
58 
     | 
    
         | 
| 
      
 59 
     | 
    
         
            +
                test('should correctly detect FASTQ and convert it to FASTA', () => {
         
     | 
| 
      
 60 
     | 
    
         
            +
                    fireEvent.change(inputEl, { target: { value: FASTQ_SEQUENCE } });
         
     | 
| 
      
 61 
     | 
    
         
            +
                    const activeNotification = container.querySelector('.notification.active');
         
     | 
| 
      
 62 
     | 
    
         
            +
                    const alertWrapper = activeNotification.children[0];
         
     | 
| 
      
 63 
     | 
    
         
            +
                    expect(activeNotification.id).toBe('fastq-sequence-notification');
         
     | 
| 
      
 64 
     | 
    
         
            +
                    expect(alertWrapper).toHaveTextContent('Detected FASTQ and automatically converted to FASTA.');
         
     | 
| 
      
 65 
     | 
    
         
            +
                    expect(inputEl).toHaveValue(FASTA_OF_FASTQ_SEQUENCE);
         
     | 
| 
       40 
66 
     | 
    
         
             
                });
         
     | 
| 
       41 
67 
     | 
    
         
             
            });
         
     |