Skip to content

Commit

Permalink
Adding test run badge to main page (#1219)
Browse files Browse the repository at this point in the history
  • Loading branch information
craigatk committed Apr 3, 2024
1 parent 9349cd9 commit e38c65e
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 31 deletions.
2 changes: 1 addition & 1 deletion server/server-app/src/main/kotlin/projektor/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,6 @@ fun createAppModule(

single { CoverageBadgeService(get(), get(), get()) }
single { SvgCoverageBadgeCreator() }
single { TestRunBadgeService(get(), get()) }
single { TestRunBadgeService(get(), get(), get()) }
single { SvgTestRunBadgeCreator() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,26 @@ package projektor.badge

import projektor.compare.PreviousTestRunService
import projektor.notification.badge.SvgTestRunBadgeCreator
import projektor.server.api.PublicId
import projektor.server.api.repository.BranchSearch
import projektor.server.api.repository.BranchType
import projektor.testrun.TestRunService

class TestRunBadgeService(
private val previousTestRunService: PreviousTestRunService,
private val testRunBadgeCreator: SvgTestRunBadgeCreator,
private val testRunService: TestRunService,
) {
suspend fun createTestsBadge(publicId: PublicId): String? {
val testRun = testRunService.fetchTestRun(publicId)

return if (testRun != null) {
testRunBadgeCreator.createBadge(testRun.summary.passed)
} else {
null
}
}

suspend fun createTestsBadge(repoName: String, projectName: String?): String? {
val previousTestRun = previousTestRunService.findMostRecentRun(
repoName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@ import io.ktor.server.application.*
import io.ktor.server.routing.*
import io.ktor.server.util.*
import projektor.badge.TestRunBadgeService
import projektor.server.api.PublicId

fun Route.badgeTests(testRunBadgeService: TestRunBadgeService) {
get("/run/{publicId}/badge/tests") {
val publicId = call.parameters.getOrFail("publicId")

val svgBadge = testRunBadgeService.createTestsBadge(PublicId(publicId))

respondWithSvg(svgBadge, call)
}

get("/repo/{orgPart}/{repoPart}/badge/tests") {
val orgPart = call.parameters.getOrFail("orgPart")
val repoPart = call.parameters.getOrFail("repoPart")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package projektor.badge

import io.ktor.http.*
import io.ktor.server.testing.*
import org.junit.jupiter.api.Test
import projektor.ApplicationTestCase
import projektor.incomingresults.randomPublicId
import projektor.util.randomOrgAndRepo
import strikt.api.expectThat
import strikt.assertions.contains
import strikt.assertions.isEqualTo
import strikt.assertions.isNotNull

class TestRunTestsBadgeApplicationTest : ApplicationTestCase() {
@Test
fun `when test run passed should tests create badge`() {
val repoName = randomOrgAndRepo()

val publicId = randomPublicId()

withTestApplication(::createTestApplication) {
handleRequest(HttpMethod.Get, "/run/$publicId/badge/tests") {
testRunDBGenerator.createSimpleTestRunInRepo(publicId, repoName, true, null)
}.apply {
expectThat(response.status()).isEqualTo(HttpStatusCode.OK)
expectThat(response.contentType().toString()).contains(ContentType.Image.SVG.toString())

expectThat(response.content).isNotNull().contains("passing")
}
}
}

@Test
fun `when test run failed should tests create badge`() {
val repoName = randomOrgAndRepo()

val publicId = randomPublicId()

withTestApplication(::createTestApplication) {
handleRequest(HttpMethod.Get, "/run/$publicId/badge/tests") {
testRunDBGenerator.createSimpleFailingTestRunInRepo(publicId, repoName, true, null)
}.apply {
expectThat(response.status()).isEqualTo(HttpStatusCode.OK)
expectThat(response.contentType().toString()).contains(ContentType.Image.SVG.toString())

expectThat(response.content).isNotNull().contains("failing")
}
}
}
}
39 changes: 39 additions & 0 deletions ui/src/Badge/tests/TestRunTestsBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as React from "react";
import TestsBadge from "./TestsBadge";
import { fetchTestsBadge } from "../../service/TestRunService";

interface TestRunTestsBadgeProps {
publicId: string;
repoName: string;
projectName?: string;
}

const TestRunTestsBadge = ({
publicId,
repoName,
projectName,
}: TestRunTestsBadgeProps) => {
const [badgeSvg, setBadgeSvg] = React.useState<string>(null);

React.useEffect(() => {
fetchTestsBadge(publicId)
.then((response) => {
setBadgeSvg(response.data);
})
.catch((_) => {});
}, [setBadgeSvg]);

if (badgeSvg) {
return (
<TestsBadge
badgeSvg={badgeSvg}
repoName={repoName}
projectName={projectName}
/>
);
} else {
return null;
}
};

export default TestRunTestsBadge;
46 changes: 19 additions & 27 deletions ui/src/Coverage/CoverageSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ import OverallCoverageGraphs from "./OverallCoverageGraphs";
import CleanLink from "../Link/CleanLink";
import { makeStyles } from "@material-ui/styles";
import TestRunCoverageBadge from "../Badge/coverage/TestRunCoverageBadge";
import { Grid, Hidden } from "@material-ui/core";

interface CoverageSummaryProps {
publicId: string;
gitMetadata?: TestRunGitMetadata;
}

const useStyles = makeStyles(() => ({
mainSection: {
marginTop: "20px",
},
coverageBadgeSection: {
marginTop: "15px",
marginLeft: "30px",
marginLeft: "15px",
},
}));

Expand All @@ -38,34 +39,25 @@ const CoverageSummary = ({ publicId, gitMetadata }: CoverageSummaryProps) => {

if (coverage) {
return (
<div>
<Grid container>
<Grid item sm={1} xs={12}>
<CleanLink to={`/tests/${publicId}/coverage`}>
<PageTitle title="Coverage" testid="coverage-summary-title" />
</CleanLink>
</Grid>
{gitMetadata && (
<Hidden xsDown>
<Grid
item
sm={4}
xs={12}
className={classes.coverageBadgeSection}
>
<TestRunCoverageBadge
publicId={publicId}
repoName={gitMetadata.repoName}
projectName={gitMetadata.projectName}
/>
</Grid>
</Hidden>
)}
</Grid>
<div className={classes.mainSection}>
<CleanLink to={`/tests/${publicId}/coverage`}>
<PageTitle title="Coverage" testid="coverage-summary-title" />
</CleanLink>

<OverallCoverageGraphs
overallStats={coverage.overallStats}
previousTestRunId={coverage.previousTestRunId}
/>

{gitMetadata && (
<div className={classes.coverageBadgeSection}>
<TestRunCoverageBadge
publicId={publicId}
repoName={gitMetadata.repoName}
projectName={gitMetadata.projectName}
/>
</div>
)}
</div>
);
} else {
Expand Down
21 changes: 20 additions & 1 deletion ui/src/Dashboard/DashboardSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,30 @@ import TestRunCleanupDate from "./TestRunCleanupDate";
import TestRunMessages from "../TestRunMessages/TestRunMessages";
import GitRepoListItem from "./GitRepoListItem";
import DashboardSummaryItem from "./DashboardSummaryItem";
import CleanLink from "../Link/CleanLink";
import { createGitHubUrl } from "../VersionControl/VersionControlHelpers";
import CleanLinkText from "../Link/CleanLinkText";
import { makeStyles } from "@material-ui/styles";
import TestRunTestsBadge from "../Badge/tests/TestRunTestsBadge";

interface DashboardSummaryProps {
publicId: string;
testRunSummary: TestRunSummary;
gitMetadata?: TestRunGitMetadata;
}

const useStyles = makeStyles(() => ({
testsBadgeSection: {
marginLeft: "15px",
},
}));

const DashboardSummary = ({
publicId,
testRunSummary,
gitMetadata,
}: DashboardSummaryProps) => {
const classes = useStyles({});

const {
totalPassingCount,
totalFailureCount,
Expand All @@ -50,6 +59,7 @@ const DashboardSummary = ({
title={hasTests ? "Tests" : "Summary"}
testid="dashboard-summary-title"
/>

<TestRunMessages publicId={publicId} />
<Grid container>
{hasTests ? (
Expand All @@ -66,6 +76,15 @@ const DashboardSummary = ({
totalCount={totalTestCount}
horizontal={false}
/>
{hasTests && gitMetadata && (
<div className={classes.testsBadgeSection}>
<TestRunTestsBadge
publicId={publicId}
repoName={gitMetadata.repoName}
projectName={gitMetadata.projectName}
/>
</div>
)}
</Grid>
) : null}
{totalTestCount > 0 ? (
Expand Down
11 changes: 10 additions & 1 deletion ui/src/TestCase/FailedTestCases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@ import { fetchFailedTestCases } from "../service/TestRunService";
import LoadingState from "../Loading/LoadingState";
import { RouteComponentProps } from "@reach/router";
import PageTitle from "../PageTitle";
import { makeStyles } from "@material-ui/styles";

interface FailedTestCasesProps extends RouteComponentProps {
publicId: string;
}

const useStyles = makeStyles(() => ({
mainSection: {
marginTop: "20px",
},
}));

const FailedTestCases = ({ publicId }: FailedTestCasesProps) => {
const classes = useStyles({});

const [failedTestCases, setFailedTestCases] = React.useState([]);
const [loadingState, setLoadingState] = React.useState(LoadingState.Loading);

Expand All @@ -22,7 +31,7 @@ const FailedTestCases = ({ publicId }: FailedTestCasesProps) => {
}, [setFailedTestCases, setLoadingState]);

return (
<div>
<div className={classes.mainSection}>
<PageTitle title="Failed tests" testid="failed-tests-title" />
<LoadingSection
loadingState={loadingState}
Expand Down
11 changes: 10 additions & 1 deletion ui/src/TestRun/TestRunAllTests.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@ import LoadingSection from "../Loading/LoadingSection";
import { fetchTestRun } from "../service/TestRunService";
import { TestRun } from "../model/TestRunModel";
import PageTitle from "../PageTitle";
import { makeStyles } from "@material-ui/styles";

interface TestRunAllTestsProps extends RouteComponentProps {
publicId: string;
}

const useStyles = makeStyles(() => ({
mainSection: {
marginTop: "20px",
},
}));

const TestRunAllTests = ({ publicId }: TestRunAllTestsProps) => {
const classes = useStyles({});

const [testRun, setTestRun] = React.useState<TestRun>(null);
const [testRunLoadingState, setTestRunLoadingState] = React.useState(
LoadingState.Loading,
Expand All @@ -27,7 +36,7 @@ const TestRunAllTests = ({ publicId }: TestRunAllTestsProps) => {
}, [setTestRun, setTestRunLoadingState]);

return (
<div>
<div className={classes.mainSection}>
<PageTitle title="All tests" testid="test-run-all-tests-title" />
<LoadingSection
loadingState={testRunLoadingState}
Expand Down
5 changes: 5 additions & 0 deletions ui/src/service/TestRunService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ const fetchCoverageBadge = (publicId: string): Promise<AxiosResponse<string>> =>
// @ts-ignore
axiosInstance.get(`/run/${publicId}/badge/coverage`);

const fetchTestsBadge = (publicId: string): Promise<AxiosResponse<string>> =>
// @ts-ignore
axiosInstance.get(`/run/${publicId}/badge/tests`);

const fetchPerformanceResults = (
publicId: string,
): Promise<AxiosResponse<PerformanceResults>> =>
Expand All @@ -193,6 +197,7 @@ export {
fetchCoverageExists,
fetchCoverageGroupFiles,
fetchCoverageBadge,
fetchTestsBadge,
fetchPerformanceResults,
fetchOverallCoverageStats,
fetchSlowTestCases,
Expand Down

0 comments on commit e38c65e

Please sign in to comment.