From 0b73d44e47bb682e35b1bcab7a1d9a22120e69fc Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Sat, 28 Jan 2023 15:02:30 -0500 Subject: [PATCH 01/23] Drop graph utils (unused, would suggest an external impl if needed) --- client/src/utils/graph.js | 623 ------------------------ client/tests/qunit/testBundle.js | 1 - client/tests/qunit/tests/graph_tests.js | 563 --------------------- 3 files changed, 1187 deletions(-) delete mode 100644 client/src/utils/graph.js delete mode 100644 client/tests/qunit/tests/graph_tests.js diff --git a/client/src/utils/graph.js b/client/src/utils/graph.js deleted file mode 100644 index 0f5a5675268..00000000000 --- a/client/src/utils/graph.js +++ /dev/null @@ -1,623 +0,0 @@ -/** deep equal of two dictionaries */ -function matches(d, d2) { - for (var k in d2) { - if (Object.prototype.hasOwnProperty.call(d2, k)) { - if (!Object.prototype.hasOwnProperty.call(d, k) || d[k] !== d2[k]) { - return false; - } - } - } - return true; -} - -/** map key/values in obj - * if propsOrFn is an object, return only those k/v that match the object - * if propsOrFn is function, call the fn and returned the mapped values from it - */ -function iterate(obj, propsOrFn) { - var fn = typeof propsOrFn === "function" ? propsOrFn : undefined; - var props = typeof propsOrFn === "object" ? propsOrFn : undefined; - var returned = []; - var index = 0; - for (var key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - var value = obj[key]; - if (fn) { - returned.push(fn.call(value, value, key, index)); - } else if (props) { - //TODO: break out to sep? - if (typeof value === "object" && matches(value, props)) { - returned.push(value); - } - } else { - returned.push(value); - } - index += 1; - } - } - return returned; -} - -// ============================================================================ -/** A graph edge containing the name/id of both source and target and optional data - */ -function Edge(source, target, data) { - var self = this; - self.source = source !== undefined ? source : null; - self.target = target !== undefined ? target : null; - self.data = data || null; - //if( typeof data === 'object' ){ - // extend( self, data ); - //} - return self; -} -/** String representation */ -Edge.prototype.toString = function () { - return `${this.source}->${this.target}`; -}; - -/** Return a plain object representing this edge */ -Edge.prototype.toJSON = function () { - //TODO: this is safe in most browsers (fns will be stripped) - alter tests to incorporate this in order to pass data - //return this; - var json = { - source: this.source, - target: this.target, - }; - if (this.data) { - json.data = this.data; - } - return json; -}; - -// ============================================================================ -/** A graph vertex with a (unique) name/id and optional data. - * A vertex contains a list of Edges (whose sources are this vertex) and maintains the degree. - */ -function Vertex(name, data) { - var self = this; - self.name = name !== undefined ? name : "(unnamed)"; - self.data = data || null; - self.edges = {}; - self.degree = 0; - return self; -} - -/** String representation */ -Vertex.prototype.toString = function () { - return `Vertex(${this.name})`; -}; - -//TODO: better name w no collision for either this.eachEdge or this.edges -/** Iterate over each edge from this vertex */ -Vertex.prototype.eachEdge = function (propsOrFn) { - return iterate(this.edges, propsOrFn); -}; - -/** Return a plain object representing this vertex */ -Vertex.prototype.toJSON = function () { - //return this; - return { - name: this.name, - data: this.data, - }; -}; - -// ============================================================================ -/** Base (abstract) class for Graph search algorithms. - * Pass in the graph to search - * and an optional dictionary containing the 3 vertex/edge processing fns listed below. - */ -var GraphSearch = function (graph, processFns) { - var self = this; - self.graph = graph; - - self.processFns = processFns || { - vertexEarly: function (vertex, search) { - //console.debug( 'processing vertex:', vertex.name, vertex ); - }, - edge: function (from, edge, search) { - //console.debug( this, 'edge:', from, edge, search ); - }, - vertexLate: function (vertex, search) { - //console.debug( this, 'vertexLate:', vertex, search ); - }, - }; - - self._cache = {}; - return self; -}; - -/** Search interface where start is the vertex (or the name/id of the vertex) to begin the search at - * This public interface caches searches and returns the cached version if it's already been done. - */ -GraphSearch.prototype.search = function _search(start) { - var self = this; - if (start in self._cache) { - return self._cache[start]; - } - if (!(start instanceof Vertex)) { - start = self.graph.vertices[start]; - } - return (self._cache[start.name] = self._search(start)); -}; - -/** Actual search (private) function (abstract here) */ -GraphSearch.prototype._search = function __search(start, search) { - search = search || { - discovered: {}, - //parents : {}, - edges: [], - }; - return search; -}; - -/** Searches graph from start and returns a search tree of the results */ -GraphSearch.prototype.searchTree = function _searchTree(start) { - return this._searchTree(this.search(start)); -}; - -/** Helper fn that returns a graph (a search tree) based on the search object passed in (does not actually search) */ -GraphSearch.prototype._searchTree = function __searchTree(search) { - var self = this; - return new Graph(true, { - edges: search.edges, - vertices: Object.keys(search.discovered).map((key) => self.graph.vertices[key].toJSON()), - }); -}; - -// ============================================================================ -/** Breadth first search algo. - */ -var BreadthFirstSearch = function (graph, processFns) { - var self = this; - GraphSearch.call(this, graph, processFns); - return self; -}; -BreadthFirstSearch.prototype = new GraphSearch(); -BreadthFirstSearch.prototype.constructor = BreadthFirstSearch; - -/** (Private) implementation of BFS */ -BreadthFirstSearch.prototype._search = function __search(start, search) { - search = search || { - discovered: {}, - //parents : {}, - edges: [], - }; - - var self = this; - var queue = []; - - function discoverAdjacent(adj, edge) { - var source = this; - if (self.processFns.edge) { - self.processFns.edge.call(self, source, edge, search); - } - if (!search.discovered[adj.name]) { - //console.debug( '\t\t\t', adj.name, 'is undiscovered:', search.discovered[ adj.name ] ); - search.discovered[adj.name] = true; - //search.parents[ adj.name ] = source; - search.edges.push({ source: source.name, target: adj.name }); - //console.debug( '\t\t\t queuing undiscovered: ', adj ); - queue.push(adj); - } - } - - //console.debug( 'BFS starting. start:', start ); - search.discovered[start.name] = true; - queue.push(start); - while (queue.length) { - var vertex = queue.shift(); - //console.debug( '\t Queue is shifting. Current:', vertex, 'queue:', queue ); - if (self.processFns.vertexEarly) { - self.processFns.vertexEarly.call(self, vertex, search); - } - self.graph.eachAdjacent(vertex, discoverAdjacent); - if (self.processFns.vertexLate) { - self.processFns.vertexLate.call(self, vertex, search); - } - } - //console.debug( 'search.edges:', JSON.stringify( search.edges ) ); - return search; -}; - -// ============================================================================ -/** Depth first search algorithm. - */ -var DepthFirstSearch = function (graph, processFns) { - var self = this; - GraphSearch.call(this, graph, processFns); - return self; -}; -DepthFirstSearch.prototype = new GraphSearch(); -DepthFirstSearch.prototype.constructor = DepthFirstSearch; - -/** (Private) implementation of DFS */ -DepthFirstSearch.prototype._search = function (start, search) { - //console.debug( 'depthFirstSearch:', start ); - search = search || { - discovered: {}, - //parents : {}, - edges: [], - entryTimes: {}, - exitTimes: {}, - }; - var self = this; - var time = 0; - - // discover verts adjacent to the source (this): - // processing each edge, saving the edge to the tree, and caching the reverse path with parents - function discoverAdjacentVertices(adjacent, edge) { - //console.debug( '\t\t adjacent:', adjacent, 'edge:', edge ); - var sourceVertex = this; - if (self.processFns.edge) { - self.processFns.edge.call(self, sourceVertex, edge, search); - } - if (!search.discovered[adjacent.name]) { - //search.parents[ adjacent.name ] = sourceVertex; - search.edges.push({ - source: sourceVertex.name, - target: adjacent.name, - }); - recurse(adjacent); - } - } - - // use function stack for DFS stack process verts, times, and discover adjacent verts (recursing into them) - function recurse(vertex) { - //console.debug( '\t recursing into: ', vertex ); - search.discovered[vertex.name] = true; - if (self.processFns.vertexEarly) { - self.processFns.vertexEarly.call(self, vertex, search); - } - search.entryTimes[vertex.name] = time++; - - self.graph.eachAdjacent(vertex, discoverAdjacentVertices); - - if (self.processFns.vertexLate) { - self.processFns.vertexLate.call(self, vertex, search); - } - search.exitTimes[vertex.name] = time++; - } - // begin recursion with the desired start - recurse(start); - - return search; -}; - -// ============================================================================ -/** A directed/non-directed graph object. - */ -function Graph(directed, data, options) { - //TODO: move directed to options - this.directed = directed || false; - return this.init(options).read(data); -} -window.Graph = Graph; - -/** Set up options and instance variables */ -Graph.prototype.init = function (options) { - options = options || {}; - var self = this; - - self.allowReflexiveEdges = options.allowReflexiveEdges || false; - - self.vertices = {}; - self.numEdges = 0; - return self; -}; - -/** Read data from the plain object data - both in d3 form (nodes and links) or vertices and edges */ -Graph.prototype.read = function (data) { - if (!data) { - return this; - } - var self = this; - if (Object.prototype.hasOwnProperty.call(data, "nodes")) { - return self.readNodesAndLinks(data); - } - if (Object.prototype.hasOwnProperty.call(data, "vertices")) { - return self.readVerticesAndEdges(data); - } - return self; -}; - -//TODO: the next two could be combined -/** Create the graph using a list of nodes and a list of edges (where source and target are indeces into nodes) */ -Graph.prototype.readNodesAndLinks = function (data) { - if (!(data && Object.prototype.hasOwnProperty.call(data, "nodes"))) { - return this; - } - //console.debug( 'readNodesAndLinks:', data ); - //console.debug( 'data:\n' + JSON.stringify( data, null, ' ' ) ); - var self = this; - data.nodes.forEach((node) => { - self.createVertex(node.name, node.data); - }); - //console.debug( JSON.stringify( self.vertices, null, ' ' ) ); - - (data.links || []).forEach((edge, i) => { - var sourceName = data.nodes[edge.source].name; - var targetName = data.nodes[edge.target].name; - self.createEdge(sourceName, targetName, self.directed); - }); - //self.print(); - //console.debug( JSON.stringify( self.toNodesAndLinks(), null, ' ' ) ); - return self; -}; - -/** Create the graph using a list of nodes and a list of edges (where source and target are names of nodes) */ -Graph.prototype.readVerticesAndEdges = function (data) { - if (!(data && Object.prototype.hasOwnProperty.call(data, "vertices"))) { - return this; - } - //console.debug( 'readVerticesAndEdges:', data ); - //console.debug( 'data:\n' + JSON.stringify( data, null, ' ' ) ); - var self = this; - data.vertices.forEach((node) => { - self.createVertex(node.name, node.data); - }); - //console.debug( JSON.stringify( self.vertices, null, ' ' ) ); - - (data.edges || []).forEach((edge, i) => { - self.createEdge(edge.source, edge.target, self.directed); - }); - //self.print(); - //console.debug( JSON.stringify( self.toNodesAndLinks(), null, ' ' ) ); - return self; -}; - -/** Return the vertex with name, creating it if necessary */ -Graph.prototype.createVertex = function (name, data) { - //console.debug( 'createVertex:', name, data ); - if (this.vertices[name]) { - return this.vertices[name]; - } - return (this.vertices[name] = new Vertex(name, data)); -}; - -/** Create an edge in vertex named sourceName to targetName (optionally adding data to it) - * If directed is false, create a second edge from targetName to sourceName. - */ -Graph.prototype.createEdge = function (sourceName, targetName, directed, data) { - //note: allows multiple 'equivalent' edges (to/from same source/target) - //console.debug( 'createEdge:', source, target, directed ); - var isReflexive = sourceName === targetName; - if (!this.allowReflexiveEdges && isReflexive) { - return null; - } - - var sourceVertex = this.vertices[sourceName]; - var targetVertex = this.vertices[targetName]; - //note: silently ignores edges from/to unknown vertices - if (!(sourceVertex && targetVertex)) { - return null; - } - - //TODO: prob. move to vertex - var self = this; - - var edge = new Edge(sourceName, targetName, data); - sourceVertex.edges[targetName] = edge; - sourceVertex.degree += 1; - self.numEdges += 1; - - //TODO:! don't like having duplicate edges for non-directed graphs - // mirror edges (reversing source and target) in non-directed graphs - // but only if not reflexive - if (!isReflexive && !directed) { - // flip directed to prevent recursion loop - self.createEdge(targetName, sourceName, true); - } - - return edge; -}; - -/** Walk over all the edges of the graph using the vertex.eachEdge iterator */ -Graph.prototype.edges = function (propsOrFn) { - return Array.prototype.concat.apply( - [], - this.eachVertex((vertex) => vertex.eachEdge(propsOrFn)) - ); -}; - -/** Iterate over all the vertices in the graph */ -Graph.prototype.eachVertex = function (propsOrFn) { - return iterate(this.vertices, propsOrFn); -}; - -/** Return a list of the vertices adjacent to vertex */ -Graph.prototype.adjacent = function (vertex) { - var self = this; - return iterate(vertex.edges, (edge) => self.vertices[edge.target]); -}; - -/** Call fn on each vertex adjacent to vertex */ -Graph.prototype.eachAdjacent = function (vertex, fn) { - var self = this; - return iterate(vertex.edges, (edge) => { - var adj = self.vertices[edge.target]; - return fn.call(vertex, adj, edge); - }); -}; - -/** Print the graph to the console (debugging) */ -Graph.prototype.print = function () { - var self = this; - console.log(`Graph has ${Object.keys(self.vertices).length} vertices`); - self.eachVertex((vertex) => { - console.log(vertex.toString()); - vertex.eachEdge((edge) => { - console.log(`\t ${edge}`); - }); - }); - return self; -}; - -/** Return a DOT format string of this graph */ -Graph.prototype.toDOT = function () { - var self = this; - var strings = []; - strings.push("graph bler {"); - self.edges((edge) => { - strings.push(`\t${edge.from} -- ${edge.to};`); - }); - strings.push("}"); - return strings.join("\n"); -}; - -/** Return vertices and edges of this graph in d3 node/link format */ -Graph.prototype.toNodesAndLinks = function () { - var self = this; - var indeces = {}; - return { - nodes: self.eachVertex((vertex, key, i) => { - indeces[vertex.name] = i; - return vertex.toJSON(); - }), - links: self.edges((edge) => { - var json = edge.toJSON(); - json.source = indeces[edge.source]; - json.target = indeces[edge.target]; - return json; - }), - }; -}; - -/** Return vertices and edges of this graph where edges use the name/id as source and target */ -Graph.prototype.toVerticesAndEdges = function () { - var self = this; - return { - vertices: self.eachVertex((vertex, key) => vertex.toJSON()), - edges: self.edges((edge) => edge.toJSON()), - }; -}; - -/** Search this graph using BFS */ -Graph.prototype.breadthFirstSearch = function (start, processFns) { - return new BreadthFirstSearch(this).search(start); -}; - -/** Return a searchtree of this graph using BFS */ -Graph.prototype.breadthFirstSearchTree = function (start, processFns) { - return new BreadthFirstSearch(this).searchTree(start); -}; - -/** Search this graph using DFS */ -Graph.prototype.depthFirstSearch = function (start, processFns) { - return new DepthFirstSearch(this).search(start); -}; - -/** Return a searchtree of this graph using DFS */ -Graph.prototype.depthFirstSearchTree = function (start, processFns) { - return new DepthFirstSearch(this).searchTree(start); -}; - -//Graph.prototype.shortestPath = function( start, end ){ -//}; -// -//Graph.prototype.articulationVertices = function(){ -//}; -// -//Graph.prototype.isAcyclic = function(){ -//}; -// -//Graph.prototype.isBipartite = function(){ -//}; - -/** Return an array of weakly connected (no edges between) sub-graphs in this graph */ -Graph.prototype.weakComponents = function () { - //TODO: alternately, instead of returning graph-like objects: - // - could simply decorate the vertices (vertex.component = componentIndex), or clone the graph and do that - var self = this; - - var searchGraph = this; - var undiscovered; - var components = []; - - function getComponent(undiscoveredVertex) { - //TODO: better interface on dfs (search v. searchTree) - var search = new DepthFirstSearch(searchGraph)._search(undiscoveredVertex); - - // remove curr discovered from undiscovered - undiscovered = undiscovered.filter((name) => !(name in search.discovered)); - - return { - vertices: Object.keys(search.discovered).map((vertexName) => self.vertices[vertexName].toJSON()), - edges: search.edges.map((edge) => { - // restore any reversed edges - var hasBeenReversed = self.vertices[edge.target].edges[edge.source] !== undefined; - if (self.directed && hasBeenReversed) { - var swap = edge.source; - edge.source = edge.target; - edge.target = swap; - } - return edge; - }), - }; - } - - if (self.directed) { - // if directed - convert to undirected for search - searchGraph = new Graph(false, self.toNodesAndLinks()); - } - undiscovered = Object.keys(searchGraph.vertices); - //console.debug( '(initial) undiscovered:', undiscovered ); - while (undiscovered.length) { - var undiscoveredVertex = searchGraph.vertices[undiscovered.shift()]; - components.push(getComponent(undiscoveredVertex)); - //console.debug( 'undiscovered now:', undiscovered ); - } - - //console.debug( 'components:\n', JSON.stringify( components, null, ' ' ) ); - return components; -}; - -/** Return a single graph containing the weakly connected components in this graph */ -Graph.prototype.weakComponentGraph = function () { - //note: although this can often look like the original graph - edges can be lost - var components = this.weakComponents(); - return new Graph(this.directed, { - vertices: components.reduce((reduction, curr) => reduction.concat(curr.vertices), []), - edges: components.reduce((reduction, curr) => reduction.concat(curr.edges), []), - }); -}; - -/** Return an array of graphs of the weakly connected components in this graph */ -Graph.prototype.weakComponentGraphArray = function () { - //note: although this can often look like the original graph - edges can be lost - var graph = this; - return this.weakComponents().map((component) => new Graph(graph.directed, component)); -}; - -// ============================================================================ -/** Create a random graph with numVerts vertices and numEdges edges (for testing) - */ -function randGraph(directed, numVerts, numEdges) { - //console.debug( 'randGraph', directed, numVerts, numEdges ); - var data = { nodes: [], links: [] }; - function randRange(range) { - return Math.floor(Math.random() * range); - } - for (var i = 0; i < numVerts; i++) { - data.nodes.push({ name: i }); - } - for (i = 0; i < numEdges; i++) { - data.links.push({ - source: randRange(numVerts), - target: randRange(numVerts), - }); - } - //console.debug( JSON.stringify( data, null, ' ' ) ); - return new Graph(directed, data); -} - -// ============================================================================ -export default { - Vertex: Vertex, - Edge: Edge, - BreadthFirstSearch: BreadthFirstSearch, - DepthFirstSearch: DepthFirstSearch, - Graph: Graph, - randGraph: randGraph, -}; diff --git a/client/tests/qunit/testBundle.js b/client/tests/qunit/testBundle.js index 2286b30def8..3d8713ee446 100644 --- a/client/tests/qunit/testBundle.js +++ b/client/tests/qunit/testBundle.js @@ -5,7 +5,6 @@ import "./tests/galaxy_app_base_tests"; import "./tests/jquery_plugin_tests"; import "./tests/metrics_logger_tests"; -import "./tests/graph_tests"; import "./tests/modal_tests"; import "./tests/utils_tests"; import "./tests/ui_tests"; diff --git a/client/tests/qunit/tests/graph_tests.js b/client/tests/qunit/tests/graph_tests.js deleted file mode 100644 index 19e945d9a6b..00000000000 --- a/client/tests/qunit/tests/graph_tests.js +++ /dev/null @@ -1,563 +0,0 @@ -/* global QUnit */ -import testApp from "../test-app"; -import GRAPH from "utils/graph"; - -QUnit.module("utils/graph.js library tests", { - beforeEach: function () { - testApp.create(); - }, - afterEach: function () { - testApp.destroy(); - }, -}); - -///* -function testEmptyObject(assert, o) { - assert.ok(typeof o === "object"); - assert.ok(Object.keys(o).length === 0); -} - -// ------------------------------------------------------------------------ vertices -QUnit.test("Empty vertex construction", function (assert) { - var vert = new GRAPH.Vertex(); - assert.ok(vert instanceof GRAPH.Vertex); - assert.ok(vert.name === "(unnamed)"); - assert.ok(vert.data === null); - testEmptyObject(assert, vert.edges); - assert.ok(vert.degree === 0); - assert.ok(vert + "" === "Vertex((unnamed))"); - assert.deepEqual(vert.toJSON(), { name: "(unnamed)", data: null }); -}); - -QUnit.test("Vertex construction", function (assert) { - var vert = new GRAPH.Vertex("blah", { blorp: 1, bleep: 2 }); - assert.ok(vert instanceof GRAPH.Vertex); - assert.ok(vert.name === "blah"); - assert.deepEqual(vert.data, { blorp: 1, bleep: 2 }); - testEmptyObject(assert, vert.edges); - assert.ok(vert.degree === 0); - assert.ok(vert + "" === "Vertex(blah)"); - assert.deepEqual(vert.toJSON(), { name: "blah", data: { blorp: 1, bleep: 2 } }); -}); - -// ------------------------------------------------------------------------ edges -QUnit.test("Empty edge construction", function (assert) { - var edge = new GRAPH.Edge(); - assert.ok(edge instanceof GRAPH.Edge); - assert.ok(edge.source === null); - assert.ok(edge.target === null); - assert.ok(edge.data === null); - assert.ok(edge + "" === "null->null"); - assert.deepEqual(edge.toJSON(), { source: null, target: null }); -}); - -QUnit.test("Edge construction", function (assert) { - var edge = new GRAPH.Edge("A", "B", { one: 1, two: 2 }); - assert.ok(edge instanceof GRAPH.Edge); - assert.ok(edge.source === "A"); - assert.ok(edge.target === "B"); - assert.deepEqual(edge.data, { one: 1, two: 2 }); - assert.ok(edge + "" === "A->B"); - assert.deepEqual(edge.toJSON(), { source: "A", target: "B", data: { one: 1, two: 2 } }); -}); - -// ------------------------------------------------------------------------ graphs -function testEmptyGraph(assert, graph) { - assert.ok(graph instanceof GRAPH.Graph); - testEmptyObject(assert, graph.vertices); - assert.ok(graph.numEdges === 0); -} - -QUnit.test("Empty graph construction", function (assert) { - var graph = new GRAPH.Graph(); - - assert.ok(graph.directed === false); - assert.ok(graph.allowReflexiveEdges === false); - - testEmptyGraph(assert, graph); -}); - -QUnit.test("Bad data graph construction", function (assert) { - var graph = new GRAPH.Graph(false, {}); - testEmptyGraph(assert, graph); - - graph = new GRAPH.Graph(false, null); - testEmptyGraph(assert, graph); -}); - -QUnit.test("Test directed and options", function (assert) { - var graph = new GRAPH.Graph(true, {}, { allowReflexiveEdges: true }); - - assert.ok(graph.directed); - assert.ok(graph.allowReflexiveEdges); - - testEmptyGraph(assert, graph); -}); - -function testSampleDirectedGraph(assert, graph) { - assert.ok(!graph.directed); - - assert.ok(Object.keys(graph.vertices).length === 3); - assert.ok(graph.vertices.A instanceof GRAPH.Vertex); - assert.ok(graph.vertices.B instanceof GRAPH.Vertex); - assert.ok(graph.vertices.C instanceof GRAPH.Vertex); - - assert.deepEqual(Object.keys(graph.vertices.A.edges), ["B", "C"]); - assert.deepEqual(Object.keys(graph.vertices.B.edges), ["A", "C"]); - assert.deepEqual(Object.keys(graph.vertices.C.edges), ["A", "B"]); - - assert.ok(graph.vertices.A.degree === 2); - assert.ok(graph.vertices.B.degree === 2); - assert.ok(graph.vertices.C.degree === 2); - - assert.deepEqual(graph.vertices.A.edges.B.toJSON(), { source: "A", target: "B" }); - assert.deepEqual(graph.vertices.A.edges.C.toJSON(), { source: "A", target: "C" }); - assert.deepEqual(graph.vertices.B.edges.A.toJSON(), { source: "B", target: "A" }); - assert.deepEqual(graph.vertices.B.edges.C.toJSON(), { source: "B", target: "C" }); - assert.deepEqual(graph.vertices.C.edges.A.toJSON(), { source: "C", target: "A" }); - assert.deepEqual(graph.vertices.C.edges.B.toJSON(), { source: "C", target: "B" }); - - assert.ok(graph.numEdges === 6); -} - -function testSampleNonDirectedGraph(assert, graph) { - assert.ok(graph.directed); - - assert.ok(Object.keys(graph.vertices).length === 3); - assert.ok(graph.vertices.A instanceof GRAPH.Vertex); - assert.ok(graph.vertices.B instanceof GRAPH.Vertex); - assert.ok(graph.vertices.C instanceof GRAPH.Vertex); - - assert.deepEqual(Object.keys(graph.vertices.A.edges), ["B", "C"]); - assert.deepEqual(Object.keys(graph.vertices.B.edges), ["C"]); - assert.deepEqual(Object.keys(graph.vertices.C.edges), []); - - assert.ok(graph.vertices.A.degree === 2); - assert.ok(graph.vertices.B.degree === 1); - assert.ok(graph.vertices.C.degree === 0); - - assert.deepEqual(graph.vertices.A.edges.B.toJSON(), { source: "A", target: "B" }); - assert.deepEqual(graph.vertices.A.edges.C.toJSON(), { source: "A", target: "C" }); - assert.deepEqual(graph.vertices.B.edges.C.toJSON(), { source: "B", target: "C" }); - - assert.ok(graph.numEdges === 3); -} - -var nodeLinkData = { - nodes: [ - { name: "A", data: 100 }, - { name: "B", data: 200 }, - { name: "C", data: 300 }, - ], - links: [ - { source: 0, target: 1 }, - { source: 0, target: 2 }, - { source: 1, target: 2 }, - ], -}; - -QUnit.test("Test nodes and links data input on *non-directed* graph", function (assert) { - var graph = new GRAPH.Graph(false, nodeLinkData); - testSampleDirectedGraph(assert, graph); -}); - -QUnit.test("Test nodes and links data input on *directed* graph", function (assert) { - var graph = new GRAPH.Graph(true, nodeLinkData); - testSampleNonDirectedGraph(assert, graph); -}); - -var vertexEdgeData = { - vertices: [ - { name: "A", data: 100 }, - { name: "B", data: 200 }, - { name: "C", data: 300 }, - ], - edges: [ - { source: "A", target: "B" }, - { source: "A", target: "C" }, - { source: "B", target: "C" }, - ], -}; - -QUnit.test("Test vertex and edge data input on *non-directed* graph", function (assert) { - var graph = new GRAPH.Graph(false, vertexEdgeData); - testSampleDirectedGraph(assert, graph); -}); - -QUnit.test("Test vertex and edge data input on *directed* graph", function (assert) { - var graph = new GRAPH.Graph(true, vertexEdgeData); - testSampleNonDirectedGraph(assert, graph); -}); - -QUnit.test("Test vertex eachEdge", function (assert) { - var graph = new GRAPH.Graph(false, nodeLinkData); - assert.ok(typeof graph.vertices.A.eachEdge === "function"); - assert.deepEqual( - graph.vertices.A.eachEdge(function (e) { - return e.target; - }), - ["B", "C"] - ); - assert.ok(graph.vertices.A.eachEdge({ target: "B" }).length === 1); -}); - -QUnit.test("Test graph eachVertex", function (assert) { - var graph = new GRAPH.Graph(true, nodeLinkData); - assert.ok(typeof graph.eachVertex === "function"); - assert.deepEqual( - graph.eachVertex(function (v) { - return { n: v.name, d: v.degree }; - }), - [ - { n: "A", d: 2 }, - { n: "B", d: 1 }, - { n: "C", d: 0 }, - ] - ); - assert.ok(graph.eachVertex({ degree: 2 })[0] === graph.vertices.A); -}); - -QUnit.test("Test createVertex", function (assert) { - var graph = new GRAPH.Graph(); - var vert1 = graph.createVertex("A", { blah: 1 }); - assert.ok(vert1 instanceof GRAPH.Vertex); - assert.ok(vert1 === graph.vertices.A); - assert.ok(graph.createVertex("A", { blah: 1 }) === vert1); -}); - -QUnit.test("Test createEdge", function (assert) { - var graph, A, B, edge; - - graph = new GRAPH.Graph(); - A = graph.createVertex("A"); - B = graph.createVertex("B"); - edge = graph.createEdge("A", "B"); - assert.ok(edge instanceof GRAPH.Edge); - assert.ok(A.degree === 1); - assert.ok(B.degree === 1); - assert.ok(A.edges.B); - assert.ok(B.edges.A); - assert.ok(graph.numEdges === 2); - - // bad target - graph = new GRAPH.Graph(); - A = graph.createVertex("A"); - B = graph.createVertex("B"); - edge = graph.createEdge("A", "C"); - assert.ok(edge === null); - assert.ok(A.degree === 0); - assert.ok(B.degree === 0); - assert.ok(!A.edges.B); - assert.ok(!B.edges.A); - assert.ok(graph.numEdges === 0); - - // bad source - graph = new GRAPH.Graph(); - A = graph.createVertex("A"); - B = graph.createVertex("B"); - edge = graph.createEdge("C", "A"); - assert.ok(graph.numEdges === 0); - - // reflexive - graph = new GRAPH.Graph(); - A = graph.createVertex("A"); - B = graph.createVertex("B"); - edge = graph.createEdge("A", "A"); - assert.ok(graph.numEdges === 0); - - // reflexive (allowed) - graph = new GRAPH.Graph(false, {}, { allowReflexiveEdges: true }); - A = graph.createVertex("A"); - edge = graph.createEdge("A", "A"); - // reflexive edges shouldn't mirror - assert.ok(graph.numEdges === 1); - assert.ok(A.edges.A); -}); - -QUnit.test("Test graph.edges", function (assert) { - var graph = new GRAPH.Graph(false, nodeLinkData); - assert.ok(graph.edges().length === 6); - assert.deepEqual( - graph.edges(function (e) { - return e.source; - }), - ["A", "A", "B", "B", "C", "C"] - ); - assert.deepEqual(graph.edges({ source: "A" }), [graph.vertices.A.edges.B, graph.vertices.A.edges.C]); - - graph = new GRAPH.Graph(true, nodeLinkData); - assert.ok(graph.edges().length === 3); - assert.deepEqual( - graph.edges(function (e) { - return e.source; - }), - ["A", "A", "B"] - ); - assert.deepEqual(graph.edges({ source: "A" }), [graph.vertices.A.edges.B, graph.vertices.A.edges.C]); -}); - -QUnit.test("Test graph.adjacent", function (assert) { - var graph = new GRAPH.Graph(true, nodeLinkData); - assert.deepEqual(graph.adjacent(graph.vertices.A), [graph.vertices.B, graph.vertices.C]); - assert.deepEqual(graph.adjacent(graph.vertices.B), [graph.vertices.C]); - assert.deepEqual(graph.adjacent(graph.vertices.C), []); -}); - -QUnit.test("Test graph.eachAdjacent", function (assert) { - var graph = new GRAPH.Graph(true, nodeLinkData); - assert.deepEqual( - graph.eachAdjacent(graph.vertices.A, function (v, e) { - return v; - }), - [graph.vertices.B, graph.vertices.C] - ); -}); - -// ------------------------------------------------------------------------ breadth first search -QUnit.test("Empty BreadthFirstSearch", function (assert) { - var search = new GRAPH.BreadthFirstSearch(); - assert.ok(search instanceof GRAPH.BreadthFirstSearch); - assert.ok(search.graph === undefined); - assert.ok(typeof search.processFns === "object"); - assert.ok(typeof search.processFns.vertexEarly === "function"); - assert.ok(typeof search.processFns.edge === "function"); - assert.ok(typeof search.processFns.vertexLate === "function"); - assert.ok(typeof search._cache === "object"); -}); - -QUnit.test("BreadthFirstSearch on undirected graph", function (assert) { - var graph = new GRAPH.Graph(false, nodeLinkData), - bfs = new GRAPH.BreadthFirstSearch(graph); - assert.ok(bfs instanceof GRAPH.BreadthFirstSearch); - assert.ok(bfs.graph === graph); - - var search = bfs.search("A"), - tree = bfs.searchTree("A"); - assert.deepEqual(search, { - discovered: { A: true, B: true, C: true }, - edges: [ - { source: "A", target: "B" }, - { source: "A", target: "C" }, - ], - }); - assert.ok(tree instanceof GRAPH.Graph); - assert.deepEqual(tree.vertices.A.toJSON(), graph.vertices.A.toJSON()); - assert.deepEqual( - tree.eachVertex(function (v) { - return v.degree; - }), - [2, 0, 0] - ); - - assert.deepEqual(bfs.search("B").edges, [ - { source: "B", target: "A" }, - { source: "B", target: "C" }, - ]); - assert.deepEqual(bfs.search("C").edges, [ - { source: "C", target: "A" }, - { source: "C", target: "B" }, - ]); - assert.ok(typeof bfs._cache.A === "object"); - assert.deepEqual(Object.keys(bfs._cache), ["A", "B", "C"]); -}); - -QUnit.test("BreadthFirstSearch on directed graph", function (assert) { - var graph = new GRAPH.Graph(true, nodeLinkData), - bfs = new GRAPH.BreadthFirstSearch(graph); - assert.ok(bfs instanceof GRAPH.BreadthFirstSearch); - assert.ok(bfs.graph === graph); - - var search = bfs.search("A"), - tree = bfs.searchTree("A"); - assert.deepEqual(search, { - discovered: { A: true, B: true, C: true }, - edges: [ - { source: "A", target: "B" }, - { source: "A", target: "C" }, - ], - }); - assert.ok(tree instanceof GRAPH.Graph); - assert.deepEqual(tree.vertices.A.toJSON(), graph.vertices.A.toJSON()); - assert.deepEqual( - tree.eachVertex(function (v) { - return v.degree; - }), - [2, 0, 0] - ); - - assert.deepEqual(bfs.search("B").edges, [{ source: "B", target: "C" }]); - assert.deepEqual(bfs.search("C").edges, []); - assert.ok(typeof bfs._cache.A === "object"); - assert.deepEqual(Object.keys(bfs._cache), ["A", "B", "C"]); -}); - -// ------------------------------------------------------------------------ depth first search -var DFSData = { - vertices: [{ name: "A" }, { name: "B" }, { name: "C" }, { name: "D" }, { name: "E" }, { name: "F" }], - edges: [ - { source: "A", target: "B" }, - { source: "B", target: "C" }, - // confound it - { source: "A", target: "C" }, - { source: "C", target: "D" }, - { source: "A", target: "E" }, - { source: "E", target: "F" }, - // confound it - { source: "F", target: "A" }, - ], -}; - -QUnit.test("Empty DepthFirstSearch", function (assert) { - var search = new GRAPH.DepthFirstSearch(); - assert.ok(search instanceof GRAPH.DepthFirstSearch); - assert.ok(search.graph === undefined); - assert.ok(typeof search.processFns === "object"); - assert.ok(typeof search.processFns.vertexEarly === "function"); - assert.ok(typeof search.processFns.edge === "function"); - assert.ok(typeof search.processFns.vertexLate === "function"); - assert.ok(typeof search._cache === "object"); -}); - -QUnit.test("DepthFirstSearch on undirected graph", function (assert) { - var graph = new GRAPH.Graph(false, DFSData), - dfs = new GRAPH.DepthFirstSearch(graph); - assert.ok(dfs instanceof GRAPH.DepthFirstSearch); - assert.ok(dfs.graph === graph); - - var search = dfs.search("A"), - tree = dfs.searchTree("A"); - assert.deepEqual(search, { - discovered: { A: true, B: true, C: true, D: true, E: true, F: true }, - edges: [ - { source: "A", target: "B" }, - { source: "B", target: "C" }, - { source: "C", target: "D" }, - { source: "A", target: "E" }, - { source: "E", target: "F" }, - ], - entryTimes: { A: 0, B: 1, C: 2, D: 3, E: 7, F: 8 }, - exitTimes: { A: 11, B: 6, C: 5, D: 4, E: 10, F: 9 }, - }); - assert.ok(tree instanceof GRAPH.Graph); - assert.deepEqual(tree.vertices.A.toJSON(), graph.vertices.A.toJSON()); - assert.deepEqual( - tree.eachVertex(function (v) { - return v.degree; - }), - [2, 1, 1, 0, 1, 0] - ); - - assert.deepEqual(dfs.search("B").edges, [ - { source: "B", target: "A" }, - { source: "A", target: "C" }, - { source: "C", target: "D" }, - { source: "A", target: "E" }, - { source: "E", target: "F" }, - ]); - - assert.ok(typeof dfs._cache.A === "object"); - assert.deepEqual(Object.keys(dfs._cache), ["A", "B"]); -}); - -QUnit.test("DepthFirstSearch on directed graph", function (assert) { - var graph = new GRAPH.Graph(true, DFSData), - dfs = new GRAPH.DepthFirstSearch(graph); - assert.ok(dfs instanceof GRAPH.DepthFirstSearch); - assert.ok(dfs.graph === graph); - - var search = dfs.search("A"), - tree = dfs.searchTree("A"); - assert.deepEqual(search, { - discovered: { A: true, B: true, C: true, D: true, E: true, F: true }, - edges: [ - { source: "A", target: "B" }, - { source: "B", target: "C" }, - { source: "C", target: "D" }, - { source: "A", target: "E" }, - { source: "E", target: "F" }, - ], - entryTimes: { A: 0, B: 1, C: 2, D: 3, E: 7, F: 8 }, - exitTimes: { A: 11, B: 6, C: 5, D: 4, E: 10, F: 9 }, - }); - assert.ok(tree instanceof GRAPH.Graph); - assert.deepEqual(tree.vertices.A.toJSON(), graph.vertices.A.toJSON()); - assert.deepEqual( - tree.eachVertex(function (v) { - return v.degree; - }), - [2, 1, 1, 0, 1, 0] - ); - - assert.deepEqual(dfs.search("B").edges, [ - { source: "B", target: "C" }, - { source: "C", target: "D" }, - ]); - - assert.ok(typeof dfs._cache.A === "object"); - assert.deepEqual(Object.keys(dfs._cache), ["A", "B"]); -}); - -// ------------------------------------------------------------------------ components -//*/ -QUnit.test("weakComponents on undirected graph", function (assert) { - var graph = new GRAPH.Graph(false, { - vertices: [{ name: "A" }, { name: "B" }, { name: "C" }, { name: "D" }, { name: "E" }], - edges: [ - { source: "A", target: "B" }, - { source: "C", target: "D" }, - ], - }); - assert.equal(graph.numEdges, 4); - var components = graph.weakComponents(); - assert.equal(components.length, 3); -}); - -QUnit.test("weakComponents on directed graph", function (assert) { - var graph, components; - graph = new GRAPH.Graph(true, { - vertices: [{ name: "A" }, { name: "B" }, { name: "C" }, { name: "D" }, { name: "E" }], - edges: [ - { source: "A", target: "B" }, - { source: "D", target: "C" }, - ], - }); - assert.equal(graph.numEdges, 2); - - components = graph.weakComponents(); - assert.equal(components.length, 3); - - graph = new GRAPH.Graph(true, { - vertices: [ - { name: "A", data: 100 }, - { name: "B", data: 200 }, - { name: "C", data: 30 }, - { name: "D", data: 40 }, - { name: "E", data: 500 }, - { name: "F", data: 600 }, - { name: "G", data: 7 }, - ], - edges: [ - { source: "A", target: "B" }, - { source: "D", target: "C" }, - { source: "E", target: "A" }, - { source: "F", target: "E" }, - ], - }); - components = graph.weakComponents(); - assert.equal(components.length, 3); - // data retained - assert.equal(components[0].vertices[0].data, 100); - - assert.equal(components[0].vertices.length, 4); - assert.deepEqual(components[0].edges, [ - { source: "A", target: "B" }, - { source: "E", target: "A" }, - { source: "F", target: "E" }, - ]); - - assert.equal(components[1].vertices.length, 2); - assert.deepEqual(components[1].edges, [{ source: "D", target: "C" }]); - - assert.deepEqual(components[2].vertices, [{ name: "G", data: 7 }]); - assert.deepEqual(components[2].edges.length, 0); -}); From 3fe5825eb556b26915ceb535e64e251f4f0a83aa Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Sun, 29 Jan 2023 23:24:13 -0500 Subject: [PATCH 02/23] Convert utils tests to jest --- client/src/utils/utils.test.js | 17 ++++++++++++++++ client/tests/qunit/testBundle.js | 1 - client/tests/qunit/tests/utils_tests.js | 26 ------------------------- 3 files changed, 17 insertions(+), 27 deletions(-) create mode 100644 client/src/utils/utils.test.js delete mode 100644 client/tests/qunit/tests/utils_tests.js diff --git a/client/src/utils/utils.test.js b/client/src/utils/utils.test.js new file mode 100644 index 00000000000..1375047df27 --- /dev/null +++ b/client/src/utils/utils.test.js @@ -0,0 +1,17 @@ +import Utils from "./utils"; + +describe("test utils", () => { + it("test isEmpty", async () => { + expect(Utils.isEmpty([])).toBe(true); // "Empty array"); + expect(Utils.isEmpty(["data", undefined])).toBe(true); // , "Array contains `undefined`"); + expect(Utils.isEmpty(["data", null])).toBe(true);//, "Array contains `null`"); + expect(Utils.isEmpty(["data", "__null__"])).toBe(true); // , "Array contains `__null__`"); + expect(Utils.isEmpty(["data", "__undefined__"])).toBe(true); // , "Array contains `__undefined__`"); + expect(Utils.isEmpty(null)).toBe(true); //, "Array is null"); + expect(Utils.isEmpty("__null__")).toBe(true); //, "Array is __null__"); + expect(Utils.isEmpty("__undefined__")).toBe(true); //, "Array is __undefined__"); + expect(Utils.isEmpty(["data"])).toBe(false); //, "Array contains `data`"); + expect(Utils.isEmpty(1)).toBe(false); //, "Value is int"); + expect(Utils.isEmpty(0)).toBe(false); //, "Value is zero"); + }); +}); diff --git a/client/tests/qunit/testBundle.js b/client/tests/qunit/testBundle.js index 3d8713ee446..c10502e1c72 100644 --- a/client/tests/qunit/testBundle.js +++ b/client/tests/qunit/testBundle.js @@ -6,5 +6,4 @@ import "./tests/galaxy_app_base_tests"; import "./tests/jquery_plugin_tests"; import "./tests/metrics_logger_tests"; import "./tests/modal_tests"; -import "./tests/utils_tests"; import "./tests/ui_tests"; diff --git a/client/tests/qunit/tests/utils_tests.js b/client/tests/qunit/tests/utils_tests.js deleted file mode 100644 index da57e48b6c6..00000000000 --- a/client/tests/qunit/tests/utils_tests.js +++ /dev/null @@ -1,26 +0,0 @@ -/* global QUnit */ -import testApp from "../test-app"; -import Utils from "utils/utils"; - -QUnit.module("Utils test", { - beforeEach: function () { - testApp.create(); - }, - afterEach: function () { - testApp.destroy(); - }, -}); - -QUnit.test("isEmpty", function (assert) { - assert.ok(Utils.isEmpty([]), "Empty array"); - assert.ok(Utils.isEmpty(["data", undefined]), "Array contains `undefined`"); - assert.ok(Utils.isEmpty(["data", null]), "Array contains `null`"); - assert.ok(Utils.isEmpty(["data", "__null__"]), "Array contains `__null__`"); - assert.ok(Utils.isEmpty(["data", "__undefined__"]), "Array contains `__undefined__`"); - assert.ok(Utils.isEmpty(null), "Array is null"); - assert.ok(Utils.isEmpty("__null__"), "Array is __null__"); - assert.ok(Utils.isEmpty("__undefined__"), "Array is __undefined__"); - assert.ok(!Utils.isEmpty(["data"]), "Array contains `data`"); - assert.ok(!Utils.isEmpty(1), "Value is int"); - assert.ok(!Utils.isEmpty(0), "Value is zero"); -}); From 1760d9a9659bdfced16fe508ba840d914ead7b21 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Sun, 29 Jan 2023 23:39:23 -0500 Subject: [PATCH 03/23] Avoid discovering tests in /dist/ directories. (node_modules was already the default) --- client/tests/jest/jest.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/tests/jest/jest.config.js b/client/tests/jest/jest.config.js index 90d99bbb5dc..a4a84c4f3b6 100644 --- a/client/tests/jest/jest.config.js +++ b/client/tests/jest/jest.config.js @@ -39,6 +39,7 @@ module.exports = { roots: ["/src/", "/tests/jest/standalone/"], setupFilesAfterEnv: ["/tests/jest/jest.setup.js"], testEnvironment: "jsdom", + testPathIgnorePatterns: ["/node_modules/", "/dist/"], transform: { "^.+\\.js$": "babel-jest", "^.*\\.(vue)$": "@vue/vue2-jest", From 59092bb7538215c1d442936f574687c2ee63e706 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 30 Jan 2023 09:32:05 -0500 Subject: [PATCH 04/23] Incremental work on metrics logger tests More work on metrics-logger tests. --- client/src/utils/metrics-logger.test.js | 354 ++++++++++++++++ .../tests/qunit/tests/metrics_logger_tests.js | 379 ------------------ 2 files changed, 354 insertions(+), 379 deletions(-) create mode 100644 client/src/utils/metrics-logger.test.js delete mode 100644 client/tests/qunit/tests/metrics_logger_tests.js diff --git a/client/src/utils/metrics-logger.test.js b/client/src/utils/metrics-logger.test.js new file mode 100644 index 00000000000..75608afc9a2 --- /dev/null +++ b/client/src/utils/metrics-logger.test.js @@ -0,0 +1,354 @@ +// This file isn't really testing anything useful yet, it is just testing +// (or demonstrating) qunit+backbone interactions. + +import jQuery from "jquery"; +import sinon from "sinon"; +import metrics from "utils/metrics-logger"; + +const MockConsole = function () { + this.lastMessage = null; + ["log", "debug", "info", "warn", "error"].forEach((fnName) => { + this[fnName] = function () { + var args = Array.prototype.slice.call(arguments, 0); + this.lastMessage = { level: fnName, args: args }; + }; + }); + return this; +}; + +describe("metrics-logger tests", function () { + test("logger construction/initializiation defaults", function () { + const logger = new metrics.MetricsLogger({}); + expect(logger.consoleLogger).toEqual(null); + expect(logger.options.logLevel).toEqual(metrics.MetricsLogger.NONE); + expect(logger.options.consoleLevel).toEqual(metrics.MetricsLogger.NONE); + expect(logger.options.defaultNamespace).toEqual("Galaxy"); + expect(logger.options.clientPrefix).toEqual("client."); + expect(logger.options.postSize).toEqual(1000); + expect(logger.options.maxCacheSize).toEqual(3000); + expect(logger.options.addTime).toEqual(true); + expect(logger.options.postUrl).toEqual("/api/metrics"); + expect(logger.options.getPingData).toEqual(undefined); + expect(logger.options.onServerResponse).toEqual(undefined); + + expect(logger._postSize).toEqual(1000); + expect(logger.cache.constructor).toEqual(metrics.LoggingCache); + }); + + test("_parseLevel", function () { + const logger = new metrics.MetricsLogger({}); + expect(logger._parseLevel("all")).toEqual(metrics.MetricsLogger.ALL); + expect(logger._parseLevel("debug")).toEqual(metrics.MetricsLogger.DEBUG); + expect(logger._parseLevel("info")).toEqual(metrics.MetricsLogger.INFO); + expect(logger._parseLevel("warn")).toEqual(metrics.MetricsLogger.WARN); + expect(logger._parseLevel("error")).toEqual(metrics.MetricsLogger.ERROR); + expect(logger._parseLevel("metric")).toEqual(metrics.MetricsLogger.METRIC); + expect(logger._parseLevel("none")).toEqual(metrics.MetricsLogger.NONE); + expect(logger._parseLevel(15)).toEqual(15); + // + expect(() => { + logger._parseLevel(undefined); + }).toThrowError(/Unknown log level/); + expect(() => { + logger._parseLevel("nope"); + }).toThrowError(/Unknown log level/); + }); + + test("emit to cache at level", function () { + const logger = new metrics.MetricsLogger({ + logLevel: "metric", + }); + logger.cache.empty(); + + expect(logger.options.logLevel).toEqual(metrics.MetricsLogger.METRIC); + logger.emit("metric", "test", [1, 2, { three: 3 }]); + expect(logger.cache.length()).toEqual(1); + + const cached = logger.cache.get(1)[0]; + expect(cached.level).toEqual(metrics.MetricsLogger.METRIC); + expect(cached.namespace).toEqual("client.test"); + expect(cached.args.length).toEqual(3); + expect(cached.args[2].three).toEqual(3); + expect(typeof cached.time === "string").toBeTruthy(); + expect(cached.time === new Date(cached.time).toISOString()); + }); + + test("emit to cache below does not cache", function () { + const logger = new metrics.MetricsLogger({ + logLevel: "metric", + }); + logger.cache.empty(); + + logger.emit("error", "test", [1, 2, { three: 3 }]); + expect(logger.cache.length()).toBe(0); + }); + + test("emit to cache (silently) drops non-parsable", function () { + const logger = new metrics.MetricsLogger({ + logLevel: "metric", + }); + logger.cache.empty(); + + logger.emit("metric", "test", [{ window: window }]); + expect(logger.cache.length()).toBe(0); + }); + + // function metricsFromRequestBody(request) { + // // assumes 'metrics' is only entry in requestBody + // return JSON.parse(decodeURIComponent(request.requestBody.replace("metrics=", ""))); + // } + + // QUnit.test("_postCache success", function (assert) { + // var callback = sinon.spy(), + // logger = new metrics.MetricsLogger({ + // logLevel: "metric", + // onServerResponse: function (response) { + // callback(); + // }, + // }); + // logger.cache.empty(); + + // var server = sinon.fakeServer.create(), + // metricsOnServer; + // server.respondWith("POST", "/api/metrics", function (request) { + // metricsOnServer = metricsFromRequestBody(request); + // //console.debug( 'requestBody:', request.requestBody ); + // //console.debug( 'metricsOnServer:', JSON.stringify( metricsOnServer, null, ' ' ) ); + // request.respond( + // 200, + // { "Content-Type": "application/json" }, + // JSON.stringify({ + // fakeResponse: "yes", + // }) + // ); + // }); + + // logger.emit("metric", "test", [1, 2, { three: 3 }]); + // logger._postCache(); + // server.respond(); + + // assert.ok(callback.calledOnce, "onServerResponse was called"); + // assert.equal(logger.cache.length(), 0, "should have emptied cache (on success)"); + // assert.equal(logger._postSize, 1000, "_postSize still at default"); + + // // metrics were in proper form on server + // assert.equal(metricsOnServer.length, 1); + // var metric = metricsOnServer[0]; + // assert.equal(metric.level, metrics.MetricsLogger.METRIC); + // assert.equal(metric.namespace, "client.test"); + // assert.equal(metric.args.length, 3); + // assert.equal(metric.args[2].three, 3); + // assert.ok(typeof metric.time === "string"); + // assert.ok(metric.time === new Date(metric.time).toISOString()); + + // server.restore(); + // }); + + // QUnit.test("_postCache failure", function (assert) { + // var callback = sinon.spy(), + // logger = new metrics.MetricsLogger({ + // logLevel: "metric", + // onServerResponse: function (response) { + // callback(); + // }, + // }); + // logger.cache.empty(); + + // var server = sinon.fakeServer.create(); + // server.respondWith("POST", "/api/metrics", function (request) { + // request.respond( + // 500, + // { "Content-Type": "application/json" }, + // JSON.stringify({ + // err_msg: "NoooOPE!", + // }) + // ); + // }); + + // logger.emit("metric", "test", [1, 2, { three: 3 }]); + // logger._postCache(); + // server.respond(); + // //TODO: is the following what we want? + // assert.ok(!callback.calledOnce, "onServerResponse was NOT called"); + // assert.equal(logger.cache.length(), 1, "should NOT have emptied cache"); + // assert.equal(logger._postSize, logger.options.maxCacheSize, "_postSize changed to max"); + + // server.restore(); + // }); + + // // ------------------------------------------------------------------------ Emit to console + // QUnit.test("emit to console at level", function (assert) { + // var mockConsole = new MockConsole(), + // logger = new metrics.MetricsLogger({ + // consoleLevel: "debug", + // consoleLogger: mockConsole, + // }); + // assert.equal(logger.options.consoleLevel, metrics.MetricsLogger.DEBUG); + // assert.equal(logger.consoleLogger.constructor, MockConsole); + + // logger.emit("debug", "test", [1, 2, { three: 3 }]); + // assert.equal(logger.cache.length(), 1); + // //console.debug( JSON.stringify( mockConsole.lastMessage ) ); + // assert.equal(mockConsole.lastMessage.level, "debug"); + // assert.equal(mockConsole.lastMessage.args.length, 4); + // assert.equal(mockConsole.lastMessage.args[0], "test"); + // assert.equal(mockConsole.lastMessage.args[3].three, 3); + // }); + + // QUnit.test("emit to console below does not output", function (assert) { + // var mockConsole = new MockConsole(), + // logger = new metrics.MetricsLogger({ + // consoleLevel: "error", + // consoleLogger: mockConsole, + // }); + // logger.emit("debug", "test", [1, 2, { three: 3 }]); + // assert.equal(mockConsole.lastMessage, null); + // }); + + // // ------------------------------------------------------------------------ Shortcuts + // QUnit.test("logger shortcuts emit to default namespace properly", function (assert) { + // var logger = new metrics.MetricsLogger({ + // logLevel: "all", + // }); + // logger.cache.empty(); + + // assert.equal(logger.options.logLevel, metrics.MetricsLogger.ALL); + // logger.log(0); + // logger.debug(1); + // logger.info(2); + // logger.warn(3); + // logger.error(4); + // logger.metric(5); + + // assert.equal(logger.cache.length(), 6); + // var cached = logger.cache.remove(6), + // entry; + + // cached.forEach(function (entry) { + // assert.ok(entry.namespace === logger.options.clientPrefix + logger.options.defaultNamespace); + // assert.ok(jQuery.type(entry.args) === "array"); + // assert.ok(typeof entry.time === "string"); + // }); + + // // log is different + // entry = cached[0]; + // assert.ok(entry.level === 1); + // assert.ok(entry.args[0] === 0); + + // ["debug", "info", "warn", "error", "metric"].forEach(function (level, i) { + // entry = cached[i + 1]; + // assert.ok(entry.level === logger._parseLevel(level)); + // assert.ok(entry.args[0] === i + 1); + // }); + // }); + + // // ======================================================================== LoggingCache + // QUnit.test("cache construction/initializiation defaults", function (assert) { + // // use empty to prevent tests stepping on one another due to persistence + // var cache = new metrics.LoggingCache({ key: "logs-test" }).empty(); + // assert.equal(cache.maxSize, 5000); + // assert.equal(window.localStorage.getItem("logs-test"), "[]"); + // }); + + // QUnit.test("cache construction/initializiation failure", function (assert) { + // ////TODO: doesn't work - readonly + // //window.localStorage = null; + // //console.debug( 'localStorage:', window.localStorage ); + // var oldFn = metrics.LoggingCache.prototype._hasStorage; + // metrics.LoggingCache.prototype._hasStorage = function () { + // return false; + // }; + // assert.throws( + // function () { + // return new metrics.LoggingCache({ key: "logs-test" }); + // }, + // /LoggingCache needs localStorage/, + // "lack of localStorage throws error" + // ); + // metrics.LoggingCache.prototype._hasStorage = oldFn; + + // assert.throws( + // function () { + // return new metrics.LoggingCache(); + // }, + // /LoggingCache needs key for localStorage/, + // "lack of key throws error" + // ); + // }); + + // QUnit.test("cache construction/initializiation setting max cache size", function (assert) { + // var cache = new metrics.LoggingCache({ + // key: "logs-test", + // maxSize: 5, + // }).empty(); + // assert.equal(cache.maxSize, 5); + // }); + + // QUnit.test("cache plays well with no data", function (assert) { + // var cache = new metrics.LoggingCache({ key: "logs-test" }).empty(); + + // assert.equal(cache.length(), 0); + // var get = cache.get(10); + // assert.ok(jQuery.type(get) === "array" && get.length === 0); + // var remove = cache.remove(10); + // assert.ok(jQuery.type(remove) === "array" && remove.length === 0); + // assert.equal(cache.length(), 0); + // }); + + test("cache add properly adds and removes data", function () { + const cache = new metrics.LoggingCache({ + key: "logs-test", + maxSize: 5, + }).empty(); + + const entry1 = [{ one: 1 }, "two"]; + cache.add(entry1); + + expect(cache.length()).toBe(1); + expect(JSON.stringify(cache.get(1)[0])).toBe(JSON.stringify(entry1)); + + const entry2 = { blah: { one: 1 }, bler: ["three", { two: 2 }] }; + cache.add(entry2); + expect(cache.length()).toBe(2); + expect(cache.stringify(2)).toBe("[" + JSON.stringify(entry1) + "," + JSON.stringify(entry2) + "]"); + + // FIFO + var returned = cache.remove(1); + assert.equal(cache.length(), 1); + assert.ok(jQuery.type(returned) === "array" && returned.length === 1); + var returned0 = returned[0]; + assert.ok(jQuery.type(returned0) === "array" && JSON.stringify(returned0) === JSON.stringify(entry1)); + }); + + test("cache past max loses oldest", function () { + const cache = new metrics.LoggingCache({ + key: "logs-test", + maxSize: 5, + }).empty(); + + for (let i = 0; i < 10; i += 1) { + cache.add({ index: i }); + } + expect(cache.length()).toBe(5); + const get = cache.get(5); + expect(get[0].index === 5).toBeTruthy(); + expect(get[1].index === 6).toBeTruthy(); + expect(get[2].index === 7).toBeTruthy(); + expect(get[3].index === 8).toBeTruthy(); + expect(get[4].index === 9).toBeTruthy(); + }); + + test("cache is properly persistent", function () { + const cache1 = new metrics.LoggingCache({ key: "logs-test" }).empty(); + const entry = [{ one: 1 }, "two"]; + cache1.add(entry); + expect(cache1.length()).toBe(1); + + const cache2 = new metrics.LoggingCache({ key: "logs-test" }); + expect(cache2.length()).toBe(1); // , "old key gets previously stored"); + expect(JSON.stringify(cache2.get(1)[0])).toBe(JSON.stringify(entry)); + + const cache3 = new metrics.LoggingCache({ key: "logs-bler" }); + expect(cache3.length()).toBe(0); //, "new key causes new storage"); + }); +}); diff --git a/client/tests/qunit/tests/metrics_logger_tests.js b/client/tests/qunit/tests/metrics_logger_tests.js deleted file mode 100644 index ce069fcf3c7..00000000000 --- a/client/tests/qunit/tests/metrics_logger_tests.js +++ /dev/null @@ -1,379 +0,0 @@ -// This file isn't really testing anything useful yet, it is just testing -// (or demonstrating) qunit+backbone interactions. - -/* global QUnit */ -import jQuery from "jquery"; -import sinon from "sinon"; -import metrics from "utils/metrics-logger"; -import testApp from "../test-app"; - -var MockConsole = function () { - var self = this; - self.lastMessage = null; - ["log", "debug", "info", "warn", "error"].forEach(function (fnName) { - self[fnName] = function () { - var args = Array.prototype.slice.call(arguments, 0); - self.lastMessage = { level: fnName, args: args }; - }; - }); - return self; -}; - -(window.bootstrapped = {}).user = { - id: "test", -}; - -QUnit.module("Metrics logger tests", { - beforeEach: function () { - testApp.create(); - }, - afterEach: function () { - testApp.destroy(); - }, -}); - -// ======================================================================== MetricsLogger -QUnit.test("logger construction/initializiation defaults", function (assert) { - var logger = new metrics.MetricsLogger({}); - assert.equal(logger.consoleLogger, null); - assert.equal(logger.options.logLevel, metrics.MetricsLogger.NONE); - assert.equal(logger.options.consoleLevel, metrics.MetricsLogger.NONE); - assert.equal(logger.options.defaultNamespace, "Galaxy"); - assert.equal(logger.options.clientPrefix, "client."); - assert.equal(logger.options.postSize, 1000); - assert.equal(logger.options.maxCacheSize, 3000); - assert.equal(logger.options.addTime, true); - assert.equal(logger.options.postUrl, "/api/metrics"); - assert.equal(logger.options.getPingData, undefined); - assert.equal(logger.options.onServerResponse, undefined); - - assert.equal(logger._postSize, 1000); - assert.equal(logger.cache.constructor, metrics.LoggingCache); -}); - -QUnit.test("_parseLevel", function (assert) { - var logger = new metrics.MetricsLogger({}); - assert.equal(logger._parseLevel("all"), metrics.MetricsLogger.ALL); - assert.equal(logger._parseLevel("debug"), metrics.MetricsLogger.DEBUG); - assert.equal(logger._parseLevel("info"), metrics.MetricsLogger.INFO); - assert.equal(logger._parseLevel("warn"), metrics.MetricsLogger.WARN); - assert.equal(logger._parseLevel("error"), metrics.MetricsLogger.ERROR); - assert.equal(logger._parseLevel("metric"), metrics.MetricsLogger.METRIC); - assert.equal(logger._parseLevel("none"), metrics.MetricsLogger.NONE); - assert.equal(logger._parseLevel(15), 15); - - assert.throws( - function () { - logger._parseLevel(undefined); - }, - /Unknown log level/, - "Unknown log level throws error" - ); - assert.throws( - function () { - logger._parseLevel("nope"); - }, - /Unknown log level/, - "Unknown log level throws error" - ); -}); - -// ------------------------------------------------------------------------ Emit to cache -QUnit.test("emit to cache at level", function (assert) { - var logger = new metrics.MetricsLogger({ - logLevel: "metric", - }); - logger.cache.empty(); - - assert.equal(logger.options.logLevel, metrics.MetricsLogger.METRIC); - logger.emit("metric", "test", [1, 2, { three: 3 }]); - assert.equal(logger.cache.length(), 1); - - var cached = logger.cache.get(1)[0]; - //console.debug( 'cached:', JSON.stringify( cached ) ); - assert.equal(cached.level, metrics.MetricsLogger.METRIC); - assert.equal(cached.namespace, "client.test"); - assert.equal(cached.args.length, 3); - assert.equal(cached.args[2].three, 3); - assert.ok(typeof cached.time === "string"); - assert.ok(cached.time === new Date(cached.time).toISOString()); -}); - -QUnit.test("emit to cache below does not cache", function (assert) { - var logger = new metrics.MetricsLogger({ - logLevel: "metric", - }); - logger.cache.empty(); - - logger.emit("error", "test", [1, 2, { three: 3 }]); - assert.equal(logger.cache.length(), 0); -}); - -QUnit.test("emit to cache (silently) drops non-parsable", function (assert) { - var logger = new metrics.MetricsLogger({ - logLevel: "metric", - }); - logger.cache.empty(); - - logger.emit("metric", "test", [{ window: window }]); - assert.equal(logger.cache.length(), 0); -}); - -function metricsFromRequestBody(request) { - // assumes 'metrics' is only entry in requestBody - return JSON.parse(decodeURIComponent(request.requestBody.replace("metrics=", ""))); -} - -QUnit.test("_postCache success", function (assert) { - var callback = sinon.spy(), - logger = new metrics.MetricsLogger({ - logLevel: "metric", - onServerResponse: function (response) { - callback(); - }, - }); - logger.cache.empty(); - - var server = sinon.fakeServer.create(), - metricsOnServer; - server.respondWith("POST", "/api/metrics", function (request) { - metricsOnServer = metricsFromRequestBody(request); - //console.debug( 'requestBody:', request.requestBody ); - //console.debug( 'metricsOnServer:', JSON.stringify( metricsOnServer, null, ' ' ) ); - request.respond( - 200, - { "Content-Type": "application/json" }, - JSON.stringify({ - fakeResponse: "yes", - }) - ); - }); - - logger.emit("metric", "test", [1, 2, { three: 3 }]); - logger._postCache(); - server.respond(); - - assert.ok(callback.calledOnce, "onServerResponse was called"); - assert.equal(logger.cache.length(), 0, "should have emptied cache (on success)"); - assert.equal(logger._postSize, 1000, "_postSize still at default"); - - // metrics were in proper form on server - assert.equal(metricsOnServer.length, 1); - var metric = metricsOnServer[0]; - assert.equal(metric.level, metrics.MetricsLogger.METRIC); - assert.equal(metric.namespace, "client.test"); - assert.equal(metric.args.length, 3); - assert.equal(metric.args[2].three, 3); - assert.ok(typeof metric.time === "string"); - assert.ok(metric.time === new Date(metric.time).toISOString()); - - server.restore(); -}); - -QUnit.test("_postCache failure", function (assert) { - var callback = sinon.spy(), - logger = new metrics.MetricsLogger({ - logLevel: "metric", - onServerResponse: function (response) { - callback(); - }, - }); - logger.cache.empty(); - - var server = sinon.fakeServer.create(); - server.respondWith("POST", "/api/metrics", function (request) { - request.respond( - 500, - { "Content-Type": "application/json" }, - JSON.stringify({ - err_msg: "NoooOPE!", - }) - ); - }); - - logger.emit("metric", "test", [1, 2, { three: 3 }]); - logger._postCache(); - server.respond(); - //TODO: is the following what we want? - assert.ok(!callback.calledOnce, "onServerResponse was NOT called"); - assert.equal(logger.cache.length(), 1, "should NOT have emptied cache"); - assert.equal(logger._postSize, logger.options.maxCacheSize, "_postSize changed to max"); - - server.restore(); -}); - -// ------------------------------------------------------------------------ Emit to console -QUnit.test("emit to console at level", function (assert) { - var mockConsole = new MockConsole(), - logger = new metrics.MetricsLogger({ - consoleLevel: "debug", - consoleLogger: mockConsole, - }); - assert.equal(logger.options.consoleLevel, metrics.MetricsLogger.DEBUG); - assert.equal(logger.consoleLogger.constructor, MockConsole); - - logger.emit("debug", "test", [1, 2, { three: 3 }]); - assert.equal(logger.cache.length(), 1); - //console.debug( JSON.stringify( mockConsole.lastMessage ) ); - assert.equal(mockConsole.lastMessage.level, "debug"); - assert.equal(mockConsole.lastMessage.args.length, 4); - assert.equal(mockConsole.lastMessage.args[0], "test"); - assert.equal(mockConsole.lastMessage.args[3].three, 3); -}); - -QUnit.test("emit to console below does not output", function (assert) { - var mockConsole = new MockConsole(), - logger = new metrics.MetricsLogger({ - consoleLevel: "error", - consoleLogger: mockConsole, - }); - logger.emit("debug", "test", [1, 2, { three: 3 }]); - assert.equal(mockConsole.lastMessage, null); -}); - -// ------------------------------------------------------------------------ Shortcuts -QUnit.test("logger shortcuts emit to default namespace properly", function (assert) { - var logger = new metrics.MetricsLogger({ - logLevel: "all", - }); - logger.cache.empty(); - - assert.equal(logger.options.logLevel, metrics.MetricsLogger.ALL); - logger.log(0); - logger.debug(1); - logger.info(2); - logger.warn(3); - logger.error(4); - logger.metric(5); - - assert.equal(logger.cache.length(), 6); - var cached = logger.cache.remove(6), - entry; - - cached.forEach(function (entry) { - assert.ok(entry.namespace === logger.options.clientPrefix + logger.options.defaultNamespace); - assert.ok(jQuery.type(entry.args) === "array"); - assert.ok(typeof entry.time === "string"); - }); - - // log is different - entry = cached[0]; - assert.ok(entry.level === 1); - assert.ok(entry.args[0] === 0); - - ["debug", "info", "warn", "error", "metric"].forEach(function (level, i) { - entry = cached[i + 1]; - assert.ok(entry.level === logger._parseLevel(level)); - assert.ok(entry.args[0] === i + 1); - }); -}); - -// ======================================================================== LoggingCache -QUnit.test("cache construction/initializiation defaults", function (assert) { - // use empty to prevent tests stepping on one another due to persistence - var cache = new metrics.LoggingCache({ key: "logs-test" }).empty(); - assert.equal(cache.maxSize, 5000); - assert.equal(window.localStorage.getItem("logs-test"), "[]"); -}); - -QUnit.test("cache construction/initializiation failure", function (assert) { - ////TODO: doesn't work - readonly - //window.localStorage = null; - //console.debug( 'localStorage:', window.localStorage ); - var oldFn = metrics.LoggingCache.prototype._hasStorage; - metrics.LoggingCache.prototype._hasStorage = function () { - return false; - }; - assert.throws( - function () { - return new metrics.LoggingCache({ key: "logs-test" }); - }, - /LoggingCache needs localStorage/, - "lack of localStorage throws error" - ); - metrics.LoggingCache.prototype._hasStorage = oldFn; - - assert.throws( - function () { - return new metrics.LoggingCache(); - }, - /LoggingCache needs key for localStorage/, - "lack of key throws error" - ); -}); - -QUnit.test("cache construction/initializiation setting max cache size", function (assert) { - var cache = new metrics.LoggingCache({ - key: "logs-test", - maxSize: 5, - }).empty(); - assert.equal(cache.maxSize, 5); -}); - -QUnit.test("cache plays well with no data", function (assert) { - var cache = new metrics.LoggingCache({ key: "logs-test" }).empty(); - - assert.equal(cache.length(), 0); - var get = cache.get(10); - assert.ok(jQuery.type(get) === "array" && get.length === 0); - var remove = cache.remove(10); - assert.ok(jQuery.type(remove) === "array" && remove.length === 0); - assert.equal(cache.length(), 0); -}); - -QUnit.test("cache add properly adds and removes data", function (assert) { - var cache = new metrics.LoggingCache({ - key: "logs-test", - maxSize: 5, - }).empty(); - - var entry1 = [{ one: 1 }, "two"]; - cache.add(entry1); - - assert.equal(cache.length(), 1); - assert.equal(JSON.stringify(cache.get(1)[0]), JSON.stringify(entry1)); - - var entry2 = { blah: { one: 1 }, bler: ["three", { two: 2 }] }; - cache.add(entry2); - assert.equal(cache.length(), 2); - assert.equal(cache.stringify(2), "[" + JSON.stringify(entry1) + "," + JSON.stringify(entry2) + "]"); - - // FIFO - var returned = cache.remove(1); - assert.equal(cache.length(), 1); - assert.ok(jQuery.type(returned) === "array" && returned.length === 1); - var returned0 = returned[0]; - assert.ok(jQuery.type(returned0) === "array" && JSON.stringify(returned0) === JSON.stringify(entry1)); -}); - -QUnit.test("cache past max loses oldest", function (assert) { - var cache = new metrics.LoggingCache({ - key: "logs-test", - maxSize: 5, - }).empty(); - - for (var i = 0; i < 10; i += 1) { - cache.add({ index: i }); - } - assert.equal(cache.length(), 5); - var get = cache.get(5); - assert.ok(get[0].index === 5); - assert.ok(get[1].index === 6); - assert.ok(get[2].index === 7); - assert.ok(get[3].index === 8); - assert.ok(get[4].index === 9); -}); - -QUnit.test("cache is properly persistent", function (assert) { - var cache1 = new metrics.LoggingCache({ key: "logs-test" }).empty(), - entry = [{ one: 1 }, "two"]; - cache1.add(entry); - assert.equal(cache1.length(), 1); - - var cache2 = new metrics.LoggingCache({ key: "logs-test" }); - assert.equal(cache2.length(), 1, "old key gets previously stored"); - assert.equal(JSON.stringify(cache2.get(1)[0]), JSON.stringify(entry)); - - var cache3 = new metrics.LoggingCache({ key: "logs-bler" }); - assert.equal(cache3.length(), 0, "new key causes new storage"); -}); From fd66b30a9dbcc062295497e3e4da80e60966914f Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 30 Jan 2023 12:24:32 -0500 Subject: [PATCH 05/23] Drop metrics-logger --- client/src/app/base-mvc.js | 34 -- client/src/app/galaxy.js | 108 ---- client/src/app/singleton.js | 4 - client/src/app/user-model.js | 13 +- .../components/Form/Elements/parameters.js | 2 +- .../LibraryFolder/TopToolbar/library-model.js | 2 +- client/src/mvc/ui/ui-select-content.js | 8 +- .../mvc/visualization/chart/views/repeat.js | 4 +- client/src/utils/add-logging.js | 35 -- client/src/utils/deferred.js | 6 +- client/src/utils/metrics-logger.js | 504 ------------------ client/src/utils/metrics-logger.test.js | 354 ------------ client/tests/qunit/test-app.js | 6 - client/tests/qunit/testBundle.js | 1 - .../qunit/tests/galaxy_app_base_tests.js | 8 - 15 files changed, 12 insertions(+), 1077 deletions(-) delete mode 100644 client/src/utils/add-logging.js delete mode 100644 client/src/utils/metrics-logger.js delete mode 100644 client/src/utils/metrics-logger.test.js diff --git a/client/src/app/base-mvc.js b/client/src/app/base-mvc.js index 961a02ff590..597df26c0bc 100644 --- a/client/src/app/base-mvc.js +++ b/client/src/app/base-mvc.js @@ -1,41 +1,8 @@ import $ from "jquery"; import _ from "underscore"; import Backbone from "backbone"; -import addLogging from "utils/add-logging"; import _l from "utils/localization"; -//============================================================================== -/** @class Mixin to add logging capabilities to an object. - * Designed to allow switching an objects log output off/on at one central - * statement. Can be used with plain browser console (or something more - * complex like an AJAX logger). - *
NOTE: currently only uses the console.debug log function - * (as opposed to debug, error, warn, etc.) - * @name LoggableMixin - * - * @example - * // Add to your models/views at the definition using chaining: - * var MyModel = Backbone.Model.extend( LoggableMixin ).extend({ // ... }); - * - * // or - more explicitly AFTER the definition: - * var MyModel = Backbone.Model.extend({ - * logger : console - * // ... - * this.log( '$#%& it! - broken already...' ); - * }) - * _.extend( MyModel.prototype, LoggableMixin ) - * - */ -var LoggableMixin = /** @lends LoggableMixin# */ { - // replace null with console (if available) to see all logs for a particular view/model - /** The logging object whose log function will be used to output - * messages. Null will supress all logging. Commonly set to console. - */ - logger: null, - /** @type {String} a namespace for filtering/focusing log output */ - _logNamespace: ".", -}; -addLogging(LoggableMixin); //============================================================================== /** Backbone model that syncs to the browser's sessionStorage API. @@ -575,7 +542,6 @@ function buildComparator(attribute_name, options) { //============================================================================== export default { - LoggableMixin: LoggableMixin, SessionStorageModel: SessionStorageModel, mixin: mixin, SearchableModelMixin: SearchableModelMixin, diff --git a/client/src/app/galaxy.js b/client/src/app/galaxy.js index 0b506a86abf..a5430f34f0c 100644 --- a/client/src/app/galaxy.js +++ b/client/src/app/galaxy.js @@ -1,10 +1,7 @@ import $ from "jquery"; import _ from "underscore"; import Backbone from "backbone"; -import BASE_MVC from "./base-mvc"; import userModel from "./user-model"; -import metricsLogger from "utils/metrics-logger"; -import addLogging from "utils/add-logging"; import { localize, _setUserLocale, _getUserLocale } from "utils/localization"; import { getGalaxyInstance } from "app"; import { create, dialog } from "utils/data"; @@ -12,7 +9,6 @@ import { create, dialog } from "utils/data"; // ============================================================================ /** Base galaxy client-side application. * Iniitializes: - * logger : the logger/metrics-logger * localize : the string localizer * config : the current configuration (any k/v in * galaxy.ini available from the configuration API) @@ -22,29 +18,9 @@ export function GalaxyApp(options = {}, bootstrapped = {}) { this._init(options, bootstrapped); } -// add logging shortcuts for this object -addLogging(GalaxyApp, "GalaxyApp"); - -// a debug flag can be set via local storage and made available during script/page loading -const DEBUGGING_KEY = "galaxy:debug"; - -const NAMESPACE_KEY = `${DEBUGGING_KEY}:namespaces`; -const FLATTEN_LOG_MESSAGES_KEY = `${DEBUGGING_KEY}:flatten`; - -let localDebugging = false; -try { - localDebugging = localStorage.getItem(DEBUGGING_KEY) == "true"; -} catch (storageErr) { - console.log(localize("localStorage not available for debug flag retrieval")); -} - /** initalize options and sub-components */ GalaxyApp.prototype._init = function (options, bootstrapped) { _.extend(this, Backbone.Events); - if (localDebugging) { - this.logger = console; - console.debug("debugging galaxy:", "options:", options, "bootstrapped:", bootstrapped); - } this._processOptions(options); this._initConfig(options.config || {}); @@ -119,9 +95,6 @@ GalaxyApp.prototype._processOptions = function _processOptions(options) { GalaxyApp.prototype._initConfig = function _initConfig(config) { this.config = config; - // give precendence to localdebugging for this setting - this.config.debug = localDebugging || this.config.debug; - return this; }; @@ -141,41 +114,6 @@ GalaxyApp.prototype._patchGalaxy = function _patchGalaxy(patchWith) { } }; -/** set up the metrics logger (utils/metrics-logger) and pass loggerOptions */ -GalaxyApp.prototype._initLogger = function _initLogger(loggerOptions) { - // default to console logging at the debug level if the debug flag is set - if (this.config.debug) { - loggerOptions.consoleLogger = loggerOptions.consoleLogger || console; - loggerOptions.consoleLevel = loggerOptions.consoleLevel || metricsLogger.MetricsLogger.ALL; - // load any logging namespaces from localStorage if we can - try { - loggerOptions.consoleNamespaceWhitelist = localStorage.getItem(NAMESPACE_KEY).split(","); - } catch (storageErr) { - console.debug(storageErr); - } - try { - loggerOptions.consoleFlattenMessages = localStorage.getItem(FLATTEN_LOG_MESSAGES_KEY) == "true"; - } catch (storageErr) { - console.debug(storageErr); - } - console.log(loggerOptions.consoleFlattenMessages); - } - - this.logger = new metricsLogger.MetricsLogger(loggerOptions); - this.emit = {}; - ["log", "debug", "info", "warn", "error", "metric"].map((i) => { - this.emit[i] = (data) => { - this.logger.emit(i, arguments[0], Array.prototype.slice.call(arguments, 1)); - }; - }); - - if (this.config.debug) { - // add this logger to mvc's loggable mixin so that all models can use the logger - BASE_MVC.LoggableMixin.logger = this.logger; - } - return this; -}; - /** add the localize fn to this object and the window namespace (as '_l') */ GalaxyApp.prototype._initLocale = function _initLocale(options) { this.debug("_initLocale:", options); @@ -222,7 +160,6 @@ GalaxyApp.prototype._initUserLocale = function _initUserLocale(options) { GalaxyApp.prototype._initUser = function _initUser(userJSON) { this.debug("_initUser:", userJSON); this.user = new userModel.User(userJSON); - this.user.logger = this.logger; return this; }; @@ -248,51 +185,6 @@ GalaxyApp.prototype._setUpListeners = function _setUpListeners() { return this; }; -/** Turn debugging/console-output on/off by passing boolean. Pass nothing to get current setting. */ -GalaxyApp.prototype.debugging = function _debugging(setting) { - try { - if (setting === undefined) { - return localStorage.getItem(DEBUGGING_KEY) === "true"; - } - if (setting) { - localStorage.setItem(DEBUGGING_KEY, true); - return true; - } - - localStorage.removeItem(DEBUGGING_KEY); - // also remove all namespaces - this.debuggingNamespaces(null); - } catch (storageErr) { - console.log(localize("localStorage not available for debug flag retrieval")); - } - return false; -}; - -/** Add, remove, or clear namespaces from the debugging filters - * Pass no arguments to retrieve the existing namespaces as an array. - * Pass in null to clear all namespaces (all logging messages will show now). - * Pass in an array of strings or single string of the namespaces to filter to. - * Returns the new/current namespaces as an array; - */ -GalaxyApp.prototype.debuggingNamespaces = function _debuggingNamespaces(namespaces) { - try { - if (namespaces === undefined) { - const csv = localStorage.getItem(NAMESPACE_KEY); - return typeof csv === "string" ? csv.split(",") : []; - } else if (namespaces === null) { - localStorage.removeItem(NAMESPACE_KEY); - } else { - localStorage.setItem(NAMESPACE_KEY, namespaces); - } - const newSettings = this.debuggingNamespaces(); - if (this.logger) { - this.logger.options.consoleNamespaceWhitelist = newSettings; - } - return newSettings; - } catch (storageErr) { - console.log(localize("localStorage not available for debug namespace retrieval")); - } -}; /** string rep */ GalaxyApp.prototype.toString = function toString() { diff --git a/client/src/app/singleton.js b/client/src/app/singleton.js index 2fcb87470b7..fb9f147c428 100644 --- a/client/src/app/singleton.js +++ b/client/src/app/singleton.js @@ -2,7 +2,6 @@ * TODO: Make this part of galaxy.js later, once the global refs are gone */ -import addLogging from "utils/add-logging"; import config from "config"; import { GalaxyApp } from "./galaxy"; import { serverPath } from "utils/serverPath"; @@ -17,9 +16,6 @@ export function setGalaxyInstance(factory) { if (!(newInstance instanceof GalaxyApp)) { newInstance = new GalaxyApp(newInstance); } - if (newInstance.debug === undefined) { - addLogging(newInstance, "GalaxyApp"); - } storage._galaxyInstance = newInstance; diff --git a/client/src/app/user-model.js b/client/src/app/user-model.js index b382f24d841..a9859e4348c 100644 --- a/client/src/app/user-model.js +++ b/client/src/app/user-model.js @@ -3,14 +3,12 @@ import { getAppRoot } from "onload/loadConfig"; import baseMVC from "./base-mvc"; import _l from "utils/localization"; -var logNamespace = "user"; //============================================================================== /** @class Model for a Galaxy user (including anonymous users). * @name User */ -var User = Backbone.Model.extend(baseMVC.LoggableMixin).extend( +var User = Backbone.Model.extend( /** @lends User.prototype */ { - _logNamespace: logNamespace, /** API location for this resource */ urlRoot: function () { @@ -142,15 +140,6 @@ User.getCurrentUserFromApi = (options) => { return currentUser; }; -// (stub) collection for users (shouldn't be common unless admin UI) -//var UserCollection = Backbone.Collection.extend(baseMVC.LoggableMixin).extend({ -// model: User, -// urlRoot: function() { -// return `${getAppRoot()}api/users`; -// } -// //logger : console, -//}); - //============================================================================== export default { User: User, diff --git a/client/src/components/Form/Elements/parameters.js b/client/src/components/Form/Elements/parameters.js index c9fbc13a745..834c27e4d8e 100644 --- a/client/src/components/Form/Elements/parameters.js +++ b/client/src/components/Form/Elements/parameters.js @@ -42,7 +42,7 @@ export default Backbone.View.extend({ this.field = typeof this[fieldClass] === "function" ? this[fieldClass].call(this, input_def) : null; if (!this.field) { this.field = input_def.options ? this._fieldSelect(input_def) : this._fieldText(input_def); - Galaxy.emit.debug("form-parameters::_addRow()", `Auto matched field type (${input_def.type}).`); + console.debug("form-parameters::_addRow()", `Auto matched field type (${input_def.type}).`); } if (input_def.value === undefined) { input_def.value = null; diff --git a/client/src/components/Libraries/LibraryFolder/TopToolbar/library-model.js b/client/src/components/Libraries/LibraryFolder/TopToolbar/library-model.js index e81e344c49c..87b7c00d403 100644 --- a/client/src/components/Libraries/LibraryFolder/TopToolbar/library-model.js +++ b/client/src/components/Libraries/LibraryFolder/TopToolbar/library-model.js @@ -153,7 +153,7 @@ var FolderContainer = Backbone.Model.extend({ var file_item = new Ldda(obj.folder_contents[i]); this.get("folder").add(file_item); } else { - Galaxy.emit.error("Unknown folder item type encountered while parsing response."); + console.error("Unknown folder item type encountered while parsing response."); } } return obj; diff --git a/client/src/mvc/ui/ui-select-content.js b/client/src/mvc/ui/ui-select-content.js index 11de437c075..f99600dd44d 100644 --- a/client/src/mvc/ui/ui-select-content.js +++ b/client/src/mvc/ui/ui-select-content.js @@ -222,7 +222,7 @@ const View = Backbone.View.extend({ /** Return the currently selected dataset values */ value: function (new_value) { - const galaxy = getGalaxyInstance(); + const Galaxy = getGalaxyInstance(); if (new_value) { this._patchValue(new_value); this.model.set("value", new_value); @@ -240,7 +240,7 @@ const View = Backbone.View.extend({ const unpatchedValue = this._unpatchValue(details); result.values.push(unpatchedValue); } else { - galaxy.emit.debug( + console.debug( "ui-select-content::value()", `Requested details not found for '${id_list[i]}'.` ); @@ -252,7 +252,7 @@ const View = Backbone.View.extend({ } } } else { - galaxy.emit.debug("ui-select-content::value()", `Invalid value/source '${new_value}'.`); + console.debug("ui-select-content::value()", `Invalid value/source '${new_value}'.`); } return null; }, @@ -303,7 +303,7 @@ const View = Backbone.View.extend({ this.config = Configurations[config_id]; } else { this.config = Configurations["data"]; - galaxy.emit.debug("ui-select-content::_changeType()", `Invalid configuration/type id '${config_id}'.`); + console.debug("ui-select-content::_changeType()", `Invalid configuration/type id '${config_id}'.`); } // prepare extension component of error message diff --git a/client/src/mvc/visualization/chart/views/repeat.js b/client/src/mvc/visualization/chart/views/repeat.js index 49965337d01..1c48e22d01c 100644 --- a/client/src/mvc/visualization/chart/views/repeat.js +++ b/client/src/mvc/visualization/chart/views/repeat.js @@ -44,7 +44,7 @@ export var View = Backbone.View.extend({ add: function (options) { const Galaxy = getGalaxyInstance(); if (!options.id || this.list[options.id]) { - Galaxy.emit.debug("form-repeat::add()", "Duplicate or invalid repeat block id."); + console.debug("form-repeat::add()", "Duplicate or invalid repeat block id."); return; } var button_delete = new Ui.Button({ @@ -77,7 +77,7 @@ export var View = Backbone.View.extend({ del: function (id) { const Galaxy = getGalaxyInstance(); if (!this.list[id]) { - Galaxy.emit.debug("form-repeat::del()", "Invalid repeat block id."); + console.debug("form-repeat::del()", "Invalid repeat block id."); return; } this.$list.find(`#${id}`).remove(); diff --git a/client/src/utils/add-logging.js b/client/src/utils/add-logging.js deleted file mode 100644 index 9b9f040930d..00000000000 --- a/client/src/utils/add-logging.js +++ /dev/null @@ -1,35 +0,0 @@ -//============================================================================== -var LOGGING_FNS = ["log", "debug", "info", "warn", "error", "metric"]; -/** adds logging functions to an obj.prototype (or obj directly) adding a namespace for filtering - * @param {Object} obj - * @param {String} namespace - */ -function addLogging(obj, namespace) { - var addTo = obj.prototype !== undefined ? obj.prototype : obj; - if (namespace !== undefined) { - addTo._logNamespace = namespace; - } - - // give the object each - LOGGING_FNS.forEach((logFn) => { - addTo[logFn] = function () { - if (!this.logger) { - return undefined; - } - if (this.logger.emit) { - return this.logger.emit(logFn, this._logNamespace, arguments); - } - if (this.logger[logFn]) { - //TODO:! there has to be a way to get the lineno/file into this - // http://stackoverflow.com/questions/13815640/a-proper-wrapper-for-console-log-with-correct-line-number - // http://www.paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ - return this.logger[logFn].apply(this.logger, arguments); - } - return undefined; - }; - }); - return obj; -} - -//============================================================================== -export default addLogging; diff --git a/client/src/utils/deferred.js b/client/src/utils/deferred.js index 7dfe38996a1..7828e5fab0b 100644 --- a/client/src/utils/deferred.js +++ b/client/src/utils/deferred.js @@ -29,7 +29,7 @@ export default Backbone.Model.extend({ process.promise().always(function () { delete self.active[id]; has_deferred && - Galaxy.emit.debug( + console.debug( "deferred::execute()", `${this.state().charAt(0).toUpperCase() + this.state().slice(1)} ${id}` ); @@ -38,7 +38,7 @@ export default Backbone.Model.extend({ // deferred queue $.when(this.last).always(() => { if (self.active[id]) { - has_deferred && Galaxy.emit.debug("deferred::execute()", `Running ${id}`); + has_deferred && console.debug("deferred::execute()", `Running ${id}`); callback(process); !has_deferred && process.resolve(); } else { @@ -52,7 +52,7 @@ export default Backbone.Model.extend({ */ reset: function () { const Galaxy = getGalaxyInstance(); - Galaxy.emit.debug("deferred::execute()", "Reset"); + console.debug("deferred::execute()", "Reset"); for (var i in this.active) { this.active[i] = false; } diff --git a/client/src/utils/metrics-logger.js b/client/src/utils/metrics-logger.js deleted file mode 100644 index ab9f5ea2338..00000000000 --- a/client/src/utils/metrics-logger.js +++ /dev/null @@ -1,504 +0,0 @@ -/*============================================================================= -TODO: - while anon: logs saved to 'logs-null' - this will never post - unless we manually do so at/after login - OR prepend when userId and localStorage has 'logs-null' - wire up _delayPost and test - -=============================================================================*/ -/** @class MetricsLogger - * - * Object to cache, output, and post log/metric messages to the server. - * Meant to be attached to the Galaxy object. - * - * Log from objects by either attaching logger directly: - * panel.logger.metric( 'user dataset deletion', panel.user.id, hda.toJSON() ) - * or using the LoggableMixin or addLogging function: - * MyBackboneModel.extend( LoggableMixin ).extend({ ... }) - * addLogging( MyBackboneModel, 'my-backbone-model' ) - * - * Log from templates by calling directly from Galaxy object: - * Galaxy.logger.metric( 'template loaded', { ownedByUser : true }); - * - * If you attempt to log an un-serializable object (circular reference, window, etc.), - * that entry will not be cached (or sent). If you set consoleLevel and consoleLogger - * appropriately, a warning will be shown when this happens: - * > panel.metric( 'something weird with window', { window : window }) - * !'Metrics logger could not stringify logArguments: ...' - */ -import jQuery from "jquery"; - -function MetricsLogger(options) { - options = options || {}; - var self = this; - - ///** get the current user's id from bootstrapped data or options */ - self.userId = window.bootstrapped && window.bootstrapped.user ? window.bootstrapped.user.id : null; - self.userId = self.userId || options.userId || null; - - /** the (optional) console to emit logs to */ - self.consoleLogger = options.consoleLogger || null; - - self._init(options); - return self; -} - -//----------------------------------------------------------------------------- defaults and constants -// see: python std lib, logging -MetricsLogger.ALL = 0; -MetricsLogger.LOG = 0; -MetricsLogger.DEBUG = 10; -MetricsLogger.INFO = 20; -MetricsLogger.WARN = 30; -MetricsLogger.ERROR = 40; -// metrics levels here? -//MetricsLogger.MinorEvent = 45; -//MetricsLogger.MajorEvent = 50; -MetricsLogger.METRIC = 50; -MetricsLogger.NONE = 100; - -/** default options - override these through the constructor */ -MetricsLogger.defaultOptions = { - /** if an incoming message has a level >= this, it will be cached - can also be a string (e.g. 'debug') */ - logLevel: MetricsLogger.NONE, - /** if an incoming message has a level >= this, it will be output to the console */ - consoleLevel: MetricsLogger.NONE, - /** the default 'namespace' or label associated with an incoming message (if none is passed) */ - defaultNamespace: "Galaxy", - /** the namespaces output to the console (all namespaces will be output if this is falsy) - * note: applies only to the console (not the event/metrics log/cache) - */ - consoleNamespaceWhitelist: null, - /** Force all messages into simple strings. */ - consoleFlattenMessages: false, - /** the prefix attached to client-side logs to distinguish them in the metrics db */ - clientPrefix: "client.", - - /** the maximum number of messages the cache should hold; if exceeded older messages are removed first */ - maxCacheSize: 3000, - /** the number of messages accumulate before posting to the server; should be <= maxCacheSize */ - postSize: 1000, - /** T/F whether to add a timestamp to incoming cached messages */ - addTime: true, - /** string to prefix to userid for cache web storage */ - cacheKeyPrefix: "logs-", - - /** the relative url to post messages to */ - postUrl: "/api/metrics", - /** delay before trying post again after two failures */ - delayPostInMs: 1000 * 60 * 10, - - /** an (optional) function that should return an object; used to send additional data with the metrics */ - getPingData: undefined, - /** an (optional) function that will handle the servers response after successfully posting messages */ - onServerResponse: undefined, -}; - -//----------------------------------------------------------------------------- set up -/** initialize the logger with options, set up instance vars and cache, and add onpageunload to window */ -MetricsLogger.prototype._init = function _init(options) { - var self = this; - self.options = {}; - for (var k in MetricsLogger.defaultOptions) { - if (Object.prototype.hasOwnProperty.call(MetricsLogger.defaultOptions, k)) { - self.options[k] = Object.prototype.hasOwnProperty.call(options, k) - ? options[k] - : MetricsLogger.defaultOptions[k]; - } - } - self.options.logLevel = self._parseLevel(self.options.logLevel); - self.options.consoleLevel = self._parseLevel(self.options.consoleLevel); - //self._emitToConsole( 'debug', 'MetricsLogger', 'MetricsLogger.options:', self.options ); - - /** is the logger currently sending? */ - self._sending = false; - /** the setTimeout id if the logger POST has failed more than once */ - self._waiting = null; - /** the current number of entries to send in a POST */ - self._postSize = self.options.postSize; - - self._initCache(); - - return self; -}; - -/** initialize the cache */ -MetricsLogger.prototype._initCache = function _initCache() { - try { - this.cache = new LoggingCache({ - maxSize: this.options.maxCacheSize, - key: this.options.cacheKeyPrefix + this.userId, - }); - } catch (err) { - this._emitToConsole("warn", "MetricsLogger", ["Could not intitialize logging cache:", err]); - this.options.logLevel = MetricsLogger.NONE; - } -}; - -/** return the numeric log level if level in 'none, debug, log, info, warn, error' */ -MetricsLogger.prototype._parseLevel = function _parseLevel(level) { - var type = typeof level; - if (type === "number") { - return level; - } - if (type === "string") { - var upper = level.toUpperCase(); - if (Object.prototype.hasOwnProperty.call(MetricsLogger, upper)) { - return MetricsLogger[upper]; - } - } - throw new Error(`Unknown log level: ${level}`); -}; - -//----------------------------------------------------------------------------- main entry point -/** record a log/message's arguments to the cache and/or the console based on level and namespace */ -MetricsLogger.prototype.emit = function emit(level, namespace, logArguments) { - //this._emitToConsole( 'debug', 'MetricsLogger', [ 'emit:', level, namespace, logArguments ]); - var self = this; - namespace = namespace || self.options.defaultNamespace; - if (!level || !logArguments) { - return self; - } - // add to cache if proper level - //TODO: respect do not track? - //if( !navigator.doNotTrack && level >= self.options.logLevel ){ - level = self._parseLevel(level); - if (level >= self.options.logLevel) { - self._addToCache(level, namespace, logArguments); - } - // also emit to consoleLogger if proper level for that - if (self.consoleLogger && level >= self.options.consoleLevel) { - self._emitToConsole(level, namespace, logArguments); - } - return self; -}; - -//----------------------------------------------------------------------------- cache -/** add a message to the cache and if messages.length is high enough post them to the server */ -MetricsLogger.prototype._addToCache = function _addToCache(level, namespace, logArguments) { - this._emitToConsole("debug", "MetricsLogger", [ - "_addToCache:", - arguments, - this.options.addTime, - this.cache.length(), - ]); - //this._emitToConsole( 'debug', 'MetricsLogger', [ '\t logArguments:', logArguments ]); - var self = this; - // try add to the cache and if we've got _postSize number of entries, attempt to post them to the server - try { - var newLength = self.cache.add(self._buildEntry(level, namespace, logArguments)); - if (newLength >= self._postSize) { - self._postCache(); - } - // discard entry if an error occurs, but warn if level set to do so - } catch (err) { - self._emitToConsole("warn", "MetricsLogger", [ - "Metrics logger could not stringify logArguments:", - namespace, - logArguments, - ]); - self._emitToConsole("error", "MetricsLogger", [err]); - } - return self; -}; - -/** build a log cache entry object from the given level, namespace, and arguments (optionally adding timestamp */ -MetricsLogger.prototype._buildEntry = function _buildEntry(level, namespace, logArguments) { - this._emitToConsole("debug", "MetricsLogger", ["_buildEntry:", arguments]); - var entry = { - level: level, - namespace: this.options.clientPrefix + namespace, - args: logArguments, - }; - if (this.options.addTime) { - entry.time = new Date().toISOString(); - } - return entry; -}; - -/** post _postSize messages from the cache to the server, removing them if successful - * if the post fails, wait until maxCacheSize is accumulated instead and try again then - * in addition to the messages from the cache ('metrics'), any info from getPingData (if set) will be sent - * onServerResponse will be called (if set) with any response from the server - */ -MetricsLogger.prototype._postCache = function _postCache(options) { - options = options || {}; - this._emitToConsole("info", "MetricsLogger", ["_postCache", options, this._postSize]); - - // short circuit if we're already sending - if (!this.options.postUrl || this._sending) { - return jQuery.when({}); - } - - var self = this; - var postSize = options.count || self._postSize; - - var // do not splice - remove after *successful* post - entries = self.cache.get(postSize); - - var entriesLength = entries.length; - - var // use the optional getPingData to add any extra info we may want to send - postData = typeof self.options.getPingData === "function" ? self.options.getPingData() : {}; - - //console.debug( postSize, entriesLength ); - - // add the metrics and send - postData.metrics = JSON.stringify(entries); - //console.debug( postData.metrics ); - self._sending = true; - return jQuery - .post(self.options.postUrl, postData) - .always(() => { - self._sending = false; - }) - .fail((xhr, status, message) => { - // if we failed the previous time, set the next post target to the max num of entries - self._postSize = self.options.maxCacheSize; - //TODO:?? - // log this failure to explain any gap in metrics - self.emit("error", "MetricsLogger", [ - "_postCache error:", - xhr.readyState, - xhr.status, - xhr.responseJSON || xhr.responseText, - ]); - //TODO: still doesn't solve the problem that when cache == max, post will be tried on every emit - //TODO: see _delayPost - }) - .done((response) => { - if (typeof self.options.onServerResponse === "function") { - self.options.onServerResponse(response); - } - // only remove if post successful - self.cache.remove(entriesLength); - //console.debug( 'removed entries:', entriesLength, 'size now:', self.cache.length() ); - // if we succeeded, reset the post target to the normal num of entries - self._postSize = self.options.postSize; - }); - // return the xhr promise -}; - -/** set _waiting to true and, after delayPostInMs, set it back to false */ -MetricsLogger.prototype._delayPost = function _delayPost() { - //TODO: this won't work between pages - var self = this; - self._waiting = window.setTimeout(() => { - self._waiting = null; - }, self.options.delayPostInMs); -}; - -function usefulToString(arg) { - var asStr = String(arg); - if (asStr == "[object Object]") { - try { - asStr = JSON.stringify(arg); - } catch (e) { - // If arg has cyclic reference, return String - // otherwise rendering stops and we have incomplete page - console.error(e); - return String(arg); - } - } - return asStr; -} - -//----------------------------------------------------------------------------- console -/** output message to console based on level and consoleLogger type */ -MetricsLogger.prototype._emitToConsole = function _emitToConsole(level, namespace, logArguments) { - //console.debug( '_emitToConsole:', level, namespace, logArguments ); - var self = this; - - var whitelist = self.options.consoleNamespaceWhitelist; - if (!self.consoleLogger) { - return self; - } - // if a whitelist for namespaces is set, bail if this namespace is not in the list - if (whitelist && whitelist.indexOf(namespace) === -1) { - return self; - } - - var args = Array.prototype.slice.call(logArguments, 0); - args.unshift(namespace); - if (self.options.consoleFlattenMessages) { - args = [args.map(usefulToString).join(" ")]; - } - //TODO: script location and/or source maps? - //TODO: branch on navigator.userAgent == AIIEEE - it only has log - if (level >= MetricsLogger.METRIC && typeof self.consoleLogger.info === "function") { - return self.consoleLogger.info.apply(self.consoleLogger, args); - } else if (level >= MetricsLogger.ERROR && typeof self.consoleLogger.error === "function") { - return self.consoleLogger.error.apply(self.consoleLogger, args); - } else if (level >= MetricsLogger.WARN && typeof self.consoleLogger.warn === "function") { - self.consoleLogger.warn.apply(self.consoleLogger, args); - } else if (level >= MetricsLogger.INFO && typeof self.consoleLogger.info === "function") { - self.consoleLogger.info.apply(self.consoleLogger, args); - } else if (level >= MetricsLogger.DEBUG && typeof self.consoleLogger.debug === "function") { - self.consoleLogger.debug.apply(self.consoleLogger, args); - } else if (typeof self.consoleLogger.log === "function") { - self.consoleLogger.log.apply(self.consoleLogger, args); - } - return self; -}; - -//----------------------------------------------------------------------------- shortcuts -// generic functions when logging from non-namespaced object (e.g. templates) -/** log to default namespace */ -MetricsLogger.prototype.log = function log() { - this.emit(1, this.options.defaultNamespace, Array.prototype.slice.call(arguments, 0)); -}; - -/** debug to default namespace */ -MetricsLogger.prototype.debug = function debug() { - this.emit(MetricsLogger.DEBUG, this.options.defaultNamespace, Array.prototype.slice.call(arguments, 0)); -}; - -/** info to default namespace */ -MetricsLogger.prototype.info = function info() { - this.emit(MetricsLogger.INFO, this.options.defaultNamespace, Array.prototype.slice.call(arguments, 0)); -}; - -/** warn to default namespace */ -MetricsLogger.prototype.warn = function warn() { - this.emit(MetricsLogger.WARN, this.options.defaultNamespace, Array.prototype.slice.call(arguments, 0)); -}; - -/** error to default namespace */ -MetricsLogger.prototype.error = function error() { - this.emit(MetricsLogger.ERROR, this.options.defaultNamespace, Array.prototype.slice.call(arguments, 0)); -}; - -/** metric to default namespace */ -MetricsLogger.prototype.metric = function metric() { - this.emit(MetricsLogger.METRIC, this.options.defaultNamespace, Array.prototype.slice.call(arguments, 0)); -}; - -/* ============================================================================ -TODO: - need a performance pass - the JSON un/parsing is a bit much - -============================================================================ */ -/** @class LoggingCache - * Simple implementation of cache wrapping an array. - * - * Formats an entry before it's cached and only keeps options.maxSize number - * of entries. Older entries are deleted first. - */ -function LoggingCache(options) { - var self = this; - return self._init(options || {}); -} - -/** default options */ -LoggingCache.defaultOptions = { - /** maximum number of entries to keep before discarding oldest */ - maxSize: 5000, -}; - -/** initialize with options */ -LoggingCache.prototype._init = function _init(options) { - if (!this._hasStorage()) { - //TODO: fall back to local storage - throw new Error("LoggingCache needs localStorage"); - } - if (!options.key) { - throw new Error("LoggingCache needs key for localStorage"); - } - this.key = options.key; - this._initStorage(); - - this.maxSize = options.maxSize || LoggingCache.defaultOptions.maxSize; - return this; -}; - -/** tests for localStorage fns */ -LoggingCache.prototype._hasStorage = function _hasStorage() { - //TODO: modernizr - var test = "test"; - try { - localStorage.setItem(test, test); - localStorage.removeItem(test); - return true; - } catch (e) { - return false; - } -}; - -/** if no localStorage set for key, initialize to empty array */ -LoggingCache.prototype._initStorage = function _initStorage() { - if (localStorage.getItem(this.key) === null) { - return this.empty(); - } - return this; -}; - -/** add an entry to the cache, removing the oldest beforehand if size >= maxSize */ -LoggingCache.prototype.add = function add(entry) { - var self = this; - var _cache = self._fetchAndParse(); - var overage = _cache.length + 1 - self.maxSize; - if (overage > 0) { - _cache.splice(0, overage); - } - _cache.push(entry); - self._unparseAndStore(_cache); - return _cache.length; -}; - -/** get the entries from localStorage and parse them */ -LoggingCache.prototype._fetchAndParse = function _fetchAndParse() { - var self = this; - return JSON.parse(localStorage.getItem(self.key)); -}; - -/** stringify the entries and put them in localStorage */ -LoggingCache.prototype._unparseAndStore = function _unparseAndStore(entries) { - var self = this; - return localStorage.setItem(self.key, JSON.stringify(entries)); -}; - -///** process the entry before caching */ -//LoggingCache.prototype._preprocessEntry = function _preprocessEntry( entry ){ -// return JSON.stringify( entry ); -//}; - -/** return the length --- oh, getters where are you? */ -LoggingCache.prototype.length = function length() { - return this._fetchAndParse().length; -}; - -/** get count number of entries starting with the oldest */ -LoggingCache.prototype.get = function get(count) { - return this._fetchAndParse().slice(0, count); -}; - -/** remove count number of entries starting with the oldest */ -LoggingCache.prototype.remove = function remove(count) { - var _cache = this._fetchAndParse(); - var removed = _cache.splice(0, count); - this._unparseAndStore(_cache); - return removed; -}; - -/** empty/clear the entire cache */ -LoggingCache.prototype.empty = function empty() { - localStorage.setItem(this.key, "[]"); - return this; -}; - -/** stringify count number of entries (but do not remove) */ -LoggingCache.prototype.stringify = function stringify(count) { - return JSON.stringify(this.get(count)); -}; - -/** outputs entire cache to console */ -LoggingCache.prototype.print = function print() { - // popup? (really, carl? a popup?) - easier to copy/paste - console.log(JSON.stringify(this._fetchAndParse(), null, " ")); -}; - -//============================================================================= -export default { - MetricsLogger: MetricsLogger, - LoggingCache: LoggingCache, -}; diff --git a/client/src/utils/metrics-logger.test.js b/client/src/utils/metrics-logger.test.js deleted file mode 100644 index 75608afc9a2..00000000000 --- a/client/src/utils/metrics-logger.test.js +++ /dev/null @@ -1,354 +0,0 @@ -// This file isn't really testing anything useful yet, it is just testing -// (or demonstrating) qunit+backbone interactions. - -import jQuery from "jquery"; -import sinon from "sinon"; -import metrics from "utils/metrics-logger"; - -const MockConsole = function () { - this.lastMessage = null; - ["log", "debug", "info", "warn", "error"].forEach((fnName) => { - this[fnName] = function () { - var args = Array.prototype.slice.call(arguments, 0); - this.lastMessage = { level: fnName, args: args }; - }; - }); - return this; -}; - -describe("metrics-logger tests", function () { - test("logger construction/initializiation defaults", function () { - const logger = new metrics.MetricsLogger({}); - expect(logger.consoleLogger).toEqual(null); - expect(logger.options.logLevel).toEqual(metrics.MetricsLogger.NONE); - expect(logger.options.consoleLevel).toEqual(metrics.MetricsLogger.NONE); - expect(logger.options.defaultNamespace).toEqual("Galaxy"); - expect(logger.options.clientPrefix).toEqual("client."); - expect(logger.options.postSize).toEqual(1000); - expect(logger.options.maxCacheSize).toEqual(3000); - expect(logger.options.addTime).toEqual(true); - expect(logger.options.postUrl).toEqual("/api/metrics"); - expect(logger.options.getPingData).toEqual(undefined); - expect(logger.options.onServerResponse).toEqual(undefined); - - expect(logger._postSize).toEqual(1000); - expect(logger.cache.constructor).toEqual(metrics.LoggingCache); - }); - - test("_parseLevel", function () { - const logger = new metrics.MetricsLogger({}); - expect(logger._parseLevel("all")).toEqual(metrics.MetricsLogger.ALL); - expect(logger._parseLevel("debug")).toEqual(metrics.MetricsLogger.DEBUG); - expect(logger._parseLevel("info")).toEqual(metrics.MetricsLogger.INFO); - expect(logger._parseLevel("warn")).toEqual(metrics.MetricsLogger.WARN); - expect(logger._parseLevel("error")).toEqual(metrics.MetricsLogger.ERROR); - expect(logger._parseLevel("metric")).toEqual(metrics.MetricsLogger.METRIC); - expect(logger._parseLevel("none")).toEqual(metrics.MetricsLogger.NONE); - expect(logger._parseLevel(15)).toEqual(15); - // - expect(() => { - logger._parseLevel(undefined); - }).toThrowError(/Unknown log level/); - expect(() => { - logger._parseLevel("nope"); - }).toThrowError(/Unknown log level/); - }); - - test("emit to cache at level", function () { - const logger = new metrics.MetricsLogger({ - logLevel: "metric", - }); - logger.cache.empty(); - - expect(logger.options.logLevel).toEqual(metrics.MetricsLogger.METRIC); - logger.emit("metric", "test", [1, 2, { three: 3 }]); - expect(logger.cache.length()).toEqual(1); - - const cached = logger.cache.get(1)[0]; - expect(cached.level).toEqual(metrics.MetricsLogger.METRIC); - expect(cached.namespace).toEqual("client.test"); - expect(cached.args.length).toEqual(3); - expect(cached.args[2].three).toEqual(3); - expect(typeof cached.time === "string").toBeTruthy(); - expect(cached.time === new Date(cached.time).toISOString()); - }); - - test("emit to cache below does not cache", function () { - const logger = new metrics.MetricsLogger({ - logLevel: "metric", - }); - logger.cache.empty(); - - logger.emit("error", "test", [1, 2, { three: 3 }]); - expect(logger.cache.length()).toBe(0); - }); - - test("emit to cache (silently) drops non-parsable", function () { - const logger = new metrics.MetricsLogger({ - logLevel: "metric", - }); - logger.cache.empty(); - - logger.emit("metric", "test", [{ window: window }]); - expect(logger.cache.length()).toBe(0); - }); - - // function metricsFromRequestBody(request) { - // // assumes 'metrics' is only entry in requestBody - // return JSON.parse(decodeURIComponent(request.requestBody.replace("metrics=", ""))); - // } - - // QUnit.test("_postCache success", function (assert) { - // var callback = sinon.spy(), - // logger = new metrics.MetricsLogger({ - // logLevel: "metric", - // onServerResponse: function (response) { - // callback(); - // }, - // }); - // logger.cache.empty(); - - // var server = sinon.fakeServer.create(), - // metricsOnServer; - // server.respondWith("POST", "/api/metrics", function (request) { - // metricsOnServer = metricsFromRequestBody(request); - // //console.debug( 'requestBody:', request.requestBody ); - // //console.debug( 'metricsOnServer:', JSON.stringify( metricsOnServer, null, ' ' ) ); - // request.respond( - // 200, - // { "Content-Type": "application/json" }, - // JSON.stringify({ - // fakeResponse: "yes", - // }) - // ); - // }); - - // logger.emit("metric", "test", [1, 2, { three: 3 }]); - // logger._postCache(); - // server.respond(); - - // assert.ok(callback.calledOnce, "onServerResponse was called"); - // assert.equal(logger.cache.length(), 0, "should have emptied cache (on success)"); - // assert.equal(logger._postSize, 1000, "_postSize still at default"); - - // // metrics were in proper form on server - // assert.equal(metricsOnServer.length, 1); - // var metric = metricsOnServer[0]; - // assert.equal(metric.level, metrics.MetricsLogger.METRIC); - // assert.equal(metric.namespace, "client.test"); - // assert.equal(metric.args.length, 3); - // assert.equal(metric.args[2].three, 3); - // assert.ok(typeof metric.time === "string"); - // assert.ok(metric.time === new Date(metric.time).toISOString()); - - // server.restore(); - // }); - - // QUnit.test("_postCache failure", function (assert) { - // var callback = sinon.spy(), - // logger = new metrics.MetricsLogger({ - // logLevel: "metric", - // onServerResponse: function (response) { - // callback(); - // }, - // }); - // logger.cache.empty(); - - // var server = sinon.fakeServer.create(); - // server.respondWith("POST", "/api/metrics", function (request) { - // request.respond( - // 500, - // { "Content-Type": "application/json" }, - // JSON.stringify({ - // err_msg: "NoooOPE!", - // }) - // ); - // }); - - // logger.emit("metric", "test", [1, 2, { three: 3 }]); - // logger._postCache(); - // server.respond(); - // //TODO: is the following what we want? - // assert.ok(!callback.calledOnce, "onServerResponse was NOT called"); - // assert.equal(logger.cache.length(), 1, "should NOT have emptied cache"); - // assert.equal(logger._postSize, logger.options.maxCacheSize, "_postSize changed to max"); - - // server.restore(); - // }); - - // // ------------------------------------------------------------------------ Emit to console - // QUnit.test("emit to console at level", function (assert) { - // var mockConsole = new MockConsole(), - // logger = new metrics.MetricsLogger({ - // consoleLevel: "debug", - // consoleLogger: mockConsole, - // }); - // assert.equal(logger.options.consoleLevel, metrics.MetricsLogger.DEBUG); - // assert.equal(logger.consoleLogger.constructor, MockConsole); - - // logger.emit("debug", "test", [1, 2, { three: 3 }]); - // assert.equal(logger.cache.length(), 1); - // //console.debug( JSON.stringify( mockConsole.lastMessage ) ); - // assert.equal(mockConsole.lastMessage.level, "debug"); - // assert.equal(mockConsole.lastMessage.args.length, 4); - // assert.equal(mockConsole.lastMessage.args[0], "test"); - // assert.equal(mockConsole.lastMessage.args[3].three, 3); - // }); - - // QUnit.test("emit to console below does not output", function (assert) { - // var mockConsole = new MockConsole(), - // logger = new metrics.MetricsLogger({ - // consoleLevel: "error", - // consoleLogger: mockConsole, - // }); - // logger.emit("debug", "test", [1, 2, { three: 3 }]); - // assert.equal(mockConsole.lastMessage, null); - // }); - - // // ------------------------------------------------------------------------ Shortcuts - // QUnit.test("logger shortcuts emit to default namespace properly", function (assert) { - // var logger = new metrics.MetricsLogger({ - // logLevel: "all", - // }); - // logger.cache.empty(); - - // assert.equal(logger.options.logLevel, metrics.MetricsLogger.ALL); - // logger.log(0); - // logger.debug(1); - // logger.info(2); - // logger.warn(3); - // logger.error(4); - // logger.metric(5); - - // assert.equal(logger.cache.length(), 6); - // var cached = logger.cache.remove(6), - // entry; - - // cached.forEach(function (entry) { - // assert.ok(entry.namespace === logger.options.clientPrefix + logger.options.defaultNamespace); - // assert.ok(jQuery.type(entry.args) === "array"); - // assert.ok(typeof entry.time === "string"); - // }); - - // // log is different - // entry = cached[0]; - // assert.ok(entry.level === 1); - // assert.ok(entry.args[0] === 0); - - // ["debug", "info", "warn", "error", "metric"].forEach(function (level, i) { - // entry = cached[i + 1]; - // assert.ok(entry.level === logger._parseLevel(level)); - // assert.ok(entry.args[0] === i + 1); - // }); - // }); - - // // ======================================================================== LoggingCache - // QUnit.test("cache construction/initializiation defaults", function (assert) { - // // use empty to prevent tests stepping on one another due to persistence - // var cache = new metrics.LoggingCache({ key: "logs-test" }).empty(); - // assert.equal(cache.maxSize, 5000); - // assert.equal(window.localStorage.getItem("logs-test"), "[]"); - // }); - - // QUnit.test("cache construction/initializiation failure", function (assert) { - // ////TODO: doesn't work - readonly - // //window.localStorage = null; - // //console.debug( 'localStorage:', window.localStorage ); - // var oldFn = metrics.LoggingCache.prototype._hasStorage; - // metrics.LoggingCache.prototype._hasStorage = function () { - // return false; - // }; - // assert.throws( - // function () { - // return new metrics.LoggingCache({ key: "logs-test" }); - // }, - // /LoggingCache needs localStorage/, - // "lack of localStorage throws error" - // ); - // metrics.LoggingCache.prototype._hasStorage = oldFn; - - // assert.throws( - // function () { - // return new metrics.LoggingCache(); - // }, - // /LoggingCache needs key for localStorage/, - // "lack of key throws error" - // ); - // }); - - // QUnit.test("cache construction/initializiation setting max cache size", function (assert) { - // var cache = new metrics.LoggingCache({ - // key: "logs-test", - // maxSize: 5, - // }).empty(); - // assert.equal(cache.maxSize, 5); - // }); - - // QUnit.test("cache plays well with no data", function (assert) { - // var cache = new metrics.LoggingCache({ key: "logs-test" }).empty(); - - // assert.equal(cache.length(), 0); - // var get = cache.get(10); - // assert.ok(jQuery.type(get) === "array" && get.length === 0); - // var remove = cache.remove(10); - // assert.ok(jQuery.type(remove) === "array" && remove.length === 0); - // assert.equal(cache.length(), 0); - // }); - - test("cache add properly adds and removes data", function () { - const cache = new metrics.LoggingCache({ - key: "logs-test", - maxSize: 5, - }).empty(); - - const entry1 = [{ one: 1 }, "two"]; - cache.add(entry1); - - expect(cache.length()).toBe(1); - expect(JSON.stringify(cache.get(1)[0])).toBe(JSON.stringify(entry1)); - - const entry2 = { blah: { one: 1 }, bler: ["three", { two: 2 }] }; - cache.add(entry2); - expect(cache.length()).toBe(2); - expect(cache.stringify(2)).toBe("[" + JSON.stringify(entry1) + "," + JSON.stringify(entry2) + "]"); - - // FIFO - var returned = cache.remove(1); - assert.equal(cache.length(), 1); - assert.ok(jQuery.type(returned) === "array" && returned.length === 1); - var returned0 = returned[0]; - assert.ok(jQuery.type(returned0) === "array" && JSON.stringify(returned0) === JSON.stringify(entry1)); - }); - - test("cache past max loses oldest", function () { - const cache = new metrics.LoggingCache({ - key: "logs-test", - maxSize: 5, - }).empty(); - - for (let i = 0; i < 10; i += 1) { - cache.add({ index: i }); - } - expect(cache.length()).toBe(5); - const get = cache.get(5); - expect(get[0].index === 5).toBeTruthy(); - expect(get[1].index === 6).toBeTruthy(); - expect(get[2].index === 7).toBeTruthy(); - expect(get[3].index === 8).toBeTruthy(); - expect(get[4].index === 9).toBeTruthy(); - }); - - test("cache is properly persistent", function () { - const cache1 = new metrics.LoggingCache({ key: "logs-test" }).empty(); - const entry = [{ one: 1 }, "two"]; - cache1.add(entry); - expect(cache1.length()).toBe(1); - - const cache2 = new metrics.LoggingCache({ key: "logs-test" }); - expect(cache2.length()).toBe(1); // , "old key gets previously stored"); - expect(JSON.stringify(cache2.get(1)[0])).toBe(JSON.stringify(entry)); - - const cache3 = new metrics.LoggingCache({ key: "logs-bler" }); - expect(cache3.length()).toBe(0); //, "new key causes new storage"); - }); -}); diff --git a/client/tests/qunit/test-app.js b/client/tests/qunit/test-app.js index bf7ebe36332..e5b7fc451f3 100644 --- a/client/tests/qunit/test-app.js +++ b/client/tests/qunit/test-app.js @@ -18,12 +18,6 @@ export function setupTestGalaxy(galaxyOptions_ = null) { galaxy.currHistoryPanel = { model: new Backbone.Model(), }; - galaxy.emit = { - debug: function () {}, - error: function (v) { - window.console.error(v); - }, - }; return galaxy; }); } diff --git a/client/tests/qunit/testBundle.js b/client/tests/qunit/testBundle.js index c10502e1c72..b6cf0381990 100644 --- a/client/tests/qunit/testBundle.js +++ b/client/tests/qunit/testBundle.js @@ -4,6 +4,5 @@ import "./tests/galaxy_app_base_tests"; import "./tests/jquery_plugin_tests"; -import "./tests/metrics_logger_tests"; import "./tests/modal_tests"; import "./tests/ui_tests"; diff --git a/client/tests/qunit/tests/galaxy_app_base_tests.js b/client/tests/qunit/tests/galaxy_app_base_tests.js index 0d18449f594..4e6150cf3e7 100644 --- a/client/tests/qunit/tests/galaxy_app_base_tests.js +++ b/client/tests/qunit/tests/galaxy_app_base_tests.js @@ -35,14 +35,6 @@ QUnit.test("App base extends from Backbone.Events", function (assert) { }); }); -QUnit.test("App base has logging methods from utils/add-logging.js", function (assert) { - var app = getGalaxyInstance(); - ["debug", "info", "warn", "error", "metric"].forEach(function (fn) { - assert.ok(typeof app[fn] === "function"); - }); - assert.ok(app._logNamespace === "GalaxyApp"); -}); - // We no longer want this behavior QUnit.test("App base will patch in attributes from existing Galaxy objects", function (assert) { var existingApp = getGalaxyInstance(); From e6eacfe45b1fdcc2679647a1dced528d8f8af15c Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 30 Jan 2023 13:39:04 -0500 Subject: [PATCH 06/23] Separate optimizations for more control in webpack config. Default dev build to saving ~30% time --- client/webpack.config.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/client/webpack.config.js b/client/webpack.config.js index f9493e6ec7c..78041f49075 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -7,6 +7,7 @@ const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const DuplicatePackageCheckerPlugin = require("@cerner/duplicate-package-checker-webpack-plugin"); const { DumpMetaPlugin } = require("dumpmeta-webpack-plugin"); const TsconfigPathsPlugin = require("tsconfig-paths-webpack-plugin"); +const TerserPlugin = require("terser-webpack-plugin"); const scriptsBase = path.join(__dirname, "src"); const testsBase = path.join(__dirname, "tests"); @@ -32,6 +33,18 @@ module.exports = (env = {}, argv = {}) => { // environment name based on -d, -p, webpack flag const targetEnv = process.env.NODE_ENV == "production" || argv.mode == "production" ? "production" : "development"; + let minimizations = {}; + if (targetEnv == "production") { + minimizations = { + minimize: true, + minimizer: [`...`, new TerserPlugin(), new CssMinimizerPlugin()], + } + } else { + minimizations = { + minimize: false, + } + } + const buildconfig = { mode: targetEnv, entry: { @@ -84,8 +97,7 @@ module.exports = (env = {}, argv = {}) => { }, }, }, - minimize: true, - minimizer: [`...`, new CssMinimizerPlugin()], + ...minimizations, }, module: { rules: [ From dfa64b6a9e7e565c3143c925455d1b41db112bd0 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 30 Jan 2023 13:56:21 -0500 Subject: [PATCH 07/23] Build highest quality sourcemaps when requested, drop console completely for production build. --- client/webpack.config.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/client/webpack.config.js b/client/webpack.config.js index 78041f49075..b749fbe4a7d 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -37,12 +37,21 @@ module.exports = (env = {}, argv = {}) => { if (targetEnv == "production") { minimizations = { minimize: true, - minimizer: [`...`, new TerserPlugin(), new CssMinimizerPlugin()], - } + minimizer: [ + new TerserPlugin({ + terserOptions: { + compress: { + drop_console: true, + }, + }, + }), + new CssMinimizerPlugin(), + ], + }; } else { minimizations = { minimize: false, - } + }; } const buildconfig = { @@ -284,8 +293,8 @@ module.exports = (env = {}, argv = {}) => { }, }; - if (process.env.GXY_BUILD_SOURCEMAPS || buildconfig.mode == "development") { - buildconfig.devtool = "eval-cheap-source-map"; + if (process.env.GXY_BUILD_SOURCEMAPS) { + buildconfig.devtool = "source-map"; } return buildconfig; From e57a45a04c18c10fca07b1d9ffb9cd2b28c86051 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 30 Jan 2023 14:00:14 -0500 Subject: [PATCH 08/23] Swap more debug routing back to console --- client/src/app/base-mvc.js | 4 +-- client/src/app/galaxy.js | 22 +++++++--------- client/src/app/user-model.js | 6 ++--- .../PairedListCollectionCreator.vue | 26 +++++++++---------- .../mvc/visualization/visualization-model.js | 10 ------- client/src/onload/defaultAppFactory.js | 2 +- 6 files changed, 29 insertions(+), 41 deletions(-) diff --git a/client/src/app/base-mvc.js b/client/src/app/base-mvc.js index 597df26c0bc..51c64fec6e6 100644 --- a/client/src/app/base-mvc.js +++ b/client/src/app/base-mvc.js @@ -163,7 +163,7 @@ var SearchableModelMixin = { /** search the attribute with key attrKey for the string searchFor; T/F if found */ searchAttribute: function (attrKey, searchFor) { var attrVal = this.get(attrKey); - //this.debug( 'searchAttribute', attrKey, attrVal, searchFor ); + //console.debug( 'searchAttribute', attrKey, attrVal, searchFor ); // bail if empty searchFor or unsearchable values if (!searchFor || attrVal === undefined || attrVal === null) { return false; @@ -177,7 +177,7 @@ var SearchableModelMixin = { /** deep(er) search for array attributes; T/F if found */ _searchArrayAttribute: function (array, searchFor) { - //this.debug( '_searchArrayAttribute', array, searchFor ); + //console.debug( '_searchArrayAttribute', array, searchFor ); searchFor = searchFor.toLowerCase(); //precondition: searchFor has already been validated as non-empty string //precondition: assumes only 1 level array diff --git a/client/src/app/galaxy.js b/client/src/app/galaxy.js index a5430f34f0c..e963c307e5a 100644 --- a/client/src/app/galaxy.js +++ b/client/src/app/galaxy.js @@ -38,24 +38,22 @@ GalaxyApp.prototype._init = function (options, bootstrapped) { this.params = options.params || {}; this.session_csrf_token = options.session_csrf_token || null; - this._initLogger(this.options.loggerOptions || {}); // at this point, either logging or not and namespaces are enabled - chat it up - this.debug("GalaxyApp.options: ", this.options); - this.debug("GalaxyApp.config: ", this.config); - this.debug("GalaxyApp.logger: ", this.logger); + console.debug("GalaxyApp.options: ", this.options); + console.debug("GalaxyApp.config: ", this.config); this._initLocale(); - this.debug("GalaxyApp.localize: ", this.localize); + console.debug("GalaxyApp.localize: ", this.localize); this.config = options.config || {}; - this.debug("GalaxyApp.config: ", this.config); + console.debug("GalaxyApp.config: ", this.config); this._initUser(options.user || {}); - this.debug("GalaxyApp.user: ", this.user); + console.debug("GalaxyApp.user: ", this.user); _setUserLocale(this.user, this.config); _getUserLocale(); - this.debug("currentLocale: ", sessionStorage.getItem("currentLocale")); + console.debug("currentLocale: ", sessionStorage.getItem("currentLocale")); this._setUpListeners(); this.trigger("ready", this); @@ -102,12 +100,12 @@ GalaxyApp.prototype._initConfig = function _initConfig(config) { GalaxyApp.prototype._patchGalaxy = function _patchGalaxy(patchWith) { // in case req or plain script tag order has created a prev. version of the Galaxy obj... if (this.options.patchExisting && patchWith) { - // this.debug( 'found existing Galaxy object:', patchWith ); + // console.debug( 'found existing Galaxy object:', patchWith ); // ...(for now) monkey patch any added attributes that the previous Galaxy may have had //TODO: move those attributes to more formal assignment in GalaxyApp for (const k in patchWith) { if (Object.prototype.hasOwnProperty.call(patchWith, k)) { - // this.debug( '\t patching in ' + k + ' to Galaxy:', this[ k ] ); + // console.debug( '\t patching in ' + k + ' to Galaxy:', this[ k ] ); this[k] = patchWith[k]; } } @@ -116,7 +114,7 @@ GalaxyApp.prototype._patchGalaxy = function _patchGalaxy(patchWith) { /** add the localize fn to this object and the window namespace (as '_l') */ GalaxyApp.prototype._initLocale = function _initLocale(options) { - this.debug("_initLocale:", options); + console.debug("_initLocale:", options); this.localize = localize; // add to window as global shortened alias // TODO: temporary - remove when can require for plugins @@ -158,7 +156,7 @@ GalaxyApp.prototype._initUserLocale = function _initUserLocale(options) { /** set up the current user as a Backbone model (mvc/user/user-model) */ GalaxyApp.prototype._initUser = function _initUser(userJSON) { - this.debug("_initUser:", userJSON); + console.debug("_initUser:", userJSON); this.user = new userModel.User(userJSON); return this; }; diff --git a/client/src/app/user-model.js b/client/src/app/user-model.js index a9859e4348c..8d6d35dad0c 100644 --- a/client/src/app/user-model.js +++ b/client/src/app/user-model.js @@ -34,13 +34,13 @@ var User = Backbone.Model.extend( * @param {Object} data Initial model data. */ initialize: function (data) { - this.log("User.initialize:", data); + console.log("User.initialize:", data); this.on("loaded", function (model, resp) { - this.log(`${this} has loaded:`, model, resp); + console.log(`${this} has loaded:`, model, resp); }); this.on("change", function (model, data) { - this.log(`${this} has changed:`, model, data.changes); + console.log(`${this} has changed:`, model, data.changes); }); }, diff --git a/client/src/components/Collections/PairedListCollectionCreator.vue b/client/src/components/Collections/PairedListCollectionCreator.vue index a2160206986..9282e15168a 100644 --- a/client/src/components/Collections/PairedListCollectionCreator.vue +++ b/client/src/components/Collections/PairedListCollectionCreator.vue @@ -669,7 +669,7 @@ export default { }, /** sort initial list */ _sortInitialList: function () { - //this.debug( '-- _sortInitialList' ); + //console.debug( '-- _sortInitialList' ); this._sortDatasetList(this.workingElements); }, @@ -813,7 +813,7 @@ export default { } // uncomment to see printlns while running tests - //this.debug = function(){ console.log.apply( console, arguments ); }; + //console.debug = function(){ console.log.apply( console, arguments ); }; // then try the remainder with something less strict strategy = strategy || this.strategy; @@ -909,7 +909,7 @@ export default { }; return function _strategy(params) { - // this.debug("autopair _strategy ---------------------------"); + // console.debug("autopair _strategy ---------------------------"); params = params || {}; var listA = params.listA; var listB = params.listB; @@ -923,8 +923,8 @@ export default { var paired = []; //console.debug( 'params:', JSON.stringify( params, null, ' ' ) ); - // this.debug("starting list lens:", listA.length, listB.length); - // this.debug("bestMatch (starting):", JSON.stringify(bestMatch, null, " ")); + // console.debug("starting list lens:", listA.length, listB.length); + // console.debug("bestMatch (starting):", JSON.stringify(bestMatch, null, " ")); while (indexA < listA.length) { var matchTo = listA[indexA]; @@ -932,8 +932,8 @@ export default { for (indexB = 0; indexB < listB.length; indexB++) { var possible = listB[indexB]; - // this.debug(`${indexA}:${matchTo.name}`); - // this.debug(`${indexB}:${possible.name}`); + // console.debug(`${indexA}:${matchTo.name}`); + // console.debug(`${indexB}:${possible.name}`); // no matching with self if (listA[indexA] !== listB[indexB]) { @@ -946,16 +946,16 @@ export default { bestMatch: bestMatch, }) ); - // this.debug("bestMatch:", JSON.stringify(bestMatch, null, " ")); + // console.debug("bestMatch:", JSON.stringify(bestMatch, null, " ")); if (bestMatch.score === 1.0) { - // this.debug("breaking early due to perfect match"); + // console.debug("breaking early due to perfect match"); break; } } } var scoreThreshold = options.scoreThreshold.call(this); - // this.debug("scoreThreshold:", scoreThreshold); - // this.debug("bestMatch.score:", bestMatch.score); + // console.debug("scoreThreshold:", scoreThreshold); + // console.debug("bestMatch.score:", bestMatch.score); if (bestMatch.score >= scoreThreshold) { //console.debug( 'autoPairFnBuilder.strategy', listA[ indexA ].name, listB[ bestMatch.index ].name ); @@ -975,8 +975,8 @@ export default { return paired; } } - // this.debug("paired:", JSON.stringify(paired, null, " ")); - // this.debug("autopair _strategy ---------------------------"); + // console.debug("paired:", JSON.stringify(paired, null, " ")); + // console.debug("autopair _strategy ---------------------------"); return paired; }; }, diff --git a/client/src/mvc/visualization/visualization-model.js b/client/src/mvc/visualization/visualization-model.js index 4de04f2b102..5f929d0cfc2 100644 --- a/client/src/mvc/visualization/visualization-model.js +++ b/client/src/mvc/visualization/visualization-model.js @@ -10,10 +10,6 @@ import { getAppRoot } from "onload/loadConfig"; */ export var Visualization = Backbone.Model.extend( /** @lends Visualization.prototype */ { - ///** logger used to record this.log messages, commonly set to console */ - //// comment this out to suppress log output - //logger : console, - /** default attributes for a model */ defaults: { config: {}, @@ -29,8 +25,6 @@ export var Visualization = Backbone.Model.extend( * @see Backbone.Model#initialize */ initialize: function (data) { - //this.log( this + '.initialize', data, this.attributes ); - // munge config sub-object here since bbone won't handle defaults with this if (_.isObject(data.config) && _.isObject(this.defaults.config)) { _.defaults(data.config, this.defaults.config); @@ -84,10 +78,6 @@ export var VisualizationCollection = Backbone.Collection.extend( /** @lends VisualizationCollection.prototype */ { model: Visualization, - ///** logger used to record this.log messages, commonly set to console */ - //// comment this out to suppress log output - //logger : console, - url: function () { return `${getAppRoot()}api/visualizations`; }, diff --git a/client/src/onload/defaultAppFactory.js b/client/src/onload/defaultAppFactory.js index c8f3ed3684e..c1ddc68a19a 100644 --- a/client/src/onload/defaultAppFactory.js +++ b/client/src/onload/defaultAppFactory.js @@ -11,7 +11,7 @@ export const defaultAppFactory = (config, label = "Galaxy") => { return setGalaxyInstance((GalaxyApp) => { const { options, bootstrapped } = config; const app = new GalaxyApp(options, bootstrapped); - app.debug(`${label} app`); + console.debug(`${label} app`); return app; }); }; From 6f00fb75a582937f92c2f3e4ca3260515226234c Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 30 Jan 2023 15:18:05 -0500 Subject: [PATCH 09/23] More minor logging cleanup --- client/src/app/base-mvc.js | 1 - client/src/app/galaxy.js | 1 - client/src/app/user-model.js | 2 -- client/src/components/Form/Elements/parameters.js | 1 - .../Libraries/LibraryFolder/TopToolbar/library-model.js | 2 -- client/src/mvc/ui/ui-select-content.js | 1 - client/src/mvc/visualization/chart/views/repeat.js | 3 --- client/src/utils/deferred.js | 3 --- client/src/utils/utils.test.js | 2 +- 9 files changed, 1 insertion(+), 15 deletions(-) diff --git a/client/src/app/base-mvc.js b/client/src/app/base-mvc.js index 51c64fec6e6..c547942779e 100644 --- a/client/src/app/base-mvc.js +++ b/client/src/app/base-mvc.js @@ -3,7 +3,6 @@ import _ from "underscore"; import Backbone from "backbone"; import _l from "utils/localization"; - //============================================================================== /** Backbone model that syncs to the browser's sessionStorage API. * This all largely happens behind the scenes and no special calls are required. diff --git a/client/src/app/galaxy.js b/client/src/app/galaxy.js index e963c307e5a..49ca605333b 100644 --- a/client/src/app/galaxy.js +++ b/client/src/app/galaxy.js @@ -183,7 +183,6 @@ GalaxyApp.prototype._setUpListeners = function _setUpListeners() { return this; }; - /** string rep */ GalaxyApp.prototype.toString = function toString() { const userEmail = this.user ? this.user.get("email") || "(anonymous)" : "uninitialized"; diff --git a/client/src/app/user-model.js b/client/src/app/user-model.js index 8d6d35dad0c..27d5c6ce948 100644 --- a/client/src/app/user-model.js +++ b/client/src/app/user-model.js @@ -1,6 +1,5 @@ import Backbone from "backbone"; import { getAppRoot } from "onload/loadConfig"; -import baseMVC from "./base-mvc"; import _l from "utils/localization"; //============================================================================== @@ -9,7 +8,6 @@ import _l from "utils/localization"; */ var User = Backbone.Model.extend( /** @lends User.prototype */ { - /** API location for this resource */ urlRoot: function () { return `${getAppRoot()}api/users`; diff --git a/client/src/components/Form/Elements/parameters.js b/client/src/components/Form/Elements/parameters.js index 834c27e4d8e..b2b9bfec050 100644 --- a/client/src/components/Form/Elements/parameters.js +++ b/client/src/components/Form/Elements/parameters.js @@ -37,7 +37,6 @@ export default Backbone.View.extend({ /** Returns an input field for a given field type */ create: function (input_def) { - const Galaxy = getGalaxyInstance(); var fieldClass = this.types[input_def.type]; this.field = typeof this[fieldClass] === "function" ? this[fieldClass].call(this, input_def) : null; if (!this.field) { diff --git a/client/src/components/Libraries/LibraryFolder/TopToolbar/library-model.js b/client/src/components/Libraries/LibraryFolder/TopToolbar/library-model.js index 87b7c00d403..3db3e4a2e6e 100644 --- a/client/src/components/Libraries/LibraryFolder/TopToolbar/library-model.js +++ b/client/src/components/Libraries/LibraryFolder/TopToolbar/library-model.js @@ -1,6 +1,5 @@ import Backbone from "backbone"; import { getAppRoot } from "onload/loadConfig"; -import { getGalaxyInstance } from "app"; // ============================================================================ // HELPER FUNCTIONS @@ -140,7 +139,6 @@ var FolderContainer = Backbone.Model.extend({ }, parse: function (obj) { - const Galaxy = getGalaxyInstance(); // empty the collection this.get("folder").reset(); // response is not a simple array, it contains metadata diff --git a/client/src/mvc/ui/ui-select-content.js b/client/src/mvc/ui/ui-select-content.js index f99600dd44d..eebc06d92cc 100644 --- a/client/src/mvc/ui/ui-select-content.js +++ b/client/src/mvc/ui/ui-select-content.js @@ -222,7 +222,6 @@ const View = Backbone.View.extend({ /** Return the currently selected dataset values */ value: function (new_value) { - const Galaxy = getGalaxyInstance(); if (new_value) { this._patchValue(new_value); this.model.set("value", new_value); diff --git a/client/src/mvc/visualization/chart/views/repeat.js b/client/src/mvc/visualization/chart/views/repeat.js index 1c48e22d01c..6d599a63dec 100644 --- a/client/src/mvc/visualization/chart/views/repeat.js +++ b/client/src/mvc/visualization/chart/views/repeat.js @@ -3,7 +3,6 @@ import _l from "utils/localization"; import _ from "underscore"; import $ from "jquery"; import Backbone from "backbone"; -import { getGalaxyInstance } from "app"; import Utils from "utils/utils"; import Portlet from "./portlet"; import Ui from "mvc/ui/ui-misc"; @@ -42,7 +41,6 @@ export var View = Backbone.View.extend({ /** Add new repeat block */ add: function (options) { - const Galaxy = getGalaxyInstance(); if (!options.id || this.list[options.id]) { console.debug("form-repeat::add()", "Duplicate or invalid repeat block id."); return; @@ -75,7 +73,6 @@ export var View = Backbone.View.extend({ /** Delete repeat block */ del: function (id) { - const Galaxy = getGalaxyInstance(); if (!this.list[id]) { console.debug("form-repeat::del()", "Invalid repeat block id."); return; diff --git a/client/src/utils/deferred.js b/client/src/utils/deferred.js index 7828e5fab0b..ae13f556888 100644 --- a/client/src/utils/deferred.js +++ b/client/src/utils/deferred.js @@ -4,7 +4,6 @@ import $ from "jquery"; import Backbone from "backbone"; import Utils from "utils/utils"; -import { getGalaxyInstance } from "app"; export default Backbone.Model.extend({ initialize: function () { @@ -19,7 +18,6 @@ export default Backbone.Model.extend({ var self = this; var id = Utils.uid(); var has_deferred = callback.length > 0; - const Galaxy = getGalaxyInstance(); // register process this.active[id] = true; @@ -51,7 +49,6 @@ export default Backbone.Model.extend({ /** Resets the promise queue. All currently queued but unexecuted callbacks/promises will be rejected. */ reset: function () { - const Galaxy = getGalaxyInstance(); console.debug("deferred::execute()", "Reset"); for (var i in this.active) { this.active[i] = false; diff --git a/client/src/utils/utils.test.js b/client/src/utils/utils.test.js index 1375047df27..eb2d10c9a0b 100644 --- a/client/src/utils/utils.test.js +++ b/client/src/utils/utils.test.js @@ -4,7 +4,7 @@ describe("test utils", () => { it("test isEmpty", async () => { expect(Utils.isEmpty([])).toBe(true); // "Empty array"); expect(Utils.isEmpty(["data", undefined])).toBe(true); // , "Array contains `undefined`"); - expect(Utils.isEmpty(["data", null])).toBe(true);//, "Array contains `null`"); + expect(Utils.isEmpty(["data", null])).toBe(true); //, "Array contains `null`"); expect(Utils.isEmpty(["data", "__null__"])).toBe(true); // , "Array contains `__null__`"); expect(Utils.isEmpty(["data", "__undefined__"])).toBe(true); // , "Array contains `__undefined__`"); expect(Utils.isEmpty(null)).toBe(true); //, "Array is null"); From ce770ed2bd9d7aea7c9f2f2652a0adb69fa8720e Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 30 Jan 2023 16:42:27 -0500 Subject: [PATCH 10/23] Fix app base tests (prior to converting..) --- client/tests/qunit/tests/galaxy_app_base_tests.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/client/tests/qunit/tests/galaxy_app_base_tests.js b/client/tests/qunit/tests/galaxy_app_base_tests.js index 4e6150cf3e7..636bfc9ac5d 100644 --- a/client/tests/qunit/tests/galaxy_app_base_tests.js +++ b/client/tests/qunit/tests/galaxy_app_base_tests.js @@ -14,7 +14,6 @@ QUnit.module("Galaxy client app tests", { QUnit.test("App base construction/initializiation defaults", function (assert) { var app = getGalaxyInstance(); assert.ok(app.hasOwnProperty("options") && typeof app.options === "object"); - assert.ok(app.hasOwnProperty("logger") && typeof app.logger === "object"); assert.ok(app.hasOwnProperty("localize") && typeof app.localize === "function"); assert.ok(app.hasOwnProperty("config") && typeof app.config === "object"); assert.ok(app.hasOwnProperty("user") && typeof app.config === "object"); @@ -47,11 +46,6 @@ QUnit.test("App base will patch in attributes from existing Galaxy objects", fun assert.ok(newApp.foo === 123); }); -QUnit.test("App base logger", function (assert) { - var app = getGalaxyInstance(); - assert.ok(app.hasOwnProperty("logger") && typeof app.config === "object"); -}); - QUnit.test("App base config", function (assert) { var app = getGalaxyInstance(); assert.ok(app.hasOwnProperty("config") && typeof app.config === "object"); From 16df8df2d201fe67f09a911cbb7387564c0c9c3c Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 30 Jan 2023 17:17:49 -0500 Subject: [PATCH 11/23] Drop jquery tests --- client/tests/qunit/tests/jquery_plugin_tests.js | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 client/tests/qunit/tests/jquery_plugin_tests.js diff --git a/client/tests/qunit/tests/jquery_plugin_tests.js b/client/tests/qunit/tests/jquery_plugin_tests.js deleted file mode 100644 index 2d2cc1cc83e..00000000000 --- a/client/tests/qunit/tests/jquery_plugin_tests.js +++ /dev/null @@ -1,17 +0,0 @@ -/* global QUnit */ -import $ from "jquery"; -import testApp from "../test-app"; - -QUnit.module("Galaxy jquery plugin test", { - beforeEach: function () { - testApp.create(); - }, - afterEach: function () { - testApp.destroy(); - }, -}); - -QUnit.test("Check jquery for tooltip and select2", function (assert) { - assert.ok($.fn.tooltip); - assert.ok($.fn.select2); -}); From f947d82203266c8c6ca37f5c3479d3d1de1e4501 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 30 Jan 2023 18:10:50 -0500 Subject: [PATCH 12/23] Migrate base app tests -- mainly for verification, we need to de-backboneize this model --- client/src/app/app.test.js | 88 +++++++++++++++++++ client/tests/jest/__mocks__/localization.js | 12 ++- .../qunit/tests/galaxy_app_base_tests.js | 62 ------------- 3 files changed, 98 insertions(+), 64 deletions(-) create mode 100644 client/src/app/app.test.js delete mode 100644 client/tests/qunit/tests/galaxy_app_base_tests.js diff --git a/client/src/app/app.test.js b/client/src/app/app.test.js new file mode 100644 index 00000000000..a4316426c57 --- /dev/null +++ b/client/src/app/app.test.js @@ -0,0 +1,88 @@ +import { getGalaxyInstance, setGalaxyInstance } from "app"; +import sinon from "sinon"; +import Backbone from "backbone"; +import { getAppRoot } from "onload"; +import galaxyOptions from "@tests/qunit/test-data/bootstrapped"; +import serverdata from "@tests/qunit/test-data/fakeserver"; + +export function setupTestGalaxy(galaxyOptions_ = null) { + galaxyOptions_ = galaxyOptions_ || galaxyOptions; + setGalaxyInstance((GalaxyApp) => { + const galaxy = new GalaxyApp(galaxyOptions_); + galaxy.currHistoryPanel = { + model: new Backbone.Model(), + }; + return galaxy; + }); +} + +describe("App base construction/initializiation defaults", () => { + beforeEach(() => { + setupTestGalaxy(galaxyOptions); + window.WAIT_FADE = 300; + window.fakeserver = sinon.fakeServer.create(); + for (const route in serverdata) { + window.fakeserver.respondWith("GET", getAppRoot() + route, [ + 200, + { "Content-Type": "application/json" }, + serverdata[route].data, + ]); + } + }); + + afterEach(() => { + if (window.fakeserver) { + window.fakeserver.restore(); + delete window.fakeserver; + } + }); + + test("App base construction/initializiation defaults", function () { + const app = getGalaxyInstance(); + expect(app.options && typeof app.options === "object").toBeTruthy(); + expect(app.config && typeof app.config === "object").toBeTruthy(); + expect(app.user && typeof app.config === "object").toBeTruthy(); + expect(app.localize).toBe(window._l); + }); + + test("App base default options", function () { + const app = getGalaxyInstance(); + expect(app.options !== undefined && typeof app.options === "object").toBeTruthy(); + expect(app.options.root).toBe("/"); + expect(app.options.patchExisting).toBe(true); + }); + + test("App base extends from Backbone.Events", function () { + const app = getGalaxyInstance(); + ["on", "off", "trigger", "listenTo", "stopListening"].forEach(function (fn) { + expect(app.fn && typeof app[fn] === "function").toBeTruthy(); + }); + }); + + // // We no longer want this behavior, but leaving the test to express that + test("App base will patch in attributes from existing Galaxy objects", function () { + const existingApp = getGalaxyInstance(); + existingApp.foo = 123; + + const newApp = setGalaxyInstance((GalaxyApp) => { + return new GalaxyApp(); + }); + + expect(newApp.foo === 123).toBeTruthy(); + }); + + test("App base config", function () { + const app = getGalaxyInstance(); + expect(app.config && typeof app.config === "object").toBeTruthy(); + expect(app.config.allow_user_deletion).toBe(false); + expect(app.config.allow_user_creation).toBe(true); + expect(app.config.wiki_url).toBe("https://galaxyproject.org/"); + expect(app.config.ftp_upload_site).toBe(null); + }); + + test("App base user", function () { + const app = getGalaxyInstance(); + expect(app.user !== undefined && typeof app.user === "object").toBeTruthy(); + expect(app.user.isAdmin() === false).toBeTruthy(); + }); +}); diff --git a/client/tests/jest/__mocks__/localization.js b/client/tests/jest/__mocks__/localization.js index c19346b6775..1c834c614b8 100644 --- a/client/tests/jest/__mocks__/localization.js +++ b/client/tests/jest/__mocks__/localization.js @@ -1,3 +1,11 @@ -export default (l) => { +export function _setUserLocale(user, config) { + sessionStorage.setItem("currentLocale", "en"); +} + +export function _getUserLocale(user, config) { + return "en"; +} + +export default function localize(l) { return l; -}; +} diff --git a/client/tests/qunit/tests/galaxy_app_base_tests.js b/client/tests/qunit/tests/galaxy_app_base_tests.js deleted file mode 100644 index 636bfc9ac5d..00000000000 --- a/client/tests/qunit/tests/galaxy_app_base_tests.js +++ /dev/null @@ -1,62 +0,0 @@ -/* global QUnit */ -import testApp from "../test-app"; -import { getGalaxyInstance, setGalaxyInstance } from "app"; - -QUnit.module("Galaxy client app tests", { - beforeEach: function () { - testApp.create(); - }, - afterEach: function () { - testApp.destroy(); - }, -}); - -QUnit.test("App base construction/initializiation defaults", function (assert) { - var app = getGalaxyInstance(); - assert.ok(app.hasOwnProperty("options") && typeof app.options === "object"); - assert.ok(app.hasOwnProperty("localize") && typeof app.localize === "function"); - assert.ok(app.hasOwnProperty("config") && typeof app.config === "object"); - assert.ok(app.hasOwnProperty("user") && typeof app.config === "object"); - assert.equal(app.localize, window._l); -}); - -QUnit.test("App base default options", function (assert) { - var app = getGalaxyInstance(); - assert.ok(app.hasOwnProperty("options") && typeof app.options === "object"); - assert.equal(app.options.root, "/"); - assert.equal(app.options.patchExisting, true); -}); - -QUnit.test("App base extends from Backbone.Events", function (assert) { - var app = getGalaxyInstance(); - ["on", "off", "trigger", "listenTo", "stopListening"].forEach(function (fn) { - assert.ok(app.hasOwnProperty(fn) && typeof app[fn] === "function"); - }); -}); - -// We no longer want this behavior -QUnit.test("App base will patch in attributes from existing Galaxy objects", function (assert) { - var existingApp = getGalaxyInstance(); - existingApp.foo = 123; - - var newApp = setGalaxyInstance((GalaxyApp) => { - return new GalaxyApp(); - }); - - assert.ok(newApp.foo === 123); -}); - -QUnit.test("App base config", function (assert) { - var app = getGalaxyInstance(); - assert.ok(app.hasOwnProperty("config") && typeof app.config === "object"); - assert.equal(app.config.allow_user_deletion, false); - assert.equal(app.config.allow_user_creation, true); - assert.equal(app.config.wiki_url, "https://galaxyproject.org/"); - assert.equal(app.config.ftp_upload_site, null); -}); - -QUnit.test("App base user", function (assert) { - var app = getGalaxyInstance(); - assert.ok(app.hasOwnProperty("user") && typeof app.user === "object"); - assert.ok(app.user.isAdmin() === false); -}); From bfc33739d6f2d860f78b28346b1aff07e920f8b1 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Mon, 30 Jan 2023 18:51:14 -0500 Subject: [PATCH 13/23] Update qunit test bundle --- client/tests/qunit/testBundle.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/tests/qunit/testBundle.js b/client/tests/qunit/testBundle.js index b6cf0381990..278d8c23d30 100644 --- a/client/tests/qunit/testBundle.js +++ b/client/tests/qunit/testBundle.js @@ -2,7 +2,5 @@ //var testsContext = require.context(".", true, /_tests$/); //testsContext.keys().forEach(testsContext); -import "./tests/galaxy_app_base_tests"; -import "./tests/jquery_plugin_tests"; import "./tests/modal_tests"; import "./tests/ui_tests"; From 35d0da9a19f064c18926b86cceff8459bc1b352a Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 31 Jan 2023 14:09:49 -0500 Subject: [PATCH 14/23] Fix up app.test.js; drop some unnecessary and chatty logs --- client/src/app/app.test.js | 21 +-------------------- client/src/app/galaxy.js | 10 +--------- client/src/app/user-model.js | 2 -- 3 files changed, 2 insertions(+), 31 deletions(-) diff --git a/client/src/app/app.test.js b/client/src/app/app.test.js index a4316426c57..e7b8d747046 100644 --- a/client/src/app/app.test.js +++ b/client/src/app/app.test.js @@ -1,9 +1,6 @@ import { getGalaxyInstance, setGalaxyInstance } from "app"; -import sinon from "sinon"; import Backbone from "backbone"; -import { getAppRoot } from "onload"; import galaxyOptions from "@tests/qunit/test-data/bootstrapped"; -import serverdata from "@tests/qunit/test-data/fakeserver"; export function setupTestGalaxy(galaxyOptions_ = null) { galaxyOptions_ = galaxyOptions_ || galaxyOptions; @@ -19,22 +16,6 @@ export function setupTestGalaxy(galaxyOptions_ = null) { describe("App base construction/initializiation defaults", () => { beforeEach(() => { setupTestGalaxy(galaxyOptions); - window.WAIT_FADE = 300; - window.fakeserver = sinon.fakeServer.create(); - for (const route in serverdata) { - window.fakeserver.respondWith("GET", getAppRoot() + route, [ - 200, - { "Content-Type": "application/json" }, - serverdata[route].data, - ]); - } - }); - - afterEach(() => { - if (window.fakeserver) { - window.fakeserver.restore(); - delete window.fakeserver; - } }); test("App base construction/initializiation defaults", function () { @@ -55,7 +36,7 @@ describe("App base construction/initializiation defaults", () => { test("App base extends from Backbone.Events", function () { const app = getGalaxyInstance(); ["on", "off", "trigger", "listenTo", "stopListening"].forEach(function (fn) { - expect(app.fn && typeof app[fn] === "function").toBeTruthy(); + expect(Object.prototype.hasOwnProperty.call(app, fn) && typeof app[fn] === "function").toBeTruthy(); }); }); diff --git a/client/src/app/galaxy.js b/client/src/app/galaxy.js index 49ca605333b..e84180f6de5 100644 --- a/client/src/app/galaxy.js +++ b/client/src/app/galaxy.js @@ -38,18 +38,11 @@ GalaxyApp.prototype._init = function (options, bootstrapped) { this.params = options.params || {}; this.session_csrf_token = options.session_csrf_token || null; - // at this point, either logging or not and namespaces are enabled - chat it up - console.debug("GalaxyApp.options: ", this.options); - console.debug("GalaxyApp.config: ", this.config); - this._initLocale(); - console.debug("GalaxyApp.localize: ", this.localize); this.config = options.config || {}; - console.debug("GalaxyApp.config: ", this.config); this._initUser(options.user || {}); - console.debug("GalaxyApp.user: ", this.user); _setUserLocale(this.user, this.config); _getUserLocale(); @@ -113,8 +106,7 @@ GalaxyApp.prototype._patchGalaxy = function _patchGalaxy(patchWith) { }; /** add the localize fn to this object and the window namespace (as '_l') */ -GalaxyApp.prototype._initLocale = function _initLocale(options) { - console.debug("_initLocale:", options); +GalaxyApp.prototype._initLocale = function _initLocale() { this.localize = localize; // add to window as global shortened alias // TODO: temporary - remove when can require for plugins diff --git a/client/src/app/user-model.js b/client/src/app/user-model.js index 27d5c6ce948..f4d6de32684 100644 --- a/client/src/app/user-model.js +++ b/client/src/app/user-model.js @@ -32,8 +32,6 @@ var User = Backbone.Model.extend( * @param {Object} data Initial model data. */ initialize: function (data) { - console.log("User.initialize:", data); - this.on("loaded", function (model, resp) { console.log(`${this} has loaded:`, model, resp); }); From ecf07d4bd67d6f2a6aeab26036c8d1aa4ade97a5 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Fri, 3 Feb 2023 21:53:00 -0500 Subject: [PATCH 15/23] Bump caniuse-lite --- client/yarn.lock | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/client/yarn.lock b/client/yarn.lock index b510fcdc084..117a021091b 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -3645,15 +3645,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001304, caniuse-lite@^1.0.30001370: - version "1.0.30001375" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz" - integrity sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw== - -caniuse-lite@^1.0.30001400: - version "1.0.30001439" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz#ab7371faeb4adff4b74dad1718a6fd122e45d9cb" - integrity sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001304, caniuse-lite@^1.0.30001370, caniuse-lite@^1.0.30001400: + version "1.0.30001450" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001450.tgz" + integrity sha512-qMBmvmQmFXaSxexkjjfMvD5rnDL0+m+dUMZKoDYsGG8iZN29RuYh9eRoMvKsT6uMAWlyUUGDEQGJJYjzCIO9ew== caniuse-lite@^1.0.30001426: version "1.0.30001451" From 69b5d043f8e85d377f096bcc4cafef92f73d0b90 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 7 Feb 2023 10:24:40 -0500 Subject: [PATCH 16/23] Drop/move remaining qunit tests and materials --- client/tests/karma/karma.config.base.js | 35 - client/tests/karma/karma.config.qunit.js | 30 - client/tests/karma/webpack.config.qunit.js | 46 - client/tests/karma/webpack.config.unittest.js | 35 - client/tests/qunit/test-app.js | 45 - client/tests/qunit/testBundle.js | 6 - client/tests/qunit/tests/modal_tests.js | 86 -- client/tests/qunit/tests/ui_tests.js | 896 ------------------ .../{qunit => }/test-data/bootstrapped.js | 0 .../tests/{qunit => }/test-data/fakeserver.js | 0 .../tests/{qunit => }/test-data/job-dag-1.js | 0 .../{qunit => }/test-data/json/datatypes.json | 0 .../test-data/json/datatypes.mapping.json | 0 .../paired-collection-creator.data.js | 0 14 files changed, 1179 deletions(-) delete mode 100644 client/tests/karma/karma.config.base.js delete mode 100644 client/tests/karma/karma.config.qunit.js delete mode 100644 client/tests/karma/webpack.config.qunit.js delete mode 100644 client/tests/karma/webpack.config.unittest.js delete mode 100644 client/tests/qunit/test-app.js delete mode 100644 client/tests/qunit/testBundle.js delete mode 100644 client/tests/qunit/tests/modal_tests.js delete mode 100644 client/tests/qunit/tests/ui_tests.js rename client/tests/{qunit => }/test-data/bootstrapped.js (100%) rename client/tests/{qunit => }/test-data/fakeserver.js (100%) rename client/tests/{qunit => }/test-data/job-dag-1.js (100%) rename client/tests/{qunit => }/test-data/json/datatypes.json (100%) rename client/tests/{qunit => }/test-data/json/datatypes.mapping.json (100%) rename client/tests/{qunit => }/test-data/paired-collection-creator.data.js (100%) diff --git a/client/tests/karma/karma.config.base.js b/client/tests/karma/karma.config.base.js deleted file mode 100644 index 96e1427b8a2..00000000000 --- a/client/tests/karma/karma.config.base.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Base karma config, assumes chrome headless and webpack - */ - -const webpackConfigFunc = require("./webpack.config.unittest"); - -module.exports = (config) => { - // Karma does not execute the webpack config function by default - const webpackConfig = webpackConfigFunc( - {}, - { - mode: "testing", - } - ); - - return { - basePath: "../../src", - browsers: ["ChromeHeadlessNoSandbox"], - client: { - captureConsole: true, - }, - failOnEmptyTestSuite: false, - singleRun: true, - webpack: webpackConfig, - webpackMiddleware: { - noInfo: true, - }, - customLaunchers: { - ChromeHeadlessNoSandbox: { - base: "ChromeHeadless", - flags: ["--no-sandbox"], - }, - }, - }; -}; diff --git a/client/tests/karma/karma.config.qunit.js b/client/tests/karma/karma.config.qunit.js deleted file mode 100644 index 2ad9bac073d..00000000000 --- a/client/tests/karma/karma.config.qunit.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Qunit Tests - * - * These legacy QUnit tests are interdependent and only execute as a single - * package. A flaw in the previous node environment parameter-checking in the - * old karma configs gave the illusion that they ran individually, but they do - * not and probably never have. Keeping them around for now, but we should get - * rid of them by rewriting the tests or preferrably by removing the code to - * which they apply. - */ - -const baseKarmaConfig = require("./karma.config.base"); -const testFiles = ["polyfills.js", "../tests/qunit/testBundle.js"]; -const preprocessors = testFiles.reduce((result, path) => { - result[path] = ["webpack"]; - return result; -}, {}); - -module.exports = function (config) { - const baseConfig = baseKarmaConfig(config); - - const settings = Object.assign({}, baseConfig, { - files: testFiles, - preprocessors: preprocessors, - frameworks: ["polyfill", "qunit"], - singleRun: true, - }); - - config.set(settings); -}; diff --git a/client/tests/karma/webpack.config.qunit.js b/client/tests/karma/webpack.config.qunit.js deleted file mode 100644 index 49e559ea620..00000000000 --- a/client/tests/karma/webpack.config.qunit.js +++ /dev/null @@ -1,46 +0,0 @@ -/** - * So the legacy QUnit tests have to perform some gymastics that should not be - * required because thse tests apparently care about assets like the CSS and images. - * TODO: Replace these tests with like... actual unit tests. - */ - -const baseConfig = require("../../webpack.config"); - -module.exports = (env, argv) => { - // load base config - const wpConfig = baseConfig(env, argv); - wpConfig.mode = "production"; - wpConfig.entry = () => ({}); - - const switchPlugin = (reMatcher, replacement) => { - const scrubLoader = (obj) => - typeof obj == "string" && obj.match(reMatcher) ? Object.assign({}, replacement) : obj; - - return (rule) => { - if (rule.use && rule.use.length) { - rule.use = rule.use.map(scrubLoader); - } - return rule; - }; - }; - - // Remove MiniCssExtractPlugin loader references - // replace mini-css-extract plugin with basic style loader - // Honestly I'm not clear on why we process css at all for unit tests - const scrubLoaderRules = (module) => { - const rePluginMatch = /mini-css-extract-plugin/; - const styleLoader = { loader: "style-loader" }; - const processor = switchPlugin(rePluginMatch, styleLoader); - module.rules = module.rules.map(processor); - }; - - // Remove mini-css-extract loader references - scrubLoaderRules(wpConfig.module); - - // remove MiniCSSExtract Plugin - wpConfig.plugins = wpConfig.plugins.filter((p) => { - return p.constructor.name != "MiniCssExtractPlugin"; - }); - - return wpConfig; -}; diff --git a/client/tests/karma/webpack.config.unittest.js b/client/tests/karma/webpack.config.unittest.js deleted file mode 100644 index e08877fcb18..00000000000 --- a/client/tests/karma/webpack.config.unittest.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Unit tests should not require css or images, so replace all those things with - * the ignore-loader for speedier testing. - */ - -const merge = require("webpack-merge"); -const baseConfig = require("../../webpack.config"); - -module.exports = (env, argv) => { - const wpConfig = baseConfig(env, argv); - wpConfig.mode = "development"; - wpConfig.entry = () => ({}); - - // Don't need any assets for unit testing - const ignoreAssetLoaders = { - rules: [ - { - test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)(\?.*$|$)/, - loader: "ignore-loader", - }, - { - test: /\.css$/, - loader: "ignore-loader", - }, - { - test: /\.scss$/, - loader: "ignore-loader", - }, - ], - }; - - wpConfig.module = merge.merge(wpConfig.module, ignoreAssetLoaders); - - return wpConfig; -}; diff --git a/client/tests/qunit/test-app.js b/client/tests/qunit/test-app.js deleted file mode 100644 index e5b7fc451f3..00000000000 --- a/client/tests/qunit/test-app.js +++ /dev/null @@ -1,45 +0,0 @@ -/** Creates a generic/global Galaxy environment, loads shared libraries and a - * fake server */ - -// jQuery.fn.tooltip lives on one of these 2 libs -import "bootstrap"; - -import sinon from "sinon"; -import Backbone from "backbone"; -import { setGalaxyInstance } from "app"; -import { getAppRoot } from "onload"; -import galaxyOptions from "./test-data/bootstrapped"; -import serverdata from "./test-data/fakeserver"; - -export function setupTestGalaxy(galaxyOptions_ = null) { - galaxyOptions_ = galaxyOptions_ || galaxyOptions; - setGalaxyInstance((GalaxyApp) => { - const galaxy = new GalaxyApp(galaxyOptions_); - galaxy.currHistoryPanel = { - model: new Backbone.Model(), - }; - return galaxy; - }); -} - -export default { - create() { - setupTestGalaxy(galaxyOptions); - window.WAIT_FADE = 300; - window.fakeserver = sinon.fakeServer.create(); - for (var route in serverdata) { - window.fakeserver.respondWith("GET", getAppRoot() + route, [ - 200, - { "Content-Type": "application/json" }, - serverdata[route].data, - ]); - } - }, - - destroy() { - if (window.fakeserver) { - window.fakeserver.restore(); - delete window.fakeserver; - } - }, -}; diff --git a/client/tests/qunit/testBundle.js b/client/tests/qunit/testBundle.js deleted file mode 100644 index 278d8c23d30..00000000000 --- a/client/tests/qunit/testBundle.js +++ /dev/null @@ -1,6 +0,0 @@ -// Load all qunit tests into a single bundle. -//var testsContext = require.context(".", true, /_tests$/); -//testsContext.keys().forEach(testsContext); - -import "./tests/modal_tests"; -import "./tests/ui_tests"; diff --git a/client/tests/qunit/tests/modal_tests.js b/client/tests/qunit/tests/modal_tests.js deleted file mode 100644 index b7d3ed7ed90..00000000000 --- a/client/tests/qunit/tests/modal_tests.js +++ /dev/null @@ -1,86 +0,0 @@ -/* global QUnit */ -import $ from "jquery"; -import testApp from "../test-app"; -import GalaxyModal from "mvc/ui/ui-modal"; - -QUnit.module("Modal dialog test", { - beforeEach: function () { - $.fx.off = true; - testApp.create(); - var self = this; - this.app = new GalaxyModal.View({ - title: "Test title", - body: "Test body", - buttons: { - Ok: function () {}, - Cancel: function () { - self.app.hide(); - }, - }, - }); - }, - afterEach: function () { - $.fx.off = false; - testApp.destroy(); - this.app.$el.remove(); - }, -}); - -QUnit.test("test dialog attributes", function (assert) { - assert.ok(this.app.$header.find(".title").html() == "Test title", "Modal header has correct title."); - assert.ok(this.app.$body.html() == "Test body", "Modal header has correct body."); -}); - -QUnit.test("test dialog visibility", function (assert) { - this.app.hide(); - assert.ok(this.app.$el.css("display") == "none", "Modal hidden manually"); - this.app.show(); - assert.ok(this.app.$el.css("display") == "block", "Modal shown manually"); - this.app.getButton("Ok").trigger("click"); - assert.ok(this.app.$el.css("display") == "block", "Modal still visible after clicking Ok"); - this.app.getButton("Cancel").trigger("click"); - assert.ok(this.app.$el.css("display") == "none", "Modal hidden after clicking Cancel"); - this.app.show(); - assert.ok(this.app.$el.css("display") == "block", "Modal manually shown again"); - assert.ok(!this.app.$header.hasClass("no-separator"), "Title separator tagged as visible."); - this.app.show({ title_separator: false }); - assert.ok(this.app.$header.hasClass("no-separator"), "Title separator tagged as hidden."); - assert.ok(this.app.$backdrop.hasClass("in"), "Backdrop tagged as shown."); - this.app.show({ backdrop: false }); - assert.ok(!this.app.$backdrop.hasClass("in"), "Backdrop tagged as hidden."); -}); - -QUnit.test("test dialog closing events", function (assert) { - this.app.show(); - this.app.$backdrop.trigger("click"); - assert.ok(this.app.$el.css("display") == "block", "Modal shown after backdrop click"); - this.app.hide(); - this.app.show({ closing_events: true }); - assert.ok(this.app.$el.css("display") == "block", "Modal shown with closing events"); - this.app.$backdrop.trigger("click"); - assert.ok(this.app.$el.css("display") == "none", "Modal hidden after backdrop click"); -}); - -QUnit.test("test dialog rendering", function (assert) { - var before = this.app.$el.html(); - this.app.render(); - assert.ok(before == this.app.$el.html(), "Re-rendering successful"); - this.app.options.title = "New Title"; - this.app.render(); - assert.ok(this.app.$header.find(".title").html() == "New Title", "Modal header has correct new title."); -}); - -QUnit.test("test button states", function (assert) { - assert.ok(this.app.getButton("Ok").html() === "Ok", "Ok has correct label"); - assert.ok(!this.app.getButton("Ok").prop("disabled"), "Ok is active"); - assert.ok(!this.app.getButton("Cancel").prop("disabled"), "Cancel is active"); - this.app.disableButton("Ok"); - assert.ok(this.app.getButton("Ok").prop("disabled"), "Ok is disabled"); - assert.ok(!this.app.getButton("Cancel").prop("disabled"), "Cancel is still active"); - this.app.disableButton("Cancel"); - assert.ok(this.app.getButton("Cancel").prop("disabled"), "Cancel is also disabled"); - assert.ok(this.app.getButton("Ok").prop("disabled"), "Ok is still disabled"); - this.app.enableButton("Ok"); - assert.ok(this.app.getButton("Cancel").prop("disabled"), "Cancel is still disabled"); - assert.ok(!this.app.getButton("Ok").prop("disabled"), "Ok is active again"); -}); diff --git a/client/tests/qunit/tests/ui_tests.js b/client/tests/qunit/tests/ui_tests.js deleted file mode 100644 index 92905554f21..00000000000 --- a/client/tests/qunit/tests/ui_tests.js +++ /dev/null @@ -1,896 +0,0 @@ -/* global QUnit */ -import $ from "jquery"; -import testApp from "../test-app"; -import sinon from "sinon"; -import Ui from "mvc/ui/ui-misc"; -import SelectContent from "mvc/ui/ui-select-content"; -import Drilldown from "mvc/ui/ui-drilldown"; -import Tabs from "mvc/ui/ui-tabs"; - -QUnit.module("Ui test", { - beforeEach: function () { - testApp.create(); - this.clock = sinon.useFakeTimers(); - }, - afterEach: function () { - testApp.destroy(); - this.clock.restore(); - }, -}); - -QUnit.test("tabs", function (assert) { - assert.ok($.fn); - var self = this; - var tabs = new Tabs.View({}); - var collection = tabs.collection; - collection.add({ id: "id_a", title: "title_a", icon: "icon_a", $el: "el_a" }); - var _test = function () { - self.clock.tick(window.WAIT_FADE); - collection.each(function (model, index) { - var $tab_element = tabs.$("#tab-" + model.id + " .nav-link"); - var $tab_content = tabs.$("#" + model.id); - var is_current = model.id == tabs.model.get("current"); - assert.ok($tab_content.hasClass("active") == is_current, "Active state of content."); - assert.ok($tab_element.hasClass("active") == is_current, "Active state of element."); - }); - }; - $("body").prepend(tabs.$el); - _test(); - collection.add({ id: "id_b", title: "title_b", icon: "icon_b", $el: "el_b" }); - _test(); - tabs.collection.get("id_b").set("hidden", true); - _test(); - collection.add({ id: "id_c", title: "title_c", icon: "icon_c", $el: "el_c" }); - tabs.model.set("current", "id_c"); - _test(); - tabs.collection.get("id_b").set("hidden", false); - _test(); - tabs.model.set("current", "id_b"); - _test(); - tabs.model.set("visible", false); - tabs.collection.reset(); - self.clock.tick(window.WAIT_FADE); - assert.ok(tabs.$el.css("display", "none"), "Everything hidden."); - tabs.model.set("visible", true); - self.clock.tick(window.WAIT_FADE); - assert.ok(tabs.$el.css("display", "block"), "Everything shown."); - collection.add({ id: "id_c", title: "title_c", icon: "icon_c", $el: "el_c" }); - tabs.model.set("current", "id_c"); - _test(); -}); - -QUnit.test("button-default", function (assert) { - var button = new Ui.Button({ title: "title" }); - var model = button.model; - $("body").prepend(button.$el); - assert.ok(button.$title.html() == "title", "Has correct title"); - model.set("title", "_title"); - assert.ok(button.$title.html() == "_title", "Has correct new title"); - assert.ok(!button.$el.attr("disabled"), "Button active"); - model.set("disabled", true); - assert.ok(button.$el.attr("disabled"), "Button disabled"); - model.set("disabled", false); - assert.ok(!button.$el.attr("disabled"), "Button active, again"); - model.set("wait", true); - assert.ok(button.$title.html() == model.get("wait_text"), "Shows correct wait text"); - model.set("wait_text", "wait_text"); - assert.ok(button.$title.html() == "wait_text", "Shows correct new wait text"); - model.set("wait", false); - assert.ok(button.$title.html() == model.get("title"), "Shows correct regular title"); -}); - -QUnit.test("button-default", function (assert) { - var button = new Ui.Button({ title: "title" }); - var model = button.model; - $("body").prepend(button.$el); - assert.ok(button.$title.html() == "title", "Has correct title"); - model.set("title", "_title"); - assert.ok(button.$title.html() == "_title", "Has correct new title"); - assert.ok(!button.$el.attr("disabled"), "Button active"); - model.set("disabled", true); - assert.ok(button.$el.attr("disabled"), "Button disabled"); - model.set("disabled", false); - assert.ok(!button.$el.attr("disabled"), "Button active, again"); - model.set("wait", true); - assert.ok(button.$title.html() == model.get("wait_text"), "Shows correct wait text"); - model.set("wait_text", "wait_text"); - assert.ok(button.$title.html() == "wait_text", "Shows correct new wait text"); - model.set("wait", false); - assert.ok(button.$title.html() == model.get("title"), "Shows correct regular title"); -}); - -QUnit.test("button-check", function (assert) { - var button = new Ui.ButtonCheck({ title: "title" }); - var model = button.model; - $("body").prepend(button.$el); - assert.ok(button.$title.html() == "title", "Has correct title"); - model.set("title", "_title"); - assert.ok(button.$title.html() == "_title", "Has correct new title"); - assert.ok(button.$icon.hasClass(button.model.get("icons")[0]), "Has correct " + model.get("value") + " value"); - button.value(1); - assert.ok(button.$icon.hasClass(button.model.get("icons")[1]), "Has correct " + model.get("value") + " value"); - button.value(2); - assert.ok(button.$icon.hasClass(button.model.get("icons")[2]), "Has correct " + model.get("value") + " value"); - button.value(0, 100); - assert.ok( - button.$icon.hasClass(button.model.get("icons")[0]), - "Has correct " + model.get("value") + " value after fraction" - ); - button.value(10, 100); - assert.ok( - button.$icon.hasClass(button.model.get("icons")[1]), - "Has correct " + model.get("value") + " value after fraction" - ); - button.value(100, 100); - assert.ok( - button.$icon.hasClass(button.model.get("icons")[2]), - "Has correct " + model.get("value") + " value after fraction" - ); - button.$el.trigger("click"); - assert.ok( - button.$icon.hasClass(button.model.get("icons")[0]), - "Has correct " + model.get("value") + " value after click" - ); - button.$el.trigger("click"); - assert.ok( - button.$icon.hasClass(button.model.get("icons")[2]), - "Has correct " + model.get("value") + " value after click" - ); - button.$el.trigger("click"); - assert.ok( - button.$icon.hasClass(button.model.get("icons")[0]), - "Has correct " + model.get("value") + " value after click" - ); -}); - -QUnit.test("options", function (assert) { - function _test(obj, options) { - assert.ok(JSON.stringify(obj.value()) == JSON.stringify(options.value), "Selected value is " + options.value); - assert.ok( - obj.$menu.css("display") == (options.menu_visible ? "block" : "none"), - "Menu visibility: " + options.menu_visible - ); - assert.ok( - obj.$message.css("display") == (options.message_visible ? "block" : "none"), - "Message visibility: " + options.message_visible - ); - assert.ok( - obj.$options.css("display") == (options.options_visible ? "inline-block" : "none"), - "Options visibility: " + options.options_visible - ); - options.message_cls && - assert.ok(obj.$message.hasClass(options.message_cls), "Message has class: " + options.message_cls); - assert.ok(obj.length() === options.length, "Number of options: " + options.length); - options.message_text && - assert.ok(obj.$message.html() === options.message_text, "Message text is: " + options.message_text); - options.first && assert.ok(obj.first() === options.first, "First value is: " + options.first); - options.all_icon && - assert.ok( - obj.all_button.$(".icon").hasClass(options.all_icon), - "All button in correct state: " + options.all_icon - ); - assert.ok( - obj.$menu.find(".ui-button-check").length === (options.all_icon ? 1 : 0), - "All button available: " + Boolean(options.all_active) - ); - } - - var radio = new Ui.Radio.View({}); - $("body").prepend(radio.$el); - radio.model.set("visible", false); - assert.ok(radio.value() === null, "Initial value is `null`."); - assert.ok(radio.$el.css("display") === "none", "Options hidden."); - radio.model.set("visible", true); - assert.ok(radio.$el.css("display") === "block", "Options shown."); - radio.model.set("value", "Unavailable."); - assert.ok(radio.value() === null, "Unavailable value ignored."); - _test(radio, { - menu_visible: false, - message_visible: true, - message_text: "No options available.", - message_cls: "alert-danger", - options_visible: false, - value: null, - length: 0, - }); - radio.model.set("wait", true); - _test(radio, { - menu_visible: false, - message_visible: true, - message_text: "Please wait...", - message_cls: "alert-info", - options_visible: false, - value: null, - length: 0, - }); - radio.model.set("wait", false); - _test(radio, { - menu_visible: false, - message_visible: true, - message_text: "No options available.", - message_cls: "alert-danger", - options_visible: false, - value: null, - length: 0, - }); - radio.model.set("data", [ - { value: "valuea", label: "labela" }, - { value: "valueb", label: "labelb" }, - ]); - _test(radio, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: "valuea", - first: "valuea", - length: 2, - }); - radio.model.set("value", "valueb"); - _test(radio, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: "valueb", - first: "valuea", - length: 2, - }); - radio.model.set("data", null); - _test(radio, { - menu_visible: false, - message_visible: true, - message_text: "No options available.", - message_cls: "alert-danger", - options_visible: false, - value: null, - first: null, - length: 0, - }); - radio.model.set("data", [ - { value: "valuea", label: "labela" }, - { value: "valueb", label: "labelb" }, - { value: "valuec", label: "labelc" }, - ]); - _test(radio, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: "valueb", - first: "valuea", - length: 3, - }); - radio.$("input").last().click(); - _test(radio, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: "valuec", - first: "valuea", - length: 3, - }); - - var check = new Ui.Checkbox.View({}); - $("body").prepend(check.$el); - _test(check, { - menu_visible: false, - message_visible: true, - message_text: "No options available.", - message_cls: "alert-danger", - options_visible: false, - value: null, - length: 0, - all_icon: "fa-square-o", - }); - check.model.set("data", [ - { value: "valuea", label: "labela" }, - { value: "valueb", label: "labelb" }, - { value: "valuec", label: "labelc" }, - ]); - _test(check, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: null, - length: 3, - all_icon: "fa-square-o", - }); - check.model.set("value", ["valuea", "valuec"]); - _test(check, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: ["valuea", "valuec"], - length: 3, - all_icon: "fa-minus-square-o", - }); - check.model.set("value", ["valuea", "valueb", "valuec"]); - _test(check, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: ["valuea", "valueb", "valuec"], - length: 3, - all_icon: "fa-check-square-o", - }); - check.model.set("data", []); - _test(check, { - menu_visible: false, - message_visible: true, - options_visible: false, - value: null, - length: 0, - all_icon: "fa-square-o", - }); - check.model.set("data", [ - { value: "valuea", label: "labela" }, - { value: "valueb", label: "labelb" }, - ]); - _test(check, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: ["valuea", "valueb"], - first: "valuea", - length: 2, - all_icon: "fa-check-square-o", - }); - check.all_button.$el.click(); - _test(check, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: null, - first: "valuea", - length: 2, - all_icon: "fa-square-o", - }); - check.all_button.$el.click(); - _test(check, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: ["valuea", "valueb"], - first: "valuea", - length: 2, - all_icon: "fa-check-square-o", - }); - check.$("input").last().click(); - _test(check, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: ["valuea"], - first: "valuea", - length: 2, - all_icon: "fa-minus-square-o", - }); - check.$("input").last().click(); - _test(check, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: ["valuea", "valueb"], - first: "valuea", - length: 2, - all_icon: "fa-check-square-o", - }); - - var radiobutton = new Ui.RadioButton.View({}); - $("body").prepend(radiobutton.$el); - radiobutton.model.set("data", [ - { value: "valuea", label: "labela" }, - { value: "valueb", label: "labelb" }, - ]); - _test(radiobutton, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: "valuea", - first: "valuea", - length: 2, - }); - radiobutton.$("input").last().click(); - _test(radiobutton, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: "valueb", - first: "valuea", - length: 2, - }); - - var drilldown = new Drilldown.View({}); - $("body").prepend(drilldown.$el); - drilldown.model.set("data", [ - { - value: "valuea", - name: "labela", - options: [ - { value: "valueb", name: "labelb" }, - { value: "valuec", name: "labelc" }, - { - value: "valued", - name: "labeld", - options: [ - { value: "valuee", name: "labele" }, - { value: "valuef", name: "labelf" }, - ], - }, - ], - }, - { - value: "valueg", - name: "labelg", - options: [ - { value: "valueh", name: "labelh" }, - { value: "valuei", name: "labeli" }, - { - value: "valuej", - name: "labelj", - options: [ - { value: "valuek", name: "labelk" }, - { value: "valuel", name: "labell" }, - { value: "valuem", name: "labelm" }, - ], - }, - ], - }, - { value: "valuen", name: "labeln" }, - ]); - _test(drilldown, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: null, - first: "valuea", - length: 14, - all_icon: "fa-square-o", - }); - drilldown.model.set("value", ["valuek", "valuen"]); - _test(drilldown, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: ["valuek", "valuen"], - first: "valuea", - length: 14, - all_icon: "fa-minus-square-o", - }); - var drillradio = new Drilldown.View({ display: "radio" }); - $("body").prepend(drillradio.$el); - _test(drillradio, { - menu_visible: false, - message_visible: true, - options_visible: false, - value: null, - length: 0, - }); - drillradio.model.set("data", drilldown.model.get("data")); - _test(drillradio, { - menu_visible: true, - message_visible: false, - options_visible: true, - value: "valuea", - first: "valuea", - length: 14, - }); -}); - -QUnit.test("select-default", function (assert) { - function _test(options) { - assert.ok( - JSON.stringify(select.value()) == JSON.stringify(options.value), - "Selected value is " + options.value - ); - assert.ok(select.text() == options.label, "Selected label is " + options.label); - assert.ok(select.$el.display === options.visible ? "block" : "none", options.visible ? "Visible" : "Hidden"); - assert.ok(select.data.length === options.count && select.length(), "Found " + options.count + " option"); - options.exists && assert.ok(select.exists(options.exists), "Found value: " + options.exists); - assert.ok( - select.$select.prop("multiple") === Boolean(options.multiple), - "Multiple state set to: " + options.multiple - ); - assert.ok(Boolean(select.all_button) === Boolean(options.multiple), "Visiblity of select all button correct."); - options.multiple && - assert.ok( - select.all_button.$(".icon").hasClass(options.all_icon), - "All button in correct state: " + options.all_icon - ); - } - var select = new Ui.Select.View({}); - $("body").prepend(select.$el); - assert.ok(select.first() === "__null__", "First select is '__null__'"); - assert.ok(select.$dropdown.hasClass("fa-caret-down"), "Caret down shown."); - select.model.set("data", [{ value: "value", label: "label" }]); - _test({ - value: "value", - label: "label", - visible: true, - count: 1, - }); - select.model.set("data", [ - { value: "valuea", label: "labela" }, - { value: "valueb", label: "labelb" }, - ]); - _test({ - value: "valuea", - label: "labela", - visible: true, - count: 2, - exists: "valueb", - }); - select.value("valueb"); - _test({ - value: "valueb", - label: "labelb", - visible: true, - count: 2, - }); - select.model.set("data", [{ value: "value", label: "label" }]); - _test({ - value: "value", - label: "label", - visible: true, - count: 1, - }); - select.model.set({ visible: false, value: "unavailable" }); - _test({ - value: "value", - label: "label", - visible: false, - count: 1, - }); - select.model.set({ - visible: true, - value: "valueb", - data: [ - { value: "valuea", label: "labela" }, - { value: "valueb", label: "labelb" }, - ], - }); - _test({ - value: "valueb", - label: "labelb", - visible: true, - count: 2, - exists: "valuea", - }); - select.model.set({ multiple: true }); - _test({ - value: ["valueb"], - label: "labelb", - visible: true, - count: 2, - exists: "valuea", - multiple: true, - all_icon: "fa-minus-square-o", - }); - select.model.set("value", ["valueb", "valuea"]); - _test({ - value: ["valuea", "valueb"], - label: "labela", - visible: true, - count: 2, - exists: "valueb", - multiple: true, - all_icon: "fa-check-square-o", - }); - select.model.set("value", []); - _test({ - value: null, - label: "", - visible: true, - count: 2, - exists: "valuea", - multiple: true, - all_icon: "fa-square-o", - }); - select.model.set({ multiple: false }); - _test({ - value: "valuea", - label: "labela", - visible: true, - count: 2, - exists: "valuea", - }); - select.model.set({ visible: false }); - _test({ - value: "valuea", - label: "labela", - visible: false, - count: 2, - exists: "valuea", - }); - select.model.set({ - multiple: true, - visible: true, - value: ["valueb", "valuec"], - data: [ - { value: "valuea", label: "labela" }, - { value: "valueb", label: "labelb" }, - { value: "valuec", label: "labelc" }, - ], - }); - _test({ - value: ["valueb", "valuec"], - label: "labelb", - visible: true, - count: 3, - exists: "valuea", - multiple: true, - all_icon: "fa-minus-square-o", - }); -}); - -QUnit.test("input", function (assert) { - var input = new Ui.Input(); - $("body").prepend(input.$el); - assert.ok(input.tagName === "input", "Created input."); - assert.ok(input.value() === undefined, "Input empty."); - input.model.set("value", "_value"); - assert.ok(input.$el.val() === "_value", "Input with value."); - assert.ok(!input.$el.hasClass("_cls"), "Has no custom class."); - input.model.set("cls", "_cls"); - assert.ok(input.$el.hasClass("_cls"), "Has custom class."); - assert.ok(!input.$el.attr("placeholder"), "Has no placeholder"); - input.model.set("placeholder", "_placeholder"); - assert.ok(input.$el.attr("placeholder") === "_placeholder", "Has correct placeholder"); - input.model.set("disabled", true); - assert.ok(input.$el.attr("disabled"), "Disabled"); - input.model.set("disabled", false); - assert.ok(!input.$el.attr("disabled"), "Enabled"); - input.model.set("visible", false); - assert.ok(input.$el.css("display") === "none", "Hidden"); - input.model.set("visible", true); - assert.ok(input.$el.css("display") === "inline-block", "Shown"); -}); - -QUnit.test("textarea", function (assert) { - var input = new Ui.Input({ area: true }); - $("body").prepend(input.$el); - assert.ok(input.tagName === "textarea", "Created textarea."); - assert.ok(input.value() === undefined, "Unavailable value."); - input.model.set("value", "_value"); - assert.ok(input.value() === "_value", "Correct new value."); - assert.ok(!input.$el.hasClass("_cls"), "Has no custom class."); - input.model.set("cls", "_cls"); - assert.ok(input.$el.hasClass("_cls"), "Has custom class."); -}); - -QUnit.test("nullableText", function (assert) { - // Start with null value, optional button should be off - var input = new Ui.NullableText({ area: false, value: null }); - $("body").prepend(input.$el); - assert.ok(input.text_input.tagName === "input", "input tag"); - assert.ok(input.value() === null, "null value"); - assert.ok(input.optional_button.model.get("value") === "false"); - // toggle button, will set value to `""` - input.optional_button.model.set("value", "true"); - assert.ok(input.value() === "", "Correct new value."); - // set value - input.text_input.model.set("value", "_value"); - assert.ok(input.value() === "_value", "Correct new value."); - assert.ok(input.optional_button.model.get("value") === "true"); - // toggle button to false, will reset value to null - input.optional_button.model.set("value", "false"); - assert.ok(input.value() === null, "Correct new value."); - assert.ok(input.optional_button.model.get("value") === "false"); - // New input, start with value, optional button should be on - var definedInput = new Ui.NullableText({ area: false, value: "123" }); - assert.ok(definedInput.value() === "123"); - assert.ok(definedInput.optional_button.model.get("value") === "true"); -}); - -QUnit.test("message", function (assert) { - var message = new Ui.Message({ - persistent: true, - message: "_message", - status: "danger", - }); - $("body").prepend(message.$el); - assert.ok(message.$el.hasClass("alert-danger"), "Alert danger."); - message.model.set("status", "info"); - assert.ok(!message.$el.hasClass("alert-danger"), "Alert danger (disabled)."); - assert.ok(message.$el.hasClass("alert-info"), "Alert info."); - assert.ok(message.$el.html() === "_message", "Correct message."); - message.model.set("message", "_new_message"); - assert.ok(message.$el.html() === "_new_message", "Correct new message."); -}); - -QUnit.test("select-content", function (assert) { - var select = new SelectContent.View({}); - $("body").prepend(select.$el); - var _testSelect = function (tag, options) { - var field = select.fields[tag == "first" ? 0 : select.fields.length - 1]; - var $select = select.$(".ui-select:" + tag); - var $button = select.$(".ui-radiobutton").find("label:" + tag); - assert.ok(field.length() == options[tag + "length"], tag + " one has " + options[tag + "length"] + " options"); - assert.ok(field.data[0].value == options[tag + "value"], tag + " option has correct value"); - assert.ok(field.data[0].label == options[tag + "label"], tag + " option has correct label"); - assert.ok($select.hasClass("ui-select-multiple") == options[tag + "multiple"], "Check multiple option"); - $button.trigger("mouseover"); - var tooltip = $(".tooltip-inner:last").text(); - $button.trigger("mouseleave"); - assert.ok(tooltip.indexOf("dataset") != -1 || tooltip.indexOf("collection") != -1, "Basic tooltip check"); - }; - var _test = function (options) { - assert.ok(select.button_type.$(".ui-option:first").hasClass("active"), "First one is toggled"); - assert.ok( - select.$(".ui-select").length == options.selectfields, - "Found " + options.selectfields + " select fields" - ); - assert.ok( - select.button_type.$(".ui-option").length == options.selectfields, - "Found " + options.selectfields + " radio button options" - ); - assert.ok( - select.$(".ui-select-multiple").length == options.totalmultiple, - "Contains " + options.totalmultiple + " multiselect fields" - ); - assert.ok( - select.$el.find(".ui-options:first .ui-option").length === options.selectfields, - "Radio button count, expected " + options.selectfields - ); - assert.ok(select.$(".ui-select:first").css("display") == "block", "Check select visibility"); - assert.ok( - select.$(".ui-select:last").css("display") == (options.selectfields == 1 ? "block" : "none"), - "Last select visibility" - ); - assert.ok( - (select.button_dialog.$el.css("display") != "none") === options.showdialog, - "Data dialog button visible" - ); - _testSelect("first", options); - _testSelect("last", options); - }; - - assert.ok(select.button_type.value() == 0, "Initial mode selected by default."); - select.model.set("data", { - hda: [ - { id: "id0", name: "name0", hid: "hid0" }, - { id: "id1", name: "name1", hid: "hid1" }, - ], - hdca: [ - { id: "id2", name: "name2", hid: "hid2" }, - { id: "id3", name: "name3", hid: "hid3" }, - { id: "id4", name: "name4", hid: "hid4" }, - ], - }); - - var initial = { - selectfields: 3, - firstlength: 2, - firstvalue: "id0", - firstlabel: "hid0: name0", - firstmultiple: false, - totalmultiple: 1, - lastvalue: "id2", - lastlabel: "hid2: name2", - lastlength: 3, - lastmultiple: false, - showdialog: true, - }; - _test(initial); - - select.model.set("multiple", true); - select.model.set("type", "data"); - _test({ - selectfields: 2, - firstlength: 2, - firstvalue: "id0", - firstlabel: "hid0: name0", - firstmultiple: true, - totalmultiple: 2, - lastvalue: "id2", - lastlabel: "hid2: name2", - lastlength: 3, - lastmultiple: true, - showdialog: true, - }); - - select.model.set("multiple", false); - select.model.set("type", "data_collection"); - _test({ - selectfields: 1, - firstlength: 3, - firstvalue: "id2", - firstlabel: "hid2: name2", - firstmultiple: false, - totalmultiple: 0, - lastvalue: "id2", - lastlabel: "hid2: name2", - lastlength: 3, - lastmultiple: false, - showdialog: false, - }); - - select.model.set("type", "module_data_collection"); - _test({ - selectfields: 2, - firstlength: 3, - firstvalue: "id2", - firstlabel: "hid2: name2", - firstmultiple: false, - totalmultiple: 1, - lastvalue: "id2", - lastlabel: "hid2: name2", - lastlength: 3, - lastmultiple: true, - showdialog: false, - }); - - select.model.set("type", "module_data"); - _test({ - selectfields: 2, - firstlength: 2, - firstvalue: "id0", - firstlabel: "hid0: name0", - firstmultiple: false, - totalmultiple: 1, - lastvalue: "id0", - lastlabel: "hid0: name0", - lastlength: 2, - lastmultiple: true, - showdialog: true, - }); - - select.model.set("type", "data"); - _test(initial); - - select.model.set("wait", true); - assert.ok(select.$(".icon-dropdown").hasClass("fa-spinner"), "Shows spinner"); - select.model.set("wait", false); - assert.ok(select.$(".icon-dropdown").hasClass("fa-caret-down"), "Shows caret"); - select.model.set("optional", true); - assert.ok(select.fields[0].data[0].value == "__null__", "First option is optional value"); - select.model.set("optional", false); - assert.ok(select.fields[0].data[0].value != "__null__", "First option is not optional value"); - - select.model.set("value", { values: [{ id: "id1", src: "hda" }] }); - assert.ok( - JSON.stringify(select.value()) == '{"values":[{"id":"id1","name":"name1","hid":"hid1"}],"batch":false}', - "Checking single value" - ); - - assert.ok(select.config[select.model.get("current")].src == "hda", "Matched dataset field"); - assert.ok(!select.config[select.model.get("current")].multiple, "Matched single select field"); - select.model.set("value", { - values: [ - { id: "id0", src: "hda" }, - { id: "id1", src: "hda" }, - ], - }); - assert.ok(select.config[select.model.get("current")].multiple, "Matched multiple field"); - assert.ok( - JSON.stringify(select.value()) == - '{"values":[{"id":"id0","name":"name0","hid":"hid0"},{"id":"id1","name":"name1","hid":"hid1"}],"batch":true}', - "Checking multiple values" - ); - select.model.set("value", { values: [{ id: "id2", src: "hdca" }] }); - assert.ok(select.config[select.model.get("current")].src == "hdca", "Matched collection field"); - assert.ok( - JSON.stringify(select.value()) == '{"values":[{"id":"id2","name":"name2","hid":"hid2"}],"batch":true}', - "Checking collection value" - ); - - select = new SelectContent.View({}); - $("body").prepend(select.$el); - var _testEmptySelect = function (tag, txt_extension, txt_label) { - var field = select.fields[tag == "first" ? 0 : select.fields.length - 1]; - select.$(".ui-select:" + tag); - assert.ok(field.data[0].value == "__null__", tag + " option has correct empty value."); - assert.ok( - field.data[0].label == "No " + txt_extension + txt_label + " available.", - tag + " option has correct empty label." - ); - }; - - var labels = select.model.get("src_labels"); - _testEmptySelect("first", "", labels.hda); - _testEmptySelect("last", "", labels.hdca); - select.model.set("extensions", ["txt", "bam"]); - _testEmptySelect("first", "txt or bam ", labels.hda); - _testEmptySelect("last", "txt or bam ", labels.hdca); - select.model.set("extensions", ["txt"]); - _testEmptySelect("first", "txt ", labels.hda); - _testEmptySelect("last", "txt ", labels.hdca); -}); diff --git a/client/tests/qunit/test-data/bootstrapped.js b/client/tests/test-data/bootstrapped.js similarity index 100% rename from client/tests/qunit/test-data/bootstrapped.js rename to client/tests/test-data/bootstrapped.js diff --git a/client/tests/qunit/test-data/fakeserver.js b/client/tests/test-data/fakeserver.js similarity index 100% rename from client/tests/qunit/test-data/fakeserver.js rename to client/tests/test-data/fakeserver.js diff --git a/client/tests/qunit/test-data/job-dag-1.js b/client/tests/test-data/job-dag-1.js similarity index 100% rename from client/tests/qunit/test-data/job-dag-1.js rename to client/tests/test-data/job-dag-1.js diff --git a/client/tests/qunit/test-data/json/datatypes.json b/client/tests/test-data/json/datatypes.json similarity index 100% rename from client/tests/qunit/test-data/json/datatypes.json rename to client/tests/test-data/json/datatypes.json diff --git a/client/tests/qunit/test-data/json/datatypes.mapping.json b/client/tests/test-data/json/datatypes.mapping.json similarity index 100% rename from client/tests/qunit/test-data/json/datatypes.mapping.json rename to client/tests/test-data/json/datatypes.mapping.json diff --git a/client/tests/qunit/test-data/paired-collection-creator.data.js b/client/tests/test-data/paired-collection-creator.data.js similarity index 100% rename from client/tests/qunit/test-data/paired-collection-creator.data.js rename to client/tests/test-data/paired-collection-creator.data.js From 9466b5c3c653bd61ae728d18bf676943abb83aa7 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 7 Feb 2023 10:25:19 -0500 Subject: [PATCH 17/23] Drop karma, qunit dependencies --- client/package.json | 9 +- client/yarn.lock | 385 ++------------------------------------------ 2 files changed, 15 insertions(+), 379 deletions(-) diff --git a/client/package.json b/client/package.json index b2907febde7..a79369ea58c 100644 --- a/client/package.json +++ b/client/package.json @@ -128,10 +128,9 @@ "format": "prettier --write .", "format-check": "prettier --check .", "prettier": "yarn run format", - "test": "yarn run qunit && yarn run jest", + "test": "yarn run jest", "jest": "jest --config tests/jest/jest.config.js", "jest-watch": "jest --config tests/jest/jest.config.js --watch", - "qunit": "karma start tests/karma/karma.config.qunit.js", "eslint-precommit": "eslint -c .eslintrc.json --quiet --ext .js,.vue,.ts src tests/jest/standalone", "eslint": "eslint -c .eslintrc.json --ext .js,.vue,.ts src tests/jest/standalone" }, @@ -170,16 +169,10 @@ "jest-environment-jsdom": "^29.3.1", "jest-location-mock": "^1.0.9", "json-loader": "^0.5.7", - "karma": "^6.3.16", - "karma-chrome-launcher": "^3.1.0", - "karma-polyfill": "^1.1.0", - "karma-qunit": "^4.1.2", - "karma-webpack": "^5.0.0", "mini-css-extract-plugin": "^2.5.3", "postcss-loader": "^7.0.2", "prettier": "^2.8.3", "process": "^0.11.10", - "qunit": "^2.17.2", "raw-loader": "^4.0.2", "sass": "^1.58.0", "sass-loader": "^13.2.0", diff --git a/client/yarn.lock b/client/yarn.lock index 117a021091b..024f7c381b6 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -1405,11 +1405,6 @@ "@citation-js/name" "^0.4.2" wikidata-sdk "^8.0.0" -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== - "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz" @@ -2023,11 +2018,6 @@ resolved "https://registry.npmjs.org/@types/circular-json/-/circular-json-0.4.0.tgz" integrity sha512-7+kYB7x5a7nFWW1YPBh3KxhwKfiaI4PbZ1RvzBU91LZy7lWJO822CI+pqzSre/DZ7KsCuMKdHnLHHFu8AyXbQg== -"@types/component-emitter@^1.2.10": - version "1.2.11" - resolved "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz" - integrity sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ== - "@types/connect-history-api-fallback@^1.3.5": version "1.3.5" resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz" @@ -2043,16 +2033,6 @@ dependencies: "@types/node" "*" -"@types/cookie@^0.4.1": - version "0.4.1" - resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz" - integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== - -"@types/cors@^2.8.12": - version "2.8.12" - resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz" - integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== - "@types/d3-array@*": version "3.0.3" resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.0.3.tgz#87d990bf504d14ad6b16766979d04e943c046dac" @@ -2378,7 +2358,7 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== -"@types/node@*", "@types/node@>=10.0.0": +"@types/node@*": version "18.6.5" resolved "https://registry.npmjs.org/@types/node/-/node-18.6.5.tgz" integrity sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw== @@ -3356,11 +3336,6 @@ base64-js@^1.3.1: resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -base64id@2.0.0, base64id@~2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz" - integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== - base@^0.11.1: version "0.11.2" resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz" @@ -3408,7 +3383,7 @@ bluebird@^3.1.1: resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -body-parser@1.20.0, body-parser@^1.19.0: +body-parser@1.20.0: version "1.20.0" resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz" integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== @@ -3704,7 +3679,7 @@ char-regex@^1.0.2: resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^2.0.0, chokidar@^3.5.1, chokidar@^3.5.3: +chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^2.0.0, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -3792,15 +3767,6 @@ cliui@^3.2.0: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - cliui@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" @@ -3941,7 +3907,7 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@7, commander@7.2.0, commander@^7.2.0: +commander@7, commander@^7.2.0: version "7.2.0" resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== @@ -3961,7 +3927,7 @@ commander@^9.4.1: resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== -component-emitter@^1.2.1, component-emitter@~1.3.0: +component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== @@ -4023,16 +3989,6 @@ connect-history-api-fallback@^2.0.0: resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz" integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== -connect@^3.7.0: - version "3.7.0" - resolved "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz" - integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== - dependencies: - debug "2.6.9" - finalhandler "1.1.2" - parseurl "~1.3.3" - utils-merge "1.0.1" - consola@^2.15.0: version "2.15.3" resolved "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz" @@ -4079,11 +4035,6 @@ cookie@0.5.0: resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookie@~0.4.1: - version "0.4.2" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== - copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz" @@ -4124,14 +4075,6 @@ core-util-is@~1.0.0: resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cors@~2.8.5: - version "2.8.5" - resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz" - integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== - dependencies: - object-assign "^4" - vary "^1" - cosmiconfig@^7.0.0: version "7.0.1" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz" @@ -4352,11 +4295,6 @@ custom-error-instance@2.1.1: resolved "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz" integrity sha512-p6JFxJc3M4OTD2li2qaHkDCw9SfMw82Ldr6OC9Je1aXiGfhx2W8p3GaoeaGrPJTUN9NirTM/KTxHWMUdR1rsUg== -custom-event@~1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz" - integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== - "d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.2.0: version "3.2.1" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.1.tgz#39331ea706f5709417d31bbb6ec152e0328b39b3" @@ -4633,11 +4571,6 @@ date-fns@^2.28.0: resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.29.1.tgz" integrity sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw== -date-format@^4.0.13: - version "4.0.13" - resolved "https://registry.npmjs.org/date-format/-/date-format-4.0.13.tgz" - integrity sha512-bnYCwf8Emc3pTD8pXnre+wfnjGtfi5ncMDKy7+cWZXbmRAsdWkOQHrfC1yz/KiwP5thDp2kCHWYWKBX4HP1hoQ== - de-indent@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz" @@ -4650,7 +4583,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4836,11 +4769,6 @@ detect-node@^2.0.4: resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== -di@^0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/di/-/di-0.0.1.tgz" - integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== - diff-sequences@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz" @@ -4897,16 +4825,6 @@ dom-event-types@^1.0.0: resolved "https://registry.npmjs.org/dom-event-types/-/dom-event-types-1.1.0.tgz" integrity sha512-jNCX+uNJ3v38BKvPbpki6j5ItVlnSqVV6vDWGS6rExzCMjsc39frLjm1n91o6YaKK6AZl0wLloItW6C6mr61BQ== -dom-serialize@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz" - integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ== - dependencies: - custom-event "~1.0.0" - ent "~2.2.0" - extend "^3.0.0" - void-elements "^2.0.0" - dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz" @@ -5049,27 +4967,6 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" -engine.io-parser@~5.0.3: - version "5.0.4" - resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz" - integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg== - -engine.io@~6.2.0: - version "6.2.0" - resolved "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz" - integrity sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg== - dependencies: - "@types/cookie" "^0.4.1" - "@types/cors" "^2.8.12" - "@types/node" ">=10.0.0" - accepts "~1.3.4" - base64id "2.0.0" - cookie "~0.4.1" - cors "~2.8.5" - debug "~4.3.1" - engine.io-parser "~5.0.3" - ws "~8.2.3" - enhanced-resolve@^5.0.0, enhanced-resolve@^5.10.0: version "5.10.0" resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz" @@ -5086,11 +4983,6 @@ enhanced-resolve@^5.7.0: graceful-fs "^4.2.4" tapable "^2.2.0" -ent@~2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz" - integrity sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA== - entities@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" @@ -5699,19 +5591,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - finalhandler@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" @@ -5803,7 +5682,7 @@ flatted@^2.0.0: resolved "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== -flatted@^3.1.0, flatted@^3.2.6: +flatted@^3.1.0: version "3.2.6" resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz" integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== @@ -5876,15 +5755,6 @@ fresh@0.5.2: resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-mkdirp-stream@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz" @@ -6040,7 +5910,7 @@ glob-watcher@^5.0.3: normalize-path "^3.0.0" object.defaults "^1.1.0" -glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.7, glob@^7.2.0, glob@~7.2.0: +glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.2.0, glob@~7.2.0: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -6095,11 +5965,6 @@ globals@^13.15.0: dependencies: type-fest "^0.20.2" -globalyzer@0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz" - integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== - globby@^11.0.1, globby@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" @@ -6123,11 +5988,6 @@ globby@^13.1.1: merge2 "^1.4.1" slash "^4.0.0" -globrex@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz" - integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== - glogg@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz" @@ -6142,7 +6002,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -6982,11 +6842,6 @@ isarray@^2.0.5: resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== -isbinaryfile@^4.0.8: - version "4.0.10" - resolved "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz" - integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" @@ -7634,13 +7489,6 @@ json5@^2.2.2: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz" - integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== - optionalDependencies: - graceful-fs "^4.1.6" - jspdf@^2.5.1: version "2.5.1" resolved "https://registry.npmjs.org/jspdf/-/jspdf-2.5.1.tgz" @@ -7671,62 +7519,6 @@ just-extend@^4.0.2: resolved "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz" integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== -karma-chrome-launcher@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz" - integrity sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ== - dependencies: - which "^1.2.1" - -karma-polyfill@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/karma-polyfill/-/karma-polyfill-1.1.0.tgz" - integrity sha512-TGWOIeTa4VBU2Af6jp4IYbROlTH6EXqpQZt15r0TRv7AYMt03BIagj5eljZgC64Uqg11QDh09j9megr6Vmzm6g== - -karma-qunit@^4.1.2: - version "4.1.2" - resolved "https://registry.npmjs.org/karma-qunit/-/karma-qunit-4.1.2.tgz" - integrity sha512-taTPqBeHCOlkeKTSzQgIKzAUb79vw3rfbCph+xwwh63tyGjNtljwx91VArhIM9DzIIR3gB9G214wQg+oXI9ycw== - -karma-webpack@^5.0.0: - version "5.0.0" - resolved "https://registry.npmjs.org/karma-webpack/-/karma-webpack-5.0.0.tgz" - integrity sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA== - dependencies: - glob "^7.1.3" - minimatch "^3.0.4" - webpack-merge "^4.1.5" - -karma@^6.3.16: - version "6.4.0" - resolved "https://registry.npmjs.org/karma/-/karma-6.4.0.tgz" - integrity sha512-s8m7z0IF5g/bS5ONT7wsOavhW4i4aFkzD4u4wgzAQWT4HGUeWI3i21cK2Yz6jndMAeHETp5XuNsRoyGJZXVd4w== - dependencies: - "@colors/colors" "1.5.0" - body-parser "^1.19.0" - braces "^3.0.2" - chokidar "^3.5.1" - connect "^3.7.0" - di "^0.0.1" - dom-serialize "^2.2.1" - glob "^7.1.7" - graceful-fs "^4.2.6" - http-proxy "^1.18.1" - isbinaryfile "^4.0.8" - lodash "^4.17.21" - log4js "^6.4.1" - mime "^2.5.2" - minimatch "^3.0.4" - mkdirp "^0.5.5" - qjobs "^1.2.0" - range-parser "^1.2.1" - rimraf "^3.0.2" - socket.io "^4.4.1" - source-map "^0.6.1" - tmp "^0.2.1" - ua-parser-js "^0.7.30" - yargs "^16.1.1" - kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" @@ -8004,17 +7796,6 @@ lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log4js@^6.4.1: - version "6.6.1" - resolved "https://registry.npmjs.org/log4js/-/log4js-6.6.1.tgz" - integrity sha512-J8VYFH2UQq/xucdNu71io4Fo+purYYudyErgBbswWKO0MC6QVOERRomt5su/z6d3RJSmLyTGmXl3Q/XjKCf+/A== - dependencies: - date-format "^4.0.13" - debug "^4.3.4" - flatted "^3.2.6" - rfdc "^1.3.0" - streamroller "^3.1.2" - lower-case@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" @@ -8234,11 +8015,6 @@ mime@1.6.0: resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.5.2: - version "2.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" @@ -8297,13 +8073,6 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.5: - version "0.5.6" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - moment@2.20.1: version "2.20.1" resolved "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz" @@ -8440,11 +8209,6 @@ node-releases@^2.0.6: resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz" integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== -node-watch@0.7.3: - version "0.7.3" - resolved "https://registry.npmjs.org/node-watch/-/node-watch-0.7.3.tgz" - integrity sha512-3l4E8uMPY1HdMMryPRUAl+oIHtXtyiTlIiESNSVSNxcPfzAFzeTbXFQkZfAwBbo0B1qMSG8nUABx+Gd+YrbKrQ== - nopt@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz" @@ -8532,11 +8296,6 @@ nwsapi@^2.2.2: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== -object-assign@^4: - version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - object-copy@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz" @@ -8641,13 +8400,6 @@ on-finished@2.4.1: dependencies: ee-first "1.1.1" -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz" - integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== - dependencies: - ee-first "1.1.1" - on-headers@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" @@ -9447,11 +9199,6 @@ pyre-to-regexp@^0.0.5: resolved "https://registry.npmjs.org/pyre-to-regexp/-/pyre-to-regexp-0.0.5.tgz" integrity sha512-AYoI8bSYhCbElpcj/Z7FyP0rrpuuw96GAYCqdrX3fDH+vRXh8lXWeRRZtyJgRO4LgBlb5fK2o2odkaoSGQ6gIQ== -qjobs@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz" - integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== - qs@6.10.3: version "6.10.3" resolved "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz" @@ -9479,15 +9226,6 @@ quick-lru@^5.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== -qunit@^2.17.2: - version "2.19.1" - resolved "https://registry.npmjs.org/qunit/-/qunit-2.19.1.tgz" - integrity sha512-gSGuw0vErE/rNjnlBW/JmE7NNubBlGrDPQvsug32ejYhcVFuZec9yoU0+C30+UgeCGwq6Ap89K65dMGo+kDGZQ== - dependencies: - commander "7.2.0" - node-watch "0.7.3" - tiny-glob "0.2.9" - raf@^3.4.1: version "3.4.1" resolved "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz" @@ -9878,17 +9616,12 @@ reusify@^1.0.4: resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rfdc@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz" - integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== - rgbcolor@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz" integrity sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw== -rimraf@^3.0.0, rimraf@^3.0.2: +rimraf@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -10273,32 +10006,6 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -socket.io-adapter@~2.4.0: - version "2.4.0" - resolved "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz" - integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg== - -socket.io-parser@~4.0.4: - version "4.0.5" - resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.5.tgz" - integrity sha512-sNjbT9dX63nqUFIOv95tTVm6elyIU4RvB1m8dOeZt+IgWwcWklFDOdmGcfo3zSiRsnR/3pJkjY5lfoGqEe4Eig== - dependencies: - "@types/component-emitter" "^1.2.10" - component-emitter "~1.3.0" - debug "~4.3.1" - -socket.io@^4.4.1: - version "4.5.1" - resolved "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz" - integrity sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ== - dependencies: - accepts "~1.3.4" - base64id "~2.0.0" - debug "~4.3.2" - engine.io "~6.2.0" - socket.io-adapter "~2.4.0" - socket.io-parser "~4.0.4" - sockjs@^0.3.24: version "0.3.24" resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" @@ -10484,7 +10191,7 @@ statuses@2.0.1: resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -"statuses@>= 1.4.0 < 2", statuses@~1.5.0: +"statuses@>= 1.4.0 < 2": version "1.5.0" resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== @@ -10512,15 +10219,6 @@ stream-shift@^1.0.0: resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== -streamroller@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/streamroller/-/streamroller-3.1.2.tgz" - integrity sha512-wZswqzbgGGsXYIrBYhOE0yP+nQ6XRk7xDcYwuQAGTYXdyAUmvgVFE0YU1g5pvQT0m7GBaQfYcSnlHbapuK0H0A== - dependencies: - date-format "^4.0.13" - debug "^4.3.4" - fs-extra "^8.1.0" - streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" @@ -10812,21 +10510,6 @@ timers-browserify@^2.0.12: dependencies: setimmediate "^1.0.4" -tiny-glob@0.2.9: - version "0.2.9" - resolved "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz" - integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== - dependencies: - globalyzer "0.1.0" - globrex "^0.1.2" - -tmp@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - tmpl@1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" @@ -11072,11 +10755,6 @@ typescript@^4.9.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== -ua-parser-js@^0.7.30: - version "0.7.33" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" - integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== - uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz" @@ -11176,11 +10854,6 @@ unique-stream@^2.0.2: json-stable-stringify-without-jsonify "^1.0.1" through2-filter "^3.0.0" -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - universalify@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" @@ -11303,7 +10976,7 @@ value-or-function@^3.0.0: resolved "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz" integrity sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg== -vary@^1, vary@~1.1.2: +vary@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== @@ -11356,11 +11029,6 @@ vinyl@^2.0.0: remove-trailing-separator "^1.0.1" replace-ext "^1.0.0" -void-elements@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz" - integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== - vue-demi@*: version "0.13.11" resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99" @@ -11606,13 +11274,6 @@ webpack-dev-server@^4.11.1: webpack-dev-middleware "^5.3.1" ws "^8.4.2" -webpack-merge@^4.1.5: - version "4.2.2" - resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz" - integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== - dependencies: - lodash "^4.17.15" - webpack-merge@^5.7.3, webpack-merge@^5.8.0: version "5.8.0" resolved "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz" @@ -11778,7 +11439,7 @@ which-typed-array@^1.1.8: has-tostringtag "^1.0.0" is-typed-array "^1.1.10" -which@^1.2.1, which@^1.2.14: +which@^1.2.14: version "1.3.1" resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -11859,11 +11520,6 @@ ws@^8.4.2: resolved "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz" integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== -ws@~8.2.3: - version "8.2.3" - resolved "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz" - integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== - xml-beautifier@^0.5.0: version "0.5.0" resolved "https://registry.npmjs.org/xml-beautifier/-/xml-beautifier-0.5.0.tgz" @@ -11937,7 +11593,7 @@ yaml@^2.0.0: resolved "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz" integrity sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw== -yargs-parser@^20.2.2, yargs-parser@^20.2.9: +yargs-parser@^20.2.9: version "20.2.9" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== @@ -11955,19 +11611,6 @@ yargs-parser@^5.0.1: camelcase "^3.0.0" object.assign "^4.1.0" -yargs@^16.1.1: - version "16.2.0" - resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - yargs@^17.3.1: version "17.6.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" From 944ddc2ab59079ceea363fc3a3110f0af7b0d905 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 7 Feb 2023 10:32:13 -0500 Subject: [PATCH 18/23] standardize test data access --- client/src/app/app.test.js | 2 +- .../Collections/PairedListCollectionCreator.test.js | 2 +- client/src/components/Datatypes/test_fixtures.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/app/app.test.js b/client/src/app/app.test.js index e7b8d747046..71fc91e4a3b 100644 --- a/client/src/app/app.test.js +++ b/client/src/app/app.test.js @@ -1,6 +1,6 @@ import { getGalaxyInstance, setGalaxyInstance } from "app"; import Backbone from "backbone"; -import galaxyOptions from "@tests/qunit/test-data/bootstrapped"; +import galaxyOptions from "@tests/test-data/bootstrapped"; export function setupTestGalaxy(galaxyOptions_ = null) { galaxyOptions_ = galaxyOptions_ || galaxyOptions; diff --git a/client/src/components/Collections/PairedListCollectionCreator.test.js b/client/src/components/Collections/PairedListCollectionCreator.test.js index 393dd33884f..ad545196207 100644 --- a/client/src/components/Collections/PairedListCollectionCreator.test.js +++ b/client/src/components/Collections/PairedListCollectionCreator.test.js @@ -1,7 +1,7 @@ import { mount } from "@vue/test-utils"; import { shallowMount } from "@vue/test-utils"; import PairedListCollectionCreator from "components/Collections/PairedListCollectionCreator"; -import DATA from "../../../tests/qunit/test-data/paired-collection-creator.data.js"; +import DATA from "@tests/test-data/paired-collection-creator.data.js"; describe("PairedListCollectionCreator", () => { let wrapper; diff --git a/client/src/components/Datatypes/test_fixtures.ts b/client/src/components/Datatypes/test_fixtures.ts index 0e58ccb475c..3108af7025f 100644 --- a/client/src/components/Datatypes/test_fixtures.ts +++ b/client/src/components/Datatypes/test_fixtures.ts @@ -1,6 +1,6 @@ // TODO: Swap these awful relative paths to an alias -import DatatypesMappingJson from "./test-data/datatypes.mapping.json"; -import DatatypesJson from "./test-data/datatypes.json"; +import DatatypesMappingJson from "@tests/test-data/json/datatypes.mapping.json"; +import DatatypesJson from "@tests/test-data/json/datatypes.json"; import { DatatypesMapperModel } from "./model"; import type { DatatypesCombinedMap } from "./model"; From 553b1a2f77b58291131b74396f98eea70777e931 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 7 Feb 2023 10:33:40 -0500 Subject: [PATCH 19/23] Update readme, linting --- client/.eslintrc.json | 2 +- client/README.md | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/client/.eslintrc.json b/client/.eslintrc.json index a02efdbb365..b05eefa6c72 100644 --- a/client/.eslintrc.json +++ b/client/.eslintrc.json @@ -59,7 +59,7 @@ "vuejs-accessibility/no-autofocus": "error", "vuejs-accessibility/tabindex-no-positive": "error" }, - "ignorePatterns": ["dist", "src/qunit", "src/mocha", "src/libs", "src/nls", "src/legacy"], + "ignorePatterns": ["dist", "src/libs", "src/nls", "src/legacy"], "overrides": [ { "files": ["**/*.vue"], diff --git a/client/README.md b/client/README.md index 4eff98fefb4..667eb23b05e 100644 --- a/client/README.md +++ b/client/README.md @@ -122,10 +122,6 @@ utils](https://vue-test-utils.vuejs.org/) to mount individual components in a test bed and check them for rendered features. Please use jest-based mocking for isolating test functionality. -A set of older qUnit tests also exist which will be phased-out as the code they -support is replaced with modern component-based implementations. In the -meantime, we still run the qunit tests in sequence. - ### Running the tests #### At Build-Time @@ -145,15 +141,7 @@ commands. This is what CI is going to run, and also what 'make client-test' invokes, executing all the client tests: - yarn run test - -You can also bypass qunit and single-run all of the jest tests like so: - - yarn run jest - -Or, if you want to run just the qunit tests: - - yarn run qunit + yarn test ##### Watch and rerun jest unit tests every time a source file changes From a6c0d5a3bd561c67530550953c75f795830641b5 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 7 Feb 2023 10:33:52 -0500 Subject: [PATCH 20/23] Update github workflow dropping qunit --- .github/workflows/jest.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/jest.yaml b/.github/workflows/jest.yaml index 0bbe2978043..634c87272e2 100644 --- a/.github/workflows/jest.yaml +++ b/.github/workflows/jest.yaml @@ -31,7 +31,5 @@ jobs: run: yarn run gulp client working-directory: client - name: Run Unit Tests - run: yarn run qunit - working-directory: client - run: yarn jest working-directory: client From 027f0c0166290748cf3e05ce9b5d01081f079ed0 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 7 Feb 2023 10:40:15 -0500 Subject: [PATCH 21/23] More test-data cleanup (from qunit) --- client/tests/test-data/fakeserver.js | 11 - client/tests/test-data/job-dag-1.js | 529 --------------------------- 2 files changed, 540 deletions(-) delete mode 100644 client/tests/test-data/fakeserver.js delete mode 100644 client/tests/test-data/job-dag-1.js diff --git a/client/tests/test-data/fakeserver.js b/client/tests/test-data/fakeserver.js deleted file mode 100644 index 684e643ae42..00000000000 --- a/client/tests/test-data/fakeserver.js +++ /dev/null @@ -1,11 +0,0 @@ -import dataTypesMapping from "./json/datatypes.mapping.json"; -import dataTypes from "./json/datatypes.json"; - -export default { - "api/datatypes/mapping": { - data: JSON.stringify(dataTypesMapping), - }, - "api/datatypes": { - data: JSON.stringify(dataTypes), - }, -}; diff --git a/client/tests/test-data/job-dag-1.js b/client/tests/test-data/job-dag-1.js deleted file mode 100644 index f2a6596eded..00000000000 --- a/client/tests/test-data/job-dag-1.js +++ /dev/null @@ -1,529 +0,0 @@ -// ============================================================================ -var tools = { - upload1: {}, - "Show beginning1": {}, - "Show tail1": {}, - random_lines1: {}, - __SET_METADATA__: {}, - cat1: {}, -}; - -// ============================================================================ -// plain 3 step job chain -var jobs1 = [ - { - tool_id: "upload1", - update_time: "2014-10-03T15:12:25.904033", - inputs: {}, - outputs: { - output0: { - src: "hda", - id: "8c959c9304a2bc4b", - }, - }, - exit_code: 0, - state: "ok", - create_time: "2014-10-03T15:12:22.589152", - params: { - // ... - }, - model_class: "Job", - id: "8a81cf6f989c4467", - tool: null, - }, - { - tool_id: "Show beginning1", - update_time: "2014-10-03T15:14:04.328484", - inputs: { - input: { - src: "hda", - id: "8c959c9304a2bc4b", - }, - }, - outputs: { - out_file1: { - src: "hda", - id: "132016f833b57406", - }, - }, - exit_code: 0, - state: "ok", - create_time: "2014-10-03T15:14:01.060662", - params: { - // ... - }, - model_class: "Job", - id: "6505e875ddb66fd2", - tool: null, - }, - { - tool_id: "Show tail1", - update_time: "2014-10-03T15:14:21.596871", - inputs: { - input: { - src: "hda", - id: "132016f833b57406", - }, - }, - outputs: { - out_file1: { - src: "hda", - id: "846fb0a2a64137c0", - }, - }, - exit_code: 0, - state: "ok", - create_time: "2014-10-03T15:14:18.425681", - params: { - // ... - }, - model_class: "Job", - id: "77f74776fd03cbc5", - tool: null, - }, -]; - -var historyContents1 = [ - { - deleted: false, - extension: "interval", - hid: 1, - history_content_type: "dataset", - history_id: "911dde3ddb677bcd", - id: "8c959c9304a2bc4b", - name: "1.interval", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/911dde3ddb677bcd/contents/datasets/8c959c9304a2bc4b", - visible: true, - }, - { - deleted: false, - extension: "interval", - hid: 2, - history_content_type: "dataset", - history_id: "911dde3ddb677bcd", - id: "132016f833b57406", - name: "Select first on data 1", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/911dde3ddb677bcd/contents/datasets/132016f833b57406", - visible: true, - }, - { - deleted: false, - extension: "interval", - hid: 3, - history_content_type: "dataset", - history_id: "911dde3ddb677bcd", - id: "846fb0a2a64137c0", - name: "Select last on data 2", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/911dde3ddb677bcd/contents/datasets/846fb0a2a64137c0", - visible: true, - }, -]; - -// ============================================================================ -// single job chain with a __SET_METADATA__ job -var jobs2 = [ - { - tool_id: "upload1", - update_time: "2014-10-03T16:09:49.590769", - inputs: {}, - outputs: { - output0: { - src: "hda", - id: "eca0af6fb47bf90c", - }, - }, - exit_code: 0, - state: "ok", - create_time: "2014-10-03T16:09:45.190023", - params: { - // ... - }, - model_class: "Job", - id: "bf60fd5f5f7f44bf", - tool: null, - }, - { - tool_id: "random_lines1", - update_time: "2014-10-03T16:10:44.743610", - inputs: { - input: { - src: "hda", - id: "eca0af6fb47bf90c", - }, - }, - outputs: { - out_file1: { - src: "hda", - id: "6fc9fbb81c497f69", - }, - }, - exit_code: 0, - state: "ok", - create_time: "2014-10-03T16:10:41.446413", - params: { - // ... - }, - model_class: "Job", - id: "90240358ebde1489", - tool: null, - }, - { - tool_id: "__SET_METADATA__", - update_time: "2014-10-03T16:14:44.196697", - inputs: { - input1: { - src: "hda", - id: "eca0af6fb47bf90c", - }, - }, - outputs: {}, - exit_code: 0, - state: "ok", - create_time: "2014-10-03T16:14:37.901222", - params: { - // ... - }, - model_class: "Job", - id: "86cf1d3beeec9f1c", - tool: null, - }, -]; - -var historyContents2 = [ - { - deleted: false, - extension: "interval", - hid: 1, - history_content_type: "dataset", - history_id: "ff5476bcf6c921fa", - id: "eca0af6fb47bf90c", - name: "1.interval", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/ff5476bcf6c921fa/contents/datasets/eca0af6fb47bf90c", - visible: true, - }, - { - deleted: false, - extension: "interval", - hid: 2, - history_content_type: "dataset", - history_id: "ff5476bcf6c921fa", - id: "6fc9fbb81c497f69", - name: "Select random lines on data 1", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/ff5476bcf6c921fa/contents/datasets/6fc9fbb81c497f69", - visible: true, - }, -]; - -var jobs3 = [ - { - tool_id: "upload1", - update_time: "2014-10-13T20:28:58.549844", - inputs: {}, - outputs: { - output0: { - src: "hda", - id: "6fb17d0cc6e8fae5", - }, - }, - exit_code: 0, - state: "ok", - create_time: "2014-10-13T20:28:43.162803", - params: { - // ... - }, - model_class: "Job", - id: "8c959c9304a2bc4b", - tool: null, - }, - { - tool_id: "upload1", - update_time: "2014-10-13T20:28:58.932152", - inputs: {}, - outputs: { - output0: { - src: "hda", - id: "5114a2a207b7caff", - }, - }, - exit_code: 0, - state: "ok", - create_time: "2014-10-13T20:28:47.421452", - params: { - // ... - }, - model_class: "Job", - id: "132016f833b57406", - tool: null, - }, - { - tool_id: "Show beginning1", - update_time: "2014-10-13T20:29:31.424058", - inputs: { - input: { - src: "hda", - id: "6fb17d0cc6e8fae5", - }, - }, - outputs: { - out_file1: { - src: "hda", - id: "06ec17aefa2d49dd", - }, - }, - exit_code: 0, - state: "ok", - create_time: "2014-10-13T20:29:28.769495", - params: { - // ... - }, - model_class: "Job", - id: "846fb0a2a64137c0", - tool: null, - }, - { - tool_id: "Show beginning1", - update_time: "2014-10-13T20:29:55.851096", - inputs: { - input: { - src: "hda", - id: "5114a2a207b7caff", - }, - }, - outputs: { - out_file1: { - src: "hda", - id: "b8a0d6158b9961df", - }, - }, - exit_code: 0, - state: "ok", - create_time: "2014-10-13T20:29:53.291703", - params: { - // ... - }, - model_class: "Job", - id: "eca0af6fb47bf90c", - tool: null, - }, - { - tool_id: "Show tail1", - update_time: "2014-10-13T20:30:16.225937", - inputs: { - input: { - src: "hda", - id: "b8a0d6158b9961df", - }, - }, - outputs: { - out_file1: { - src: "hda", - id: "24d84bcf64116fe7", - }, - }, - exit_code: 0, - state: "ok", - create_time: "2014-10-13T20:30:13.789842", - params: { - // ... - }, - model_class: "Job", - id: "6fc9fbb81c497f69", - tool: null, - }, -]; - -var historyContents3 = [ - { - deleted: false, - extension: "bed", - hid: 1, - history_content_type: "dataset", - history_id: "5564089c81cf7fe8", - id: "6fb17d0cc6e8fae5", - name: "1.bed", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/5564089c81cf7fe8/contents/datasets/6fb17d0cc6e8fae5", - visible: true, - }, - { - deleted: false, - extension: "interval", - hid: 2, - history_content_type: "dataset", - history_id: "5564089c81cf7fe8", - id: "5114a2a207b7caff", - name: "1.interval", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/5564089c81cf7fe8/contents/datasets/5114a2a207b7caff", - visible: true, - }, - { - deleted: false, - extension: "bed", - hid: 3, - history_content_type: "dataset", - history_id: "5564089c81cf7fe8", - id: "06ec17aefa2d49dd", - name: "Select first on data 1", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/5564089c81cf7fe8/contents/datasets/06ec17aefa2d49dd", - visible: true, - }, - { - deleted: false, - extension: "interval", - hid: 4, - history_content_type: "dataset", - history_id: "5564089c81cf7fe8", - id: "b8a0d6158b9961df", - name: "Select first on data 2", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/5564089c81cf7fe8/contents/datasets/b8a0d6158b9961df", - visible: true, - }, - { - deleted: false, - extension: "interval", - hid: 5, - history_content_type: "dataset", - history_id: "5564089c81cf7fe8", - id: "24d84bcf64116fe7", - name: "Select last on data 4", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/5564089c81cf7fe8/contents/datasets/24d84bcf64116fe7", - visible: true, - }, -]; - -var jobs4 = [ - { - tool_id: "cat1", - update_time: "2014-10-21T17:33:36.960857", - inputs: { - input1: { - src: "hda", - id: "422eef6b1b545329", - name: "input1", - }, - "queries_0|input2": { - src: "hda", - id: "c86c1b73aa7102dd", - name: "queries_0|input2", - }, - }, - outputs: { - out_file1: { - src: "hda", - id: "52d6bdfafedbb5e5", - name: "out_file1", - }, - }, - exit_code: 0, - state: "ok", - create_time: "2014-10-21T17:33:34.302245", - params: { - // ... - }, - model_class: "Job", - id: "92b83968e0b52980", - }, -]; - -var historyContents4 = [ - { - dataset_id: 29, - deleted: false, - extension: "vcf", - hid: 1, - history_content_type: "dataset", - history_id: "c24141d7e4e77705", - id: "422eef6b1b545329", - name: "1.vcf", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/c24141d7e4e77705/contents/datasets/422eef6b1b545329", - visible: true, - }, - { - dataset_id: 56, - deleted: false, - extension: "maf", - hid: 2, - history_content_type: "dataset", - history_id: "c24141d7e4e77705", - id: "c86c1b73aa7102dd", - name: "3.maf", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/c24141d7e4e77705/contents/datasets/c86c1b73aa7102dd", - visible: true, - }, - { - dataset_id: 131, - deleted: false, - extension: "maf", - hid: 3, - history_content_type: "dataset", - history_id: "c24141d7e4e77705", - id: "52d6bdfafedbb5e5", - name: "Concatenate datasets on data 1 and data 2", - purged: false, - resubmitted: false, - state: "ok", - type: "file", - url: "/api/histories/c24141d7e4e77705/contents/datasets/52d6bdfafedbb5e5", - visible: true, - }, -]; - -// ============================================================================ -export default { - tools: tools, - jobs1: jobs1, - historyContents1: historyContents1, - jobs2: jobs2, - historyContents2: historyContents2, - jobs3: jobs3, - historyContents3: historyContents3, - jobs4: jobs4, - historyContents4: historyContents4, -}; From 48b8362b903b93035f2f1dd079ef9eacd72be26e Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 7 Feb 2023 10:44:01 -0500 Subject: [PATCH 22/23] Delete unused, busted symlinks --- client/src/components/Datatypes/test-data/datatypes.json | 1 - client/src/components/Datatypes/test-data/datatypes.mapping.json | 1 - 2 files changed, 2 deletions(-) delete mode 120000 client/src/components/Datatypes/test-data/datatypes.json delete mode 120000 client/src/components/Datatypes/test-data/datatypes.mapping.json diff --git a/client/src/components/Datatypes/test-data/datatypes.json b/client/src/components/Datatypes/test-data/datatypes.json deleted file mode 120000 index f32f2aee513..00000000000 --- a/client/src/components/Datatypes/test-data/datatypes.json +++ /dev/null @@ -1 +0,0 @@ -../../../../tests/qunit/test-data/json/datatypes.json \ No newline at end of file diff --git a/client/src/components/Datatypes/test-data/datatypes.mapping.json b/client/src/components/Datatypes/test-data/datatypes.mapping.json deleted file mode 120000 index de79c06a39b..00000000000 --- a/client/src/components/Datatypes/test-data/datatypes.mapping.json +++ /dev/null @@ -1 +0,0 @@ -../../../../tests/qunit/test-data/json/datatypes.mapping.json \ No newline at end of file From 8b315562017bc3528546ff0593e270802336cc31 Mon Sep 17 00:00:00 2001 From: Dannon Baker Date: Tue, 7 Feb 2023 10:45:43 -0500 Subject: [PATCH 23/23] Use @tests alias instead of long relative paths --- client/src/components/History/Export/HistoryExport.test.ts | 2 +- .../Management/Cleanup/CleanupOperationSummary.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/History/Export/HistoryExport.test.ts b/client/src/components/History/Export/HistoryExport.test.ts index e2ec1de4f38..73021ce655c 100644 --- a/client/src/components/History/Export/HistoryExport.test.ts +++ b/client/src/components/History/Export/HistoryExport.test.ts @@ -8,7 +8,7 @@ import { } from "@/components/Common/models/testData/exportData"; import flushPromises from "flush-promises"; import type { components } from "@/schema"; -import { getLocalVue } from "../../../../tests/jest/helpers"; +import { getLocalVue } from "@tests/jest/helpers"; import HistoryExport from "./HistoryExport.vue"; import { getExportRecords } from "./services"; diff --git a/client/src/components/User/DiskUsage/Management/Cleanup/CleanupOperationSummary.test.ts b/client/src/components/User/DiskUsage/Management/Cleanup/CleanupOperationSummary.test.ts index 50f75803877..ea56e1bb073 100644 --- a/client/src/components/User/DiskUsage/Management/Cleanup/CleanupOperationSummary.test.ts +++ b/client/src/components/User/DiskUsage/Management/Cleanup/CleanupOperationSummary.test.ts @@ -1,6 +1,6 @@ import { mount } from "@vue/test-utils"; import flushPromises from "flush-promises"; -import { getLocalVue } from "../../../../../../tests/jest/helpers"; +import { getLocalVue } from "@tests/jest/helpers"; import CleanupOperationSummary from "./CleanupOperationSummary.vue"; import { CleanableSummary, type CleanupOperation, CleanupResult } from "./model";