add force unlock config
parent
cb44640ca5
commit
412a46415c
|
@ -0,0 +1,9 @@
|
|||
class AddDisableEmergencyUnlockConfirmationToWebAppConfig < ActiveRecord::Migration[5.2]
|
||||
safety_assured
|
||||
def change
|
||||
add_column :web_app_configs,
|
||||
:disable_emergency_unlock_confirmation,
|
||||
:boolean,
|
||||
default: false
|
||||
end
|
||||
end
|
|
@ -310,6 +310,7 @@ export function fakeWebAppConfig(): TaggedWebAppConfig {
|
|||
show_historic_points: false,
|
||||
time_format_24_hour: false,
|
||||
show_pins: false,
|
||||
disable_emergency_unlock_confirmation: false,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ describe("fetchLabFeatures", () => {
|
|||
Object.defineProperty(window.location, "reload", { value: jest.fn() });
|
||||
it("basically just initializes stuff", () => {
|
||||
const val = fetchLabFeatures(jest.fn());
|
||||
expect(val.length).toBe(9);
|
||||
expect(val.length).toBe(10);
|
||||
expect(val[0].value).toBeFalsy();
|
||||
const { callback } = val[0];
|
||||
if (callback) {
|
||||
|
|
|
@ -82,7 +82,15 @@ export const fetchLabFeatures =
|
|||
description: t(Content.TIME_FORMAT_24_HOUR),
|
||||
storageKey: BooleanSetting.time_format_24_hour,
|
||||
value: false,
|
||||
}
|
||||
},
|
||||
{
|
||||
name: t("Confirm emergency unlock"),
|
||||
description: t(Content.EMERGENCY_UNLOCK_CONFIRM_CONFIG),
|
||||
confirmationMessage: t(Content.CONFIRM_EMERGENCY_UNLOCK_CONFIRM_DISABLE),
|
||||
storageKey: BooleanSetting.disable_emergency_unlock_confirmation,
|
||||
value: false,
|
||||
displayInvert: true,
|
||||
},
|
||||
].map(fetchSettingValue(getConfigValue)));
|
||||
|
||||
/** Always allow toggling from true => false (deactivate).
|
||||
|
|
|
@ -464,6 +464,15 @@ export namespace Content {
|
|||
export const SHOW_PINS =
|
||||
trim(`Show raw pin lists in Read Sensor and Control Peripheral steps.`);
|
||||
|
||||
export const EMERGENCY_UNLOCK_CONFIRM_CONFIG =
|
||||
trim(`Confirm when unlocking FarmBot after an emergency stop.`);
|
||||
|
||||
export const CONFIRM_EMERGENCY_UNLOCK_CONFIRM_DISABLE =
|
||||
trim(`Warning! When disabled, clicking the UNLOCK button will immediately
|
||||
unlock FarmBot instead of confirming that it is safe to do so.
|
||||
As a result, double-clicking the E-STOP button may not stop FarmBot.
|
||||
Are you sure you want to disable this feature?`);
|
||||
|
||||
// Device
|
||||
export const NOT_HTTPS =
|
||||
trim(`WARNING: Sending passwords via HTTP:// is not secure.`);
|
||||
|
|
|
@ -37,7 +37,10 @@ export class Move extends React.Component<MoveProps, {}> {
|
|||
toggle={this.toggle}
|
||||
getValue={this.getValue} />
|
||||
</Popover>
|
||||
<EStopButton bot={this.props.bot} />
|
||||
<EStopButton
|
||||
bot={this.props.bot}
|
||||
forceUnlock={this.getValue(
|
||||
BooleanSetting.disable_emergency_unlock_confirmation)} />
|
||||
</WidgetHeader>
|
||||
<WidgetBody>
|
||||
<MustBeOnline
|
||||
|
|
|
@ -116,9 +116,9 @@ export function emergencyLock() {
|
|||
.then(commandOK(noun), commandErr(noun));
|
||||
}
|
||||
|
||||
export function emergencyUnlock() {
|
||||
export function emergencyUnlock(force = false) {
|
||||
const noun = "Emergency unlock";
|
||||
if (confirm(t(`Are you sure you want to unlock the device?`))) {
|
||||
if (force || confirm(t(`Are you sure you want to unlock the device?`))) {
|
||||
getDevice()
|
||||
.emergencyUnlock()
|
||||
.then(commandOK(noun), commandErr(noun));
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
const mockDevice = { emergencyUnlock: jest.fn(() => Promise.resolve()) };
|
||||
jest.mock("../../../device", () => ({ getDevice: () => mockDevice }));
|
||||
|
||||
import * as React from "react";
|
||||
import { mount } from "enzyme";
|
||||
import { EStopButton } from "../e_stop_btn";
|
||||
|
@ -5,7 +8,7 @@ import { bot } from "../../../__test_support__/fake_state/bot";
|
|||
import { EStopButtonProps } from "../../interfaces";
|
||||
|
||||
describe("<EStopButton />", () => {
|
||||
const fakeProps = (): EStopButtonProps => ({ bot });
|
||||
const fakeProps = (): EStopButtonProps => ({ bot, forceUnlock: false });
|
||||
it("renders", () => {
|
||||
bot.hardware.informational_settings.sync_status = "synced";
|
||||
const wrapper = mount(<EStopButton {...fakeProps()} />);
|
||||
|
@ -13,18 +16,45 @@ describe("<EStopButton />", () => {
|
|||
expect(wrapper.find("button").hasClass("red")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("grayed out", () => {
|
||||
it("is grayed out when offline", () => {
|
||||
bot.hardware.informational_settings.sync_status = undefined;
|
||||
const wrapper = mount(<EStopButton {...fakeProps()} />);
|
||||
expect(wrapper.text()).toEqual("E-STOP");
|
||||
expect(wrapper.find("button").hasClass("pseudo-disabled")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("locked", () => {
|
||||
it("shows locked state", () => {
|
||||
bot.hardware.informational_settings.sync_status = "synced";
|
||||
bot.hardware.informational_settings.locked = true;
|
||||
const wrapper = mount(<EStopButton {...fakeProps()} />);
|
||||
expect(wrapper.text()).toEqual("UNLOCK");
|
||||
expect(wrapper.find("button").hasClass("yellow")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("confirms unlock", () => {
|
||||
bot.hardware.informational_settings.sync_status = "synced";
|
||||
bot.hardware.informational_settings.locked = true;
|
||||
const p = fakeProps();
|
||||
p.forceUnlock = false;
|
||||
window.confirm = jest.fn(() => false);
|
||||
const wrapper = mount(<EStopButton {...p} />);
|
||||
expect(wrapper.text()).toEqual("UNLOCK");
|
||||
wrapper.find("button").simulate("click");
|
||||
expect(window.confirm).toHaveBeenCalledWith(
|
||||
"Are you sure you want to unlock the device?");
|
||||
expect(mockDevice.emergencyUnlock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("doesn't confirm unlock", () => {
|
||||
bot.hardware.informational_settings.sync_status = "synced";
|
||||
bot.hardware.informational_settings.locked = true;
|
||||
const p = fakeProps();
|
||||
p.forceUnlock = true;
|
||||
window.confirm = jest.fn(() => false);
|
||||
const wrapper = mount(<EStopButton {...p} />);
|
||||
expect(wrapper.text()).toEqual("UNLOCK");
|
||||
wrapper.find("button").simulate("click");
|
||||
expect(window.confirm).not.toHaveBeenCalled();
|
||||
expect(mockDevice.emergencyUnlock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,9 @@ export class EStopButton extends React.Component<EStopButtonProps, {}> {
|
|||
render() {
|
||||
const i = this.props.bot.hardware.informational_settings;
|
||||
const isLocked = !!i.locked;
|
||||
const toggleEmergencyLock = isLocked ? emergencyUnlock : emergencyLock;
|
||||
const toggleEmergencyLock = isLocked
|
||||
? () => emergencyUnlock(this.props.forceUnlock)
|
||||
: emergencyLock;
|
||||
const color = isLocked ? "yellow" : "red";
|
||||
const emergencyLockStatusColor = isBotUp(i.sync_status) ? color : GRAY;
|
||||
const emergencyLockStatusText = isLocked ? t("UNLOCK") : "E-STOP";
|
||||
|
|
|
@ -198,6 +198,7 @@ export interface McuInputBoxProps {
|
|||
|
||||
export interface EStopButtonProps {
|
||||
bot: BotState;
|
||||
forceUnlock: boolean;
|
||||
}
|
||||
|
||||
export interface PeripheralsProps {
|
||||
|
|
|
@ -18,6 +18,7 @@ import { Connectivity } from "../devices/connectivity/connectivity";
|
|||
import { connectivityData } from "../devices/connectivity/generate_data";
|
||||
import { DiagnosisSaucer } from "../devices/connectivity/diagnosis";
|
||||
import { maybeSetTimezone } from "../devices/timezones/guess_timezone";
|
||||
import { BooleanSetting } from "../session_keys";
|
||||
|
||||
export class NavBar extends React.Component<NavBarProps, Partial<NavBarState>> {
|
||||
|
||||
|
@ -111,7 +112,10 @@ export class NavBar extends React.Component<NavBarProps, Partial<NavBarState>> {
|
|||
{AdditionalMenu({ logout: this.logout, close })}
|
||||
</Popover>
|
||||
</div>
|
||||
<EStopButton bot={this.props.bot} />
|
||||
<EStopButton
|
||||
bot={this.props.bot}
|
||||
forceUnlock={!!this.props.getConfigValue(
|
||||
BooleanSetting.disable_emergency_unlock_confirmation)} />
|
||||
{this.syncButton()}
|
||||
<div className="connection-status-popover">
|
||||
<Popover position={Position.BOTTOM_RIGHT}
|
||||
|
|
|
@ -22,6 +22,7 @@ export const BooleanSetting: Record<BooleanConfigKey, BooleanConfigKey> = {
|
|||
show_historic_points: "show_historic_points",
|
||||
time_format_24_hour: "time_format_24_hour",
|
||||
show_pins: "show_pins",
|
||||
disable_emergency_unlock_confirmation: "disable_emergency_unlock_confirmation",
|
||||
|
||||
/** "Labs" feature names. (App preferences) */
|
||||
stub_config: "stub_config",
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
"coveralls": "3.0.3",
|
||||
"enzyme": "3.9.0",
|
||||
"enzyme-adapter-react-16": "1.12.1",
|
||||
"farmbot": "7.0.4-rc3",
|
||||
"farmbot": "7.0.4",
|
||||
"farmbot-toastr": "1.0.3",
|
||||
"i18next": "15.0.9",
|
||||
"jest": "24.7.1",
|
||||
|
|
Loading…
Reference in New Issue