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 = `
Practical XSLTAda
Browser XMLLin
`;
const xslt10Text = `
`;
const xslt20Text = `
`;
const xslt30Text = `
`;
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('
Practical XSLT');
expect(result.output).toContain('Browser XML');
});
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('PRACTICAL XSLT');
expect(result.output).toContain('BROWSER XML');
});
it('returns diagnostics for invalid XSLT', async () => {
const result = await runTransformation({
xmlText,
xsltText: '',
engine: 'saxon-js-dynamic',
});
expect(result.diagnostics.some((item) => item.severity === 'error')).toBe(
true
);
});
});