harmonyc 0.7.1 → 0.8.0
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 +3 -8
- package/code_generator/JavaScript.js +4 -4
- package/compiler/compile.js +5 -5
- package/compiler/compiler.js +7 -7
- package/filenames/filenames.js +3 -3
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -56,11 +56,11 @@ The lines of a file are nodes of a tree. The tree is specified with the indentat
|
|
|
56
56
|
|
|
57
57
|
`+` means a fork: the node directly follows its parent node. All siblings with `+` are separate branches, they will generate separate scenarios.
|
|
58
58
|
|
|
59
|
-
###
|
|
59
|
+
### Phrases (actions and responses)
|
|
60
60
|
|
|
61
|
-
After the mark, every node can contain an action and zero or more responses
|
|
61
|
+
After the mark, every node can contain an **action** and zero or more **responses**, together called **phrases**. The action is the text before the `=>`, and the responses are the text after the `=>`.
|
|
62
62
|
|
|
63
|
-
Both actions and responses get compiled to simple function calls - in JavaScript, awaited function calls. The return value of the action is passed to the responses of the same step as the last argument.
|
|
63
|
+
Both actions and responses get compiled to simple function calls - in JavaScript, awaited function calls. Actions will become `When_*` functions, and responses will become `Then_*` functions. The return value of the action is passed to the responses of the same step as the last argument.
|
|
64
64
|
|
|
65
65
|
### Arguments
|
|
66
66
|
|
|
@@ -75,11 +75,6 @@ They are not included in the test case, but the test case name is generated from
|
|
|
75
75
|
|
|
76
76
|
Lines starting with `#` or `//` are comments and are ignored.
|
|
77
77
|
|
|
78
|
-
### Steps
|
|
79
|
-
|
|
80
|
-
All other lines are steps. A step consists of an action, and one or more responses denoted by `=>`.
|
|
81
|
-
Actions will become `When` steps, and responses will become `Then` steps.
|
|
82
|
-
|
|
83
78
|
### Error matching
|
|
84
79
|
|
|
85
80
|
You can use `!!` to denote an error response. This will verify that the action throws an error. You can specify the error message after the `!!`.
|
|
@@ -11,18 +11,18 @@ export class NodeTest {
|
|
|
11
11
|
this.extraArgs = [];
|
|
12
12
|
}
|
|
13
13
|
feature(feature) {
|
|
14
|
-
const
|
|
14
|
+
const phrasesModule = './' + basename(this.sf.name.replace(/.(js|ts)$/, ''));
|
|
15
15
|
const fn = (this.currentFeatureName = pascalCase(feature.name));
|
|
16
16
|
this.phraseFns = new Map();
|
|
17
17
|
if (this.framework === 'vitest') {
|
|
18
18
|
this.tf.print(`import { describe, test, expect } from 'vitest';`);
|
|
19
19
|
}
|
|
20
|
-
this.tf.print(`import ${fn}
|
|
20
|
+
this.tf.print(`import ${fn}Phrases from ${str(phrasesModule)};`);
|
|
21
21
|
this.tf.print(``);
|
|
22
22
|
for (const item of feature.testGroups) {
|
|
23
23
|
item.toCode(this);
|
|
24
24
|
}
|
|
25
|
-
this.sf.print(`export default class ${pascalCase(feature.name)}
|
|
25
|
+
this.sf.print(`export default class ${pascalCase(feature.name)}Phrases {`);
|
|
26
26
|
this.sf.indent(() => {
|
|
27
27
|
for (const ph of this.phraseFns.keys()) {
|
|
28
28
|
const p = this.phraseFns.get(ph);
|
|
@@ -72,7 +72,7 @@ export class NodeTest {
|
|
|
72
72
|
let f = this.featureVars.get(feature);
|
|
73
73
|
if (!f) {
|
|
74
74
|
f = toId(feature, abbrev, this.featureVars);
|
|
75
|
-
this.tf.print(`const ${f} = new ${pascalCase(feature)}
|
|
75
|
+
this.tf.print(`const ${f} = new ${pascalCase(feature)}Phrases();`);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
if (responses.length === 0) {
|
package/compiler/compile.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NodeTest } from "../code_generator/JavaScript.js";
|
|
2
2
|
import { OutFile } from "../code_generator/outFile.js";
|
|
3
3
|
import { parse } from "../parser/parser.js";
|
|
4
|
-
import { base,
|
|
4
|
+
import { base, phrasesFileName, testFileName } from "../filenames/filenames.js";
|
|
5
5
|
import { Feature } from "../model/model.js";
|
|
6
6
|
import { basename } from 'node:path';
|
|
7
7
|
import { autoLabel } from "../optimizations/autoLabel/autoLabel.js";
|
|
@@ -24,9 +24,9 @@ export function compileFeature(fileName, src) {
|
|
|
24
24
|
autoLabel(feature.root);
|
|
25
25
|
const testFn = testFileName(fileName);
|
|
26
26
|
const testFile = new OutFile(testFn);
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const cg = new NodeTest(testFile,
|
|
27
|
+
const phrasesFn = phrasesFileName(fileName);
|
|
28
|
+
const phrasesFile = new OutFile(phrasesFn);
|
|
29
|
+
const cg = new NodeTest(testFile, phrasesFile);
|
|
30
30
|
feature.toCode(cg);
|
|
31
|
-
return { outFile: testFile,
|
|
31
|
+
return { outFile: testFile, phrasesFile };
|
|
32
32
|
}
|
package/compiler/compiler.js
CHANGED
|
@@ -14,7 +14,7 @@ export async function compileFiles(pattern) {
|
|
|
14
14
|
console.log((_a = error.message) !== null && _a !== void 0 ? _a : error);
|
|
15
15
|
}
|
|
16
16
|
console.log(`Compiled ${fns.length} file${fns.length === 1 ? '' : 's'}.`);
|
|
17
|
-
const generated = features.filter((f) => f.
|
|
17
|
+
const generated = features.filter((f) => f.phrasesFileAction === 'generated');
|
|
18
18
|
if (generated.length) {
|
|
19
19
|
console.log(`Generated ${generated.length} steps file${generated.length === 1 ? '' : 's'}.`);
|
|
20
20
|
}
|
|
@@ -26,12 +26,12 @@ export async function compileFile(fn) {
|
|
|
26
26
|
.toString()
|
|
27
27
|
.replace(/\r\n/g, '\n')
|
|
28
28
|
.replace(/\r/g, '\n');
|
|
29
|
-
const { outFile,
|
|
29
|
+
const { outFile, phrasesFile } = compileFeature(fn, src);
|
|
30
30
|
writeFileSync(outFile.name, outFile.value);
|
|
31
|
-
let
|
|
32
|
-
if (!existsSync(
|
|
33
|
-
|
|
34
|
-
writeFileSync(
|
|
31
|
+
let phrasesFileAction = 'ignored';
|
|
32
|
+
if (!existsSync(phrasesFile.name)) {
|
|
33
|
+
phrasesFileAction = 'generated';
|
|
34
|
+
writeFileSync(phrasesFile.name, phrasesFile.value);
|
|
35
35
|
}
|
|
36
|
-
return {
|
|
36
|
+
return { phrasesFileAction, outFile, phrasesFile };
|
|
37
37
|
}
|
package/filenames/filenames.js
CHANGED
|
@@ -6,12 +6,12 @@ export function base(fn) {
|
|
|
6
6
|
export function testFileName(fn) {
|
|
7
7
|
return base(fn) + '.test.mjs';
|
|
8
8
|
}
|
|
9
|
-
export function
|
|
9
|
+
export function phrasesFileName(fn) {
|
|
10
10
|
const baseFn = base(fn);
|
|
11
11
|
const pattern = convertPathToPattern(baseFn);
|
|
12
|
-
const existing = globSync(`${pattern}.
|
|
12
|
+
const existing = globSync(`${pattern}.phrases.{tsx,jsx,ts,js}`);
|
|
13
13
|
if (existing.length) {
|
|
14
14
|
return existing.sort().at(-1);
|
|
15
15
|
}
|
|
16
|
-
return `${baseFn}.
|
|
16
|
+
return `${baseFn}.phrases.ts`;
|
|
17
17
|
}
|
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.
|
|
4
|
+
"version": "0.8.0",
|
|
5
5
|
"author": "Bernát Kalló",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -22,10 +22,9 @@
|
|
|
22
22
|
"url": "https://github.com/harmony-ac/code/issues"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@cucumber/cucumber-expressions": "^17.1.0",
|
|
26
25
|
"fast-glob": "^3.3.2",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
26
|
+
"typescript-parsec": "0.3.4",
|
|
27
|
+
"watcher": "^2.3.1"
|
|
29
28
|
},
|
|
30
29
|
"license": "MIT",
|
|
31
30
|
"keywords": [
|