diff --git a/web_src/js/components/RepoActionView.test.ts b/web_src/js/components/RepoActionView.test.ts new file mode 100644 index 0000000000..0bbd739206 --- /dev/null +++ b/web_src/js/components/RepoActionView.test.ts @@ -0,0 +1,110 @@ +// Test for workflow command line filtering logic + +// These are the constants and functions from RepoActionView.vue +const LogLinePrefixesHidden = ['::add-matcher::', '##[add-matcher]', '::workflow-command', '::remove-matcher']; + +type LogLine = { + index: number; + timestamp: number; + message: string; +}; + +function shouldHideLine(line: LogLine): boolean { + for (const prefix of LogLinePrefixesHidden) { + if (line.message.startsWith(prefix)) { + return true; + } + } + return false; +} + +test('shouldHideLine filters workflow commands starting with ::add-matcher::', () => { + const line: LogLine = { + index: 1, + timestamp: 1000, + message: '::add-matcher::.github/problem-matcher.json', + }; + expect(shouldHideLine(line)).toBe(true); +}); + +test('shouldHideLine filters workflow commands starting with ##[add-matcher]', () => { + const line: LogLine = { + index: 2, + timestamp: 1001, + message: '##[add-matcher].github/eslint.json', + }; + expect(shouldHideLine(line)).toBe(true); +}); + +test('shouldHideLine filters workflow commands starting with ::workflow-command', () => { + const line: LogLine = { + index: 3, + timestamp: 1002, + message: '::workflow-command::some-command', + }; + expect(shouldHideLine(line)).toBe(true); +}); + +test('shouldHideLine filters workflow commands starting with ::remove-matcher', () => { + const line: LogLine = { + index: 4, + timestamp: 1003, + message: '::remove-matcher::owner=eslint', + }; + expect(shouldHideLine(line)).toBe(true); +}); + +test('shouldHideLine does not filter normal log lines', () => { + const line: LogLine = { + index: 5, + timestamp: 1004, + message: 'Normal log line without workflow commands', + }; + expect(shouldHideLine(line)).toBe(false); +}); + +test('shouldHideLine does not filter lines with workflow commands not at the start', () => { + const line: LogLine = { + index: 6, + timestamp: 1005, + message: 'Some text before ::add-matcher:: in the middle', + }; + expect(shouldHideLine(line)).toBe(false); +}); + +test('shouldHideLine handles group commands (should not hide them)', () => { + const groupLine: LogLine = { + index: 7, + timestamp: 1006, + message: '::group::Build Step', + }; + expect(shouldHideLine(groupLine)).toBe(false); + + const endGroupLine: LogLine = { + index: 8, + timestamp: 1007, + message: '::endgroup::', + }; + expect(shouldHideLine(endGroupLine)).toBe(false); +}); + +test('shouldHideLine handles various log formats', () => { + const testCases = [ + {message: '::add-matcher::', expected: true}, + {message: '##[add-matcher]', expected: true}, + {message: '::workflow-command', expected: true}, + {message: '::remove-matcher', expected: true}, + {message: 'Build started', expected: false}, + {message: 'Error: test failed', expected: false}, + {message: ' ::add-matcher::.github/test.json', expected: false}, // with leading space + ]; + + testCases.forEach(({message, expected}, index) => { + const line: LogLine = { + index: index + 10, + timestamp: 2000 + index, + message, + }; + expect(shouldHideLine(line)).toBe(expected); + }); +}); diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 69579d3687..6679ba93ce 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -23,6 +23,7 @@ type LogLine = { const LogLinePrefixesGroup = ['::group::', '##[group]']; const LogLinePrefixesEndGroup = ['::endgroup::', '##[endgroup]']; +const LogLinePrefixesHidden = ['::add-matcher::', '##[add-matcher]', '::workflow-command', '::remove-matcher']; type LogLineCommand = { name: 'group' | 'endgroup', @@ -63,6 +64,15 @@ function parseLineCommand(line: LogLine): LogLineCommand | null { return null; } +function shouldHideLine(line: LogLine): boolean { + for (const prefix of LogLinePrefixesHidden) { + if (line.message.startsWith(prefix)) { + return true; + } + } + return false; +} + function isLogElementInViewport(el: Element, {extraViewPortHeight}={extraViewPortHeight: 0}): boolean { const rect = el.getBoundingClientRect(); // only check whether bottom is in viewport, because the log element can be a log group which is usually tall @@ -315,6 +325,10 @@ export default defineComponent({ appendLogs(stepIndex: number, startTime: number, logLines: LogLine[]) { for (const line of logLines) { + // Skip lines that should be hidden (workflow commands) + if (shouldHideLine(line)) { + continue; + } const el = this.getActiveLogsContainer(stepIndex); const cmd = parseLineCommand(line); if (cmd?.name === 'group') {