diff --git a/apps/scan/frontend/src/screens/poll_worker_screen.test.tsx b/apps/scan/frontend/src/screens/poll_worker_screen.test.tsx
index eecd9a3edd..a6cd86a339 100644
--- a/apps/scan/frontend/src/screens/poll_worker_screen.test.tsx
+++ b/apps/scan/frontend/src/screens/poll_worker_screen.test.tsx
@@ -386,7 +386,7 @@ describe('must have printer attached to transition polls and print reports', ()
});
describe('must have usb drive attached to transition polls', () => {
- test('polls open', async () => {
+ test('opening polls', async () => {
apiMock.expectGetPollsInfo('polls_closed_initial');
apiMock.setPrinterStatusV3({ connected: true });
apiMock.expectGetUsbDriveStatus('no_drive');
@@ -405,7 +405,7 @@ describe('must have usb drive attached to transition polls', () => {
await screen.findByText('Print Additional Polls Opened Report');
});
- test('polls open from fallback screen', async () => {
+ test('opening polls from fallback screen', async () => {
apiMock.expectGetPollsInfo('polls_closed_initial');
apiMock.setPrinterStatusV3({ connected: true });
apiMock.expectGetUsbDriveStatus('no_drive');
@@ -431,7 +431,7 @@ describe('must have usb drive attached to transition polls', () => {
await screen.findByText('Print Additional Polls Opened Report');
});
- test('polls paused', async () => {
+ test('resuming voting', async () => {
apiMock.expectGetPollsInfo('polls_paused');
apiMock.setPrinterStatusV3({ connected: true });
apiMock.expectGetUsbDriveStatus('no_drive');
@@ -451,7 +451,7 @@ describe('must have usb drive attached to transition polls', () => {
await screen.findByText('Voting Resumed');
});
- test('polls paused from fallback screen', async () => {
+ test('resuming voting from fallback screen', async () => {
apiMock.expectGetPollsInfo('polls_paused');
apiMock.setPrinterStatusV3({ connected: true });
apiMock.expectGetUsbDriveStatus('no_drive');
@@ -476,7 +476,7 @@ describe('must have usb drive attached to transition polls', () => {
await screen.findByText('Print Additional Polls Closed Report');
});
- test('polls close', async () => {
+ test('closing polls', async () => {
apiMock.expectGetPollsInfo('polls_open');
apiMock.setPrinterStatusV3({ connected: true });
apiMock.expectGetUsbDriveStatus('no_drive');
@@ -497,7 +497,7 @@ describe('must have usb drive attached to transition polls', () => {
expect(startNewVoterSessionMock).toHaveBeenCalledTimes(1);
});
- test('polls close from fallback screen', async () => {
+ test('closing polls from fallback screen', async () => {
apiMock.expectGetPollsInfo('polls_open');
apiMock.setPrinterStatusV3({ connected: true });
apiMock.expectGetUsbDriveStatus('no_drive');
@@ -524,6 +524,172 @@ describe('must have usb drive attached to transition polls', () => {
});
});
+describe('does not need usb drive attached to transition polls if continuous export disabled', () => {
+ beforeEach(() => {
+ apiMock.mockApiClient.getConfig.reset();
+ apiMock.expectGetConfig({
+ isContinuousExportEnabled: false,
+ });
+ });
+
+ test('opening polls', async () => {
+ apiMock.expectGetPollsInfo('polls_closed_initial');
+ apiMock.setPrinterStatusV4();
+ apiMock.expectGetUsbDriveStatus('no_drive');
+ renderScreen({});
+
+ await screen.findButton('Open Polls');
+ expect(
+ screen.queryByText('Insert a USB drive to continue.')
+ ).not.toBeInTheDocument();
+
+ apiMock.expectOpenPolls();
+ apiMock.expectPrintReportV4().resolve();
+ apiMock.expectGetPollsInfo('polls_open');
+ userEvent.click(screen.getButton('Open Polls'));
+ await screen.findByText('Opening Polls…');
+ await screen.findByText('Polls Opened');
+ });
+
+ test('opening polls from fallback screen', async () => {
+ apiMock.expectGetPollsInfo('polls_closed_initial');
+ apiMock.setPrinterStatusV4();
+ apiMock.expectGetUsbDriveStatus('no_drive');
+ renderScreen({});
+
+ await screen.findButton('Open Polls');
+ expect(
+ screen.queryByText('Insert a USB drive to continue.')
+ ).not.toBeInTheDocument();
+
+ userEvent.click(screen.getButton('Menu'));
+ apiMock.expectOpenPolls();
+ apiMock.expectPrintReportV4().resolve();
+ apiMock.expectGetPollsInfo('polls_open');
+ userEvent.click(screen.getButton('Open Polls'));
+ await screen.findByText('Opening Polls…');
+ await screen.findByText('Polls Opened');
+ });
+
+ test('pausing voting', async () => {
+ apiMock.expectGetPollsInfo('polls_open');
+ apiMock.setPrinterStatusV4();
+ apiMock.expectGetUsbDriveStatus('no_drive');
+ renderScreen({});
+
+ await screen.findButton('Close Polls');
+ expect(
+ screen.queryByText('Insert a USB drive to continue.')
+ ).not.toBeInTheDocument();
+
+ userEvent.click(screen.getButton('Menu'));
+ apiMock.expectPauseVoting();
+ apiMock.expectPrintReportV4().resolve();
+ apiMock.expectGetPollsInfo('polls_paused');
+ userEvent.click(screen.getButton('Pause Voting'));
+ await screen.findByText('Pausing Voting…');
+ await screen.findByText('Voting Paused');
+ });
+
+ test('resuming voting', async () => {
+ apiMock.expectGetPollsInfo('polls_paused');
+ apiMock.setPrinterStatusV4();
+ apiMock.expectGetUsbDriveStatus('no_drive');
+ renderScreen({});
+
+ await screen.findButton('Resume Voting');
+ expect(
+ screen.queryByText('Insert a USB drive to continue.')
+ ).not.toBeInTheDocument();
+
+ apiMock.expectResumeVoting();
+ apiMock.expectPrintReportV4().resolve();
+ apiMock.expectGetPollsInfo('polls_open');
+ userEvent.click(screen.getButton('Resume Voting'));
+ await screen.findByText('Resuming Voting…');
+ await screen.findByText('Voting Resumed');
+ });
+
+ test('resuming voting from fallback screen', async () => {
+ apiMock.expectGetPollsInfo('polls_paused');
+ apiMock.setPrinterStatusV4();
+ apiMock.expectGetUsbDriveStatus('no_drive');
+ renderScreen({});
+
+ await screen.findButton('Resume Voting');
+ expect(
+ screen.queryByText('Insert a USB drive to continue.')
+ ).not.toBeInTheDocument();
+
+ userEvent.click(screen.getButton('Menu'));
+ apiMock.expectResumeVoting();
+ apiMock.expectPrintReportV4().resolve();
+ apiMock.expectGetPollsInfo('polls_open');
+ userEvent.click(screen.getButton('Resume Voting'));
+ await screen.findByText('Resuming Voting…');
+ await screen.findByText('Voting Resumed');
+ });
+
+ test('closing polls', async () => {
+ apiMock.expectGetPollsInfo('polls_open');
+ apiMock.setPrinterStatusV4();
+ apiMock.expectGetUsbDriveStatus('no_drive');
+ renderScreen({});
+
+ await screen.findButton('Close Polls');
+ expect(
+ screen.queryByText('Insert a USB drive to continue.')
+ ).not.toBeInTheDocument();
+
+ apiMock.expectClosePolls();
+ apiMock.expectPrintReportV4().resolve();
+ apiMock.expectGetPollsInfo('polls_closed_final');
+ userEvent.click(screen.getButton('Close Polls'));
+ await screen.findByText('Closing Polls…');
+ await screen.findByText('Polls Closed');
+ });
+
+ test('closing polls from fallback screen', async () => {
+ apiMock.expectGetPollsInfo('polls_open');
+ apiMock.setPrinterStatusV4();
+ apiMock.expectGetUsbDriveStatus('no_drive');
+ renderScreen({});
+
+ await screen.findButton('Close Polls');
+ expect(
+ screen.queryByText('Insert a USB drive to continue.')
+ ).not.toBeInTheDocument();
+
+ userEvent.click(screen.getButton('Menu'));
+ apiMock.expectClosePolls();
+ apiMock.expectPrintReportV4().resolve();
+ apiMock.expectGetPollsInfo('polls_closed_final');
+ userEvent.click(screen.getButton('Close Polls'));
+ await screen.findByText('Closing Polls…');
+ await screen.findByText('Polls Closed');
+ });
+
+ test('closing polls from voting paused', async () => {
+ apiMock.expectGetPollsInfo('polls_paused');
+ apiMock.setPrinterStatusV4();
+ apiMock.expectGetUsbDriveStatus('no_drive');
+ renderScreen({});
+
+ await screen.findButton('Resume Voting');
+ expect(
+ screen.queryByText('Insert a USB drive to continue.')
+ ).not.toBeInTheDocument();
+
+ userEvent.click(screen.getButton('Menu'));
+ apiMock.expectClosePolls();
+ apiMock.expectPrintReportV4().resolve();
+ apiMock.expectGetPollsInfo('polls_closed_final');
+ userEvent.click(screen.getButton('Close Polls'));
+ await screen.findByText('Closing Polls…');
+ await screen.findByText('Polls Closed');
+ });
+});
+
describe('hardware V4 report printing', () => {
test('single report printing happy path', async () => {
apiMock.setPrinterStatusV4();
diff --git a/apps/scan/frontend/src/screens/poll_worker_screen.tsx b/apps/scan/frontend/src/screens/poll_worker_screen.tsx
index 7a17748b90..bc73a83e31 100644
--- a/apps/scan/frontend/src/screens/poll_worker_screen.tsx
+++ b/apps/scan/frontend/src/screens/poll_worker_screen.tsx
@@ -23,7 +23,6 @@ import type {
PrecinctScannerPollsInfo,
PrintResult,
} from '@votingworks/scan-backend';
-import type { UsbDriveStatus } from '@votingworks/usb-drive';
import {
getUsbDriveStatus,
printReport,
@@ -34,6 +33,7 @@ import {
resumeVoting as resumeVotingApi,
getPollsInfo,
useApiClient,
+ getConfig,
} from '../api';
import { FullScreenPromptLayout } from '../components/full_screen_prompt_layout';
import {
@@ -106,11 +106,11 @@ function PrinterAlertText({
}
function UsbDriveAlertText({
- usbDriveStatus,
+ mustInsertUsbDriveToContinue,
}: {
- usbDriveStatus: UsbDriveStatus;
+ mustInsertUsbDriveToContinue: boolean;
}): JSX.Element | null {
- if (usbDriveStatus.status === 'mounted') {
+ if (!mustInsertUsbDriveToContinue) {
return null;
}
@@ -123,21 +123,21 @@ function UsbDriveAlertText({
function shouldAllowTogglingPolls(
printerSummary: PollsFlowPrinterSummary,
- usbDriveStatus: UsbDriveStatus
+ mustInsertUsbDriveToContinue: boolean
): boolean {
- return printerSummary.ready && usbDriveStatus.status === 'mounted';
+ return printerSummary.ready && !mustInsertUsbDriveToContinue;
}
function OpenPollsPromptScreen({
onConfirm,
onClose,
printerSummary,
- usbDriveStatus,
+ mustInsertUsbDriveToContinue,
}: {
onConfirm: () => void;
onClose: () => void;
printerSummary: PollsFlowPrinterSummary;
- usbDriveStatus: UsbDriveStatus;
+ mustInsertUsbDriveToContinue: boolean;
}): JSX.Element {
return (