XSLTDebugX uses Playwright for end-to-end (E2E) testing. Tests verify the complete user workflows without mocking any backend systems.
Status: ✅ 98 tests passing (smoke + 9 workflow specs)
<repo root>/
├── playwright.config.js # Playwright configuration (project: chromium)
├── package.json # Test scripts and dependencies
└── tests/
├── utils/
│ └── test-helpers.js # EditorPage class - all test utilities
├── fixtures/
│ └── sample-data.js # Test data (XML, XSLT, XPath examples)
└── e2e/
├── smoke.spec.js # 4 tests: smoke checks (incl. 1 SUMMARY)
└── workflows/
├── mode-switching.spec.js # 13 tests: XSLT ↔ XPath mode switching
├── session-management.spec.js # 8 tests: localStorage persistence
├── xpath-evaluation.spec.js # 7 tests: XPath expression evaluation
├── xslt-transform.spec.js # 8 tests: XSLT transformations
├── cpi-simulation.spec.js # 12 tests: CPI headers/properties, interceptors
├── cpi-validation.spec.js # 11 tests: CPI pre-flight validation (unknown fns, arity, line mapping)
├── examples-library.spec.js # 14 tests: Examples modal, search, categories
├── share-url.spec.js # 9 tests: URL encoding, session sharing
└── kv-search.spec.js # 12 tests: KV header/property search filter
npm run test:e2enpx playwright test tests/e2e/workflows/mode-switching.spec.jsnpx playwright test tests/e2e/workflows/mode-switching.spec.js -g "should switch from XSLT"npm run test:e2e:uinpm run test:e2e:headednpm run test:e2e:debug# Only the `chromium` project is enabled in playwright.config.js
npx playwright test --project=chromiumnpm run build
TEST_SERVER=dist npx playwright test --workers=4Quick sanity checks - 4 tests (3 real + 1 SUMMARY):
- ✅ should load app with all UI elements visible
- ✅ should perform basic XSLT transform
- ✅ should switch between XSLT and XPath modes
- (SUMMARY pseudo-test prints suite summary)
Run: npx playwright test tests/e2e/smoke.spec.js
Tests switching between XSLT and XPath modes - 13 tests (12 real + 1 SUMMARY):
- ✅ should switch from XSLT mode to XPath mode
- ✅ should switch from XPath mode to XSLT mode
- ✅ should preserve XML content when switching modes
- ✅ should preserve XSLT content when switching modes and back
- ✅ should preserve transform output when mode is preserved
- ✅ should show correct mode indicator badge for XSLT
- ✅ should show correct mode indicator badge for XPath
- ✅ should have separate XML state per mode (isolation test)
- ✅ should update mode indicator when button is clicked
- ✅ should clear transform output when switching modes
- ✅ should support rapid mode switching
- ✅ should persist mode preference across page reload
Run: npx playwright test tests/e2e/workflows/mode-switching.spec.js
Tests localStorage persistence - 8 tests (7 real + 1 SUMMARY):
- ✅ should auto-save session to localStorage
- ✅ should persist session after page reload
- ✅ should store output when transform runs
- ✅ should maintain session mode (XSLT vs XPath) in localStorage
- ✅ should restore session mode after reload
- ✅ should preserve session across mode switches
- ✅ should load empty editors on fresh start
Run: npx playwright test tests/e2e/workflows/session-management.spec.js
Tests XPath expressions - 7 tests (6 real + 1 SUMMARY):
- ✅ should evaluate simple XPath expression (count)
- ✅ should select nodes with XPath
- ✅ should handle XPath with predicates
- ✅ should handle empty result set gracefully
- ✅ should switch mode from XPath to XSLT
- ✅ should preserve XML content in XPath mode
Run: npx playwright test tests/e2e/workflows/xpath-evaluation.spec.js
Tests XSLT transformations - 8 tests (7 real + 1 SUMMARY):
- ✅ should perform a basic XSLT transform (XML → XSLT → Output)
- ✅ should detect malformed XML
- ✅ should run XSLT via Ctrl+Enter keyboard shortcut
- ✅ should handle empty XML input
- ✅ should preserve XSLT across mode switch
- ✅ should store session after transform
- ✅ should be in XSLT mode initially
Run: npx playwright test tests/e2e/workflows/xslt-transform.spec.js
Tests SAP CPI (Cloud Integration) simulation features - 12 tests (11 real + 1 SUMMARY):
- ✅ should add a single header and display count
- ✅ should add multiple headers
- ✅ should add a property and display count
- ✅ should add both headers and properties together
- ✅ should update an existing header
- ✅ should delete a header
- ✅ should execute XSLT with headers and verify in output
- ✅ should execute XSLT with multiple headers and properties
- ✅ should persist headers/properties across page reload
- ✅ should clear headers/properties when clearing session
- ✅ should not include headers/properties in XPath mode
Run: npx playwright test tests/e2e/workflows/cpi-simulation.spec.js
Tests CPI pre-flight validation: catches unknown cpi: functions, wrong arity, missing xmlns:cpi, missing <xsl:param name="exchange"/>, and accurate line mapping - 11 tests:
- ✅ unknown cpi function (cpi:setHeaders typo) is rejected with clear message
- ✅ cpi:getHeader is rejected (does not exist in real CPI)
- ✅ cpi:setHeader with 2 args is rejected with arity message
- ✅ missing xmlns:cpi is detected
- ✅ wrong xmlns:cpi URI is rejected as a hard error
- ✅ missing
<xsl:param name="exchange"/>is detected - ✅ first arg not $exchange is detected
- ✅ valid minimal CPI XSLT passes pre-flight and produces output
- ✅ exclude-result-prefixes="#all" covers cpi — no false-positive warning
- ✅ whitespace-rich XPath expression resolves to correct source line
- ✅ bundled "CPI Headers & Properties" example still runs end-to-end
Run: npx playwright test tests/e2e/workflows/cpi-validation.spec.js
Tests example modal, search, filtering, and loading - 14 tests (13 real + 1 SUMMARY):
- ✅ should open examples modal and render sidebar categories
- ✅ should close modal on backdrop click
- ✅ should load transform example and populate XML/XSLT
- ✅ should load XPath example and switch to XPath mode
- ✅ should load CPI example with auto-populated headers
- ✅ should search examples by keyword
- ✅ should return to full list when clearing search
- ✅ should toggle auto-run checkbox and persist preference
- ✅ should auto-run example when preference is enabled
- ✅ should clear validation errors when loading example
- ✅ should display correct mode indicator in modal
- ✅ should isolate examples by category
- ✅ should not populate headers in XPath examples
Run: npx playwright test tests/e2e/workflows/examples-library.spec.js
Tests session sharing via encoded URLs - 9 tests (8 real + 1 SUMMARY):
- ✅ should generate a share URL from current editor state
- ✅ should include headers in share URL and decode correctly
- ✅ should include properties in share URL and decode correctly
- ✅ should generate consistent share URL for same content
- ✅ should handle corrupted share URL gracefully
- ✅ should perform round-trip: generate URL, load, generate again with same result
- ✅ should clean up hash from URL after loading share data, and verify content is decoded
- ✅ should close share modal when clicking outside
Run: npx playwright test tests/e2e/workflows/share-url.spec.js
Tests substring filter on Headers / Properties / output KV panels - 12 tests:
- ✅ toggles the search bar open and closed
- ✅ filters rows by substring match against the name column
- ✅ filters rows by substring match against the value column
- ✅ clear button restores all rows
- ✅ shows a "No matches" line when query matches nothing
- ✅ keeps filter active across adding a new row
- ✅ keeps filter active across deleting a matching row
- ✅ closing the search bar clears the query and restores rows
- ✅ does not mutate kvData (persistence unchanged)
- ✅ search-toggle button shows active state when query is non-empty
- ✅ Properties panel filter works the same way
- ✅ filters output Headers panel after a transform sets multiple headers
Run: npx playwright test tests/e2e/workflows/kv-search.spec.js
The EditorPage class provides all utilities needed for testing:
const page = new EditorPage(testPage);
// Navigation
await page.navigate(); // Go to http://localhost:8000
// Editor content
await page.fillXml(xmlString);
await page.fillXslt(xsltString);
const xml = await page.getXmlContent();
const xslt = await page.getXsltContent();
const output = await page.getOutput();
// Mode switching
await page.switchToXslt();
await page.switchToXpath();
const mode = await page.getMode(); // Returns 'XSLT' or 'XPATH'
// Running transforms
await page.clickRun(); // Click run button
await page.runViaKeyboard(); // Ctrl+Enter
// Session/Storage
await page.getStoredSession(); // Get localStorage session
const session = localStorage.getItem('xdebugx-session-v1');
// Console
const errors = await page.getConsoleErrors();
const messages = await page.getConsoleMessages();
// Helpers
await page.hasErrors(); // Check for error badge
const mode = await page.getModeIndicator();test.beforeEach(async ({ page: testPage }) => {
page = new EditorPage(testPage);
// Navigate first (localStorage requires DOM)
await page.navigate();
// THEN clear storage
await testPage.evaluate(() => {
localStorage.clear();
sessionStorage.clear();
});
});// Session auto-saves after ~800ms debounce
await page.fillXml(xml);
await testPage.waitForTimeout(1500); // Wait for save
const session = await page.getStoredSession();await page.switchToXpath();
await testPage.waitForTimeout(1000); // Wait for animations
const mode = await page.getMode();
expect(mode).toBe('XPATH');// Content
expect(output).toBeTruthy();
expect(output).toContain('<tag>');
expect(xmlContent.length).toBeGreaterThan(0);
// Mode
expect(mode).toBe('XSLT');
// Storage
expect(session).not.toBeNull();
expect(session.xmlXslt).toBeTruthy();- Increase
waitForTimeoutvalues (browser animations, editor loading) - Check that app is running on
http://localhost:8000 - Use
--headedflag to see what browser is doing
- Always navigate BEFORE trying to access localStorage
- Don't clear localStorage before navigating
isXpathandisXsltare getters (properties), not methods- Don't call them like functions:
isXpath()❌, useisXpath✅
- Wait a bit after
fillXml():await testPage.waitForTimeout(500) - Monaco editor events need time to trigger
- Keep tests focused - One workflow per test
- Use helper methods - EditorPage abstracts DOM details
- Wait for async operations - Editor saves, animations, loads
- Clean state - Clear localStorage before each test
- Readable assertions - Use
.toBeTruthy()instead of checking length > 0 - Test order doesn't matter - Each test should be independent
The CI workflow already exists at .github/workflows/e2e-tests.yml.
It runs on push and pull request to dev and main, and:
- Uses Node.js 24 with
npm ci - Runs
npm run buildto producedist/ - Verifies the built bundle (asserts no raw
js/script tags remain indist/index.html) - Installs Chromium via
npx playwright install chromium(+install-deps) - Runs the suite against the production build with:
TEST_SERVER=dist npx playwright test --workers=4 - Uploads the HTML report (
playwright-report/) and JUnit/JSON results (test-results/) as artifacts
To reproduce the CI run locally:
npm ci
npm run build
TEST_SERVER=dist npx playwright test --workers=4Sample data is in fixtures/sample-data.js:
simpleXml- Basic user list XMLsimpleXslt- Basic transform stylesheetnamespacedXml- XML with namespacesxmlForXpath- XML optimized for XPath testingxpathExpressions- Common XPath examples
npx playwright test --debugnpx playwright show-trace test-results/trace.zipconsole.log('Debug message'); // Shows in test outputawait testPage.screenshot({ path: 'debug.png' });- Create test in appropriate
.spec.jsfile - Use existing
EditorPagemethods - Run test locally:
npm run test:e2e -- -g "test name" - Ensure it passes consistently (no flakiness)
- Add to this guide if it's a new pattern
Edit tests/utils/test-helpers.js to add new utilities or fix existing ones.
Edit tests/fixtures/sample-data.js to add new examples.
✅ 98 tests passing (across 10 spec files)
- Smoke tests: 4/4
- Mode switching: 13/13
- Session management: 8/8
- XPath evaluation: 7/7
- XSLT transforms: 8/8
- CPI simulation: 12/12
- CPI validation: 11/11
- Examples library: 14/14
- Share URL: 9/9
- KV search: 12/12
Last updated: June 22, 2026