-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ref: Optimize file fetching code #96
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
import React from "dom-chef"; | ||
import browser from "webextension-polyfill"; | ||
import alpha from "color-alpha"; | ||
import Drop from "tether-drop"; | ||
|
@@ -27,15 +26,14 @@ import { | |
import { colors } from "../common/constants"; | ||
import { createDropdown } from "./utils/dropdown"; | ||
import { | ||
getMetadata, | ||
getComponents, | ||
getCommitReport, | ||
getFlags, | ||
getBranchReport, | ||
} from "../common/fetchers"; | ||
import { print } from "src/utils"; | ||
import { isFileUrl } from "../common/utils"; | ||
import Sentry from '../../common/sentry'; | ||
import Sentry from "../../common/sentry"; | ||
|
||
const globals: { | ||
coverageReport?: FileCoverageReport; | ||
|
@@ -47,7 +45,7 @@ const globals: { | |
prompt?: HTMLElement; | ||
} = {}; | ||
|
||
init() | ||
init(); | ||
|
||
function init(): Promise<void> { | ||
// this event discovered by "reverse-engineering GitHub" | ||
|
@@ -62,22 +60,61 @@ function init(): Promise<void> { | |
} | ||
|
||
async function main(): Promise<void> { | ||
try { | ||
if (!isFileUrl(document.URL)) { | ||
print("file not detected at current URL"); | ||
return; | ||
} | ||
const urlMetadata = getMetadataFromURL(); | ||
if (!urlMetadata) { | ||
print("file not detected at current URL"); | ||
return; | ||
} | ||
|
||
let metadata: FileMetadata; | ||
metadata = await getMetadata(document.URL); | ||
globals.coverageButton = createCoverageButton(); | ||
|
||
globals.coverageButton = createCoverageButton(); | ||
process(urlMetadata).catch((e) => { | ||
print("unexpected error", e); | ||
updateButton("Coverage: ⚠"); | ||
}); | ||
} | ||
|
||
process(metadata) | ||
} catch (e) { | ||
Sentry.captureException(e) | ||
throw e | ||
function getMetadataFromURL(): { [key: string]: string } | null { | ||
const regexp = | ||
/\/(?<owner>.+?)\/(?<repo>.+?)\/blob\/(?<branch>.+?)\/(?<path>.+?)$/; | ||
const matches = regexp.exec(window.location.pathname); | ||
const groups = matches?.groups; | ||
if (!groups) { | ||
return null; | ||
} | ||
|
||
const branch = groups.branch; | ||
const commitMatch = branch.match(/[\da-f]+/); | ||
|
||
// branch could be a commit sha | ||
if ( | ||
commitMatch && | ||
commitMatch[0].length == branch.length && | ||
(groups.branch.length === 40 || branch.length === 7) | ||
) { | ||
// branch is actually a commit sha | ||
let commit = branch; | ||
|
||
// if it's a short sha, we need to get the full sha | ||
if (commit.length === 7) { | ||
const commitLink = document.querySelector( | ||
`[href^="/${groups.owner}/${groups.repo}/tree/${commit}"]` | ||
); | ||
if (!commitLink) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the long sha can be found inside a few links on this page, that's what this code extracts. |
||
throw new Error("Could not find commit link from short sha"); | ||
const longSha = commitLink | ||
.getAttribute("href") | ||
?.match(/[\da-f]{40}/)?.[0]; | ||
if (!longSha) throw new Error("Could not get long sha from commit link"); | ||
commit = longSha; | ||
} | ||
|
||
return { | ||
...groups, | ||
commit, | ||
}; | ||
} | ||
return groups; | ||
} | ||
|
||
async function process(metadata: FileMetadata): Promise<void> { | ||
|
@@ -111,17 +148,16 @@ async function process(metadata: FileMetadata): Promise<void> { | |
previousElement: globals.coverageButton!, | ||
selectedOptions: selectedFlags, | ||
onClick: handleFlagClick, | ||
}) | ||
.then(({ button, list }) => { | ||
globals.flagsButton = button; | ||
globals.flagsDrop = new Drop({ | ||
target: button, | ||
content: list, | ||
classes: "drop-theme-arrows codecov-z1 codecov-bg-white", | ||
position: "bottom right", | ||
openOn: "click", | ||
}); | ||
}) | ||
}).then(({ button, list }) => { | ||
globals.flagsButton = button; | ||
globals.flagsDrop = new Drop({ | ||
target: button, | ||
content: list, | ||
classes: "drop-theme-arrows codecov-z1 codecov-bg-white", | ||
position: "bottom right", | ||
openOn: "click", | ||
}); | ||
}); | ||
} | ||
|
||
const components = await getComponents(metadata); | ||
|
@@ -134,7 +170,7 @@ async function process(metadata: FileMetadata): Promise<void> { | |
return []; | ||
}); | ||
|
||
// TODO: allow setting selected flags for different files at the same time | ||
// TODO: allow setting selected components for different files at the same time | ||
if ( | ||
selectedComponents.length > 0 && | ||
_.intersection(components, selectedComponents).length === 0 | ||
|
@@ -151,34 +187,36 @@ async function process(metadata: FileMetadata): Promise<void> { | |
previousElement: globals.coverageButton!, | ||
onClick: handleComponentClick, | ||
selectedOptions: selectedComponents, | ||
}) | ||
.then(({ button, list }) => { | ||
globals.componentsButton = button; | ||
globals.componentsDrop = new Drop({ | ||
target: button, | ||
content: list, | ||
classes: "drop-theme-arrows codecov-z1 codecov-bg-white", | ||
position: "bottom right", | ||
openOn: "click", | ||
}); | ||
}) | ||
}).then(({ button, list }) => { | ||
globals.componentsButton = button; | ||
globals.componentsDrop = new Drop({ | ||
target: button, | ||
content: list, | ||
classes: "drop-theme-arrows codecov-z1 codecov-bg-white", | ||
position: "bottom right", | ||
openOn: "click", | ||
}); | ||
}); | ||
} | ||
|
||
// If commit sha is defined use that, otherwise just branch name | ||
const getReportFn = metadata.commit ? getCommitReport : getBranchReport; | ||
|
||
let coverageReportResponses: Array<FileCoverageReportResponse>; | ||
try { | ||
if (selectedFlags?.length > 0) { | ||
coverageReportResponses = await Promise.all( | ||
selectedFlags.map((flag) => getCommitReport(metadata, flag, undefined)) | ||
selectedFlags.map((flag) => getReportFn(metadata, flag, undefined)) | ||
); | ||
} else if (selectedComponents?.length > 0) { | ||
coverageReportResponses = await Promise.all( | ||
selectedComponents.map((component) => | ||
getCommitReport(metadata, undefined, component) | ||
getReportFn(metadata, undefined, component) | ||
) | ||
); | ||
} else { | ||
coverageReportResponses = await Promise.all([ | ||
spalmurray-codecov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
await getCommitReport(metadata, undefined, undefined), | ||
await getReportFn(metadata, undefined, undefined), | ||
]); | ||
} | ||
spalmurray-codecov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} catch (e) { | ||
|
@@ -220,7 +258,6 @@ async function process(metadata: FileMetadata): Promise<void> { | |
if (_.isEmpty(coverageReport)) { | ||
updateButton(`Coverage: N/A`); | ||
globals.coverageReport = {}; | ||
await promptPastReport(metadata); | ||
return; | ||
} | ||
|
||
|
@@ -232,40 +269,6 @@ async function process(metadata: FileMetadata): Promise<void> { | |
animateAndAnnotateLines(noVirtLineSelector, annotateLine); | ||
} | ||
|
||
async function promptPastReport(metadata: FileMetadata): Promise<void> { | ||
if (!metadata.branch) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There will never be a need for this code anymore. This was called when the metadata for a branch gave the HEAD commit and there was no coverage for that HEAD commit. If you're on a branch now, Codecov will handle this on the API side and give you the last good coverage. |
||
return; | ||
} | ||
const response = await getBranchReport(metadata); | ||
const regexp = /app.codecov.io\/github\/.*\/.*\/commit\/(?<commit>.*)\/blob/; | ||
const matches = regexp.exec(response.commit_file_url); | ||
const commit = matches?.groups?.commit; | ||
if (!commit) { | ||
throw new Error("Could not parse commit hash from response for past coverage report") | ||
} | ||
const link = document.URL.replace( | ||
`blob/${metadata.branch}`, | ||
`blob/${commit}` | ||
); | ||
globals.prompt = createPrompt( | ||
<span> | ||
Coverage report not available for branch HEAD ( | ||
{metadata.commit.substr(0, 7)}), most recent coverage report for this | ||
branch available at commit <a href={link}>{commit.substr(0, 7)}</a> | ||
</span> | ||
); | ||
} | ||
|
||
function createPrompt(child: any) { | ||
const ref = document.querySelector('[data-testid="latest-commit"]') | ||
?.parentElement?.parentElement; | ||
if (!ref) { | ||
throw new Error("Could not find reference element to render prompt") | ||
} | ||
const prompt = <div className="codecov-mb2 codecov-mx1">{child}</div>; | ||
return ref.insertAdjacentElement("afterend", prompt) as HTMLElement; | ||
} | ||
|
||
function createCoverageButton() { | ||
const rawButton = document.querySelector('[data-testid="raw-button"]'); | ||
if (!rawButton) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,15 +17,34 @@ export async function createDropdown({ | |
previousElement: HTMLElement; | ||
selectedOptions: string[]; | ||
}) { | ||
const editButton = document | ||
.querySelector('[data-testid="more-edit-button"]')! | ||
.closest("div")!; | ||
const dropdownButton = editButton.cloneNode(true) as HTMLElement; | ||
const textNode: HTMLElement = dropdownButton.querySelector('[data-component="IconButton"]')!; | ||
// Build the button out of the Raw/copy/download button group | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're looking at a file at a commit, the old button we were building from does not exist. I adjusted the code to use a button that exists on both types of view (the raw/copy/download group). Additionally, I made the code a bit safer by removing |
||
const rawButton = document | ||
.querySelector('[data-testid="download-raw-button"]')! | ||
.closest("div"); | ||
if (!rawButton) throw new Error("Could not find raw button group"); | ||
const dropdownButton = rawButton.cloneNode(true) as HTMLElement; | ||
// Remove copy button | ||
const copyButton = dropdownButton.querySelector( | ||
'[data-testid="copy-raw-button"]' | ||
); | ||
if (!copyButton) throw new Error("Could not find copy button"); | ||
dropdownButton.removeChild(copyButton); | ||
// Replace download button with dropdown button | ||
const downloadButton = dropdownButton.querySelector( | ||
'[data-testid="download-raw-button"]' | ||
); | ||
if (!downloadButton || !downloadButton.firstChild) | ||
throw new Error("Could not find download button or it is missing children"); | ||
const triangleDownSvg = document.querySelector(".octicon-triangle-down"); | ||
if (!triangleDownSvg) throw new Error("Could not find triangle down svg"); | ||
downloadButton.replaceChild(triangleDownSvg, downloadButton.firstChild); | ||
|
||
const textNode = dropdownButton.querySelector('[data-testid="raw-button"]'); | ||
if (!textNode || !textNode.parentElement) | ||
throw new Error("Could not find textNode"); | ||
textNode.innerHTML = ""; | ||
textNode.ariaDisabled = "false"; | ||
textNode.parentElement!.ariaLabel = tooltip; | ||
textNode.style.padding = `0 ${title.length * 5}px`; | ||
textNode.parentElement.ariaLabel = tooltip; | ||
textNode.appendChild(<span>{title}</span>); | ||
previousElement.insertAdjacentElement("afterend", dropdownButton); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🫡