diff --git a/pkg/sbombastic-image-vulnerability-scanner/components/CveDetails.vue b/pkg/sbombastic-image-vulnerability-scanner/components/CveDetails.vue
index 463ba7b..369122f 100644
--- a/pkg/sbombastic-image-vulnerability-scanner/components/CveDetails.vue
+++ b/pkg/sbombastic-image-vulnerability-scanner/components/CveDetails.vue
@@ -249,8 +249,8 @@ export default {
@@ -411,7 +411,8 @@ export default {
const metadata = this.currentImage.imageMetadata;
if (metadata?.registryURI && metadata?.repository && metadata?.tag) {
- return `${metadata.registryURI}/${metadata.repository}:${metadata.tag}`;
+ // return `${metadata.registryURI}/${metadata.repository}:${metadata.tag}`;
+ return `${metadata.repository.split("/")[0]}:${metadata.tag}`;
}
// Fallback to the hash ID if metadata is not available
diff --git a/pkg/sbombastic-image-vulnerability-scanner/components/InstallView3.vue b/pkg/sbombastic-image-vulnerability-scanner/components/InstallView3.vue
index 6f400fc..c845de8 100644
--- a/pkg/sbombastic-image-vulnerability-scanner/components/InstallView3.vue
+++ b/pkg/sbombastic-image-vulnerability-scanner/components/InstallView3.vue
@@ -27,7 +27,7 @@ export default {
[]
},
- reloadFn: {
- type: Function,
- default: () => {}
- }
},
methods: {
async startScan() {
@@ -54,10 +50,6 @@ export default {
title: this.t('imageScanner.registries.messages.registryScanFailed'),
message: e.message,
});
- } finally {
- setTimeout(() => {
- this.reloadFn(true);
- }, 2000);
}
});
},
diff --git a/pkg/sbombastic-image-vulnerability-scanner/config/sbombastic-image-vulnerability-scanner.ts b/pkg/sbombastic-image-vulnerability-scanner/config/sbombastic-image-vulnerability-scanner.ts
index e1ca271..c5c1cb3 100644
--- a/pkg/sbombastic-image-vulnerability-scanner/config/sbombastic-image-vulnerability-scanner.ts
+++ b/pkg/sbombastic-image-vulnerability-scanner/config/sbombastic-image-vulnerability-scanner.ts
@@ -33,10 +33,11 @@ export function init($plugin: any, store: any) {
route: {
name: `c-cluster-${PRODUCT_NAME}-${PAGE.REGISTRIES}`,
params: {
- product: PRODUCT_NAME
+ product: PRODUCT_NAME,
+ resource: RESOURCE.REGISTRY,
},
meta: { pkg: PRODUCT_NAME, product: PRODUCT_NAME }
- }
+ },
});
virtualType({
@@ -52,18 +53,18 @@ export function init($plugin: any, store: any) {
}
});
- virtualType({
- labelKey: "imageScanner.vulnerabilities.title",
- name: PAGE.VULNERABILITIES,
- namespaced: false,
- route: {
- name: `c-cluster-${PRODUCT_NAME}-${PAGE.VULNERABILITIES}`,
- params: {
- product: PRODUCT_NAME,
- },
- meta: { pkg: PRODUCT_NAME, product: PRODUCT_NAME },
- },
- });
+ // virtualType({
+ // labelKey: "imageScanner.vulnerabilities.title",
+ // name: PAGE.VULNERABILITIES,
+ // namespaced: false,
+ // route: {
+ // name: `c-cluster-${PRODUCT_NAME}-${PAGE.VULNERABILITIES}`,
+ // params: {
+ // product: PRODUCT_NAME,
+ // },
+ // meta: { pkg: PRODUCT_NAME, product: PRODUCT_NAME },
+ // },
+ // });
virtualType({
labelKey: "imageScanner.vexManagement.title",
@@ -80,13 +81,13 @@ export function init($plugin: any, store: any) {
weightType(PAGE.DASHBOARD, 98, true);
weightType(PAGE.IMAGES, 97, true);
- weightType(PAGE.VULNERABILITIES, 96, true);
+ // weightType(PAGE.VULNERABILITIES, 96, true);
weightType(PAGE.VEX_MANAGEMENT, 95, true);
basicType([
PAGE.DASHBOARD,
PAGE.IMAGES,
- PAGE.VULNERABILITIES,
+ // PAGE.VULNERABILITIES,
]);
// Prepend spaces on group name, as Rancher 2.12 render group name algin with sidemenu
basicType([PAGE.REGISTRIES, PAGE.VEX_MANAGEMENT], ' Advanced');
diff --git a/pkg/sbombastic-image-vulnerability-scanner/config/table-headers.ts b/pkg/sbombastic-image-vulnerability-scanner/config/table-headers.ts
index 2c52384..e029a88 100644
--- a/pkg/sbombastic-image-vulnerability-scanner/config/table-headers.ts
+++ b/pkg/sbombastic-image-vulnerability-scanner/config/table-headers.ts
@@ -27,7 +27,8 @@ export const REGISTRY_SCAN_TABLE = [
{
name: "scanInterval",
labelKey: "imageScanner.registries.registrytable.header.schedule",
- value: (row: any) => `Every ${row.spec.scanInterval}`,
+ value: "spec.scanInterval",
+ formatter: "ScanInterval",
sort: "spec.scanInterval",
},
{
diff --git a/pkg/sbombastic-image-vulnerability-scanner/formatters/ImageNameCell.vue b/pkg/sbombastic-image-vulnerability-scanner/formatters/ImageNameCell.vue
index 9b9325d..4187671 100644
--- a/pkg/sbombastic-image-vulnerability-scanner/formatters/ImageNameCell.vue
+++ b/pkg/sbombastic-image-vulnerability-scanner/formatters/ImageNameCell.vue
@@ -25,7 +25,8 @@ export default {
displayName() {
// For real Kubernetes resources, show human-readable reference
if (this.row.imageMetadata?.repository) {
- return `${this.row.imageMetadata.registryURI}/${this.row.imageMetadata.repository}:${this.row.imageMetadata.tag}`;
+ // return `${this.row.imageMetadata.registryURI}/${this.row.imageMetadata.repository}:${this.row.imageMetadata.tag}`;
+ return `${this.row.imageMetadata.repository.split("/")[0]}:${this.row.imageMetadata.tag}`;
}
// For mock data, show the metadata name
return this.row.metadata.name;
diff --git a/pkg/sbombastic-image-vulnerability-scanner/formatters/ScanInterval.vue b/pkg/sbombastic-image-vulnerability-scanner/formatters/ScanInterval.vue
new file mode 100644
index 0000000..8cc405e
--- /dev/null
+++ b/pkg/sbombastic-image-vulnerability-scanner/formatters/ScanInterval.vue
@@ -0,0 +1,29 @@
+
+
+ {{ t('imageScanner.general.every') }} {{ value }}
+ n/a
+
+
+
+
+
\ No newline at end of file
diff --git a/pkg/sbombastic-image-vulnerability-scanner/l10n/en-us.yaml b/pkg/sbombastic-image-vulnerability-scanner/l10n/en-us.yaml
index 839c4ef..57dcc27 100644
--- a/pkg/sbombastic-image-vulnerability-scanner/l10n/en-us.yaml
+++ b/pkg/sbombastic-image-vulnerability-scanner/l10n/en-us.yaml
@@ -275,7 +275,7 @@ imageScanner:
medium: Medium
low: Low
none: None
- unknown: Unknown
+ unknown: None
suppressed: Suppressed
status:
pending: Pending
@@ -302,6 +302,7 @@ imageScanner:
schedule: Every {i}
reload: Reload
any: Any
+ every: Every
typeLabel:
sbombastic.rancher.io.registry: Registries configuration
diff --git a/pkg/sbombastic-image-vulnerability-scanner/list/sbombastic.rancher.io.registry.vue b/pkg/sbombastic-image-vulnerability-scanner/list/sbombastic.rancher.io.registry.vue
new file mode 100644
index 0000000..85f34ca
--- /dev/null
+++ b/pkg/sbombastic-image-vulnerability-scanner/list/sbombastic.rancher.io.registry.vue
@@ -0,0 +1,597 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pkg/sbombastic-image-vulnerability-scanner/list/sbombastic.rancher.io.registry_bak.vue b/pkg/sbombastic-image-vulnerability-scanner/list/sbombastic.rancher.io.registry_bak.vue
deleted file mode 100644
index f1b2972..0000000
--- a/pkg/sbombastic-image-vulnerability-scanner/list/sbombastic.rancher.io.registry_bak.vue
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/pkg/sbombastic-image-vulnerability-scanner/models/sbombastic.rancher.io.registry.js b/pkg/sbombastic-image-vulnerability-scanner/models/sbombastic.rancher.io.registry.js
index 66b51da..b8a2ca4 100644
--- a/pkg/sbombastic-image-vulnerability-scanner/models/sbombastic.rancher.io.registry.js
+++ b/pkg/sbombastic-image-vulnerability-scanner/models/sbombastic.rancher.io.registry.js
@@ -46,12 +46,6 @@ export default class Registry extends SteveModel {
title: this.$rootGetters['i18n/t']('imageScanner.registries.messages.registryScanFailed'),
message: e.message,
}, { root: true });
- } finally {
- if (target.refreshFn instanceof Function) {
- setTimeout(() => {
- target.refreshFn();
- }, 2000);
- }
}
},
};
diff --git a/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/ImageOverview.vue b/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/ImageOverview.vue
index 7b0a4ac..bf784c5 100644
--- a/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/ImageOverview.vue
+++ b/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/ImageOverview.vue
@@ -362,8 +362,9 @@
this.rows.push({
id: report.id,
metadata: {
- name: `${report.imageMetadata.registry}:${report.imageMetadata.tag}`
+ name: report.metadata.name,
},
+ imageMetadata: report.imageMetadata,
spec: {
isBaseImage: true,
hasAffectedPackages: false,
@@ -396,6 +397,7 @@
currRepo.images.push(
{
metadata: { name: image.metadata.name },
+ imageMetadata: image.imageMetadata,
scanResult: currImageScanResult,
}
);
@@ -408,7 +410,8 @@
cveCntByRepo: currImageScanResult,
images: [
{
- metadata: { name: image.metadata.name} ,
+ metadata: { name: image.metadata.name },
+ imageMetadata: image.imageMetadata,
scanResult: currImageScanResult,
}
]
@@ -421,6 +424,7 @@
});
return {
imageName: image.metadata.name,
+ mageMetadata: image.imageMetadata,
cveCnt: image.spec.scanResult,
}
});
diff --git a/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/RegistriesConfiguration.vue b/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/RegistriesConfiguration.vue
index 759bfbc..2f946d6 100644
--- a/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/RegistriesConfiguration.vue
+++ b/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/RegistriesConfiguration.vue
@@ -100,7 +100,7 @@
-
-
+
-
+
@@ -198,15 +198,7 @@
},
async fetch() {
- this.registriesCRD = await this.$store.dispatch('cluster/findAll', { type: RESOURCE.REGISTRY });
- if (this.$store.getters['cluster/canList'](RESOURCE.SCAN_JOB)) {
- await this.$store.dispatch('cluster/findAll', { type: RESOURCE.SCAN_JOB });
- }
- await this.loadData(false);
- clearInterval(this.keepAliveTimer);
- this.keepAliveTimer = setInterval(async () => {
- await this.loadData(true);
- }, 20 * 1000);
+ await this.loadData();
},
beforeUnmount() {
clearInterval(this.keepAliveTimer);
@@ -218,14 +210,29 @@
}, 500),
deep: true,
},
+ // registriesCRD: {
+ // async handler() {
+ // await this.preprocessData();
+ // },
+ // immediate: true,
+ // deep: true,
+ // }
},
methods: {
- async loadData(isReloading) {
+ async loadData() {
+ this.registriesCRD = await this.$store.dispatch('cluster/findAll', { type: RESOURCE.REGISTRY });
+ if (this.$store.getters['cluster/canList'](RESOURCE.SCAN_JOB)) {
+ await this.$store.dispatch('cluster/findAll', { type: RESOURCE.SCAN_JOB });
+ }
+ await this.preprocessData();
+ clearInterval(this.keepAliveTimer);
+ this.keepAliveTimer = setInterval(async () => {
+ await this.preprocessData();
+ }, 20 * 1000);
+ },
+ async preprocessData() {
this.registryStatusList = [];
this.statusSummary = {};
- if (isReloading) {
- this.registriesCRD = this.$store.getters['cluster/all'](RESOURCE.REGISTRY);
- }
this.rows = this.registriesCRD;
const summaryData = this.getSummaryData(this.registriesCRD);
this.registryStatusList = summaryData.registryStatusList;
@@ -251,7 +258,7 @@
this.selectedRows = selected || [];
},
async promptRemoveRegistry() {
- const table = this.$refs.registryTable;
+ const table = this.$refs.registryTable.$refs.table.$refs.table;
const act = findBy(table.availableActions, 'action', 'promptRemove');
if ( act ) {
table.setBulkActionOfInterest(act);
@@ -272,7 +279,7 @@
registriesCRD.forEach((rec, index) => {
// Summarize the data for Latest status updates panel
- const scanRec = _.cloneDeep(rec.scanRec || {});
+ const scanRec = rec.scanRec//_.cloneDeep(rec.scanRec || {});
console.log('scanRec', scanRec);
registryStatusList.push({
registryName: rec.metadata.name,
@@ -328,24 +335,9 @@
const scanJobs = await this.$store.dispatch(`cluster/findPage`, { type: RESOURCE.SCAN_JOB, opt });
return scanJobs;
},
- },
- computed: {
- schema() {
- return this.$store.getters['cluster/schemaFor'](RESOURCE.REGISTRY);
- },
- canPaginate() {
- const args = { id: RESOURCE.REGISTRY };
- return this.$store.getters[`cluster/paginationEnabled`]?.(args);
- },
- latestUpdateDateText() {
- return day(new Date(this.latestUpdateTime).getTime()).format('MMM D, YYYY');
- },
- latestUpdateTimeText() {
- return day(new Date(this.latestUpdateTime).getTime()).format('h:mm A');
- },
- filteredRows() {
+ filterRowsLocal(rows) {
const filters = this.debouncedFilters;
- return this.rows.filter(row => {
+ return rows.filter(row => {
const registryMatch = !filters.registrySearch ||
row.metadata?.name?.toLowerCase().includes(filters.registrySearch.toLowerCase());
@@ -367,7 +359,58 @@
return registryMatch && namespaceMatch && uriMatch && repoMatch && statusMatch;
});
- }
+ },
+ filterRowsApi(pagination) {
+ const filters = this.debouncedFilters;
+ const colFields = [{
+ field: 'metadata.name',
+ value: filters.registrySearch,
+ equals: true,
+ exact: false,
+ },
+ {
+ field: 'metadata.namespace',
+ value: filters.namespaceSearch,
+ equals: true,
+ exact: false,
+ },
+ {
+ field: 'spec.uri',
+ value: filters.uriSearch,
+ equals: true,
+ exact: false,
+ },
+ {
+ field: 'spec.repositories',
+ value: filters.repositorySearch,
+ equals: true,
+ exact: false,
+ },
+ {
+ field: 'scanRec.currStatus',
+ value: typeof filters.statusSearch === 'object' ? filters.statusSearch.value : filters.statusSearch,
+ equals: true,
+ exact: false,
+ }];
+ const colFilter = PaginationParamFilter.createMultipleFields(colFields);
+ pagination.filters.push(colFilter);
+ return pagination;
+ },
+ },
+ computed: {
+ schema() {
+ return this.$store.getters['cluster/schemaFor'](RESOURCE.REGISTRY);
+ },
+ canPaginate() {
+ const args = { id: RESOURCE.REGISTRY };
+ return this.$store.getters[`cluster/paginationEnabled`]?.(args);
+ },
+ latestUpdateDateText() {
+ return day(new Date(this.latestUpdateTime).getTime()).format('MMM D, YYYY');
+ },
+ latestUpdateTimeText() {
+ return day(new Date(this.latestUpdateTime).getTime()).format('h:mm A');
+ },
},
}
diff --git a/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/__tests__/RegistriesConfiguration.spec.ts b/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/__tests__/RegistriesConfiguration.spec.ts
index 7732780..bafc0ac 100644
--- a/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/__tests__/RegistriesConfiguration.spec.ts
+++ b/pkg/sbombastic-image-vulnerability-scanner/pages/c/_cluster/sbombastic-image-vulnerability-scanner/__tests__/RegistriesConfiguration.spec.ts
@@ -1,5 +1,6 @@
import { shallowMount, flushPromises } from '@vue/test-utils';
import registries from '../RegistriesConfiguration.vue';
+import { RESOURCE } from '@pkg/types';
describe('registries.vue', () => {
let storeMock: any;
@@ -50,9 +51,9 @@ describe('registries.vue', () => {
expect(wrapper.find('.title').text()).toBe('imageScanner.registries.title');
});
- it('calls loadData when refresh is clicked', async () => {
+ it('calls preprocessData when refresh is clicked', async () => {
const wrapper = factory();
- const refreshSpy = jest.spyOn(wrapper.vm, 'loadData').mockResolvedValue();
+ const refreshSpy = jest.spyOn(wrapper.vm, 'preprocessData').mockResolvedValue();
await wrapper.find('button[aria-label="Refresh data"]').trigger('click');
expect(refreshSpy).toHaveBeenCalled();
@@ -72,14 +73,11 @@ describe('registries.vue', () => {
});
});
- it('updates rows after loadData', async () => {
+ it('updates rows after preprocessData', async () => {
const wrapper = factory();
- storeMock.getters['cluster/all']
- .mockReturnValueOnce([{ metadata: { name: 'reg1', namespace: 'ns1' }, spec: {} }]) // registries
- .mockReturnValueOnce([]); // scanJobs
-
- await wrapper.vm.loadData(true);
+ (wrapper.vm.registriesCRD as any[]) = [{ metadata: { name: 'reg1', namespace: 'ns1' }, spec: {} }]; // registries
+ await wrapper.vm.preprocessData();
await flushPromises();
// Ensure rows is typed as any[] for test purposes
@@ -108,26 +106,6 @@ describe('registries.vue', () => {
wrapper.vm.filterByStatus('pending');
expect(wrapper.vm.filters.statusSearch).toBe('pending');
});
- it('filters rows correctly by registry, namespace, uri, repository, and status', () => {
- const wrapper = factory();
- (wrapper.vm.rows as any[]) = [{
- metadata: { name: 'reg1', namespace: 'ns1' },
- spec: { uri: 'http://my-uri', repositories: ['repo1'] },
- scanRec: { currStatus: 'complete' }
- }];
-
- wrapper.vm.filters = {
- registrySearch: 'reg1',
- namespaceSearch: 'ns1',
- uriSearch: 'uri',
- repositorySearch: 'repo1',
- statusSearch: 'complete'
- };
-
- const filtered = wrapper.vm.filteredRows;
- expect(filtered).toHaveLength(1);
- expect(filtered[0].metadata.name).toBe('reg1');
- });
it('formats latest update time correctly', () => {
const wrapper = factory();
wrapper.vm.latestUpdateTime = new Date('2023-01-01T12:34:56Z');
@@ -155,4 +133,147 @@ describe('registries.vue', () => {
expect(storeMock.dispatch).toHaveBeenCalledWith('cluster/findPage', expect.any(Object));
expect(result).toEqual([scanJob]);
});
+
+ it('filters rows locally based on filters', () => {
+ const wrapper = factory();
+ wrapper.vm.debouncedFilters = {
+ registrySearch: 'reg1',
+ namespaceSearch: '',
+ uriSearch: '',
+ repositorySearch: '',
+ statusSearch: 'any'
+ };
+
+ const rows = [
+ { metadata: { name: 'reg1', namespace: 'ns1' }, spec: { uri: 'u1', repositories: [] }, scanRec: { currStatus: 'pending' } },
+ { metadata: { name: 'reg2', namespace: 'ns2' }, spec: { uri: 'u2', repositories: [] }, scanRec: { currStatus: 'complete' } }
+ ];
+
+ const result = wrapper.vm.filterRowsLocal(rows);
+ expect(result).toHaveLength(1);
+ expect(result[0].metadata.name).toBe('reg1');
+ });
+
+ it('applies API filters correctly in filterRowsApi', () => {
+ const wrapper = factory();
+ wrapper.vm.debouncedFilters = {
+ registrySearch: 'r1',
+ namespaceSearch: 'ns1',
+ uriSearch: 'uri',
+ repositorySearch: 'repo',
+ statusSearch: 'pending',
+ };
+
+ const pagination = { filters: [] };
+ const result = wrapper.vm.filterRowsApi(pagination);
+
+ expect(result.filters.length).toBeGreaterThan(0);
+ expect(result.filters[0]).toBeDefined();
+ });
+
+ it('preprocessData computes status summary and registry list', async () => {
+ const wrapper = factory();
+ (wrapper.vm.registriesCRD as any[]) = [
+ { metadata: { name: 'reg1', namespace: 'ns1', creationTimestamp: new Date().toISOString() },
+ spec: { uri: 'http://uri' },
+ scanRec: { currStatus: 'failed', previousStatus: 'pending', lastTransitionTime: new Date().toISOString() }
+ }
+ ];
+
+ await wrapper.vm.preprocessData();
+
+ expect(wrapper.vm.registryStatusList.length).toBeGreaterThan(0);
+ expect(wrapper.vm.statusSummary.failed).toBe(1);
+ });
+
+ it('calls store dispatches and preprocessData in loadData', async () => {
+ const wrapper = factory();
+ const preprocessSpy = jest.spyOn(wrapper.vm, 'preprocessData').mockResolvedValue();
+
+ storeMock.dispatch.mockResolvedValueOnce([{ id: 'reg1' }]); // for REGISTRY
+ storeMock.dispatch.mockResolvedValueOnce([{ id: 'scanJob1' }]); // for SCAN_JOB
+
+ await wrapper.vm.loadData();
+
+ expect(storeMock.dispatch).toHaveBeenCalledWith('cluster/findAll', { type: 'sbombastic.rancher.io.registry' });
+ expect(storeMock.dispatch).toHaveBeenCalledWith('cluster/findAll', { type: 'sbombastic.rancher.io.scanjob' });
+ expect(preprocessSpy).toHaveBeenCalled();
+ });
+
+ it('skips SCAN_JOB fetch if canList returns false', async () => {
+ storeMock.getters['cluster/canList'] = jest.fn().mockReturnValue(false);
+ const wrapper = factory();
+ const preprocessSpy = jest.spyOn(wrapper.vm, 'preprocessData').mockResolvedValue();
+
+ storeMock.dispatch.mockResolvedValueOnce([{ id: 'reg1' }]); // only registry
+
+ await wrapper.vm.loadData();
+
+ expect(storeMock.dispatch).toHaveBeenCalledTimes(1); // only REGISTRY
+ expect(preprocessSpy).toHaveBeenCalled();
+ });
+
+ it('clears keepAliveTimer on beforeUnmount', () => {
+ const wrapper = factory();
+ const clearSpy = jest.spyOn(global, 'clearInterval');
+
+ wrapper.vm.keepAliveTimer = setInterval(() => {}, 1000);
+ if (wrapper.vm.$options.beforeUnmount && wrapper.vm.$options.beforeUnmount[0]) {
+ wrapper.vm.$options.beforeUnmount[0].call(wrapper.vm);
+ }
+ });
+
+ it('updates selectedRows when onSelectionChange is called', () => {
+ const wrapper = factory();
+ wrapper.vm.onSelectionChange([{ id: 'row1' }]);
+ expect(wrapper.vm.selectedRows).toEqual([{ id: 'row1' }]);
+ });
+
+ it('matches by name and namespace in filterRowsLocal', () => {
+ const wrapper = factory();
+ wrapper.vm.debouncedFilters = {
+ registrySearch: 'reg1',
+ namespaceSearch: 'ns1',
+ uriSearch: '',
+ repositorySearch: '',
+ statusSearch: 'any'
+ };
+
+ const rows = [
+ { metadata: { name: 'reg1', namespace: 'ns1' }, spec: { uri: '', repositories: [] }, scanRec: {} },
+ { metadata: { name: 'reg2', namespace: 'ns2' }, spec: { uri: '', repositories: [] }, scanRec: {} }
+ ];
+
+ const result = wrapper.vm.filterRowsLocal(rows);
+ expect(result).toHaveLength(1);
+ expect(result[0].metadata.namespace).toBe('ns1');
+ });
+
+ it('uses statusSearch.value if statusSearch is an object', () => {
+ const wrapper = factory();
+ wrapper.vm.debouncedFilters = {
+ registrySearch: '',
+ namespaceSearch: '',
+ uriSearch: '',
+ repositorySearch: '',
+ statusSearch: { value: 'pending' },
+ };
+
+ const pagination = { filters: [] };
+ const result = wrapper.vm.filterRowsApi(pagination);
+
+ // stringify and search for the filter field/value
+ const serialized = JSON.stringify(result.filters);
+ expect(serialized).toContain('"field":"scanRec.currStatus"');
+ expect(serialized).toContain('"value":"pending"');
+ });
+
+ it('returns canPaginate from store getter', () => {
+ storeMock.getters['cluster/paginationEnabled'] = jest.fn().mockReturnValue(true);
+ const wrapper = factory();
+
+ expect(wrapper.vm.canPaginate).toBe(true);
+ expect(storeMock.getters['cluster/paginationEnabled']).toHaveBeenCalledWith({ id: 'sbombastic.rancher.io.registry' });
+ });
+
});
\ No newline at end of file
diff --git a/pkg/sbombastic-image-vulnerability-scanner/routes/sbombastic-image-vulnerability-scanner-routes.ts b/pkg/sbombastic-image-vulnerability-scanner/routes/sbombastic-image-vulnerability-scanner-routes.ts
index deff783..c26ef63 100644
--- a/pkg/sbombastic-image-vulnerability-scanner/routes/sbombastic-image-vulnerability-scanner-routes.ts
+++ b/pkg/sbombastic-image-vulnerability-scanner/routes/sbombastic-image-vulnerability-scanner-routes.ts
@@ -30,11 +30,11 @@ const routes = [
path: `/c/:cluster/${PRODUCT_NAME}/${PAGE.IMAGES}/:id`,
component: ImageDetails,
},
- {
- name: `c-cluster-${PRODUCT_NAME}-${PAGE.VULNERABILITIES}`,
- path: `/c/:cluster/${PRODUCT_NAME}/${PAGE.VULNERABILITIES}`,
- component: Vulnerabilities,
- },
+ // {
+ // name: `c-cluster-${PRODUCT_NAME}-${PAGE.VULNERABILITIES}`,
+ // path: `/c/:cluster/${PRODUCT_NAME}/${PAGE.VULNERABILITIES}`,
+ // component: Vulnerabilities,
+ // },
{
name: `c-cluster-${PRODUCT_NAME}-${PAGE.REGISTRIES}`,
path: `/c/:cluster/${PRODUCT_NAME}/${PAGE.REGISTRIES}`,