From ba71cf771e4173aa902ce3ba5e96aebde6add93c Mon Sep 17 00:00:00 2001 From: keeramis Date: Tue, 21 May 2024 21:15:26 -0700 Subject: [PATCH] Refactor and add proper error handling --- package.json | 1 + src/cli/wifi.js | 10 + src/cmd/wifi.js | 4 + src/lib/wifi-control-request.js | 524 ++++++++++++++------------- src/lib/wifi-control-request.test.js | 58 +-- test/e2e/device.e2e.js | 2 +- 6 files changed, 318 insertions(+), 281 deletions(-) diff --git a/package.json b/package.json index 70fe797b0..95e38aa79 100644 --- a/package.json +++ b/package.json @@ -142,6 +142,7 @@ "test:e2e": "npm run build -- --target host && mocha 'test/{,!(__fixtures__)/**}/*.e2e.js' --timeout 120000 --exit", "test:e2e:ci": "npm run test:e2e:no-device -- --forbid-only", "test:e2e:no-device": "npm run test:e2e -- --grep @device --invert", + "test:e2e:wifi": "npm run test:e2e -- --grep @wifi", "test:e2e:watch": "npm run test:e2e -- --watch --watch-extensions js,json", "test:e2e:inspect": "npm test:e2e -- --inspect-brk", "test:e2e:silent": "PARTICLE_NOOP=$(npm run test:e2e:ci)", diff --git a/src/cli/wifi.js b/src/cli/wifi.js index 7b0cd30b4..0ec9b7116 100644 --- a/src/cli/wifi.js +++ b/src/cli/wifi.js @@ -99,6 +99,16 @@ module.exports = ({ commandProcessor, root }) => { '$0 $command ssid': 'Removes network from the device', } }); + + commandProcessor.createCommand(wifi, 'status', 'Gets the current network', { + handler: (args) => { + const WiFiCommands = require('../cmd/wifi'); + return new WiFiCommands().getCurrentNetwork(); + }, + examples: { + '$0 $command ssid': 'Gets the current network', + } + }); return wifi; }; diff --git a/src/cmd/wifi.js b/src/cmd/wifi.js index 7f073514a..9f9efe73e 100644 --- a/src/cmd/wifi.js +++ b/src/cmd/wifi.js @@ -37,4 +37,8 @@ module.exports = class WiFiCommands extends CLICommandBase { removeNetwork(ssid) { return this.wifiControlRequest.removeNetwork(ssid); } + + getCurrentNetwork() { + return this.wifiControlRequest.getCurrentNetwork(); + } } \ No newline at end of file diff --git a/src/lib/wifi-control-request.js b/src/lib/wifi-control-request.js index 47c764e15..920ae241c 100644 --- a/src/lib/wifi-control-request.js +++ b/src/lib/wifi-control-request.js @@ -11,8 +11,17 @@ const settings = require('../../settings'); const createApiCache = require('./api-cache'); const utilities = require('./utilities'); const os = require('os'); -const semver = require('semver'); const { WifiSecurityEnum } = require('particle-usb'); +const chalk = require('chalk'); +const { platformForId } = require('../lib/platform'); + +// TODO: Fix retries - Only retry if it makes sense + +// TODO: Tell people if you want to use a hidden nwetwork, use particle wifi add and use the file optionparticle wifi join + +// Tests!!!!!!! + +const WIFI_COMMANDS_SUPPORTED_DEVICE_GEN = 3; module.exports = class WiFiControlRequest { constructor(deviceId, { ui, newSpin, stopSpin, file }) { @@ -27,110 +36,79 @@ module.exports = class WiFiControlRequest { } async addNetwork() { - let network; - try { - if (!this.device || this.device.isOpen === false) { - this.device = await usbUtils.getOneUsbDevice({ api: this.api, idOrName: this.deviceId, ui: this.ui }); - } - - const fwVer = this.device.firmwareVersion; - if (semver.lt(fwVer, '6.2.0')) { - throw new Error(`The 'add' command is not supported on this firmware version.${os.EOL}Use 'particle wifi join --help' to join a network.${os.EOL}`); - } - await this.ensureVersionCompat({ - version: this.device.firmwareVersion, - command: 'add' - }); - - if (this.file) { - network = await this.getNetworkToConnectFromJson(); - } else { - network = await this.getNetworkToConnect(this.device); - } - await this.addWifi(network); - } catch (error) { - throw error; - } finally { - if (this.device && this.device.isOpen) { - await this.device.close(); + await this._withDevice(async () => { + try { + const network = await this._getNetwork(); + await this.addWifi(network); + } catch (error) { + if (error.message.endsWith('Not supported')) { + if (this.device.generation < 3) { + throw new Error(`The 'add' command is not supported on this device (${this.device.deviceId}). Use 'particle serial wifi'.${os.EOL}`); + } + throw new Error(`The 'add' command is not supported on this firmware version.${os.EOL}Use 'particle wifi join --help' to join a network.${os.EOL}Alternatively, check 'particle serial wifi' for more options.${os.EOL}`); + } + throw error; } - } + }); } async joinNetwork() { - let network; - try { - if (!this.device || this.device.isOpen === false) { - this.device = await usbUtils.getOneUsbDevice({ api: this.api, idOrName: this.deviceId, ui: this.ui }); - } - - if (this.file) { - network = await this.getNetworkToConnectFromJson(); - } else { - network = await this.getNetworkToConnect(this.device); - } - await this.joinWifi(network); - } catch (error) { - throw error; - } finally { - if (this.device && this.device.isOpen) { - await this.device.close(); + await this._withDevice(async () => { + try { + const network = await this._getNetwork(); + await this.joinWifi(network); + } catch (error) { + if (error.message.endsWith('Not found')) { + throw new Error(`Network not found.${os.EOL}If you are using a hidden network, please add the hidden network credentials first using 'particle wifi add'.${os.EOL}`); + } + throw error; } - } + }); } async joinKnownNetwork(ssid) { - try { - if (!this.device || this.device.isOpen === false) { - this.device = await usbUtils.getOneUsbDevice({ api: this.api, idOrName: this.deviceId, ui: this.ui }); - } + await this._withDevice(async () => { await this.joinKnownWifi(ssid); - } catch (error) { - throw error; - } finally { - if (this.device && this.device.isOpen) { - await this.device.close(); - } - } + }); } async listNetworks() { - try { - if (!this.device || this.device.isOpen === false) { - this.device = await usbUtils.getOneUsbDevice({ api: this.api, idOrName: this.deviceId, ui: this.ui }); - } + await this._withDevice(async () => { await this.listWifi(); - } catch (error) { - throw error; - } finally { - if (this.device && this.device.isOpen) { - await this.device.close(); - } - } + }); } async removeNetwork(ssid) { - try { - if (!this.device || this.device.isOpen === false) { - this.device = await usbUtils.getOneUsbDevice({ api: this.api, idOrName: this.deviceId, ui: this.ui }); - } + await this._withDevice(async () => { await this.removeWifi(ssid); await this.listNetworks(); - } catch (error) { - throw error; - } finally { - if (this.device && this.device.isOpen) { - await this.device.close(); - } - } + }); } async clearNetworks() { + await this._withDevice(async () => { + await this.clearWifi(); + }); + } + + async getCurrentNetwork() { + await this._withDevice(async () => { + await this.getCurrentWifiNetwork(); + }); + } + + async _withDevice(fn) { try { if (!this.device || this.device.isOpen === false) { this.device = await usbUtils.getOneUsbDevice({ api: this.api, idOrName: this.deviceId, ui: this.ui }); + const deviceGen = platformForId(this.device.platformId).generation; + const platformName = platformForId(this.device.platformId).name; + this.deviceId = this.device._id; + if (deviceGen < WIFI_COMMANDS_SUPPORTED_DEVICE_GEN) { + throw new Error(`The 'particle wifi' commands are not supported on this device (${this.deviceId} / ${platformName}).${os.EOL} Use 'particle serial wifi'.${os.EOL}`); + } } - await this.clearWifi(); + return await fn(); } catch (error) { throw error; } finally { @@ -140,27 +118,37 @@ module.exports = class WiFiControlRequest { } } - async getNetworkToConnectFromJson() { - const { network, password } = await fs.readJSON(this.file); + async _getNetwork() { + let network; + if (this.file) { + network = await this._getNetworkToConnectFromJson(); + } else { + network = await this._getNetworkToConnect(); + } + return network; + } + + async _getNetworkToConnectFromJson() { + const { network, security, password } = await fs.readJSON(this.file); if (!network) { const error = new Error('No network name found in the file'); error.isUsageError = true; throw error; } - return { ssid: network, password }; + return { ssid: network, security: WifiSecurityEnum[this._convertToKnownSecType(security)], password }; } - async getNetworkToConnect({ prompt = true } = { }) { + async _getNetworkToConnect({ prompt = true } = { }) { let scan = true; if (prompt) { - scan = await this.promptForScanNetworks(); + scan = await this._promptForScanNetworks(); } if (scan) { - const networks = await this.scanNetworks(); + const networks = await this._scanNetworks(); if (networks.length) { - const network = await this.promptToSelectNetwork(networks); + const network = await this._promptToSelectNetwork(networks); if (network?.rescan){ - return await this.getNetworkToConnect({ prompt: false }); + return await this._getNetworkToConnect({ prompt: false }); } else { return network; } @@ -168,10 +156,10 @@ module.exports = class WiFiControlRequest { throw new Error('No Wi-Fi networks found'); } } - return this.pickNetworkManually(); + return this._pickNetworkManually(); } - async promptForScanNetworks() { + async _promptForScanNetworks() { const question = { type: 'confirm', name: 'scan', @@ -181,7 +169,7 @@ module.exports = class WiFiControlRequest { return ans.scan; } - async scanNetworks() { + async _scanNetworks() { const networks = await this._deviceScanNetworks(); if (!networks.length) { const answers = await this.ui.prompt([{ @@ -191,7 +179,7 @@ module.exports = class WiFiControlRequest { default: true }]); if (answers.rescan){ - return this.scanNetworks(); + return this._scanNetworks(); } } return this._filterNetworks(networks); @@ -244,7 +232,7 @@ module.exports = class WiFiControlRequest { throw this._handleDeviceError(lastError, { action: 'scan for Wi-Fi networks' }); } - async promptToSelectNetwork(networks) { + async _promptToSelectNetwork(networks) { let password; const questions = [ { @@ -269,204 +257,219 @@ module.exports = class WiFiControlRequest { if (!network.unsecure) { password = await this._promptForPassword(); } - return { ssid: network.ssid, password }; + return { ssid: network.ssid, security: WifiSecurityEnum[this._convertToKnownSecType(network.security)], password }; } - async addWifi({ ssid, password }) { - let retries = RETRY_COUNT; - const spin = this.newSpin(`Joining Wi-Fi network '${ssid}'`).start(); + async _performWifiOperation(operationName, operationCallback) { + const spin = this.newSpin(`${operationName}`).start(); let lastError; - while (retries > 0) { + + const tryOperation = async () => { + if (!this.device) { + throw new Error('No device found'); + } + + return await operationCallback(); + }; + + for (let attempt = 0; attempt < RETRY_COUNT; attempt++) { try { - if (!this.device) { - throw new Error('No device found'); - } - const { pass, error } = await this.device.setWifiCredentials({ ssid, password }, { timeout: JOIN_NETWORK_TIMEOUT }); - if (pass) { + const result = await tryOperation(); + if (result !== false) { this.stopSpin(); - this.ui.stdout.write('Wi-Fi network added successfully.'); - this.ui.stdout.write(os.EOL); - return; + return result; } - retries = 0; - lastError = new Error(error); } catch (error) { - spin.setSpinnerTitle(`Joining Wi-Fi network '${ssid}' is taking longer than expected.`); + if (error.message === 'NOT_SUPPORTED') { + this.stopSpin(); + throw error; + } lastError = error; - await utilities.delay(TIME_BETWEEN_RETRIES); - retries--; } + + spin.setSpinnerTitle(`${operationName} is taking longer than expected.`); + await utilities.delay(TIME_BETWEEN_RETRIES); } + this.stopSpin(); - throw this._handleDeviceError(lastError, { action: 'add Wi-Fi network' }); + console.log('operationName', operationName); + throw this._handleDeviceError(lastError, { action: operationName.toLowerCase() }); + } + + + async addWifi({ ssid, security, password }) { + // Error can be coming from the particle-usb device API call + // or the device API call might return the error in the result object without throwing an error + let result; + await this._performWifiOperation(`Adding Wi-Fi network '${ssid}'`, async () => { + result = await this.device.setWifiCredentials({ ssid, security, password }, { timeout: JOIN_NETWORK_TIMEOUT }); + }); + + const { pass, error } = result; + + if (pass) { + this.ui.stdout.write(`Wi-Fi network '${ssid}' added successfully.${os.EOL}`); + this.ui.stdout.write(`To join this network, run ${chalk.yellow('particle wifi join --ssid ')}${os.EOL}`); + this.ui.stdout.write(os.EOL); + return true; + } + throw this._handleDeviceError(error, { action: 'add Wi-Fi network' }); } async joinWifi({ ssid, password }) { - let retries = RETRY_COUNT; - const spin = this.newSpin(`Joining Wi-Fi network '${ssid}'`).start(); - let lastError; - while (retries > 0) { - try { - if (!this.device) { - throw new Error('No device found'); - } - const { pass, error } = await this.device.joinNewWifiNetwork({ ssid, password }, { timeout: JOIN_NETWORK_TIMEOUT }); - if (pass) { - this.stopSpin(); - this.ui.stdout.write('Wi-Fi network configured successfully, your device should now restart.'); - this.ui.stdout.write(os.EOL); - await this.device.reset(); - return; - } - retries = 0; - lastError = new Error(error); - } catch (error) { - spin.setSpinnerTitle(`Joining Wi-Fi network '${ssid}' is taking longer than expected.`); - lastError = error; - await utilities.delay(TIME_BETWEEN_RETRIES); - retries--; - } + let result; + await this._performWifiOperation(`Joining Wi-Fi network '${ssid}'`, async () => { + result = await this.device.joinNewWifiNetwork({ ssid, password }, { timeout: JOIN_NETWORK_TIMEOUT }); + }); + const { pass, error } = result; + if (pass) { + this.ui.stdout.write(`Wi-Fi network '${ssid}' configured and joined successfully.${os.EOL}`); + return true; } - this.stopSpin(); - throw this._handleDeviceError(lastError, { action: 'join Wi-Fi network' }); + throw this._handleDeviceError(error, { action: 'join Wi-Fi network' }); } async joinKnownWifi({ ssid }) { - let retries = RETRY_COUNT; - const spin = this.newSpin(`Joining Wi-Fi network '${ssid}'`).start(); - let lastError; - while (retries > 0) { - try { - if (!this.device) { - throw new Error('No device found'); - } - const { pass, error } = await this.device.joinKnownWifiNetwork({ ssid }, { timeout: JOIN_NETWORK_TIMEOUT }); - if (pass) { - this.stopSpin(); - this.ui.stdout.write('Wi-Fi network configured successfully, your device should now restart.'); - this.ui.stdout.write(os.EOL); - await this.device.reset(); - return; - } - retries = 0; - lastError = new Error(error); - } catch (error) { - lastError = error; - spin.setSpinnerTitle(`Joining Wi-Fi network '${ssid}' is taking longer than expected.`); - await utilities.delay(TIME_BETWEEN_RETRIES); - retries--; - } + let result; + await this._performWifiOperation(`Joining Wi-Fi network '${ssid}'`, async () => { + result = await this.device.joinKnownWifiNetwork({ ssid }, { timeout: JOIN_NETWORK_TIMEOUT }); + }); + + const { pass, error } = result; + if (pass) { + this.ui.stdout.write(`Wi-Fi network '${ssid}' joined successfully.${os.EOL}`); + await this.device.reset(); + return true; } - this.stopSpin(); - // TODO: Add a more helpful error msg. "Not found" could be either not found in the device or the network - throw this._handleDeviceError(lastError, { action: 'join Wi-Fi network' }); + throw this._handleDeviceError(error, { action: 'join known Wi-Fi network' }); } async clearWifi() { - let retries = RETRY_COUNT; - const spin = this.newSpin('Clearing Wi-Fi networks').start(); - let lastError; - while (retries > 0) { - try { - if (!this.device) { - throw new Error('No device found'); - } - const { pass, error } = await this.device.clearWifiNetworks({ timeout : JOIN_NETWORK_TIMEOUT }); - if (pass) { - this.stopSpin(); - this.ui.stdout.write('Wi-Fi networks cleared successfully.'); - this.ui.stdout.write(os.EOL); - return; - } - retries = 0; - lastError = new Error(error); - } catch (error) { - lastError = error; - spin.setSpinnerTitle('Clearing Wi-Fi networks is taking longer than expected.'); - await utilities.delay(TIME_BETWEEN_RETRIES); - retries--; - } + let result; + await this._performWifiOperation('Clearing Wi-Fi networks', async () => { + result = await this.device.clearWifiNetworks({ timeout: JOIN_NETWORK_TIMEOUT }); + }); + + const { pass, error } = result; + if (pass) { + this.ui.stdout.write(`Wi-Fi networks cleared successfully.${os.EOL}`); + return true; } - this.stopSpin(); - throw this._handleDeviceError(lastError, { action: 'clear Wi-Fi networks' }); + throw this._handleDeviceError(error, { action: 'clear Wi-Fi networks' }); } async listWifi() { - let retries = RETRY_COUNT; - const spin = this.newSpin('Listing Wi-Fi networks').start(); - let lastError; - while (retries > 0) { - try { - if (!this.device) { - throw new Error('No device found'); - } - const { pass, replyObject } = await this.device.listWifiNetworks({ timeout : JOIN_NETWORK_TIMEOUT }); - if (pass) { - this.stopSpin(); - this.ui.stdout.write(`List of Wi-Fi networks:${os.EOL}${os.EOL}`); - const networks = replyObject.networks; - if (networks.length) { - networks.forEach((network) => { - this.ui.stdout.write(`- SSID: ${network.ssid}\n Security: ${WifiSecurityEnum[network.security]}\n Credentials Type: ${network.credentialsType}`); - this.ui.stdout.write(os.EOL); - this.ui.stdout.write(os.EOL); - }); + let result, resultCurrNw; + await this._performWifiOperation('Listing Wi-Fi networks', async () => { + result = await this.device.listWifiNetworks({ timeout: JOIN_NETWORK_TIMEOUT }); + }); + try { + resultCurrNw = await this.device.getCurrentWifiNetwork({ timeout: JOIN_NETWORK_TIMEOUT }); + } catch (error) { + // Ignore error as it's not mandatory to get current network + resultCurrNw = { pass: false }; + } + + const { pass, error, replyObject } = result; + const { pass: passCurrNw, replyObject: replyObjectCurrNw } = resultCurrNw; + + if (pass) { + this.ui.stdout.write(`List of Wi-Fi networks:${os.EOL}${os.EOL}`); + const networks = replyObject.networks; + if (networks.length) { + networks.forEach((network) => { + const passwordProtected = network.credentialsType === 0 ? 'Open' : null; + const currentSsid = passCurrNw && replyObjectCurrNw ? replyObjectCurrNw.ssid : null; + const networkInfo = `- ${network.ssid} (${WifiSecurityEnum[network.security]})${passwordProtected ? `, ${passwordProtected}` : ''}`; + if (currentSsid === network.ssid) { + this.ui.stdout.write(`${networkInfo} - current network${os.EOL}`); } else { - this.ui.stdout.write('\tNo Wi-Fi networks found.'); - this.ui.stdout.write(os.EOL); + this.ui.stdout.write(`${networkInfo}${os.EOL}`); } this.ui.stdout.write(os.EOL); - return; - } - retries = 0; - lastError = new Error(error); - } catch (error) { - lastError = error; - spin.setSpinnerTitle('Listing Wi-Fi networks is taking longer than expected.'); - await utilities.delay(TIME_BETWEEN_RETRIES); - retries--; + }); } - } - this.stopSpin(); - throw this._handleDeviceError(lastError, { action: 'list Wi-Fi networks' }); + return true; + } else if (error) { + throw this._handleDeviceError(error, { action: 'list Wi-Fi networks' }); + } else { + this.ui.stdout.write('\tNo Wi-Fi networks found.'); + this.ui.stdout.write(os.EOL); + return true; + } } async removeWifi(ssid) { - let retries = RETRY_COUNT; - const spin = this.newSpin('Removing Wi-Fi networks').start(); - let lastError; - while (retries > 0) { - try { - if (!this.device) { - throw new Error('No device found'); - } - const { pass, error } = await this.device.removeWifiNetwork( { ssid }, { timeout : JOIN_NETWORK_TIMEOUT }); - if (pass) { - this.stopSpin(); - this.ui.stdout.write(`Wi-Fi network ${ssid} removed successfully.${os.EOL}`); - this.ui.stdout.write(`Your device will stay connected to this network until reset or connected to other network. Run 'particle wifi --help' to learn more.${os.EOL}`); - // XXX: What about disconnecting from the network? - this.ui.stdout.write(os.EOL); - return; + let result; + + await this._performWifiOperation('Removing Wi-Fi networks', async () => { + result = await this.device.removeWifiNetwork({ ssid }, { timeout: JOIN_NETWORK_TIMEOUT }); + }); + + const { pass, error } = result; + if (pass) { + this.ui.stdout.write(`Wi-Fi network ${ssid} removed successfully.${os.EOL}`); + this.ui.stdout.write(`Your device will stay connected to this network until reset or connected to another network. Run 'particle wifi --help' to learn more.${os.EOL}`); + this.ui.stdout.write(os.EOL); + return true; + } + throw this._handleDeviceError(error, { action: 'remove Wi-Fi network' }); + } + + async getCurrentWifiNetwork() { + let result; + + await this._performWifiOperation('Getting current Wi-Fi network', async () => { + result = await this.device.getCurrentWifiNetwork({ timeout: JOIN_NETWORK_TIMEOUT }); + }); + + const { pass, error, replyObject } = result; + + if (pass) { + this.ui.stdout.write(`Current Wi-Fi network:${os.EOL}${os.EOL}`); + if (replyObject.ssid) { + let bssid = null; + if (replyObject.bssid) { + // Convert buffer to string separated by colons + bssid = Array.from(replyObject.bssid).map((byte) => byte.toString(16).padStart(2, '0')).join(':'); } - retries = 0; - lastError = new Error(error); - } catch (error) { - lastError = error; - spin.setSpinnerTitle('Removing Wi-Fi networks is taking longer than expected.'); - await utilities.delay(TIME_BETWEEN_RETRIES); - retries--; + this.ui.stdout.write(`- SSID: ${replyObject.ssid}${os.EOL}` + + (bssid ? ` BSSID: ${bssid}${os.EOL}` : '') + + ` Channel: ${replyObject.channel}${os.EOL}` + + ` RSSI: ${replyObject.rssi}${os.EOL}${os.EOL}`); } + return true; } - this.stopSpin(); - throw this._handleDeviceError(lastError, { action: 'remove Wi-Fi networks' }); - + throw this._handleDeviceError(error, { action: 'get current Wi-Fi network' }); } - async pickNetworkManually() { + async _pickNetworkManually() { const ssid = await this._promptForSSID(); + const security = await this._promptForSecurityType(); const password = await this._promptForPassword(); - return { ssid, password }; + + return { ssid, security: WifiSecurityEnum[this._convertToKnownSecType(security)], password }; + } + + _convertToKnownSecType(security) { + // Upon observation of device-os mappings, + // the following are the known security types + // FIXME: This mapping may change as per device-os changes + if (security.startsWith('WEP')) { + return 'WEP'; + } else if (security.startsWith('WPA_WPA2')) { + return 'WPA_WPA2_PSK'; + } else if (security.startsWith('WPA2_WPA3')) { + return 'WPA2_WPA3_PSK'; + } else if (security.startsWith('WPA3')) { + return 'WPA3_PSK'; + } else if (security.startsWith('WPA2')) { + return 'WPA2_PSK'; + } else if (security.startsWith('WPA')) { + return 'WPA_PSK'; + } else { + return 'NONE'; + } } async _promptForSSID() { @@ -503,6 +506,22 @@ module.exports = class WiFiControlRequest { return ans.password; } + async _promptForSecurityType() { + // TODO: Expand the list of security types to include more relevant options + // (e.g., WPA_AES for WPA_PSK) to assist users who may not know the specific associations + const securityChoices = Object.keys(WifiSecurityEnum); + const question = [ + { + type: 'list', + name: 'security', + message: 'Select the security type for your Wi-Fi network:', + choices: securityChoices + }, + ]; + const ans = await this.ui.prompt(question); + return ans.security; + } + _serializeNetworks(networks) { return networks?.map((ap) => { return { @@ -517,6 +536,9 @@ module.exports = class WiFiControlRequest { } _handleDeviceError(_error, { action } = { }) { + if (typeof _error === 'string' && _error.startsWith('Request timed out')) { + return new Error(`Unable to ${action}: Request timed out`); + } const error = _error; if (_error.cause) { error.message = deviceControlError[error.name]; diff --git a/src/lib/wifi-control-request.test.js b/src/lib/wifi-control-request.test.js index 5342f5be5..d3d2c9f5f 100644 --- a/src/lib/wifi-control-request.test.js +++ b/src/lib/wifi-control-request.test.js @@ -82,13 +82,13 @@ describe('Wifi Control Request', () => { }); }); - describe('pickNetworkManually', () => { + describe('_pickNetworkManually', () => { it('prompts for ssid and password', async () => { const wifiControlRequest = new WifiControlRequest('deviceId', { ui, newSpin, stopSpin }); ui.prompt = sinon.stub(); ui.prompt.onCall(0).returns({ ssid: 'ssid' }).onCall(1).returns({ password: 'password' }); - const result = await wifiControlRequest.pickNetworkManually(); + const result = await wifiControlRequest._pickNetworkManually(); expect(result).to.eql({ ssid: 'ssid', password: 'password' }); expect(ui.prompt).to.have.been.calledTwice; expect(ui.prompt.firstCall).to.have.been.calledWith([{ @@ -218,7 +218,7 @@ describe('Wifi Control Request', () => { }); }); - describe('promptToSelectNetwork', () => { + describe('_promptToSelectNetwork', () => { it('prompts to select a network', async () => { const networks = [ { @@ -232,7 +232,7 @@ describe('Wifi Control Request', () => { const wifiControlRequest = new WifiControlRequest('deviceId', { ui, newSpin, stopSpin }); ui.prompt = sinon.stub(); ui.prompt.resolves({ network: 'network1' }); - const result = await wifiControlRequest.promptToSelectNetwork(networks); + const result = await wifiControlRequest._promptToSelectNetwork(networks); expect(result).to.eql({ ssid: 'network1', password: undefined }); }); @@ -250,7 +250,7 @@ describe('Wifi Control Request', () => { ui.prompt = sinon.stub(); ui.prompt.onCall(0).resolves({ network: 'network1' }); ui.prompt.onCall(1).resolves({ password: 'password' }); - const result = await wifiControlRequest.promptToSelectNetwork(networks); + const result = await wifiControlRequest._promptToSelectNetwork(networks); expect(result).to.eql({ ssid: 'network1', password: 'password' }); expect(ui.prompt).to.have.been.calledTwice; expect(ui.prompt).to.calledWithMatch([{ @@ -282,12 +282,12 @@ describe('Wifi Control Request', () => { const wifiControlRequest = new WifiControlRequest('deviceId', { ui, newSpin, stopSpin }); ui.prompt = sinon.stub(); ui.prompt.resolves({ network: '[rescan networks]' }); - const result = await wifiControlRequest.promptToSelectNetwork(networks); + const result = await wifiControlRequest._promptToSelectNetwork(networks); expect(result).to.eql({ ssid: null, rescan: true }); }); }); - describe('getNetworkToConnectFromJson', () => { + describe('_getNetworkToConnect', () => { beforeEach(async () => { await fs.ensureDir(path.join(PATH_TMP_DIR, 'networks')); }); @@ -300,7 +300,7 @@ describe('Wifi Control Request', () => { const file = path.join(PATH_TMP_DIR, 'networks', 'network.json'); fs.writeJsonSync(file, network); const wifiControlRequest = new WifiControlRequest('deviceId', { ui, newSpin, stopSpin, file }); - const result = await wifiControlRequest.getNetworkToConnectFromJson(); + const result = await wifiControlRequest._getNetworkToConnect(); expect(result).to.eql({ ssid: network.network, password: network.password }); }); it('throws error if file does not exist', async () => { @@ -308,7 +308,7 @@ describe('Wifi Control Request', () => { const wifiControlRequest = new WifiControlRequest('deviceId', { ui, newSpin, stopSpin, file: fileName }); let expectedErrorMessage = `ENOENT: no such file or directory, open '${fileName}'`; try { - await wifiControlRequest.getNetworkToConnectFromJson(); + await wifiControlRequest._getNetworkToConnect(); } catch (error) { expect(error.message).to.eql(expectedErrorMessage); } @@ -320,7 +320,7 @@ describe('Wifi Control Request', () => { fs.writeJsonSync(file, network); const wifiControlRequest = new WifiControlRequest('deviceId', { ui, newSpin, stopSpin, file }); try { - await wifiControlRequest.getNetworkToConnectFromJson(); + await wifiControlRequest._getNetworkToConnect(); } catch (_error) { error = _error; } @@ -329,7 +329,7 @@ describe('Wifi Control Request', () => { }); }); - describe('scanNetworks', () => { + describe('_scanNetworks', () => { it('returns a list of networks', async () => { const networks = [ { @@ -341,7 +341,7 @@ describe('Wifi Control Request', () => { ]; const wifiControlRequest = new WifiControlRequest('deviceId', { ui, newSpin, stopSpin }); wifiControlRequest._deviceScanNetworks = sinon.stub().resolves(networks); - const result = await wifiControlRequest.scanNetworks(); + const result = await wifiControlRequest._scanNetworks(); expect(result).to.eql(networks); }); @@ -351,7 +351,7 @@ describe('Wifi Control Request', () => { ui.prompt = sinon.stub(); ui.prompt.onCall(0).resolves({ rescan: true }); ui.prompt.onCall(1).resolves({ rescan: false }); - const result = await wifiControlRequest.scanNetworks(); + const result = await wifiControlRequest._scanNetworks(); expect(result).to.eql([]); expect(ui.prompt).to.have.been.calledWith([{ default: true, @@ -362,7 +362,7 @@ describe('Wifi Control Request', () => { }); }); - describe('getNetworkToConnect', () => { + describe('_getNetworkToConnect', () => { it('returns network from prompt', async () => { const networks = [ { @@ -374,18 +374,18 @@ describe('Wifi Control Request', () => { mac: '' }]; const wifiControlRequest = new WifiControlRequest('deviceId', { ui, newSpin, stopSpin }); - wifiControlRequest.promptForScanNetworks = sinon.stub().resolves(true); - wifiControlRequest.scanNetworks = sinon.stub().resolves(networks); - wifiControlRequest.promptToSelectNetwork = sinon.stub().resolves({ ssid: 'network1', password: 'password' }); - const result = await wifiControlRequest.getNetworkToConnect(); + wifiControlRequest._promptForScanNetworks = sinon.stub().resolves(true); + wifiControlRequest._scanNetworks = sinon.stub().resolves(networks); + wifiControlRequest._promptToSelectNetwork = sinon.stub().resolves({ ssid: 'network1', password: 'password' }); + const result = await wifiControlRequest._getNetworkToConnect(); expect(result).to.eql({ ssid: 'network1', password: 'password' }); }); it('returns network from manual input', async () => { const wifiControlRequest = new WifiControlRequest('deviceId', { ui, newSpin, stopSpin }); - wifiControlRequest.promptForScanNetworks = sinon.stub().resolves(false); - wifiControlRequest.pickNetworkManually = sinon.stub().resolves({ ssid: 'network1', password: 'password' }); - const result = await wifiControlRequest.getNetworkToConnect(); + wifiControlRequest._promptForScanNetworks = sinon.stub().resolves(false); + wifiControlRequest._pickNetworkManually = sinon.stub().resolves({ ssid: 'network1', password: 'password' }); + const result = await wifiControlRequest._getNetworkToConnect(); expect(result).to.eql({ ssid: 'network1', password: 'password' }); }); }); @@ -423,24 +423,24 @@ describe('Wifi Control Request', () => { describe('configureWifi', () => { it('performs the wifi configuration flow', async () => { const wifiControlRequest = new WifiControlRequest('deviceId', { ui, newSpin, stopSpin }); - wifiControlRequest.getNetworkToConnect = sinon.stub().resolves({ ssid: 'network1', password: 'password' }); - wifiControlRequest.getNetworkToConnectFromJson = sinon.stub(); + wifiControlRequest._getNetworkToConnect = sinon.stub().resolves({ ssid: 'network1', password: 'password' }); + wifiControlRequest._getNetworkToConnect = sinon.stub(); wifiControlRequest.joinWifi = sinon.stub().resolves(true); await wifiControlRequest.configureWifi(); - expect(wifiControlRequest.getNetworkToConnect).to.have.been.calledOnce; + expect(wifiControlRequest._getNetworkToConnect).to.have.been.calledOnce; expect(wifiControlRequest.joinWifi).to.have.been.calledOnce; - expect(wifiControlRequest.getNetworkToConnectFromJson).not.to.have.been.called; + expect(wifiControlRequest._getNetworkToConnect).not.to.have.been.called; }); it('performs the wifi configuration flow from json', async () => { const wifiControlRequest = new WifiControlRequest('deviceId', { ui, newSpin, stopSpin, file: 'file' }); - wifiControlRequest.getNetworkToConnect = sinon.stub(); - wifiControlRequest.getNetworkToConnectFromJson = sinon.stub().resolves({ ssid: 'network1', password: 'password' }); + wifiControlRequest._getNetworkToConnect = sinon.stub(); + wifiControlRequest._getNetworkToConnect = sinon.stub().resolves({ ssid: 'network1', password: 'password' }); wifiControlRequest.joinWifi = sinon.stub().resolves(true); await wifiControlRequest.configureWifi(); - expect(wifiControlRequest.getNetworkToConnect).not.to.have.been.called; + expect(wifiControlRequest._getNetworkToConnect).not.to.have.been.called; expect(wifiControlRequest.joinWifi).to.have.been.calledOnce; - expect(wifiControlRequest.getNetworkToConnectFromJson).to.have.been.calledOnce; + expect(wifiControlRequest._getNetworkToConnect).to.have.been.calledOnce; }); }); }); diff --git a/test/e2e/device.e2e.js b/test/e2e/device.e2e.js index 381ba0413..00777d428 100644 --- a/test/e2e/device.e2e.js +++ b/test/e2e/device.e2e.js @@ -6,7 +6,7 @@ const { } = require('../lib/env'); -describe('Device Commands [@device]', () => { +describe('Device Commands [@device]', () => { // [@device,wifi]??? const help = [ 'Manipulate a device', 'Usage: particle device ',