From f25a04b02b1f2ddb18deced61be1f16f69833642 Mon Sep 17 00:00:00 2001 From: Matteo Cristino <102997993+matteo-cristino@users.noreply.github.com> Date: Thu, 9 May 2024 12:49:30 +0200 Subject: [PATCH] feat: improve error messages color and format (#132) * refactor(core): improve lexer error message * fix(ignored): trim ingored lines * refactor(core): improve parser error with colors and new error message format * refactor(core): improve slangroom error message with new colors and format * test(core): update error tests to new colors and format * fix(fs): catch more error and report them thhrough plugin context fail method * test(fs): update failing tests * test(core): reactivate error tests * test: update error messages on failing tests * refacotr(core): use colored text instead of colored backgorund on parser errors Remove also quotation marks around worng and suggested words * refacotr(core): use colored text instead of colored backgorund on lexer errors * test: update failing tests error message checks * feat(core): improve a bit the parser In case a statment start with 'open' or 'connect' then it will be only parsed against, respectively, open or connect slangroom statements. * refactor(core): imrpove parse error reporintg when multiple tokens are suggested as solution * fix(pocketbase): catch error when listing and return it through context fail method --- pkg/core/src/lexer.ts | 2 +- pkg/core/src/parser.ts | 37 +++++++---- pkg/core/src/slangroom.ts | 16 ++++- pkg/core/test/errors.ts | 123 +++++++++++++++++++++++++++++++---- pkg/core/test/lexer.ts | 2 +- pkg/core/test/parser.ts | 29 ++++++++- pkg/fs/package.json | 1 + pkg/fs/src/plugin.ts | 38 +++++++---- pkg/fs/test/e2e.ts | 43 +++++++++++- pkg/helpers/test/concat.ts | 10 ++- pkg/helpers/test/pick.ts | 7 ++ pkg/http/test/e2e.ts | 7 ++ pkg/ignored/src/index.ts | 2 +- pkg/pocketbase/src/plugin.ts | 18 +++-- pkg/pocketbase/test/e2e.ts | 7 ++ pkg/shell/test/e2e.ts | 14 ++++ pkg/zencode/test/e2e.ts | 7 ++ pnpm-lock.yaml | 52 +++++++-------- 18 files changed, 337 insertions(+), 78 deletions(-) diff --git a/pkg/core/src/lexer.ts b/pkg/core/src/lexer.ts index e1039272..f03d2ba1 100644 --- a/pkg/core/src/lexer.ts +++ b/pkg/core/src/lexer.ts @@ -88,7 +88,7 @@ export class LexError extends Error { constructor(t: Token) { super(); this.name = 'LexError'; - this.message = `unclosed single-quote at ${t.lineNo}:${t.start + 1}-${t.end + 1}: ${t.raw}`; + this.message = `at ${t.lineNo}:${t.start + 1}-${t.end + 1}\n unclosed single-quote \x1b[31m${t.raw}\x1b[0m`; } } diff --git a/pkg/core/src/parser.ts b/pkg/core/src/parser.ts index 0b99c218..73345867 100644 --- a/pkg/core/src/parser.ts +++ b/pkg/core/src/parser.ts @@ -4,6 +4,11 @@ import { PluginMap, Token, type PluginMapKey } from '@slangroom/core'; +export const errorColor = (s: string): string => '\x1b[31m' + s + '\x1b[0m'; +export const suggestedColor = (s: string): string => '\x1b[32m' + s + '\x1b[0m'; +export const missingColor = (s: string): string => '\x1b[36m' + s + '\x1b[0m'; +export const extraColor = (s: string): string => '\x1b[35m' + s + '\x1b[0m'; + /** * Represents an error encountered during the parsing phrase. * @@ -36,11 +41,11 @@ export class ParseError extends Error { * ``` */ static wrong(have: Token, wantFirst: string, ...wantRest: string[]) { - const wantsQuoted = [wantFirst, ...wantRest].map((x) => JSON.stringify(x)).join(' '); - const haveQuoted = JSON.stringify(have.raw); + const wantsColored = [wantFirst, ...wantRest].map((x) => suggestedColor(x)).join(' or '); + const haveRaw = have.raw; return new ParseError( - `${haveQuoted} at ${have.lineNo}:${have.start + 1}-${have.end + 1 - } must be one of: ${wantsQuoted}`, + `at ${have.lineNo}:${have.start + 1}-${have.end + 1 + }\n ${errorColor(haveRaw)} may be ${wantsColored}`, ); } @@ -69,15 +74,15 @@ export class ParseError extends Error { * which means that there exist no prior token. */ static missing(prevTokenOrLineNo: Token | number, wantFirst: string, ...wantRest: string[]) { - const wantsQuoted = [wantFirst, ...wantRest].map((x) => JSON.stringify(x)).join(' '); + const wantsColored = [wantFirst, ...wantRest].map((x) => missingColor(x)).join(', '); if (typeof prevTokenOrLineNo == 'number') { const lineNo = prevTokenOrLineNo; - return new ParseError(`at ${lineNo}, missing one of: ${wantsQuoted}`); + return new ParseError(`at ${lineNo}\n missing one of: ${wantsColored}`); } const token = prevTokenOrLineNo; return new ParseError( `at ${token.lineNo}:${token.start + 1}-${token.end + 1 - }, must be followed by one of: ${wantsQuoted}`, + }\n must be followed by one of: ${wantsColored}`, ); } @@ -92,7 +97,8 @@ export class ParseError extends Error { */ static extra(token: Token) { return new ParseError( - `extra token ${token.lineNo}:${token.start + 1}-${token.end + 1}: ${token.raw}`, + `at ${token.lineNo}:${token.start + 1}-${token.end + 1 + }\n extra token ${extraColor(token.raw)}`, ); } @@ -214,13 +220,17 @@ export const parse = (p: PluginMap, t: Token[], lineNo: number): Cst => { errors: [], }; let givenThen: 'given' | 'then' | undefined; - + let openConnect: 'open' | 'connect' | undefined; if (t[0]) { if (t[0].name != 'given' && t[0].name != 'then') cst.errors.push({ message: ParseError.wrong(t[0], 'given', 'then'), lineNo, start: t[0].start, end: t[0].end }); else givenThen = t[0].name; if (t[1]) { if (t[1].raw !== 'I') cst.errors.push({message: ParseError.wrong(t[1], 'I'), lineNo, start: t[1].start, end: t[1].end}); + if (t[2]) { + if (t[2].raw == 'open') openConnect = 'open'; + else if (t[2].raw == 'connect') openConnect = 'connect'; + } } else cst.errors.push({ message: ParseError.missing(t[0], 'I'), lineNo, start: t[0].start, end: t[0].end}); } else { cst.errors.push({ message: ParseError.missing(lineNo, 'Given I', 'Then I'), lineNo}); @@ -241,19 +251,22 @@ export const parse = (p: PluginMap, t: Token[], lineNo: number): Cst => { if (curErrLen !== undefined && m.err.length > curErrLen) throw lemmeout; }; try { + // check open and connect statement only against the correct statements + if(openConnect && (openConnect !== k.openconnect)) throw lemmeout; + // Open 'ident' and|Connect to 'ident' and if (k.openconnect === 'open') { if (t[++i]?.name !== 'open') newErr(i, 'open'); const ident = t[++i]; if (ident?.isIdent) m.open = ident.raw.slice(1, -1); - else newErr(i, ''); + else newErr(i, '\'\''); if (t[++i]?.name !== 'and') newErr(i, 'and'); } else if (k.openconnect === 'connect') { if (t[++i]?.name !== 'connect') newErr(i, 'connect'); if (t[++i]?.name !== 'to') newErr(i, 'to'); const ident = t[++i]; if (ident?.isIdent) m.connect = ident.raw.slice(1, -1); - else newErr(i, ''); + else newErr(i, '\'\''); if (t[++i]?.name !== 'and') newErr(i, 'and'); } @@ -275,7 +288,7 @@ export const parse = (p: PluginMap, t: Token[], lineNo: number): Cst => { if (ident?.isIdent) { if (tokName) m.bindings.set(tokName.name, ident.raw.slice(1, -1)); } else { - newErr(i, ''); + newErr(i, '\'\''); } if (t[++i]?.name !== 'and') newErr(i, 'and'); }); diff --git a/pkg/core/src/slangroom.ts b/pkg/core/src/slangroom.ts index 7dc8382e..b8c4977d 100644 --- a/pkg/core/src/slangroom.ts +++ b/pkg/core/src/slangroom.ts @@ -5,6 +5,8 @@ import { getIgnoredStatements } from '@slangroom/ignored'; import { type ZenOutput, ZenParams, zencodeExec } from '@slangroom/shared'; import { lex, parse, visit, Plugin, PluginMap, PluginContextImpl } from '@slangroom/core'; +// error colors +import { errorColor, suggestedColor, missingColor, extraColor } from '@slangroom/core'; /** * Just a utility type to ease typing. @@ -145,13 +147,21 @@ const thorwErrors = (errorArray: GenericError[], contract: string) => { let e = ""; for (let i = lineStart; i < lineEnd; i++) { const linePrefix = `${i} | `; - e = e.concat(`\x1b[33m${linePrefix}\x1b[0m${contractLines[i]}\n`); if (i === lineNumber -1) { + let bold = '\x1b[1;30m' + contractLines[i]!.slice(colStart, colEnd) + '\x1b[0m' + '\x1b[41m'; + const redBackground = '\x1b[41m' + contractLines[i]!.slice(0, colStart) + bold + contractLines[i]!.slice(colEnd) + '\x1b[0m'; + e = e.concat(`\x1b[33m${linePrefix}\x1b[0m${redBackground}\n`); e = e.concat(' '.repeat(colStart + linePrefix.length) + '\x1b[31m' + '^'.repeat(colEnd - colStart) + '\x1b[0m', '\n'); - } + } else { e = e.concat(`\x1b[33m${linePrefix}\x1b[0m${contractLines[i]}\n`); } } + e = e.concat('\n' + 'Error colors:\n'); + e = e.concat(` - ${errorColor('error')}\n`); + e = e.concat(` - ${suggestedColor('suggested words')}\n`); + e = e.concat(` - ${missingColor('missing words')}\n`); + e = e.concat(` - ${extraColor('extra words')}\n`); + for (let err of errorArray) { - e = e.concat(err.message + '\n'); + e = e.concat('\n' + err.message + '\n'); } throw new Error(e); } diff --git a/pkg/core/test/errors.ts b/pkg/core/test/errors.ts index e3ca0d0b..72410e00 100644 --- a/pkg/core/test/errors.ts +++ b/pkg/core/test/errors.ts @@ -1,6 +1,9 @@ import { Plugin, Slangroom } from '@slangroom/core'; import test from 'ava'; +// error colors +import { errorColor, suggestedColor, missingColor, extraColor } from '@slangroom/core'; + test('@slangroom/core errors are shown and context is shown with line number', async (t) => { const plugin = new Plugin(); @@ -13,17 +16,37 @@ test('@slangroom/core errors are shown and context is shown with line number', a Then print data`) const expected = `\x1b[33m0 | \x1b[0mRule unknown ignore -\x1b[33m1 | \x1b[0m Given I gibberish +\x1b[33m1 | \x1b[0m\x1b[41m Given I \x1b[1;30mgibberish\x1b[0m\x1b[41m\x1b[0m \x1b[31m^^^^^^^^^\x1b[0m \x1b[33m2 | \x1b[0m Given nothing \x1b[33m3 | \x1b[0m Then print data -ParseError: "gibberish" at 2:9-17 must be one of: "send" -ParseError: at 2:9-17, must be followed by one of: "param" -ParseError: at 2, missing one of: "" -ParseError: at 2, missing one of: "and" -ParseError: at 2, missing one of: "do" -ParseError: at 2, missing one of: "some" -ParseError: at 2, missing one of: "action" + +Error colors: + - ${errorColor('error')} + - ${suggestedColor('suggested words')} + - ${missingColor('missing words')} + - ${extraColor('extra words')} + +ParseError: at 2:9-17 + ${errorColor('gibberish')} may be ${suggestedColor('send')} + +ParseError: at 2:9-17 + must be followed by one of: ${missingColor('param')} + +ParseError: at 2 + missing one of: ${missingColor('\'\'')} + +ParseError: at 2 + missing one of: ${missingColor('and')} + +ParseError: at 2 + missing one of: ${missingColor('do')} + +ParseError: at 2 + missing one of: ${missingColor('some')} + +ParseError: at 2 + missing one of: ${missingColor('action')} ` const err = await t.throwsAsync(fn); @@ -41,13 +64,91 @@ test('@slangroom/core lexer error', async (t) => { Then print data`) const expected = `\x1b[33m0 | \x1b[0mRule unknown ignore -\x1b[33m1 | \x1b[0m Given I send param 'param and do some action +\x1b[33m1 | \x1b[0m\x1b[41m Given I send param \x1b[1;30m'param and do some action\x1b[0m\x1b[41m\x1b[0m \x1b[31m^^^^^^^^^^^^^^^^^^^^^^^^^\x1b[0m \x1b[33m2 | \x1b[0m Given nothing \x1b[33m3 | \x1b[0m Then print data -LexError: unclosed single-quote at 2:20-44: 'param and do some action + +Error colors: + - ${errorColor('error')} + - ${suggestedColor('suggested words')} + - ${missingColor('missing words')} + - ${extraColor('extra words')} + +LexError: at 2:20-44 + unclosed single-quote ${errorColor('\'param and do some action')} ` const err = await t.throwsAsync(fn); - t.is(err?.message, expected); + t.is(err?.message, expected); +}); + + +test('@slangroom/core parser error does not start with given', async (t) => { + const plugin = new Plugin(); + plugin.new('connect', ['param'], 'do some action', (_) => _.pass(null)); + + const slang = new Slangroom(plugin); + const fn = slang.execute(`Rule unknown ignore + Gibberish connect to 'url' and send param 'param' and do some action and aoibndwebnd + Given nothing + Then print data`) + + const expected = `\x1b[33m0 | \x1b[0mRule unknown ignore +\x1b[33m1 | \x1b[0m\x1b[41m \x1b[1;30mGibberish\x1b[0m\x1b[41m connect to 'url' and send param 'param' and do some action and aoibndwebnd\x1b[0m + \x1b[31m^^^^^^^^^\x1b[0m +\x1b[33m2 | \x1b[0m Given nothing +\x1b[33m3 | \x1b[0m Then print data + +Error colors: + - ${errorColor('error')} + - ${suggestedColor('suggested words')} + - ${missingColor('missing words')} + - ${extraColor('extra words')} + +ParseError: at 2:1-9 + ${errorColor('Gibberish')} may be ${suggestedColor('given')} or ${suggestedColor('then')} + +ParseError: at 2:11-17 + ${errorColor('connect')} may be ${suggestedColor('I')} + +ParseError: at 2:19-20 + ${errorColor('to')} may be ${suggestedColor('connect')} + +ParseError: at 2:22-26 + ${errorColor('\'url\'')} may be ${suggestedColor('to')} + +ParseError: at 2:28-30 + ${errorColor('and')} may be ${suggestedColor('\'\'')} + +ParseError: at 2:32-35 + ${errorColor('send')} may be ${suggestedColor('and')} + +ParseError: at 2:37-41 + ${errorColor('param')} may be ${suggestedColor('send')} + +ParseError: at 2:43-49 + ${errorColor('\'param\'')} may be ${suggestedColor('param')} + +ParseError: at 2:51-53 + ${errorColor('and')} may be ${suggestedColor('\'\'')} + +ParseError: at 2:55-56 + ${errorColor('do')} may be ${suggestedColor('and')} + +ParseError: at 2:58-61 + ${errorColor('some')} may be ${suggestedColor('do')} + +ParseError: at 2:63-68 + ${errorColor('action')} may be ${suggestedColor('some')} + +ParseError: at 2:70-72 + ${errorColor('and')} may be ${suggestedColor('action')} + +ParseError: at 2:74-84 + extra token ${extraColor('aoibndwebnd')} +` + + const err = await t.throwsAsync(fn); + t.is(err?.message, expected); }); diff --git a/pkg/core/test/lexer.ts b/pkg/core/test/lexer.ts index f24e2c48..07eb0a8d 100644 --- a/pkg/core/test/lexer.ts +++ b/pkg/core/test/lexer.ts @@ -136,7 +136,7 @@ test('lexer works', (t) => { const res = lex("When I encrypt the secret message 'message", 1); if (res.ok) throw new Error("Lex fail to dectect unclosed single-quote"); - t.is(res.error.message.message as string, `unclosed single-quote at 1:35-42: 'message`); + t.is(res.error.message.message as string, `at 1:35-42\n unclosed single-quote \x1b[31m'message\x1b[0m`); }); test('token constructor erros', (t) => { diff --git a/pkg/core/test/parser.ts b/pkg/core/test/parser.ts index fbaaf463..b90dc5ef 100644 --- a/pkg/core/test/parser.ts +++ b/pkg/core/test/parser.ts @@ -571,7 +571,7 @@ test('parser works', (t) => { {message: ParseError.wrong(new Token('send', 1, 24, 27), 'and'), lineNo: 1, start: 24, end: 27}, {message: ParseError.wrong(new Token('object', 1, 29, 34), 'send'), lineNo: 1, start: 29, end: 34}, {message: ParseError.wrong(new Token('\'myObj\'', 1, 36, 42), 'object'), lineNo: 1, start: 36, end: 42}, - {message: ParseError.wrong(new Token('and', 1, 44, 46), ''), lineNo: 1, start: 44, end: 46}, + {message: ParseError.wrong(new Token('and', 1, 44, 46), '\'\''), lineNo: 1, start: 44, end: 46}, {message: ParseError.wrong(new Token('send', 1, 48, 51), 'and'), lineNo: 1, start: 48, end: 51}, {message: ParseError.wrong(new Token('http', 1, 53, 56), 'send'), lineNo: 1, start: 53, end: 56}, {message: ParseError.wrong(new Token('request', 1, 58, 64), 'http'), lineNo: 1, start: 58, end: 64}, @@ -601,6 +601,33 @@ test('parser works', (t) => { }, ], }, + "Given I connect to": { + givenThen: 'given', + errors: [], + matches: [ + { + key: { + openconnect: 'connect', + phrase: 'send http request', + params: ['object'], + }, + bindings: new Map(), + err: [ + {message: ParseError.missing(new Token('to', 1, 16, 17), '\'\''), lineNo: 1}, + {message: ParseError.missing(1, 'and'), lineNo: 1}, + {message: ParseError.missing(1, 'send'), lineNo: 1}, + {message: ParseError.missing(1, 'object'), lineNo: 1}, + {message: ParseError.missing(1, '\'\''), lineNo: 1}, + {message: ParseError.missing(1, 'and'), lineNo: 1}, + {message: ParseError.missing(1, 'send'), lineNo: 1}, + {message: ParseError.missing(1, 'http'), lineNo: 1}, + {message: ParseError.missing(1, 'request'), lineNo: 1}, + + ], + lineNo: 1, + }, + ], + }, }).forEach(([give, want], index) => { const lexed = lex(give, 1); if (!lexed.ok) throw new Error(lexed.error.message.message); diff --git a/pkg/fs/package.json b/pkg/fs/package.json index 5c50d0eb..17e2b977 100644 --- a/pkg/fs/package.json +++ b/pkg/fs/package.json @@ -3,6 +3,7 @@ "version": "1.29.0", "dependencies": { "@slangroom/core": "workspace:*", + "@slangroom/shared": "workspace:*", "axios": "^1.6.2", "extract-zip": "^2.0.1" }, diff --git a/pkg/fs/src/plugin.ts b/pkg/fs/src/plugin.ts index 0e7fe869..d780ae84 100644 --- a/pkg/fs/src/plugin.ts +++ b/pkg/fs/src/plugin.ts @@ -3,6 +3,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later import { Plugin } from '@slangroom/core'; +import { JsonableObject } from '@slangroom/shared'; import * as path from 'node:path'; import * as fspkg from 'node:fs/promises'; import * as os from 'node:os'; @@ -49,14 +50,21 @@ const resolveFilepath = (unsafe: string): ({ok: true, filepath: string} | {ok: f return { ok: true, filepath: path.join(sandboxdir, normalized) }; }; -const readFile = async (safePath: string) => { - const str = await fspkg.readFile(safePath, 'utf8'); - return str; +const readFile = async (safePath: string): Promise<{ok: true, value: string} | {ok: false, error: string}> => { + try { + return {ok: true, value: await fspkg.readFile(safePath, 'utf8')}; + } catch(e) { + return {ok: false, error: e.message}; + } }; -const readJSON = async (safePath: string) => { - const str = await fspkg.readFile(safePath, 'utf8'); - return JSON.parse(str); +const readJSON = async (safePath: string): Promise<{ok: true, value: JsonableObject} | {ok: false, error: string}> => { + try { + const str = await fspkg.readFile(safePath, 'utf8'); + return {ok: true, value: JSON.parse(str)}; + } catch (e) { + return {ok: false, error: e.message}; + } }; const checkFileExists = async (safePath: string) => { @@ -111,7 +119,9 @@ export const readFileContent = p.new(['path'], 'read file content', async (ctx) const res = resolveFilepath(unsafe); if (!res.ok) return ctx.fail(new FsError(res.error)); - return ctx.pass(await readJSON(res.filepath)); + const cont = await readJSON(res.filepath); + if (!cont.ok) return ctx.fail(new FsError(cont.error)); + return ctx.pass(cont.value); }); /** @@ -123,8 +133,9 @@ export const readVerbatimFileContent = p.new(['path'], 'read verbatim file conte const res = resolveFilepath(unsafe); if (!res.ok) return ctx.fail(new FsError(res.error)); - - return ctx.pass(await readFile(res.filepath)); + const cont = await readFile(res.filepath) + if (!cont.ok) return ctx.fail(new FsError(cont.error)); + return ctx.pass(cont.value); }); /** @@ -139,8 +150,13 @@ export const storeInFile = p.new(['content', 'path'], 'store in file', async (ct const res = resolveFilepath(unsafe); if (!res.ok) return ctx.fail(new FsError(res.error)); - await fspkg.mkdir(path.dirname(res.filepath), { recursive: true }); - await fspkg.writeFile(res.filepath, content); + try { + await fspkg.mkdir(path.dirname(res.filepath), { recursive: true }); + await fspkg.writeFile(res.filepath, content); + } catch (e) { + return ctx.fail(new FsError(e.message)); + } + return ctx.pass(null); }); diff --git a/pkg/fs/test/e2e.ts b/pkg/fs/test/e2e.ts index 4de84c8c..2192400d 100644 --- a/pkg/fs/test/e2e.ts +++ b/pkg/fs/test/e2e.ts @@ -30,6 +30,13 @@ test.serial('unset FILES_DIR', async (t) => { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Given I have a 'string' named 'filename' 3 | Given I have a 'string' named 'content' + +Error colors: + - error + - suggested words + - missing words + - extra words + Slangroom @slangroom/fs Error: $FILES_DIR must be provided `); }); @@ -43,7 +50,20 @@ test.serial('File not found', async (t) => { }, }); const error = await t.throwsAsync(fn); - t.is((error as Error).message, 'ENOENT: no such file or directory, open \'test.txt\''); + t.is(stripAnsiCodes((error as Error).message), `0 | Rule unknown ignore +1 | Given I send path 'filename' and read verbatim file content and output into 'content' + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +2 | Given I have a 'string' named 'filename' +3 | Given I have a 'string' named 'content' + +Error colors: + - error + - suggested words + - missing words + - extra words + +Slangroom @slangroom/fs Error: ENOENT: no such file or directory, open 'test.txt' +`); }); test.serial('Read verbatim file', async (t) => { @@ -78,6 +98,13 @@ test.serial('path not a string', async (t) => { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Given I have a 'string' named 'filename' 3 | Given I have a 'string' named 'content' + +Error colors: + - error + - suggested words + - missing words + - extra words + Slangroom @slangroom/fs Error: path must be string `); }); @@ -113,6 +140,13 @@ Then print the string 'the file exists' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Given I have a 'string' named 'filename' 3 | Then print the 'filename' + +Error colors: + - error + - suggested words + - missing words + - extra words + Slangroom @slangroom/fs Error: no such file or directory: test/test_not_exist.txt `); }); @@ -138,6 +172,13 @@ Then print the string 'the file does not exist' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Given I have a 'string' named 'filename' 3 | Then print the 'filename' + +Error colors: + - error + - suggested words + - missing words + - extra words + Slangroom @slangroom/fs Error: file or directory found under: test/test.txt `); const resultNotExists = slangroom.execute(verifyDoesNotExists, { diff --git a/pkg/helpers/test/concat.ts b/pkg/helpers/test/concat.ts index e7ba898f..bfcacf23 100644 --- a/pkg/helpers/test/concat.ts +++ b/pkg/helpers/test/concat.ts @@ -58,7 +58,15 @@ Then print 'r' ^^^^^^ 2 | Given I have a 'string array' named 'the_array' 3 | Given I have a 'string dictionary' named 'r' -ParseError: "concat" at 2:51-56 must be one of: "compact" + +Error colors: + - error + - suggested words + - missing words + - extra words + +ParseError: at 2:51-56 + concat may be compact `); }); diff --git a/pkg/helpers/test/pick.ts b/pkg/helpers/test/pick.ts index 4cc2969d..7e6a71d4 100644 --- a/pkg/helpers/test/pick.ts +++ b/pkg/helpers/test/pick.ts @@ -183,6 +183,13 @@ Then print 'r' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Given I have a 'string dictionary' named 'complex_object' 3 | Given I have a 'string dictionary' named 'r' + +Error colors: + - error + - suggested words + - missing words + - extra words + Slangroom @slangroom/helper Error: MANIPULATION ERRROR: None of the properties diff --git a/pkg/http/test/e2e.ts b/pkg/http/test/e2e.ts index 01fe68e7..50d704c4 100644 --- a/pkg/http/test/e2e.ts +++ b/pkg/http/test/e2e.ts @@ -116,6 +116,13 @@ Then print data ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 3 | Given I have a 'string' named 'greeting_es' 4 | Then print data + +Error colors: + - error + - suggested words + - missing words + - extra words + Slangroom @slangroom/http Error: sequential requests are not implemented `); }); diff --git a/pkg/ignored/src/index.ts b/pkg/ignored/src/index.ts index 2a9cf2cb..affd4af0 100644 --- a/pkg/ignored/src/index.ts +++ b/pkg/ignored/src/index.ts @@ -47,7 +47,7 @@ export const getIgnoredStatements = async ( throw new Error('no match'); } const lineNo = l.groups['lineNo'], - stmnt = l.groups['statement']; + stmnt = l.groups['statement'].trim(); ret.push([stmnt, parseInt(lineNo)]); }); return ret; diff --git a/pkg/pocketbase/src/plugin.ts b/pkg/pocketbase/src/plugin.ts index 9d6fc196..c51a7c34 100644 --- a/pkg/pocketbase/src/plugin.ts +++ b/pkg/pocketbase/src/plugin.ts @@ -178,13 +178,17 @@ export const getList = p.new(['list_parameters'], 'get some records', async (ctx if (expand) options['expand'] = expand; let res: RecordModel | RecordModel[] | ListResult; - if (type === 'all') { - res = await pb.collection(collection).getFullList(options); - } else if (type === 'list') { - const { page, perPage } = params.pagination; - res = await pb.collection(collection).getList(page, perPage, options); - } else { - res = await pb.collection(collection).getFirstListItem(filter, options); + try { + if (type === 'all') { + res = await pb.collection(collection).getFullList(options); + } else if (type === 'list') { + const { page, perPage } = params.pagination; + res = await pb.collection(collection).getList(page, perPage, options); + } else { + res = await pb.collection(collection).getFirstListItem(filter, options); + } + } catch (err) { + return ctx.fail(new PocketBaseError(err.message)); } //@ts-expect-error Jsonable should take also ListResult return ctx.pass({ records: res }); diff --git a/pkg/pocketbase/test/e2e.ts b/pkg/pocketbase/test/e2e.ts index 4c57f377..4b15e7ba 100644 --- a/pkg/pocketbase/test/e2e.ts +++ b/pkg/pocketbase/test/e2e.ts @@ -63,6 +63,13 @@ test('should create a new slangroom capacitor client', async (t) => { ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 3 | Given I have a 'string' named 'res' 4 | Then print data + +Error colors: + - error + - suggested words + - missing words + - extra words + Slangroom @slangroom/pocketbase Error: Can not start capacitor client in node environment `); }); diff --git a/pkg/shell/test/e2e.ts b/pkg/shell/test/e2e.ts index 3439d4c0..582bc2e1 100644 --- a/pkg/shell/test/e2e.ts +++ b/pkg/shell/test/e2e.ts @@ -48,6 +48,13 @@ Then print data ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Given I have a 'string' named 'r' 3 | + +Error colors: + - error + - suggested words + - missing words + - extra words + Slangroom @slangroom/shell Error: Command failed with ENOENT: notfound -v spawn notfound ENOENT `); @@ -74,6 +81,13 @@ Then print data ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2 | Given I have a 'string' named 'r' 3 | + +Error colors: + - error + - suggested words + - missing words + - extra words + Slangroom @slangroom/shell Error: Command failed with exit code 1: cat notfound.txt cat: notfound.txt: No such file or directory `); diff --git a/pkg/zencode/test/e2e.ts b/pkg/zencode/test/e2e.ts index 5f2b3e8f..8b3b63b6 100644 --- a/pkg/zencode/test/e2e.ts +++ b/pkg/zencode/test/e2e.ts @@ -98,6 +98,13 @@ Then print data ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 3 | Given I have a 'string dictionary' named 'ecdh_public_key' 4 | Then print data + +Error colors: + - error + - suggested words + - missing words + - extra words + Slangroom @slangroom/zencode Error:`)) t.true(((error as Error).message).includes("[!] Zencode runtime error")); t.true(((error as Error).message).includes("Zencode line 2: Given I have the 'string' named 'variable_that_does_not_exists'")); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2685c379..452c773b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -167,7 +167,7 @@ importers: version: 1.6.2 web3: specifier: ^4.3.0 - version: 4.3.0(typescript@5.4.5) + version: 4.3.0(typescript@5.4.3) web3-validator: specifier: ^2.0.3 version: 2.0.3 @@ -177,6 +177,9 @@ importers: '@slangroom/core': specifier: workspace:* version: link:../core + '@slangroom/shared': + specifier: workspace:* + version: link:../shared axios: specifier: ^1.6.2 version: 1.6.2 @@ -2415,7 +2418,7 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dev: true - /abitype@0.7.1(typescript@5.4.5): + /abitype@0.7.1(typescript@5.4.3): resolution: {integrity: sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==} peerDependencies: typescript: '>=4.9.4' @@ -2424,7 +2427,7 @@ packages: zod: optional: true dependencies: - typescript: 5.4.5 + typescript: 5.4.3 dev: false /acorn-import-attributes@1.9.2(acorn@8.11.3): @@ -6390,13 +6393,6 @@ packages: resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==} engines: {node: '>=14.17'} hasBin: true - dev: true - - /typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} - hasBin: true - dev: false /uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} @@ -6659,11 +6655,11 @@ packages: web3-types: 1.3.1 dev: false - /web3-eth-abi@4.1.4(typescript@5.4.5): + /web3-eth-abi@4.1.4(typescript@5.4.3): resolution: {integrity: sha512-YLOBVVxxxLYKXjaiwZjEWYEnkMmmrm0nswZsvzSsINy/UgbWbzfoiZU+zn4YNWIEhORhx1p37iS3u/dP6VyC2w==} engines: {node: '>=14', npm: '>=6.12.0'} dependencies: - abitype: 0.7.1(typescript@5.4.5) + abitype: 0.7.1(typescript@5.4.3) web3-errors: 1.1.4 web3-types: 1.3.1 web3-utils: 4.1.0 @@ -6686,14 +6682,14 @@ packages: web3-validator: 2.0.3 dev: false - /web3-eth-contract@4.1.4(typescript@5.4.5): + /web3-eth-contract@4.1.4(typescript@5.4.3): resolution: {integrity: sha512-tJ4z6QLgtu8EQu2sXnLA7g427oxmngnbAUh+9kJKbP6Yep/oe+z79PqJv7H3MwqwUNW9T+/FeB2PnSQSyxz6ig==} engines: {node: '>=14', npm: '>=6.12.0'} dependencies: web3-core: 4.3.2 web3-errors: 1.1.4 - web3-eth: 4.3.1(typescript@5.4.5) - web3-eth-abi: 4.1.4(typescript@5.4.5) + web3-eth: 4.3.1(typescript@5.4.3) + web3-eth-abi: 4.1.4(typescript@5.4.3) web3-types: 1.3.1 web3-utils: 4.1.0 web3-validator: 2.0.3 @@ -6705,15 +6701,15 @@ packages: - zod dev: false - /web3-eth-ens@4.0.8(typescript@5.4.5): + /web3-eth-ens@4.0.8(typescript@5.4.3): resolution: {integrity: sha512-nj0JfeD45BbzVJcVYpUJnSo8iwDcY9CQ7CZhhIVVOFjvpMAPw0zEwjTvZEIQyCW61OoDG9xcBzwxe2tZoYhMRw==} engines: {node: '>=14', npm: '>=6.12.0'} dependencies: '@adraffy/ens-normalize': 1.10.0 web3-core: 4.3.2 web3-errors: 1.1.4 - web3-eth: 4.3.1(typescript@5.4.5) - web3-eth-contract: 4.1.4(typescript@5.4.5) + web3-eth: 4.3.1(typescript@5.4.3) + web3-eth-contract: 4.1.4(typescript@5.4.3) web3-net: 4.0.7 web3-types: 1.3.1 web3-utils: 4.1.0 @@ -6736,12 +6732,12 @@ packages: web3-validator: 2.0.3 dev: false - /web3-eth-personal@4.0.8(typescript@5.4.5): + /web3-eth-personal@4.0.8(typescript@5.4.3): resolution: {integrity: sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw==} engines: {node: '>=14', npm: '>=6.12.0'} dependencies: web3-core: 4.3.2 - web3-eth: 4.3.1(typescript@5.4.5) + web3-eth: 4.3.1(typescript@5.4.3) web3-rpc-methods: 1.1.4 web3-types: 1.3.1 web3-utils: 4.1.0 @@ -6754,14 +6750,14 @@ packages: - zod dev: false - /web3-eth@4.3.1(typescript@5.4.5): + /web3-eth@4.3.1(typescript@5.4.3): resolution: {integrity: sha512-zJir3GOXooHQT85JB8SrufE+Voo5TtXdjhf1D8IGXmxM8MrhI8AT+Pgt4siBTupJcu5hF17iGmTP/Nj2XnaibQ==} engines: {node: '>=14', npm: '>=6.12.0'} dependencies: setimmediate: 1.0.5 web3-core: 4.3.2 web3-errors: 1.1.4 - web3-eth-abi: 4.1.4(typescript@5.4.5) + web3-eth-abi: 4.1.4(typescript@5.4.3) web3-eth-accounts: 4.1.0 web3-net: 4.0.7 web3-providers-ws: 4.0.7 @@ -6868,19 +6864,19 @@ packages: zod: 3.22.4 dev: false - /web3@4.3.0(typescript@5.4.5): + /web3@4.3.0(typescript@5.4.3): resolution: {integrity: sha512-YiLCsb5wmgJlSxRLzt7Z7H+CmlVVIKD8VaUQaZ+xKVG3Q7CpsO5Z6jmeKnlr6M9c6fDDsDnRM6G8g+nchZehbA==} engines: {node: '>=14.0.0', npm: '>=6.12.0'} dependencies: web3-core: 4.3.2 web3-errors: 1.1.4 - web3-eth: 4.3.1(typescript@5.4.5) - web3-eth-abi: 4.1.4(typescript@5.4.5) + web3-eth: 4.3.1(typescript@5.4.3) + web3-eth-abi: 4.1.4(typescript@5.4.3) web3-eth-accounts: 4.1.0 - web3-eth-contract: 4.1.4(typescript@5.4.5) - web3-eth-ens: 4.0.8(typescript@5.4.5) + web3-eth-contract: 4.1.4(typescript@5.4.3) + web3-eth-ens: 4.0.8(typescript@5.4.3) web3-eth-iban: 4.0.7 - web3-eth-personal: 4.0.8(typescript@5.4.5) + web3-eth-personal: 4.0.8(typescript@5.4.3) web3-net: 4.0.7 web3-providers-http: 4.1.0 web3-providers-ws: 4.0.7