loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request 'Adds support for log-line groups' (#3337) from Mai-Lapyst/forgejo:actions-add-logline-groups into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3337
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>

+161 -44
+105
web_src/js/components/RepoActionView.test.js
··· 1 + import {mount, flushPromises} from '@vue/test-utils'; 2 + import RepoActionView from './RepoActionView.vue'; 3 + 4 + test('processes ##[group] and ##[endgroup]', async () => { 5 + Object.defineProperty(document.documentElement, 'lang', {value: 'en'}); 6 + vi.spyOn(global, 'fetch').mockImplementation((url, opts) => { 7 + const artifacts_value = { 8 + artifacts: [], 9 + }; 10 + const stepsLog_value = [ 11 + { 12 + step: 0, 13 + cursor: 0, 14 + lines: [ 15 + {index: 1, message: '##[group]Test group', timestamp: 0}, 16 + {index: 2, message: 'A test line', timestamp: 0}, 17 + {index: 3, message: '##[endgroup]', timestamp: 0}, 18 + {index: 4, message: 'A line outside the group', timestamp: 0}, 19 + ], 20 + }, 21 + ]; 22 + const jobs_value = { 23 + state: { 24 + run: { 25 + status: 'success', 26 + commit: { 27 + pusher: {}, 28 + }, 29 + }, 30 + currentJob: { 31 + steps: [ 32 + { 33 + summary: 'Test Job', 34 + duration: '1s', 35 + status: 'success', 36 + }, 37 + ], 38 + }, 39 + }, 40 + logs: { 41 + stepsLog: opts.body?.includes('"cursor":null') ? stepsLog_value : [], 42 + }, 43 + }; 44 + 45 + return Promise.resolve({ 46 + ok: true, 47 + json: vi.fn().mockResolvedValue( 48 + url.endsWith('/artifacts') ? artifacts_value : jobs_value, 49 + ), 50 + }); 51 + }); 52 + 53 + const wrapper = mount(RepoActionView, { 54 + props: { 55 + jobIndex: '1', 56 + locale: { 57 + approve: '', 58 + cancel: '', 59 + rerun: '', 60 + artifactsTitle: '', 61 + areYouSure: '', 62 + confirmDeleteArtifact: '', 63 + rerun_all: '', 64 + showTimeStamps: '', 65 + showLogSeconds: '', 66 + showFullScreen: '', 67 + downloadLogs: '', 68 + status: { 69 + unknown: '', 70 + waiting: '', 71 + running: '', 72 + success: '', 73 + failure: '', 74 + cancelled: '', 75 + skipped: '', 76 + blocked: '', 77 + }, 78 + }, 79 + }, 80 + }); 81 + await flushPromises(); 82 + await wrapper.get('.job-step-summary').trigger('click'); 83 + await flushPromises(); 84 + 85 + // Test if header was loaded correctly 86 + expect(wrapper.get('.step-summary-msg').text()).toEqual('Test Job'); 87 + 88 + // Check if 3 lines where rendered 89 + expect(wrapper.findAll('.job-log-line').length).toEqual(3); 90 + 91 + // Check if line 1 contains the group header 92 + expect(wrapper.get('.job-log-line:nth-of-type(1) > details.log-msg').text()).toEqual('Test group'); 93 + 94 + // Check if right after the header line exists a log list 95 + expect(wrapper.find('.job-log-line:nth-of-type(1) + .job-log-list.hidden').exists()).toBe(true); 96 + 97 + // Check if inside the loglist exist exactly one log line 98 + expect(wrapper.findAll('.job-log-list > .job-log-line').length).toEqual(1); 99 + 100 + // Check if inside the loglist is an logline with our second logline 101 + expect(wrapper.get('.job-log-list > .job-log-line > .log-msg').text()).toEqual('A test line'); 102 + 103 + // Check if after the log list exists another log line 104 + expect(wrapper.get('.job-log-list + .job-log-line > .log-msg').text()).toEqual('A line outside the group'); 105 + });
+56 -44
web_src/js/components/RepoActionView.vue
··· 110 110 }, 111 111 112 112 methods: { 113 - // get the active container element, either the `job-step-logs` or the `job-log-list` in the `job-log-group` 114 - getLogsContainer(idx) { 115 - const el = this.$refs.logs[idx]; 116 - return el._stepLogsActiveContainer ?? el; 117 - }, 118 - // begin a log group 119 - beginLogGroup(idx) { 120 - const el = this.$refs.logs[idx]; 121 - 122 - const elJobLogGroup = document.createElement('div'); 123 - elJobLogGroup.classList.add('job-log-group'); 124 - 125 - const elJobLogGroupSummary = document.createElement('div'); 126 - elJobLogGroupSummary.classList.add('job-log-group-summary'); 127 - 128 - const elJobLogList = document.createElement('div'); 129 - elJobLogList.classList.add('job-log-list'); 130 - 131 - elJobLogGroup.append(elJobLogGroupSummary); 132 - elJobLogGroup.append(elJobLogList); 133 - el._stepLogsActiveContainer = elJobLogList; 134 - }, 135 - // end a log group 136 - endLogGroup(idx) { 137 - const el = this.$refs.logs[idx]; 138 - el._stepLogsActiveContainer = null; 139 - }, 140 - 141 113 // show/hide the step logs for a step 142 114 toggleStepLogs(idx) { 143 115 this.currentJobStepsStates[idx].expanded = !this.currentJobStepsStates[idx].expanded; ··· 153 125 approveRun() { 154 126 POST(`${this.run.link}/approve`); 155 127 }, 128 + // show/hide the step logs for a group 129 + toggleGroupLogs(event) { 130 + const line = event.target.parentElement; 131 + const list = line.nextSibling; 132 + if (event.newState === 'open') { 133 + list.classList.remove('hidden'); 134 + } else { 135 + list.classList.add('hidden'); 136 + } 137 + }, 156 138 157 - createLogLine(line, startTime, stepIndex) { 139 + createLogLine(line, startTime, stepIndex, group) { 158 140 const div = document.createElement('div'); 159 141 div.classList.add('job-log-line'); 160 142 div.setAttribute('id', `jobstep-${stepIndex}-${line.index}`); ··· 180 162 logTimeSeconds.textContent = `${seconds}s`; 181 163 toggleElem(logTimeSeconds, this.timeVisible['log-time-seconds']); 182 164 183 - const logMessage = document.createElement('span'); 184 - logMessage.className = 'log-msg'; 165 + let logMessage = document.createElement('span'); 185 166 logMessage.innerHTML = renderAnsi(line.message); 167 + if (group.isHeader) { 168 + const details = document.createElement('details'); 169 + details.addEventListener('toggle', this.toggleGroupLogs); 170 + const summary = document.createElement('summary'); 171 + summary.append(logMessage); 172 + details.append(summary); 173 + logMessage = details; 174 + } 175 + logMessage.className = 'log-msg'; 176 + logMessage.style.paddingLeft = `${group.depth}em`; 177 + 186 178 div.append(logTimeStamp); 187 179 div.append(logMessage); 188 180 div.append(logTimeSeconds); ··· 191 183 }, 192 184 193 185 appendLogs(stepIndex, logLines, startTime) { 186 + const groupStack = []; 187 + const container = this.$refs.logs[stepIndex]; 194 188 for (const line of logLines) { 195 - // TODO: group support: ##[group]GroupTitle , ##[endgroup] 196 - const el = this.getLogsContainer(stepIndex); 197 - el.append(this.createLogLine(line, startTime, stepIndex)); 189 + const el = groupStack.length > 0 ? groupStack[groupStack.length - 1] : container; 190 + const group = { 191 + depth: groupStack.length, 192 + isHeader: false, 193 + }; 194 + if (line.message.startsWith('##[group]')) { 195 + group.isHeader = true; 196 + 197 + const logLine = this.createLogLine( 198 + { 199 + ...line, 200 + message: line.message.substring(9), 201 + }, 202 + startTime, stepIndex, group, 203 + ); 204 + logLine.setAttribute('data-group', group.index); 205 + el.append(logLine); 206 + 207 + const list = document.createElement('div'); 208 + list.classList.add('job-log-list'); 209 + list.classList.add('hidden'); 210 + list.setAttribute('data-group', group.index); 211 + groupStack.push(list); 212 + el.append(list); 213 + } else if (line.message.startsWith('##[endgroup]')) { 214 + groupStack.pop(); 215 + } else { 216 + el.append(this.createLogLine(line, startTime, stepIndex, group)); 217 + } 198 218 } 199 219 }, 200 220 ··· 878 898 border-radius: 0; 879 899 } 880 900 881 - /* TODO: group support 882 - 883 - .job-log-group { 884 - 885 - } 886 - .job-log-group-summary { 887 - 901 + .job-log-list.hidden { 902 + display: none; 888 903 } 889 - .job-log-list { 890 - 891 - } */ 892 904 </style>