You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ya...@apache.org on 2026/04/25 05:16:35 UTC
(airavata-portals) 18/34: feat(launcher): InterfacePicker verb cards
This is an automated email from the ASF dual-hosted git repository.
yasithdev pushed a commit to branch feat/generic-experiment-launcher
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git
commit 91509ab9cf18d22be88ef0115c9d1ab96ac14c1a
Author: yasithdev <ya...@icloud.com>
AuthorDate: Fri Apr 24 22:39:31 2026 -0400
feat(launcher): InterfacePicker verb cards
---
.../js/components/launch/InterfacePicker.vue | 34 +++++++++++++++++++
.../unit/components/launch/InterfacePicker.spec.ts | 38 ++++++++++++++++++++++
2 files changed, 72 insertions(+)
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/InterfacePicker.vue b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/InterfacePicker.vue
new file mode 100644
index 000000000..5216b013a
--- /dev/null
+++ b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/InterfacePicker.vue
@@ -0,0 +1,34 @@
+<template>
+ <div v-if="store.pickedApp" class="row g-2">
+ <div
+ v-for="iface in store.pickedApp.interfaces"
+ :key="iface.name"
+ class="col-6 col-md-3"
+ data-test="iface-card"
+ >
+ <button
+ type="button"
+ class="card w-100 text-start p-2"
+ :class="{ 'border-primary': store.draft.interface_name === iface.name }"
+ :data-test="`iface-card-${iface.name}`"
+ @click="store.pickInterface(iface.name)"
+ >
+ <code class="d-block fw-bold">{{ iface.name }}</code>
+ <small class="text-muted" :data-test="`iface-sig-${iface.name}`">
+ ({{ formatList(iface.inputs) }}) → {{ formatList(iface.outputs) || "void" }}
+ </small>
+ </button>
+ </div>
+ </div>
+</template>
+
+<script setup lang="ts">
+import type { IODescriptor } from "django-airavata-common-ui/js/stores/launch-types";
+import { useLaunchStore } from "django-airavata-common-ui/js/stores/launch";
+
+const store = useLaunchStore();
+
+function formatList(io: IODescriptor[]): string {
+ return io.map((x) => `${x.name}: ${x.type}`).join(", ");
+}
+</script>
diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/InterfacePicker.spec.ts b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/InterfacePicker.spec.ts
new file mode 100644
index 000000000..201db1b9a
--- /dev/null
+++ b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/InterfacePicker.spec.ts
@@ -0,0 +1,38 @@
+import { mount } from "@vue/test-utils";
+import { createPinia, setActivePinia } from "pinia";
+import { beforeEach, describe, expect, it } from "vitest";
+import InterfacePicker from "../../../../js/components/launch/InterfacePicker.vue";
+import { useLaunchStore } from "django-airavata-common-ui/js/stores/launch";
+
+const APP = {
+ app_id: "namd", name: "NAMD", category: "MD",
+ content: { kind: "github" as const, url: "g" },
+ interfaces: [
+ { name: "compile", inputs: [], outputs: [] },
+ { name: "run", inputs: [{ name: "x", type: "int" as const, required: true }], outputs: [] },
+ ],
+};
+
+describe("InterfacePicker", () => {
+ beforeEach(() => {
+ setActivePinia(createPinia());
+ useLaunchStore().pickApp(APP);
+ });
+
+ it("renders one card per interface", () => {
+ const w = mount(InterfacePicker);
+ expect(w.findAll("[data-test='iface-card']")).toHaveLength(2);
+ });
+
+ it("clicking a card sets interface_name in the store", async () => {
+ const w = mount(InterfacePicker);
+ await w.find("[data-test='iface-card-run']").trigger("click");
+ expect(useLaunchStore().draft.interface_name).toBe("run");
+ });
+
+ it("renders an input/output signature line per card", () => {
+ const w = mount(InterfacePicker);
+ const sig = w.find("[data-test='iface-sig-run']").text();
+ expect(sig).toContain("x: int");
+ });
+});