diff --git a/src/main/frontend/common/RestClient.tsx b/src/main/frontend/common/RestClient.tsx index 08011764..4c1aa40e 100644 --- a/src/main/frontend/common/RestClient.tsx +++ b/src/main/frontend/common/RestClient.tsx @@ -118,6 +118,7 @@ export async function getExceptionText(stepId: string): Promise { const response = await fetch(`exceptionText?nodeId=${stepId}`); if (!response.ok) throw response.statusText; const text = await response.text(); + if (!text) return []; return text.split("\n"); } catch (e) { console.error(`Caught error when fetching console: '${e}'`); diff --git a/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java b/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java index 6e18fcab..5ed4468e 100644 --- a/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java +++ b/src/main/java/io/jenkins/plugins/pipelinegraphview/consoleview/PipelineConsoleViewAction.java @@ -185,7 +185,8 @@ public HttpResponse getExceptionText(StaplerRequest2 req, StaplerResponse2 rsp) run.checkPermission(Item.READ); String nodeId = req.getParameter("nodeId"); if (nodeId == null) return HttpResponses.error(400, "missing ?nodeId"); - return HttpResponses.text(getNodeExceptionText(nodeId)); + String exceptionText = getNodeExceptionText(nodeId); + return HttpResponses.text(exceptionText != null ? exceptionText : ""); } private AnnotatedLargeText getLogForNode(String nodeId) throws IOException { diff --git a/src/test/java/io/jenkins/plugins/pipelinegraphview/PipelineGraphViewTest.java b/src/test/java/io/jenkins/plugins/pipelinegraphview/PipelineGraphViewTest.java index fd2ad6d7..aa381dd8 100644 --- a/src/test/java/io/jenkins/plugins/pipelinegraphview/PipelineGraphViewTest.java +++ b/src/test/java/io/jenkins/plugins/pipelinegraphview/PipelineGraphViewTest.java @@ -164,4 +164,42 @@ void searchOffScreen(Page p, JenkinsConfiguredWithCodeRule j) throws Exception { .scrollToText("Hello, world 1000!") .scrollToText("Hello, world 1!"); } + + @Test + @ConfiguredWithCode("configure-appearance.yml") + void errorWithMessage(Page p, JenkinsConfiguredWithCodeRule j) throws Exception { + String name = "gh1169"; + WorkflowRun run = TestUtils.createAndRunJob(j, name, "gh1169_errorWithMessage.jenkinsfile", Result.FAILURE); + + // Note that the locator used in stageHasSteps accumulates the error step's message text content into the found + // step name so we just check that instead of also calling stepContainText + new PipelineJobPage(p, run.getParent()) + .goTo() + .hasBuilds(1) + .nthBuild(0) + .goToBuild() + .goToPipelineOverview() + .hasStagesInGraph(1, "Stage") + .selectStageInGraph("Stage") + .stageHasSteps("Error signalError"); + } + + @Issue("GH#1169") + @Test + @ConfiguredWithCode("configure-appearance.yml") + void errorWithNoMessage(Page p, JenkinsConfiguredWithCodeRule j) throws Exception { + String name = "gh1169"; + WorkflowRun run = TestUtils.createAndRunJob(j, name, "gh1169_errorWithNoMessage.jenkinsfile", Result.FAILURE); + + new PipelineJobPage(p, run.getParent()) + .goTo() + .hasBuilds(1) + .nthBuild(0) + .goToBuild() + .goToPipelineOverview() + .hasStagesInGraph(1, "Stage") + .selectStageInGraph("Stage") + .stageHasSteps("Error signal") + .stepDoesNotContainText("Error signal", "null"); + } } diff --git a/src/test/java/io/jenkins/plugins/pipelinegraphview/playwright/PipelineConsole.java b/src/test/java/io/jenkins/plugins/pipelinegraphview/playwright/PipelineConsole.java index fdb405ca..17005948 100644 --- a/src/test/java/io/jenkins/plugins/pipelinegraphview/playwright/PipelineConsole.java +++ b/src/test/java/io/jenkins/plugins/pipelinegraphview/playwright/PipelineConsole.java @@ -77,6 +77,28 @@ public void stepContainsText(String stepName, String textToFind) { } } + public void stepDoesNotContainText(String stepName, String textToNotFind) { + log.info("Checking that the step {} does not contain a log with the text {}", stepName, textToNotFind); + + Locator stepContainer = steps().filter(new Locator.FilterOptions() + .setHas(page.locator( + STEP_NAME_CLASS, + new Page.LocatorOptions().setHasText(Pattern.compile("^" + stepName + "$"))))) + .first(); + + if (!isOpenStep(stepContainer)) { + stepContainer.click(); + } + Locator stepLogs = stepContainer.getByRole(AriaRole.LOG); + assertThat(stepLogs).not().containsText(textToNotFind); + + Locator plainTextLogsLink = stepContainer.getByRole( + AriaRole.LINK, new Locator.GetByRoleOptions().setName("View step as plain text")); + try (Page plainText = page.context().waitForPage(plainTextLogsLink::click)) { + assertThat(plainText.locator("body")).not().containsText(textToNotFind); + } + } + public void stageHasSteps(String step, String... additional) { List expectedSteps = new ArrayList<>(); expectedSteps.add(step); diff --git a/src/test/java/io/jenkins/plugins/pipelinegraphview/playwright/PipelineOverviewPage.java b/src/test/java/io/jenkins/plugins/pipelinegraphview/playwright/PipelineOverviewPage.java index f1195f29..b642f01a 100644 --- a/src/test/java/io/jenkins/plugins/pipelinegraphview/playwright/PipelineOverviewPage.java +++ b/src/test/java/io/jenkins/plugins/pipelinegraphview/playwright/PipelineOverviewPage.java @@ -85,6 +85,11 @@ public PipelineOverviewPage stepContainsText(String stepName, String textToFind) return this; } + public PipelineOverviewPage stepDoesNotContainText(String stepName, String textToNotFind) { + logs.stepDoesNotContainText(stepName, textToNotFind); + return this; + } + public PipelineOverviewPage stageHasSteps(String step, String... additional) { logs.stageHasSteps(step, additional); return this; diff --git a/src/test/resources/io/jenkins/plugins/pipelinegraphview/utils/gh1169_errorWithMessage.jenkinsfile b/src/test/resources/io/jenkins/plugins/pipelinegraphview/utils/gh1169_errorWithMessage.jenkinsfile new file mode 100644 index 00000000..0f63240f --- /dev/null +++ b/src/test/resources/io/jenkins/plugins/pipelinegraphview/utils/gh1169_errorWithMessage.jenkinsfile @@ -0,0 +1,3 @@ +stage("Stage") { + error("Error") +} diff --git a/src/test/resources/io/jenkins/plugins/pipelinegraphview/utils/gh1169_errorWithNoMessage.jenkinsfile b/src/test/resources/io/jenkins/plugins/pipelinegraphview/utils/gh1169_errorWithNoMessage.jenkinsfile new file mode 100644 index 00000000..67daca36 --- /dev/null +++ b/src/test/resources/io/jenkins/plugins/pipelinegraphview/utils/gh1169_errorWithNoMessage.jenkinsfile @@ -0,0 +1,3 @@ +stage("Stage") { + error() +}