Files
jasmine/src/core/TreeProcessor.js
2022-08-22 17:04:44 -07:00

265 lines
7.0 KiB
JavaScript

getJasmineRequireObj().TreeProcessor = function() {
function TreeProcessor(attrs) {
const tree = attrs.tree;
const runnableIds = attrs.runnableIds;
const queueRunnerFactory = attrs.queueRunnerFactory;
const nodeStart = attrs.nodeStart || function() {};
const nodeComplete = attrs.nodeComplete || function() {};
const failSpecWithNoExpectations = !!attrs.failSpecWithNoExpectations;
const orderChildren =
attrs.orderChildren ||
function(node) {
return node.children;
};
const excludeNode =
attrs.excludeNode ||
function(node) {
return false;
};
let stats = { valid: true };
let processed = false;
const defaultMin = Infinity;
const defaultMax = 1 - Infinity;
this.processTree = function() {
processNode(tree, true);
processed = true;
return stats;
};
this.execute = async function() {
if (!processed) {
this.processTree();
}
if (!stats.valid) {
throw 'invalid order';
}
const childFns = wrapChildren(tree, 0);
await new Promise(function(resolve) {
queueRunnerFactory({
queueableFns: childFns,
userContext: tree.sharedUserContext(),
onException: function() {
tree.handleException.apply(tree, arguments);
},
onComplete: resolve,
onMultipleDone: tree.onMultipleDone
? tree.onMultipleDone.bind(tree)
: null
});
});
};
function runnableIndex(id) {
for (let i = 0; i < runnableIds.length; i++) {
if (runnableIds[i] === id) {
return i;
}
}
}
function processNode(node, parentExcluded) {
const executableIndex = runnableIndex(node.id);
if (executableIndex !== undefined) {
parentExcluded = false;
}
if (!node.children) {
const excluded = parentExcluded || excludeNode(node);
stats[node.id] = {
excluded: excluded,
willExecute: !excluded && !node.markedPending,
segments: [
{
index: 0,
owner: node,
nodes: [node],
min: startingMin(executableIndex),
max: startingMax(executableIndex)
}
]
};
} else {
let hasExecutableChild = false;
const orderedChildren = orderChildren(node);
for (let i = 0; i < orderedChildren.length; i++) {
const child = orderedChildren[i];
processNode(child, parentExcluded);
if (!stats.valid) {
return;
}
const childStats = stats[child.id];
hasExecutableChild = hasExecutableChild || childStats.willExecute;
}
stats[node.id] = {
excluded: parentExcluded,
willExecute: hasExecutableChild
};
segmentChildren(node, orderedChildren, stats[node.id], executableIndex);
if (!node.canBeReentered() && stats[node.id].segments.length > 1) {
stats = { valid: false };
}
}
}
function startingMin(executableIndex) {
return executableIndex === undefined ? defaultMin : executableIndex;
}
function startingMax(executableIndex) {
return executableIndex === undefined ? defaultMax : executableIndex;
}
function segmentChildren(
node,
orderedChildren,
nodeStats,
executableIndex
) {
let currentSegment = {
index: 0,
owner: node,
nodes: [],
min: startingMin(executableIndex),
max: startingMax(executableIndex)
},
result = [currentSegment],
lastMax = defaultMax,
orderedChildSegments = orderChildSegments(orderedChildren);
function isSegmentBoundary(minIndex) {
return (
lastMax !== defaultMax &&
minIndex !== defaultMin &&
lastMax < minIndex - 1
);
}
for (let i = 0; i < orderedChildSegments.length; i++) {
const childSegment = orderedChildSegments[i],
maxIndex = childSegment.max,
minIndex = childSegment.min;
if (isSegmentBoundary(minIndex)) {
currentSegment = {
index: result.length,
owner: node,
nodes: [],
min: defaultMin,
max: defaultMax
};
result.push(currentSegment);
}
currentSegment.nodes.push(childSegment);
currentSegment.min = Math.min(currentSegment.min, minIndex);
currentSegment.max = Math.max(currentSegment.max, maxIndex);
lastMax = maxIndex;
}
nodeStats.segments = result;
}
function orderChildSegments(children) {
const specifiedOrder = [],
unspecifiedOrder = [];
for (let i = 0; i < children.length; i++) {
const child = children[i],
segments = stats[child.id].segments;
for (let j = 0; j < segments.length; j++) {
const seg = segments[j];
if (seg.min === defaultMin) {
unspecifiedOrder.push(seg);
} else {
specifiedOrder.push(seg);
}
}
}
specifiedOrder.sort(function(a, b) {
return a.min - b.min;
});
return specifiedOrder.concat(unspecifiedOrder);
}
function executeNode(node, segmentNumber) {
if (node.children) {
return {
fn: function(done) {
const onStart = {
fn: function(next) {
nodeStart(node, next);
}
};
queueRunnerFactory({
onComplete: function() {
const args = Array.prototype.slice.call(arguments, [0]);
node.cleanupBeforeAfter();
nodeComplete(node, node.getResult(), function() {
done.apply(undefined, args);
});
},
queueableFns: [onStart].concat(wrapChildren(node, segmentNumber)),
userContext: node.sharedUserContext(),
onException: function() {
node.handleException.apply(node, arguments);
},
onMultipleDone: node.onMultipleDone
? node.onMultipleDone.bind(node)
: null
});
}
};
} else {
return {
fn: function(done) {
node.execute(
queueRunnerFactory,
done,
stats[node.id].excluded,
failSpecWithNoExpectations
);
}
};
}
}
function wrapChildren(node, segmentNumber) {
const result = [],
segmentChildren = stats[node.id].segments[segmentNumber].nodes;
for (let i = 0; i < segmentChildren.length; i++) {
result.push(
executeNode(segmentChildren[i].owner, segmentChildren[i].index)
);
}
if (!stats[node.id].willExecute) {
return result;
}
return node.beforeAllFns.concat(result).concat(node.afterAllFns);
}
}
return TreeProcessor;
};