📝 (index.tsx): add convertTestName function to utils.ts to convert card names to test ids

📝 (index.tsx): add `data-testid` attribute to Card component to improve testability
📝 (index.tsx): remove unnecessary parentheses in ternary operator
📝 (index.tsx): remove unnecessary `undefined` in ternary operator
📝 (index.tsx): add `data-testid` attribute to delete button to improve testability
📝 (index.tsx): add `convertTestName` function to utils.ts to convert card names to test ids
📝 (utils.ts): add `convertTestName` function to convert card names to test ids
📝 (deleteComponentFlows.spec.ts): add end-to-end tests for deleting a flow and a component
This commit is contained in:
cristhianzl 2024-05-03 10:16:24 -03:00
commit 5ef34e48f3
3 changed files with 94 additions and 43 deletions

View file

@ -8,7 +8,7 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { useStoreStore } from "../../stores/storeStore"; import { useStoreStore } from "../../stores/storeStore";
import { storeComponent } from "../../types/store"; import { storeComponent } from "../../types/store";
import cloneFLowWithParent from "../../utils/storeUtils"; import cloneFLowWithParent from "../../utils/storeUtils";
import { cn } from "../../utils/utils"; import { cn, convertTestName } from "../../utils/utils";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import ShadTooltip from "../shadTooltipComponent"; import ShadTooltip from "../shadTooltipComponent";
import { Badge } from "../ui/badge"; import { Badge } from "../ui/badge";
@ -48,11 +48,11 @@ export default function CollectionCardComponent({
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [loadingLike, setLoadingLike] = useState(false); const [loadingLike, setLoadingLike] = useState(false);
const [liked_by_user, setLiked_by_user] = useState( const [liked_by_user, setLiked_by_user] = useState(
data?.liked_by_user ?? false data?.liked_by_user ?? false,
); );
const [likes_count, setLikes_count] = useState(data?.liked_by_count ?? 0); const [likes_count, setLikes_count] = useState(data?.liked_by_count ?? 0);
const [downloads_count, setDownloads_count] = useState( const [downloads_count, setDownloads_count] = useState(
data?.downloads_count ?? 0 data?.downloads_count ?? 0,
); );
const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
@ -63,7 +63,7 @@ export default function CollectionCardComponent({
const [openPlayground, setOpenPlayground] = useState(false); const [openPlayground, setOpenPlayground] = useState(false);
const [openDelete, setOpenDelete] = useState(false); const [openDelete, setOpenDelete] = useState(false);
const setCurrentFlowId = useFlowsManagerStore( const setCurrentFlowId = useFlowsManagerStore(
(state) => state.setCurrentFlowId (state) => state.setCurrentFlowId,
); );
const [loadingPlayground, setLoadingPlayground] = useState(false); const [loadingPlayground, setLoadingPlayground] = useState(false);
@ -106,16 +106,18 @@ export default function CollectionCardComponent({
addFlow(true, newFlow) addFlow(true, newFlow)
.then((id) => { .then((id) => {
setSuccessData({ setSuccessData({
title: `${name} ${isStore ? "Downloaded" : "Installed" title: `${name} ${
} Successfully.`, isStore ? "Downloaded" : "Installed"
} Successfully.`,
}); });
setLoading(false); setLoading(false);
}) })
.catch((error) => { .catch((error) => {
setLoading(false); setLoading(false);
setErrorData({ setErrorData({
title: `Error ${isStore ? "downloading" : "installing" title: `Error ${
} the ${name}`, isStore ? "downloading" : "installing"
} the ${name}`,
list: [error["response"]["data"]["detail"]], list: [error["response"]["data"]["detail"]],
}); });
}); });
@ -167,10 +169,11 @@ export default function CollectionCardComponent({
return ( return (
<> <>
<Card <Card
data-testid={`card-${convertTestName(data.name)}`}
className={cn( className={cn(
"group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:shadow-md", "group relative flex min-h-[11rem] flex-col justify-between overflow-hidden transition-all hover:shadow-md",
disabled ? "pointer-events-none opacity-50" : "", disabled ? "pointer-events-none opacity-50" : "",
onClick ? "cursor-pointer" : "" onClick ? "cursor-pointer" : "",
)} )}
onClick={onClick} onClick={onClick}
> >
@ -183,7 +186,7 @@ export default function CollectionCardComponent({
"flex-shrink-0", "flex-shrink-0",
data.is_component data.is_component
? "mx-0.5 h-6 w-6 text-component-icon" ? "mx-0.5 h-6 w-6 text-component-icon"
: "h-7 w-7 flex-shrink-0 text-flow-icon" : "h-7 w-7 flex-shrink-0 text-flow-icon",
)} )}
name={data.is_component ? "ToyBrick" : "Group"} name={data.is_component ? "ToyBrick" : "Group"}
/> />
@ -235,17 +238,17 @@ export default function CollectionCardComponent({
)} )}
{onDelete && data?.metadata === undefined && ( {onDelete && data?.metadata === undefined && (
<button onClick={(e) => { <button
onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
setOpenDelete(true); setOpenDelete(true);
}} }}
> >
<IconComponent
<IconComponent name="Trash2"
name="Trash2" className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100"
className="h-5 w-5 text-primary opacity-0 transition-all hover:text-destructive group-hover:opacity-100" />
/> </button>
</button>
)} )}
</CardTitle> </CardTitle>
</div> </div>
@ -326,9 +329,7 @@ export default function CollectionCardComponent({
)} )}
Playground Playground
</Button> </Button>
) : ( ) : undefined}
undefined
)}
{data.liked_by_count != undefined && ( {data.liked_by_count != undefined && (
<div className="flex gap-0.5"> <div className="flex gap-0.5">
{onDelete && data?.metadata !== undefined ? ( {onDelete && data?.metadata !== undefined ? (
@ -351,10 +352,11 @@ export default function CollectionCardComponent({
} }
> >
<IconComponent <IconComponent
data-testid={`delete-${convertTestName(data.name)}`}
name="Trash2" name="Trash2"
className={cn( className={cn(
"h-5 w-5", "h-5 w-5",
!authorized ? " text-ring" : "" !authorized ? " text-ring" : "",
)} )}
/> />
</Button> </Button>
@ -389,7 +391,7 @@ export default function CollectionCardComponent({
liked_by_user liked_by_user
? "fill-destructive stroke-destructive" ? "fill-destructive stroke-destructive"
: "", : "",
!authorized ? " text-ring" : "" !authorized ? " text-ring" : "",
)} )}
/> />
</Button> </Button>
@ -427,7 +429,7 @@ export default function CollectionCardComponent({
} }
className={cn( className={cn(
loading ? "h-5 w-5 animate-spin" : "h-5 w-5", loading ? "h-5 w-5 animate-spin" : "h-5 w-5",
!authorized ? " text-ring" : "" !authorized ? " text-ring" : "",
)} )}
/> />
</Button> </Button>
@ -490,13 +492,12 @@ export default function CollectionCardComponent({
open={openDelete} open={openDelete}
setOpen={setOpenDelete} setOpen={setOpenDelete}
onConfirm={() => { onConfirm={() => {
if(onDelete) onDelete(); if (onDelete) onDelete();
}} }}
> >
<></> <></>
</DeleteConfirmationModal> </DeleteConfirmationModal>
) )}
}
</> </>
); );
} }

View file

@ -61,7 +61,7 @@ export function normalCaseToSnakeCase(str: string): string {
export function toTitleCase( export function toTitleCase(
str: string | undefined, str: string | undefined,
isNodeField?: boolean isNodeField?: boolean,
): string { ): string {
if (!str) return ""; if (!str) return "";
let result = str let result = str
@ -70,7 +70,7 @@ export function toTitleCase(
if (isNodeField) return word; if (isNodeField) return word;
if (index === 0) { if (index === 0) {
return checkUpperWords( return checkUpperWords(
word[0].toUpperCase() + word.slice(1).toLowerCase() word[0].toUpperCase() + word.slice(1).toLowerCase(),
); );
} }
return checkUpperWords(word.toLowerCase()); return checkUpperWords(word.toLowerCase());
@ -83,7 +83,7 @@ export function toTitleCase(
if (isNodeField) return word; if (isNodeField) return word;
if (index === 0) { if (index === 0) {
return checkUpperWords( return checkUpperWords(
word[0].toUpperCase() + word.slice(1).toLowerCase() word[0].toUpperCase() + word.slice(1).toLowerCase(),
); );
} }
return checkUpperWords(word.toLowerCase()); return checkUpperWords(word.toLowerCase());
@ -93,8 +93,8 @@ export function toTitleCase(
export function getUnavailableFields(variables: { export function getUnavailableFields(variables: {
[key: string]: { default_fields?: string[] }; [key: string]: { default_fields?: string[] };
}): {[name: string]: string} { }): { [name: string]: string } {
const unVariables:{[name: string]: string} = {}; const unVariables: { [name: string]: string } = {};
Object.keys(variables).forEach((key) => { Object.keys(variables).forEach((key) => {
if (variables[key].default_fields) { if (variables[key].default_fields) {
variables[key].default_fields!.forEach((field) => { variables[key].default_fields!.forEach((field) => {
@ -123,7 +123,7 @@ export function groupByFamily(
data: APIDataType, data: APIDataType,
baseClasses: string, baseClasses: string,
left: boolean, left: boolean,
flow?: NodeType[] flow?: NodeType[],
): groupedObjType[] { ): groupedObjType[] {
const baseClassesSet = new Set(baseClasses.split("\n")); const baseClassesSet = new Set(baseClasses.split("\n"));
let arrOfPossibleInputs: Array<{ let arrOfPossibleInputs: Array<{
@ -149,7 +149,7 @@ export function groupByFamily(
baseClassesSet.has(template.type)) || baseClassesSet.has(template.type)) ||
(template.input_types && (template.input_types &&
template.input_types.some((inputType) => template.input_types.some((inputType) =>
baseClassesSet.has(inputType) baseClassesSet.has(inputType),
))) )))
); );
}; };
@ -169,7 +169,7 @@ export function groupByFamily(
hasBaseClassInBaseClasses: hasBaseClassInBaseClasses:
foundNode?.hasBaseClassInBaseClasses || foundNode?.hasBaseClassInBaseClasses ||
nodeData.node!.base_classes.some((baseClass) => nodeData.node!.base_classes.some((baseClass) =>
baseClassesSet.has(baseClass) baseClassesSet.has(baseClass),
), //seta como anterior ou verifica se o node tem base class ), //seta como anterior ou verifica se o node tem base class
displayName: nodeData.node?.display_name, displayName: nodeData.node?.display_name,
}); });
@ -186,10 +186,10 @@ export function groupByFamily(
if (!foundNode) { if (!foundNode) {
foundNode = { foundNode = {
hasBaseClassInTemplate: Object.values(node!.template).some( hasBaseClassInTemplate: Object.values(node!.template).some(
checkBaseClass checkBaseClass,
), ),
hasBaseClassInBaseClasses: node!.base_classes.some((baseClass) => hasBaseClassInBaseClasses: node!.base_classes.some((baseClass) =>
baseClassesSet.has(baseClass) baseClassesSet.has(baseClass),
), ),
displayName: node?.display_name, displayName: node?.display_name,
}; };
@ -246,7 +246,7 @@ export function getRandomDescription(): string {
export function getRandomName( export function getRandomName(
retry: number = 0, retry: number = 0,
noSpace: boolean = false, noSpace: boolean = false,
maxRetries: number = 3 maxRetries: number = 3,
): string { ): string {
const left: string[] = ADJECTIVES; const left: string[] = ADJECTIVES;
const right: string[] = NOUNS; const right: string[] = NOUNS;
@ -325,7 +325,7 @@ export function getChatInputField(flowState?: FlowState) {
export function getPythonApiCode( export function getPythonApiCode(
flow: FlowType, flow: FlowType,
isAuth: boolean, isAuth: boolean,
tweak?: any[] tweak?: any[],
): string { ): string {
const flowId = flow.id; const flowId = flow.id;
@ -393,7 +393,7 @@ print(run_flow(message=message, flow_id=FLOW_ID, tweaks=TWEAKS${
export function getCurlCode( export function getCurlCode(
flow: FlowType, flow: FlowType,
isAuth: boolean, isAuth: boolean,
tweak?: any[] tweak?: any[],
): string { ): string {
const flowId = flow.id; const flowId = flow.id;
const tweaks = buildTweaks(flow); const tweaks = buildTweaks(flow);
@ -463,7 +463,7 @@ result = run_flow_from_json(flow="${flowName}.json",
export function getWidgetCode( export function getWidgetCode(
flow: FlowType, flow: FlowType,
isAuth: boolean, isAuth: boolean,
flowState?: FlowState flowState?: FlowState,
): string { ): string {
const flowId = flow.id; const flowId = flow.id;
const flowName = flow.name; const flowName = flow.name;
@ -593,7 +593,7 @@ export function checkLocalStorageKey(key: string): boolean {
export function IncrementObjectKey( export function IncrementObjectKey(
object: object, object: object,
key: string key: string,
): { newKey: string; increment: number } { ): { newKey: string; increment: number } {
let count = 1; let count = 1;
const type = removeCountFromString(key); const type = removeCountFromString(key);
@ -665,7 +665,7 @@ export function getSetFromObject(obj: object, key?: string): Set<string> {
export function getFieldTitle( export function getFieldTitle(
template: APITemplateType, template: APITemplateType,
templateField: string templateField: string,
): string { ): string {
return template[templateField].display_name return template[templateField].display_name
? template[templateField].display_name! ? template[templateField].display_name!
@ -715,3 +715,7 @@ export function freezeObject(obj: any) {
if (!obj) return obj; if (!obj) return obj;
return JSON.parse(JSON.stringify(obj)); return JSON.parse(JSON.stringify(obj));
} }
export function convertTestName(name: string): string {
return name.replace(/ /g, "-").toLowerCase();
}

View file

@ -0,0 +1,46 @@
import { test } from "@playwright/test";
test("shoud delete a flow", async ({ page }) => {
await page.goto("/");
await page.waitForTimeout(2000);
await page.getByText("Store").nth(0).click();
await page.getByTestId("install-Website Content QA").click();
await page.waitForTimeout(5000);
await page.getByText("My Collection").nth(0).click();
await page.getByText("Website Content QA").first().isVisible();
await page
.getByTestId("card-website-content-qa")
.first()
.hover()
.then(async () => {
await page.getByTestId("icon-Trash2").first().click();
await page.waitForTimeout(2000);
});
await page.getByText("Confirm deletion of component?").isVisible();
await page.getByText("Delete").nth(1).click();
await page.waitForTimeout(1000);
await page.getByText("Successfully").first().isVisible();
});
test("shoud delete a component", async ({ page }) => {
await page.goto("/");
await page.waitForTimeout(2000);
await page.getByText("Store").nth(0).click();
await page.getByTestId("install-Basic RAG").click();
await page.waitForTimeout(5000);
await page.getByText("My Collection").nth(0).click();
await page.getByText("Components").first().click();
await page.getByText("Basic RAG").first().isVisible();
await page
.getByTestId("card-basic-rag")
.first()
.hover()
.then(async () => {
await page.getByTestId("icon-Trash2").first().click();
await page.waitForTimeout(2000);
});
await page.getByText("Confirm deletion of component?").isVisible();
await page.getByText("Delete").nth(1).click();
await page.waitForTimeout(1000);
await page.getByText("Successfully").first().isVisible();
});