Merge pull request #15452 from dannon/qunit-elim

QUnit elimination
This commit is contained in:
Dannon
2023-02-14 10:13:37 -05:00
committed by GitHub
50 changed files with 182 additions and 4597 deletions
-2
View File
@@ -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
+1 -1
View File
@@ -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"],
+1 -13
View File
@@ -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
+1 -8
View File
@@ -128,10 +128,9 @@
"format": "prettier --write . --cache",
"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",
+69
View File
@@ -0,0 +1,69 @@
import { getGalaxyInstance, setGalaxyInstance } from "app";
import Backbone from "backbone";
import galaxyOptions from "@tests/test-data/bootstrapped";
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);
});
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(Object.prototype.hasOwnProperty.call(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();
});
});
+2 -37
View File
@@ -1,42 +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).
* <br />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.
* This all largely happens behind the scenes and no special calls are required.
@@ -196,7 +162,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;
@@ -210,7 +176,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
@@ -575,7 +541,6 @@ function buildComparator(attribute_name, options) {
//==============================================================================
export default {
LoggableMixin: LoggableMixin,
SessionStorageModel: SessionStorageModel,
mixin: mixin,
SearchableModelMixin: SearchableModelMixin,
+5 -124
View File
@@ -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 || {});
@@ -62,24 +38,15 @@ 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);
this._initLocale();
this.debug("GalaxyApp.localize: ", this.localize);
this.config = options.config || {};
this.debug("GalaxyApp.config: ", this.config);
this._initUser(options.user || {});
this.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);
@@ -119,9 +86,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;
};
@@ -129,56 +93,20 @@ 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];
}
}
}
};
/** 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);
GalaxyApp.prototype._initLocale = function _initLocale() {
this.localize = localize;
// add to window as global shortened alias
// TODO: temporary - remove when can require for plugins
@@ -220,9 +148,8 @@ 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);
this.user.logger = this.logger;
return this;
};
@@ -248,52 +175,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() {
const userEmail = this.user ? this.user.get("email") || "(anonymous)" : "uninitialized";
-4
View File
@@ -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;
+3 -18
View File
@@ -1,17 +1,13 @@
import Backbone from "backbone";
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 () {
return `${getAppRoot()}api/users`;
@@ -36,13 +32,11 @@ var User = Backbone.Model.extend(baseMVC.LoggableMixin).extend(
* @param {Object} data Initial model data.
*/
initialize: function (data) {
this.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);
});
},
@@ -142,15 +136,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,
@@ -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;
@@ -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;
};
},
@@ -1 +0,0 @@
../../../../tests/qunit/test-data/json/datatypes.json
@@ -1 +0,0 @@
../../../../tests/qunit/test-data/json/datatypes.mapping.json
@@ -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";
@@ -37,12 +37,11 @@ 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) {
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;
@@ -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";
@@ -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
@@ -153,7 +151,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;
@@ -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";
+3 -4
View File
@@ -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);
@@ -240,7 +239,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 +251,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 +302,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
@@ -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,9 +41,8 @@ export var View = Backbone.View.extend({
/** Add new repeat block */
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({
@@ -75,9 +73,8 @@ export var View = Backbone.View.extend({
/** Delete repeat block */
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();
@@ -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`;
},
+1 -1
View File
@@ -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;
});
};
-35
View File
@@ -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;
+3 -6
View File
@@ -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;
@@ -29,7 +27,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 +36,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 {
@@ -51,8 +49,7 @@ export default Backbone.Model.extend({
/** Resets the promise queue. All currently queued but unexecuted callbacks/promises will be rejected.
*/
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;
}
-623
View File
@@ -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,
};
-504
View File
@@ -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,
};
+17
View File
@@ -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");
});
});
+10 -2
View File
@@ -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;
};
}
+1
View File
@@ -39,6 +39,7 @@ module.exports = {
roots: ["<rootDir>/src/", "<rootDir>/tests/jest/standalone/"],
setupFilesAfterEnv: ["<rootDir>/tests/jest/jest.setup.js"],
testEnvironment: "jsdom",
testPathIgnorePatterns: ["/node_modules/", "/dist/"],
transform: {
"^.+\\.js$": "babel-jest",
"^.*\\.(vue)$": "@vue/vue2-jest",
-35
View File
@@ -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"],
},
},
};
};
-30
View File
@@ -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);
};
@@ -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;
};
@@ -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;
};
-51
View File
@@ -1,51 +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(),
};
galaxy.emit = {
debug: function () {},
error: function (v) {
window.console.error(v);
},
};
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;
}
},
};
@@ -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),
},
};
-529
View File
@@ -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,
};
-11
View File
@@ -1,11 +0,0 @@
// Load all qunit tests into a single bundle.
//var testsContext = require.context(".", true, /_tests$/);
//testsContext.keys().forEach(testsContext);
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";
@@ -1,76 +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("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");
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");
});
});
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();
existingApp.foo = 123;
var newApp = setGalaxyInstance((GalaxyApp) => {
return new GalaxyApp();
});
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");
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);
});
-563
View File
@@ -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);
});
-17
View File
@@ -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);
});
@@ -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");
});
-86
View File
@@ -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");
});
-896
View File
@@ -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);
});
-26
View File
@@ -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");
});
+25 -4
View File
@@ -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,27 @@ 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({
terserOptions: {
compress: {
drop_console: true,
},
},
}),
new CssMinimizerPlugin(),
],
};
} else {
minimizations = {
minimize: false,
};
}
const buildconfig = {
mode: targetEnv,
entry: {
@@ -84,8 +106,7 @@ module.exports = (env = {}, argv = {}) => {
},
},
},
minimize: true,
minimizer: [`...`, new CssMinimizerPlugin()],
...minimizations,
},
module: {
rules: [
@@ -272,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;
+18 -380
View File
@@ -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==
@@ -3645,15 +3620,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"
@@ -3709,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==
@@ -3797,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"
@@ -3946,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==
@@ -3966,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==
@@ -4028,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"
@@ -4084,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"
@@ -4129,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"
@@ -4357,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"
@@ -4638,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"
@@ -4655,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==
@@ -4841,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"
@@ -4902,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"
@@ -5054,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"
@@ -5091,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"
@@ -5704,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"
@@ -5808,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==
@@ -5881,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"
@@ -6045,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==
@@ -6100,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"
@@ -6128,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"
@@ -6147,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==
@@ -6987,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"
@@ -7639,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"
@@ -7676,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"
@@ -8009,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"
@@ -8239,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"
@@ -8302,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"
@@ -8445,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"
@@ -8537,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"
@@ -8646,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"
@@ -9452,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"
@@ -9484,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"
@@ -9883,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==
@@ -10278,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"
@@ -10489,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==
@@ -10517,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"
@@ -10817,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"
@@ -11077,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"
@@ -11181,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"
@@ -11308,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==
@@ -11361,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"
@@ -11611,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"
@@ -11783,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==
@@ -11864,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"
@@ -11942,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==
@@ -11960,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"