mirror of
https://github.com/lana-k/sqliteviz.git
synced 2026-05-07 04:19:19 +08:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3a75346fd | ||
|
|
366ffcff10 | ||
|
|
778db0d9eb | ||
|
|
c00d76904e | ||
|
|
4cc6ec86ea | ||
|
|
c0d972c2ab | ||
|
|
d9435a80c3 | ||
|
|
62fb92d824 | ||
|
|
5019013eff | ||
|
|
c2c376219f | ||
|
|
0199415dde | ||
|
|
534b186d76 | ||
|
|
4f6efb5bda | ||
|
|
2c0b8f9124 | ||
|
|
5265f5493e | ||
|
|
c0e59f6fb8 | ||
|
|
7471744633 | ||
|
|
e6e5efa8c6 | ||
|
|
57c36b3900 | ||
|
|
1e8c1761e6 | ||
|
|
dd30e17ff5 | ||
|
|
4e5adc147f | ||
|
|
7edc196a02 |
@@ -3,12 +3,12 @@
|
|||||||
# docker build -t sqliteviz/test -f Dockerfile.test .
|
# docker build -t sqliteviz/test -f Dockerfile.test .
|
||||||
#
|
#
|
||||||
|
|
||||||
FROM node:12.22-bullseye
|
FROM node:18.20.8-bookworm
|
||||||
|
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
apt update; \
|
apt update; \
|
||||||
apt install -y chromium firefox-esr; \
|
apt install -y chromium firefox-esr; \
|
||||||
npm install -g npm@7
|
npm install -g npm@10
|
||||||
|
|
||||||
WORKDIR /tmp/build
|
WORKDIR /tmp/build
|
||||||
|
|
||||||
@@ -19,6 +19,6 @@ RUN npm install
|
|||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
sed -i 's/browsers: \[.*\],/browsers: ['"'FirefoxHeadlessTouch'"'],/' karma.conf.js
|
sed -i 's/browsers: \[.*\],/browsers: ['"'FirefoxHeadlessTouch'"'],/' karma.conf.cjs
|
||||||
|
|
||||||
RUN npm run lint -- --no-fix && npm run test
|
RUN npm run lint -- --no-fix && npm run test
|
||||||
|
|||||||
@@ -92,11 +92,23 @@ module.exports = function (config) {
|
|||||||
'dom.w3c_touch_events.enabled': 1,
|
'dom.w3c_touch_events.enabled': 1,
|
||||||
'dom.events.asyncClipboard.clipboardItem': true
|
'dom.events.asyncClipboard.clipboardItem': true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
ChromiumHeadlessWebGL: {
|
||||||
|
base: 'ChromiumHeadless',
|
||||||
|
flags: [
|
||||||
|
'--headless=new',
|
||||||
|
'--use-angle=swiftshader',
|
||||||
|
'--use-gl=angle',
|
||||||
|
'--enable-webgl',
|
||||||
|
'--ignore-gpu-blocklist',
|
||||||
|
'--disable-gpu-sandbox',
|
||||||
|
'--no-sandbox'
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// start these browsers
|
// start these browsers
|
||||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||||
browsers: ['ChromiumHeadless', 'FirefoxHeadlessTouch'],
|
browsers: ['ChromiumHeadlessWebGL', 'FirefoxHeadlessTouch'],
|
||||||
|
|
||||||
// Continuous Integration mode
|
// Continuous Integration mode
|
||||||
// if true, Karma captures browsers, runs the tests and exits
|
// if true, Karma captures browsers, runs the tests and exits
|
||||||
|
|||||||
219
package-lock.json
generated
219
package-lock.json
generated
@@ -1,12 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "sqliteviz",
|
"name": "sqliteviz",
|
||||||
"version": "0.28.2",
|
"version": "0.29.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "sqliteviz",
|
"name": "sqliteviz",
|
||||||
"version": "0.28.2",
|
"version": "0.29.0",
|
||||||
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sigma/export-image": "^3.0.0",
|
"@sigma/export-image": "^3.0.0",
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
"react-chart-editor": "^0.46.1",
|
"react-chart-editor": "^0.46.1",
|
||||||
"react-dom": "^16.14.0",
|
"react-dom": "^16.14.0",
|
||||||
"seedrandom": "^3.0.5",
|
"seedrandom": "^3.0.5",
|
||||||
"sigma": "^3.0.1",
|
"sigma": "3.0.2",
|
||||||
"sql.js": "file:./lib/sql-js",
|
"sql.js": "file:./lib/sql-js",
|
||||||
"tiny-emitter": "^2.1.0",
|
"tiny-emitter": "^2.1.0",
|
||||||
"tinycolor2": "^1.4.2",
|
"tinycolor2": "^1.4.2",
|
||||||
@@ -64,6 +65,8 @@
|
|||||||
"karma-spec-reporter": "^0.0.36",
|
"karma-spec-reporter": "^0.0.36",
|
||||||
"karma-vite": "^1.0.5",
|
"karma-vite": "^1.0.5",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
|
"patch-package": "^8.0.1",
|
||||||
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"prettier": "3.5.3",
|
"prettier": "3.5.3",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"url-loader": "^4.1.1",
|
"url-loader": "^4.1.1",
|
||||||
@@ -3093,6 +3096,12 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@yarnpkg/lockfile": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/abbrev": {
|
"node_modules/abbrev": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
@@ -4776,6 +4785,21 @@
|
|||||||
"node": ">=6.0"
|
"node": ">=6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ci-info": {
|
||||||
|
"version": "3.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
|
||||||
|
"integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/sibiraj-s"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cipher-base": {
|
"node_modules/cipher-base": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -7809,6 +7833,15 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/find-yarn-workspace-root": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"micromatch": "^4.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/flat-cache": {
|
"node_modules/flat-cache": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -10358,11 +10391,36 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/json-stable-stringify": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.8",
|
||||||
|
"call-bound": "^1.0.4",
|
||||||
|
"isarray": "^2.0.5",
|
||||||
|
"jsonify": "^0.0.1",
|
||||||
|
"object-keys": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/json-stable-stringify-without-jsonify": {
|
"node_modules/json-stable-stringify-without-jsonify": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/json-stable-stringify/node_modules/isarray": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/json-stringify-pretty-compact": {
|
"node_modules/json-stringify-pretty-compact": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
@@ -10385,6 +10443,15 @@
|
|||||||
"graceful-fs": "^4.1.6"
|
"graceful-fs": "^4.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jsonify": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jsonpointer": {
|
"node_modules/jsonpointer": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -10683,6 +10750,15 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/klaw-sync": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.1.11"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lazy-cache": {
|
"node_modules/lazy-cache": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -12318,6 +12394,22 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/open": {
|
||||||
|
"version": "7.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
|
||||||
|
"integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"is-docker": "^2.0.0",
|
||||||
|
"is-wsl": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optimist": {
|
"node_modules/optimist": {
|
||||||
"version": "0.6.1",
|
"version": "0.6.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -12550,6 +12642,106 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/patch-package": {
|
||||||
|
"version": "8.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz",
|
||||||
|
"integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@yarnpkg/lockfile": "^1.1.0",
|
||||||
|
"chalk": "^4.1.2",
|
||||||
|
"ci-info": "^3.7.0",
|
||||||
|
"cross-spawn": "^7.0.3",
|
||||||
|
"find-yarn-workspace-root": "^2.0.0",
|
||||||
|
"fs-extra": "^10.0.0",
|
||||||
|
"json-stable-stringify": "^1.0.2",
|
||||||
|
"klaw-sync": "^6.0.0",
|
||||||
|
"minimist": "^1.2.6",
|
||||||
|
"open": "^7.4.2",
|
||||||
|
"semver": "^7.5.3",
|
||||||
|
"slash": "^2.0.0",
|
||||||
|
"tmp": "^0.2.4",
|
||||||
|
"yaml": "^2.2.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"patch-package": "index.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14",
|
||||||
|
"npm": ">5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/patch-package/node_modules/fs-extra": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/patch-package/node_modules/jsonfile": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/patch-package/node_modules/minimist": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/patch-package/node_modules/semver": {
|
||||||
|
"version": "7.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||||
|
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/patch-package/node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/patch-package/node_modules/yaml": {
|
||||||
|
"version": "2.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
|
||||||
|
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"yaml": "bin.mjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/eemeli"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-browserify": {
|
"node_modules/path-browserify": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -12994,6 +13186,13 @@
|
|||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/postinstall-postinstall": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true
|
||||||
|
},
|
||||||
"node_modules/potpack": {
|
"node_modules/potpack": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
@@ -14454,6 +14653,15 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/slash": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/smob": {
|
"node_modules/smob": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -15571,9 +15779,10 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/tmp": {
|
"node_modules/tmp": {
|
||||||
"version": "0.2.3",
|
"version": "0.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
||||||
|
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.14"
|
"node": ">=14.14"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sqliteviz",
|
"name": "sqliteviz",
|
||||||
"version": "0.28.2",
|
"version": "0.30.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -10,7 +10,8 @@
|
|||||||
"serve": "vite preview",
|
"serve": "vite preview",
|
||||||
"test": "karma start karma.conf.cjs",
|
"test": "karma start karma.conf.cjs",
|
||||||
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
|
"lint": "eslint --ext .js,.vue --ignore-path .gitignore --fix src",
|
||||||
"format": "prettier . --write"
|
"format": "prettier . --write",
|
||||||
|
"postinstall": "patch-package"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sigma/export-image": "^3.0.0",
|
"@sigma/export-image": "^3.0.0",
|
||||||
@@ -33,7 +34,7 @@
|
|||||||
"react-chart-editor": "^0.46.1",
|
"react-chart-editor": "^0.46.1",
|
||||||
"react-dom": "^16.14.0",
|
"react-dom": "^16.14.0",
|
||||||
"seedrandom": "^3.0.5",
|
"seedrandom": "^3.0.5",
|
||||||
"sigma": "^3.0.1",
|
"sigma": "3.0.2",
|
||||||
"sql.js": "file:./lib/sql-js",
|
"sql.js": "file:./lib/sql-js",
|
||||||
"tiny-emitter": "^2.1.0",
|
"tiny-emitter": "^2.1.0",
|
||||||
"tinycolor2": "^1.4.2",
|
"tinycolor2": "^1.4.2",
|
||||||
@@ -68,6 +69,8 @@
|
|||||||
"karma-spec-reporter": "^0.0.36",
|
"karma-spec-reporter": "^0.0.36",
|
||||||
"karma-vite": "^1.0.5",
|
"karma-vite": "^1.0.5",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
|
"patch-package": "^8.0.1",
|
||||||
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"prettier": "3.5.3",
|
"prettier": "3.5.3",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"url-loader": "^4.1.1",
|
"url-loader": "^4.1.1",
|
||||||
|
|||||||
24
patches/sigma+3.0.2.patch
Normal file
24
patches/sigma+3.0.2.patch
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
diff --git a/node_modules/sigma/dist/colors-beb06eb2.esm.js b/node_modules/sigma/dist/colors-beb06eb2.esm.js
|
||||||
|
index b7130d1..bf101b6 100644
|
||||||
|
--- a/node_modules/sigma/dist/colors-beb06eb2.esm.js
|
||||||
|
+++ b/node_modules/sigma/dist/colors-beb06eb2.esm.js
|
||||||
|
@@ -51,7 +51,6 @@ function _nonIterableRest() {
|
||||||
|
function _slicedToArray(r, e) {
|
||||||
|
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
|
||||||
|
}
|
||||||
|
-
|
||||||
|
var HTML_COLORS = {
|
||||||
|
black: "#000000",
|
||||||
|
silver: "#C0C0C0",
|
||||||
|
@@ -267,7 +266,10 @@ for (var htmlColor in HTML_COLORS) {
|
||||||
|
FLOAT_COLOR_CACHE[HTML_COLORS[htmlColor]] = FLOAT_COLOR_CACHE[htmlColor];
|
||||||
|
}
|
||||||
|
function rgbaToFloat(r, g, b, a, masking) {
|
||||||
|
- INT32[0] = a << 24 | b << 16 | g << 8 | r;
|
||||||
|
+ const r_= Math.floor(r * a / 255)
|
||||||
|
+ const g_= Math.floor(g * a / 255)
|
||||||
|
+ const b_= Math.floor(b * a / 255)
|
||||||
|
+ INT32[0] = a << 24 | b_ << 16 | g_ << 8 | r_;
|
||||||
|
if (masking) INT32[0] = INT32[0] & 0xfeffffff;
|
||||||
|
return FLOAT32[0];
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import CloseIcon from '@/components/svg/close'
|
import CloseIcon from '@/components/svg/close'
|
||||||
import { version } from '../../../package.json'
|
import { version } from '../../package.json'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AppDiagnosticInfo',
|
name: 'AppDiagnosticInfo',
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import tooltipMixin from '@/tooltipMixin'
|
import tooltipMixin from '@/tooltipMixin'
|
||||||
import LoadingIndicator from '@/components/LoadingIndicator'
|
import LoadingIndicator from '@/components/Common/LoadingIndicator'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SideBarButton',
|
name: 'SideBarButton',
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LoadingIndicator from '@/components/LoadingIndicator'
|
import LoadingIndicator from '@/components/Common/LoadingIndicator'
|
||||||
import CloseIcon from '@/components/svg/close'
|
import CloseIcon from '@/components/svg/close'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import LoadingIndicator from '@/components/LoadingIndicator'
|
import LoadingIndicator from '@/components/Common/LoadingIndicator'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Logs',
|
name: 'Logs',
|
||||||
@@ -13,16 +13,16 @@
|
|||||||
:style="movableSplitterStyle"
|
:style="movableSplitterStyle"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
v-show="!before.hidden"
|
||||||
ref="left"
|
ref="left"
|
||||||
class="splitpanes-pane"
|
class="splitpanes-pane"
|
||||||
:size="paneBefore.size"
|
|
||||||
max-size="30"
|
|
||||||
:style="styles.before"
|
:style="styles.before"
|
||||||
>
|
>
|
||||||
<slot name="left-pane" />
|
<slot name="left-pane" />
|
||||||
</div>
|
</div>
|
||||||
<!-- Splitter start-->
|
<!-- Splitter start-->
|
||||||
<div
|
<div
|
||||||
|
v-show="!before.hidden && !after.hidden"
|
||||||
class="splitpanes-splitter"
|
class="splitpanes-splitter"
|
||||||
@mousedown="bindEvents"
|
@mousedown="bindEvents"
|
||||||
@touchstart="bindEvents"
|
@touchstart="bindEvents"
|
||||||
@@ -64,7 +64,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- splitter end -->
|
<!-- splitter end -->
|
||||||
<div ref="right" class="splitpanes-pane" :style="styles.after">
|
<div
|
||||||
|
v-show="!after.hidden"
|
||||||
|
ref="right"
|
||||||
|
class="splitpanes-pane"
|
||||||
|
:style="styles.after"
|
||||||
|
>
|
||||||
<slot name="right-pane" />
|
<slot name="right-pane" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,10 +119,12 @@ export default {
|
|||||||
styles() {
|
styles() {
|
||||||
return {
|
return {
|
||||||
before: {
|
before: {
|
||||||
[this.horizontal ? 'height' : 'width']: `${this.paneBefore.size}%`
|
[this.horizontal ? 'height' : 'width']:
|
||||||
|
`${this.after.hidden ? 100 : this.paneBefore.size}%`
|
||||||
},
|
},
|
||||||
after: {
|
after: {
|
||||||
[this.horizontal ? 'height' : 'width']: `${this.paneAfter.size}%`
|
[this.horizontal ? 'height' : 'width']:
|
||||||
|
`${this.before.hidden ? 100 : this.paneAfter.size}%`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -102,11 +102,11 @@
|
|||||||
<script>
|
<script>
|
||||||
import csv from '@/lib/csv'
|
import csv from '@/lib/csv'
|
||||||
import CloseIcon from '@/components/svg/close'
|
import CloseIcon from '@/components/svg/close'
|
||||||
import TextField from '@/components/TextField'
|
import TextField from '@/components/Common/TextField'
|
||||||
import DelimiterSelector from './DelimiterSelector'
|
import DelimiterSelector from './DelimiterSelector'
|
||||||
import CheckBox from '@/components/CheckBox'
|
import CheckBox from '@/components/Common/CheckBox'
|
||||||
import SqlTable from '@/components/SqlTable'
|
import SqlTable from '@/components/SqlTable'
|
||||||
import Logs from '@/components/Logs'
|
import Logs from '@/components/Common/Logs'
|
||||||
import time from '@/lib/utils/time'
|
import time from '@/lib/utils/time'
|
||||||
import fIo from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
:initOptions="initOptionsByMode[mode]"
|
:initOptions="initOptionsByMode[mode]"
|
||||||
:data-sources="dataSource"
|
:data-sources="dataSource"
|
||||||
:showViewSettings="showViewSettings"
|
:showViewSettings="showViewSettings"
|
||||||
|
:showValueViewer="viewValuePanelVisible"
|
||||||
@loading-image-completed="loadingImage = false"
|
@loading-image-completed="loadingImage = false"
|
||||||
@update="$emit('update')"
|
@update="$emit('update')"
|
||||||
/>
|
/>
|
||||||
@@ -56,6 +57,17 @@
|
|||||||
<settings-icon />
|
<settings-icon />
|
||||||
</icon-button>
|
</icon-button>
|
||||||
|
|
||||||
|
<icon-button
|
||||||
|
v-if="mode === 'graph'"
|
||||||
|
ref="viewNodeOrEdgeBtn"
|
||||||
|
tooltip="View node or edge details"
|
||||||
|
tooltipPosition="top-left"
|
||||||
|
:active="viewValuePanelVisible"
|
||||||
|
@click="viewValuePanelVisible = !viewValuePanelVisible"
|
||||||
|
>
|
||||||
|
<view-cell-value-icon />
|
||||||
|
</icon-button>
|
||||||
|
|
||||||
<div class="side-tool-bar-divider" />
|
<div class="side-tool-bar-divider" />
|
||||||
|
|
||||||
<icon-button
|
<icon-button
|
||||||
@@ -113,11 +125,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Chart from './Chart/index.vue'
|
import Chart from '@/components/Chart.vue'
|
||||||
import Pivot from './Pivot/index.vue'
|
import Pivot from '@/components/Pivot'
|
||||||
import Graph from './Graph/index.vue'
|
import Graph from '@/components/Graph/index.vue'
|
||||||
import SideToolBar from '../SideToolBar'
|
import SideToolBar from '@/components/SideToolBar'
|
||||||
import IconButton from '@/components/IconButton'
|
import IconButton from '@/components/Common/IconButton'
|
||||||
import ChartIcon from '@/components/svg/chart'
|
import ChartIcon from '@/components/svg/chart'
|
||||||
import PivotIcon from '@/components/svg/pivot'
|
import PivotIcon from '@/components/svg/pivot'
|
||||||
import GraphIcon from '@/components/svg/graph.vue'
|
import GraphIcon from '@/components/svg/graph.vue'
|
||||||
@@ -126,8 +138,9 @@ import HtmlIcon from '@/components/svg/html'
|
|||||||
import ExportToSvgIcon from '@/components/svg/exportToSvg'
|
import ExportToSvgIcon from '@/components/svg/exportToSvg'
|
||||||
import PngIcon from '@/components/svg/png'
|
import PngIcon from '@/components/svg/png'
|
||||||
import ClipboardIcon from '@/components/svg/clipboard'
|
import ClipboardIcon from '@/components/svg/clipboard'
|
||||||
|
import ViewCellValueIcon from '@/components/svg/viewCellValue'
|
||||||
import cIo from '@/lib/utils/clipboardIo'
|
import cIo from '@/lib/utils/clipboardIo'
|
||||||
import loadingDialog from '@/components/LoadingDialog.vue'
|
import loadingDialog from '@/components/Common/LoadingDialog.vue'
|
||||||
import time from '@/lib/utils/time'
|
import time from '@/lib/utils/time'
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
|
|
||||||
@@ -144,6 +157,7 @@ export default {
|
|||||||
GraphIcon,
|
GraphIcon,
|
||||||
SettingsIcon,
|
SettingsIcon,
|
||||||
ExportToSvgIcon,
|
ExportToSvgIcon,
|
||||||
|
ViewCellValueIcon,
|
||||||
PngIcon,
|
PngIcon,
|
||||||
HtmlIcon,
|
HtmlIcon,
|
||||||
ClipboardIcon,
|
ClipboardIcon,
|
||||||
@@ -172,7 +186,8 @@ export default {
|
|||||||
graph: this.initMode === 'graph' ? this.initOptions : null
|
graph: this.initMode === 'graph' ? this.initOptions : null
|
||||||
},
|
},
|
||||||
showLoadingDialog: false,
|
showLoadingDialog: false,
|
||||||
showViewSettings: true
|
showViewSettings: true,
|
||||||
|
viewValuePanelVisible: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
</multiselect>
|
</multiselect>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
<Field label="Seed value">
|
<Field label="Seed value" fieldContainerClassName="test_seed_value">
|
||||||
<NumericInput
|
<NumericInput
|
||||||
:value="modelValue.seedValue"
|
:value="modelValue.seedValue"
|
||||||
@update="update('seedValue', $event)"
|
@update="update('seedValue', $event)"
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ export default {
|
|||||||
]),
|
]),
|
||||||
сolorscaleDirections: markRaw([
|
сolorscaleDirections: markRaw([
|
||||||
{ label: 'Normal', value: 'normal' },
|
{ label: 'Normal', value: 'normal' },
|
||||||
{ label: 'Recersed', value: 'reversed' }
|
{ label: 'Reversed', value: 'reversed' }
|
||||||
]),
|
]),
|
||||||
colorSourceUsageOptions: markRaw([
|
colorSourceUsageOptions: markRaw([
|
||||||
{ label: 'Direct', value: 'direct' },
|
{ label: 'Direct', value: 'direct' },
|
||||||
|
|||||||
86
src/components/Graph/ForceAtlasSeedLayoutSettings.vue
Normal file
86
src/components/Graph/ForceAtlasSeedLayoutSettings.vue
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<Field label="Initial algorithm">
|
||||||
|
<Dropdown
|
||||||
|
:options="layoutOptions"
|
||||||
|
:value="modelValue.initialAlgorithm"
|
||||||
|
:clearable="false"
|
||||||
|
className="test_fa2_initial_layout_algorithm_select"
|
||||||
|
@change="updateInitialAlgorithm"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
|
<component
|
||||||
|
:is="layoutSettingsComponentMap[modelValue.initialAlgorithm]"
|
||||||
|
v-if="modelValue.initialAlgorithm !== 'circular'"
|
||||||
|
:modelValue="modelValue"
|
||||||
|
:keyOptions="keyOptions"
|
||||||
|
@update:model-value="$emit('update:modelValue', $event)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { markRaw } from 'vue'
|
||||||
|
import { applyPureReactInVue } from 'veaury'
|
||||||
|
import Field from 'react-chart-editor/lib/components/fields/Field'
|
||||||
|
import NumericInput from 'react-chart-editor/lib/components/widgets/NumericInput'
|
||||||
|
import Dropdown from 'react-chart-editor/lib/components/widgets/Dropdown'
|
||||||
|
import 'react-chart-editor/lib/react-chart-editor.css'
|
||||||
|
import CirclePackLayoutSettings from '@/components/Graph/CirclePackLayoutSettings.vue'
|
||||||
|
import RandomLayoutSettings from '@/components/Graph/RandomLayoutSettings.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Field: applyPureReactInVue(Field),
|
||||||
|
Dropdown: applyPureReactInVue(Dropdown),
|
||||||
|
NumericInput: applyPureReactInVue(NumericInput),
|
||||||
|
RandomLayoutSettings,
|
||||||
|
CirclePackLayoutSettings
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: Object,
|
||||||
|
keyOptions: Array
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
layoutOptions: markRaw([
|
||||||
|
{ label: 'Circular', value: 'circular' },
|
||||||
|
{ label: 'Random', value: 'random' },
|
||||||
|
{ label: 'Circle pack', value: 'circlepack' }
|
||||||
|
]),
|
||||||
|
layoutSettingsComponentMap: markRaw({
|
||||||
|
random: RandomLayoutSettings,
|
||||||
|
circlepack: CirclePackLayoutSettings
|
||||||
|
}),
|
||||||
|
defaultSeedLayoutSettings: {
|
||||||
|
circular: { initialAlgorithm: 'circular' },
|
||||||
|
random: { initialAlgorithm: 'random', seedValue: 1 },
|
||||||
|
circlepack: {
|
||||||
|
initialAlgorithm: 'circlepack',
|
||||||
|
hierarchyAttributes: [],
|
||||||
|
seedValue: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateInitialAlgorithm(newAlgorithm) {
|
||||||
|
const newSettings = {
|
||||||
|
...this.modelValue
|
||||||
|
}
|
||||||
|
|
||||||
|
const prevAlgorithmSettings =
|
||||||
|
this.defaultSeedLayoutSettings[this.modelValue.initialAlgorithm]
|
||||||
|
Object.keys(prevAlgorithmSettings).forEach(key => {
|
||||||
|
delete newSettings[key]
|
||||||
|
prevAlgorithmSettings[key] = this.modelValue[key]
|
||||||
|
})
|
||||||
|
|
||||||
|
this.$emit('update:modelValue', {
|
||||||
|
...newSettings,
|
||||||
|
...this.defaultSeedLayoutSettings[newAlgorithm]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -67,6 +67,15 @@
|
|||||||
@color-change="settings.style.backgroundColor = $event"
|
@color-change="settings.style.backgroundColor = $event"
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
<Field label="Highlight mode">
|
||||||
|
<Dropdown
|
||||||
|
:options="highlightModeOptions"
|
||||||
|
:value="settings.style.highlightMode"
|
||||||
|
className="test_highlight_mode_select"
|
||||||
|
@change="updateHighlightNodeMode"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
</Fold>
|
</Fold>
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel group="Style" name="Nodes">
|
<Panel group="Style" name="Nodes">
|
||||||
@@ -162,6 +171,17 @@
|
|||||||
/>
|
/>
|
||||||
</Fold>
|
</Fold>
|
||||||
<template v-if="settings.layout.type === 'forceAtlas2'">
|
<template v-if="settings.layout.type === 'forceAtlas2'">
|
||||||
|
<Fold name="Seed layout">
|
||||||
|
<Field>
|
||||||
|
If you already built a graph using another layout, the initial
|
||||||
|
algorithm doesn't apply unless you restart it.
|
||||||
|
</Field>
|
||||||
|
<ForceAtlasSeedLayoutSettings
|
||||||
|
v-model="settings.layout.options"
|
||||||
|
:keyOptions="keysOptions"
|
||||||
|
@update:model-value="updateLayout(settings.layout.type)"
|
||||||
|
/>
|
||||||
|
</Fold>
|
||||||
<Fold name="Advanced layout settings">
|
<Fold name="Advanced layout settings">
|
||||||
<AdvancedForceAtlasLayoutSettings
|
<AdvancedForceAtlasLayoutSettings
|
||||||
v-model="settings.layout.options"
|
v-model="settings.layout.options"
|
||||||
@@ -173,6 +193,7 @@
|
|||||||
<Button
|
<Button
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
class="test_fa2_reset"
|
class="test_fa2_reset"
|
||||||
|
title="Set the settings to default or previously saved ones."
|
||||||
@click="resetFA2LayoutSettings"
|
@click="resetFA2LayoutSettings"
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
@@ -183,16 +204,25 @@
|
|||||||
@click="toggleFA2Layout"
|
@click="toggleFA2Layout"
|
||||||
>
|
>
|
||||||
<template #node:icon>
|
<template #node:icon>
|
||||||
<div
|
<div>
|
||||||
:style="{
|
|
||||||
padding: '0 3px'
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<RunIcon v-if="!fa2Running" />
|
<RunIcon v-if="!fa2Running" />
|
||||||
<StopIcon v-else />
|
<PauseIcon v-else />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
{{ fa2Running ? 'Stop' : 'Start' }}
|
{{ fa2Running ? 'Pause' : 'Continue' }}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
class="test_fa2_restart"
|
||||||
|
title="Clear node coordinates and run the layout algorithm anew."
|
||||||
|
@click="restartFA2Layout"
|
||||||
|
>
|
||||||
|
<template #node:icon>
|
||||||
|
<div>
|
||||||
|
<RestartIcon />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
Restart
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -225,19 +255,24 @@ import Button from 'react-chart-editor/lib/components/widgets/Button'
|
|||||||
import Field from 'react-chart-editor/lib/components/fields/Field'
|
import Field from 'react-chart-editor/lib/components/fields/Field'
|
||||||
import RandomLayoutSettings from '@/components/Graph/RandomLayoutSettings.vue'
|
import RandomLayoutSettings from '@/components/Graph/RandomLayoutSettings.vue'
|
||||||
import ForceAtlasLayoutSettings from '@/components/Graph/ForceAtlasLayoutSettings.vue'
|
import ForceAtlasLayoutSettings from '@/components/Graph/ForceAtlasLayoutSettings.vue'
|
||||||
|
import ForceAtlasSeedLayoutSettings from '@/components/Graph/ForceAtlasSeedLayoutSettings.vue'
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
import AdvancedForceAtlasLayoutSettings from '@/components/Graph/AdvancedForceAtlasLayoutSettings.vue'
|
import AdvancedForceAtlasLayoutSettings from '@/components/Graph/AdvancedForceAtlasLayoutSettings.vue'
|
||||||
import CirclePackLayoutSettings from '@/components/Graph/CirclePackLayoutSettings.vue'
|
import CirclePackLayoutSettings from '@/components/Graph/CirclePackLayoutSettings.vue'
|
||||||
import FA2Layout from 'graphology-layout-forceatlas2/worker'
|
import FA2Layout from 'graphology-layout-forceatlas2/worker'
|
||||||
import * as forceAtlas2 from 'graphology-layout-forceatlas2'
|
import * as forceAtlas2 from 'graphology-layout-forceatlas2'
|
||||||
import RunIcon from '@/components/svg/run.vue'
|
import RunIcon from '@/components/svg/run.vue'
|
||||||
import StopIcon from '@/components/svg/stop.vue'
|
import RestartIcon from '@/components/svg/restart.vue'
|
||||||
|
import PauseIcon from '@/components/svg/pause.vue'
|
||||||
import { downloadAsPNG, drawOnCanvas } from '@sigma/export-image'
|
import { downloadAsPNG, drawOnCanvas } from '@sigma/export-image'
|
||||||
import {
|
import {
|
||||||
buildNodes,
|
buildNodes,
|
||||||
buildEdges,
|
buildEdges,
|
||||||
updateNodes,
|
updateNodes,
|
||||||
updateEdges
|
updateEdges,
|
||||||
|
reduceNodes,
|
||||||
|
reduceEdges,
|
||||||
|
clearNodeCoordinates
|
||||||
} from '@/lib/graphHelper'
|
} from '@/lib/graphHelper'
|
||||||
import Graph from 'graphology'
|
import Graph from 'graphology'
|
||||||
import { circular, random, circlepack } from 'graphology-layout'
|
import { circular, random, circlepack } from 'graphology-layout'
|
||||||
@@ -262,14 +297,16 @@ export default {
|
|||||||
Button: applyPureReactInVue(Button),
|
Button: applyPureReactInVue(Button),
|
||||||
ColorPicker: applyPureReactInVue(ColorPicker),
|
ColorPicker: applyPureReactInVue(ColorPicker),
|
||||||
RunIcon,
|
RunIcon,
|
||||||
StopIcon,
|
RestartIcon,
|
||||||
|
PauseIcon,
|
||||||
RandomLayoutSettings,
|
RandomLayoutSettings,
|
||||||
CirclePackLayoutSettings,
|
CirclePackLayoutSettings,
|
||||||
NodeColorSettings,
|
NodeColorSettings,
|
||||||
NodeSizeSettings,
|
NodeSizeSettings,
|
||||||
EdgeSizeSettings,
|
EdgeSizeSettings,
|
||||||
EdgeColorSettings,
|
EdgeColorSettings,
|
||||||
AdvancedForceAtlasLayoutSettings
|
AdvancedForceAtlasLayoutSettings,
|
||||||
|
ForceAtlasSeedLayoutSettings
|
||||||
},
|
},
|
||||||
inject: ['tabLayout'],
|
inject: ['tabLayout'],
|
||||||
props: {
|
props: {
|
||||||
@@ -277,7 +314,7 @@ export default {
|
|||||||
initOptions: Object,
|
initOptions: Object,
|
||||||
showViewSettings: Boolean
|
showViewSettings: Boolean
|
||||||
},
|
},
|
||||||
emits: ['update'],
|
emits: ['update', 'selectItem', 'clearSelection'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
graph: new Graph({ multi: true, allowSelfLoops: true }),
|
graph: new Graph({ multi: true, allowSelfLoops: true }),
|
||||||
@@ -300,7 +337,16 @@ export default {
|
|||||||
circlepack: CirclePackLayoutSettings,
|
circlepack: CirclePackLayoutSettings,
|
||||||
forceAtlas2: ForceAtlasLayoutSettings
|
forceAtlas2: ForceAtlasLayoutSettings
|
||||||
}),
|
}),
|
||||||
|
layoutMethodMap: markRaw({
|
||||||
|
circular: this.applyCircularLayout,
|
||||||
|
random: this.applyRandomLayout,
|
||||||
|
circlepack: this.applyCirclePackLayout,
|
||||||
|
forceAtlas2: this.applyFA2Layout
|
||||||
|
}),
|
||||||
|
selectedNodeId: undefined,
|
||||||
|
hoveredNodeId: undefined,
|
||||||
|
selectedEdgeId: undefined,
|
||||||
|
hoveredEdgeId: undefined,
|
||||||
settings: this.initOptions
|
settings: this.initOptions
|
||||||
? JSON.parse(JSON.stringify(this.initOptions))
|
? JSON.parse(JSON.stringify(this.initOptions))
|
||||||
: {
|
: {
|
||||||
@@ -312,6 +358,7 @@ export default {
|
|||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: 'white',
|
backgroundColor: 'white',
|
||||||
|
highlightMode: 'node_and_neighbors',
|
||||||
nodes: {
|
nodes: {
|
||||||
size: {
|
size: {
|
||||||
type: 'constant',
|
type: 'constant',
|
||||||
@@ -319,7 +366,8 @@ export default {
|
|||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: 'constant',
|
type: 'constant',
|
||||||
value: '#1F77B4'
|
value: '#1F77B4',
|
||||||
|
opacity: 100
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
source: null,
|
source: null,
|
||||||
@@ -351,7 +399,15 @@ export default {
|
|||||||
random: null,
|
random: null,
|
||||||
circlepack: null,
|
circlepack: null,
|
||||||
forceAtlas2: null
|
forceAtlas2: null
|
||||||
|
},
|
||||||
|
highlightModeOptions: markRaw([
|
||||||
|
{ label: 'Node alone', value: 'node_alone' },
|
||||||
|
{ label: 'Node and neighbors', value: 'node_and_neighbors' },
|
||||||
|
{
|
||||||
|
label: 'Include edges between neighbors',
|
||||||
|
value: 'include_neighbor_edges'
|
||||||
}
|
}
|
||||||
|
])
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -378,6 +434,46 @@ export default {
|
|||||||
}, new Set())
|
}, new Set())
|
||||||
|
|
||||||
return Array.from(keySet)
|
return Array.from(keySet)
|
||||||
|
},
|
||||||
|
neighborsOfSelectedNode() {
|
||||||
|
if (this.settings.style.highlightMode === 'node_alone') {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return this.selectedNodeId
|
||||||
|
? new Set(this.graph.neighbors(this.selectedNodeId))
|
||||||
|
: undefined
|
||||||
|
},
|
||||||
|
neighborsOfHoveredNode() {
|
||||||
|
if (this.settings.style.highlightMode === 'node_alone') {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
return this.hoveredNodeId
|
||||||
|
? new Set(this.graph.neighbors(this.hoveredNodeId))
|
||||||
|
: undefined
|
||||||
|
},
|
||||||
|
hoveredEdgeExtremities() {
|
||||||
|
return this.hoveredEdgeId
|
||||||
|
? this.graph.extremities(this.hoveredEdgeId)
|
||||||
|
: []
|
||||||
|
},
|
||||||
|
selectedEdgeExtremities() {
|
||||||
|
return this.selectedEdgeId
|
||||||
|
? this.graph.extremities(this.selectedEdgeId)
|
||||||
|
: []
|
||||||
|
},
|
||||||
|
interactionState() {
|
||||||
|
return {
|
||||||
|
selectedNodeId: this.selectedNodeId,
|
||||||
|
hoveredNodeId: this.hoveredNodeId,
|
||||||
|
selectedEdgeId: this.selectedEdgeId,
|
||||||
|
hoveredEdgeId: this.hoveredEdgeId,
|
||||||
|
|
||||||
|
neighborsOfSelectedNode: this.neighborsOfSelectedNode,
|
||||||
|
neighborsOfHoveredNode: this.neighborsOfHoveredNode,
|
||||||
|
|
||||||
|
hoveredEdgeExtremities: this.hoveredEdgeExtremities,
|
||||||
|
selectedEdgeExtremities: this.selectedEdgeExtremities
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -422,6 +518,7 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
buildGraph() {
|
buildGraph() {
|
||||||
|
this.clearSelection()
|
||||||
if (this.renderer) {
|
if (this.renderer) {
|
||||||
this.renderer.kill()
|
this.renderer.kill()
|
||||||
}
|
}
|
||||||
@@ -439,12 +536,85 @@ export default {
|
|||||||
renderEdgeLabels: true,
|
renderEdgeLabels: true,
|
||||||
allowInvalidContainer: true,
|
allowInvalidContainer: true,
|
||||||
labelColor: { attribute: 'labelColor', color: '#444444' },
|
labelColor: { attribute: 'labelColor', color: '#444444' },
|
||||||
edgeLabelColor: { attribute: 'labelColor', color: '#a2b1c6' }
|
edgeLabelColor: { attribute: 'labelColor', color: '#a2b1c6' },
|
||||||
|
enableEdgeEvents: true,
|
||||||
|
zIndex: true,
|
||||||
|
nodeReducer: (node, data) =>
|
||||||
|
reduceNodes(node, data, this.interactionState, this.settings),
|
||||||
|
edgeReducer: (edge, data) =>
|
||||||
|
reduceEdges(
|
||||||
|
edge,
|
||||||
|
data,
|
||||||
|
this.interactionState,
|
||||||
|
this.settings,
|
||||||
|
this.graph
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
this.renderer.on('clickNode', ({ node }) => {
|
||||||
|
this.selectedNodeId = node
|
||||||
|
this.selectedEdgeId = undefined
|
||||||
|
this.$emit('selectItem', this.graph.getNodeAttributes(node).data)
|
||||||
|
this.renderer.refresh({
|
||||||
|
skipIndexation: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.renderer.on('clickEdge', ({ edge }) => {
|
||||||
|
this.selectedEdgeId = edge
|
||||||
|
this.selectedNodeId = undefined
|
||||||
|
this.$emit('selectItem', this.graph.getEdgeAttributes(edge).data)
|
||||||
|
this.renderer.refresh({
|
||||||
|
skipIndexation: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.renderer.on('clickStage', () => {
|
||||||
|
this.clearSelection()
|
||||||
|
this.renderer.refresh({
|
||||||
|
skipIndexation: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.renderer.on('enterNode', ({ node }) => {
|
||||||
|
this.hoveredNodeId = node
|
||||||
|
this.renderer.refresh({
|
||||||
|
skipIndexation: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.renderer.on('enterEdge', ({ edge }) => {
|
||||||
|
this.hoveredEdgeId = edge
|
||||||
|
this.renderer.refresh({
|
||||||
|
skipIndexation: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.renderer.on('leaveNode', () => {
|
||||||
|
this.hoveredNodeId = undefined
|
||||||
|
this.renderer.refresh({
|
||||||
|
skipIndexation: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.renderer.on('leaveEdge', () => {
|
||||||
|
this.hoveredEdgeId = undefined
|
||||||
|
this.renderer.refresh({
|
||||||
|
skipIndexation: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
if (this.settings.layout.type === 'forceAtlas2') {
|
if (this.settings.layout.type === 'forceAtlas2') {
|
||||||
this.autoRunFA2Layout()
|
this.autoRunFA2Layout()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
clearSelection() {
|
||||||
|
this.selectedNodeId = undefined
|
||||||
|
this.selectedEdgeId = undefined
|
||||||
|
this.$emit('clearSelection')
|
||||||
|
},
|
||||||
|
updateHighlightNodeMode(mode) {
|
||||||
|
this.settings.style.highlightMode = mode
|
||||||
|
|
||||||
|
if (this.renderer) {
|
||||||
|
this.renderer.refresh({
|
||||||
|
skipIndexation: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
updateStructure(attributeName, value) {
|
updateStructure(attributeName, value) {
|
||||||
this.settings.structure[attributeName] = value
|
this.settings.structure[attributeName] = value
|
||||||
},
|
},
|
||||||
@@ -504,19 +674,21 @@ export default {
|
|||||||
this.fa2Layout.kill()
|
this.fa2Layout.kill()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (layoutType === 'circular') {
|
this.layoutMethodMap[layoutType]()
|
||||||
circular.assign(this.graph)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layoutType === 'random') {
|
if (layoutType === 'forceAtlas2' && layoutType !== prevLayout) {
|
||||||
|
this.autoRunFA2Layout()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
applyCircularLayout() {
|
||||||
|
circular.assign(this.graph)
|
||||||
|
},
|
||||||
|
applyRandomLayout() {
|
||||||
random.assign(this.graph, {
|
random.assign(this.graph, {
|
||||||
rng: seedrandom(this.settings.layout.options.seedValue)
|
rng: seedrandom(this.settings.layout.options.seedValue)
|
||||||
})
|
})
|
||||||
return
|
},
|
||||||
}
|
applyCirclePackLayout() {
|
||||||
|
|
||||||
if (layoutType === 'circlepack') {
|
|
||||||
this.graph.forEachNode(nodeId => {
|
this.graph.forEachNode(nodeId => {
|
||||||
this.graph.updateNode(nodeId, attributes => {
|
this.graph.updateNode(nodeId, attributes => {
|
||||||
const newAttributes = { ...attributes }
|
const newAttributes = { ...attributes }
|
||||||
@@ -546,33 +718,32 @@ export default {
|
|||||||
) || [],
|
) || [],
|
||||||
rng: seedrandom(this.settings.layout.options.seedValue)
|
rng: seedrandom(this.settings.layout.options.seedValue)
|
||||||
})
|
})
|
||||||
return
|
},
|
||||||
}
|
applyFA2Layout() {
|
||||||
|
|
||||||
if (layoutType === 'forceAtlas2') {
|
|
||||||
if (
|
if (
|
||||||
!this.graph.someNode(
|
!this.graph.someNode(
|
||||||
(nodeKey, attributes) =>
|
(nodeKey, attributes) =>
|
||||||
typeof attributes.x === 'number' &&
|
typeof attributes.x === 'number' && typeof attributes.y === 'number'
|
||||||
typeof attributes.y === 'number'
|
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
circular.assign(this.graph)
|
this.layoutMethodMap[this.settings.layout.options.initialAlgorithm]()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fa2settings = { ...this.settings.layout.options }
|
||||||
|
// delete all custom settings because they can break the algorithm running
|
||||||
|
delete fa2settings.initialAlgorithm
|
||||||
|
delete fa2settings.seedValue
|
||||||
|
delete fa2settings.initialIterationsAmount
|
||||||
|
delete fa2settings.hierarchyAttributes
|
||||||
this.fa2Layout = markRaw(
|
this.fa2Layout = markRaw(
|
||||||
new FA2Layout(this.graph, {
|
new FA2Layout(this.graph, {
|
||||||
getEdgeWeight: (_, attr) =>
|
getEdgeWeight: (_, attr) =>
|
||||||
this.settings.layout.options.weightSource
|
this.settings.layout.options.weightSource
|
||||||
? attr.data[this.settings.layout.options.weightSource]
|
? attr.data[this.settings.layout.options.weightSource]
|
||||||
: 1,
|
: 1,
|
||||||
settings: this.settings.layout.options
|
settings: fa2settings
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
if (layoutType !== prevLayout) {
|
|
||||||
this.autoRunFA2Layout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
toggleFA2Layout() {
|
toggleFA2Layout() {
|
||||||
if (this.fa2Layout.isRunning()) {
|
if (this.fa2Layout.isRunning()) {
|
||||||
@@ -593,6 +764,15 @@ export default {
|
|||||||
this.checkIteration = null
|
this.checkIteration = null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
restartFA2Layout() {
|
||||||
|
if (this.fa2Layout.isRunning()) {
|
||||||
|
this.stopFA2Layout()
|
||||||
|
}
|
||||||
|
this.fa2Layout.kill()
|
||||||
|
clearNodeCoordinates(this.graph)
|
||||||
|
this.applyFA2Layout()
|
||||||
|
this.autoRunFA2Layout()
|
||||||
|
},
|
||||||
autoRunFA2Layout() {
|
autoRunFA2Layout() {
|
||||||
let iteration = 1
|
let iteration = 1
|
||||||
this.checkIteration = () => {
|
this.checkIteration = () => {
|
||||||
@@ -610,6 +790,7 @@ export default {
|
|||||||
setRecommendedFA2Settings() {
|
setRecommendedFA2Settings() {
|
||||||
const sensibleSettings = forceAtlas2.default.inferSettings(this.graph)
|
const sensibleSettings = forceAtlas2.default.inferSettings(this.graph)
|
||||||
this.settings.layout.options = {
|
this.settings.layout.options = {
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
initialIterationsAmount: 50,
|
initialIterationsAmount: 50,
|
||||||
adjustSizes: false,
|
adjustSizes: false,
|
||||||
barnesHutOptimize: false,
|
barnesHutOptimize: false,
|
||||||
@@ -671,4 +852,8 @@ export default {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
flex-basis: 0;
|
flex-basis: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.force-atlas-buttons :deep(.button__icon > div) {
|
||||||
|
padding: 0 3px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -57,10 +57,20 @@
|
|||||||
</template>
|
</template>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
<Field label="Opacity" fieldContainerClassName="test_node_opacity">
|
||||||
|
<NumericInput
|
||||||
|
:value="modelValue.opacity"
|
||||||
|
:showSlider="true"
|
||||||
|
:integerOnly="true"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
units="%"
|
||||||
|
@update="updateSettings('opacity', $event)"
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
v-if="
|
v-if="modelValue.type === 'map_to' || modelValue.type === 'calculated'"
|
||||||
modelValue.sourceUsage === 'map_to' || modelValue.type === 'calculated'
|
|
||||||
"
|
|
||||||
label="Color as"
|
label="Color as"
|
||||||
fieldContainerClassName="test_node_color_as"
|
fieldContainerClassName="test_node_color_as"
|
||||||
>
|
>
|
||||||
@@ -89,6 +99,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { markRaw } from 'vue'
|
import { markRaw } from 'vue'
|
||||||
import { applyPureReactInVue } from 'veaury'
|
import { applyPureReactInVue } from 'veaury'
|
||||||
|
import NumericInput from 'react-chart-editor/lib/components/widgets/NumericInput'
|
||||||
import Dropdown from 'react-chart-editor/lib/components/widgets/Dropdown'
|
import Dropdown from 'react-chart-editor/lib/components/widgets/Dropdown'
|
||||||
import RadioBlocks from 'react-chart-editor/lib/components/widgets/RadioBlocks'
|
import RadioBlocks from 'react-chart-editor/lib/components/widgets/RadioBlocks'
|
||||||
import ColorscalePicker from 'react-chart-editor/lib/components/widgets/ColorscalePicker'
|
import ColorscalePicker from 'react-chart-editor/lib/components/widgets/ColorscalePicker'
|
||||||
@@ -98,6 +109,7 @@ import 'react-chart-editor/lib/react-chart-editor.css'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
NumericInput: applyPureReactInVue(NumericInput),
|
||||||
Dropdown: applyPureReactInVue(Dropdown),
|
Dropdown: applyPureReactInVue(Dropdown),
|
||||||
RadioBlocks: applyPureReactInVue(RadioBlocks),
|
RadioBlocks: applyPureReactInVue(RadioBlocks),
|
||||||
Field: applyPureReactInVue(Field),
|
Field: applyPureReactInVue(Field),
|
||||||
@@ -127,26 +139,28 @@ export default {
|
|||||||
]),
|
]),
|
||||||
сolorscaleDirections: markRaw([
|
сolorscaleDirections: markRaw([
|
||||||
{ label: 'Normal', value: 'normal' },
|
{ label: 'Normal', value: 'normal' },
|
||||||
{ label: 'Recersed', value: 'reversed' }
|
{ label: 'Reversed', value: 'reversed' }
|
||||||
]),
|
]),
|
||||||
colorSourceUsageOptions: markRaw([
|
colorSourceUsageOptions: markRaw([
|
||||||
{ label: 'Direct', value: 'direct' },
|
{ label: 'Direct', value: 'direct' },
|
||||||
{ label: 'Map to', value: 'map_to' }
|
{ label: 'Map to', value: 'map_to' }
|
||||||
]),
|
]),
|
||||||
defaultColorSettings: {
|
defaultColorSettings: {
|
||||||
constant: { value: '#1F77B4' },
|
constant: { value: '#1F77B4', opacity: 100 },
|
||||||
variable: {
|
variable: {
|
||||||
source: null,
|
source: null,
|
||||||
sourceUsage: 'map_to',
|
sourceUsage: 'map_to',
|
||||||
colorscale: null,
|
colorscale: null,
|
||||||
mode: 'categorical',
|
mode: 'categorical',
|
||||||
colorscaleDirection: 'normal'
|
colorscaleDirection: 'normal',
|
||||||
|
opacity: 100
|
||||||
},
|
},
|
||||||
calculated: {
|
calculated: {
|
||||||
method: 'degree',
|
method: 'degree',
|
||||||
colorscale: null,
|
colorscale: null,
|
||||||
mode: 'continious',
|
mode: 'continious',
|
||||||
colorscaleDirection: 'normal'
|
colorscaleDirection: 'normal',
|
||||||
|
opacity: 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Field label="Seed value">
|
<Field label="Seed value" fieldContainerClassName="test_seed_value">
|
||||||
<NumericInput
|
<NumericInput
|
||||||
:value="modelValue.seedValue"
|
:value="modelValue.seedValue"
|
||||||
@update="update('seedValue', $event)"
|
@update="update('seedValue', $event)"
|
||||||
|
|||||||
@@ -13,21 +13,38 @@
|
|||||||
documentation</a
|
documentation</a
|
||||||
>.
|
>.
|
||||||
</div>
|
</div>
|
||||||
<div
|
<splitpanes
|
||||||
|
:before="{ size: 70, max: 100 }"
|
||||||
|
:after="{ size: 30, max: 50, hidden: !showValueViewer }"
|
||||||
|
:default="{ before: 70, after: 30 }"
|
||||||
class="graph"
|
class="graph"
|
||||||
:style="{
|
:style="{
|
||||||
height:
|
height:
|
||||||
!dataSources || !dataSourceIsValid ? 'calc(100% - 40px)' : '100%'
|
!dataSources || !dataSourceIsValid ? 'calc(100% - 40px)' : '100%'
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
|
<template #left-pane>
|
||||||
|
<div ref="graphEditorContainer" :style="{ height: '100%' }">
|
||||||
<GraphEditor
|
<GraphEditor
|
||||||
ref="graphEditor"
|
ref="graphEditor"
|
||||||
:dataSources="dataSources"
|
:dataSources="dataSources"
|
||||||
:initOptions="initOptions"
|
:initOptions="initOptions"
|
||||||
:showViewSettings="showViewSettings"
|
:showViewSettings="showViewSettings"
|
||||||
@update="$emit('update')"
|
@update="$emit('update')"
|
||||||
|
@select-item="selectedItem = $event"
|
||||||
|
@clear-selection="selectedItem = null"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-if="showValueViewer" #right-pane>
|
||||||
|
<value-viewer
|
||||||
|
:empty="!selectedItem"
|
||||||
|
emptyMessage="No node or edge selected to view"
|
||||||
|
:value="JSON.stringify(selectedItem)"
|
||||||
|
defaultFormat="json"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</splitpanes>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -35,17 +52,20 @@
|
|||||||
import 'react-chart-editor/lib/react-chart-editor.css'
|
import 'react-chart-editor/lib/react-chart-editor.css'
|
||||||
import GraphEditor from '@/components/Graph/GraphEditor.vue'
|
import GraphEditor from '@/components/Graph/GraphEditor.vue'
|
||||||
import { dataSourceIsValid } from '@/lib/graphHelper'
|
import { dataSourceIsValid } from '@/lib/graphHelper'
|
||||||
|
import ValueViewer from '@/components/ValueViewer.vue'
|
||||||
|
import Splitpanes from '@/components/Common/Splitpanes'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Graph',
|
name: 'Graph',
|
||||||
components: { GraphEditor },
|
components: { GraphEditor, ValueViewer, Splitpanes },
|
||||||
props: {
|
props: {
|
||||||
dataSources: Object,
|
dataSources: Object,
|
||||||
initOptions: Object,
|
initOptions: Object,
|
||||||
exportToPngEnabled: Boolean,
|
exportToPngEnabled: Boolean,
|
||||||
exportToSvgEnabled: Boolean,
|
exportToSvgEnabled: Boolean,
|
||||||
exportToHtmlEnabled: Boolean,
|
exportToHtmlEnabled: Boolean,
|
||||||
showViewSettings: Boolean
|
showViewSettings: Boolean,
|
||||||
|
showValueViewer: Boolean
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
'update:exportToSvgEnabled',
|
'update:exportToSvgEnabled',
|
||||||
@@ -57,7 +77,8 @@ export default {
|
|||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
resizeObserver: null
|
resizeObserver: null,
|
||||||
|
selectedItem: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -83,10 +104,10 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.resizeObserver = new ResizeObserver(this.handleResize)
|
this.resizeObserver = new ResizeObserver(this.handleResize)
|
||||||
this.resizeObserver.observe(this.$refs.graphContainer)
|
this.resizeObserver.observe(this.$refs.graphEditorContainer)
|
||||||
},
|
},
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
this.resizeObserver.unobserve(this.$refs.graphContainer)
|
this.resizeObserver.unobserve(this.$refs.graphEditorContainer)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getOptionsForSave() {
|
getOptionsForSave() {
|
||||||
@@ -100,7 +121,7 @@ export default {
|
|||||||
return this.$refs.graphEditor.prepareCopy()
|
return this.$refs.graphEditor.prepareCopy()
|
||||||
},
|
},
|
||||||
async handleResize() {
|
async handleResize() {
|
||||||
const renderer = this.$refs.graphEditor.renderer
|
const renderer = this.$refs.graphEditor?.renderer
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
renderer.refresh()
|
renderer.refresh()
|
||||||
renderer.getCamera().animatedReset({ duration: 600 })
|
renderer.getCamera().animatedReset({ duration: 600 })
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TextField from '@/components/TextField'
|
import TextField from '@/components/Common/TextField'
|
||||||
import CloseIcon from '@/components/svg/close'
|
import CloseIcon from '@/components/svg/close'
|
||||||
import storedInquiries from '@/lib/storedInquiries'
|
import storedInquiries from '@/lib/storedInquiries'
|
||||||
import AppDiagnosticInfo from './AppDiagnosticInfo'
|
import AppDiagnosticInfo from './AppDiagnosticInfo'
|
||||||
@@ -33,7 +33,7 @@ import 'pivottable'
|
|||||||
import 'pivottable/dist/pivot.css'
|
import 'pivottable/dist/pivot.css'
|
||||||
import PivotUi from './PivotUi/index.vue'
|
import PivotUi from './PivotUi/index.vue'
|
||||||
import pivotHelper from './pivotHelper'
|
import pivotHelper from './pivotHelper'
|
||||||
import Chart from '@/views/MainView/Workspace/Tabs/Tab/DataView/Chart'
|
import Chart from '@/components/Chart'
|
||||||
import chartHelper from '@/lib/chartHelper'
|
import chartHelper from '@/lib/chartHelper'
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
import plotly from 'plotly.js'
|
import plotly from 'plotly.js'
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import IconButton from '@/components/IconButton'
|
import IconButton from '@/components/Common/IconButton'
|
||||||
import ArrowIcon from '@/components/svg/arrow'
|
import ArrowIcon from '@/components/svg/arrow'
|
||||||
import EdgeArrowIcon from '@/components/svg/edgeArrow'
|
import EdgeArrowIcon from '@/components/svg/edgeArrow'
|
||||||
|
|
||||||
@@ -1,27 +1,57 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="runResultPanel" class="run-result-panel">
|
<div ref="runResultPanel" class="run-result-panel">
|
||||||
<component
|
<splitpanes
|
||||||
:is="viewValuePanelVisible ? 'splitpanes' : 'div'"
|
|
||||||
:before="{ size: 50, max: 100 }"
|
:before="{ size: 50, max: 100 }"
|
||||||
:after="{ size: 50, max: 100 }"
|
:after="{ size: 50, max: 100, hidden: !viewValuePanelVisible }"
|
||||||
:default="{ before: 50, after: 50 }"
|
:default="{ before: 50, after: 50 }"
|
||||||
class="run-result-panel-content"
|
class="run-result-panel-content"
|
||||||
>
|
>
|
||||||
<template #left-pane>
|
<template #left-pane>
|
||||||
|
<div class="result-set-container">
|
||||||
<div
|
<div
|
||||||
:id="'run-result-left-pane-' + tab.id"
|
v-show="result === null && !isGettingResults && !error"
|
||||||
class="result-set-container"
|
class="table-preview result-before"
|
||||||
|
>
|
||||||
|
Run your query and get results here
|
||||||
|
</div>
|
||||||
|
<div v-if="isGettingResults" class="table-preview result-in-progress">
|
||||||
|
<loading-indicator :size="30" />
|
||||||
|
Fetching results...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-show="result === undefined && !isGettingResults && !error"
|
||||||
|
class="table-preview result-empty"
|
||||||
|
>
|
||||||
|
No rows retrieved according to your query
|
||||||
|
</div>
|
||||||
|
<logs v-if="error" :messages="[error]" />
|
||||||
|
<sql-table
|
||||||
|
v-if="result && !viewRecord"
|
||||||
|
:data-set="result"
|
||||||
|
:time="time"
|
||||||
|
:pageSize="pageSize"
|
||||||
|
:page="defaultPage"
|
||||||
|
:selectedCellCoordinates="defaultSelectedCell"
|
||||||
|
class="straight"
|
||||||
|
@update-selected-cell="onUpdateSelectedCell"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<record
|
||||||
|
v-if="result && viewRecord"
|
||||||
|
ref="recordView"
|
||||||
|
:data-set="result"
|
||||||
|
:time="time"
|
||||||
|
:selectedColumnIndex="selectedCell ? +selectedCell.dataset.col : 0"
|
||||||
|
:rowIndex="selectedCell ? +selectedCell.dataset.row : 0"
|
||||||
|
@update-selected-cell="onUpdateSelectedCell"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div
|
|
||||||
:id="'run-result-result-set-' + tab.id"
|
|
||||||
class="result-set-container"
|
|
||||||
/>
|
|
||||||
<template v-if="viewValuePanelVisible" #right-pane>
|
<template v-if="viewValuePanelVisible" #right-pane>
|
||||||
<div class="value-viewer-container">
|
|
||||||
<value-viewer
|
<value-viewer
|
||||||
v-show="selectedCell"
|
:empty="!selectedCell"
|
||||||
:cellValue="
|
emptyMessage="No cell selected to view"
|
||||||
|
:value="
|
||||||
selectedCell
|
selectedCell
|
||||||
? result.values[result.columns[selectedCell.dataset.col]][
|
? result.values[result.columns[selectedCell.dataset.col]][
|
||||||
selectedCell.dataset.row
|
selectedCell.dataset.row
|
||||||
@@ -29,12 +59,8 @@
|
|||||||
: ''
|
: ''
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<div v-show="!selectedCell" class="table-preview">
|
|
||||||
No cell selected to view
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</component>
|
</splitpanes>
|
||||||
|
|
||||||
<side-tool-bar panel="table" @switch-to="$emit('switchTo', $event)">
|
<side-tool-bar panel="table" @switch-to="$emit('switchTo', $event)">
|
||||||
<icon-button
|
<icon-button
|
||||||
@@ -89,69 +115,27 @@
|
|||||||
@action="copyToClipboard"
|
@action="copyToClipboard"
|
||||||
@cancel="cancelCopy"
|
@cancel="cancelCopy"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<teleport defer :to="resultSetTeleportTarget" :disabled="!enableTeleport">
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
v-show="result === null && !isGettingResults && !error"
|
|
||||||
class="table-preview result-before"
|
|
||||||
>
|
|
||||||
Run your query and get results here
|
|
||||||
</div>
|
|
||||||
<div v-if="isGettingResults" class="table-preview result-in-progress">
|
|
||||||
<loading-indicator :size="30" />
|
|
||||||
Fetching results...
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-show="result === undefined && !isGettingResults && !error"
|
|
||||||
class="table-preview result-empty"
|
|
||||||
>
|
|
||||||
No rows retrieved according to your query
|
|
||||||
</div>
|
|
||||||
<logs v-if="error" :messages="[error]" />
|
|
||||||
<sql-table
|
|
||||||
v-if="result && !viewRecord"
|
|
||||||
:data-set="result"
|
|
||||||
:time="time"
|
|
||||||
:pageSize="pageSize"
|
|
||||||
:page="defaultPage"
|
|
||||||
:selectedCellCoordinates="defaultSelectedCell"
|
|
||||||
class="straight"
|
|
||||||
@update-selected-cell="onUpdateSelectedCell"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<record
|
|
||||||
v-if="result && viewRecord"
|
|
||||||
ref="recordView"
|
|
||||||
:data-set="result"
|
|
||||||
:time="time"
|
|
||||||
:selectedColumnIndex="selectedCell ? +selectedCell.dataset.col : 0"
|
|
||||||
:rowIndex="selectedCell ? +selectedCell.dataset.row : 0"
|
|
||||||
@update-selected-cell="onUpdateSelectedCell"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</teleport>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Logs from '@/components/Logs'
|
import Logs from '@/components/Common/Logs'
|
||||||
import SqlTable from '@/components/SqlTable/index.vue'
|
import SqlTable from '@/components/SqlTable'
|
||||||
import LoadingIndicator from '@/components/LoadingIndicator'
|
import LoadingIndicator from '@/components/Common/LoadingIndicator'
|
||||||
import SideToolBar from '../SideToolBar'
|
import SideToolBar from '@/components/SideToolBar'
|
||||||
import Splitpanes from '@/components/Splitpanes'
|
import Splitpanes from '@/components/Common/Splitpanes'
|
||||||
import ExportToCsvIcon from '@/components/svg/exportToCsv'
|
import ExportToCsvIcon from '@/components/svg/exportToCsv'
|
||||||
import ClipboardIcon from '@/components/svg/clipboard'
|
import ClipboardIcon from '@/components/svg/clipboard'
|
||||||
import ViewCellValueIcon from '@/components/svg/viewCellValue'
|
import ViewCellValueIcon from '@/components/svg/viewCellValue'
|
||||||
import RowIcon from '@/components/svg/row'
|
import RowIcon from '@/components/svg/row'
|
||||||
import IconButton from '@/components/IconButton'
|
import IconButton from '@/components/Common/IconButton'
|
||||||
import csv from '@/lib/csv'
|
import csv from '@/lib/csv'
|
||||||
import fIo from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
import cIo from '@/lib/utils/clipboardIo'
|
import cIo from '@/lib/utils/clipboardIo'
|
||||||
import time from '@/lib/utils/time'
|
import time from '@/lib/utils/time'
|
||||||
import loadingDialog from '@/components/LoadingDialog'
|
import loadingDialog from '@/components/Common/LoadingDialog'
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
import ValueViewer from './ValueViewer'
|
import ValueViewer from '@/components/ValueViewer'
|
||||||
import Record from './Record/index.vue'
|
import Record from './Record/index.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -172,7 +156,6 @@ export default {
|
|||||||
Splitpanes
|
Splitpanes
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
tab: Object,
|
|
||||||
result: Object,
|
result: Object,
|
||||||
isGettingResults: Boolean,
|
isGettingResults: Boolean,
|
||||||
error: Object,
|
error: Object,
|
||||||
@@ -194,20 +177,6 @@ export default {
|
|||||||
showLoadingDialog: false
|
showLoadingDialog: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
resultSetTeleportTarget() {
|
|
||||||
if (!this.enableTeleport) {
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
const base = `#${
|
|
||||||
this.viewValuePanelVisible
|
|
||||||
? 'run-result-left-pane'
|
|
||||||
: 'run-result-result-set'
|
|
||||||
}`
|
|
||||||
const tabIdPostfix = `-${this.tab.id}`
|
|
||||||
return base + tabIdPostfix
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
watch: {
|
||||||
result() {
|
result() {
|
||||||
this.defaultSelectedCell = null
|
this.defaultSelectedCell = null
|
||||||
@@ -332,19 +301,12 @@ export default {
|
|||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-set-container,
|
.result-set-container {
|
||||||
.result-set-container > div {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.value-viewer-container {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
background-color: var(--color-white);
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-preview {
|
.table-preview {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
import fIo from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
import TableDescription from './TableDescription'
|
import TableDescription from './TableDescription'
|
||||||
import TextField from '@/components/TextField'
|
import TextField from '@/components/Common/TextField'
|
||||||
import TreeChevron from '@/components/svg/treeChevron'
|
import TreeChevron from '@/components/svg/treeChevron'
|
||||||
import DbUploader from '@/components/DbUploader'
|
import DbUploader from '@/components/DbUploader'
|
||||||
import ExportIcon from '@/components/svg/export'
|
import ExportIcon from '@/components/svg/export'
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import IconButton from '@/components/IconButton'
|
import IconButton from '@/components/Common/IconButton'
|
||||||
import TableIcon from '@/components/svg/table'
|
import TableIcon from '@/components/svg/table'
|
||||||
import SqlEditorIcon from '@/components/svg/sqlEditor'
|
import SqlEditorIcon from '@/components/svg/sqlEditor'
|
||||||
import DataViewIcon from '@/components/svg/dataView'
|
import DataViewIcon from '@/components/svg/dataView'
|
||||||
@@ -34,7 +34,7 @@ import 'codemirror/theme/neo.css'
|
|||||||
import 'codemirror/addon/hint/show-hint.css'
|
import 'codemirror/addon/hint/show-hint.css'
|
||||||
import 'codemirror/addon/display/autorefresh.js'
|
import 'codemirror/addon/display/autorefresh.js'
|
||||||
import SideToolBar from '../SideToolBar'
|
import SideToolBar from '../SideToolBar'
|
||||||
import IconButton from '@/components/IconButton'
|
import IconButton from '@/components/Common/IconButton'
|
||||||
import RunIcon from '@/components/svg/run'
|
import RunIcon from '@/components/svg/run'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Pager from './Pager.vue'
|
import Pager from '@/components/Common/Pager.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SqlTable',
|
name: 'SqlTable',
|
||||||
@@ -37,7 +37,6 @@
|
|||||||
:disabled="!enableTeleport"
|
:disabled="!enableTeleport"
|
||||||
>
|
>
|
||||||
<run-result
|
<run-result
|
||||||
:tab="tab"
|
|
||||||
:result="tab.result"
|
:result="tab.result"
|
||||||
:isGettingResults="tab.isGettingResults"
|
:isGettingResults="tab.isGettingResults"
|
||||||
:error="tab.error"
|
:error="tab.error"
|
||||||
@@ -64,10 +63,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Splitpanes from '@/components/Splitpanes'
|
import Splitpanes from '@/components/Common/Splitpanes'
|
||||||
import SqlEditor from './SqlEditor'
|
import SqlEditor from '@/components/SqlEditor'
|
||||||
import DataView from './DataView'
|
import DataView from '@/components/DataView'
|
||||||
import RunResult from './RunResult'
|
import RunResult from '@/components/RunResult'
|
||||||
import { nextTick, computed } from 'vue'
|
import { nextTick, computed } from 'vue'
|
||||||
|
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="value-viewer">
|
<div class="value-viewer">
|
||||||
|
<template v-if="!empty">
|
||||||
<div class="value-viewer-toolbar">
|
<div class="value-viewer-toolbar">
|
||||||
<button
|
<button
|
||||||
v-for="format in formats"
|
v-for="format in formats"
|
||||||
@@ -12,7 +13,9 @@
|
|||||||
{{ format.text }}
|
{{ format.text }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button type="button" class="copy" @click="copyToClipboard">Copy</button>
|
<button type="button" class="copy" @click="copyToClipboard">
|
||||||
|
Copy
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="line-wrap"
|
class="line-wrap"
|
||||||
@@ -44,6 +47,11 @@
|
|||||||
class="messages"
|
class="messages"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div v-show="empty" class="empty-message">
|
||||||
|
{{ emptyMessage }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -57,15 +65,22 @@ import 'codemirror/addon/fold/foldgutter.css'
|
|||||||
import 'codemirror/addon/fold/brace-fold.js'
|
import 'codemirror/addon/fold/brace-fold.js'
|
||||||
import 'codemirror/theme/neo.css'
|
import 'codemirror/theme/neo.css'
|
||||||
import cIo from '@/lib/utils/clipboardIo'
|
import cIo from '@/lib/utils/clipboardIo'
|
||||||
import Logs from '@/components/Logs'
|
import Logs from '@/components/Common/Logs'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
name: 'ValueViewer',
|
||||||
components: {
|
components: {
|
||||||
Codemirror,
|
Codemirror,
|
||||||
Logs
|
Logs
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
cellValue: [String, Number, Uint8Array]
|
value: [String, Number, Uint8Array],
|
||||||
|
empty: Boolean,
|
||||||
|
emptyMessage: String,
|
||||||
|
defaultFormat: {
|
||||||
|
type: String,
|
||||||
|
default: 'text'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -73,7 +88,7 @@ export default {
|
|||||||
{ text: 'Text', value: 'text' },
|
{ text: 'Text', value: 'text' },
|
||||||
{ text: 'JSON', value: 'json' }
|
{ text: 'JSON', value: 'json' }
|
||||||
],
|
],
|
||||||
currentFormat: 'text',
|
currentFormat: this.defaultFormat,
|
||||||
lineWrapping: false,
|
lineWrapping: false,
|
||||||
formattedJson: '',
|
formattedJson: '',
|
||||||
messages: []
|
messages: []
|
||||||
@@ -94,13 +109,13 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
isBlob() {
|
isBlob() {
|
||||||
return this.cellValue && ArrayBuffer.isView(this.cellValue)
|
return this.value && ArrayBuffer.isView(this.value)
|
||||||
},
|
},
|
||||||
isNull() {
|
isNull() {
|
||||||
return this.cellValue === null
|
return this.value === null
|
||||||
},
|
},
|
||||||
cellText() {
|
cellText() {
|
||||||
const value = this.cellValue
|
const value = this.value
|
||||||
if (this.isNull) {
|
if (this.isNull) {
|
||||||
return 'NULL'
|
return 'NULL'
|
||||||
}
|
}
|
||||||
@@ -111,17 +126,23 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
currentFormat() {
|
currentFormat: {
|
||||||
|
immediate: true,
|
||||||
|
handler() {
|
||||||
this.messages = []
|
this.messages = []
|
||||||
this.formattedJson = ''
|
this.formattedJson = ''
|
||||||
if (this.currentFormat === 'json') {
|
if (this.currentFormat === 'json') {
|
||||||
this.formatJson(this.cellValue)
|
this.formatJson(this.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cellValue() {
|
value: {
|
||||||
|
immediate: true,
|
||||||
|
handler() {
|
||||||
this.messages = []
|
this.messages = []
|
||||||
if (this.currentFormat === 'json') {
|
if (this.currentFormat === 'json') {
|
||||||
this.formatJson(this.cellValue)
|
this.formatJson(this.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -141,7 +162,7 @@ export default {
|
|||||||
},
|
},
|
||||||
copyToClipboard() {
|
copyToClipboard() {
|
||||||
cIo.copyText(
|
cIo.copyText(
|
||||||
this.currentFormat === 'json' ? this.formattedJson : this.cellValue,
|
this.currentFormat === 'json' ? this.formattedJson : this.value,
|
||||||
'The value is copied to clipboard.'
|
'The value is copied to clipboard.'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -153,8 +174,10 @@ export default {
|
|||||||
.value-viewer {
|
.value-viewer {
|
||||||
background-color: var(--color-white);
|
background-color: var(--color-white);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
.value-viewer-toolbar {
|
.value-viewer-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -219,4 +242,14 @@ export default {
|
|||||||
width: 1px;
|
width: 1px;
|
||||||
background: var(--color-text-base);
|
background: var(--color-text-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-message {
|
||||||
|
position: absolute;
|
||||||
|
top: 40%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
color: var(--color-text-base);
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
27
src/components/svg/pause.vue
Normal file
27
src/components/svg/pause.vue
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 18 18"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3 4C3 3.44772 3.14924 3 3.33333 3H6.66667C6.85076 3 7 3.44772 7
|
||||||
|
4V14C7 14.5523 6.85076 15 6.66667 15H3.33333C3.14924 15 3 14.5523 3 14V4Z"
|
||||||
|
fill="#A2B1C6"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M11 4C11 3.44772 11.1492 3 11.3333 3H14.6667C14.8508 3 15 3.44772 15
|
||||||
|
4V14C15 14.5523 14.8508 15 14.6667 15H11.3333C11.1492 15 11 14.5523 11
|
||||||
|
14V4Z"
|
||||||
|
fill="#A2B1C6"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'PauseIcon'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
36
src/components/svg/restart.vue
Normal file
36
src/components/svg/restart.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 18 18"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M7.34708 3.32922C8.63088 2.90468 10.017 2.90308 11.3022
|
||||||
|
3.32434C11.9084 3.52308 12.4768 3.8114 12.9906 4.1759V3.34387C12.9906
|
||||||
|
2.79159 13.4384 2.34387 13.9906 2.34387C14.5429 2.34387 14.9906 2.79159
|
||||||
|
14.9906 3.34387V6.67102C14.9906 7.22331 14.5429 7.67102 13.9906
|
||||||
|
7.67102H10.6635C10.1112 7.67102 9.66349 7.22331 9.66349 6.67102C9.66351
|
||||||
|
6.11876 10.1112 5.67102 10.6635 5.67102H11.6313C11.3351 5.4851 11.0154
|
||||||
|
5.33498 10.6791 5.22473C9.80069 4.93681 8.85289 4.93738 7.97501
|
||||||
|
5.22766C7.09726 5.51795 6.33574 6.08228 5.80216 6.83704C5.26867
|
||||||
|
7.59191 4.99088 8.49846 5.01017 9.42297C5.02954 10.3475 5.34482
|
||||||
|
11.2417 5.90958 11.9738C6.47435 12.7058 7.25871 13.237 8.14787
|
||||||
|
13.4904C9.03716 13.7437 9.9843 13.7055 10.85 13.381C11.7157 13.0565
|
||||||
|
12.4548 12.463 12.9584 11.6876C13.2592 11.2244 13.879 11.0929 14.3422
|
||||||
|
11.3937C14.805 11.6945 14.9366 12.3135 14.6361 12.7765C13.8996 13.9106
|
||||||
|
12.8185 14.7794 11.5522 15.254C10.2859 15.7287 8.90058 15.7846 7.60001
|
||||||
|
15.4142C6.2994 15.0437 5.15167 14.2661 4.3256 13.1954C3.49956 12.1247
|
||||||
|
3.03849 10.8169 3.01017 9.46497C2.98191 8.1131 3.38782 6.7872 4.16837
|
||||||
|
5.68274C4.94892 4.57842 6.06331 3.75379 7.34708 3.32922Z"
|
||||||
|
fill="#A2B1C6"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'RestartIcon'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<template>
|
|
||||||
<svg
|
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 18 18"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M3 4C3 3.44772 3.44772 3 4 3H14C14.5523 3 15 3.44772 15 4V14C15
|
|
||||||
14.5523 14.5523 15 14 15H4C3.44772 15 3 14.5523 3 14V4Z"
|
|
||||||
fill="#A2B1C6"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'StopIcon'
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -36,6 +36,13 @@ export function dataSourceIsValid(dataSources) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function clearNodeCoordinates(graph) {
|
||||||
|
graph.forEachNode((nodeId, attributes) => {
|
||||||
|
attributes.x = undefined
|
||||||
|
attributes.y = undefined
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export function buildNodes(graph, dataSources, options) {
|
export function buildNodes(graph, dataSources, options) {
|
||||||
const docColumn = Object.keys(dataSources)[0]
|
const docColumn = Object.keys(dataSources)[0]
|
||||||
const { objectType, nodeId } = options.structure
|
const { objectType, nodeId } = options.structure
|
||||||
@@ -127,6 +134,122 @@ export function updateEdges(graph, attributeUpdates) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function reduceNodes(nodeId, nodeData, interactionState, settings) {
|
||||||
|
const {
|
||||||
|
selectedNodeId,
|
||||||
|
hoveredNodeId,
|
||||||
|
selectedEdgeId,
|
||||||
|
hoveredEdgeId,
|
||||||
|
neighborsOfSelectedNode,
|
||||||
|
neighborsOfHoveredNode,
|
||||||
|
selectedEdgeExtremities,
|
||||||
|
hoveredEdgeExtremities
|
||||||
|
} = interactionState
|
||||||
|
|
||||||
|
const res = { ...nodeData }
|
||||||
|
|
||||||
|
if (selectedNodeId || hoveredNodeId || hoveredEdgeId || selectedEdgeId) {
|
||||||
|
res.zIndex = 2
|
||||||
|
res.highlighted = nodeId === selectedNodeId || nodeId === hoveredNodeId
|
||||||
|
if (res.highlighted) {
|
||||||
|
res.labelColor = 'black'
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInHoveredFamily =
|
||||||
|
nodeId === hoveredNodeId ||
|
||||||
|
neighborsOfHoveredNode?.has(nodeId) ||
|
||||||
|
hoveredEdgeExtremities.includes(nodeId)
|
||||||
|
const isInSelectedFamily =
|
||||||
|
nodeId === selectedNodeId ||
|
||||||
|
neighborsOfSelectedNode?.has(nodeId) ||
|
||||||
|
selectedEdgeExtremities.includes(nodeId)
|
||||||
|
if (isInSelectedFamily || isInHoveredFamily) {
|
||||||
|
res.forceLabel = true
|
||||||
|
} else {
|
||||||
|
res.color = getDiminishedColor(
|
||||||
|
nodeData.color,
|
||||||
|
settings.style.backgroundColor
|
||||||
|
)
|
||||||
|
res.label = ''
|
||||||
|
res.zIndex = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reduceEdges(
|
||||||
|
edgeId,
|
||||||
|
edgeData,
|
||||||
|
interactionState,
|
||||||
|
settings,
|
||||||
|
graph
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
selectedNodeId,
|
||||||
|
hoveredNodeId,
|
||||||
|
selectedEdgeId,
|
||||||
|
hoveredEdgeId,
|
||||||
|
neighborsOfSelectedNode,
|
||||||
|
neighborsOfHoveredNode
|
||||||
|
} = interactionState
|
||||||
|
|
||||||
|
const res = { ...edgeData }
|
||||||
|
if (hoveredEdgeId || selectedEdgeId || selectedNodeId || hoveredNodeId) {
|
||||||
|
const extremities = graph.extremities(edgeId)
|
||||||
|
res.zIndex = 2
|
||||||
|
const isHighlighted = hoveredEdgeId === edgeId || selectedEdgeId === edgeId
|
||||||
|
|
||||||
|
let isVisible
|
||||||
|
if (settings.style.highlightMode === 'node_alone') {
|
||||||
|
isVisible = isHighlighted
|
||||||
|
} else if (settings.style.highlightMode === 'node_and_neighbors') {
|
||||||
|
isVisible =
|
||||||
|
isHighlighted ||
|
||||||
|
(selectedNodeId && extremities.includes(selectedNodeId)) ||
|
||||||
|
(hoveredNodeId && extremities.includes(hoveredNodeId))
|
||||||
|
} else {
|
||||||
|
isVisible =
|
||||||
|
isHighlighted ||
|
||||||
|
(selectedNodeId &&
|
||||||
|
extremities.every(
|
||||||
|
n => n === selectedNodeId || neighborsOfSelectedNode.has(n)
|
||||||
|
)) ||
|
||||||
|
(hoveredNodeId &&
|
||||||
|
extremities.every(
|
||||||
|
n => n === hoveredNodeId || neighborsOfHoveredNode.has(n)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
if (isHighlighted) {
|
||||||
|
res.size = res.size * 2
|
||||||
|
res.forceLabel = true
|
||||||
|
} else if (!isVisible) {
|
||||||
|
res.color = getDiminishedColor(
|
||||||
|
edgeData.color,
|
||||||
|
settings.style.backgroundColor
|
||||||
|
)
|
||||||
|
res.zIndex = 1
|
||||||
|
res.label = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDiminishedColor(color, bgColor) {
|
||||||
|
const colorObj = tinycolor(color)
|
||||||
|
const colorOpacity = colorObj.getAlpha()
|
||||||
|
colorObj.setAlpha(0.25 * colorOpacity)
|
||||||
|
|
||||||
|
const fg = colorObj.toRgb()
|
||||||
|
const bg = tinycolor(bgColor).toRgb()
|
||||||
|
|
||||||
|
const r = Math.round(fg.r * fg.a + bg.r * (1 - fg.a))
|
||||||
|
const g = Math.round(fg.g * fg.a + bg.g * (1 - fg.a))
|
||||||
|
const b = Math.round(fg.b * fg.a + bg.b * (1 - fg.a))
|
||||||
|
|
||||||
|
return tinycolor({ r, g, b, a: 1 }).toHexString()
|
||||||
|
}
|
||||||
|
|
||||||
function getUpdateLabelMethod(labelSettings) {
|
function getUpdateLabelMethod(labelSettings) {
|
||||||
const { source, color } = labelSettings
|
const { source, color } = labelSettings
|
||||||
return attributes => {
|
return attributes => {
|
||||||
@@ -161,9 +284,14 @@ function getUpdateSizeMethod(graph, sizeSettings) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDirectVariableColorUpdateMethod(source) {
|
function getDirectVariableColorUpdateMethod(source, opacity = 100) {
|
||||||
return attributes =>
|
return attributes => {
|
||||||
(attributes.color = tinycolor(attributes.data[source]).toHexString())
|
const color = tinycolor(attributes.data[source])
|
||||||
|
const colorOpacity = color.getAlpha()
|
||||||
|
attributes.color = color
|
||||||
|
.setAlpha((opacity / 100) * colorOpacity)
|
||||||
|
.toHex8String()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUpdateNodeColorMethod(graph, colorSettings) {
|
function getUpdateNodeColorMethod(graph, colorSettings) {
|
||||||
@@ -175,10 +303,16 @@ function getUpdateNodeColorMethod(graph, colorSettings) {
|
|||||||
colorscale,
|
colorscale,
|
||||||
colorscaleDirection,
|
colorscaleDirection,
|
||||||
mode,
|
mode,
|
||||||
method
|
method,
|
||||||
|
opacity
|
||||||
} = colorSettings
|
} = colorSettings
|
||||||
if (type === 'constant') {
|
if (type === 'constant') {
|
||||||
return attributes => (attributes.color = value)
|
const color = tinycolor(value)
|
||||||
|
const colorOpacity = color.getAlpha()
|
||||||
|
return attributes =>
|
||||||
|
(attributes.color = color
|
||||||
|
.setAlpha((opacity / 100) * colorOpacity)
|
||||||
|
.toHex8String())
|
||||||
} else if (type === 'variable') {
|
} else if (type === 'variable') {
|
||||||
return sourceUsage === 'map_to'
|
return sourceUsage === 'map_to'
|
||||||
? getColorMethod(
|
? getColorMethod(
|
||||||
@@ -187,9 +321,10 @@ function getUpdateNodeColorMethod(graph, colorSettings) {
|
|||||||
(nodeId, attributes) => attributes.data[source],
|
(nodeId, attributes) => attributes.data[source],
|
||||||
colorscale,
|
colorscale,
|
||||||
colorscaleDirection,
|
colorscaleDirection,
|
||||||
getNodeValueScale
|
getNodeValueScale,
|
||||||
|
opacity
|
||||||
)
|
)
|
||||||
: getDirectVariableColorUpdateMethod(source)
|
: getDirectVariableColorUpdateMethod(source, opacity)
|
||||||
} else {
|
} else {
|
||||||
return getColorMethod(
|
return getColorMethod(
|
||||||
graph,
|
graph,
|
||||||
@@ -197,7 +332,8 @@ function getUpdateNodeColorMethod(graph, colorSettings) {
|
|||||||
nodeId => graph[method](nodeId),
|
nodeId => graph[method](nodeId),
|
||||||
colorscale,
|
colorscale,
|
||||||
colorscaleDirection,
|
colorscaleDirection,
|
||||||
getNodeValueScale
|
getNodeValueScale,
|
||||||
|
opacity
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,8 +380,10 @@ function getColorMethod(
|
|||||||
sourceGetter,
|
sourceGetter,
|
||||||
selectedColorscale,
|
selectedColorscale,
|
||||||
colorscaleDirection,
|
colorscaleDirection,
|
||||||
valueScaleGetter
|
valueScaleGetter,
|
||||||
|
opacity = 100
|
||||||
) {
|
) {
|
||||||
|
const opacityFactor = opacity / 100
|
||||||
const valueScale = valueScaleGetter(graph, sourceGetter)
|
const valueScale = valueScaleGetter(graph, sourceGetter)
|
||||||
let colorscale = selectedColorscale || DEFAULT_SCALE
|
let colorscale = selectedColorscale || DEFAULT_SCALE
|
||||||
if (colorscaleDirection === 'reversed') {
|
if (colorscaleDirection === 'reversed') {
|
||||||
@@ -261,7 +399,9 @@ function getColorMethod(
|
|||||||
)
|
)
|
||||||
return (attributes, nodeId) => {
|
return (attributes, nodeId) => {
|
||||||
const category = sourceGetter(nodeId, attributes)
|
const category = sourceGetter(nodeId, attributes)
|
||||||
attributes.color = colorMap[category]
|
attributes.color = tinycolor(colorMap[category])
|
||||||
|
.setAlpha(opacityFactor)
|
||||||
|
.toHex8String()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const min = valueScale[0]
|
const min = valueScale[0]
|
||||||
@@ -274,14 +414,18 @@ function getColorMethod(
|
|||||||
const value = sourceGetter(nodeId, attributes)
|
const value = sourceGetter(nodeId, attributes)
|
||||||
const normalizedValue = (value - min) / (max - min)
|
const normalizedValue = (value - min) / (max - min)
|
||||||
if (isNaN(normalizedValue)) {
|
if (isNaN(normalizedValue)) {
|
||||||
attributes.color = '#000000'
|
attributes.color = tinycolor('#000000')
|
||||||
|
.setAlpha(opacityFactor)
|
||||||
|
.toHex8String()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const exactMatch = normalizedColorscale.find(
|
const exactMatch = normalizedColorscale.find(
|
||||||
([value]) => value === normalizedValue
|
([value]) => value === normalizedValue
|
||||||
)
|
)
|
||||||
if (exactMatch) {
|
if (exactMatch) {
|
||||||
attributes.color = tinycolor(exactMatch[1]).toHexString()
|
attributes.color = tinycolor(exactMatch[1])
|
||||||
|
.setAlpha(opacityFactor)
|
||||||
|
.toHex8String()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +449,9 @@ function getColorMethod(
|
|||||||
r: r0 + interpolationFactor * (r1 - r0),
|
r: r0 + interpolationFactor * (r1 - r0),
|
||||||
g: g0 + interpolationFactor * (g1 - g0),
|
g: g0 + interpolationFactor * (g1 - g0),
|
||||||
b: b0 + interpolationFactor * (b1 - b0)
|
b: b0 + interpolationFactor * (b1 - b0)
|
||||||
}).toHexString()
|
})
|
||||||
|
.setAlpha(opacityFactor)
|
||||||
|
.toHex8String()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,33 @@
|
|||||||
export default {
|
export default {
|
||||||
_migrate(installedVersion, inquiries) {
|
_migrate(installedVersion, inquiries) {
|
||||||
if (installedVersion === 1) {
|
if (installedVersion < 2) {
|
||||||
inquiries.forEach(inquire => {
|
inquiries.forEach(inquiry => {
|
||||||
inquire.viewType = 'chart'
|
inquiry.viewType = 'chart'
|
||||||
inquire.viewOptions = inquire.chart
|
inquiry.viewOptions = inquiry.chart
|
||||||
delete inquire.chart
|
delete inquiry.chart
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (installedVersion < 3) {
|
||||||
|
inquiries.forEach(inquiry => {
|
||||||
|
if (inquiry.viewType === 'graph') {
|
||||||
|
inquiry.viewOptions.style.nodes.color.opacity = 100
|
||||||
|
inquiry.viewOptions.style.highlightMode = 'node_and_neighbors'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (installedVersion < 4) {
|
||||||
|
inquiries.forEach(inquiry => {
|
||||||
|
if (
|
||||||
|
inquiry.viewType === 'graph' &&
|
||||||
|
inquiry.viewOptions.layout.type === 'forceAtlas2'
|
||||||
|
) {
|
||||||
|
inquiry.viewOptions.layout.options.initialAlgorithm = 'circular'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return inquiries
|
return inquiries
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -5,9 +5,10 @@ import migration from './_migrations'
|
|||||||
|
|
||||||
const migrate = migration._migrate
|
const migrate = migration._migrate
|
||||||
const myInquiriesKey = 'myInquiries'
|
const myInquiriesKey = 'myInquiries'
|
||||||
|
const latestVersion = 4
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
version: 2,
|
version: latestVersion,
|
||||||
myInquiriesKey,
|
myInquiriesKey,
|
||||||
getStoredInquiries() {
|
getStoredInquiries() {
|
||||||
let myInquiries = JSON.parse(localStorage.getItem(myInquiriesKey))
|
let myInquiries = JSON.parse(localStorage.getItem(myInquiriesKey))
|
||||||
@@ -21,7 +22,13 @@ export default {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return (myInquiries && myInquiries.inquiries) || []
|
if (myInquiries.version < latestVersion) {
|
||||||
|
myInquiries = migrate(myInquiries.version, myInquiries.inquiries)
|
||||||
|
this.updateStorage(myInquiries)
|
||||||
|
return myInquiries
|
||||||
|
}
|
||||||
|
|
||||||
|
return myInquiries.inquiries || []
|
||||||
},
|
},
|
||||||
|
|
||||||
duplicateInquiry(baseInquiry) {
|
duplicateInquiry(baseInquiry) {
|
||||||
@@ -63,6 +70,8 @@ export default {
|
|||||||
// Turn data into array if they are not
|
// Turn data into array if they are not
|
||||||
inquiryList = !Array.isArray(inquiries) ? [inquiries] : inquiries
|
inquiryList = !Array.isArray(inquiries) ? [inquiries] : inquiries
|
||||||
inquiryList = migrate(1, inquiryList)
|
inquiryList = migrate(1, inquiryList)
|
||||||
|
} else if (inquiries.version < latestVersion) {
|
||||||
|
inquiryList = migrate(inquiries.version, inquiries.inquiries)
|
||||||
} else {
|
} else {
|
||||||
inquiryList = inquiries.inquiries || []
|
inquiryList = inquiries.inquiries || []
|
||||||
}
|
}
|
||||||
@@ -82,11 +91,11 @@ export default {
|
|||||||
|
|
||||||
importInquiries() {
|
importInquiries() {
|
||||||
return fu.importFile().then(str => {
|
return fu.importFile().then(str => {
|
||||||
const inquires = this.deserialiseInquiries(str)
|
const inquiries = this.deserialiseInquiries(str)
|
||||||
|
|
||||||
events.send('inquiry.import', inquires.length)
|
events.send('inquiry.import', inquiries.length)
|
||||||
|
|
||||||
return inquires
|
return inquiries
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
export(inquiryList, fileName) {
|
export(inquiryList, fileName) {
|
||||||
@@ -102,6 +111,8 @@ export default {
|
|||||||
|
|
||||||
if (!data.version) {
|
if (!data.version) {
|
||||||
return data.length > 0 ? migrate(1, data) : []
|
return data.length > 0 ? migrate(1, data) : []
|
||||||
|
} else if (data.version < latestVersion) {
|
||||||
|
return migrate(data.version, data.inquiries)
|
||||||
} else {
|
} else {
|
||||||
return data.inquiries
|
return data.inquiries
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
import Workspace from '@/views/MainView/Workspace'
|
import Workspace from '@/views/Workspace'
|
||||||
import Inquiries from '@/views/MainView/Inquiries'
|
import Inquiries from '@/views/Inquiries'
|
||||||
import Welcome from '@/views/Welcome'
|
import Welcome from '@/views/Welcome'
|
||||||
import MainView from '@/views/MainView'
|
import MainView from '@/views/MainView'
|
||||||
import LoadView from '@/views/LoadView'
|
import LoadView from '@/views/LoadView'
|
||||||
|
|||||||
@@ -184,14 +184,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import RenameIcon from './svg/rename'
|
import RenameIcon from '@/components/svg/rename'
|
||||||
import CopyIcon from './svg/copy'
|
import CopyIcon from '@/components/svg/copy'
|
||||||
import ExportIcon from '@/components/svg/export'
|
import ExportIcon from '@/components/svg/export'
|
||||||
import DeleteIcon from './svg/delete'
|
import DeleteIcon from '@/components/svg/delete'
|
||||||
import CloseIcon from '@/components/svg/close'
|
import CloseIcon from '@/components/svg/close'
|
||||||
import TextField from '@/components/TextField'
|
import TextField from '@/components/Common/TextField'
|
||||||
import CheckBox from '@/components/CheckBox'
|
import CheckBox from '@/components/Common/CheckBox'
|
||||||
import LoadingIndicator from '@/components/LoadingIndicator'
|
import LoadingIndicator from '@/components/Common/LoadingIndicator'
|
||||||
import tooltipMixin from '@/tooltipMixin'
|
import tooltipMixin from '@/tooltipMixin'
|
||||||
import storedInquiries from '@/lib/storedInquiries'
|
import storedInquiries from '@/lib/storedInquiries'
|
||||||
import eventBus from '@/lib/eventBus'
|
import eventBus from '@/lib/eventBus'
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import fu from '@/lib/utils/fileIo'
|
import fu from '@/lib/utils/fileIo'
|
||||||
import database from '@/lib/database'
|
import database from '@/lib/database'
|
||||||
import Logs from '@/components/Logs'
|
import Logs from '@/components/Common/Logs'
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MainMenu from './MainMenu'
|
import MainMenu from '@/components/MainMenu'
|
||||||
import '@/assets/styles/scrollbars.css'
|
import '@/assets/styles/scrollbars.css'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -17,9 +17,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Splitpanes from '@/components/Splitpanes'
|
import Splitpanes from '@/components/Common/Splitpanes'
|
||||||
import Schema from './Schema'
|
import Schema from '@/components/Schema'
|
||||||
import Tabs from './Tabs'
|
import Tabs from '@/components/Tabs'
|
||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import { mount, flushPromises } from '@vue/test-utils'
|
import { mount, flushPromises } from '@vue/test-utils'
|
||||||
import Chart from '@/views/MainView/Workspace/Tabs/Tab/DataView/Chart/index.vue'
|
import Chart from '@/components/Chart.vue'
|
||||||
import chartHelper from '@/lib/chartHelper'
|
import chartHelper from '@/lib/chartHelper'
|
||||||
import * as dereference from 'react-chart-editor/lib/lib/dereference'
|
import * as dereference from 'react-chart-editor/lib/lib/dereference'
|
||||||
import fIo from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
@@ -128,6 +128,8 @@ describe('Chart.vue', () => {
|
|||||||
expect(plot.scrollWidth).not.to.equal(initialPlotWidth)
|
expect(plot.scrollWidth).not.to.equal(initialPlotWidth)
|
||||||
expect(plot.scrollHeight).not.to.equal(initialPlotHeight)
|
expect(plot.scrollHeight).not.to.equal(initialPlotHeight)
|
||||||
|
|
||||||
|
container.style.width = 'unset'
|
||||||
|
container.style.height = 'unset'
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { shallowMount } from '@vue/test-utils'
|
import { shallowMount } from '@vue/test-utils'
|
||||||
import CheckBox from '@/components/CheckBox'
|
import CheckBox from '@/components/Common/CheckBox'
|
||||||
|
|
||||||
describe('CheckBox', () => {
|
describe('CheckBox', () => {
|
||||||
it('unchecked by default', () => {
|
it('unchecked by default', () => {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { shallowMount } from '@vue/test-utils'
|
import { shallowMount } from '@vue/test-utils'
|
||||||
import LoadingIndicator from '@/components/LoadingIndicator'
|
import LoadingIndicator from '@/components/Common/LoadingIndicator'
|
||||||
|
|
||||||
describe('LoadingIndicator.vue', () => {
|
describe('LoadingIndicator.vue', () => {
|
||||||
it('Calculates animation class', async () => {
|
it('Calculates animation class', async () => {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { shallowMount } from '@vue/test-utils'
|
import { shallowMount } from '@vue/test-utils'
|
||||||
import Logs from '@/components/Logs'
|
import Logs from '@/components/Common/Logs'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
|
|
||||||
let place
|
let place
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import Pager from '@/components/SqlTable/Pager'
|
import Pager from '@/components/Common/Pager'
|
||||||
|
|
||||||
describe('Pager.vue', () => {
|
describe('Pager.vue', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { shallowMount } from '@vue/test-utils'
|
import { shallowMount } from '@vue/test-utils'
|
||||||
import Splitpanes from '@/components/Splitpanes'
|
import Splitpanes from '@/components/Common/Splitpanes'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
|
|
||||||
describe('Splitpanes.vue', () => {
|
describe('Splitpanes.vue', () => {
|
||||||
@@ -49,6 +49,41 @@ describe('Splitpanes.vue', () => {
|
|||||||
).to.equal('40%')
|
).to.equal('40%')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('renders correctly with hidden panels', async () => {
|
||||||
|
// mount the component
|
||||||
|
const wrapper = shallowMount(Splitpanes, {
|
||||||
|
attachTo: document.body,
|
||||||
|
slots: {
|
||||||
|
leftPane: '<div />',
|
||||||
|
rightPane: '<div />'
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
before: { size: 60, max: 100, hidden: true },
|
||||||
|
after: { size: 40, max: 100 },
|
||||||
|
horizontal: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[0].isVisible()).to.equal(false)
|
||||||
|
expect(wrapper.find('.splitpanes-splitter').isVisible()).to.equal(false)
|
||||||
|
expect(
|
||||||
|
wrapper.findAll('.splitpanes-pane')[1].element.style.height
|
||||||
|
).to.equal('100%')
|
||||||
|
|
||||||
|
await wrapper.setProps({
|
||||||
|
before: { size: 60, max: 100 },
|
||||||
|
after: { size: 40, max: 100, hidden: true }
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].isVisible()).to.equal(false)
|
||||||
|
expect(wrapper.find('.splitpanes-splitter').isVisible()).to.equal(false)
|
||||||
|
expect(
|
||||||
|
wrapper.findAll('.splitpanes-pane')[0].element.style.height
|
||||||
|
).to.equal('100%')
|
||||||
|
|
||||||
|
wrapper.unmount()
|
||||||
|
})
|
||||||
|
|
||||||
it('toggles correctly - no maximized initially', async () => {
|
it('toggles correctly - no maximized initially', async () => {
|
||||||
// mount the component
|
// mount the component
|
||||||
const wrapper = shallowMount(Splitpanes, {
|
const wrapper = shallowMount(Splitpanes, {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import splitter from '@/components/Splitpanes/splitter'
|
import splitter from '@/components/Common/Splitpanes/splitter'
|
||||||
|
|
||||||
describe('splitter.js', () => {
|
describe('splitter.js', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import DataView from '@/views/MainView/Workspace/Tabs/Tab/DataView/index.vue'
|
import DataView from '@/components/DataView.vue'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
import cIo from '@/lib/utils/clipboardIo'
|
import cIo from '@/lib/utils/clipboardIo'
|
||||||
@@ -418,4 +418,52 @@ describe('DataView.vue', () => {
|
|||||||
wrapper.findComponent({ name: 'graph' }).props('initOptions')
|
wrapper.findComponent({ name: 'graph' }).props('initOptions')
|
||||||
).to.eql({ test_options: 'latest_graph_options' })
|
).to.eql({ test_options: 'latest_graph_options' })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('switches visibility of node or edge in graph mode', async () => {
|
||||||
|
const wrapper = mount(DataView, {
|
||||||
|
global: {
|
||||||
|
mocks: { $store },
|
||||||
|
provide: {
|
||||||
|
tabLayout: { dataView: 'above' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// viewNodeOrEdgeBtn is not disaplyed in chart mode
|
||||||
|
expect(
|
||||||
|
wrapper.findComponent({ ref: 'viewNodeOrEdgeBtn' }).exists()
|
||||||
|
).to.equal(false)
|
||||||
|
|
||||||
|
// Switch to pivot
|
||||||
|
const pivotBtn = wrapper.findComponent({ ref: 'pivotBtn' })
|
||||||
|
await pivotBtn.trigger('click')
|
||||||
|
|
||||||
|
// viewNodeOrEdgeBtn is not disaplyed in pivot mode
|
||||||
|
expect(
|
||||||
|
wrapper.findComponent({ ref: 'viewNodeOrEdgeBtn' }).exists()
|
||||||
|
).to.equal(false)
|
||||||
|
|
||||||
|
// Switch to graph
|
||||||
|
const graphBtn = wrapper.findComponent({ ref: 'graphBtn' })
|
||||||
|
await graphBtn.trigger('click')
|
||||||
|
|
||||||
|
// viewNodeOrEdgeBtn is disaplyed in graph mode
|
||||||
|
const viewNodeOrEdgeBtn = wrapper.findComponent({
|
||||||
|
ref: 'viewNodeOrEdgeBtn'
|
||||||
|
})
|
||||||
|
expect(viewNodeOrEdgeBtn.exists()).to.equal(true)
|
||||||
|
|
||||||
|
// by default node viewer is hidden
|
||||||
|
expect(wrapper.findComponent({ name: 'value-viewer' }).exists()).to.equal(
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
// Click to show node viewer
|
||||||
|
await viewNodeOrEdgeBtn.trigger('click')
|
||||||
|
expect(wrapper.findComponent({ name: 'value-viewer' }).exists()).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
wrapper.unmount()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import { mount, flushPromises } from '@vue/test-utils'
|
import { mount, flushPromises } from '@vue/test-utils'
|
||||||
import Graph from '@/views/MainView/Workspace/Tabs/Tab/DataView/Graph/index.vue'
|
import Graph from '@/components/Graph/index.vue'
|
||||||
|
import { nextTick } from 'vue'
|
||||||
|
|
||||||
function getPixels(canvas) {
|
function getPixels(canvas) {
|
||||||
const context = canvas.getContext('webgl2')
|
const context = canvas.getContext('webgl2')
|
||||||
@@ -163,6 +164,326 @@ describe('Graph.vue', () => {
|
|||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('the graph resizes when node viewer visibillity togglles', async () => {
|
||||||
|
const wrapper = mount(Graph, {
|
||||||
|
attachTo: document.body,
|
||||||
|
props: {
|
||||||
|
dataSources: {
|
||||||
|
doc: [
|
||||||
|
'{"object_type":0,"node_id":"Gryffindor"}',
|
||||||
|
'{"object_type":0,"node_id":"Hufflepuff"}'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
initOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
nodes: {
|
||||||
|
size: {
|
||||||
|
type: 'constant',
|
||||||
|
value: 10
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: 'constant',
|
||||||
|
value: '#1F77B4'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
source: null,
|
||||||
|
color: '#444444'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: {
|
||||||
|
type: 'constant',
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: 'constant',
|
||||||
|
value: '#a2b1c6'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
source: null,
|
||||||
|
color: '#a2b1c6'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'circular',
|
||||||
|
options: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showValueViewer: false
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
mocks: { $store },
|
||||||
|
provide: {
|
||||||
|
tabLayout: { dataView: 'above' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const canvas = wrapper.find('canvas.sigma-nodes').wrapperElement
|
||||||
|
const initialCanvasWidth = canvas.scrollWidth
|
||||||
|
|
||||||
|
await wrapper.setProps({ showValueViewer: true })
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(canvas.scrollWidth).not.to.equal(initialCanvasWidth)
|
||||||
|
|
||||||
|
await wrapper.setProps({ showValueViewer: false })
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(canvas.scrollWidth).to.equal(initialCanvasWidth)
|
||||||
|
|
||||||
|
wrapper.unmount()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('the graph resizes when the split pane resizes', async () => {
|
||||||
|
const wrapper = mount(Graph, {
|
||||||
|
attachTo: document.body,
|
||||||
|
props: {
|
||||||
|
dataSources: {
|
||||||
|
doc: [
|
||||||
|
'{"object_type":0,"node_id":"Gryffindor"}',
|
||||||
|
'{"object_type":0,"node_id":"Hufflepuff"}'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
initOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
nodes: {
|
||||||
|
size: {
|
||||||
|
type: 'constant',
|
||||||
|
value: 10
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: 'constant',
|
||||||
|
value: '#1F77B4'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
source: null,
|
||||||
|
color: '#444444'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: {
|
||||||
|
type: 'constant',
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: 'constant',
|
||||||
|
value: '#a2b1c6'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
source: null,
|
||||||
|
color: '#a2b1c6'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'circular',
|
||||||
|
options: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showValueViewer: true
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
mocks: { $store },
|
||||||
|
provide: {
|
||||||
|
tabLayout: { dataView: 'above' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const container =
|
||||||
|
wrapper.find('.graph-container').wrapperElement.parentElement
|
||||||
|
const canvas = wrapper.find('canvas.sigma-nodes').wrapperElement
|
||||||
|
|
||||||
|
const initialContainerWidth = container.scrollWidth
|
||||||
|
const initialCanvasWidth = canvas.scrollWidth
|
||||||
|
await wrapper.find('.splitpanes-splitter').trigger('mousedown')
|
||||||
|
document.dispatchEvent(
|
||||||
|
new MouseEvent('mousemove', {
|
||||||
|
clientX: initialContainerWidth / 2,
|
||||||
|
clientY: 80
|
||||||
|
})
|
||||||
|
)
|
||||||
|
document.dispatchEvent(new MouseEvent('mouseup'))
|
||||||
|
await nextTick()
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
await flushPromises()
|
||||||
|
|
||||||
|
expect(canvas.scrollWidth).not.to.equal(initialCanvasWidth)
|
||||||
|
|
||||||
|
wrapper.unmount()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('opens and closes node viewer', async () => {
|
||||||
|
const wrapper = mount(Graph, {
|
||||||
|
props: {
|
||||||
|
dataSources: {
|
||||||
|
doc: [
|
||||||
|
'{"object_type":0,"node_id":"Gryffindor"}',
|
||||||
|
'{"object_type":0,"node_id":"Hufflepuff"}'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
initOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
nodes: {
|
||||||
|
size: {
|
||||||
|
type: 'constant',
|
||||||
|
value: 10
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: 'constant',
|
||||||
|
value: '#1F77B4'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
source: null,
|
||||||
|
color: '#444444'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: {
|
||||||
|
type: 'constant',
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: 'constant',
|
||||||
|
value: '#a2b1c6'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
source: null,
|
||||||
|
color: '#a2b1c6'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'circular',
|
||||||
|
options: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showValueViewer: false
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
mocks: { $store },
|
||||||
|
provide: {
|
||||||
|
tabLayout: { dataView: 'above' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(wrapper.text()).not.contain('No node or edge selected to view')
|
||||||
|
await wrapper.setProps({ showValueViewer: true })
|
||||||
|
expect(wrapper.text()).contains('No node or edge selected to view')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('passes selected item to node viewer', async () => {
|
||||||
|
const wrapper = mount(Graph, {
|
||||||
|
attachTo: document.body,
|
||||||
|
props: {
|
||||||
|
dataSources: {
|
||||||
|
doc: [
|
||||||
|
'{"object_type":0,"node_id":"Gryffindor"}',
|
||||||
|
'{"object_type":0,"node_id":"Hufflepuff"}'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
initOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
nodes: {
|
||||||
|
size: {
|
||||||
|
type: 'constant',
|
||||||
|
value: 10
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: 'constant',
|
||||||
|
value: '#1F77B4'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
source: null,
|
||||||
|
color: '#444444'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: {
|
||||||
|
type: 'constant',
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: 'constant',
|
||||||
|
value: '#a2b1c6'
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
source: null,
|
||||||
|
color: '#a2b1c6'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'circular',
|
||||||
|
options: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showValueViewer: true
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
mocks: { $store },
|
||||||
|
provide: {
|
||||||
|
tabLayout: { dataView: 'above' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(wrapper.find('.value-viewer .value-body').exists()).to.equal(false)
|
||||||
|
await wrapper
|
||||||
|
.findComponent({ ref: 'graphEditor' })
|
||||||
|
.vm.$emit('selectItem', { object_type: 0, node_id: 'Gryffindor' })
|
||||||
|
|
||||||
|
expect(wrapper.find('.value-viewer .value-body').text()).contains(
|
||||||
|
'"object_type": 0,'
|
||||||
|
)
|
||||||
|
expect(wrapper.find('.value-viewer .value-body').text()).contains(
|
||||||
|
'"node_id": "Gryffindor"'
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapper
|
||||||
|
.findComponent({ ref: 'graphEditor' })
|
||||||
|
.vm.$emit('clearSelection')
|
||||||
|
expect(wrapper.find('.value-viewer .value-body').exists()).to.equal(false)
|
||||||
|
|
||||||
|
wrapper.unmount()
|
||||||
|
})
|
||||||
|
|
||||||
it('nodes and edges are rendered', async () => {
|
it('nodes and edges are rendered', async () => {
|
||||||
const wrapper = mount(Graph, {
|
const wrapper = mount(Graph, {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
@@ -24,7 +24,8 @@ const defaultInitOptions = {
|
|||||||
},
|
},
|
||||||
color: {
|
color: {
|
||||||
type: 'constant',
|
type: 'constant',
|
||||||
value: '#1F77B4'
|
value: '#1F77B4',
|
||||||
|
opacity: 100
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
source: null,
|
source: null,
|
||||||
@@ -404,7 +405,7 @@ describe('GraphEditor', () => {
|
|||||||
doc: [
|
doc: [
|
||||||
'{"type": 0, "node_id": 1, "color": "#ff0000", "points": 5}',
|
'{"type": 0, "node_id": 1, "color": "#ff0000", "points": 5}',
|
||||||
'{"type": 0, "node_id": 2, "color": "#abcdff", "points": 15}',
|
'{"type": 0, "node_id": 2, "color": "#abcdff", "points": 15}',
|
||||||
'{"type": 0, "node_id": 3, "color": "#123456", "points": 10}',
|
'{"type": 0, "node_id": 3, "color": "#12345680", "points": 10}',
|
||||||
'{"type": 1, "source": 2, "target": 3}'
|
'{"type": 1, "source": 2, "target": 3}'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -440,11 +441,11 @@ describe('GraphEditor', () => {
|
|||||||
// Set constant color
|
// Set constant color
|
||||||
await wrapper
|
await wrapper
|
||||||
.findAllComponents({ name: 'ColorPicker' })[1]
|
.findAllComponents({ name: 'ColorPicker' })[1]
|
||||||
.vm.$emit('colorChange', '#ff00ff')
|
.vm.$emit('colorChange', '#ff00ff80')
|
||||||
|
|
||||||
expect(graph.export().nodes[0].attributes.color).to.equal('#ff00ff')
|
expect(graph.export().nodes[0].attributes.color).to.equal('#ff00ff80')
|
||||||
expect(graph.export().nodes[1].attributes.color).to.equal('#ff00ff')
|
expect(graph.export().nodes[1].attributes.color).to.equal('#ff00ff80')
|
||||||
expect(graph.export().nodes[2].attributes.color).to.equal('#ff00ff')
|
expect(graph.export().nodes[2].attributes.color).to.equal('#ff00ff80')
|
||||||
|
|
||||||
// Switch to Variable
|
// Switch to Variable
|
||||||
const variable = wrapper.findAll('.test_node_color .radio-block__option')[1]
|
const variable = wrapper.findAll('.test_node_color .radio-block__option')[1]
|
||||||
@@ -459,27 +460,27 @@ describe('GraphEditor', () => {
|
|||||||
|
|
||||||
await wrapper.findAll('.Select__menu .Select__option')[2].trigger('click')
|
await wrapper.findAll('.Select__menu .Select__option')[2].trigger('click')
|
||||||
|
|
||||||
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e')
|
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6eff')
|
||||||
expect(graph.export().nodes[1].attributes.color).to.equal('#bdea75')
|
expect(graph.export().nodes[1].attributes.color).to.equal('#bdea75ff')
|
||||||
expect(graph.export().nodes[2].attributes.color).to.equal('#86d780')
|
expect(graph.export().nodes[2].attributes.color).to.equal('#86d780ff')
|
||||||
|
|
||||||
// Select Direct mapping
|
// Select Direct mapping
|
||||||
await wrapper
|
await wrapper
|
||||||
.find('.test_node_color_mapping_mode .radio-block__option')
|
.find('.test_node_color_mapping_mode .radio-block__option')
|
||||||
.trigger('click')
|
.trigger('click')
|
||||||
|
|
||||||
expect(graph.export().nodes[0].attributes.color).to.equal('#ff0000')
|
expect(graph.export().nodes[0].attributes.color).to.equal('#ff0000ff')
|
||||||
expect(graph.export().nodes[1].attributes.color).to.equal('#abcdff')
|
expect(graph.export().nodes[1].attributes.color).to.equal('#abcdffff')
|
||||||
expect(graph.export().nodes[2].attributes.color).to.equal('#123456')
|
expect(graph.export().nodes[2].attributes.color).to.equal('#12345680')
|
||||||
|
|
||||||
// Switch to Calculated
|
// Switch to Calculated
|
||||||
const calculated = wrapper.findAll(
|
const calculated = wrapper.findAll(
|
||||||
'.test_node_color .radio-block__option'
|
'.test_node_color .radio-block__option'
|
||||||
)[2]
|
)[2]
|
||||||
await calculated.trigger('click')
|
await calculated.trigger('click')
|
||||||
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e')
|
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6eff')
|
||||||
expect(graph.export().nodes[1].attributes.color).to.equal('#2a4858')
|
expect(graph.export().nodes[1].attributes.color).to.equal('#2a4858ff')
|
||||||
expect(graph.export().nodes[2].attributes.color).to.equal('#2a4858')
|
expect(graph.export().nodes[2].attributes.color).to.equal('#2a4858ff')
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
// Choose in-degree
|
// Choose in-degree
|
||||||
@@ -491,45 +492,68 @@ describe('GraphEditor', () => {
|
|||||||
|
|
||||||
await wrapper.findAll('.Select__menu .Select__option')[1].trigger('click')
|
await wrapper.findAll('.Select__menu .Select__option')[1].trigger('click')
|
||||||
|
|
||||||
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e')
|
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6eff')
|
||||||
expect(graph.export().nodes[1].attributes.color).to.equal('#fafa6e')
|
expect(graph.export().nodes[1].attributes.color).to.equal('#fafa6eff')
|
||||||
expect(graph.export().nodes[2].attributes.color).to.equal('#2a4858')
|
expect(graph.export().nodes[2].attributes.color).to.equal('#2a4858ff')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// Set another opacity for calculated color
|
||||||
|
let opacityInput = wrapper.find(
|
||||||
|
'.test_node_opacity input.numeric-input__number'
|
||||||
|
)
|
||||||
|
await opacityInput.setValue(50)
|
||||||
|
opacityInput.wrapperElement.dispatchEvent(
|
||||||
|
new Event('blur', { bubbles: true })
|
||||||
|
)
|
||||||
|
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e80')
|
||||||
|
expect(graph.export().nodes[1].attributes.color).to.equal('#fafa6e80')
|
||||||
|
expect(graph.export().nodes[2].attributes.color).to.equal('#2a485880')
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
// Set Color as to Categorical
|
// Set Color as to Categorical
|
||||||
await wrapper
|
await wrapper
|
||||||
.findAll('.test_node_color_as .radio-block__option')[1]
|
.findAll('.test_node_color_as .radio-block__option')[1]
|
||||||
.trigger('click')
|
.trigger('click')
|
||||||
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e')
|
expect(graph.export().nodes[0].attributes.color).to.equal('#fafa6e80')
|
||||||
expect(graph.export().nodes[1].attributes.color).to.equal('#fafa6e')
|
expect(graph.export().nodes[1].attributes.color).to.equal('#fafa6e80')
|
||||||
expect(graph.export().nodes[2].attributes.color).to.equal('#bdea75')
|
expect(graph.export().nodes[2].attributes.color).to.equal('#bdea7580')
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
// Change colorscale direction
|
// Change colorscale direction
|
||||||
await wrapper
|
await wrapper
|
||||||
.findAll('.test_node_color_colorscale_direction .radio-block__option')[1]
|
.findAll('.test_node_color_colorscale_direction .radio-block__option')[1]
|
||||||
.trigger('click')
|
.trigger('click')
|
||||||
expect(graph.export().nodes[0].attributes.color).to.equal('#2a4858')
|
expect(graph.export().nodes[0].attributes.color).to.equal('#2a485880')
|
||||||
expect(graph.export().nodes[1].attributes.color).to.equal('#2a4858')
|
expect(graph.export().nodes[1].attributes.color).to.equal('#2a485880')
|
||||||
expect(graph.export().nodes[2].attributes.color).to.equal('#1f5f70')
|
expect(graph.export().nodes[2].attributes.color).to.equal('#1f5f7080')
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
// Switch to Variable
|
// Switch to Variable
|
||||||
await variable.trigger('click')
|
await variable.trigger('click')
|
||||||
|
|
||||||
// The latest settings from variable mode are applied
|
// The latest settings from variable mode are applied
|
||||||
expect(graph.export().nodes[0].attributes.color).to.equal('#ff0000')
|
expect(graph.export().nodes[0].attributes.color).to.equal('#ff0000ff')
|
||||||
expect(graph.export().nodes[1].attributes.color).to.equal('#abcdff')
|
expect(graph.export().nodes[1].attributes.color).to.equal('#abcdffff')
|
||||||
expect(graph.export().nodes[2].attributes.color).to.equal('#123456')
|
expect(graph.export().nodes[2].attributes.color).to.equal('#12345680')
|
||||||
|
|
||||||
// Switch to Constant
|
// Switch to Constant
|
||||||
const constant = wrapper.findAll('.test_node_color .radio-block__option')[0]
|
const constant = wrapper.findAll('.test_node_color .radio-block__option')[0]
|
||||||
await constant.trigger('click')
|
await constant.trigger('click')
|
||||||
|
|
||||||
// The latest settings from constant mode are applied
|
// The latest settings from constant mode are applied
|
||||||
expect(graph.export().nodes[0].attributes.color).to.equal('#ff00ff')
|
expect(graph.export().nodes[0].attributes.color).to.equal('#ff00ff80')
|
||||||
expect(graph.export().nodes[1].attributes.color).to.equal('#ff00ff')
|
expect(graph.export().nodes[1].attributes.color).to.equal('#ff00ff80')
|
||||||
expect(graph.export().nodes[2].attributes.color).to.equal('#ff00ff')
|
expect(graph.export().nodes[2].attributes.color).to.equal('#ff00ff80')
|
||||||
|
|
||||||
|
// Set another opacity for constant color
|
||||||
|
await opacityInput.setValue(50)
|
||||||
|
opacityInput.wrapperElement.dispatchEvent(
|
||||||
|
new Event('blur', { bubbles: true })
|
||||||
|
)
|
||||||
|
expect(graph.export().nodes[0].attributes.color).to.equal('#ff00ff40')
|
||||||
|
expect(graph.export().nodes[1].attributes.color).to.equal('#ff00ff40')
|
||||||
|
expect(graph.export().nodes[2].attributes.color).to.equal('#ff00ff40')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
@@ -840,26 +864,26 @@ describe('GraphEditor', () => {
|
|||||||
)
|
)
|
||||||
await wrapper.findAll('.Select__menu .Select__option')[5].trigger('click')
|
await wrapper.findAll('.Select__menu .Select__option')[5].trigger('click')
|
||||||
|
|
||||||
expect(graph.export().edges[0].attributes.color).to.equal('#fafa6e')
|
expect(graph.export().edges[0].attributes.color).to.equal('#fafa6eff')
|
||||||
expect(graph.export().edges[1].attributes.color).to.equal('#bdea75')
|
expect(graph.export().edges[1].attributes.color).to.equal('#bdea75ff')
|
||||||
expect(graph.export().edges[2].attributes.color).to.equal('#86d780')
|
expect(graph.export().edges[2].attributes.color).to.equal('#86d780ff')
|
||||||
|
|
||||||
// Set Color as to Continious
|
// Set Color as to Continious
|
||||||
await wrapper
|
await wrapper
|
||||||
.findAll('.test_edge_color_as .radio-block__option')[0]
|
.findAll('.test_edge_color_as .radio-block__option')[0]
|
||||||
.trigger('click')
|
.trigger('click')
|
||||||
expect(graph.export().edges[0].attributes.color).to.equal('#fafa6e')
|
expect(graph.export().edges[0].attributes.color).to.equal('#fafa6eff')
|
||||||
expect(graph.export().edges[1].attributes.color).to.equal('#39b48d')
|
expect(graph.export().edges[1].attributes.color).to.equal('#39b48dff')
|
||||||
expect(graph.export().edges[2].attributes.color).to.equal('#2a4858')
|
expect(graph.export().edges[2].attributes.color).to.equal('#2a4858ff')
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
// Change colorscale direction
|
// Change colorscale direction
|
||||||
await wrapper
|
await wrapper
|
||||||
.findAll('.test_edge_color_colorscale_direction .radio-block__option')[1]
|
.findAll('.test_edge_color_colorscale_direction .radio-block__option')[1]
|
||||||
.trigger('click')
|
.trigger('click')
|
||||||
expect(graph.export().edges[0].attributes.color).to.equal('#2a4858')
|
expect(graph.export().edges[0].attributes.color).to.equal('#2a4858ff')
|
||||||
expect(graph.export().edges[1].attributes.color).to.equal('#139f8e')
|
expect(graph.export().edges[1].attributes.color).to.equal('#139f8eff')
|
||||||
expect(graph.export().edges[2].attributes.color).to.equal('#fafa6e')
|
expect(graph.export().edges[2].attributes.color).to.equal('#fafa6eff')
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
// Clear color source
|
// Clear color source
|
||||||
@@ -883,9 +907,9 @@ describe('GraphEditor', () => {
|
|||||||
.find('.test_edge_color_mapping_mode .radio-block__option')
|
.find('.test_edge_color_mapping_mode .radio-block__option')
|
||||||
.trigger('click')
|
.trigger('click')
|
||||||
|
|
||||||
expect(graph.export().edges[0].attributes.color).to.equal('#ff0000')
|
expect(graph.export().edges[0].attributes.color).to.equal('#ff0000ff')
|
||||||
expect(graph.export().edges[1].attributes.color).to.equal('#abcdff')
|
expect(graph.export().edges[1].attributes.color).to.equal('#abcdffff')
|
||||||
expect(graph.export().edges[2].attributes.color).to.equal('#123456')
|
expect(graph.export().edges[2].attributes.color).to.equal('#123456ff')
|
||||||
|
|
||||||
// Switch to Constant
|
// Switch to Constant
|
||||||
const constant = wrapper.findAll('.test_edge_color .radio-block__option')[0]
|
const constant = wrapper.findAll('.test_edge_color .radio-block__option')[0]
|
||||||
@@ -1285,7 +1309,7 @@ describe('GraphEditor', () => {
|
|||||||
|
|
||||||
expect(startSpy.calledOnce).to.equal(true)
|
expect(startSpy.calledOnce).to.equal(true)
|
||||||
await waitCondition(() => stopSpy.callCount === 1)
|
await waitCondition(() => stopSpy.callCount === 1)
|
||||||
expect(wrapper.text()).to.contain('Start')
|
expect(wrapper.text()).to.contain('Continue')
|
||||||
|
|
||||||
const coordinates = graph
|
const coordinates = graph
|
||||||
.export()
|
.export()
|
||||||
@@ -1325,6 +1349,7 @@ describe('GraphEditor', () => {
|
|||||||
layout: {
|
layout: {
|
||||||
type: 'forceAtlas2',
|
type: 'forceAtlas2',
|
||||||
options: {
|
options: {
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
initialIterationsAmount: 55,
|
initialIterationsAmount: 55,
|
||||||
gravity: 1.5,
|
gravity: 1.5,
|
||||||
scalingRatio: 1.2,
|
scalingRatio: 1.2,
|
||||||
@@ -1359,7 +1384,7 @@ describe('GraphEditor', () => {
|
|||||||
|
|
||||||
expect(startSpy.calledOnce).to.equal(true)
|
expect(startSpy.calledOnce).to.equal(true)
|
||||||
await waitCondition(() => stopSpy.callCount === 1)
|
await waitCondition(() => stopSpy.callCount === 1)
|
||||||
expect(wrapper.text()).to.contain('Start')
|
expect(wrapper.text()).to.contain('Continue')
|
||||||
|
|
||||||
const initialCoordinates = graph
|
const initialCoordinates = graph
|
||||||
.export()
|
.export()
|
||||||
@@ -1373,7 +1398,7 @@ describe('GraphEditor', () => {
|
|||||||
new Event('blur', { bubbles: true })
|
new Event('blur', { bubbles: true })
|
||||||
)
|
)
|
||||||
// Call nextTick after setting number input,
|
// Call nextTick after setting number input,
|
||||||
// otherwise the value will be changed beck to initial for some reason
|
// otherwise the value will be changed back to initial for some reason
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
expect(wrapper.vm.settings.layout.options.gravity).to.equal(12)
|
expect(wrapper.vm.settings.layout.options.gravity).to.equal(12)
|
||||||
@@ -1381,6 +1406,30 @@ describe('GraphEditor', () => {
|
|||||||
// Algorithm wasn't called
|
// Algorithm wasn't called
|
||||||
expect(startSpy.calledOnce).to.equal(true)
|
expect(startSpy.calledOnce).to.equal(true)
|
||||||
|
|
||||||
|
// Change initial algorithm
|
||||||
|
await wrapper
|
||||||
|
.find(
|
||||||
|
'.test_fa2_initial_layout_algorithm_select .dropdown-container .Select__indicator'
|
||||||
|
)
|
||||||
|
.wrapperElement.dispatchEvent(
|
||||||
|
new MouseEvent('mousedown', { bubbles: true })
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapper.findAll('.Select__menu .Select__option')[1].trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
expect(wrapper.vm.settings.layout.options.initialAlgorithm).to.equal(
|
||||||
|
'random'
|
||||||
|
)
|
||||||
|
|
||||||
|
// Change seed value
|
||||||
|
const seedValueInput = wrapper.find('.test_seed_value input')
|
||||||
|
await seedValueInput.setValue(123)
|
||||||
|
seedValueInput.wrapperElement.dispatchEvent(
|
||||||
|
new Event('blur', { bubbles: true })
|
||||||
|
)
|
||||||
|
await nextTick()
|
||||||
|
expect(wrapper.vm.settings.layout.options.seedValue).to.equal(123)
|
||||||
|
|
||||||
// Change scaling ratio
|
// Change scaling ratio
|
||||||
const scalingInput = wrapper.find('.test_fa2_scaling input')
|
const scalingInput = wrapper.find('.test_fa2_scaling input')
|
||||||
await scalingInput.setValue(2)
|
await scalingInput.setValue(2)
|
||||||
@@ -1469,13 +1518,13 @@ describe('GraphEditor', () => {
|
|||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
|
||||||
// Click Start
|
// Click Continue
|
||||||
const toggleButton = wrapper.find('button.test_fa2_toggle')
|
const toggleButton = wrapper.find('button.test_fa2_toggle')
|
||||||
await toggleButton.trigger('click')
|
await toggleButton.trigger('click')
|
||||||
expect(toggleButton.text()).to.contain('Stop')
|
expect(toggleButton.text()).to.contain('Pause')
|
||||||
expect(startSpy.callCount).to.equal(2)
|
expect(startSpy.callCount).to.equal(2)
|
||||||
|
|
||||||
// Wait a bit and click Stop
|
// Wait a bit and click Pause
|
||||||
await time.sleep(500)
|
await time.sleep(500)
|
||||||
await toggleButton.trigger('click')
|
await toggleButton.trigger('click')
|
||||||
expect(stopSpy.callCount).to.equal(2)
|
expect(stopSpy.callCount).to.equal(2)
|
||||||
@@ -1489,9 +1538,479 @@ describe('GraphEditor', () => {
|
|||||||
|
|
||||||
// Click Reset
|
// Click Reset
|
||||||
await wrapper.find('button.test_fa2_reset').trigger('click')
|
await wrapper.find('button.test_fa2_reset').trigger('click')
|
||||||
expect(toggleButton.text()).to.contain('Start')
|
expect(toggleButton.text()).to.contain('Continue')
|
||||||
expect(startSpy.callCount).to.equal(2)
|
expect(startSpy.callCount).to.equal(2)
|
||||||
expect(wrapper.vm.settings.layout.options).to.eql({
|
expect(wrapper.vm.settings.layout.options).to.eql({
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
|
initialIterationsAmount: 55,
|
||||||
|
gravity: 1.5,
|
||||||
|
scalingRatio: 1.2,
|
||||||
|
adjustSizes: true,
|
||||||
|
barnesHutOptimize: true,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
strongGravityMode: false,
|
||||||
|
linLogMode: true,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
slowDown: 1,
|
||||||
|
weightSource: 'wgt',
|
||||||
|
edgeWeightInfluence: 0.5
|
||||||
|
})
|
||||||
|
|
||||||
|
wrapper.unmount()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('FA2: restarts and applies selected initial layout', async () => {
|
||||||
|
const stopSpy = sinon.spy(FA2Layout.prototype, 'stop')
|
||||||
|
const startSpy = sinon.spy(FA2Layout.prototype, 'start')
|
||||||
|
const wrapper = mount(GraphEditor, {
|
||||||
|
attachTo: document.body,
|
||||||
|
props: {
|
||||||
|
dataSources: {
|
||||||
|
doc: [
|
||||||
|
'{"type": 0, "node_id": 1, "size": 20}',
|
||||||
|
'{"type": 0, "node_id": 2, "size": 2}',
|
||||||
|
'{"type": 0, "node_id": 3, "size": 2}',
|
||||||
|
'{"type": 0, "node_id": 4, "size": 2}',
|
||||||
|
'{"type": 1, "source": 1, "target": 3, "wgt": 20}',
|
||||||
|
'{"type": 1, "source": 1, "target": 2, "wgt": 15}',
|
||||||
|
'{"type": 1, "source": 1, "target": 4, "wgt": 5}'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
initOptions: {
|
||||||
|
...defaultInitOptions,
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'forceAtlas2',
|
||||||
|
options: {
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
|
initialIterationsAmount: 55,
|
||||||
|
gravity: 1.5,
|
||||||
|
scalingRatio: 1.2,
|
||||||
|
adjustSizes: true,
|
||||||
|
barnesHutOptimize: true,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
strongGravityMode: false,
|
||||||
|
linLogMode: true,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
slowDown: 1,
|
||||||
|
weightSource: 'wgt',
|
||||||
|
edgeWeightInfluence: 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showViewSettings: true
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
provide: {
|
||||||
|
tabLayout: { dataView: 'above' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const graph = wrapper.vm.graph
|
||||||
|
|
||||||
|
const styleMenuItem = wrapper.findAll('.sidebar__group__title')[1]
|
||||||
|
await styleMenuItem.trigger('click')
|
||||||
|
|
||||||
|
const layoutMenuItem = wrapper.findAll('.sidebar__item')[4]
|
||||||
|
await layoutMenuItem.trigger('click')
|
||||||
|
|
||||||
|
expect(startSpy.calledOnce).to.equal(true)
|
||||||
|
await waitCondition(() => stopSpy.callCount === 1)
|
||||||
|
|
||||||
|
const initialCoordinates = graph
|
||||||
|
.export()
|
||||||
|
.nodes.map(node => `x:${node.attributes.x},y:${node.attributes.y}`)
|
||||||
|
.join()
|
||||||
|
|
||||||
|
// Change initial algorithm to Random
|
||||||
|
await wrapper
|
||||||
|
.find(
|
||||||
|
'.test_fa2_initial_layout_algorithm_select .dropdown-container .Select__indicator'
|
||||||
|
)
|
||||||
|
.wrapperElement.dispatchEvent(
|
||||||
|
new MouseEvent('mousedown', { bubbles: true })
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapper.findAll('.Select__menu .Select__option')[1].trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// Click Restart
|
||||||
|
const restartButton = wrapper.find('button.test_fa2_restart')
|
||||||
|
await restartButton.trigger('click')
|
||||||
|
const toggleButton = wrapper.find('button.test_fa2_toggle')
|
||||||
|
expect(toggleButton.text()).to.contain('Pause')
|
||||||
|
expect(startSpy.callCount).to.equal(2)
|
||||||
|
|
||||||
|
// Wait until restarting finished
|
||||||
|
await waitCondition(() => stopSpy.callCount === 2)
|
||||||
|
|
||||||
|
const randomCoordinates1 = graph
|
||||||
|
.export()
|
||||||
|
.nodes.map(node => `x:${node.attributes.x},y:${node.attributes.y}`)
|
||||||
|
.join()
|
||||||
|
|
||||||
|
// Change seed value to 123
|
||||||
|
let seedValueInput = wrapper.find('.test_seed_value input')
|
||||||
|
await seedValueInput.setValue(123)
|
||||||
|
seedValueInput.wrapperElement.dispatchEvent(
|
||||||
|
new Event('blur', { bubbles: true })
|
||||||
|
)
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// Click Restart
|
||||||
|
await restartButton.trigger('click')
|
||||||
|
expect(toggleButton.text()).to.contain('Pause')
|
||||||
|
expect(startSpy.callCount).to.equal(3)
|
||||||
|
|
||||||
|
// Wait until restarting finished
|
||||||
|
await waitCondition(() => stopSpy.callCount === 3)
|
||||||
|
|
||||||
|
const randomCoordinates2 = graph
|
||||||
|
.export()
|
||||||
|
.nodes.map(node => `x:${node.attributes.x},y:${node.attributes.y}`)
|
||||||
|
.join()
|
||||||
|
|
||||||
|
// Change seed value back to 1
|
||||||
|
await seedValueInput.setValue(1)
|
||||||
|
seedValueInput.wrapperElement.dispatchEvent(
|
||||||
|
new Event('blur', { bubbles: true })
|
||||||
|
)
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// Click Restart
|
||||||
|
await restartButton.trigger('click')
|
||||||
|
|
||||||
|
// Wait until restarting finished
|
||||||
|
await waitCondition(() => stopSpy.callCount === 4)
|
||||||
|
|
||||||
|
const randomCoordinates1After = graph
|
||||||
|
.export()
|
||||||
|
.nodes.map(node => `x:${node.attributes.x},y:${node.attributes.y}`)
|
||||||
|
.join()
|
||||||
|
|
||||||
|
// Change initial algorithm to CirclePack
|
||||||
|
await wrapper
|
||||||
|
.find(
|
||||||
|
'.test_fa2_initial_layout_algorithm_select .dropdown-container .Select__indicator'
|
||||||
|
)
|
||||||
|
.wrapperElement.dispatchEvent(
|
||||||
|
new MouseEvent('mousedown', { bubbles: true })
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapper.findAll('.Select__menu .Select__option')[2].trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// Change seed value to 456
|
||||||
|
seedValueInput = wrapper.find('.test_seed_value input')
|
||||||
|
await seedValueInput.setValue(456)
|
||||||
|
seedValueInput.wrapperElement.dispatchEvent(
|
||||||
|
new Event('blur', { bubbles: true })
|
||||||
|
)
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// Set another hierarchy
|
||||||
|
const hierarchyInput = wrapper.find(
|
||||||
|
'.multiselect.sqliteviz-select .multiselect__select'
|
||||||
|
)
|
||||||
|
await hierarchyInput.trigger('mousedown')
|
||||||
|
await wrapper
|
||||||
|
.find('ul.multiselect__content')
|
||||||
|
.findAll('li')[2]
|
||||||
|
.find('span')
|
||||||
|
.trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// Click Restart
|
||||||
|
await restartButton.trigger('click')
|
||||||
|
// Wait until restarting finished
|
||||||
|
await waitCondition(() => stopSpy.callCount === 5)
|
||||||
|
|
||||||
|
const circlePackCoordinates = graph
|
||||||
|
.export()
|
||||||
|
.nodes.map(node => `x:${node.attributes.x},y:${node.attributes.y}`)
|
||||||
|
.join()
|
||||||
|
|
||||||
|
expect(initialCoordinates).not.to.equal(randomCoordinates1)
|
||||||
|
expect(randomCoordinates1).not.to.equal(randomCoordinates2)
|
||||||
|
expect(randomCoordinates1).to.equal(randomCoordinates1After)
|
||||||
|
expect(circlePackCoordinates).not.to.equal(randomCoordinates1After)
|
||||||
|
expect(circlePackCoordinates).not.to.equal(initialCoordinates)
|
||||||
|
|
||||||
|
wrapper.unmount()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('FA2: restore previously set parameters for initial layout', async () => {
|
||||||
|
const wrapper = mount(GraphEditor, {
|
||||||
|
attachTo: document.body,
|
||||||
|
props: {
|
||||||
|
dataSources: {
|
||||||
|
doc: [
|
||||||
|
'{"type": 0, "node_id": 1}',
|
||||||
|
'{"type": 0, "node_id": 2}',
|
||||||
|
'{"type": 1, "source": 1, "target": 2, "wgt": 15}'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
initOptions: {
|
||||||
|
...defaultInitOptions,
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'forceAtlas2',
|
||||||
|
options: {
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
|
initialIterationsAmount: 55,
|
||||||
|
gravity: 1.5,
|
||||||
|
scalingRatio: 1.2,
|
||||||
|
adjustSizes: true,
|
||||||
|
barnesHutOptimize: true,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
strongGravityMode: false,
|
||||||
|
linLogMode: true,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
slowDown: 1,
|
||||||
|
weightSource: 'wgt',
|
||||||
|
edgeWeightInfluence: 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showViewSettings: true
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
provide: {
|
||||||
|
tabLayout: { dataView: 'above' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const styleMenuItem = wrapper.findAll('.sidebar__group__title')[1]
|
||||||
|
await styleMenuItem.trigger('click')
|
||||||
|
|
||||||
|
const layoutMenuItem = wrapper.findAll('.sidebar__item')[4]
|
||||||
|
await layoutMenuItem.trigger('click')
|
||||||
|
|
||||||
|
// Change initial algorithm to Random
|
||||||
|
await wrapper
|
||||||
|
.find(
|
||||||
|
'.test_fa2_initial_layout_algorithm_select .dropdown-container .Select__indicator'
|
||||||
|
)
|
||||||
|
.wrapperElement.dispatchEvent(
|
||||||
|
new MouseEvent('mousedown', { bubbles: true })
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapper.findAll('.Select__menu .Select__option')[1].trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.settings.layout.options).to.eql({
|
||||||
|
initialAlgorithm: 'random',
|
||||||
|
seedValue: 1,
|
||||||
|
initialIterationsAmount: 55,
|
||||||
|
gravity: 1.5,
|
||||||
|
scalingRatio: 1.2,
|
||||||
|
adjustSizes: true,
|
||||||
|
barnesHutOptimize: true,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
strongGravityMode: false,
|
||||||
|
linLogMode: true,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
slowDown: 1,
|
||||||
|
weightSource: 'wgt',
|
||||||
|
edgeWeightInfluence: 0.5
|
||||||
|
})
|
||||||
|
|
||||||
|
// Change seed value to 123
|
||||||
|
let seedValueInput = wrapper.find('.test_seed_value input')
|
||||||
|
await seedValueInput.setValue(123)
|
||||||
|
seedValueInput.wrapperElement.dispatchEvent(
|
||||||
|
new Event('blur', { bubbles: true })
|
||||||
|
)
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.settings.layout.options).to.eql({
|
||||||
|
initialAlgorithm: 'random',
|
||||||
|
seedValue: 123,
|
||||||
|
initialIterationsAmount: 55,
|
||||||
|
gravity: 1.5,
|
||||||
|
scalingRatio: 1.2,
|
||||||
|
adjustSizes: true,
|
||||||
|
barnesHutOptimize: true,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
strongGravityMode: false,
|
||||||
|
linLogMode: true,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
slowDown: 1,
|
||||||
|
weightSource: 'wgt',
|
||||||
|
edgeWeightInfluence: 0.5
|
||||||
|
})
|
||||||
|
|
||||||
|
// Change initial algorithm to CirclePack
|
||||||
|
await wrapper
|
||||||
|
.find(
|
||||||
|
'.test_fa2_initial_layout_algorithm_select .dropdown-container .Select__indicator'
|
||||||
|
)
|
||||||
|
.wrapperElement.dispatchEvent(
|
||||||
|
new MouseEvent('mousedown', { bubbles: true })
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapper.findAll('.Select__menu .Select__option')[2].trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.settings.layout.options).to.eql({
|
||||||
|
initialAlgorithm: 'circlepack',
|
||||||
|
seedValue: 1,
|
||||||
|
hierarchyAttributes: [],
|
||||||
|
initialIterationsAmount: 55,
|
||||||
|
gravity: 1.5,
|
||||||
|
scalingRatio: 1.2,
|
||||||
|
adjustSizes: true,
|
||||||
|
barnesHutOptimize: true,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
strongGravityMode: false,
|
||||||
|
linLogMode: true,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
slowDown: 1,
|
||||||
|
weightSource: 'wgt',
|
||||||
|
edgeWeightInfluence: 0.5
|
||||||
|
})
|
||||||
|
|
||||||
|
// Change seed value to 456
|
||||||
|
seedValueInput = wrapper.find('.test_seed_value input')
|
||||||
|
await seedValueInput.setValue(456)
|
||||||
|
seedValueInput.wrapperElement.dispatchEvent(
|
||||||
|
new Event('blur', { bubbles: true })
|
||||||
|
)
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.settings.layout.options).to.eql({
|
||||||
|
initialAlgorithm: 'circlepack',
|
||||||
|
seedValue: 456,
|
||||||
|
hierarchyAttributes: [],
|
||||||
|
initialIterationsAmount: 55,
|
||||||
|
gravity: 1.5,
|
||||||
|
scalingRatio: 1.2,
|
||||||
|
adjustSizes: true,
|
||||||
|
barnesHutOptimize: true,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
strongGravityMode: false,
|
||||||
|
linLogMode: true,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
slowDown: 1,
|
||||||
|
weightSource: 'wgt',
|
||||||
|
edgeWeightInfluence: 0.5
|
||||||
|
})
|
||||||
|
|
||||||
|
// Set another hierarchy
|
||||||
|
const hierarchyInput = wrapper.find(
|
||||||
|
'.multiselect.sqliteviz-select .multiselect__select'
|
||||||
|
)
|
||||||
|
await hierarchyInput.trigger('mousedown')
|
||||||
|
await wrapper
|
||||||
|
.find('ul.multiselect__content')
|
||||||
|
.findAll('li')[1]
|
||||||
|
.find('span')
|
||||||
|
.trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.settings.layout.options).to.eql({
|
||||||
|
initialAlgorithm: 'circlepack',
|
||||||
|
seedValue: 456,
|
||||||
|
hierarchyAttributes: ['node_id'],
|
||||||
|
initialIterationsAmount: 55,
|
||||||
|
gravity: 1.5,
|
||||||
|
scalingRatio: 1.2,
|
||||||
|
adjustSizes: true,
|
||||||
|
barnesHutOptimize: true,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
strongGravityMode: false,
|
||||||
|
linLogMode: true,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
slowDown: 1,
|
||||||
|
weightSource: 'wgt',
|
||||||
|
edgeWeightInfluence: 0.5
|
||||||
|
})
|
||||||
|
|
||||||
|
// Change initial algorithm to Random
|
||||||
|
await wrapper
|
||||||
|
.find(
|
||||||
|
'.test_fa2_initial_layout_algorithm_select .dropdown-container .Select__indicator'
|
||||||
|
)
|
||||||
|
.wrapperElement.dispatchEvent(
|
||||||
|
new MouseEvent('mousedown', { bubbles: true })
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapper.findAll('.Select__menu .Select__option')[1].trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.settings.layout.options).to.eql({
|
||||||
|
initialAlgorithm: 'random',
|
||||||
|
seedValue: 123,
|
||||||
|
initialIterationsAmount: 55,
|
||||||
|
gravity: 1.5,
|
||||||
|
scalingRatio: 1.2,
|
||||||
|
adjustSizes: true,
|
||||||
|
barnesHutOptimize: true,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
strongGravityMode: false,
|
||||||
|
linLogMode: true,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
slowDown: 1,
|
||||||
|
weightSource: 'wgt',
|
||||||
|
edgeWeightInfluence: 0.5
|
||||||
|
})
|
||||||
|
|
||||||
|
// Change initial algorithm to Circular
|
||||||
|
await wrapper
|
||||||
|
.find(
|
||||||
|
'.test_fa2_initial_layout_algorithm_select .dropdown-container .Select__indicator'
|
||||||
|
)
|
||||||
|
.wrapperElement.dispatchEvent(
|
||||||
|
new MouseEvent('mousedown', { bubbles: true })
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapper.findAll('.Select__menu .Select__option')[0].trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.settings.layout.options).to.eql({
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
|
initialIterationsAmount: 55,
|
||||||
|
gravity: 1.5,
|
||||||
|
scalingRatio: 1.2,
|
||||||
|
adjustSizes: true,
|
||||||
|
barnesHutOptimize: true,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
strongGravityMode: false,
|
||||||
|
linLogMode: true,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
slowDown: 1,
|
||||||
|
weightSource: 'wgt',
|
||||||
|
edgeWeightInfluence: 0.5
|
||||||
|
})
|
||||||
|
|
||||||
|
// Change initial algorithm to CirclePack
|
||||||
|
await wrapper
|
||||||
|
.find(
|
||||||
|
'.test_fa2_initial_layout_algorithm_select .dropdown-container .Select__indicator'
|
||||||
|
)
|
||||||
|
.wrapperElement.dispatchEvent(
|
||||||
|
new MouseEvent('mousedown', { bubbles: true })
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapper.findAll('.Select__menu .Select__option')[2].trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.settings.layout.options).to.eql({
|
||||||
|
initialAlgorithm: 'circlepack',
|
||||||
|
seedValue: 456,
|
||||||
|
hierarchyAttributes: ['node_id'],
|
||||||
initialIterationsAmount: 55,
|
initialIterationsAmount: 55,
|
||||||
gravity: 1.5,
|
gravity: 1.5,
|
||||||
scalingRatio: 1.2,
|
scalingRatio: 1.2,
|
||||||
@@ -1579,6 +2098,26 @@ describe('GraphEditor', () => {
|
|||||||
)
|
)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
|
// Change initial algorithm
|
||||||
|
await wrapper
|
||||||
|
.find(
|
||||||
|
'.test_fa2_initial_layout_algorithm_select .dropdown-container .Select__indicator'
|
||||||
|
)
|
||||||
|
.wrapperElement.dispatchEvent(
|
||||||
|
new MouseEvent('mousedown', { bubbles: true })
|
||||||
|
)
|
||||||
|
|
||||||
|
await wrapper.findAll('.Select__menu .Select__option')[1].trigger('click')
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
// Change seed value
|
||||||
|
const seedValueInput = wrapper.find('.test_seed_value input')
|
||||||
|
await seedValueInput.setValue(123)
|
||||||
|
seedValueInput.wrapperElement.dispatchEvent(
|
||||||
|
new Event('blur', { bubbles: true })
|
||||||
|
)
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
// Change scaling ratio
|
// Change scaling ratio
|
||||||
const scalingInput = wrapper.find('.test_fa2_scaling input')
|
const scalingInput = wrapper.find('.test_fa2_scaling input')
|
||||||
await scalingInput.setValue(2)
|
await scalingInput.setValue(2)
|
||||||
@@ -1647,6 +2186,8 @@ describe('GraphEditor', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
expect(wrapper.vm.settings.layout.options).to.eql({
|
expect(wrapper.vm.settings.layout.options).to.eql({
|
||||||
|
initialAlgorithm: 'random',
|
||||||
|
seedValue: 123,
|
||||||
initialIterationsAmount: 120,
|
initialIterationsAmount: 120,
|
||||||
gravity: 12,
|
gravity: 12,
|
||||||
scalingRatio: 2,
|
scalingRatio: 2,
|
||||||
@@ -1732,6 +2273,7 @@ describe('GraphEditor', () => {
|
|||||||
layout: {
|
layout: {
|
||||||
type: 'forceAtlas2',
|
type: 'forceAtlas2',
|
||||||
options: {
|
options: {
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
initialIterationsAmount: 50,
|
initialIterationsAmount: 50,
|
||||||
gravity: 1.5,
|
gravity: 1.5,
|
||||||
scalingRatio: 1.2,
|
scalingRatio: 1.2,
|
||||||
@@ -1764,7 +2306,7 @@ describe('GraphEditor', () => {
|
|||||||
expect(startSpy.calledOnce).to.equal(true)
|
expect(startSpy.calledOnce).to.equal(true)
|
||||||
await waitCondition(() => stopSpy.callCount === 1)
|
await waitCondition(() => stopSpy.callCount === 1)
|
||||||
|
|
||||||
// Click Start
|
// Click Continue
|
||||||
const toggleButton = wrapper.find('button.test_fa2_toggle')
|
const toggleButton = wrapper.find('button.test_fa2_toggle')
|
||||||
await toggleButton.trigger('click')
|
await toggleButton.trigger('click')
|
||||||
expect(startSpy.callCount).to.equal(2)
|
expect(startSpy.callCount).to.equal(2)
|
||||||
@@ -1860,7 +2402,7 @@ describe('GraphEditor', () => {
|
|||||||
|
|
||||||
expect(startSpy.calledOnce).to.equal(true)
|
expect(startSpy.calledOnce).to.equal(true)
|
||||||
await waitCondition(() => stopSpy.callCount === 1)
|
await waitCondition(() => stopSpy.callCount === 1)
|
||||||
expect(wrapper.text()).to.contain('Start')
|
expect(wrapper.text()).to.contain('Continue')
|
||||||
|
|
||||||
const coordinates = graph
|
const coordinates = graph
|
||||||
.export()
|
.export()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { expect } from 'chai'
|
|||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import { mount, shallowMount } from '@vue/test-utils'
|
import { mount, shallowMount } from '@vue/test-utils'
|
||||||
import { createStore } from 'vuex'
|
import { createStore } from 'vuex'
|
||||||
import MainMenu from '@/views/MainView/MainMenu'
|
import MainMenu from '@/components/MainMenu'
|
||||||
import storedInquiries from '@/lib/storedInquiries'
|
import storedInquiries from '@/lib/storedInquiries'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
import eventBus from '@/lib/eventBus'
|
import eventBus from '@/lib/eventBus'
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { mount, flushPromises } from '@vue/test-utils'
|
import { mount, flushPromises } from '@vue/test-utils'
|
||||||
import Pivot from '@/views/MainView/Workspace/Tabs/Tab/DataView/Pivot/index.vue'
|
import Pivot from '@/components/Pivot/index.vue'
|
||||||
import chartHelper from '@/lib/chartHelper'
|
import chartHelper from '@/lib/chartHelper'
|
||||||
import fIo from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import pivotHelper from '@/views/MainView/Workspace/Tabs/Tab/DataView/Pivot/pivotHelper'
|
import pivotHelper from '@/components/Pivot/pivotHelper'
|
||||||
|
|
||||||
describe('Pivot.vue', () => {
|
describe('Pivot.vue', () => {
|
||||||
let container
|
let container
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { shallowMount } from '@vue/test-utils'
|
import { shallowMount } from '@vue/test-utils'
|
||||||
import PivotSortBtn from '@/views/MainView/Workspace/Tabs/Tab/DataView/Pivot/PivotUi/PivotSortBtn'
|
import PivotSortBtn from '@/components/Pivot/PivotUi/PivotSortBtn'
|
||||||
|
|
||||||
describe('PivotSortBtn.vue', () => {
|
describe('PivotSortBtn.vue', () => {
|
||||||
it('switches order', async () => {
|
it('switches order', async () => {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import PivotUi from '@/views/MainView/Workspace/Tabs/Tab/DataView/Pivot/PivotUi'
|
import PivotUi from '@/components/Pivot/PivotUi'
|
||||||
|
|
||||||
describe('PivotUi.vue', () => {
|
describe('PivotUi.vue', () => {
|
||||||
it('returns value when settings changed', async () => {
|
it('returns value when settings changed', async () => {
|
||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
_getDataSources,
|
_getDataSources,
|
||||||
getPivotCanvas,
|
getPivotCanvas,
|
||||||
getPivotHtml
|
getPivotHtml
|
||||||
} from '@/views/MainView/Workspace/Tabs/Tab/DataView/Pivot/pivotHelper'
|
} from '@/components/Pivot/pivotHelper'
|
||||||
|
|
||||||
describe('pivotHelper.js', () => {
|
describe('pivotHelper.js', () => {
|
||||||
it('_getDataSources returns data sources', () => {
|
it('_getDataSources returns data sources', () => {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import Record from '@/views/MainView/Workspace/Tabs/Tab/RunResult/Record'
|
import Record from '@/components/RunResult/Record'
|
||||||
|
|
||||||
describe('Record.vue', () => {
|
describe('Record.vue', () => {
|
||||||
it('shows record with selected cell', async () => {
|
it('shows record with selected cell', async () => {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import RunResult from '@/views/MainView/Workspace/Tabs/Tab/RunResult'
|
import RunResult from '@/components/RunResult'
|
||||||
import csv from '@/lib/csv'
|
import csv from '@/lib/csv'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
@@ -18,7 +18,6 @@ describe('RunResult.vue', () => {
|
|||||||
sinon.spy(window, 'alert')
|
sinon.spy(window, 'alert')
|
||||||
const wrapper = mount(RunResult, {
|
const wrapper = mount(RunResult, {
|
||||||
props: {
|
props: {
|
||||||
tab: { id: 1 },
|
|
||||||
result: {
|
result: {
|
||||||
columns: ['id', 'name'],
|
columns: ['id', 'name'],
|
||||||
values: {
|
values: {
|
||||||
@@ -54,7 +53,6 @@ describe('RunResult.vue', () => {
|
|||||||
const wrapper = mount(RunResult, {
|
const wrapper = mount(RunResult, {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
props: {
|
props: {
|
||||||
tab: { id: 1 },
|
|
||||||
result: {
|
result: {
|
||||||
columns: ['id', 'name'],
|
columns: ['id', 'name'],
|
||||||
values: {
|
values: {
|
||||||
@@ -114,7 +112,6 @@ describe('RunResult.vue', () => {
|
|||||||
const wrapper = mount(RunResult, {
|
const wrapper = mount(RunResult, {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
props: {
|
props: {
|
||||||
tab: { id: 1 },
|
|
||||||
result: {
|
result: {
|
||||||
columns: ['id', 'name'],
|
columns: ['id', 'name'],
|
||||||
values: {
|
values: {
|
||||||
@@ -154,7 +151,6 @@ describe('RunResult.vue', () => {
|
|||||||
const wrapper = mount(RunResult, {
|
const wrapper = mount(RunResult, {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
props: {
|
props: {
|
||||||
tab: { id: 1 },
|
|
||||||
result: {
|
result: {
|
||||||
columns: ['id', 'name'],
|
columns: ['id', 'name'],
|
||||||
values: {
|
values: {
|
||||||
@@ -196,7 +192,6 @@ describe('RunResult.vue', () => {
|
|||||||
it('shows value of selected cell - result set', async () => {
|
it('shows value of selected cell - result set', async () => {
|
||||||
const wrapper = mount(RunResult, {
|
const wrapper = mount(RunResult, {
|
||||||
props: {
|
props: {
|
||||||
tab: { id: 1 },
|
|
||||||
result: {
|
result: {
|
||||||
columns: ['id', 'name'],
|
columns: ['id', 'name'],
|
||||||
values: {
|
values: {
|
||||||
@@ -250,9 +245,9 @@ describe('RunResult.vue', () => {
|
|||||||
|
|
||||||
// Click on 'bar' cell again
|
// Click on 'bar' cell again
|
||||||
await rows[1].findAll('td')[1].trigger('click')
|
await rows[1].findAll('td')[1].trigger('click')
|
||||||
expect(
|
expect(wrapper.find('.value-viewer').text()).to.equals(
|
||||||
wrapper.find('.value-viewer-container .table-preview').text()
|
'No cell selected to view'
|
||||||
).to.equals('No cell selected to view')
|
)
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -260,7 +255,6 @@ describe('RunResult.vue', () => {
|
|||||||
const wrapper = mount(RunResult, {
|
const wrapper = mount(RunResult, {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
props: {
|
props: {
|
||||||
tab: { id: 1 },
|
|
||||||
result: {
|
result: {
|
||||||
columns: ['id', 'name'],
|
columns: ['id', 'name'],
|
||||||
values: {
|
values: {
|
||||||
@@ -323,9 +317,9 @@ describe('RunResult.vue', () => {
|
|||||||
|
|
||||||
// Click on 'foo' cell again
|
// Click on 'foo' cell again
|
||||||
await rows[1].find('td').trigger('click')
|
await rows[1].find('td').trigger('click')
|
||||||
expect(
|
expect(wrapper.find('.value-viewer').text()).to.equals(
|
||||||
wrapper.find('.value-viewer-container .table-preview').text()
|
'No cell selected to view'
|
||||||
).to.equals('No cell selected to view')
|
)
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -333,7 +327,6 @@ describe('RunResult.vue', () => {
|
|||||||
const wrapper = mount(RunResult, {
|
const wrapper = mount(RunResult, {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
props: {
|
props: {
|
||||||
tab: { id: 1 },
|
|
||||||
result: {
|
result: {
|
||||||
columns: ['id', 'name'],
|
columns: ['id', 'name'],
|
||||||
values: {
|
values: {
|
||||||
@@ -4,8 +4,8 @@ import { mount } from '@vue/test-utils'
|
|||||||
import { createStore } from 'vuex'
|
import { createStore } from 'vuex'
|
||||||
import actions from '@/store/actions'
|
import actions from '@/store/actions'
|
||||||
import mutations from '@/store/mutations'
|
import mutations from '@/store/mutations'
|
||||||
import Schema from '@/views/MainView/Workspace/Schema'
|
import Schema from '@/components/Schema'
|
||||||
import TableDescription from '@/views/MainView/Workspace/Schema/TableDescription'
|
import TableDescription from '@/components/Schema/TableDescription'
|
||||||
import database from '@/lib/database'
|
import database from '@/lib/database'
|
||||||
import fIo from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
import csv from '@/lib/csv'
|
import csv from '@/lib/csv'
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { shallowMount } from '@vue/test-utils'
|
import { shallowMount } from '@vue/test-utils'
|
||||||
import TableDescription from '@/views/MainView/Workspace/Schema/TableDescription'
|
import TableDescription from '@/components/Schema/TableDescription'
|
||||||
|
|
||||||
describe('TableDescription.vue', () => {
|
describe('TableDescription.vue', () => {
|
||||||
it('Initially the columns are hidden and table name is rendered', () => {
|
it('Initially the columns are hidden and table name is rendered', () => {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import { createStore } from 'vuex'
|
import { createStore } from 'vuex'
|
||||||
import SqlEditor from '@/views/MainView/Workspace/Tabs/Tab/SqlEditor'
|
import SqlEditor from '@/components/SqlEditor'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
|
|
||||||
describe('SqlEditor.vue', () => {
|
describe('SqlEditor.vue', () => {
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import state from '@/store/state'
|
import state from '@/store/state'
|
||||||
import showHint, {
|
import showHint, { getHints } from '@/components/SqlEditor/hint'
|
||||||
getHints
|
|
||||||
} from '@/views/MainView/Workspace/Tabs/Tab/SqlEditor/hint'
|
|
||||||
import CM from 'codemirror'
|
import CM from 'codemirror'
|
||||||
|
|
||||||
describe('hint.js', () => {
|
describe('hint.js', () => {
|
||||||
@@ -3,7 +3,7 @@ import sinon from 'sinon'
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import mutations from '@/store/mutations'
|
import mutations from '@/store/mutations'
|
||||||
import { createStore } from 'vuex'
|
import { createStore } from 'vuex'
|
||||||
import Tab from '@/views/MainView/Workspace/Tabs/Tab'
|
import Tab from '@/components/Tab'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
|
|
||||||
let place
|
let place
|
||||||
@@ -3,7 +3,7 @@ import sinon from 'sinon'
|
|||||||
import { shallowMount, mount } from '@vue/test-utils'
|
import { shallowMount, mount } from '@vue/test-utils'
|
||||||
import mutations from '@/store/mutations'
|
import mutations from '@/store/mutations'
|
||||||
import { createStore } from 'vuex'
|
import { createStore } from 'vuex'
|
||||||
import Tabs from '@/views/MainView/Workspace/Tabs'
|
import Tabs from '@/components/Tabs'
|
||||||
import eventBus from '@/lib/eventBus'
|
import eventBus from '@/lib/eventBus'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
import cIo from '@/lib/utils/clipboardIo'
|
import cIo from '@/lib/utils/clipboardIo'
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import ValueViewer from '@/views/MainView/Workspace/Tabs/Tab/RunResult/ValueViewer.vue'
|
import ValueViewer from '@/components/ValueViewer.vue'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
|
|
||||||
describe('ValueViewer.vue', () => {
|
describe('ValueViewer.vue', () => {
|
||||||
@@ -11,28 +11,45 @@ describe('ValueViewer.vue', () => {
|
|||||||
it('shows value in text mode', () => {
|
it('shows value in text mode', () => {
|
||||||
const wrapper = mount(ValueViewer, {
|
const wrapper = mount(ValueViewer, {
|
||||||
props: {
|
props: {
|
||||||
cellValue: 'foo'
|
value: 'foo'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(wrapper.find('.value-body').text()).to.equals('foo')
|
expect(wrapper.find('.value-body').text()).to.equals('foo')
|
||||||
|
expect(wrapper.find('button.text').attributes('aria-selected')).to.equal(
|
||||||
|
'true'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows meta values', async () => {
|
||||||
|
const wrapper = mount(ValueViewer, {
|
||||||
|
props: {
|
||||||
|
value: new Uint8Array()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(wrapper.find('.value-body').text()).to.equals('BLOB')
|
||||||
|
|
||||||
|
await wrapper.setProps({ value: null })
|
||||||
|
expect(wrapper.find('.value-body').text()).to.equals('NULL')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('shows error in json mode if the value is not json', async () => {
|
it('shows error in json mode if the value is not json', async () => {
|
||||||
const wrapper = mount(ValueViewer, {
|
const wrapper = mount(ValueViewer, {
|
||||||
props: {
|
props: {
|
||||||
cellValue: 'foo'
|
value: 'foo',
|
||||||
|
defaultFormat: 'json'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
await wrapper.find('button.json').trigger('click')
|
|
||||||
expect(wrapper.find('.value-body').text()).to.equals("Can't parse JSON.")
|
expect(wrapper.find('.value-body').text()).to.equals("Can't parse JSON.")
|
||||||
|
expect(wrapper.find('button[aria-selected="true"]').text()).contains('JSON')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('copy to clipboard', async () => {
|
it('copy to clipboard', async () => {
|
||||||
sinon.stub(window.navigator.clipboard, 'writeText').resolves()
|
sinon.stub(window.navigator.clipboard, 'writeText').resolves()
|
||||||
const wrapper = mount(ValueViewer, {
|
const wrapper = mount(ValueViewer, {
|
||||||
props: {
|
props: {
|
||||||
cellValue: 'foo'
|
value: 'foo'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -41,13 +58,20 @@ describe('ValueViewer.vue', () => {
|
|||||||
expect(window.navigator.clipboard.writeText.calledOnceWith('foo')).to.equal(
|
expect(window.navigator.clipboard.writeText.calledOnceWith('foo')).to.equal(
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await wrapper.setProps({ value: '{"foo": "bar"}' })
|
||||||
|
await wrapper.find('button.json').trigger('click')
|
||||||
|
await wrapper.find('button.copy').trigger('click')
|
||||||
|
expect(window.navigator.clipboard.writeText.args[1][0]).to.equal(
|
||||||
|
'{\n "foo": "bar"\n}'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('wraps lines', async () => {
|
it('wraps lines', async () => {
|
||||||
const wrapper = mount(ValueViewer, {
|
const wrapper = mount(ValueViewer, {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
props: {
|
props: {
|
||||||
cellValue: 'foo'
|
value: 'foo'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -55,7 +79,7 @@ describe('ValueViewer.vue', () => {
|
|||||||
const valueBody = wrapper.find('.value-body').wrapperElement
|
const valueBody = wrapper.find('.value-body').wrapperElement
|
||||||
expect(valueBody.scrollWidth).to.equal(valueBody.clientWidth)
|
expect(valueBody.scrollWidth).to.equal(valueBody.clientWidth)
|
||||||
|
|
||||||
await wrapper.setProps({ cellValue: 'foo'.repeat(100) })
|
await wrapper.setProps({ value: 'foo'.repeat(100) })
|
||||||
expect(valueBody.scrollWidth).not.to.equal(valueBody.clientWidth)
|
expect(valueBody.scrollWidth).not.to.equal(valueBody.clientWidth)
|
||||||
|
|
||||||
await wrapper.find('button.line-wrap').trigger('click')
|
await wrapper.find('button.line-wrap').trigger('click')
|
||||||
@@ -67,7 +91,7 @@ describe('ValueViewer.vue', () => {
|
|||||||
const wrapper = mount(ValueViewer, {
|
const wrapper = mount(ValueViewer, {
|
||||||
attachTo: document.body,
|
attachTo: document.body,
|
||||||
props: {
|
props: {
|
||||||
cellValue: '{"foo": "foofoofoofoofoofoofoofoofoofoo"}'
|
value: '{"foo": "foofoofoofoofoofoofoofoofoofoo"}'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -83,4 +107,15 @@ describe('ValueViewer.vue', () => {
|
|||||||
expect(codeMirrorScroll.scrollWidth).to.equal(codeMirrorScroll.clientWidth)
|
expect(codeMirrorScroll.scrollWidth).to.equal(codeMirrorScroll.clientWidth)
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('shows empty message if empty is true', () => {
|
||||||
|
const wrapper = mount(ValueViewer, {
|
||||||
|
props: {
|
||||||
|
empty: true,
|
||||||
|
emptyMessage: 'I am empty'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(wrapper.find('.value-viewer').text()).to.equals('I am empty')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -39,4 +39,262 @@ describe('_migrations.js', () => {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('migrates from version 2 to the current', () => {
|
||||||
|
const oldInquiries = [
|
||||||
|
{
|
||||||
|
id: '123',
|
||||||
|
name: 'foo',
|
||||||
|
query: 'SELECT * FROM foo',
|
||||||
|
viewType: 'chart',
|
||||||
|
viewOptions: { here_are: 'foo chart settings' },
|
||||||
|
createdAt: '2021-05-06T11:05:50.877Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '456',
|
||||||
|
name: 'bar',
|
||||||
|
query: 'SELECT * FROM bar',
|
||||||
|
viewType: 'graph',
|
||||||
|
viewOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
nodes: {
|
||||||
|
size: { type: 'constant', value: 10 },
|
||||||
|
color: {
|
||||||
|
type: 'calculated',
|
||||||
|
method: 'degree',
|
||||||
|
colorscale: null,
|
||||||
|
mode: 'continious',
|
||||||
|
colorscaleDirection: 'reversed'
|
||||||
|
},
|
||||||
|
label: { source: 'label', color: '#444444' }
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: { type: 'constant', value: 2 },
|
||||||
|
color: { type: 'constant', value: '#a2b1c6' },
|
||||||
|
label: { source: null, color: '#a2b1c6' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'forceAtlas2',
|
||||||
|
options: {
|
||||||
|
initialIterationsAmount: 50,
|
||||||
|
adjustSizes: false,
|
||||||
|
barnesHutOptimize: false,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
edgeWeightInfluence: 0,
|
||||||
|
gravity: 1,
|
||||||
|
linLogMode: false,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
scalingRatio: 1,
|
||||||
|
slowDown: 1,
|
||||||
|
strongGravityMode: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createdAt: '2021-05-07T11:05:50.877Z'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
expect(migrations._migrate(2, oldInquiries)).to.eql([
|
||||||
|
{
|
||||||
|
id: '123',
|
||||||
|
name: 'foo',
|
||||||
|
query: 'SELECT * FROM foo',
|
||||||
|
viewType: 'chart',
|
||||||
|
viewOptions: { here_are: 'foo chart settings' },
|
||||||
|
createdAt: '2021-05-06T11:05:50.877Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '456',
|
||||||
|
name: 'bar',
|
||||||
|
query: 'SELECT * FROM bar',
|
||||||
|
viewType: 'graph',
|
||||||
|
viewOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
highlightMode: 'node_and_neighbors',
|
||||||
|
nodes: {
|
||||||
|
size: { type: 'constant', value: 10 },
|
||||||
|
color: {
|
||||||
|
type: 'calculated',
|
||||||
|
method: 'degree',
|
||||||
|
colorscale: null,
|
||||||
|
mode: 'continious',
|
||||||
|
colorscaleDirection: 'reversed',
|
||||||
|
opacity: 100
|
||||||
|
},
|
||||||
|
label: { source: 'label', color: '#444444' }
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: { type: 'constant', value: 2 },
|
||||||
|
color: { type: 'constant', value: '#a2b1c6' },
|
||||||
|
label: { source: null, color: '#a2b1c6' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'forceAtlas2',
|
||||||
|
options: {
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
|
initialIterationsAmount: 50,
|
||||||
|
adjustSizes: false,
|
||||||
|
barnesHutOptimize: false,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
edgeWeightInfluence: 0,
|
||||||
|
gravity: 1,
|
||||||
|
linLogMode: false,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
scalingRatio: 1,
|
||||||
|
slowDown: 1,
|
||||||
|
strongGravityMode: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createdAt: '2021-05-07T11:05:50.877Z'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('migrates from version 3 to the current', () => {
|
||||||
|
const oldInquiries = [
|
||||||
|
{
|
||||||
|
id: '123',
|
||||||
|
name: 'foo',
|
||||||
|
query: 'SELECT * FROM foo',
|
||||||
|
viewType: 'chart',
|
||||||
|
viewOptions: { here_are: 'foo chart settings' },
|
||||||
|
createdAt: '2021-05-06T11:05:50.877Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '456',
|
||||||
|
name: 'bar',
|
||||||
|
query: 'SELECT * FROM bar',
|
||||||
|
viewType: 'graph',
|
||||||
|
viewOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
highlightMode: 'node_and_neighbors',
|
||||||
|
nodes: {
|
||||||
|
size: { type: 'constant', value: 10 },
|
||||||
|
color: {
|
||||||
|
type: 'calculated',
|
||||||
|
method: 'degree',
|
||||||
|
colorscale: null,
|
||||||
|
mode: 'continious',
|
||||||
|
colorscaleDirection: 'reversed',
|
||||||
|
opacity: 100
|
||||||
|
},
|
||||||
|
label: { source: 'label', color: '#444444' }
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: { type: 'constant', value: 2 },
|
||||||
|
color: { type: 'constant', value: '#a2b1c6' },
|
||||||
|
label: { source: null, color: '#a2b1c6' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'forceAtlas2',
|
||||||
|
options: {
|
||||||
|
initialIterationsAmount: 50,
|
||||||
|
adjustSizes: false,
|
||||||
|
barnesHutOptimize: false,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
edgeWeightInfluence: 0,
|
||||||
|
gravity: 1,
|
||||||
|
linLogMode: false,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
scalingRatio: 1,
|
||||||
|
slowDown: 1,
|
||||||
|
strongGravityMode: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createdAt: '2021-05-07T11:05:50.877Z'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
expect(migrations._migrate(3, oldInquiries)).to.eql([
|
||||||
|
{
|
||||||
|
id: '123',
|
||||||
|
name: 'foo',
|
||||||
|
query: 'SELECT * FROM foo',
|
||||||
|
viewType: 'chart',
|
||||||
|
viewOptions: { here_are: 'foo chart settings' },
|
||||||
|
createdAt: '2021-05-06T11:05:50.877Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '456',
|
||||||
|
name: 'bar',
|
||||||
|
query: 'SELECT * FROM bar',
|
||||||
|
viewType: 'graph',
|
||||||
|
viewOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
highlightMode: 'node_and_neighbors',
|
||||||
|
nodes: {
|
||||||
|
size: { type: 'constant', value: 10 },
|
||||||
|
color: {
|
||||||
|
type: 'calculated',
|
||||||
|
method: 'degree',
|
||||||
|
colorscale: null,
|
||||||
|
mode: 'continious',
|
||||||
|
colorscaleDirection: 'reversed',
|
||||||
|
opacity: 100
|
||||||
|
},
|
||||||
|
label: { source: 'label', color: '#444444' }
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: { type: 'constant', value: 2 },
|
||||||
|
color: { type: 'constant', value: '#a2b1c6' },
|
||||||
|
label: { source: null, color: '#a2b1c6' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'forceAtlas2',
|
||||||
|
options: {
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
|
initialIterationsAmount: 50,
|
||||||
|
adjustSizes: false,
|
||||||
|
barnesHutOptimize: false,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
edgeWeightInfluence: 0,
|
||||||
|
gravity: 1,
|
||||||
|
linLogMode: false,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
scalingRatio: 1,
|
||||||
|
slowDown: 1,
|
||||||
|
strongGravityMode: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createdAt: '2021-05-07T11:05:50.877Z'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe('storedInquiries.js', () => {
|
|||||||
expect(inquiries).to.eql([])
|
expect(inquiries).to.eql([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('getStoredInquiries migrate and returns inquiries of v1', () => {
|
it('getStoredInquiries migrates and returns inquiries of v1', () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'myQueries',
|
'myQueries',
|
||||||
JSON.stringify([
|
JSON.stringify([
|
||||||
@@ -55,6 +55,145 @@ describe('storedInquiries.js', () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('getStoredInquiries migrates and returns inquiries of v2', () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'myInquiries',
|
||||||
|
JSON.stringify({
|
||||||
|
version: 2,
|
||||||
|
inquiries: [
|
||||||
|
{
|
||||||
|
id: 'Xh1Hc9v7P3mRPZVM59QiC',
|
||||||
|
query: 'SELECT * from doc',
|
||||||
|
viewType: 'graph',
|
||||||
|
viewOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
nodes: {
|
||||||
|
size: { type: 'constant', value: 10 },
|
||||||
|
color: {
|
||||||
|
type: 'calculated',
|
||||||
|
method: 'degree',
|
||||||
|
colorscale: null,
|
||||||
|
mode: 'continious',
|
||||||
|
colorscaleDirection: 'reversed'
|
||||||
|
},
|
||||||
|
label: { source: 'label', color: '#444444' }
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: { type: 'constant', value: 2 },
|
||||||
|
color: { type: 'constant', value: '#a2b1c6' },
|
||||||
|
label: { source: null, color: '#a2b1c6' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'forceAtlas2',
|
||||||
|
options: {
|
||||||
|
initialIterationsAmount: 50,
|
||||||
|
adjustSizes: false,
|
||||||
|
barnesHutOptimize: false,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
edgeWeightInfluence: 0,
|
||||||
|
gravity: 1,
|
||||||
|
linLogMode: false,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
scalingRatio: 1,
|
||||||
|
slowDown: 1,
|
||||||
|
strongGravityMode: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
name: 'student graph FA2',
|
||||||
|
updatedAt: '2026-01-19T21:49:40.708Z',
|
||||||
|
createdAt: '2026-01-19T21:46:13.899Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Yh1Hc9v7P3mRPZVM59QiD',
|
||||||
|
query: 'SELECT * from test',
|
||||||
|
viewType: 'chart',
|
||||||
|
viewOptions: 'some chart view options',
|
||||||
|
name: 'student chart',
|
||||||
|
updatedAt: '2026-01-19T21:49:40.708Z',
|
||||||
|
createdAt: '2026-01-19T21:46:13.899Z'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const inquiries = storedInquiries.getStoredInquiries()
|
||||||
|
expect(inquiries).to.eql([
|
||||||
|
{
|
||||||
|
id: 'Xh1Hc9v7P3mRPZVM59QiC',
|
||||||
|
query: 'SELECT * from doc',
|
||||||
|
viewType: 'graph',
|
||||||
|
viewOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
highlightMode: 'node_and_neighbors',
|
||||||
|
nodes: {
|
||||||
|
size: { type: 'constant', value: 10 },
|
||||||
|
color: {
|
||||||
|
type: 'calculated',
|
||||||
|
method: 'degree',
|
||||||
|
colorscale: null,
|
||||||
|
mode: 'continious',
|
||||||
|
colorscaleDirection: 'reversed',
|
||||||
|
opacity: 100
|
||||||
|
},
|
||||||
|
label: { source: 'label', color: '#444444' }
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: { type: 'constant', value: 2 },
|
||||||
|
color: { type: 'constant', value: '#a2b1c6' },
|
||||||
|
label: { source: null, color: '#a2b1c6' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'forceAtlas2',
|
||||||
|
options: {
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
|
initialIterationsAmount: 50,
|
||||||
|
adjustSizes: false,
|
||||||
|
barnesHutOptimize: false,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
edgeWeightInfluence: 0,
|
||||||
|
gravity: 1,
|
||||||
|
linLogMode: false,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
scalingRatio: 1,
|
||||||
|
slowDown: 1,
|
||||||
|
strongGravityMode: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
name: 'student graph FA2',
|
||||||
|
updatedAt: '2026-01-19T21:49:40.708Z',
|
||||||
|
createdAt: '2026-01-19T21:46:13.899Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Yh1Hc9v7P3mRPZVM59QiD',
|
||||||
|
query: 'SELECT * from test',
|
||||||
|
viewType: 'chart',
|
||||||
|
viewOptions: 'some chart view options',
|
||||||
|
name: 'student chart',
|
||||||
|
updatedAt: '2026-01-19T21:49:40.708Z',
|
||||||
|
createdAt: '2026-01-19T21:46:13.899Z'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
it('updateStorage and getStoredInquiries', () => {
|
it('updateStorage and getStoredInquiries', () => {
|
||||||
const data = [{ id: 1 }, { id: 2 }]
|
const data = [{ id: 1 }, { id: 2 }]
|
||||||
storedInquiries.updateStorage(data)
|
storedInquiries.updateStorage(data)
|
||||||
@@ -136,7 +275,7 @@ describe('storedInquiries.js', () => {
|
|||||||
const str = storedInquiries.serialiseInquiries(inquiryList)
|
const str = storedInquiries.serialiseInquiries(inquiryList)
|
||||||
const parsedJson = JSON.parse(str)
|
const parsedJson = JSON.parse(str)
|
||||||
|
|
||||||
expect(parsedJson.version).to.equal(2)
|
expect(parsedJson.version).to.equal(4)
|
||||||
expect(parsedJson.inquiries).to.have.lengthOf(2)
|
expect(parsedJson.inquiries).to.have.lengthOf(2)
|
||||||
expect(parsedJson.inquiries[1]).to.eql(inquiryList[1])
|
expect(parsedJson.inquiries[1]).to.eql(inquiryList[1])
|
||||||
expect(parsedJson.inquiries[0]).to.eql({
|
expect(parsedJson.inquiries[0]).to.eql({
|
||||||
@@ -149,7 +288,7 @@ describe('storedInquiries.js', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('deserialiseInquiries migrates inquiries', () => {
|
it('deserialiseInquiries migrates inquiries of v1', () => {
|
||||||
const str = `[
|
const str = `[
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@@ -188,7 +327,141 @@ describe('storedInquiries.js', () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('deserialiseInquiries return array for one inquiry of v1', () => {
|
it('deserialiseInquiries migrates inquiries of v2', () => {
|
||||||
|
const str = `{
|
||||||
|
"version": 2,
|
||||||
|
"inquiries": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "foo",
|
||||||
|
"query": "select * from foo",
|
||||||
|
"viewType": "chart",
|
||||||
|
"viewOptions": [],
|
||||||
|
"createdAt": "2020-11-03T14:17:49.524Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Xh1Hc9v7P3mRPZVM59QiC",
|
||||||
|
"query": "SELECT * from doc",
|
||||||
|
"viewType": "graph",
|
||||||
|
"viewOptions": {
|
||||||
|
"structure": {
|
||||||
|
"nodeId": "node_id",
|
||||||
|
"objectType": "object_type",
|
||||||
|
"edgeSource": "source",
|
||||||
|
"edgeTarget": "target"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"backgroundColor": "white",
|
||||||
|
"nodes": {
|
||||||
|
"size": { "type": "constant", "value": 10 },
|
||||||
|
"color": {
|
||||||
|
"type": "calculated",
|
||||||
|
"method": "degree",
|
||||||
|
"colorscale": null,
|
||||||
|
"mode": "continious",
|
||||||
|
"colorscaleDirection": "reversed"
|
||||||
|
},
|
||||||
|
"label": { "source": "label", "color": "#444444" }
|
||||||
|
},
|
||||||
|
"edges": {
|
||||||
|
"showDirection": true,
|
||||||
|
"size": { "type": "constant", "value": 2 },
|
||||||
|
"color": { "type": "constant", "value": "#a2b1c6" },
|
||||||
|
"label": { "source": null, "color": "#a2b1c6" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"type": "forceAtlas2",
|
||||||
|
"options": {
|
||||||
|
"initialIterationsAmount": 50,
|
||||||
|
"adjustSizes": false,
|
||||||
|
"barnesHutOptimize": false,
|
||||||
|
"barnesHutTheta": 0.5,
|
||||||
|
"edgeWeightInfluence": 0,
|
||||||
|
"gravity": 1,
|
||||||
|
"linLogMode": false,
|
||||||
|
"outboundAttractionDistribution": false,
|
||||||
|
"scalingRatio": 1,
|
||||||
|
"slowDown": 1,
|
||||||
|
"strongGravityMode": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "student graph",
|
||||||
|
"createdAt": "2026-01-19T21:46:13.899Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const inquiry = storedInquiries.deserialiseInquiries(str)
|
||||||
|
expect(inquiry).to.eql([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'foo',
|
||||||
|
query: 'select * from foo',
|
||||||
|
viewType: 'chart',
|
||||||
|
viewOptions: [],
|
||||||
|
createdAt: '2020-11-03T14:17:49.524Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Xh1Hc9v7P3mRPZVM59QiC',
|
||||||
|
query: 'SELECT * from doc',
|
||||||
|
viewType: 'graph',
|
||||||
|
viewOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
highlightMode: 'node_and_neighbors',
|
||||||
|
nodes: {
|
||||||
|
size: { type: 'constant', value: 10 },
|
||||||
|
color: {
|
||||||
|
type: 'calculated',
|
||||||
|
method: 'degree',
|
||||||
|
colorscale: null,
|
||||||
|
mode: 'continious',
|
||||||
|
colorscaleDirection: 'reversed',
|
||||||
|
opacity: 100
|
||||||
|
},
|
||||||
|
label: { source: 'label', color: '#444444' }
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: { type: 'constant', value: 2 },
|
||||||
|
color: { type: 'constant', value: '#a2b1c6' },
|
||||||
|
label: { source: null, color: '#a2b1c6' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'forceAtlas2',
|
||||||
|
options: {
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
|
initialIterationsAmount: 50,
|
||||||
|
adjustSizes: false,
|
||||||
|
barnesHutOptimize: false,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
edgeWeightInfluence: 0,
|
||||||
|
gravity: 1,
|
||||||
|
linLogMode: false,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
scalingRatio: 1,
|
||||||
|
slowDown: 1,
|
||||||
|
strongGravityMode: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
name: 'student graph',
|
||||||
|
createdAt: '2026-01-19T21:46:13.899Z'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('deserialiseInquiries returns array for one inquiry of v1', () => {
|
||||||
const str = `
|
const str = `
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@@ -215,7 +488,7 @@ describe('storedInquiries.js', () => {
|
|||||||
it('deserialiseInquiries generates new id to avoid duplication', () => {
|
it('deserialiseInquiries generates new id to avoid duplication', () => {
|
||||||
storedInquiries.updateStorage([{ id: 1 }])
|
storedInquiries.updateStorage([{ id: 1 }])
|
||||||
const str = `{
|
const str = `{
|
||||||
"version": 2,
|
"version": 3,
|
||||||
"inquiries": [
|
"inquiries": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@@ -275,7 +548,7 @@ describe('storedInquiries.js', () => {
|
|||||||
|
|
||||||
it('importInquiries', async () => {
|
it('importInquiries', async () => {
|
||||||
const str = `{
|
const str = `{
|
||||||
"version": 2,
|
"version": 4,
|
||||||
"inquiries": [{
|
"inquiries": [{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "foo",
|
"name": "foo",
|
||||||
@@ -300,7 +573,7 @@ describe('storedInquiries.js', () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('readPredefinedInquiries old', async () => {
|
it('readPredefinedInquiries v1', async () => {
|
||||||
const str = `[
|
const str = `[
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
@@ -325,7 +598,7 @@ describe('storedInquiries.js', () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('readPredefinedInquiries', async () => {
|
it('readPredefinedInquiries v2', async () => {
|
||||||
const str = `{
|
const str = `{
|
||||||
"version": 2,
|
"version": 2,
|
||||||
"inquiries": [
|
"inquiries": [
|
||||||
@@ -336,7 +609,59 @@ describe('storedInquiries.js', () => {
|
|||||||
"viewType": "chart",
|
"viewType": "chart",
|
||||||
"viewOptions": [],
|
"viewOptions": [],
|
||||||
"createdAt": "2020-11-03T14:17:49.524Z"
|
"createdAt": "2020-11-03T14:17:49.524Z"
|
||||||
}]
|
},
|
||||||
|
{
|
||||||
|
"id": "Xh1Hc9v7P3mRPZVM59QiC",
|
||||||
|
"query": "SELECT * from doc",
|
||||||
|
"viewType": "graph",
|
||||||
|
"viewOptions": {
|
||||||
|
"structure": {
|
||||||
|
"nodeId": "node_id",
|
||||||
|
"objectType": "object_type",
|
||||||
|
"edgeSource": "source",
|
||||||
|
"edgeTarget": "target"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"backgroundColor": "white",
|
||||||
|
"nodes": {
|
||||||
|
"size": { "type": "constant", "value": 10 },
|
||||||
|
"color": {
|
||||||
|
"type": "calculated",
|
||||||
|
"method": "degree",
|
||||||
|
"colorscale": null,
|
||||||
|
"mode": "continious",
|
||||||
|
"colorscaleDirection": "reversed"
|
||||||
|
},
|
||||||
|
"label": { "source": "label", "color": "#444444" }
|
||||||
|
},
|
||||||
|
"edges": {
|
||||||
|
"showDirection": true,
|
||||||
|
"size": { "type": "constant", "value": 2 },
|
||||||
|
"color": { "type": "constant", "value": "#a2b1c6" },
|
||||||
|
"label": { "source": null, "color": "#a2b1c6" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"type": "forceAtlas2",
|
||||||
|
"options": {
|
||||||
|
"initialIterationsAmount": 50,
|
||||||
|
"adjustSizes": false,
|
||||||
|
"barnesHutOptimize": false,
|
||||||
|
"barnesHutTheta": 0.5,
|
||||||
|
"edgeWeightInfluence": 0,
|
||||||
|
"gravity": 1,
|
||||||
|
"linLogMode": false,
|
||||||
|
"outboundAttractionDistribution": false,
|
||||||
|
"scalingRatio": 1,
|
||||||
|
"slowDown": 1,
|
||||||
|
"strongGravityMode": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "student graph",
|
||||||
|
"createdAt": "2026-01-19T21:46:13.899Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
sinon.stub(fu, 'readFile').returns(Promise.resolve(new Response(str)))
|
sinon.stub(fu, 'readFile').returns(Promise.resolve(new Response(str)))
|
||||||
@@ -350,6 +675,106 @@ describe('storedInquiries.js', () => {
|
|||||||
viewType: 'chart',
|
viewType: 'chart',
|
||||||
viewOptions: [],
|
viewOptions: [],
|
||||||
createdAt: '2020-11-03T14:17:49.524Z'
|
createdAt: '2020-11-03T14:17:49.524Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Xh1Hc9v7P3mRPZVM59QiC',
|
||||||
|
query: 'SELECT * from doc',
|
||||||
|
viewType: 'graph',
|
||||||
|
viewOptions: {
|
||||||
|
structure: {
|
||||||
|
nodeId: 'node_id',
|
||||||
|
objectType: 'object_type',
|
||||||
|
edgeSource: 'source',
|
||||||
|
edgeTarget: 'target'
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
highlightMode: 'node_and_neighbors',
|
||||||
|
nodes: {
|
||||||
|
size: { type: 'constant', value: 10 },
|
||||||
|
color: {
|
||||||
|
type: 'calculated',
|
||||||
|
method: 'degree',
|
||||||
|
colorscale: null,
|
||||||
|
mode: 'continious',
|
||||||
|
colorscaleDirection: 'reversed',
|
||||||
|
opacity: 100
|
||||||
|
},
|
||||||
|
label: { source: 'label', color: '#444444' }
|
||||||
|
},
|
||||||
|
edges: {
|
||||||
|
showDirection: true,
|
||||||
|
size: { type: 'constant', value: 2 },
|
||||||
|
color: { type: 'constant', value: '#a2b1c6' },
|
||||||
|
label: { source: null, color: '#a2b1c6' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
type: 'forceAtlas2',
|
||||||
|
options: {
|
||||||
|
initialAlgorithm: 'circular',
|
||||||
|
initialIterationsAmount: 50,
|
||||||
|
adjustSizes: false,
|
||||||
|
barnesHutOptimize: false,
|
||||||
|
barnesHutTheta: 0.5,
|
||||||
|
edgeWeightInfluence: 0,
|
||||||
|
gravity: 1,
|
||||||
|
linLogMode: false,
|
||||||
|
outboundAttractionDistribution: false,
|
||||||
|
scalingRatio: 1,
|
||||||
|
slowDown: 1,
|
||||||
|
strongGravityMode: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
name: 'student graph',
|
||||||
|
createdAt: '2026-01-19T21:46:13.899Z'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('readPredefinedInquiries', async () => {
|
||||||
|
const str = `{
|
||||||
|
"version": 4,
|
||||||
|
"inquiries": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "foo",
|
||||||
|
"query": "select * from foo",
|
||||||
|
"viewType": "chart",
|
||||||
|
"viewOptions": [],
|
||||||
|
"createdAt": "2020-11-03T14:17:49.524Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "boo",
|
||||||
|
"query": "select * from boo",
|
||||||
|
"viewType": "graph",
|
||||||
|
"viewOptions": {},
|
||||||
|
"createdAt": "2020-11-03T14:17:49.524Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`
|
||||||
|
sinon.stub(fu, 'readFile').returns(Promise.resolve(new Response(str)))
|
||||||
|
const inquiries = await storedInquiries.readPredefinedInquiries()
|
||||||
|
expect(fu.readFile.calledOnceWith('./inquiries.json')).to.equal(true)
|
||||||
|
expect(inquiries).to.eql([
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: 'foo',
|
||||||
|
query: 'select * from foo',
|
||||||
|
viewType: 'chart',
|
||||||
|
viewOptions: [],
|
||||||
|
createdAt: '2020-11-03T14:17:49.524Z'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: 'boo',
|
||||||
|
query: 'select * from boo',
|
||||||
|
viewType: 'graph',
|
||||||
|
viewOptions: {},
|
||||||
|
createdAt: '2020-11-03T14:17:49.524Z'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { expect } from 'chai'
|
|||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import { mount, shallowMount } from '@vue/test-utils'
|
import { mount, shallowMount } from '@vue/test-utils'
|
||||||
import { createStore } from 'vuex'
|
import { createStore } from 'vuex'
|
||||||
import Inquiries from '@/views/MainView/Inquiries'
|
import Inquiries from '@/views/Inquiries'
|
||||||
import storedInquiries from '@/lib/storedInquiries'
|
import storedInquiries from '@/lib/storedInquiries'
|
||||||
import mutations from '@/store/mutations'
|
import mutations from '@/store/mutations'
|
||||||
import actions from '@/store/actions'
|
import actions from '@/store/actions'
|
||||||
@@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils'
|
|||||||
import actions from '@/store/actions'
|
import actions from '@/store/actions'
|
||||||
import mutations from '@/store/mutations'
|
import mutations from '@/store/mutations'
|
||||||
import { createStore } from 'vuex'
|
import { createStore } from 'vuex'
|
||||||
import Workspace from '@/views/MainView/Workspace'
|
import Workspace from '@/views/Workspace'
|
||||||
|
|
||||||
describe('Workspace.vue', () => {
|
describe('Workspace.vue', () => {
|
||||||
it('Creates a tab with example if schema is empty', () => {
|
it('Creates a tab with example if schema is empty', () => {
|
||||||
Reference in New Issue
Block a user