diff --git a/src/escape_html.ts b/src/escape_html.ts index 1f8955e..97959f3 100644 --- a/src/escape_html.ts +++ b/src/escape_html.ts @@ -6,6 +6,6 @@ const lookup: Record = { ">": ">" } -export default function escapeHTML(s: string): string { - return s.replace(/[&"'<>]/g, c => lookup[c]) +export default function escapeHTML(s: any): string { + return String(s).replace(/[&"'<>]/g, c => lookup[c] || c) } diff --git a/src/test_parser.ts b/src/test_parser.ts index 9798822..82ae1d2 100644 --- a/src/test_parser.ts +++ b/src/test_parser.ts @@ -199,6 +199,11 @@ export async function parseTap(data: string): Promise { } } +export async function parseTapFile(filename: string): Promise { + const readfile = util.promisify(fs.readFile) + return await parseTap(await readfile(filename, "utf8")) +} + async function parseJunitXml(xml: any): Promise { let testsuites @@ -292,16 +297,78 @@ export async function parseJunit(data: string): Promise { return await parseJunitXml(xml) } -export async function parseTapFile(filename: string): Promise { - const readfile = util.promisify(fs.readFile) - return await parseTap(await readfile(filename, "utf8")) -} - export async function parseJunitFile(filename: string): Promise { const readfile = util.promisify(fs.readFile) return await parseJunit(await readfile(filename, "utf8")) } +export async function parseTrx(xml: any): Promise { + if (xml.TestRun.$.xmlns != "http://microsoft.com/schemas/VisualStudio/TeamTest/2010" + || !Array.isArray(xml.TestRun.Results)) { + throw new Error("Not a valid .trx file.") + } + + const suites: TestSuite[] = [ ] + const counts = { + passed: 0, + failed: 0, + skipped: 0 + } + + for (const result of xml.TestRun.Results) { + const cases: TestCase[] = [ ] + + if (!Array.isArray(result.UnitTestResult)) { + continue + } + + for (const item of result.UnitTestResult) { + let status = TestStatus.Pass + + const id = item.$.testId + const name = item.$.testName + const duration = item.$.duration + const outcome = item.$.outcome + + let message: string | undefined = undefined + let details: string = "" + + const output = item?.Output?.[0] + details = "StdOut:" + output?.StdOut?.[0] + + if (outcome == "Passed") { + counts.passed++ + } else if (outcome == "Failed") { + status = TestStatus.Fail + counts.failed++ + + message = output?.ErrorInfo?.[0]?.Message + details = "StackTrace:" + output?.ErrorInfo?.[0]?.StackTrace + '\n' + details + } else { + status = TestStatus.Pass + counts.skipped++ + } + + cases.push({ + status: status, + name: name, + message: message, + details: details, + duration: duration + }) + } + + suites.push({ + cases: cases + }) + } + + return { + counts: counts, + suites: suites + } +} + export async function parseFile(filename: string): Promise { const readfile = util.promisify(fs.readFile) const parser = util.promisify(xml2js.parseString) diff --git a/test/resources/trx/example.trx b/test/resources/trx/example.trx new file mode 100644 index 0000000..dc1ece9 --- /dev/null +++ b/test/resources/trx/example.trx @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + Fail to add two numbers + + + + FeatureTitle + Addition + + + + + + Add two numbers + + + + FeatureTitle + Addition + + + + + + Adding several numbers + + + + FeatureTitle + Addition + + + VariantName + 40 + + + Parameter:Second Number + 50 + + + Parameter:Result + 90 + + + Parameter:First Number + 40 + + + + + + Adding several numbers + + + + FeatureTitle + Addition + + + VariantName + 60 + + + Parameter:Second Number + 70 + + + Parameter:Result + 130 + + + Parameter:First Number + 60 + + + + + + + + + + + + + + + + + + + + Given I have entered 40 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(40) (0.0s) + And I have entered 50 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(50) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be 90 on the screen + -> done: Steps.ThenTheResultShouldBePass(90) (0.0s) + + + + + + + Given I have entered 60 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(60) (0.0s) + And I have entered 70 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(70) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be 130 on the screen + -> done: Steps.ThenTheResultShouldBePass(130) (0.0s) + + + + + + + Given I have entered 50 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(50) (0.0s) + And I have entered 70 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(70) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be 120 on the screen + -> done: Steps.ThenTheResultShouldBePass(120) (0.0s) + + + + + + + Given I have entered 50 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(50) (0.0s) + And I have entered -1 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(-1) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be -50 on the screen + -> error: Assert.NotEqual() Failure + + + + Test method Pickles.TestHarness.MSTest.AdditionFeature.FailToAddTwoNumbers threw exception: + Should.Core.Exceptions.NotEqualException: Assert.NotEqual() Failure + + + at Pickles.TestHarness.MSTest.Steps.ThenTheResultShouldBePass(Int32 result) in C:\dev\pickles-results-harness\Pickles.TestHarness\Pickles.TestHarness.MSTest\Steps.cs:line 28 + at lambda_method(Closure , IContextManager , Int32 ) + at TechTalk.SpecFlow.Bindings.MethodBinding.InvokeAction(IContextManager contextManager, Object[] arguments, ITestTracer testTracer, TimeSpan& duration) + at TechTalk.SpecFlow.Bindings.StepDefinitionBinding.Invoke(IContextManager contextManager, ITestTracer testTracer, Object[] arguments, TimeSpan& duration) + at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStepMatch(BindingMatch match, Object[] arguments) + at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStep(StepArgs stepArgs) + at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnAfterLastStep() + at TechTalk.SpecFlow.TestRunner.CollectScenarioErrors() + at Pickles.TestHarness.MSTest.AdditionFeature.ScenarioCleanup() in C:\dev\pickles-results-harness\Pickles.TestHarness\Pickles.TestHarness.MSTest\Addition.feature.cs:line 0 + at Pickles.TestHarness.MSTest.AdditionFeature.FailToAddTwoNumbers() in c:\dev\pickles-results-harness\Pickles.TestHarness\Pickles.TestHarness.MSTest\Addition.feature:line 18 + + + + + + \ No newline at end of file diff --git a/test/trx.ts b/test/trx.ts new file mode 100644 index 0000000..dfe75cd --- /dev/null +++ b/test/trx.ts @@ -0,0 +1,103 @@ +import * as chai from "chai" +import { expect } from "chai" + +import * as fs from "fs" +import * as util from "util" + +import xml2js from "xml2js" + +import { TestStatus, parseTrx } from "../src/test_parser" + +const resourcePath = `${__dirname}/resources/trx` + +async function parseTrxFile(filename: string) { + const readfile = util.promisify(fs.readFile) + const parser = util.promisify(xml2js.parseString) + const xml: any = await parser(await readfile(filename, "utf8")) + return await parseTrx(xml) +} + +describe("trx", async () => { + it("parses common", async () => { + const result = await parseTrxFile(`${resourcePath}/example.trx`) + + expect(result.counts.passed).to.eql(3) + expect(result.counts.failed).to.eql(1) + expect(result.counts.skipped).to.eql(0) + + expect(result.suites.length).to.eql(1) + expect(result.suites[0].cases.length).to.eql(4) + }) + + it("parses example", async () => { + const result = await parseTrxFile(`${resourcePath}/example.trx`) + + expect(result.counts.passed).to.eql(3) + expect(result.counts.failed).to.eql(1) + expect(result.counts.skipped).to.eql(0) + + expect(result.suites.length).to.eql(1) + expect(result.suites[0].cases.length).to.eql(4) + + expect(result.suites[0].cases[0].status).to.eql(TestStatus.Pass) + expect(result.suites[0].cases[0].name).to.eql("AddingSeveralNumbers_40") + expect(result.suites[0].cases[0].details).to.eql(`StdOut: + Given I have entered 40 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(40) (0.0s) + And I have entered 50 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(50) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be 90 on the screen + -> done: Steps.ThenTheResultShouldBePass(90) (0.0s) + `) + expect(result.suites[0].cases[1].status).to.eql(TestStatus.Pass) + expect(result.suites[0].cases[1].name).to.eql("AddingSeveralNumbers_60") + expect(result.suites[0].cases[1].details).to.eql(`StdOut: + Given I have entered 60 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(60) (0.0s) + And I have entered 70 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(70) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be 130 on the screen + -> done: Steps.ThenTheResultShouldBePass(130) (0.0s) + `) + expect(result.suites[0].cases[2].status).to.eql(TestStatus.Pass) + expect(result.suites[0].cases[2].name).to.eql("AddTwoNumbers") + expect(result.suites[0].cases[2].details).to.eql(`StdOut: + Given I have entered 50 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(50) (0.0s) + And I have entered 70 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(70) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be 120 on the screen + -> done: Steps.ThenTheResultShouldBePass(120) (0.0s) + `) + expect(result.suites[0].cases[3].status).to.eql(TestStatus.Fail) + expect(result.suites[0].cases[3].name).to.eql("FailToAddTwoNumbers") + expect(result.suites[0].cases[3].details).to.eql(`StackTrace: + at Pickles.TestHarness.MSTest.Steps.ThenTheResultShouldBePass(Int32 result) in C:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarness.MSTest\\Steps.cs:line 28 + at lambda_method(Closure , IContextManager , Int32 ) + at TechTalk.SpecFlow.Bindings.MethodBinding.InvokeAction(IContextManager contextManager, Object[] arguments, ITestTracer testTracer, TimeSpan& duration) + at TechTalk.SpecFlow.Bindings.StepDefinitionBinding.Invoke(IContextManager contextManager, ITestTracer testTracer, Object[] arguments, TimeSpan& duration) + at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStepMatch(BindingMatch match, Object[] arguments) + at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.ExecuteStep(StepArgs stepArgs) + at TechTalk.SpecFlow.Infrastructure.TestExecutionEngine.OnAfterLastStep() + at TechTalk.SpecFlow.TestRunner.CollectScenarioErrors() + at Pickles.TestHarness.MSTest.AdditionFeature.ScenarioCleanup() in C:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarness.MSTest\\Addition.feature.cs:line 0 + at Pickles.TestHarness.MSTest.AdditionFeature.FailToAddTwoNumbers() in c:\\dev\\pickles-results-harness\\Pickles.TestHarness\\Pickles.TestHarness.MSTest\\Addition.feature:line 18 + +StdOut: + Given I have entered 50 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(50) (0.0s) + And I have entered -1 into the calculator + -> done: Steps.GivenIHaveEnteredSomethingIntoTheCalculator(-1) (0.0s) + When I press add + -> done: Steps.WhenIPressAdd() (0.0s) + Then the result should be -50 on the screen + -> error: Assert.NotEqual() Failure + `) + }) +})