diff --git a/.circleci/config.yml b/.circleci/config.yml
index 68ce3361d0..a719f31e0b 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -69,7 +69,7 @@ commands:
- run:
name: Run JS tests
command: |
- sudo docker compose run web npm run test-slow -- -c .circleci/jest-ci.config.js
+ sudo docker compose run web npm run test-slow -- -c .circleci/jest-ci.config.js -w 6
echo 'export COVERAGE_AVAILABLE=true' >> $BASH_ENV
lint-commands:
steps:
diff --git a/Gemfile.lock b/Gemfile.lock
index 5a7745dc89..a9bd18e878 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -141,12 +141,12 @@ GEM
google-apis-core (>= 0.15.0, < 2.a)
google-apis-storage_v1 (0.50.0)
google-apis-core (>= 0.15.0, < 2.a)
- google-cloud-core (1.7.1)
+ google-cloud-core (1.8.0)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (2.2.1)
faraday (>= 1.0, < 3.a)
- google-cloud-errors (1.4.0)
+ google-cloud-errors (1.5.0)
google-cloud-storage (1.55.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
@@ -248,7 +248,7 @@ GEM
hashie (~> 4.1)
multi_json (~> 1.15)
racc (1.8.1)
- rack (2.2.11)
+ rack (2.2.13)
rack-attack (6.7.0)
rack (>= 1.0, < 4)
rack-cors (2.0.2)
@@ -376,7 +376,7 @@ GEM
rails
warden (1.2.9)
rack (>= 2.0.9)
- webmock (3.25.0)
+ webmock (3.25.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@@ -439,4 +439,4 @@ RUBY VERSION
ruby 3.3.7p123
BUNDLED WITH
- 2.6.2
+ 2.6.5
diff --git a/frontend/__test_support__/fake_designer_state.ts b/frontend/__test_support__/fake_designer_state.ts
index d4953a1dbf..1ffae18e72 100644
--- a/frontend/__test_support__/fake_designer_state.ts
+++ b/frontend/__test_support__/fake_designer_state.ts
@@ -1,4 +1,4 @@
-import { DesignerState } from "../farm_designer/interfaces";
+import { DesignerState, DrawnPointPayl } from "../farm_designer/interfaces";
import { HelpState } from "../help/reducer";
import { RunButtonMenuOpen } from "../sequences/interfaces";
@@ -20,7 +20,6 @@ export const fakeDesignerState = (): DesignerState => ({
bulkPlantSlug: undefined,
chosenLocation: { x: undefined, y: undefined, z: undefined },
drawnPoint: undefined,
- drawnWeed: undefined,
openedSavedGarden: undefined,
tryGroupSortType: undefined,
editGroupAreaInMap: false,
@@ -63,3 +62,13 @@ export const fakeMenuOpenState = (): RunButtonMenuOpen => ({
component: undefined,
uuid: undefined,
});
+
+export const fakeDrawnPoint = (): DrawnPointPayl => ({
+ name: "Fake Point",
+ cx: 10,
+ cy: 20,
+ r: 30,
+ color: "green",
+ z: 0,
+ at_soil_level: false,
+});
diff --git a/frontend/__tests__/hotkeys_test.tsx b/frontend/__tests__/hotkeys_test.tsx
index 18b81c9ab3..1794c6f00d 100644
--- a/frontend/__tests__/hotkeys_test.tsx
+++ b/frontend/__tests__/hotkeys_test.tsx
@@ -12,58 +12,80 @@ jest.mock("../api/crud", () => ({ save: jest.fn() }));
import React from "react";
import { shallow } from "enzyme";
import {
- HotKey, HotKeys, HotKeysProps, hotkeysWithActions, toggleHotkeyHelpOverlay,
+ HotKey, HotKeys, HotKeysProps, hotkeysWithActions, HotkeysWithActionsProps,
+ toggleHotkeyHelpOverlay,
} from "../hotkeys";
import { sync } from "../devices/actions";
import { save } from "../api/crud";
import { Actions } from "../constants";
import { Path } from "../internal_urls";
import { mockDispatch } from "../__test_support__/fake_dispatch";
+import {
+ fakeDesignerState, fakeDrawnPoint,
+} from "../__test_support__/fake_designer_state";
+import { resetDrawnPointDataAction } from "../points/create_points";
describe("hotkeysWithActions()", () => {
beforeEach(() => {
location.pathname = Path.mock(Path.designer());
});
+ const fakeProps = (): HotkeysWithActionsProps => ({
+ navigate: jest.fn(),
+ dispatch: jest.fn(),
+ designer: fakeDesignerState(),
+ slug: "",
+ });
+
it("has key bindings", () => {
- const dispatch = jest.fn();
- const navigate = jest.fn();
- const hotkeys = hotkeysWithActions(navigate, dispatch, "");
+ const p = fakeProps();
+ const hotkeys = hotkeysWithActions(p);
expect(Object.values(hotkeys).length).toBe(8);
const e = {} as KeyboardEvent;
hotkeys[HotKey.save].onKeyDown?.(e);
expect(save).not.toHaveBeenCalled();
mockState.resources.consumers.sequences.current = "uuid";
- const hotkeysSettingsPath = hotkeysWithActions(navigate, dispatch, "settings");
+ p.slug = "settings";
+ const hotkeysSettingsPath = hotkeysWithActions(p);
hotkeysSettingsPath[HotKey.save].onKeyDown?.(e);
expect(save).not.toHaveBeenCalled();
- const hotkeysSequencesPath = hotkeysWithActions(
- navigate, dispatch, "sequences");
+ p.slug = "sequences";
+ const hotkeysSequencesPath = hotkeysWithActions(p);
hotkeysSequencesPath[HotKey.save].onKeyDown?.(e);
expect(save).toHaveBeenCalledWith("uuid");
hotkeys[HotKey.sync].onKeyDown?.(e);
- expect(dispatch).toHaveBeenCalledWith(sync());
+ expect(p.dispatch).toHaveBeenCalledWith(sync());
hotkeys[HotKey.navigateRight].onKeyDown?.(e);
- expect(navigate).toHaveBeenCalledWith(Path.plants());
+ expect(p.navigate).toHaveBeenCalledWith(Path.plants());
hotkeys[HotKey.navigateLeft].onKeyDown?.(e);
- expect(navigate).toHaveBeenCalledWith(Path.settings());
+ expect(p.navigate).toHaveBeenCalledWith(Path.settings());
hotkeys[HotKey.addPlant].onKeyDown?.(e);
- expect(navigate).toHaveBeenCalledWith(Path.cropSearch());
+ expect(p.navigate).toHaveBeenCalledWith(Path.cropSearch());
hotkeys[HotKey.addEvent].onKeyDown?.(e);
- expect(navigate).toHaveBeenCalledWith(Path.farmEvents("add"));
+ expect(p.navigate).toHaveBeenCalledWith(Path.farmEvents("add"));
- const hotkeysWithDispatch =
- hotkeysWithActions(navigate, mockDispatch(dispatch), "");
+ p.slug = "";
+ const dispatch = jest.fn();
+ p.dispatch = mockDispatch(dispatch);
+ const hotkeysWithDispatch = hotkeysWithActions(p);
hotkeysWithDispatch[HotKey.closePanel].onKeyDown?.(e);
expect(dispatch).toHaveBeenCalledWith({
type: Actions.SET_PANEL_OPEN, payload: false,
});
+
+ p.dispatch = jest.fn();
+ const point = fakeDrawnPoint();
+ point.cx = 1;
+ p.designer.drawnPoint = point;
+ const hotkeysWithDrawnPoint = hotkeysWithActions(p);
+ hotkeysWithDrawnPoint[HotKey.closePanel].onKeyDown?.(e);
+ expect(p.dispatch).toHaveBeenCalledWith(resetDrawnPointDataAction());
});
});
@@ -81,6 +103,7 @@ describe("", () => {
const fakeProps = (): HotKeysProps => ({
dispatch: jest.fn(),
hotkeyGuide: false,
+ designer: fakeDesignerState(),
});
it("renders", () => {
diff --git a/frontend/app.tsx b/frontend/app.tsx
index 3c198dfbe2..a4211f8f45 100644
--- a/frontend/app.tsx
+++ b/frontend/app.tsx
@@ -185,7 +185,9 @@ export class RawApp extends React.Component {
{(Path.equals("") || Path.equals(Path.app())) && isString(landingPage) &&
}
{!syncLoaded && }
-
+
{syncLoaded && ;
+ navigate = this.context;
+
Realtime = () => {
const { informational_settings } = this.props.bot.hardware;
const {
@@ -157,7 +162,7 @@ export class Connectivity
-
+
{t("Learn more about ports")}
diff --git a/frontend/devices/connectivity/diagnosis.tsx b/frontend/devices/connectivity/diagnosis.tsx
index b86b6a0116..ecc9343aa2 100644
--- a/frontend/devices/connectivity/diagnosis.tsx
+++ b/frontend/devices/connectivity/diagnosis.tsx
@@ -69,11 +69,11 @@ export function Diagnosis(props: DiagnosisProps) {
{diagnosisMessage(getDiagnosisCode(props.statusFlags))}
-
+
{t("Click here to learn more about connectivity codes.")}
-
+
{t("Click here for document to show to your IT department.")}
diff --git a/frontend/devices/connectivity/qos_panel.tsx b/frontend/devices/connectivity/qos_panel.tsx
index fd941147ef..a2012a2f51 100644
--- a/frontend/devices/connectivity/qos_panel.tsx
+++ b/frontend/devices/connectivity/qos_panel.tsx
@@ -7,6 +7,7 @@ import React from "react";
import { t } from "../../i18next_wrapper";
import { docLinkClick, Saucer } from "../../ui";
import { Actions } from "../../constants";
+import { NavigationContext } from "../../routes_helpers";
export interface QosPanelProps {
pings: PingDictionary;
@@ -63,6 +64,10 @@ export class QosPanel extends React.Component {
return calculatePingLoss(this.pingState);
}
+ static contextType = NavigationContext;
+ context!: React.ContextType;
+ navigate = this.context;
+
render() {
const r = { ...this.latencyReport, ...this.qualityReport };
const errorRateDecimal = r.complete / r.total;
@@ -82,7 +87,8 @@ export class QosPanel extends React.Component {
-
+
{t("Learn more about connecting")}
diff --git a/frontend/farm_designer/__tests__/location_info_test.tsx b/frontend/farm_designer/__tests__/location_info_test.tsx
index f63b4794a7..3fcc06b03e 100644
--- a/frontend/farm_designer/__tests__/location_info_test.tsx
+++ b/frontend/farm_designer/__tests__/location_info_test.tsx
@@ -151,7 +151,10 @@ describe("", () => {
wrapper.find(".add-point").simulate("click");
expect(p.dispatch).toHaveBeenCalledWith({
type: Actions.SET_DRAWN_POINT_DATA,
- payload: { cx: 1, cy: 1 }
+ payload: {
+ name: "Location Point", cx: 1, cy: 1, color: "gray", r: 0, z: 0,
+ at_soil_level: false,
+ },
});
expect(mockNavigate).toHaveBeenCalledWith(Path.points("add"));
});
diff --git a/frontend/farm_designer/__tests__/reducer_test.ts b/frontend/farm_designer/__tests__/reducer_test.ts
index e4bc45b2a9..cdbf437c45 100644
--- a/frontend/farm_designer/__tests__/reducer_test.ts
+++ b/frontend/farm_designer/__tests__/reducer_test.ts
@@ -1,9 +1,11 @@
import { designer } from "../reducer";
import { Actions } from "../../constants";
import { ReduxAction } from "../../redux/interfaces";
-import { HoveredPlantPayl, DrawnPointPayl, DrawnWeedPayl } from "../interfaces";
+import { HoveredPlantPayl, DrawnPointPayl } from "../interfaces";
import { BotPosition } from "../../devices/interfaces";
-import { fakeDesignerState } from "../../__test_support__/fake_designer_state";
+import {
+ fakeDesignerState, fakeDrawnPoint,
+} from "../../__test_support__/fake_designer_state";
import { PointGroupSortType } from "farmbot/dist/resources/api_resources";
import { PlantStage, PointType } from "farmbot";
import { UUID } from "../../resources/interfaces";
@@ -200,71 +202,10 @@ describe("designer reducer", () => {
it("sets current point data", () => {
const action: ReduxAction = {
type: Actions.SET_DRAWN_POINT_DATA,
- payload: { cx: 10, cy: 20, z: 0, r: 30, color: "red" }
+ payload: fakeDrawnPoint(),
};
const newState = designer(oldState(), action);
- expect(newState.drawnPoint).toEqual({
- cx: 10, cy: 20, z: 0, r: 30, color: "red"
- });
- });
-
- it("uses current point color", () => {
- const action: ReduxAction = {
- type: Actions.SET_DRAWN_POINT_DATA,
- payload: { cx: 10, cy: 20, z: 0, r: 30 }
- };
- const state = oldState();
- state.drawnPoint = { cx: 0, cy: 0, z: 0, r: 0, color: "red" };
- const newState = designer(state, action);
- expect(newState.drawnPoint).toEqual({
- cx: 10, cy: 20, z: 0, r: 30, color: "red"
- });
- });
-
- it("uses default point color", () => {
- const action: ReduxAction = {
- type: Actions.SET_DRAWN_POINT_DATA,
- payload: { cx: 10, cy: 20, z: 0, r: 30 }
- };
- const newState = designer(oldState(), action);
- expect(newState.drawnPoint).toEqual({
- cx: 10, cy: 20, z: 0, r: 30, color: "green"
- });
- });
-
- it("sets current weed data", () => {
- const action: ReduxAction = {
- type: Actions.SET_DRAWN_WEED_DATA,
- payload: { cx: 10, cy: 20, z: 0, r: 30, color: "red" }
- };
- const newState = designer(oldState(), action);
- expect(newState.drawnWeed).toEqual({
- cx: 10, cy: 20, z: 0, r: 30, color: "red"
- });
- });
-
- it("uses current weed color", () => {
- const action: ReduxAction = {
- type: Actions.SET_DRAWN_WEED_DATA,
- payload: { cx: 10, cy: 20, z: 0, r: 30 }
- };
- const state = oldState();
- state.drawnWeed = { cx: 0, cy: 0, z: 0, r: 0, color: "red" };
- const newState = designer(state, action);
- expect(newState.drawnWeed).toEqual({
- cx: 10, cy: 20, z: 0, r: 30, color: "red"
- });
- });
-
- it("uses default weed color", () => {
- const action: ReduxAction = {
- type: Actions.SET_DRAWN_WEED_DATA,
- payload: { cx: 10, cy: 20, z: 0, r: 30 }
- };
- const newState = designer(oldState(), action);
- expect(newState.drawnWeed).toEqual({
- cx: 10, cy: 20, z: 0, r: 30, color: "red"
- });
+ expect(newState.drawnPoint).toEqual(fakeDrawnPoint());
});
it("sets opened saved garden", () => {
diff --git a/frontend/farm_designer/interfaces.ts b/frontend/farm_designer/interfaces.ts
index 433bf5aebc..dac57756b1 100644
--- a/frontend/farm_designer/interfaces.ts
+++ b/frontend/farm_designer/interfaces.ts
@@ -146,7 +146,6 @@ export interface DesignerState {
bulkPlantSlug: string | undefined;
chosenLocation: BotPosition;
drawnPoint: DrawnPointPayl | undefined;
- drawnWeed: DrawnWeedPayl | undefined;
openedSavedGarden: number | undefined;
tryGroupSortType: ExtendedPointGroupSortType | undefined;
editGroupAreaInMap: boolean;
@@ -359,20 +358,11 @@ export interface CameraCalibrationData {
}
export interface DrawnPointPayl {
- name?: string;
- cx: number;
- cy: number;
+ name: string;
+ cx: number | undefined;
+ cy: number | undefined;
z: number;
r: number;
- color?: string;
- at_soil_level?: boolean;
-}
-
-export interface DrawnWeedPayl {
- name?: string;
- cx: number;
- cy: number;
- z: number;
- r: number;
- color?: string;
+ color: string;
+ at_soil_level: boolean;
}
diff --git a/frontend/farm_designer/location_info.tsx b/frontend/farm_designer/location_info.tsx
index f0e1534124..b550716af9 100644
--- a/frontend/farm_designer/location_info.tsx
+++ b/frontend/farm_designer/location_info.tsx
@@ -45,6 +45,7 @@ import { ImageFlipper } from "../photos/images/image_flipper";
import { PhotoFooter } from "../photos/images/photos";
import { Path } from "../internal_urls";
import { NavigationContext } from "../routes_helpers";
+import { DrawnPointPayl } from "./interfaces";
export const mapStateToProps = (props: Everything): LocationInfoProps => ({
chosenLocation: props.resources.consumers.farm_designer.chosenLocation,
@@ -482,13 +483,16 @@ const LocationActions = (props: LocationActionsProps) => {
{props.currentBotLocation.z})
}