diff --git a/api/app/src/main/kotlin/packit/model/dto/RunnerPackageDto.kt b/api/app/src/main/kotlin/packit/model/dto/RunnerPackageDto.kt index 84492e41..d96fdcfa 100644 --- a/api/app/src/main/kotlin/packit/model/dto/RunnerPackageDto.kt +++ b/api/app/src/main/kotlin/packit/model/dto/RunnerPackageDto.kt @@ -3,4 +3,5 @@ package packit.model.dto data class RunnerPackageDto( val name: String, val version: String, + val location: String, ) diff --git a/api/app/src/test/kotlin/packit/integration/controllers/RunnerControllerTest.kt b/api/app/src/test/kotlin/packit/integration/controllers/RunnerControllerTest.kt index 06cbdd3b..220700a4 100644 --- a/api/app/src/test/kotlin/packit/integration/controllers/RunnerControllerTest.kt +++ b/api/app/src/test/kotlin/packit/integration/controllers/RunnerControllerTest.kt @@ -128,7 +128,9 @@ class RunnerControllerTest : IntegrationTest() { HttpMethod.GET, getTokenizedHttpEntity() ) - assertEquals(listOf(RunnerPackageDto(name = "minimalRPackage", version = "0.0.1")), res.body) + assertThat( + res.body + ).contains(RunnerPackageDto(name = "minimalRPackage", version = "0.0.1", location = "/library")) } @Test diff --git a/api/app/src/test/kotlin/packit/integration/services/OrderlyRunnerClientTest.kt b/api/app/src/test/kotlin/packit/integration/services/OrderlyRunnerClientTest.kt index e47ad734..eb81d891 100644 --- a/api/app/src/test/kotlin/packit/integration/services/OrderlyRunnerClientTest.kt +++ b/api/app/src/test/kotlin/packit/integration/services/OrderlyRunnerClientTest.kt @@ -1,5 +1,6 @@ package packit.integration.services +import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Value @@ -51,7 +52,9 @@ class OrderlyRunnerClientTest( val result = sut.getPackages() assertIs>(result) - assertEquals(listOf(RunnerPackageDto(name = "minimalRPackage", version = "0.0.1")), result) + assertThat( + result + ).contains(RunnerPackageDto(name = "minimalRPackage", version = "0.0.1", location = "/library")) } @Test diff --git a/api/app/src/test/kotlin/packit/unit/controllers/RunnerControllerTest.kt b/api/app/src/test/kotlin/packit/unit/controllers/RunnerControllerTest.kt index 32bc5a1b..c8057685 100644 --- a/api/app/src/test/kotlin/packit/unit/controllers/RunnerControllerTest.kt +++ b/api/app/src/test/kotlin/packit/unit/controllers/RunnerControllerTest.kt @@ -13,8 +13,8 @@ import kotlin.test.assertEquals class RunnerControllerTest { private val runnerService = mock { on { getPackages() } doReturn listOf( - RunnerPackageDto("package1", "1.0.0"), - RunnerPackageDto("package2", "2.0.0"), + RunnerPackageDto("package1", "1.0.0", "/library"), + RunnerPackageDto("package2", "2.0.0", "/usr/lib/R/library"), ) } @@ -33,5 +33,8 @@ class RunnerControllerTest { assertThat( listOf(responseBody?.get(0)?.version, responseBody?.get(1)?.version) ).containsExactly("1.0.0", "2.0.0") + assertThat( + listOf(responseBody?.get(0)?.location, responseBody?.get(1)?.location) + ).containsExactly("/library", "/usr/lib/R/library") } } diff --git a/api/app/src/test/kotlin/packit/unit/service/RunnerServiceTest.kt b/api/app/src/test/kotlin/packit/unit/service/RunnerServiceTest.kt index d8221e1f..29bd3bc3 100644 --- a/api/app/src/test/kotlin/packit/unit/service/RunnerServiceTest.kt +++ b/api/app/src/test/kotlin/packit/unit/service/RunnerServiceTest.kt @@ -56,8 +56,8 @@ class RunnerServiceTest { private val client = mock { on { getVersion() } doReturn version on { getPackages() } doReturn listOf( - RunnerPackageDto("package1", "1.0.0"), - RunnerPackageDto("package2", "2.0.0"), + RunnerPackageDto("package1", "1.0.0", "/library"), + RunnerPackageDto("package2", "2.0.0", "/usr/lib/R/library"), ) } private val runInfoRepository = mock { @@ -91,8 +91,8 @@ class RunnerServiceTest { val result = sut.getPackages() assertEquals( listOf( - RunnerPackageDto("package1", "1.0.0"), - RunnerPackageDto("package2", "2.0.0"), + RunnerPackageDto("package1", "1.0.0", "/library"), + RunnerPackageDto("package2", "2.0.0", "/usr/lib/R/library"), ), result ) diff --git a/app/e2e/runnerPage.demo.spec.ts b/app/e2e/runnerPage.demo.spec.ts index 3dd78cd9..be90da5c 100644 --- a/app/e2e/runnerPage.demo.spec.ts +++ b/app/e2e/runnerPage.demo.spec.ts @@ -1,22 +1,16 @@ -import { Locator } from "@playwright/test"; import { test, expect, TAG_DEMO_R_LIBRARY } from "./tagCheckFixture"; import { getContentLocator } from "./utils"; test.describe("Runner page", () => { - let content: Locator; - - test.beforeEach(async ({ page }) => { - await page.goto("./"); - content = await getContentLocator(page); - await page.getByRole("link", { name: "Runner" }).click(); - }); - test.describe("Packages tab", { tag: TAG_DEMO_R_LIBRARY }, () => { // Expect the example R package to be listed as installed; this package will not be present on // demo or prod environments, nor if PACKIT_HOST_R_LIBRARY_PATH was set to anything other than // the `./scripts/runnerDemoLib` path when running dependencies. // See api/README.md for more details. test("can see list of installed packages", async ({ page }) => { + await page.goto("./"); + const content = await getContentLocator(page); + await page.getByRole("link", { name: "Runner" }).click(); await page.getByRole("link", { name: "Package versions" }).click(); await expect(content.getByText(/minimalRPackage.*0\.0\.1/)).toBeVisible(); }); diff --git a/app/src/app/components/contents/runner/PacketRunnerPackages.tsx b/app/src/app/components/contents/runner/PacketRunnerPackages.tsx index 63847f8c..0e7e779f 100644 --- a/app/src/app/components/contents/runner/PacketRunnerPackages.tsx +++ b/app/src/app/components/contents/runner/PacketRunnerPackages.tsx @@ -1,9 +1,9 @@ -import { Library } from "lucide-react"; import { useGetPackages } from "./hooks/useGetPackages"; import { Skeleton } from "@components/Base/Skeleton"; import { HttpStatus } from "@lib/types/HttpStatus"; import { Unauthorized } from "../common/Unauthorized"; import { ErrorComponent } from "../common/ErrorComponent"; +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@components/Base/Accordion"; export const PacketRunnerPackages = () => { const { packages, error } = useGetPackages(); @@ -19,6 +19,9 @@ export const PacketRunnerPackages = () => { return ; } + const sharedLibraryPackages = packages.filter((pkg) => pkg.location === "/library"); + const otherPackages = packages.filter((pkg) => pkg.location !== "/library"); + return ( <>
@@ -26,22 +29,48 @@ export const PacketRunnerPackages = () => {

View installed R packages and versions

-

- This list includes only the additional packages installed in the runner’s R library for use by reports, - and excludes some standard packages. -

- - -

Installed packages

-
-
    - {packages.map((pkg, name) => ( -
  • - {pkg.name} - {pkg.version} -
  • - ))} -
+ + + +

Runner packages

+
+ +

+ This list includes only the additional packages installed in the runner’s R library for use by + reports. +

+
    + {sharedLibraryPackages.map((pkg, name) => ( +
  • + {pkg.name} + {pkg.version} +
  • + ))} +
+
+
+ + +

Other packages

+
+ +

+ This list includes packages that are part of the base R installation, or which are available in the + runner environment without having been specifically installed in the runner’s R library. They are + included here for reference. It is recommended to specifically install any packages your reports depend + on in the runner’s R library to ensure consistent behavior across different environments. +

+
    + {otherPackages.map((pkg, name) => ( +
  • + {pkg.name} + {pkg.version} +
  • + ))} +
+
+
+
); diff --git a/app/src/tests/components/contents/runner/PacketRunnerPackages.test.tsx b/app/src/tests/components/contents/runner/PacketRunnerPackages.test.tsx index dd223489..6213d5e6 100644 --- a/app/src/tests/components/contents/runner/PacketRunnerPackages.test.tsx +++ b/app/src/tests/components/contents/runner/PacketRunnerPackages.test.tsx @@ -1,4 +1,5 @@ import { render, screen, waitFor } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import { PacketRunnerPackages } from "@components/contents/runner"; import { mockRunnerPackages } from "@/tests/mocks"; import { SWRConfig } from "swr"; @@ -18,17 +19,35 @@ describe("PacketRunnerPackages component", () => { ); - it("should render packages page with a list of installed packages", async () => { + it("should render packages page with accordion sections for library and other packages", async () => { renderComponent(); await waitFor(() => { expect(screen.getByText("Package versions")).toBeVisible(); }); - mockRunnerPackages.forEach((pkg) => { + expect(screen.getByText("Runner packages")).toBeVisible(); + expect(screen.getByText("Other packages")).toBeVisible(); + + const libraryPackages = mockRunnerPackages.filter((pkg) => pkg.location === "/library"); + const otherPackages = mockRunnerPackages.filter((pkg) => pkg.location !== "/library"); + libraryPackages.forEach((pkg) => { expect(screen.getByText(pkg.name)).toBeVisible(); expect(screen.getByText(pkg.version)).toBeVisible(); }); + otherPackages.forEach((pkg) => { + expect(screen.queryByText(pkg.name)).not.toBeInTheDocument(); + expect(screen.queryByText(pkg.version)).not.toBeInTheDocument(); + }); + + await userEvent.click(screen.getByText("Other packages")); + + await waitFor(() => { + otherPackages.forEach((pkg) => { + expect(screen.getByText(pkg.name)).toBeVisible(); + expect(screen.getByText(pkg.version)).toBeVisible(); + }); + }); }); it("should render unauthorized when 401 error fetching packages", async () => { diff --git a/app/src/tests/mocks.ts b/app/src/tests/mocks.ts index 6f2e77ba..aadc4e0b 100644 --- a/app/src/tests/mocks.ts +++ b/app/src/tests/mocks.ts @@ -842,11 +842,13 @@ export const mockGitBranches: GitBranches = { export const mockRunnerPackages: RunnerPackage[] = [ { name: "package1", - version: "0.1.0" + version: "0.1.0", + location: "/library" }, { name: "package2", - version: "2.0.0" + version: "2.0.0", + location: "/usr/lib/R/library" } ]; diff --git a/app/src/types.ts b/app/src/types.ts index 614d458b..59cc1026 100644 --- a/app/src/types.ts +++ b/app/src/types.ts @@ -96,10 +96,11 @@ export interface Custom { }; } -// R packages installed into a shared library for the runner and workers +// R packages installed for the runner and workers export interface RunnerPackage { name: string; version: string; + location: string; } interface Description { diff --git a/scripts/common b/scripts/common index 68297d8d..59e1d1a2 100644 --- a/scripts/common +++ b/scripts/common @@ -3,7 +3,7 @@ set -exu # Export env vars needed for running test dependencies export OUTPACK_SERVER_IMAGE=docker.io/mrcide/outpack_server:main -export ORDERLY_RUNNER_IMAGE=ghcr.io/mrc-ide/orderly.runner:main +export ORDERLY_RUNNER_IMAGE=ghcr.io/mrc-ide/orderly.runner:library-list-report-all-packages export RUNNER_CONTAINER_NAMESPACE=orderly.runner export RUNNER_REDIS=$RUNNER_CONTAINER_NAMESPACE-redis