Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions tests/xslt/arithmetic.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { Xslt, XmlParser } from '../../src/index';

describe('XPath arithmetic operations', () => {
let xslt: Xslt;
let xmlParser: XmlParser;

beforeEach(() => {
xslt = new Xslt();
xmlParser = new XmlParser();
});

it('addition: number + 1 returns 2', async () => {
const xml = xmlParser.xmlParse('<page><number>1</number></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="page">
<result><xsl:value-of select="number + 1"/></result>
</xsl:template>
</xsl:stylesheet>`);
const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<result>2</result>');
});

it('subtraction: number - 1 returns 0', async () => {
const xml = xmlParser.xmlParse('<page><number>1</number></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="page">
<result><xsl:value-of select="number - 1"/></result>
</xsl:template>
</xsl:stylesheet>`);
const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<result>0</result>');
});

it('multiplication: number * 2 returns 2', async () => {
const xml = xmlParser.xmlParse('<page><number>1</number></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="page">
<result><xsl:value-of select="number * 2"/></result>
</xsl:template>
</xsl:stylesheet>`);
const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<result>2</result>');
});

it('division: number div 2 returns 5', async () => {
const xml = xmlParser.xmlParse('<page><number>10</number></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="page">
<result><xsl:value-of select="number div 2"/></result>
</xsl:template>
</xsl:stylesheet>`);
const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<result>5</result>');
});

it('modulo: number mod 3 returns 1', async () => {
const xml = xmlParser.xmlParse('<page><number>10</number></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="page">
<result><xsl:value-of select="number mod 3"/></result>
</xsl:template>
</xsl:stylesheet>`);
const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<result>1</result>');
});

it('node + node: first + second returns correct sum', async () => {
const xml = xmlParser.xmlParse('<page><first>3</first><second>4</second></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="page">
<result><xsl:value-of select="first + second"/></result>
</xsl:template>
</xsl:stylesheet>`);
const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<result>7</result>');
});

it('decimal node: price * 2 returns 7', async () => {
const xml = xmlParser.xmlParse('<page><price>3.5</price></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="page">
<result><xsl:value-of select="price * 2"/></result>
</xsl:template>
</xsl:stylesheet>`);
const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<result>7</result>');
});

it('attribute node: @value + 1 returns 6', async () => {
const xml = xmlParser.xmlParse('<page><item value="5"/></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="page">
<result><xsl:value-of select="item/@value + 1"/></result>
</xsl:template>
</xsl:stylesheet>`);
const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<result>6</result>');
});

it('unary negation on node: -number returns -5', async () => {
const xml = xmlParser.xmlParse('<page><number>5</number></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="page">
<result><xsl:value-of select="-number"/></result>
</xsl:template>
</xsl:stylesheet>`);
const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<result>-5</result>');
});

it('empty node-set in arithmetic does not throw', async () => {
// XPath 1.0 typically yields NaN for arithmetic on an empty node-set.
// This test only asserts that xsltProcess resolves without throwing.
const xml = xmlParser.xmlParse('<page><number>1</number></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="page">
<result><xsl:value-of select="missing + 1"/></result>
</xsl:template>
</xsl:stylesheet>`);
await xslt.xsltProcess(xml, stylesheet);
});

it('plain number select still works: select="number" returns 1', async () => {
const xml = xmlParser.xmlParse('<page><number>1</number></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="page">
<result><xsl:value-of select="number"/></result>
</xsl:template>
</xsl:stylesheet>`);
const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<result>1</result>');
});
});
43 changes: 41 additions & 2 deletions tests/xslt/copy-of.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
import assert from 'assert';

import { XmlParser } from '../../src/dom';
import { Xslt } from '../../src/xslt';
import { Xslt, XmlParser } from '../../src/index';

describe('xsl:copy-of', () => {
it('keeps comment content stable across repeated identity transforms', async () => {
const xslt = new Xslt();
const xmlParser = new XmlParser();

const xmlInput = '<root><!--1234567890 1234567890--></root>';
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>`);

const firstInputDoc = xmlParser.xmlParse(xmlInput);
const firstPass = await xslt.xsltProcess(firstInputDoc, stylesheet);
assert.equal(firstPass, xmlInput);

const secondInputDoc = xmlParser.xmlParse(firstPass);
const secondPass = await xslt.xsltProcess(secondInputDoc, stylesheet);
assert.equal(secondPass, firstPass);
});

it('keeps empty comments stable across repeated identity transforms', async () => {
const xslt = new Xslt();
const xmlParser = new XmlParser();

const xmlInput = '<root><!----></root>';
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>`);

const firstInputDoc = xmlParser.xmlParse(xmlInput);
const firstPass = await xslt.xsltProcess(firstInputDoc, stylesheet);
assert.equal(firstPass, xmlInput);

const secondInputDoc = xmlParser.xmlParse(firstPass);
const secondPass = await xslt.xsltProcess(secondInputDoc, stylesheet);
assert.equal(secondPass, firstPass);
});

it('Trivial', async () => {
const xmlSource = `<?xml version="1.0" encoding="UTF-8"?>
<test>
Expand Down
43 changes: 0 additions & 43 deletions tests/xslt/issue-187.test.ts

This file was deleted.

26 changes: 11 additions & 15 deletions tests/xslt/issue-183.test.ts → tests/xslt/template-match.test.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,43 @@
import { Xslt, XmlParser } from '../../src/index';

describe('Issue #183: Template match with nested elements', () => {
it('should match //message when nested under <child>', async () => {
const xslt = new Xslt();
const xmlParser = new XmlParser();
describe('xsl:template match patterns', () => {
let xslt: Xslt;
let xmlParser: XmlParser;

beforeEach(() => {
xslt = new Xslt();
xmlParser = new XmlParser();
});

it('matches //message when nested under a child element', async () => {
const xml = xmlParser.xmlParse('<page><child><message>Hello World.</message></child></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="//message">
<div><xsl:value-of select="."/></div>
</xsl:template>
</xsl:stylesheet>`);

const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<div>Hello World.</div>');
});

it('should match with explicit path /page/child/message', async () => {
const xslt = new Xslt();
const xmlParser = new XmlParser();

it('matches with explicit absolute path /page/child/message', async () => {
const xml = xmlParser.xmlParse('<page><child><message>Hello World.</message></child></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/page/child/message">
<div><xsl:value-of select="."/></div>
</xsl:template>
</xsl:stylesheet>`);

const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<div>Hello World.</div>');
});

it('should match //message when NOT nested (direct child of root)', async () => {
const xslt = new Xslt();
const xmlParser = new XmlParser();

it('matches //message when element is a direct child of root', async () => {
const xml = xmlParser.xmlParse('<page><message>Hello World.</message></page>');
const stylesheet = xmlParser.xmlParse(`<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="//message">
<div><xsl:value-of select="."/></div>
</xsl:template>
</xsl:stylesheet>`);

const result = await xslt.xsltProcess(xml, stylesheet);
expect(result).toBe('<div>Hello World.</div>');
});
Expand Down
Loading