Skip to content

Commit

Permalink
Stu-346 placeholder test (flojoy-ai#1159)
Browse files Browse the repository at this point in the history
* [stu-346-placeholder-test] feat: new Test type: "Placeholder"

* [stu-346-placeholder-test] feat: Change the path and test type of placeholder

* chore: all test step can be edited

* fix: css overflow

* chore: display error as toast

* formatting

* fix: Eslint

* chore: python formatting

* Update src/renderer/routes/test_sequencer_panel/components/modals/ChangeLinkedTest.tsx

Co-authored-by: Jeff Zhang <47371088+39bytes@users.noreply.github.com>

* chore: EsLint fix

* chore: removing dead code

* chore: fix Eslint

* chore: Eslint fix

* chore: formatting

* fix: new return Result when test are duplicated doesn't close Modal. -> Fix test

* [stu-346-placeholder-test] chore: placeholder test => Using form

* [stu-346-placeholder-test] chore: Using object as input for better clarity

* chore: formatting

* chore: pruned useless abstraction

* build: remove duplicated div

* chore: formatting

---------

Co-authored-by: Jeff Zhang <47371088+39bytes@users.noreply.github.com>
  • Loading branch information
LatentDream and 39bytes authored Apr 25, 2024
1 parent 2f405d4 commit 5ad968d
Show file tree
Hide file tree
Showing 12 changed files with 527 additions and 94 deletions.
1 change: 1 addition & 0 deletions captain/models/test_sequencer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class TestTypes(StrEnum):
python = "python"
flojoy = "flojoy"
matlab = "matlab"
placeholder = "placeholder"


class StatusTypes(StrEnum):
Expand Down
22 changes: 22 additions & 0 deletions captain/utils/test_sequencer/run_test_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,27 @@ def _run_pytest(node: TestNode) -> Extract:
)


@_with_stream_test_result
def _run_placeholder(node: TestNode) -> Extract:
"""
@params file_path: path to the file
@returns:
bool: result of the test
float: time taken to execute the test
str: error message if any
"""
return (
lambda _: None,
TestResult(
node,
False,
0,
"Placeholder test not implemented",
utcnow_str(),
),
)


def _eval_condition(
result_dict: dict[str, TestResult], condition: str, identifiers: set[str]
):
Expand Down Expand Up @@ -244,6 +265,7 @@ def get_next_children_from_context(context: Context):
{
TestTypes.python: (None, _run_python),
TestTypes.pytest: (None, _run_pytest),
TestTypes.placeholder: (None, _run_placeholder),
},
),
"conditional": (
Expand Down
19 changes: 1 addition & 18 deletions playwright-test/13_create_test_sequence.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,25 +67,8 @@ test.describe("Create a test sequence", () => {
await expect(window.getByTestId(Selectors.newDropdown)).toBeEnabled({
timeout: 15000,
});
await window.getByTestId(Selectors.newDropdown).click();
await window.getByTestId(Selectors.importTestBtn).click();

// Select the fixture file
const customTestFile = join(__dirname, "fixtures/custom-sequences/test.py");
await app.evaluate(async ({ dialog }, customTestFile) => {
dialog.showOpenDialog = () =>
Promise.resolve({ filePaths: [customTestFile], canceled: false });
}, customTestFile);

// Click on Pytest test to open modal
await window.getByTestId(Selectors.pytestBtn).click();

// Expect test to be loaded
await expect(
window.locator("div", { hasText: "test_one" }).first(),
).toBeVisible();

// Ctrl/meta + p key shortcut to save the sequence
// Ctrl/meta + s key shortcut to save the sequence
if (process.platform === "darwin") {
await window.keyboard.press("Meta+s");
} else {
Expand Down
58 changes: 52 additions & 6 deletions src/renderer/hooks/useTestImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ function parseDiscoverContainer(
settings: ImportTestSettings,
) {
return map(data.response, (container) => {
const new_elem = createNewTest(
container.testName,
container.path,
settings.importType,
);
const new_elem = createNewTest({
name: container.testName,
path: container.path,
type: settings.importType,
});
return new_elem;
});
}

export const useTestImport = () => {
export const useDiscoverAndImportTests = () => {
const { addNewElems } = useDisplayedSequenceState();
const { openErrorModal } = useSequencerModalStore();

Expand Down Expand Up @@ -125,3 +125,49 @@ export const useTestImport = () => {

return openFilePicker;
};

export const useDiscoverPytestElements = () => {
const handleUserDepInstall = useCallback(async (depName: string) => {
const promise = () => window.api.poetryInstallDepUserGroup(depName);
toast.promise(promise, {
loading: `Installing ${depName}...`,
success: () => {
return `${depName} has been added.`;
},
error:
"Could not install the library. Please consult the Dependency Manager in the settings.",
});
}, []);

async function getTests(path: string) {
const res = await discoverPytest(path, false);
if (res.isErr()) {
return err(res.error);
}
const data = res.value;
if (data.error) {
return err(Error(data.error));
}
for (const lib of data.missingLibraries) {
toast.error(`Missing Python Library: ${lib}`, {
action: {
label: "Install",
onClick: () => {
handleUserDepInstall(lib);
},
},
});
return err(Error("Please retry after installing the missing libraries."));
}
const newElems = parseDiscoverContainer(data, {
importAsOneRef: false,
importType: "pytest",
});
if (newElems.length === 0) {
return err(Error("No tests were found in the specified file."));
}
return ok(newElems);
}

return getTests;
};
56 changes: 31 additions & 25 deletions src/renderer/hooks/useTestSequencerState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import useWithPermission from "@/renderer/hooks/useWithPermission";
import { useSequencerStore } from "@/renderer/stores/sequencer";
import { useShallow } from "zustand/react/shallow";
import { v4 as uuidv4 } from "uuid";
import { Err, Ok, Result } from "neverthrow";
import { Err, Result, err, ok } from "neverthrow";
import { verifyElementCompatibleWithSequence } from "@/renderer/routes/test_sequencer_panel/utils/SequenceHandler";
import { toast } from "sonner";
import { SendJsonMessage } from "react-use-websocket/dist/lib/types";
Expand All @@ -28,6 +28,7 @@ import {
testSequenceStopRequest,
} from "../routes/test_sequencer_panel/models/models";
import { produce } from "immer";
import { z } from "zod";

// sync this with the definition of setElems
export type SetElemsFn = {
Expand Down Expand Up @@ -98,34 +99,38 @@ const validateElements = (
return !validators.some((validator) => !validator(elems), validators);
};

export function createNewTest(
name: string,
path: string,
export const NewTest = z.object({
name: z.string(),
path: z.string(),
type: TestType,
exportToCloud?: boolean,
id?: string,
groupId?: string,
minValue?: number,
maxValue?: number,
unit?: string,
): Test {
exportToCloud: z.boolean().optional(),
id: z.string().optional(),
groupId: z.string().optional(),
minValue: z.number().optional(),
maxValue: z.number().optional(),
unit: z.string().optional(),
});

export type NewTest = z.infer<typeof NewTest>;

export function createNewTest(test: NewTest): Test {
const newTest: Test = {
type: "test",
id: id || uuidv4(),
groupId: groupId || uuidv4(),
path: path,
testName: name,
id: test.id || uuidv4(),
groupId: test.groupId || uuidv4(),
path: test.path,
testName: test.name,
runInParallel: false,
testType: type,
testType: test.type,
status: "pending",
completionTime: undefined,
error: null,
isSavedToCloud: false,
exportToCloud: exportToCloud === undefined ? true : exportToCloud,
exportToCloud: test.exportToCloud === undefined ? true : test.exportToCloud,
createdAt: new Date().toISOString(),
minValue: minValue,
maxValue: maxValue,
unit: unit,
minValue: test.minValue,
maxValue: test.maxValue,
unit: test.unit,
};
return newTest;
}
Expand Down Expand Up @@ -172,7 +177,7 @@ export function useDisplayedSequenceState() {
p:
| TestSequenceElement[]
| ((elems: TestSequenceElement[]) => TestSequenceElement[]),
) {
): Result<void, Error> {
let candidateElems: TestSequenceElement[];

// handle overloads
Expand All @@ -189,7 +194,7 @@ export function useDisplayedSequenceState() {
);
if (!res) {
console.error("Validation failed");
return;
return err(new Error("Validation failed"));
}

// PASS
Expand All @@ -198,13 +203,14 @@ export function useDisplayedSequenceState() {

// creates tree to send to backend
setTree(createTestSequenceTree(candidateElems));
return ok(undefined);
}

const setElemsWithPermissions = withPermissionCheck(setElems);

async function AddNewElems(
newElems: TestSequenceElement[],
): Promise<Result<null, Error>> {
): Promise<Result<void, Error>> {
// Validate with project
if (project !== null) {
const result = await verifyElementCompatibleWithSequence(
Expand All @@ -216,8 +222,8 @@ export function useDisplayedSequenceState() {
}
}
// Add new elements
setElems((elems) => [...elems, ...newElems]);
return new Ok(null);
const result = setElems((elems) => [...elems, ...newElems]);
return result;
}

const addNewElemsWithPermissions = withPermissionCheck(AddNewElems);
Expand Down
32 changes: 21 additions & 11 deletions src/renderer/routes/test_sequencer_panel/components/DesignBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
HoverCardTrigger,
} from "@/renderer/components/ui/hover-card";
import _ from "lodash";
import { CreatePlaceholderTestModal } from "./modals/CreatePlaceholderTestModal";
import { SequencerGalleryModal } from "./modals/SequencerGalleryModal";

export function DesignBar() {
Expand Down Expand Up @@ -64,10 +65,18 @@ export function DesignBar() {
}, [elems, sequences, cycleRuns]);

const [displayTotal, setDisplayTotal] = useState(false);
const [
isCreatePlaceholderTestModalOpen,
setIsCreatePlaceholderTestModalOpen,
] = useState(false);
const [isGalleryOpen, setIsGalleryOpen] = useState(false);

return (
<div className=" border-b" style={{ height: ACTIONS_HEIGHT }}>
<CreatePlaceholderTestModal
isModalOpen={isCreatePlaceholderTestModalOpen}
setModalOpen={setIsCreatePlaceholderTestModalOpen}
/>
<SequencerGalleryModal
isGalleryOpen={isGalleryOpen}
setIsGalleryOpen={setIsGalleryOpen}
Expand Down Expand Up @@ -100,6 +109,18 @@ export function DesignBar() {
/>
New Test
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
setIsCreatePlaceholderTestModalOpen(true);
}}
data-testid="placeholder-test-button"
>
<FlaskConical
size={16}
className="mr-2 stroke-muted-foreground"
/>
New Placeholder
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {
setIsCreateProjectModalOpen(true);
Expand Down Expand Up @@ -136,17 +157,6 @@ export function DesignBar() {
<div className="grow" />
</>
)}
{/* Comming soon
<Button
data-testid="add-text-button"
className="gap-2"
variant="ghost"
disabled={true}
>
<HardDriveDownload size={20} className="stroke-muted-foreground" />
Load Test Profile From Cloud
</Button>
*/}

{sequences.length <= 1 ? (
<code className="inline-flex items-center justify-center p-3 text-sm text-muted-foreground">
Expand Down
Loading

0 comments on commit 5ad968d

Please sign in to comment.