From 548b921211f036e1504f35e04f97448202c704f1 Mon Sep 17 00:00:00 2001 From: Stanyslas Bres Date: Mon, 10 Jun 2024 11:28:40 +0200 Subject: [PATCH 1/5] refactor: use templated strings instead of react --- package.json | 13 +- src/components/DetailPage/DetailPage.tsx | 81 --------- .../DetailPage/__tests__/DetailPage.test.tsx | 24 --- .../__snapshots__/DetailPage.test.tsx.snap | 113 ------------ src/components/DetailPage/index.ts | 3 - src/components/SummaryPage/SummaryPage.tsx | 87 --------- .../__tests__/SummaryPage.test.tsx | 26 --- .../__snapshots__/SummaryPage.test.tsx.snap | 170 ------------------ src/components/SummaryPage/index.ts | 3 - src/lib/reporters/{html.ts => html/index.ts} | 18 +- src/lib/reporters/html/pages/details.ts | 69 +++++++ src/lib/reporters/html/pages/summary.ts | 72 ++++++++ 12 files changed, 149 insertions(+), 530 deletions(-) delete mode 100644 src/components/DetailPage/DetailPage.tsx delete mode 100644 src/components/DetailPage/__tests__/DetailPage.test.tsx delete mode 100644 src/components/DetailPage/__tests__/__snapshots__/DetailPage.test.tsx.snap delete mode 100644 src/components/DetailPage/index.ts delete mode 100644 src/components/SummaryPage/SummaryPage.tsx delete mode 100644 src/components/SummaryPage/__tests__/SummaryPage.test.tsx delete mode 100644 src/components/SummaryPage/__tests__/__snapshots__/SummaryPage.test.tsx.snap delete mode 100644 src/components/SummaryPage/index.ts rename src/lib/reporters/{html.ts => html/index.ts} (90%) create mode 100644 src/lib/reporters/html/pages/details.ts create mode 100644 src/lib/reporters/html/pages/summary.ts diff --git a/package.json b/package.json index cc8a70b..154644a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "commander": "^5.0.0", "ncp": "^2.0.0", "rimraf": "^3.0.2", - "semantic-ui-react": "^0.88.2", "type-coverage-core": "^2.23.0" }, "scripts": { @@ -31,19 +30,14 @@ "devDependencies": { "@babel/core": "^7.9.0", "@babel/preset-env": "^7.9.0", - "@babel/preset-react": "^7.9.4", "@babel/preset-typescript": "^7.9.0", "@commitlint/cli": "^8.3.5", "@commitlint/config-conventional": "^8.3.4", "@testing-library/jest-dom": "^6.4.2", - "@testing-library/react": "^14.2.1", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.2.5", "@types/ncp": "^2.0.3", "@types/node": "^20.11.17", - "@types/react": "^16.9.23", - "@types/react-dom": "^16.9.5", - "@types/testing-library__react": "^9.1.3", "@typescript-eslint/eslint-plugin": "^2.25.0", "@typescript-eslint/parser": "^2.25.0", "babel-jest": "^25.1.0", @@ -51,7 +45,6 @@ "eslint-config-prettier": "^6.10.1", "eslint-plugin-jest": "^23.8.2", "eslint-plugin-prettier": "^3.1.2", - "eslint-plugin-react": "^7.19.0", "github-release-notes": "^0.17.1", "husky": "^4.2.3", "jest": "^29.3.1", @@ -59,14 +52,10 @@ "lint-staged": "^10.0.8", "package-json-type": "^1.0.3", "prettier": "^2.0.2", - "react": "^18.2.0", - "react-dom": "^18.2.0", "typescript": "5.0.4" }, "peerDependencies": { - "typescript": "2 || 3 || 4 || 5", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "typescript": "2 || 3 || 4 || 5" }, "husky": { "hooks": { diff --git a/src/components/DetailPage/DetailPage.tsx b/src/components/DetailPage/DetailPage.tsx deleted file mode 100644 index 4daae7f..0000000 --- a/src/components/DetailPage/DetailPage.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React from "react"; -import path from "path"; -import { Container, Table, Header } from "semantic-ui-react"; - -const headers = [ - "Filename", - "Percent", - "Threshold", - "Total", - "Covered", - "Uncovered" -]; - -type Annotation = { - file: string; - line: number; - character: number; - text: string; -}; - -type Props = { - filename: string; - sourceCode: string; - totalCount: number; - correctCount: number; - annotations: readonly Annotation[]; - threshold: number; -}; - -const DetailPage = ({ - filename, - sourceCode, - totalCount, - correctCount, - annotations, - threshold -}: Props) => { - const percentage = totalCount === 0 ? 100 : (correctCount * 100) / totalCount; - const percentageCoverage = percentage.toFixed(2) + "%"; - const isValid = percentage >= threshold; - - return ( - -
- - TypeScript coverage report - -
- - - - {headers.map((header, index) => ( - {header} - ))} - - - - - {filename} - {percentageCoverage} - {threshold}% - {totalCount} - {correctCount} - {totalCount - correctCount} - - -
- - - -`; diff --git a/src/components/DetailPage/index.ts b/src/components/DetailPage/index.ts deleted file mode 100644 index 7569d91..0000000 --- a/src/components/DetailPage/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import DetailPage from "./DetailPage"; - -export default DetailPage; diff --git a/src/components/SummaryPage/SummaryPage.tsx b/src/components/SummaryPage/SummaryPage.tsx deleted file mode 100644 index c5823d3..0000000 --- a/src/components/SummaryPage/SummaryPage.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React from "react"; -import path from "path"; -import { Container, Header, Table } from "semantic-ui-react"; -import { CoverageData } from "../../lib/getCoverage"; - -const headers = ["Filename", "Percent", "Total", "Covered", "Uncovered"]; - -type Props = Omit & { - threshold: number; -}; - -const SummaryPage = ({ - fileCounts, - percentage, - total, - covered, - uncovered, - threshold -}: Props) => { - const isSummaryValid = percentage >= threshold; - - return ( - -
TypeScript coverage report
-
Summary
- - - - {["Percent", "Threshold", "Total", "Covered", "Uncovered"].map( - (header, index) => ( - {header} - ) - )} - - - - - {percentage.toFixed(2) + "%"} - {threshold}% - {total} - {covered} - {uncovered} - - -
-
Files
- - - - {headers.map((header, index) => ( - {header} - ))} - - - - {Array.from(fileCounts).map( - ([filename, { correctCount, totalCount }], index) => { - const percentage = - totalCount === 0 ? 100 : (correctCount * 100) / totalCount; - const percentageCoverage = percentage.toFixed(2) + "%"; - const isValid = percentage >= threshold; - - return ( - - - - {filename} - - - {percentageCoverage} - {totalCount} - {correctCount} - {totalCount - correctCount} - - ); - } - )} - -
-
- ); -}; - -export default SummaryPage; diff --git a/src/components/SummaryPage/__tests__/SummaryPage.test.tsx b/src/components/SummaryPage/__tests__/SummaryPage.test.tsx deleted file mode 100644 index 15cfb21..0000000 --- a/src/components/SummaryPage/__tests__/SummaryPage.test.tsx +++ /dev/null @@ -1,26 +0,0 @@ -/** - * @jest-environment jsdom - */ - -import React from "react"; -import SummaryPage from "../"; -import { render } from "@testing-library/react"; - -describe("SummaryPage component", () => { - it("renders correctly", () => { - const { container } = render( - - ); - - expect(container.firstChild).toMatchSnapshot(); - }); -}); diff --git a/src/components/SummaryPage/__tests__/__snapshots__/SummaryPage.test.tsx.snap b/src/components/SummaryPage/__tests__/__snapshots__/SummaryPage.test.tsx.snap deleted file mode 100644 index 614d110..0000000 --- a/src/components/SummaryPage/__tests__/__snapshots__/SummaryPage.test.tsx.snap +++ /dev/null @@ -1,170 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SummaryPage component renders correctly 1`] = ` -
-

- TypeScript coverage report -

-

- Summary -

- - - - - - - - - - - - - - - - - - - -
- Percent - - Threshold - - Total - - Covered - - Uncovered -
- 100.00% - - 90 - % - - 100 - - 100 - - 0 -
-

- Files -

- - - - - - - - - - - - - - - - - - - -
- Filename - - Percent - - Total - - Covered - - Uncovered -
- - index.html - - - 100.00% - - 100 - - 100 - - 0 -
-
-`; diff --git a/src/components/SummaryPage/index.ts b/src/components/SummaryPage/index.ts deleted file mode 100644 index 4dd0bbd..0000000 --- a/src/components/SummaryPage/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import SummaryPage from "./SummaryPage"; - -export default SummaryPage; diff --git a/src/lib/reporters/html.ts b/src/lib/reporters/html/index.ts similarity index 90% rename from src/lib/reporters/html.ts rename to src/lib/reporters/html/index.ts index cef2bef..14c58eb 100644 --- a/src/lib/reporters/html.ts +++ b/src/lib/reporters/html/index.ts @@ -1,11 +1,9 @@ -import React from "react"; -import react from "react-dom/server"; import path from "path"; import fs from "fs"; import { promisify } from "util"; -import { CoverageData } from "../getCoverage"; -import SummaryPage from "../../components/SummaryPage"; -import DetailPage from "../../components/DetailPage"; +import { CoverageData } from "../../getCoverage"; +import { generateSummaryPage } from "./pages/summary"; +import { generateDetailsPage } from "./pages/details"; const writeFile = promisify(fs.writeFile); const readFile = promisify(fs.readFile); @@ -43,7 +41,7 @@ const includeAssets = (assets: readonly string[]): string => assets.map(includeAsset).join("\n"); const wrapHTMLContent = ( - Component: React.ElementType, + renderFn: Function, props?: Record, options: | { @@ -52,9 +50,7 @@ const wrapHTMLContent = ( } | undefined = {} ): string => { - const content = react.renderToStaticMarkup( - React.createElement(Component, props) - ); + const content = renderFn(props); return ` @@ -81,7 +77,7 @@ export const generate = async ( ): Promise => { // NOTE: Create index file const fileContent = wrapHTMLContent( - SummaryPage, + generateSummaryPage, { fileCounts: data.fileCounts, percentage: data.percentage, @@ -115,7 +111,7 @@ export const generate = async ( const assetsFolder = path.relative(filename, "assets"); const sourceCode = await readFile(filename, "utf-8"); const detailContent = wrapHTMLContent( - DetailPage, + generateDetailsPage, { filename, sourceCode, diff --git a/src/lib/reporters/html/pages/details.ts b/src/lib/reporters/html/pages/details.ts new file mode 100644 index 0000000..df770bf --- /dev/null +++ b/src/lib/reporters/html/pages/details.ts @@ -0,0 +1,69 @@ +const headers = [ + "Filename", + "Percent", + "Threshold", + "Total", + "Covered", + "Uncovered" +]; + +type Annotation = { + file: string; + line: number; + character: number; + text: string; +}; + +type Props = { + filename: string; + sourceCode: string; + totalCount: number; + correctCount: number; + annotations: readonly Annotation[]; + threshold: number; +}; + +export const generateDetailsPage = ({ + filename, + sourceCode, + totalCount, + correctCount, + annotations, + threshold +}: Props) => { + const percentage = totalCount === 0 ? 100 : (correctCount * 100) / totalCount; + const percentageCoverage = percentage.toFixed(2) + "%"; + const isValid = percentage >= threshold; + + return `
+

+ TypeScript coverage report +

+ + + + ${headers.map((header) => ``).join("\n")} + + + + + + + + + + + + +
${header}
${filename}${percentageCoverage}${threshold}%${totalCount}${correctCount}${totalCount - correctCount}
+ diff --git a/src/lib/reporters/html/pages/summary.ts b/src/lib/reporters/html/pages/summary.ts index 2c5b0e5..d131711 100644 --- a/src/lib/reporters/html/pages/summary.ts +++ b/src/lib/reporters/html/pages/summary.ts @@ -1,10 +1,10 @@ import { CoverageData } from "../../../getCoverage"; -const globalHeaders = ["Percent", "Threshold", "Total", "Covered", "Uncovered"]; +const reportHeaders = ["Percent", "Threshold", "Total", "Covered", "Uncovered"]; const headers = ["Filename", "Percent", "Total", "Covered", "Uncovered"]; -type Props = Omit & { +type GenerateSummaryPageContext = Omit & { threshold: number; }; @@ -15,39 +15,39 @@ export const generateSummaryPage = ({ covered, uncovered, threshold -}: Props) => { +}: GenerateSummaryPageContext) => { const isSummaryValid = percentage >= threshold; return ` -
-

TypeScript coverage report

-

Summary

- +
+

TypeScript coverage report

+

Summary

+
- ${globalHeaders.map((header) => ``).join("\n")} + ${reportHeaders.map((header) => ``).join("\n")} - - - - - + + + + +
${header}${header}
${percentage.toFixed(2) + "%"}${threshold}%${total}${covered}${uncovered}${percentage.toFixed(2) + "%"}${threshold}%${total}${covered}${uncovered}
-

Files

- +

Files

+
${headers.map((header) => ``).join("\n")} - ${Array.from(fileCounts).map( - ([filename, { correctCount, totalCount }]) => { + ${Array.from(fileCounts) + .map(([filename, { correctCount, totalCount }]) => { const percentage = totalCount === 0 ? 100 : (correctCount * 100) / totalCount; const percentageCoverage = percentage.toFixed(2) + "%"; @@ -62,10 +62,9 @@ export const generateSummaryPage = ({ - - `; - } - )} + `; + }) + .join("\n")}
${header}
${totalCount} ${correctCount} ${totalCount - correctCount}
`; From 7dcb03b2fca1912e63e07a5307d9d2e1b1db0707 Mon Sep 17 00:00:00 2001 From: Stanyslas Bres Date: Mon, 10 Jun 2024 11:29:14 +0200 Subject: [PATCH 4/5] feat: improve table display by using fixed layout and ellipsis --- assets/{source-file.css => report.css} | 8 +++ src/lib/reporters/html/index.ts | 6 +- src/lib/reporters/html/pages/details.ts | 76 +++++++++++++------------ src/lib/reporters/html/pages/summary.ts | 49 +++++++++------- 4 files changed, 80 insertions(+), 59 deletions(-) rename assets/{source-file.css => report.css} (72%) diff --git a/assets/source-file.css b/assets/report.css similarity index 72% rename from assets/source-file.css rename to assets/report.css index 9a9dd80..9691b6e 100644 --- a/assets/source-file.css +++ b/assets/report.css @@ -1,3 +1,11 @@ +#coverage-table tbody td:nth-child(1) { + direction: rtl; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + text-align: left; +} + .uncovered { background: rgba(235, 26, 26, 0.3); } diff --git a/src/lib/reporters/html/index.ts b/src/lib/reporters/html/index.ts index 14c58eb..e10a0e0 100644 --- a/src/lib/reporters/html/index.ts +++ b/src/lib/reporters/html/index.ts @@ -88,7 +88,7 @@ export const generate = async ( }, { assets: includeAssets([ - "./assets/source-file.css", + "./assets/report.css", "https://cdn.jsdelivr.net/npm/sorttable@1.0.2/sorttable.min.js" ]) } @@ -126,8 +126,8 @@ export const generate = async ( "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.2/codemirror.min.js", "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.2/mode/javascript/javascript.min.js", "https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.58.2/codemirror.min.css", - path.join(assetsFolder, "source-file.js"), - path.join(assetsFolder, "source-file.css") + path.join(assetsFolder, "report.css"), + path.join(assetsFolder, "source-file.js") ]) } ); diff --git a/src/lib/reporters/html/pages/details.ts b/src/lib/reporters/html/pages/details.ts index 282db4a..8760dac 100644 --- a/src/lib/reporters/html/pages/details.ts +++ b/src/lib/reporters/html/pages/details.ts @@ -1,3 +1,5 @@ +import path from "node:path"; + type Annotation = { file: string; line: number; @@ -26,39 +28,43 @@ export const generateDetailsPage = ({ const percentageCoverage = percentage.toFixed(2) + "%"; const isValid = percentage >= threshold; - return `
-

- TypeScript coverage report -

- - - - - - - - - - - - - - - - - - - - - -
FilenamePercentThresholdTotalCoveredUncovered
${filename}${percentageCoverage}${threshold}%${totalCount}${correctCount}${totalCount - correctCount}
- - -
`; + const relativePathToIndex = path.relative(`${filename}.html`, "index.html"); + + return ` +
+

+ TypeScript coverage report +

+ + + + + + + + + + + + + + + + + + + + + +
FilenamePercentThresholdTotalCoveredUncovered
${filename}${percentageCoverage}${threshold}%${totalCount}${correctCount}${totalCount - correctCount}
+ + +
+ `; }; diff --git a/src/lib/reporters/html/pages/summary.ts b/src/lib/reporters/html/pages/summary.ts index d131711..1603af4 100644 --- a/src/lib/reporters/html/pages/summary.ts +++ b/src/lib/reporters/html/pages/summary.ts @@ -1,9 +1,6 @@ +import path from "node:path"; import { CoverageData } from "../../../getCoverage"; -const reportHeaders = ["Percent", "Threshold", "Total", "Covered", "Uncovered"]; - -const headers = ["Filename", "Percent", "Total", "Covered", "Uncovered"]; - type GenerateSummaryPageContext = Omit & { threshold: number; }; @@ -22,27 +19,35 @@ export const generateSummaryPage = ({

TypeScript coverage report

Summary

- +
- ${reportHeaders.map((header) => ``).join("\n")} + + + + + - - - - - + + + + +
${header}PercentThresholdTotalCoveredUncovered
${percentage.toFixed(2) + "%"}${threshold}%${total}${covered}${uncovered}${percentage.toFixed(2) + "%"}${threshold}%${total}${covered}${uncovered}

Files

- +
- ${headers.map((header) => ``).join("\n")} + + + + + @@ -53,16 +58,18 @@ export const generateSummaryPage = ({ const percentageCoverage = percentage.toFixed(2) + "%"; const isValid = percentage >= threshold; + const pathToFile = path.join("files", `${filename}.html`); + return ` - - - - - - `; + + + + + + `; }) .join("\n")} From 78f1b3fbbba60cc90daa52443ec51b51931dad1c Mon Sep 17 00:00:00 2001 From: Stanyslas Bres Date: Mon, 10 Jun 2024 11:29:23 +0200 Subject: [PATCH 5/5] chore: improve formatting of templated strings --- src/lib/reporters/html/pages/details.ts | 70 ++++++------ src/lib/reporters/html/pages/summary.ts | 140 ++++++++++++++---------- 2 files changed, 115 insertions(+), 95 deletions(-) diff --git a/src/lib/reporters/html/pages/details.ts b/src/lib/reporters/html/pages/details.ts index 8760dac..7167989 100644 --- a/src/lib/reporters/html/pages/details.ts +++ b/src/lib/reporters/html/pages/details.ts @@ -31,40 +31,40 @@ export const generateDetailsPage = ({ const relativePathToIndex = path.relative(`${filename}.html`, "index.html"); return ` -
${header}FilenamePercentTotalCoveredUncovered
- ${filename} - ${percentageCoverage}${totalCount}${correctCount}${totalCount - correctCount}
+ ${filename} + ${percentageCoverage}${totalCount}${correctCount}${totalCount - correctCount}
- - - - - - - - - - - - - - - - - - - - -
FilenamePercentThresholdTotalCoveredUncovered
${filename}${percentageCoverage}${threshold}%${totalCount}${correctCount}${totalCount - correctCount}
- - -
+
+

+ TypeScript coverage report +

+ + + + + + + + + + + + + + + + + + + + + +
FilenamePercentThresholdTotalCoveredUncovered
${filename}${percentageCoverage}${threshold}%${totalCount}${correctCount}${totalCount - correctCount}
+ + +
`; }; diff --git a/src/lib/reporters/html/pages/summary.ts b/src/lib/reporters/html/pages/summary.ts index 1603af4..422a646 100644 --- a/src/lib/reporters/html/pages/summary.ts +++ b/src/lib/reporters/html/pages/summary.ts @@ -1,10 +1,40 @@ import path from "node:path"; -import { CoverageData } from "../../../getCoverage"; +import type { CoverageData } from "../../../getCoverage"; type GenerateSummaryPageContext = Omit & { threshold: number; }; +const generateSummaryTableRow = ({ + filename, + totalCount, + correctCount, + threshold +}: { + filename: string; + correctCount: number; + totalCount: number; + threshold: number; +}) => { + const percentage = totalCount === 0 ? 100 : (correctCount * 100) / totalCount; + const percentageCoverage = percentage.toFixed(2) + "%"; + const isValid = percentage >= threshold; + + const pathToFile = path.join("files", `${filename}.html`); + + return ` + + + ${filename} + + ${percentageCoverage} + ${totalCount} + ${correctCount} + ${totalCount - correctCount} + + `; +}; + export const generateSummaryPage = ({ fileCounts, percentage, @@ -16,63 +46,53 @@ export const generateSummaryPage = ({ const isSummaryValid = percentage >= threshold; return ` -
-

TypeScript coverage report

-

Summary

- - - - - - - - - - - - - - - - - - - -
PercentThresholdTotalCoveredUncovered
${percentage.toFixed(2) + "%"}${threshold}%${total}${covered}${uncovered}
-

Files

- - - - - - - - - - - - ${Array.from(fileCounts) - .map(([filename, { correctCount, totalCount }]) => { - const percentage = - totalCount === 0 ? 100 : (correctCount * 100) / totalCount; - const percentageCoverage = percentage.toFixed(2) + "%"; - const isValid = percentage >= threshold; - - const pathToFile = path.join("files", `${filename}.html`); - - return ` - - - - - - - `; - }) - .join("\n")} - -
FilenamePercentTotalCoveredUncovered
- ${filename} - ${percentageCoverage}${totalCount}${correctCount}${totalCount - correctCount}
-`; +
+

TypeScript coverage report

+

Summary

+ + + + + + + + + + + + + + + + + + + +
PercentThresholdTotalCoveredUncovered
${percentage.toFixed(2) + "%"}${threshold}%${total}${covered}${uncovered}
+

Files

+ + + + + + + + + + + + ${Array.from(fileCounts) + .map(([filename, { correctCount, totalCount }]) => + generateSummaryTableRow({ + filename, + correctCount, + totalCount, + threshold + }) + ) + .join("\n")} + +
FilenamePercentTotalCoveredUncovered
+
+ `; };