Merge pull request #1616 from FarmBot/staging

v8.2.4 - Iridescent Iris
pull/1620/head
Rick Carlino 2019-12-06 13:51:20 -06:00 committed by GitHub
commit b3eb1fff13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 121 additions and 63 deletions

View File

@ -1 +1 @@
<%# Intentionally blank template required by router for content generated by frontend. %>
<h1 class="initial-loading-text">Loading...</h1>

View File

@ -1,2 +1 @@
<h1 class="initial-loading-text">Loading...</h1>
<%# Intentionally blank template required by router for content generated by frontend. %>

View File

@ -12,27 +12,45 @@ describe("<SensorList/>", function () {
const fakeProps = (): SensorListProps => {
const pins: Pins = {
50: {
mode: 0,
value: 1
mode: 1,
value: 500,
},
51: {
mode: 1,
value: 500
}
mode: 0,
value: 1,
},
52: {
mode: 0,
value: 1,
},
53: {
mode: 0,
value: 0,
},
};
const fakeSensor1 = fakeSensor();
const fakeSensor2 = fakeSensor();
const fakeSensor3 = fakeSensor();
const fakeSensor4 = fakeSensor();
fakeSensor1.body.id = 1;
fakeSensor1.body.pin = 51;
fakeSensor1.body.mode = 1;
fakeSensor1.body.mode = 0;
fakeSensor1.body.label = "GPIO 51";
fakeSensor2.body.id = 2;
fakeSensor2.body.pin = 50;
fakeSensor2.body.mode = 0;
fakeSensor2.body.mode = 1;
fakeSensor2.body.label = "GPIO 50 - Moisture";
fakeSensor3.body.id = 3;
fakeSensor3.body.pin = 52;
fakeSensor3.body.mode = 0;
fakeSensor3.body.label = "GPIO 52 - Tool Verification";
fakeSensor4.body.id = 4;
fakeSensor4.body.pin = 53;
fakeSensor4.body.mode = 0;
fakeSensor4.body.label = "GPIO 53 - Tool Verification";
return {
dispatch: jest.fn(),
sensors: [fakeSensor2, fakeSensor1],
sensors: [fakeSensor2, fakeSensor1, fakeSensor3, fakeSensor4],
pins,
disabled: false
};
@ -42,12 +60,18 @@ describe("<SensorList/>", function () {
const wrapper = mount(<SensorList {...fakeProps()} />);
const labels = wrapper.find("label");
const pinNumbers = wrapper.find("p");
expect(labels.first().text()).toEqual("GPIO 51");
expect(pinNumbers.first().text()).toEqual("51");
expect(wrapper.find(".indicator").first().text()).toEqual("500");
expect(labels.last().text()).toEqual("GPIO 50 - Moisture");
expect(pinNumbers.last().text()).toEqual("50");
expect(wrapper.find(".indicator").last().text()).toEqual("1");
expect(labels.at(0).text()).toEqual("GPIO 51");
expect(pinNumbers.at(0).text()).toEqual("51");
expect(wrapper.find(".indicator").at(0).text()).toEqual("1");
expect(labels.at(1).text()).toEqual("GPIO 50 - Moisture");
expect(pinNumbers.at(1).text()).toEqual("50");
expect(wrapper.find(".indicator").at(1).text()).toEqual("500");
expect(labels.at(2).text()).toEqual("GPIO 52 - Tool Verification");
expect(pinNumbers.at(2).text()).toEqual("52");
expect(wrapper.find(".indicator").at(2).text()).toEqual("1 (NO TOOL)");
expect(labels.at(3).text()).toEqual("GPIO 53 - Tool Verification");
expect(pinNumbers.at(3).text()).toEqual("53");
expect(wrapper.find(".indicator").at(3).text()).toEqual("0 (TOOL ON)");
});
const expectedPayload = (pin_number: number, pin_mode: 0 | 1) =>
@ -60,10 +84,10 @@ describe("<SensorList/>", function () {
it("reads sensors", () => {
const wrapper = mount(<SensorList {...fakeProps()} />);
const readSensorBtn = wrapper.find("button");
readSensorBtn.first().simulate("click");
expect(mockDevice.readPin).toHaveBeenCalledWith(expectedPayload(51, 1));
readSensorBtn.last().simulate("click");
expect(mockDevice.readPin).toHaveBeenLastCalledWith(expectedPayload(50, 0));
readSensorBtn.at(0).simulate("click");
expect(mockDevice.readPin).toHaveBeenCalledWith(expectedPayload(51, 0));
readSensorBtn.at(1).simulate("click");
expect(mockDevice.readPin).toHaveBeenLastCalledWith(expectedPayload(50, 1));
expect(mockDevice.readPin).toHaveBeenCalledTimes(2);
});

View File

@ -38,11 +38,17 @@ const SensorReadingDisplay =
({ label, value, mode }: SensorReadingDisplayProps) => {
const moistureSensor = label.toLowerCase().includes("moisture") ?
"moisture-sensor" : "";
return <div className={`sensor-reading-display ${moistureSensor}`}>
const toolSensor = label.toLowerCase().includes("verification") ?
"tool-verification-sensor" : "";
const valueLabel = toolSensor
? `${value} (${value ? t("NO TOOL") : t("TOOL ON")})`
: value;
return <div
className={`sensor-reading-display ${moistureSensor} ${toolSensor}`}>
{isNumber(value) && value >= 0 &&
<div className="indicator" style={calcIndicatorStyle({ value, mode })}>
<span style={calcValueStyle({ value, mode })}>
{value}
{valueLabel}
</span>
</div>}
</div>;

View File

@ -312,6 +312,15 @@
opacity: 1;
}
}
.step-block {
display: flex;
padding: 0;
.step-block-drag {
width: 100%;
padding: 0.2rem 0.8rem;
line-height: 3rem;
}
}
&.quick-del {
&:hover {
background: lighten($red, 10%) !important;
@ -334,6 +343,8 @@
float: right;
font-size: 1.4rem !important;
padding-left: 1rem;
line-height: 3rem;
pointer-events: none;
}
.fb-toggle-button {

View File

@ -1195,6 +1195,11 @@ ul {
&.moisture-sensor {
background: linear-gradient(to right, rgba($blue, 0) 20%, $blue 80%, rgba($blue, 0) 85%);
}
&.tool-verification-sensor {
span {
margin-left: 3.5rem !important;
}
}
border-style: solid;
border-color: $dark_gray;
border-width: 0.1px;

View File

@ -1,8 +1,8 @@
import React from "react";
import { OtaTimeSelector, changeOtaHour, assertIsHour } from "..";
import { OtaTimeSelector, changeOtaHour, assertIsHour } from "../ota_time_selector";
import { shallow } from "enzyme";
import { FBSelect } from "../../../../../ui";
import { fakeDevice } from "../../../../../__test_support__/resource_index_builder";
import { FBSelect } from "../../../../ui";
import { fakeDevice } from "../../../../__test_support__/resource_index_builder";
describe("OTA time selector", () => {
it("asserts that a variable is an HOUR", () => {

View File

@ -1,9 +1,9 @@
import { DropDownItem, FBSelect, Row, Col } from "../../../../ui";
import { DropDownItem, FBSelect, Row, Col } from "../../../ui";
import React from "react";
import { t } from "../../../../i18next_wrapper";
import { t } from "../../../i18next_wrapper";
import { TaggedDevice } from "farmbot";
import { edit, save } from "../../../../api/crud";
import { ColWidth } from "../../farmbot_os_settings";
import { edit, save } from "../../../api/crud";
import { ColWidth } from "../farmbot_os_settings";
// tslint:disable-next-line:no-null-keyword
const UNDEFINED = null as unknown as undefined;

View File

@ -64,18 +64,22 @@ export class RawFarmDesigner extends React.Component<Props, Partial<State>> {
return isBotOriginQuadrant(value) ? value : 2;
}
state: State = {
legend_menu_open: this.initializeSetting(BooleanSetting.legend_menu_open, false),
show_plants: this.initializeSetting(BooleanSetting.show_plants, true),
show_points: this.initializeSetting(BooleanSetting.show_points, true),
show_spread: this.initializeSetting(BooleanSetting.show_spread, false),
show_farmbot: this.initializeSetting(BooleanSetting.show_farmbot, true),
show_images: this.initializeSetting(BooleanSetting.show_images, false),
show_sensor_readings: this.initializeSetting(
BooleanSetting.show_sensor_readings, false),
bot_origin_quadrant: this.getBotOriginQuadrant(),
zoom_level: calcZoomLevel(getZoomLevelIndex(this.props.getConfigValue))
};
getState(): State {
const init = this.initializeSetting;
return {
legend_menu_open: init(BooleanSetting.legend_menu_open, false),
show_plants: init(BooleanSetting.show_plants, true),
show_points: init(BooleanSetting.show_points, true),
show_spread: init(BooleanSetting.show_spread, false),
show_farmbot: init(BooleanSetting.show_farmbot, true),
show_images: init(BooleanSetting.show_images, false),
show_sensor_readings: init(BooleanSetting.show_sensor_readings, false),
bot_origin_quadrant: this.getBotOriginQuadrant(),
zoom_level: calcZoomLevel(getZoomLevelIndex(this.props.getConfigValue)),
};
}
state: State = this.getState();
componentDidMount() {
this.updateBotOriginQuadrant(this.state.bot_origin_quadrant)();

View File

@ -1,10 +1,5 @@
const mockDevice = { setUserEnv: jest.fn(() => Promise.resolve({})) };
jest.mock("../../../device", () => ({
getDevice: () => {
return mockDevice;
}
}));
jest.mock("../../../device", () => ({ getDevice: () => mockDevice }));
jest.mock("../actions", () => ({ scanImage: jest.fn() }));
jest.mock("../../images/actions", () => ({ selectImage: jest.fn() }));
@ -90,4 +85,14 @@ describe("<CameraCalibration/>", () => {
expect(p.saveFarmwareEnv)
.toHaveBeenCalledWith("CAMERA_CALIBRATION_camera_offset_x", "10");
});
it("saves string WeedDetectorConfig changes: API", () => {
const p = fakeProps();
p.shouldDisplay = () => true;
const wrapper = shallow(<CameraCalibration {...p} />);
wrapper.find("WeedDetectorConfig")
.simulate("change", "CAMERA_CALIBRATION_image_bot_origin_location", 4);
expect(p.saveFarmwareEnv).toHaveBeenCalledWith(
"CAMERA_CALIBRATION_image_bot_origin_location", "\"BOTTOM_LEFT\"");
});
});

View File

@ -12,6 +12,7 @@ import { WeedDetectorConfig } from "../weed_detector/config";
import { Feature } from "../../devices/interfaces";
import { namespace } from "../weed_detector";
import { t } from "../../i18next_wrapper";
import { formatEnvKey } from "../weed_detector/remote_env/translators";
export class CameraCalibration extends
React.Component<CameraCalibrationProps, {}> {
@ -23,7 +24,8 @@ export class CameraCalibration extends
saveEnvVar = (key: WDENVKey, value: number) =>
this.props.shouldDisplay(Feature.api_farmware_env)
? this.props.dispatch(this.props.saveFarmwareEnv(key, "" + value))
? this.props.dispatch(this.props.saveFarmwareEnv(
key, JSON.stringify(formatEnvKey(key, value))))
: envSave(key, value)
render() {

View File

@ -1,10 +1,10 @@
import * as React from "react";
import { StepButtonParams } from "../../interfaces";
import { StepButton, stepClick } from "../index";
import { StepButtonParams } from "../interfaces";
import { StepButton, stepClick } from "../step_buttons";
import { shallow } from "enzyme";
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
import { Actions } from "../../../constants";
import { error } from "../../../toast/toast";
import { fakeSequence } from "../../__test_support__/fake_state/resources";
import { Actions } from "../../constants";
import { error } from "../../toast/toast";
function props(): StepButtonParams {
return {

View File

@ -1,5 +1,5 @@
import * as React from "react";
import { StepButton } from "./step_buttons/index";
import { StepButton } from "./step_buttons";
import { scrollToBottom } from "../util";
import { Row } from "../ui/index";
import { TaggedSequence } from "farmbot";

View File

@ -1,11 +1,11 @@
import * as React from "react";
import { SequenceBodyItem as Step, TaggedSequence } from "farmbot";
import { error } from "../../toast/toast";
import { StepDragger, NULL_DRAGGER_ID } from "../../draggable/step_dragger";
import { pushStep, closeCommandMenu } from "../actions";
import { StepButtonParams } from "../interfaces";
import { Col } from "../../ui/index";
import { t } from "../../i18next_wrapper";
import { error } from "../toast/toast";
import { StepDragger, NULL_DRAGGER_ID } from "../draggable/step_dragger";
import { pushStep, closeCommandMenu } from "./actions";
import { StepButtonParams } from "./interfaces";
import { Col } from "../ui/index";
import { t } from "../i18next_wrapper";
export const stepClick =
(dispatch: Function,
@ -29,10 +29,12 @@ export function StepButton({ children, step, color, dispatch, current, index }:
intent="step_splice"
draggerId={NULL_DRAGGER_ID}>
<button draggable={true}
className={`fb-button full-width block ${color}`}
className={`fb-button full-width block step-block ${color}`}
onClick={stepClick(dispatch, step, current, index)}>
{children}
<i className="fa fa-arrows block-control" />
<div className="step-block-drag">
{children}
<i className="fa fa-arrows block-control" />
</div>
</button>
</StepDragger>
</div>