harmonyc 0.10.1 → 0.10.3
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/code_generator/JavaScript.js +3 -0
- package/package.json +4 -1
- package/parser/lexer_rules.js +1 -1
- package/parser/parser.js +8 -8
- package/vitest/index.js +52 -0
|
@@ -52,6 +52,7 @@ export class NodeTest {
|
|
|
52
52
|
this.featureVars.set(new Object(), this.currentFeatureName);
|
|
53
53
|
this.tf.print(`test(${str(t.name)}, async (context) => {`);
|
|
54
54
|
this.tf.indent(() => {
|
|
55
|
+
this.tf.print(`context.task.meta.phrases ??= [];`);
|
|
55
56
|
for (const step of t.steps) {
|
|
56
57
|
step.toCode(this);
|
|
57
58
|
}
|
|
@@ -69,6 +70,7 @@ export class NodeTest {
|
|
|
69
70
|
}
|
|
70
71
|
step(action, responses) {
|
|
71
72
|
this.declareFeatureVariables([action, ...responses]);
|
|
73
|
+
this.tf.print(`context.task.meta.phrases.push(${str(action.toString())});`);
|
|
72
74
|
if (responses.length === 0) {
|
|
73
75
|
action.toCode(this);
|
|
74
76
|
return;
|
|
@@ -80,6 +82,7 @@ export class NodeTest {
|
|
|
80
82
|
try {
|
|
81
83
|
this.extraArgs = [res];
|
|
82
84
|
for (const response of responses) {
|
|
85
|
+
this.tf.print(`context.task.meta.phrases.push(${str(`=> ${response.toString()}`)});`);
|
|
83
86
|
response.toCode(this);
|
|
84
87
|
}
|
|
85
88
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harmonyc",
|
|
3
3
|
"description": "Harmony Code - model-driven BDD for Vitest",
|
|
4
|
-
"version": "0.10.
|
|
4
|
+
"version": "0.10.3",
|
|
5
5
|
"author": "Bernát Kalló",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
"typescript-parsec": "0.3.4",
|
|
27
27
|
"watcher": "^2.3.1"
|
|
28
28
|
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"vitest": "^2.1.0"
|
|
31
|
+
},
|
|
29
32
|
"license": "MIT",
|
|
30
33
|
"keywords": [
|
|
31
34
|
"unit test",
|
package/parser/lexer_rules.js
CHANGED
|
@@ -57,6 +57,6 @@ const rules = [
|
|
|
57
57
|
[true, /^`[^`]+`/g, T.BacktickString],
|
|
58
58
|
[true, /^`[^`]*/g, T.UnclosedBacktickString],
|
|
59
59
|
[true, /^\|(?: .*|(?=\n|$))/g, T.MultilineString],
|
|
60
|
-
[true, /^\|[^ ]/g, T.InvalidMultilineStringMark],
|
|
60
|
+
[true, /^\|[^ \n]/g, T.InvalidMultilineStringMark],
|
|
61
61
|
];
|
|
62
62
|
export default rules;
|
package/parser/parser.js
CHANGED
|
@@ -5,16 +5,16 @@ export function parse(input, production = TEST_DESIGN) {
|
|
|
5
5
|
const tokens = lexer.parse(input);
|
|
6
6
|
return expectSingleResult(expectEOF(production.parse(tokens)));
|
|
7
7
|
}
|
|
8
|
-
export const
|
|
9
|
-
export const NEWLINES = list_sc(tok(T.Newline),
|
|
8
|
+
export const S = rep_sc(tok(T.Space));
|
|
9
|
+
export const NEWLINES = list_sc(tok(T.Newline), S); // empty lines can have spaces
|
|
10
10
|
export const WORDS = apply(tok(T.Words), ({ text }) => new Word(text.trimEnd().split(/\s+/).join(' ')));
|
|
11
11
|
export const ERROR_MARK = apply(tok(T.ErrorMark), ({ text }) => new Word(text));
|
|
12
12
|
export const BULLET_POINT_LIKE_WORD = apply(alt_sc(tok(T.Plus), tok(T.Minus)), ({ text }) => new Word(text));
|
|
13
13
|
export const DOUBLE_QUOTE_STRING = alt_sc(apply(tok(T.DoubleQuoteString), ({ text }) => new StringLiteral(JSON.parse(text))), seq(tok(T.UnclosedDoubleQuoteString), fail('unclosed double-quote string')));
|
|
14
14
|
export const BACKTICK_STRING = apply(tok(T.BacktickString), ({ text }) => new CodeLiteral(text.slice(1, -1)));
|
|
15
|
-
export const DOCSTRING = apply(list_sc(tok(T.MultilineString), seq(tok(T.Newline),
|
|
15
|
+
export const DOCSTRING = apply(list_sc(tok(T.MultilineString), seq(tok(T.Newline), S)), (lines) => lines.map(({ text }) => text.slice(2)).join('\n'));
|
|
16
16
|
export const PART = alt_sc(WORDS, ERROR_MARK, BULLET_POINT_LIKE_WORD, DOUBLE_QUOTE_STRING, BACKTICK_STRING);
|
|
17
|
-
export const PHRASE = seq(opt_sc(list_sc(PART,
|
|
17
|
+
export const PHRASE = seq(opt_sc(list_sc(PART, S)), opt_sc(kright(opt_sc(NEWLINES), kright(S, DOCSTRING))));
|
|
18
18
|
export const ACTION = apply(PHRASE, ([parts, docstring]) => new Action(parts, docstring));
|
|
19
19
|
export const RESPONSE = apply(PHRASE, ([parts, docstring]) => {
|
|
20
20
|
if ((parts === null || parts === void 0 ? void 0 : parts[0]) instanceof Word && parts[0].text === '!!') {
|
|
@@ -22,13 +22,13 @@ export const RESPONSE = apply(PHRASE, ([parts, docstring]) => {
|
|
|
22
22
|
}
|
|
23
23
|
return new Response(parts, docstring);
|
|
24
24
|
});
|
|
25
|
-
export const ARROW = kmid(seq(opt_sc(NEWLINES),
|
|
25
|
+
export const ARROW = kmid(seq(opt_sc(NEWLINES), S), tok(T.ResponseArrow), S);
|
|
26
26
|
export const RESPONSE_ITEM = kright(ARROW, RESPONSE);
|
|
27
27
|
export const STEP = apply(seq(ACTION, rep_sc(RESPONSE_ITEM)), ([action, responses]) => new Step(action, responses).setFork(true));
|
|
28
|
-
export const LABEL = apply(kleft(list_sc(PART,
|
|
28
|
+
export const LABEL = apply(kleft(list_sc(PART, S), seq(tok(T.Colon), S)), (words) => new Label(words.map((w) => w.toString()).join(' ')));
|
|
29
29
|
export const SECTION = apply(LABEL, (text) => new Section(text));
|
|
30
30
|
export const BRANCH = alt_sc(SECTION, STEP); // section first, to make sure there is no colon after step
|
|
31
|
-
export const DENTS = apply(opt_sc(seq(
|
|
31
|
+
export const DENTS = apply(opt_sc(seq(S, alt_sc(tok(T.Plus), tok(T.Minus)), tok(T.Space))), (lineHead) => {
|
|
32
32
|
if (!lineHead)
|
|
33
33
|
return { dent: 0, isFork: true };
|
|
34
34
|
const [dents, seqOrFork] = lineHead;
|
|
@@ -66,7 +66,7 @@ export const TEST_DESIGN = kmid(rep_sc(NEWLINES), apply(list_sc(apply(LINE, (lin
|
|
|
66
66
|
parent.addChild(branch);
|
|
67
67
|
}
|
|
68
68
|
return root;
|
|
69
|
-
}), rep_sc(NEWLINES));
|
|
69
|
+
}), seq(rep_sc(NEWLINES), S));
|
|
70
70
|
function inputText(start, end) {
|
|
71
71
|
let text = '';
|
|
72
72
|
let t = start;
|
package/vitest/index.js
CHANGED
|
@@ -1,9 +1,61 @@
|
|
|
1
1
|
import { watchFiles } from "../cli/watch.js";
|
|
2
|
+
import c from 'tinyrainbow';
|
|
2
3
|
export default function harmonyPlugin({ watchDir, }) {
|
|
3
4
|
return {
|
|
4
5
|
name: 'harmony',
|
|
5
6
|
configureServer(server) {
|
|
6
7
|
watchFiles([`${watchDir}/**/*.harmony`]);
|
|
7
8
|
},
|
|
9
|
+
config(config) {
|
|
10
|
+
var _a, _b;
|
|
11
|
+
var _c;
|
|
12
|
+
(_a = config.test) !== null && _a !== void 0 ? _a : (config.test = {});
|
|
13
|
+
(_b = (_c = config.test).reporters) !== null && _b !== void 0 ? _b : (_c.reporters = ['default']);
|
|
14
|
+
if (!Array.isArray(config.test.reporters)) {
|
|
15
|
+
config.test.reporters = [config.test.reporters];
|
|
16
|
+
}
|
|
17
|
+
config.test.reporters.splice(0, 0, new HarmonyReporter());
|
|
18
|
+
},
|
|
8
19
|
};
|
|
9
20
|
}
|
|
21
|
+
class HarmonyReporter {
|
|
22
|
+
onInit(ctx) {
|
|
23
|
+
this.ctx = ctx;
|
|
24
|
+
}
|
|
25
|
+
onCollected(files) {
|
|
26
|
+
this.files = files;
|
|
27
|
+
}
|
|
28
|
+
onTaskUpdate(packs) {
|
|
29
|
+
if (this.files)
|
|
30
|
+
for (const file of this.files)
|
|
31
|
+
addPhrases(file);
|
|
32
|
+
}
|
|
33
|
+
onFinished(files, errors) {
|
|
34
|
+
for (const file of files)
|
|
35
|
+
addPhrases(file);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function addPhrases(task, depth = 2) {
|
|
39
|
+
var _a, _b;
|
|
40
|
+
if ('tasks' in task) {
|
|
41
|
+
for (const child of task.tasks)
|
|
42
|
+
addPhrases(child, depth + 1);
|
|
43
|
+
}
|
|
44
|
+
else if (task.type === 'test' &&
|
|
45
|
+
((_a = task.result) === null || _a === void 0 ? void 0 : _a.state) === 'fail' &&
|
|
46
|
+
((_b = task.meta) === null || _b === void 0 ? void 0 : _b.hasOwnProperty('phrases')) &&
|
|
47
|
+
task.meta.phrases.length > 0) {
|
|
48
|
+
const x = task;
|
|
49
|
+
x.name +=
|
|
50
|
+
'\n' +
|
|
51
|
+
task.meta
|
|
52
|
+
.phrases.map((step, i, a) => {
|
|
53
|
+
const indent = ' '.repeat(depth);
|
|
54
|
+
const failed = i === a.length - 1;
|
|
55
|
+
const figure = failed ? c.red('×') : c.green('✓');
|
|
56
|
+
return `${indent}${figure} ${step}`;
|
|
57
|
+
})
|
|
58
|
+
.join('\n');
|
|
59
|
+
delete task.meta.phrases; // to make sure not to add them again
|
|
60
|
+
}
|
|
61
|
+
}
|