From e03ad4723e661c10c4ea7c4bc57526380a77bf0c Mon Sep 17 00:00:00 2001 From: Debsmita Santra Date: Thu, 10 Sep 2026 19:05:16 +0530 Subject: [PATCH] feat(bulk-import): add if predicate gating Signed-off-by: Debsmita Santra --- .../bulk-import-permission-refactor.md | 7 ++ workspaces/bulk-import/app-config.yaml | 3 +- workspaces/bulk-import/e2e-tests/app.test.ts | 32 ++--- .../bulk-import/e2e-tests/permissions.test.ts | 115 ++++++++++++++++++ .../bulk-import/e2e-tests/utils/helpers.ts | 5 +- .../e2e-tests/utils/permissionUtils.ts | 96 +++++++++++++++ .../e2e-tests/utils/translations.ts | 11 ++ .../plugins/bulk-import-backend/README.md | 4 +- .../bulk-import-backend/src/helpers/auth.ts | 1 - .../plugins/bulk-import-common/report.api.md | 4 +- .../src/permissions.test.ts | 5 +- .../bulk-import-common/src/permissions.ts | 1 - .../bulk-import/plugins/bulk-import/README.md | 13 +- .../AddRepositories/AddRepositoriesPage.tsx | 1 - .../src/components/BulkImportPage.tsx | 1 - .../src/components/BulkImportSidebarItem.tsx | 1 - .../Repositories/CatalogInfoAction.tsx | 1 - .../plugins/bulk-import/src/index.tsx | 10 ++ .../bulk-import/src/nfsExports.test.ts | 14 +++ 19 files changed, 286 insertions(+), 39 deletions(-) create mode 100644 workspaces/bulk-import/.changeset/bulk-import-permission-refactor.md create mode 100644 workspaces/bulk-import/e2e-tests/permissions.test.ts create mode 100644 workspaces/bulk-import/e2e-tests/utils/permissionUtils.ts diff --git a/workspaces/bulk-import/.changeset/bulk-import-permission-refactor.md b/workspaces/bulk-import/.changeset/bulk-import-permission-refactor.md new file mode 100644 index 00000000000..e8118a7033b --- /dev/null +++ b/workspaces/bulk-import/.changeset/bulk-import-permission-refactor.md @@ -0,0 +1,7 @@ +--- +'@red-hat-developer-hub/backstage-plugin-bulk-import-common': minor +'@red-hat-developer-hub/backstage-plugin-bulk-import': minor +'@red-hat-developer-hub/backstage-plugin-bulk-import-backend': minor +--- + +Convert `bulk.import` from a resource permission to a feature permission, and register the NFS bulk import page using the `if` permission predicate. diff --git a/workspaces/bulk-import/app-config.yaml b/workspaces/bulk-import/app-config.yaml index d61e84c9d54..74b44d7cfd1 100644 --- a/workspaces/bulk-import/app-config.yaml +++ b/workspaces/bulk-import/app-config.yaml @@ -143,8 +143,7 @@ catalog: # see https://backstage.io/docs/permissions/getting-started for more on the permission framework permission: - # setting this to `false` will disable permissions - enabled: false + enabled: true rbac: # Paths are relative to packages/backend (yarn start cwd). policies-csv-file: ../../examples/rbac-policy.csv diff --git a/workspaces/bulk-import/e2e-tests/app.test.ts b/workspaces/bulk-import/e2e-tests/app.test.ts index 85e8951de8b..f22d7e8e2ad 100644 --- a/workspaces/bulk-import/e2e-tests/app.test.ts +++ b/workspaces/bulk-import/e2e-tests/app.test.ts @@ -35,8 +35,10 @@ import { PreviewSidebarSnapshotsType, } from './utils/ariaSnapshots'; import { runAccessibilityTests, switchToLocale } from './utils/helpers'; +import { installMockBulkImportPermission } from './utils/permissionUtils'; import { BulkImportMessages, + getBulkImportNavLabel, getSelectedRepositoriesHeading, getTranslations, } from './utils/translations'; @@ -70,6 +72,7 @@ test.describe('Bulk Import', () => { // // This lets us focus on UI behaviour without needing a real OAuth provider // set up in the test environment. + await installMockBulkImportPermission(sharedPage, 'ALLOW'); await mockBulkImportSCMHostsResponse(sharedPage, mockSCMHostsData); await mockBulkImportRepositoriesResponse(sharedPage, mockRepositoriesData); await sharedPage.goto('/'); @@ -78,11 +81,6 @@ test.describe('Bulk Import', () => { await expect(enterButton).toBeVisible(); await enterButton.click(); - // Wait for authentication to complete - wait for sidebar or main content to appear - await sharedPage.waitForLoadState('networkidle'); - // Additional wait to ensure auth state is fully initialized - await sharedPage.waitForTimeout(500); - const currentLocale = await sharedPage.evaluate( () => globalThis.navigator.language, ); @@ -111,7 +109,9 @@ test.describe('Bulk Import', () => { } await sharedPage.reload(); - await sharedPage.waitForLoadState('networkidle'); + await expect(sharedPage.getByText('All Components')).toBeVisible({ + timeout: 30000, + }); } } @@ -120,21 +120,11 @@ test.describe('Bulk Import', () => { translations = getTranslations(currentLocale); previewSidebarSnapshots = getPreviewSidebarSnapshots(translations); - // Sidebar text is not yet getting translated and will be covered as part of story https://issues.redhat.com/browse/RHIDP-12094. - // TODO: Revert the change once the story is resolved. - if (process.env.APP_MODE === 'legacy') { - await expect( - sharedPage.getByRole('link', { name: translations.sidebar.bulkImport }), - ).toBeVisible(); - await sharedPage - .getByRole('link', { name: translations.sidebar.bulkImport }) - .click(); - } else { - await expect( - sharedPage.getByRole('link', { name: 'Bulk import' }), - ).toBeVisible(); - await sharedPage.getByRole('link', { name: 'Bulk import' }).click(); - } + const bulkImportNavLabel = getBulkImportNavLabel(currentLocale); + await expect( + sharedPage.getByRole('link', { name: bulkImportNavLabel }), + ).toBeVisible(); + await sharedPage.getByRole('link', { name: bulkImportNavLabel }).click(); }); test.afterAll(async () => { diff --git a/workspaces/bulk-import/e2e-tests/permissions.test.ts b/workspaces/bulk-import/e2e-tests/permissions.test.ts new file mode 100644 index 00000000000..0277eba4153 --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/permissions.test.ts @@ -0,0 +1,115 @@ +/* + * Copyright Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, test, type TestInfo } from '@playwright/test'; + +import { + mockBulkImportRepositoriesResponse, + mockBulkImportSCMHostsResponse, + mockRepositoriesData, + mockSCMHostsData, +} from './utils/apiUtils'; +import { + installMockBulkImportPermission, + loginAsGuest, +} from './utils/permissionUtils'; +import { getBulkImportNavLabel, getTranslations } from './utils/translations'; + +const isLegacy = process.env.APP_MODE === 'legacy'; + +function getLocaleContext(testInfo: TestInfo) { + const locale = testInfo.project.name; + const translations = getTranslations(locale); + const bulkImportNavLabel = getBulkImportNavLabel(locale); + + return { locale, translations, bulkImportNavLabel }; +} + +test.describe('Bulk Import permissions', () => { + test('shows bulk import in the sidebar when bulk.import is allowed', async ({ + page, + }, testInfo) => { + const { locale, bulkImportNavLabel } = getLocaleContext(testInfo); + + await installMockBulkImportPermission(page, 'ALLOW'); + await loginAsGuest(page, locale); + + await expect( + page.getByRole('link', { name: bulkImportNavLabel }), + ).toBeVisible(); + }); + + test('hides bulk import from the sidebar when bulk.import is denied', async ({ + page, + }, testInfo) => { + const { locale, bulkImportNavLabel } = getLocaleContext(testInfo); + + await installMockBulkImportPermission(page, 'DENY'); + await loginAsGuest(page, locale); + + await expect( + page.getByRole('link', { name: bulkImportNavLabel }), + ).not.toBeVisible(); + }); + + test('opens the bulk import page when bulk.import is allowed', async ({ + page, + }, testInfo) => { + const { locale, translations, bulkImportNavLabel } = + getLocaleContext(testInfo); + + await installMockBulkImportPermission(page, 'ALLOW'); + await mockBulkImportSCMHostsResponse(page, mockSCMHostsData); + await mockBulkImportRepositoriesResponse(page, mockRepositoriesData); + await loginAsGuest(page, locale); + + await page.getByRole('link', { name: bulkImportNavLabel }).click(); + + await expect( + page.getByText(translations.addRepositories.approvalTool.title), + ).toBeVisible(); + }); + + test('denies direct navigation to bulk import when bulk.import is denied', async ({ + page, + }, testInfo) => { + const { locale, translations, bulkImportNavLabel } = + getLocaleContext(testInfo); + + await installMockBulkImportPermission(page, 'DENY'); + await loginAsGuest(page, locale); + + // Fresh navigation resets NFS if-predicate session state (per test page). + await page.goto('/bulk-import'); + + if (isLegacy) { + await expect(page.getByTestId('no-permission-alert')).toBeVisible({ + timeout: 30000, + }); + await expect( + page.getByText(translations.permissions.addRepositoriesMessage), + ).toBeVisible(); + return; + } + + await expect( + page.getByText(translations.addRepositories.approvalTool.title), + ).not.toBeVisible(); + await expect( + page.getByRole('link', { name: bulkImportNavLabel }), + ).not.toBeVisible(); + }); +}); diff --git a/workspaces/bulk-import/e2e-tests/utils/helpers.ts b/workspaces/bulk-import/e2e-tests/utils/helpers.ts index 09d43f6810c..0405910f561 100644 --- a/workspaces/bulk-import/e2e-tests/utils/helpers.ts +++ b/workspaces/bulk-import/e2e-tests/utils/helpers.ts @@ -52,8 +52,9 @@ export async function switchToLocale( await page.getByRole('button', { name: 'English' }).click(); await page.getByRole('option', { name: displayName }).click(); await page.goto('/'); - // Wait for page to settle after locale switch and reload - await page.waitForLoadState('networkidle'); + await expect(page.getByText('All Components')).toBeVisible({ + timeout: 30000, + }); } } diff --git a/workspaces/bulk-import/e2e-tests/utils/permissionUtils.ts b/workspaces/bulk-import/e2e-tests/utils/permissionUtils.ts new file mode 100644 index 00000000000..fdde98b27ce --- /dev/null +++ b/workspaces/bulk-import/e2e-tests/utils/permissionUtils.ts @@ -0,0 +1,96 @@ +/* + * Copyright Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, Page } from '@playwright/test'; + +import { switchToLocale } from './helpers'; + +export const BULK_IMPORT_PERMISSION_NAME = 'bulk.import'; + +type AuthorizeItem = { + id: string; + permission?: { + name?: string; + }; +}; + +type AuthorizeRequestBody = { + items?: AuthorizeItem[]; +}; + +/** + * Mocks /api/permission/authorize for bulk.import while leaving other + * permission checks to the real backend. + * + * bulk.import is user-scoped (not per catalog entity), so we control allow/deny + * here instead of using multiple catalog entities like entity-scoped plugins. + */ +export async function installMockBulkImportPermission( + page: Page, + result: 'ALLOW' | 'DENY', +) { + await page.route('**/api/permission/authorize', async route => { + const request = route.request(); + if (request.method() !== 'POST') { + await route.continue(); + return; + } + + let body: AuthorizeRequestBody; + try { + body = request.postDataJSON() as AuthorizeRequestBody; + } catch { + await route.continue(); + return; + } + + const items = body.items ?? []; + const affectsBulkImport = items.some( + item => item.permission?.name === BULK_IMPORT_PERMISSION_NAME, + ); + + if (!affectsBulkImport) { + await route.continue(); + return; + } + + await route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + items: items.map(item => ({ + id: item.id, + result: + item.permission?.name === BULK_IMPORT_PERMISSION_NAME + ? result + : 'ALLOW', + })), + }), + }); + }); +} + +/** Sign in as guest, switch to the project locale, and wait for the catalog. */ +export async function loginAsGuest(page: Page, locale: string) { + await page.goto('/'); + const enterButton = page.getByRole('button', { name: 'Enter' }); + await expect(enterButton).toBeVisible(); + await enterButton.click(); + await expect(page.getByText('All Components')).toBeVisible({ + timeout: 30000, + }); + await switchToLocale(page, locale); +} diff --git a/workspaces/bulk-import/e2e-tests/utils/translations.ts b/workspaces/bulk-import/e2e-tests/utils/translations.ts index 76cf55845f4..56668a4b440 100644 --- a/workspaces/bulk-import/e2e-tests/utils/translations.ts +++ b/workspaces/bulk-import/e2e-tests/utils/translations.ts @@ -92,3 +92,14 @@ export function getSelectedRepositoriesHeading( ): string { return `${translations.addRepositories.selectedLabel} ${translations.addRepositories.selectedRepositories} (${count})`; } + +/** + * Sidebar label for the bulk import page. + * Legacy uses translated nav text; NFS still uses a hardcoded English title (RHIDP-12094). + */ +export function getBulkImportNavLabel(locale: string): string { + if (process.env.APP_MODE === 'legacy') { + return getTranslations(locale).sidebar.bulkImport; + } + return 'Bulk import'; +} diff --git a/workspaces/bulk-import/plugins/bulk-import-backend/README.md b/workspaces/bulk-import/plugins/bulk-import-backend/README.md index 7b97cf1922f..f6c37117433 100644 --- a/workspaces/bulk-import/plugins/bulk-import-backend/README.md +++ b/workspaces/bulk-import/plugins/bulk-import-backend/README.md @@ -30,7 +30,9 @@ To set up the bulk import backend package for the backend: #### Permission Framework Support -The Bulk Import Backend plugin has support for the permission framework. A basic example permission policy is shown below to disallow access to the bulk import API for all users except those in the `backstage-admins` group. +The Bulk Import Backend plugin has support for the permission framework. The plugin registers `bulk.import` as a **feature permission** (`BasicPermission`) from `@red-hat-developer-hub/backstage-plugin-bulk-import-common`. + +A basic example permission policy is shown below to disallow access to the bulk import API for all users except those in the `backstage-admins` group. 1. Create a backend module for the permission policy, under a `packages/backend/src/plugins/permissions.ts` file: diff --git a/workspaces/bulk-import/plugins/bulk-import-backend/src/helpers/auth.ts b/workspaces/bulk-import/plugins/bulk-import-backend/src/helpers/auth.ts index 80fb31d60b0..6959cb24cee 100644 --- a/workspaces/bulk-import/plugins/bulk-import-backend/src/helpers/auth.ts +++ b/workspaces/bulk-import/plugins/bulk-import-backend/src/helpers/auth.ts @@ -43,7 +43,6 @@ export async function permissionCheck( [ { permission: bulkImportPermission, - resourceRef: bulkImportPermission.resourceType, }, ], { diff --git a/workspaces/bulk-import/plugins/bulk-import-common/report.api.md b/workspaces/bulk-import/plugins/bulk-import-common/report.api.md index e2d11760f48..edad2c3a5a3 100644 --- a/workspaces/bulk-import/plugins/bulk-import-common/report.api.md +++ b/workspaces/bulk-import/plugins/bulk-import-common/report.api.md @@ -3,8 +3,8 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { ResourcePermission } from '@backstage/plugin-permission-common'; +import { BasicPermission } from '@backstage/plugin-permission-common'; // @public -export const bulkImportPermission: ResourcePermission<'bulk-import'>; +export const bulkImportPermission: BasicPermission; ``` diff --git a/workspaces/bulk-import/plugins/bulk-import-common/src/permissions.test.ts b/workspaces/bulk-import/plugins/bulk-import-common/src/permissions.test.ts index 530dc40623f..11c725b2a16 100644 --- a/workspaces/bulk-import/plugins/bulk-import-common/src/permissions.test.ts +++ b/workspaces/bulk-import/plugins/bulk-import-common/src/permissions.test.ts @@ -17,8 +17,9 @@ import { bulkImportPermission } from './permissions'; describe('bulkImportPermission', () => { - it('exports the stable permission name and resource type contract', () => { + it('exports the stable permission name as a feature permission', () => { expect(bulkImportPermission.name).toBe('bulk.import'); - expect(bulkImportPermission.resourceType).toBe('bulk-import'); + expect(bulkImportPermission.attributes).toEqual({}); + expect(bulkImportPermission).not.toHaveProperty('resourceType'); }); }); diff --git a/workspaces/bulk-import/plugins/bulk-import-common/src/permissions.ts b/workspaces/bulk-import/plugins/bulk-import-common/src/permissions.ts index 70ad6e7bd4c..ec42aa08ef9 100644 --- a/workspaces/bulk-import/plugins/bulk-import-common/src/permissions.ts +++ b/workspaces/bulk-import/plugins/bulk-import-common/src/permissions.ts @@ -22,5 +22,4 @@ import { createPermission } from '@backstage/plugin-permission-common'; export const bulkImportPermission = createPermission({ name: 'bulk.import', attributes: {}, - resourceType: 'bulk-import', }); diff --git a/workspaces/bulk-import/plugins/bulk-import/README.md b/workspaces/bulk-import/plugins/bulk-import/README.md index a4aaa96c074..c3f7dd2d16c 100644 --- a/workspaces/bulk-import/plugins/bulk-import/README.md +++ b/workspaces/bulk-import/plugins/bulk-import/README.md @@ -29,7 +29,7 @@ The sections below are relevant for static plugins. If the plugin is expected to **NOTE** -- When RBAC permission framework is enabled, for non-admin users to access bulk import UI, the role associated with your user should have the following permission policies associated with it. Add the following in your permission policies configuration file: +- When the permission framework is enabled, users need `bulk.import` to access the Bulk Import UI and API. For non-admin users, associate a role with the following policies: ```CSV p, role:default/team_a, bulk.import, use, allow @@ -143,13 +143,20 @@ export default createApp({ The plugin will automatically provide: - Bulk Import page at `/bulk-import` with all existing features -- A "Bulk import" navigation item in the sidebar +- A "Bulk import" navigation item in the sidebar (only when the signed-in user is allowed `bulk.import`) - Translations support (via `bulkImportTranslationsModule`) +#### Permission-based visibility (NFS) + +The NFS page is registered with an `if` predicate that checks for the `bulk.import` permission. When permission checks deny access, the page and sidebar entry are not registered for that session. NFS gates page and nav registration via the `if` predicate; shared route components (such as `AddRepositoriesPage`) and the legacy entry point still use `usePermission` for in-page checks. + +Users who lack `bulk.import` will not see the Bulk Import nav item and cannot reach `/bulk-import` through the registered NFS routes. + +> **Legacy (OFS) note:** The `./legacy` entry point still uses `usePermission` in `BulkImportSidebarItem` and shows a permission-denied alert on direct navigation to `/bulk-import`. + ### Extensions The following extensions are available in the plugin: - `api:bulk-import` - `page:bulk-import` -- `nav-item:bulk-import` diff --git a/workspaces/bulk-import/plugins/bulk-import/src/components/AddRepositories/AddRepositoriesPage.tsx b/workspaces/bulk-import/plugins/bulk-import/src/components/AddRepositories/AddRepositoriesPage.tsx index e7eb131fdac..268abc052aa 100644 --- a/workspaces/bulk-import/plugins/bulk-import/src/components/AddRepositories/AddRepositoriesPage.tsx +++ b/workspaces/bulk-import/plugins/bulk-import/src/components/AddRepositories/AddRepositoriesPage.tsx @@ -35,7 +35,6 @@ export const AddRepositoriesPage = () => { const bulkImportViewPermissionResult = usePermission({ permission: bulkImportPermission, - resourceRef: bulkImportPermission.resourceType, }); const { numberOfApprovalTools } = useNumberOfApprovalTools(); diff --git a/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportPage.tsx b/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportPage.tsx index 59c75d429a9..436f485c9bf 100644 --- a/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportPage.tsx +++ b/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportPage.tsx @@ -46,7 +46,6 @@ export const BulkImportPage = () => { const bulkImportViewPermissionResult = usePermission({ permission: bulkImportPermission, - resourceRef: bulkImportPermission.resourceType, }); const showContent = () => { diff --git a/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportSidebarItem.tsx b/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportSidebarItem.tsx index f357c787395..2525f257264 100644 --- a/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportSidebarItem.tsx +++ b/workspaces/bulk-import/plugins/bulk-import/src/components/BulkImportSidebarItem.tsx @@ -29,7 +29,6 @@ export const BulkImportSidebarItem = () => { const { t } = useTranslation(); const { loading: isUserLoading, allowed } = usePermission({ permission: bulkImportPermission, - resourceRef: bulkImportPermission.resourceType, }); const config = useApi(configApiRef); diff --git a/workspaces/bulk-import/plugins/bulk-import/src/components/Repositories/CatalogInfoAction.tsx b/workspaces/bulk-import/plugins/bulk-import/src/components/Repositories/CatalogInfoAction.tsx index 28d7bdab8df..3f1eaccc092 100644 --- a/workspaces/bulk-import/plugins/bulk-import/src/components/Repositories/CatalogInfoAction.tsx +++ b/workspaces/bulk-import/plugins/bulk-import/src/components/Repositories/CatalogInfoAction.tsx @@ -55,7 +55,6 @@ const CatalogInfoAction = ({ data }: { data: AddRepositoryData }) => { const { allowed } = usePermission({ permission: bulkImportPermission, - resourceRef: bulkImportPermission.resourceType, }); const { value, loading } = useAsync(async () => { if (repoUrl) { diff --git a/workspaces/bulk-import/plugins/bulk-import/src/index.tsx b/workspaces/bulk-import/plugins/bulk-import/src/index.tsx index 1f3d05852e2..c418e19c5be 100644 --- a/workspaces/bulk-import/plugins/bulk-import/src/index.tsx +++ b/workspaces/bulk-import/plugins/bulk-import/src/index.tsx @@ -27,6 +27,8 @@ import { } from '@backstage/frontend-plugin-api'; import { TranslationBlueprint } from '@backstage/plugin-app-react'; +import { bulkImportPermission } from '@red-hat-developer-hub/backstage-plugin-bulk-import-common'; + import { bulkImportApiRef, BulkImportBackendClient, @@ -34,6 +36,12 @@ import { import BulkImportIcon from './components/BulkImportIcon'; import { bulkImportTranslations } from './translations'; +const bulkImportAccess = { + permissions: { + $contains: bulkImportPermission.name, + }, +}; + const rootRouteRef = createRouteRef(); const importHistoryRouteRef = createSubRouteRef({ parent: rootRouteRef, @@ -57,6 +65,8 @@ const bulkImportApi = ApiBlueprint.make({ }); const bulkImportPage = PageBlueprint.make({ + // Re-visit this when we have the custom NavContent Extension in place. Ref Jira: https://redhat.atlassian.net/browse/RHIDP-12094 + if: bulkImportAccess, params: { title: 'Bulk import', icon: , diff --git a/workspaces/bulk-import/plugins/bulk-import/src/nfsExports.test.ts b/workspaces/bulk-import/plugins/bulk-import/src/nfsExports.test.ts index 367d2a335b4..5f5868b3c6b 100644 --- a/workspaces/bulk-import/plugins/bulk-import/src/nfsExports.test.ts +++ b/workspaces/bulk-import/plugins/bulk-import/src/nfsExports.test.ts @@ -16,9 +16,14 @@ import { coreExtensionData } from '@backstage/frontend-plugin-api'; import { createExtensionTester } from '@backstage/frontend-test-utils'; +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + import translationsModuleDefault from './bulkImportTranslationsModuleExport'; import bulkImportPlugin, { bulkImportTranslationsModule } from './index'; +const nfsPluginSource = readFileSync(resolve(__dirname, 'index.tsx'), 'utf8'); + describe('bulk-import NFS exports', () => { it('should export a translations module as a FrontendModule', () => { expect(bulkImportTranslationsModule).toBeDefined(); @@ -30,6 +35,15 @@ describe('bulk-import NFS exports', () => { it('should export the translations module as default for NFS discovery', () => { expect(translationsModuleDefault).toBe(bulkImportTranslationsModule); }); + + it('registers the bulk import page with a bulk.import permission if predicate', () => { + expect(nfsPluginSource).toMatch( + /PageBlueprint\.make\(\{[\s\S]*?if:\s*bulkImportAccess/, + ); + expect(nfsPluginSource).toMatch( + /permissions:\s*\{\s*\$contains:\s*bulkImportPermission\.name/, + ); + }); }); describe('bulk-import NFS wiring', () => {