* Add Wi-Fi QR sharing to network panel * Refine Wi-Fi QR sharing * Use QR glyph for Wi-Fi sharing * Add click-to-reveal password to the Wi-Fi share card Scanning the QR is the fast path, but the person typing on a laptop needs the actual password. A dimmed "Show password" hint under the QR toggles the secret in place. The password stays out of the shell until asked for: a click runs the new omarchy-network-password helper (a private pipe, never an argument), and closing the card drops it again. Open and enterprise networks never show the control. The card loses its Close button -- Escape and clicking outside already cover it -- and now sizes itself to its content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Keep a dismissed Wi-Fi share card closed and make Escape reliable Closing the card mid-generation killed the helper, but its buffered stdout still arrived and repopulated the matrix, reopening the card the user just closed. Both collectors now honor qrExpectedStop, and the flag survives onExited because exit and stream-finished have no guaranteed order. The password fetch gets the same treatment so a reveal in flight during dismissal can't stash the secret into a closed card's state. The content's focus was claimed while the window was still unmapped, so Escape could land nowhere. Re-acquire it after mapping, the way KeyboardPanel does. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Share WEP networks correctly and restore the QR quiet zone NetworkManager models WEP as key-mgmt "none" plus a wep-key, so the QR helper encoded WEP networks as open -- a QR that scans fine and then silently fails to join. Encode them as T:WEP and let the password helper print the key. Also widen qrencode's margin from 2 to the spec's 4-module quiet zone; the card surround is dark, so that white border is all a scanner gets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: David Heinemeier Hansson <david@hey.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
133 lines
7.4 KiB
Bash
133 lines
7.4 KiB
Bash
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
|
|
|
run_node_test <<'JS'
|
|
const network = requireFromRoot('shell/plugins/panels/network/Model.js')
|
|
|
|
assertDeepEqual(
|
|
network.parseNetworkStatus('wifi\tCafe WiFi\t78\t5200\n'),
|
|
{ kind: 'wifi', label: 'Cafe WiFi', signalStrength: 78, frequency: '5200' },
|
|
'network parses bar status'
|
|
)
|
|
assertEqual(network.connectionIcon('wifi', 80), network.wifiIconFor(80), 'network maps wifi icon from signal')
|
|
assertEqual(network.formatHeaderSpeed('1000'), '1gbit', 'network formats gigabit speed')
|
|
assertEqual(network.formatHeaderSpeed('2500'), '2.5gbit', 'network formats fractional gigabit speed')
|
|
assertEqual(network.formatHeaderFreq('2462'), '2.4ghz', 'network formats 2.4GHz wifi band')
|
|
assertEqual(network.formatHeaderFreq('5200'), '5ghz', 'network formats 5GHz wifi band')
|
|
assertEqual(network.formatHeaderFreq('6455.0'), '6ghz', 'network formats 6GHz wifi band')
|
|
assertEqual(network.formatHeaderFreq('18300'), '18.3ghz', 'network falls back to exact GHz for unknown bands')
|
|
assertEqual(network.headerDetail({ type: 'ethernet', speed: '100' }), '100mbit', 'network header uses ethernet speed')
|
|
|
|
assertDeepEqual(
|
|
network.parseKeyValue('iface\twlan0\nrx_bytes\t100\ntx_bytes\t50\n'),
|
|
{ iface: 'wlan0', rx_bytes: '100', tx_bytes: '50' },
|
|
'network parses detail key values'
|
|
)
|
|
assertDeepEqual(
|
|
network.throughputState({ prevIface: '', prevSampleTime: 0 }, { iface: 'wlan0', rx_bytes: '100', tx_bytes: '50' }, 10),
|
|
{ prevIface: 'wlan0', prevRxBytes: 100, prevTxBytes: 50, prevSampleTime: 10, downloadRate: 0, uploadRate: 0 },
|
|
'network seeds throughput state on first sample'
|
|
)
|
|
assertDeepEqual(
|
|
network.throughputState({ prevIface: 'wlan0', prevRxBytes: 100, prevTxBytes: 50, prevSampleTime: 10 }, { iface: 'wlan0', rx_bytes: '300', tx_bytes: '90' }, 12),
|
|
{ prevIface: 'wlan0', prevRxBytes: 300, prevTxBytes: 90, prevSampleTime: 12, downloadRate: 100, uploadRate: 20 },
|
|
'network computes throughput deltas'
|
|
)
|
|
|
|
let ping = network.pingLatencyState(
|
|
{ pingIface: '', routerPingSamples: [], internetPingSamples: [] },
|
|
{ iface: 'wlan0', router_ping_ms: '2.0', internet_ping_ms: '20.0' },
|
|
4
|
|
)
|
|
assertDeepEqual(
|
|
ping,
|
|
{ pingIface: 'wlan0', routerPingSamples: [2], internetPingSamples: [20], routerPingLatency: 2, internetPingLatency: 20, internetPingPacketLoss: 0 },
|
|
'network seeds ping latency samples'
|
|
)
|
|
|
|
ping = network.pingLatencyState(ping, { iface: 'wlan0', router_ping_ms: '4.0', internet_ping_ms: '' }, 4)
|
|
assertDeepEqual(
|
|
ping,
|
|
{ pingIface: 'wlan0', routerPingSamples: [2, 4], internetPingSamples: [20, null], routerPingLatency: 3, internetPingLatency: 20, internetPingPacketLoss: 50 },
|
|
'network averages recent successful ping samples'
|
|
)
|
|
|
|
assertDeepEqual(
|
|
network.pingLatencyState(ping, { iface: 'eth0', router_ping_ms: '1.5', internet_ping_ms: '10.0' }, 4),
|
|
{ pingIface: 'eth0', routerPingSamples: [1.5], internetPingSamples: [10], routerPingLatency: 1.5, internetPingLatency: 10, internetPingPacketLoss: 0 },
|
|
'network resets ping samples when interface changes'
|
|
)
|
|
|
|
assertDeepEqual(
|
|
network.pingLatencyState(ping, { iface: 'wlan0', internet_ping_ms: '22.0' }, 4),
|
|
{ pingIface: 'wlan0', routerPingSamples: [], internetPingSamples: [20, null, 22], routerPingLatency: -1, internetPingLatency: 21, internetPingPacketLoss: 33 },
|
|
'network clears ping samples when a target is unavailable'
|
|
)
|
|
|
|
assertEqual(network.formatBytes(1536), '1.5 KB', 'network formats bytes')
|
|
assertEqual(network.formatRate(1536), '1.5 KB/s', 'network formats rates')
|
|
assertEqual(network.formatSpeedMbps('250.4'), '250 Mbps', 'network formats speed test results')
|
|
assertEqual(network.formatPingLatency('2.54'), '2.5 ms', 'network formats low ping with precision')
|
|
assertEqual(network.formatPingLatency('25.4'), '25 ms', 'network formats ping')
|
|
assertEqual(network.formatPingLatency(''), 'Timeout', 'network formats missing ping as timeout')
|
|
assertEqual(network.formatPingLatency(-1, false), '--', 'network holds the ping row before the first sample')
|
|
assertEqual(network.formatPingLatency('25.4', true), '25 ms', 'network formats ping once samples exist')
|
|
assertEqual(network.formatPingLatency('', true), 'Timeout', 'network still reports a timeout among real samples')
|
|
assertEqual(network.formatPacketLoss(2), '2%', 'network formats packet loss')
|
|
assertEqual(network.formatPacketLoss(0), '0%', 'network formats zero packet loss')
|
|
assertEqual(network.formatPacketLoss(0, false), '--', 'network holds the packet loss row before the first sample')
|
|
assertEqual(network.formatPacketLoss(0, true), '0%', 'network reports zero loss once samples exist')
|
|
|
|
const rows = network.sortWifiRows([
|
|
{ ssid: 'Open', connected: false, known: false, signal: 95 },
|
|
{ ssid: 'Known', connected: false, known: true, signal: 10 },
|
|
{ ssid: 'Connected', connected: true, known: true, signal: 20 }
|
|
])
|
|
assertDeepEqual(rows.map(row => row.ssid), ['Connected', 'Known', 'Open'], 'network sorts wifi rows by connection and known state')
|
|
assertEqual(network.wifiSectionTitle(rows, 0), 'KNOWN NETWORKS', 'network labels known wifi section')
|
|
assertEqual(network.wifiSectionTitle(rows, 2), 'OTHER NETWORKS', 'network labels other wifi section')
|
|
|
|
assertDeepEqual(
|
|
network.parseQrMatrix('010\n111\n010\n'),
|
|
{ rows: ['010', '111', '010'], size: 3 },
|
|
'network parses a square QR matrix'
|
|
)
|
|
assertDeepEqual(network.parseQrMatrix('01\n111\n'), { rows: [], size: 0 }, 'network rejects ragged QR rows')
|
|
assertDeepEqual(network.parseQrMatrix('010\n101\n'), { rows: [], size: 0 }, 'network rejects a non-square QR matrix')
|
|
assertDeepEqual(network.parseQrMatrix('010\n1x1\n010\n'), { rows: [], size: 0 }, 'network rejects invalid QR modules')
|
|
|
|
const reasons = { NoSecrets: 1, WifiAuthTimeout: 2, WifiNetworkLost: 3, WifiClientDisconnected: 4, WifiClientFailed: 5 }
|
|
assertEqual(network.networkFailureReason(1, reasons), 'Passphrase required', 'network maps missing passphrase failures')
|
|
assertEqual(network.networkFailureReason(2, reasons), 'Wrong password', 'network maps auth timeout failures')
|
|
assertEqual(network.networkFailureReason(99, reasons), 'Failed to connect', 'network maps unknown failures')
|
|
|
|
|
|
assertEqual(network.bandLabel('2.4'), '2.4ghz', 'network labels the 2.4GHz band')
|
|
assertEqual(network.bandLabel('6'), '6ghz', 'network labels the 6GHz band')
|
|
assertEqual(network.bandLabel('auto'), 'Auto', 'network labels the automatic band choice')
|
|
|
|
assertEqual(network.bandSectionTitle('auto', '2.4'), 'WI-FI BAND: 2.4GHZ', 'network names the live band in the header under automatic')
|
|
assertEqual(network.bandSectionTitle('auto', ''), 'WI-FI BAND', 'network omits an unknown band from the header')
|
|
assertEqual(network.bandSectionTitle('5', '5'), 'WI-FI BAND', 'network drops the header band once the pills are showing')
|
|
assertEqual(network.bandSectionTitle('5', '2.4'), 'WI-FI BAND', 'network keeps a plain header while a pin is settling')
|
|
|
|
assertDeepEqual(
|
|
network.parseBandStatus('band\t5\navailable\t2.4 5 6\nselected\tauto\n'),
|
|
{ band: '5', selected: 'auto', available: ['2.4', '5', '6'] },
|
|
'network parses band status'
|
|
)
|
|
assertDeepEqual(
|
|
network.parseBandStatus(''),
|
|
{ band: '', selected: 'auto', available: [] },
|
|
'network parses empty band status without a wifi connection'
|
|
)
|
|
|
|
|
|
|
|
assertEqual(network.headerDetail({ type: 'wifi', freq: '5745' }), '', 'network keeps wifi band state out of the hero')
|
|
assertEqual(network.headerDetail({ type: 'ethernet', speed: '100' }), '100mbit', 'network keeps ethernet speed in the hero')
|
|
JS
|