Merge pull request #1285 from gabrielburnworth/staging
Prevent saving of empty Farm Eventspull/1286/head
commit
8bc3849e6c
|
@ -23,16 +23,15 @@ import { isString, isFunction } from "lodash";
|
||||||
import { repeatOptions } from "../map_state_to_props_add_edit";
|
import { repeatOptions } from "../map_state_to_props_add_edit";
|
||||||
import { SpecialStatus, ParameterApplication } from "farmbot";
|
import { SpecialStatus, ParameterApplication } from "farmbot";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { fakeState } from "../../../__test_support__/fake_state";
|
|
||||||
import { history } from "../../../history";
|
import { history } from "../../../history";
|
||||||
import {
|
import {
|
||||||
buildResourceIndex
|
buildResourceIndex
|
||||||
} from "../../../__test_support__/resource_index_builder";
|
} from "../../../__test_support__/resource_index_builder";
|
||||||
import { fakeVariableNameSet } from "../../../__test_support__/fake_variables";
|
import { fakeVariableNameSet } from "../../../__test_support__/fake_variables";
|
||||||
import { clickButton } from "../../../__test_support__/helpers";
|
import { clickButton } from "../../../__test_support__/helpers";
|
||||||
import { destroy } from "../../../api/crud";
|
import { destroy, save } from "../../../api/crud";
|
||||||
import { fakeTimeSettings } from "../../../__test_support__/fake_time_settings";
|
import { fakeTimeSettings } from "../../../__test_support__/fake_time_settings";
|
||||||
import { error, success, warning } from "../../../toast/toast";
|
import { error, success } from "../../../toast/toast";
|
||||||
|
|
||||||
const mockSequence = fakeSequence();
|
const mockSequence = fakeSequence();
|
||||||
|
|
||||||
|
@ -275,10 +274,14 @@ describe("<FarmEventForm/>", () => {
|
||||||
|
|
||||||
it("warns about missed regimen items", async () => {
|
it("warns about missed regimen items", async () => {
|
||||||
const p = props();
|
const p = props();
|
||||||
const state = fakeState();
|
|
||||||
state.resources.index.references = { [p.farmEvent.uuid]: p.farmEvent };
|
|
||||||
p.dispatch = jest.fn(x => { isFunction(x) && x(); return Promise.resolve(); });
|
|
||||||
p.farmEvent.body.executable_type = "Regimen";
|
p.farmEvent.body.executable_type = "Regimen";
|
||||||
|
const regimen = fakeRegimen();
|
||||||
|
regimen.body.regimen_items = [
|
||||||
|
{ sequence_id: -1, time_offset: 0 },
|
||||||
|
{ sequence_id: -1, time_offset: 1000000000 },
|
||||||
|
];
|
||||||
|
p.findExecutable = () => regimen;
|
||||||
|
p.dispatch = jest.fn(x => { isFunction(x) && x(); return Promise.resolve(); });
|
||||||
p.farmEvent.body.start_time = "2017-05-22T05:00:00.000Z";
|
p.farmEvent.body.start_time = "2017-05-22T05:00:00.000Z";
|
||||||
p.farmEvent.body.end_time = "2017-05-22T06:00:00.000Z";
|
p.farmEvent.body.end_time = "2017-05-22T06:00:00.000Z";
|
||||||
const i = instance(p);
|
const i = instance(p);
|
||||||
|
@ -334,14 +337,15 @@ describe("<FarmEventForm/>", () => {
|
||||||
expectStartTimeToBeRejected();
|
expectStartTimeToBeRejected();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("doesn't display error message on edit: start time has passed", () => {
|
it("displays error message on edit: start time has passed", () => {
|
||||||
const p = props();
|
const p = props();
|
||||||
p.title = "edit";
|
p.title = "edit";
|
||||||
p.farmEvent.body.start_time = "2017-05-22T05:00:00.000Z";
|
p.farmEvent.body.start_time = "2017-05-22T05:00:00.000Z";
|
||||||
p.farmEvent.body.end_time = "2017-05-22T06:00:00.000Z";
|
p.farmEvent.body.end_time = "2017-05-22T06:00:00.000Z";
|
||||||
const i = instance(p);
|
const i = instance(p);
|
||||||
i.commitViewModel(moment("2017-06-22T05:00:00.000Z"));
|
i.commitViewModel(moment("2017-06-22T05:00:00.000Z"));
|
||||||
expect(error).not.toHaveBeenCalled();
|
expect(error).toHaveBeenCalledWith(expect.stringContaining(
|
||||||
|
"Nothing to run."), "Unable to save event.");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("displays error message on save: no items", async () => {
|
it("displays error message on save: no items", async () => {
|
||||||
|
@ -351,8 +355,22 @@ describe("<FarmEventForm/>", () => {
|
||||||
p.farmEvent.body.end_time = "2017-05-22T06:00:00.000Z";
|
p.farmEvent.body.end_time = "2017-05-22T06:00:00.000Z";
|
||||||
const i = instance(p);
|
const i = instance(p);
|
||||||
await i.commitViewModel(moment("2017-06-22T05:00:00.000Z"));
|
await i.commitViewModel(moment("2017-06-22T05:00:00.000Z"));
|
||||||
expect(warning).toHaveBeenCalledWith(expect.stringContaining(
|
expect(error).toHaveBeenCalledWith(expect.stringContaining(
|
||||||
"Nothing to run."));
|
"Nothing to run."), "Unable to save event.");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("displays error message on save: save error", async () => {
|
||||||
|
const p = props();
|
||||||
|
p.dispatch = jest.fn()
|
||||||
|
.mockResolvedValueOnce("")
|
||||||
|
.mockRejectedValueOnce("error");
|
||||||
|
p.shouldDisplay = () => true;
|
||||||
|
p.farmEvent.body.start_time = "2017-07-22T05:00:00.000Z";
|
||||||
|
p.farmEvent.body.end_time = "2017-07-22T06:00:00.000Z";
|
||||||
|
const i = instance(p);
|
||||||
|
await i.commitViewModel(moment("2017-06-22T05:00:00.000Z"));
|
||||||
|
await expect(save).toHaveBeenCalled();
|
||||||
|
expect(error).toHaveBeenCalledWith("Unable to save event.");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows start time: edit with unsupported OS", () => {
|
it("allows start time: edit with unsupported OS", () => {
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import { maybeWarnAboutMissedTasks } from "../util";
|
import { maybeWarnAboutMissedTasks } from "../util";
|
||||||
import { fakeFarmEvent } from "../../../__test_support__/fake_state/resources";
|
import { fakeFarmEvent } from "../../../__test_support__/fake_state/resources";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
import { ExecutableType } from "farmbot/dist/resources/api_resources";
|
||||||
|
|
||||||
describe("maybeWarnAboutMissedTasks()", () => {
|
describe("maybeWarnAboutMissedTasks()", () => {
|
||||||
function testWarn(time: string): () => void {
|
function testWarn(
|
||||||
|
time: string, executableType: ExecutableType = "Regimen"
|
||||||
|
): () => void {
|
||||||
const callback = jest.fn();
|
const callback = jest.fn();
|
||||||
const fe = fakeFarmEvent("Regimen", 1);
|
const fe = fakeFarmEvent(executableType, 1);
|
||||||
fe.body.start_time = "2017-05-21T22:00:00.000";
|
fe.body.start_time = "2017-05-21T22:00:00.000";
|
||||||
maybeWarnAboutMissedTasks(fe,
|
maybeWarnAboutMissedTasks(fe,
|
||||||
() => callback("missed event warning"),
|
() => callback("missed event warning"),
|
||||||
|
@ -21,4 +24,9 @@ describe("maybeWarnAboutMissedTasks()", () => {
|
||||||
const cb = testWarn("2017-05-01T22:00:00.000");
|
const cb = testWarn("2017-05-01T22:00:00.000");
|
||||||
expect(cb).not.toHaveBeenCalled();
|
expect(cb).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("doesn't warn when not a regimen", () => {
|
||||||
|
const cb = testWarn("2017-05-21T22:00:00.000", "Sequence");
|
||||||
|
expect(cb).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { success, error, warning } from "../../toast/toast";
|
import { success, error } from "../../toast/toast";
|
||||||
import {
|
import {
|
||||||
TaggedFarmEvent, SpecialStatus, TaggedSequence, TaggedRegimen,
|
TaggedFarmEvent, SpecialStatus, TaggedSequence, TaggedRegimen,
|
||||||
ParameterApplication
|
ParameterApplication
|
||||||
|
@ -94,6 +94,13 @@ const startTimeWarning = () => {
|
||||||
error(message, title);
|
error(message, title);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const nothingToRunWarning = () => {
|
||||||
|
const message =
|
||||||
|
t("All items scheduled before the start time. Nothing to run.");
|
||||||
|
const title = t("Unable to save event.");
|
||||||
|
error(message, title);
|
||||||
|
};
|
||||||
|
|
||||||
type RecombineOptions = { forceRegimensToMidnight: boolean };
|
type RecombineOptions = { forceRegimensToMidnight: boolean };
|
||||||
|
|
||||||
/** Take a FormViewModel and recombine the fields into a FarmEvent
|
/** Take a FormViewModel and recombine the fields into a FarmEvent
|
||||||
|
@ -323,9 +330,8 @@ export class EditFEForm extends React.Component<EditFEProps, State> {
|
||||||
return recombine(vm, opts);
|
return recombine(vm, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Use the next item run time to display toast messages and return to
|
/** Use the next item run time to display toast messages. */
|
||||||
* the form if necessary. */
|
nextRunTimeActions = (now = moment()) => {
|
||||||
nextRunTimeActions = (now = moment()): boolean => {
|
|
||||||
const nextRun = this.nextItemTime(this.props.farmEvent.body, now);
|
const nextRun = this.nextItemTime(this.props.farmEvent.body, now);
|
||||||
if (nextRun) {
|
if (nextRun) {
|
||||||
const nextRunText = this.props.autoSyncEnabled
|
const nextRunText = this.props.autoSyncEnabled
|
||||||
|
@ -335,10 +341,6 @@ export class EditFEForm extends React.Component<EditFEProps, State> {
|
||||||
you must first SYNC YOUR DEVICE. If you do not sync, the event will
|
you must first SYNC YOUR DEVICE. If you do not sync, the event will
|
||||||
not run.`.replace(/\s+/g, " "), { timeFromNow: nextRun.from(now) });
|
not run.`.replace(/\s+/g, " "), { timeFromNow: nextRun.from(now) });
|
||||||
success(nextRunText);
|
success(nextRunText);
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
warning(t("All items scheduled before the start time. Nothing to run."));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,24 +348,27 @@ export class EditFEForm extends React.Component<EditFEProps, State> {
|
||||||
* - Regimen Farm Event:
|
* - Regimen Farm Event:
|
||||||
* * If scheduled for today, warn about the possibility of missing tasks.
|
* * If scheduled for today, warn about the possibility of missing tasks.
|
||||||
* * Display the start time difference from now and maybe prompt to sync.
|
* * Display the start time difference from now and maybe prompt to sync.
|
||||||
* * Return to calendar view if items exist to be run past the start time.
|
* * Return to calendar view.
|
||||||
* - Sequence Farm Event:
|
* - Sequence Farm Event:
|
||||||
* * Determine the time for the next item to be run.
|
* * Determine the time for the next item to be run.
|
||||||
* * If auto-sync is disabled, prompt the user to sync.
|
* * If auto-sync is disabled, prompt the user to sync.
|
||||||
* * Return to calendar view only if more items exist to be run.
|
* * Return to calendar view.
|
||||||
*/
|
*/
|
||||||
commitViewModel = (now = moment()) => {
|
commitViewModel = (now = moment()) => {
|
||||||
if (this.maybeRejectStartTime(this.updatedFarmEvent)) {
|
if (this.maybeRejectStartTime(this.updatedFarmEvent)) {
|
||||||
return startTimeWarning();
|
return startTimeWarning();
|
||||||
}
|
}
|
||||||
|
if (!this.nextItemTime(this.updatedFarmEvent, now)) {
|
||||||
|
return nothingToRunWarning();
|
||||||
|
}
|
||||||
this.dispatch(overwrite(this.props.farmEvent, this.updatedFarmEvent));
|
this.dispatch(overwrite(this.props.farmEvent, this.updatedFarmEvent));
|
||||||
this.dispatch(save(this.props.farmEvent.uuid))
|
this.dispatch(save(this.props.farmEvent.uuid))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.setState({ specialStatusLocal: SpecialStatus.SAVED });
|
this.setState({ specialStatusLocal: SpecialStatus.SAVED });
|
||||||
this.dispatch(maybeWarnAboutMissedTasks(this.props.farmEvent,
|
this.dispatch(maybeWarnAboutMissedTasks(this.props.farmEvent,
|
||||||
() => alert(t(Content.REGIMEN_TODAY_SKIPPED_ITEM_RISK)), now));
|
() => alert(t(Content.REGIMEN_TODAY_SKIPPED_ITEM_RISK)), now));
|
||||||
const itemsScheduled = this.nextRunTimeActions(now);
|
this.nextRunTimeActions(now);
|
||||||
if (itemsScheduled) { history.push("/app/designer/events"); }
|
history.push("/app/designer/events");
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
error(t("Unable to save event."));
|
error(t("Unable to save event."));
|
||||||
|
|
Loading…
Reference in New Issue