0

Need to write XSpec test case to test the XSLT, in which multiple modes are used for transformation. But with below test-case, the xspec only tests the output with default mode applied. I wonder if there is a way to test the final output of the transformation.

<!-- input.xml -->
<body>
 <div>
   <p class="Title"><span>My first title</span></p>
   <p class="BodyText"><span style="font-weight:bold">AAAAAAA</span><span>2 Jan 2020</span></p>
 </div>
</body>
<!-- conv.xsl -->
<xsl:template match="/">
        <xsl:apply-templates/>
    </xsl:template>

<!-- default mode : adding text-align attribute where @class=Title -->
<xsl:template match="*[ancestor::body]">
        <xsl:choose>
            <xsl:when test="@class = 'Title'">
                <xsl:element name="{local-name()}">
                    <xsl:copy-of select="@* except @style"/>
                    <xsl:attribute name="text-align" select="'center'"/>
                    <xsl:apply-templates/>
                </xsl:element>
            </xsl:when>
            <xsl:otherwise>
                <xsl:element name="{local-name()}">
                    <xsl:copy-of select="@*"/>
                    <xsl:apply-templates/>
                </xsl:element>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

<!-- bodytext mode : changing element name to <title> where p[@class=Title] -->
<xsl:template match="p[@class]" mode="bodytext">
        <xsl:choose>
            <xsl:when test="@class = 'Title'">
                <title>
                    <xsl:copy-of select="@* except @class"/>
                    <xsl:apply-templates mode="bodytext"/>
                </title>
            </xsl:when>
            <xsl:otherwise>
              <para>
                    <xsl:apply-templates mode="bodytext"/>
              </para>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

<xsl:template match="body">
        <xsl:variable name="data">
            <body>
                <xsl:copy-of select="@*"/>
                <xsl:apply-templates/>
            </body>
        </xsl:variable>
        <xsl:apply-templates select="$data" mode="bodytext"/>
    </xsl:template>

<xsl:template match="node() | @*" mode="#all">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*" mode="#current"/>
        </xsl:copy>
    </xsl:template>

O\P for first <p>:

-- after default mode applied: <p class="Title" text-align="center">. [below xspec tests this o\p]

-- final: <title text-align="center">. [Want to test this o\p]

<!-- test.xspec -->
<x:description xmlns:x="http://www.jenitennison.com/xslt/xspec" stylesheet="conv.xsl">
  <x:scenario label="XSS00001: Testing 'p[@class=Title]' converts to 'title'">
     <x:context href="input.xml" select="/body/div[1]/p[1]"/>
     <x:expect label="Testing 'p' converts to 'title'">
        <title text-align="center">
           <span>My first title</span>
        </title>
     </x:expect>
  </x:scenario>
</x:description>

Any suggestion in this regard would be a great help. Thanks...

sspsujit
  • 301
  • 4
  • 12

2 Answers2

0

I don't think it is solely the use of the modes that doesn't give you the result you want. However, the way you have set up the modes in your XSLT, if you match on that /body/div[1]/p[1] in the XSpec test scenario, you will get the stylesheet applied to only that p element. And obviously for that p there is the match on *[ancestor::body] in the unnamed mode and processing stops in that mode as the other mode is never used from that template.

So you might need to make the body element the context and use a scenario like the following:

<x:scenario label="XSS00002: Testing 'p[@class=Title]' converts to 'title'">
    <x:context>
        <body>
            <div>
                <p class="Title">...</p>
                <p class="BodyText">...</p>
            </div>
        </body>
    </x:context>
    <x:expect label="Testing 'p' converts to 'title'">
        <body>
            <div>
                <title text-align="center">...</title>
                <para>...</para>
            </div>
        </body>
    </x:expect>
</x:scenario>
Martin Honnen
  • 160,499
  • 6
  • 90
  • 110
  • Thanks for pointing out the processing flow. But, what is the other way to set up the modes in xslt, so that the xspec would consider the final o\p? – sspsujit Jul 10 '20 at 05:01
  • @sspsujit, the XSpec considers the final output but your test scenario worked against the final result of just transforming the `p` element and for that `p` element your code solely creates a text-align attribute, it doesn't change the element from `p` to `title`. To reorganize the XSLT it might help to separate the modes into two different stylesheet modules and import them into a third perhaps, then write tests for the first stylesheet, the second and for final one importing the two modules. – Martin Honnen Jul 10 '20 at 09:13
0

Martin is quite right.

Another way of writing would be:

  <x:scenario label="When a document contains 'body//p[@class=Title]'">
     <x:context href="input.xml" />
     <x:expect label="'p' is converted to 'title[@text-align]'"
               test="body/div/title">
        <title text-align="center">
           <span>My first title</span>
        </title>
     </x:expect>
  </x:scenario>

that is,

  • Remove @select from x:context, because you and/or conv.xsl seem to assume the transformation to start always from the document node (/).
  • Add @test to x:expect, because you seem to be interested only in the title element in the transformation result.
AirQuick
  • 61
  • 3