Skip to content

Commit 7363ea4

Browse files
feat: Reduce clutter from the dashboard
- Design is inspired by the suggestion from @aronhoyer as part of #402
1 parent 9be8624 commit 7363ea4

File tree

11 files changed

+1895
-467
lines changed

11 files changed

+1895
-467
lines changed
Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { describe, it, expect, vi } from 'vitest';
1+
import { describe, expect, it, vi } from 'vitest';
22
import FailureGridCell from '@/components/FailureGridCell.vue';
3-
import { mount } from '../test-utils';
4-
import { getRelativeTime } from '@/services/utils';
3+
import { shallowMount } from '@vue/test-utils';
4+
import GridCell from '@/components/GridCell.vue';
55

66
describe('<FailureGridCell />', () => {
77
vi.mock('@/services/utils', () => {
@@ -13,19 +13,31 @@ describe('<FailureGridCell />', () => {
1313
it('should render grid cell', () => {
1414
const createdAt = '2022-08-15T02:20:34Z';
1515

16-
const wrapper = mount(FailureGridCell, {
16+
const name = 'test name';
17+
const url = 'https://test.com';
18+
const wrapper = shallowMount(FailureGridCell, {
1719
props: {
1820
content: {
1921
id: '1234',
20-
name: 'test name',
21-
url: 'https://test.com',
22+
name,
23+
url,
2224
createdAt
2325
}
2426
}
2527
});
2628

27-
expect(wrapper.html()).toMatchSnapshot();
28-
expect(getRelativeTime).toHaveBeenCalledOnce();
29-
expect(getRelativeTime).toHaveBeenCalledWith(createdAt);
29+
const gridCellComponent = wrapper.findComponent(GridCell);
30+
31+
expect(gridCellComponent.exists()).toBeTruthy();
32+
expect(gridCellComponent.props()).toEqual({
33+
displayToggleVisibility: false,
34+
hidden: false,
35+
inProgress: false,
36+
lastExecutedTime: createdAt,
37+
name,
38+
showRelativeTime: true,
39+
status: 'failure',
40+
url
41+
});
3042
});
3143
});
Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { describe, it, expect } from 'vitest';
2-
import { mount } from '../test-utils';
1+
import { describe, expect, it } from 'vitest';
2+
import { mount, requestAnimationFrameAsPromise, retryUntil } from '../test-utils';
33
import GridCell from '@/components/GridCell.vue';
44

55
describe('<GridCell />', () => {
@@ -9,33 +9,101 @@ describe('<GridCell />', () => {
99
lastExecutedTime: '2022-08-16T03:45:02.000Z'
1010
};
1111

12-
it('should render grid cell', () => {
13-
const wrapper = mount(GridCell, {
14-
props: { ...defaultProps }
12+
const rootId = `${defaultProps.name.replaceAll(/[\\:\s]/g, '-')}`;
13+
14+
it.each([
15+
[undefined, undefined, undefined, undefined, undefined],
16+
[false, undefined, undefined, undefined, undefined],
17+
[true, undefined, undefined, undefined, undefined],
18+
[false, 'success', undefined, undefined, undefined],
19+
[false, 'SUCCESS', undefined, undefined, undefined],
20+
[false, 'unknown', undefined, undefined, undefined],
21+
[false, 'FAILURE', undefined, undefined, undefined],
22+
[false, 'random', undefined, undefined, undefined],
23+
[true, 'success', undefined, undefined, undefined],
24+
[true, 'SUCCESS', undefined, undefined, undefined],
25+
[true, 'unknown', undefined, undefined, undefined],
26+
[true, 'FAILURE', undefined, undefined, undefined],
27+
[true, 'random', undefined, undefined, undefined],
28+
[false, 'failure', undefined, undefined, true],
29+
[false, 'success', undefined, undefined, true],
30+
[true, 'failure', undefined, undefined, true],
31+
[true, 'success', undefined, undefined, true],
32+
[true, 'failure', undefined, undefined, false],
33+
[true, 'success', undefined, undefined, false],
34+
[false, 'failure', false, true, true]
35+
])(
36+
'should render grid cell when inProgress=%s, status=%s, hidden=%s, displayToggleVisibility=%s, showRelativeTime=%s',
37+
(inProgress, status, hidden, displayToggleVisibility, showRelativeTime) => {
38+
const wrapper = mount(GridCell, {
39+
props: { ...defaultProps, inProgress, status, hidden, displayToggleVisibility, showRelativeTime }
40+
});
41+
expect(wrapper.html()).toMatchSnapshot();
1542
});
16-
expect(wrapper.html()).toMatchSnapshot();
17-
});
1843

19-
describe('Toggle Visibility', () => {
20-
it.each([
21-
[undefined],
22-
[false],
23-
[true]
24-
])('should display toggle visibility icon with hide icon when hidden passed as %s', async (hidden) => {
44+
it.each([
45+
[true, true, true],
46+
[false, true, true],
47+
[true, false, true],
48+
[false, false, true],
49+
[true, true, false],
50+
[false, false, false],
51+
[false, true, false], [true, false, false]
52+
])('should flip on hover when hidden=%s, displayToggleVisibility=%s, showRelativeTime=%s',
53+
async (hidden, displayToggleVisibility, showRelativeTime) => {
2554
const wrapper = mount(GridCell, {
26-
props: { ...defaultProps, displayToggleVisibility: true, hidden }
55+
props: { ...defaultProps, hidden, displayToggleVisibility, showRelativeTime }
56+
});
57+
await wrapper.find(`#${defaultProps.name.replaceAll(/[\\:\s]/g, '-')}`).trigger('mouseenter');
58+
await retryUntil(async () => {
59+
await requestAnimationFrameAsPromise();
60+
expect(wrapper.find(`[test-id="${rootId}-toolbar"]`).exists()).toBeTruthy();
2761
});
62+
expect(wrapper.html()).toMatchSnapshot();
63+
});
2864

29-
expect(wrapper.html()).toMatchSnapshot();
65+
it.each([
66+
[true, true, true],
67+
[false, true, true],
68+
[true, false, true],
69+
[false, false, true],
70+
[true, true, false],
71+
[false, false, false],
72+
[false, true, false], [true, false, false]
73+
])('should flip back once mouse is moved away when hidden=%s, displayToggleVisibility=%s, showRelativeTime=%s',
74+
async (hidden, displayToggleVisibility, showRelativeTime) => {
75+
const wrapper = mount(GridCell, {
76+
props: { ...defaultProps, hidden, displayToggleVisibility, showRelativeTime }
77+
});
3078

31-
const toggleVisibilityTooltip = wrapper.find('[aria-describedby="v-tooltip-1"]');
32-
expect(toggleVisibilityTooltip.exists()).toBeTruthy();
79+
await wrapper.find(`#${rootId}`).trigger('mouseenter');
80+
await retryUntil(async () => {
81+
await requestAnimationFrameAsPromise();
82+
expect(wrapper.find(`[test-id="${rootId}-toolbar"]`).exists()).toBeTruthy();
83+
});
3384

34-
await toggleVisibilityTooltip.trigger('click');
85+
await wrapper.find(`#${rootId}`).trigger('mouseleave');
86+
await retryUntil(async () => {
87+
await requestAnimationFrameAsPromise();
88+
expect(wrapper.find(`[test-id="${rootId}-toolbar"]`).exists()).toBeFalsy();
89+
});
90+
expect(wrapper.html()).toMatchSnapshot();
91+
});
3592

36-
const emitted = wrapper.emitted();
37-
expect(emitted).toHaveProperty('toggleVisibility');
38-
expect(emitted.toggleVisibility).toEqual([[defaultProps.name]]);
93+
it('should emit toggle visibility event when clicked on toggle icon', async () => {
94+
const wrapper = mount(GridCell, {
95+
props: { ...defaultProps, displayToggleVisibility: true }
96+
});
97+
98+
await wrapper.find(`#${defaultProps.name.replaceAll(/[\\:\s]/g, '-')}`).trigger('mouseenter');
99+
await retryUntil(async () => {
100+
await requestAnimationFrameAsPromise();
101+
expect(wrapper.find(`[test-id="${rootId}-change-visibility-icon"]`).exists()).toBeTruthy();
39102
});
103+
104+
await wrapper.find(`[test-id="${rootId}-change-visibility-icon"]`).trigger('click');
105+
const emitted = wrapper.emitted();
106+
expect(emitted).toHaveProperty('toggleVisibility');
107+
expect(emitted.toggleVisibility).toEqual([[defaultProps.name]]);
40108
});
41109
});

frontend/__tests__/components/Job.test.js

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@ describe('<Job />', () => {
1414
};
1515

1616
it.each([
17-
['Sleeping', 'Success', false, false, 'success'],
18-
['Sleeping', 'Success', true, false, 'success'],
19-
['Sleeping', 'Failure', false, true, 'failure'],
20-
['Sleeping', 'Unknown', false, true, 'unknown'],
21-
['Sleeping', 'Exception', false, true, 'failure'],
22-
['Building', 'Success', false, false, 'success building'],
23-
['Building', 'Failure', false, false, 'failure building'],
24-
['Building', 'Unknown', false, false, 'unknown building'],
25-
['Building', 'Exception', false, false, 'failure building'],
26-
['CheckingModifications', 'Success', false, false, 'success'],
27-
['CheckingModifications', 'Failure', false, true, 'failure'],
28-
['CheckingModifications', 'Unknown', false, true, 'unknown'],
29-
['CheckingModifications', 'Exception', false, true, 'failure']
17+
['Sleeping', 'Success', false, false, false],
18+
['Sleeping', 'Success', true, false, false],
19+
['Sleeping', 'Failure', false, true, false],
20+
['Sleeping', 'Unknown', false, true, false],
21+
['Sleeping', 'Exception', false, true, false],
22+
['Building', 'Success', false, false, true],
23+
['Building', 'Failure', false, false, true],
24+
['Building', 'Unknown', false, false, true],
25+
['Building', 'Exception', false, false, true],
26+
['CheckingModifications', 'Success', false, false, false],
27+
['CheckingModifications', 'Failure', false, true, false],
28+
['CheckingModifications', 'Unknown', false, true, false],
29+
['CheckingModifications', 'Exception', false, true, false]
3030
])('should render job grid when activity is %s and last build status is %s and hidden is %s',
31-
async (activity, lastBuildStatus, hidden, showRelativeTime, appendClassNames) => {
31+
async (activity, lastBuildStatus, hidden, showRelativeTime, inProgress) => {
3232
const wrapper = shallowMount(Job, {
3333
props: {
3434
content: { ...jobDetails, activity, lastBuildStatus },
@@ -47,7 +47,8 @@ describe('<Job />', () => {
4747
hidden,
4848
displayToggleVisibility: true,
4949
showRelativeTime,
50-
appendClassNames
50+
status: lastBuildStatus,
51+
inProgress
5152
});
5253

5354
await gridCellComponent.vm.$emit('toggle-visibility', jobDetails.name);

frontend/__tests__/components/WorkflowDashboard.test.js

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2-
import { mountWithWrapper, promiseWithResolvers } from '../test-utils';
2+
import { mountWithWrapper, promiseWithResolvers, requestAnimationFrameAsPromise, retryUntil } from '../test-utils';
33
import WorkflowDashboard from '@/components/WorkflowDashboard.vue';
44
import { fetchCctrayJson } from '@/services/apiService';
55
import Spinner from '@/components/Spinner.vue';
@@ -24,6 +24,7 @@ describe('<WorkflowDashboard />', () => {
2424
webUrl: 'https://github.com/webpack/webpack-cli/runs/7831157675?check_suite_focus=true',
2525
triggeredEvent: 'schedule'
2626
};
27+
2728
const jobDetails2 = {
2829
name: 'webpack-cli :: webpack-cli :: Lint Commit Messages',
2930
activity: 'Sleeping',
@@ -34,6 +35,27 @@ describe('<WorkflowDashboard />', () => {
3435
triggeredEvent: 'push'
3536
};
3637

38+
const jobDetails1RootId = jobDetails1.name.replaceAll(/[\\:\s]/g, '-');
39+
const hiddenContentsTitleBarTestId = '[test-id="job-hidden-contents-tool-bar"]';
40+
41+
const clickOnJobDetailsVisibilityToggleIcon = async (wrapper, jobDetailsRootId) => {
42+
const jobDetailsCard = wrapper.find(`#${jobDetailsRootId}`);
43+
expect(jobDetailsCard.exists()).toBeTruthy();
44+
45+
await jobDetailsCard.trigger('mouseenter');
46+
47+
await retryUntil(async () => {
48+
await requestAnimationFrameAsPromise();
49+
expect(wrapper.find(`[test-id="${jobDetailsRootId}-toolbar"]`).exists()).toBeTruthy();
50+
});
51+
52+
wrapper
53+
.find(`[test-id="${jobDetailsRootId}-change-visibility-icon"]`)
54+
.trigger('click');
55+
await wrapper.vm.$nextTick();
56+
await flushPromises();
57+
};
58+
3759
beforeEach(() => {
3860
getVersion.mockReturnValueOnce('3.3.0');
3961
});
@@ -119,7 +141,7 @@ describe('<WorkflowDashboard />', () => {
119141
const jobComponents = workflowDashboardWrapper.findAllComponents(Job);
120142

121143
expect(jobComponents).length(1);
122-
expect(workflowDashboardWrapper.find(`#${jobDetails1.name.replaceAll(/[\\:\s]/g, '-')}`).exists()).toBeTruthy();
144+
expect(workflowDashboardWrapper.find(`#${jobDetails1RootId}`).exists()).toBeTruthy();
123145
expect(workflowDashboardWrapper.find(`#${jobDetails2.name.replaceAll(/[\\:\s]/g, '-')}`).exists()).toBeFalsy();
124146
});
125147

@@ -137,15 +159,13 @@ describe('<WorkflowDashboard />', () => {
137159
await flushPromises();
138160

139161
expect(workflowDashboardWrapper.html()).toMatchSnapshot();
140-
expect(workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar"]').exists()).toBeFalsy();
141-
workflowDashboardWrapper
142-
.find(`[test-id="${jobDetails1.name.replaceAll(/[\\:\s]/g, '-')}-change-visibility-icon"]`)
143-
.trigger('click');
144-
await workflowDashboardWrapper.vm.$nextTick();
145-
await flushPromises();
146-
expect(workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar"]').exists()).toBeTruthy();
162+
expect(workflowDashboardWrapper.find(hiddenContentsTitleBarTestId).exists()).toBeFalsy();
163+
164+
await clickOnJobDetailsVisibilityToggleIcon(workflowDashboardWrapper, jobDetails1RootId);
165+
166+
expect(workflowDashboardWrapper.find(hiddenContentsTitleBarTestId).exists()).toBeTruthy();
147167
expect(workflowDashboardWrapper.html()).toMatchSnapshot();
148-
expect(workflowDashboardWrapper.find(`#${jobDetails1.name.replaceAll(/[\\:\s]/g, '-')}`).exists()).toBeFalsy();
168+
expect(workflowDashboardWrapper.find(`#${jobDetails1RootId}`).exists()).toBeFalsy();
149169
});
150170

151171
it('should hide job details depends previous config', async () => {
@@ -162,8 +182,8 @@ describe('<WorkflowDashboard />', () => {
162182
await flushPromises();
163183

164184
expect(workflowDashboardWrapper.html()).toMatchSnapshot();
165-
expect(workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar"]').exists()).toBeTruthy();
166-
expect(workflowDashboardWrapper.find(`#${jobDetails1.name.replaceAll(/[\\:\s]/g, '-')}`).exists()).toBeFalsy();
185+
expect(workflowDashboardWrapper.find(hiddenContentsTitleBarTestId).exists()).toBeTruthy();
186+
expect(workflowDashboardWrapper.find(`#${jobDetails1RootId}`).exists()).toBeFalsy();
167187
});
168188

169189
it('should display hidden jobs when user clicks on show', async () => {
@@ -179,11 +199,11 @@ describe('<WorkflowDashboard />', () => {
179199

180200
await flushPromises();
181201

182-
expect(workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar"]').exists()).toBeTruthy();
202+
expect(workflowDashboardWrapper.find(hiddenContentsTitleBarTestId).exists()).toBeTruthy();
183203
workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar-icon"]').trigger('click');
184204
await workflowDashboardWrapper.vm.$nextTick();
185205
await flushPromises();
186-
expect(workflowDashboardWrapper.find(`#${jobDetails1.name.replaceAll(/[\\:\s]/g, '-')}`).exists()).toBeTruthy();
206+
expect(workflowDashboardWrapper.find(`#${jobDetails1RootId}`).exists()).toBeTruthy();
187207
expect(workflowDashboardWrapper.html()).toMatchSnapshot();
188208
});
189209

@@ -200,17 +220,12 @@ describe('<WorkflowDashboard />', () => {
200220

201221
await flushPromises();
202222

203-
expect(workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar"]').exists()).toBeTruthy();
223+
expect(workflowDashboardWrapper.find(hiddenContentsTitleBarTestId).exists()).toBeTruthy();
204224
workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar-icon"]').trigger('click');
205225
await workflowDashboardWrapper.vm.$nextTick();
206226
await flushPromises();
207-
expect(workflowDashboardWrapper.find(`#${jobDetails1.name.replaceAll(/[\\:\s]/g, '-')}`).exists()).toBeTruthy();
208-
workflowDashboardWrapper
209-
.find(`[test-id="${jobDetails1.name.replaceAll(/[\\:\s]/g, '-')}-change-visibility-icon"]`)
210-
.trigger('click');
211-
await workflowDashboardWrapper.vm.$nextTick();
212-
await flushPromises();
213-
expect(workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar"]').exists()).toBeFalsy();
227+
await clickOnJobDetailsVisibilityToggleIcon(workflowDashboardWrapper, jobDetails1RootId);
228+
expect(workflowDashboardWrapper.find(hiddenContentsTitleBarTestId).exists()).toBeFalsy();
214229
expect(workflowDashboardWrapper.html()).toMatchSnapshot();
215230
});
216231

@@ -228,17 +243,12 @@ describe('<WorkflowDashboard />', () => {
228243

229244
await flushPromises();
230245

231-
expect(workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar"]').exists()).toBeTruthy();
246+
expect(workflowDashboardWrapper.find(hiddenContentsTitleBarTestId).exists()).toBeTruthy();
232247
workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar-icon"]').trigger('click');
233248
await workflowDashboardWrapper.vm.$nextTick();
234249
await flushPromises();
235-
expect(workflowDashboardWrapper.find(`#${jobDetails1.name.replaceAll(/[\\:\s]/g, '-')}`).exists()).toBeTruthy();
236-
workflowDashboardWrapper
237-
.find(`[test-id="${jobDetails1.name.replaceAll(/[\\:\s]/g, '-')}-change-visibility-icon"]`)
238-
.trigger('click');
239-
await workflowDashboardWrapper.vm.$nextTick();
240-
await flushPromises();
241-
expect(workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar"]').exists()).toBeTruthy();
250+
await clickOnJobDetailsVisibilityToggleIcon(workflowDashboardWrapper, jobDetails1RootId);
251+
expect(workflowDashboardWrapper.find(hiddenContentsTitleBarTestId).exists()).toBeTruthy();
242252
expect(workflowDashboardWrapper.html()).toMatchSnapshot();
243253
});
244254

@@ -296,7 +306,7 @@ describe('<WorkflowDashboard />', () => {
296306
const jobComponents = workflowDashboardWrapper.findAllComponents(Job);
297307

298308
expect(jobComponents).length(1);
299-
expect(workflowDashboardWrapper.find(`#${jobDetails1.name.replaceAll(/[\\:\s]/g, '-')}`).exists()).toBeTruthy();
309+
expect(workflowDashboardWrapper.find(`#${jobDetails1RootId}`).exists()).toBeTruthy();
300310
expect(workflowDashboardWrapper.find(`#${jobDetails2.name.replaceAll(/[\\:\s]/g, '-')}`).exists()).toBeFalsy();
301311
});
302312

@@ -364,7 +374,7 @@ describe('<WorkflowDashboard />', () => {
364374
await flushPromises();
365375

366376
expect(workflowDashboardWrapper.findComponent(NoFailures).exists()).toBeFalsy();
367-
expect(workflowDashboardWrapper.find('[test-id="job-hidden-contents-tool-bar"]').exists()).toBeTruthy();
377+
expect(workflowDashboardWrapper.find(hiddenContentsTitleBarTestId).exists()).toBeTruthy();
368378
});
369379
});
370380

0 commit comments

Comments
 (0)