@contract-case/case-plugin-base
Version:
Plugin framework for writing plugins for the ContractCase test framework
170 lines • 6.07 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.addLocation = exports.applyNodeToContext = exports.constructMatchContext = exports.constructDataContext = exports.foldIntoContext = void 0;
const case_plugin_dsl_types_1 = require("@contract-case/case-plugin-dsl-types");
const types_1 = require("../matchers/types");
/**
* `_case:currentRun:context:*` is not clobberable by child matchers
* (with the exception of _case:currentRun:context:logLevel).
*
* `_case:context:*` is clobberable by child matchers. This means if you set
* any `_case:context:*` properties on a matcher, they will override the current
* context before the matcher is applied.
*/
const DEFAULT_CONTEXT = {
'_case:currentRun:context:parentVersions': [],
'_case:currentRun:context:connectorClient': 'No Connector Client Supplied',
'_case:currentRun:context:location': [],
'_case:currentRun:context:contractMode': 'write',
'_case:currentRun:context:logLevel': 'warn',
'_case:currentRun:context:autoVersionFrom': 'TAG',
'_case:currentRun:context:printResults': true,
'_case:context:matchBy': 'exact',
'_case:context:serialisableTo': 'json',
};
const contextProperties = (caseNode) => Object.entries(caseNode)
.filter(([k]) => k.startsWith('_case:context') ||
k === '_case:currentRun:context:logLevel')
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {});
const updateFunctions = (context) => {
const logger = context.makeLogger(context);
const nextContext = { ...context, logger };
return {
...nextContext,
...nextContext.makeLookup(nextContext),
};
};
/**
* Folds this case matcher into the context. This is called by the core
* before invoking the matcher's executor.
*
* This means that if you set any `_case:context:*` properties on a matcher,
* they will override the current context before the matcher is applied.
*
* For example, if you have a context object with `_case:context:matchBy: 'exact'`
* and you set `_case:context:matchBy: 'type'` on a matcher, the matcher will receive
* context for matching with `_case:context:matchBy: 'type'` instead of `'exact'`.
*
* @internal
*
* @param caseNode - any descriptor
* @param context - the current context
* @returns a context object containing the descriptor combined with the context
*/
const foldIntoContext = (caseNode, context) => ({
...context,
...contextProperties(caseNode),
});
exports.foldIntoContext = foldIntoContext;
let exampleId = 0;
const combineWithRoot = (caseNodeOrData, context, runConfig) => {
const newContext = {
...((0, types_1.isCaseNode)(caseNodeOrData) || (0, case_plugin_dsl_types_1.isCaseMock)(caseNodeOrData)
? (0, exports.foldIntoContext)(caseNodeOrData, context)
: context),
...runConfig,
'_case:currentRun:context:location': [
...context['_case:currentRun:context:location'],
`Interaction[${exampleId}]`,
],
};
exampleId += 1;
return updateFunctions(newContext);
};
/**
* Constructs a data context object
*
* @internal
*
*/
const constructDataContext = (makeLogger, resultPrinter, runConfig, defaults, parentVersions) => {
const context = {
makeLogger,
...DEFAULT_CONTEXT,
'_case:currentRun:context:defaultConfig': defaults,
'_case:currentRun:context:testName': 'OUTSIDE_TESTS',
'_case:currentRun:context:variables': {},
'_case:currentRun:context:parentVersions': parentVersions,
...runConfig,
};
const logContext = {
...context,
logger: makeLogger(context),
resultPrinter,
};
return {
...logContext,
};
};
exports.constructDataContext = constructDataContext;
/**
* TODO: Move this out of the plugin lib
*
* @internal
*/
const constructMatchContext = (traversals, makeLogger, makeLookup, resultPrinter, runConfig, defaults, parentVersions) => {
const context = {
...traversals,
...(0, exports.constructDataContext)(makeLogger, resultPrinter, runConfig, defaults, parentVersions),
makeLookup,
};
return {
...context,
...makeLookup(context),
};
};
exports.constructMatchContext = constructMatchContext;
/**
* TODO: Move this out of the plugin lib
*
* @internal
*/
const applyNodeToContext = (caseNodeOrData, context, runConfig = {}) => combineWithRoot(caseNodeOrData, context, runConfig);
exports.applyNodeToContext = applyNodeToContext;
/**
* Adds the current location to the context. Used to determine where we are when
* printing logs and errors.
*
* There are some semantics here:
*
* Locations are joined together with `.`. For example, calling:
*
* ```
* addLocation('b', addLocation('a', context))
* ```
*
* will result in `"a.b"` being the location at print time.
*
* If the location is part of an iteration, put it in square brackets, for
* example `[2]` or `[keyName]`. This will prevent the location logger from
* adding `.` between consecutive locations. For example:
*
* ```
* addLocation('[1]', addLocation('a', context))
* ```
*
* will result in `"a[1]"` being the location at print time.
*
* If the location starts with `:`, it's only logged during maintainer logs. For
* example:
*
* ```
* addLocation(':internalDetail', addLocation('a', context))
* ```
*
* will result in `"a"` during normal and debug logging, but `a:internalDetail`
* when maintainer logging (or deeper) is enabled
*
* @public
*
* @param location - a string representing the current location. Prefix with `:`
* if this location should only be printed during maintainer debugging
* @param context - the current {@link MatchContext}
* @returns a new {@link MatchContext} with updated location.
*/
const addLocation = (location, context) => updateFunctions({
...context,
'_case:currentRun:context:location': context['_case:currentRun:context:location'].concat([location]),
});
exports.addLocation = addLocation;
//# sourceMappingURL=context.js.map