(
+ { devices: { [deviceID]: { htmode: { $set: value } } } }
+ ),
+ )}
+
+ {...props}
+ />
+
+ (
+ { devices: { [deviceID]: { channel: { $set: value } } } }
+ ),
+ )}
+
+ {...props}
+ />
+
+ {hasGuestNetwork && (
+
+ )}
+ >
+ )
+ : null}
+ >
+ );
+}
+
+function getChannelChoices(device) {
+ const channelChoices = {
+ 0: _("auto"),
+ };
+
+ device.available_bands.forEach((availableBand) => {
+ if (availableBand.hwmode !== device.hwmode) return;
+
+ availableBand.available_channels.forEach((availableChannel) => {
+ channelChoices[availableChannel.number.toString()] = `
+ ${availableChannel.number}
+ (${availableChannel.frequency} MHz ${availableChannel.radar ? " ,DFS" : ""})
+ `;
+ });
+ });
+
+ return channelChoices;
+}
+
+function getHtmodeChoices(device) {
+ const htmodeChoices = {};
+
+ device.available_bands.forEach((availableBand) => {
+ if (availableBand.hwmode !== device.hwmode) return;
+
+ availableBand.available_htmodes.forEach((availableHtmod) => {
+ htmodeChoices[availableHtmod] = HTMODES[availableHtmod];
+ });
+ });
+ return htmodeChoices;
+}
+
+function getHwmodeChoices(device) {
+ return device.available_bands.map((availableBand) => ({
+ label: HWMODES[availableBand.hwmode],
+ value: availableBand.hwmode,
+ }));
+}
diff --git a/src/common/WiFiSettings/WiFiGuestForm.js b/src/common/WiFiSettings/WiFiGuestForm.js
new file mode 100644
index 0000000..2fdabf6
--- /dev/null
+++ b/src/common/WiFiSettings/WiFiGuestForm.js
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
+ *
+ * This is free software, licensed under the GNU General Public License v3.
+ * See /LICENSE for more information.
+ */
+
+import React from "react";
+import PropTypes from "prop-types";
+
+import { CheckBox } from "../../bootstrap/CheckBox";
+import { TextInput } from "../../bootstrap/TextInput";
+import { PasswordInput } from "../../bootstrap/PasswordInput";
+import WiFiQRCode from "./WiFiQRCode";
+import { HELP_TEXTS } from "./constants";
+
+WifiGuestForm.propTypes = {
+ formData: PropTypes.shape({
+ id: PropTypes.number.isRequired,
+ SSID: PropTypes.string.isRequired,
+ password: PropTypes.string.isRequired,
+ enabled: PropTypes.bool.isRequired,
+ }),
+ formErrors: PropTypes.shape({
+ SSID: PropTypes.string,
+ password: PropTypes.string,
+ }),
+ setFormValue: PropTypes.func.isRequired,
+};
+
+export default function WifiGuestForm({
+ formData, formErrors, setFormValue, ...props
+}) {
+ return (
+ <>
+ (
+ { devices: { [formData.id]: { guest_wifi: { enabled: { $set: value } } } } }
+ ),
+ )}
+
+ {...props}
+ />
+ {formData.enabled
+ ? (
+ <>
+ ({
+ devices: {
+ [formData.id]: { guest_wifi: { SSID: { $set: value } } },
+ },
+ }),
+ )}
+
+ {...props}
+ >
+
+
+
+
+
+ ({
+ devices: {
+ [formData.id]: {
+ guest_wifi: { password: { $set: value } },
+ },
+ },
+ }),
+ )}
+
+ {...props}
+ />
+ >
+ )
+ : null}
+ >
+ );
+}
diff --git a/src/common/WiFiSettings/WiFiQRCode.js b/src/common/WiFiSettings/WiFiQRCode.js
new file mode 100644
index 0000000..ded70f7
--- /dev/null
+++ b/src/common/WiFiSettings/WiFiQRCode.js
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
+ *
+ * This is free software, licensed under the GNU General Public License v3.
+ * See /LICENSE for more information.
+ */
+
+import React, { useState } from "react";
+import QRCode from "qrcode.react";
+import PropTypes from "prop-types";
+
+import { ForisURLs } from "../../forisUrls";
+import { Button } from "../../bootstrap/Button";
+import {
+ Modal, ModalBody, ModalFooter, ModalHeader,
+} from "../../bootstrap/Modal";
+import { createAndDownloadPdf, toQRCodeContent } from "./qrCodeHelpers";
+
+WiFiQRCode.propTypes = {
+ SSID: PropTypes.string.isRequired,
+ password: PropTypes.string.isRequired,
+};
+
+const QR_ICON_PATH = `${ForisURLs.static}/imgs/QR_icon.svg`;
+
+export default function WiFiQRCode({ SSID, password }) {
+ const [modal, setModal] = useState(false);
+
+ return (
+ <>
+ {
+ e.preventDefault();
+ setModal(true);
+ }}
+ >
+
+
+ {modal
+ ?
+ : null}
+ >
+ );
+}
+
+QRCodeModal.propTypes = {
+ SSID: PropTypes.string.isRequired,
+ password: PropTypes.string.isRequired,
+ shown: PropTypes.bool.isRequired,
+ setShown: PropTypes.func.isRequired,
+};
+
+function QRCodeModal({
+ shown, setShown, SSID, password,
+}) {
+ return (
+
+
+
+
+
+
+ {
+ e.preventDefault();
+ createAndDownloadPdf(SSID, password);
+ }}
+ >
+
+ {_("Download PDF")}
+
+
+
+ );
+}
diff --git a/src/common/WiFiSettings/WiFiSettings.js b/src/common/WiFiSettings/WiFiSettings.js
new file mode 100644
index 0000000..ded5214
--- /dev/null
+++ b/src/common/WiFiSettings/WiFiSettings.js
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
+ *
+ * This is free software, licensed under the GNU General Public License v3.
+ * See /LICENSE for more information.
+ */
+
+import React from "react";
+import PropTypes from "prop-types";
+
+import { ForisForm } from "../../form/components/ForisForm";
+import WiFiForm from "./WiFiForm";
+import ResetWiFiSettings from "./ResetWiFiSettings";
+
+WiFiSettings.propTypes = {
+ ws: PropTypes.object.isRequired,
+ endpoint: PropTypes.string.isRequired,
+ resetEndpoint: PropTypes.string.isRequired,
+ hasGuestNetwork: PropTypes.bool,
+};
+
+export function WiFiSettings({
+ ws, endpoint, resetEndpoint, hasGuestNetwork,
+}) {
+ return (
+ <>
+
+
+
+
+ >
+ );
+}
+
+function prepData(formData) {
+ formData.devices.forEach((device, idx) => {
+ formData.devices[idx].channel = device.channel.toString();
+ });
+ return formData;
+}
+
+function prepDataToSubmit(formData) {
+ formData.devices.forEach((device, idx) => {
+ delete device.available_bands;
+
+ formData.devices[idx].channel = parseInt(device.channel);
+
+ if (!device.enabled) {
+ formData.devices[idx] = { id: device.id, enabled: false };
+ return;
+ }
+
+ if (!device.guest_wifi.enabled) formData.devices[idx].guest_wifi = { enabled: false };
+ });
+ return formData;
+}
+
+function validator(formData) {
+ const formErrors = formData.devices.map(
+ (device) => {
+ if (!device.enabled) return {};
+
+ const errors = {};
+ if (device.SSID.length > 32) errors.SSID = _("SSID can't be longer than 32 symbols");
+ if (device.SSID.length === 0) errors.SSID = _("SSID can't be empty");
+
+ if (device.password.length < 8) errors.password = _("Password must contain at least 8 symbols");
+
+ if (!device.guest_wifi.enabled) return errors;
+
+ const guest_wifi_errors = {};
+ if (device.guest_wifi.SSID.length > 32) guest_wifi_errors.SSID = _("SSID can't be longer than 32 symbols");
+ if (device.guest_wifi.SSID.length === 0) guest_wifi_errors.SSID = _("SSID can't be empty");
+
+ if (device.guest_wifi.password.length < 8) guest_wifi_errors.password = _("Password must contain at least 8 symbols");
+
+ if (guest_wifi_errors.SSID || guest_wifi_errors.password) {
+ errors.guest_wifi = guest_wifi_errors;
+ }
+ return errors;
+ },
+ );
+ return JSON.stringify(formErrors) === "[{},{}]" ? null : formErrors;
+}
diff --git a/src/common/WiFiSettings/__tests__/ResetWiFiSettings.test.js b/src/common/WiFiSettings/__tests__/ResetWiFiSettings.test.js
new file mode 100644
index 0000000..6b5f9ed
--- /dev/null
+++ b/src/common/WiFiSettings/__tests__/ResetWiFiSettings.test.js
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
+ *
+ * This is free software, licensed under the GNU General Public License v3.
+ * See /LICENSE for more information.
+ */
+
+import React from "react";
+import { render, fireEvent, wait } from "customTestRender";
+
+import mockAxios from "jest-mock-axios";
+import { WebSockets } from "webSockets/WebSockets";
+import { mockJSONError } from "testUtils/network";
+import { mockSetAlert } from "testUtils/alertContextMock";
+import { ALERT_TYPES } from "../../../bootstrap/Alert";
+
+import ResetWiFiSettings from "../ResetWiFiSettings";
+
+describe(" ", () => {
+ const webSockets = new WebSockets();
+ const endpoint = "/reforis/api/wifi-reset";
+ let getAllByText;
+
+ beforeEach(() => {
+ ({ getAllByText } = render( ));
+ });
+
+ it("should display alert on open ports - success", async () => {
+ fireEvent.click(getAllByText("Reset Wi-Fi Settings")[1]);
+ expect(mockAxios.post).toBeCalledWith(endpoint, undefined, expect.anything());
+ mockAxios.mockResponse({ data: { foo: "bar" } });
+ await wait(() => expect(mockSetAlert).toBeCalledWith("Wi-Fi settings are set to defaults.", ALERT_TYPES.SUCCESS));
+ });
+
+ it("should display alert on open ports - failure", async () => {
+ fireEvent.click(getAllByText("Reset Wi-Fi Settings")[1]);
+ mockJSONError();
+ await wait(() => expect(mockSetAlert).toBeCalledWith("An error occurred during resetting Wi-Fi settings."));
+ });
+});
diff --git a/src/common/WiFiSettings/__tests__/WiFiSettings.test.js b/src/common/WiFiSettings/__tests__/WiFiSettings.test.js
new file mode 100644
index 0000000..cb71edf
--- /dev/null
+++ b/src/common/WiFiSettings/__tests__/WiFiSettings.test.js
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
+ *
+ * This is free software, licensed under the GNU General Public License v3.
+ * See /LICENSE for more information.
+ */
+
+import React from "react";
+import diffSnapshot from "snapshot-diff";
+import mockAxios from "jest-mock-axios";
+
+import { fireEvent, render, wait } from "customTestRender";
+import { WebSockets } from "webSockets/WebSockets";
+import { mockJSONError } from "testUtils/network";
+
+import { wifiSettingsFixture } from "./__fixtures__/wifiSettings";
+import { WiFiSettings } from "../WiFiSettings";
+
+describe(" ", () => {
+ let firstRender;
+ let getAllByText;
+ let getAllByLabelText;
+ let getByText;
+ let asFragment;
+ const endpoint = "/reforis/api/wifi";
+
+ beforeEach(async () => {
+ const webSockets = new WebSockets();
+ const renderRes = render( );
+ asFragment = renderRes.asFragment;
+ getAllByText = renderRes.getAllByText;
+ getAllByLabelText = renderRes.getAllByLabelText;
+ getByText = renderRes.getByText;
+ mockAxios.mockResponse({ data: wifiSettingsFixture() });
+ await wait(() => renderRes.getByText("Wi-Fi 1"));
+ firstRender = renderRes.asFragment();
+ });
+
+ it("should handle error", async () => {
+ const webSockets = new WebSockets();
+ const { getByText } = render( );
+ mockJSONError();
+ await wait(() => {
+ expect(getByText("An error occurred while fetching data.")).toBeTruthy();
+ });
+ });
+
+ it("Snapshot both modules disabled.", () => {
+ expect(firstRender).toMatchSnapshot();
+ });
+
+ it("Snapshot one module enabled.", () => {
+ fireEvent.click(getAllByText("Enable")[0]);
+ expect(diffSnapshot(firstRender, asFragment())).toMatchSnapshot();
+ });
+
+ it("Snapshot 2.4 GHz", () => {
+ fireEvent.click(getAllByText("Enable")[0]);
+ const enabledRender = asFragment();
+ fireEvent.click(getAllByText("2.4")[0]);
+ expect(diffSnapshot(enabledRender, asFragment())).toMatchSnapshot();
+ });
+
+ it("Snapshot guest network.", () => {
+ fireEvent.click(getAllByText("Enable")[0]);
+ const enabledRender = asFragment();
+ fireEvent.click(getAllByText("Enable Guest Wifi")[0]);
+ expect(diffSnapshot(enabledRender, asFragment())).toMatchSnapshot();
+ });
+
+ it("Post form: both modules disabled.", () => {
+ fireEvent.click(getByText("Save"));
+ expect(mockAxios.post).toBeCalled();
+ const data = {
+ devices: [
+ { enabled: false, id: 0 },
+ { enabled: false, id: 1 },
+ ],
+ };
+ expect(mockAxios.post).toHaveBeenCalledWith(endpoint, data, expect.anything());
+ });
+
+ it("Post form: one module enabled.", () => {
+ fireEvent.click(getAllByText("Enable")[0]);
+
+ fireEvent.click(getByText("Save"));
+ expect(mockAxios.post).toBeCalled();
+ const data = {
+ devices: [
+ {
+ SSID: "TestSSID1",
+ channel: 60,
+ enabled: true,
+ guest_wifi: { enabled: false },
+ hidden: false,
+ htmode: "HT40",
+ hwmode: "11a",
+ id: 0,
+ password: "TestPass",
+ },
+ { enabled: false, id: 1 },
+ ],
+ };
+ expect(mockAxios.post).toHaveBeenCalledWith(endpoint, data, expect.anything());
+ });
+
+ it("Post form: 2.4 GHz", () => {
+ fireEvent.click(getAllByText("Enable")[0]);
+ fireEvent.click(getAllByText("2.4")[0]);
+
+ fireEvent.click(getByText("Save"));
+ expect(mockAxios.post).toBeCalled();
+ const data = {
+ devices: [
+ {
+ SSID: "TestSSID1",
+ channel: 0,
+ enabled: true,
+ guest_wifi: { enabled: false },
+ hidden: false,
+ htmode: "HT40",
+ hwmode: "11g",
+ id: 0,
+ password: "TestPass",
+ },
+ { enabled: false, id: 1 },
+ ],
+ };
+ expect(mockAxios.post).toHaveBeenCalledWith(endpoint, data, expect.anything());
+ });
+
+ it("Post form: guest network.", () => {
+ fireEvent.click(getAllByText("Enable")[0]);
+ fireEvent.click(getAllByText("Enable Guest Wifi")[0]);
+ fireEvent.change(getAllByLabelText("Password")[1], { target: { value: "test_password" } });
+
+ fireEvent.click(getByText("Save"));
+ expect(mockAxios.post).toBeCalled();
+ const data = {
+ devices: [
+ {
+ SSID: "TestSSID1",
+ channel: 60,
+ enabled: true,
+ guest_wifi: {
+ SSID: "TestGuestSSID",
+ enabled: true,
+ password: "test_password",
+ },
+ hidden: false,
+ htmode: "HT40",
+ hwmode: "11a",
+ id: 0,
+ password: "TestPass",
+ },
+ { enabled: false, id: 1 },
+ ],
+ };
+ expect(mockAxios.post).toHaveBeenCalledWith(endpoint, data, expect.anything());
+ });
+});
diff --git a/src/common/WiFiSettings/__tests__/__fixtures__/wifiSettings.js b/src/common/WiFiSettings/__tests__/__fixtures__/wifiSettings.js
new file mode 100644
index 0000000..71c8eb2
--- /dev/null
+++ b/src/common/WiFiSettings/__tests__/__fixtures__/wifiSettings.js
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2019 CZ.NIC z.s.p.o. (http://www.nic.cz/)
+ *
+ * This is free software, licensed under the GNU General Public License v3.
+ * See /LICENSE for more information.
+ */
+
+export function wifiSettingsFixture() {
+ return {
+ devices: [
+ {
+ SSID: "TestSSID1",
+ available_bands: [
+ {
+ available_channels: [
+ {
+ frequency: 2412,
+ number: 1,
+ radar: false,
+ },
+ {
+ frequency: 2417,
+ number: 2,
+ radar: false,
+ },
+ {
+ frequency: 2422,
+ number: 3,
+ radar: false,
+ },
+ {
+ frequency: 2427,
+ number: 4,
+ radar: false,
+ },
+ {
+ frequency: 2432,
+ number: 5,
+ radar: false,
+ },
+ {
+ frequency: 2437,
+ number: 6,
+ radar: false,
+ },
+ {
+ frequency: 2442,
+ number: 7,
+ radar: false,
+ },
+ {
+ frequency: 2447,
+ number: 8,
+ radar: false,
+ },
+ {
+ frequency: 2452,
+ number: 9,
+ radar: false,
+ },
+ {
+ frequency: 2457,
+ number: 10,
+ radar: false,
+ },
+ {
+ frequency: 2462,
+ number: 11,
+ radar: false,
+ },
+ ],
+ available_htmodes: [
+ "NOHT",
+ "HT20",
+ "HT40",
+ "VHT20",
+ "VHT40",
+ "VHT80",
+ ],
+ hwmode: "11g",
+ },
+ {
+ available_channels: [
+ {
+ frequency: 5180,
+ number: 36,
+ radar: false,
+ },
+ {
+ frequency: 5200,
+ number: 40,
+ radar: false,
+ },
+ {
+ frequency: 5220,
+ number: 44,
+ radar: false,
+ },
+ {
+ frequency: 5240,
+ number: 48,
+ radar: false,
+ },
+ {
+ frequency: 5260,
+ number: 52,
+ radar: true,
+ },
+ {
+ frequency: 5280,
+ number: 56,
+ radar: true,
+ },
+ {
+ frequency: 5300,
+ number: 60,
+ radar: true,
+ },
+ {
+ frequency: 5320,
+ number: 64,
+ radar: true,
+ },
+ {
+ frequency: 5500,
+ number: 100,
+ radar: true,
+ },
+ {
+ frequency: 5520,
+ number: 104,
+ radar: true,
+ },
+ {
+ frequency: 5540,
+ number: 108,
+ radar: true,
+ },
+ {
+ frequency: 5560,
+ number: 112,
+ radar: true,
+ },
+ {
+ frequency: 5580,
+ number: 116,
+ radar: true,
+ },
+ {
+ frequency: 5600,
+ number: 120,
+ radar: true,
+ },
+ {
+ frequency: 5620,
+ number: 124,
+ radar: true,
+ },
+ {
+ frequency: 5640,
+ number: 128,
+ radar: true,
+ },
+ {
+ frequency: 5660,
+ number: 132,
+ radar: true,
+ },
+ {
+ frequency: 5680,
+ number: 136,
+ radar: true,
+ },
+ {
+ frequency: 5700,
+ number: 140,
+ radar: true,
+ },
+ {
+ frequency: 5720,
+ number: 144,
+ radar: true,
+ },
+ {
+ frequency: 5745,
+ number: 149,
+ radar: false,
+ },
+ {
+ frequency: 5765,
+ number: 153,
+ radar: false,
+ },
+ {
+ frequency: 5785,
+ number: 157,
+ radar: false,
+ },
+ {
+ frequency: 5805,
+ number: 161,
+ radar: false,
+ },
+ {
+ frequency: 5825,
+ number: 165,
+ radar: false,
+ },
+ ],
+ available_htmodes: [
+ "NOHT",
+ "HT20",
+ "HT40",
+ "VHT20",
+ "VHT40",
+ "VHT80",
+ ],
+ hwmode: "11a",
+ },
+ ],
+ channel: 60,
+ enabled: false,
+ guest_wifi: {
+ SSID: "TestGuestSSID",
+ enabled: false,
+ password: "",
+ },
+ hidden: false,
+ htmode: "HT40",
+ hwmode: "11a",
+ id: 0,
+ password: "TestPass",
+ },
+ {
+ SSID: "Turris",
+ available_bands: [
+ {
+ available_channels: [
+ {
+ frequency: 2412,
+ number: 1,
+ radar: false,
+ },
+ {
+ frequency: 2417,
+ number: 2,
+ radar: false,
+ },
+ {
+ frequency: 2422,
+ number: 3,
+ radar: false,
+ },
+ {
+ frequency: 2427,
+ number: 4,
+ radar: false,
+ },
+ {
+ frequency: 2432,
+ number: 5,
+ radar: false,
+ },
+ {
+ frequency: 2437,
+ number: 6,
+ radar: false,
+ },
+ {
+ frequency: 2442,
+ number: 7,
+ radar: false,
+ },
+ {
+ frequency: 2447,
+ number: 8,
+ radar: false,
+ },
+ {
+ frequency: 2452,
+ number: 9,
+ radar: false,
+ },
+ {
+ frequency: 2457,
+ number: 10,
+ radar: false,
+ },
+ {
+ frequency: 2462,
+ number: 11,
+ radar: false,
+ },
+ ],
+ available_htmodes: [
+ "NOHT",
+ "HT20",
+ "HT40",
+ ],
+ hwmode: "11g",
+ },
+ ],
+ channel: 11,
+ enabled: false,
+ guest_wifi: {
+ SSID: "TestSSID",
+ enabled: false,
+ password: "",
+ },
+ hidden: false,
+ htmode: "HT40",
+ hwmode: "11g",
+ id: 1,
+ password: "TestPass",
+ },
+ ],
+ };
+}
diff --git a/src/common/WiFiSettings/__tests__/__snapshots__/WiFiSettings.test.js.snap b/src/common/WiFiSettings/__tests__/__snapshots__/WiFiSettings.test.js.snap
new file mode 100644
index 0000000..b75bb05
--- /dev/null
+++ b/src/common/WiFiSettings/__tests__/__snapshots__/WiFiSettings.test.js.snap
@@ -0,0 +1,912 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` Snapshot 2.4 GHz 1`] = `
+"Snapshot Diff:
+- First value
++ Second value
+
+@@ -246,207 +246,95 @@
+ value=\\"0\\"
+ >
+ auto
+
+
+
+- 36
+- (5180 MHz )
++ 1
++ (2412 MHz )
+
+
+
+
+- 40
+- (5200 MHz )
++ 2
++ (2417 MHz )
+
+
+
+
+- 44
+- (5220 MHz )
++ 3
++ (2422 MHz )
+
+
+
+-
+- 48
+- (5240 MHz )
+-
+-
+-
+-
+- 52
+- (5260 MHz ,DFS)
+-
+-
+-
+-
+- 56
+- (5280 MHz ,DFS)
+-
+-
+-
+
+- 60
+- (5300 MHz ,DFS)
++ 4
++ (2427 MHz )
+
+
+
+
+- 64
+- (5320 MHz ,DFS)
++ 5
++ (2432 MHz )
+
+
+
+
+- 100
+- (5500 MHz ,DFS)
++ 6
++ (2437 MHz )
+
+
+
+
+- 104
+- (5520 MHz ,DFS)
++ 7
++ (2442 MHz )
+
+
+
+
+- 108
+- (5540 MHz ,DFS)
++ 8
++ (2447 MHz )
+
+
+
+
+- 112
+- (5560 MHz ,DFS)
++ 9
++ (2452 MHz )
+
+
+
+
+- 116
+- (5580 MHz ,DFS)
++ 10
++ (2457 MHz )
+
+
+
+
+- 120
+- (5600 MHz ,DFS)
+-
+-
+-
+-
+- 124
+- (5620 MHz ,DFS)
+-
+-
+-
+-
+- 128
+- (5640 MHz ,DFS)
+-
+-
+-
+-
+- 132
+- (5660 MHz ,DFS)
+-
+-
+-
+-
+- 136
+- (5680 MHz ,DFS)
+-
+-
+-
+-
+- 140
+- (5700 MHz ,DFS)
+-
+-
+-
+-
+- 144
+- (5720 MHz ,DFS)
+-
+-
+-
+-
+- 149
+- (5745 MHz )
+-
+-
+-
+-
+- 153
+- (5765 MHz )
+-
+-
+-
+-
+- 157
+- (5785 MHz )
+-
+-
+-
+-
+- 161
+- (5805 MHz )
+-
+-
+-
+-
+- 165
+- (5825 MHz )
++ 11
++ (2462 MHz )
+
+
+
+
+
Snapshot both modules disabled. 1`] = `
+
+
+
+ Reset Wi-Fi Settings
+
+
+
+If a number of wireless cards doesn't match, you may try to reset the Wi-Fi settings. Note that this will remove the
+current Wi-Fi configuration and restore the default values.
+
+
+
+
+ Reset Wi-Fi Settings
+
+
+
+`;
+
+exports[` Snapshot guest network. 1`] = `
+"Snapshot Diff:
+- First value
++ Second value
+
+@@ -475,10 +475,89 @@
+
+
+
+
+
++
++
+
+ Wi-Fi 2
+
+
+
+ Save
+
+
"
+`;
+
+exports[` Snapshot one module enabled. 1`] = `
+"Snapshot Diff:
+- First value
++ Second value
+
+@@ -23,10 +23,462 @@
+ >
+ Enable
+
+
+
++
++
++
++
++
++
++ 802.11n/ac mode
++
++
++
++ Disabled
++
++
++ 802.11n - 20 MHz wide channel
++
++
++ 802.11n - 40 MHz wide channel
++
++
++ 802.11ac - 20 MHz wide channel
++
++
++ 802.11ac - 40 MHz wide channel
++
++
++ 802.11ac - 80 MHz wide channel
++
++
++
++
++ Change this to adjust 802.11n/ac mode of operation. 802.11n with 40 MHz wide channels can yield higher
++ throughput but can cause more interference in the network. If you don't know what to choose, use the default
++ option with 20 MHz wide channel.
++
++
++
++
++
++ Channel
++
++
++
++ auto
++
++
++
++ 36
++ (5180 MHz )
++
++
++
++
++ 40
++ (5200 MHz )
++
++
++
++
++ 44
++ (5220 MHz )
++
++
++
++
++ 48
++ (5240 MHz )
++
++
++
++
++ 52
++ (5260 MHz ,DFS)
++
++
++
++
++ 56
++ (5280 MHz ,DFS)
++
++
++
++
++ 60
++ (5300 MHz ,DFS)
++
++
++
++
++ 64
++ (5320 MHz ,DFS)
++
++
++
++
++ 100
++ (5500 MHz ,DFS)
++
++
++
++
++ 104
++ (5520 MHz ,DFS)
++
++
++
++
++ 108
++ (5540 MHz ,DFS)
++
++
++
++
++ 112
++ (5560 MHz ,DFS)
++
++
++
++
++ 116
++ (5580 MHz ,DFS)
++
++
++
++
++ 120
++ (5600 MHz ,DFS)
++
++
++
++
++ 124
++ (5620 MHz ,DFS)
++
++
++
++
++ 128
++ (5640 MHz ,DFS)
++
++
++
++
++ 132
++ (5660 MHz ,DFS)
++
++
++
++
++ 136
++ (5680 MHz ,DFS)
++
++
++
++
++ 140
++ (5700 MHz ,DFS)
++
++
++
++
++ 144
++ (5720 MHz ,DFS)
++
++
++
++
++ 149
++ (5745 MHz )
++
++
++
++
++ 153
++ (5765 MHz )
++
++
++
++
++ 157
++ (5785 MHz )
++
++
++
++
++ 161
++ (5805 MHz )
++
++
++
++
++ 165
++ (5825 MHz )
++
++
++
++
++
+
+ Wi-Fi 2
+
+
", () => {
let componentContainer;
beforeEach(() => {
const { container } = render(<>
-
-
+
+
>);
componentContainer = container;
});
@@ -51,5 +53,4 @@ describe(" ", () => {
await wait(() => expect(mockSetAlert)
.toBeCalledWith("Reboot request failed."));
});
-
});
diff --git a/src/form/__tests__/SubmitButton.test.js b/src/form/__tests__/SubmitButton.test.js
index c121809..593d92e 100644
--- a/src/form/__tests__/SubmitButton.test.js
+++ b/src/form/__tests__/SubmitButton.test.js
@@ -12,17 +12,17 @@ import { STATES, SubmitButton } from "../components/SubmitButton";
describe(" ", () => {
it("Render ready", () => {
- const { container } = render();
+ const { container } = render( );
expect(container)
.toMatchSnapshot();
});
it("Render saving", () => {
- const { container } = render();
+ const { container } = render( );
expect(container)
.toMatchSnapshot();
});
it("Render load", () => {
- const { container } = render();
+ const { container } = render( );
expect(container)
.toMatchSnapshot();
});
diff --git a/src/form/__tests__/hooks.test.js b/src/form/__tests__/hooks.test.js
index 0c535a6..8f69adb 100644
--- a/src/form/__tests__/hooks.test.js
+++ b/src/form/__tests__/hooks.test.js
@@ -5,99 +5,98 @@
* See /LICENSE for more information.
*/
-import React from 'react';
+import React from "react";
-import { act, fireEvent, render, waitForElement } from 'customTestRender';
-import mockAxios from 'jest-mock-axios';
-import { ForisForm } from "../components/ForisForm";
+import {
+ act, fireEvent, render, waitForElement,
+} from "customTestRender";
+import mockAxios from "jest-mock-axios";
import { WebSockets } from "webSockets/WebSockets";
-
+import { ForisForm } from "../components/ForisForm";
// It's possible to unittest each hooks via react-hooks-testing-library.
// But it's better and easier to test it by test components which uses this hooks.
-const TestForm = ({formData, formErrors, setFormValue}) => {
- return <>
+const TestForm = ({ formData, formErrors, setFormValue }) => (
+ <>
({field: {$set: value}}))}
+ onChange={setFormValue((value) => ({ field: { $set: value } }))}
/>
{formErrors.field}
>
-};
+);
-describe('useForm hook.', () => {
+describe("useForm hook.", () => {
let mockValidator;
let mockPrepData;
let mockPrepDataToSubmit;
let input;
let form;
- const Child = jest.fn(props => );
+ const Child = jest.fn((props) => );
beforeEach(async () => {
- mockPrepData = jest.fn(() => ({field: 'preparedData'}));
- mockPrepDataToSubmit = jest.fn(() => ({field: 'preparedDataToSubmit'}));
- mockValidator = jest.fn(data => data.field === 'invalidValue' ? {field: 'Error'} : {});
- const {getByTestId, container} = render(
+ mockPrepData = jest.fn(() => ({ field: "preparedData" }));
+ mockPrepDataToSubmit = jest.fn(() => ({ field: "preparedDataToSubmit" }));
+ mockValidator = jest.fn((data) => (data.field === "invalidValue" ? { field: "Error" } : {}));
+ const { getByTestId, container } = render(
-
-
+
+ ,
);
- mockAxios.mockResponse({field: 'fetchedData'});
+ mockAxios.mockResponse({ field: "fetchedData" });
- input = await waitForElement(() =>
- getByTestId('test-input')
- );
+ input = await waitForElement(() => getByTestId("test-input"));
form = container.firstChild.firstChild;
});
- it('Validation on changing.', () => {
+ it("Validation on changing.", () => {
expect(mockValidator).toHaveBeenCalledTimes(1);
expect(Child).toHaveBeenCalledTimes(1);
expect(Child.mock.calls[0][0].formErrors).toMatchObject({});
act(() => {
- fireEvent.change(input, {target: {value: 'invalidValue', type: 'text'}});
+ fireEvent.change(input, { target: { value: "invalidValue", type: "text" } });
});
expect(Child).toHaveBeenCalledTimes(2);
expect(mockValidator).toHaveBeenCalledTimes(2);
- expect(Child.mock.calls[1][0].formErrors).toMatchObject({field: 'Error'});
+ expect(Child.mock.calls[1][0].formErrors).toMatchObject({ field: "Error" });
});
- it('Update text value.', () => {
- fireEvent.change(input, {target: {value: 'newValue', type: 'text'}})
- expect(input.value).toBe('newValue');
+ it("Update text value.", () => {
+ fireEvent.change(input, { target: { value: "newValue", type: "text" } });
+ expect(input.value).toBe("newValue");
});
- it('Update text value.', () => {
- fireEvent.change(input, {target: {value: 123, type: 'number'}})
- expect(input.value).toBe('123');
+ it("Update text value.", () => {
+ fireEvent.change(input, { target: { value: 123, type: "number" } });
+ expect(input.value).toBe("123");
});
- it('Update checkbox value.', () => {
- fireEvent.change(input, {target: {checked: true, type: 'checkbox'}})
+ it("Update checkbox value.", () => {
+ fireEvent.change(input, { target: { checked: true, type: "checkbox" } });
expect(input.checked).toBe(true);
});
- it('Fetch data.', () => {
- expect(mockAxios.get).toHaveBeenCalledWith('testEndpoint', expect.anything());
+ it("Fetch data.", () => {
+ expect(mockAxios.get).toHaveBeenCalledWith("testEndpoint", expect.anything());
expect(mockPrepData).toHaveBeenCalledTimes(1);
- expect(Child.mock.calls[0][0].formData).toMatchObject({field: 'preparedData'});
+ expect(Child.mock.calls[0][0].formData).toMatchObject({ field: "preparedData" });
});
- it('Submit.', () => {
+ it("Submit.", () => {
expect(mockAxios.get).toHaveBeenCalledTimes(1);
expect(mockPrepDataToSubmit).toHaveBeenCalledTimes(0);
@@ -106,8 +105,8 @@ describe('useForm hook.', () => {
expect(mockPrepDataToSubmit).toHaveBeenCalledTimes(1);
expect(mockAxios.post).toHaveBeenCalledTimes(1);
expect(mockAxios.post).toHaveBeenCalledWith(
- 'testEndpoint',
- {'field': 'preparedDataToSubmit'},
+ "testEndpoint",
+ { field: "preparedDataToSubmit" },
expect.anything(),
);
});
diff --git a/src/form/__tests__/validation.test.js b/src/form/__tests__/validation.test.js
index 844cf52..4a4d3f3 100644
--- a/src/form/__tests__/validation.test.js
+++ b/src/form/__tests__/validation.test.js
@@ -11,7 +11,7 @@ import {
validateIPv4Address,
validateIPv6Address,
validateIPv6Prefix,
- validateMAC
+ validateMAC,
} from "validations";
describe("Validation functions", () => {
@@ -50,7 +50,6 @@ describe("Validation functions", () => {
.toBe(undefined);
expect(validateIPv6Address("::"))
.toBe(undefined);
-
});
it("validateIPv6Address invalid", () => {
expect(validateIPv6Address("invalid"))
@@ -85,7 +84,6 @@ describe("Validation functions", () => {
.toBe(undefined);
});
-
it("validateDomain valid", () => {
expect(validateDomain("example.com"))
.toBe(undefined);
@@ -108,7 +106,6 @@ describe("Validation functions", () => {
.toBe(undefined);
expect(validateDUID("ABCDEF12AB"))
.toBe(undefined);
-
});
it("validateDUID invalid", () => {
expect(validateDUID("gggggggg"))
diff --git a/src/index.js b/src/index.js
index 2ad5a95..afe60fa 100644
--- a/src/index.js
+++ b/src/index.js
@@ -45,6 +45,7 @@ export {
// Common
export { RebootButton } from "./common/RebootButton";
+export { WiFiSettings } from "./common/WiFiSettings/WiFiSettings";
// Form
export { ForisForm } from "./form/components/ForisForm";
diff --git a/src/testUtils/alertContextMock.js b/src/testUtils/alertContextMock.js
index d93338b..b1f6b81 100644
--- a/src/testUtils/alertContextMock.js
+++ b/src/testUtils/alertContextMock.js
@@ -15,7 +15,7 @@ window.AlertContext = React.createContext();
function AlertContextMock({ children }) {
return (
- { children }
+ { children }
);
}
diff --git a/src/testUtils/network.js b/src/testUtils/network.js
index 05efa59..b7473d3 100644
--- a/src/testUtils/network.js
+++ b/src/testUtils/network.js
@@ -5,7 +5,7 @@
* See /LICENSE for more information.
*/
-import mockAxios from 'jest-mock-axios';
+import mockAxios from "jest-mock-axios";
export function mockJSONError(data) {
mockAxios.mockError({ response: { data, headers: { "content-type": "application/json" } } });
diff --git a/src/testUtils/setup.js b/src/testUtils/setup.js
index 5c9ca17..5aac2eb 100644
--- a/src/testUtils/setup.js
+++ b/src/testUtils/setup.js
@@ -5,8 +5,8 @@
* See /LICENSE for more information.
*/
-import mockAxios from 'jest-mock-axios';
-import moment from 'moment-timezone';
+import mockAxios from "jest-mock-axios";
+import moment from "moment-timezone";
// Setup axios cleanup
global.afterEach(() => {
@@ -14,9 +14,9 @@ global.afterEach(() => {
});
// Mock babel (gettext)
-global._ = str => str;
-global.ngettext = str => str;
-global.babel = {format: (str) => str};
+global._ = (str) => str;
+global.ngettext = (str) => str;
+global.babel = { format: (str) => str };
global.ForisTranslations = {};
// Mock web sockets
@@ -25,9 +25,9 @@ window.WebSocket = jest.fn();
// Mock scrollIntoView
global.HTMLElement.prototype.scrollIntoView = () => {};
-jest.doMock('moment', () => {
- moment.tz.setDefault('UTC');
+// Mock timezone utilities
+jest.doMock("moment", () => {
+ moment.tz.setDefault("UTC");
return moment;
});
-
Date.now = jest.fn(() => new Date(Date.UTC(2019, 1, 1, 12, 13, 14)).valueOf());
diff --git a/src/utils/__tests__/conditionalHOCs.test.js b/src/utils/__tests__/conditionalHOCs.test.js
index eb8985e..eab07d1 100644
--- a/src/utils/__tests__/conditionalHOCs.test.js
+++ b/src/utils/__tests__/conditionalHOCs.test.js
@@ -7,10 +7,10 @@
import React from "react";
import { render } from "customTestRender";
+import { API_STATE } from "api/utils";
import {
withEither, withSpinner, withSending, withSpinnerOnSending, withError, withErrorMessage,
} from "../conditionalHOCs";
-import { API_STATE } from "api/utils";
describe("conditional HOCs", () => {
const First = () => First
;