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 7e7ddf4778719a0cf5d4892fa7dc32db08bf2cde Author: yasithdev <[email protected]> AuthorDate: Fri Apr 24 22:55:06 2026 -0400 feat(launcher): Tab2Runtime assembly + readouts --- .../js/components/launch/Tab2Runtime.vue | 47 ++++++++++++++++++- .../unit/components/launch/Tab2Runtime.spec.ts | 52 ++++++++++++++++++++++ 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/Tab2Runtime.vue b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/Tab2Runtime.vue index 25a445eed..cb7733c2f 100644 --- a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/Tab2Runtime.vue +++ b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/js/components/launch/Tab2Runtime.vue @@ -1,2 +1,45 @@ -<template><div /></template> -<script setup lang="ts"></script> +<template> + <div v-if="store.profile"> + <RuntimeInputs + :profile="store.profile" + :model-value="store.draft.runtime" + @update:model-value="store.setRuntime($event)" + /> + <p class="small text-muted mt-2"> + Allocation <code>{{ store.profile.allocation_id }}</code> (auto from project) · + <span v-if="pickedCR"> + Resource <code>{{ pickedCR.compute_resource_id }}</code> · + Compute storage <code>{{ pickedCR.mapped_storage.storage_id }}</code> + scratch <code>{{ pickedCR.mapped_storage.scratch_path }}</code> + </span> + </p> + </div> + <div v-else class="text-muted">Loading resource profile…</div> +</template> + +<script setup lang="ts"> +import { computed, onMounted, watch } from "vue"; +import { useLaunchStore } from "django-airavata-common-ui/js/stores/launch"; +import { launcherService } from "django-airavata-common-ui/js/services/launcherService"; +import RuntimeInputs from "./runtime/RuntimeInputs.vue"; + +const store = useLaunchStore(); + +async function refetch(projectId: string) { + const p = await launcherService.getProjectResourceProfile(projectId); + store.setProfile(p); +} + +onMounted(() => { + if (store.draft.project_id) void refetch(store.draft.project_id); +}); + +watch( + () => store.draft.project_id, + (id) => { if (id) void refetch(id); else store.setProfile(null); }, +); + +const pickedCR = computed(() => + store.profile?.compute_resources.find((c) => c.compute_resource_id === store.draft.runtime.compute_resource_id) ?? null, +); +</script> diff --git a/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/Tab2Runtime.spec.ts b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/Tab2Runtime.spec.ts new file mode 100644 index 000000000..8d59fe6c9 --- /dev/null +++ b/airavata-django-portal/django_airavata/apps/workspace/static/django_airavata_workspace/tests/unit/components/launch/Tab2Runtime.spec.ts @@ -0,0 +1,52 @@ +import { mount, flushPromises } from "@vue/test-utils"; +import { createPinia, setActivePinia } from "pinia"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import Tab2Runtime from "../../../../js/components/launch/Tab2Runtime.vue"; +import { useLaunchStore } from "django-airavata-common-ui/js/stores/launch"; + +const PROFILE = { + project_id: "p1", allocation_id: "NSF-1", + compute_resources: [{ + compute_resource_id: "bridges-2", name: "Bridges-2", + mapped_storage: { storage_id: "scratch", scratch_path: "/scratch/p1" }, + partitions: [{ name: "RM", max_walltime: "48:00:00", max_nodes: 64, cpus_per_node: 128 }], + }], +}; + +vi.mock("django-airavata-common-ui/js/services/launcherService", () => ({ + launcherService: { + getProjectResourceProfile: vi.fn(), + }, +})); + +import { launcherService } from "django-airavata-common-ui/js/services/launcherService"; + +describe("Tab2Runtime", () => { + beforeEach(() => { + setActivePinia(createPinia()); + vi.mocked(launcherService.getProjectResourceProfile).mockResolvedValue(PROFILE); + useLaunchStore().setMeta({ name: "x", project_id: "p1", description: "" }); + }); + + it("renders the readout once a CR is picked", async () => { + const w = mount(Tab2Runtime); + await flushPromises(); + useLaunchStore().setRuntime({ + compute_resource_id: "bridges-2", partition: "RM", + walltime: "01:00:00", nodes: 1, cpus_per_node: 8, + }); + await flushPromises(); + expect(w.text()).toContain("NSF-1"); + expect(w.text()).toContain("bridges-2"); + expect(w.text()).toContain("/scratch/p1"); + }); + + it("re-fetches the profile when project changes", async () => { + const w = mount(Tab2Runtime); + await flushPromises(); + useLaunchStore().setMeta({ name: "x", project_id: "p2", description: "" }); + await flushPromises(); + expect(launcherService.getProjectResourceProfile).toHaveBeenCalledWith("p2"); + expect(w).toBeTruthy(); + }); +});
