Skip to content
This repository has been archived by the owner on Oct 29, 2020. It is now read-only.

Commit

Permalink
Merge pull request #128 from votingworks/fix/show-invalid-test-mode-s…
Browse files Browse the repository at this point in the history
…heet-correctly

fix: show invalid test mode pages correctly
  • Loading branch information
eventualbuddha committed Oct 13, 2020
2 parents 71b40a3 + acc52ff commit 33e1df9
Show file tree
Hide file tree
Showing 4 changed files with 365 additions and 54 deletions.
7 changes: 6 additions & 1 deletion src/AppRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,12 @@ const App: React.FC = () => {

if (election) {
if (adjudication.remaining > 0 && !isScanning) {
return <BallotEjectScreen continueScanning={continueScanning} />
return (
<BallotEjectScreen
continueScanning={continueScanning}
isTestMode={isTestMode}
/>
)
}

return (
Expand Down
110 changes: 107 additions & 3 deletions src/screens/BallotEjectScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ test('says the sheet is unreadable if it is', async () => {
const continueScanning = jest.fn()

const { container, getByText } = render(
<BallotEjectScreen continueScanning={continueScanning} />
<BallotEjectScreen continueScanning={continueScanning} isTestMode />
)

await act(async () => {
Expand Down Expand Up @@ -109,7 +109,7 @@ test('says the ballot sheet is overvoted if it is', async () => {
const continueScanning = jest.fn()

const { container, getByText } = render(
<BallotEjectScreen continueScanning={continueScanning} />
<BallotEjectScreen continueScanning={continueScanning} isTestMode />
)

await act(async () => {
Expand Down Expand Up @@ -188,7 +188,7 @@ test('says the ballot sheet is blank if it is', async () => {
const continueScanning = jest.fn()

const { container, getByText } = render(
<BallotEjectScreen continueScanning={continueScanning} />
<BallotEjectScreen continueScanning={continueScanning} isTestMode />
)

await act(async () => {
Expand All @@ -207,3 +207,107 @@ test('says the ballot sheet is blank if it is', async () => {
fireEvent.click(getByText('Tabulate Ballot and Continue Scanning'))
expect(continueScanning).toHaveBeenCalledWith(true)
})

test('calls out live ballot sheets in test mode', async () => {
const response: BallotSheetInfo = {
id: 'mock-sheet-id',
front: {
image: { url: '/front/url' },
interpretation: {
type: 'InvalidTestModePage',
metadata: {
ballotStyleId: '1',
precinctId: '1',
ballotType: BallotType.Standard,
electionHash: '',
isTestMode: false,
locales: { primary: 'en-US' },
pageNumber: 1,
},
},
},
back: {
image: { url: '/back/url' },
interpretation: {
type: 'InvalidTestModePage',
metadata: {
ballotStyleId: '1',
precinctId: '1',
ballotType: BallotType.Standard,
electionHash: '',
isTestMode: false,
locales: { primary: 'en-US' },
pageNumber: 2,
},
},
},
}
fetchMock.getOnce('/scan/hmpb/review/next-sheet', response)

const continueScanning = jest.fn()

const { container, getByText } = render(
<BallotEjectScreen continueScanning={continueScanning} isTestMode />
)

await act(async () => {
await waitFor(() => fetchMock.called)
})

expect(container).toMatchSnapshot()

fireEvent.click(getByText('Confirm Ballot Removed and Continue Scanning'))
expect(continueScanning).toHaveBeenCalledWith()
})

test('calls out test ballot sheets in live mode', async () => {
const response: BallotSheetInfo = {
id: 'mock-sheet-id',
front: {
image: { url: '/front/url' },
interpretation: {
type: 'InvalidTestModePage',
metadata: {
ballotStyleId: '1',
precinctId: '1',
ballotType: BallotType.Standard,
electionHash: '',
isTestMode: false,
locales: { primary: 'en-US' },
pageNumber: 1,
},
},
},
back: {
image: { url: '/back/url' },
interpretation: {
type: 'InvalidTestModePage',
metadata: {
ballotStyleId: '1',
precinctId: '1',
ballotType: BallotType.Standard,
electionHash: '',
isTestMode: false,
locales: { primary: 'en-US' },
pageNumber: 2,
},
},
},
}
fetchMock.getOnce('/scan/hmpb/review/next-sheet', response)

const continueScanning = jest.fn()

const { container, getByText } = render(
<BallotEjectScreen continueScanning={continueScanning} isTestMode={false} />
)

await act(async () => {
await waitFor(() => fetchMock.called)
})

expect(container).toMatchSnapshot()

fireEvent.click(getByText('Confirm Ballot Removed and Continue Scanning'))
expect(continueScanning).toHaveBeenCalledWith()
})
120 changes: 70 additions & 50 deletions src/screens/BallotEjectScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const RectoVerso = styled.div`

interface Props {
continueScanning: (override?: boolean) => void
isTestMode: boolean
}

type EjectState = undefined | 'removeBallot' | 'acceptBallot'
Expand All @@ -56,7 +57,7 @@ const doNothing = () => {
console.log('disabled') // eslint-disable-line no-console
}

const BallotEjectScreen = ({ continueScanning }: Props) => {
const BallotEjectScreen = ({ continueScanning, isTestMode }: Props) => {
const [sheetInfo, setSheetInfo] = useState<BallotSheetInfo | undefined>()

const [ballotState, setBallotState] = useState<EjectState>(undefined)
Expand All @@ -74,9 +75,12 @@ const BallotEjectScreen = ({ continueScanning }: Props) => {
let isOvervotedSheet = false
let isBlankSheet = false
let isUnreadableSheet = false
let isInvalidTestModeSheet = false

for (const { interpretation } of [sheetInfo.front, sheetInfo.back]) {
if (interpretation.type === 'InterpretedHmpbPage') {
if (interpretation.type === 'InvalidTestModePage') {
isInvalidTestModeSheet = true
} else if (interpretation.type === 'InterpretedHmpbPage') {
if (interpretation.adjudicationInfo.requiresAdjudication) {
for (const { type } of interpretation.adjudicationInfo.allReasonInfos) {
if (interpretation.adjudicationInfo.enabledReasons.includes(type)) {
Expand All @@ -96,7 +100,11 @@ const BallotEjectScreen = ({ continueScanning }: Props) => {
return (
<Screen>
<MainNav>
{ballotState === 'removeBallot' ? (
{isInvalidTestModeSheet ? (
<Button primary onPress={() => continueScanning()}>
Confirm Ballot Removed and Continue Scanning
</Button>
) : ballotState === 'removeBallot' ? (
<Button primary onPress={() => continueScanning()}>
Confirm Ballot Removed and Continue Scanning
</Button>
Expand All @@ -115,7 +123,11 @@ const BallotEjectScreen = ({ continueScanning }: Props) => {
<Columns>
<Prose maxWidth={false}>
<EjectReason>
{isUnreadableSheet
{isInvalidTestModeSheet
? isTestMode
? 'Live Ballot'
: 'Test Ballot'
: isUnreadableSheet
? 'Unreadable'
: isOvervotedSheet
? 'Overvote'
Expand All @@ -126,52 +138,60 @@ const BallotEjectScreen = ({ continueScanning }: Props) => {
<p>
This last scanned sheet <strong>was not tabulated</strong>.
</p>
<h4>Original Ballot Scan</h4>
<p>
Remove ballot and create a duplicate ballot for the Resolution
Board to review.
<br />
<Button
primary={ballotState === 'removeBallot'}
onPress={() => setBallotState('removeBallot')}
>
Original Ballot Removed
</Button>
</p>
<h4>Duplicate Ballot Scan</h4>
<p>
{isUnreadableSheet ? (
<React.Fragment>
Confirm sheet was reviewed by the Resolution Board and
tabulate as <strong>unreadable</strong>.
</React.Fragment>
) : isOvervotedSheet ? (
<React.Fragment>
Confirm ballot sheet was reviewed by the Resolution Board
and tabulate as ballot sheet with an{' '}
<strong>overvote</strong>.
</React.Fragment>
) : isBlankSheet ? (
<React.Fragment>
Confirm ballot sheet was reviewed by the Resolution Board
and tabulate as a <strong>blank</strong> ballot sheet and
has no votes.
</React.Fragment>
) : (
<React.Fragment>
Confirm ballot sheet was reviewed by the Resolution Board
and tabulate as ballow with issue which could not be
determined.
</React.Fragment>
)}
<br />
<Button
primary={ballotState === 'acceptBallot'}
onPress={() => setBallotState('acceptBallot')}
>
Tabulate Duplicate Ballot
</Button>
</p>
{!isInvalidTestModeSheet ? (
<React.Fragment>
<h4>Original Ballot Scan</h4>
<p>
Remove ballot and create a duplicate ballot for the
Resolution Board to review.
<br />
<Button
primary={ballotState === 'removeBallot'}
onPress={() => setBallotState('removeBallot')}
>
Original Ballot Removed
</Button>
</p>
<h4>Duplicate Ballot Scan</h4>
<p>
{isUnreadableSheet ? (
<React.Fragment>
Confirm sheet was reviewed by the Resolution Board and
tabulate as <strong>unreadable</strong>.
</React.Fragment>
) : isOvervotedSheet ? (
<React.Fragment>
Confirm ballot sheet was reviewed by the Resolution
Board and tabulate as ballot sheet with an{' '}
<strong>overvote</strong>.
</React.Fragment>
) : isBlankSheet ? (
<React.Fragment>
Confirm ballot sheet was reviewed by the Resolution
Board and tabulate as a <strong>blank</strong> ballot
sheet and has no votes.
</React.Fragment>
) : (
<React.Fragment>
Confirm ballot sheet was reviewed by the Resolution
Board and tabulate as ballow with issue which could not
be determined.
</React.Fragment>
)}
<br />
<Button
primary={ballotState === 'acceptBallot'}
onPress={() => setBallotState('acceptBallot')}
>
Tabulate Duplicate Ballot
</Button>
</p>
</React.Fragment>
) : isTestMode ? (
<p>Remove the LIVE ballot before continuing.</p>
) : (
<p>Remove the TEST ballot before continuing.</p>
)}
</Prose>
<RectoVerso>
<div>
Expand Down
Loading

0 comments on commit 33e1df9

Please sign in to comment.