4.3. Create the Layout of the Report

Klaros-Testmanagement uses the JBoss Seam PDF framework to render the report and fill the retrieved data into the layout template.

  1. We will start with an empty document that contains a header and footer definition.

    <p:document xmlns:ui="http://java.sun.com/jsf/facelets"
        xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://jboss.org/products/seam/pdf"
        title="Klaros-Testmanagement Test Plan Report" marginMirroring="true"
        author="#{user.name}" creator="#{user.name}" pageSize="A4">
        <f:facet name="header">
            <p:font size="8">
                <p:header borderWidthBottom="0.1" borderColorBottom="black"
                    borderWidthTop="0" alignment="center">
                    <p:text value="Test Run report - generated #{date} by #{user.name}" />
                </p:header>
                <p:footer borderWidthTop="0.1" borderColorTop="black"
                    borderWidthBottom="0" alignment="center">
                    <p:text value="Page " />
                    <p:pageNumber />
                    <p:text value=" - Created with Klaros-Testmanagement (www.klaros-testmanagement.com)" />
                </p:footer>
            </p:font>
        </f:facet>
    
        <!-- here goes the content -->
    
    </p:document>
    
    

    This snippet creates a header that is displayed on every page. It contains the date and the name of the Klaros-Testmanagement user who created the report. The footer contains the page number and an informative text that the report was created with Klaros-Testmanagement. Note that for the header the borderWidthBottom attribute was set to provide a separation from the following text, while for the footer the borderWidthTop attribute was used to create a single line for separation.

  2. Next we create a front page accumulating the main information about the report.

    The layout of the front page is done using <p:paragraph> elements for formatting. These elements use alignment and spacing to define the position where the text will be placed. To change the font of the paragraphs, you can use the <p:font> element and use its style and size attributes to highlight certain parts.

      <p:paragraph alignment="center" spacingAfter="100">
            <p:text value="" />
        </p:paragraph>
        <p:font style="bold" size="32">
            <p:paragraph alignment="center" spacingAfter="75">
                <p:text value="Test Run Report" />
            </p:paragraph>
        </p:font>
        <p:font style="normal" size="12">
            <p:paragraph alignment="center" spacingAfter="5">
                <p:text value="Created by" />
            </p:paragraph>
        </p:font>
        <p:font style="bold" size="16">
            <p:paragraph alignment="center" spacingAfter="5">
                <p:text value="#{user.name} (#{user.email})" />
            </p:paragraph>
        </p:font>
        <p:font style="normal" size="12">
            <p:paragraph alignment="center" spacingAfter="5">
                <p:text value="on" />
            </p:paragraph>
        </p:font>
        <p:font style="bold" size="16">
            <p:paragraph alignment="center" spacingAfter="75">
                <p:text value="#{date}" />
            </p:paragraph>
        </p:font>
        <p:font style="normal" size="12">
            <p:paragraph alignment="center" spacingAfter="30">
                <p:text value="Testsuite " />
                <p:text value="#{testSuite.name}" />
                <p:text value=" - " />
                <p:text value="#{testSuite.shortname}" />
                <p:text value=" - revision " />
                <p:text value="#{testSuite.revisionId}" />
            </p:paragraph>
            <p:paragraph alignment="center" spacingAfter="5">
                <p:text value="SUT: " />
                <p:text value="#{testSuite.sut.name}" />
                <p:text value=" - " />
                <p:text value="#{testSuite.sut.productversion}" />
            </p:paragraph>
        </p:font>
        <p:newPage />
    

    An image of a sample front page can be found below.

    A Sample Front Page of a Report

    Figure 4.10. A Sample Front Page of a Report


    As you might have noticed from the header and footer definition, values stored in the context can be accessed by preceding the context variable enclosed in curly brackets with a #, e.g. #{user.name}. The context variable for the user is automatically inserted into the context by Klaros-Testmanagement. You might remember the part where we prepared the data for this report and added the test suite to the context.

    context.add("testSuite", testSuite);
    
    

    For the front page we access this context variable to retrieve the data that came with this test suite, e.g. #{testSuite.name}, #{testSuite.shortname}, and #{testSuite.revisionId}. You can find the accessible attributes in the Klaros-Testmanagement API Documentation reference in the online documentation. All attributes that have a getter can be accessed, e.g. for #{testSuite.name} see the interface IKlarosTestSuite where you will find the method getName. To add a page break to the document, you can use

      <p:newPage />
    
  3. Now that we have a front page we can start to fill in the data we prepared and stored in the context. To give a quick overview over the test results we will start with a pie chart displaying the amount of successful, failed, skipped, and errorneous test case results in this test suite.

      <p:paragraph horizontalAlignment="center" spacingAfter="25">
            <p:piechart title="Testresults per Testrun" direction="anticlockwise"
                circular="true" startAngle="30" labelGap="0.1" labelLinkPaint="black"
                plotBackgroundPaint="white" labelBackgroundPaint="white" is3D="true"
                borderVisible="false">
                <p:series key="results">
                    <p:data key="Error [#{error.size}]" value="#{error.size}"
                        sectionPaint="#FF0A0A" />
                    <p:data key="Success [#{success.size}]" value="#{success.size}"
                        sectionPaint="#33CC00" />
                    <p:data key="Failure [#{failure.size}]" value="#{failure.size}"
                        sectionPaint="#FFCC00" explodedPercent=".2" />
                    <p:data key="Skipped [#{skipped.size}]" value="#{skipped.size}"
                        sectionPaint="#FFFFFF" />
                </p:series>
            </p:piechart>
        </p:paragraph>
    

    An example of a generated pie chart can be found below.

    A Generated Pie Chart

    Figure 4.11. A Generated Pie Chart


    For a detailed description about how to layout the pie chart and many other charts, please check the seam-pdf documentation. Again you might remember that we added four List<KlarosTestCaseResult> objects to the context:

      context.add("error", error);
      context.add("failure", failure);
      context.add("success", success);
      context.add("skipped", skipped);
    

    These four objects represent the results of our test suite. We can simply use the size of the collections representing the data for the pie chart:

    <p:data key="Error [#{error.size}]" value="#{error.size}" sectionPaint="#FF0A0A" />
    
  4. Next we want to print a page for each Test Case contained in the test suite and present its data in a table.

    As this might get a bit lengthy, only fragments will be presented here. An example can be found below.

    The Header of a Test Case Report

    Figure 4.12. The Header of a Test Case Report


    First we need to loop over all test cases of the test suite:

      <ui:repeat value="#{testSuite.testCases}" var="tc">
        ...
      </ui:repeat>
    

    A loop can be done using the <ui:repeat> element. The attribute value gets a collection and the attribute var gets the name of a variable that can be used for further processing, e.g. tc. With every step through the loop, the variable tc will get the next element from the collection.

    Now for the table. We need a two column table and the width of the second column should be three times the size of the first. The code is as follows:

      <p:table columns="2" widths="1 3">
        ...
      </p:table>
    

    Let's get some data into the table. Therefore we have to describe what each table cell will look like. This code goes inside the <p:table> element.

      <p:cell horizontalAlignment="right">
            <p:font style="bold" size="8">
                <p:paragraph>
                    <p:text value="Project: " />
                </p:paragraph>
            </p:font>
        </p:cell>
        <p:cell>
            <p:paragraph alignment="left">
                <p:text value="#{tc.configuration.name}" />
            </p:paragraph>
        </p:cell>
        <p:cell horizontalAlignment="right">
            <p:font style="bold" size="8">
                <p:paragraph>
                    <p:text value="Creator: " />
                </p:paragraph>
            </p:font>
        </p:cell>
        <p:cell>
            <p:paragraph alignment="left">
                <p:text value="#{tc.creator.name}" />
            </p:paragraph>
        </p:cell>
        ...
    

    Since we defined that the table should be two columns wide, two <p:cell> elements build one row in the table. In the code above the first row holds the name of the project while the second line holds the name of the user who created the test case. A cell can also contain text formatting information as you can see from the <p:font> elements.

  5. Next we need the test case results for the test case being displayed. This is where another element comes in. If there is no test result for a test case contained in the test suite an informative text should be displayed instead of just leaving a blank space.

      <ui:fragment rendered="#{tc.results.isEmpty()}">
            <p:font style="normal" size="14">
                <p:paragraph alignment="left" spacingAfter="15"
                    indentationLeft="10">
                    <p:text value="No test runs found for this Test Case." />
                </p:paragraph>
            </p:font>
        </ui:fragment>
    

    Notice the <ui:fragment rendered="..."> element. This part of the document is only integrated into the pdf if the condition defined in the rendered attribute evaluates to true. This could be compared to an if-clause in modern programming languages. In the expression

    #{tc.results.isEmpty()}

    the isEmpty() method of a the test case result list is called which evaluates to a boolean value. Next we have to define the block that displays the data in case there are test results present for the test case:

      <ui:fragment rendered="#{!tc.results.isEmpty()}">
        ...
      </ui:fragment>
    

    Note the ! which is used to negate the boolean expression we used in the block before. The code inside this expression is called if there is at least one test case result.

  6. And now we loop over the results of the test cases, which will include each test run for a test case. An example of a Test Run can be found below.

      <ui:repeat value="#{tc.results.toArray()}" var="tres">
        <!-- Start Test Result summary (equivalent to Test Run) -->
        ...
      </ui:repeat>
    

    This will provide us with the test results of a test case in the variable tres. The next pieces of code include the displaying of a summary of the test result which is quite similar to the code used displaying the test case summary. You can have a look at it in the attached sourcefile. The next thing to display are the test case steps and their results for the current test run.

  7. Therefore another loop is added inside the Test Result loop.

      <!-- Start Test Step Result summary -->
        <ui:fragment rendered="#{!tres.stepResults.isEmpty()}">
            <ui:repeat value="#{tres.stepResults}" var="tstepResult">
            ...
            </ui:repeat>
        </ui:fragment>
        <ui:fragment rendered="#{tres.stepResults.isEmpty()}">
            <p:font style="normal" size="10">
                <p:paragraph alignment="left" spacingAfter="35"
                    indentationLeft="25">
                    <p:text value="No test step results found." />
                </p:paragraph>
            </p:font>
        </ui:fragment>
    

    Inside the <ui:repeat> </ui:repeat> block a table is created. This table should be displayed if there are any results for the test case step. The table displays the precondition, action, and postcondition of the test case step and also the test case step result, the test case step summary, and the test case step description. The test case step result cell of the table will get coloured according to the result, e.g. green if the step passed. Each table will have the number of the step as a headline.

  8. Displaying the step number.

      <p:font style="bold" size="8">
          <p:paragraph indentationLeft="20">
              <p:text value="Step #{tres.stepResults.indexOf(tstepResult)+1}" />
          </p:paragraph>
      </p:font>
    

    Here you can see how to retrieve the index of the test case step result from the list of test results. tstepResult is the variable from the inner loop, while tres ist the variable from the outer loop. As counting starts at zero we have to increment the retrieved value, otherwise the first step would be step 0.

  9. Displaying the table if the test case has at least one test case step defined.

      <p:table columns="3" widths="3 3 3" spacingBefore="5"
            rendered="#{tc.testCaseSteps!=null and tc.testCaseSteps.size() > 0}">
        ...
      </p:table>
    
  10. Retrieving the test case step properties (precondition, action, postcondition)

      <p:cell horizontalAlignment="left">
        <p:font style="normal" size="8">
            <p:paragraph>
                <p:text
                value="#{tc.testCaseSteps.get(tres.stepResults.indexOf(tstepResult)).precondition}" />
            </p:paragraph>
        </p:font>
      </p:cell>
    

    To get to the test case step properties we have to access the test case to retrieve the data. From the list of test case steps we retrieve the test case step via the index in the list of the test case step results and then the precondition can be accessed. The same applies to the action and the postcondition of the test case step.

  11. Colouring a cell depending on the test step result.

        <ui:fragment rendered="#{tstepResult.isPassed()}">
            <p:cell backgroundColor="rgb(0,255,0)"
                horizontalAlignment="center">
                <p:font style="normal" size="8">
                    <p:paragraph>
                        <p:text value="Passed" />
                    </p:paragraph>
                </p:font>
            </p:cell>
        </ui:fragment>
        <ui:fragment rendered="#{tstepResult.isError()}">
            <p:cell backgroundColor="rgb(255,0,0)"
                horizontalAlignment="center">
                <p:font style="normal" size="8">
                    <p:paragraph>
                        <p:text value="Error" />
                    </p:paragraph>
                </p:font>
            </p:cell>
        </ui:fragment>
        <ui:fragment rendered="#{tstepResult.isFailure()}">
            <p:cell backgroundColor="rgb(255,215,0)"
                horizontalAlignment="center">
                <p:font style="normal" size="8">
                    <p:paragraph>
                        <p:text value="Failed" />
                    </p:paragraph>
                </p:font>
            </p:cell>
        </ui:fragment>
        <ui:fragment rendered="#{tstepResult.isSkipped()}">
            <p:cell horizontalAlignment="center">
                <p:font style="normal" size="8">
                    <p:paragraph>
                        <p:text value="Skipped" />
                    </p:paragraph>
                </p:font>
            </p:cell>
      </ui:fragment>
    

    The cells are rendered depending on the status of the test case step, therefore the methods isError(), isFailure() and so on are called for the test result object under processing. The cell gets its colour by setting the backgroundColor attribute to the desired rgb value.