Skip to content

Commit 8edc93a

Browse files
authored
Version 3.5.3: Add shortcuts to the repo table (#55)
1 parent bc8fa07 commit 8edc93a

File tree

6 files changed

+90
-17
lines changed

6 files changed

+90
-17
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "resc-frontend",
3-
"version": "3.5.2",
3+
"version": "3.5.3",
44
"author": "ABN AMRO Bank",
55
"description": "Repository Scanner Frontend",
66
"license": "MIT",

src/components/Filters/ScanFindingsFilter.vue

+8
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ import { ref, watch, type Ref } from 'vue';
8080
import { useRoute, useRouter } from 'vue-router';
8181
import type { FindingStatus, RepositoryRead, ScanRead } from '@/services/shema-to-types';
8282
import { BFormCheckbox, BFormGroup } from 'bootstrap-vue-next';
83+
import { onKeyStroke } from '@vueuse/core';
8384
8485
const ruleFilterChildComponent = ref();
8586
const ruleTagsFilterChildComponent = ref();
@@ -351,5 +352,12 @@ watch(
351352
}
352353
},
353354
);
355+
356+
/* istanbul ignore next @preserve */
357+
onKeyStroke('p', () => {
358+
includePreviousScans.value = !includePreviousScans.value;
359+
togglePreviousScans();
360+
handleToggleButtonClick();
361+
});
354362
</script>
355363
<style src="vue-multiselect/dist/vue-multiselect.css"></style>

src/components/Help/KeybindingModal.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ const keybindings = ref([
8585
combination: [['&UnderBracket;']],
8686
},
8787
{
88-
effect: 'Open commit url.',
88+
effect: 'Open commit url/Open repository.',
8989
combination: [['o']],
9090
},
9191
{

src/views/RepositoriesPanel.vue

+59-14
Original file line numberDiff line numberDiff line change
@@ -24,42 +24,45 @@
2424

2525
<div class="p-3" v-if="hasRecords">
2626
<BTable
27+
ref="repositoriesTable"
2728
id="repositories-table"
2829
:items="repositoryList"
2930
:fields="fields"
3031
:current-page="1"
3132
:per-page="0"
33+
:selectable="true"
34+
:select-mode="'single'"
3235
primary-key="id_"
3336
v-model="currentItems"
3437
responsive
3538
small
3639
head-variant="light"
3740
:tbody-tr-class="rowClass"
38-
@row-clicked="goToScanFindings"
41+
@row-clicked="handleRowClicked"
3942
>
4043
<!-- Repository Column -->
4144
<template #cell(repository_name)="data">
42-
{{ (data.item as RepositoryEnrichedRead).repository_name }}
45+
{{ data.item.repository_name }}
4346
</template>
4447

4548
<template #cell(vcs_provider)="data">
46-
{{ formatVcsProvider((data.item as RepositoryEnrichedRead).vcs_provider) }}
49+
{{ formatVcsProvider(data.item.vcs_provider) }}
4750
</template>
4851

4952
<template #cell(last_scan_timestamp)="data">
50-
{{ formatDate((data.item as RepositoryEnrichedRead).last_scan_timestamp ?? '') }}
53+
{{ formatDate(data.item.last_scan_timestamp ?? '') }}
5154
</template>
5255

5356
<!-- Health Bar Column -->
5457
<template #cell(findings)="data">
5558
<HealthBar
56-
:truePositive="(data.item as RepositoryEnrichedRead).true_positive"
57-
:falsePositive="(data.item as RepositoryEnrichedRead).false_positive"
58-
:notAnalyzed="(data.item as RepositoryEnrichedRead).not_analyzed"
59-
:notAccessible="(data.item as RepositoryEnrichedRead).not_accessible"
60-
:clarificationRequired="(data.item as RepositoryEnrichedRead).clarification_required"
61-
:outdated="(data.item as RepositoryEnrichedRead).outdated"
62-
:totalCount="(data.item as RepositoryEnrichedRead).total_findings_count"
59+
:truePositive="data.item.true_positive"
60+
:falsePositive="data.item.false_positive"
61+
:notAnalyzed="data.item.not_analyzed"
62+
:notAccessible="data.item.not_accessible"
63+
:clarificationRequired="data.item.clarification_required"
64+
:outdated="data.item.outdated"
65+
:totalCount="data.item.total_findings_count"
6366
/>
6467
</template>
6568
</BTable>
@@ -97,6 +100,7 @@ import { onKeyStroke } from '@vueuse/core';
97100
import { shouldIgnoreKeystroke } from '@/utils/keybind-utils';
98101
99102
const loadedData = ref(false);
103+
const repositoriesTable = ref();
100104
const router = useRouter();
101105
102106
type TableRepositoryEnrichedRead = RepositoryEnrichedRead & TableItem;
@@ -113,6 +117,7 @@ const repositoryFilter = ref(undefined as string | undefined);
113117
const projectFilter = ref(undefined as string | undefined);
114118
const projectNames = ref([] as string[]);
115119
const repositoryNames = ref([] as string[]);
120+
const selectedIndex = ref(undefined as number | undefined);
116121
const includeZeroFindingRepos = ref(false);
117122
const includeDeletedRepositories = ref(false);
118123
const onlyIfHasUntriagedFindings = ref(false);
@@ -192,9 +197,13 @@ function handlePageSizeChange(pageSize: number) {
192197
fetchPaginatedRepositories();
193198
}
194199
195-
function goToScanFindings(record: TableItem) {
196-
// Casting back to RepositoryEnrichedRead
197-
const recordItem = record as RepositoryEnrichedRead;
200+
function handleRowClicked(_row: TableItem, index: number) {
201+
selectedIndex.value = index;
202+
goToScanFindings();
203+
}
204+
205+
function goToScanFindings() {
206+
const recordItem = getCurrentRepositorySelected() as RepositoryEnrichedRead;
198207
if (recordItem.last_scan_id) {
199208
const routeData = router.resolve({
200209
name: 'ScanFindings',
@@ -243,6 +252,7 @@ function fetchPaginatedRepositories() {
243252
loadedData.value = true;
244253
})
245254
.catch((error) => {
255+
/* istanbul ignore next @preserve */
246256
AxiosConfig.handleError(error);
247257
});
248258
}
@@ -261,6 +271,7 @@ function fetchDistinctProjects() {
261271
}
262272
})
263273
.catch((error) => {
274+
/* istanbul ignore next @preserve */
264275
AxiosConfig.handleError(error);
265276
});
266277
}
@@ -279,15 +290,49 @@ function fetchDistinctRepositories() {
279290
}
280291
})
281292
.catch((error) => {
293+
/* istanbul ignore next @preserve */
282294
AxiosConfig.handleError(error);
283295
});
284296
}
285297
298+
function getCurrentRepositorySelected(): RepositoryEnrichedRead | undefined {
299+
if (selectedIndex.value === undefined) {
300+
return undefined;
301+
}
302+
303+
return repositoryList.value[selectedIndex.value];
304+
}
305+
306+
function selectUp(): boolean {
307+
selectedIndex.value = Math.max(0, (selectedIndex.value ?? 1) - 1);
308+
repositoriesTable.value.clearSelected();
309+
repositoriesTable.value.selectRow(selectedIndex.value);
310+
return true;
311+
}
312+
313+
function selectDown(): boolean {
314+
selectedIndex.value = ((selectedIndex.value ?? -1) + 1) % repositoryList.value.length;
315+
repositoriesTable.value.clearSelected();
316+
repositoriesTable.value.selectRow(selectedIndex.value);
317+
return true;
318+
}
319+
286320
/* istanbul ignore next @preserve */
287321
onKeyStroke('r', () => !shouldIgnoreKeystroke() && fetchPaginatedRepositories(), {
288322
eventName: 'keydown',
289323
});
290324
325+
/* istanbul ignore next @preserve */
326+
onKeyStroke(['ArrowDown', 'j', 'J'], () => !shouldIgnoreKeystroke() && selectDown(), {
327+
eventName: 'keydown',
328+
});
329+
/* istanbul ignore next @preserve */
330+
onKeyStroke(['ArrowUp', 'k', 'K'], () => !shouldIgnoreKeystroke() && selectUp(), {
331+
eventName: 'keydown',
332+
});
333+
/* istanbul ignore next @preserve */
334+
onKeyStroke('o', () => !shouldIgnoreKeystroke() && goToScanFindings(), { eventName: 'keydown' });
335+
291336
onMounted(() => {
292337
fetchDistinctProjects();
293338
fetchDistinctRepositories();

src/views/RuleAnalysis.vue

+5
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ function fetchPaginatedDetailedFindings() {
153153
loadedData.value = true;
154154
})
155155
.catch((error) => {
156+
/* istanbul ignore next @preserve */
156157
AxiosConfig.handleError(error);
157158
});
158159
}
@@ -189,6 +190,7 @@ function fetchDistinctProjects() {
189190
}
190191
})
191192
.catch((error) => {
193+
/* istanbul ignore next @preserve */
192194
AxiosConfig.handleError(error);
193195
});
194196
}
@@ -208,6 +210,7 @@ function fetchDistinctRepositories() {
208210
}
209211
})
210212
.catch((error) => {
213+
/* istanbul ignore next @preserve */
211214
AxiosConfig.handleError(error);
212215
});
213216
}
@@ -236,6 +239,7 @@ function fetchRulePackVersionsWhenRedirectedFromRuleMetricsPage() {
236239
}
237240
})
238241
.catch((error) => {
242+
/* istanbul ignore next @preserve */
239243
AxiosConfig.handleError(error);
240244
});
241245
}
@@ -258,6 +262,7 @@ function fetchRulePackVersions() {
258262
fetchPaginatedDetailedFindings();
259263
})
260264
.catch((error) => {
265+
/* istanbul ignore next @preserve */
261266
AxiosConfig.handleError(error);
262267
});
263268
}

tests/unit/views/RepositoriesPanel.spec.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ vi.mock('vue-router', async () => {
2727
useRoute: vi.fn(),
2828
useRouter: vi.fn(() => ({
2929
push: () => {},
30+
resolve: () => {
31+
return { href: 'link' };
32+
},
3033
})),
3134
};
3235
});
@@ -51,7 +54,7 @@ describe('RepositoriesPanel tests', () => {
5154
});
5255
}
5356

54-
it('Given a RepositoriesPanel then RepositoriesPanel will be displayed', () => {
57+
it('Given a RepositoriesPanel then RepositoriesPanel will be displayed', async () => {
5558
axios.get.mockResolvedValueOnce({ data: allProjects });
5659
axios.get.mockResolvedValueOnce({ data: allRepos });
5760
axios.get.mockResolvedValueOnce(vcs_providers);
@@ -72,5 +75,17 @@ describe('RepositoriesPanel tests', () => {
7275
axios.get.mockResolvedValueOnce({ data: allRepos });
7376
axios.get.mockResolvedValueOnce({ data: repositories });
7477
wrapper.vm.handleFilterChange(['AZURE_DEVOPS'], undefined, undefined);
78+
expect(wrapper.vm.getCurrentRepositorySelected()).toBe(undefined);
79+
await wrapper.vm.$nextTick();
80+
await wrapper.vm.$nextTick();
81+
await wrapper.vm.$nextTick();
82+
await wrapper.vm.$nextTick();
83+
expect(() => wrapper.vm.selectUp()).not.toThrow();
84+
expect(() => wrapper.vm.selectDown()).not.toThrow();
85+
expect(wrapper.vm.selectedIndex).toBe(1);
86+
expect(() => wrapper.vm.handleRowClicked('', 0)).not.toThrow();
87+
expect(wrapper.vm.selectedIndex).toBe(0);
88+
expect(wrapper.vm.getCurrentRepositorySelected()).not.toBe(undefined);
89+
expect(() => wrapper.vm.goToScanFindings()).not.toThrow();
7590
});
7691
});

0 commit comments

Comments
 (0)