Merge pull request #1767 from FarmBot/fe_updates

Misc updates
pull/1769/head
Rick Carlino 2020-04-23 15:04:05 -05:00 committed by GitHub
commit 7f9ecd450d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 159 additions and 164 deletions

View File

@ -11,12 +11,15 @@ import { OsUpdateButton } from "../os_update_button";
import { OsUpdateButtonProps } from "../interfaces";
import { ShouldDisplay } from "../../../interfaces";
import { Content } from "../../../../constants";
import { ConfigurationName } from "farmbot";
const UPDATE_CHANNEL = "update_channel" as ConfigurationName;
describe("<OsUpdateButton/>", () => {
beforeEach(() => {
bot.currentOSVersion = "6.1.6";
bot.hardware.informational_settings.controller_version = "6.1.6";
bot.hardware.configuration.beta_opt_in = false;
(bot.hardware.configuration[UPDATE_CHANNEL] as string) = "stable";
});
const fakeProps = (): OsUpdateButtonProps => ({
@ -33,7 +36,6 @@ describe("<OsUpdateButton/>", () => {
availableVersion: string | undefined;
availableBetaVersion: string | undefined;
availableBetaCommit: string | undefined;
betaOptIn: boolean | undefined;
onBeta: boolean | undefined;
update_available?: boolean | undefined;
shouldDisplay: ShouldDisplay;
@ -46,7 +48,6 @@ describe("<OsUpdateButton/>", () => {
availableVersion: "6.1.6",
availableBetaVersion: undefined,
availableBetaCommit: undefined,
betaOptIn: false,
onBeta: false,
shouldDisplay: () => false,
update_channel: "stable",
@ -104,7 +105,7 @@ describe("<OsUpdateButton/>", () => {
expected: Results) => {
const {
installedVersion, installedCommit, onBeta, update_available,
availableVersion, availableBetaVersion, availableBetaCommit, betaOptIn,
availableVersion, availableBetaVersion, availableBetaCommit,
shouldDisplay, update_channel,
} = testProps;
bot.hardware.informational_settings.controller_version = installedVersion;
@ -115,9 +116,7 @@ describe("<OsUpdateButton/>", () => {
bot.currentOSVersion = availableVersion;
bot.currentBetaOSVersion = availableBetaVersion;
bot.currentBetaOSCommit = availableBetaCommit;
bot.hardware.configuration.beta_opt_in = betaOptIn;
// tslint:disable-next-line:no-any
(bot.hardware.configuration as any).update_channel = update_channel;
(bot.hardware.configuration[UPDATE_CHANNEL] as string) = update_channel;
const p = fakeProps();
p.shouldDisplay = shouldDisplay;
@ -156,7 +155,7 @@ describe("<OsUpdateButton/>", () => {
const testProps = defaultTestProps();
testProps.installedVersion = "6.1.6";
testProps.availableVersion = undefined;
testProps.betaOptIn = true;
testProps.update_channel = "beta";
const expectedResults = cantConnect("release server");
testButtonState(testProps, expectedResults);
});
@ -166,7 +165,7 @@ describe("<OsUpdateButton/>", () => {
testProps.installedVersion = "6.1.6";
testProps.availableVersion = undefined;
testProps.availableBetaVersion = "6.1.7-beta";
testProps.betaOptIn = true;
testProps.update_channel = "beta";
const expectedResults = updateNeeded("6.1.7-beta");
testButtonState(testProps, expectedResults);
});
@ -175,7 +174,7 @@ describe("<OsUpdateButton/>", () => {
const testProps = defaultTestProps();
testProps.installedVersion = "6.1.6";
testProps.availableBetaVersion = undefined;
testProps.betaOptIn = true;
testProps.update_channel = "beta";
const expectedResults = upToDate("6.1.6");
testButtonState(testProps, expectedResults);
});
@ -205,7 +204,7 @@ describe("<OsUpdateButton/>", () => {
const testProps = defaultTestProps();
testProps.installedVersion = "6.1.5";
testProps.availableBetaVersion = "7.0.0-beta";
testProps.betaOptIn = true;
testProps.update_channel = "beta";
const expectedResults = updateNeeded("7.0.0-beta");
testButtonState(testProps, expectedResults);
});
@ -214,7 +213,7 @@ describe("<OsUpdateButton/>", () => {
const testProps = defaultTestProps();
testProps.installedVersion = "6.1.6";
testProps.availableBetaVersion = "6.1.6-beta";
testProps.betaOptIn = true;
testProps.update_channel = "beta";
const expectedResults = upToDate("6.1.6");
testButtonState(testProps, expectedResults);
});
@ -223,7 +222,7 @@ describe("<OsUpdateButton/>", () => {
const testProps = defaultTestProps();
testProps.installedVersion = "6.1.6";
testProps.availableBetaVersion = "6.1.6-beta";
testProps.betaOptIn = true;
testProps.update_channel = "beta";
testProps.onBeta = true;
const expectedResults = updateNeeded("6.1.6");
testButtonState(testProps, expectedResults);
@ -233,7 +232,7 @@ describe("<OsUpdateButton/>", () => {
const testProps = defaultTestProps();
testProps.installedVersion = "6.1.6";
testProps.availableBetaVersion = "6.1.6-beta";
testProps.betaOptIn = false;
testProps.update_channel = "stable";
testProps.onBeta = true;
const expectedResults = updateNeeded("6.1.6");
testButtonState(testProps, expectedResults);
@ -243,7 +242,7 @@ describe("<OsUpdateButton/>", () => {
const testProps = defaultTestProps();
testProps.installedVersion = "6.1.7";
testProps.availableBetaVersion = "6.1.7-beta";
testProps.betaOptIn = true;
testProps.update_channel = "beta";
testProps.onBeta = true;
const expectedResults = upToDate("6.1.7-beta");
testButtonState(testProps, expectedResults);
@ -253,7 +252,7 @@ describe("<OsUpdateButton/>", () => {
const testProps = defaultTestProps();
testProps.installedVersion = "6.1.7-beta";
testProps.availableBetaVersion = "6.1.7-beta";
testProps.betaOptIn = true;
testProps.update_channel = "beta";
const expectedResults = upToDate("6.1.7-beta");
testButtonState(testProps, expectedResults);
});
@ -264,7 +263,7 @@ describe("<OsUpdateButton/>", () => {
testProps.installedCommit = "old commit";
testProps.availableBetaVersion = "7.0.0-beta";
testProps.availableBetaCommit = "new commit";
testProps.betaOptIn = true;
testProps.update_channel = "beta";
testProps.onBeta = true;
const expectedResults = updateNeeded("7.0.0-beta");
testButtonState(testProps, expectedResults);
@ -273,7 +272,7 @@ describe("<OsUpdateButton/>", () => {
it("handles installed version newer than available (beta enabled)", () => {
const testProps = defaultTestProps();
testProps.installedVersion = "6.1.7";
testProps.betaOptIn = true;
testProps.update_channel = "beta";
testProps.onBeta = false;
testProps.availableBetaVersion = "6.1.7-beta";
const expectedResults = upToDate("6.1.7-beta");
@ -308,16 +307,6 @@ describe("<OsUpdateButton/>", () => {
testButtonState(testProps, expectedResults);
});
it("doesn't use update_channel value", () => {
const testProps = defaultTestProps();
testProps.installedVersion = "6.1.6";
testProps.shouldDisplay = () => false;
testProps.update_channel = "beta";
testProps.availableBetaVersion = "6.1.7-beta";
const expectedResults = upToDate("6.1.6");
testButtonState(testProps, expectedResults);
});
it("compares release candidates: newer", () => {
const testProps = defaultTestProps();
testProps.availableVersion = "6.1.5";

View File

@ -4,7 +4,7 @@ import { SemverResult, semverCompare } from "../../../util";
import { OsUpdateButtonProps } from "./interfaces";
import { checkControllerUpdates } from "../../actions";
import { isString } from "lodash";
import { BotState, Feature } from "../../interfaces";
import { BotState } from "../../interfaces";
import { Content } from "../../../constants";
import { t } from "../../../i18next_wrapper";
@ -154,9 +154,8 @@ export const OsUpdateButton = (props: OsUpdateButtonProps) => {
const { controller_version } = bot.hardware.informational_settings;
/** FBOS beta release opt-in setting. */
const betaOptIn = props.shouldDisplay(Feature.use_update_channel)
? sourceFbosConfig("update_channel" as ConfigurationName).value !== "stable"
: !!sourceFbosConfig("beta_opt_in").value;
const betaOptIn =
sourceFbosConfig("update_channel" as ConfigurationName).value !== "stable";
/** FBOS update availability. */
const buttonStatusProps = buttonVersionStatus({ bot, betaOptIn });

View File

@ -117,7 +117,6 @@ describe("<Logs />", () => {
it("shows filtered overall filter status", () => {
const p = fakeProps();
p.shouldDisplay = () => true;
const wrapper = mount(<Logs {...p} />);
const state = fakeLogsState();
state.assertion = 2;
@ -129,10 +128,9 @@ describe("<Logs />", () => {
it("shows unfiltered overall filter status", () => {
const p = fakeProps();
p.shouldDisplay = () => false;
const wrapper = mount(<Logs {...p} />);
const state = fakeLogsState();
state.assertion = 2;
state.assertion = 3;
wrapper.setState(state);
const filterBtn = wrapper.find("button").first();
expect(filterBtn.text().toLowerCase()).toEqual("filter");

View File

@ -5,7 +5,6 @@ import { Filters } from "../interfaces";
import { startCase } from "lodash";
import { MESSAGE_TYPES, MessageType } from "../../sequences/interfaces";
import { t } from "../../i18next_wrapper";
import { Feature, ShouldDisplay } from "../../devices/interfaces";
const MENU_ORDER: string[] = [
MessageType.success,
@ -26,11 +25,9 @@ const menuSort = (a: string, b: string) =>
/** Get log filter keys from LogsState. */
export const filterStateKeys =
(state: LogsState, shouldDisplay: ShouldDisplay) =>
(state: LogsState) =>
Object.keys(state)
.filter(key => !["autoscroll", "markdown", "searchTerm"].includes(key))
.filter(key => shouldDisplay(Feature.assertion_block)
|| key !== "assertion");
.filter(key => !["autoscroll", "markdown", "searchTerm"].includes(key));
export const LogsFilterMenu = (props: LogsFilterMenuProps) => {
/** Filter level 0: logs hidden. */
@ -56,7 +53,7 @@ export const LogsFilterMenu = (props: LogsFilterMenuProps) => {
{t("normal")}
</button>
</fieldset>
{filterStateKeys(props.state, props.shouldDisplay).sort(menuSort)
{filterStateKeys(props.state).sort(menuSort)
.map((logType: keyof Filters) =>
<fieldset key={logType}>
<label>

View File

@ -76,7 +76,7 @@ export class RawLogs extends React.Component<LogsProps, Partial<LogsState>> {
/** Determine if log type filters are active. */
get filterActive() {
const filterKeys = filterStateKeys(this.state, this.props.shouldDisplay);
const filterKeys = filterStateKeys(this.state);
const filterValues = filterKeys
.map((key: keyof Filters) => this.state[key]);
// Filters active if every log type level is not equal to 3 (max verbosity)

View File

@ -1,25 +1,22 @@
import { regimensReducer, RegimenState } from "../reducer";
import { regimensReducer, RegimenState, newWeek } from "../reducer";
import { Actions } from "../../constants";
import { popWeek, pushWeek, selectDays, deselectDays } from "../bulk_scheduler/actions";
import {
popWeek, pushWeek, selectDays, deselectDays,
} from "../bulk_scheduler/actions";
import { defensiveClone } from "../../util";
import { Week } from "../bulk_scheduler/interfaces";
const week = newWeek();
Object.entries(week.days).map(([day, _]: [keyof Week["days"], boolean]) => {
week.days[day] = true;
});
week.days.day7 = false;
const STATE: RegimenState = {
dailyOffsetMs: 300000,
selectedSequenceUUID: "Sequence.71.167",
currentRegimen: "Regimen.4.56",
weeks: [
{
"days": {
"day1": true,
"day2": true,
"day3": true,
"day4": true,
"day5": true,
"day6": true,
"day7": false
}
},
],
weeks: [week],
schedulerOpen: false,
};

View File

@ -1,39 +0,0 @@
import * as React from "react";
import { render } from "enzyme";
import { WeekRow } from "../bulk_scheduler/week_row";
import { WeekRowProps } from "../bulk_scheduler/interfaces";
import { betterMerge } from "../../util";
function weekProps(p?: Partial<WeekRowProps>): WeekRowProps {
return betterMerge({
dispatch: jest.fn(),
index: 0,
week: {
"days": {
"day1": false,
"day2": false,
"day3": false,
"day4": false,
"day5": false,
"day6": false,
"day7": false
}
}
}, p || {});
}
describe("<WeekRow/>", () => {
it("renders week 1 day numbers", () => {
const wrapper = render(<WeekRow {...weekProps() } />);
const txt = wrapper.text();
expect(txt).toEqual("Week 11234567");
});
});
describe("<WeekRow/>", () => {
it("renders week 2 day numbers", () => {
const wrapper = render(<WeekRow {...weekProps({ index: 1 }) } />);
const txt = wrapper.text();
expect(txt).toEqual("Week 2891011121314");
});
});

View File

@ -19,6 +19,7 @@ import { arrayUnwrap } from "../../../resources/util";
import { overwrite } from "../../../api/crud";
import { fakeVariableNameSet } from "../../../__test_support__/fake_variables";
import { error, warning } from "../../../toast/toast";
import { newWeek } from "../../reducer";
const sequence_id = 23;
const regimen_id = 32;
@ -53,18 +54,9 @@ describe("commitBulkEditor()", () => {
state.resources.consumers.regimens.currentRegimen = regimenUuid;
state.resources.consumers.regimens.selectedSequenceUUID = sequenceUuid;
state.resources.consumers.regimens.dailyOffsetMs = 2000;
state.resources.consumers.regimens.weeks = [{
days:
{
day1: true,
day2: false,
day3: false,
day4: false,
day5: false,
day6: false,
day7: false
}
}];
const week = newWeek();
week.days.day1 = true;
state.resources.consumers.regimens.weeks = [week];
return state;
}

View File

@ -0,0 +1,36 @@
import { groupRegimenItemsByWeek } from "../group_regimen_items_by_week";
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
import { newWeek } from "../../reducer";
describe("groupRegimenItemsByWeek()", () => {
it("groups regimen items by week", () => {
const sequence = fakeSequence();
sequence.body.id = 1;
const week1 = newWeek();
week1.days.day1 = true;
const week2 = newWeek();
const week3 = newWeek();
week3.days.day2 = true;
week3.days.day4 = true;
const { day1, day2, day3, day4, day5, day6, day7 } = week3.days;
week3.days = { day1, day4, day3, day2, day5, day6, day7 };
const weeks = [week1, week2, week3];
const result = groupRegimenItemsByWeek(weeks, 100, sequence.body);
expect(result).toEqual([
{ time_offset: 100, sequence_id: 1 },
{ time_offset: 1296000100, sequence_id: 1 },
{ time_offset: 1468800100, sequence_id: 1 },
]);
});
it("handles missing sequence id", () => {
const sequence = fakeSequence();
sequence.body.id = undefined;
const week = newWeek();
week.days.day1 = true;
const result = groupRegimenItemsByWeek([week], 0, sequence.body);
expect(result).toEqual([
{ time_offset: 0, sequence_id: -1 },
]);
});
});

View File

@ -8,20 +8,12 @@ import {
import { Actions } from "../../../constants";
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
import { AddButton } from "../add_button";
import { newWeek } from "../../reducer";
describe("<BulkScheduler />", () => {
const weeks = [{
days:
{
day1: true,
day2: false,
day3: false,
day4: false,
day5: false,
day6: false,
day7: false
}
}];
const week = newWeek();
week.days.day1 = true;
const weeks = [week];
function fakeProps(): BulkEditorProps {
const sequence = fakeSequence();

View File

@ -3,20 +3,12 @@ import { mount } from "enzyme";
import { WeekGrid } from "../week_grid";
import { WeekGridProps } from "../interfaces";
import { Actions } from "../../../constants";
import { newWeek } from "../../reducer";
describe("<WeekGrid />", () => {
const weeks = [{
days:
{
day1: true,
day2: false,
day3: false,
day4: false,
day5: false,
day6: false,
day7: false
}
}];
const week = newWeek();
week.days.day1 = true;
const weeks = [week];
it("renders", () => {
const props: WeekGridProps = { weeks, dispatch: jest.fn() };

View File

@ -0,0 +1,36 @@
import * as React from "react";
import { render, mount } from "enzyme";
import { WeekRow } from "../week_row";
import { WeekRowProps } from "../interfaces";
import { betterMerge } from "../../../util";
import { newWeek } from "../../reducer";
import { Actions } from "../../../constants";
describe("<WeekRow/>", () => {
const fakeProps = (p?: Partial<WeekRowProps>): WeekRowProps =>
betterMerge({
dispatch: jest.fn(),
index: 0,
week: newWeek()
}, p || {});
it("renders week 1 day numbers", () => {
const wrapper = render(<WeekRow {...fakeProps()} />);
expect(wrapper.text()).toEqual("Week 11234567");
});
it("renders week 2 day numbers", () => {
const wrapper = render(<WeekRow {...fakeProps({ index: 1 })} />);
expect(wrapper.text()).toEqual("Week 2891011121314");
});
it("selects day", () => {
const p = fakeProps();
const wrapper = mount(<WeekRow {...p} />);
wrapper.find("input").first().simulate("click");
expect(p.dispatch).toHaveBeenCalledWith({
type: Actions.TOGGLE_DAY,
payload: { week: 0, day: 1 },
});
});
});

View File

@ -6,17 +6,15 @@ import { t } from "../../i18next_wrapper";
export function WeekRow({ index, dispatch, week }: WeekRowProps) {
return <div className="week-row">
<label className="week-label">{t("Week")} {index + 1}</label>
{
DAYS.map(function (day, i) {
const id = `${index}-${day}`;
return <Day day={i + 1}
week={index}
dispatch={dispatch}
id={id}
key={id}
active={week.days[day]} />;
})
}
{DAYS.map(function (day, i) {
const id = `${index}-${day}`;
return <Day day={i + 1}
week={index}
dispatch={dispatch}
id={id}
key={id}
active={week.days[day]} />;
})}
</div>;
}

View File

@ -12,7 +12,7 @@ export interface RegimenState {
schedulerOpen: boolean;
}
function newWeek() {
export function newWeek(): Week {
return {
days: {
day1: false,

View File

@ -33,7 +33,8 @@ describe("<StepButtonCluster />", () => {
it("has correct drag data", () => {
const p = fakeProps();
const wrapper = mount(<StepButtonCluster {...p} />);
const stepButton = wrapper.find("div").last();
const steps = wrapper.find(".step-dragger");
const stepButton = steps.at(steps.length - 2);
expect(stepButton.text().toLowerCase()).toEqual("take photo");
stepButton.simulate("dragStart", { dataTransfer: { setData: jest.fn() } });
expect(p.dispatch).toHaveBeenCalledWith(expect.objectContaining({

View File

@ -9,7 +9,6 @@ import {
determineVector, determineDropdown, SequenceMeta, determineVarDDILabel,
} from "../../resources/sequence_meta";
import { ResourceIndex, UUID } from "../../resources/interfaces";
import { Feature } from "../../devices/interfaces";
import { DefaultValueForm } from "./default_value_form";
import { t } from "../../i18next_wrapper";
import { CoordinateInputBoxes } from "./location_form_coordinate_input_boxes";
@ -49,13 +48,12 @@ export const LocationForm =
const { celeryNode, dropdown, vector } = maybeUseStepData({
resources, bodyVariables, variable, uuid: sequenceUuid
});
const displayVariables = props.shouldDisplay(Feature.variables) &&
allowedVariableNodes !== AllowedVariableNodes.variable;
const displayVariables = allowedVariableNodes !== AllowedVariableNodes.variable;
const headerForm = allowedVariableNodes === AllowedVariableNodes.parameter;
const variableListItems = displayVariables ? [PARENT(determineVarDDILabel({
label: "parent", resources, uuid: sequenceUuid, forceExternal: headerForm
}))] : [];
const displayGroups = props.shouldDisplay(Feature.groups) && !hideGroups;
const displayGroups = !hideGroups;
const unfiltered = locationFormList(resources, variableListItems, displayGroups);
const list = props.customFilterRule ?
unfiltered.filter(props.customFilterRule) : unfiltered;

View File

@ -163,22 +163,21 @@ export function StepButtonCluster(props: StepButtonProps) {
step={{ kind: "take_photo", args: {} }}>
{t("TAKE PHOTO")}
</StepButton>,
<StepButton
{...commonStepProps}
step={{
kind: "assertion",
args: {
lua: "return 2 + 2 == 4",
_then: { kind: "nothing", args: {} },
assertion_type: "abort_recover",
}
}}
color="purple">
{t("ASSERTION")}
</StepButton>,
];
shouldDisplay(Feature.assertion_block) && ALL_THE_BUTTONS.push(<StepButton
{...commonStepProps}
step={{
kind: "assertion",
args: {
lua: "return 2 + 2 == 4",
_then: { kind: "nothing", args: {} },
assertion_type: "abort_recover",
}
}}
color="purple">
{t("ASSERTION")}
</StepButton>);
shouldDisplay(Feature.update_resource) && ALL_THE_BUTTONS.push(<StepButton
{...commonStepProps}
step={{
@ -196,6 +195,7 @@ export function StepButtonCluster(props: StepButtonProps) {
color="brown">
{t("Mark As...")}
</StepButton>);
return <Row>
<div className="step-button-cluster">
{ALL_THE_BUTTONS.map((stepButton, inx) =>

View File

@ -104,10 +104,19 @@ export enum FbosVersionFallback {
NULL = "0.0.0",
}
const fallbackData: MinOsFeatureLookup = {
[Feature.api_farmware_env]: "8.0.0",
[Feature.api_farmware_installations]: "8.0.0",
[Feature.criteria_groups]: "9.2.2",
[Feature.update_resource]: MinVersionOverride.NEVER,
[Feature.boot_sequence]: MinVersionOverride.NEVER,
};
/**
* Determine whether a feature should be displayed based on
* the user's current FBOS version. Min FBOS version feature data is pulled
* from an external source to allow App and FBOS development flexibility.
* Device-less accounts can use features compatible with supported versions.
*
* @param current installed OS version string to compare against data ("0.0.0")
* @param lookupData min req versions data, for example {"feature": "1.0.0"}
@ -120,7 +129,7 @@ export function createShouldDisplayFn(
const fallback = globalConfig.FBOS_END_OF_LIFE_VERSION ||
FbosVersionFallback.NULL;
const target = override || current || fallback;
const table = lookupData || {};
const table = lookupData || fallbackData;
const min = table[feature] || MinVersionOverride.NEVER;
switch (semverCompare(target, min)) {
case SemverResult.LEFT_IS_GREATER: