fix(parameterComponent): add unique id to textarea and input components for accessibility and testing purposes

fix(codeAreaComponent): add id prop to Input component for accessibility and testing purposes
fix(promptComponent): add id prop to GenericModal and span elements for accessibility and testing purposes
fix(textAreaComponent): add id prop to Input component for accessibility and testing purposes
fix(EditNodeModal): add unique id to textarea and input components for accessibility and testing purposes
fix(codeAreaModal): add id prop to Input component for accessibility and testing purposes
fix(genericModal): add id prop to GenericModal and span elements for accessibility and testing purposes
fix(components/types): add id prop to component types for consistency and future use
feat(tests): add tests for CodeAreaModalComponent and PromptTemplateComponent
This commit is contained in:
cristhianzl 2023-10-11 08:26:38 -03:00
commit 88657553cb
10 changed files with 347 additions and 2 deletions

View file

@ -327,6 +327,7 @@ export default function ParameterComponent({
disabled={disabled} disabled={disabled}
value={data.node.template[name].value ?? ""} value={data.node.template[name].value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
id={"textarea-" + index}
/> />
) : ( ) : (
<InputComponent <InputComponent
@ -379,6 +380,7 @@ export default function ParameterComponent({
disabled={disabled} disabled={disabled}
value={data.node?.template[name].value ?? ""} value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
id={"code-input-" + index}
/> />
</div> </div>
) : left === true && type === "file" ? ( ) : left === true && type === "file" ? (
@ -419,6 +421,7 @@ export default function ParameterComponent({
onChange={(e) => { onChange={(e) => {
handleOnNewValue(e); handleOnNewValue(e);
}} }}
id={"prompt-input-" + index}
/> />
</div> </div>
) : left === true && type === "NestedDict" ? ( ) : left === true && type === "NestedDict" ? (

View file

@ -12,6 +12,7 @@ export default function CodeAreaComponent({
nodeClass, nodeClass,
dynamic, dynamic,
setNodeClass, setNodeClass,
id = "",
}: CodeAreaComponentType) { }: CodeAreaComponentType) {
const [myValue, setMyValue] = useState( const [myValue, setMyValue] = useState(
typeof value == "string" ? value : JSON.stringify(value) typeof value == "string" ? value : JSON.stringify(value)
@ -41,6 +42,7 @@ export default function CodeAreaComponent({
> >
<div className="flex w-full items-center"> <div className="flex w-full items-center">
<span <span
id={id}
className={ className={
editNode editNode
? "input-edit-node input-dialog" ? "input-edit-node input-dialog"

View file

@ -14,6 +14,7 @@ export default function PromptAreaComponent({
onChange, onChange,
disabled, disabled,
editNode = false, editNode = false,
id = "",
}: PromptAreaComponentType) { }: PromptAreaComponentType) {
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
@ -35,6 +36,7 @@ export default function PromptAreaComponent({
return ( return (
<div className={disabled ? "pointer-events-none w-full " : " w-full"}> <div className={disabled ? "pointer-events-none w-full " : " w-full"}>
<GenericModal <GenericModal
id={id}
type={TypeModal.PROMPT} type={TypeModal.PROMPT}
value={value} value={value}
buttonText="Check & Save" buttonText="Check & Save"
@ -47,6 +49,7 @@ export default function PromptAreaComponent({
> >
<div className="flex w-full items-center"> <div className="flex w-full items-center">
<span <span
id={id}
className={ className={
editNode editNode
? "input-edit-node input-dialog" ? "input-edit-node input-dialog"

View file

@ -10,6 +10,7 @@ export default function TextAreaComponent({
onChange, onChange,
disabled, disabled,
editNode = false, editNode = false,
id = "",
}: TextAreaComponentType): JSX.Element { }: TextAreaComponentType): JSX.Element {
// Clear text area // Clear text area
useEffect(() => { useEffect(() => {
@ -21,6 +22,7 @@ export default function TextAreaComponent({
return ( return (
<div className="flex w-full items-center"> <div className="flex w-full items-center">
<Input <Input
id={id}
value={value} value={value}
disabled={disabled} disabled={disabled}
className={editNode ? "input-edit-node" : ""} className={editNode ? "input-edit-node" : ""}

View file

@ -203,6 +203,7 @@ const EditNodeModal = forwardRef(
templateParam templateParam
].multiline ? ( ].multiline ? (
<TextAreaComponent <TextAreaComponent
id={"textarea-edit-" + index}
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
@ -434,6 +435,7 @@ const EditNodeModal = forwardRef(
onChange={(value: string | string[]) => { onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam); handleOnNewValue(value, templateParam);
}} }}
id={"prompt-area-edit" + index}
/> />
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.current.node?.template[templateParam]
@ -458,6 +460,7 @@ const EditNodeModal = forwardRef(
onChange={(value: string | string[]) => { onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam); handleOnNewValue(value, templateParam);
}} }}
id={"code-area-edit" + index}
/> />
</div> </div>
) : myData.current.node?.template[templateParam] ) : myData.current.node?.template[templateParam]

View file

@ -8,6 +8,7 @@ import { useContext, useEffect, useState } from "react";
import AceEditor from "react-ace"; import AceEditor from "react-ace";
import IconComponent from "../../components/genericIconComponent"; import IconComponent from "../../components/genericIconComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input";
import { CODE_PROMPT_DIALOG_SUBTITLE } from "../../constants/constants"; import { CODE_PROMPT_DIALOG_SUBTITLE } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { darkContext } from "../../contexts/darkContext"; import { darkContext } from "../../contexts/darkContext";
@ -143,6 +144,7 @@ export default function CodeAreaModal({
/> />
</BaseModal.Header> </BaseModal.Header>
<BaseModal.Content> <BaseModal.Content>
<Input value={code} className="absolute left-[500%]" id="codeValue" />
<div className="flex h-full w-full flex-col transition-all"> <div className="flex h-full w-full flex-col transition-all">
<div className="h-full w-full"> <div className="h-full w-full">
<AceEditor <AceEditor
@ -180,7 +182,12 @@ export default function CodeAreaModal({
</div> </div>
</div> </div>
<div className="flex h-fit w-full justify-end"> <div className="flex h-fit w-full justify-end">
<Button className="mt-3" onClick={handleClick} type="submit"> <Button
className="mt-3"
onClick={handleClick}
type="submit"
id="checkAndSaveBtn"
>
Check & Save Check & Save
</Button> </Button>
</div> </div>

View file

@ -30,6 +30,7 @@ export default function GenericModal({
nodeClass, nodeClass,
setNodeClass, setNodeClass,
children, children,
id = "",
}: genericModalPropsType): JSX.Element { }: genericModalPropsType): JSX.Element {
const [myButtonText] = useState(buttonText); const [myButtonText] = useState(buttonText);
const [myModalTitle] = useState(modalTitle); const [myModalTitle] = useState(modalTitle);
@ -210,6 +211,7 @@ export default function GenericModal({
> >
{type === TypeModal.PROMPT && isEdit ? ( {type === TypeModal.PROMPT && isEdit ? (
<Textarea <Textarea
id={"modal-" + id}
ref={divRefPrompt} ref={divRefPrompt}
className="form-input h-full w-full rounded-lg custom-scroll focus-visible:ring-1" className="form-input h-full w-full rounded-lg custom-scroll focus-visible:ring-1"
value={inputValue} value={inputValue}
@ -284,7 +286,7 @@ export default function GenericModal({
className="m-1 max-w-[40vw] cursor-default truncate p-2.5 text-sm" className="m-1 max-w-[40vw] cursor-default truncate p-2.5 text-sm"
> >
<div className="relative bottom-[1px]"> <div className="relative bottom-[1px]">
<span> <span id={"badge" + index.toString()}>
{word.replace(/[{}]/g, "").length > 59 {word.replace(/[{}]/g, "").length > 59
? word.replace(/[{}]/g, "").slice(0, 56) + ? word.replace(/[{}]/g, "").slice(0, 56) +
"..." "..."
@ -304,6 +306,7 @@ export default function GenericModal({
)} )}
</div> </div>
<Button <Button
id="genericModalBtnSave"
onClick={() => { onClick={() => {
switch (myModalType) { switch (myModalType) {
case TypeModal.TEXT: case TypeModal.TEXT:

View file

@ -83,6 +83,7 @@ export type TextAreaComponentType = {
onChange: (value: string[] | string) => void; onChange: (value: string[] | string) => void;
value: string; value: string;
editNode?: boolean; editNode?: boolean;
id?: string;
}; };
export type PromptAreaComponentType = { export type PromptAreaComponentType = {
@ -93,6 +94,7 @@ export type PromptAreaComponentType = {
onChange: (value: string[] | string) => void; onChange: (value: string[] | string) => void;
value: string; value: string;
editNode?: boolean; editNode?: boolean;
id?: string;
}; };
export type CodeAreaComponentType = { export type CodeAreaComponentType = {
@ -103,6 +105,7 @@ export type CodeAreaComponentType = {
nodeClass?: APIClassType; nodeClass?: APIClassType;
setNodeClass?: (value: APIClassType) => void; setNodeClass?: (value: APIClassType) => void;
dynamic?: boolean; dynamic?: boolean;
id?: string;
}; };
export type FileComponentType = { export type FileComponentType = {
@ -489,6 +492,7 @@ export type genericModalPropsType = {
nodeClass?: APIClassType; nodeClass?: APIClassType;
setNodeClass?: (Class: APIClassType) => void; setNodeClass?: (Class: APIClassType) => void;
children: ReactNode; children: ReactNode;
id?: string;
}; };
export type buttonBoxPropsType = { export type buttonBoxPropsType = {

View file

@ -0,0 +1,144 @@
import { expect, test } from "@playwright/test";
test("CodeAreaModalComponent", async ({ page }) => {
await page.goto("http://localhost:3000/");
await page.waitForTimeout(2000);
await page.locator('//*[@id="new-project-btn"]').click();
await page.waitForTimeout(2000);
await page.getByPlaceholder("Search").click();
await page.getByPlaceholder("Search").fill("pythonfunctiontool");
await page.waitForTimeout(2000);
await page
.locator('//*[@id="sidePythonFunctionTool"]')
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
await page.locator('//*[@id="code-input-0"]').click();
let value = await page.locator('//*[@id="codeValue"]').inputValue();
if (
value !=
'def python_function(text: str) -> str: """This is a default python function that returns the input text""" return text'
) {
expect(false).toBeTruthy();
}
await page.locator('//*[@id="checkAndSaveBtn"]').click();
await page
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
.click();
await page.locator('//*[@id="advancedIcon"]').click();
await page.locator('//*[@id="editAdvancedBtn"]').click();
await page.locator('//*[@id="showcode"]').click();
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showdescription"]').click();
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showcode"]').click();
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
await page.locator('//*[@id="showdescription"]').click();
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeTruthy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showcode"]').click();
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showdescription"]').click();
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showcode"]').click();
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
await page.locator('//*[@id="showdescription"]').click();
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeTruthy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showcode"]').click();
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="saveChangesBtn"]').click();
const plusButtonLocator = page.locator('//*[@id="code-input-0"]');
const elementCount = await plusButtonLocator.count();
if (elementCount === 0) {
expect(true).toBeTruthy();
await page
.locator('//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div')
.click();
await page.locator('//*[@id="advancedIcon"]').click();
await page.locator('//*[@id="editAdvancedBtn"]').click();
await page.locator('//*[@id="showcode"]').click();
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
await page
.locator(
'//*[@id="radix-:r2l:"]/div[2]/div/div[2]/div/div/div/table/tbody/tr[1]/td[2]/div/div/button/div/span'
)
.click();
let value = await page.locator('//*[@id="codeValue"]').inputValue();
if (
value !=
'def python_function(text: str) -> str: """This is a default python function that returns the input text""" return text'
) {
expect(false).toBeTruthy();
}
await page.locator('//*[@id="checkAndSaveBtn"]').click();
await page.locator('//*[@id="saveChangesBtn"]').click();
await page.locator('//*[@id="code-input-0"]').click();
}
});

View file

@ -0,0 +1,174 @@
import { expect, test } from "@playwright/test";
test("PromptTemplateComponent", async ({ page }) => {
await page.goto("http://localhost:3000/");
await page.waitForTimeout(2000);
await page.locator('//*[@id="new-project-btn"]').click();
await page.waitForTimeout(2000);
await page.getByPlaceholder("Search").click();
await page.getByPlaceholder("Search").fill("promptTemplate");
await page.waitForTimeout(2000);
await page
.locator('//*[@id="sidePromptTemplate"]')
.dragTo(page.locator('//*[@id="react-flow-id"]'));
await page.mouse.up();
await page.mouse.down();
await page.locator('//*[@id="prompt-input-3"]').click();
await page
.locator('//*[@id="modal-prompt-input-3"]')
.fill("{prompt} example {prompt1}");
let value = await page
.locator('//*[@id="modal-prompt-input-3"]')
.inputValue();
if (value != "{prompt} example {prompt1}") {
expect(false).toBeTruthy();
}
let valueBadgeOne = await page.locator('//*[@id="badge0"]').innerText();
if (valueBadgeOne != "prompt") {
expect(false).toBeTruthy();
}
let valueBadgeTwo = await page.locator('//*[@id="badge1"]').innerText();
if (valueBadgeTwo != "prompt1") {
expect(false).toBeTruthy();
}
await page.locator('//*[@id="genericModalBtnSave"]').click();
await page.locator('//*[@id="textarea-6"]').click();
await page.locator('//*[@id="textarea-6"]').fill("prompt_value_!@#!@#");
value = await page.locator('//*[@id="textarea-6"]').inputValue();
if (value != "prompt_value_!@#!@#") {
expect(false).toBeTruthy();
}
await page.locator('//*[@id="textarea-7"]').click();
await page
.locator('//*[@id="textarea-7"]')
.fill("prompt_name_test_123123!@#!@#");
value = await page.locator('//*[@id="textarea-7"]').inputValue();
if (value != "prompt_name_test_123123!@#!@#") {
expect(false).toBeTruthy();
}
value = await page.locator('//*[@id="prompt-input-3"]').innerText();
if (value != "{prompt} example {prompt1}") {
expect(false).toBeTruthy();
}
await page.locator('//*[@id="editAdvancedIcon"]').click();
value = await page.locator('//*[@id="textarea-edit-1"]').inputValue();
if (value != "prompt_value_!@#!@#") {
expect(false).toBeTruthy();
}
value = await page.locator('//*[@id="textarea-edit-2"]').inputValue();
if (value != "prompt_name_test_123123!@#!@#") {
expect(false).toBeTruthy();
}
value = await page.locator('//*[@id="prompt-area-edit0"]').innerText();
if (value != "{prompt} example {prompt1}") {
expect(false).toBeTruthy();
}
await page
.locator('//*[@id="textarea-edit-2"]')
.fill("prompt_edit_test_12312312321!@#$");
await page
.locator('//*[@id="textarea-edit-1"]')
.fill("prompt_edit_test_44444444444!@#$");
await page.locator('//*[@id="showtemplate"]').click();
expect(await page.locator('//*[@id="showtemplate"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showprompt"]').click();
expect(await page.locator('//*[@id="showprompt"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showprompt1"]').click();
expect(await page.locator('//*[@id="showprompt1"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showtemplate"]').click();
expect(
await page.locator('//*[@id="showtemplate"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showprompt"]').click();
expect(await page.locator('//*[@id="showprompt"]').isChecked()).toBeTruthy();
await page.locator('//*[@id="showprompt1"]').click();
expect(await page.locator('//*[@id="showprompt1"]').isChecked()).toBeTruthy();
await page.locator('//*[@id="showtemplate"]').click();
expect(await page.locator('//*[@id="showtemplate"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showprompt"]').click();
expect(await page.locator('//*[@id="showprompt"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showprompt1"]').click();
expect(await page.locator('//*[@id="showprompt1"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showtemplate"]').click();
expect(
await page.locator('//*[@id="showtemplate"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showprompt"]').click();
expect(await page.locator('//*[@id="showprompt"]').isChecked()).toBeTruthy();
await page.locator('//*[@id="saveChangesBtn"]').click();
const plusButtonLocator = page.locator('//*[@id="textarea-7"]');
const elementCount = await plusButtonLocator.count();
if (elementCount === 0) {
expect(true).toBeTruthy();
await page
.locator(
'//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div/div/div[1]/div/div[1]'
)
.click();
await page.locator('//*[@id="editAdvancedIcon"]').click();
await page.locator('//*[@id="showprompt1"]').click();
expect(
await page.locator('//*[@id="showprompt1"]').isChecked()
).toBeTruthy();
value = await page.locator('//*[@id="textarea-edit-1"]').inputValue();
if (value != "prompt_edit_test_44444444444!@#$") {
expect(false).toBeTruthy();
}
value = await page.locator('//*[@id="textarea-edit-2"]').inputValue();
if (value != "prompt_edit_test_12312312321!@#$") {
expect(false).toBeTruthy();
}
value = await page.locator('//*[@id="prompt-area-edit0"]').innerText();
if (value != "{prompt} example {prompt1}") {
expect(false).toBeTruthy();
}
}
});