jats-xml 0.0.16 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,6 +53,28 @@ This will provide a summary, including a list of what the JATS file contains.
53
53
  jats validate article.jats --jats 1.2 --mathmml 2
54
54
  ```
55
55
 
56
+ `test`: test a JATS file against a list of unit tests in YAML
57
+
58
+ The test cases are useful for known exports and expecting specific pieces of information in the XML.
59
+
60
+ ```bash
61
+ jats test article.jats --cases tests.yml
62
+ ```
63
+
64
+ ```yaml
65
+ cases:
66
+ - title: Correct publisher ID (publisher-id)
67
+ select: 'front > journal-meta > journal-id[journal-id-type="publisher-id"] > *'
68
+ equals:
69
+ type: text
70
+ value: plos
71
+ - title: Every orcid is authenticated
72
+ selectAll: 'front > article-meta > contrib-group > contrib > contrib-id'
73
+ equals:
74
+ contrib-id-type: orcid
75
+ authenticated: 'true'
76
+ ```
77
+
56
78
  ## Working in Typescript
57
79
 
58
80
  All tags are accessible as types/enums. There is also documentation from each node-type
@@ -8,9 +8,11 @@ const commander_1 = __importDefault(require("commander"));
8
8
  const version_1 = __importDefault(require("../version"));
9
9
  const parse_1 = require("./parse");
10
10
  const validate_1 = require("./validate");
11
+ const jats_test_1 = require("./jats-test");
11
12
  const program = new commander_1.default.Command();
12
13
  (0, parse_1.addDownloadCLI)(program);
13
14
  (0, validate_1.addValidateCLI)(program);
15
+ (0, jats_test_1.addTestCLI)(program);
14
16
  program.version(`v${version_1.default}`, '-v, --version', 'Print the current version of jats-xml');
15
17
  program.option('-d, --debug', 'Log out any errors to the console.');
16
18
  program.parse(process.argv);
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.addTestCLI = exports.testJatsFile = void 0;
16
+ const commander_1 = require("commander");
17
+ const myst_cli_utils_1 = require("myst-cli-utils");
18
+ const session_1 = require("../session");
19
+ const js_yaml_1 = __importDefault(require("js-yaml"));
20
+ const fs_1 = __importDefault(require("fs"));
21
+ const parse_1 = require("./parse");
22
+ const unist_util_select_1 = require("unist-util-select");
23
+ const unist_util_is_1 = require("unist-util-is");
24
+ const chalk_1 = __importDefault(require("chalk"));
25
+ const INDENT = ' ';
26
+ function printNodes(expected, received) {
27
+ return chalk_1.default.reset(`\n${INDENT}${chalk_1.default.greenBright('Expected node containing')}:\n${INDENT} ${js_yaml_1.default
28
+ .dump(expected)
29
+ .replace(/\n/g, `\n${INDENT} `)}\n${INDENT}${chalk_1.default.redBright('Received node')}:\n${INDENT} ${js_yaml_1.default.dump(received).replace(/\n/g, `\n${INDENT} `)}`);
30
+ }
31
+ function testJatsFile(session, file, opts) {
32
+ return __awaiter(this, void 0, void 0, function* () {
33
+ const toc = (0, myst_cli_utils_1.tic)();
34
+ const jats = yield (0, parse_1.parseJats)(session, file);
35
+ const tests = js_yaml_1.default.load(fs_1.default.readFileSync(opts.cases).toString());
36
+ const results = tests.cases.map((testCase, index) => {
37
+ if (!testCase.title) {
38
+ return [`Test Case ${index}`, null, 'Test must include a title'];
39
+ }
40
+ if (testCase.equals === undefined) {
41
+ return [testCase.title, null, 'Test must have an equals statement'];
42
+ }
43
+ if (testCase.select) {
44
+ const node = (0, unist_util_select_1.select)(testCase.select, jats.tree);
45
+ const pass = (0, unist_util_is_1.is)(node, testCase.equals);
46
+ if (testCase.equals == null && node) {
47
+ return [testCase.title, false, 'Expected no node to be present'];
48
+ }
49
+ if (!node && testCase.equals == null)
50
+ return [testCase.title, true];
51
+ if (!node)
52
+ return [testCase.title, false];
53
+ let failed = false;
54
+ const messages = [];
55
+ if (!pass) {
56
+ failed = failed || true;
57
+ messages.push(`Failed to validate node\n${printNodes(testCase.equals, node)}`);
58
+ }
59
+ return [testCase.title, !failed, messages.join('\n')];
60
+ }
61
+ else if (testCase.selectAll) {
62
+ const testNodes = (0, unist_util_select_1.selectAll)(testCase.selectAll, jats.tree);
63
+ if (!testNodes && testCase.equals == null)
64
+ return [testCase.title, true];
65
+ if (!testNodes)
66
+ return [testCase.title, false, 'Node not found'];
67
+ let equals = testCase.equals;
68
+ if (!Array.isArray(testCase.equals)) {
69
+ equals = Array(testNodes.length).fill(testCase.equals);
70
+ }
71
+ let failed = false;
72
+ const messages = [];
73
+ if (equals.length !== testNodes.length) {
74
+ failed = failed || true;
75
+ messages.push(`Expected ${equals.length} nodes, got ${testNodes.length}\n${printNodes(equals, testNodes)}`);
76
+ }
77
+ else {
78
+ equals.forEach((node, ii) => {
79
+ const pass = (0, unist_util_is_1.is)(testNodes[ii], node);
80
+ if (!pass) {
81
+ failed = failed || true;
82
+ messages.push(`Failed to validate node ${ii}\n${printNodes(node, testNodes[ii])}`);
83
+ }
84
+ });
85
+ }
86
+ return [testCase.title, !failed, messages.join('\n')];
87
+ }
88
+ else {
89
+ return [testCase.title, false, 'Test must have either `select` or `selectAll`'];
90
+ }
91
+ });
92
+ results.forEach((result) => {
93
+ const [title, pass, message] = result;
94
+ if (pass === null)
95
+ session.log.info(`${chalk_1.default.redBright.bold(`ERROR`)} - ${title}\n ${chalk_1.default.blueBright(message)}`);
96
+ else if (pass)
97
+ session.log.info(`${chalk_1.default.green(`PASS`)} - ${title}`);
98
+ else
99
+ session.log.info(`${chalk_1.default.red(`FAIL`)} - ${title}\n\n${INDENT}${chalk_1.default.blueBright(message)}\n`);
100
+ }, true);
101
+ const passed = results.reduce((num, [, pass]) => num + (pass ? 1 : 0), 0);
102
+ const failed = results.length - passed;
103
+ if (failed > 0 && passed === 0) {
104
+ throw new Error(toc(`${chalk_1.default.red(`Failed ${failed} tests in %s`)} 👎`));
105
+ }
106
+ if (failed > 0) {
107
+ throw new Error(toc(`${chalk_1.default.green(`Passed ${passed}/${results.length} tests in %s`)}\n${chalk_1.default.red(`Failed ${failed} tests`)} 👎`));
108
+ }
109
+ session.log.info(chalk_1.default.green(toc(`Passed ${passed} tests in %s 🚀`)));
110
+ return true;
111
+ });
112
+ }
113
+ exports.testJatsFile = testJatsFile;
114
+ function makeTestCLI(program) {
115
+ const command = new commander_1.Command('test')
116
+ .description('Test JATS file against a list of cases')
117
+ .argument('<file>', 'JATS file to test')
118
+ .addOption(new commander_1.Option('--cases <value>', 'The YAML file of unit tests to test against'))
119
+ .action((0, myst_cli_utils_1.clirun)(testJatsFile, { program, getSession: session_1.getSession }));
120
+ return command;
121
+ }
122
+ function addTestCLI(program) {
123
+ program.addCommand(makeTestCLI(program));
124
+ }
125
+ exports.addTestCLI = addTestCLI;
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.addDownloadCLI = void 0;
15
+ exports.addDownloadCLI = exports.parseJats = void 0;
16
16
  const commander_1 = require("commander");
17
17
  const fs_1 = __importDefault(require("fs"));
18
18
  const path_1 = require("path");
@@ -77,6 +77,7 @@ function parseJats(session, file, opts = { resolvers: resolvers_1.DEFAULT_RESOLV
77
77
  return jats;
78
78
  });
79
79
  }
80
+ exports.parseJats = parseJats;
80
81
  function formatLongString(data, offset = 0, length = 88 - offset) {
81
82
  const out = [data.slice(0, length)];
82
83
  let left = data.slice(length);
package/dist/cjs/utils.js CHANGED
@@ -14,13 +14,18 @@ function convertToUnist(node) {
14
14
  const { name, attributes, elements } = node;
15
15
  const children = elements === null || elements === void 0 ? void 0 : elements.map(convertToUnist).filter((n) => !!n);
16
16
  const next = Object.assign({ type: name !== null && name !== void 0 ? name : 'unknown' }, attributes);
17
- if (children)
17
+ if (name === 'code') {
18
+ next.value = elements === null || elements === void 0 ? void 0 : elements[0].text;
19
+ }
20
+ else if (children)
18
21
  next.children = children;
19
22
  return next;
20
23
  }
21
24
  case 'text': {
22
25
  const { attributes, text } = node;
23
- return Object.assign(Object.assign({ type: 'text' }, attributes), { value: String(text) });
26
+ return Object.assign(Object.assign({ type: 'text' }, attributes), { value: String(text)
27
+ .replace(/\n(\s+)$/, '')
28
+ .replace('\n', ' ') });
24
29
  }
25
30
  case 'cdata': {
26
31
  const { attributes, cdata } = node;
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const version = '0.0.16';
3
+ const version = '0.0.17';
4
4
  exports.default = version;
@@ -3,9 +3,11 @@ import commander from 'commander';
3
3
  import version from '../version';
4
4
  import { addDownloadCLI } from './parse';
5
5
  import { addValidateCLI } from './validate';
6
+ import { addTestCLI } from './jats-test';
6
7
  const program = new commander.Command();
7
8
  addDownloadCLI(program);
8
9
  addValidateCLI(program);
10
+ addTestCLI(program);
9
11
  program.version(`v${version}`, '-v, --version', 'Print the current version of jats-xml');
10
12
  program.option('-d, --debug', 'Log out any errors to the console.');
11
13
  program.parse(process.argv);
@@ -0,0 +1,117 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Command, Option } from 'commander';
11
+ import { clirun, tic } from 'myst-cli-utils';
12
+ import { getSession } from '../session';
13
+ import yaml from 'js-yaml';
14
+ import fs from 'fs';
15
+ import { parseJats } from './parse';
16
+ import { select, selectAll } from 'unist-util-select';
17
+ import { is } from 'unist-util-is';
18
+ import chalk from 'chalk';
19
+ const INDENT = ' ';
20
+ function printNodes(expected, received) {
21
+ return chalk.reset(`\n${INDENT}${chalk.greenBright('Expected node containing')}:\n${INDENT} ${yaml
22
+ .dump(expected)
23
+ .replace(/\n/g, `\n${INDENT} `)}\n${INDENT}${chalk.redBright('Received node')}:\n${INDENT} ${yaml.dump(received).replace(/\n/g, `\n${INDENT} `)}`);
24
+ }
25
+ export function testJatsFile(session, file, opts) {
26
+ return __awaiter(this, void 0, void 0, function* () {
27
+ const toc = tic();
28
+ const jats = yield parseJats(session, file);
29
+ const tests = yaml.load(fs.readFileSync(opts.cases).toString());
30
+ const results = tests.cases.map((testCase, index) => {
31
+ if (!testCase.title) {
32
+ return [`Test Case ${index}`, null, 'Test must include a title'];
33
+ }
34
+ if (testCase.equals === undefined) {
35
+ return [testCase.title, null, 'Test must have an equals statement'];
36
+ }
37
+ if (testCase.select) {
38
+ const node = select(testCase.select, jats.tree);
39
+ const pass = is(node, testCase.equals);
40
+ if (testCase.equals == null && node) {
41
+ return [testCase.title, false, 'Expected no node to be present'];
42
+ }
43
+ if (!node && testCase.equals == null)
44
+ return [testCase.title, true];
45
+ if (!node)
46
+ return [testCase.title, false];
47
+ let failed = false;
48
+ const messages = [];
49
+ if (!pass) {
50
+ failed = failed || true;
51
+ messages.push(`Failed to validate node\n${printNodes(testCase.equals, node)}`);
52
+ }
53
+ return [testCase.title, !failed, messages.join('\n')];
54
+ }
55
+ else if (testCase.selectAll) {
56
+ const testNodes = selectAll(testCase.selectAll, jats.tree);
57
+ if (!testNodes && testCase.equals == null)
58
+ return [testCase.title, true];
59
+ if (!testNodes)
60
+ return [testCase.title, false, 'Node not found'];
61
+ let equals = testCase.equals;
62
+ if (!Array.isArray(testCase.equals)) {
63
+ equals = Array(testNodes.length).fill(testCase.equals);
64
+ }
65
+ let failed = false;
66
+ const messages = [];
67
+ if (equals.length !== testNodes.length) {
68
+ failed = failed || true;
69
+ messages.push(`Expected ${equals.length} nodes, got ${testNodes.length}\n${printNodes(equals, testNodes)}`);
70
+ }
71
+ else {
72
+ equals.forEach((node, ii) => {
73
+ const pass = is(testNodes[ii], node);
74
+ if (!pass) {
75
+ failed = failed || true;
76
+ messages.push(`Failed to validate node ${ii}\n${printNodes(node, testNodes[ii])}`);
77
+ }
78
+ });
79
+ }
80
+ return [testCase.title, !failed, messages.join('\n')];
81
+ }
82
+ else {
83
+ return [testCase.title, false, 'Test must have either `select` or `selectAll`'];
84
+ }
85
+ });
86
+ results.forEach((result) => {
87
+ const [title, pass, message] = result;
88
+ if (pass === null)
89
+ session.log.info(`${chalk.redBright.bold(`ERROR`)} - ${title}\n ${chalk.blueBright(message)}`);
90
+ else if (pass)
91
+ session.log.info(`${chalk.green(`PASS`)} - ${title}`);
92
+ else
93
+ session.log.info(`${chalk.red(`FAIL`)} - ${title}\n\n${INDENT}${chalk.blueBright(message)}\n`);
94
+ }, true);
95
+ const passed = results.reduce((num, [, pass]) => num + (pass ? 1 : 0), 0);
96
+ const failed = results.length - passed;
97
+ if (failed > 0 && passed === 0) {
98
+ throw new Error(toc(`${chalk.red(`Failed ${failed} tests in %s`)} 👎`));
99
+ }
100
+ if (failed > 0) {
101
+ throw new Error(toc(`${chalk.green(`Passed ${passed}/${results.length} tests in %s`)}\n${chalk.red(`Failed ${failed} tests`)} 👎`));
102
+ }
103
+ session.log.info(chalk.green(toc(`Passed ${passed} tests in %s 🚀`)));
104
+ return true;
105
+ });
106
+ }
107
+ function makeTestCLI(program) {
108
+ const command = new Command('test')
109
+ .description('Test JATS file against a list of cases')
110
+ .argument('<file>', 'JATS file to test')
111
+ .addOption(new Option('--cases <value>', 'The YAML file of unit tests to test against'))
112
+ .action(clirun(testJatsFile, { program, getSession }));
113
+ return command;
114
+ }
115
+ export function addTestCLI(program) {
116
+ program.addCommand(makeTestCLI(program));
117
+ }
@@ -53,7 +53,7 @@ function logAboutJatsFailing(session, jatsUrls) {
53
53
  session.log.debug(formatPrinciples('A*', { chalk }));
54
54
  session.log.info(`\n${chalk.blue('The link may work in a browser.')}\n`);
55
55
  }
56
- function parseJats(session, file, opts = { resolvers: DEFAULT_RESOLVERS }) {
56
+ export function parseJats(session, file, opts = { resolvers: DEFAULT_RESOLVERS }) {
57
57
  return __awaiter(this, void 0, void 0, function* () {
58
58
  const toc = tic();
59
59
  if (fs.existsSync(file)) {
package/dist/esm/utils.js CHANGED
@@ -8,13 +8,18 @@ export function convertToUnist(node) {
8
8
  const { name, attributes, elements } = node;
9
9
  const children = elements === null || elements === void 0 ? void 0 : elements.map(convertToUnist).filter((n) => !!n);
10
10
  const next = Object.assign({ type: name !== null && name !== void 0 ? name : 'unknown' }, attributes);
11
- if (children)
11
+ if (name === 'code') {
12
+ next.value = elements === null || elements === void 0 ? void 0 : elements[0].text;
13
+ }
14
+ else if (children)
12
15
  next.children = children;
13
16
  return next;
14
17
  }
15
18
  case 'text': {
16
19
  const { attributes, text } = node;
17
- return Object.assign(Object.assign({ type: 'text' }, attributes), { value: String(text) });
20
+ return Object.assign(Object.assign({ type: 'text' }, attributes), { value: String(text)
21
+ .replace(/\n(\s+)$/, '')
22
+ .replace('\n', ' ') });
18
23
  }
19
24
  case 'cdata': {
20
25
  const { attributes, cdata } = node;
@@ -1,2 +1,2 @@
1
- const version = '0.0.16';
1
+ const version = '0.0.17';
2
2
  export default version;