misc fixes
parent
3871a0b083
commit
5d76123682
|
@ -157,8 +157,8 @@ export function fakeDiagnosticDump(): TaggedDiagnosticDump {
|
|||
firmware_state: string,
|
||||
network_interface: string,
|
||||
fbos_dmesg_dump: string,
|
||||
created_at: string,
|
||||
updated_at: string,
|
||||
created_at: "2018-01-11T20:20:38.362Z",
|
||||
updated_at: "2018-01-11T20:20:38.362Z",
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import { onLogs } from "../log_handlers";
|
||||
import { Log } from "farmbot/dist/resources/api_resources";
|
||||
|
||||
describe("onLogs()", () => {
|
||||
it("inits log", () => {
|
||||
const msg = { message: "test log", type: undefined, channels: [] };
|
||||
const log = onLogs(jest.fn(), jest.fn())(msg as unknown as Log);
|
||||
expect(log).toEqual(expect.objectContaining({
|
||||
body: expect.objectContaining({ type: "info" })
|
||||
}));
|
||||
});
|
||||
});
|
|
@ -12,6 +12,7 @@ import { globalQueue } from "./batch_queue";
|
|||
export const onLogs =
|
||||
(_dispatch: Function, getState: GetState) => (msg: Log) => {
|
||||
if (isLog(msg)) {
|
||||
!msg.type && (msg.type = "info");
|
||||
actOnChannelName(msg, "toast", showLogOnScreen);
|
||||
actOnChannelName(msg, "espeak", speakLogAloud(getState));
|
||||
const log = initLog(msg).payload;
|
||||
|
|
|
@ -77,16 +77,6 @@ export class FarmbotOsSettings
|
|||
const { bot, sourceFbosConfig, botToMqttStatus } = this.props;
|
||||
const { sync_status } = bot.hardware.informational_settings;
|
||||
const botOnline = isBotOnline(sync_status, botToMqttStatus);
|
||||
const bootRow = <Row>
|
||||
<Col xs={ColWidth.label}>
|
||||
<label>
|
||||
{t("BOOT SEQUENCE")}
|
||||
</label>
|
||||
</Col>
|
||||
<Col xs={7}>
|
||||
<BootSequenceSelector />
|
||||
</Col>
|
||||
</Row>;
|
||||
return <Widget className="device-widget">
|
||||
<form onSubmit={(e) => e.preventDefault()}>
|
||||
<WidgetHeader title="Device">
|
||||
|
@ -160,7 +150,8 @@ export class FarmbotOsSettings
|
|||
shouldDisplay={this.props.shouldDisplay}
|
||||
timeSettings={this.props.timeSettings}
|
||||
sourceFbosConfig={sourceFbosConfig} />
|
||||
{this.props.shouldDisplay(Feature.boot_sequence) && bootRow}
|
||||
{this.props.shouldDisplay(Feature.boot_sequence) &&
|
||||
<BootSequenceSelector />}
|
||||
<PowerAndReset
|
||||
controlPanelState={this.props.bot.controlPanelState}
|
||||
dispatch={this.props.dispatch}
|
||||
|
|
|
@ -2,11 +2,13 @@ import * as React from "react";
|
|||
import { connect } from "react-redux";
|
||||
import { Everything } from "../../../interfaces";
|
||||
import { getFbosConfig } from "../../../resources/getters";
|
||||
import { FBSelect, DropDownItem } from "../../../ui";
|
||||
import { FBSelect, DropDownItem, Row, Col } from "../../../ui";
|
||||
import { edit, save } from "../../../api/crud";
|
||||
import { TaggedFbosConfig, TaggedSequence } from "farmbot";
|
||||
import { selectAllSequences, findSequenceById } from "../../../resources/selectors";
|
||||
import { betterCompact } from "../../../util";
|
||||
import { ColWidth } from "../farmbot_os_settings";
|
||||
import { t } from "../../../i18next_wrapper";
|
||||
|
||||
interface Props {
|
||||
list: DropDownItem[];
|
||||
|
@ -53,13 +55,20 @@ export class RawBootSequenceSelector extends React.Component<Props, {}> {
|
|||
}
|
||||
|
||||
render() {
|
||||
return <div>
|
||||
<FBSelect
|
||||
allowEmpty={true}
|
||||
list={this.props.list}
|
||||
selectedItem={this.props.selectedItem}
|
||||
onChange={this.onChange} />
|
||||
</div>;
|
||||
return <Row>
|
||||
<Col xs={ColWidth.label}>
|
||||
<label>
|
||||
{t("BOOT SEQUENCE")}
|
||||
</label>
|
||||
</Col>
|
||||
<Col xs={7}>
|
||||
<FBSelect
|
||||
allowEmpty={true}
|
||||
list={this.props.list}
|
||||
selectedItem={this.props.selectedItem}
|
||||
onChange={this.onChange} />
|
||||
</Col>
|
||||
</Row>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,22 +94,25 @@ export function DesignerNavTabs(props: { hidden?: boolean }) {
|
|||
panel={Panel.Groups}
|
||||
linkTo={"/app/designer/groups"}
|
||||
title={t("Groups")} />
|
||||
<NavTab
|
||||
panel={Panel.SavedGardens}
|
||||
linkTo={"/app/designer/saved_gardens"}
|
||||
title={t("Gardens")} />
|
||||
{DevSettings.futureFeaturesEnabled() &&
|
||||
<NavTab
|
||||
panel={Panel.SavedGardens}
|
||||
linkTo={"/app/designer/saved_gardens"}
|
||||
title={t("Gardens")} />}
|
||||
<NavTab
|
||||
panel={Panel.FarmEvents}
|
||||
linkTo={"/app/designer/events"}
|
||||
title={t("Events")} />
|
||||
{DevSettings.futureFeaturesEnabled() && <NavTab
|
||||
panel={Panel.Points}
|
||||
linkTo={"/app/designer/points"}
|
||||
title={t("Points")} />}
|
||||
{DevSettings.futureFeaturesEnabled() && <NavTab
|
||||
panel={Panel.Tools}
|
||||
linkTo={"/app/designer/tools"}
|
||||
title={t("Tools")} />}
|
||||
{DevSettings.futureFeaturesEnabled() &&
|
||||
<NavTab
|
||||
panel={Panel.Points}
|
||||
linkTo={"/app/designer/points"}
|
||||
title={t("Points")} />}
|
||||
{DevSettings.futureFeaturesEnabled() &&
|
||||
<NavTab
|
||||
panel={Panel.Tools}
|
||||
linkTo={"/app/designer/tools"}
|
||||
title={t("Tools")} />}
|
||||
<NavTab
|
||||
panel={Panel.Settings}
|
||||
icon={"fa fa-gear"}
|
||||
|
|
|
@ -46,7 +46,7 @@ export class RawEditPoint extends React.Component<EditPointProps, {}> {
|
|||
<ul>
|
||||
{
|
||||
Object.entries(body.meta).map(([k, v]) => {
|
||||
return <li>{k}: {v}</li>;
|
||||
return <li key={k}>{k}: {v}</li>;
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
|
|
|
@ -4,12 +4,12 @@ mockGroup.body.point_ids = [1, 2, 3];
|
|||
jest.mock("../group_detail", () => ({ fetchGroupFromUrl: () => mockGroup }));
|
||||
|
||||
import * as React from "react";
|
||||
import { mount } from "enzyme";
|
||||
import { GroupOrder, GroupOrderProps } from "../group_order_visual";
|
||||
import {
|
||||
fakeMapTransformProps
|
||||
} from "../../../__test_support__/map_transform_props";
|
||||
import { fakePlant } from "../../../__test_support__/fake_state/resources";
|
||||
import { svgMount } from "../../../__test_support__/svg_mount";
|
||||
|
||||
describe("<GroupOrder />", () => {
|
||||
const fakeProps = (): GroupOrderProps => {
|
||||
|
@ -30,7 +30,7 @@ describe("<GroupOrder />", () => {
|
|||
};
|
||||
|
||||
it("renders group order", () => {
|
||||
const wrapper = mount(<GroupOrder {...fakeProps()} />);
|
||||
const wrapper = svgMount(<GroupOrder {...fakeProps()} />);
|
||||
expect(wrapper.find("line").length).toEqual(3);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,7 +14,6 @@ describe("AdditionalMenu", () => {
|
|||
close={jest.fn()} />);
|
||||
const text = wrapper.text();
|
||||
expect(text).toContain("Account Settings");
|
||||
expect(text).toContain("Documentation");
|
||||
expect(text).toContain("Logout");
|
||||
expect(text).toContain("VERSION");
|
||||
});
|
||||
|
@ -33,7 +32,7 @@ describe("AdditionalMenu", () => {
|
|||
const wrapper = shallow(<AdditionalMenu
|
||||
logout={logout}
|
||||
close={jest.fn()} />);
|
||||
wrapper.find("a").at(1).simulate("click");
|
||||
wrapper.find("a").at(0).simulate("click");
|
||||
expect(logout).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
let mockPath = "/app/designer/plants";
|
||||
jest.mock("../../history", () => ({
|
||||
getPathArray: jest.fn(() => mockPath.split("/")),
|
||||
}));
|
||||
|
||||
let mockDev = false;
|
||||
jest.mock("../../account/dev/dev_support", () => ({
|
||||
DevSettings: { futureFeaturesEnabled: () => mockDev }
|
||||
}));
|
||||
|
||||
import * as React from "react";
|
||||
import { shallow, mount } from "enzyme";
|
||||
import { NavLinks } from "../nav_links";
|
||||
|
||||
describe("NavLinks", () => {
|
||||
describe("<NavLinks />", () => {
|
||||
it("toggles the mobile nav menu", () => {
|
||||
const close = jest.fn();
|
||||
const wrapper = shallow(<NavLinks close={(x) => () => close(x)}
|
||||
|
@ -16,4 +26,16 @@ describe("NavLinks", () => {
|
|||
const wrapper = mount(<NavLinks close={jest.fn()} alertCount={1} />);
|
||||
expect(wrapper.text()).toContain("1");
|
||||
});
|
||||
|
||||
it("shows links", () => {
|
||||
mockDev = true;
|
||||
const wrapper = mount(<NavLinks close={jest.fn()} alertCount={1} />);
|
||||
expect(wrapper.text().toLowerCase()).not.toContain("tools");
|
||||
});
|
||||
|
||||
it("shows active link", () => {
|
||||
mockPath = "/app/designer";
|
||||
const wrapper = shallow(<NavLinks close={jest.fn()} alertCount={1} />);
|
||||
expect(wrapper.find("Link").first().hasClass("active")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import * as React from "react";
|
||||
import { AccountMenuProps } from "./interfaces";
|
||||
import { docLink } from "../ui/doc_link";
|
||||
import { Link } from "../link";
|
||||
import { shortRevision } from "../util";
|
||||
import { t } from "../i18next_wrapper";
|
||||
|
@ -23,12 +22,6 @@ export const AdditionalMenu = (props: AccountMenuProps) => {
|
|||
<i className="fa fa-question-circle"></i>
|
||||
{t("Help")}
|
||||
</Link>
|
||||
<div>
|
||||
<a href={docLink("the-farmbot-web-app")}
|
||||
target="_blank">
|
||||
<i className="fa fa-file-text-o"></i>{t("Documentation")}
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<a onClick={props.logout}>
|
||||
<i className="fa fa-sign-out"></i>
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
import { Link } from "../link";
|
||||
import { t } from "../i18next_wrapper";
|
||||
import { betterCompact } from "../util";
|
||||
import { DevSettings } from "../account/dev/dev_support";
|
||||
/** Uses a slug and a child path to compute the `href` of a navbar link. */
|
||||
export type LinkComputeFn = (slug: string, childPath: string) => string;
|
||||
|
||||
|
@ -36,7 +37,8 @@ export const getLinks = (): NavLinkParams[] => betterCompact([
|
|||
name: "Regimens", icon: "calendar-check-o", slug: "regimens",
|
||||
computeHref: computeEditorUrlFromState("Regimen")
|
||||
},
|
||||
{ name: "Tools", icon: "wrench", slug: "tools" },
|
||||
DevSettings.futureFeaturesEnabled() ? undefined :
|
||||
{ name: "Tools", icon: "wrench", slug: "tools" },
|
||||
{
|
||||
name: "Farmware", icon: "crosshairs", slug: "farmware",
|
||||
computeHref: computeFarmwareUrlFromState
|
||||
|
|
|
@ -39,7 +39,6 @@ describe("<LocalsList/>", () => {
|
|||
onChange: jest.fn(),
|
||||
shouldDisplay: jest.fn(),
|
||||
allowedVariableNodes: AllowedVariableNodes.parameter,
|
||||
customFilterRule: undefined
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@ import {
|
|||
|
||||
describe("locationFormList()", () => {
|
||||
it("returns dropdown list", () => {
|
||||
const items = locationFormList(fakeResourceIndex(), []);
|
||||
const pg = fakePointGroup();
|
||||
pg.body.id = 1;
|
||||
const items = locationFormList(fakeResourceIndex([pg]), [], true);
|
||||
const coordinate = items[0];
|
||||
expect(coordinate).toEqual({
|
||||
headingId: "Coordinate",
|
||||
|
@ -54,6 +56,19 @@ describe("locationFormList()", () => {
|
|||
label: "Point 1 (10, 20, 30)",
|
||||
value: "2"
|
||||
});
|
||||
const groupHeading = items[8];
|
||||
expect(groupHeading).toEqual({
|
||||
headingId: "PointGroup",
|
||||
label: "Groups",
|
||||
value: 0,
|
||||
heading: true,
|
||||
});
|
||||
const group = items[9];
|
||||
expect(group).toEqual({
|
||||
headingId: "PointGroup",
|
||||
label: "Fake",
|
||||
value: "1"
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -34,18 +34,19 @@ describe("<LocationForm/>", () => {
|
|||
onChange: jest.fn(),
|
||||
shouldDisplay: jest.fn(),
|
||||
allowedVariableNodes: AllowedVariableNodes.parameter,
|
||||
customFilterRule: undefined
|
||||
});
|
||||
|
||||
it("renders correct UI components", () => {
|
||||
const p = fakeProps();
|
||||
p.shouldDisplay = () => true;
|
||||
const el = shallow(<LocationForm {...p} />);
|
||||
const selects = el.find(FBSelect);
|
||||
const inputs = el.find(BlurableInput);
|
||||
|
||||
expect(selects.length).toBe(1);
|
||||
const select = selects.first().props();
|
||||
const choices = locationFormList(p.resources, [PARENT("")]);
|
||||
const choices = locationFormList(
|
||||
p.resources, [PARENT("Externally defined")], true);
|
||||
const actualLabels = select.list.map(x => x.label).sort();
|
||||
const expectedLabels = choices.map(x => x.label).sort();
|
||||
const diff = difference(actualLabels, expectedLabels);
|
||||
|
|
|
@ -35,6 +35,7 @@ export const DefaultValueForm = (props: DefaultValueFormProps) => {
|
|||
shouldDisplay={() => true}
|
||||
allowedVariableNodes={AllowedVariableNodes.variable}
|
||||
hideTypeLabel={true}
|
||||
hideGroups={true}
|
||||
onChange={change(props.onChange, props.variableNode)}
|
||||
customFilterRule={NO_GROUPS} />
|
||||
</div>;
|
||||
|
|
|
@ -60,6 +60,7 @@ export const LocalsList = (props: LocalsListProps) => {
|
|||
collapsed={props.collapsed}
|
||||
toggleVarShow={props.toggleVarShow}
|
||||
onChange={props.onChange}
|
||||
hideGroups={props.hideGroups}
|
||||
customFilterRule={props.customFilterRule} />)}
|
||||
</div>;
|
||||
};
|
||||
|
|
|
@ -54,6 +54,8 @@ interface CommonProps {
|
|||
collapsible?: boolean;
|
||||
collapsed?: boolean;
|
||||
toggleVarShow?: () => void;
|
||||
/** Don't show groups in dropdown. */
|
||||
hideGroups?: boolean;
|
||||
/** Optional filter to allow removal of arbitrary dropdown items.
|
||||
* Return `false` to omit an item from display. */
|
||||
customFilterRule?: (ddi: DropDownItem) => boolean;
|
||||
|
|
|
@ -45,7 +45,7 @@ const maybeUseStepData = ({ resources, bodyVariables, variable, uuid }: {
|
|||
export const LocationForm =
|
||||
(props: LocationFormProps) => {
|
||||
const { sequenceUuid, resources, bodyVariables, variable,
|
||||
allowedVariableNodes } = props;
|
||||
allowedVariableNodes, hideGroups } = props;
|
||||
const { celeryNode, dropdown, vector } = maybeUseStepData({
|
||||
resources, bodyVariables, variable, uuid: sequenceUuid
|
||||
});
|
||||
|
@ -55,7 +55,8 @@ export const LocationForm =
|
|||
const variableListItems = displayVariables ? [PARENT(determineVarDDILabel({
|
||||
label: "parent", resources, uuid: sequenceUuid, forceExternal: headerForm
|
||||
}))] : [];
|
||||
const unfiltered = locationFormList(resources, variableListItems);
|
||||
const displayGroups = props.shouldDisplay(Feature.groups) && !hideGroups;
|
||||
const unfiltered = locationFormList(resources, variableListItems, displayGroups);
|
||||
const list = props.customFilterRule ?
|
||||
unfiltered.filter(props.customFilterRule) : unfiltered;
|
||||
/** Variable name. */
|
||||
|
|
|
@ -66,7 +66,7 @@ export const groups2Ddi = (groups: TaggedPointGroup[]): DropDownItem[] => {
|
|||
|
||||
/** Location selection menu items. */
|
||||
export function locationFormList(resources: ResourceIndex,
|
||||
additionalItems: DropDownItem[]): DropDownItem[] {
|
||||
additionalItems: DropDownItem[], displayGroups?: boolean): DropDownItem[] {
|
||||
const points = selectAllActivePoints(resources)
|
||||
.filter(x => x.body.pointer_type !== "ToolSlot");
|
||||
const plantDDI = points2ddi(points, "Plant");
|
||||
|
@ -80,8 +80,8 @@ export function locationFormList(resources: ResourceIndex,
|
|||
.concat(plantDDI)
|
||||
.concat(heading("GenericPointer"))
|
||||
.concat(genericPointerDDI)
|
||||
.concat(heading("PointGroup"))
|
||||
.concat(groups2Ddi(selectAllPointGroups(resources)));
|
||||
.concat(displayGroups ? heading("PointGroup") : [])
|
||||
.concat(displayGroups ? groups2Ddi(selectAllPointGroups(resources)) : []);
|
||||
}
|
||||
|
||||
/** Create drop down item with label; i.e., "Point/Plant (1, 2, 3)" */
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ResourceIndex } from "../../resources/interfaces";
|
|||
import { TaggedResource } from "farmbot";
|
||||
import { newTaggedResource } from "../../sync/actions";
|
||||
|
||||
export function fakeResourceIndex(): ResourceIndex {
|
||||
export function fakeResourceIndex(extra: TaggedResource[] = []): ResourceIndex {
|
||||
const fakeResources: TaggedResource[] = [
|
||||
...newTaggedResource("Point", {
|
||||
"id": 1,
|
||||
|
@ -56,7 +56,8 @@ export function fakeResourceIndex(): ResourceIndex {
|
|||
"id": 1,
|
||||
"name": "Generic Tool",
|
||||
"status": "active"
|
||||
})
|
||||
}),
|
||||
...extra,
|
||||
];
|
||||
return buildResourceIndex(fakeResources).index;
|
||||
}
|
||||
|
|
|
@ -212,6 +212,7 @@ const SequenceHeader = (props: SequenceHeaderProps) => {
|
|||
collapsed={props.variablesCollapsed}
|
||||
toggleVarShow={props.toggleVarShow}
|
||||
shouldDisplay={props.shouldDisplay}
|
||||
hideGroups={true}
|
||||
customFilterRule={NO_GROUPS} />
|
||||
</div>;
|
||||
};
|
||||
|
|
|
@ -95,6 +95,7 @@ export class TileMoveAbsolute extends React.Component<StepParams, MoveAbsState>
|
|||
this.updateLocation(x)}
|
||||
shouldDisplay={this.props.shouldDisplay || (() => false)}
|
||||
hideHeader={true}
|
||||
hideGroups={true}
|
||||
locationDropdownKey={JSON.stringify(this.props.currentSequence)}
|
||||
allowedVariableNodes={AllowedVariableNodes.identifier}
|
||||
width={3}
|
||||
|
|
Loading…
Reference in New Issue