Files
omarchy/test/shell.d/fixtures/network-owe-transition/shell.qml
T
David Heinemeier HanssonandClaude Opus 5.5 18328559b9 Keep the Wi-Fi icon steady on OWE transition-mode networks (#14133)
* Keep the Wi-Fi icon steady on OWE transition-mode networks

An OWE transition-mode network pairs an open SSID with a hidden "_owetm_"
twin on the same BSSID. Between scans NetworkManager reports the in-use
access point under the hidden SSID and drops the active profile from the
device's AvailableConnections, which is the only place Quickshell builds
known networks from. No listed network is then connected, so the bar fell
back to the disconnected icon until the next scan brought the open SSID
back, flickering on and off while the link stayed up.

Fall back to the Wi-Fi device's own connected state when no listed network
is connected, and read the in-use access point's strength from nmcli
(without a rescan) while the connected network has none of its own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Discard stale in-use AP reads and key Wi-Fi checks on the device

Bump a generation whenever the cached in-use access point strength stops
describing the link (leaving Wi-Fi or a device change), and drop any nmcli
read started before it, re-reading immediately instead of a full interval
later.

Key Wi-Fi connectivity checks on the device rather than the SSID, so the
listed network coming and going with each scan on an OWE transition-mode
network no longer schedules a check. A real network switch still passes
through "disconnected".

Add a QML fixture that runs the panel against a mocked device through the
scan churn, a mid-read disconnect, and a final disconnect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 20:05:32 -04:00

118 lines
4.2 KiB
QML

import QtQuick
import Quickshell
import qs.Commons
import "mocks"
import "network" as Network
import "network/Model.js" as Model
// Drives the bar state through an OWE transition-mode scan cycle: the
// connected network drops out of the device's list between scans while the
// device itself stays connected. The runner stubs nmcli to answer, in order,
// 57, then a slow stale 11, then 33.
ShellRoot {
id: test
property bool failed: false
property int checksBefore: 0
property var listed: ({ values: [NetworkMock.network] })
function check(ok, message) {
if (!ok) {
failed = true
console.log("RESULT fail " + message)
}
}
Item {
Network.Panel {
id: panel
bar: QtObject {
property color foreground: Color.foreground
property color barForeground: Color.foreground
property color urgent: Color.urgent
property string fontFamily: Style.font.family
property string position: "top"
property int barSize: 24
property bool vertical: false
property bool foregroundAnimationEnabled: false
property var activePopout: null
function requestPopout(owner) { activePopout = owner }
function releasePopout(owner) { activePopout = null }
function registerClickTarget(target) {}
function unregisterClickTarget(target) {}
function hideTooltip(target) {}
function showTooltip(target, text) {}
}
}
}
function unlist() { NetworkMock.wifi.networks = { values: [] } }
function relist() { NetworkMock.wifi.networks = test.listed }
Timer {
interval: 250
running: true
onTriggered: {
test.check(panel.kind === "wifi" && panel.signalStrength === 80, "listed network supplies its own strength")
test.check(!panel.testApPoll.running, "a network with its own strength adds no polling")
test.check(panel.connectionKey === "wifi:test-wifi", "Wi-Fi connection key follows the device")
test.checksBefore = NetworkMock.checks
test.unlist()
Qt.callLater(test.unlistedChecks)
}
}
function unlistedChecks() {
check(panel.kind === "wifi", "connected device keeps Wi-Fi when no network is listed as connected")
check(panel.icon !== "󰤮", "unlisted network does not flicker to the disconnected icon")
check(panel.testApPoll.running, "missing strength starts the in-use access point poll")
check(panel.connectionKey === "wifi:test-wifi", "connection key survives the listed network disappearing")
firstReading.start()
}
Timer {
id: firstReading
interval: 600
onTriggered: {
test.check(panel.activeApSignal === 57 && panel.signalStrength === 57, "in-use access point strength fills the gap")
test.check(panel.icon === Model.wifiIconFor(57), "bar icon follows the in-use access point strength")
test.relist()
Qt.callLater(test.relistedChecks)
}
}
function relistedChecks() {
check(panel.signalStrength === 80 && !panel.testApPoll.running, "a relisted network takes over and stops the poll")
check(NetworkMock.checks === checksBefore, "scan churn schedules no extra connectivity checks")
// Second nmcli read is slow; drop the link while it is in flight.
unlist()
Qt.callLater(dropWhileReading)
}
function dropWhileReading() {
NetworkMock.wifi.connected = false
Qt.callLater(function() {
check(panel.kind === "disconnected" && panel.icon === "󰤮", "a real disconnect still shows disconnected")
check(panel.activeApSignal === -1 && !panel.testApPoll.running, "disconnect clears the cached strength and stops polling")
NetworkMock.wifi.connected = true
staleReading.start()
})
}
Timer {
id: staleReading
interval: 1500
onTriggered: {
test.check(panel.activeApSignal === 33, "a read from before the reset is discarded and re-read (got " + panel.activeApSignal + ")")
NetworkMock.wifi.connected = false
Qt.callLater(test.finalChecks)
}
}
function finalChecks() {
check(panel.activeApSignal === -1 && !panel.testApPoll.running, "final disconnect clears the reading")
if (!failed) console.log("RESULT pass")
done.start()
}
Timer { id: done; interval: 200; onTriggered: Qt.quit() }
}