diff --git a/web/src/Network.jsx b/web/src/Network.jsx index 11559c730a..0cb3ffc159 100644 --- a/web/src/Network.jsx +++ b/web/src/Network.jsx @@ -22,8 +22,7 @@ import React, { useEffect, useState } from "react"; import { Button, Stack, StackItem } from "@patternfly/react-core"; import { useInstallerClient } from "./context/installer"; -import { useCancellablePromise } from "./utils"; -import { ConnectionTypes } from "./client/network"; +import { ConnectionTypes, NetworkEventTypes } from "./client/network"; import NetworkWiredStatus from "./NetworkWiredStatus"; import NetworkWifiStatus from "./NetworkWifiStatus"; import WirelessSelector from "./WirelessSelector"; @@ -31,41 +30,38 @@ import WirelessSelector from "./WirelessSelector"; export default function Network() { const client = useInstallerClient(); const [initialized, setInitialized] = useState(false); - const { cancellablePromise } = useCancellablePromise(); const [connections, setConnections] = useState([]); const [accessPoints, setAccessPoints] = useState([]); const [openWirelessSelector, setOpenWirelessSelector] = useState(false); useEffect(() => { - cancellablePromise(client.network.activeConnections()).then(setConnections); - }, [client.network, cancellablePromise]); - - useEffect(() => { - const onConnectionAdded = addedConnection => { - setConnections(conns => [...conns, addedConnection]); - }; + if (!initialized) return; - return client.network.listen("connectionAdded", onConnectionAdded); - }, [client.network]); + setConnections(client.network.activeConnections()); + }, [client.network, initialized]); useEffect(() => { - const onConnectionRemoved = id => { - setConnections(conns => conns.filter(c => c.id !== id)); - }; + return client.network.onNetworkEvent(({ type, payload }) => { + switch (type) { + case NetworkEventTypes.ACTIVE_CONNECTION_ADDED: { + setConnections(conns => [...conns, payload]); + break; + } - return client.network.listen("connectionRemoved", onConnectionRemoved); - }, [client.network]); - - useEffect(() => { - const onConnectionUpdated = connection => { - setConnections(conns => { - const newConnections = conns.filter(c => c.id !== connection.id); - return [...newConnections, connection]; - }); - }; + case NetworkEventTypes.ACTIVE_CONNECTION_UPDATED: { + setConnections(conns => { + const newConnections = conns.filter(c => c.id !== payload.id); + return [...newConnections, payload]; + }); + break; + } - return client.network.listen("connectionUpdated", onConnectionUpdated); - }, [client.network]); + case NetworkEventTypes.ACTIVE_CONNECTION_REMOVED: { + setConnections(conns => conns.filter(c => c.id !== payload.id)); + } + } + }); + }); useEffect(() => { client.network.setUp().then(() => setInitialized(true)); diff --git a/web/src/TargetIpsPopup.jsx b/web/src/TargetIpsPopup.jsx index 5a31bb67f6..8f5236fdab 100644 --- a/web/src/TargetIpsPopup.jsx +++ b/web/src/TargetIpsPopup.jsx @@ -30,64 +30,45 @@ import { formatIp } from "./client/network"; export default function TargetIpsPopup() { const client = useInstallerClient(); const { cancellablePromise } = useCancellablePromise(); - const [connections, setConnections] = useState([]); - const [hostname, setHostname] = useState(""); + const [addresses, setAddresses] = useState([]); + const [initialized, setInitialized] = useState(false); + const [hostname, setHostname] = useState(); const [isOpen, setIsOpen] = useState(false); useEffect(() => { - cancellablePromise(client.network.config()).then(config => { - setConnections(config.connections); - setHostname(config.hostname); - }); + cancellablePromise(client.network.setUp()).then(() => setInitialized(true)); }, [client.network, cancellablePromise]); useEffect(() => { - const onConnectionAdded = addedConnection => { - setConnections(conns => [...conns, addedConnection]); - }; - - return client.network.listen("connectionAdded", onConnectionAdded); - }, [client.network]); + if (!initialized) return; - useEffect(() => { - const onConnectionRemoved = id => { - setConnections(conns => conns.filter(c => c.id !== id)); + const refreshState = () => { + setAddresses(client.network.addresses()); + setHostname(client.network.hostname()); }; - return client.network.listen("connectionRemoved", onConnectionRemoved); - }, [client.network]); - - useEffect(() => { - const onConnectionUpdated = updatedConnection => { - setConnections(conns => { - const newConnections = conns.filter(c => c.id !== updatedConnection.id); - return [...newConnections, updatedConnection]; - }); - }; - - return client.network.listen("connectionUpdated", onConnectionUpdated); - }, [client.network]); - - if (connections.length === 0) return null; - - const ips = connections.flatMap(conn => conn.addresses.map(formatIp)); - const [firstIp] = ips; + refreshState(); + return client.network.onNetworkEvent(() => { + refreshState(); + }); + }, [client.network, initialized]); - if (ips.length === 0) return null; + if (addresses.length === 0) return null; + const [firstIp] = addresses; const open = () => setIsOpen(true); const close = () => setIsOpen(false); return ( <> - - + - {ips.map(ip => ( - {ip} + {addresses.map((ip, index) => ( + {formatIp(ip)} ))} diff --git a/web/src/TargetIpsPopup.test.jsx b/web/src/TargetIpsPopup.test.jsx index 26ed3aa510..079b59f7ad 100644 --- a/web/src/TargetIpsPopup.test.jsx +++ b/web/src/TargetIpsPopup.test.jsx @@ -29,29 +29,25 @@ import TargetIpsPopup from "./TargetIpsPopup"; jest.mock("./client"); -const conn0 = { - id: "7a9470b5-aa0e-4e20-b48e-3eee105543e9", - addresses: [ - { address: "1.2.3.4", prefix: 24 }, - { address: "5.6.7.8", prefix: 16 }, - ], -}; +const addresses = [ + { address: "1.2.3.4", prefix: 24 }, + { address: "5.6.7.8", prefix: 16 }, +]; +const addressFn = jest.fn().mockReturnValue(addresses); +const hostnameFn = jest.fn().mockReturnValue("example.net"); describe("TargetIpsPopup", () => { let callbacks; - const hostname = "example.net"; - beforeEach(() => { - callbacks = {}; - const listenFn = (event, cb) => { callbacks[event] = cb }; + callbacks = []; + const onNetworkEventFn = (cb) => { callbacks.push(cb) }; createClient.mockImplementation(() => { return { network: { - listen: listenFn, - config: () => Promise.resolve({ - connections: [conn0], - hostname - }), + onNetworkEvent: onNetworkEventFn, + addresses: addressFn, + hostname: hostnameFn, + setUp: jest.fn().mockResolvedValue() } }; }); @@ -65,7 +61,7 @@ describe("TargetIpsPopup", () => { const dialog = await screen.findByRole("dialog"); - within(dialog).getByText(/Ip Addresses/); + within(dialog).getByText(/IP Addresses/); within(dialog).getByText("5.6.7.8/16"); const closeButton = within(dialog).getByRole("button", { name: /Close/i }); @@ -76,33 +72,15 @@ describe("TargetIpsPopup", () => { }); }); - it("updates the IP if the connection changes", async () => { - installerRender(); - await screen.findByRole("button", { name: /1.2.3.4\/24 \(example.net\)/i }); - const updatedConn = { - ...conn0, - addresses: [{ address: "5.6.7.8", prefix: 24 }] - }; - - act(() => { - callbacks.connectionUpdated(updatedConn); - }); - await screen.findByRole("button", { name: /5.6.7.8\/24 \(example.net\)/i }); - }); - - it("updates the IP if the connection is replaced", async () => { + it("updates address and hostname if they change", async () => { installerRender(); await screen.findByRole("button", { name: /1.2.3.4\/24 \(example.net\)/i }); - const conn1 = { - ...conn0, - id: "2f1b1c0d-c835-479d-ae7d-e828bb4a75fa", - addresses: [{ address: "5.6.7.8", prefix: 24 }] - }; + addressFn.mockReturnValue([{ address: "5.6.7.8", prefix: 24 }]); + hostnameFn.mockReturnValue("localhost.localdomain"); act(() => { - callbacks.connectionAdded(conn1); - callbacks.connectionRemoved(conn0.id); + callbacks.forEach(cb => cb()); }); - await screen.findByRole("button", { name: /5.6.7.8\/24 \(example.net\)/i }); + await screen.findByRole("button", { name: /5.6.7.8\/24 \(localhost.localdomain\)/i }); }); }); diff --git a/web/src/client/network.test.js b/web/src/client/network.test.js index cb349333ea..561a3bede6 100644 --- a/web/src/client/network.test.js +++ b/web/src/client/network.test.js @@ -33,8 +33,8 @@ const conn = { const adapter = { setUp: jest.fn(), - activeConnections: jest.fn().mockResolvedValue([conn]), - hostname: jest.fn().mockResolvedValue("localhost"), + activeConnections: jest.fn().mockReturnValue([conn]), + hostname: jest.fn().mockReturnValue("localhost.localdomain"), subscribe: jest.fn(), getConnection: jest.fn(), addConnection: jest.fn(), @@ -43,16 +43,25 @@ const adapter = { }; describe("NetworkClient", () => { - describe("#config", () => { - it("returns an object containing the hostname, known IPv4 addresses, and active connections", async () => { + describe("#activeConnections", () => { + it("retuns the list of active connections from the adapter", () => { const client = new NetworkClient(adapter); - const config = await client.config(); + const connections = client.activeConnections(); + expect(connections).toEqual([conn]); + }); + }); - expect(config.hostname).toEqual("localhost"); - expect(config.addresses).toEqual([ - { address: "192.168.122.1", prefix: 24 } - ]); - expect(config.connections).toEqual([conn]); + describe("#addresses", () => { + it("returns the list of addresses", () => { + const client = new NetworkClient(adapter); + expect(client.addresses()).toEqual([{ address: "192.168.122.1", prefix: 24 }]); + }); + }); + + describe("#hostname", () => { + it("returns the hostname from the adapter", () => { + const client = new NetworkClient(adapter); + expect(client.hostname()).toEqual("localhost.localdomain"); }); }); }); diff --git a/web/src/client/network/index.js b/web/src/client/network/index.js index 22f561bb21..e185221793 100644 --- a/web/src/client/network/index.js +++ b/web/src/client/network/index.js @@ -37,25 +37,15 @@ const NetworkEventTypes = Object.freeze({ ACTIVE_CONNECTION_REMOVED: "active_connection_removed" }); -/** @typedef {(conns: ActiveConnection[]) => void} ConnectionFn */ -/** @typedef {(conns: string[]) => void} ConnectionPathsFn */ - -/** - * @typedef {object} Handlers - * @property {ConnectionFn[]} connectionAdded - * @property {ConnectionFn[]} connectionRemoved - * @property {ConnectionFn[]} connectionUpdated - */ - /** * @typedef {object} NetworkAdapter - * @property {() => Promise} activeConnections + * @property {() => ActiveConnection[]} activeConnections * @property {() => AccessPoint[]} accessPoints * @property {(handler: (event: NetworkEvent) => void) => void} subscribe * @property {(id: string) => Promise} getConnection * @property {(connection: Connection) => Promise} addConnection * @property {(connection: Connection) => Promise} updateConnection - * @property {() => Promise} hostname + * @property {() => string} hostname * @property {() => void} setUp */ @@ -75,6 +65,12 @@ const formatIp = addr => `${addr.address}/${addr.prefix}`; * @property {object} payload */ +/** + * Network event handler + * + * @typedef {(event: NetworkEvent) => void} NetworkEventFn + */ + /** * Network client */ @@ -87,43 +83,23 @@ o * NetworkManagerAdapter. this.adapter = adapter || new NetworkManagerAdapter(); /** @type {!boolean} */ this.subscribed = false; - /** @type {Handlers} */ - this.handlers = { - connectionAdded: [], - connectionRemoved: [], - connectionUpdated: [] - }; this.setUpDone = false; + /** @type {NetworkEventFn[]} */ + this.handlers = []; } /** - * Returns IP config overview - addresses, connections and hostname - * @return {Promise<{addresses: IPAddress[], hostname: string, connections: ActiveConnection[]}>} - */ - async config() { - return { - connections: await this.adapter.activeConnections(), - addresses: await this.addresses(), - hostname: await this.adapter.hostname() - }; - } - - /** - * Registers a callback to run when a given event happens + * Adds a callback to run when a network event happens (a connection is added, + * updated, removed, etc.). * - * @param {"connectionAdded" | "connectionUpdated" | "connectionRemoved"} eventType - event type - * @param {ConnectionFn} handler - the callback to be executed - * @return {function} a function to remove the callback + * @param {NetworkEventFn} handler - Callback function + * @return {() => void} Function to remove the handler */ - listen(eventType, handler) { - if (!this.subscribed) { - // FIXME: when/where should we unsubscribe? - this.subscribe(); - } - - this.handlers[eventType].push(handler); + onNetworkEvent(handler) { + this.handlers.push(handler); return () => { - this.handlers[eventType].filter(h => h !== handler); + const position = this.handlers.indexOf(handler); + if (position > -1) this.handlers.splice(position, 1); }; } @@ -133,46 +109,15 @@ o * NetworkManagerAdapter. async setUp() { if (this.setUpDone) return; - return this.adapter.setUp(); - } - - /** - * FIXME: improve this documentation - * Starts listening changes on active connections - * - * @private - * @return {Promise} function to disable the callback - */ - async subscribe() { - // TODO: refactor this method - this.subscribed = true; - - this.adapter.subscribe(({ type, payload }) => { - switch (type) { - case NetworkEventTypes.ACTIVE_CONNECTION_ADDED: { - this.handlers.connectionAdded.forEach(handler => handler(payload)); - break; - } - - case NetworkEventTypes.ACTIVE_CONNECTION_UPDATED: { - this.handlers.connectionUpdated.forEach(handler => handler(payload)); - break; - } - - case NetworkEventTypes.ACTIVE_CONNECTION_REMOVED: { - this.handlers.connectionRemoved.forEach(handler => handler(payload.path)); - break; - } - } - }); + return this.adapter.setUp(e => this.handlers.forEach(f => f(e))); } /** * Returns the active connections * - * @returns {Promise} + * @returns {ActiveConnection[]} */ - async activeConnections() { + activeConnections() { return this.adapter.activeConnections(); } @@ -222,10 +167,19 @@ o * NetworkManagerAdapter. * @private * @return {Promise} */ - async addresses() { - const conns = await this.adapter.activeConnections(); + addresses() { + const conns = this.adapter.activeConnections(); return conns.flatMap(c => c.addresses); } + + /** + * Returns the computer's hostname + * + * @return {string} + */ + hostname() { + return this.adapter.hostname(); + } } export { diff --git a/web/src/client/network/network_manager.js b/web/src/client/network/network_manager.js index 8fdef1c308..97f64449e3 100644 --- a/web/src/client/network/network_manager.js +++ b/web/src/client/network/network_manager.js @@ -24,7 +24,7 @@ import { DBusClient } from "../dbus"; import cockpit from "../../lib/cockpit"; import { intToIPString, stringToIPInt } from "./utils"; -import { NetworkEventTypes, ConnectionState } from "./index"; +import { NetworkEventTypes } from "./index"; import { createAccessPoint, SecurityProtocols } from "./model"; /** @@ -32,16 +32,19 @@ import { createAccessPoint, SecurityProtocols } from "./model"; * @typedef {import("./model").ActiveConnection} ActiveConnection * @typedef {import("./model").IPAddress} IPAddress * @typedef {import("./model").AccessPoint} AccessPoint + * @typedef {import("./index").NetworkEventFn} NetworkEventFn */ -const NM_SERVICE_NAME = "org.freedesktop.NetworkManager"; -const NM_IFACE = "org.freedesktop.NetworkManager"; -const NM_SETTINGS_IFACE = "org.freedesktop.NetworkManager.Settings"; -const NM_CONNECTION_IFACE = "org.freedesktop.NetworkManager.Settings.Connection"; -const NM_ACTIVE_CONNECTION_IFACE = "org.freedesktop.NetworkManager.Connection.Active"; -const NM_IP4CONFIG_IFACE = "org.freedesktop.NetworkManager.IP4Config"; -const AP_IFACE = "org.freedesktop.NetworkManager.AccessPoint"; -const AP_NAMESPACE = "/org/freedesktop/NetworkManager/AccessPoint"; +const SERVICE_NAME = "org.freedesktop.NetworkManager"; +const IFACE = "org.freedesktop.NetworkManager"; +const SETTINGS_IFACE = "org.freedesktop.NetworkManager.Settings"; +const CONNECTION_IFACE = "org.freedesktop.NetworkManager.Settings.Connection"; +const ACTIVE_CONNECTION_IFACE = "org.freedesktop.NetworkManager.Connection.Active"; +const ACTIVE_CONNECTION_NAMESPACE = "/org/freedesktop/NetworkManager/ActiveConnection"; +const IP4CONFIG_IFACE = "org.freedesktop.NetworkManager.IP4Config"; +const IP4CONFIG_NAMESPACE = "/org/freedesktop/NetworkManager/IP4Config"; +const ACCESS_POINT_IFACE = "org.freedesktop.NetworkManager.AccessPoint"; +const ACCESS_POINT_NAMESPACE = "/org/freedesktop/NetworkManager/AccessPoint"; const ApFlags = Object.freeze({ NONE: 0x00000000, @@ -164,37 +167,46 @@ class NetworkManagerAdapter { * @param {DBusClient} [dbusClient] - D-Bus client */ constructor(dbusClient) { - this.client = dbusClient || new DBusClient(NM_SERVICE_NAME); + this.client = dbusClient || new DBusClient(SERVICE_NAME); /** @type {{[k: string]: string}} */ this.connectionIds = {}; this.proxies = { - accessPoints: {} + accessPoints: {}, + activeConnections: {}, + ip4Configs: {}, + settings: null }; + this.eventsHandler = null; } /** * Builds proxies and starts listening to them + * + * @param {NetworkEventFn} handler - Events handler */ - async setUp() { + async setUp(handler) { + this.eventsHandler = handler; this.proxies = { - accessPoints: await this.client.proxies(AP_IFACE, AP_NAMESPACE) + accessPoints: await this.client.proxies(ACCESS_POINT_IFACE, ACCESS_POINT_NAMESPACE), + activeConnections: await this.client.proxies( + ACTIVE_CONNECTION_IFACE, ACTIVE_CONNECTION_NAMESPACE + ), + ip4Configs: await this.client.proxies(IP4CONFIG_IFACE, IP4CONFIG_NAMESPACE), + settings: await this.client.proxy(SETTINGS_IFACE) }; + this.subscribeToEvents(); } /** * Returns the list of active connections * - * @return {Promise} + * @return {ActiveConnection[]} * @see https://developer-old.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.html */ - async activeConnections() { - const proxy = await this.client.proxy(NM_IFACE); - let connections = []; - const paths = await proxy.ActiveConnections; - for (const path of paths) { - connections = [...connections, await this.activeConnectionFromPath(path)]; - } - return connections; + activeConnections() { + return Object.values(this.proxies.activeConnections).map(proxy => { + return this.activeConnectionFromProxy(proxy); + }); } /** @@ -243,7 +255,7 @@ class NetworkManagerAdapter { * @param {Connection} connection - Connection to add */ async addConnection(connection) { - const proxy = await this.client.proxy(NM_SETTINGS_IFACE); + const proxy = await this.client.proxy(SETTINGS_IFACE); const connCockpit = connectionToCockpit(connection); const path = await proxy.AddConnection(connCockpit); await this.activateConnection(path); @@ -270,32 +282,21 @@ class NetworkManagerAdapter { * Registers a handler for changes in /org/freedesktop/NetworkManager/ActiveConnection/*. * The handler recevies a NetworkEvent object. * - * @param {(event: import("./index").NetworkEvent) => void} handler - Event handler function + * @private */ - async subscribe(handler) { - const proxies = await this.client.proxies( - NM_ACTIVE_CONNECTION_IFACE, - "/org/freedesktop/NetworkManager/ActiveConnection" - ); - - proxies.addEventListener("added", (_event, proxy) => { - this.activeConnectionFromProxy(proxy).then(connection => { - this.connectionIds[proxy.path] = connection.id; - handler({ type: NetworkEventTypes.ACTIVE_CONNECTION_ADDED, payload: connection }); - }); - }); + async subscribeToEvents() { + const proxies = this.proxies.activeConnections; - proxies.addEventListener("changed", (_event, proxy) => { - this.activeConnectionFromProxy(proxy).then(connection => { - handler({ type: NetworkEventTypes.ACTIVE_CONNECTION_UPDATED, payload: connection }); - }); - }); + /** @type {(eventType: string) => NetworkEventFn} */ + const handleWrapper = (eventType) => (_event, proxy) => { + const connection = this.activeConnectionFromProxy(proxy); + this.eventsHandler({ type: eventType, payload: connection }); + }; - proxies.addEventListener("removed", (_event, proxy) => { - const connectionId = this.connectionIds[proxy.path]; - delete this.connectionIds[proxy.path]; - handler({ type: NetworkEventTypes.ACTIVE_CONNECTION_REMOVED, payload: connectionId }); - }); + // FIXME: do not build a map (eventTypesMap), just inject the type here + proxies.addEventListener("added", handleWrapper(NetworkEventTypes.ACTIVE_CONNECTION_ADDED)); + proxies.addEventListener("changed", handleWrapper(NetworkEventTypes.ACTIVE_CONNECTION_UPDATED)); + proxies.addEventListener("removed", handleWrapper(NetworkEventTypes.ACTIVE_CONNECTION_REMOVED)); } /** @@ -307,7 +308,7 @@ class NetworkManagerAdapter { * https://developer-old.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.html */ async activateConnection(path) { - const proxy = await this.client.proxy(NM_IFACE); + const proxy = await this.client.proxy(IFACE); return proxy.ActivateConnection(path, "/", "/"); } @@ -318,13 +319,12 @@ class NetworkManagerAdapter { * * @private * @param {object} proxy - Proxy object from /org/freedesktop/NetworkManager/ActiveConnection/* - * @return {Promise} + * @return {ActiveConnection} */ - async activeConnectionFromProxy(proxy) { + activeConnectionFromProxy(proxy) { + const ip4Config = this.proxies.ip4Configs[proxy.Ip4Config]; let addresses = []; - - if (proxy.State === ConnectionState.ACTIVATED) { - const ip4Config = await this.client.proxy(NM_IP4CONFIG_IFACE, proxy.Ip4Config); + if (ip4Config) { addresses = ip4Config.AddressData.map(this.connectionIPAddress); } @@ -337,18 +337,6 @@ class NetworkManagerAdapter { }; } - /** - * Builds a connection object from a D-Bus path. - * - * @private - * @param {string} path - Connection D-Bus path - * @returns {Promise} - */ - async activeConnectionFromPath(path) { - const proxy = await this.client.proxy(NM_ACTIVE_CONNECTION_IFACE, path); - return this.activeConnectionFromProxy(proxy); - } - /** * * Returns connection settings for the given connection @@ -358,9 +346,9 @@ class NetworkManagerAdapter { * @return {Promise} */ async connectionSettingsObject(id) { - const proxy = await this.client.proxy(NM_SETTINGS_IFACE); + const proxy = await this.client.proxy(SETTINGS_IFACE); const path = await proxy.GetConnectionByUuid(id); - return await this.client.proxy(NM_CONNECTION_IFACE, path); + return await this.client.proxy(CONNECTION_IFACE, path); } /* @@ -382,13 +370,14 @@ class NetworkManagerAdapter { /** * Returns the computer's hostname * - * @returns {Promise} + * @return {string} * * https://developer-old.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.Settings.html */ - async hostname() { - const proxy = await this.client.proxy(NM_SETTINGS_IFACE); - return proxy.Hostname; + hostname() { + if (!this.proxies.settings) return ""; + + return this.proxies.settings.Hostname; } } diff --git a/web/src/client/network/network_manager.test.js b/web/src/client/network/network_manager.test.js index 90bb08fafd..499ecf4195 100644 --- a/web/src/client/network/network_manager.test.js +++ b/web/src/client/network/network_manager.test.js @@ -27,9 +27,9 @@ import cockpit from "../../lib/cockpit"; const NM_IFACE = "org.freedesktop.NetworkManager"; const NM_SETTINGS_IFACE = "org.freedesktop.NetworkManager.Settings"; -const NM_ACTIVE_CONNECTION_IFACE = "org.freedesktop.NetworkManager.Connection.Active"; -const NM_IP4CONFIG_IFACE = "org.freedesktop.NetworkManager.IP4Config"; +const IP4CONFIG_IFACE = "org.freedesktop.NetworkManager.IP4Config"; const NM_CONNECTION_IFACE = "org.freedesktop.NetworkManager.Settings.Connection"; +const ACTIVE_CONNECTION_IFACE = "org.freedesktop.NetworkManager.Connection.Active"; const dbusClient = new DBusClient(""); @@ -47,9 +47,14 @@ const activeConnections = { State: ConnectionState.ACTIVATED, Type: ConnectionTypes.ETHERNET, Ip4Config: "/ip4Config/1" - } + }, + }; +Object.defineProperties(activeConnections, { + addEventListener: { value: jest.fn(), enumerable: false } +}); + const addressesData = { "/ip4Config/1": { wait: jest.fn(), @@ -85,10 +90,6 @@ const networkSettingsProxy = () => ({ AddConnection: AddConnectionFn }); -const activeConnectionProxy = path => activeConnections[path]; - -const ipConfigProxy = path => addressesData[path]; - const connectionSettingsMock = { wait: jest.fn(), GetSettings: () => ({ @@ -118,19 +119,24 @@ const connectionSettingsProxy = () => connectionSettingsMock; describe("NetworkManagerAdapter", () => { beforeEach(() => { - dbusClient.proxy = jest.fn().mockImplementation((iface, path) => { + dbusClient.proxy = jest.fn().mockImplementation(iface => { if (iface === NM_IFACE) return networkProxy(); if (iface === NM_SETTINGS_IFACE) return networkSettingsProxy(); - if (iface === NM_ACTIVE_CONNECTION_IFACE) return activeConnectionProxy(path); - if (iface === NM_IP4CONFIG_IFACE) return ipConfigProxy(path); if (iface === NM_CONNECTION_IFACE) return connectionSettingsProxy(); }); + + dbusClient.proxies = jest.fn().mockImplementation(iface => { + if (iface === ACTIVE_CONNECTION_IFACE) return activeConnections; + if (iface === IP4CONFIG_IFACE) return addressesData; + return {}; + }); }); describe("#activeConnections", () => { it("returns the list of active connections", async () => { const client = new NetworkManagerAdapter(dbusClient); - const availableConnections = await client.activeConnections(); + await client.setUp(); + const availableConnections = client.activeConnections(); expect(availableConnections.length).toEqual(2); const [wireless, ethernet] = availableConnections;