Skip to content

Commit 810545f

Browse files
authored
Merge pull request #97 from os-checker/feat/pkg-info
feat: 添加 info 页面来展示 Package 和测例信息
2 parents 311004d + 59ddf85 commit 810545f

File tree

5 files changed

+325
-2
lines changed

5 files changed

+325
-2
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<template>
2+
3+
<DataTable :value="data" showGridlines>
4+
5+
<Column field="idx" header="Idx" />
6+
<Column field="binary_name" header="Binary Name" />
7+
<Column field="kind" header="Kind" />
8+
<Column field="test" header="Test Case" />
9+
10+
</DataTable>
11+
12+
</template>
13+
14+
<script setup lang="ts">
15+
import type { Test } from '~/shared/info';
16+
17+
const { tests } = defineProps<{ tests: Test[] }>();
18+
19+
const data = computed(() => {
20+
let idx = 0;
21+
return tests.map(test => {
22+
return test.testcases.map(t => {
23+
idx += 1;
24+
return {
25+
idx,
26+
binary_name: test.binary_name,
27+
kind: test.kind,
28+
test: t,
29+
}
30+
})
31+
}).flat();
32+
});
33+
</script>

os-checks/components/TopBar.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
<Button title="Github Workflows" icon="pi pi-bell" />
2424
</NuxtLink>
2525

26+
<NuxtLink to="/info">
27+
<Button title="Package & Testcase Infomation" icon="pi pi-microchip" />
28+
</NuxtLink>
29+
2630
</div>
2731

2832
<div class="topBarRight">

os-checks/pages/index.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,13 @@ const progressRatio = computed(() => {
147147
}
148148
149149
.nav-link {
150-
color: #336ad7;
150+
color: var(--p-indigo-500);
151151
/* 统一的链接颜色 */
152152
text-decoration: none;
153153
}
154154
155155
.nav-link.router-link-active {
156-
color: #336ad7;
156+
color: var(--p-indigo-500);
157157
/* 重置激活链接的颜色 */
158158
}
159159

os-checks/pages/info.vue

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
<template>
2+
<div>
3+
<DataTable :value="data" tableStyle="min-width: 50rem;" scrollable scrollHeight="800px" showGridlines
4+
selectionMode="single" v-model:selection="selectedPkg" v-model:filters="filters"
5+
:globalFilterFields="['user', 'repo', 'pkg', 'description', 'categories', 'os_categories']" removableSort
6+
sortMode="multiple" paginator :rows="10" :rowsPerPageOptions="[5, 10, 20, 50, 100, 200, 1000]">
7+
8+
<template #header>
9+
<div style="display: flex; justify-content: space-between;">
10+
<div style="display: flex; gap: 20px;">
11+
<MultiSelect v-model="selectedCategories" display="chip" :options="categories" filter :maxSelectedLabels="4"
12+
placeholder="Select Categories" />
13+
14+
<MultiSelect v-model="selectedOSCategories" display="chip" :options="os_categories" filter
15+
:maxSelectedLabels="4" placeholder="Select OS Categories" />
16+
17+
<MultiSelect v-model="selectedAuthors" display="chip" :options="authors" filter :maxSelectedLabels="4"
18+
placeholder="Select Authors" />
19+
</div>
20+
21+
<div style="width: 30%">
22+
<IconField>
23+
<InputIcon>
24+
<i class="pi pi-search" />
25+
</InputIcon>
26+
<InputText style="width: 100%" v-model="filters['global'].value"
27+
placeholder="Search in all text columns" />
28+
</IconField>
29+
</div>
30+
31+
</div>
32+
</template>
33+
34+
<Column frozen sortable field="idx" header="Idx" />
35+
<Column frozen sortable field="user" header="User" style="min-width: 150px;" />
36+
<Column frozen sortable field="repo" header="Repo" style="min-width: 180px;" />
37+
<Column frozen sortable field="pkg" header="Package" style="min-width: 200px;" />
38+
39+
<Column sortable field="version" header="Version" style="text-align: center;" />
40+
<Column sortable field="dependencies" header="Depen-dencies" style="text-align: center;" />
41+
42+
<Column sortable field="testcases" header="Test Cases" style="text-align: center;" />
43+
44+
<Column sortable field="tests" header="Tests" style="text-align: center;" />
45+
<Column sortable field="examples" header="Examples" style="text-align: center;" />
46+
<Column sortable field="benches" header="Benches" style="text-align: center;" />
47+
48+
<Column sortable field="categories" header="Categories" style="min-width: 210px;">
49+
<template #body="{ data: { categories } }">
50+
<div v-for="tag of categories">
51+
<Tag severity="warn" :value="tag" style="margin-bottom: 5px;"></Tag>
52+
</div>
53+
</template>
54+
</Column>
55+
56+
<Column sortable field="os_categories" header="OS Categories">
57+
<template #body="{ data: { os_categories } }">
58+
<div v-for="tag of os_categories">
59+
<Tag severity="warn" :value="tag" style="margin-bottom: 5px;"></Tag>
60+
</div>
61+
</template>
62+
</Column>
63+
64+
<Column field="description" header="Description" style="min-width: 400px;" />
65+
66+
<Column sortable field="author" header="Author" style="min-width: 400px;">
67+
<template #body="{ data: { author } }">
68+
<div v-for="tag of author">
69+
<Tag severity="info" :value="tag" style="margin-bottom: 5px;"></Tag>
70+
</div>
71+
</template>
72+
</Column>
73+
74+
</DataTable>
75+
76+
<Dialog v-model:visible="dialogShow" modal :style="{ width: '70%' }">
77+
<template #header>
78+
<span style="display: inline-flex; justify-content: center; gap: 40px; font-size: larger; font-weight: bold;">
79+
<div>
80+
<NuxtLink :to="dialogHeader?.repo_url" target="_blank">
81+
<Tag icon="pi pi-github" severity="info" style="font-weight: bold;">
82+
{{ dialogHeader?.repo }}
83+
</Tag>
84+
</NuxtLink>
85+
</div>
86+
87+
<div>Test Cases of package
88+
<span style="color: var(--p-emerald-500); margin-right: 5px;">{{ dialogHeader?.pkg_name }}</span>
89+
</div>
90+
</span>
91+
</template>
92+
93+
<div>
94+
<div class="dialog-header">
95+
Description: <b style="color: var(--p-emerald-500)">{{ dialogHeader?.pkg.description }}</b>
96+
</div>
97+
<div class="dialog-header">
98+
Categories:
99+
<Tag v-for="tag of dialogHeader?.pkg.categories" severity="warn" :value="tag" style="margin-right: 6px;" />
100+
</div>
101+
<div class="dialog-header">
102+
OS Categories:
103+
<Tag v-for="tag of dialogHeader?.pkg.os_categories" severity="warn" :value="tag" style="margin-right: 6px;" />
104+
</div>
105+
<div class="dialog-header">
106+
Authors:
107+
<Tag v-for="tag of dialogHeader?.pkg.author" severity="info" :value="tag" style="margin-bottom: 5px;"></Tag>
108+
</div>
109+
110+
<InfoTestCases :tests="testCases" />
111+
</div>
112+
</Dialog>
113+
</div>
114+
</template>
115+
116+
<script setup lang="ts">
117+
import type { Pkg, PkgInfo, Test } from '~/shared/info';
118+
import { FilterMatchMode } from '@primevue/core/api';
119+
120+
// interactive filter/search inputs
121+
const filters = ref<any>({
122+
global: { value: null, matchMode: FilterMatchMode.CONTAINS },
123+
});
124+
125+
const summaries = ref<PkgInfo[]>([]);
126+
127+
githubFetch<PkgInfo[]>({
128+
path: "plugin/cargo/info/summaries.json"
129+
}).then(val => {
130+
summaries.value = val;
131+
});
132+
133+
const summaryTable = computed<SummaryTable[]>(() => {
134+
const value = summaries.value.map(val => {
135+
return Object.entries(val.pkgs).map(([name, pkg]) => {
136+
return {
137+
idx: 0,
138+
user: val.user,
139+
repo: val.repo,
140+
pkg: name,
141+
version: pkg.version,
142+
dependencies: pkg.dependencies || null,
143+
testcases: pkg.testcases ? pkg.testcases.pkg_tests_count : null,
144+
tests: pkg.tests || null,
145+
examples: pkg.examples || null,
146+
benches: pkg.benches || null,
147+
author: pkg.author.length === 0 ? null : pkg.author,
148+
description: pkg.description,
149+
categories: pkg.categories.length === 0 ? null : pkg.categories,
150+
os_categories: pkg.os_categories.length === 0 ? null : pkg.os_categories,
151+
}
152+
})
153+
}).flat();
154+
155+
return value.sort((a, b) => {
156+
const a_test = a.testcases ?? 0;
157+
const b_test = b.testcases ?? 0;
158+
if (a_test < b_test) {
159+
return 1;
160+
} else if (a_test > b_test) {
161+
return -1;
162+
} else if (a.user < b.user) {
163+
return -1;
164+
} else if (a.user > b.user) {
165+
return 1;
166+
} else if (a.repo < b.repo) {
167+
return -1;
168+
} else if (a.repo > b.repo) {
169+
return 1;
170+
} else if (a.pkg < b.pkg) {
171+
return -1;
172+
} else if (a.pkg > b.pkg) {
173+
return 1;
174+
}
175+
return 0;
176+
}).map((val, idx) => {
177+
val.idx = idx + 1;
178+
return val;
179+
});
180+
});
181+
182+
type SummaryTable = { idx: number; user: string; repo: string; pkg: string; version: string; dependencies: number | null; testcases: number | null; tests: number | null; examples: number | null; benches: number | null; author: string[] | null; description: string[]; categories: string[] | null; os_categories: string[] | null; };
183+
const data = ref<SummaryTable[]>([]);
184+
watch(summaryTable, (val) => data.value = val);
185+
186+
const categories = computed(() => [...new Set(summaryTable.value.map(val => val.categories).flat().filter(c => c))].sort());
187+
const os_categories = computed(() => [...new Set(summaryTable.value.map(val => val.os_categories).flat().filter(c => c))].sort());
188+
const authors = computed(() => [...new Set(summaryTable.value.map(val => val.author).flat().filter(c => c))].sort());
189+
const selectedCategories = ref<string[]>([]);
190+
const selectedOSCategories = ref<string[]>([]);
191+
const selectedAuthors = ref<string[]>([]);
192+
watchEffect(() => {
193+
const cat = selectedCategories.value;
194+
const os_cat = selectedOSCategories.value;
195+
const au = selectedAuthors.value;
196+
197+
const is_empty_cat = cat.length === 0;
198+
const is_empty_os_cat = os_cat.length === 0;
199+
const is_empty_au = au.length === 0;
200+
201+
// reset
202+
if (is_empty_cat && is_empty_os_cat && is_empty_au) {
203+
data.value = summaryTable.value;
204+
return;
205+
}
206+
207+
data.value = summaryTable.value.filter(val => {
208+
const find_cat = cat.find(c => val.categories?.find(vc => vc === c));
209+
const find_os_cat = os_cat.find(o => val.os_categories?.find(vo => vo === o));
210+
const find_au = au.find(a => val.author?.find(va => va === a));
211+
212+
return (is_empty_cat ? true : find_cat) && (is_empty_os_cat ? true : find_os_cat) && (is_empty_au ? true : find_au)
213+
}).map((x, idx) => {
214+
x.idx = idx + 1;
215+
return x;
216+
});
217+
});
218+
219+
const dialogShow = ref(false);
220+
const dialogHeader = ref<{ repo: string, repo_url: string, pkg_name: string, pkg: Pkg } | null>();
221+
const testCases = ref<Test[]>([]);
222+
223+
type SelectedRow = { user: string, repo: string, pkg: string, testcases: number };
224+
const selectedPkg = ref<SelectedRow | null>(null);
225+
watch(selectedPkg, val => {
226+
227+
if (!val?.testcases) { return; }
228+
229+
// for now, pop up a dialog to display testcases only if any
230+
dialogShow.value = true;
231+
232+
const pkg = summaries.value
233+
.find(summary => summary.user === val.user && summary.repo === val.repo)
234+
?.pkgs[val.pkg];
235+
236+
if (!pkg?.testcases) { return; }
237+
238+
const repo = `${val.user}/${val.repo}`;
239+
const repo_url = `https://github.com/${repo}`;
240+
dialogHeader.value = { repo, repo_url, pkg_name: val.pkg, pkg };
241+
242+
testCases.value = pkg.testcases.tests;
243+
});
244+
245+
246+
</script>
247+
248+
<style lang="css">
249+
.dialog-header {
250+
margin-bottom: 10px;
251+
}
252+
</style>

os-checks/shared/info.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
export type PkgInfo = {
2+
user: string,
3+
repo: string,
4+
pkgs: { [key: string]: Pkg }
5+
}
6+
7+
export type Pkg = {
8+
version: string,
9+
dependencies: number,
10+
lib: boolean,
11+
bin: boolean,
12+
testcases: TestCases | null,
13+
tests: number,
14+
examples: number,
15+
benches: number,
16+
author: string[]
17+
description: string[],
18+
categories: string[]
19+
os_categories: string[],
20+
}
21+
22+
export type TestCases = {
23+
tests: Test[],
24+
pkg_tests_count: number,
25+
workspace_tests_count: number,
26+
}
27+
28+
export type Test = {
29+
id: string,
30+
kind: string,
31+
binary_name: string,
32+
binary_path: string,
33+
testcases: string[]
34+
}

0 commit comments

Comments
 (0)