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 = ` <xsl:value-of select="title"/> `; 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 ); }); });