Skip to content

Commit

Permalink
feat: change coverage processing
Browse files Browse the repository at this point in the history
  • Loading branch information
AKharytonchyk committed Apr 19, 2024
1 parent aa6ae71 commit e7122b8
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 78 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"cSpell.words": [
"mergable",
"mergeable"
"mergeable",
"tanstack"
]
}
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"build": "react-scripts build && cp parsed-summary.txt build/ | true",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
],
"rules": {
"no-unused-vars": "warn",
"max-len": ["warn", { "code": 250 }]
}
},
"browserslist": {
"production": [
Expand Down
66 changes: 66 additions & 0 deletions scripts/update.coverage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// get changes from the last commit
const fs = require("fs");
const path = require("path");
const lz = require("lz-string");
const root = path.resolve(__dirname, "..");
const parsedSummaryPath = path.resolve(root, "parsed-summary.txt");
const coveragePath = path.resolve(root, "coverage");
const repositories = fs.readdirSync(coveragePath).flatMap((owner) =>
fs
.readdirSync(path.resolve(coveragePath, owner))
.map((repo) => `${owner}/${repo}`)
.map((repo) => ({
name: repo,
path: path.resolve(coveragePath, repo),
report: path.resolve(coveragePath, repo, "report.json"),
coverage: path.resolve(coveragePath, repo, "coverage-summary.json"),
}))
);

const parsedSummary = fs.existsSync(parsedSummaryPath)
? JSON.parse(
lz.decompressFromBase64(fs.readFileSync(parsedSummaryPath, "utf-8"))
)
: {};

// console.warn("parsedSummary", parsedSummary);

repositories.forEach((repo) => {
if (!fs.existsSync(repo.coverage)) {
return;
}

const { total } = JSON.parse(fs.readFileSync(repo.coverage, "utf-8"));
const { current: storedCoverage } = parsedSummary[repo.name] || {};

const updatedCoverage = Object.keys(total).reduce((acc, key) => {
const current = total[key].pct;
const previous = storedCoverage?.[key] || 0;

console.warn("storedCoverage", storedCoverage);
const diff = current - previous;

if (acc.current === undefined) {
acc.current = {};
}
if (acc.previous === undefined) {
acc.previous = {};
}
if (acc.diff === undefined) {
acc.diff = {};
}

acc.current[key] = current;
acc.previous[key] = previous;
acc.diff[key] = diff;
acc.changed = diff !== 0 && previous !== 0;

return acc;
}, {});

parsedSummary[repo.name] = updatedCoverage;
fs.rmSync(repo.path, { recursive: true });
});

console.warn("parsedSummary", parsedSummary);
fs.writeFileSync(parsedSummaryPath, lz.compressToBase64(JSON.stringify(parsedSummary)));
2 changes: 1 addition & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { RouterProvider, createHashRouter } from "react-router-dom";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ScopedCssBaseline } from "@mui/material";
import LandingPage from "./pages/LandingPage";
import { Coverage } from "./pages/Coverage";
import { Dashboard } from "./components/Dashboard";
import { Coverage } from "./pages/Coverage";

const router = createHashRouter([
{
Expand Down
202 changes: 128 additions & 74 deletions src/pages/Coverage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,99 +10,186 @@ import {
TableContainer,
TableHead,
TableRow,
Tooltip,
Typography,
} from "@mui/material";
import { Navigate } from "react-router-dom";
import { ConfigContext } from "../App";
import ErrorMessage from "../components/MissingCoverageErrorMessage";
import lz from "lz-string";
import { ArrowDownward, ArrowUpward } from "@mui/icons-material";

interface CoverageData {
lines: { pct: number };
statements: { pct: number };
functions: { pct: number };
branches: { pct: number };
interface CoverageDetails {
current: CoverageValues;
previous: CoverageValues;
diff: CoverageValues;
changed: string;
}

interface RepositoryData {
repoName: string;
interface CoverageValues {
lines: number;
statements: number;
functions: number;
branches: number;
}

interface CoverageResponse {
[key: string]: CoverageDetails;
}

const coverageStyles = (coverage: number) => {
console.log(coverage, coverage < 60);
if (coverage < 60) return { color: "#f44336", fontWeight: "bolder" };
if (coverage < 80) return { color: "#ed6c02", fontWeight: "bold" };
return { color: "#4caf50", fontWeight: "normal" };
};

const calculateSummary = (tableData: RepositoryData[]) => ({
lines: (
tableData.reduce((acc: any, row: { lines: any; }) => acc + row.lines, 0) / tableData.length
).toFixed(2),
statements: (
tableData.reduce((acc: any, row: { statements: any; }) => acc + row.statements, 0) / tableData.length
).toFixed(2),
functions: (
tableData.reduce((acc: any, row: { functions: any; }) => acc + row.functions, 0) / tableData.length
).toFixed(2),
branches: (
tableData.reduce((acc: any, row: { branches: any; }) => acc + row.branches, 0) / tableData.length
).toFixed(2),
});
const coverageDiffIcon = (diff: number, prev: number) => {
if (prev === 0) return;
if (diff > 0) {
return (
<Tooltip title={`+${diff.toFixed(2)}%`}>
<ArrowUpward color="success" />
</Tooltip>
);
}
if (diff < 0) {
return (
<Tooltip title={`-${diff.toFixed(2)}%`}>
<ArrowDownward color="error" />
</Tooltip>
);
}
};

const CoverageCell: React.FC<{ coverage: number; previous: number }> = ({
coverage,
previous,
}) => {
const icon = React.useMemo(
() => coverageDiffIcon(coverage - previous, previous),
[coverage, previous]
);
const styles = React.useMemo(() => coverageStyles(coverage), [coverage]);
return (
<TableCell sx={styles}>
{coverage.toFixed(2)} {icon}
</TableCell>
);
};

const CoverageRow: React.FC<{ data: CoverageDetails; name: string }> = ({
name,
data,
}) => {
return (
<TableRow key={name}>
<TableCell>{name}</TableCell>
<CoverageCell
coverage={data.current.lines}
previous={data.previous.lines}
/>
<CoverageCell
coverage={data.current.statements}
previous={data.previous.statements}
/>
<CoverageCell
coverage={data.current.functions}
previous={data.previous.functions}
/>
<CoverageCell
coverage={data.current.branches}
previous={data.previous.branches}
/>
</TableRow>
);
};

const SummaryRow: React.FC<{ data: CoverageDetails[] }> = ({ data }) => {
const summaryData = React.useMemo(() => {
return {
lines:
data.reduce((acc, curr) => acc + curr.current.lines, 0) / data.length,
statements:
data.reduce((acc, curr) => acc + curr.current.statements, 0) /
data.length,
functions:
data.reduce((acc, curr) => acc + curr.current.functions, 0) /
data.length,
branches:
data.reduce((acc, curr) => acc + curr.current.branches, 0) /
data.length,
};
}, [data]);

return (
<TableRow key="Summary">
<TableCell sx={{ fontWeight: "bold" }}>Summary</TableCell>
<CoverageCell coverage={summaryData.lines} previous={0} />
<CoverageCell coverage={summaryData.statements} previous={0} />
<CoverageCell coverage={summaryData.functions} previous={0} />
<CoverageCell coverage={summaryData.branches} previous={0} />
</TableRow>
);
};

export const Coverage: React.FC = () => {
const { data, isLoading, isError } = useQuery({
queryKey: ["coverage"],
queryFn: async () => {
const coverage = await fetch("coverage.json", {
const coverage = await fetch("parsed-summary.txt", {
headers: {
Accept: "application/json",
Accept: "text/plain",
"Cache-Control": "no-cache",
},
});
return coverage.json() as Promise<Record<string, CoverageData>> ;
const textResponse = await coverage.text();
const text = lz.decompressFromBase64(textResponse);
return JSON.parse(text) as CoverageResponse;
},
retry: false,
});

const dataLength = React.useMemo(() => Object.keys(data || {}).length, [data]);

const { repositorySettings } = React.useContext(ConfigContext);

const tableData = React.useMemo(() => {
const tableRows = React.useMemo(() => {
if (!data) return [];
return Object.keys(data)
const rows = Object.keys(data)
.filter((key) => repositorySettings[key])
.map((key) => {
return {
repoName: key,
lines: data[key].lines.pct,
statements: data[key].statements.pct,
functions: data[key].functions.pct,
branches: data[key].branches.pct,
};
const { current, previous, diff, changed } = data[key];

return (
<CoverageRow
key={key}
name={key}
data={{ current, previous, diff, changed }}
/>
);
});
}, [data, repositorySettings]);

const summaryData = React.useMemo(
() => calculateSummary(tableData),
[tableData]
);
rows.push(<SummaryRow key="Summary" data={Object.values(data)} />);

return rows;
}, [data, repositorySettings]);

if (!localStorage.getItem("token")) {
return <Navigate to="/login" />;
}

return (
<Box padding={2} width={"calc(100vw - 2em)"}>
<h1>Coverage</h1>
{isLoading && <CircularProgress color="inherit" />}
{isError && <ErrorMessage showError={true} />}
{data && tableData.length === 0 && (
{data && dataLength === 0 && (
<Typography variant="h6">
No coverage matching you selected repositories was found!
</Typography>
)}
{tableData && tableData.length > 0 && (
{data && dataLength > 0 && (
<TableContainer component={Paper}>
<Table>
<TableHead>
Expand All @@ -114,40 +201,7 @@ export const Coverage: React.FC = () => {
<TableCell>Branches</TableCell>
</TableRow>
</TableHead>
<TableBody>
{tableData.map((row) => (
<TableRow key={row.repoName}>
<TableCell>{row.repoName}</TableCell>
<TableCell sx={coverageStyles(row.lines)}>
{row.lines}
</TableCell>
<TableCell sx={coverageStyles(row.statements)}>
{row.statements}
</TableCell>
<TableCell sx={coverageStyles(row.functions)}>
{row.functions}
</TableCell>
<TableCell sx={coverageStyles(row.branches)}>
{row.branches}
</TableCell>
</TableRow>
))}
<TableRow key="Summary">
<TableCell sx={{ fontWeight: "bold" }}>Summary</TableCell>
<TableCell sx={{ fontWeight: "bold" }}>
{summaryData.lines}
</TableCell>
<TableCell sx={{ fontWeight: "bold" }}>
{summaryData.statements}
</TableCell>
<TableCell sx={{ fontWeight: "bold" }}>
{summaryData.functions}
</TableCell>
<TableCell sx={{ fontWeight: "bold" }}>
{summaryData.branches}
</TableCell>
</TableRow>
</TableBody>
<TableBody>{tableRows}</TableBody>
</Table>
</TableContainer>
)}
Expand Down

0 comments on commit e7122b8

Please sign in to comment.