Adds tests for all components
This commit is contained in:
145
__tests__/components/Controls.test.js
Normal file
145
__tests__/components/Controls.test.js
Normal file
@@ -0,0 +1,145 @@
|
||||
import React from "react";
|
||||
import { shallow, configure } from "enzyme";
|
||||
import Adapter from "enzyme-adapter-react-16";
|
||||
configure({ adapter: new Adapter() });
|
||||
import _ from "lodash";
|
||||
|
||||
import { Controls, mapStateToProps } from "../../app/src/components/Controls";
|
||||
|
||||
|
||||
describe("Controls", () => {
|
||||
const tags = {
|
||||
cfg_PID_FlowSP: { value: 0 },
|
||||
cfg_PID_FluidLevelSP: { value: 0 },
|
||||
cfg_PID_TubingPressureSP: { value: 0 },
|
||||
cfg_PID_ManualSP: { value: 0 },
|
||||
Device_Status_INT: { value: 0 },
|
||||
sts_PID_Control: { value: 5 }
|
||||
};
|
||||
|
||||
it("should render loading if no tags prop", () => {
|
||||
const wrapper = shallow(<Controls tags={undefined}/>);
|
||||
expect(wrapper.find(".loading-notags")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should render loading if no Device_Status_INT in tags", () => {
|
||||
const wrapper = shallow(<Controls tags={_.omit(tags, "Device_Status_INT")} />);
|
||||
expect(wrapper.find(".loading-nostatus")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should render controls div", () => {
|
||||
const wrapper = shallow(<Controls tags={tags} />);
|
||||
expect(wrapper.find(".controls")).toHaveLength(1);
|
||||
});
|
||||
|
||||
describe("control parameters", () => {
|
||||
let writeTag;
|
||||
beforeEach(() => {
|
||||
writeTag = jest.fn((tag, val) => {
|
||||
return {tag, val};
|
||||
});
|
||||
});
|
||||
|
||||
it("should run the writeTag function with cmd_Start and true on writeStart", () => {
|
||||
tags.Device_Status_INT.value = 4;
|
||||
const wrapper = shallow(<Controls tags={tags} writeTag={writeTag} />);
|
||||
wrapper.find(".start-button").simulate("click", null);
|
||||
expect(writeTag).toHaveBeenCalledWith("cmd_Start", true);
|
||||
|
||||
});
|
||||
|
||||
it("should run the writeTag function with cmd_Stop and true on writeStop", () => {
|
||||
tags.Device_Status_INT.value = 0;
|
||||
const wrapper = shallow(<Controls tags={tags} writeTag={writeTag} />);
|
||||
wrapper.find(".stop-button").simulate("click", null);
|
||||
expect(writeTag).toHaveBeenCalledWith("cmd_Stop", true);
|
||||
});
|
||||
|
||||
it("should disable set button on flowrate select button if parameter already selected", () => {
|
||||
tags.sts_PID_Control.value = 0;
|
||||
const wrapper = shallow(<Controls tags={tags} writeTag={writeTag} />);
|
||||
expect(wrapper.find(".flowrate-select").hasClass("disabled")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should disable set button on fluidlevel select button if parameter already selected", () => {
|
||||
tags.sts_PID_Control.value = 1;
|
||||
const wrapper = shallow(<Controls tags={tags} writeTag={writeTag} />);
|
||||
expect(wrapper.find(".fluidlevel-select").hasClass("disabled")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should disable set button on tubingpressure select button if parameter already selected", () => {
|
||||
tags.sts_PID_Control.value = 2;
|
||||
const wrapper = shallow(<Controls tags={tags} writeTag={writeTag} />);
|
||||
expect(wrapper.find(".tubingpressure-select").hasClass("disabled")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should disable set button on frequency select button if parameter already selected", () => {
|
||||
tags.sts_PID_Control.value = 3;
|
||||
const wrapper = shallow(<Controls tags={tags} writeTag={writeTag} />);
|
||||
expect(wrapper.find(".frequency-select").hasClass("disabled")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
describe("control setpoints", () => {
|
||||
let writeTag;
|
||||
let wrapper;
|
||||
beforeEach(() => {
|
||||
writeTag = jest.fn((tag, val) => {
|
||||
return {tag, val};
|
||||
});
|
||||
wrapper = shallow(<Controls tags={tags} writeTag={writeTag} />);
|
||||
});
|
||||
|
||||
it("should update setpoint state on setpoint change", () => {
|
||||
wrapper.find(".flowrate-input").simulate("change", {target: {value: 100.0 }});
|
||||
expect(wrapper.state().setpoints.flowrate).toEqual(100.0);
|
||||
|
||||
wrapper.find(".fluidlevel-input").simulate("change", {target: {value: 200.0 }});
|
||||
expect(wrapper.state().setpoints.fluidlevel).toEqual(200.0);
|
||||
|
||||
wrapper.find(".tubingpressure-input").simulate("change", {target: {value: 300.0 }});
|
||||
expect(wrapper.state().setpoints.tubingpressure).toEqual(300.0);
|
||||
|
||||
wrapper.find(".frequency-input").simulate("change", {target: {value: 400.0 }});
|
||||
expect(wrapper.state().setpoints.frequency).toEqual(400.0);
|
||||
});
|
||||
|
||||
it("should call writeTag with the setpoint tag and the value", () => {
|
||||
wrapper.find(".flowrate-input").simulate("change", {target: {value: 100.0 }});
|
||||
wrapper.find(".flowrate-submit").simulate("click", {preventDefault: jest.fn()});
|
||||
expect(writeTag).toBeCalledWith("cfg_PID_FlowSP", 100.0);
|
||||
|
||||
wrapper.find(".fluidlevel-input").simulate("change", {target: {value: 200.0 }});
|
||||
wrapper.find(".fluidlevel-submit").simulate("click", {preventDefault: jest.fn()});
|
||||
expect(writeTag).toBeCalledWith("cfg_PID_FluidLevelSP", 200.0);
|
||||
|
||||
wrapper.find(".tubingpressure-input").simulate("change", {target: {value: 300.0 }});
|
||||
wrapper.find(".tubingpressure-submit").simulate("click", {preventDefault: jest.fn()});
|
||||
expect(writeTag).toBeCalledWith("cfg_PID_TubingPressureSP", 300.0);
|
||||
|
||||
wrapper.find(".frequency-input").simulate("change", {target: {value: 400.0 }});
|
||||
wrapper.find(".frequency-submit").simulate("click", {preventDefault: jest.fn()});
|
||||
expect(writeTag).toBeCalledWith("cfg_PID_ManualSP", 400.0);
|
||||
});
|
||||
|
||||
it("should call writeTag with cfg_PID_* and true when selecting control parameter", () => {
|
||||
wrapper.find(".flowrate-select").simulate("click", {preventDefault: jest.fn()});
|
||||
expect(writeTag).toHaveBeenCalledWith("cfg_PID_Flow", true);
|
||||
|
||||
wrapper.find(".fluidlevel-select").simulate("click", {preventDefault: jest.fn()});
|
||||
expect(writeTag).toHaveBeenCalledWith("cfg_PID_FluidLevel", true);
|
||||
|
||||
wrapper.find(".tubingpressure-select").simulate("click", {preventDefault: jest.fn()});
|
||||
expect(writeTag).toHaveBeenCalledWith("cfg_PID_TubingPressure", true);
|
||||
|
||||
wrapper.find(".frequency-select").simulate("click", {preventDefault: jest.fn()});
|
||||
expect(writeTag).toHaveBeenCalledWith("cfg_PID_Manual", true);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it("should map state to props", () => {
|
||||
expect(mapStateToProps({ tags: "tags", extra: "extra" })).toEqual({ tags: "tags" });
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
52
__tests__/components/EventLog.test.js
Normal file
52
__tests__/components/EventLog.test.js
Normal file
@@ -0,0 +1,52 @@
|
||||
import React from "react";
|
||||
import { shallow, configure } from "enzyme";
|
||||
import Adapter from "enzyme-adapter-react-16";
|
||||
configure({ adapter: new Adapter() });
|
||||
|
||||
import { EventLog, mapStateToProps } from "../../app/src/components/EventLog";
|
||||
|
||||
describe("EventLog", () => {
|
||||
|
||||
const writeTag = jest.fn();
|
||||
|
||||
it("renders loading div if events not defined", () => {
|
||||
const wrapper = shallow(<EventLog />);
|
||||
expect(wrapper.find(".loading")).toHaveLength(1);
|
||||
expect(wrapper.find(".loading").text()).toMatch("Loading");
|
||||
});
|
||||
|
||||
it("should call writeTag with cmd_ResetAlarms and true on Reset button click", () => {
|
||||
const wrapper = shallow(<EventLog events={[]} writeTag={writeTag} />);
|
||||
wrapper.find(".reset-button").simulate("click", null);
|
||||
expect(writeTag).toBeCalledWith("cmd_ResetAlarms", true);
|
||||
});
|
||||
|
||||
describe("event timeline", () => {
|
||||
let events, wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
events = [
|
||||
{ tag: "test", timestamp: new Date(), eventType: "alarm"},
|
||||
{ tag: "tttt", timestamp: new Date(), eventType: "cmd" }
|
||||
];
|
||||
wrapper = shallow(<EventLog events={events} />);
|
||||
});
|
||||
|
||||
it("should return a Timeline Event for each event", () => {
|
||||
expect(wrapper.find("TimelineEvent")).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("shows a calendar icon for non-alarm events", () => {
|
||||
expect(wrapper.find("TimelineEvent").last().html()).toMatch("calendar");
|
||||
});
|
||||
|
||||
it("shows an exclamation triangle for alarm events", () => {
|
||||
expect(wrapper.find("TimelineEvent").first().html()).toMatch("exclamation-triangle");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should map state to props", () => {
|
||||
expect(mapStateToProps({ events: "events", extra: "extra" })).toEqual({ events: "events" });
|
||||
});
|
||||
});
|
||||
89
__tests__/components/Header.test.js
Normal file
89
__tests__/components/Header.test.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import React from "react";
|
||||
import { shallow, configure } from "enzyme";
|
||||
import Adapter from "enzyme-adapter-react-16";
|
||||
configure({ adapter: new Adapter() });
|
||||
|
||||
import { Header, mapStateToProps } from "../../app/src/components/Header";
|
||||
|
||||
|
||||
describe("Header", () => {
|
||||
it("should display a nav item", () => {
|
||||
const wrapper = shallow(<Header />);
|
||||
expect(wrapper.find("nav")).toHaveLength(1);
|
||||
});
|
||||
|
||||
describe("alarm button", () => {
|
||||
|
||||
it("does not display anything if props haven't been set", () => {
|
||||
const wrapper = shallow(<Header alarms={undefined} />);
|
||||
expect(wrapper.find(".no-alarm")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("renders button for no alarms", () => {
|
||||
const wrapper = shallow(<Header alarms={[]} />);
|
||||
expect(wrapper.find(".alarm-button")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("renders button for alarms", () => {
|
||||
const wrapper = shallow(<Header alarms={["alarm_Test"]} />);
|
||||
expect(wrapper.find(".alarm-button")).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("running state indicator", () => {
|
||||
const tags = {
|
||||
Device_Status_INT: { name: "Device_Status_INT", value: 0 }
|
||||
};
|
||||
|
||||
it("renders an empty span for no tags", () => {
|
||||
const wrapper = shallow(<Header alarms={[]} tags={undefined} />);
|
||||
expect(wrapper.find(".no-tags").text()).toEqual("");
|
||||
});
|
||||
|
||||
it("renders and empty span for tags, but no Device_Status_INT", () => {
|
||||
const wrapper = shallow(<Header alarms={[]} tags={[]} />);
|
||||
expect(wrapper.find(".no-device-status").text()).toEqual("");
|
||||
});
|
||||
|
||||
it("renders Running for value 0", () => {
|
||||
const wrapper = shallow(<Header alarms={[]} tags={tags} />);
|
||||
expect(wrapper.find(".status-indicator").text()).toMatch("Running");
|
||||
});
|
||||
|
||||
it("renders Pumped Off for value 1", () => {
|
||||
tags.Device_Status_INT.value = 1;
|
||||
const wrapper = shallow(<Header alarms={[]} tags={tags} />);
|
||||
expect(wrapper.find(".status-indicator").text()).toMatch("Pumped Off");
|
||||
});
|
||||
|
||||
it("renders Alarmed for value 2", () => {
|
||||
tags.Device_Status_INT.value = 2;
|
||||
const wrapper = shallow(<Header alarms={[]} tags={tags} />);
|
||||
expect(wrapper.find(".status-indicator").text()).toMatch("Alarmed");
|
||||
});
|
||||
|
||||
it("renders Locked Out for value 3", () => {
|
||||
tags.Device_Status_INT.value = 3;
|
||||
const wrapper = shallow(<Header alarms={[]} tags={tags} />);
|
||||
expect(wrapper.find(".status-indicator").text()).toMatch("Locked Out");
|
||||
});
|
||||
|
||||
it("renders Stopped for value 4", () => {
|
||||
tags.Device_Status_INT.value = 4;
|
||||
const wrapper = shallow(<Header alarms={[]} tags={tags} />);
|
||||
expect(wrapper.find(".status-indicator").text()).toMatch("Stopped");
|
||||
});
|
||||
|
||||
it("renders Unknown for value 5", () => {
|
||||
tags.Device_Status_INT.value = 5;
|
||||
const wrapper = shallow(<Header alarms={[]} tags={tags} />);
|
||||
expect(wrapper.find(".status-indicator").text()).toMatch("Unknown");
|
||||
});
|
||||
});
|
||||
|
||||
it("should map state to props", () => {
|
||||
const state = { tags: "tags", alarms: "alarms", extra: "extra"};
|
||||
expect(mapStateToProps(state)).toEqual({ tags: "tags", alarms: "alarms" });
|
||||
});
|
||||
|
||||
});
|
||||
83
__tests__/components/Main.test.js
Normal file
83
__tests__/components/Main.test.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import React from "react";
|
||||
import { shallow, configure } from "enzyme";
|
||||
import Adapter from "enzyme-adapter-react-16";
|
||||
configure({ adapter: new Adapter() });
|
||||
|
||||
import { Main, mapStateToProps } from "../../app/src/components/Main";
|
||||
|
||||
describe("Main", () => {
|
||||
const tags = {
|
||||
val_FluidLevel: { name: "val_FluidLevel", value: 100.0 },
|
||||
val_Flowmeter_BarrelsPerDay: { name: "val_Flowmeter_BarrelsPerDay", value: 200.0 },
|
||||
val_TubingPressure: { name: "val_TubingPressure", value: 150.0 },
|
||||
VFD_OutCurrent: { name: "VFD_OutCurrent", value: 30.0 },
|
||||
VFD_SpeedFdbk: { name: "VFD_SpeedFdbk", value: 60.0 }
|
||||
};
|
||||
|
||||
const tagHistory = {
|
||||
val_FluidLevel: [{ timestamp: 0, value: 100.0 }],
|
||||
val_Flowmeter_BarrelsPerDay: [{ timestamp: 0, value: 200.0 }],
|
||||
val_TubingPressure: [{ timestamp: 0, value: 150.0 }],
|
||||
VFD_OutCurrent: [{ timestamp: 0, value: 30.0 }],
|
||||
VFD_SpeedFdbk: [{ timestamp: 0, value: 60.0 }]
|
||||
};
|
||||
|
||||
it("should render loading div if no tag history", () =>{
|
||||
const wrapper = shallow(<Main tagHistory={undefined} />);
|
||||
expect(wrapper.find(".loading").text()).toMatch("Loading");
|
||||
});
|
||||
|
||||
it("should render plc error class if PLC error", () => {
|
||||
const wrapper = shallow(<Main tagHistory={{}} plc={{ error: true }} />);
|
||||
expect(wrapper.find(".plc-error").text()).toMatch("PLC Error");
|
||||
});
|
||||
|
||||
it("should render waiting if no tags yet", () => {
|
||||
const wrapper = shallow(<Main tagHistory={{}} plc={{}} tags={{}} />);
|
||||
expect(wrapper.find(".waiting").text()).toMatch("Waiting for data");
|
||||
});
|
||||
|
||||
it("should render the main container if everything ready", () => {
|
||||
const wrapper = shallow(<Main tagHistory={tagHistory} plc={{}} tags={tags} />);
|
||||
expect(wrapper.find(".main")).toHaveLength(1);
|
||||
});
|
||||
|
||||
describe("tag current values", () => {
|
||||
let wrapper;
|
||||
beforeEach(() => {
|
||||
wrapper = shallow(<Main tagHistory={tagHistory} plc={{}} tags={tags} />);
|
||||
});
|
||||
|
||||
it("should render a liquid fill gauge for all gauges", () => {
|
||||
expect(wrapper.find(".liquidfill")).toHaveLength(5);
|
||||
});
|
||||
|
||||
it("should render a liquidfill gauge for val_FluidLevel", () => {
|
||||
expect(wrapper.find(".lqdfill-val_FluidLevel")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should render a liquidfill gauge for val_Flowmeter_BarrelsPerDay", () => {
|
||||
expect(wrapper.find(".lqdfill-val_Flowmeter_BarrelsPerDay")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should render a liquidfill gauge for val_TubingPressure", () => {
|
||||
expect(wrapper.find(".lqdfill-val_TubingPressure")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should render a liquidfill gauge for VFD_OutCurrent", () => {
|
||||
expect(wrapper.find(".lqdfill-VFD_OutCurrent")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should render a liquidfill gauge for VFD_SpeedFdbk", () => {
|
||||
expect(wrapper.find(".lqdfill-VFD_SpeedFdbk")).toHaveLength(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
it("should map state to props", () => {
|
||||
expect(mapStateToProps({ tags: "tags", plc: "plc", tagHistory: "tagHistory", extra: "extra" })).toEqual({ tags: "tags", plc: "plc", tagHistory: "tagHistory" });
|
||||
});
|
||||
});
|
||||
53
__tests__/components/Permissives.test.js
Normal file
53
__tests__/components/Permissives.test.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import React from "react";
|
||||
import { shallow, configure } from "enzyme";
|
||||
import Adapter from "enzyme-adapter-react-16";
|
||||
configure({ adapter: new Adapter() });
|
||||
|
||||
import { Permissives, mapStateToProps } from "../../app/src/components/Permissives";
|
||||
|
||||
describe("Permissives", () => {
|
||||
it("should render a loading page if no tags", () => {
|
||||
const wrapper = shallow(<Permissives tags={undefined} />);
|
||||
expect(wrapper.find(".loading").text()).toMatch("Loading");
|
||||
});
|
||||
|
||||
it("should render the permissives page", () => {
|
||||
const wrapper = shallow(<Permissives tags={[]} />);
|
||||
expect(wrapper.find(".permissives")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should render green button for permissive true", () => {
|
||||
const tags = {
|
||||
sp_ALL: { value: true },
|
||||
rp_ALL: { value: true },
|
||||
sp_Flowmeter: { value: true },
|
||||
rp_Flowmeter: { value: true },
|
||||
sp_IntakePressure: { value: true },
|
||||
rp_IntakePressure: { value: true },
|
||||
sp_IntakeTemperature: { value: true },
|
||||
rp_IntakeTemperature: { value: true }
|
||||
};
|
||||
const wrapper = shallow(<Permissives tags={tags} />);
|
||||
expect(wrapper.find(".btn-success")).toHaveLength(8);
|
||||
});
|
||||
|
||||
it("should render red button for permissive false", () => {
|
||||
const tags = {
|
||||
sp_ALL: { value: false },
|
||||
rp_ALL: { value: false },
|
||||
sp_Flowmeter: { value: false },
|
||||
rp_Flowmeter: { value: false },
|
||||
sp_IntakePressure: { value: false },
|
||||
rp_IntakePressure: { value: false },
|
||||
sp_IntakeTemperature: { value: false },
|
||||
rp_IntakeTemperature: { value: false }
|
||||
};
|
||||
const wrapper = shallow(<Permissives tags={tags} />);
|
||||
expect(wrapper.find(".btn-danger")).toHaveLength(8);
|
||||
});
|
||||
|
||||
it("should map state to props correctly", () => {
|
||||
const state = { tags: "tags", extra: "extra" };
|
||||
expect(mapStateToProps(state)).toEqual({ tags: "tags" });
|
||||
});
|
||||
});
|
||||
109
__tests__/components/Settings.test.js
Normal file
109
__tests__/components/Settings.test.js
Normal file
@@ -0,0 +1,109 @@
|
||||
import React from "react";
|
||||
import { shallow, configure } from "enzyme";
|
||||
import Adapter from "enzyme-adapter-react-16";
|
||||
configure({ adapter: new Adapter() });
|
||||
|
||||
import { Settings, mapStateToProps } from "../../app/src/components/Settings";
|
||||
|
||||
describe("Settings", () => {
|
||||
let container;
|
||||
const testTagList = [
|
||||
{
|
||||
name: "test1",
|
||||
value: 100
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
value: 200
|
||||
}
|
||||
];
|
||||
|
||||
const plcTestData = {
|
||||
details: {
|
||||
name: "test PLC"
|
||||
},
|
||||
ipAddress: "192.168.1.10"
|
||||
};
|
||||
|
||||
const setPlcIpAddress = jest.fn((ipAddress) => ipAddress);
|
||||
const storeNewTag = jest.fn((tagName) => tagName);
|
||||
const deleteTag = jest.fn((tagName) => tagName);
|
||||
const ipcPlcInitializeSend = jest.fn((addr, tagList) => {
|
||||
return {addr, tagList};
|
||||
});
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
container = shallow(
|
||||
<Settings
|
||||
plc={plcTestData}
|
||||
tags={testTagList}
|
||||
setPlcIpAddress={setPlcIpAddress}
|
||||
storeNewTag={storeNewTag}
|
||||
deleteTag={deleteTag}
|
||||
ipcPlcInitializeSend={ipcPlcInitializeSend}
|
||||
history={[]}
|
||||
/>);
|
||||
});
|
||||
|
||||
it("should display a settings item", () => {
|
||||
expect(container.find(".settings")).toHaveLength(1);
|
||||
});
|
||||
|
||||
describe("ip address field", () => {
|
||||
it("should update state when ip address changed", () => {
|
||||
container.find(".ip-address-field").simulate("change", {target: {value: "1.1.1.1"}});
|
||||
expect(container.state().ipAddress).toEqual("1.1.1.1");
|
||||
});
|
||||
|
||||
it("should run the setPlcIpaddress function when update clicked", () =>{
|
||||
container.find(".ip-address-field").simulate("change", {target: {value: "1.1.1.1"}});
|
||||
container.find(".ip-submit-button").simulate("click", {preventDefault: jest.fn()});
|
||||
expect(setPlcIpAddress).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not allow setting ip address if ip address not changed", () => {
|
||||
expect(container.find(".ip-submit-button").hasClass("disabled")).toBeTruthy();
|
||||
|
||||
container.find(".ip-address-field").simulate("change", {target: {value: "1.1.1.1"}});
|
||||
expect(container.find(".ip-submit-button").hasClass("disabled")).toBeFalsy();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("tag list", () => {
|
||||
it("should have one row for each tag", () => {
|
||||
expect(container.find(".tag-list li")).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should update state when new tag input changed", () => {
|
||||
container.find(".tag-name-input").simulate("change", {target: {value: "testtag"}});
|
||||
expect(container.state().newTag).toEqual("testtag");
|
||||
});
|
||||
|
||||
it("should run the storeNewTag function on submit button click", () => {
|
||||
container.find(".tag-name-input").simulate("change", {target: {value: "testtag"}});
|
||||
container.find(".add-tag-button").simulate("click", {preventDefault: jest.fn()});
|
||||
expect(storeNewTag).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should run the ipcPlcInitializeSend function on save button click", () => {
|
||||
container.find(".ip-address-field").simulate("change", {target: {value: "1.1.1.1"}});
|
||||
container.find(".ip-submit-button").simulate("click", {preventDefault: jest.fn()});
|
||||
container.find(".tag-name-input").simulate("change", {target: {value: "testtag"}});
|
||||
container.find(".add-tag-button").simulate("click", {preventDefault: jest.fn()});
|
||||
container.find(".save-button").simulate("click", {preventDefault: jest.fn()});
|
||||
expect(ipcPlcInitializeSend).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should run the deleteTag function on delete button click", () => {
|
||||
container.find(".delete-button").first().simulate("click", {preventDefault: jest.fn()});
|
||||
expect(deleteTag).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("should map state to props", () => {
|
||||
expect(mapStateToProps({ tags: "tags", plc: "plc", extra: "extra" })).toEqual({ tags: "tags", plc: "plc" });
|
||||
});
|
||||
|
||||
});
|
||||
75
__tests__/components/TagsIndex.test.js
Normal file
75
__tests__/components/TagsIndex.test.js
Normal file
@@ -0,0 +1,75 @@
|
||||
import React from "react";
|
||||
import { shallow, configure } from "enzyme";
|
||||
import Adapter from "enzyme-adapter-react-16";
|
||||
configure({ adapter: new Adapter() });
|
||||
|
||||
import { TagsIndex, mapStateToProps } from "../../app/src/components/TagsIndex";
|
||||
|
||||
describe("TagsIndex", () => {
|
||||
let container;
|
||||
beforeEach(() => {
|
||||
null;
|
||||
});
|
||||
|
||||
it("should display a div for no tags found", () => {
|
||||
container = shallow(<TagsIndex plc={{}} tags={[]} />);
|
||||
expect(container.find(".tags-index-notags")).toHaveLength(1);
|
||||
});
|
||||
|
||||
describe("when tags are found", () => {
|
||||
const testTagList = [
|
||||
{
|
||||
name: "test1",
|
||||
value: 100
|
||||
},
|
||||
{
|
||||
name: "test2",
|
||||
value: 200
|
||||
}
|
||||
];
|
||||
|
||||
const plcTestData = {
|
||||
details: {
|
||||
name: "test PLC"
|
||||
},
|
||||
ipAddress: "192.168.1.10"
|
||||
};
|
||||
|
||||
const writeTag = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
container = shallow(<TagsIndex plc={plcTestData} tags={testTagList} writeTag={writeTag} />);
|
||||
});
|
||||
|
||||
|
||||
it("should display a div for tags found", () => {
|
||||
expect(container.find(".tags-index-withtags")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should render 1 row for each tag", () => {
|
||||
expect(container.find(".tags-index-withtags table tbody tr")).toHaveLength(2);
|
||||
});
|
||||
|
||||
|
||||
it("should display a plc IP address", () => {
|
||||
expect(container.find(".tags-index-withtags h2").text()).toMatch("192.168.1.10");
|
||||
});
|
||||
|
||||
it("should update state on changing write value", () => {
|
||||
container.find(".tag-write-input").first().simulate("change", {target: {value: 111.1}});
|
||||
expect(container.state().writes["test1"]).toEqual(111.1);
|
||||
});
|
||||
|
||||
it("should run the writeTag function when clicking Write", () => {
|
||||
container.find(".tag-write-input").first().simulate("change", {target: {value: 111.1}});
|
||||
container.find(".tag-write-button").first().simulate("click", {preventDefault: jest.fn()});
|
||||
expect(writeTag).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("should map state to props", () => {
|
||||
expect(mapStateToProps({ tags: "tags", plc: "plc", extra: "extra" })).toEqual({ tags: "tags", plc: "plc" });
|
||||
});
|
||||
|
||||
});
|
||||
File diff suppressed because one or more lines are too long
@@ -3,7 +3,7 @@ import { connect } from "react-redux";
|
||||
|
||||
import { writeTag } from "../actions/actions_tags";
|
||||
|
||||
class Controls extends Component {
|
||||
export class Controls extends Component {
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
@@ -50,7 +50,7 @@ class Controls extends Component {
|
||||
|
||||
}
|
||||
|
||||
onPIDParameterSelect = (e, parName) => {
|
||||
onPIDParameterSelect = (event, parName) => {
|
||||
event.preventDefault();
|
||||
|
||||
switch(parName){
|
||||
@@ -90,7 +90,7 @@ class Controls extends Component {
|
||||
render(){
|
||||
if (!this.props.tags){
|
||||
return(
|
||||
<div>
|
||||
<div className="loading-notags">
|
||||
<h1>Loading...</h1>
|
||||
</div>
|
||||
);
|
||||
@@ -98,17 +98,17 @@ class Controls extends Component {
|
||||
|
||||
if (!this.props.tags.Device_Status_INT){
|
||||
return(
|
||||
<div>
|
||||
<div className="loading-nostatus">
|
||||
<h1>Loading...</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const startBtnClass = this.props.tags.Device_Status_INT.value === 4 ? "btn btn-success control-button" : "btn btn-secondary disabled control-button";
|
||||
const stopBtnClass = this.props.tags.Device_Status_INT.value !== 4 ? "btn btn-danger control-button" : "btn btn-secondary disabled control-button";
|
||||
const startBtnClass = this.props.tags.Device_Status_INT.value === 4 ? "btn btn-success control-button start-button" : "btn btn-secondary disabled control-button start-button";
|
||||
const stopBtnClass = this.props.tags.Device_Status_INT.value !== 4 ? "btn btn-danger control-button stop-button" : "btn btn-secondary disabled control-button stop-button";
|
||||
|
||||
return (
|
||||
<div className="container" style={{textAlign: "center"}}>
|
||||
<div className="container controls" style={{textAlign: "center"}}>
|
||||
<h1>Controls</h1>
|
||||
<table className="table">
|
||||
<tbody>
|
||||
@@ -138,7 +138,7 @@ class Controls extends Component {
|
||||
<tr>
|
||||
<td>
|
||||
<button
|
||||
className={this.props.tags.sts_PID_Control.value === 0 ? "btn btn-success disabled" : "btn btn-light"}
|
||||
className={this.props.tags.sts_PID_Control.value === 0 ? "btn btn-success disabled flowrate-select" : "btn btn-light flowrate-select"}
|
||||
onClick={(e) => this.onPIDParameterSelect(e, "flowrate")}
|
||||
>
|
||||
Flow Rate
|
||||
@@ -146,7 +146,7 @@ class Controls extends Component {
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
className={this.props.tags.sts_PID_Control.value === 1 ? "btn btn-success disabled" : "btn btn-light"}
|
||||
className={this.props.tags.sts_PID_Control.value === 1 ? "btn btn-success disabled fluidlevel-select" : "btn btn-light fluidlevel-select"}
|
||||
onClick={(e) => this.onPIDParameterSelect(e, "fluidlevel")}
|
||||
>
|
||||
Fluid Level
|
||||
@@ -154,7 +154,7 @@ class Controls extends Component {
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
className={this.props.tags.sts_PID_Control.value === 2 ? "btn btn-success disabled" : "btn btn-light"}
|
||||
className={this.props.tags.sts_PID_Control.value === 2 ? "btn btn-success disabled tubingpressure-select" : "btn btn-light tubingpressure-select"}
|
||||
onClick={(e) => this.onPIDParameterSelect(e, "tubingpressure")}
|
||||
>
|
||||
Tubing Pressure
|
||||
@@ -162,7 +162,7 @@ class Controls extends Component {
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
className={this.props.tags.sts_PID_Control.value === 3 ? "btn btn-success disabled" : "btn btn-light"}
|
||||
className={this.props.tags.sts_PID_Control.value === 3 ? "btn btn-success disabled frequency-select" : "btn btn-light frequency-select"}
|
||||
onClick={(e) => this.onPIDParameterSelect(e, "frequency")}
|
||||
>
|
||||
Manual Frequency
|
||||
@@ -175,7 +175,7 @@ class Controls extends Component {
|
||||
<input
|
||||
value={this.state.setpoints.flowrate}
|
||||
onChange={(e) => this.onSetpointChange(e, "flowrate")}
|
||||
className="form-control"
|
||||
className="form-control flowrate-input"
|
||||
type="number"
|
||||
/>
|
||||
</td>
|
||||
@@ -183,7 +183,7 @@ class Controls extends Component {
|
||||
<input
|
||||
value={this.state.setpoints.fluidlevel}
|
||||
onChange={(e) => this.onSetpointChange(e, "fluidlevel")}
|
||||
className="form-control"
|
||||
className="form-control fluidlevel-input"
|
||||
type="number"
|
||||
/>
|
||||
</td>
|
||||
@@ -191,7 +191,7 @@ class Controls extends Component {
|
||||
<input
|
||||
value={this.state.setpoints.tubingpressure}
|
||||
onChange={(e) => this.onSetpointChange(e, "tubingpressure")}
|
||||
className="form-control"
|
||||
className="form-control tubingpressure-input"
|
||||
type="number"
|
||||
/>
|
||||
</td>
|
||||
@@ -199,7 +199,7 @@ class Controls extends Component {
|
||||
<input
|
||||
value={this.state.setpoints.frequency}
|
||||
onChange={(e) => this.onSetpointChange(e, "frequency")}
|
||||
className="form-control"
|
||||
className="form-control frequency-input"
|
||||
type="number"
|
||||
/>
|
||||
</td>
|
||||
@@ -209,7 +209,7 @@ class Controls extends Component {
|
||||
<td>
|
||||
<button
|
||||
onClick={(e) => this.onSetpointSubmit(e, "flowrate")}
|
||||
className="btn btn-primary"
|
||||
className="btn btn-primary flowrate-submit"
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
@@ -218,7 +218,7 @@ class Controls extends Component {
|
||||
<td>
|
||||
<button
|
||||
onClick={(e) => this.onSetpointSubmit(e, "fluidlevel")}
|
||||
className="btn btn-primary"
|
||||
className="btn btn-primary fluidlevel-submit"
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
@@ -227,7 +227,7 @@ class Controls extends Component {
|
||||
<td>
|
||||
<button
|
||||
onClick={(e) => this.onSetpointSubmit(e, "tubingpressure")}
|
||||
className="btn btn-primary"
|
||||
className="btn btn-primary tubingpressure-submit"
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
@@ -236,7 +236,7 @@ class Controls extends Component {
|
||||
<td>
|
||||
<button
|
||||
onClick={(e) => this.onSetpointSubmit(e, "frequency")}
|
||||
className="btn btn-primary"
|
||||
className="btn btn-primary frequency-submit"
|
||||
>
|
||||
Update
|
||||
</button>
|
||||
@@ -258,7 +258,7 @@ class Controls extends Component {
|
||||
*
|
||||
* @returns {Object} mapped state
|
||||
*/
|
||||
function mapStateToProps(state){
|
||||
export function mapStateToProps(state){
|
||||
return {
|
||||
tags: state.tags
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { faExclamationTriangle, faCalendar } from "@fortawesome/fontawesome-pro-
|
||||
|
||||
import { writeTag } from "../actions/actions_tags";
|
||||
|
||||
class EventLog extends Component {
|
||||
export class EventLog extends Component {
|
||||
|
||||
renderTimelineEvents(){
|
||||
return _.map(this.props.events, (event, key) => {
|
||||
@@ -38,7 +38,7 @@ class EventLog extends Component {
|
||||
render(){
|
||||
if (!this.props.events){
|
||||
return(
|
||||
<div>
|
||||
<div className="loading">
|
||||
<h1>Loading...</h1>
|
||||
</div>
|
||||
);
|
||||
@@ -46,7 +46,7 @@ class EventLog extends Component {
|
||||
return (
|
||||
<div className="container">
|
||||
<button
|
||||
className="btn btn-primary"
|
||||
className="btn btn-primary reset-button"
|
||||
onClick={() => this.props.writeTag("cmd_ResetAlarms", true)}
|
||||
style={{marginTop: "10px", marginBottom: "10px"}}
|
||||
>
|
||||
@@ -66,7 +66,7 @@ class EventLog extends Component {
|
||||
*
|
||||
* @returns {Object} mapped state
|
||||
*/
|
||||
function mapStateToProps(state){
|
||||
export function mapStateToProps(state){
|
||||
return {
|
||||
events: state.events
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ import { faTint, faCog } from "@fortawesome/fontawesome-pro-regular";
|
||||
*
|
||||
* @extends React.Component
|
||||
*/
|
||||
class Header extends Component {
|
||||
export class Header extends Component {
|
||||
|
||||
|
||||
/**
|
||||
@@ -18,11 +18,11 @@ class Header extends Component {
|
||||
*/
|
||||
renderRunningState(){
|
||||
if(!this.props.tags ){
|
||||
return <span></span>;
|
||||
return <span className="no-tags"></span>;
|
||||
}
|
||||
|
||||
if (!this.props.tags.Device_Status_INT){
|
||||
return <span></span>;
|
||||
return <span className="no-device-status"></span>;
|
||||
}
|
||||
|
||||
let deviceStatus;
|
||||
@@ -53,7 +53,7 @@ class Header extends Component {
|
||||
deviceClass = "btn btn-info";
|
||||
}
|
||||
|
||||
return <button className={deviceClass + " disabled"}>{deviceStatus}</button>;
|
||||
return <button className={deviceClass + " disabled status-indicator"}>{deviceStatus}</button>;
|
||||
|
||||
}
|
||||
|
||||
@@ -64,10 +64,10 @@ class Header extends Component {
|
||||
*/
|
||||
renderAlarmButton(){
|
||||
if (!this.props.alarms){
|
||||
return <span></span>;
|
||||
return <span className="no-alarm"></span>;
|
||||
}
|
||||
|
||||
const btnClassName = this.props.alarms.length > 0 ? "btn btn-danger alarms-active" : "btn btn-light";
|
||||
const btnClassName = this.props.alarms.length > 0 ? "btn btn-danger alarms-active alarm-button" : "btn btn-light alarm-button";
|
||||
const btnText = this.props.alarms.length > 0 ? `Alarms Active: ${this.props.alarms.length}` : "No Alarms";
|
||||
|
||||
return (
|
||||
@@ -115,7 +115,7 @@ class Header extends Component {
|
||||
*
|
||||
* @returns {Object} mapped state
|
||||
*/
|
||||
function mapStateToProps(state){
|
||||
export function mapStateToProps(state){
|
||||
return {
|
||||
tags: state.tags,
|
||||
alarms: state.alarms
|
||||
|
||||
@@ -2,18 +2,17 @@ import React, { Component } from "react";
|
||||
import _ from "lodash";
|
||||
import { connect } from "react-redux";
|
||||
import { FlexibleWidthXYPlot, LineSeries, VerticalGridLines, HorizontalGridLines, XAxis, YAxis, DiscreteColorLegend } from "react-vis";
|
||||
// import "../../../node_modules/react-vis/dist/style.css";
|
||||
import "../../../node_modules/react-vis/dist/style.css";
|
||||
|
||||
import { color } from "d3-color";
|
||||
import { interpolateRgb } from "d3-interpolate";
|
||||
import LiquidFillGauge from "react-liquid-gauge";
|
||||
import { RadialGauge } from "react-canvas-gauges";
|
||||
|
||||
/** Class for Main Page
|
||||
*
|
||||
* @extends React.Component
|
||||
*/
|
||||
class Main extends Component {
|
||||
export class Main extends Component {
|
||||
|
||||
/**
|
||||
* Map an array of objects to a graph-usable array of objects
|
||||
@@ -68,9 +67,9 @@ class Main extends Component {
|
||||
offset: "100%"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
return (
|
||||
<div className="col" style={{textAlign: "center"}}>
|
||||
<div className={"col liquidfill lqdfill-" + tag.name} style={{textAlign: "center"}}>
|
||||
<h3>{label}</h3>
|
||||
<LiquidFillGauge
|
||||
style={{ margin: "0 auto" }}
|
||||
@@ -124,39 +123,6 @@ class Main extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a radial gauge.
|
||||
*
|
||||
* @param {Object} tag - the tag structure
|
||||
* @param {string} units - units for the tag
|
||||
* @param {number} maxValue - maximum value to be displayed
|
||||
* @param {number} label - label for the value (typically the tag name)
|
||||
*/
|
||||
renderRadialGauge(tag, units, maxValue, label){
|
||||
if (!tag){
|
||||
return <span></span>;
|
||||
}
|
||||
|
||||
const ticks = [ 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0 ];
|
||||
|
||||
return (
|
||||
<div className="col" style={{textAlign: "center"}}>
|
||||
<h3>{label}</h3>
|
||||
<RadialGauge
|
||||
height={200}
|
||||
width={200}
|
||||
units={units}
|
||||
title={label}
|
||||
value={tag.value}
|
||||
minValue={0}
|
||||
maxValue={maxValue}
|
||||
majorTicks={ticks.map((t) => t * maxValue)}
|
||||
minorTicks={2}
|
||||
></RadialGauge>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* render
|
||||
@@ -166,7 +132,7 @@ class Main extends Component {
|
||||
render(){
|
||||
if (!this.props.tagHistory){
|
||||
return(
|
||||
<div className="container">
|
||||
<div className="container loading">
|
||||
<h1>Loading...</h1>
|
||||
</div>
|
||||
);
|
||||
@@ -183,7 +149,7 @@ class Main extends Component {
|
||||
|
||||
if (Object.keys(this.props.tags).length === 0){
|
||||
return(
|
||||
<div className="container">
|
||||
<div className="container waiting">
|
||||
<h1>Waiting for data...</h1>
|
||||
</div>
|
||||
);
|
||||
@@ -191,12 +157,12 @@ class Main extends Component {
|
||||
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="container main">
|
||||
<h3>Process Values</h3>
|
||||
<div className="row" style={{marginBottom: "20px"}}>
|
||||
{this.renderLiquidGauge(this.props.tags.val_FluidLevel, "ft.", 500, "Level")}
|
||||
{this.renderRadialGauge(this.props.tags.val_Flowmeter_BarrelsPerDay, "BPD", 5000, "Flow Rate")}
|
||||
{this.renderRadialGauge(this.props.tags.val_TubingPressure, "PSI", 400, "Tubing Pressure")}
|
||||
{this.renderLiquidGauge(this.props.tags.val_Flowmeter_BarrelsPerDay, "BPD", 5000, "Flow Rate")}
|
||||
{this.renderLiquidGauge(this.props.tags.val_TubingPressure, "PSI", 400, "Tubing Pressure")}
|
||||
</div>
|
||||
|
||||
<FlexibleWidthXYPlot
|
||||
@@ -207,21 +173,11 @@ class Main extends Component {
|
||||
<HorizontalGridLines />
|
||||
<XAxis />
|
||||
<YAxis />
|
||||
<LineSeries
|
||||
data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.val_IntakePressure)}
|
||||
/>
|
||||
<LineSeries
|
||||
data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.val_Flowmeter)}
|
||||
/>
|
||||
<LineSeries
|
||||
data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.val_FluidLevel)}
|
||||
/>
|
||||
<LineSeries
|
||||
data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.val_IntakeTemperature)}
|
||||
/>
|
||||
<LineSeries
|
||||
data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.val_TubingPressure)}
|
||||
/>
|
||||
<LineSeries data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.val_IntakePressure)} />
|
||||
<LineSeries data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.val_Flowmeter)} />
|
||||
<LineSeries data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.val_FluidLevel)} />
|
||||
<LineSeries data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.val_IntakeTemperature)} />
|
||||
<LineSeries data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.val_TubingPressure)} />
|
||||
</FlexibleWidthXYPlot>
|
||||
<DiscreteColorLegend
|
||||
items={[
|
||||
@@ -237,8 +193,8 @@ class Main extends Component {
|
||||
|
||||
<h3>VFD Data</h3>
|
||||
<div className="row" style={{marginBottom: "20px"}}>
|
||||
{this.renderRadialGauge(this.props.tags.VFD_OutCurrent, "A.", 100, "Current")}
|
||||
{this.renderRadialGauge(this.props.tags.VFD_SpeedFdbk, "Hz", 60, "Frequency")}
|
||||
{this.renderLiquidGauge(this.props.tags.VFD_OutCurrent, "A.", 100, "Current")}
|
||||
{this.renderLiquidGauge(this.props.tags.VFD_SpeedFdbk, "Hz", 60, "Frequency")}
|
||||
</div>
|
||||
<FlexibleWidthXYPlot
|
||||
height={300}
|
||||
@@ -248,18 +204,10 @@ class Main extends Component {
|
||||
<HorizontalGridLines />
|
||||
<XAxis />
|
||||
<YAxis />
|
||||
<LineSeries
|
||||
data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.VFD_OutCurrent)}
|
||||
/>
|
||||
<LineSeries
|
||||
data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.VFD_SpeedFdbk)}
|
||||
/>
|
||||
<LineSeries
|
||||
data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.VFD_OutPower)}
|
||||
/>
|
||||
<LineSeries
|
||||
data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.VFD_Temp)}
|
||||
/>
|
||||
<LineSeries data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.VFD_OutCurrent)} />
|
||||
<LineSeries data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.VFD_SpeedFdbk)} />
|
||||
<LineSeries data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.VFD_OutPower)} />
|
||||
<LineSeries data={this.mapTimestampAndValuePropToXY(this.props.tagHistory.VFD_Temp)} />
|
||||
</FlexibleWidthXYPlot>
|
||||
<DiscreteColorLegend
|
||||
items={[
|
||||
@@ -283,7 +231,7 @@ class Main extends Component {
|
||||
*
|
||||
* @returns {Object} mapped state
|
||||
*/
|
||||
function mapStateToProps(state){
|
||||
export function mapStateToProps(state){
|
||||
return {
|
||||
tags: state.tags,
|
||||
tagHistory: state.tagHistory,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
class Permissives extends Component {
|
||||
export class Permissives extends Component {
|
||||
|
||||
renderTagButton = (tagName, description) => {
|
||||
if (!this.props.tags[tagName]){
|
||||
@@ -21,14 +21,14 @@ class Permissives extends Component {
|
||||
render(){
|
||||
if (!this.props.tags){
|
||||
return (
|
||||
<div>
|
||||
<div className="loading">
|
||||
<h2>Loading...</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return(
|
||||
<div>
|
||||
<div className="permissives">
|
||||
<table id="permissives-table" className="table">
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -72,7 +72,7 @@ class Permissives extends Component {
|
||||
*
|
||||
* @returns {Object} mapped state
|
||||
*/
|
||||
function mapStateToProps(state){
|
||||
export function mapStateToProps(state){
|
||||
return {
|
||||
tags: state.tags
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { storeNewTag, deleteTag } from "../actions/actions_tags";
|
||||
import FontAwesomeIcon from "@fortawesome/react-fontawesome";
|
||||
import { faTimesSquare } from "@fortawesome/fontawesome-pro-regular";
|
||||
|
||||
class Settings extends Component {
|
||||
export class Settings extends Component {
|
||||
constructor(props){
|
||||
super(props);
|
||||
|
||||
@@ -24,7 +24,7 @@ class Settings extends Component {
|
||||
<li key={tag.name} className="list-group-item">
|
||||
{tag.name}
|
||||
<button
|
||||
className="btn btn-outline-danger float-right"
|
||||
className="btn btn-outline-danger float-right delete-button"
|
||||
onClick={(e) => this.onDeleteClick(e, tag.name)}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTimesSquare} />
|
||||
@@ -35,9 +35,7 @@ class Settings extends Component {
|
||||
}
|
||||
|
||||
componentWillMount(){
|
||||
console.log(this.props.plc);
|
||||
if (this.props.plc && this.props.plc.ipAddress){
|
||||
console.log(this.props.plc.ipAddress);
|
||||
this.setState({ipAddress: this.props.plc.ipAddress});
|
||||
}
|
||||
}
|
||||
@@ -73,24 +71,24 @@ class Settings extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const ipAddressBtnClass = ((this.state.ipAddress === this.props.plc.ipAddress) || (this.state.ipAddress.length === 0)) ? "btn btn-primary disabled" : "btn btn-primary";
|
||||
const initializeBtnClass = (this.props.plc.ipAddress && _.map(this.props.tags, (t)=>t).length > 0) ? "btn btn-success" : "btn sbtn-success disabled";
|
||||
const ipAddressBtnClass = ((this.state.ipAddress === this.props.plc.ipAddress) || (this.state.ipAddress.length === 0)) ? "disabled" : "";
|
||||
const initializeBtnClass = (this.props.plc.ipAddress && _.map(this.props.tags, (t)=>t).length > 0) ? "" : "disabled";
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
<div className="container settings">
|
||||
<h1>Settings</h1>
|
||||
<form className="form-inline mb-2">
|
||||
<div className="form-group">
|
||||
<label htmlFor="ipAddress">IP Address</label>
|
||||
<input
|
||||
id="ipAddress"
|
||||
className="form-control "
|
||||
className="form-control ip-address-field"
|
||||
value={this.state.ipAddress}
|
||||
placeholder="PLC IP Address"
|
||||
onChange={this.onIpAddressInputChange}
|
||||
/>
|
||||
<button
|
||||
className={ipAddressBtnClass}
|
||||
className={ipAddressBtnClass + " btn btn-primary ip-submit-button"}
|
||||
onClick={(e) => this.sendIpAddress(e)}>
|
||||
Set IP Address
|
||||
</button>
|
||||
@@ -99,7 +97,7 @@ class Settings extends Component {
|
||||
|
||||
|
||||
<h4>Tag List</h4>
|
||||
<ul className="list-group">
|
||||
<ul className="list-group tag-list">
|
||||
{this.getTagList()}
|
||||
</ul>
|
||||
<form>
|
||||
@@ -107,14 +105,15 @@ class Settings extends Component {
|
||||
value={this.state.newTag}
|
||||
onChange={this.onNewTagChange}
|
||||
placeholder="New Tag Name..."
|
||||
className="tag-name-input"
|
||||
/>
|
||||
<button
|
||||
className="btn btn-success float-right"
|
||||
className="btn btn-success float-right add-tag-button"
|
||||
onClick={(e) => this.onNewTagSubmit(e)}
|
||||
>Add Tag</button>
|
||||
|
||||
<button
|
||||
className={initializeBtnClass}
|
||||
className={initializeBtnClass + " btn btn-success save-button"}
|
||||
onClick={(e) => this.onSave(e)}
|
||||
>Save</button>
|
||||
</form>
|
||||
@@ -131,7 +130,7 @@ class Settings extends Component {
|
||||
*
|
||||
* @returns {Object} mapped state
|
||||
*/
|
||||
function mapStateToProps(state){
|
||||
export function mapStateToProps(state){
|
||||
return{
|
||||
tags: state.tags,
|
||||
plc: state.plc
|
||||
|
||||
@@ -2,13 +2,12 @@ import React, { Component } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Link } from "react-router-dom";
|
||||
import _ from "lodash";
|
||||
import Gauge from "react-svg-gauge";
|
||||
|
||||
import { writeTag } from "../actions/actions_tags";
|
||||
|
||||
|
||||
|
||||
class TagsIndex extends Component {
|
||||
export class TagsIndex extends Component {
|
||||
|
||||
constructor(props){
|
||||
super(props);
|
||||
@@ -22,36 +21,21 @@ class TagsIndex extends Component {
|
||||
<td>{Math.round(t.value * 100) / 100}</td>
|
||||
<td><input
|
||||
onChange={(e) => this.onTagWriteFieldChanged(e, t.name)}
|
||||
className="tag-write-input"
|
||||
/></td>
|
||||
<td><button
|
||||
className="waves-effect waves-light btn"
|
||||
className="waves-effect waves-light btn tag-write-button"
|
||||
onClick={() => this.onWriteButtonClick(t.name)}
|
||||
>Write</button></td>
|
||||
</tr>);
|
||||
});
|
||||
}
|
||||
|
||||
renderTagsGauges(){
|
||||
return _.map(this.props.tags, (tag) => {
|
||||
return (<div className="col s4" key={tag.name}>
|
||||
<Gauge value={Math.round(tag.value * 100) / 100}
|
||||
width={200}
|
||||
height={150}
|
||||
label={tag.name}
|
||||
valueLabelStyle={styles.valueLabel}
|
||||
topLabelStyle={styles.topLabel}
|
||||
minMaxLabelStyle={styles.minMaxLabel} />
|
||||
</div>);
|
||||
});
|
||||
}
|
||||
|
||||
onWriteButtonClick = (tagName) => {
|
||||
console.log(tagName, this.state.writes[tagName]);
|
||||
this.props.writeTag(tagName, this.state.writes[tagName]);
|
||||
}
|
||||
|
||||
onTagWriteFieldChanged = (e, tagName) => {
|
||||
console.log(tagName, e.target.value);
|
||||
this.setState({writes: {...this.state.writes, [tagName]: e.target.value}});
|
||||
}
|
||||
|
||||
@@ -63,7 +47,9 @@ class TagsIndex extends Component {
|
||||
|
||||
if (!this.props.tags || _.map(this.props.tags, (t)=> t ).length === 0) {
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<div style={styles.container}
|
||||
className="tags-index-notags"
|
||||
>
|
||||
<h3>
|
||||
No Tags.
|
||||
</h3>
|
||||
@@ -73,7 +59,7 @@ class TagsIndex extends Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<div style={styles.container} className="tags-index-withtags">
|
||||
<h2>Tags for {this.props.plc.ipAddress}</h2>
|
||||
<h3>{PLC}</h3>
|
||||
<table className="table">
|
||||
@@ -127,7 +113,7 @@ const styles = {
|
||||
*
|
||||
* @returns {Object} mapped state
|
||||
*/
|
||||
function mapStateToProps(state){
|
||||
export function mapStateToProps(state){
|
||||
return {
|
||||
tags: state.tags,
|
||||
plc: state.plc
|
||||
|
||||
12
package.json
12
package.json
@@ -19,7 +19,8 @@
|
||||
"node_modules/(?!(electron)/)"
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"electron": "<rootDir>/__mock__/electron.js"
|
||||
"electron": "<rootDir>/__mock__/electron.js",
|
||||
"\\.(css|less)$": "identity-obj-proxy"
|
||||
},
|
||||
"testEnvironment": "node"
|
||||
},
|
||||
@@ -37,16 +38,17 @@
|
||||
"electron": "^2.0.0-beta.5",
|
||||
"electron-builder": "^20.8.1",
|
||||
"electron-reload": "^1.2.2",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.1.1",
|
||||
"eslint": "^4.19.1",
|
||||
"eslint-plugin-react": "^7.7.0",
|
||||
"file-loader": "^1.1.10",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^22.4.3",
|
||||
"mini-css-extract-plugin": "^0.4.0",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"webpack": "^4.1.1",
|
||||
"webpack-cli": "^2.0.11",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.1.1"
|
||||
"webpack-cli": "^2.0.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome": "^1.1.5",
|
||||
@@ -59,14 +61,12 @@
|
||||
"ethernet-ip": "^1.1.4",
|
||||
"lodash": "^4.17.5",
|
||||
"react": "^16.2.0",
|
||||
"react-canvas-gauges": "^1.2.1",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-event-timeline": "^1.5.1",
|
||||
"react-liquid-gauge": "^1.2.4",
|
||||
"react-redux": "^5.0.7",
|
||||
"react-router": "^4.2.0",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-svg-gauge": "^1.0.7",
|
||||
"react-vis": "^1.9.2",
|
||||
"redux": "^3.7.2",
|
||||
"redux-electron-ipc": "^1.1.12",
|
||||
|
||||
Reference in New Issue
Block a user