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

View File

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

View File

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

View File

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

View File

@ -76,7 +76,7 @@ export class RawLogs extends React.Component<LogsProps, Partial<LogsState>> {
/** Determine if log type filters are active. */ /** Determine if log type filters are active. */
get filterActive() { get filterActive() {
const filterKeys = filterStateKeys(this.state, this.props.shouldDisplay); const filterKeys = filterStateKeys(this.state);
const filterValues = filterKeys const filterValues = filterKeys
.map((key: keyof Filters) => this.state[key]); .map((key: keyof Filters) => this.state[key]);
// Filters active if every log type level is not equal to 3 (max verbosity) // 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 { 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 { 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 = { const STATE: RegimenState = {
dailyOffsetMs: 300000, dailyOffsetMs: 300000,
selectedSequenceUUID: "Sequence.71.167", selectedSequenceUUID: "Sequence.71.167",
currentRegimen: "Regimen.4.56", currentRegimen: "Regimen.4.56",
weeks: [ weeks: [week],
{
"days": {
"day1": true,
"day2": true,
"day3": true,
"day4": true,
"day5": true,
"day6": true,
"day7": false
}
},
],
schedulerOpen: false, 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 { overwrite } from "../../../api/crud";
import { fakeVariableNameSet } from "../../../__test_support__/fake_variables"; import { fakeVariableNameSet } from "../../../__test_support__/fake_variables";
import { error, warning } from "../../../toast/toast"; import { error, warning } from "../../../toast/toast";
import { newWeek } from "../../reducer";
const sequence_id = 23; const sequence_id = 23;
const regimen_id = 32; const regimen_id = 32;
@ -53,18 +54,9 @@ describe("commitBulkEditor()", () => {
state.resources.consumers.regimens.currentRegimen = regimenUuid; state.resources.consumers.regimens.currentRegimen = regimenUuid;
state.resources.consumers.regimens.selectedSequenceUUID = sequenceUuid; state.resources.consumers.regimens.selectedSequenceUUID = sequenceUuid;
state.resources.consumers.regimens.dailyOffsetMs = 2000; state.resources.consumers.regimens.dailyOffsetMs = 2000;
state.resources.consumers.regimens.weeks = [{ const week = newWeek();
days: week.days.day1 = true;
{ state.resources.consumers.regimens.weeks = [week];
day1: true,
day2: false,
day3: false,
day4: false,
day5: false,
day6: false,
day7: false
}
}];
return state; 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 { Actions } from "../../../constants";
import { fakeSequence } from "../../../__test_support__/fake_state/resources"; import { fakeSequence } from "../../../__test_support__/fake_state/resources";
import { AddButton } from "../add_button"; import { AddButton } from "../add_button";
import { newWeek } from "../../reducer";
describe("<BulkScheduler />", () => { describe("<BulkScheduler />", () => {
const weeks = [{ const week = newWeek();
days: week.days.day1 = true;
{ const weeks = [week];
day1: true,
day2: false,
day3: false,
day4: false,
day5: false,
day6: false,
day7: false
}
}];
function fakeProps(): BulkEditorProps { function fakeProps(): BulkEditorProps {
const sequence = fakeSequence(); const sequence = fakeSequence();

View File

@ -3,20 +3,12 @@ import { mount } from "enzyme";
import { WeekGrid } from "../week_grid"; import { WeekGrid } from "../week_grid";
import { WeekGridProps } from "../interfaces"; import { WeekGridProps } from "../interfaces";
import { Actions } from "../../../constants"; import { Actions } from "../../../constants";
import { newWeek } from "../../reducer";
describe("<WeekGrid />", () => { describe("<WeekGrid />", () => {
const weeks = [{ const week = newWeek();
days: week.days.day1 = true;
{ const weeks = [week];
day1: true,
day2: false,
day3: false,
day4: false,
day5: false,
day6: false,
day7: false
}
}];
it("renders", () => { it("renders", () => {
const props: WeekGridProps = { weeks, dispatch: jest.fn() }; 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,8 +6,7 @@ import { t } from "../../i18next_wrapper";
export function WeekRow({ index, dispatch, week }: WeekRowProps) { export function WeekRow({ index, dispatch, week }: WeekRowProps) {
return <div className="week-row"> return <div className="week-row">
<label className="week-label">{t("Week")} {index + 1}</label> <label className="week-label">{t("Week")} {index + 1}</label>
{ {DAYS.map(function (day, i) {
DAYS.map(function (day, i) {
const id = `${index}-${day}`; const id = `${index}-${day}`;
return <Day day={i + 1} return <Day day={i + 1}
week={index} week={index}
@ -15,8 +14,7 @@ export function WeekRow({ index, dispatch, week }: WeekRowProps) {
id={id} id={id}
key={id} key={id}
active={week.days[day]} />; active={week.days[day]} />;
}) })}
}
</div>; </div>;
} }

View File

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

View File

@ -33,7 +33,8 @@ describe("<StepButtonCluster />", () => {
it("has correct drag data", () => { it("has correct drag data", () => {
const p = fakeProps(); const p = fakeProps();
const wrapper = mount(<StepButtonCluster {...p} />); 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"); expect(stepButton.text().toLowerCase()).toEqual("take photo");
stepButton.simulate("dragStart", { dataTransfer: { setData: jest.fn() } }); stepButton.simulate("dragStart", { dataTransfer: { setData: jest.fn() } });
expect(p.dispatch).toHaveBeenCalledWith(expect.objectContaining({ expect(p.dispatch).toHaveBeenCalledWith(expect.objectContaining({

View File

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

View File

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

View File

@ -104,10 +104,19 @@ export enum FbosVersionFallback {
NULL = "0.0.0", 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 * Determine whether a feature should be displayed based on
* the user's current FBOS version. Min FBOS version feature data is pulled * the user's current FBOS version. Min FBOS version feature data is pulled
* from an external source to allow App and FBOS development flexibility. * 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 current installed OS version string to compare against data ("0.0.0")
* @param lookupData min req versions data, for example {"feature": "1.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 || const fallback = globalConfig.FBOS_END_OF_LIFE_VERSION ||
FbosVersionFallback.NULL; FbosVersionFallback.NULL;
const target = override || current || fallback; const target = override || current || fallback;
const table = lookupData || {}; const table = lookupData || fallbackData;
const min = table[feature] || MinVersionOverride.NEVER; const min = table[feature] || MinVersionOverride.NEVER;
switch (semverCompare(target, min)) { switch (semverCompare(target, min)) {
case SemverResult.LEFT_IS_GREATER: case SemverResult.LEFT_IS_GREATER: