UNPKG

siesta-lite

Version:

Stress-free JavaScript unit testing and functional testing tool, works in NodeJS and browsers

362 lines (242 loc) 11.4 kB
/* Siesta 5.6.1 Copyright(c) 2009-2022 Bryntum AB https://bryntum.com/contact https://bryntum.com/products/siesta/license */ Class('Ariadne.QueryFinder.Maze', { does : [ Siesta.Util.Role.CanGetType ], has : { finder : { required : true }, nodes : Joose.I.Array, segmentsIdGen : 1, encounteredMandatoryId : false }, methods : { getRoot : function () { return this.nodes[ this.nodes.length - 1 ] }, doQuery : function (query, rootNode) { var queryCache = rootNode.queryCache // TUNE // if (queryCache[ query ]) console.log("CACHE HIT!") if (queryCache[ query ]) return queryCache[ query ] // // TUNE // QUERYCOUNTER = window.QUERYCOUNTER || 0 // QUERYCOUNTER++ // console.log("QUERY from:", rootNode.index, " query: ", query) return queryCache[ query ] = this.finder.doQuery(query, rootNode.el) }, buildVertices : function (target, root) { // console.time('buildVertices') var nodes = this.nodes = [] var currentEl = target var prevNode var finder = this.finder var identifiers = finder.identifiers var directChildIdentifier = finder.directChildIdentifier while (currentEl) { var node = new Ariadne.QueryFinder.Node({ finder : finder, maze : this, el : currentEl, index : nodes.length }) if (prevNode) { prevNode.upNode = node node.downNode = prevNode if (directChildIdentifier) { var segment = directChildIdentifier.identify(prevNode.el, root, this) if (segment) { segment = this.normalizeIdentifierResult(segment)[ 0 ] var directChildUpEdge = new Ariadne.QueryFinder.Edge({ finder : finder, maze : this, isDirectChild : true, fromNode : prevNode, toNode : node }) prevNode.directChildUpEdge = directChildUpEdge prevNode.adoptSegment(segment) directChildUpEdge.consumeVariant([ segment ], finder.combineSegments([ segment ], true)) } } } prevNode = node nodes.push(node) // exit a bit earlier for root node - segments won't be set for it if (currentEl == root) break var segments = [] for (var j = 0; j < identifiers.length; j++) { var compSegments = identifiers[ j ].identify(currentEl, root, this) if (compSegments) { segments.push.apply(segments, this.normalizeIdentifierResult(compSegments)) } } node.setSegments(segments) currentEl = finder.getParent(currentEl) } nodes[ 0 ].weightOptions = [ { weight : 0, minFromEdgesById : {} } ] nodes[ 0 ].weightOptionsByWeight[ 0 ] = nodes[ 0 ].weightOptions[ 0 ] // console.timeEnd('buildVertices') }, normalizeIdentifierResult : function (segments) { var me = this if (this.typeOf(segments) != 'Array') segments = [ segments ] return segments.map(function (segment) { if (me.typeOf(segment) == 'String') return { query : segment, weight : 1000 } else { if (!segment.hasOwnProperty('weight')) segment.weight = 1000 return segment } }) }, computePath : function (prevUpdatedNodes) { var nodes = this.nodes var root = this.getRoot() var iteration = 0 if (!prevUpdatedNodes) { prevUpdatedNodes = {} prevUpdatedNodes[ nodes[ 0 ].id ] = nodes[ 0 ] } do { iteration++ var updatedNodes = {} var somethingUpdated = false for (var id in prevUpdatedNodes) { var fromNode = prevUpdatedNodes[ id ] if (fromNode.weightOptions.length === 0) continue for (var toIndex in fromNode.edgesToByIndex) { var edge = fromNode.edgesToByIndex[ toIndex ] var toNode = edge.toNode if (edge.relax()) { updatedNodes[ toNode.id ] = toNode somethingUpdated = true } } var directChildUpEdge = fromNode.directChildUpEdge if (directChildUpEdge && directChildUpEdge.relax()) { var toNode = directChildUpEdge.toNode updatedNodes[ toNode.id ] = toNode somethingUpdated = true } } prevUpdatedNodes = updatedNodes } while (somethingUpdated && iteration < nodes.length) }, buildEdges : function () { // console.time('buildEdges') var recognizers = this.finder.recognizers for (var j = 0; j < recognizers.length; j++) { recognizers[ j ].recognize(this) } var nodes = this.nodes for (var i = 0; i < nodes.length - 1; i++) { // early exit, as soon as we found identifying edge // by definition, we aren't interested in edges above it if (nodes[ i ].buildEdges3(1) === false) break nodes[ i ].buildEdges3(2) nodes[ i ].buildEdges3(3) } // console.timeEnd('buildEdges') }, expandSimplePaths : function (paths) { var nodes = this.nodes var pathsHashes = {} var pathVariants = [] for (var i = 0; i < paths.length; i++) { var path = paths[ i ].split('_') var variants = [] var prevIndex for (var j = path.length - 1; j > 1; j--) { var isDirectChildEdge = path[ j ][ 0 ] == '+' var onlyWithOwnIndex = j < path.length - 1 && path[ j + 1 ][ 0 ] == '+' var fromNode = nodes[ Number(path[ j ]) ] var edge = isDirectChildEdge ? fromNode.directChildUpEdge : fromNode.edgesToByIndex[ Number(path[ j - 1 ]) ] variants = fromNode.extendPathVariants(variants, edge.segmentVariants, onlyWithOwnIndex ? fromNode.index : null) } for (var k = 0; k < variants.length; k++) { var pathVariant = variants[ k ] if (!pathsHashes[ pathVariant.hash ]) { pathVariants.push(pathVariant) pathsHashes[ pathVariant.hash ] = true } } } pathVariants.sort(function (a, b) { return a.weight - b.weight }) return pathVariants }, findAllPaths : function (options) { // console.time('computePath') this.computePath() // console.timeEnd('computePath') var me = this var root = this.getRoot() // console.time('collectAllPaths') var simplePathsCollection = root.collectFullSimplePathCollection() // console.timeEnd('collectAllPaths') // console.log("Simple paths: ", simplePathsCollection) for (var i = 0; i < simplePathsCollection.length; i++) { var simplePaths = simplePathsCollection[ i ].simplePaths var queryInfos = this.verifySimplePathGroup(simplePaths, options) if (queryInfos.length) { if (options && options.detailed) { return queryInfos } else return queryInfos.map(function (queryInfo) { return queryInfo.query }) } } return [] }, verifySimplePathGroup : function (simplePaths, options) { var root = this.getRoot() var finder = this.finder // console.time('expandAllPaths') var pathVariants = this.expandSimplePaths(simplePaths) // console.timeEnd('expandAllPaths') // console.time('verifyAllPaths') var queryInfos = [] var targetEl = this.nodes[ 0 ].el var rootEl = root.el for (var i = 0; i < pathVariants.length; i++) { var variant = pathVariants[ i ] var segments = [] for (var id in variant.segmentsById) segments.push(variant.segmentsById[ id ]) // query can potentially not be constructed because of clash between leading segments var queryInfo = finder.combineSegments(segments) if (queryInfo) { if (finder.verifyQuery(queryInfo.query, targetEl, rootEl)) { queryInfos.push(queryInfo) if (options && options.singleOnly) return queryInfos } else { // console.log("WRONG QUERY: " + queryInfo.query) } } } // console.timeEnd('verifyAllPaths') return queryInfos }, // DEBUG METHOD countEdges : function (log) { var counter = 0 this.nodes.forEach(function (node) { for (var index in node.edgesToByIndex) { var edge = node.edgesToByIndex[ index ] counter += edge.segmentVariants.length if (log) { edge.segmentVariants.forEach(function (variant) { console.log("Edge from: " + edge.fromNode.index + "[" + edge.fromNode.el.tagName + "] to " + edge.toNode.index + "[" + edge.toNode.el.tagName + "] query: " + edge.finder.combineSegments(variant.variant).query) }) } } }) return counter } } });