bug fixes

pull/1389/head
gabrielburnworth 2019-08-23 14:19:22 -07:00
parent d868625ba3
commit 2f0bd67222
13 changed files with 79 additions and 37 deletions

View File

@ -67,7 +67,7 @@ const peripheralItems = (resources: ResourceIndex): DropDownItem[] => {
value: "" + peripheral.body.pin,
headingId: PinGroupName.Peripheral
}));
return list.length ? [PERIPHERAL_HEADING, ...list] : [];
return list.length ? [PERIPHERAL_HEADING(), ...list] : [];
};
interface OnChangeProps {

View File

@ -29,6 +29,7 @@ describe("<Logs />", () => {
dispatch: jest.fn(),
sourceFbosConfig: jest.fn(),
getConfigValue: x => mockStorj[x],
shouldDisplay: () => false,
});
it("renders", () => {

View File

@ -9,24 +9,31 @@ const logTypes = MESSAGE_TYPES;
describe("<LogsFilterMenu />", () => {
const fakeState: LogsState = {
autoscroll: true, assertion: 1, success: 1, busy: 1, warn: 1,
error: 1, info: 1, fun: 1, debug: 1
autoscroll: true, success: 1, busy: 1, warn: 1,
error: 1, info: 1, fun: 1, debug: 1, assertion: 1,
};
const fakeProps = (): LogsFilterMenuProps => {
return {
toggle: jest.fn(),
setFilterLevel: jest.fn(),
state: fakeState,
};
};
const fakeProps = (): LogsFilterMenuProps => ({
toggle: jest.fn(),
setFilterLevel: jest.fn(),
state: fakeState,
shouldDisplay: () => false,
});
it("renders", () => {
const wrapper = mount(<LogsFilterMenu {...fakeProps()} />);
logTypes.filter(x => x !== "assertion").map(string =>
expect(wrapper.text().toLowerCase()).toContain(string.toLowerCase()));
expect(wrapper.text().toLowerCase()).not.toContain("autoscroll");
});
it("renders new types", () => {
const p = fakeProps();
p.shouldDisplay = () => true;
const wrapper = mount(<LogsFilterMenu {...p} />);
logTypes.map(string =>
expect(wrapper.text().toLowerCase())
.toContain(string.toLowerCase()));
expect(wrapper.text()).not.toContain("autscroll");
expect(wrapper.text().toLowerCase()).toContain(string.toLowerCase()));
expect(wrapper.text().toLowerCase()).not.toContain("autoscroll");
});
it("filters logs", () => {
@ -36,7 +43,7 @@ describe("<LogsFilterMenu />", () => {
p.toggle = (x) => () => toggle(x);
p.setFilterLevel = (x) => () => setFilterLevel(x);
const wrapper = mount(<LogsFilterMenu {...p} />);
wrapper.find("button").at(3).simulate("click");
wrapper.find("button").at(2).simulate("click");
expect(toggle).toHaveBeenCalledWith(MessageType.success);
});
@ -46,7 +53,7 @@ describe("<LogsFilterMenu />", () => {
const wrapper = mount(<LogsFilterMenu {...fakeProps()} />);
const toggles = wrapper.find("button");
expect(toggles.last().hasClass("green")).toBeTruthy();
expect(toggles.at(3).hasClass("red")).toBeTruthy();
expect(toggles.at(2).hasClass("red")).toBeTruthy();
});
it("bulk toggles filter levels", () => {

View File

@ -1,11 +1,28 @@
import * as React from "react";
import { LogsFilterMenuProps } from "../interfaces";
import { Slider } from "@blueprintjs/core";
import { Filters } from "../interfaces";
import { startCase } from "lodash";
import { MESSAGE_TYPES } from "../../sequences/interfaces";
import { MESSAGE_TYPES, MessageType } from "../../sequences/interfaces";
import { t } from "../../i18next_wrapper";
import { Feature } from "../../devices/interfaces";
const MENU_ORDER: string[] = [
MessageType.success,
MessageType.busy,
MessageType.warn,
MessageType.error,
MessageType.info,
MessageType.fun,
MessageType.debug,
MessageType.assertion,
];
const REVERSE_MENU_ORDER = MENU_ORDER.slice().reverse();
/** Order the log filter sort menu, adding unknown types last. */
const menuSort = (a: string, b: string) =>
REVERSE_MENU_ORDER.indexOf(b) - REVERSE_MENU_ORDER.indexOf(a);
export const LogsFilterMenu = (props: LogsFilterMenuProps) => {
/** Filter level 0: logs hidden. */
@ -27,8 +44,10 @@ export const LogsFilterMenu = (props: LogsFilterMenuProps) => {
{t("normal")}
</button>
</fieldset>
{Object.keys(props.state)
.filter(x => { if (!(x == "autoscroll")) { return x; } })
{Object.keys(props.state).sort(menuSort)
.filter(x => x !== "autoscroll")
.filter(x =>
props.shouldDisplay(Feature.assertion_block) || x !== "assertion")
.map((logType: keyof Filters) => {
return <fieldset key={logType}>
<label>

View File

@ -110,6 +110,7 @@ export class Logs extends React.Component<LogsProps, Partial<LogsState>> {
</button>
<LogsFilterMenu
toggle={this.toggle} state={this.state}
shouldDisplay={this.props.shouldDisplay}
setFilterLevel={this.setFilterLevel} />
</Popover>
</div>

View File

@ -1,5 +1,5 @@
import { TaggedLog, ConfigurationName, ALLOWED_MESSAGE_TYPES } from "farmbot";
import { SourceFbosConfig } from "../devices/interfaces";
import { SourceFbosConfig, ShouldDisplay } from "../devices/interfaces";
import { GetWebAppConfigValue } from "../config_storage/actions";
import { TimeSettings } from "../interfaces";
@ -9,6 +9,7 @@ export interface LogsProps {
dispatch: Function;
sourceFbosConfig: SourceFbosConfig;
getConfigValue: GetWebAppConfigValue;
shouldDisplay: ShouldDisplay;
}
export type Filters = Record<ALLOWED_MESSAGE_TYPES, number>;
@ -30,6 +31,7 @@ export interface LogsFilterMenuProps {
toggle: (property: keyof LogsState) => ToggleEventHandler;
state: LogsState;
setFilterLevel: SetNumSetting;
shouldDisplay: ShouldDisplay;
}
export interface LogSettingProps {

View File

@ -1,17 +1,21 @@
import { Everything } from "../interfaces";
import {
selectAllLogs, maybeGetTimeSettings
selectAllLogs, maybeGetTimeSettings, maybeGetDevice
} from "../resources/selectors";
import { LogsProps } from "./interfaces";
import {
sourceFbosConfigValue
} from "../devices/components/source_config_value";
import { validFbosConfig } from "../util";
import {
validFbosConfig, determineInstalledOsVersion,
shouldDisplay as shouldDisplayFunc
} from "../util";
import { ResourceIndex } from "../resources/interfaces";
import { TaggedLog } from "farmbot";
import { getWebAppConfigValue } from "../config_storage/actions";
import { getFbosConfig } from "../resources/getters";
import { chain } from "lodash";
import { DevSettings } from "../account/dev/dev_support";
/** Take the specified number of logs after sorting by time created. */
export function takeSortedLogs(
@ -28,11 +32,17 @@ export function mapStateToProps(props: Everything): LogsProps {
const fbosConfig = validFbosConfig(getFbosConfig(props.resources.index));
const sourceFbosConfig =
sourceFbosConfigValue(fbosConfig, hardware.configuration);
const installedOsVersion = determineInstalledOsVersion(
props.bot, maybeGetDevice(props.resources.index));
const fbosVersionOverride = DevSettings.overriddenFbosVersion();
const shouldDisplay = shouldDisplayFunc(
installedOsVersion, props.bot.minOsFeatureData, fbosVersionOverride);
return {
dispatch: props.dispatch,
sourceFbosConfig,
logs: takeSortedLogs(250, props.resources.index),
timeSettings: maybeGetTimeSettings(props.resources.index),
getConfigValue: getWebAppConfigValue(() => props),
shouldDisplay,
};
}

View File

@ -94,7 +94,8 @@ export enum MessageType {
error = "error",
info = "info",
fun = "fun",
debug = "debug"
debug = "debug",
assertion = "assertion",
}
export const MESSAGE_TYPES = Object.keys(MessageType);

View File

@ -59,7 +59,7 @@ describe("Pin and Peripheral support files", () => {
it("has a list of unnamed pins", () => {
expect(pinDropdowns(n => n).length)
.toBe(PIN_RANGE.length + 1); // 54 pins plus the header.
expect(pinDropdowns(n => n)[0]).toBe(PIN_HEADING);
expect(pinDropdowns(n => n)[0]).toEqual(PIN_HEADING());
// Grab all uniq heading IDs- we expect only 1.
const values = chain(pinDropdowns(n => n))
.tail()
@ -75,7 +75,7 @@ describe("Pin and Peripheral support files", () => {
const ri = buildResourceIndex([p]);
const result = peripheralsAsDropDowns(ri.index);
expect(result.length).toEqual(2); // Heading + 1 peripheral
expect(result[0]).toBe(PERIPHERAL_HEADING);
expect(result[0]).toEqual(PERIPHERAL_HEADING());
expect(result[1]).toEqual({
label: p.body.label,
headingId: PinGroupName.Peripheral,
@ -89,7 +89,7 @@ describe("Pin and Peripheral support files", () => {
const ri = buildResourceIndex([s]);
const result = sensorsAsDropDowns(ri.index);
expect(result.length).toEqual(2); // Heading + 1 sensor
expect(result[0]).toBe(SENSOR_HEADING);
expect(result[0]).toEqual(SENSOR_HEADING());
expect(result[1]).toEqual({
label: s.body.label,
headingId: PinGroupName.Sensor,
@ -101,8 +101,8 @@ describe("Pin and Peripheral support files", () => {
const ri = buildResourceIndex([]);
const sResult = sensorsAsDropDowns(ri.index);
const pResult = peripheralsAsDropDowns(ri.index);
expect(sResult).not.toContain(SENSOR_HEADING);
expect(pResult).not.toContain(PERIPHERAL_HEADING);
expect(sResult).not.toContain(SENSOR_HEADING());
expect(pResult).not.toContain(PERIPHERAL_HEADING());
});
it("Validates correctness of `pin_type` at runtime", () => {

View File

@ -35,16 +35,16 @@ const BOX_LED_LABELS: { [x: string]: string } = {
[BoxLed.BoxLed4]: t("Box LED 4"),
};
export const PERIPHERAL_HEADING: DropDownItem =
export const PERIPHERAL_HEADING = (): DropDownItem =>
({ heading: true, label: t("Peripherals"), value: 0, headingId: PinGroupName.Peripheral });
export const SENSOR_HEADING: DropDownItem =
export const SENSOR_HEADING = (): DropDownItem =>
({ heading: true, label: t("Sensors"), value: 0, headingId: PinGroupName.Sensor });
export const BOX_LED_HEADING: DropDownItem =
export const BOX_LED_HEADING = (): DropDownItem =>
({ heading: true, label: t("Box LEDs"), value: 0, headingId: PinGroupName.BoxLed });
export const PIN_HEADING: DropDownItem =
export const PIN_HEADING = (): DropDownItem =>
({ heading: true, label: t("Pins"), value: 0, headingId: PinGroupName.Pin });
/** Pass it the number X and it will generate a DropDownItem for `pin x`. */
@ -79,17 +79,17 @@ const boxLed2DropDown =
export function peripheralsAsDropDowns(input: ResourceIndex): DropDownItem[] {
const list = selectAllSavedPeripherals(input).map(peripheral2DropDown);
return list.length ? [PERIPHERAL_HEADING, ...list] : [];
return list.length ? [PERIPHERAL_HEADING(), ...list] : [];
}
export function sensorsAsDropDowns(input: ResourceIndex): DropDownItem[] {
const list = selectAllSavedSensors(input).map(sensor2DropDown);
return list.length ? [SENSOR_HEADING, ...list] : [];
return list.length ? [SENSOR_HEADING(), ...list] : [];
}
export function boxLedsAsDropDowns(): DropDownItem[] {
const list = Object.values(BoxLed).map(boxLed2DropDown);
return [BOX_LED_HEADING, ...list];
return [BOX_LED_HEADING(), ...list];
}
/** Number of pins in an Arduino Mega */
@ -97,7 +97,7 @@ export const PIN_RANGE = range(0, 70);
export function pinDropdowns(
valueFormat: (n: number) => string | number): DropDownItem[] {
return [PIN_HEADING, ...PIN_RANGE.map(pinNumber2DropDown(valueFormat))];
return [PIN_HEADING(), ...PIN_RANGE.map(pinNumber2DropDown(valueFormat))];
}
export const pinsAsDropDownsWritePin = (

View File

@ -13,7 +13,6 @@ describe("<TypePart/>", () => {
.simulate("change", { value: "anything", label: "y" });
expect(p.dispatch).toHaveBeenCalled();
const calls = (p.dispatch as jest.Mock).mock.calls[0][0];
console.log(calls);
const { assertion_type } = calls.payload.update.body[1].args;
expect(assertion_type).toEqual("anything");
});

View File

@ -26,6 +26,7 @@ export function SequencePart(props: AssertionStepProps) {
return <span>
<label>Recovery Sequence</label>
<SequenceSelectBox
key={JSON.stringify(props.currentStep)}
onChange={onChange}
resources={props.resources}
sequenceId={sequenceId} />

View File

@ -16,6 +16,7 @@ export function TypePart(props: AssertionStepProps) {
return <span>
<label>If Test Fails</label>
<FBSelect
key={JSON.stringify(props.currentStep)}
selectedItem={ASSERTION_TYPES[assertion_type]}
onChange={(ddi) => {
props.dispatch(editStep({