// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. // CI util unit tests: run with `node --test detox/utils/xml_escape.test.js`. // Exercises xml_escape() from maestro/scripts/run_ci_batches.sh (Bash 3.2 + 5.x safe). const assert = require('node:assert/strict'); const {execFileSync} = require('node:child_process'); const fs = require('node:fs'); const path = require('node:path'); const {describe, it} = require('node:test'); const SCRIPT_PATH = path.join(__dirname, '../maestro/scripts/run_ci_batches.sh'); function extractXmlEscapeFn() { const script = fs.readFileSync(SCRIPT_PATH, 'utf8'); const match = script.match(/xml_escape\(\) \{[\s\S]*?\n\s*\}/); assert.ok(match, 'xml_escape() function must exist in run_ci_batches.sh'); return match[0]; } function xmlEscape(input) { const fn = extractXmlEscapeFn(); return execFileSync( 'bash', ['-c', `${fn}\nprintf '%s' "$(xml_escape "$1")"`, 'xml_escape', input], {encoding: 'utf8'}, ); } describe('run_ci_batches.sh xml_escape', () => { it('should escape < > & " in an order that keeps amp first', () => { assert.equal(xmlEscape('&"'), '<flow>&"'); assert.equal(xmlEscape('a&b'), 'a&b'); assert.equal(xmlEscape('xz'), 'x<y>z'); assert.equal(xmlEscape('say "hi"'), 'say "hi"'); }); it('should produce attribute-safe values for synthetic JUnit XML', () => { const label = 'flows/channels/&"link".yml'; const escaped = xmlEscape(label); const xml = ` batch failed (${escaped}) `; assert.equal(escaped, 'flows/channels/<bookmark>&"link".yml'); assert.doesNotMatch(escaped, /[<>"]/); assert.match(xml, /name="flows\/channels\/<bookmark>&"link"\.yml"/); assert.ok(!xml.includes('name="<'), 'unescaped < must not appear in attributes'); }); });