Skip to content

Commit 34dfae9

Browse files
committed
Build support for new github pr experience
1 parent 4386d50 commit 34dfae9

File tree

2 files changed

+132
-13
lines changed

2 files changed

+132
-13
lines changed

src/content/github/pr/constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export const lineSelector = ".js-file-line";
1+
export const oldLineSelector = ".js-file-line";
2+
export const newLineSelector = ".diff-line-row";

src/content/github/pr/main.tsx

Lines changed: 130 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
clearAnimation,
1010
clearAnnotations,
1111
} from "../common/animation";
12-
import { lineSelector } from "./constants";
12+
import { oldLineSelector, newLineSelector } from "./constants";
1313
import { colors } from "../common/constants";
1414
import { print } from "src/utils";
1515
import { getConsent, getPRReport } from "../common/fetchers";
@@ -82,19 +82,41 @@ async function execute() {
8282
updateContainer(head, patch, change);
8383

8484
globals.coverageReport = transformReport(coverageReport.files);
85-
animateAndAnnotateLines(lineSelector, annotateLine);
85+
86+
annotateLines();
8687
}
8788

88-
function createContainer() {
89-
const parent = document.getElementsByClassName("pr-review-tools").item(0)!;
89+
function isNewExperience() {
90+
const toolbar = document.querySelector(
91+
"section[class*=' PullRequestFilesToolbar-module__toolbar']"
92+
);
93+
return !!toolbar;
94+
}
9095

96+
function createContainer() {
9197
const element = (
92-
<div className="codecov-flex float-left mr-4" id="coverage-report-data">
93-
<div className="my-auto mr-6">Loading coverage report...</div>
98+
<div className="ml-auto" id="coverage-report-data">
99+
<div className="ml-auto mr-6">Loading coverage report...</div>
94100
</div>
95101
);
96102

97-
parent.prepend(element);
103+
if (!isNewExperience()) {
104+
// Old experience
105+
const parent = document
106+
.getElementsByClassName("pr-review-tools")
107+
.item(0)?.parentElement;
108+
109+
parent?.insertBefore(element, parent.lastElementChild);
110+
111+
return;
112+
}
113+
114+
// New experience code
115+
const parent = document.querySelector(
116+
"section[class*=' PullRequestFilesToolbar-module__toolbar']"
117+
)!;
118+
119+
parent.insertBefore(element, parent.lastChild!);
98120
}
99121

100122
function getMetadataFromURL(): { [key: string]: string } | null {
@@ -111,7 +133,7 @@ const handleToggleClick: React.MouseEventHandler = (event) => {
111133
const button = event.target as HTMLElement;
112134
const isInactive = button.getAttribute("data-inactive");
113135
if (isInactive == "true") {
114-
animateAndAnnotateLines(lineSelector, annotateLine);
136+
annotateLines();
115137
button.removeAttribute("data-inactive");
116138
button.innerText = "Hide Coverage";
117139
} else {
@@ -172,7 +194,38 @@ function transformReport(filesReport: any) {
172194
return result;
173195
}
174196

175-
function annotateLine(line: HTMLElement) {
197+
function annotateLines() {
198+
if (!isNewExperience()) {
199+
// old selector/annotation logic
200+
animateAndAnnotateLines(oldLineSelector, oldAnnotateLine);
201+
} else {
202+
// new selector/annotation logic
203+
animateAndAnnotateLines(newLineSelector, newAnnotateLine);
204+
}
205+
}
206+
207+
function clearAnimationAndAnnotations() {
208+
if (!isNewExperience()) {
209+
// old selector/annotation logic
210+
clearAnimation(oldLineSelector, oldAnnotateLine);
211+
clearAnnotations((line: HTMLElement) => (line.style.boxShadow = "inherit"));
212+
} else {
213+
// new selector/annotation logic
214+
clearAnimation(newLineSelector, newAnnotateLine);
215+
clearAnnotations((line: HTMLElement) => {
216+
if (line.children.length < 3) {
217+
return;
218+
}
219+
let child = line.lastElementChild as HTMLElement;
220+
if (child.style.boxShadow !== "inherit") {
221+
child.style.boxShadow = "inherit";
222+
return;
223+
}
224+
});
225+
}
226+
}
227+
228+
function oldAnnotateLine(line: HTMLElement) {
176229
if (line.getAttribute("data-split-side") === "left") {
177230
// split diff view: ignore deleted line
178231
return;
@@ -203,9 +256,74 @@ function annotateLine(line: HTMLElement) {
203256
}
204257
}
205258

206-
function clearAnimationAndAnnotations() {
207-
clearAnimation(lineSelector, annotateLine);
208-
clearAnnotations((line: HTMLElement) => (line.style.boxShadow = "inherit"));
259+
function newAnnotateLine(line: HTMLElement) {
260+
const secondChild = line.children[1];
261+
const thirdChild = line.children[2];
262+
263+
if (!secondChild || !thirdChild) {
264+
return;
265+
}
266+
267+
// If the second child of the row is a line number cell (possibly empty), we're looking at a unified diff.
268+
const isUnifiedDiff = line
269+
.querySelectorAll("td[class*=' diff-line-number']")
270+
.values()
271+
.toArray()
272+
.includes(secondChild);
273+
274+
// New line number cell is in cell 2 in a unified diff and cell 3 in a split diff.
275+
const newLineNumberCell = isUnifiedDiff ? secondChild : thirdChild;
276+
277+
// We want to ignore deleted lines.
278+
// If the new line number cell does not contain a line number, then the line was deleted.
279+
if (!newLineNumberCell.textContent) {
280+
return;
281+
}
282+
283+
// This is not a deleted line, grab the line number and find coverage value.
284+
const lineNumber = newLineNumberCell.textContent;
285+
286+
// Get the file name.
287+
// Up to the shared root of the file section then down to the file header
288+
//
289+
// For some reason the text content here contains three invisible bytes
290+
// adding up to one utf-8 character, which we need to remove.
291+
//
292+
// >> e = new TextEncoder()
293+
// >> e.encode(newLineNumberCell.textContent)
294+
// Uint8Array(59) [ 226, 128, 142, 97, 112, 112, 115, 47, 119, 111, … ]
295+
// >> e.encode("apps/worker/services/test_analytics/ta_process_flakes.py")
296+
// Uint8Array(56) [ 97, 112, 112, 115, 47, 119, 111, 114, 107, 101, … ]
297+
// >> e.encode(newLineNumberCell.textContent.slice(1))
298+
// Uint8Array(56) [ 97, 112, 112, 115, 47, 119, 111, 114, 107, 101, … ]
299+
//
300+
// Idk why these are here, but we can just remove them.
301+
302+
const fileNameContainer = line
303+
.closest("div[class^='Diff-module__diffTargetable']")
304+
?.querySelector("h3[class^='DiffFileHeader-module__file-name']");
305+
const fileName = fileNameContainer?.textContent?.slice(1);
306+
if (!fileName) {
307+
return;
308+
}
309+
310+
const status =
311+
globals.coverageReport?.[fileName]?.lines[lineNumber]?.coverage["head"];
312+
if (status == null) {
313+
return;
314+
}
315+
316+
const lineContentCell = newLineNumberCell.nextSibling as HTMLElement;
317+
const borderStylePrefix = "inset 2px 0 ";
318+
if (status === CoverageStatus.COVERED) {
319+
lineContentCell.style.boxShadow = `${borderStylePrefix} ${colors.green}`;
320+
} else if (status === CoverageStatus.UNCOVERED) {
321+
lineContentCell.style.boxShadow = `${borderStylePrefix} ${colors.red}`;
322+
} else if (status === CoverageStatus.PARTIAL) {
323+
lineContentCell.style.boxShadow = `${borderStylePrefix} ${colors.yellow}`;
324+
} else {
325+
lineContentCell.style.boxShadow = "inherit";
326+
}
209327
}
210328

211329
await init();

0 commit comments

Comments
 (0)