From 8006c5868e60a4a433ac86765467bcf6253a2d50 Mon Sep 17 00:00:00 2001 From: Jon Friesen Date: Sat, 24 Aug 2024 21:13:13 -0600 Subject: [PATCH] adds github action runs (#10) --- README.md | 1 + src/content/config.e2e.js | 9 +++++- src/content/config.js | 62 ++++++++++++++++++++++++++++++++++++ src/content/github.test.js | 35 ++++++++++++++++++++ web/src/components/Intro.jsx | 2 +- 5 files changed, 107 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d30c83a..41a6c18 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Want support for more sites? Please [submit an issue](https://github.com/jonfrie | | Repository | | | User/Organization | | | Release | +| | Action Runs | | **Instagram** | | | | Profile | | **LinkedIn** | | diff --git a/src/content/config.e2e.js b/src/content/config.e2e.js index 379ef7e..bbef4fe 100644 --- a/src/content/config.e2e.js +++ b/src/content/config.e2e.js @@ -27,7 +27,13 @@ async function testPageConfig(page, url, config) { // Verify that all expected properties are present and non-empty for (const key in info) { - expect(info[key]).toBeTruthy() + expect(info[key]).not.toBe(undefined, `Expected info[${key}] to be defined, but it was undefined`) + expect(info[key]).not.toBe(null, `Expected info[${key}] to be non-null, but it was null`) + if (typeof info[key] === 'string') { + expect(info[key].trim()).not.toBe('', `Expected info[${key}] to be non-empty string, but it was: "${info[key]}"`) + } else { + expect(info[key]).toBeTruthy(`Expected info[${key}] to be truthy, but it was: ${JSON.stringify(info[key])}`) + } } // Test markdown and plaintext building functions @@ -54,6 +60,7 @@ for (const [siteName, siteConfig] of Object.entries(siteConfigs)) { user: 'https://github.com/microsoft', release: 'https://github.com/microsoft/playwright/releases/tag/v1.32.3', commit: 'https://github.com/golang/go/commit/96d8ff00c2d6a88384863a656fb5e53716b614d3', + action_run: 'https://github.com/jonfriesen/quickcite/actions/runs/10446232111', }, instagram: { profile: 'https://www.instagram.com/jonfriesen/', diff --git a/src/content/config.js b/src/content/config.js index f0dd70c..22a75bb 100644 --- a/src/content/config.js +++ b/src/content/config.js @@ -148,6 +148,68 @@ const siteConfigs = { return `(${info.hash.slice(0, 7)}) -${info.title}\nBy ${info.author} on ${formattedDate}\n${url}` }, }, + action_run: { + urlPattern: /^https:\/\/github\.com\/[^\/]+\/[^\/]+\/actions\/runs\/\d+$/, + buttonId: 'gh-action-run-copy-button', + getInfo: () => { + const getStatus = () => { + const statusLabel = Array.from(document.querySelectorAll('span.mb-1.d-block.text-small.color-fg-muted')).find((el) => el.textContent.trim() === 'Status') + + if (statusLabel) { + // If found, get the next sibling span which contains the status value + const statusValue = statusLabel.nextElementSibling + if (statusValue && statusValue.classList.contains('h4') && statusValue.classList.contains('color-fg-default')) { + return statusValue.textContent.trim() + } + } + return null + } + const getStatusEmoji = (status) => { + const statusMap = { + Queued: 'โณ', + 'In progress': '๐Ÿ”„', + Success: 'โœ…', + Failure: 'โŒ', + Cancelled: '๐Ÿšซ', + Skipped: 'โญ๏ธ', + } + return statusMap[status] || '' + } + const extractRepoInfo = (str) => { + const parts = str.split('ยท').map((part) => part.trim()) + + if (parts.length >= 2) { + const repoInfo = parts[1].split('/') + if (repoInfo.length >= 2) { + const [name, hash] = repoInfo[1].split('@') + return { + owner: repoInfo[0], + name: name, + hash: hash || null, // In case there's no hash + } + } + } + + return null + } + + // Test the function + const repoInfo = extractRepoInfo(document.title) + const workflowName = document.querySelector('h1.PageHeader-title span.markdown-title').textContent.trim() + const runNumber = document.querySelector('h1.PageHeader-title span.color-fg-muted').textContent.trim() + const runStatus = getStatus() + const repoOwner = repoInfo.owner + const repoName = repoInfo.name + const statusEmoji = getStatusEmoji(runStatus) + return { workflowName, runStatus, runNumber, repoName, repoOwner, statusEmoji } + }, + buildMarkdown: (info, url) => { + return `[${info.repoOwner}/${info.repoName}: ${info.workflowName} ${info.runNumber} ${info.statusEmoji} (${info.runStatus})](${url})` + }, + buildPlaintext: (info, url) => { + return `${info.repoOwner}/${info.repoName}: ${info.workflowName} ${info.runNumber} ${info.statusEmoji} (${info.runStatus}) - ${url}` + }, + }, }, }, instagram: { diff --git a/src/content/github.test.js b/src/content/github.test.js index 43cce32..df4fef3 100644 --- a/src/content/github.test.js +++ b/src/content/github.test.js @@ -10,6 +10,11 @@ describe('GitHub URL pattern tests', () => { { page: 'release', url: 'https://github.com/user/repo/releases/tag/v1.0.0', shouldMatch: true }, { page: 'commit', url: 'https://github.com/user/repo/commit/1234567890123456789012345678901234567890', shouldMatch: true }, { page: 'pr', url: 'https://github.com/user/repo', shouldMatch: false }, + { page: 'action_run', url: 'https://github.com/jonfriesen/quickcite/actions/runs/10476888027', shouldMatch: true }, + { page: 'action_run', url: 'https://github.com/octocat/Hello-World/actions/runs/12345678', shouldMatch: true }, + { page: 'action_run', url: 'https://github.com/octocat/Hello-World/actions', shouldMatch: false }, + { page: 'action_run', url: 'https://github.com/octocat/Hello-World/actions/runs/abcdefgh', shouldMatch: false }, + { page: 'action_run', url: 'https://github.com/octocat/Hello-World/actions/runs/abcdefgh/job/12345678', shouldMatch: false }, ] test.each(testCases)('$page: $url should ${shouldMatch ? "match" : "not match"}', ({ page, url, shouldMatch }) => { @@ -23,6 +28,18 @@ describe('GitHub URL pattern tests', () => { }) describe('GitHub markdown generator tests', () => { + const mockGetStatusEmoji = (status) => { + const statusMap = { + Queued: 'โณ', + 'In progress': '๐Ÿ”„', + Success: 'โœ…', + Failure: 'โŒ', + Cancelled: '๐Ÿšซ', + Skipped: 'โญ๏ธ', + } + return statusMap[status] || '' + } + const markdownTestCases = [ { page: 'pr', @@ -66,6 +83,24 @@ describe('GitHub markdown generator tests', () => { url: 'https://github.com/user/repo/commit/1234567890abcdef', expected: '[(1234567) - Update README.md](https://github.com/user/repo/commit/1234567890abcdef)\nBy John Doe on ' + new Date('2023-05-01T00:00:00Z').toLocaleString(), }, + { + page: 'action_run', + info: { repoOwner: 'jonfriesen', repoName: 'quickcite', workflowName: 'CI', runNumber: '#1234', runStatus: 'Success', statusEmoji: 'โœ…' }, + url: 'https://github.com/jonfriesen/quickcite/actions/runs/10476888027', + expected: '[jonfriesen/quickcite: CI #1234 โœ… (Success)](https://github.com/jonfriesen/quickcite/actions/runs/10476888027)', + }, + { + page: 'action_run', + info: { repoOwner: 'octocat', repoName: 'Hello-World', workflowName: 'Build', runNumber: '#5678', runStatus: 'Failure', statusEmoji: 'โŒ' }, + url: 'https://github.com/octocat/Hello-World/actions/runs/12345678', + expected: '[octocat/Hello-World: Build #5678 โŒ (Failure)](https://github.com/octocat/Hello-World/actions/runs/12345678)', + }, + { + page: 'action_run', + info: { repoOwner: 'test', repoName: 'repo', workflowName: 'Deploy', runNumber: '#9012', runStatus: 'In progress', statusEmoji: '๐Ÿ”„' }, + url: 'https://github.com/test/repo/actions/runs/90123456', + expected: '[test/repo: Deploy #9012 ๐Ÿ”„ (In progress)](https://github.com/test/repo/actions/runs/90123456)', + }, ] test.each(markdownTestCases)('$page: should generate correct markdown', ({ page, info, url, expected }) => { diff --git a/web/src/components/Intro.jsx b/web/src/components/Intro.jsx index a51e598..1a7daf6 100644 --- a/web/src/components/Intro.jsx +++ b/web/src/components/Intro.jsx @@ -123,7 +123,7 @@ export function Intro() { - GitHub - Pull Requests, Issues, Repositories, Discussions, Releases, Users/Orgs + GitHub - Pull Requests, Issues, Repositories, Discussions, Releases, Users/Orgs, Action Runs