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 5be9e50237c4075dccd770dcb26e7a24c9f86d67
Author: yasithdev <[email protected]>
AuthorDate: Fri Apr 24 22:51:17 2026 -0400

    feat(launcher): Tab1ApplicationInputs assembly
    
    Replaces the empty stub with the full Tab1 component: AppPicker →
    InterfacePicker → InputList → OutputList with progressive disclosure.
    Adds 2 tests; also fixes LaunchContainer mock to stub the new service
    methods so no unhandled rejections surface from the child component.
---
 .../js/components/launch/Tab1ApplicationInputs.vue | 46 +++++++++++++++++++++-
 .../unit/components/launch/LaunchContainer.spec.ts |  5 ++-
 .../launch/Tab1ApplicationInputs.spec.ts           | 43 ++++++++++++++++++++
 3 files changed, 91 insertions(+), 3 deletions(-)

diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/Tab1ApplicationInputs.vue
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/Tab1ApplicationInputs.vue
index 25a445eed..dc2e24282 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/Tab1ApplicationInputs.vue
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/Tab1ApplicationInputs.vue
@@ -1,2 +1,44 @@
-<template><div /></template>
-<script setup lang="ts"></script>
+<template>
+  <div>
+    <section class="mb-3">
+      <div class="text-uppercase text-primary small fw-bold 
mb-1">Application</div>
+      <AppPicker :applications="apps" />
+    </section>
+    <section v-if="store.pickedApp" class="mb-3">
+      <div class="text-uppercase text-primary small fw-bold 
mb-1">Interface</div>
+      <InterfacePicker />
+    </section>
+    <section v-if="store.pickedInterface" data-test="inputs-section" 
class="mb-3">
+      <div class="text-uppercase text-primary small fw-bold mb-1">Inputs</div>
+      <InputList :storages="store.storages" />
+    </section>
+    <section v-if="store.pickedInterface && fileOutputCount > 0" class="mb-3">
+      <div class="text-uppercase text-primary small fw-bold mb-1">Outputs</div>
+      <OutputList :storages="store.storages" />
+    </section>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { computed, onMounted, ref } from "vue";
+import { launcherService } from 
"django-airavata-common-ui/js/services/launcherService";
+import { useLaunchStore } from "django-airavata-common-ui/js/stores/launch";
+import type { Application } from 
"django-airavata-common-ui/js/stores/launch-types";
+import AppPicker from "./AppPicker.vue";
+import InterfacePicker from "./InterfacePicker.vue";
+import InputList from "./InputList.vue";
+import OutputList from "./OutputList.vue";
+
+const store = useLaunchStore();
+const apps = ref<Application[]>([]);
+
+onMounted(async () => {
+  const [a, s] = await Promise.all([launcherService.listApplications(), 
launcherService.listUserStorages()]);
+  apps.value = a?.results ?? [];
+  store.setStorages(s?.results ?? []);
+});
+
+const fileOutputCount = computed(() =>
+  (store.pickedInterface?.outputs ?? []).filter((o) => o.type === "file" || 
o.type === "dir").length,
+);
+</script>
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/LaunchContainer.spec.ts
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/LaunchContainer.spec.ts
index 44d8f5e46..22cced971 100644
--- 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/LaunchContainer.spec.ts
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/LaunchContainer.spec.ts
@@ -5,10 +5,13 @@ import LaunchContainer from 
"../../../../js/containers/LaunchContainer.vue";
 
 vi.mock("django-airavata-common-ui/js/services/launcherService", () => ({
   launcherService: {
-    listProjects: vi.fn().mockResolvedValue({ results: [] }),
+    listProjects: vi.fn(),
+    listApplications: vi.fn(),
+    listUserStorages: vi.fn(),
   },
 }));
 
+
 describe("LaunchContainer", () => {
   beforeEach(() => setActivePinia(createPinia()));
 
diff --git 
a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/Tab1ApplicationInputs.spec.ts
 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/Tab1ApplicationInputs.spec.ts
new file mode 100644
index 000000000..b90b43ecb
--- /dev/null
+++ 
b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/Tab1ApplicationInputs.spec.ts
@@ -0,0 +1,43 @@
+import { mount, flushPromises } from "@vue/test-utils";
+import { createPinia, setActivePinia } from "pinia";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+import Tab1ApplicationInputs from 
"../../../../js/components/launch/Tab1ApplicationInputs.vue";
+import { useLaunchStore } from "django-airavata-common-ui/js/stores/launch";
+
+vi.mock("django-airavata-common-ui/js/services/launcherService", () => ({
+  launcherService: {
+    listApplications: vi.fn(),
+    listUserStorages: vi.fn(),
+  },
+}));
+
+describe("Tab1ApplicationInputs", () => {
+  beforeEach(async () => {
+    setActivePinia(createPinia());
+    const { launcherService } = await import(
+      "django-airavata-common-ui/js/services/launcherService"
+    );
+    vi.mocked(launcherService.listApplications).mockResolvedValue({
+      results: [{
+        app_id: "namd", name: "NAMD", category: "MD",
+        content: { kind: "github", url: "g" }, interfaces: [],
+      }],
+    });
+    vi.mocked(launcherService.listUserStorages).mockResolvedValue({
+      results: [{ storage_id: "my-home", name: "My Home", is_primary: true }],
+    });
+  });
+
+  it("loads applications + storages on mount", async () => {
+    const w = mount(Tab1ApplicationInputs);
+    await flushPromises();
+    expect(w.findAll("[data-test='app-tile']")).toHaveLength(1);
+    expect(useLaunchStore().storages).toHaveLength(1);
+  });
+
+  it("does not render the inputs section before an app+interface are picked", 
async () => {
+    const w = mount(Tab1ApplicationInputs);
+    await flushPromises();
+    expect(w.find("[data-test='inputs-section']").exists()).toBe(false);
+  });
+});

Reply via email to