diff --git a/frontend/farm_designer/farm_events/__tests__/edit_fe_form_test.tsx b/frontend/farm_designer/farm_events/__tests__/edit_fe_form_test.tsx index 46ea654af..8b601b4a8 100644 --- a/frontend/farm_designer/farm_events/__tests__/edit_fe_form_test.tsx +++ b/frontend/farm_designer/farm_events/__tests__/edit_fe_form_test.tsx @@ -23,16 +23,15 @@ import { isString, isFunction } from "lodash"; import { repeatOptions } from "../map_state_to_props_add_edit"; import { SpecialStatus, ParameterApplication } from "farmbot"; import moment from "moment"; -import { fakeState } from "../../../__test_support__/fake_state"; import { history } from "../../../history"; import { buildResourceIndex } from "../../../__test_support__/resource_index_builder"; import { fakeVariableNameSet } from "../../../__test_support__/fake_variables"; 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 { error, success, warning } from "../../../toast/toast"; +import { error, success } from "../../../toast/toast"; const mockSequence = fakeSequence(); @@ -275,10 +274,14 @@ describe("", () => { it("warns about missed regimen items", async () => { 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"; + 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.end_time = "2017-05-22T06:00:00.000Z"; const i = instance(p); @@ -334,14 +337,15 @@ describe("", () => { 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(); p.title = "edit"; p.farmEvent.body.start_time = "2017-05-22T05:00:00.000Z"; p.farmEvent.body.end_time = "2017-05-22T06:00:00.000Z"; const i = instance(p); 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 () => { @@ -351,8 +355,22 @@ describe("", () => { p.farmEvent.body.end_time = "2017-05-22T06:00:00.000Z"; const i = instance(p); await i.commitViewModel(moment("2017-06-22T05:00:00.000Z")); - expect(warning).toHaveBeenCalledWith(expect.stringContaining( - "Nothing to run.")); + expect(error).toHaveBeenCalledWith(expect.stringContaining( + "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", () => { diff --git a/frontend/farm_designer/farm_events/__tests__/util_test.ts b/frontend/farm_designer/farm_events/__tests__/util_test.ts index 2803dd650..46177d50e 100644 --- a/frontend/farm_designer/farm_events/__tests__/util_test.ts +++ b/frontend/farm_designer/farm_events/__tests__/util_test.ts @@ -1,11 +1,14 @@ import { maybeWarnAboutMissedTasks } from "../util"; import { fakeFarmEvent } from "../../../__test_support__/fake_state/resources"; import moment from "moment"; +import { ExecutableType } from "farmbot/dist/resources/api_resources"; describe("maybeWarnAboutMissedTasks()", () => { - function testWarn(time: string): () => void { + function testWarn( + time: string, executableType: ExecutableType = "Regimen" + ): () => void { const callback = jest.fn(); - const fe = fakeFarmEvent("Regimen", 1); + const fe = fakeFarmEvent(executableType, 1); fe.body.start_time = "2017-05-21T22:00:00.000"; maybeWarnAboutMissedTasks(fe, () => callback("missed event warning"), @@ -21,4 +24,9 @@ describe("maybeWarnAboutMissedTasks()", () => { const cb = testWarn("2017-05-01T22:00:00.000"); 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(); + }); }); diff --git a/frontend/farm_designer/farm_events/edit_fe_form.tsx b/frontend/farm_designer/farm_events/edit_fe_form.tsx index 7b8dc3f9e..6185e57da 100644 --- a/frontend/farm_designer/farm_events/edit_fe_form.tsx +++ b/frontend/farm_designer/farm_events/edit_fe_form.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import moment from "moment"; -import { success, error, warning } from "../../toast/toast"; +import { success, error } from "../../toast/toast"; import { TaggedFarmEvent, SpecialStatus, TaggedSequence, TaggedRegimen, ParameterApplication @@ -94,6 +94,13 @@ const startTimeWarning = () => { 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 }; /** Take a FormViewModel and recombine the fields into a FarmEvent @@ -323,9 +330,8 @@ export class EditFEForm extends React.Component { return recombine(vm, opts); } - /** Use the next item run time to display toast messages and return to - * the form if necessary. */ - nextRunTimeActions = (now = moment()): boolean => { + /** Use the next item run time to display toast messages. */ + nextRunTimeActions = (now = moment()) => { const nextRun = this.nextItemTime(this.props.farmEvent.body, now); if (nextRun) { const nextRunText = this.props.autoSyncEnabled @@ -335,10 +341,6 @@ export class EditFEForm extends React.Component { you must first SYNC YOUR DEVICE. If you do not sync, the event will not run.`.replace(/\s+/g, " "), { timeFromNow: nextRun.from(now) }); 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 { * - Regimen Farm Event: * * If scheduled for today, warn about the possibility of missing tasks. * * 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: * * Determine the time for the next item to be run. * * 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()) => { if (this.maybeRejectStartTime(this.updatedFarmEvent)) { return startTimeWarning(); } + if (!this.nextItemTime(this.updatedFarmEvent, now)) { + return nothingToRunWarning(); + } this.dispatch(overwrite(this.props.farmEvent, this.updatedFarmEvent)); this.dispatch(save(this.props.farmEvent.uuid)) .then(() => { this.setState({ specialStatusLocal: SpecialStatus.SAVED }); this.dispatch(maybeWarnAboutMissedTasks(this.props.farmEvent, () => alert(t(Content.REGIMEN_TODAY_SKIPPED_ITEM_RISK)), now)); - const itemsScheduled = this.nextRunTimeActions(now); - if (itemsScheduled) { history.push("/app/designer/events"); } + this.nextRunTimeActions(now); + history.push("/app/designer/events"); }) .catch(() => { error(t("Unable to save event."));