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 <[email protected]>
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");
+  });
+});

Reply via email to