Merge pull request #940 from gabrielburnworth/staging

Misc fixes
pull/941/head
Rick Carlino 2018-07-30 15:18:39 -05:00 committed by GitHub
commit fbdebb1b76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 146 additions and 76 deletions

View File

@ -129,7 +129,7 @@ const DataPoints = ({ plotProps, parentProps }: {
plotProps: PlotProps, parentProps: SensorReadingPlotProps plotProps: PlotProps, parentProps: SensorReadingPlotProps
}) => }) =>
<g id="sensor-readings"> <g id="sensor-readings">
{["current", "previous"].map((period: "current" | "previous") => {["previous", "current"].map((period: "current" | "previous") =>
<g id={period} key={period}> <g id={period} key={period}>
{parentProps.readingsForPeriod(period).map(r => { {parentProps.readingsForPeriod(period).map(r => {
const created_at = const created_at =

View File

@ -90,18 +90,19 @@ export class SensorReadingsTable
<table className="sensor-history-table-contents"> <table className="sensor-history-table-contents">
<tbody> <tbody>
{["current", "previous"].map((period: "current" | "previous") => { {["current", "previous"].map((period: "current" | "previous") => {
return this.props.readingsForPeriod(period).map(sensorReading => { return this.props.readingsForPeriod(period).reverse()
const pin = sensorReading.body.pin; .map(sensorReading => {
const sensorName = `${sensorNameByPinLookup[pin]} (pin ${pin})`; const pin = sensorReading.body.pin;
return <TableRow const sensorName = `${sensorNameByPinLookup[pin]} (pin ${pin})`;
key={sensorReading.uuid} return <TableRow
sensorName={sensorName} key={sensorReading.uuid}
sensorReading={sensorReading} sensorName={sensorName}
timeOffset={this.props.timeOffset} sensorReading={sensorReading}
period={period} timeOffset={this.props.timeOffset}
hover={this.props.hover} period={period}
hovered={this.props.hovered} />; hover={this.props.hover}
}); hovered={this.props.hovered} />;
});
})} })}
</tbody> </tbody>
</table> </table>

View File

@ -280,7 +280,7 @@ a {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
ul { ul {
font-size: 0.3rem; font-size: 0.75rem;
} }
} }

View File

@ -31,15 +31,16 @@ describe("<OsUpdateButton/>", () => {
}; };
it("renders buttons: not connected", () => { it("renders buttons: not connected", () => {
bot.hardware.informational_settings.controller_version = undefined;
bot.currentOSVersion = undefined; bot.currentOSVersion = undefined;
const buttons = mount(<OsUpdateButton {...fakeProps()} />); const buttons = mount(<OsUpdateButton {...fakeProps()} />);
expect(buttons.find("button").length).toBe(1); expect(buttons.find("button").length).toBe(1);
const autoUpdate = buttons.find("button").first(); const autoUpdate = buttons.find("button").first();
expect(autoUpdate.hasClass("yellow")).toBeTruthy(); expect(autoUpdate.hasClass("yellow")).toBeTruthy();
const osUpdateButton = buttons.find("button").last(); const osUpdateButton = buttons.find("button").last();
expect(osUpdateButton.text()).toBe("Can't connect to release server"); expect(osUpdateButton.text()).toBe("Can't connect to bot");
}); });
it("renders buttons: not connected to bot", () => { it("renders buttons: connected to releases but not bot", () => {
bot.hardware.informational_settings.controller_version = undefined; bot.hardware.informational_settings.controller_version = undefined;
const buttons = mount(<OsUpdateButton {...fakeProps()} />); const buttons = mount(<OsUpdateButton {...fakeProps()} />);
expect(buttons.find("button").length).toBe(1); expect(buttons.find("button").length).toBe(1);
@ -48,14 +49,30 @@ describe("<OsUpdateButton/>", () => {
const osUpdateButton = buttons.find("button").last(); const osUpdateButton = buttons.find("button").last();
expect(osUpdateButton.text()).toBe("Can't connect to bot"); expect(osUpdateButton.text()).toBe("Can't connect to bot");
}); });
it("renders buttons: no beta releases", () => { it("renders buttons: no releases available", () => {
bot.hardware.informational_settings.controller_version = "3.1.6";
bot.hardware.configuration.beta_opt_in = true; bot.hardware.configuration.beta_opt_in = true;
bot.currentOSVersion = undefined;
const buttons = mount(<OsUpdateButton {...fakeProps()} />); const buttons = mount(<OsUpdateButton {...fakeProps()} />);
expect(buttons.find("button").length).toBe(1);
const autoUpdate = buttons.find("button").first();
expect(autoUpdate.hasClass("yellow")).toBeTruthy();
const osUpdateButton = buttons.find("button").last(); const osUpdateButton = buttons.find("button").last();
expect(osUpdateButton.text()).toBe("No beta releases available"); expect(osUpdateButton.text()).toBe("Can't connect to release server");
});
it("renders buttons: only beta release available", () => {
bot.hardware.informational_settings.controller_version = "3.1.6";
bot.hardware.configuration.beta_opt_in = true;
bot.currentOSVersion = undefined;
bot.currentBetaOSVersion = "3.1.7";
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
const osUpdateButton = buttons.find("button").last();
expect(osUpdateButton.text()).toBe("UPDATE");
});
it("renders buttons: no beta release available", () => {
bot.hardware.informational_settings.controller_version = "3.1.6";
bot.hardware.configuration.beta_opt_in = true;
bot.currentBetaOSVersion = undefined;
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
const osUpdateButton = buttons.find("button").last();
expect(osUpdateButton.text()).toBe("UP TO DATE");
}); });
it("up to date", () => { it("up to date", () => {
bot.hardware.informational_settings.controller_version = "3.1.6"; bot.hardware.informational_settings.controller_version = "3.1.6";
@ -87,6 +104,25 @@ describe("<OsUpdateButton/>", () => {
expect(osUpdateButton.text()).toBe("UPDATE"); expect(osUpdateButton.text()).toBe("UPDATE");
expect(osUpdateButton.props().title).toBe("5.0.0-beta"); expect(osUpdateButton.props().title).toBe("5.0.0-beta");
}); });
it("latest newer than beta update: latest installed", () => {
bot.hardware.informational_settings.controller_version = "3.1.6";
bot.hardware.configuration.beta_opt_in = true;
bot.currentBetaOSVersion = "3.1.4-beta";
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
const osUpdateButton = buttons.find("button").last();
expect(osUpdateButton.text()).toBe("UP TO DATE");
expect(osUpdateButton.props().title).toBe("3.1.6");
});
it("latest newer than beta update: beta installed", () => {
bot.hardware.informational_settings.controller_version = "3.1.5";
bot.hardware.configuration.beta_opt_in = true;
bot.currentBetaOSVersion = "3.1.5-beta";
const buttons = mount(<OsUpdateButton {...fakeProps()} />);
const osUpdateButton = buttons.find("button").last();
expect(osUpdateButton.text()).toBe("UPDATE");
expect(osUpdateButton.props().title).toBe("3.1.6");
});
it("beta update has same numeric version: newer commit", () => { it("beta update has same numeric version: newer commit", () => {
bot.hardware.informational_settings.controller_version = "5.0.0"; bot.hardware.informational_settings.controller_version = "5.0.0";
bot.hardware.informational_settings.commit = "old commit"; bot.hardware.informational_settings.commit = "old commit";

View File

@ -1,21 +1,23 @@
import * as React from "react"; import * as React from "react";
import { t } from "i18next"; import { t } from "i18next";
import * as _ from "lodash";
import { JobProgress } from "farmbot/dist"; import { JobProgress } from "farmbot/dist";
import { SemverResult, semverCompare } from "../../../util"; 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";
enum UpdateButton { upToDate, needsUpdate, unknown, unknownBeta, none } /** FBOS update button states. */
enum UpdateButton { upToDate, needsUpdate, unknown, none }
const buttonProps = (status: UpdateButton) => { /** FBOS update button state => props. */
const buttonProps = (status: UpdateButton): {
color: "green" | "gray" | "yellow", text: string
} => {
switch (status) { switch (status) {
case UpdateButton.needsUpdate: case UpdateButton.needsUpdate:
return { color: "green", text: t("UPDATE") }; return { color: "green", text: t("UPDATE") };
case UpdateButton.upToDate: case UpdateButton.upToDate:
return { color: "gray", text: t("UP TO DATE") }; return { color: "gray", text: t("UP TO DATE") };
case UpdateButton.unknownBeta:
return { color: "yellow", text: t("No beta releases available") };
case UpdateButton.unknown: case UpdateButton.unknown:
return { color: "yellow", text: t("Can't connect to release server") }; return { color: "yellow", text: t("Can't connect to release server") };
default: default:
@ -23,68 +25,99 @@ const buttonProps = (status: UpdateButton) => {
} }
}; };
export let OsUpdateButton = (props: OsUpdateButtonProps) => { /** FBOS update download in progress. */
const isWorking = (job: JobProgress | undefined) =>
job && (job.status == "working");
/** FBOS update download progress. */
function downloadProgress(job: JobProgress | undefined) {
if (job && isWorking(job)) {
switch (job.unit) {
case "bytes":
const kiloBytes = Math.round(job.bytes / 1024);
const megaBytes = Math.round(job.bytes / 1048576);
if (kiloBytes < 1) {
return job.bytes + "B";
} else if (megaBytes < 1) {
return kiloBytes + "kB";
} else {
return megaBytes + "MB";
}
case "percent":
return job.percent + "%";
}
}
}
/** Determine the latest available version. */
const getLatestVersion = (
currentOSVersion: string | undefined,
currentBetaOSVersion: string | undefined,
betaOptIn: boolean
): string | undefined => {
if (!betaOptIn) { return currentOSVersion; }
switch (semverCompare(currentOSVersion || "", currentBetaOSVersion || "")) {
case SemverResult.RIGHT_IS_GREATER: return currentBetaOSVersion;
default: return currentOSVersion;
}
};
/** Unequal beta commits => needs update. */
const betaCommitsAreEqual = (
fbosCommit: string | undefined,
currentBetaOSCommit: string | undefined): boolean =>
!(isString(fbosCommit) && isString(currentBetaOSCommit)
&& fbosCommit !== currentBetaOSCommit);
/** Determine the FBOS update button state. */
const compareWithBotVersion = (
candidate: string | undefined,
controller_version: string | undefined
): UpdateButton => {
if (!isString(controller_version)) { return UpdateButton.none; }
if (!isString(candidate)) { return UpdateButton.unknown; }
switch (semverCompare(candidate, controller_version)) {
case SemverResult.RIGHT_IS_GREATER:
case SemverResult.EQUAL:
return UpdateButton.upToDate;
default:
return UpdateButton.needsUpdate;
}
};
export const OsUpdateButton = (props: OsUpdateButtonProps) => {
const { bot, sourceFbosConfig, botOnline } = props; const { bot, sourceFbosConfig, botOnline } = props;
let buttonStatus = UpdateButton.none; let buttonStatus = UpdateButton.none;
/** FBOS beta release opt-in setting. */
const betaOptIn = sourceFbosConfig("beta_opt_in").value; const betaOptIn = sourceFbosConfig("beta_opt_in").value;
// Information about available releases.
const { currentOSVersion, currentBetaOSVersion, currentBetaOSCommit } = bot; const { currentOSVersion, currentBetaOSVersion, currentBetaOSCommit } = bot;
const { commit } = bot.hardware.informational_settings; // Currently installed FBOS version data.
const latestReleaseV = betaOptIn const { controller_version, commit } = bot.hardware.informational_settings;
? currentBetaOSVersion
: currentOSVersion; /** Newest release version, given settings and data available. */
const { controller_version } = bot.hardware.informational_settings; const latestReleaseV =
if (_.isString(latestReleaseV) && _.isString(controller_version)) { getLatestVersion(currentOSVersion, currentBetaOSVersion, !!betaOptIn);
switch (semverCompare(latestReleaseV, controller_version)) { // Set FBOS update button status.
case SemverResult.RIGHT_IS_GREATER: buttonStatus = compareWithBotVersion(latestReleaseV, controller_version);
case SemverResult.EQUAL:
buttonStatus = UpdateButton.upToDate; // Button status modification for beta release edge cases.
break; if ((buttonStatus === UpdateButton.upToDate) && betaOptIn &&
default: latestReleaseV === currentBetaOSVersion &&
buttonStatus = UpdateButton.needsUpdate; !betaCommitsAreEqual(commit, currentBetaOSCommit)) {
}
} else {
if (!_.isString(latestReleaseV)) {
buttonStatus = betaOptIn
? UpdateButton.unknownBeta
: UpdateButton.unknown;
}
}
if (betaOptIn
&& _.isString(commit) && _.isString(currentBetaOSCommit)
&& commit !== currentBetaOSCommit) {
buttonStatus = UpdateButton.needsUpdate; buttonStatus = UpdateButton.needsUpdate;
} }
/** FBOS update download progress data. */
const osUpdateJob = (bot.hardware.jobs || {})["FBOS_OTA"]; const osUpdateJob = (bot.hardware.jobs || {})["FBOS_OTA"];
const isWorking = (job: JobProgress | undefined) =>
job && (job.status == "working");
function downloadProgress(job: JobProgress | undefined) {
if (job && isWorking(job)) {
switch (job.unit) {
case ("bytes"):
const kiloBytes = Math.round(job.bytes / 1024);
const megaBytes = Math.round(job.bytes / 1048576);
if (kiloBytes < 1) {
return job.bytes + "B";
} else if (megaBytes < 1) {
return kiloBytes + "kB";
} else {
return megaBytes + "MB";
}
case ("percent"):
return job.percent + "%";
}
}
}
return <button return <button
className={"fb-button " + buttonProps(buttonStatus).color} className={"fb-button " + buttonProps(buttonStatus).color}
title={latestReleaseV} title={latestReleaseV}
disabled={isWorking(osUpdateJob) || !botOnline} disabled={isWorking(osUpdateJob) || !botOnline}
onClick={() => checkControllerUpdates()}> onClick={checkControllerUpdates}>
{downloadProgress(osUpdateJob) || buttonProps(buttonStatus).text} {downloadProgress(osUpdateJob) || buttonProps(buttonStatus).text}
</button>; </button>;
}; };

View File

@ -84,7 +84,7 @@ export class Logs extends React.Component<LogsProps, Partial<LogsState>> {
</Col> </Col>
<Col xs={2}> <Col xs={2}>
<div className={"settings-menu-button"}> <div className={"settings-menu-button"}>
<Popover position={Position.BOTTOM_RIGHT}> <Popover position={Position.TOP_RIGHT}>
<i className="fa fa-gear" /> <i className="fa fa-gear" />
<LogsSettingsMenu <LogsSettingsMenu
setFilterLevel={this.setFilterLevel} setFilterLevel={this.setFilterLevel}
@ -93,7 +93,7 @@ export class Logs extends React.Component<LogsProps, Partial<LogsState>> {
</Popover> </Popover>
</div> </div>
<div className={"settings-menu-button"}> <div className={"settings-menu-button"}>
<Popover position={Position.BOTTOM_RIGHT}> <Popover position={Position.TOP_RIGHT}>
<button className={`fb-button ${filterBtnColor}`}> <button className={`fb-button ${filterBtnColor}`}>
{this.filterActive ? t("Filters active") : t("filter")} {this.filterActive ? t("Filters active") : t("filter")}
</button> </button>