-
Notifications
You must be signed in to change notification settings - Fork 44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added Selenium tests for inventory page #75
base: react
Are you sure you want to change the base?
Changes from 28 commits
4b53fc0
64cb3f7
6f941b1
2f8eeeb
63a0264
0e263ee
b5beac0
f40744f
2cd5f12
89876e7
8e8aec2
9f7d9e3
ccb900b
300c291
dff1b0c
4654203
874c362
6a74f87
718f5bc
3068e09
85767ca
9bb3030
26cd5fa
464dca3
0a744e6
d8dace6
9b334f7
c112398
e5c0c5c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
import React, { useState, useEffect } from 'react'; | ||
import axios from 'axios'; | ||
import TableManager from '../../core/all-patients/TableManager'; | ||
import CSRFToken from '../../util/CSRFToken'; | ||
import compare from '../../util/compare'; | ||
import simpleComparator from '../../util/simpleComparator'; | ||
import Container from 'react-bootstrap/Container'; | ||
import Button from 'react-bootstrap/Button'; | ||
import Modal from 'react-bootstrap/Modal'; | ||
import Form from 'react-bootstrap/Form'; | ||
import globalFilter from './globalFilter'; | ||
import { DateTime } from 'luxon'; | ||
|
||
|
||
function DrugListTable(props) { | ||
|
||
const [state, setState] = useState({ 'show': false, 'drug': {} }); | ||
|
||
const handleClose = () => { | ||
setState({ 'show': false, 'drug': {} }); | ||
} | ||
const handleShow = (e, row) => { | ||
setState({ 'show': true, 'drug': row }); | ||
} | ||
|
||
const nameComparator = simpleComparator('name'); | ||
const categoryComparator = simpleComparator('category'); | ||
const expirationDateComparator = simpleComparator('expiration_date'); | ||
const stockComparator = simpleComparator('stock'); | ||
Comment on lines
+26
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's still a bug with the comparators. It seems that all the comparators after nameComparator are just returning the same result as the name comparator. I'm fairly certain this is a bug related to the memoization in simpleComparator. useMemo() is required by React Table, and it allows you to specify dependencies, which I'm guessing we need to do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct sorting is something that the selenium tests really need to check for |
||
|
||
const manufacturerComparator = React.useMemo(() => (rowA,rowB,columnId,desc) => { | ||
let cmp = compare(rowA.original.unit,rowB.original.unit); | ||
if (cmp == 0) { | ||
cmp = compare(rowA.original.manufacturer.toLowerCase(), rowB.original.manufacturer.toLowerCase()); | ||
} | ||
return cmp; | ||
}); | ||
Comment on lines
+31
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There shouldn't be any comparison of units. The simple lowerCase() comparator should work fine here |
||
|
||
const doseComparator = React.useMemo(() => (rowA,rowB,columnId,desc) => { | ||
let cmp = compare(rowA.original.unit,rowB.original.unit); | ||
if (cmp == 0) { | ||
cmp = compare(rowA.original.dose, rowB.original.dose); | ||
} | ||
return cmp; | ||
}); | ||
|
||
const columns = React.useMemo( | ||
() => { | ||
let cols = [ | ||
{ | ||
Header: 'Name', | ||
accessor: (row) => <a href={"drug/update/" + row.id}>{row.name}</a>, | ||
sortType: nameComparator, | ||
}, | ||
{ | ||
Header: 'Dose', | ||
accessor: (row) => `${row.dose} ${row.unit}`, | ||
sortType: doseComparator, | ||
}, | ||
{ | ||
Header: 'Category', | ||
accessor: (row) => <strong>{row.category}</strong>, | ||
sortType: categoryComparator, | ||
}, | ||
{ | ||
Header: 'Stock', | ||
accessor: (row) => <strong>{row.stock}</strong>, | ||
sortType: stockComparator, | ||
}, | ||
{ | ||
Header: 'Lot Number', | ||
accessor: 'lot_number', | ||
}, | ||
{ | ||
Header: 'Expiration Date', | ||
accessor: (row) => { | ||
const dt = DateTime.fromISO(row.expiration_date); | ||
const now = DateTime.now(); | ||
const options = { | ||
year: 'numeric', month: 'short', day: 'numeric' | ||
} | ||
|
||
if (dt <= now) { | ||
return <strong style={{color: "red"}}>{dt.toLocaleString(options)}</strong> | ||
} | ||
if (dt <= now.plus({ days: 60})) { | ||
return <strong style={{color: "gold"}}>{dt.toLocaleString(options)}</strong> | ||
} | ||
return dt.toLocaleString(options); | ||
}, | ||
sortType: expirationDateComparator, | ||
}, | ||
{ | ||
Header: 'Manufacturer', | ||
accessor: 'manufacturer', | ||
sortType: manufacturerComparator, | ||
}, | ||
{ | ||
Header: 'Dispense', | ||
accessor: (row) => { | ||
return ( | ||
<Button variant="success" onClick={e => handleShow(e, row)}> | ||
Dispense | ||
</Button> | ||
); | ||
}, | ||
disableSortBy: true, | ||
} | ||
] | ||
return cols; | ||
} | ||
, | ||
[] | ||
); | ||
|
||
const [loading, setLoading] = useState(true); | ||
const [drugs, setDrugs] = useState([]); | ||
const [patients, setPatients] = useState([]); | ||
|
||
useEffect(() => { | ||
const drugUrl = "/api/drugs"; | ||
const ptUrl = "/api/patients/?fields=name,age,gender,id&filter=active"; | ||
Promise.all([ | ||
axios.get(drugUrl), | ||
axios.get(ptUrl), | ||
]).then(function(res) { | ||
setDrugs(res[0].data); | ||
setPatients(res[1].data); | ||
setLoading(false); | ||
}); | ||
}, []); | ||
|
||
return ( | ||
<Container> | ||
{loading ? ( | ||
<span>Loading...</span> | ||
) : ( | ||
<> | ||
<TableManager | ||
columns={columns} | ||
data={drugs} | ||
globalFilter={globalFilter} | ||
id='drug-list-table' | ||
/> | ||
<Modal show={state.show} onHide={handleClose}> | ||
{state.drug && | ||
<Form action="/inventory/drug-dispense/" method="post"> | ||
<CSRFToken /> | ||
<Modal.Header closeButton> | ||
<Modal.Title>Dispense Drug</Modal.Title> | ||
</Modal.Header> | ||
<Modal.Body> | ||
<Form.Group> | ||
<Form.Label> | ||
How much <strong>{state.drug.dose} {state.drug.unit}</strong> would you like to dispense? (Current stock: <strong>{state.drug.stock}</strong>) | ||
</Form.Label> | ||
<Form.Control type="number" min={1} max={state.drug.stock} defaultValue={1} name="num" id="num"/> | ||
</Form.Group> | ||
<Form.Group> | ||
<Form.Control type="hidden" name="pk" id="pk" value={state.drug.id} /> | ||
</Form.Group> | ||
<Form.Group> | ||
<Form.Label> | ||
For which patient would you like to dispense <strong>{state.drug.name}</strong>? | ||
</Form.Label> | ||
<Form.Control as="select" name="patient_pk" id="patient_pk"> | ||
<option disabled value> -- select patient -- </option> | ||
{patients.map(pt => | ||
<option value={pt.id} key={pt.id}> | ||
{pt.name} {pt.age}/{pt.gender} | ||
</option> | ||
)} | ||
</Form.Control> | ||
</Form.Group> | ||
</Modal.Body> | ||
<Modal.Footer> | ||
<Button variant="secondary" onClick={handleClose}> | ||
Cancel | ||
</Button> | ||
<Button variant="primary" type="submit"> | ||
Dispense Drug | ||
</Button> | ||
</Modal.Footer> | ||
</Form> | ||
} | ||
</Modal> | ||
</> | ||
)} | ||
</Container> | ||
); | ||
} | ||
|
||
export default DrugListTable |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
function globalFilter(rows, columnIds, globalFilterValue) { | ||
const filterValue = globalFilterValue.toLowerCase(); | ||
return rows.filter((row) => { | ||
const name = row.original.name.toLowerCase(); | ||
const lot_number = row.original.lot_number.toLowerCase(); | ||
return (name.includes(filterValue) || lot_number.includes(filterValue)); | ||
}); | ||
} | ||
|
||
export default globalFilter; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import "regenerator-runtime/runtime"; | ||
|
||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import DrugListTable from './DrugListTable'; | ||
|
||
|
||
export function render(props) { | ||
ReactDOM.render( | ||
<DrugListTable {...props} />, | ||
document.getElementById('root') | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import React from 'react'; | ||
import getCookie from './getCookie'; | ||
|
||
const csrftoken = getCookie('csrftoken'); | ||
|
||
const CSRFToken = () => { | ||
return ( | ||
<input type="hidden" name="csrfmiddlewaretoken" value={csrftoken} /> | ||
); | ||
}; | ||
export default CSRFToken; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
function compare(a,b) { | ||
if (a < b) return -1; | ||
if (b > a) return 1; | ||
return 0; | ||
} | ||
|
||
export default compare; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
function getCookie(name) { | ||
let cookieValue = null; | ||
if (document.cookie && document.cookie !== '') { | ||
const cookies = document.cookie.split(';'); | ||
for (let i = 0; i < cookies.length; i++) { | ||
const cookie = cookies[i].trim(); | ||
// Does this cookie string begin with the name we want? | ||
if (cookie.substring(0, name.length + 1) === (name + '=')) { | ||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); | ||
break; | ||
} | ||
} | ||
} | ||
return cookieValue; | ||
} | ||
|
||
export default getCookie; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import compare from './compare'; | ||
import React from 'react'; | ||
|
||
const simpleComparator = (field) => React.useMemo( | ||
() => (rowA,rowB,columnId,desc) => compare(rowA.original[field],rowB.original[field])); | ||
|
||
export default simpleComparator; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove extra brackets