123 lines
4.0 KiB
TypeScript
123 lines
4.0 KiB
TypeScript
import { beforeAll, describe, expect, it } from 'vitest';
|
|
import { readFileSync } from 'node:fs';
|
|
import { resolve } from 'node:path';
|
|
import vm from 'node:vm';
|
|
import {
|
|
availableTransformEngines,
|
|
runTransformation,
|
|
} from '../src/transform/transformService';
|
|
import { compileXsltTextToSefJson } from '../src/transform/saxonJsDynamicCompiler';
|
|
|
|
const xmlText = `<?xml version="1.0" encoding="UTF-8"?>
|
|
<library>
|
|
<book id="b1"><title>Practical XSLT</title><author>Ada</author></book>
|
|
<book id="b2"><title>Browser XML</title><author>Lin</author></book>
|
|
</library>`;
|
|
|
|
const xslt10Text = `<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
<xsl:output method="xml" omit-xml-declaration="yes"/>
|
|
<xsl:template match="/library">
|
|
<titles><xsl:for-each select="book"><title><xsl:value-of select="title"/></title></xsl:for-each></titles>
|
|
</xsl:template>
|
|
</xsl:stylesheet>`;
|
|
|
|
const xslt20Text = `<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
<xsl:output method="xml" omit-xml-declaration="yes"/>
|
|
<xsl:template match="/library">
|
|
<books><xsl:value-of select="upper-case(string-join(book/title, '|'))"/></books>
|
|
</xsl:template>
|
|
</xsl:stylesheet>`;
|
|
|
|
const xslt30Text = `<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
<xsl:output method="xml" omit-xml-declaration="yes"/>
|
|
<xsl:mode on-no-match="shallow-copy"/>
|
|
<xsl:template match="book/title/text()">
|
|
<xsl:value-of select="upper-case(.)"/>
|
|
</xsl:template>
|
|
</xsl:stylesheet>`;
|
|
|
|
beforeAll(() => {
|
|
if (window.SaxonJS) return;
|
|
|
|
const scriptPath = resolve(process.cwd(), 'public/vendor/saxon/SaxonJS2.js');
|
|
const script = readFileSync(scriptPath, 'utf8');
|
|
vm.runInContext(script, vm.createContext(window));
|
|
});
|
|
|
|
describe('transform engine registry', () => {
|
|
it('keeps the native browser XSLTProcessor engine selectable', () => {
|
|
expect(availableTransformEngines.map((engine) => engine.id)).toContain(
|
|
'native-xsltprocessor'
|
|
);
|
|
});
|
|
|
|
it('exposes the dynamic SaxonJS engine for XSLT 2.0/3.0', () => {
|
|
expect(availableTransformEngines.map((engine) => engine.id)).toContain(
|
|
'saxon-js-dynamic'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('SaxonJS dynamic compiler', () => {
|
|
it('compiles raw XSLT text to SEF JSON', async () => {
|
|
const sef = await compileXsltTextToSefJson(xslt30Text);
|
|
const parsed = JSON.parse(sef) as { N?: string; target?: string };
|
|
|
|
expect(parsed.N).toBe('package');
|
|
expect(parsed.target).toBe('JS');
|
|
});
|
|
|
|
it('runs an XSLT 1.0 stylesheet', async () => {
|
|
const result = await runTransformation({
|
|
xmlText,
|
|
xsltText: xslt10Text,
|
|
engine: 'saxon-js-dynamic',
|
|
});
|
|
|
|
expect(
|
|
result.diagnostics.filter((item) => item.severity === 'error')
|
|
).toHaveLength(0);
|
|
expect(result.output).toContain('<title>Practical XSLT</title>');
|
|
expect(result.output).toContain('<title>Browser XML</title>');
|
|
});
|
|
|
|
it('runs an XSLT 2.0 stylesheet with XPath 2.0 functions', async () => {
|
|
const result = await runTransformation({
|
|
xmlText,
|
|
xsltText: xslt20Text,
|
|
engine: 'saxon-js-dynamic',
|
|
});
|
|
|
|
expect(
|
|
result.diagnostics.filter((item) => item.severity === 'error')
|
|
).toHaveLength(0);
|
|
expect(result.output).toContain('PRACTICAL XSLT|BROWSER XML');
|
|
});
|
|
|
|
it('runs an XSLT 3.0 stylesheet with xsl:mode on-no-match', async () => {
|
|
const result = await runTransformation({
|
|
xmlText,
|
|
xsltText: xslt30Text,
|
|
engine: 'saxon-js-dynamic',
|
|
});
|
|
|
|
expect(
|
|
result.diagnostics.filter((item) => item.severity === 'error')
|
|
).toHaveLength(0);
|
|
expect(result.output).toContain('<title>PRACTICAL XSLT</title>');
|
|
expect(result.output).toContain('<title>BROWSER XML</title>');
|
|
});
|
|
|
|
it('returns diagnostics for invalid XSLT', async () => {
|
|
const result = await runTransformation({
|
|
xmlText,
|
|
xsltText: '<xsl:stylesheet version="3.0">',
|
|
engine: 'saxon-js-dynamic',
|
|
});
|
|
|
|
expect(result.diagnostics.some((item) => item.severity === 'error')).toBe(
|
|
true
|
|
);
|
|
});
|
|
});
|