Skip to content

Commit

Permalink
Merge pull request #139 from currents-dev/feat/migrate-fulltestsuite-…
Browse files Browse the repository at this point in the history
…convert

[CSR-2066] feat: added full test suite generation for convert command
  • Loading branch information
vCaisim authored Feb 10, 2025
2 parents ca0e246 + e182cc7 commit 03e06cb
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 216 deletions.
2 changes: 1 addition & 1 deletion examples/jest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"private": true,
"scripts": {
"test": "jest",
"report": "CURRENTS_API_URL=http://localhost:1234 currents --project-id=2gT26j --key=nHw5cTjgW7f1rvL5",
"report": "CURRENTS_API_URL=http://localhost:1234 currents --project-id=xW2Ijf --key=9bqJY1huXL2l3ONF",
"build": "echo \"No build specified\" && exit 0"
},
"devDependencies": {
Expand Down
21 changes: 0 additions & 21 deletions examples/jest/src/basic/basic2.spec.ts

This file was deleted.

16 changes: 10 additions & 6 deletions packages/cmd/src/commands/convert/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export const inputFormatOption = new Option(
).choices(Object.values(REPORT_INPUT_FORMATS));

export const inputFileOption = new Option(
'--input-file <pattern>',
'the pattern to search for test reports'
'--input-file <patterns>',
'comma-separated glob patterns to match report file paths (e.g., "report1.xml,report2.xml")'
).argParser(validateGlobPattern);

export const outputDirOption = new Option(
Expand All @@ -40,9 +40,13 @@ export const frameworkVersionOption = new Option(
);

function validateGlobPattern(value: string) {
const result = glob.globSync(value);
if (result.length === 0) {
throw new InvalidArgumentError('No files found with the provided pattern');
const patterns = value.split(',').map((pattern) => pattern.trim());

const allResults = glob.globSync(patterns);

if (allResults.length === 0) {
throw new InvalidArgumentError('No files found with the provided patterns');
}
return result;

return allResults;
}
21 changes: 21 additions & 0 deletions packages/cmd/src/lib/fs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,27 @@ export async function writeFileAsync(filePath: string, content: string) {
}
}

export async function writeFileAsyncIfNotExists(
filePath: string,
content: string
) {
try {
// Check if file exists
try {
await fs.access(filePath);
// If we reach here, file exists
return filePath;
} catch {
// File doesn't exist, create it
await fs.writeFile(filePath, content, 'utf8');
return filePath;
}
} catch (err) {
error(`Error writing file at ${filePath}:`, err);
throw err;
}
}

export async function ensurePathExists(
filePath: string,
isDirectory?: boolean
Expand Down
17 changes: 9 additions & 8 deletions packages/cmd/src/services/convert/__tests__/instances.test.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { parseStringPromise } from 'xml2js';
import { InstanceReport } from '../../../types';
import { createSuiteJson, getInstanceMap } from '../postman/instances';
import { timeToMilliseconds } from '../utils';
import { mockDate } from './fixtures';

describe('getInstanceMap', () => {
it('should return an empty Map when the XML input is empty', async () => {
expect(await getInstanceMap('')).toEqual(new Map());
expect(await getInstanceMap([])).toEqual(new Map());
});

it('should return an empty Map when the test suite array is empty', async () => {
expect(await getInstanceMap('<testsuites></testsuites>')).toEqual(
new Map()
);
expect(await getInstanceMap([])).toEqual(new Map());
});

it('should return an empty Map when instances have no tests', async () => {
expect(
await getInstanceMap('<testsuites><testsuite></testsuite></testsuites>')
).toEqual(new Map());
expect(await getInstanceMap([])).toEqual(new Map());
});

const xmlInput = `
Expand Down Expand Up @@ -46,7 +43,11 @@ describe('getInstanceMap', () => {
}

beforeEach(async () => {
instanceMap = await getInstanceMap(xmlInput);
const parsedXMLInput = await parseStringPromise(xmlInput, {
explicitArray: false,
mergeAttrs: true,
});
instanceMap = await getInstanceMap([parsedXMLInput]);
});

it('returns a map of instances', () => {
Expand Down
64 changes: 0 additions & 64 deletions packages/cmd/src/services/convert/combineInputFiles.ts

This file was deleted.

48 changes: 48 additions & 0 deletions packages/cmd/src/services/convert/createFullTestSuite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
FullSuiteProject,
FullSuiteTest,
FullTestSuite,
} from '../upload/discovery';
import { TestCase, TestSuite, TestSuites } from './types';
import {
ensureArray,
generateTestId,
getSuiteName,
getTestTitle,
} from './utils';

export function createFullTestSuite(parsedXMLInputs: TestSuites[]) {
const fullTestSuite: FullTestSuite = [];

parsedXMLInputs.forEach((item) => {
const testsuites = ensureArray<TestSuite>(item.testsuites?.testsuite);

const fullSuiteProject: FullSuiteProject = {
name: item.testsuites?.name ?? 'No name',
tags: [],
tests: [],
};

testsuites?.forEach((suite) => {
const suiteName = getSuiteName(suite, testsuites);
const testcases = ensureArray<TestCase>(suite?.testcase);

testcases?.forEach((testcase) => {
const fullSuiteTest: FullSuiteTest = {
title: getTestTitle(testcase.name, suiteName),
spec: suiteName,
tags: [],
testId: generateTestId(
getTestTitle(testcase.name, suiteName).join(', '),
suiteName
),
};

fullSuiteProject.tests.push(fullSuiteTest);
});
});
fullTestSuite.push(fullSuiteProject);
});

return fullTestSuite;
}
28 changes: 7 additions & 21 deletions packages/cmd/src/services/convert/getInstanceMap.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,37 @@
import { warn } from '@logger';
import { readFile } from 'fs-extra';
import {
REPORT_FRAMEWORKS,
REPORT_INPUT_FORMATS,
} from '../../commands/convert/options';
import { InstanceReport } from '../../types';
import { combineInputFiles, saveXMLInput } from './combineInputFiles';
import { getInstanceMap as getInstanceMapForPostman } from './postman/instances';
import { TestSuites } from './types';

export async function getInstanceMap({
inputFormat,
inputFiles,
outputDir,
framework,
parsedXMLArray,
}: {
inputFormat: REPORT_INPUT_FORMATS;
inputFiles: string[];
outputDir: string;
framework: REPORT_FRAMEWORKS;
parsedXMLArray: TestSuites[];
}): Promise<Map<string, InstanceReport>> {
if (inputFormat === REPORT_INPUT_FORMATS.junit) {
let xmlInput = '';
if (inputFiles.length > 1) {
xmlInput = await combineInputFiles(inputFiles);
} else if (inputFiles.length === 1) {
xmlInput = await readFile(inputFiles[0], 'utf-8');
}

const trimmedXMLInput = xmlInput.trim();
if (trimmedXMLInput) {
await saveXMLInput(outputDir, trimmedXMLInput);
return getInstanceMapByFramework(framework, trimmedXMLInput);
}
return getInstanceMapByFramework(framework, parsedXMLArray);
}

return new Map();
}

async function getInstanceMapByFramework(
framework: REPORT_FRAMEWORKS,
xmlInput: string
parsedXMLArray: TestSuites[]
) {
switch (framework) {
case 'postman':
return getInstanceMapForPostman(xmlInput);
return getInstanceMapForPostman(parsedXMLArray);
case 'vitest':
return getInstanceMapForPostman(xmlInput);
return getInstanceMapForPostman(parsedXMLArray);
default:
warn('Unsupported framework: %s', framework);
return new Map<string, InstanceReport>();
Expand Down
41 changes: 41 additions & 0 deletions packages/cmd/src/services/convert/getParsedXMLArray.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { debug } from '@debug';
import { warn } from '@logger';
import { readFile } from 'fs-extra';
import { parseStringPromise } from 'xml2js';
import { TestSuites } from './types';

export async function getParsedXMLArray(
inputFiles: string[]
): Promise<TestSuites[]> {
const filesData: string[] = await Promise.all(
inputFiles.map((item) => readFile(item, 'utf-8'))
);

const parsedXMLInputs = (
await Promise.all(filesData.map(getParsedXMLInput))
).filter(Boolean);

if (filesData.length !== parsedXMLInputs.length) {
warn(
'Some files could not be parsed. Enable debug logging for more details'
);
}

return parsedXMLInputs;
}

async function getParsedXMLInput(XMLString: string) {
const trimmedXMLString = XMLString.trim();
if (!trimmedXMLString) return null;

try {
const parsedXMLInput = await parseStringPromise(trimmedXMLString, {
explicitArray: false,
mergeAttrs: true,
});
return parsedXMLInput || null;
} catch (e) {
debug('Error parsing XML input', e);
return null;
}
}
Loading

0 comments on commit 03e06cb

Please sign in to comment.