Improve UI and workflow test readability

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-09-08 15:59:35 +08:00
parent 3132ecd498
commit a7e35c165f
10 changed files with 99 additions and 0 deletions
@@ -397,6 +397,7 @@ class ConnectionController(QtCore.QObject):
)
self._finishConnecting()
self.notificationRequested.emit(
f'{configuration.coreName()}: ' + _('Connected')
)
@@ -419,6 +420,7 @@ class ConnectionController(QtCore.QObject):
return
self._startOperation = None
self._emitRuntimesChanged()
while not self._actionQueue.empty():
@@ -443,6 +445,7 @@ class ConnectionController(QtCore.QObject):
return
self._startOperation = None
self._emitRuntimesChanged()
configuration = self.activeProfile
@@ -461,6 +464,7 @@ class ConnectionController(QtCore.QObject):
return
self._startOperation = None
self._emitRuntimesChanged()
self._reset()
+1
View File
@@ -348,6 +348,7 @@ class _LatencyScheduler(QtCore.QObject):
_LatencyTestJob(ProfileTestTarget.capture(profile), options)
for profile in profiles
)
self.scheduleDrain()
def ensureTcpingEngine(self):
+3
View File
@@ -501,6 +501,7 @@ class SubscriptionManager(HttpGetManager):
)
self._preparationJobs[jobId] = job
self._preparationPool.start(job)
return jobId
@@ -681,6 +682,7 @@ class SubscriptionManager(HttpGetManager):
self.subscriptionCommitted.emit(context['unique'])
self._recordGroupSuccess(committed, result)
self.subscriptionStateChanged.emit((context['unique'],))
self._finishOperation(committed, successful=committed, structural=True)
@@ -696,6 +698,7 @@ class SubscriptionManager(HttpGetManager):
if self._isCurrentRequest(context):
self._recordGroupFailure(failed)
self.subscriptionStateChanged.emit((context.get('unique', ''),))
self._finishOperation(context, failed=failed)
+2
View File
@@ -1454,6 +1454,7 @@ class ServerTableView(
# Refresh index
"""Refresh all."""
self.sourceModel.refreshIndexes()
self.sourceModel.emitAllChanged()
def _profileIdsForSourceRows(self, rows) -> list[str]:
@@ -1998,6 +1999,7 @@ class ServerTableView(
)
mbox.setText(mbox.customText())
mbox.setColumnMinWidth()
mbox.open()
def appendNewItemByFactory(self, factory: CoreConfiguration | ServerProfile):
+10
View File
@@ -654,6 +654,7 @@ class HomePage(Mixins.QTranslatable, QMainWindow):
qrCodeWindowFactory=QRCodeWindow,
importActionsFactory=self.serverImportActions,
)
pluginRegistry = getPluginRegistry()
# These two settings dialogs intentionally remain parent-owned and are
@@ -693,6 +694,7 @@ class HomePage(Mixins.QTranslatable, QMainWindow):
if serverActions:
serverActions.append(AppQSeparator())
serverActions.append(
AppQAction(
_('New Empty Configuration'),
@@ -748,6 +750,7 @@ class HomePage(Mixins.QTranslatable, QMainWindow):
self.activateSelectedServerForConnection,
parent=self,
)
self.routingSelector = RoutingSelector(parent=self)
self.systemProxyComboBox = AppQComboBox(parent=self)
@@ -780,6 +783,7 @@ class HomePage(Mixins.QTranslatable, QMainWindow):
)
self.searchButton = SearchButton()
self.subscriptionFilterComboBox = AppQComboBox()
self.subscriptionFilterComboBox.setContentWidthAdjustable()
self.subscriptionFilterComboBox.setMinimumWidth(190)
@@ -817,12 +821,16 @@ class HomePage(Mixins.QTranslatable, QMainWindow):
self._layout.addLayout(self.headerLayout)
self._layout.addLayout(self.connectionLayout)
self._layout.addLayout(self.actionLayout)
self.emptyState = QWidget(parent=self)
emptyLayout = QHBoxLayout(self.emptyState)
emptyLayout.setContentsMargins(0, 0, 0, 0)
self.emptyStateLabel = AppQLabel(translatable=False, parent=self.emptyState)
self.emptyStateLabel.setWordWrap(True)
emptyLayout.addWidget(self.emptyStateLabel, 1)
self._layout.addWidget(self.emptyState)
self._layout.addWidget(self.userServersQTableWidget, 1)
@@ -837,6 +845,7 @@ class HomePage(Mixins.QTranslatable, QMainWindow):
model.layoutChanged,
):
connectWeakly(signal, self, 'refreshEmptyState', sender=model)
self.refreshEmptyState()
self.searchButton.clicked.connect(
@@ -888,6 +897,7 @@ class HomePage(Mixins.QTranslatable, QMainWindow):
"""Explain empty storage separately from an empty filtered view."""
table = self.userServersQTableWidget
empty = table.proxyModel.rowCount() == 0
self.emptyState.setVisible(empty)
if empty:
self.emptyStateLabel.setText(
+5
View File
@@ -216,6 +216,7 @@ class LogPage(Mixins.QTranslatable, QMainWindow):
QSizePolicy.Policy.Expanding,
QSizePolicy.Policy.Fixed,
)
self._searchRegex = None
self.textBrowser = DraculaTextBrowser(
@@ -292,10 +293,12 @@ class LogPage(Mixins.QTranslatable, QMainWindow):
self.emptyState = QWidget(parent=self)
emptyLayout = QHBoxLayout(self.emptyState)
emptyLayout.setContentsMargins(0, 0, 0, 0)
self.emptyStateLabel = AppQLabel(
_('No logs match the current filters.'), parent=self.emptyState
)
self.emptyStateLabel.setWordWrap(True)
emptyLayout.addWidget(self.emptyStateLabel, 1)
self.emptyState.hide()
@@ -711,6 +714,7 @@ class LogPage(Mixins.QTranslatable, QMainWindow):
or self._renderedCategoryId != selectedCategoryId
else self._entryCursor
)
batch = self.manager.entriesSince(cursor, selectedCategoryId)
if self._searchRegex is not None:
@@ -739,6 +743,7 @@ class LogPage(Mixins.QTranslatable, QMainWindow):
self._entryCursor = batch.cursor
self._renderedSequence = batch.cursor.sequence
self._entriesDirty = False
self.emptyState.setVisible(
not self._renderedEntries
and (self._searchRegex is not None or selectedCategoryId != ALL_LOGS_FILTER)
+20
View File
@@ -315,25 +315,32 @@ class ProfileTestServiceTest(unittest.TestCase):
"""Cancel active and queued work across all schedulers without shutting down."""
profiles = [self._profile(str(i), f'{i}.example') for i in range(3)]
profiles[0].metadata.latency = 'old latency'
manager = self._manager(profiles)
scheduler = manager._latencyScheduler
scheduler.threadPool = pool = _ControlledThreadPool()
scheduler.pingWorkerFactory = _ControlledLatencyWorker
manager.testPing(profiles)
manager.testDownloadSpeed(profiles, concurrent=False)
manager.testDownloadSpeed(profiles, concurrent=True)
processQtEvents()
workers = list(_ControlledDownloadWorker.instances)
workers[0].publish('partial speed')
manager.cancelAll()
manager.cancelAll()
pool.started[0].finish('late latency')
processQtEvents()
self.assertEqual(profiles[0].metadata.latency, 'old latency')
self.assertEqual(profiles[0].metadata.speed, 'partial speed')
self.assertTrue(all(worker.cancelCount == 1 for worker in workers))
self.assertFalse(scheduler.queue)
self.assertFalse(scheduler.activeJobs)
for downloads in (
manager._serialDownloadScheduler,
manager._concurrentDownloadScheduler,
@@ -341,10 +348,13 @@ class ProfileTestServiceTest(unittest.TestCase):
self.assertFalse(downloads.queue)
self.assertFalse(downloads.activeJobs)
self.assertFalse(downloads.activePorts)
manager.testPing(profiles[:1])
processQtEvents()
pool.started[-1].finish('new latency')
processQtEvents()
self.assertEqual(profiles[0].metadata.latency, 'new latency')
def testCancelAllDropsBufferedTcpingFanoutAndAcceptsNewGeneration(self):
@@ -354,22 +364,29 @@ class ProfileTestServiceTest(unittest.TestCase):
scheduler = manager._latencyScheduler
sink = QtCore.QObject()
scheduler.tcpingEngine = sink
try:
manager.testTcping(profiles)
oldRequest = next(iter(scheduler.tcpingRequests))
scheduler.handleTcpingResult(oldRequest, '5ms')
scheduler.drainTcpingResults()
manager.cancelAll()
processQtEvents()
self.assertEqual(sum(p.metadata.latency == '5ms' for p in profiles), 64)
self.assertFalse(scheduler.tcpingRequests)
self.assertFalse(scheduler.tcpingEndpointRequests)
self.assertFalse(scheduler.tcpingCompletionQueue)
manager.testTcping(profiles[-1:])
scheduler.handleTcpingResult(oldRequest, 'late')
newRequest = next(iter(scheduler.tcpingRequests))
scheduler.handleTcpingResult(newRequest, '9ms')
processQtEvents()
self.assertEqual(profiles[-1].metadata.latency, '9ms')
finally:
scheduler.tcpingEngine = None
@@ -379,7 +396,9 @@ class ProfileTestServiceTest(unittest.TestCase):
"""A retained shutdown manager cannot silently acquire new work."""
profile = self._profile('profile', 'example.test')
manager = self._manager((profile,))
manager.shutdown()
with mock.patch.object(
manager._latencyScheduler, 'enqueue'
) as latency, mock.patch.object(
@@ -388,6 +407,7 @@ class ProfileTestServiceTest(unittest.TestCase):
manager.testPing((profile,))
manager.testTcping((profile,))
manager.testDownloadSpeed((profile,))
latency.assert_not_called()
download.assert_not_called()
+22
View File
@@ -1008,12 +1008,14 @@ class SharedSettingsQtWorkflowTest(unittest.TestCase):
routing = _RoutingControllerFixture(
(RoutingOption('default', 'Default'),), 'default'
)
imported = []
action = AppQAction(
'Fixture import',
callback=lambda: imported.append(True),
translatable=False,
)
try:
with self._home(
settings, connection, routing, importActions=(action,)
@@ -1022,54 +1024,71 @@ class SharedSettingsQtWorkflowTest(unittest.TestCase):
home.show()
home.activateWindow()
processQtEvents()
self.assertTrue(home.emptyState.isVisible())
self.assertIn('No profiles yet', home.emptyStateLabel.text())
self.assertIs(home.importMenu.actions()[0], action)
self.assertIn(
action, home.userServersQTableWidget.contextMenu.actions()
)
QTest.mouseClick(home.importButton, QtCore.Qt.LeftButton)
processQtEvents()
QTest.keyClick(home.importMenu, QtCore.Qt.Key_Down)
QTest.keyClick(home.importMenu, QtCore.Qt.Key_Return)
processQtEvents()
self.assertEqual(imported, [True])
profile = ServerTableQtInteractionTest._profile('alpha')
home.userServersQTableWidget.appendNewItemByFactory(profile)
processQtEvents()
self.assertFalse(home.emptyState.isVisible())
home.searchLineEdit.setFocus()
QTest.keyClicks(home.searchLineEdit, 'missing')
QTest.keyClick(home.searchLineEdit, QtCore.Qt.Key_Return)
processQtEvents()
self.assertTrue(home.emptyState.isVisible())
QTest.mouseClick(
home.searchLineEdit.findChild(QToolButton), QtCore.Qt.LeftButton
)
processQtEvents()
self.assertFalse(home.emptyState.isVisible())
self.assertTrue(home.searchLineEdit.hasFocus())
self.assertEqual(home.searchLineEdit.text(), '')
self.assertIs(Storage.UserServers()[0], profile)
home.subscriptionFilterComboBox.addItem(
'Empty group', 'missing-group'
)
home.subscriptionFilterComboBox.setCurrentIndex(2)
processQtEvents()
self.assertTrue(home.emptyState.isVisible())
home.subscriptionFilterComboBox.setFocus()
QTest.keyClick(home.subscriptionFilterComboBox, QtCore.Qt.Key_Home)
processQtEvents()
self.assertEqual(home.subscriptionFilterComboBox.currentIndex(), 0)
self.assertEqual(
home.userServersQTableWidget.proxyModel.rowCount(), 1
)
for testAction in home.userServersQTableWidget.testActions:
self.assertIn(
testAction,
home.userServersQTableWidget.contextMenu.actions(),
)
manager = home.userServersQTableWidget.profileTestManager
with mock.patch.object(
manager._latencyScheduler, 'cancelAll'
) as cancel:
@@ -1080,11 +1099,14 @@ class SharedSettingsQtWorkflowTest(unittest.TestCase):
menu.setActiveAction(table.testActions[-1])
QTest.keyClick(menu, QtCore.Qt.Key_Return)
processQtEvents()
cancel.assert_called_once_with()
home.userServersQTableWidget.deleteItemByIndex(
[0], showTrayMessage=False, showProgress=False
)
processQtEvents()
self.assertTrue(home.emptyState.isVisible())
self.assertIn('No profiles yet', home.emptyStateLabel.text())
finally:
+14
View File
@@ -88,12 +88,14 @@ class StyleSheetStateRenderingTest(unittest.TestCase):
"""Keep embedded buttons centered, theme-correct and bounded after restyling."""
app = application()
originalStyleSheet = app.styleSheet()
edit = AppQLineEdit()
edit.setClearButtonEnabled(True)
edit.resize(340, 36)
edit.show()
edit.activateWindow()
edit.setFocus()
try:
for theme in (AppStyleSheet.Light, AppStyleSheet.Dark) * 3:
for direction in (QtCore.Qt.LeftToRight, QtCore.Qt.RightToLeft):
@@ -101,9 +103,12 @@ class StyleSheetStateRenderingTest(unittest.TestCase):
edit.setLayoutDirection(direction)
edit.setText('example search')
edit.setSelection(0, 7)
app.setStyleSheet(AppStyleSheet.forTheme(theme))
processQtEvents()
self.assertEqual(edit.selectedText(), 'example')
buttons = edit.findChildren(QToolButton)
self.assertEqual(len(buttons), 1)
button = buttons[0]
@@ -115,6 +120,7 @@ class StyleSheetStateRenderingTest(unittest.TestCase):
),
1,
)
self.assertFalse(button.icon().isNull())
pixmap = button.icon().pixmap(16, 16).toImage()
colors = [
@@ -123,6 +129,7 @@ class StyleSheetStateRenderingTest(unittest.TestCase):
for y in range(pixmap.height())
if pixmap.pixelColor(x, y).alpha() > 128
]
self.assertTrue(colors)
self.assertTrue(
all(
@@ -131,25 +138,32 @@ class StyleSheetStateRenderingTest(unittest.TestCase):
for color in colors
)
)
QTest.mouseClick(button, QtCore.Qt.LeftButton)
self.assertEqual(edit.text(), '')
self.assertTrue(edit.hasFocus())
self.assertTrue(waitFor(lambda: not button.isVisible()))
edit.setText('read only')
edit.setReadOnly(True)
app.setStyleSheet(AppStyleSheet.forTheme(AppStyleSheet.Light))
processQtEvents()
self.assertEqual(edit.text(), 'read only')
self.assertFalse(edit.findChild(QToolButton).isEnabled())
app.setStyleSheet(AppStyleSheet.forTheme(AppStyleSheet.Dark))
edit.setClearButtonEnabled(False)
processQtEvents()
self.assertFalse(edit.isClearButtonEnabled())
self.assertFalse(edit.findChildren(QToolButton))
finally:
edit.close()
edit.deleteLater()
processQtEvents()
app.setStyleSheet(originalStyleSheet)
def testFlatAndLinkButtonsRetainFocusedOutlineDuringHover(self):
+18
View File
@@ -1654,6 +1654,7 @@ class UnifiedLogPageTest(unittest.TestCase):
page.searchLineEdit.setText('[literal')
self.assertRendered(page)
self.assertEqual(page.plainText(), 'application beta [literal]')
self.assertTrue(page.searchLineEdit.toolTip())
@@ -1667,8 +1668,10 @@ class UnifiedLogPageTest(unittest.TestCase):
clearButton = page.searchLineEdit.findChild(QToolButton)
self.assertIsNotNone(clearButton)
QTest.mouseClick(clearButton, QtCore.Qt.MouseButton.LeftButton)
self.assertRendered(page)
self.assertEqual(page.searchLineEdit.text(), '')
self.assertFalse(page.searchLineEdit.toolTip())
self.assertEqual(
@@ -1692,24 +1695,30 @@ class UnifiedLogPageTest(unittest.TestCase):
manager = LogManager(maximumEntries=5)
page = LogPage(manager=manager)
self.addCleanup(self.disposePage, page)
page.show()
self.assertRendered(page)
self.assertFalse(page.emptyState.isVisible())
manager.append('application one', APPLICATION_LOG_CATEGORY)
self.assertRendered(page)
page.filterComboBox.setCurrentIndex(
page.filterComboBox.findData(CORE_LOG_CATEGORY)
)
self.assertRendered(page)
self.assertTrue(page.emptyState.isVisible())
manager.append('core one', CORE_LOG_CATEGORY)
self.assertRendered(page)
self.assertFalse(page.emptyState.isVisible())
page.searchLineEdit.setText('[missing')
self.assertRendered(page)
self.assertTrue(page.emptyState.isVisible())
self.assertEqual(page.plainText(), '')
self.assertTrue(page.searchLineEdit.toolTip())
@@ -1719,15 +1728,18 @@ class UnifiedLogPageTest(unittest.TestCase):
QtCore.Qt.MouseButton.LeftButton,
)
self.assertRendered(page)
self.assertFalse(page.emptyState.isVisible())
self.assertEqual(page.searchLineEdit.text(), '')
self.assertFalse(page.searchLineEdit.toolTip())
self.assertEqual(page.filterComboBox.currentData(), CORE_LOG_CATEGORY)
self.assertEqual(page.plainText(), 'core one')
self.assertTrue(page.searchLineEdit.hasFocus())
page.filterComboBox.setFocus()
QTest.keyClick(page.filterComboBox, QtCore.Qt.Key.Key_Home)
self.assertRendered(page)
self.assertEqual(page.filterComboBox.currentData(), ALL_LOGS_FILTER)
self.assertEqual(
AppSettings.get('LogViewerSelectedCategory'), ALL_LOGS_FILTER
@@ -1742,23 +1754,29 @@ class UnifiedLogPageTest(unittest.TestCase):
manager = LogManager(maximumEntries=2)
page = LogPage(manager=manager)
self.addCleanup(self.disposePage, page)
manager.append('match first')
page.searchLineEdit.setText('match')
page.show()
self.assertRendered(page)
self.assertFalse(page.emptyState.isVisible())
manager.append('other one')
manager.append('other two')
self.assertRendered(page)
self.assertTrue(page.emptyState.isVisible())
self.assertEqual(page.plainText(), '')
page.hide()
manager.append('match live')
processQtEvents()
page.show()
self.assertRendered(page)
self.assertFalse(page.emptyState.isVisible())
self.assertEqual(page.plainText(), 'match live')