├── data ├── thing.txt ├── invalid.xml ├── subfolder │ └── _thingy.xml ├── utf-16.xml ├── complete_single_case_only.xml ├── no_class_name.xml ├── passing_suite.xml ├── name.has.dots.xml ├── skipped_suite.xml ├── issue_2.xml ├── complete_no_suite_multi_cases.xml ├── with_html.xml ├── error_suite.xml ├── failing_suite.xml ├── pytest_testcase_properties.xml ├── multi_error_test_with_system_out.xml ├── defect_suite.xml ├── malformed.xml ├── nested-nested.xml ├── issue_3.xml ├── russian-unicode.xml ├── multi_cases.xml ├── properties_in_test_meta.xml ├── special_chars_suite.xml ├── semi-colon.xml ├── suite-system-out.xml ├── class_not_classname.xml ├── test-system-out.xml ├── multi-name-unique-classname.xml ├── duplicate_name_unique_classanme.xml ├── multi_suite.xml ├── complete_no_suite_single_suite.xml ├── most_complex.xml ├── test.xml ├── complete_no_suite.xml ├── embedded_html_sysout.xml └── complete_single_suite.xml ├── .env ├── public ├── icon.png ├── favicon.ico └── index.html ├── example-header.png ├── gh-pages ├── icon.png └── favicon.ico ├── XunitViewerIcon.png ├── src ├── app │ ├── parse.js │ ├── logo.test.js │ ├── toggle.test.js │ ├── loading.js │ ├── suite-options.test.js │ ├── properties-options.test.js │ ├── test-options.test.js │ ├── __snapshots__ │ │ ├── toggle.test.js.snap │ │ ├── logo.test.js.snap │ │ ├── properties-options.test.js.snap │ │ ├── suite-options.test.js.snap │ │ ├── test-options.test.js.snap │ │ └── hero.test.js.snap │ ├── suite-count.js │ ├── toggle.js │ ├── logo.js │ ├── error.js │ ├── initial-state.js │ ├── visible.js │ ├── parse-all.js │ ├── suite-options.js │ ├── properties-options.js │ ├── files.js │ ├── app.js │ ├── test-options.js │ ├── hero.js │ ├── suite.js │ ├── reducer.js │ └── index.css ├── cli │ ├── watch.js │ ├── parse.test.js │ ├── get-description.js │ ├── get-suites.test.js │ ├── get-description.test.js │ ├── get-suites.js │ ├── update-expected.js │ ├── server.js │ ├── get-files.js │ ├── render.js │ ├── logger.js │ ├── terminal.js │ ├── static │ │ └── js │ │ │ └── main.4e6e0818.js.LICENSE.txt │ ├── get-files.test.js │ ├── index.html │ ├── args.js │ ├── parse.js │ └── parse-expected.json └── index.js ├── bin └── xunit-viewer.js ├── sample-usage.js ├── .npmignore ├── .github ├── workflows │ └── main.yml └── ISSUE_TEMPLATE │ └── issue.md ├── .gitignore ├── release.sh ├── XunitViewerIcon.svg ├── SECURITY.md ├── component └── icon-map.jsx ├── junit.xml ├── LICENCE ├── xunit-viewer.js ├── package.json └── README.md /data/thing.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/invalid.xml: -------------------------------------------------------------------------------- 1 | bacon -------------------------------------------------------------------------------- /data/subfolder/_thingy.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true -------------------------------------------------------------------------------- /data/utf-16.xml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukejpreston/xunit-viewer/HEAD/data/utf-16.xml -------------------------------------------------------------------------------- /public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukejpreston/xunit-viewer/HEAD/public/icon.png -------------------------------------------------------------------------------- /example-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukejpreston/xunit-viewer/HEAD/example-header.png -------------------------------------------------------------------------------- /gh-pages/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukejpreston/xunit-viewer/HEAD/gh-pages/icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukejpreston/xunit-viewer/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /XunitViewerIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukejpreston/xunit-viewer/HEAD/XunitViewerIcon.png -------------------------------------------------------------------------------- /gh-pages/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukejpreston/xunit-viewer/HEAD/gh-pages/favicon.ico -------------------------------------------------------------------------------- /src/app/parse.js: -------------------------------------------------------------------------------- 1 | import '../cli/parse.js' 2 | const parse = window.parse 3 | export default parse 4 | -------------------------------------------------------------------------------- /bin/xunit-viewer.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import xunitViewer from '../xunit-viewer.js' 4 | import { args } from '../src/cli/args.js' 5 | 6 | xunitViewer(args) 7 | -------------------------------------------------------------------------------- /data/complete_single_case_only.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /data/no_class_name.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /data/passing_suite.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /data/name.has.dots.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /data/skipped_suite.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample-usage.js: -------------------------------------------------------------------------------- 1 | import xunitViewer from './xunit-viewer' 2 | 3 | xunitViewer({ 4 | server: false, 5 | results: 'data', 6 | ignore: ['_thingy', 'invalid'], 7 | title: 'Xunit View Sample Tests', 8 | output: 'output.html' 9 | }) 10 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/__snapshots__ 2 | src/app 3 | src/index.js 4 | data 5 | public 6 | scripts 7 | .env 8 | build 9 | coverage 10 | gh-pages 11 | junit.xml 12 | sample-usage.js 13 | XunitViewerIcon.png 14 | XunitViewerIcon.svg 15 | xunit-viewer-results.html -------------------------------------------------------------------------------- /src/app/logo.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Logo from './logo' 3 | import renderer from 'react-test-renderer' 4 | 5 | test('renders logo', () => { 6 | const tree = renderer.create().toJSON() 7 | expect(tree).toMatchSnapshot() 8 | }) 9 | -------------------------------------------------------------------------------- /src/cli/watch.js: -------------------------------------------------------------------------------- 1 | import chokidar from 'chokidar' 2 | import debounce from 'debounce' 3 | 4 | export default ({ results }, cb) => { 5 | cb = debounce(cb) 6 | chokidar.watch(results) 7 | .on('all', (event, path) => { 8 | cb() 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /src/app/toggle.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Toggle from './toggle' 3 | import renderer from 'react-test-renderer' 4 | 5 | test('renders toggle', () => { 6 | const tree = renderer.create().toJSON() 7 | expect(tree).toMatchSnapshot() 8 | }) 9 | -------------------------------------------------------------------------------- /data/issue_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /data/complete_no_suite_multi_cases.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/app/loading.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Loading = () => ( 4 |
5 |
8 | ) 9 | 10 | export default Loading 11 | -------------------------------------------------------------------------------- /src/app/suite-options.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import SuiteOptions from './suite-options' 3 | import renderer from 'react-test-renderer' 4 | 5 | test('renders suite options', () => { 6 | const tree = renderer.create().toJSON() 7 | expect(tree).toMatchSnapshot() 8 | }) 9 | -------------------------------------------------------------------------------- /data/with_html.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <i>ARGG</i><b>BOO</b> 6 | 7 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-node@v1 9 | with: 10 | node-version: '12.x' 11 | - run: npm install 12 | - run: npm run lint 13 | - run: npm run test:ci -------------------------------------------------------------------------------- /data/error_suite.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | java.lang.RuntimeException: There was an error 5 | 6 | -------------------------------------------------------------------------------- /src/app/properties-options.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropertiesOptions from './properties-options' 3 | import renderer from 'react-test-renderer' 4 | 5 | test('renders properties options', () => { 6 | const tree = renderer.create().toJSON() 7 | expect(tree).toMatchSnapshot() 8 | }) 9 | -------------------------------------------------------------------------------- /src/app/test-options.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import TestOptions from './test-options' 3 | import renderer from 'react-test-renderer' 4 | 5 | test('renders test options', () => { 6 | const tree = renderer.create().toJSON() 7 | expect(tree).toMatchSnapshot() 8 | }) 9 | -------------------------------------------------------------------------------- /data/failing_suite.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | FILENAME:XX Expected <string>: Luke to equal <string>: luke 5 | 6 | -------------------------------------------------------------------------------- /data/pytest_testcase_properties.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /data/multi_error_test_with_system_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Error message 5 | Some messgae 6 | FILENAME:XX 7 | 8 | -------------------------------------------------------------------------------- /src/app/__snapshots__/toggle.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders toggle 1`] = ` 4 | 20 | `; 21 | -------------------------------------------------------------------------------- /src/cli/parse.test.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import path from 'path' 3 | import './parse' 4 | import expected from './parse-expected.json' 5 | 6 | const parse = window.parse 7 | const dataDir = path.resolve(__dirname, '../../data') 8 | 9 | test('complete multi suites', async () => { 10 | const data = path.join(dataDir, '/test.xml') 11 | const result = await parse(fs.readFileSync(data).toString()) 12 | expect(result).toEqual(expected) 13 | }) 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # IDEs 4 | .idea 5 | 6 | # dependencies 7 | /node_modules 8 | /.pnp 9 | .pnp.js 10 | 11 | # testing 12 | /coverage 13 | /output 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | .env.local 21 | .env.development.local 22 | .env.test.local 23 | .env.production.local 24 | 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | -------------------------------------------------------------------------------- /src/cli/get-description.js: -------------------------------------------------------------------------------- 1 | export default (suites) => { 2 | const testCounts = {} 3 | Object.values(suites.suites).forEach((suite) => { 4 | Object.values(suite.tests).forEach((test) => { 5 | const status = test.status || 'unknown' 6 | testCounts[status] = testCounts[status] || 0 7 | testCounts[status] += 1 8 | }) 9 | }) 10 | return Object.entries(testCounts) 11 | .map(([status, count]) => { 12 | return `${count} ${status}` 13 | }) 14 | .join(', ') 15 | } 16 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Xunit Viewer 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /src/app/suite-count.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const icons = { 4 | passed: 'check', 5 | failure: 'times', 6 | error: 'exclamation', 7 | skipped: 'ban', 8 | unknown: 'question' 9 | } 10 | 11 | const SuiteCount = ({ count, type }) => count > 0 12 | ? 13 | 14 |