harmonyc 0.11.2 → 0.12.1
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/VitestGenerator.js +12 -5
- package/model/model.js +14 -4
- package/package.json +1 -1
- package/parser/lexer.js +2 -2
- package/parser/parser.js +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { basename } from 'path';
|
|
2
|
-
import { Arg, Word, } from "../model/model.js";
|
|
2
|
+
import { Arg, Response, Word, } from "../model/model.js";
|
|
3
3
|
export class VitestGenerator {
|
|
4
4
|
constructor(tf, sf) {
|
|
5
5
|
this.tf = tf;
|
|
@@ -60,14 +60,15 @@ export class VitestGenerator {
|
|
|
60
60
|
this.tf.print('});');
|
|
61
61
|
}
|
|
62
62
|
errorStep(action, errorResponse) {
|
|
63
|
-
var _a;
|
|
64
63
|
this.declareFeatureVariables([action]);
|
|
65
64
|
this.tf.print(`context.task.meta.phrases.push(${str(errorResponse.toSingleLineString())});`);
|
|
66
65
|
this.tf.print(`await expect(async () => {`);
|
|
67
66
|
this.tf.indent(() => {
|
|
68
67
|
action.toCode(this);
|
|
69
68
|
});
|
|
70
|
-
this.tf.print(`}).rejects.toThrow(${(
|
|
69
|
+
this.tf.print(`}).rejects.toThrow(${(errorResponse === null || errorResponse === void 0 ? void 0 : errorResponse.message) !== undefined
|
|
70
|
+
? str(errorResponse.message.text)
|
|
71
|
+
: ''});`);
|
|
71
72
|
}
|
|
72
73
|
step(action, responses) {
|
|
73
74
|
this.declareFeatureVariables([action, ...responses]);
|
|
@@ -113,16 +114,22 @@ export class VitestGenerator {
|
|
|
113
114
|
const f = this.featureVars.get(p.feature.name);
|
|
114
115
|
const args = p.args.map((a) => a.toCode(this));
|
|
115
116
|
args.push(...this.extraArgs);
|
|
117
|
+
if (p instanceof Response && p.parts.length === 1 && p.saveToVariable) {
|
|
118
|
+
return this.saveToVariable(p.saveToVariable);
|
|
119
|
+
}
|
|
116
120
|
this.tf.print(`(context.task.meta.phrases.push(${str(p.toString())}),`);
|
|
121
|
+
if (p instanceof Response && p.saveToVariable) {
|
|
122
|
+
this.saveToVariable(p.saveToVariable, '');
|
|
123
|
+
}
|
|
117
124
|
this.tf.print(`await ${f}.${functionName(p)}(${args.join(', ')}));`);
|
|
118
125
|
}
|
|
119
126
|
setVariable(action) {
|
|
120
127
|
this.tf.print(`(context.task.meta.variables ??= {})[${str(action.variableName)}] = ${action.value.toCode(this)};`);
|
|
121
128
|
}
|
|
122
|
-
saveToVariable(s) {
|
|
129
|
+
saveToVariable(s, what = this.extraArgs[0] + ';') {
|
|
123
130
|
if (this.extraArgs.length !== 1)
|
|
124
131
|
return;
|
|
125
|
-
this.tf.print(`(context.task.meta.variables ??= {})[${str(s.variableName)}] = ${
|
|
132
|
+
this.tf.print(`(context.task.meta.variables ??= {})[${str(s.variableName)}] = ${what}`.trimEnd());
|
|
126
133
|
}
|
|
127
134
|
stringLiteral(text, { withVariables }) {
|
|
128
135
|
if (withVariables && text.match(/\$\{/)) {
|
package/model/model.js
CHANGED
|
@@ -256,10 +256,14 @@ export class Action extends Phrase {
|
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
export class Response extends Phrase {
|
|
259
|
-
constructor() {
|
|
260
|
-
super(...
|
|
259
|
+
constructor(parts, saveToVariable) {
|
|
260
|
+
super([...parts, ...(saveToVariable ? [saveToVariable] : [])]);
|
|
261
|
+
this.saveToVariable = saveToVariable;
|
|
261
262
|
this.kind = 'response';
|
|
262
263
|
}
|
|
264
|
+
get isEmpty() {
|
|
265
|
+
return this.parts.length === 0 && !this.saveToVariable;
|
|
266
|
+
}
|
|
263
267
|
toString() {
|
|
264
268
|
return `=> ${super.toString()}`;
|
|
265
269
|
}
|
|
@@ -277,6 +281,9 @@ export class ErrorResponse extends Response {
|
|
|
277
281
|
super(message ? [new DummyKeyword('!!'), message] : [new DummyKeyword('!!')]);
|
|
278
282
|
this.message = message;
|
|
279
283
|
}
|
|
284
|
+
toCode(cg) {
|
|
285
|
+
cg.errorStep;
|
|
286
|
+
}
|
|
280
287
|
}
|
|
281
288
|
export class SetVariable extends Action {
|
|
282
289
|
constructor(variableName, value) {
|
|
@@ -288,14 +295,17 @@ export class SetVariable extends Action {
|
|
|
288
295
|
cg.setVariable(this);
|
|
289
296
|
}
|
|
290
297
|
}
|
|
291
|
-
export class SaveToVariable extends
|
|
298
|
+
export class SaveToVariable extends Part {
|
|
292
299
|
constructor(variableName) {
|
|
293
|
-
super(
|
|
300
|
+
super();
|
|
294
301
|
this.variableName = variableName;
|
|
295
302
|
}
|
|
296
303
|
toCode(cg) {
|
|
297
304
|
cg.saveToVariable(this);
|
|
298
305
|
}
|
|
306
|
+
toString() {
|
|
307
|
+
return `\${${this.variableName}}`;
|
|
308
|
+
}
|
|
299
309
|
}
|
|
300
310
|
export class Precondition extends Branch {
|
|
301
311
|
constructor(state = '') {
|
package/package.json
CHANGED
package/parser/lexer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TokenError } from 'typescript-parsec';
|
|
2
|
-
import rules from
|
|
3
|
-
export { T } from
|
|
2
|
+
import rules from './lexer_rules.js';
|
|
3
|
+
export { T } from './lexer_rules.js';
|
|
4
4
|
// based on https://github.com/microsoft/ts-parsec/blob/3350fcb/packages/ts-parsec/src/Lexer.ts
|
|
5
5
|
/*
|
|
6
6
|
MIT License
|
package/parser/parser.js
CHANGED
|
@@ -5,7 +5,7 @@ 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 NEWLINES = list_sc(tok(T.Newline), nil()), WORDS = apply(tok(T.Words), ({ text }) => new Word(text.trimEnd().split(/\s+/).join(' '))), DOUBLE_QUOTE_STRING = alt_sc(apply(tok(T.DoubleQuoteString), ({ text }) => new StringLiteral(JSON.parse(text))), seq(tok(T.UnclosedDoubleQuoteString), fail('unclosed double-quote string'))), BACKTICK_STRING = apply(tok(T.BacktickString), ({ text }) => new CodeLiteral(text.slice(1, -1))), DOCSTRING = kright(opt_sc(NEWLINES), apply(list_sc(tok(T.MultilineString), tok(T.Newline)), (lines) => new Docstring(lines.map(({ text }) => text.slice(2)).join('\n')))), ERROR_MARK = tok(T.ErrorMark), VARIABLE = apply(tok(T.Variable), ({ text }) => text.slice(2, -1)), PART = alt_sc(WORDS, DOUBLE_QUOTE_STRING, BACKTICK_STRING, DOCSTRING), PHRASE = rep_sc(PART), ARG = alt_sc(DOUBLE_QUOTE_STRING, BACKTICK_STRING, DOCSTRING), SET_VARIABLE = apply(seq(VARIABLE, ARG), ([variable, value]) => new SetVariable(variable, value)), ACTION = alt_sc(SET_VARIABLE, apply(PHRASE, (parts) => new Action(parts))), RESPONSE = apply(PHRASE, (parts) => new Response(parts)), ERROR_RESPONSE = apply(seq(ERROR_MARK, opt_sc(alt_sc(DOUBLE_QUOTE_STRING, DOCSTRING))), ([, parts]) => new ErrorResponse(parts)), SAVE_TO_VARIABLE = apply(VARIABLE, (variable) => new SaveToVariable(variable)), ARROW = kright(opt_sc(NEWLINES), tok(T.ResponseArrow)), RESPONSE_ITEM = kright(ARROW, alt_sc(SAVE_TO_VARIABLE, ERROR_RESPONSE, RESPONSE)), STEP = apply(seq(ACTION, rep_sc(RESPONSE_ITEM)), ([action, responses]) => new Step(action, responses).setFork(true)), LABEL = apply(kleft(list_sc(PART, nil()), tok(T.Colon)), (words) => new Label(words.map((w) => w.toString()).join(' '))), SECTION = apply(LABEL, (text) => new Section(text)), BRANCH = alt_sc(SECTION, STEP), // section first, to make sure there is no colon after step
|
|
8
|
+
export const NEWLINES = list_sc(tok(T.Newline), nil()), WORDS = apply(tok(T.Words), ({ text }) => new Word(text.trimEnd().split(/\s+/).join(' '))), DOUBLE_QUOTE_STRING = alt_sc(apply(tok(T.DoubleQuoteString), ({ text }) => new StringLiteral(JSON.parse(text))), seq(tok(T.UnclosedDoubleQuoteString), fail('unclosed double-quote string'))), BACKTICK_STRING = apply(tok(T.BacktickString), ({ text }) => new CodeLiteral(text.slice(1, -1))), DOCSTRING = kright(opt_sc(NEWLINES), apply(list_sc(tok(T.MultilineString), tok(T.Newline)), (lines) => new Docstring(lines.map(({ text }) => text.slice(2)).join('\n')))), ERROR_MARK = tok(T.ErrorMark), VARIABLE = apply(tok(T.Variable), ({ text }) => text.slice(2, -1)), PART = alt_sc(WORDS, DOUBLE_QUOTE_STRING, BACKTICK_STRING, DOCSTRING), PHRASE = rep_sc(PART), ARG = alt_sc(DOUBLE_QUOTE_STRING, BACKTICK_STRING, DOCSTRING), SET_VARIABLE = apply(seq(VARIABLE, ARG), ([variable, value]) => new SetVariable(variable, value)), ACTION = alt_sc(SET_VARIABLE, apply(PHRASE, (parts) => new Action(parts))), RESPONSE = apply(seq(PHRASE, opt_sc(VARIABLE)), ([parts, variable]) => new Response(parts, variable !== undefined ? new SaveToVariable(variable) : undefined)), ERROR_RESPONSE = apply(seq(ERROR_MARK, opt_sc(alt_sc(DOUBLE_QUOTE_STRING, DOCSTRING))), ([, parts]) => new ErrorResponse(parts)), SAVE_TO_VARIABLE = apply(VARIABLE, (variable) => new Response([], new SaveToVariable(variable))), ARROW = kright(opt_sc(NEWLINES), tok(T.ResponseArrow)), RESPONSE_ITEM = kright(ARROW, alt_sc(SAVE_TO_VARIABLE, ERROR_RESPONSE, RESPONSE)), STEP = apply(seq(ACTION, rep_sc(RESPONSE_ITEM)), ([action, responses]) => new Step(action, responses).setFork(true)), LABEL = apply(kleft(list_sc(PART, nil()), tok(T.Colon)), (words) => new Label(words.map((w) => w.toString()).join(' '))), SECTION = apply(LABEL, (text) => new Section(text)), BRANCH = alt_sc(SECTION, STEP), // section first, to make sure there is no colon after step
|
|
9
9
|
DENTS = apply(alt_sc(tok(T.Plus), tok(T.Minus)), (seqOrFork) => {
|
|
10
10
|
return {
|
|
11
11
|
dent: (seqOrFork.text.length - 2) / 2,
|