'
+ 'QR-Codes and visible web addresses'
+ ' '
+ 'Never open web addresses or scan QR codes contained in printed documents without '
+ 'taking security precautions. If you are unsure how to manage this risk, please '
+ 'contact your administrator.'
+ '
'
+ 'Printer dots'
+ ' '
+ 'Any part of a printed page may contain identifying information '
+ 'invisible to the naked eye, such as printer dots. Please carefully '
+ 'consider this risk when working with or publishing scanned printouts.')
+ self.insert_usb_message = _('Please connect your printer to a USB port.')
+ self.generic_error_message = _('See your administrator for help.')
+
+ self._show_starting_instructions()
+ self._run_preflight()
+
+ def _show_starting_instructions(self):
+ self.header.setText(self.starting_header)
+ self.body.setText(self.starting_message)
+ self.error_details.hide()
+ self.adjustSize()
+ self.center_dialog()
+
+ def _show_insert_usb_message(self):
+ self.continue_button.clicked.disconnect()
+ self.continue_button.clicked.connect(self._run_preflight)
+ self.header.setText(self.insert_usb_header)
+ self.body.setText(self.insert_usb_message)
+ self.error_details.hide()
+ self.adjustSize()
+ self.center_dialog()
+
+ def _show_generic_error_message(self):
+ self.continue_button.clicked.disconnect()
+ self.continue_button.clicked.connect(self.close)
+ self.continue_button.setText('DONE')
+ self.header.setText(self.error_header)
+ self.body.setText('{}: {}'.format(self.error_status, self.generic_error_message))
+ self.error_details.hide()
+ self.adjustSize()
+ self.center_dialog()
@pyqtSlot()
- def _on_retry_button_clicked(self):
- self.print()
+ def _run_preflight(self):
+ self.controller.run_printer_preflight_checks()
@pyqtSlot()
- def _on_print_success(self):
+ def _print_file(self):
+ self.controller.print_file(self.file_uuid)
self.close()
- @pyqtSlot(object)
- def _on_print_failure(self, error: ExportError):
- self._update(error.status)
-
- def _update(self, status):
- logger.debug('updating status... ')
- if status == ExportStatus.PRINTER_NOT_FOUND.value:
- self._request_to_insert_usb_device()
- else:
- self.error_status_code.setText(_(status))
- self.starting_message.hide()
- self.printing_message.hide()
- self.generic_error.show()
- self.insert_usb_form.hide()
+ @pyqtSlot()
+ def _on_preflight_success(self):
+ # If the continue button is disabled then this is the result of a background preflight check
+ if not self.continue_button.isEnabled():
+ self.continue_button.clicked.disconnect()
+ self.continue_button.clicked.connect(self._print_file)
+ self.continue_button.setEnabled(True)
+ return
- def _request_to_insert_usb_device(self):
- self.starting_message.hide()
- self.printing_message.hide()
- self.generic_error.hide()
- self.insert_usb_form.show()
+ self._print_file()
+ @pyqtSlot(object)
+ def _on_preflight_failure(self, error: ExportError):
+ self.error_status = error.status
+ # If the continue button is disabled then this is the result of a background preflight check
+ if not self.continue_button.isEnabled():
+ self.continue_button.clicked.disconnect()
+ if error.status == ExportStatus.PRINTER_NOT_FOUND.value:
+ self.continue_button.clicked.connect(self._show_insert_usb_message)
+ else:
+ self.continue_button.clicked.connect(self._show_generic_error_message)
-class ExportDialog(QDialog):
+ self.continue_button.setEnabled(True)
+ else:
+ if error.status == ExportStatus.PRINTER_NOT_FOUND.value:
+ self._show_insert_usb_message()
+ else:
+ self._show_generic_error_message()
- CSS = '''
- #export_dialog {
- min-width: 830;
- min-height: 330;
- border: 1px solid #2a319d;
- }
- '''
- CSS_FOR_DIALOG_WITH_ERROR = '''
- #export_dialog {
- min-width: 830;
- min-height: 430;
- border: 1px solid #2a319d;
- }
- '''
+class ExportDialog(FramelessDialog):
- CSS = '''
- #export_dialog {
- min-width: 400;
- max-width: 400;
- min-height: 200;
- max-height: 200;
- }
- #passphrase_label {
+ PASSPHRASE_FORM_CSS = '''
+ #passphrase_form QLabel {
font-family: 'Montserrat';
font-weight: 500;
- font-size: 13px;
+ font-size: 12px;
+ color: #2a319d;
+ padding-top: 6px;
}
#passphrase_form QLineEdit {
border-radius: 0px;
min-height: 30px;
- margin: 0px 0px 10px 0px;
+ max-height: 30px;
+ background-color: #f8f8f8;
+ padding-bottom: 2px;
}
'''
- def __init__(self, controller, file_uuid, file_name):
+ PASSPHRASE_LABEL_SPACING = 0.5
+ NO_MARGIN = 0
+
+ def __init__(self, controller: Controller, file_uuid: str, file_name: str):
super().__init__()
self.controller = controller
self.file_uuid = file_uuid
- self.file_name = file_name
-
- self.setObjectName('export_dialog')
- self.setStyleSheet(self.CSS)
- self.setWindowFlags(Qt.Popup)
- self.setWindowModality(Qt.WindowModal)
-
- layout = QVBoxLayout(self)
- self.setLayout(layout)
-
- # Starting export message
- self.starting_export_message = SecureQLabel(_(
- 'Preparing to export:\n' + self.file_name))
- self.starting_export_message.setWordWrap(True)
-
- # Widget to show error messages that occur during an export
- self.generic_error = QWidget()
- self.generic_error.setObjectName('gener_error')
- generic_error_layout = QHBoxLayout()
- self.generic_error.setLayout(generic_error_layout)
- self.error_status_code = SecureQLabel()
- generic_error_message = SecureQLabel(_('See your administrator for help.'))
- generic_error_message.setWordWrap(True)
- generic_error_layout.addWidget(self.error_status_code)
- generic_error_layout.addWidget(generic_error_message)
-
- # Insert USB Device Form
- self.insert_usb_form = QWidget()
- self.insert_usb_form.setObjectName('insert_usb_form')
- usb_form_layout = QVBoxLayout()
- self.insert_usb_form.setLayout(usb_form_layout)
- self.usb_error_message = SecureQLabel(_(
- 'Either the drive is not LUKS-encrypted, or there is something '
- 'else wrong with it.'))
- self.usb_error_message.setWordWrap(True)
- usb_instructions = SecureQLabel(_(
+ self.file_name = SecureQLabel(file_name).text()
+ self.error_status = '' # Hold onto the error status we receive from the Export VM
+
+ # Connect controller signals to slots
+ self.controller.export.preflight_check_call_success.connect(self._on_preflight_success)
+ self.controller.export.preflight_check_call_failure.connect(self._on_preflight_failure)
+ self.controller.export.export_usb_call_success.connect(self._on_export_success)
+ self.controller.export.export_usb_call_failure.connect(self._on_export_failure)
+
+ # Connect parent signals to slots
+ self.continue_button.setEnabled(False)
+
+ self.header_icon.update_image('savetodisk.svg', QSize(64, 64))
+
+ # Dialog content
+ self.starting_header = _(
+ 'Preparing to export:'
+ ' '
+ '{}'.format(self.file_name))
+ self.insert_usb_header = _('Insert encrypted USB drive')
+ self.passphrase_header = _('Enter passphrase for USB drive')
+ self.success_header = _('Export successful')
+ self.error_header = _('Unable to export')
+ self.starting_message = _(
+ '
Proceed with caution when exporting files
'
+ 'Malware'
+ ' '
+ 'This workstation lets you open documents securely. If you open documents on another '
+ 'computer, any embedded malware may spread to your computer or network. If you are '
+ 'unsure how to manage this risk, please print the document, or contact your '
+ 'administrator.'
+ '
' \
+ 'Malware' \
+ ' ' \
+ 'This workstation lets you open documents securely. If you open documents on another ' \
+ 'computer, any embedded malware may spread to your computer or network. If you are ' \
+ 'unsure how to manage this risk, please print the document, or contact your ' \
+ 'administrator.' \
+ '
' \
+ 'Anonymity' \
+ ' ' \
+ 'Documents submitted by sources may contain information or hidden metadata that ' \
+ 'identifies who they are. To protect your sources, please consider redacting documents ' \
+ 'before working with them on network-connected computers.'
+ assert not dialog.header.isHidden()
+ assert not dialog.header_line.isHidden()
+ assert dialog.error_details.isHidden()
+ assert not dialog.body.isHidden()
+ assert dialog.passphrase_form.isHidden()
+ assert not dialog.continue_button.isHidden()
+ assert not dialog.cancel_button.isHidden()
+
+
+def test_ExportDialog___show_passphrase_request_message(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
- assert not export_dialog.passphrase_form.isHidden()
- assert export_dialog.insert_usb_form.isHidden()
- assert export_dialog.passphrase_error_message.isHidden()
- assert not export_dialog.passphrase_instructions.isHidden()
+ dialog._show_passphrase_request_message()
+ assert dialog.header.text() == 'Enter passphrase for USB drive'
+ assert not dialog.header.isHidden()
+ assert dialog.header_line.isHidden()
+ assert dialog.error_details.isHidden()
+ assert dialog.body.isHidden()
+ assert not dialog.passphrase_form.isHidden()
+ assert not dialog.continue_button.isHidden()
+ assert not dialog.cancel_button.isHidden()
-def test_ExportDialog__request_passphrase_more_than_once(mocker):
- """Ensure that the correct widgets are visible or hidden."""
- export_dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
- export_dialog._request_passphrase(bad_passphrase=True)
+def test_ExportDialog__show_passphrase_request_message_again(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
- assert not export_dialog.passphrase_form.isHidden()
- assert export_dialog.insert_usb_form.isHidden()
- assert not export_dialog.passphrase_error_message.isHidden()
- assert export_dialog.passphrase_instructions.isHidden()
+ dialog._show_passphrase_request_message_again()
+ assert dialog.header.text() == 'Enter passphrase for USB drive'
+ assert dialog.error_details.text() == 'The passphrase provided did not work. Please try again.'
+ assert dialog.body.isHidden()
+ assert not dialog.header.isHidden()
+ assert dialog.header_line.isHidden()
+ assert not dialog.error_details.isHidden()
+ assert dialog.body.isHidden()
+ assert not dialog.passphrase_form.isHidden()
+ assert not dialog.continue_button.isHidden()
+ assert not dialog.cancel_button.isHidden()
-def test_ExportDialog__on_export_success_closes_window(mocker):
- """
- Ensure successful export results in the export dialog window closing.
- """
- controller = mocker.MagicMock()
- export_dialog = ExportDialog(controller, 'mock_uuid', 'mock.jpg')
- export_dialog.close = mocker.MagicMock()
- export_dialog._on_export_success()
+def test_ExportDialog__show_success_message(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
- export_dialog.close.assert_called_once_with()
+ dialog._show_success_message()
+ assert dialog.header.text() == 'Export successful'
+ assert dialog.body.text() == \
+ 'Remember to be careful when working with files outside of your Workstation machine.'
+ assert not dialog.header.isHidden()
+ assert not dialog.header_line.isHidden()
+ assert dialog.error_details.isHidden()
+ assert not dialog.body.isHidden()
+ assert dialog.passphrase_form.isHidden()
+ assert not dialog.continue_button.isHidden()
+ assert dialog.cancel_button.isHidden()
-def test_ExportDialog__on_unlock_disk_clicked(mocker):
- """
- Ensure export of file begins once the passphrase is retrieved from the uesr.
- """
+
+def test_ExportDialog__show_insert_usb_message(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+
+ dialog._show_insert_usb_message()
+
+ assert dialog.header.text() == 'Insert encrypted USB drive'
+ assert dialog.body.text() == \
+ 'Please insert one of the export drives provisioned specifically ' \
+ 'for the SecureDrop Workstation.'
+ assert not dialog.header.isHidden()
+ assert not dialog.header_line.isHidden()
+ assert dialog.error_details.isHidden()
+ assert not dialog.body.isHidden()
+ assert dialog.passphrase_form.isHidden()
+ assert not dialog.continue_button.isHidden()
+ assert not dialog.cancel_button.isHidden()
+
+
+def test_ExportDialog__show_insert_encrypted_usb_message(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+
+ dialog._show_insert_encrypted_usb_message()
+
+ assert dialog.header.text() == 'Insert encrypted USB drive'
+ assert dialog.error_details.text() == \
+ 'Either the drive is not encrypted or there is something else wrong with it.'
+ assert dialog.body.text() == \
+ 'Please insert one of the export drives provisioned specifically for the SecureDrop ' \
+ 'Workstation.'
+ assert not dialog.header.isHidden()
+ assert not dialog.header_line.isHidden()
+ assert not dialog.error_details.isHidden()
+ assert not dialog.body.isHidden()
+ assert dialog.passphrase_form.isHidden()
+ assert not dialog.continue_button.isHidden()
+ assert not dialog.cancel_button.isHidden()
+
+
+def test_ExportDialog__show_generic_error_message(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ dialog.error_status = 'mock_error_status'
+
+ dialog._show_generic_error_message()
+
+ assert dialog.header.text() == 'Unable to export'
+ assert dialog.body.text() == 'mock_error_status: See your administrator for help.'
+ assert not dialog.header.isHidden()
+ assert not dialog.header_line.isHidden()
+ assert dialog.error_details.isHidden()
+ assert not dialog.body.isHidden()
+ assert dialog.passphrase_form.isHidden()
+ assert not dialog.continue_button.isHidden()
+ assert not dialog.cancel_button.isHidden()
+
+
+def test_ExportDialog__export_file(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
controller = mocker.MagicMock()
controller.export_file_to_usb_drive = mocker.MagicMock()
- export_dialog = ExportDialog(controller, 'mock_uuid', 'mock.jpg')
- export_dialog._update = mocker.MagicMock()
- export_dialog.passphrase_field.text = mocker.MagicMock(return_value='mock_passphrase')
+ dialog = ExportDialog(controller, 'mock_uuid', 'mock.jpg')
+ dialog.passphrase_field.text = mocker.MagicMock(return_value='mock_passphrase')
- export_dialog._on_unlock_disk_clicked()
+ dialog._export_file()
controller.export_file_to_usb_drive.assert_called_once_with('mock_uuid', 'mock_passphrase')
- export_dialog._update.assert_not_called()
-def test_ExportDialog__on_unlock_disk_clicked_asks_for_passphrase_again_on_error(mocker):
- """
- Ensure user is asked for passphrase when there is a bad passphrase error.
- """
- controller = mocker.MagicMock()
- bad_password_export_error = ExportError(ExportStatus.BAD_PASSPHRASE.value)
- export_dialog = ExportDialog(controller, 'mock_uuid', 'mock.jpg')
- export_dialog._request_passphrase = mocker.MagicMock()
- export_dialog.passphrase_field.text = mocker.MagicMock(return_value='mock_passphrase')
+def test_ExportDialog__on_preflight_success(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ dialog._show_passphrase_request_message = mocker.MagicMock()
+ dialog.continue_button = mocker.MagicMock()
+ dialog.continue_button.clicked = mocker.MagicMock()
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=False)
- export_dialog._on_export_usb_call_failure(bad_password_export_error)
+ dialog._on_preflight_success()
- export_dialog._request_passphrase.assert_called_with(True)
+ dialog._show_passphrase_request_message.assert_not_called()
+ dialog.continue_button.clicked.connect.assert_called_once_with(
+ dialog._show_passphrase_request_message)
-def test_ExportDialog__update_preflight_non_called_process_error(mocker):
- """
- Ensure generic errors are passed through to _update
- """
- export_dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
- export_dialog.generic_error = mocker.MagicMock()
- error = ExportError('generic error')
- export_dialog._on_preflight_check_call_failure(error)
- export_dialog.generic_error.show.assert_called_once_with()
+def test_ExportDialog__on_preflight_success_when_continue_enabled(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ dialog._show_passphrase_request_message = mocker.MagicMock()
+ dialog.continue_button.setEnabled(True)
+ dialog._on_preflight_success()
-def test_ExportDialog__update_after_USB_NOT_CONNECTED(mocker):
- """
- Ensure USB_NOT_CONNECTED results in asking the user connect their USB device.
- """
- export_dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
- export_dialog._request_to_insert_usb_device = mocker.MagicMock()
- export_dialog._update(ExportStatus.USB_NOT_CONNECTED.value)
- export_dialog._request_to_insert_usb_device.assert_called_once_with()
+ dialog._show_passphrase_request_message.assert_called_once_with()
-def test_ExportDialog__update_after_DISK_ENCRYPTION_NOT_SUPPORTED_ERROR(mocker):
- """
- Ensure USB_NOT_CONNECTED results in asking the user connect their USB device.
- """
- export_dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
- export_dialog._request_to_insert_usb_device = mocker.MagicMock()
- export_dialog._update(ExportStatus.DISK_ENCRYPTION_NOT_SUPPORTED_ERROR.value)
- export_dialog._request_to_insert_usb_device.assert_called_once_with(True)
+def test_ExportDialog__on_preflight_success_enabled_after_preflight_success(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ assert not dialog.continue_button.isEnabled()
+ dialog._on_preflight_success()
+ assert dialog.continue_button.isEnabled()
-def test_ExportDialog__update_after_CALLED_PROCESS_ERROR(mocker):
- """
- Ensure CALLED_PROCESS_ERROR shows generic 'contact admin' error with correct
- error status code.
- """
- export_dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
- export_dialog._request_to_insert_usb_device = mocker.MagicMock()
- export_dialog._request_passphrase = mocker.MagicMock()
- error = ExportError(ExportStatus.CALLED_PROCESS_ERROR.value)
+def test_ExportDialog__on_preflight_success_enabled_after_preflight_failure(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ assert not dialog.continue_button.isEnabled()
+ dialog._on_preflight_failure(mocker.MagicMock())
+ assert dialog.continue_button.isEnabled()
- export_dialog._on_export_usb_call_failure(error)
- assert export_dialog.starting_export_message.isHidden()
- assert export_dialog.passphrase_form.isHidden()
- assert export_dialog.insert_usb_form.isHidden()
- assert not export_dialog.generic_error.isHidden()
- assert export_dialog.error_status_code.text() == 'CALLED_PROCESS_ERROR'
- export_dialog._request_to_insert_usb_device.assert_not_called()
- export_dialog._request_passphrase.assert_not_called()
+def test_ExportDialog__on_preflight_failure(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ dialog._update_dialog = mocker.MagicMock()
+ error = ExportError('mock_error_status')
+ dialog._on_preflight_failure(error)
-def test_PrintDialog__on_retry_button_clicked(mocker):
- """
- Ensure happy path prints the file.
- """
- controller = mocker.MagicMock()
- dialog = PrintDialog(controller, 'mock_uuid')
+ dialog._update_dialog.assert_called_with('mock_error_status')
- dialog._on_retry_button_clicked()
- controller.print_file.assert_called_with('mock_uuid')
+def test_ExportDialog__on_export_success(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ dialog._show_success_message = mocker.MagicMock()
+ dialog._on_export_success()
+
+ dialog._show_success_message.assert_called_once_with()
-def test_PrintDialog__update_print_button_clicked_PRINTER_NOT_FOUND(mocker):
- """
- Ensure request to insert USB device on PRINTER_NOT_FOUND.
- """
- controller = mocker.MagicMock()
- error = ExportError(ExportStatus.PRINTER_NOT_FOUND.value)
- dialog = PrintDialog(controller, 'mock_uuid')
- dialog._request_to_insert_usb_device = mocker.MagicMock()
- dialog._update(error.status)
+def test_ExportDialog__on_export_failure(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ dialog._update_dialog = mocker.MagicMock()
- dialog._request_to_insert_usb_device.assert_called_once_with()
+ error = ExportError('mock_error_status')
+ dialog._on_export_failure(error)
+ dialog._update_dialog.assert_called_with('mock_error_status')
-def test_PrintDialog__request_to_insert_usb_device(mocker):
- """Ensure that the correct widgets are visible or hidden."""
- dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid')
- dialog._request_to_insert_usb_device()
+def test_ExportDialog__update_dialog_when_status_is_USB_NOT_CONNECTED(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock_filename')
+ dialog._show_insert_usb_message = mocker.MagicMock()
+ dialog.continue_button = mocker.MagicMock()
+ dialog.continue_button.clicked = mocker.MagicMock()
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=False)
- assert dialog.starting_message.isHidden()
- assert dialog.printing_message.isHidden()
- assert dialog.generic_error.isHidden()
- assert not dialog.insert_usb_form.isHidden()
- assert not dialog.usb_error_message.isHidden()
+ # When the continue button is enabled, ensure clicking continue will show next instructions
+ dialog._update_dialog(ExportStatus.USB_NOT_CONNECTED.value)
+ dialog.continue_button.clicked.connect.assert_called_once_with(dialog._show_insert_usb_message)
+ # When the continue button is enabled, ensure next instructions are shown
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=True)
+ dialog._update_dialog(ExportStatus.USB_NOT_CONNECTED.value)
+ dialog._show_insert_usb_message.assert_called_once_with()
-def test_PrintDialog__on_print_success_closes_window(mocker):
- """
- Ensure successful print results in the print dialog window closing.
- """
- controller = mocker.MagicMock()
- dialog = PrintDialog(controller, 'mock_uuid')
+
+def test_ExportDialog__update_dialog_when_status_is_BAD_PASSPHRASE(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock_filename')
+ dialog._show_passphrase_request_message_again = mocker.MagicMock()
+ dialog.continue_button = mocker.MagicMock()
+ dialog.continue_button.clicked = mocker.MagicMock()
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=False)
+
+ # When the continue button is enabled, ensure clicking continue will show next instructions
+ dialog._update_dialog(ExportStatus.BAD_PASSPHRASE.value)
+ dialog.continue_button.clicked.connect.assert_called_once_with(
+ dialog._show_passphrase_request_message_again)
+
+ # When the continue button is enabled, ensure next instructions are shown
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=True)
+ dialog._update_dialog(ExportStatus.BAD_PASSPHRASE.value)
+ dialog._show_passphrase_request_message_again.assert_called_once_with()
+
+
+def test_ExportDialog__update_dialog_when_status_DISK_ENCRYPTION_NOT_SUPPORTED_ERROR(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock_filename')
+ dialog._show_insert_encrypted_usb_message = mocker.MagicMock()
+ dialog.continue_button = mocker.MagicMock()
+ dialog.continue_button.clicked = mocker.MagicMock()
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=False)
+
+ # When the continue button is enabled, ensure clicking continue will show next instructions
+ dialog._update_dialog(ExportStatus.DISK_ENCRYPTION_NOT_SUPPORTED_ERROR.value)
+ dialog.continue_button.clicked.connect.assert_called_once_with(
+ dialog._show_insert_encrypted_usb_message)
+
+ # When the continue button is enabled, ensure next instructions are shown
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=True)
+ dialog._update_dialog(ExportStatus.DISK_ENCRYPTION_NOT_SUPPORTED_ERROR.value)
+ dialog._show_insert_encrypted_usb_message.assert_called_once_with()
+
+
+def test_ExportDialog__update_dialog_when_status_is_CALLED_PROCESS_ERROR(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock_filename')
+ dialog._show_generic_error_message = mocker.MagicMock()
+ dialog.continue_button = mocker.MagicMock()
+ dialog.continue_button.clicked = mocker.MagicMock()
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=False)
+
+ # When the continue button is enabled, ensure clicking continue will show next instructions
+ dialog._update_dialog(ExportStatus.CALLED_PROCESS_ERROR.value)
+ dialog.continue_button.clicked.connect.assert_called_once_with(
+ dialog._show_generic_error_message)
+ assert dialog.error_status == ExportStatus.CALLED_PROCESS_ERROR.value
+
+ # When the continue button is enabled, ensure next instructions are shown
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=True)
+ dialog._update_dialog(ExportStatus.CALLED_PROCESS_ERROR.value)
+ dialog._show_generic_error_message.assert_called_once_with()
+ assert dialog.error_status == ExportStatus.CALLED_PROCESS_ERROR.value
+
+
+def test_ExportDialog__update_dialog_when_status_is_unknown(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = ExportDialog(mocker.MagicMock(), 'mock_uuid', 'mock_filename')
+ dialog._show_generic_error_message = mocker.MagicMock()
+ dialog.continue_button = mocker.MagicMock()
+ dialog.continue_button.clicked = mocker.MagicMock()
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=False)
+
+ # When the continue button is enabled, ensure clicking continue will show next instructions
+ dialog._update_dialog('Some Unknown Error Status')
+ dialog.continue_button.clicked.connect.assert_called_once_with(
+ dialog._show_generic_error_message)
+ assert dialog.error_status == 'Some Unknown Error Status'
+
+ # When the continue button is enabled, ensure next instructions are shown
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=True)
+ dialog._update_dialog('Some Unknown Error Status')
+ dialog._show_generic_error_message.assert_called_once_with()
+ assert dialog.error_status == 'Some Unknown Error Status'
+
+
+def test_PrintDialog_init(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ _show_starting_instructions_fn = mocker.patch(
+ 'securedrop_client.gui.widgets.PrintDialog._show_starting_instructions')
+
+ PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+
+ _show_starting_instructions_fn.assert_called_once_with()
+
+
+def test_PrintDialog_init_sanitizes_filename(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ secure_qlabel = mocker.patch('securedrop_client.gui.widgets.SecureQLabel')
+ filename = ''
+
+ PrintDialog(mocker.MagicMock(), 'mock_uuid', filename)
+
+ secure_qlabel.assert_called_with(filename)
+
+
+def test_PrintDialog__show_starting_instructions(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+
+ dialog._show_starting_instructions()
+
+ assert dialog.header.text() == \
+ 'Preparing to print:' \
+ ' ' \
+ 'mock.jpg'
+ assert dialog.body.text() == \
+ '
Managing printout risks
' \
+ 'QR-Codes and visible web addresses' \
+ ' ' \
+ 'Never open web addresses or scan QR codes contained in printed documents without ' \
+ 'taking security precautions. If you are unsure how to manage this risk, please ' \
+ 'contact your administrator.' \
+ '
' \
+ 'Printer dots' \
+ ' ' \
+ 'Any part of a printed page may contain identifying information ' \
+ 'invisible to the naked eye, such as printer dots. Please carefully ' \
+ 'consider this risk when working with or publishing scanned printouts.'
+ assert not dialog.header.isHidden()
+ assert not dialog.header_line.isHidden()
+ assert dialog.error_details.isHidden()
+ assert not dialog.body.isHidden()
+ assert not dialog.continue_button.isHidden()
+ assert not dialog.cancel_button.isHidden()
+
+
+def test_PrintDialog__show_insert_usb_message(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock_filename')
+
+ dialog._show_insert_usb_message()
+
+ assert dialog.header.text() == 'Insert USB printer'
+ assert dialog.body.text() == 'Please connect your printer to a USB port.'
+ assert not dialog.header.isHidden()
+ assert not dialog.header_line.isHidden()
+ assert dialog.error_details.isHidden()
+ assert not dialog.body.isHidden()
+ assert not dialog.continue_button.isHidden()
+ assert not dialog.cancel_button.isHidden()
+
+
+def test_PrintDialog__show_generic_error_message(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ dialog.error_status = 'mock_error_status'
+
+ dialog._show_generic_error_message()
+
+ assert dialog.header.text() == 'Unable to print'
+ assert dialog.body.text() == 'mock_error_status: See your administrator for help.'
+ assert not dialog.header.isHidden()
+ assert not dialog.header_line.isHidden()
+ assert dialog.error_details.isHidden()
+ assert not dialog.body.isHidden()
+ assert not dialog.continue_button.isHidden()
+ assert not dialog.cancel_button.isHidden()
+
+
+def test_PrintDialog__print_file(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock_filename')
dialog.close = mocker.MagicMock()
- dialog._on_print_success()
+ dialog._print_file()
dialog.close.assert_called_once_with()
-def test_PrintDialog__on_print_call_failure_generic_error(mocker):
- """
- Ensure generic errors are passed through to _update
- """
- dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid')
- dialog.generic_error = mocker.MagicMock()
- error = ExportError('generic error')
+def test_PrintDialog__on_preflight_success(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ dialog._print_file = mocker.MagicMock()
+ dialog.continue_button = mocker.MagicMock()
+ dialog.continue_button.clicked = mocker.MagicMock()
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=False)
- dialog._on_print_failure(error)
+ dialog._on_preflight_success()
- dialog.generic_error.show.assert_called_once_with()
+ dialog._print_file.assert_not_called()
+ dialog.continue_button.clicked.connect.assert_called_once_with(dialog._print_file)
-def test_PrintDialog__update_after_PRINTER_NOT_FOUND(mocker):
- """
- Ensure PRINTER_NOT_FOUND results in asking the user connect their USB device.
- """
- dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid')
- dialog._request_to_insert_usb_device = mocker.MagicMock()
- dialog._update(ExportStatus.PRINTER_NOT_FOUND.value)
- dialog._request_to_insert_usb_device.assert_called_once_with()
+def test_PrintDialog__on_preflight_success_when_continue_enabled(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ dialog._print_file = mocker.MagicMock()
+ dialog.continue_button.setEnabled(True)
+ dialog._on_preflight_success()
-def test_PrintDialog__update_after_MISSING_PRINTER_URI(mocker):
- """
- Ensure MISSING_PRINTER_URI shows generic 'contact admin' error with correct
- error status code.
- """
- dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid')
- dialog._request_to_insert_usb_device = mocker.MagicMock()
- dialog._update(ExportStatus.MISSING_PRINTER_URI.value)
- dialog._request_to_insert_usb_device = mocker.MagicMock()
+ dialog._print_file.assert_called_once_with()
- error = ExportError(ExportStatus.MISSING_PRINTER_URI.value)
- dialog._on_print_failure(error)
- assert dialog.error_status_code.text() == 'ERROR_MISSING_PRINTER_URI'
- assert dialog.starting_message.isHidden()
- assert dialog.printing_message.isHidden()
- assert not dialog.generic_error.isHidden()
- assert dialog.insert_usb_form.isHidden()
- dialog._request_to_insert_usb_device.assert_not_called()
+def test_PrintDialog__on_preflight_success_enabled_after_preflight_success(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ assert not dialog.continue_button.isEnabled()
+ dialog._on_preflight_success()
+ assert dialog.continue_button.isEnabled()
-def test_PrintDialog__update_after_CALLED_PROCESS_ERROR(mocker):
- """
- Ensure CALLED_PROCESS_ERROR shows generic 'contact admin' error with correct
- error status code.
- """
- dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid')
- dialog._request_to_insert_usb_device = mocker.MagicMock()
- dialog._update(ExportStatus.MISSING_PRINTER_URI.value)
- dialog._request_to_insert_usb_device = mocker.MagicMock()
+def test_PrintDialog__on_preflight_success_enabled_after_preflight_failure(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock.jpg')
+ assert not dialog.continue_button.isEnabled()
+ dialog._on_preflight_failure(mocker.MagicMock())
+ assert dialog.continue_button.isEnabled()
- error = ExportError(ExportStatus.CALLED_PROCESS_ERROR.value)
- dialog._on_print_failure(error)
- assert dialog.error_status_code.text() == 'CALLED_PROCESS_ERROR'
- assert dialog.starting_message.isHidden()
- assert dialog.insert_usb_form.isHidden()
- assert not dialog.generic_error.isHidden()
- dialog._request_to_insert_usb_device.assert_not_called()
+def test_PrintDialog__on_preflight_failure_when_status_is_PRINTER_NOT_FOUND(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock_filename')
+ dialog._show_insert_usb_message = mocker.MagicMock()
+ dialog.continue_button = mocker.MagicMock()
+ dialog.continue_button.clicked = mocker.MagicMock()
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=False)
+
+ # When the continue button is enabled, ensure clicking continue will show next instructions
+ dialog._on_preflight_failure(ExportError(ExportStatus.PRINTER_NOT_FOUND.value))
+ dialog.continue_button.clicked.connect.assert_called_once_with(dialog._show_insert_usb_message)
+
+ # When the continue button is enabled, ensure next instructions are shown
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=True)
+ dialog._on_preflight_failure(ExportError(ExportStatus.PRINTER_NOT_FOUND.value))
+ dialog._show_insert_usb_message.assert_called_once_with()
+
+
+def test_PrintDialog__on_preflight_failure_when_status_is_MISSING_PRINTER_URI(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock_filename')
+ dialog._show_generic_error_message = mocker.MagicMock()
+ dialog.continue_button = mocker.MagicMock()
+ dialog.continue_button.clicked = mocker.MagicMock()
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=False)
+
+ # When the continue button is enabled, ensure clicking continue will show next instructions
+ dialog._on_preflight_failure(ExportError(ExportStatus.MISSING_PRINTER_URI.value))
+ dialog.continue_button.clicked.connect.assert_called_once_with(
+ dialog._show_generic_error_message)
+ assert dialog.error_status == ExportStatus.MISSING_PRINTER_URI.value
+
+ # When the continue button is enabled, ensure next instructions are shown
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=True)
+ dialog._on_preflight_failure(ExportError(ExportStatus.MISSING_PRINTER_URI.value))
+ dialog._show_generic_error_message.assert_called_once_with()
+ assert dialog.error_status == ExportStatus.MISSING_PRINTER_URI.value
+
+
+def test_PrintDialog__on_preflight_failure_when_status_is_CALLED_PROCESS_ERROR(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock_filename')
+ dialog._show_generic_error_message = mocker.MagicMock()
+ dialog.continue_button = mocker.MagicMock()
+ dialog.continue_button.clicked = mocker.MagicMock()
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=False)
+
+ # When the continue button is enabled, ensure clicking continue will show next instructions
+ dialog._on_preflight_failure(ExportError(ExportStatus.CALLED_PROCESS_ERROR.value))
+ dialog.continue_button.clicked.connect.assert_called_once_with(
+ dialog._show_generic_error_message)
+ assert dialog.error_status == ExportStatus.CALLED_PROCESS_ERROR.value
+
+ # When the continue button is enabled, ensure next instructions are shown
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=True)
+ dialog._on_preflight_failure(ExportError(ExportStatus.CALLED_PROCESS_ERROR.value))
+ dialog._show_generic_error_message.assert_called_once_with()
+ assert dialog.error_status == ExportStatus.CALLED_PROCESS_ERROR.value
+
+
+def test_PrintDialog__on_preflight_failure_when_status_is_unknown(mocker):
+ mocker.patch(
+ 'securedrop_client.gui.widgets.QApplication.activeWindow', return_value=QMainWindow())
+ dialog = PrintDialog(mocker.MagicMock(), 'mock_uuid', 'mock_filename')
+ dialog._show_generic_error_message = mocker.MagicMock()
+ dialog.continue_button = mocker.MagicMock()
+ dialog.continue_button.clicked = mocker.MagicMock()
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=False)
+
+ # When the continue button is enabled, ensure clicking continue will show next instructions
+ dialog._on_preflight_failure(ExportError('Some Unknown Error Status'))
+ dialog.continue_button.clicked.connect.assert_called_once_with(
+ dialog._show_generic_error_message)
+ assert dialog.error_status == 'Some Unknown Error Status'
+
+ # When the continue button is enabled, ensure next instructions are shown
+ mocker.patch.object(dialog.continue_button, 'isEnabled', return_value=True)
+ dialog._on_preflight_failure(ExportError('Some Unknown Error Status'))
+ dialog._show_generic_error_message.assert_called_once_with()
+ assert dialog.error_status == 'Some Unknown Error Status'
def test_ConversationView_init(mocker, homedir):
diff --git a/tests/test_export.py b/tests/test_export.py
index fd9267cae..24351ee7a 100644
--- a/tests/test_export.py
+++ b/tests/test_export.py
@@ -6,6 +6,74 @@
from securedrop_client.export import Export, ExportError
+def test_run_printer_preflight(mocker):
+ '''
+ Ensure TemporaryDirectory is used when creating and sending the archives during the preflight
+ checks and that the success signal is emitted by Export.
+ '''
+ mock_temp_dir = mocker.MagicMock()
+ mock_temp_dir.__enter__ = mocker.MagicMock(return_value='mock_temp_dir')
+ mocker.patch('securedrop_client.export.TemporaryDirectory', return_value=mock_temp_dir)
+ export = Export()
+ export.printer_preflight_success = mocker.MagicMock()
+ export.printer_preflight_success.emit = mocker.MagicMock()
+ _run_printer_preflight = mocker.patch.object(export, '_run_printer_preflight')
+
+ export.run_printer_preflight()
+
+ _run_printer_preflight.assert_called_once_with('mock_temp_dir')
+ export.printer_preflight_success.emit.assert_called_once_with()
+
+
+def test_run_printer_preflight_error(mocker):
+ '''
+ Ensure TemporaryDirectory is used when creating and sending the archives during the preflight
+ checks and that the failure signal is emitted by Export.
+ '''
+ mock_temp_dir = mocker.MagicMock()
+ mock_temp_dir.__enter__ = mocker.MagicMock(return_value='mock_temp_dir')
+ mocker.patch('securedrop_client.export.TemporaryDirectory', return_value=mock_temp_dir)
+ export = Export()
+ export.printer_preflight_failure = mocker.MagicMock()
+ export.printer_preflight_failure.emit = mocker.MagicMock()
+ error = ExportError('bang!')
+ _run_print_preflight = mocker.patch.object(export, '_run_printer_preflight', side_effect=error)
+
+ export.run_printer_preflight()
+
+ _run_print_preflight.assert_called_once_with('mock_temp_dir')
+ export.printer_preflight_failure.emit.assert_called_once_with(error)
+
+
+def test__run_printer_preflight(mocker):
+ '''
+ Ensure _export_archive and _create_archive are called with the expected parameters,
+ _export_archive is called with the return value of _create_archive, and
+ _run_disk_test returns without error if 'USB_CONNECTED' is the return value of _export_archive.
+ '''
+ export = Export()
+ export._create_archive = mocker.MagicMock(return_value='mock_archive_path')
+ export._export_archive = mocker.MagicMock(return_value='')
+
+ export._run_printer_preflight('mock_archive_dir')
+
+ export._export_archive.assert_called_once_with('mock_archive_path')
+ export._create_archive.assert_called_once_with(
+ 'mock_archive_dir', 'printer-preflight.sd-export', {'device': 'printer-preflight'})
+
+
+def test__run_printer_preflight_raises_ExportError_if_not_empty_string(mocker):
+ '''
+ Ensure ExportError is raised if _run_disk_test returns anything other than 'USB_CONNECTED'.
+ '''
+ export = Export()
+ export._create_archive = mocker.MagicMock(return_value='mock_archive_path')
+ export._export_archive = mocker.MagicMock(return_value='SOMETHING_OTHER_THAN_EMPTY_STRING')
+
+ with pytest.raises(ExportError):
+ export._run_printer_preflight('mock_archive_dir')
+
+
def test_print(mocker):
'''
Ensure TemporaryDirectory is used when creating and sending the archive containing the file to
@@ -151,7 +219,7 @@ def test_run_preflight_checks(mocker):
_run_usb_export.assert_called_once_with('mock_temp_dir')
_run_disk_export.assert_called_once_with('mock_temp_dir')
- export.preflight_check_call_success.emit.assert_called_once_with('success')
+ export.preflight_check_call_success.emit.assert_called_once_with()
def test_run_preflight_checks_error(mocker):
diff --git a/tests/test_logic.py b/tests/test_logic.py
index 438a82e06..68bdcdb8f 100644
--- a/tests/test_logic.py
+++ b/tests/test_logic.py
@@ -1516,6 +1516,32 @@ def test_Controller_call_update_star_success(homedir, config, mocker, session_ma
co.on_update_star_failure, type=Qt.QueuedConnection)
+def test_Controller_run_printer_preflight_checks(homedir, mocker, session, source):
+ co = Controller('http://localhost', mocker.MagicMock(), mocker.MagicMock(), homedir)
+ co.export = mocker.MagicMock()
+ co.export.begin_printer_preflight = mocker.MagicMock()
+ co.export.begin_printer_preflight.emit = mocker.MagicMock()
+
+ co.run_printer_preflight_checks()
+
+ co.export.begin_printer_preflight.emit.call_count == 1
+
+
+def test_Controller_run_printer_preflight_checks_not_qubes(homedir, mocker, session, source):
+ co = Controller('http://localhost', mocker.MagicMock(), mocker.MagicMock(), homedir)
+ co.qubes = False
+ co.export = mocker.MagicMock()
+ co.export.begin_printer_preflight = mocker.MagicMock()
+ co.export.begin_printer_preflight.emit = mocker.MagicMock()
+ co.export.printer_preflight_success = mocker.MagicMock()
+ co.export.printer_preflight_success.emit = mocker.MagicMock()
+
+ co.run_printer_preflight_checks()
+
+ co.export.begin_printer_preflight.emit.call_count == 0
+ co.export.printer_preflight_success.emit.call_count == 1
+
+
def test_Controller_run_print_file(mocker, session, homedir):
co = Controller('http://localhost', mocker.MagicMock(), mocker.MagicMock(), homedir)
co.export = mocker.MagicMock()