flecks/packages/dox/test/server/visitors.js

297 lines
6.4 KiB
JavaScript
Raw Normal View History

2024-01-23 09:06:00 -06:00
import traverse from '@babel/traverse';
import {expect} from 'chai';
import {parseCode} from '@flecks/dox/build/parser';
import {
buildFileVisitor,
configVisitor,
hookImplementationVisitor,
hookInvocationVisitor,
hookSpecificationVisitor,
hookVisitor,
todoVisitor,
} from '@flecks/dox/build/visitors';
async function confirmVisited(code, visitor, test) {
const visited = [];
traverse(
await parseCode(code),
visitor((result) => {
visited.push(result);
}),
);
test(visited);
}
function constHooksWrapper(code) {
return `
export const hooks = {
${code}
};
`;
}
function exportsHooksWrapper(code) {
return `
exports.hooks = {
${code}
};
`;
}
async function confirmVisitedWrapped(code, visitor, test) {
await Promise.all(
[constHooksWrapper, exportsHooksWrapper]
.map(async (wrapper) => {
const source = wrapper(code);
await confirmVisited(source, visitor, test(source));
}),
);
}
it('parses hooks', async () => {
await confirmVisitedWrapped(
`
'hello': () => {},
`,
hookImplementationVisitor,
() => (visited) => {
expect(visited.length)
.to.equal(1);
expect(visited[0].key.value)
.to.equal('hello');
},
);
});
it('visits hooks', async () => {
await confirmVisitedWrapped(
`
'hello': () => {},
'goodbye': () => {},
`,
hookVisitor('hello'),
() => (visited) => {
expect(visited.length)
.to.equal(1);
expect(visited[0].key.value)
.to.equal('hello');
},
);
});
function buildFileWrappers(code) {
return [
`
'@flecks/build.files': () => ([
${code}
]),
`,
`
'@flecks/build.files': () => {
return [
${code}
];
},
`,
`
'@flecks/build.files': function f() {
return [
${code}
];
},
`,
];
}
it('visits build files', async () => {
await Promise.all(
buildFileWrappers(
`
/**
* This is a description.
*
* It is on multiple lines.
*/
'first.config.js',
/**
* This is another description.
*/
'second.config.js',
'third.config.js',
`,
)
.map(async (wrapped) => {
await confirmVisitedWrapped(
wrapped,
buildFileVisitor,
() => (visited) => {
expect(visited)
.to.deep.equal([
{
filename: 'first.config.js',
description: 'This is a description.\n\nIt is on multiple lines.',
},
{
filename: 'second.config.js',
description: 'This is another description.',
},
{
filename: 'third.config.js',
description: undefined,
},
]);
},
);
}),
);
});
function configWrappers(code) {
return [
`
'@flecks/core.config': () => ({
${code}
}),
`,
`
'@flecks/core.config': () => {
return {
${code}
};
},
`,
`
'@flecks/core.config': function f() {
return {
${code}
};
},
`,
];
}
it('visits config', async () => {
await Promise.all(
configWrappers(
`
/**
* Description.
*/
first: 'default',
second: 1111,
`,
)
.map(async (wrapped) => {
await confirmVisitedWrapped(
wrapped,
configVisitor,
(source) => (visited) => {
const expected = [
{
key: 'first',
description: 'Description.',
},
{
key: 'second',
description: undefined,
},
];
visited.forEach((config, i) => {
expect(config.key)
.to.equal(expected[i].key);
expect(config.description)
.to.equal(expected[i].description);
});
expect(source.slice(visited[0].location.start.index, visited[0].location.end.index))
.to.equal("'default'");
expect(source.slice(visited[1].location.start.index, visited[1].location.end.index))
.to.equal('1111');
},
);
}),
);
});
it('visits hook invocations', async () => {
await confirmVisited(
`
flecks.invoke('sup');
flecks.invokeAsync('yep');
flecks.gather('stuff');
flecks.nope();
this.invoke('sup');
this.invokeAsync('yep');
this.gather('stuff');
this.nope();
`,
hookInvocationVisitor,
(visited) => {
expect(visited.length)
.to.equal(8);
},
);
});
it('visits hook specifications', async () => {
const f = '(one, two) => { return one + parseInt(two, 10); }';
await confirmVisitedWrapped(
`
/**
* This is a hook.
* @param {number} one First.
* @param {string} two Second.
* @returns {number} Result.
*/
'hook': ${f},
`,
hookSpecificationVisitor,
(source) => (visited) => {
expect(visited.length)
.to.equal(1);
expect(visited[0].description)
.to.equal('This is a hook.');
expect(visited[0].hook)
.to.equal('hook');
expect(visited[0].returns)
.to.deep.equal({description: 'Result.', type: 'number'});
expect(visited[0].params)
.to.deep.equal([
{description: 'First.', name: 'one', type: 'number'},
{description: 'Second.', name: 'two', type: 'string'},
]);
expect(source.slice(visited[0].location.start.index, visited[0].location.end.index))
.to.equal(f);
},
);
});
it('visits todos', async () => {
const source = `
someStuff();
// @todo This should be a function.
new OhYeah();
// @todo This is a really
// long todo item.
something();
`;
await confirmVisited(
source,
todoVisitor,
(visited) => {
expect(visited.map(({description}) => description))
.to.deep.equal([
'This should be a function.',
'This is a really long todo item.',
]);
expect(visited.map(({location: {start, end}}) => (source.slice(start.index, end.index))))
.to.deep.equal([
'new OhYeah();',
'something();',
]);
},
);
});