-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1463 from sanger/dpl-962-enhance-tube-arraying-sc…
…reen DPL-962 added tube summary panel to tube arraying screen
- Loading branch information
Showing
3 changed files
with
269 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 147 additions & 0 deletions
147
app/javascript/multi-stamp-tubes/components/TubeArraySummary.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
// Import the component being tested | ||
import { mount } from '@vue/test-utils' | ||
import TubeArraySummary from './TubeArraySummary.vue' | ||
|
||
// create an extended `Vue` constructor | ||
import localVue from 'test_support/base_vue' | ||
|
||
describe('TubeArraySummary', () => { | ||
var emptyTubes = [] | ||
for (let i = 0; i < 96; i++) { | ||
emptyTubes.push({ index: i, labware: null, state: 'empty' }) | ||
} | ||
|
||
var fullSetOfTubes = [] | ||
for (let i = 0; i < 96; i++) { | ||
var machine_barcode = (1000000000000 + i).toString() | ||
var human_barcode = 'NT' + (1000 + i).toString() + 'G' | ||
fullSetOfTubes.push({ | ||
index: i, | ||
labware: { labware_barcode: { human_barcode: human_barcode, machine_barcode: machine_barcode } }, | ||
state: 'valid', | ||
}) | ||
} | ||
|
||
var mixtureOfTubesWithDuplicates = [] | ||
for (let i = 0; i < 96; i++) { | ||
switch (true) { | ||
case i < 6: | ||
var human_barcode1 = 'NT1001G' | ||
var machine_barcode1 = '1000000000001' | ||
mixtureOfTubesWithDuplicates.push({ | ||
index: i.toString(), | ||
labware: { labware_barcode: { human_barcode: human_barcode1, machine_barcode: machine_barcode1 } }, | ||
state: 'valid', | ||
}) | ||
break | ||
case i < 12: | ||
var human_barcode2 = 'NT1002H' | ||
var machine_barcode2 = '1000000000002' | ||
mixtureOfTubesWithDuplicates.push({ | ||
index: i.toString(), | ||
labware: { labware_barcode: { human_barcode: human_barcode2, machine_barcode: machine_barcode2 } }, | ||
state: 'valid', | ||
}) | ||
break | ||
case i < 18: | ||
var human_barcode3 = 'NT1003I' | ||
var machine_barcode3 = '1000000000003' | ||
mixtureOfTubesWithDuplicates.push({ | ||
index: i.toString(), | ||
labware: { labware_barcode: { human_barcode: human_barcode3, machine_barcode: machine_barcode3 } }, | ||
state: 'valid', | ||
}) | ||
break | ||
default: | ||
mixtureOfTubesWithDuplicates.push({ index: i.toString(), labware: null, state: 'empty' }) | ||
break | ||
} | ||
} | ||
|
||
const wrapperTubeArraySummaryEmpty = function () { | ||
return mount(TubeArraySummary, { | ||
propsData: { | ||
tubes: emptyTubes, | ||
}, | ||
localVue, | ||
}) | ||
} | ||
|
||
const wrapperTubeArraySummaryWithDuplicates = function () { | ||
return mount(TubeArraySummary, { | ||
propsData: { | ||
tubes: mixtureOfTubesWithDuplicates, | ||
}, | ||
localVue, | ||
}) | ||
} | ||
|
||
const wrapperTubeArraySummaryFull = function () { | ||
return mount(TubeArraySummary, { | ||
propsData: { | ||
tubes: fullSetOfTubes, | ||
}, | ||
localVue, | ||
}) | ||
} | ||
it('renders the provided caption', () => { | ||
const wrapper = wrapperTubeArraySummaryWithDuplicates() | ||
|
||
expect(wrapper.find('caption').text()).toEqual('Summary of scanned tubes') | ||
}) | ||
|
||
it('renders the provided tubes summary headers', () => { | ||
const wrapper = wrapperTubeArraySummaryWithDuplicates() | ||
|
||
expect(wrapper.find('#header_human_barcode').text()).toEqual('Human Barcode') | ||
expect(wrapper.find('#header_machine_barcode').text()).toEqual('Machine Barcode') | ||
expect(wrapper.find('#header_replicates').text()).toEqual('Replicates') | ||
}) | ||
|
||
it('renders the provided tubes summary rows', () => { | ||
const wrapper = wrapperTubeArraySummaryWithDuplicates() | ||
|
||
// row 1 | ||
expect(wrapper.find('#row_human_barcode_index_0').text()).toEqual('NT1001G') | ||
expect(wrapper.find('#row_machine_barcode_index_0').text()).toEqual('1000000000001') | ||
expect(wrapper.find('#row_replicates_index_0').text()).toEqual('6') | ||
|
||
// row 2 | ||
expect(wrapper.find('#row_human_barcode_index_1').text()).toEqual('NT1002H') | ||
expect(wrapper.find('#row_machine_barcode_index_1').text()).toEqual('1000000000002') | ||
expect(wrapper.find('#row_replicates_index_1').text()).toEqual('6') | ||
|
||
// row 3 | ||
expect(wrapper.find('#row_human_barcode_index_2').text()).toEqual('NT1003I') | ||
expect(wrapper.find('#row_machine_barcode_index_2').text()).toEqual('1000000000003') | ||
expect(wrapper.find('#row_replicates_index_2').text()).toEqual('6') | ||
|
||
// row 4 | ||
expect(wrapper.find('#row_human_barcode_index_3').text()).toEqual('Empty') | ||
expect(wrapper.find('#row_machine_barcode_index_3').text()).toEqual('Empty') | ||
expect(wrapper.find('#row_replicates_index_3').text()).toEqual('78') | ||
}) | ||
|
||
it('only renders a row for the empty positions when there are no tubes', () => { | ||
const wrapper = wrapperTubeArraySummaryEmpty() | ||
|
||
// row 1 | ||
expect(wrapper.find('#row_human_barcode_index_0').text()).toEqual('Empty') | ||
expect(wrapper.find('#row_machine_barcode_index_0').text()).toEqual('Empty') | ||
expect(wrapper.find('#row_replicates_index_0').text()).toEqual('96') | ||
}) | ||
|
||
it('does not render a row for empty positions when there are none', () => { | ||
const wrapper = wrapperTubeArraySummaryFull() | ||
|
||
// row 1 | ||
expect(wrapper.find('#row_human_barcode_index_0').text()).toEqual('NT1000G') | ||
expect(wrapper.find('#row_machine_barcode_index_0').text()).toEqual('1000000000000') | ||
expect(wrapper.find('#row_replicates_index_0').text()).toEqual('1') | ||
|
||
// row 96 | ||
expect(wrapper.find('#row_human_barcode_index_95').text()).toEqual('NT1095G') | ||
expect(wrapper.find('#row_machine_barcode_index_95').text()).toEqual('1000000000095') | ||
expect(wrapper.find('#row_replicates_index_95').text()).toEqual('1') | ||
}) | ||
}) |
103 changes: 103 additions & 0 deletions
103
app/javascript/multi-stamp-tubes/components/TubeArraySummary.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
<template> | ||
<table id="tube_scan_summary" :class="['plate-view', 'pool-colours']"> | ||
<caption> | ||
{{ | ||
'Summary of scanned tubes' | ||
}} | ||
</caption> | ||
<thead> | ||
<tr> | ||
<th class="first-col" /> | ||
<th id="header_human_barcode" class="headingcell">Human Barcode</th> | ||
<th id="header_machine_barcode" class="headingcell">Machine Barcode</th> | ||
<th id="header_replicates" class="headingcell">Replicates</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr v-for="(value, machine_barcode, rowIndex) in tubesDict" :key="rowIndex"> | ||
<th class="first-col"> | ||
{{ (rowIndex + 1).toString() + '.' }} | ||
</th> | ||
<td :id="`row_human_barcode_index_${rowIndex}`" class="summarycell"> | ||
{{ value.human_barcode }} | ||
</td> | ||
<td :id="`row_machine_barcode_index_${rowIndex}`" class="summarycell"> | ||
{{ machine_barcode }} | ||
</td> | ||
<td :id="`row_replicates_index_${rowIndex}`" class="replicate_cell"> | ||
{{ value.replicates }} | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</template> | ||
|
||
<script> | ||
export default { | ||
name: 'TubeArraySummary', | ||
props: { | ||
// See parent component MultiStampTubes for more details about the Lab process. | ||
// This prop is the array of tube objects from the parent component, it represents the tubes | ||
// scanned into the MultiStampTubes arraying component. It gets updated every time there is | ||
// a change to the tubes in the parent component. | ||
// | ||
// Each tube object contains the following: | ||
// - index: the index of the tube in the array, related to the position in the tube rack | ||
// - labware: the labware object scanned into the parent screen, null if position is empty, here | ||
// we are most interested in the barcodes of the labware | ||
// - state: the state of the tube at this index, or 'empty' if nothing scanned yet | ||
tubes: { | ||
type: Array, | ||
required: true, | ||
}, | ||
}, | ||
computed: { | ||
// Create a dictionary of tube barcodes and their numbers of replicates for use | ||
// in the summary table | ||
tubesDict() { | ||
var summary_dict = {} | ||
this.tubes.forEach(function (tube) { | ||
var tube_machine_barcode = 'Empty' | ||
var tube_human_barcode = 'Empty' | ||
// extract the labware barcodes where the labware has been scanned | ||
if (tube.labware != null) { | ||
tube_machine_barcode = tube.labware.labware_barcode.machine_barcode | ||
tube_human_barcode = tube.labware.labware_barcode.human_barcode | ||
} | ||
// build up the dictionary of summary table replicates by barcode (and empties) | ||
if (tube_machine_barcode in summary_dict) { | ||
summary_dict[tube_machine_barcode]['replicates'] += 1 | ||
} else { | ||
summary_dict[tube_machine_barcode] = { | ||
replicates: 1, | ||
human_barcode: tube_human_barcode, | ||
} | ||
} | ||
}) | ||
return summary_dict | ||
}, | ||
}, | ||
} | ||
</script> | ||
|
||
<style scoped lang="scss"> | ||
.headingcell { | ||
margin-top: 2px; | ||
text-align: left; | ||
padding: 4px; | ||
border: 1px #343a40 solid; | ||
border-radius: 2px; | ||
} | ||
.summarycell { | ||
margin-top: 2px; | ||
text-align: left; | ||
padding: 4px; | ||
} | ||
.replicate_cell { | ||
margin-top: 2px; | ||
text-align: right; | ||
padding: 4px; | ||
} | ||
</style> |