Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(core): cover almost all branches in visitor.ts, plugin.ts and slangroom.ts with tests #176

Merged
merged 4 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions pkg/core/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,7 @@
) {
phrase = phraseOrParamsOrOpenconnect;
executor = executorOrPhraseOrParams;
} else {
/* c8 ignore next 3 */
} /* c8 ignore next 4 */ else {
// This should be unreachable.
throw new Error('unreachable');
}
Expand Down Expand Up @@ -397,7 +396,7 @@
/**
* Result of a plugin execution, indicating failure.
*/
export type ResultErr = { ok: false; error: any };

Check failure on line 399 in pkg/core/src/plugin.ts

View workflow job for this annotation

GitHub Actions / build_and_test (lts/*)

Unexpected any. Specify a different type

Check failure on line 399 in pkg/core/src/plugin.ts

View workflow job for this annotation

GitHub Actions / build_and_test (22.2.0)

Unexpected any. Specify a different type

/**
* The Plugin Context. It has every info a plugin needs, plus some utilities.
Expand Down Expand Up @@ -453,7 +452,7 @@
* The utility function that makes a {@link Plugin} fail. Must be used with a
* `return` statement.
*/
fail(reason: any): PluginResult;

Check failure on line 455 in pkg/core/src/plugin.ts

View workflow job for this annotation

GitHub Actions / build_and_test (lts/*)

Unexpected any. Specify a different type

Check failure on line 455 in pkg/core/src/plugin.ts

View workflow job for this annotation

GitHub Actions / build_and_test (22.2.0)

Unexpected any. Specify a different type

/**
* The utility function that makes a {@link Plugin} pass/succeed. Must be used with
Expand Down Expand Up @@ -489,7 +488,7 @@
/**
* {@inheritDoc PluginContext.fail}
*/
fail(reason: any): PluginResult {

Check failure on line 491 in pkg/core/src/plugin.ts

View workflow job for this annotation

GitHub Actions / build_and_test (lts/*)

Unexpected any. Specify a different type

Check failure on line 491 in pkg/core/src/plugin.ts

View workflow job for this annotation

GitHub Actions / build_and_test (22.2.0)

Unexpected any. Specify a different type
return { ok: false, error: reason };
}

Expand Down Expand Up @@ -592,7 +591,7 @@
/**
* {@inheritDoc PluginContext.fail}
*/
fail(reason: any): PluginResult {

Check failure on line 594 in pkg/core/src/plugin.ts

View workflow job for this annotation

GitHub Actions / build_and_test (lts/*)

Unexpected any. Specify a different type

Check failure on line 594 in pkg/core/src/plugin.ts

View workflow job for this annotation

GitHub Actions / build_and_test (22.2.0)

Unexpected any. Specify a different type
return { ok: false, error: reason };
}

Expand Down
77 changes: 56 additions & 21 deletions pkg/core/test/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Plugin, Slangroom } from '@slangroom/core';
import test from 'ava';
// read the version from the package.json
import packageJson from '@slangroom/core/package.json' with { type: 'json' };
import ignoredPackageJson from '@slangroom/ignored/package.json' with { type: 'json' };

import {
sentenceHighlight,
textHighlight,
Expand All @@ -16,6 +18,13 @@ import {
lineNoColor
} from '@slangroom/shared';

const errorColorDef = `
Error colors:
- ${errorColor('error')}
- ${suggestedColor('suggested words')}
- ${missingColor('missing words')}
- ${extraColor('extra words')}
`

test('@slangroom/core errors are shown and context is shown with line number', async (t) => {
const plugin = new Plugin();
Expand All @@ -32,13 +41,7 @@ ${lineNoColor('1 | ')}${sentenceHighlight(` Given I ${textHighlight('gibberis
${errorColor('^^^^^^^^^')}
${lineNoColor('2 | ')} Given nothing
${lineNoColor('3 | ')} Then print data

Error colors:
- ${errorColor('error')}
- ${suggestedColor('suggested words')}
- ${missingColor('missing words')}
- ${extraColor('extra words')}

${errorColorDef}
ParseError @slangroom/core@${packageJson.version}: at 2:9-17
${errorColor('gibberish')} may be ${suggestedColor('send')}

Expand Down Expand Up @@ -80,13 +83,7 @@ ${lineNoColor('1 | ')}${sentenceHighlight(` Given I send param ${textHighligh
${errorColor('^^^^^^^^^^^^^^^^^^^^^^^^^')}
${lineNoColor('2 | ')} Given nothing
${lineNoColor('3 | ')} Then print data

Error colors:
- ${errorColor('error')}
- ${suggestedColor('suggested words')}
- ${missingColor('missing words')}
- ${extraColor('extra words')}

${errorColorDef}
LexError @slangroom/core@${packageJson.version}: at 2:20-44
unclosed single-quote ${errorColor('\'param and do some action')}
`
Expand All @@ -111,13 +108,7 @@ ${lineNoColor('1 | ')}${sentenceHighlight(` ${textHighlight('Gibberish')} con
${errorColor('^^^^^^^^^')}
${lineNoColor('2 | ')} Given nothing
${lineNoColor('3 | ')} Then print data

Error colors:
- ${errorColor('error')}
- ${suggestedColor('suggested words')}
- ${missingColor('missing words')}
- ${extraColor('extra words')}

${errorColorDef}
ParseError @slangroom/core@${packageJson.version}: at 2:1-9
${errorColor('Gibberish')} may be ${suggestedColor('given')} or ${suggestedColor('then')}

Expand Down Expand Up @@ -164,3 +155,47 @@ ParseError @slangroom/core@${packageJson.version}: at 2:74-84
const err = await t.throwsAsync(fn);
t.is(err?.message, expected);
});

test('@slangroom/core invalid line error', 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
Given I connect to 'url' and send param 'param' and do some action
Given nothing
Gibberish
Then print data`)

const expected = `${lineNoColor('2 | ')} Given nothing
${lineNoColor('3 | ')}${sentenceHighlight(` ${textHighlight('Gibberish')}`)}
${errorColor('^^^^^^^^^')}
${lineNoColor('4 | ')} Then print data
${errorColorDef}
Zencode Invalid Statement @slangroom/ignored@${ignoredPackageJson.version} Error: Invalid Zencode line
`

const err = await t.throwsAsync(fn);
t.is(err?.message, expected);
});

test('@slangroom/core error in the then phase', async (t) => {
const plugin = new Plugin();
plugin.new('do some action', (_) => _.fail(new Error('failed')));

const slang = new Slangroom(plugin);
const fn = slang.execute(`Rule unknown ignore
Given nothing
Then print data
Then I do some action`)

const expected = `${lineNoColor('2 | ')} Then print data
${lineNoColor('3 | ')}${sentenceHighlight(` ${textHighlight('Then I do some action')}`)}
${errorColor('^^^^^^^^^^^^^^^^^^^^^')}
${errorColorDef}
Error: failed
`

const err = await t.throwsAsync(fn);
t.is(err?.message, expected);
});
87 changes: 85 additions & 2 deletions pkg/core/test/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

import test from 'ava';
import { DuplicatePluginError, Plugin, isSane, PluginContextTest, type PluginExecutor } from '@slangroom/core';
import {
DuplicatePluginError,
Plugin,
isSane,
PluginContextTest,
PluginContextImpl,
type PluginExecutor
} from '@slangroom/core';

const insanes = [
'',
Expand Down Expand Up @@ -118,6 +125,19 @@ test('Plugin.new() duplicates detected', (t) => {
);
});

test('Plugin.new() duplicated params detected', (t) => {
t.throws(
() => {
const p = new Plugin();
p.new(['foo', 'foo'], 'a', (ctx) => ctx.pass(null));
},
{
instanceOf: Error,
message: `params must not have duplicate values: foo`,
},
);
});

test('Plugin.new() clauses work', (t) => {
const f: PluginExecutor = (ctx) => ctx.pass(null);
{
Expand Down Expand Up @@ -157,7 +177,7 @@ test('Plugin.new() clauses work', (t) => {
}
});

test('PluginContext', async (t) => {
test('PluginContextTest', async (t) => {
const p = new Plugin();
// ctxs
const emptyCtx = new PluginContextTest([], {});
Expand Down Expand Up @@ -193,3 +213,66 @@ test('PluginContext', async (t) => {
const err = await t.throwsAsync(async() => await fetch(emptyCtx));
t.is(err.message, 'the parameter isn\'t provided: obj');
});

test('PluginContextImpl', async (t) => {
const p = new Plugin();
const emptyCtx = new PluginContextImpl({
key: {
phrase: 'abc'
},
params: new Map([]),
});
const openCtx = new PluginContextImpl({
key: {
openconnect: 'open',
phrase: 'abc'
},
params: new Map([]),
open: ['some_path'],
});
const connectCtx = new PluginContextImpl({
key: {
openconnect: 'connect',
phrase: 'abc'
},
params: new Map([]),
connect: ['some_path'],
});
const objCtx = new PluginContextImpl({
key: {
params: ['obj'],
phrase: 'abc'
},
params: new Map([['obj', 'obj']]),
})

// pass and fail
const pass = p.new('test pass', (ctx) => ctx.pass('done'));
const fail = p.new('test fail', (ctx) => ctx.fail(new Error('failed')));
t.deepEqual(await pass(emptyCtx), { ok: true, value: 'done' });
t.deepEqual(await fail(emptyCtx), { ok: false, error: new Error('failed') });
// connect
const getConnect = p.new('connect', 'test get connect', (ctx) => ctx.pass(ctx.getConnect()));
const fetchConnect = p.new('connect', 'test fetch connect', (ctx) => ctx.pass(ctx.fetchConnect()));
t.deepEqual(await getConnect(connectCtx), { ok: true, value: ['some_path']});
t.deepEqual(await fetchConnect(connectCtx), { ok: true, value: ['some_path']});
t.deepEqual(await getConnect(emptyCtx), { ok: true, value: []});
const fetchConnectErr = await t.throwsAsync(async() => await fetchConnect(emptyCtx));
t.is(fetchConnectErr.message, 'a connect is required');
// open
const getOpen = p.new('open', 'test get open', (ctx) => ctx.pass(ctx.getOpen()));
const fetchOpen = p.new('open', 'test fetch open', (ctx) => ctx.pass(ctx.fetchOpen()));
t.deepEqual(await getOpen(openCtx), { ok: true, value: ['some_path']});
t.deepEqual(await fetchOpen(openCtx), { ok: true, value: ['some_path']});
t.deepEqual(await getOpen(emptyCtx), { ok: true, value: []});
const fetchOpenErr = await t.throwsAsync(async() => await fetchOpen(emptyCtx));
t.is(fetchOpenErr.message, 'a open is required');
// get and fetch
const get = p.new(['obj'],'test get', (ctx) => ctx.pass(ctx.get('obj') || null));
const fetch = p.new(['obj'], 'test fetch', (ctx) => ctx.pass(ctx.fetch('obj') || null));
t.deepEqual(await get(objCtx), { ok: true, value: 'obj' });
t.deepEqual(await fetch(objCtx), { ok: true, value: 'obj' });
t.deepEqual(await get(emptyCtx), { ok: true, value: null });
const err = await t.throwsAsync(async() => await fetch(emptyCtx));
t.is(err.message, 'the parameter isn\'t provided: obj');
});
67 changes: 67 additions & 0 deletions pkg/core/test/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,54 @@ test('visitor works', (t) => {
test('visitor throws error correctly', (t) => {
(
[
[
{ data: {}, keys: {} },
{
givenThen: 'given',
errors: [
{
message: new Error('at 1\n missing one of: Given I, Then I'),
lineNo: 1
}
],
matches: [],
},
'cst must not have any general errors'
],
[
{ data: {}, keys: {} },
{
givenThen: 'given',
errors: [],
matches: [],
},
'cst must have only one match'
],
[
{ data: {}, keys: {} },
{
givenThen: 'given',
errors: [],
matches: [
{
key: {
phrase: 'a b c'
},
bindings: new Map(),
err: [
{
message: new Error('at 1:19.22\n save may be and'),
lineNo: 1,
start: 19,
end: 22
}
],
lineNo: 1
}
]
},
'cst\'s match must not have any errors'
],
[
{ data: {}, keys: {} },
{
Expand Down Expand Up @@ -215,6 +263,25 @@ test('visitor throws error correctly', (t) => {
},
'the array referenced by myFile must solely composed of strings'
],
[
{ data: {}, keys: {} },
{
givenThen: 'given',
errors: [],
matches: [
{
key: {
params: ['object'],
phrase: 'a b c'
},
bindings: new Map([['object', 'myObj']]),
err: [],
lineNo: 1
},
],
},
'Can\'t find myObj in DATA or KEYS'
],
] as [ZenParams, Cst, string][]
).forEach(([params, cst, errMessage]) => {
const err = t.throws(() => {
Expand Down
Loading