diff --git a/.gitignore b/.gitignore index cbff9ab..1a4b2ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.DS_Store *.zip *~ node_modules +web-ext-artifacts/ diff --git a/content.test.js b/content.test.js index b6ebfa2..f49e594 100644 --- a/content.test.js +++ b/content.test.js @@ -4,14 +4,110 @@ const { getAttachLinks, getBugIdsFromPRTitle, MERGE_CONTAINER_ID, + PR_STATE_CLOSED, PR_STATE_MERGED, + PR_STATE_OPEN, + PR_STATE_UNKNOWN, + TAB_CONVERSATION, + TAB_OTHER, + getPRNum, + getPRTitle, + getPRState, + getSelectedTab, } = require("./github-bugzilla-content"); +// Minimal GitHub PR page header HTML matching the current (post-2024) structure, +// derived from mozilla-services/socorro pull requests. +const PR_HEADER_HTML = (title, num, status, selectedTab) => ` +
+

+ ${title}#${num} +

+
+
+ Merged +
+
+ +
+
+`; + describe("Content script", () => { afterEach(() => { // restore spies created with spyOn jest.restoreAllMocks(); }); + + describe("getPRNum", () => { + it("returns the PR number from the page", () => { + document.body.innerHTML = PR_HEADER_HTML("bug-1746636: move PROCESS_TYPES to socorro directory", "7170", "pullMerged", "Conversation"); + expect(getPRNum()).toBe("7170"); + }); + + it("returns undefined when PH_Title is not present", () => { + document.body.innerHTML = "
"; + expect(getPRNum()).toBeUndefined(); + }); + }); + + describe("getPRTitle", () => { + it("returns the PR title from the page", () => { + document.body.innerHTML = PR_HEADER_HTML("bug-1746636: move PROCESS_TYPES to socorro directory", "7170", "pullMerged", "Conversation"); + expect(getPRTitle()).toBe("bug-1746636: move PROCESS_TYPES to socorro directory"); + }); + + it("returns undefined when PH_Title is not present", () => { + document.body.innerHTML = "
"; + expect(getPRTitle()).toBeUndefined(); + }); + }); + + describe("getPRState", () => { + it("returns PR_STATE_MERGED when the PR is merged", () => { + document.body.innerHTML = PR_HEADER_HTML("bug-1: fix things", "99", "pullMerged", "Conversation"); + expect(getPRState()).toBe(PR_STATE_MERGED); + }); + + it("returns PR_STATE_OPEN when the PR is open", () => { + document.body.innerHTML = PR_HEADER_HTML("bug-1: fix things", "99", "pullOpened", "Conversation"); + expect(getPRState()).toBe(PR_STATE_OPEN); + }); + + it("returns PR_STATE_CLOSED when the PR is closed", () => { + document.body.innerHTML = PR_HEADER_HTML("bug-1: fix things", "99", "pullClosed", "Conversation"); + expect(getPRState()).toBe(PR_STATE_CLOSED); + }); + + it("returns PR_STATE_UNKNOWN when the PR is closed", () => { + document.body.innerHTML = PR_HEADER_HTML("bug-1: fix things", "99", "randomValue", "Conversation"); + expect(getPRState()).toBe(PR_STATE_UNKNOWN); + }); + }); + + describe("getSelectedTab", () => { + it("returns TAB_CONVERSATION when the Conversation tab is selected", () => { + document.body.innerHTML = PR_HEADER_HTML("bug-1: fix things", "99", "pullMerged", "Conversation"); + expect(getSelectedTab()).toBe(TAB_CONVERSATION); + }); + + it("returns TAB_OTHER when a non-Conversation tab is selected", () => { + document.body.innerHTML = PR_HEADER_HTML("bug-1: fix things", "99", "pullMerged", "Commits"); + expect(getSelectedTab()).toBe(TAB_OTHER); + }); + + it("returns TAB_OTHER when no tab is selected", () => { + document.body.innerHTML = "
"; + expect(getSelectedTab()).toBe(TAB_OTHER); + }); + }); + describe("getAttachLinks", () => { const bugIds = [1]; const repoInfo = ''; @@ -89,10 +185,7 @@ describe("Content script", () => { // Mock the HTML structure document.body.innerHTML = ` -
-
-
-
+ ${PR_HEADER_HTML(prTitle, prNum, "pullMerged", "Conversation")}
${author} merged commit ${commitSha}into main @@ -108,13 +201,10 @@ describe("Content script", () => { it("sends a message to the background script when the merge link is clicked when a PR is merged via a merge queue", () => { const sendMessage = jest.spyOn(browser.runtime, "sendMessage").mockResolvedValue({}); - + // Mock the HTML structure document.body.innerHTML = ` -
-
-
-
+ ${PR_HEADER_HTML(prTitle, prNum, "pullMerged", "Conversation")}
${author} diff --git a/github-bugzilla-content.js b/github-bugzilla-content.js index 386f83f..7e0c625 100644 --- a/github-bugzilla-content.js +++ b/github-bugzilla-content.js @@ -21,11 +21,13 @@ const ATTACH_CONTAINER_ID = "robBugsonAttachLinks"; const MERGE_CONTAINER_ID = "robBugsonMergeLinks"; const LIST_CONTAINER_ID = "robBugsonListLinks"; +const PR_STATE_CLOSED = "Closed"; const PR_STATE_MERGED = "Merged"; +const PR_STATE_OPEN = "Open"; const PR_STATE_UNKNOWN = "Unknown"; -const TAB_UNKNOWN = "Unknown"; const TAB_CONVERSATION = "Conversation"; +const TAB_OTHER = "Other"; /** @@ -33,7 +35,7 @@ const TAB_CONVERSATION = "Conversation"; */ function getPRNum() { // Get the PR number which is like "#4099" - let elem = document.querySelector("h1.gh-header-title span"); + let elem = document.querySelectorAll('[data-component="PH_Title"] span')[1]; if (!elem) { return; } @@ -47,7 +49,7 @@ function getPRNum() { * Retrieve the PR title from the pull request page. */ function getPRTitle() { - let elem = document.querySelector("bdi.js-issue-title"); + let elem = document.querySelector('[data-component="PH_Title"] span.markdown-title'); if (!elem) { return; } @@ -85,16 +87,16 @@ function getRepoInfo() { * Retrieve PR state. */ function getPRState() { - // See if there"s been a merge - let state = document.querySelector(".State.State--merged"); + let state = document.querySelector('[data-status]'); if (state === null) { return PR_STATE_UNKNOWN; } - state = state.textContent.trim(); - if (state == "Merged") { - return PR_STATE_MERGED; + switch (state.getAttribute('data-status')) { + case 'pullMerged': return PR_STATE_MERGED; + case 'pullOpened': return PR_STATE_OPEN; + case 'pullClosed': return PR_STATE_CLOSED; + default: return PR_STATE_UNKNOWN; } - return PR_STATE_UNKNOWN; } @@ -102,15 +104,15 @@ function getPRState() { * For PRs, get the selected tab. */ function getSelectedTab() { - let tab = document.querySelector("a.tabnav-tab.selected"); + let tab = document.querySelector('[role="tab"][aria-selected="true"]'); if (!tab) { - return TAB_UNKNOWN; + return TAB_OTHER; } let tabText = tab.textContent.trim(); if (tabText.startsWith("Conversation")) { return TAB_CONVERSATION; } - return TAB_UNKNOWN; + return TAB_OTHER; } @@ -233,7 +235,7 @@ function addAttachLinksToPage(pageKind, repoInfo, prNum, prTitle, prUrl, bugIds) // If there"s no link container, then we create a new one linkContainer = document.createElement("p"); linkContainer.id = ATTACH_CONTAINER_ID; - linkContainer.className = "subtext"; + linkContainer.style.cssText = "font-size: 16px;"; } // Remove everything from the link container so we don't end up with @@ -242,8 +244,6 @@ function addAttachLinksToPage(pageKind, repoInfo, prNum, prTitle, prUrl, bugIds) linkContainer.removeChild(linkContainer.firstChild); } - let headerShow = document.querySelector("div.gh-header-show"); - // If there are no bug ids, just return if (bugIds.length == 0) { return; @@ -258,7 +258,8 @@ function addAttachLinksToPage(pageKind, repoInfo, prNum, prTitle, prUrl, bugIds) linkContainer.appendChild(bugLink); }); - headerShow.appendChild(linkContainer); + let headerShow = document.querySelector('[data-component="PH_Navigation"]').parentElement; + headerShow.insertAdjacentElement('beforebegin',linkContainer); } @@ -267,7 +268,7 @@ function createBugsList(bugIds){ if (bugsListContainer == null) { bugsListContainer = document.createElement("p"); bugsListContainer.id = LIST_CONTAINER_ID; - bugsListContainer.className = "subtext"; + bugsListContainer.style.cssText = "font-size: 16px;"; } // Remove everything from container so we don't have duplicates @@ -310,8 +311,8 @@ function addBugListToPage(pageKind, bugIds) { parentElement.insertBefore(createBugsList(bugIds), insertBeforeEl); } else if (pageKind == "pr") { - parentElement = document.querySelector('div.gh-header-show'); - parentElement.appendChild(createBugsList(bugIds)); + parentElement = document.querySelector('[data-component="PH_Navigation"]').parentElement; + parentElement.insertAdjacentElement('beforebegin', createBugsList(bugIds)); } } @@ -331,7 +332,7 @@ function addMergeLinks(pageKind, repoInfo, prNum, prTitle, prUrl, prState, bugId // If there"s no link container, then we create a new one linkContainer = document.createElement("p"); linkContainer.id = MERGE_CONTAINER_ID; - linkContainer.className = "subtext"; + linkContainer.style.cssText = "font-size: 16px;"; } // Removes everything from the link container so we don't end up @@ -380,7 +381,7 @@ function addMergeLinks(pageKind, repoInfo, prNum, prTitle, prUrl, prState, bugId } }); - let headerShow = document.querySelector("div.gh-header-show"); + let headerShow = document.querySelector('[data-component="PH_Navigation"]').parentElement; let separator = document.createTextNode(", "); if (author && prNum && commitSha && commitUrl) { @@ -419,7 +420,7 @@ function addMergeLinks(pageKind, repoInfo, prNum, prTitle, prUrl, prState, bugId }); } - headerShow.appendChild(linkContainer); + headerShow.insertAdjacentElement('beforebegin',linkContainer); } @@ -511,6 +512,15 @@ module.exports = { BUG_BASE_URL, getAttachLinks, getBugIdsFromPRTitle, + getPRNum, + getPRState, + getPRTitle, + getSelectedTab, MERGE_CONTAINER_ID, + PR_STATE_CLOSED, PR_STATE_MERGED, + PR_STATE_OPEN, + PR_STATE_UNKNOWN, + TAB_CONVERSATION, + TAB_OTHER, }