Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into MAT-7525_madie-editor-vuln…
Browse files Browse the repository at this point in the history
…erability
  • Loading branch information
Cecilia Liu committed Oct 15, 2024
2 parents 25914e3 + 513f077 commit feb3489
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 12 deletions.
2 changes: 2 additions & 0 deletions src/AceEditor/madie-ace-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import "./madie-custom.css";
import { ParsedCql, Statement } from "../model/ParsedCql";
import {
CqlMetaData,
Parameter,
ValueSetForSearch,
} from "../api/useTerminologyServiceApi";
import { Definition } from "../CqlBuilderPanel/definitionsSection/definitionBuilder/DefinitionBuilder";
Expand All @@ -29,6 +30,7 @@ export interface EditorPropsType {
value: string;
onChange?: (value: string) => void;
handleApplyCode?: (code: string) => void;
handleApplyParameter?: (parameter: Parameter) => void;
handleApplyValueSet?: (vs: ValueSetForSearch) => void;
handleApplyDefinition?: (def: Definition) => void;
handleDefinitionEdit?: (lib: SelectedLibrary, def: Definition) => void;
Expand Down
144 changes: 143 additions & 1 deletion src/CqlBuilderPanel/CqlBuilderPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ describe("CqlBuilderPanel", () => {
expect(parameterTab).not.toBeEnabled();
});

it("Parameters works", async () => {
it("Parameters clear works", async () => {
useFeatureFlags.mockImplementationOnce(() => ({
CQLBuilderIncludes: true,
QDMValueSetSearch: true,
Expand Down Expand Up @@ -725,4 +725,146 @@ describe("CqlBuilderPanel", () => {
expect(aceEditor.value).toContain("");
});
});
it("Parameters apply works and clears input fields", async () => {
useFeatureFlags.mockImplementationOnce(() => ({
CQLBuilderIncludes: true,
QDMValueSetSearch: true,
CQLBuilderDefinitions: true,
qdmCodeSearch: true,
CQLBuilderParameters: true,
}));
mockedAxios.put.mockResolvedValue({
data: mockCqlBuilderLookUpData,
});

const applyParameter = jest.fn();
applyParameter.mockReturnValue("success");
const copiedProps = { ...props, handleApplyParameter: applyParameter };
let result = render(<CqlBuilderPanel {...copiedProps} />);
const parameterTab = await screen.queryByText("Parameters");
expect(parameterTab).toBeInTheDocument();
userEvent.click(screen.getByRole("tab", { name: "Parameters" }));
expect(screen.getByTestId("cql-editor-parameters")).toBeInTheDocument();

await userEvent.click(screen.getByTestId("saved-parameters-tab"));

await waitFor(() => {
expect(screen.getByTestId("saved-parameters-tab")).toHaveAttribute(
"aria-selected",
"true"
);
});
expect(screen.getByTestId("saved-parameters")).toBeInTheDocument();
// switch back
await userEvent.click(screen.getByTestId("parameter-tab"));
await waitFor(() => {
expect(screen.getByTestId("parameter-tab")).toHaveAttribute(
"aria-selected",
"true"
);
});
// write some stuff in parameter name
const parameterInput = screen.getByTestId(
"parameter-name-input"
) as HTMLInputElement;
userEvent.type(parameterInput, "SomeText");
expect(parameterInput.value).toBe("SomeText");
// we now see expression edtior
const editor = screen.getByTestId(
"terminology-section-sub-header-content-Expression Editor"
);
expect(editor).toBeVisible();

//paste into editor, check it's there
const editorValue = "Some more Text";
let aceEditor: any = await result.container.querySelector(
"#ace-editor-wrapper textarea"
);
userEvent.paste(aceEditor, editorValue);
aceEditor = await result.container.querySelector(
"#ace-editor-wrapper textarea"
);
expect(aceEditor.value).toContain(editorValue);
// check that clear does anything
userEvent.click(getByTestId("apply-parameter-btn"));
await waitFor(() => {
expect(applyParameter).toHaveBeenCalledWith({
expression: "Some more Text",
parameterName: "SomeText",
});
expect(parameterInput.value).toBe("");
expect(aceEditor.value).toContain("");
});
});
it("Parameters apply did not work, fields did not get updated", async () => {
useFeatureFlags.mockImplementationOnce(() => ({
CQLBuilderIncludes: true,
QDMValueSetSearch: true,
CQLBuilderDefinitions: true,
qdmCodeSearch: true,
CQLBuilderParameters: true,
}));
mockedAxios.put.mockResolvedValue({
data: mockCqlBuilderLookUpData,
});

const applyParameter = jest.fn();
applyParameter.mockReturnValue("failure");
const copiedProps = { ...props, handleApplyParameter: applyParameter };
let result = render(<CqlBuilderPanel {...copiedProps} />);
const parameterTab = await screen.queryByText("Parameters");
expect(parameterTab).toBeInTheDocument();
userEvent.click(screen.getByRole("tab", { name: "Parameters" }));
expect(screen.getByTestId("cql-editor-parameters")).toBeInTheDocument();

await userEvent.click(screen.getByTestId("saved-parameters-tab"));

await waitFor(() => {
expect(screen.getByTestId("saved-parameters-tab")).toHaveAttribute(
"aria-selected",
"true"
);
});
expect(screen.getByTestId("saved-parameters")).toBeInTheDocument();
// switch back
await userEvent.click(screen.getByTestId("parameter-tab"));
await waitFor(() => {
expect(screen.getByTestId("parameter-tab")).toHaveAttribute(
"aria-selected",
"true"
);
});
// write some stuff in parameter name
const parameterInput = screen.getByTestId(
"parameter-name-input"
) as HTMLInputElement;
userEvent.type(parameterInput, "SomeText");
expect(parameterInput.value).toBe("SomeText");
// we now see expression edtior
const editor = screen.getByTestId(
"terminology-section-sub-header-content-Expression Editor"
);
expect(editor).toBeVisible();

//paste into editor, check it's there
const editorValue = "Some more Text";
let aceEditor: any = await result.container.querySelector(
"#ace-editor-wrapper textarea"
);
userEvent.paste(aceEditor, editorValue);
aceEditor = await result.container.querySelector(
"#ace-editor-wrapper textarea"
);
expect(aceEditor.value).toContain(editorValue);
// check that clear does anything
userEvent.click(getByTestId("apply-parameter-btn"));
await waitFor(() => {
expect(applyParameter).toHaveBeenCalledWith({
expression: "Some more Text",
parameterName: "SomeText",
});
expect(parameterInput.value).toContain("SomeText");
expect(aceEditor.value).toContain("Some more Text");
});
});
});
5 changes: 4 additions & 1 deletion src/CqlBuilderPanel/CqlBuilderPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default function CqlBuilderPanel({
handleDeleteLibrary,
handleEditLibrary,
handleApplyCode,
handleApplyParameter,
handleApplyValueSet,
handleApplyDefinition,
handleDefinitionEdit,
Expand Down Expand Up @@ -219,7 +220,9 @@ export default function CqlBuilderPanel({
handleApplyCode={handleApplyCode}
/>
)}
{activeTab === "parameters" && <Parameters />}
{activeTab === "parameters" && (
<Parameters handleApplyParameter={handleApplyParameter} />
)}

{activeTab === "definitions" && (
<DefinitionsSection
Expand Down
22 changes: 14 additions & 8 deletions src/CqlBuilderPanel/Parameters/ParameterPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,26 @@ const validationSchema = Yup.object({
"Only alphanumeric characters are allowed, no spaces."
)
.required("Parameter Name is required"),
expressionEditorValue: Yup.string(),
parameterExpression: Yup.string(),
});

export default function ParameterPane() {
export default function ParameterPane({ handleApplyParameter }) {
const textAreaRef = useRef(null);
const [editorHeight, setEditorHeight] = useState("100px");
const [showEditor, setShowEditor] = useState(false);
const formik = useFormik({
initialValues: {
parameterName: "",
expressionEditorValue: "",
expression: "",
},
validationSchema,
enableReinitialize: true,
onSubmit: (values) => {},
onSubmit: async (values) => {
const result = handleApplyParameter(values);
if (result === "success") {
formik.resetForm();
}
},
});
const { resetForm } = formik;
// adjusting the height of the editor based on the inserted text
Expand All @@ -36,7 +41,7 @@ export default function ParameterPane() {
const newHeight = Math.max(lineCount * 20, 100) + "px";
setEditorHeight(newHeight);
}
}, [formik.values.expressionEditorValue]);
}, [formik.values.expression]);
return (
<>
<div className="row">
Expand Down Expand Up @@ -65,9 +70,9 @@ export default function ParameterPane() {
mode="sql"
ref={textAreaRef}
theme="monokai"
value={formik.values.expressionEditorValue}
value={formik.values.expression}
onChange={(value) => {
formik.setFieldValue("expressionEditorValue", value);
formik.setFieldValue("expression", value);
}}
onLoad={(aceEditor) => {
// On load we want to tell the ace editor that it's inside of a scrollabel page
Expand All @@ -93,8 +98,9 @@ export default function ParameterPane() {
Clear
</Button>
<Button
data-testId="apply-parameter"
data-testId="apply-parameter-btn"
disabled={!formik.dirty || !formik.isValid}
onClick={formik.handleSubmit}
>
Apply
</Button>
Expand Down
6 changes: 4 additions & 2 deletions src/CqlBuilderPanel/Parameters/Parameters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ import ParametersNavTabs from "./ParamatersNavTabs";
import ParameterPane from "./ParameterPane";
import "./Parameters.scss";

export default function Parameters() {
export default function Parameters({ handleApplyParameter }) {
const [activeTab, setActiveTab] = useState("parameters");

return (
<form id="cql-editor-parameters" data-testId="cql-editor-parameters">
<ParametersNavTabs activeTab={activeTab} setActiveTab={setActiveTab} />

{activeTab === "parameters" && <ParameterPane />}
{activeTab === "parameters" && (
<ParameterPane handleApplyParameter={handleApplyParameter} />
)}
{activeTab === "savedParameters" && (
<div data-testId="saved-parameters" />
)}
Expand Down
5 changes: 5 additions & 0 deletions src/api/useTerminologyServiceApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ export type ValueSet = {
errorMsg: string;
};

export type Parameter = {
parameterName?: string;
expression?: string;
};

export interface CodeSystem {
id: string;
lastUpdated: string;
Expand Down
2 changes: 2 additions & 0 deletions src/cqlEditorWithTerminology/CqlEditorWithTerminology.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const CqlEditorWithTerminology = ({
handleCodeDelete,
handleDefinitionDelete,
handleApplyCode,
handleApplyParameter,
handleApplyValueSet,
handleApplyDefinition,
handleApplyLibrary,
Expand Down Expand Up @@ -106,6 +107,7 @@ const CqlEditorWithTerminology = ({
setIsCQLUnchanged={setIsCQLUnchanged}
isCQLUnchanged={isCQLUnchanged}
handleApplyCode={handleApplyCode}
handleApplyParameter={handleApplyParameter}
handleApplyValueSet={handleApplyValueSet}
handleApplyDefinition={handleApplyDefinition}
handleDefinitionEdit={handleDefinitionEdit}
Expand Down

0 comments on commit feb3489

Please sign in to comment.