Skip to content

Instantly share code, notes, and snippets.

@muthuishere
Last active October 23, 2024 05:54
Show Gist options
  • Save muthuishere/484408168dccc0c241eb5497f4cbee10 to your computer and use it in GitHub Desktop.
Save muthuishere/484408168dccc0c241eb5497f4cbee10 to your computer and use it in GitHub Desktop.
Bb
const fs = require('fs');
const xml2js = require('xml2js');
class SoapUIProjectParser {
constructor() {
// Configure parser to preserve whitespace and attributes
this.parser = new xml2js.Parser({
preserveChildrenOrder: true,
explicitChildren: false,
explicitArray: true
});
// Configure builder for SoapUI 5.5.0 compatibility
this.builder = new xml2js.Builder({
renderOpts: { pretty: true, indent: ' ' },
xmldec: { version: '1.0', encoding: 'UTF-8' },
cdata: true
});
this.rawXml = null;
}
async loadProject(filePath) {
try {
const xmlData = await fs.promises.readFile(filePath, 'utf8');
this.rawXml = await this.parser.parseStringPromise(xmlData);
return true;
} catch (error) {
throw new Error(`Failed to load SoapUI project: ${error.message}`);
}
}
setTestCasesStatus(testCaseNames) {
if (!this.rawXml || !this.rawXml['con:soapui-project']) {
throw new Error('No project loaded');
}
const summary = {
enabled: [],
disabled: [],
notFound: [...testCaseNames],
totalModified: 0
};
// Get all test suites
const testSuites = this.rawXml['con:soapui-project']['con:testSuite'] || [];
testSuites.forEach(testSuite => {
const testCases = testSuite['con:testCase'] || [];
testCases.forEach(testCase => {
if (!testCase['$']) {
testCase['$'] = {};
}
const testCaseName = testCase['$']['name'] || '';
const isTargetTestCase = testCaseNames.includes(testCaseName);
const currentlyDisabled = testCase['$']['disabled'] === 'true';
// Set disabled attribute based on whether it's in the target list
if (isTargetTestCase) {
// Enable the test case
if (currentlyDisabled) {
testCase['$']['disabled'] = 'false';
summary.totalModified++;
}
summary.enabled.push(testCaseName);
summary.notFound = summary.notFound.filter(name => name !== testCaseName);
} else {
// Disable the test case
if (!currentlyDisabled) {
testCase['$']['disabled'] = 'true';
summary.totalModified++;
}
summary.disabled.push(testCaseName);
}
});
});
return summary;
}
async saveProject(outputPath) {
if (!this.rawXml) {
throw new Error('No project loaded to save');
}
try {
// Ensure SoapUI 5.5.0 namespace is present
if (!this.rawXml['con:soapui-project']['$']) {
this.rawXml['con:soapui-project']['$'] = {};
}
this.rawXml['con:soapui-project']['$']['xmlns:con'] = 'http://eviware.com/soapui/config';
// Generate XML
let xmlOutput = this.builder.buildObject(this.rawXml);
// SoapUI 5.5.0 specific formatting
xmlOutput = xmlOutput
.replace(/^\s*[\r\n]/gm, '\n') // Clean up empty lines
.replace(/\r\n/g, '\n'); // Normalize line endings
await fs.promises.writeFile(outputPath, xmlOutput, 'utf8');
return true;
} catch (error) {
throw new Error(`Failed to save project: ${error.message}`);
}
}
}
// Example usage
async function main() {
const parser = new SoapUIProjectParser();
try {
// Load existing project
await parser.loadProject('./original-project.xml');
// Specify test cases to enable (all others will be disabled)
const testCasesToEnable = ['Login Test', 'Validate User'];
// Apply changes
const summary = parser.setTestCasesStatus(testCasesToEnable);
// Log changes
console.log('Modification Summary:', JSON.stringify(summary, null, 2));
// Save modified project
await parser.saveProject('./modified-project.xml');
console.log('Project successfully modified and saved!');
} catch (error) {
console.error('Error:', error.message);
process.exit(1);
}
}
module.exports = SoapUIProjectParser;
const fs = require('fs');
const xml2js = require('xml2js');
class SoapUIProjectParser {
constructor() {
// Configure parser to preserve everything
this.parser = new xml2js.Parser({
explicitArray: false,
preserveChildrenOrder: true,
xmldec: { 'version': '1.0', 'encoding': 'UTF-8' },
// Preserve all attributes and formatting
attrNameProcessors: [],
attrValueProcessors: [],
tagNameProcessors: [],
valueProcessors: []
});
// Configure builder to maintain exact format
this.builder = new xml2js.Builder({
renderOpts: { pretty: true },
xmldec: { 'version': '1.0', 'encoding': 'UTF-8' },
// Keep original formatting
headless: true,
rootName: 'con:soapui-project'
});
}
async modifyTestCases(inputFile, outputFile, testCaseNames) {
try {
// Read the original file content
const originalContent = await fs.promises.readFile(inputFile, 'utf8');
// Parse while preserving everything
let project = await this.parser.parseStringPromise(originalContent);
const summary = {
enabled: [],
disabled: [],
notFound: [...testCaseNames]
};
// Handle both single testSuite and array of testSuites
const testSuites = project['con:soapui-project']['con:testSuite'];
const testSuiteArray = Array.isArray(testSuites) ? testSuites : [testSuites];
testSuiteArray.forEach(testSuite => {
if (testSuite && testSuite['con:testCase']) {
// Handle both single testCase and array of testCases
const testCases = Array.isArray(testSuite['con:testCase'])
? testSuite['con:testCase']
: [testSuite['con:testCase']];
testCases.forEach(testCase => {
if (testCase && testCase['$']) {
const testCaseName = testCase['$']['name'];
const shouldEnable = testCaseNames.includes(testCaseName);
// Only modify the disabled attribute
testCase['$']['disabled'] = (!shouldEnable).toString();
// Update summary
if (shouldEnable) {
summary.enabled.push(testCaseName);
summary.notFound = summary.notFound.filter(name => name !== testCaseName);
} else {
summary.disabled.push(testCaseName);
}
}
});
}
});
// Get the XML declaration from original file
const xmlDeclaration = originalContent.split('\n')[0];
// Build the modified XML while preserving everything else
let modifiedContent = this.builder.buildObject(project);
// Ensure we keep the original XML declaration
modifiedContent = xmlDeclaration + '\n' + modifiedContent;
// Write back to file
await fs.promises.writeFile(outputFile, modifiedContent);
return summary;
} catch (error) {
throw new Error(`Failed to process SoapUI project: ${error.message}`);
}
}
}
// Example usage
async function main() {
try {
const parser = new SoapUIProjectParser();
const result = await parser.modifyTestCases(
'original-project.xml', // Input file
'modified-project.xml', // Output file
['Login Test', 'Payment Validation'] // Test cases to enable
);
console.log('Modification Summary:', result);
} catch (error) {
console.error('Error:', error.message);
}
}
module.exports = SoapUIProjectParser;
const fs = require('fs');
const xml2js = require('xml2js');
const path = require('path');
class SoapUIProjectParser {
constructor() {
this.parser = new xml2js.Parser();
this.SOAPUI_NS = 'http://eviware.com/soapui/config';
}
async parseProject(filePath) {
try {
const xmlData = await fs.promises.readFile(filePath, 'utf8');
const result = await this.parser.parseStringPromise(xmlData);
return this.extractProjectInfo(result);
} catch (error) {
throw new Error(`Failed to parse SoapUI project: ${error.message}`);
}
}
getSafeValue(obj, key, defaultValue = '') {
return obj && obj[key] ? obj[key] : defaultValue;
}
getArraySafe(obj, key) {
return obj && Array.isArray(obj[key]) ? obj[key] : [];
}
extractProjectInfo(parsedXml) {
const projectKey = 'con:soapui-project';
const project = parsedXml[projectKey] || {};
const attributes = project['$'] || {};
return {
projectInfo: {
name: this.getSafeValue(attributes, 'name'),
id: this.getSafeValue(attributes, 'id'),
created: this.getSafeValue(attributes, 'created'),
updated: this.getSafeValue(attributes, 'updated'),
version: this.getSafeValue(attributes, 'soapui-version', '5.5.0')
},
testSuites: this.extractTestSuites(this.getArraySafe(project, 'con:testSuite')),
endpoints: this.extractEndpoints(this.getArraySafe(project, 'con:interface')),
properties: this.extractProperties(
this.getArraySafe(project['con:properties'] || [], 'con:property')
)
};
}
extractTestSuites(testSuites) {
return testSuites.map(suite => {
const attributes = suite['$'] || {};
return {
name: this.getSafeValue(attributes, 'name'),
id: this.getSafeValue(attributes, 'id'),
testCases: this.extractTestCases(this.getArraySafe(suite, 'con:testCase'))
};
});
}
extractTestCases(testCases) {
return testCases.map(testCase => {
const attributes = testCase['$'] || {};
return {
name: this.getSafeValue(attributes, 'name'),
id: this.getSafeValue(attributes, 'id'),
steps: this.extractTestSteps(this.getArraySafe(testCase, 'con:testStep'))
};
});
}
extractTestSteps(testSteps) {
return testSteps.map(step => {
const attributes = step['$'] || {};
return {
type: this.getSafeValue(attributes, 'type'),
name: this.getSafeValue(attributes, 'name'),
id: this.getSafeValue(attributes, 'id'),
config: this.extractStepConfig(step)
};
});
}
extractStepConfig(step) {
const config = {};
const stepConfig = (step['con:config'] || [])[0] || {};
if (stepConfig['con:request'] && stepConfig['con:request'][0]) {
const request = stepConfig['con:request'][0];
const requestAttributes = request['$'] || {};
config['request'] = {
endpoint: this.getSafeValue(requestAttributes, 'endpoint'),
method: this.getSafeValue(requestAttributes, 'method'),
parameters: this.extractParameters(
this.getArraySafe(request['con:parameters'] && request['con:parameters'][0], 'con:parameter')
),
// SoapUI 5.5.0 specific fields
mediaType: this.getSafeValue(requestAttributes, 'mediaType'),
postQueryString: this.getSafeValue(requestAttributes, 'postQueryString', false),
timeout: this.getSafeValue(requestAttributes, 'timeout', '0'),
followRedirects: this.getSafeValue(requestAttributes, 'followRedirects', 'true'),
encoding: this.getSafeValue(requestAttributes, 'encoding', 'UTF-8')
};
}
if (stepConfig['con:assertions'] && stepConfig['con:assertions'][0]) {
config['assertions'] = this.extractAssertions(stepConfig['con:assertions'][0]);
}
// SoapUI 5.5.0 specific configurations
if (stepConfig['con:credentials'] && stepConfig['con:credentials'][0]) {
config['credentials'] = this.extractCredentials(stepConfig['con:credentials'][0]);
}
return config;
}
extractParameters(parameters) {
return parameters.map(param => {
const attributes = param['$'] || {};
return {
name: this.getSafeValue(attributes, 'name'),
value: this.getSafeValue(attributes, 'value'),
type: this.getSafeValue(attributes, 'type', 'string'),
// SoapUI 5.5.0 specific fields
style: this.getSafeValue(attributes, 'style', 'PLAIN'),
required: this.getSafeValue(attributes, 'required', 'false'),
defaultValue: this.getSafeValue(attributes, 'default')
};
});
}
extractAssertions(assertions) {
const assertionTypes = [
'con:XPathAssertion',
'con:ResponseSLAAssertion',
'con:SimpleContainsAssertion',
'con:ValidHttpStatusCodesAssertion',
'con:JsonPathAssertion', // Added for 5.5.0
'con:GroovyScriptAssertion', // Added for 5.5.0
'con:RestResponseStatusAssertion' // Added for 5.5.0
];
return assertionTypes.reduce((acc, type) => {
const typeAssertions = this.getArraySafe(assertions, type);
if (typeAssertions.length > 0) {
acc.push(...typeAssertions.map(assertion => ({
type: type.replace('con:', ''),
...this.extractAssertionDetails(assertion)
})));
}
return acc;
}, []);
}
extractAssertionDetails(assertion) {
const details = {};
if (assertion['con:xpath']) {
details['xpath'] = this.getSafeValue(assertion['con:xpath'], '0');
}
if (assertion['con:expression']) {
details['expression'] = this.getSafeValue(assertion['con:expression'], '0');
}
if (assertion['con:sla']) {
details['sla'] = this.getSafeValue(assertion['con:sla'], '0');
}
// SoapUI 5.5.0 specific assertion details
if (assertion['con:jsonPath']) {
details['jsonPath'] = this.getSafeValue(assertion['con:jsonPath'], '0');
}
if (assertion['con:script']) {
details['script'] = this.getSafeValue(assertion['con:script'], '0');
}
return details;
}
extractEndpoints(interfaces) {
return interfaces.map(intf => {
const attributes = intf['$'] || {};
const endpoints = this.getArraySafe(
intf['con:endpoints'] && intf['con:endpoints'][0],
'con:endpoint'
);
return {
name: this.getSafeValue(attributes, 'name'),
type: this.getSafeValue(attributes, 'type'),
endpoints: endpoints.map(endpoint => this.getSafeValue(endpoint['$'], 'url'))
};
});
}
extractProperties(properties) {
return properties.map(prop => {
const attributes = prop['$'] || {};
return {
name: this.getSafeValue(attributes, 'name'),
value: this.getSafeValue(attributes, 'value'),
// SoapUI 5.5.0 specific fields
type: this.getSafeValue(attributes, 'type', 'string'),
isEncrypted: this.getSafeValue(attributes, 'encrypted', 'false')
};
});
}
extractCredentials(credentials) {
return {
username: this.getSafeValue(credentials['con:username'], '0'),
password: this.getSafeValue(credentials['con:password'], '0'),
domain: this.getSafeValue(credentials['con:domain'], '0'),
authType: this.getSafeValue(credentials['$'], 'authType', 'No Authorization')
};
}
}
// Example usage
async function main() {
const parser = new SoapUIProjectParser();
try {
const projectInfo = await parser.parseProject('path/to/your/soapui-project.xml');
console.log(JSON.stringify(projectInfo, null, 2));
} catch (error) {
console.error('Error parsing project:', error.message);
}
}
module.exports = SoapUIProjectParser;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment