Merge pull request #1650 from FarmBot/new_steps
(DRAFT) New Sequence Steps, Part IIlog_problems_iii
commit
a32d9f025b
|
@ -1 +1 @@
|
|||
2.6.5
|
||||
2.7.0
|
||||
|
|
3
Gemfile
3
Gemfile
|
@ -1,5 +1,5 @@
|
|||
source "https://rubygems.org"
|
||||
ruby "~> 2.6.5"
|
||||
ruby "~> 2.7.0"
|
||||
|
||||
gem "rails"
|
||||
gem "active_model_serializers"
|
||||
|
@ -24,6 +24,7 @@ gem "scenic"
|
|||
gem "secure_headers"
|
||||
gem "tzinfo" # For validation of user selected timezone names
|
||||
gem "valid_url"
|
||||
# gem "farady", "~> 1.0.0"
|
||||
|
||||
group :development, :test do
|
||||
gem "climate_control"
|
||||
|
|
24
Gemfile.lock
24
Gemfile.lock
|
@ -109,7 +109,7 @@ GEM
|
|||
factory_bot_rails (5.1.1)
|
||||
factory_bot (~> 5.1.0)
|
||||
railties (>= 4.2.0)
|
||||
faker (2.9.0)
|
||||
faker (2.10.0)
|
||||
i18n (>= 1.6, < 1.8)
|
||||
faraday (0.15.4)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
|
@ -119,7 +119,7 @@ GEM
|
|||
railties (>= 3.2, < 6.1)
|
||||
globalid (0.4.2)
|
||||
activesupport (>= 4.2.0)
|
||||
google-api-client (0.36.1)
|
||||
google-api-client (0.36.3)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (~> 0.9)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
|
@ -178,13 +178,13 @@ GEM
|
|||
passenger (6.0.4)
|
||||
rack
|
||||
rake (>= 0.8.1)
|
||||
pg (1.1.4)
|
||||
pg (1.2.1)
|
||||
pry (0.12.2)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.9.0)
|
||||
pry-rails (0.3.9)
|
||||
pry (>= 0.10.4)
|
||||
public_suffix (4.0.1)
|
||||
public_suffix (4.0.2)
|
||||
rabbitmq_http_api_client (1.12.0)
|
||||
faraday (~> 0.15.4)
|
||||
faraday_middleware (~> 0.13.0)
|
||||
|
@ -193,7 +193,7 @@ GEM
|
|||
rack (2.0.8)
|
||||
rack-attack (6.2.2)
|
||||
rack (>= 1.0, < 3)
|
||||
rack-cors (1.1.0)
|
||||
rack-cors (1.1.1)
|
||||
rack (>= 2.0.0)
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
|
@ -245,12 +245,12 @@ GEM
|
|||
rspec-core (~> 3.9.0)
|
||||
rspec-expectations (~> 3.9.0)
|
||||
rspec-mocks (~> 3.9.0)
|
||||
rspec-core (3.9.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-core (3.9.1)
|
||||
rspec-support (~> 3.9.1)
|
||||
rspec-expectations (3.9.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-mocks (3.9.0)
|
||||
rspec-mocks (3.9.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.9.0)
|
||||
rspec-rails (4.0.0.beta3)
|
||||
|
@ -261,7 +261,7 @@ GEM
|
|||
rspec-expectations (~> 3.8)
|
||||
rspec-mocks (~> 3.8)
|
||||
rspec-support (~> 3.8)
|
||||
rspec-support (3.9.0)
|
||||
rspec-support (3.9.2)
|
||||
scenic (1.5.1)
|
||||
activerecord (>= 4.0.0)
|
||||
railties (>= 4.0.0)
|
||||
|
@ -285,7 +285,7 @@ GEM
|
|||
sprockets (>= 3.0.0)
|
||||
thor (1.0.1)
|
||||
thread_safe (0.3.6)
|
||||
tzinfo (1.2.5)
|
||||
tzinfo (1.2.6)
|
||||
thread_safe (~> 0.1)
|
||||
uber (0.1.0)
|
||||
url (0.3.2)
|
||||
|
@ -341,7 +341,7 @@ DEPENDENCIES
|
|||
valid_url
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.6.5p114
|
||||
ruby 2.7.0p0
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
||||
2.1.3
|
||||
|
|
|
@ -26,7 +26,7 @@ module CeleryScript
|
|||
|
||||
def maybe_initialize(parent, leaf_or_node, key = NEVER)
|
||||
if is_node?(leaf_or_node)
|
||||
AstNode.new(parent, leaf_or_node)
|
||||
AstNode.new(parent, **leaf_or_node)
|
||||
else
|
||||
raise TypeCheckError, LEAVES_NEED_KEYS if key == NEVER
|
||||
AstLeaf.new(parent, leaf_or_node, key)
|
||||
|
|
|
@ -78,7 +78,7 @@ class Fragment < ApplicationRecord
|
|||
|
||||
def self.from_celery(device:, kind:, args:, body:, owner:)
|
||||
p = { device: device, kind: kind, args: args, body: body }
|
||||
flat_ast = Fragments::Preprocessor.run!(p)
|
||||
flat_ast = Fragments::Preprocessor.run!(**p)
|
||||
Fragments::Create.run!(device: device,
|
||||
flat_ast: flat_ast,
|
||||
owner: owner)
|
||||
|
|
|
@ -43,7 +43,7 @@ class Image < ApplicationRecord
|
|||
has_one_attached :attachment
|
||||
|
||||
def set_attachment_by_url(url)
|
||||
attachment.attach(io: open(url), filename: "image_#{self.id}")
|
||||
attachment.attach(io: URI.open(url), filename: "image_#{self.id}")
|
||||
self.attachment_processed_at = Time.now
|
||||
self
|
||||
end
|
||||
|
|
|
@ -16,7 +16,7 @@ module FarmEvents
|
|||
kind: "internal_#{kind}",
|
||||
args: {},
|
||||
body: body }
|
||||
flat_ast = Fragments::Preprocessor.run!(params)
|
||||
flat_ast = Fragments::Preprocessor.run!(**params)
|
||||
Fragments::Create.run!(device: device,
|
||||
flat_ast: flat_ast,
|
||||
owner: owner)
|
||||
|
|
|
@ -5,7 +5,7 @@ module Fragments
|
|||
def self.run!(kind:, args:, body:, device:)
|
||||
canonical = {kind: kind, args: args, body: body}
|
||||
slicer = CeleryScript::Slicer.new
|
||||
tree = CeleryScript::AstNode.new(canonical.deep_symbolize_keys)
|
||||
tree = CeleryScript::AstNode.new(**canonical.deep_symbolize_keys)
|
||||
checker = CeleryScript::Checker.new(tree, Corpus, device)
|
||||
checker.run!
|
||||
slicer.run!(canonical)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM ruby:2.6.5
|
||||
FROM ruby:2.7.0
|
||||
RUN wget -q https://www.postgresql.org/media/keys/ACCC4CF8.asc -O - | apt-key add - && \
|
||||
sh -c 'VERSION_CODENAME=stretch; . /etc/os-release; echo "deb http://apt.postgresql.org/pub/repos/apt/ $VERSION_CODENAME-pgdg main" >> /etc/apt/sources.list.d/pgdg.list' && \
|
||||
apt-get update -qq && apt-get install -y build-essential libpq-dev postgresql postgresql-contrib && \
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
jest.mock("../../api/crud", () => {
|
||||
return { editStep: jest.fn() };
|
||||
});
|
||||
import { TileReboot, editTheRebootStep } from "../step_tiles/tile_reboot";
|
||||
import { render } from "enzyme";
|
||||
import { TileReboot, editTheRebootStep, rebootExecutor, MultiChoiceRadio } from "../step_tiles/tile_reboot";
|
||||
import { render, mount } from "enzyme";
|
||||
import React from "react";
|
||||
import { StepParams } from "../interfaces";
|
||||
import { fakeSequence } from "../../__test_support__/fake_state/resources";
|
||||
import { buildResourceIndex } from "../../__test_support__/resource_index_builder";
|
||||
import { editStep } from "../../api/crud";
|
||||
import { Reboot } from "farmbot";
|
||||
|
||||
const fakeProps = (): StepParams => {
|
||||
const currentSequence = fakeSequence();
|
||||
|
@ -55,4 +56,31 @@ describe("<TileReboot/>", () => {
|
|||
executor: expect.any(Function),
|
||||
});
|
||||
});
|
||||
|
||||
it("executes the executor", () => {
|
||||
const props = fakeProps();
|
||||
const step = props.currentStep as Reboot;
|
||||
step.args.package = "X";
|
||||
const fn = rebootExecutor("arduino_firmware");
|
||||
fn(step);
|
||||
expect(step.args.package).toBe("arduino_firmware");
|
||||
});
|
||||
});
|
||||
|
||||
describe("MultiChoiceRadio", () => {
|
||||
it("triggers callbacks", () => {
|
||||
const PACKAGE_CHOICES = {
|
||||
"a": "1",
|
||||
"b": "2"
|
||||
};
|
||||
const onChange = jest.fn();
|
||||
const el = mount(<MultiChoiceRadio
|
||||
uuid={"WHATEVER"}
|
||||
choices={PACKAGE_CHOICES}
|
||||
currentChoice={"a"}
|
||||
onChange={onChange} />);
|
||||
const radio = el.find("input[type='radio']").first();
|
||||
radio.simulate("change", "a");
|
||||
expect(onChange).toHaveBeenCalledWith("a");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -55,7 +55,7 @@ export function StepButtonCluster(props: StepButtonProps) {
|
|||
pin_number: 4
|
||||
}
|
||||
}}
|
||||
color="yellow">
|
||||
color="orange">
|
||||
{t("TOGGLE PERIPHERAL")}
|
||||
</StepButton>,
|
||||
<StepButton {...commonStepProps}
|
||||
|
@ -103,7 +103,7 @@ export function StepButtonCluster(props: StepButtonProps) {
|
|||
<StepButton {...commonStepProps}
|
||||
step={{ kind: "emergency_lock", args: {} }}
|
||||
color="brown">
|
||||
{t("EMERGENCY LOCK")}
|
||||
{t("E-STOP")}
|
||||
</StepButton>,
|
||||
<StepButton {...commonStepProps}
|
||||
step={{ kind: "reboot", args: { package: "farmbot_os" } }}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
jest.mock("../../../api/crud", () => ({ editStep: jest.fn() }));
|
||||
|
||||
import * as React from "react";
|
||||
import { TileSetServoAngle } from "../tile_set_servo_angle";
|
||||
import { TileSetServoAngle, pinNumberChanger, createServoEditFn, ServoPinSelection } from "../tile_set_servo_angle";
|
||||
import { mount } from "enzyme";
|
||||
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
||||
import { SetServoAngle } from "farmbot";
|
||||
import { emptyState } from "../../../resources/reducer";
|
||||
import { StepParams } from "../../interfaces";
|
||||
import { editStep } from "../../../api/crud";
|
||||
|
||||
describe("<TileSetServoAngle/>", () => {
|
||||
const currentStep: SetServoAngle = {
|
||||
|
@ -18,20 +21,64 @@ describe("<TileSetServoAngle/>", () => {
|
|||
const fakeProps = (): StepParams => ({
|
||||
currentSequence: fakeSequence(),
|
||||
currentStep: currentStep,
|
||||
dispatch: jest.fn(),
|
||||
dispatch: jest.fn((fn: Function) => typeof fn === "function" && fn()),
|
||||
index: 0,
|
||||
resources: emptyState().index,
|
||||
confirmStepDeletion: false,
|
||||
});
|
||||
|
||||
it("renders inputs", () => {
|
||||
const block = mount(<TileSetServoAngle {...fakeProps()} />);
|
||||
const props = fakeProps();
|
||||
const block = mount(<TileSetServoAngle {...props} />);
|
||||
const inputs = block.find("input");
|
||||
const labels = block.find("label");
|
||||
expect(inputs.length).toEqual(3);
|
||||
expect(labels.length).toEqual(2);
|
||||
expect(inputs.first().props().placeholder).toEqual("Set Servo Angle");
|
||||
expect(labels.at(0).text()).toContain("Servo pin");
|
||||
expect(inputs.at(1).props().value).toEqual(4);
|
||||
const stepArgs = props.currentStep.args as SetServoAngle["args"];
|
||||
expect(inputs.length).toEqual(4);
|
||||
expect(labels.length).toEqual(4);
|
||||
expect(inputs.first().props().placeholder).toEqual("Control Servo");
|
||||
expect(labels.at(0).text()).toContain("Servo angle (0-180)");
|
||||
expect(inputs.at(1).props().value).toEqual(stepArgs.pin_value);
|
||||
expect(inputs.at(2).props().value).toEqual("" + stepArgs.pin_number);
|
||||
});
|
||||
|
||||
it("Changes pin number", () => {
|
||||
const props = fakeProps();
|
||||
const fn = pinNumberChanger(props);
|
||||
fn("5");
|
||||
expect(editStep).toHaveBeenCalledWith({
|
||||
step: props.currentStep,
|
||||
sequence: props.currentSequence,
|
||||
index: props.index,
|
||||
executor: expect.any(Function)
|
||||
});
|
||||
});
|
||||
|
||||
it("dissallows named_pins", () => {
|
||||
const p = fakeProps();
|
||||
const step = p.currentStep;
|
||||
if (step.kind === "set_servo_angle") {
|
||||
step.args.pin_number = {
|
||||
kind: "named_pin",
|
||||
args: { pin_id: 0, pin_type: "Peripheral" }
|
||||
};
|
||||
const boom = () => ServoPinSelection(p);
|
||||
expect(boom).toThrowError();
|
||||
return;
|
||||
}
|
||||
|
||||
fail();
|
||||
});
|
||||
|
||||
it("creates a servo edit function", () => {
|
||||
const props = fakeProps();
|
||||
const step = props.currentStep;
|
||||
const fn = createServoEditFn("4");
|
||||
if (step.kind === "set_servo_angle") {
|
||||
step.args.pin_number = 1;
|
||||
fn(step);
|
||||
expect(step.args.pin_number).toEqual(4);
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -27,10 +27,9 @@ describe("<TileTogglePin/>", () => {
|
|||
const block = mount(<TileTogglePin {...fakeProps()} />);
|
||||
const inputs = block.find("input");
|
||||
const labels = block.find("label");
|
||||
expect(inputs.length).toEqual(2);
|
||||
expect(inputs.length).toEqual(1);
|
||||
expect(labels.length).toEqual(1);
|
||||
expect(inputs.first().props().placeholder).toEqual("Toggle Pin");
|
||||
expect(labels.at(0).text()).toContain("Pin");
|
||||
expect(inputs.at(1).props().value).toEqual(13);
|
||||
expect(inputs.first().props().placeholder).toEqual("Toggle Peripheral");
|
||||
expect(labels.at(0).text()).toContain("Peripheral");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,35 +2,36 @@ const mockEditStep = jest.fn();
|
|||
jest.mock("../../../api/crud", () => ({ editStep: mockEditStep }));
|
||||
|
||||
import * as React from "react";
|
||||
import { TileWritePin, WritePinStep } from "../tile_write_pin";
|
||||
import { TileWritePin, WritePinStep, PinSelect } from "../tile_write_pin";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import { fakeSequence } from "../../../__test_support__/fake_state/resources";
|
||||
import { WritePin } from "farmbot/dist";
|
||||
import { emptyState } from "../../../resources/reducer";
|
||||
import { FBSelect } from "../../../ui";
|
||||
import { StepParams } from "../../interfaces";
|
||||
|
||||
function fakeProps() {
|
||||
const currentStep: WritePin = {
|
||||
kind: "write_pin",
|
||||
args: {
|
||||
pin_number: 3,
|
||||
pin_value: 2,
|
||||
pin_mode: 1
|
||||
}
|
||||
};
|
||||
return {
|
||||
currentSequence: fakeSequence(),
|
||||
currentStep: currentStep,
|
||||
dispatch: jest.fn(),
|
||||
index: 0,
|
||||
resources: emptyState().index,
|
||||
confirmStepDeletion: false,
|
||||
shouldDisplay: () => false,
|
||||
showPins: false,
|
||||
};
|
||||
}
|
||||
|
||||
describe("<TileWritePin/>", () => {
|
||||
function fakeProps() {
|
||||
const currentStep: WritePin = {
|
||||
kind: "write_pin",
|
||||
args: {
|
||||
pin_number: 3,
|
||||
pin_value: 2,
|
||||
pin_mode: 1
|
||||
}
|
||||
};
|
||||
return {
|
||||
currentSequence: fakeSequence(),
|
||||
currentStep: currentStep,
|
||||
dispatch: jest.fn(),
|
||||
index: 0,
|
||||
resources: emptyState().index,
|
||||
confirmStepDeletion: false,
|
||||
shouldDisplay: () => false,
|
||||
showPins: false,
|
||||
};
|
||||
}
|
||||
|
||||
it("renders inputs: Analog", () => {
|
||||
const wrapper = mount(<TileWritePin {...fakeProps()} />);
|
||||
const inputs = wrapper.find("input");
|
||||
|
@ -91,3 +92,12 @@ describe("<TileWritePin/>", () => {
|
|||
.toThrow("Not a write_pin block.");
|
||||
});
|
||||
});
|
||||
|
||||
describe("<PinSelect/>", () => {
|
||||
it("crashes on bad step `kind`s", () => {
|
||||
const props = fakeProps() as StepParams;
|
||||
props.currentStep = { kind: "execute", args: { sequence_id: 23 } };
|
||||
const boom = () => PinSelect(props);
|
||||
expect(boom).toThrowError("PinSelect can't render execute");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -210,6 +210,7 @@ export const setArgsDotPinNumber =
|
|||
switch (c.kind) {
|
||||
case "read_pin":
|
||||
case "write_pin":
|
||||
case "toggle_pin":
|
||||
c.args.pin_number = dropDown2CeleryArg(resources, d);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ function translate(input: Step): string {
|
|||
"take_photo": t("Take a Photo"),
|
||||
"resource_update": t("Mark As"),
|
||||
"assertion": t("Assertion"),
|
||||
"set_servo_angle": t("Set Servo Angle"),
|
||||
"set_servo_angle": t("Control Servo"),
|
||||
"wait": t("Wait"),
|
||||
"write_pin": t("Control Peripheral"),
|
||||
"sync": t("Sync"),
|
||||
|
@ -39,7 +39,7 @@ function translate(input: Step): string {
|
|||
"home": t("Move to Home"),
|
||||
"factory_reset": t("Factory reset"),
|
||||
"reboot": t("Reboot"),
|
||||
"toggle_pin": t("Toggle Pin"),
|
||||
"toggle_pin": t("Toggle Peripheral"),
|
||||
"zero": t("Set zero"),
|
||||
"set_user_env": t("Set Farmware Env"),
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ interface MultiChoiceRadioProps<T extends StringMap> {
|
|||
onChange(key: keyof T): void;
|
||||
}
|
||||
|
||||
const MultiChoiceRadio =
|
||||
export const MultiChoiceRadio =
|
||||
<T extends StringMap>(props: MultiChoiceRadioProps<T>) => {
|
||||
const choices = Object.keys(props.choices);
|
||||
return <Row>
|
||||
|
@ -74,7 +74,7 @@ export const editTheRebootStep =
|
|||
|
||||
export function TileReboot(props: StepParams) {
|
||||
const { dispatch, currentStep, index, currentSequence } = props;
|
||||
const className = "set-zero-step";
|
||||
const className = "take-photo-step";
|
||||
assertReboot(currentStep);
|
||||
return <StepWrapper>
|
||||
<StepHeader
|
||||
|
|
|
@ -5,6 +5,47 @@ import { ToolTips } from "../../constants";
|
|||
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
||||
import { Row, Col } from "../../ui/index";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
import { MultiChoiceRadio } from "./tile_reboot";
|
||||
import { SetServoAngle } from "farmbot";
|
||||
import { editStep } from "../../api/crud";
|
||||
|
||||
const PACKAGE_CHOICES: Record<string, string> = {
|
||||
"4": "Pin 4",
|
||||
"5": "Pin 5",
|
||||
};
|
||||
|
||||
type Keys =
|
||||
| "dispatch"
|
||||
| "currentStep"
|
||||
| "currentSequence"
|
||||
| "index";
|
||||
type Props = Pick<StepParams, Keys>;
|
||||
|
||||
export const createServoEditFn = (y: string) => (x: SetServoAngle) => {
|
||||
x.args.pin_number = parseInt(y, 10);
|
||||
};
|
||||
|
||||
export const pinNumberChanger = (props: Props) => (y: string) => {
|
||||
props.dispatch(editStep({
|
||||
step: props.currentStep,
|
||||
sequence: props.currentSequence,
|
||||
index: props.index,
|
||||
executor: createServoEditFn(y)
|
||||
}));
|
||||
};
|
||||
|
||||
export function ServoPinSelection(props: Props) {
|
||||
const { currentSequence, index, currentStep } = props;
|
||||
const num = (currentStep as SetServoAngle).args.pin_number;
|
||||
if (typeof num !== "number") { throw new Error("NO!"); }
|
||||
const onChange = pinNumberChanger(props);
|
||||
|
||||
return <MultiChoiceRadio
|
||||
uuid={currentSequence.uuid + index}
|
||||
choices={PACKAGE_CHOICES}
|
||||
currentChoice={"" + num}
|
||||
onChange={onChange} />;
|
||||
}
|
||||
|
||||
export function TileSetServoAngle(props: StepParams) {
|
||||
const { dispatch, currentStep, index, currentSequence } = props;
|
||||
|
@ -20,24 +61,21 @@ export function TileSetServoAngle(props: StepParams) {
|
|||
confirmStepDeletion={props.confirmStepDeletion} />
|
||||
<StepContent className={className}>
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<label>{t("Servo pin")}</label>
|
||||
<StepInputBox dispatch={dispatch}
|
||||
step={currentStep}
|
||||
sequence={currentSequence}
|
||||
index={index}
|
||||
field={"pin_number"} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<label>{t("Servo angle (0-180)")}</label>
|
||||
<StepInputBox dispatch={dispatch}
|
||||
<Col lg={4} xs={6}>
|
||||
<label>
|
||||
{t("Servo angle (0-180)")}
|
||||
</label>
|
||||
<StepInputBox
|
||||
dispatch={dispatch}
|
||||
step={currentStep}
|
||||
sequence={currentSequence}
|
||||
index={index}
|
||||
field={"pin_value"} />
|
||||
</Col>
|
||||
<Col lg={8} xs={6}>
|
||||
<label>{t("Servo pin")}</label>
|
||||
<ServoPinSelection {...props} />
|
||||
</Col>
|
||||
</Row>
|
||||
</StepContent>
|
||||
</StepWrapper>;
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import * as React from "react";
|
||||
import { StepInputBox } from "../inputs/step_input_box";
|
||||
import { StepParams } from "../interfaces";
|
||||
import { ToolTips } from "../../constants";
|
||||
import { StepWrapper, StepHeader, StepContent } from "../step_ui/index";
|
||||
import { Row, Col } from "../../ui/index";
|
||||
import { t } from "../../i18next_wrapper";
|
||||
import { Row } from "../../ui/index";
|
||||
import { PinSelect } from "./tile_write_pin";
|
||||
|
||||
export function TileTogglePin(props: StepParams) {
|
||||
const { dispatch, currentStep, index, currentSequence } = props;
|
||||
const className = "toggle-pin-step";
|
||||
const className = "write-pin-step";
|
||||
return <StepWrapper>
|
||||
<StepHeader
|
||||
className={className}
|
||||
|
@ -20,14 +19,7 @@ export function TileTogglePin(props: StepParams) {
|
|||
confirmStepDeletion={props.confirmStepDeletion} />
|
||||
<StepContent className={className}>
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<label>{t("Pin")}</label>
|
||||
<StepInputBox dispatch={dispatch}
|
||||
step={currentStep}
|
||||
sequence={currentSequence}
|
||||
index={index}
|
||||
field={"pin_number"} />
|
||||
</Col>
|
||||
<PinSelect {...props} width={6} />
|
||||
</Row>
|
||||
</StepContent>
|
||||
</StepWrapper>;
|
||||
|
|
|
@ -44,22 +44,35 @@ export interface WritePinStepParams {
|
|||
showPins: boolean;
|
||||
}
|
||||
|
||||
export class WritePinStep extends React.Component<WritePinStepParams> {
|
||||
interface PinSelectProps extends StepParams {
|
||||
width?: number;
|
||||
}
|
||||
|
||||
PinSelect = (): JSX.Element => {
|
||||
const { currentSequence, resources, showPins } = this.props;
|
||||
const { pin_number } = this.props.currentStep.args;
|
||||
return <Col xs={6} md={6}>
|
||||
export const PinSelect = (props: PinSelectProps): JSX.Element => {
|
||||
const step = props.currentStep;
|
||||
if (
|
||||
step.kind === "write_pin" ||
|
||||
step.kind === "toggle_pin" ||
|
||||
step.kind === "read_pin") {
|
||||
const { currentSequence, resources, showPins } = props;
|
||||
const { pin_number } = step.args;
|
||||
const width = props.width || 6;
|
||||
|
||||
return <Col xs={width} md={width}>
|
||||
<label>{t("Peripheral")}</label>
|
||||
<FBSelect
|
||||
key={JSON.stringify(currentSequence)}
|
||||
selectedItem={celery2DropDown(pin_number, resources)}
|
||||
customNullLabel={t("Select a peripheral")}
|
||||
onChange={setArgsDotPinNumber(this.props)}
|
||||
onChange={setArgsDotPinNumber(props)}
|
||||
list={pinsAsDropDownsWritePin(resources, !!showPins)} />
|
||||
</Col>;
|
||||
}
|
||||
|
||||
throw new Error("PinSelect can't render " + step.kind);
|
||||
};
|
||||
|
||||
export class WritePinStep extends React.Component<WritePinStepParams> {
|
||||
PinValueField = (): JSX.Element => {
|
||||
const { dispatch, currentStep, index, currentSequence } = this.props;
|
||||
return (!(currentStep.args.pin_mode === 0) || currentStep.args.pin_value > 1)
|
||||
|
@ -90,7 +103,7 @@ export class WritePinStep extends React.Component<WritePinStepParams> {
|
|||
confirmStepDeletion={this.props.confirmStepDeletion} />
|
||||
<StepContent className={className}>
|
||||
<Row>
|
||||
<this.PinSelect />
|
||||
<PinSelect {...this.props} />
|
||||
<PinMode {...this.props} />
|
||||
<Col xs={6} md={3}>
|
||||
<label>{t("set to")}</label>
|
||||
|
|
|
@ -116,8 +116,8 @@ namespace :api do
|
|||
# 60 days is the current policy.
|
||||
cutoff = 60.days.ago
|
||||
# Download release data from github
|
||||
string_page_1 = open("#{RELEASES_URL}?per_page=100&page=1").read
|
||||
string_page_2 = open("#{RELEASES_URL}?per_page=100&page=2").read
|
||||
string_page_1 = URI.open("#{RELEASES_URL}?per_page=100&page=1").read
|
||||
string_page_2 = URI.open("#{RELEASES_URL}?per_page=100&page=2").read
|
||||
data = JSON.parse(string_page_1).push(*JSON.parse(string_page_2))
|
||||
.map { |x| x.slice(VERSION, TIMESTAMP, PRERELEASE) } # Only grab keys that matter
|
||||
.reject { |x| x.fetch(VERSION).include?("-") } # Remove RC/Beta releases
|
||||
|
|
|
@ -14,7 +14,7 @@ FRACTION_DELIM = "/"
|
|||
# Fetch JSON over HTTP. Rails probably already has a helper for this :shrug:
|
||||
def open_json(url)
|
||||
begin
|
||||
JSON.parse(open(url).read)
|
||||
JSON.parse(URI.open(url).read)
|
||||
rescue *[OpenURI::HTTPError, SocketError] => exception
|
||||
puts exception.message
|
||||
return {}
|
||||
|
@ -155,7 +155,8 @@ namespace :coverage do
|
|||
"compare against a PR's base branch and would always return 0% change."
|
||||
task run: :environment do
|
||||
# Fetch current build coverage data from the HTML summary.
|
||||
statements, branches, functions, lines = Nokogiri::HTML(open(COVERAGE_FILE_PATH))
|
||||
statements, branches, functions, lines =
|
||||
Nokogiri::HTML(URI.open(COVERAGE_FILE_PATH))
|
||||
.css(CSS_SELECTOR)
|
||||
.map(&:text)
|
||||
.map { |x| x.split(FRACTION_DELIM).map(&:to_f) }
|
||||
|
|
|
@ -5,12 +5,12 @@ EXCLUDE = []
|
|||
|
||||
# Load package.json as JSON.
|
||||
def load_package_json()
|
||||
return JSON.parse(open(PACKAGE_JSON_FILE).read)
|
||||
return JSON.parse(URI.open(PACKAGE_JSON_FILE).read)
|
||||
end
|
||||
|
||||
# Save JSON to package.json.
|
||||
def save_package_json(json)
|
||||
open(PACKAGE_JSON_FILE, "w") { |file|
|
||||
URI.open(PACKAGE_JSON_FILE, "w") { |file|
|
||||
file.write(JSON.pretty_generate(json))
|
||||
file.puts
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ describe Api::PasswordResetsController do
|
|||
|
||||
it "handles token expiration" do
|
||||
token = PasswordResetToken
|
||||
.issue_to(user, { exp: Time.now.yesterday.to_i })
|
||||
.issue_to(user, **{ exp: Time.now.yesterday.to_i })
|
||||
.encoded
|
||||
|
||||
params = { password: "xpassword123",
|
||||
|
|
|
@ -9,7 +9,7 @@ describe "Body nodes" do
|
|||
let(:device) { FactoryBot.create(:device) }
|
||||
|
||||
it "always always empty bodies" do
|
||||
tree = CeleryScript::AstNode.new({
|
||||
tree = CeleryScript::AstNode.new(**{
|
||||
"kind": "baz",
|
||||
"args": {},
|
||||
"body": []
|
||||
|
@ -19,7 +19,7 @@ describe "Body nodes" do
|
|||
end
|
||||
|
||||
it "kicks back unexpected nodes" do
|
||||
tree = CeleryScript::AstNode.new({
|
||||
tree = CeleryScript::AstNode.new(**{
|
||||
"kind": "baz",
|
||||
"args": {},
|
||||
"body": [{ "kind": "wrong", "args": {}}]
|
||||
|
@ -30,7 +30,7 @@ describe "Body nodes" do
|
|||
end
|
||||
|
||||
it "handles body members of nodes that shouldn't have bodies." do
|
||||
tree = CeleryScript::AstNode.new({
|
||||
tree = CeleryScript::AstNode.new(**{
|
||||
"kind": "baz",
|
||||
"args": {},
|
||||
"body": [{ "kind": "wrong", "args": {}}]
|
||||
|
@ -41,7 +41,7 @@ describe "Body nodes" do
|
|||
end
|
||||
|
||||
it 'disallows leaves in the body field of a node' do
|
||||
tree = CeleryScript::AstNode.new({
|
||||
tree = CeleryScript::AstNode.new(**{
|
||||
"kind": "wrong",
|
||||
"args": {},
|
||||
"body": [
|
||||
|
|
|
@ -14,9 +14,7 @@ describe CeleryScript::Checker do
|
|||
}.deep_symbolize_keys
|
||||
end
|
||||
|
||||
let(:tree) do
|
||||
CeleryScript::AstNode.new(hash)
|
||||
end
|
||||
let(:tree) { CeleryScript::AstNode.new(**hash) }
|
||||
|
||||
let (:corpus) { Sequence::Corpus }
|
||||
|
||||
|
@ -205,14 +203,14 @@ describe CeleryScript::Checker do
|
|||
|
||||
it "catches bad `axis` nodes" do
|
||||
t =
|
||||
CeleryScript::AstNode.new({ kind: "home", args: { speed: 100, axis: "?" } })
|
||||
CeleryScript::AstNode.new(kind: "home", args: { speed: 100, axis: "?" })
|
||||
chk = CeleryScript::Checker.new(t, corpus, device)
|
||||
expect(chk.valid?).to be false
|
||||
expect(chk.error.message).to include("not a valid axis")
|
||||
end
|
||||
|
||||
it "catches bad `package` nodes" do
|
||||
t = CeleryScript::AstNode.new({ kind: "factory_reset", args: { package: "?" } })
|
||||
t = CeleryScript::AstNode.new(kind: "factory_reset", args: { package: "?" })
|
||||
chk = CeleryScript::Checker.new(t, corpus, device)
|
||||
expect(chk.valid?).to be false
|
||||
expect(chk.error.message).to include("not a valid package")
|
||||
|
@ -251,7 +249,7 @@ describe CeleryScript::Checker do
|
|||
},
|
||||
],
|
||||
}
|
||||
tree = CeleryScript::AstNode.new(ast)
|
||||
tree = CeleryScript::AstNode.new(**ast)
|
||||
chk = CeleryScript::Checker.new(tree, corpus, device)
|
||||
expect(chk.valid?).to be true
|
||||
end
|
||||
|
@ -289,7 +287,7 @@ describe CeleryScript::Checker do
|
|||
},
|
||||
],
|
||||
}
|
||||
tree = CeleryScript::AstNode.new(ast)
|
||||
tree = CeleryScript::AstNode.new(**ast)
|
||||
chk = CeleryScript::Checker.new(tree, corpus, device)
|
||||
expect(chk.valid?).to be false
|
||||
message = "must provide a value for all parameters"
|
||||
|
|
|
@ -5,7 +5,7 @@ describe CeleryScript::Corpus do
|
|||
let(:corpus) { Sequence::Corpus }
|
||||
|
||||
it "handles valid move_absolute blocks" do
|
||||
ok1 = CeleryScript::AstNode.new({
|
||||
ok1 = CeleryScript::AstNode.new(**{
|
||||
kind: "move_absolute",
|
||||
args: {
|
||||
location: {
|
||||
|
@ -30,7 +30,7 @@ describe CeleryScript::Corpus do
|
|||
check1 = CeleryScript::Checker.new(ok1, corpus, device)
|
||||
expect(check1.valid?).to be_truthy
|
||||
|
||||
ok2 = CeleryScript::AstNode.new({
|
||||
ok2 = CeleryScript::AstNode.new(**{
|
||||
kind: "move_absolute",
|
||||
args: {
|
||||
location: {
|
||||
|
@ -53,7 +53,7 @@ describe CeleryScript::Corpus do
|
|||
end
|
||||
|
||||
it "kicks back invalid move_absolute nodes" do
|
||||
bad = CeleryScript::AstNode.new({
|
||||
bad = CeleryScript::AstNode.new(**{
|
||||
kind: "move_absolute",
|
||||
args: {
|
||||
location: 42,
|
||||
|
@ -75,7 +75,7 @@ describe CeleryScript::Corpus do
|
|||
end
|
||||
|
||||
it "finds problems with nested nodes" do
|
||||
bad = CeleryScript::AstNode.new({
|
||||
bad = CeleryScript::AstNode.new(**{
|
||||
kind: "move_absolute",
|
||||
args: {
|
||||
location: {
|
||||
|
@ -109,7 +109,7 @@ describe CeleryScript::Corpus do
|
|||
it "Handles message_type validations for version 1" do
|
||||
# This test is __ONLY__ relevant for version 1.
|
||||
# Change / delete / update as needed.
|
||||
tree = CeleryScript::AstNode.new({
|
||||
tree = CeleryScript::AstNode.new(**{
|
||||
"kind": "send_message",
|
||||
"args": {
|
||||
"message": "Hello, world!",
|
||||
|
@ -122,7 +122,7 @@ describe CeleryScript::Corpus do
|
|||
end
|
||||
|
||||
it "Handles channel_name validations" do
|
||||
tree = CeleryScript::AstNode.new({
|
||||
tree = CeleryScript::AstNode.new(**{
|
||||
"kind": "send_message",
|
||||
"args": {
|
||||
"message": "Hello, world!",
|
||||
|
@ -141,7 +141,7 @@ describe CeleryScript::Corpus do
|
|||
|
||||
it "validates tool_ids" do
|
||||
ast = { "kind": "tool", "args": { "tool_id": 0 } }
|
||||
checker = CeleryScript::Checker.new(CeleryScript::AstNode.new(ast),
|
||||
checker = CeleryScript::Checker.new(CeleryScript::AstNode.new(**ast),
|
||||
corpus,
|
||||
device)
|
||||
expect(checker.valid?).to be(false)
|
||||
|
@ -154,7 +154,8 @@ describe CeleryScript::Corpus do
|
|||
"resource_id" => 23, # Mutated to "0" later..
|
||||
"label" => "mounted_tool_id",
|
||||
"value" => 1 } }
|
||||
checker = CeleryScript::Checker.new(CeleryScript::AstNode.new(ast), corpus, device)
|
||||
checker = CeleryScript::Checker
|
||||
.new(CeleryScript::AstNode.new(**ast), corpus, device)
|
||||
expect(checker.valid?).to be(true)
|
||||
expect(checker.tree.args[:resource_id].value).to eq(device.id)
|
||||
end
|
||||
|
@ -167,7 +168,7 @@ describe CeleryScript::Corpus do
|
|||
"resource_id" => fake_id,
|
||||
"label" => "foo",
|
||||
"value" => "Should Fail" } }
|
||||
hmm = CeleryScript::AstNode.new(ast)
|
||||
hmm = CeleryScript::AstNode.new(**ast)
|
||||
expect(hmm.args.fetch(:resource_id).value).to eq(fake_id)
|
||||
checker = CeleryScript::Checker.new(hmm, corpus, device)
|
||||
expect(checker.valid?).to be(false)
|
||||
|
@ -180,7 +181,7 @@ describe CeleryScript::Corpus do
|
|||
"resource_id" => 0,
|
||||
"label" => "foo",
|
||||
"value" => "Should Fail" } }
|
||||
checker = CeleryScript::Checker.new(CeleryScript::AstNode.new(ast),
|
||||
checker = CeleryScript::Checker.new(CeleryScript::AstNode.new(**ast),
|
||||
corpus,
|
||||
device)
|
||||
expect(checker.valid?).to be(false)
|
||||
|
@ -222,7 +223,7 @@ describe CeleryScript::Corpus do
|
|||
end
|
||||
|
||||
it "sets a MAX_WAIT_MS limit for `wait` nodes" do
|
||||
bad = CeleryScript::AstNode.new({
|
||||
bad = CeleryScript::AstNode.new(**{
|
||||
kind: "wait",
|
||||
args: { milliseconds: CeleryScriptSettingsBag::MAX_WAIT_MS + 10 },
|
||||
})
|
||||
|
@ -236,7 +237,7 @@ describe CeleryScript::Corpus do
|
|||
pg = PointGroups::Create.run!(device: device,
|
||||
name: "cs checks",
|
||||
point_ids: [])
|
||||
bad = CeleryScript::AstNode.new({
|
||||
bad = CeleryScript::AstNode.new(**{
|
||||
kind: "point_group",
|
||||
args: { point_group_id: pg.id },
|
||||
})
|
||||
|
@ -247,7 +248,7 @@ describe CeleryScript::Corpus do
|
|||
|
||||
it "disallows invalid `point_group` nodes" do
|
||||
device.auto_sync_transaction do
|
||||
bad = CeleryScript::AstNode.new({
|
||||
bad = CeleryScript::AstNode.new(**{
|
||||
kind: "point_group",
|
||||
args: { point_group_id: -1 },
|
||||
})
|
||||
|
|
|
@ -22,7 +22,7 @@ describe "Celery Script `point` node" do
|
|||
}.deep_symbolize_keys
|
||||
end
|
||||
|
||||
let(:tree) { CeleryScript::AstNode.new(hash) }
|
||||
let(:tree) { CeleryScript::AstNode.new(**hash) }
|
||||
let(:corpus) { Sequence::Corpus }
|
||||
let(:device) { plant.device }
|
||||
let(:checker) { CeleryScript::Checker.new(tree, corpus, device) }
|
||||
|
|
|
@ -5,7 +5,7 @@ describe CeleryScript::TreeClimber do
|
|||
|
||||
let(:node) do
|
||||
hash = JSON.parse(file_path).deep_symbolize_keys
|
||||
CeleryScript::AstNode.new(hash)
|
||||
CeleryScript::AstNode.new(**hash)
|
||||
end
|
||||
|
||||
it "travels to each node with a callable object" do
|
||||
|
|
|
@ -47,7 +47,7 @@ describe Fragments::Create do
|
|||
},
|
||||
],
|
||||
}
|
||||
flat_ast = Fragments::Preprocessor.run!(origin)
|
||||
flat_ast = Fragments::Preprocessor.run!(**origin)
|
||||
fragment = Fragments::Create.run!(flat_ast: flat_ast, owner: farm_event)
|
||||
result = Fragments::Show.run!(owner: farm_event)
|
||||
diff = Hashdiff.diff(origin.without(:device), result.deep_symbolize_keys)
|
||||
|
@ -83,7 +83,7 @@ describe Fragments::Create do
|
|||
config.logger = spy_logger
|
||||
fragment = Fragments::Create.run!(device: device,
|
||||
owner: farm_event,
|
||||
flat_ast: Fragments::Preprocessor.run!(origin))
|
||||
flat_ast: Fragments::Preprocessor.run!(**origin))
|
||||
# Warm the cache up with two dry-runs:
|
||||
Fragments::Show.run!(owner: farm_event)
|
||||
Fragments::Show.run!(owner: farm_event)
|
||||
|
|
Loading…
Reference in New Issue