mirror of
https://github.com/lana-k/sqliteviz.git
synced 2025-12-06 18:18:53 +08:00
format
This commit is contained in:
12
.eslintrc.js
12
.eslintrc.js
@@ -2,12 +2,9 @@ module.exports = {
|
|||||||
root: true,
|
root: true,
|
||||||
env: {
|
env: {
|
||||||
node: true,
|
node: true,
|
||||||
es2022: true,
|
es2022: true
|
||||||
},
|
},
|
||||||
extends: [
|
extends: ['eslint:recommended', 'plugin:vue/vue3-recommended', 'prettier'],
|
||||||
'plugin:vue/essential',
|
|
||||||
'@vue/standard'
|
|
||||||
],
|
|
||||||
rules: {
|
rules: {
|
||||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
@@ -20,10 +17,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: [
|
files: ['**/__tests__/*.{j,t}s?(x)', '**/tests/**/*.spec.{j,t}s?(x)'],
|
||||||
'**/__tests__/*.{j,t}s?(x)',
|
|
||||||
'**/tests/**/*.spec.{j,t}s?(x)'
|
|
||||||
],
|
|
||||||
env: {
|
env: {
|
||||||
mocha: true
|
mocha: true
|
||||||
}
|
}
|
||||||
|
|||||||
13
.github/workflows/config.grenrc.js
vendored
13
.github/workflows/config.grenrc.js
vendored
@@ -1,17 +1,14 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
dataSource: 'milestones',
|
dataSource: 'milestones',
|
||||||
ignoreIssuesWith: [
|
ignoreIssuesWith: ['wontfix', 'duplicate'],
|
||||||
'wontfix',
|
|
||||||
'duplicate'
|
|
||||||
],
|
|
||||||
milestoneMatch: 'v{{tag_name}}',
|
milestoneMatch: 'v{{tag_name}}',
|
||||||
template: {
|
template: {
|
||||||
issue: '- {{name}} [{{text}}]({{url}})',
|
issue: '- {{name}} [{{text}}]({{url}})',
|
||||||
changelogTitle: "",
|
changelogTitle: '',
|
||||||
release: "{{body}}",
|
release: '{{body}}'
|
||||||
},
|
},
|
||||||
groupBy: {
|
groupBy: {
|
||||||
'Enhancements': ["enhancement", "internal"],
|
Enhancements: ['enhancement', 'internal'],
|
||||||
'Bug fixes': ["bug"]
|
'Bug fixes': ['bug']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@@ -40,6 +40,6 @@ jobs:
|
|||||||
- name: Create release
|
- name: Create release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
artifacts: "dist.zip,dist_map.zip"
|
artifacts: 'dist.zip,dist_map.zip'
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
bodyFile: "CHANGELOG.md"
|
bodyFile: 'CHANGELOG.md'
|
||||||
|
|||||||
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"trailingComma": "none",
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ Sqliteviz is a single-page offline-first PWA for fully client-side visualisation
|
|||||||
of SQLite databases, CSV, JSON or NDJSON files.
|
of SQLite databases, CSV, JSON or NDJSON files.
|
||||||
|
|
||||||
With sqliteviz you can:
|
With sqliteviz you can:
|
||||||
|
|
||||||
- run SQL queries against a SQLite database and create [Plotly][11] charts and pivot tables based on the result sets
|
- run SQL queries against a SQLite database and create [Plotly][11] charts and pivot tables based on the result sets
|
||||||
- import a CSV/JSON/NDJSON file into a SQLite database and visualize imported data
|
- import a CSV/JSON/NDJSON file into a SQLite database and visualize imported data
|
||||||
- export result set to CSV file
|
- export result set to CSV file
|
||||||
@@ -19,15 +20,19 @@ With sqliteviz you can:
|
|||||||
https://user-images.githubusercontent.com/24638357/128249848-f8fab0f5-9add-46e0-a9c1-dd5085a8623e.mp4
|
https://user-images.githubusercontent.com/24638357/128249848-f8fab0f5-9add-46e0-a9c1-dd5085a8623e.mp4
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
The latest release of sqliteviz is deployed on [sqliteviz.com/app][6].
|
The latest release of sqliteviz is deployed on [sqliteviz.com/app][6].
|
||||||
|
|
||||||
## Wiki
|
## Wiki
|
||||||
|
|
||||||
For user documentation, check out sqliteviz [documentation][7].
|
For user documentation, check out sqliteviz [documentation][7].
|
||||||
|
|
||||||
## Motivation
|
## Motivation
|
||||||
|
|
||||||
It's a kind of middleground between [Plotly Falcon][1] and [Redash][2].
|
It's a kind of middleground between [Plotly Falcon][1] and [Redash][2].
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
It is built on top of [react-chart-editor][3], [PivotTable.js][12], [sql.js][4] and [Vue-Codemirror][8] in [Vue.js][5]. CSV parsing is performed with [Papa Parse][9].
|
It is built on top of [react-chart-editor][3], [PivotTable.js][12], [sql.js][4] and [Vue-Codemirror][8] in [Vue.js][5]. CSV parsing is performed with [Papa Parse][9].
|
||||||
|
|
||||||
[1]: https://github.com/plotly/falcon
|
[1]: https://github.com/plotly/falcon
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: ['@vue/cli-plugin-babel/preset']
|
||||||
'@vue/cli-plugin-babel/preset'
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
40
index.html
40
index.html
@@ -1,11 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
<link rel="icon" href="favicon.png">
|
<link rel="icon" href="favicon.png" />
|
||||||
<link rel="manifest" href="manifest.webmanifest">
|
<link rel="manifest" href="manifest.webmanifest" />
|
||||||
<title>sqliteviz</title>
|
<title>sqliteviz</title>
|
||||||
<style>
|
<style>
|
||||||
#sqliteviz-loading-wrapper {
|
#sqliteviz-loading-wrapper {
|
||||||
@@ -38,15 +38,18 @@
|
|||||||
|
|
||||||
#sqliteviz-loading-wrapper circle {
|
#sqliteviz-loading-wrapper circle {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0; right: 0; top: 0; bottom: 0;
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 5px;
|
stroke-width: 5px;
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
stroke: #119DFF;
|
stroke: #119dff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sqliteviz-loading-wrapper circle.bg {
|
#sqliteviz-loading-wrapper circle.bg {
|
||||||
stroke: #C8D4E3;
|
stroke: #c8d4e3;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sqliteviz-loading-wrapper circle.front {
|
#sqliteviz-loading-wrapper circle.front {
|
||||||
@@ -74,24 +77,17 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
<strong>We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
<strong>
|
||||||
|
We're sorry but this app doesn't work properly without JavaScript
|
||||||
|
enabled. Please enable it to continue.
|
||||||
|
</strong>
|
||||||
</noscript>
|
</noscript>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<div id="sqliteviz-loading-wrapper">
|
<div id="sqliteviz-loading-wrapper">
|
||||||
<div id="sqliteviz-loading-text">LOADING</div>
|
<div id="sqliteviz-loading-text">LOADING</div>
|
||||||
<svg height="170" width="170" viewBox="0 0 170 170">
|
<svg height="170" width="170" viewBox="0 0 170 170">
|
||||||
<circle
|
<circle class="bg" cx="85" cy="85" r="80" />
|
||||||
class="bg"
|
<circle class="front" cx="85" cy="85" r="80" />
|
||||||
cx="85"
|
|
||||||
cy="85"
|
|
||||||
r="80"
|
|
||||||
/>
|
|
||||||
<circle
|
|
||||||
class="front"
|
|
||||||
cx="85"
|
|
||||||
cy="85"
|
|
||||||
r="80"
|
|
||||||
/>
|
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
10
jsconfig.json
Normal file
10
jsconfig.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"include": ["src/**/*"],
|
||||||
|
"exclude": ["node_modules", "dist"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./",
|
||||||
|
"paths": {
|
||||||
|
"@*": ["./src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ module.exports = function (config) {
|
|||||||
config: {
|
config: {
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'vue': 'vue/dist/vue.esm-bundler.js'
|
vue: 'vue/dist/vue.esm-bundler.js'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
@@ -32,19 +32,19 @@ module.exports = function (config) {
|
|||||||
pattern: 'test.setup.js',
|
pattern: 'test.setup.js',
|
||||||
type: 'module',
|
type: 'module',
|
||||||
watched: false,
|
watched: false,
|
||||||
served: false,
|
served: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: 'tests/**/*.spec.js',
|
pattern: 'tests/**/*.spec.js',
|
||||||
type: 'module',
|
type: 'module',
|
||||||
watched: false,
|
watched: false,
|
||||||
served: false,
|
served: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: 'src/assets/styles/*.css',
|
pattern: 'src/assets/styles/*.css',
|
||||||
type: 'css',
|
type: 'css',
|
||||||
watched: false,
|
watched: false,
|
||||||
served: false,
|
served: false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
module.exports = function (config) {
|
module.exports = function (config) {
|
||||||
const timeout = 15 * 60 * 1000
|
const timeout = 15 * 60 * 1000
|
||||||
config.set({
|
config.set({
|
||||||
|
|
||||||
frameworks: ['mocha'],
|
frameworks: ['mocha'],
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
'suite.js',
|
'suite.js',
|
||||||
{ pattern: 'node_modules/sql.js/dist/sql-wasm.wasm', served: true, included: false },
|
{
|
||||||
|
pattern: 'node_modules/sql.js/dist/sql-wasm.wasm',
|
||||||
|
served: true,
|
||||||
|
included: false
|
||||||
|
},
|
||||||
{ pattern: 'sample.csv', served: true, included: false }
|
{ pattern: 'sample.csv', served: true, included: false }
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -15,7 +18,10 @@ module.exports = function (config) {
|
|||||||
singleRun: true,
|
singleRun: true,
|
||||||
|
|
||||||
customLaunchers: {
|
customLaunchers: {
|
||||||
ChromiumHeadlessNoSandbox: { base: 'ChromiumHeadless', flags: ['--no-sandbox'] }
|
ChromiumHeadlessNoSandbox: {
|
||||||
|
base: 'ChromiumHeadless',
|
||||||
|
flags: ['--no-sandbox']
|
||||||
|
}
|
||||||
},
|
},
|
||||||
browsers: ['ChromiumHeadlessNoSandbox', 'FirefoxHeadless'],
|
browsers: ['ChromiumHeadlessNoSandbox', 'FirefoxHeadless'],
|
||||||
concurrency: 1,
|
concurrency: 1,
|
||||||
@@ -47,6 +53,5 @@ module.exports = function (config) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
jsonToFileReporter: { outputPath: '.', fileName: 'suite-result.json' }
|
jsonToFileReporter: { outputPath: '.', fileName: 'suite-result.json' }
|
||||||
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import lodash from 'lodash'
|
|||||||
import Papa from 'papaparse'
|
import Papa from 'papaparse'
|
||||||
import useragent from 'ua-parser-js'
|
import useragent from 'ua-parser-js'
|
||||||
|
|
||||||
|
|
||||||
describe('SQLite build benchmark', function () {
|
describe('SQLite build benchmark', function () {
|
||||||
let parsedCsv
|
let parsedCsv
|
||||||
let sqlModule
|
let sqlModule
|
||||||
@@ -50,10 +49,8 @@ describe('SQLite build benchmark', function () {
|
|||||||
suite.add('select', { initCount: 3, minSamples: 50, fn: benchmarkSelect })
|
suite.add('select', { initCount: 3, minSamples: 50, fn: benchmarkSelect })
|
||||||
await run(suite)
|
await run(suite)
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
function importToTable(db, parsedCsv, chunkSize = 1024) {
|
function importToTable(db, parsedCsv, chunkSize = 1024) {
|
||||||
const columnListString = parsedCsv.meta.fields.join(', ')
|
const columnListString = parsedCsv.meta.fields.join(', ')
|
||||||
db.exec(`CREATE TABLE csv_import(${columnListString})`)
|
db.exec(`CREATE TABLE csv_import(${columnListString})`)
|
||||||
@@ -67,7 +64,6 @@ function importToTable (db, parsedCsv, chunkSize = 1024) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class PromiseWrapper {
|
class PromiseWrapper {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.promise = new Promise((resolve, reject) => {
|
this.promise = new Promise((resolve, reject) => {
|
||||||
@@ -102,7 +98,6 @@ function chunkArray (arr, size) {
|
|||||||
}, [])
|
}, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function createSuite() {
|
function createSuite() {
|
||||||
// Combined workaround from:
|
// Combined workaround from:
|
||||||
// - https://github.com/bestiejs/benchmark.js/issues/106
|
// - https://github.com/bestiejs/benchmark.js/issues/106
|
||||||
@@ -124,10 +119,12 @@ function run (suite) {
|
|||||||
console.info(String(event.target))
|
console.info(String(event.target))
|
||||||
})
|
})
|
||||||
.on('complete', function () {
|
.on('complete', function () {
|
||||||
console.log(JSON.stringify({
|
console.log(
|
||||||
|
JSON.stringify({
|
||||||
browser: useragent(navigator.userAgent).browser,
|
browser: useragent(navigator.userAgent).browser,
|
||||||
result: this.filter('successful')
|
result: this.filter('successful')
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
suiteResult.resolve()
|
suiteResult.resolve()
|
||||||
})
|
})
|
||||||
.on('error', function (event) {
|
.on('error', function (event) {
|
||||||
|
|||||||
58
package-lock.json
generated
58
package-lock.json
generated
@@ -42,6 +42,7 @@
|
|||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"chai-as-promised": "^8.0.1",
|
"chai-as-promised": "^8.0.1",
|
||||||
"eslint": "^8.57.1",
|
"eslint": "^8.57.1",
|
||||||
|
"eslint-config-prettier": "^10.1.1",
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.20.2",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
@@ -56,6 +57,7 @@
|
|||||||
"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",
|
||||||
|
"prettier": "3.5.3",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"url-loader": "^4.1.1",
|
"url-loader": "^4.1.1",
|
||||||
"vite": "^5.4.14",
|
"vite": "^5.4.14",
|
||||||
@@ -3421,6 +3423,22 @@
|
|||||||
"url": "https://opencollective.com/postcss/"
|
"url": "https://opencollective.com/postcss/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/component-compiler-utils/node_modules/prettier": {
|
||||||
|
"version": "2.8.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||||
|
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin-prettier.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vue/component-compiler-utils/node_modules/yallist": {
|
"node_modules/@vue/component-compiler-utils/node_modules/yallist": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||||
@@ -7967,6 +7985,18 @@
|
|||||||
"url": "https://opencollective.com/eslint"
|
"url": "https://opencollective.com/eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eslint-config-prettier": {
|
||||||
|
"version": "10.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz",
|
||||||
|
"integrity": "sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"eslint-config-prettier": "bin/cli.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"eslint": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/eslint-import-resolver-custom-alias": {
|
"node_modules/eslint-import-resolver-custom-alias": {
|
||||||
"version": "1.3.2",
|
"version": "1.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/eslint-import-resolver-custom-alias/-/eslint-import-resolver-custom-alias-1.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/eslint-import-resolver-custom-alias/-/eslint-import-resolver-custom-alias-1.3.2.tgz",
|
||||||
@@ -14788,16 +14818,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "2.8.8",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin-prettier.js"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.13.0"
|
"node": ">=14"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
@@ -20029,6 +20058,23 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-cli-plugin-ui-karma/node_modules/prettier": {
|
||||||
|
"version": "2.8.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||||
|
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"peer": true,
|
||||||
|
"bin": {
|
||||||
|
"prettier": "bin-prettier.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-cli-plugin-ui-karma/node_modules/read-pkg": {
|
"node_modules/vue-cli-plugin-ui-karma/node_modules/read-pkg": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"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"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
@@ -45,6 +46,7 @@
|
|||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"chai-as-promised": "^8.0.1",
|
"chai-as-promised": "^8.0.1",
|
||||||
"eslint": "^8.57.1",
|
"eslint": "^8.57.1",
|
||||||
|
"eslint-config-prettier": "^10.1.1",
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.20.2",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
@@ -59,6 +61,7 @@
|
|||||||
"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",
|
||||||
|
"prettier": "3.5.3",
|
||||||
"process": "^0.11.10",
|
"process": "^0.11.10",
|
||||||
"url-loader": "^4.1.1",
|
"url-loader": "^4.1.1",
|
||||||
"vite": "^5.4.14",
|
"vite": "^5.4.14",
|
||||||
|
|||||||
26
src/App.vue
26
src/App.vue
@@ -32,43 +32,43 @@ export default {
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Open Sans";
|
font-family: 'Open Sans';
|
||||||
src: url("@/assets/fonts/OpenSans-Regular.woff2");
|
src: url('@/assets/fonts/OpenSans-Regular.woff2');
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Open Sans";
|
font-family: 'Open Sans';
|
||||||
src: url("@/assets/fonts/OpenSans-SemiBold.woff2");
|
src: url('@/assets/fonts/OpenSans-SemiBold.woff2');
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Open Sans";
|
font-family: 'Open Sans';
|
||||||
src: url("@/assets/fonts/OpenSans-Bold.woff2");
|
src: url('@/assets/fonts/OpenSans-Bold.woff2');
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Open Sans";
|
font-family: 'Open Sans';
|
||||||
src: url("@/assets/fonts/OpenSans-Italic.woff2");
|
src: url('@/assets/fonts/OpenSans-Italic.woff2');
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Open Sans";
|
font-family: 'Open Sans';
|
||||||
src: url("@/assets/fonts/OpenSans-SemiBoldItalic.woff2");
|
src: url('@/assets/fonts/OpenSans-SemiBoldItalic.woff2');
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Open Sans";
|
font-family: 'Open Sans';
|
||||||
src: url("@/assets/fonts/OpenSans-BoldItalic.woff2");
|
src: url('@/assets/fonts/OpenSans-BoldItalic.woff2');
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,7 @@ label,
|
|||||||
button,
|
button,
|
||||||
.plotly_editor *,
|
.plotly_editor *,
|
||||||
.CodeMirror pre.CodeMirror-line {
|
.CodeMirror pre.CodeMirror-line {
|
||||||
font-family: "Open Sans", Helvetica, Arial, sans-serif;
|
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,5 +59,3 @@ button.secondary:disabled {
|
|||||||
text-shadow: none;
|
text-shadow: none;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ table.sqliteviz-table {
|
|||||||
margin-top: -35px;
|
margin-top: -35px;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
.sqliteviz-table thead th, .fixed-header {
|
.sqliteviz-table thead th,
|
||||||
|
.fixed-header {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -108,8 +109,8 @@ table.sqliteviz-table {
|
|||||||
color: var(--color-text-base);
|
color: var(--color-text-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sqliteviz-table tbody td[data-isNull="true"],
|
.sqliteviz-table tbody td[data-isNull='true'],
|
||||||
.sqliteviz-table tbody td[data-isBlob="true"] {
|
.sqliteviz-table tbody td[data-isBlob='true'] {
|
||||||
color: var(--color-text-light-2);
|
color: var(--color-text-light-2);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
line-height: 19px;;
|
line-height: 19px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
height: 19px;
|
height: 19px;
|
||||||
border-radius: var(--border-radius-medium);
|
border-radius: var(--border-radius-medium);
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
:root {
|
:root {
|
||||||
--color-white: #ffffff;
|
--color-white: #ffffff;
|
||||||
--color-gray-light: #F3F6FA;
|
--color-gray-light: #f3f6fa;
|
||||||
--color-gray-light-2: #DFE8F3;
|
--color-gray-light-2: #dfe8f3;
|
||||||
--color-gray-light-3: #C8D4E3;
|
--color-gray-light-3: #c8d4e3;
|
||||||
--color-gray-light-4:#EBF0F8;
|
--color-gray-light-4: #ebf0f8;
|
||||||
--color-gray-light-5: #f8f8f9;
|
--color-gray-light-5: #f8f8f9;
|
||||||
--color-gray-medium: #A2B1C6;
|
--color-gray-medium: #a2b1c6;
|
||||||
--color-gray-dark: #506784;
|
--color-gray-dark: #506784;
|
||||||
--color-blue-medium: #119DFF;
|
--color-blue-medium: #119dff;
|
||||||
--color-blue-dark: #0D76BF;
|
--color-blue-dark: #0d76bf;
|
||||||
--color-blue-dark-2: #2A3F5F;
|
--color-blue-dark-2: #2a3f5f;
|
||||||
--color-red: #EF553B;
|
--color-red: #ef553b;
|
||||||
--color-red-2: #DE350B;
|
--color-red-2: #de350b;
|
||||||
--color-red-light: #FFBDAD;
|
--color-red-light: #ffbdad;
|
||||||
--color-yellow: #FBEFCB;
|
--color-yellow: #fbefcb;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--color-bg-light: var(--color-gray-light);
|
--color-bg-light: var(--color-gray-light);
|
||||||
--color-bg-light-2: var(--color-gray-light-2);
|
--color-bg-light-2: var(--color-gray-light-2);
|
||||||
@@ -48,6 +46,3 @@
|
|||||||
.plotly-editor--theme-provider {
|
.plotly-editor--theme-provider {
|
||||||
--sidebar-width: 112px;
|
--sidebar-width: 112px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
:class="['checkbox-container', { 'checked': checked }, {'disabled': disabled}]"
|
:class="[
|
||||||
|
'checkbox-container',
|
||||||
|
{ checked: checked },
|
||||||
|
{ disabled: disabled }
|
||||||
|
]"
|
||||||
@click.stop="onClick"
|
@click.stop="onClick"
|
||||||
>
|
>
|
||||||
<div v-show="!checked" class="unchecked" />
|
<div v-show="!checked" class="unchecked" />
|
||||||
@@ -31,7 +35,7 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: 'accent',
|
default: 'accent',
|
||||||
validator: (value) => {
|
validator: value => {
|
||||||
return ['accent', 'light'].includes(value)
|
return ['accent', 'light'].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="{ 'disabled': disabled }">
|
<div :class="{ disabled: disabled }">
|
||||||
<div class="text-field-label">Delimiter</div>
|
<div class="text-field-label">Delimiter</div>
|
||||||
<div
|
<div
|
||||||
class="delimiter-selector-container"
|
class="delimiter-selector-container"
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
>
|
>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<input
|
<input
|
||||||
:class="{ 'filled': filled }"
|
:class="{ filled: filled }"
|
||||||
ref="delimiterInput"
|
ref="delimiterInput"
|
||||||
type="text"
|
type="text"
|
||||||
maxlength="1"
|
maxlength="1"
|
||||||
@@ -33,7 +33,8 @@
|
|||||||
@click="chooseOption(option)"
|
@click="chooseOption(option)"
|
||||||
class="option"
|
class="option"
|
||||||
>
|
>
|
||||||
<pre>{{option}}</pre><div>{{ getSymbolName(option) }}</div>
|
<pre>{{ option }}</pre>
|
||||||
|
<div>{{ getSymbolName(option) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -66,10 +66,7 @@
|
|||||||
class="preview-table"
|
class="preview-table"
|
||||||
/>
|
/>
|
||||||
<div v-else class="no-data">No data</div>
|
<div v-else class="no-data">No data</div>
|
||||||
<logs
|
<logs class="import-errors" :messages="importMessages" />
|
||||||
class="import-errors"
|
|
||||||
:messages="importMessages"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-buttons-container">
|
<div class="dialog-buttons-container">
|
||||||
<button
|
<button
|
||||||
@@ -175,8 +172,7 @@ export default {
|
|||||||
if (!this.tableName) {
|
if (!this.tableName) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.db.validateTableName(this.tableName)
|
this.db.validateTableName(this.tableName).catch(err => {
|
||||||
.catch(err => {
|
|
||||||
this.tableNameError = err.message + '. Try another table name.'
|
this.tableNameError = err.message + '. Try another table name.'
|
||||||
})
|
})
|
||||||
}, 400)
|
}, 400)
|
||||||
@@ -257,10 +253,12 @@ export default {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
this.importMessages = [{
|
this.importMessages = [
|
||||||
|
{
|
||||||
message: err,
|
message: err,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async getJsonParseResult(file) {
|
async getJsonParseResult(file) {
|
||||||
@@ -273,7 +271,7 @@ export default {
|
|||||||
},
|
},
|
||||||
hasErrors: false,
|
hasErrors: false,
|
||||||
messages: [],
|
messages: [],
|
||||||
rowCount: +(!isEmpty)
|
rowCount: +!isEmpty
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async loadToDb(file) {
|
async loadToDb(file) {
|
||||||
@@ -297,7 +295,9 @@ export default {
|
|||||||
})
|
})
|
||||||
// Get *reactive* link to parsing message for later updates
|
// Get *reactive* link to parsing message for later updates
|
||||||
parsingMsg = this.importMessages[this.importMessages.length - 1]
|
parsingMsg = this.importMessages[this.importMessages.length - 1]
|
||||||
const parsingLoadingIndicator = setTimeout(() => { parsingMsg.type = 'loading' }, 1000)
|
const parsingLoadingIndicator = setTimeout(() => {
|
||||||
|
parsingMsg.type = 'loading'
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
let importMsg = {}
|
let importMsg = {}
|
||||||
let importLoadingIndicator = null
|
let importLoadingIndicator = null
|
||||||
@@ -321,7 +321,9 @@ export default {
|
|||||||
parsingMsg.type = 'success'
|
parsingMsg.type = 'success'
|
||||||
|
|
||||||
if (parseResult.messages.length > 0) {
|
if (parseResult.messages.length > 0) {
|
||||||
this.importMessages = this.importMessages.concat(parseResult.messages)
|
this.importMessages = this.importMessages.concat(
|
||||||
|
parseResult.messages
|
||||||
|
)
|
||||||
parsingMsg.message = `${rowCount} rows are parsed in ${period}.`
|
parsingMsg.message = `${rowCount} rows are parsed in ${period}.`
|
||||||
} else {
|
} else {
|
||||||
// Inform about parsing success
|
// Inform about parsing success
|
||||||
@@ -345,13 +347,18 @@ export default {
|
|||||||
|
|
||||||
// Add table
|
// Add table
|
||||||
start = new Date()
|
start = new Date()
|
||||||
await this.db.addTableFromCsv(this.tableName, parseResult.data, progressCounterId)
|
await this.db.addTableFromCsv(
|
||||||
|
this.tableName,
|
||||||
|
parseResult.data,
|
||||||
|
progressCounterId
|
||||||
|
)
|
||||||
end = new Date()
|
end = new Date()
|
||||||
|
|
||||||
this.addedTable = this.tableName
|
this.addedTable = this.tableName
|
||||||
// Inform about import success
|
// Inform about import success
|
||||||
period = time.getPeriod(start, end)
|
period = time.getPeriod(start, end)
|
||||||
importMsg.message = `Importing ${this.typeName} ` +
|
importMsg.message =
|
||||||
|
`Importing ${this.typeName} ` +
|
||||||
`into a SQLite database is completed in ${period}.`
|
`into a SQLite database is completed in ${period}.`
|
||||||
importMsg.type = 'success'
|
importMsg.type = 'success'
|
||||||
|
|
||||||
@@ -508,5 +515,4 @@ margin-bottom: 24px;
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
@click="browse"
|
@click="browse"
|
||||||
>
|
>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
Drop the database, CSV, JSON or NDJSON file here
|
Drop the database, CSV, JSON or NDJSON file here or click to choose a
|
||||||
or click to choose a file from your computer.
|
file from your computer.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -19,16 +19,16 @@
|
|||||||
<img id="drop-file-top-img" src="~@/assets/images/top.svg" />
|
<img id="drop-file-top-img" src="~@/assets/images/top.svg" />
|
||||||
<img
|
<img
|
||||||
id="left-arm-img"
|
id="left-arm-img"
|
||||||
:class="{'swing': state === 'dragover'}"
|
:class="{ swing: state === 'dragover' }"
|
||||||
src="~@/assets/images/leftArm.svg"
|
src="~@/assets/images/leftArm.svg"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
id="file-img"
|
id="file-img"
|
||||||
ref="fileImg"
|
ref="fileImg"
|
||||||
:class="{
|
:class="{
|
||||||
'swing': state === 'dragover',
|
swing: state === 'dragover',
|
||||||
'fly': state === 'dropping',
|
fly: state === 'dropping',
|
||||||
'hidden': state === 'dropped'
|
hidden: state === 'dropped'
|
||||||
}"
|
}"
|
||||||
src="~@/assets/images/file.png"
|
src="~@/assets/images/file.png"
|
||||||
/>
|
/>
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
<img id="body-img" src="~@/assets/images/body.svg" />
|
<img id="body-img" src="~@/assets/images/body.svg" />
|
||||||
<img
|
<img
|
||||||
id="right-arm-img"
|
id="right-arm-img"
|
||||||
:class="{'swing': state === 'dragover'}"
|
:class="{ swing: state === 'dragover' }"
|
||||||
src="~@/assets/images/rightArm.svg"
|
src="~@/assets/images/rightArm.svg"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,7 +68,7 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
required: false,
|
required: false,
|
||||||
default: 'small',
|
default: 'small',
|
||||||
validator: (value) => {
|
validator: value => {
|
||||||
return ['illustrated', 'small'].includes(value)
|
return ['illustrated', 'small'].includes(value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -93,7 +93,7 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.type === 'illustrated') {
|
if (this.type === 'illustrated') {
|
||||||
this.animationPromise = new Promise((resolve) => {
|
this.animationPromise = new Promise(resolve => {
|
||||||
this.$refs.fileImg.addEventListener('animationend', event => {
|
this.$refs.fileImg.addEventListener('animationend', event => {
|
||||||
if (event.animationName.startsWith('fly')) {
|
if (event.animationName.startsWith('fly')) {
|
||||||
this.state = 'dropped'
|
this.state = 'dropped'
|
||||||
@@ -119,8 +119,9 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
loadDb(file) {
|
loadDb(file) {
|
||||||
return Promise.all([this.newDb.loadDb(file), this.animationPromise])
|
return Promise.all([this.newDb.loadDb(file), this.animationPromise]).then(
|
||||||
.then(this.finish)
|
this.finish
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
async checkFile(file) {
|
async checkFile(file) {
|
||||||
@@ -140,12 +141,15 @@ export default {
|
|||||||
await this.$nextTick()
|
await this.$nextTick()
|
||||||
const csvJsonImportModal = this.$refs.addCsvJson
|
const csvJsonImportModal = this.$refs.addCsvJson
|
||||||
csvJsonImportModal.reset()
|
csvJsonImportModal.reset()
|
||||||
return Promise.all([csvJsonImportModal.preview(), this.animationPromise])
|
return Promise.all([
|
||||||
.then(csvJsonImportModal.open)
|
csvJsonImportModal.preview(),
|
||||||
|
this.animationPromise
|
||||||
|
]).then(csvJsonImportModal.open)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
browse() {
|
browse() {
|
||||||
fIo.getFileFromUser('.db,.sqlite,.sqlite3,.csv,.json,.ndjson')
|
fIo
|
||||||
|
.getFileFromUser('.db,.sqlite,.sqlite3,.csv,.json,.ndjson')
|
||||||
.then(this.checkFile)
|
.then(this.checkFile)
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -246,8 +250,12 @@ export default {
|
|||||||
transform-origin: -74px 139px;
|
transform-origin: -74px 139px;
|
||||||
}
|
}
|
||||||
@keyframes swing {
|
@keyframes swing {
|
||||||
0% { transform: rotate(0deg); }
|
0% {
|
||||||
100% { transform: rotate(-7deg); }
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(-7deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#file-img.fly {
|
#file-img.fly {
|
||||||
|
|||||||
@@ -10,7 +10,12 @@
|
|||||||
<div v-show="loading" class="icon-in-progress">
|
<div v-show="loading" class="icon-in-progress">
|
||||||
<loading-indicator />
|
<loading-indicator />
|
||||||
</div>
|
</div>
|
||||||
<span v-if="tooltip" class="icon-tooltip" :style="tooltipStyle" ref="tooltip">
|
<span
|
||||||
|
v-if="tooltip"
|
||||||
|
class="icon-tooltip"
|
||||||
|
:style="tooltipStyle"
|
||||||
|
ref="tooltip"
|
||||||
|
>
|
||||||
{{ tooltip }}
|
{{ tooltip }}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -16,7 +16,10 @@
|
|||||||
{{ loadingMsg }}
|
{{ loadingMsg }}
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="loading-dialog-body">
|
<div v-else class="loading-dialog-body">
|
||||||
<img src="~@/assets/images/success.svg" class="success-icon state-icon" />
|
<img
|
||||||
|
src="~@/assets/images/success.svg"
|
||||||
|
class="success-icon state-icon"
|
||||||
|
/>
|
||||||
{{ successMsg }}
|
{{ successMsg }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg :class="animationClass" :height="size" :width="size" :viewBox="`0 0 ${size} ${size}`">
|
<svg
|
||||||
|
:class="animationClass"
|
||||||
|
:height="size"
|
||||||
|
:width="size"
|
||||||
|
:viewBox="`0 0 ${size} ${size}`"
|
||||||
|
>
|
||||||
<circle
|
<circle
|
||||||
class="loader-svg bg"
|
class="loader-svg bg"
|
||||||
:style="{ strokeWidth }"
|
:style="{ strokeWidth }"
|
||||||
@@ -9,7 +14,11 @@
|
|||||||
/>
|
/>
|
||||||
<circle
|
<circle
|
||||||
class="loader-svg front"
|
class="loader-svg front"
|
||||||
:style="{ strokeDasharray: circleProgress, strokeDashoffset: offset, strokeWidth }"
|
:style="{
|
||||||
|
strokeDasharray: circleProgress,
|
||||||
|
strokeDashoffset: offset,
|
||||||
|
strokeWidth
|
||||||
|
}"
|
||||||
:cx="size / 2"
|
:cx="size / 2"
|
||||||
:cy="size / 2"
|
:cy="size / 2"
|
||||||
:r="radius"
|
:r="radius"
|
||||||
@@ -35,7 +44,9 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
circleProgress() {
|
circleProgress() {
|
||||||
const circle = this.radius * 3.14 * 2
|
const circle = this.radius * 3.14 * 2
|
||||||
const dash = this.progress ? (circle * this.progress) / 100 : circle * 1 / 3
|
const dash = this.progress
|
||||||
|
? (circle * this.progress) / 100
|
||||||
|
: (circle * 1) / 3
|
||||||
const space = circle - dash
|
const space = circle - dash
|
||||||
return `${dash}px, ${space}px`
|
return `${dash}px, ${space}px`
|
||||||
},
|
},
|
||||||
@@ -46,7 +57,7 @@ export default {
|
|||||||
return this.size / 2 - this.strokeWidth
|
return this.size / 2 - this.strokeWidth
|
||||||
},
|
},
|
||||||
offset() {
|
offset() {
|
||||||
return this.radius * 3.14 / 2
|
return (this.radius * 3.14) / 2
|
||||||
},
|
},
|
||||||
strokeWidth() {
|
strokeWidth() {
|
||||||
return this.size / 10
|
return this.size / 10
|
||||||
@@ -58,7 +69,10 @@ export default {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.loader-svg {
|
.loader-svg {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0; right: 0; top: 0; bottom: 0;
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
stroke: var(--color-accent);
|
stroke: var(--color-accent);
|
||||||
@@ -112,5 +126,4 @@ export default {
|
|||||||
r: 8;
|
r: 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="logs-container" ref="logsContainer">
|
<div class="logs-container" ref="logsContainer">
|
||||||
<div v-for="(msg, index) in messages" :key="index" class="msg">
|
<div v-for="(msg, index) in messages" :key="index" class="msg">
|
||||||
<img v-if="msg.type === 'error'" src="~@/assets/images/error.svg">
|
<img v-if="msg.type === 'error'" src="~@/assets/images/error.svg" />
|
||||||
<img v-if="msg.type === 'info'" src="~@/assets/images/info.svg" width="20px">
|
<img
|
||||||
<img v-if="msg.type === 'success'" src="~@/assets/images/success.svg">
|
v-if="msg.type === 'info'"
|
||||||
<loading-indicator v-if="msg.type === 'loading'" :progress="msg.progress" />
|
src="~@/assets/images/info.svg"
|
||||||
|
width="20px"
|
||||||
|
/>
|
||||||
|
<img v-if="msg.type === 'success'" src="~@/assets/images/success.svg" />
|
||||||
|
<loading-indicator
|
||||||
|
v-if="msg.type === 'loading'"
|
||||||
|
:progress="msg.progress"
|
||||||
|
/>
|
||||||
<span class="msg-text">{{ serializeMessage(msg) }}</span>
|
<span class="msg-text">{{ serializeMessage(msg) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,7 +51,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result += msg.message
|
result += msg.message
|
||||||
if (!(/(\.|!|\?)$/.test(result))) {
|
if (!/(\.|!|\?)$/.test(result)) {
|
||||||
result += '.'
|
result += '.'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,11 @@
|
|||||||
{ 'splitpanes-dragging': dragging }
|
{ 'splitpanes-dragging': dragging }
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<div class="movable-splitter" ref="movableSplitter" :style="movableSplitterStyle" />
|
<div
|
||||||
|
class="movable-splitter"
|
||||||
|
ref="movableSplitter"
|
||||||
|
:style="movableSplitterStyle"
|
||||||
|
/>
|
||||||
<div
|
<div
|
||||||
class="splitpanes-pane"
|
class="splitpanes-pane"
|
||||||
ref="left"
|
ref="left"
|
||||||
@@ -27,8 +31,11 @@
|
|||||||
:class="[
|
:class="[
|
||||||
'toggle-btns',
|
'toggle-btns',
|
||||||
{
|
{
|
||||||
'both': after.max === 100 && before.max === 100 &&
|
both:
|
||||||
paneAfter.size > 0 && paneBefore.size > 0
|
after.max === 100 &&
|
||||||
|
before.max === 100 &&
|
||||||
|
paneAfter.size > 0 &&
|
||||||
|
paneBefore.size > 0
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
@@ -41,7 +48,7 @@
|
|||||||
class="direction-icon"
|
class="direction-icon"
|
||||||
src="~@/assets/images/chevron.svg"
|
src="~@/assets/images/chevron.svg"
|
||||||
:style="directionBeforeIconStyle"
|
:style="directionBeforeIconStyle"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="before.max === 100 && paneBefore.size > 0"
|
v-if="before.max === 100 && paneBefore.size > 0"
|
||||||
@@ -52,16 +59,12 @@
|
|||||||
class="direction-icon"
|
class="direction-icon"
|
||||||
src="~@/assets/images/chevron.svg"
|
src="~@/assets/images/chevron.svg"
|
||||||
:style="directionAfterIconStyle"
|
:style="directionAfterIconStyle"
|
||||||
>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- splitter end -->
|
<!-- splitter end -->
|
||||||
<div
|
<div class="splitpanes-pane" ref="right" :style="styles.after">
|
||||||
class="splitpanes-pane"
|
|
||||||
ref="right"
|
|
||||||
:style="styles.after"
|
|
||||||
>
|
|
||||||
<slot name="right-pane" />
|
<slot name="right-pane" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -92,7 +95,8 @@ export default {
|
|||||||
container: null,
|
container: null,
|
||||||
paneBefore: this.before,
|
paneBefore: this.before,
|
||||||
paneAfter: this.after,
|
paneAfter: this.after,
|
||||||
beforeMinimising: !this.after.size || !this.before.size
|
beforeMinimising:
|
||||||
|
!this.after.size || !this.before.size
|
||||||
? this.default
|
? this.default
|
||||||
: {
|
: {
|
||||||
before: this.before.size,
|
before: this.before.size,
|
||||||
@@ -109,8 +113,12 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
styles() {
|
styles() {
|
||||||
return {
|
return {
|
||||||
before: { [this.horizontal ? 'height' : 'width']: `${this.paneBefore.size}%` },
|
before: {
|
||||||
after: { [this.horizontal ? 'height' : 'width']: `${this.paneAfter.size}%` }
|
[this.horizontal ? 'height' : 'width']: `${this.paneBefore.size}%`
|
||||||
|
},
|
||||||
|
after: {
|
||||||
|
[this.horizontal ? 'height' : 'width']: `${this.paneAfter.size}%`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
movableSplitterStyle() {
|
movableSplitterStyle() {
|
||||||
@@ -154,21 +162,29 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
bindEvents() {
|
bindEvents() {
|
||||||
// Passive: false to prevent scrolling while touch dragging.
|
// Passive: false to prevent scrolling while touch dragging.
|
||||||
document.addEventListener('mousemove', this.onMouseMove, { passive: false })
|
document.addEventListener('mousemove', this.onMouseMove, {
|
||||||
|
passive: false
|
||||||
|
})
|
||||||
document.addEventListener('mouseup', this.onMouseUp)
|
document.addEventListener('mouseup', this.onMouseUp)
|
||||||
|
|
||||||
if ('ontouchstart' in window) {
|
if ('ontouchstart' in window) {
|
||||||
document.addEventListener('touchmove', this.onMouseMove, { passive: false })
|
document.addEventListener('touchmove', this.onMouseMove, {
|
||||||
|
passive: false
|
||||||
|
})
|
||||||
document.addEventListener('touchend', this.onMouseUp)
|
document.addEventListener('touchend', this.onMouseUp)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
unbindEvents() {
|
unbindEvents() {
|
||||||
document.removeEventListener('mousemove', this.onMouseMove, { passive: false })
|
document.removeEventListener('mousemove', this.onMouseMove, {
|
||||||
|
passive: false
|
||||||
|
})
|
||||||
document.removeEventListener('mouseup', this.onMouseUp)
|
document.removeEventListener('mouseup', this.onMouseUp)
|
||||||
|
|
||||||
if ('ontouchstart' in window) {
|
if ('ontouchstart' in window) {
|
||||||
document.removeEventListener('touchmove', this.onMouseMove, { passive: false })
|
document.removeEventListener('touchmove', this.onMouseMove, {
|
||||||
|
passive: false
|
||||||
|
})
|
||||||
document.removeEventListener('touchend', this.onMouseUp)
|
document.removeEventListener('touchend', this.onMouseUp)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -218,7 +234,8 @@ export default {
|
|||||||
this.beforeMinimising.before = this.paneBefore.size
|
this.beforeMinimising.before = this.paneBefore.size
|
||||||
this.beforeMinimising.after = this.paneAfter.size
|
this.beforeMinimising.after = this.paneAfter.size
|
||||||
pane.size = 0
|
pane.size = 0
|
||||||
const otherPane = pane === this.paneBefore ? this.paneAfter : this.paneBefore
|
const otherPane =
|
||||||
|
pane === this.paneBefore ? this.paneAfter : this.paneBefore
|
||||||
otherPane.size = 100 - pane.size
|
otherPane.size = 100 - pane.size
|
||||||
} else {
|
} else {
|
||||||
this.paneBefore.size = this.beforeMinimising.before
|
this.paneBefore.size = this.beforeMinimising.before
|
||||||
@@ -239,9 +256,15 @@ export default {
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.splitpanes-vertical {flex-direction: row;}
|
.splitpanes-vertical {
|
||||||
.splitpanes-horizontal {flex-direction: column;}
|
flex-direction: row;
|
||||||
.splitpanes-dragging * {user-select: none;}
|
}
|
||||||
|
.splitpanes-horizontal {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.splitpanes-dragging * {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
.splitpanes-pane {
|
.splitpanes-pane {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -288,7 +311,7 @@ export default {
|
|||||||
.splitpanes-vertical > .movable-splitter {
|
.splitpanes-vertical > .movable-splitter {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
height: 100%
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.splitpanes-horizontal > .splitpanes-splitter,
|
.splitpanes-horizontal > .splitpanes-splitter,
|
||||||
@@ -339,20 +362,32 @@ export default {
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.splitpanes-horizontal > .splitpanes-splitter .toggle-btns.both .toggle-btn:first-child {
|
.splitpanes-horizontal
|
||||||
|
> .splitpanes-splitter
|
||||||
|
.toggle-btns.both
|
||||||
|
.toggle-btn:first-child {
|
||||||
border-radius: var(--border-radius-small) 0 0 var(--border-radius-small);
|
border-radius: var(--border-radius-small) 0 0 var(--border-radius-small);
|
||||||
}
|
}
|
||||||
|
|
||||||
.splitpanes-horizontal > .splitpanes-splitter .toggle-btns.both .toggle-btn:last-child {
|
.splitpanes-horizontal
|
||||||
|
> .splitpanes-splitter
|
||||||
|
.toggle-btns.both
|
||||||
|
.toggle-btn:last-child {
|
||||||
border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0;
|
border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0;
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.splitpanes-vertical > .splitpanes-splitter .toggle-btns.both .toggle-btn:first-child {
|
.splitpanes-vertical
|
||||||
|
> .splitpanes-splitter
|
||||||
|
.toggle-btns.both
|
||||||
|
.toggle-btn:first-child {
|
||||||
border-radius: var(--border-radius-small) var(--border-radius-small) 0 0;
|
border-radius: var(--border-radius-small) var(--border-radius-small) 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.splitpanes-vertical > .splitpanes-splitter .toggle-btns.both .toggle-btn:last-child {
|
.splitpanes-vertical
|
||||||
|
> .splitpanes-splitter
|
||||||
|
.toggle-btns.both
|
||||||
|
.toggle-btn:last-child {
|
||||||
border-radius: 0 0 var(--border-radius-small) var(--border-radius-small);
|
border-radius: 0 0 var(--border-radius-small) var(--border-radius-small);
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ export default {
|
|||||||
// Get the cursor position relative to the splitpane container.
|
// Get the cursor position relative to the splitpane container.
|
||||||
getCurrentMouseDrag(event, container) {
|
getCurrentMouseDrag(event, container) {
|
||||||
const rect = container.getBoundingClientRect()
|
const rect = container.getBoundingClientRect()
|
||||||
const { clientX, clientY } = ('ontouchstart' in window && event.touches)
|
const { clientX, clientY } =
|
||||||
? event.touches[0]
|
'ontouchstart' in window && event.touches ? event.touches[0] : event
|
||||||
: event
|
|
||||||
return {
|
return {
|
||||||
x: clientX - rect.left,
|
x: clientX - rect.left,
|
||||||
y: clientY - rect.top
|
y: clientY - rect.top
|
||||||
@@ -15,20 +14,32 @@ export default {
|
|||||||
getCurrentDragPercentage(event, container, isHorisontal) {
|
getCurrentDragPercentage(event, container, isHorisontal) {
|
||||||
let drag = this.getCurrentMouseDrag(event, container)
|
let drag = this.getCurrentMouseDrag(event, container)
|
||||||
drag = drag[isHorisontal ? 'y' : 'x']
|
drag = drag[isHorisontal ? 'y' : 'x']
|
||||||
const containerSize = container[isHorisontal ? 'clientHeight' : 'clientWidth']
|
const containerSize =
|
||||||
return drag * 100 / containerSize
|
container[isHorisontal ? 'clientHeight' : 'clientWidth']
|
||||||
|
return (drag * 100) / containerSize
|
||||||
},
|
},
|
||||||
|
|
||||||
// Returns the new position in percents.
|
// Returns the new position in percents.
|
||||||
calculateOffset (event, { container, isHorisontal, paneBeforeMax, paneAfterMax }) {
|
calculateOffset(
|
||||||
const dragPercentage = this.getCurrentDragPercentage(event, container, isHorisontal)
|
event,
|
||||||
|
{ container, isHorisontal, paneBeforeMax, paneAfterMax }
|
||||||
|
) {
|
||||||
|
const dragPercentage = this.getCurrentDragPercentage(
|
||||||
|
event,
|
||||||
|
container,
|
||||||
|
isHorisontal
|
||||||
|
)
|
||||||
|
|
||||||
const paneBeforeMaxReached = paneBeforeMax < 100 && (dragPercentage >= paneBeforeMax)
|
const paneBeforeMaxReached =
|
||||||
const paneAfterMaxReached = paneAfterMax < 100 && (dragPercentage <= 100 - paneAfterMax)
|
paneBeforeMax < 100 && dragPercentage >= paneBeforeMax
|
||||||
|
const paneAfterMaxReached =
|
||||||
|
paneAfterMax < 100 && dragPercentage <= 100 - paneAfterMax
|
||||||
|
|
||||||
// Prevent dragging beyond pane max.
|
// Prevent dragging beyond pane max.
|
||||||
if (paneBeforeMaxReached || paneAfterMaxReached) {
|
if (paneBeforeMaxReached || paneAfterMaxReached) {
|
||||||
return paneBeforeMaxReached ? paneBeforeMax : Math.max(100 - paneAfterMax, 0)
|
return paneBeforeMaxReached
|
||||||
|
? paneBeforeMax
|
||||||
|
: Math.max(100 - paneAfterMax, 0)
|
||||||
} else {
|
} else {
|
||||||
return Math.min(Math.max(dragPercentage, 0), paneBeforeMax)
|
return Math.min(Math.max(dragPercentage, 0), paneBeforeMax)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,8 +131,9 @@ export default {
|
|||||||
|
|
||||||
if (this.selectedCellCoordinates) {
|
if (this.selectedCellCoordinates) {
|
||||||
const { row, col } = this.selectedCellCoordinates
|
const { row, col } = this.selectedCellCoordinates
|
||||||
const cell = this.$refs.table
|
const cell = this.$refs.table.querySelector(
|
||||||
.querySelector(`td[data-col="${col}"][data-row="${row}"]`)
|
`td[data-col="${col}"][data-row="${row}"]`
|
||||||
|
)
|
||||||
if (cell) {
|
if (cell) {
|
||||||
this.selectCell(cell)
|
this.selectCell(cell)
|
||||||
}
|
}
|
||||||
@@ -167,7 +168,8 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
onScrollTable() {
|
onScrollTable() {
|
||||||
this.$refs['header-container'].scrollLeft = this.$refs['table-container'].scrollLeft
|
this.$refs['header-container'].scrollLeft =
|
||||||
|
this.$refs['table-container'].scrollLeft
|
||||||
},
|
},
|
||||||
onTableKeydown(e) {
|
onTableKeydown(e) {
|
||||||
const keyCodeMap = {
|
const keyCodeMap = {
|
||||||
@@ -242,8 +244,9 @@ export default {
|
|||||||
newColIndex = currentColIndex
|
newColIndex = currentColIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
const newCell = this.$refs.table
|
const newCell = this.$refs.table.querySelector(
|
||||||
.querySelector(`td[data-col="${newColIndex}"][data-row="${newRowIndex}"]`)
|
`td[data-col="${newColIndex}"][data-row="${newRowIndex}"]`
|
||||||
|
)
|
||||||
if (newCell) {
|
if (newCell) {
|
||||||
this.selectCell(newCell)
|
this.selectCell(newCell)
|
||||||
}
|
}
|
||||||
@@ -271,7 +274,7 @@ table.sqliteviz-table:focus {
|
|||||||
.sqliteviz-table tbody td:hover {
|
.sqliteviz-table tbody td:hover {
|
||||||
background-color: var(--color-bg-light-3);
|
background-color: var(--color-bg-light-3);
|
||||||
}
|
}
|
||||||
.sqliteviz-table tbody td[aria-selected="true"] {
|
.sqliteviz-table tbody td[aria-selected='true'] {
|
||||||
box-shadow: inset 0 0 0 1px var(--color-accent);
|
box-shadow: inset 0 0 0 1px var(--color-accent);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-if="label" :class="['text-field-label', { error: errorMsg }, {'disabled': disabled}]">
|
<div
|
||||||
|
v-if="label"
|
||||||
|
:class="['text-field-label', { error: errorMsg }, { disabled: disabled }]"
|
||||||
|
>
|
||||||
{{ label }}
|
{{ label }}
|
||||||
<hint-icon class="hint" v-if="hint" :hint="hint" :max-width="maxHintWidth || '149px'"/>
|
<hint-icon
|
||||||
|
class="hint"
|
||||||
|
v-if="hint"
|
||||||
|
:hint="hint"
|
||||||
|
:max-width="maxHintWidth || '149px'"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
|||||||
@@ -15,6 +15,5 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 18 18"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
@@ -46,7 +41,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ChartIcon'
|
name: 'ChartIcon'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg
|
||||||
:class="['clear-icon', {'disabled': disabled}]"
|
:class="['clear-icon', { disabled: disabled }]"
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@@ -21,7 +21,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ClearIcon',
|
name: 'ClearIcon',
|
||||||
props: ['disabled']
|
props: ['disabled']
|
||||||
@@ -42,6 +41,6 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.disabled.clear-icon:hover path {
|
.disabled.clear-icon:hover path {
|
||||||
fill: #C8D4E3;
|
fill: #c8d4e3;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 18 18"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
d="M14.1917 1.3851H12.4806V0.703125C12.4806 0.314758 12.1658 0 11.7775 0H6.246C5.85764 0
|
d="M14.1917 1.3851H12.4806V0.703125C12.4806 0.314758 12.1658 0 11.7775 0H6.246C5.85764 0
|
||||||
5.54288 0.314758 5.54288 0.703125V1.3851H3.83203C2.86276 1.3851 2.07422 2.17365 2.07422
|
5.54288 0.314758 5.54288 0.703125V1.3851H3.83203C2.86276 1.3851 2.07422 2.17365 2.07422
|
||||||
@@ -26,7 +21,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ClipboardIcon'
|
name: 'ClipboardIcon'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg
|
||||||
@click.stop="$emit('click')"
|
@click.stop="$emit('click')"
|
||||||
:class="['icon', {'disabled': disabled }]"
|
:class="['icon', { disabled: disabled }]"
|
||||||
:width="size"
|
:width="size"
|
||||||
:height="size"
|
:height="size"
|
||||||
viewBox="0 0 14 14"
|
viewBox="0 0 14 14"
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 18 18"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
@@ -31,7 +26,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DataViewIcon'
|
name: 'DataViewIcon'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg
|
||||||
:class="['chevron-icon', {'disabled': disabled}]"
|
:class="['chevron-icon', { disabled: disabled }]"
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
viewBox="0 0 20 20"
|
viewBox="0 0 20 20"
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DropDownChevron',
|
name: 'DropDownChevron',
|
||||||
props: ['disabled']
|
props: ['disabled']
|
||||||
@@ -36,6 +35,6 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.disabled.chevron-icon:hover path {
|
.disabled.chevron-icon:hover path {
|
||||||
fill: #C8D4E3;
|
fill: #c8d4e3;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -21,6 +21,5 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="19" height="18" viewBox="0 0 19 18" fill="none">
|
||||||
width="19"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 19 18"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
d="M6.07959 13.5756C6.05908 14.0209 5.93896 14.415 5.71924 14.7578C5.49951 15.0976 5.19043
|
d="M6.07959 13.5756C6.05908 14.0209 5.93896 14.415 5.71924 14.7578C5.49951 15.0976 5.19043
|
||||||
15.3613 4.79199 15.5488C4.39648 15.7363 3.94385 15.83 3.43408 15.83C2.59326 15.83
|
15.3613 4.79199 15.5488C4.39648 15.7363 3.94385 15.83 3.43408 15.83C2.59326 15.83
|
||||||
@@ -55,7 +50,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ExportToCsvIcon'
|
name: 'ExportToCsvIcon'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="19" height="18" viewBox="0 0 19 18" fill="none">
|
||||||
width="19"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 19 18"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
d="M4.28369 13.9966C4.28369 13.7711 4.20312 13.5953 4.04199 13.4693C3.88379 13.3433 3.604
|
d="M4.28369 13.9966C4.28369 13.7711 4.20312 13.5953 4.04199 13.4693C3.88379 13.3433 3.604
|
||||||
13.213 3.20264 13.0782C2.80127 12.9434 2.47314 12.813 2.21826 12.6871C1.38916 12.2798
|
13.213 3.20264 13.0782C2.80127 12.9434 2.47314 12.813 2.21826 12.6871C1.38916 12.2798
|
||||||
@@ -54,7 +49,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ExportToSvgIcon'
|
name: 'ExportToSvgIcon'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,11 @@
|
|||||||
fill="#A2B1C6"
|
fill="#A2B1C6"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="icon-tooltip" :style="{...tooltipStyle, maxWidth: maxWidth }" ref="tooltip">
|
<span
|
||||||
|
class="icon-tooltip"
|
||||||
|
:style="{ ...tooltipStyle, maxWidth: maxWidth }"
|
||||||
|
ref="tooltip"
|
||||||
|
>
|
||||||
{{ hint }}
|
{{ hint }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 18 18"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
@@ -21,7 +16,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'PivotIcon'
|
name: 'PivotIcon'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 18 18"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
d="M9 5.51953C6.57686 5.51953 4.60547 7.49092 4.60547 9.91406C4.60547 12.3372 6.57686
|
d="M9 5.51953C6.57686 5.51953 4.60547 7.49092 4.60547 9.91406C4.60547 12.3372 6.57686
|
||||||
14.3086 9 14.3086C11.4231 14.3086 13.3945 12.3372 13.3945 9.91406C13.3945 7.49092 11.4231
|
14.3086 9 14.3086C11.4231 14.3086 13.3945 12.3372 13.3945 9.91406C13.3945 7.49092 11.4231
|
||||||
@@ -30,7 +25,10 @@
|
|||||||
5.5195V15.0117Z"
|
5.5195V15.0117Z"
|
||||||
fill="#A2B1C6"
|
fill="#A2B1C6"
|
||||||
/>
|
/>
|
||||||
<path d="M15.1875 6.22266H13.7812V7.62891H15.1875V6.22266Z" fill="#A2B1C6"/>
|
<path
|
||||||
|
d="M15.1875 6.22266H13.7812V7.62891H15.1875V6.22266Z"
|
||||||
|
fill="#A2B1C6"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="19" height="19" viewBox="0 0 19 19" fill="none">
|
||||||
width="19"
|
|
||||||
height="19"
|
|
||||||
viewBox="0 0 19 19"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<g clip-path="url(#clip0_2130_5292)">
|
<g clip-path="url(#clip0_2130_5292)">
|
||||||
<path
|
<path
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
@@ -46,7 +41,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'RowIcon'
|
name: 'RowIcon'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="12" height="13" viewBox="0 0 12 13" fill="none">
|
||||||
width="12"
|
<path
|
||||||
height="13"
|
d="M11.1624 6.94358L0.770043 12.9436L0.770043 0.943573L11.1624 6.94358Z"
|
||||||
viewBox="0 0 12 13"
|
fill="#A2B1C6"
|
||||||
fill="none"
|
/>
|
||||||
>
|
|
||||||
<path d="M11.1624 6.94358L0.770043 12.9436L0.770043 0.943573L11.1624 6.94358Z" fill="#A2B1C6"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'RunIcon'
|
name: 'RunIcon'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SortIcon',
|
name: 'SortIcon',
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="18" height="19" viewBox="0 0 18 19" fill="none">
|
||||||
width="18"
|
|
||||||
height="19"
|
|
||||||
viewBox="0 0 18 19"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<g clip-path="url(#clip0)">
|
<g clip-path="url(#clip0)">
|
||||||
<path
|
<path
|
||||||
d="M4.5 1.51343H10.5L15 6.01343V8.45284H13.5V6.76343H9.75V3.01343H4.5V8.45284H3V3.01343C3
|
d="M4.5 1.51343H10.5L15 6.01343V8.45284H13.5V6.76343H9.75V3.01343H4.5V8.45284H3V3.01343C3
|
||||||
@@ -47,14 +42,18 @@
|
|||||||
</g>
|
</g>
|
||||||
<defs>
|
<defs>
|
||||||
<clipPath id="clip0">
|
<clipPath id="clip0">
|
||||||
<rect width="18" height="18" fill="white" transform="translate(0 0.0134277)"/>
|
<rect
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
fill="white"
|
||||||
|
transform="translate(0 0.0134277)"
|
||||||
|
/>
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SqlEditorIcon'
|
name: 'SqlEditorIcon'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 18 18"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<path
|
<path
|
||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
@@ -41,7 +36,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TableIcon'
|
name: 'TableIcon'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'treeChevron',
|
name: 'treeChevron',
|
||||||
props: {
|
props: {
|
||||||
@@ -31,7 +30,7 @@ export default {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.chevron-icon {
|
.chevron-icon {
|
||||||
-webkit-transition: transform .15s ease-in-out;
|
-webkit-transition: transform 0.15s ease-in-out;
|
||||||
transition: transform .15s ease-in-out;
|
transition: transform 0.15s ease-in-out;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg
|
<svg width="19" height="19" viewBox="0 0 19 19" fill="none">
|
||||||
width="19"
|
|
||||||
height="19"
|
|
||||||
viewBox="0 0 19 19"
|
|
||||||
fill="none"
|
|
||||||
>
|
|
||||||
<g clip-path="url(#clip0_2131_6054)">
|
<g clip-path="url(#clip0_2131_6054)">
|
||||||
<path
|
<path
|
||||||
d="M3.53784 11.5846L3.53784 3.14734L11.9751 3.14734V7.676C12.4655 7.51991
|
d="M3.53784 11.5846L3.53784 3.14734L11.9751 3.14734V7.676C12.4655 7.51991
|
||||||
@@ -43,7 +38,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ViewCellValueIcon'
|
name: 'ViewCellValueIcon'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,9 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let rowNumber = 0; rowNumber < rowCount; rowNumber++) {
|
for (let rowNumber = 0; rowNumber < rowCount; rowNumber++) {
|
||||||
result.data.push(columns.map(column => resultSet.values[column][rowNumber]))
|
result.data.push(
|
||||||
|
columns.map(column => resultSet.values[column][rowNumber])
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import wasmUrl from 'sql.js/dist/sql-wasm.wasm?url'
|
|||||||
let SQL = null
|
let SQL = null
|
||||||
const sqlModuleReady = initSqlJs({
|
const sqlModuleReady = initSqlJs({
|
||||||
locateFile: () => wasmUrl
|
locateFile: () => wasmUrl
|
||||||
}).then(sqlModule => { SQL = sqlModule })
|
}).then(sqlModule => {
|
||||||
|
SQL = sqlModule
|
||||||
|
})
|
||||||
|
|
||||||
function _getDataSourcesFromSqlResult(sqlResult) {
|
function _getDataSourcesFromSqlResult(sqlResult) {
|
||||||
if (!sqlResult) {
|
if (!sqlResult) {
|
||||||
@@ -24,8 +26,7 @@ export default class Sql {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static build() {
|
static build() {
|
||||||
return sqlModuleReady
|
return sqlModuleReady.then(() => {
|
||||||
.then(() => {
|
|
||||||
return new Sql()
|
return new Sql()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -80,7 +81,10 @@ export default class Sql {
|
|||||||
}
|
}
|
||||||
this.db.exec('COMMIT')
|
this.db.exec('COMMIT')
|
||||||
count++
|
count++
|
||||||
progressCallback({ progress: 100 * (count / chunksAmount), id: progressCounterId })
|
progressCallback({
|
||||||
|
progress: 100 * (count / chunksAmount),
|
||||||
|
id: progressCounterId
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ export default {
|
|||||||
*generateChunks(data, size) {
|
*generateChunks(data, size) {
|
||||||
const matrix = Object.keys(data).map(col => data[col])
|
const matrix = Object.keys(data).map(col => data[col])
|
||||||
const [row] = matrix
|
const [row] = matrix
|
||||||
const transposedMatrix = row.map((value, column) => matrix.map(row => row[column]))
|
const transposedMatrix = row.map((value, column) =>
|
||||||
|
matrix.map(row => row[column])
|
||||||
|
)
|
||||||
|
|
||||||
const count = Math.ceil(transposedMatrix.length / size)
|
const count = Math.ceil(transposedMatrix.length / size)
|
||||||
|
|
||||||
@@ -38,7 +40,8 @@ export default {
|
|||||||
type = 'TEXT'
|
type = 'TEXT'
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default: type = 'TEXT'
|
default:
|
||||||
|
type = 'TEXT'
|
||||||
}
|
}
|
||||||
result += `"${col}" ${type}, `
|
result += `"${col}" ${type}, `
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,5 @@ function onError (error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerPromiseWorker(data => {
|
registerPromiseWorker(data => {
|
||||||
return sqlReady
|
return sqlReady.then(processMsg.bind(data)).catch(onError)
|
||||||
.then(processMsg.bind(data))
|
|
||||||
.catch(onError)
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,10 +7,9 @@ import PromiseWorker from 'promise-worker'
|
|||||||
import events from '@/lib/utils/events'
|
import events from '@/lib/utils/events'
|
||||||
|
|
||||||
function getNewDatabase() {
|
function getNewDatabase() {
|
||||||
const worker = new Worker(
|
const worker = new Worker(new URL('./_worker.js', import.meta.url), {
|
||||||
new URL('./_worker.js', import.meta.url),
|
type: 'module'
|
||||||
{ type: 'module' }
|
})
|
||||||
)
|
|
||||||
return new Database(worker)
|
return new Database(worker)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,9 +30,11 @@ class Database {
|
|||||||
const progress = e.data.progress
|
const progress = e.data.progress
|
||||||
if (progress !== undefined) {
|
if (progress !== undefined) {
|
||||||
const id = e.data.id
|
const id = e.data.id
|
||||||
this.importProgresses[id].dispatchEvent(new CustomEvent('progress', {
|
this.importProgresses[id].dispatchEvent(
|
||||||
|
new CustomEvent('progress', {
|
||||||
detail: progress
|
detail: progress
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -45,7 +46,9 @@ class Database {
|
|||||||
createProgressCounter(callback) {
|
createProgressCounter(callback) {
|
||||||
const id = progressCounterIds++
|
const id = progressCounterIds++
|
||||||
this.importProgresses[id] = new EventTarget()
|
this.importProgresses[id] = new EventTarget()
|
||||||
this.importProgresses[id].addEventListener('progress', e => { callback(e.detail) })
|
this.importProgresses[id].addEventListener('progress', e => {
|
||||||
|
callback(e.detail)
|
||||||
|
})
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +73,10 @@ class Database {
|
|||||||
|
|
||||||
async loadDb(file) {
|
async loadDb(file) {
|
||||||
const fileContent = file ? await fu.readAsArrayBuffer(file) : null
|
const fileContent = file ? await fu.readAsArrayBuffer(file) : null
|
||||||
const res = await this.pw.postMessage({ action: 'open', buffer: fileContent })
|
const res = await this.pw.postMessage({
|
||||||
|
action: 'open',
|
||||||
|
buffer: fileContent
|
||||||
|
})
|
||||||
|
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
throw new Error(res.error)
|
throw new Error(res.error)
|
||||||
@@ -130,7 +136,9 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (/[^\w]/.test(name)) {
|
if (/[^\w]/.test(name)) {
|
||||||
throw new Error('Table name can contain only letters, digits and underscores')
|
throw new Error(
|
||||||
|
'Table name can contain only letters, digits and underscores'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/^(\d)/.test(name)) {
|
if (/^(\d)/.test(name)) {
|
||||||
|
|||||||
@@ -37,13 +37,20 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateStorage(inquiries) {
|
updateStorage(inquiries) {
|
||||||
localStorage.setItem('myInquiries', JSON.stringify({ version: this.version, inquiries }))
|
localStorage.setItem(
|
||||||
|
'myInquiries',
|
||||||
|
JSON.stringify({ version: this.version, inquiries })
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
serialiseInquiries(inquiryList) {
|
serialiseInquiries(inquiryList) {
|
||||||
const preparedData = JSON.parse(JSON.stringify(inquiryList))
|
const preparedData = JSON.parse(JSON.stringify(inquiryList))
|
||||||
preparedData.forEach(inquiry => delete inquiry.isPredefined)
|
preparedData.forEach(inquiry => delete inquiry.isPredefined)
|
||||||
return JSON.stringify({ version: this.version, inquiries: preparedData }, null, 4)
|
return JSON.stringify(
|
||||||
|
{ version: this.version, inquiries: preparedData },
|
||||||
|
null,
|
||||||
|
4
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
deserialiseInquiries(str) {
|
deserialiseInquiries(str) {
|
||||||
@@ -59,7 +66,9 @@ export default {
|
|||||||
|
|
||||||
// Generate new ids if they are the same as existing inquiries
|
// Generate new ids if they are the same as existing inquiries
|
||||||
inquiryList.forEach(inquiry => {
|
inquiryList.forEach(inquiry => {
|
||||||
const allInquiriesIds = this.getStoredInquiries().map(inquiry => inquiry.id)
|
const allInquiriesIds = this.getStoredInquiries().map(
|
||||||
|
inquiry => inquiry.id
|
||||||
|
)
|
||||||
if (allInquiriesIds.includes(inquiry.id)) {
|
if (allInquiriesIds.includes(inquiry.id)) {
|
||||||
inquiry.id = nanoid()
|
inquiry.id = nanoid()
|
||||||
}
|
}
|
||||||
@@ -69,8 +78,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
importInquiries() {
|
importInquiries() {
|
||||||
return fu.importFile()
|
return fu.importFile().then(str => {
|
||||||
.then(str => {
|
|
||||||
const inquires = this.deserialiseInquiries(str)
|
const inquires = this.deserialiseInquiries(str)
|
||||||
|
|
||||||
events.send('inquiry.import', inquires.length)
|
events.send('inquiry.import', inquires.length)
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ export default class Tab {
|
|||||||
constructor(state, inquiry = {}) {
|
constructor(state, inquiry = {}) {
|
||||||
this.id = inquiry.id || nanoid()
|
this.id = inquiry.id || nanoid()
|
||||||
this.name = inquiry.id ? inquiry.name : null
|
this.name = inquiry.id ? inquiry.name : null
|
||||||
this.tempName = inquiry.name || (state.untitledLastIndex
|
this.tempName =
|
||||||
|
inquiry.name ||
|
||||||
|
(state.untitledLastIndex
|
||||||
? `Untitled ${state.untitledLastIndex}`
|
? `Untitled ${state.untitledLastIndex}`
|
||||||
: 'Untitled')
|
: 'Untitled')
|
||||||
this.query = inquiry.query
|
this.query = inquiry.query
|
||||||
@@ -39,7 +41,8 @@ export default class Tab {
|
|||||||
this.time = time.getPeriod(start, new Date())
|
this.time = time.getPeriod(start, new Date())
|
||||||
|
|
||||||
if (this.result && this.result.values) {
|
if (this.result && this.result.values) {
|
||||||
events.send('resultset.create',
|
events.send(
|
||||||
|
'resultset.create',
|
||||||
this.result.values[this.result.columns[0]].length
|
this.result.values[this.result.columns[0]].length
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ export default {
|
|||||||
|
|
||||||
async _copyBlob(blob) {
|
async _copyBlob(blob) {
|
||||||
await navigator.clipboard.write([
|
await navigator.clipboard.write([
|
||||||
new ClipboardItem({ // eslint-disable-line no-undef
|
new ClipboardItem({
|
||||||
|
// eslint-disable-line no-undef
|
||||||
[blob.type]: blob
|
[blob.type]: blob
|
||||||
})
|
})
|
||||||
])
|
])
|
||||||
@@ -32,9 +33,13 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async _copyCanvas(canvas) {
|
async _copyCanvas(canvas) {
|
||||||
canvas.toBlob(async (blob) => {
|
canvas.toBlob(
|
||||||
|
async blob => {
|
||||||
await this._copyBlob(blob)
|
await this._copyBlob(blob)
|
||||||
Lib.notifier('Image copied to clipboard successfully', 'long')
|
Lib.notifier('Image copied to clipboard successfully', 'long')
|
||||||
}, 'image/png', 1)
|
},
|
||||||
|
'image/png',
|
||||||
|
1
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,8 +57,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
importFile() {
|
importFile() {
|
||||||
return this.getFileFromUser('.json')
|
return this.getFileFromUser('.json').then(file => {
|
||||||
.then(file => {
|
|
||||||
return this.getFileContent(file)
|
return this.getFileContent(file)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ export default {
|
|||||||
|
|
||||||
sleep(ms) {
|
sleep(ms) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
setTimeout(() => { resolve() }, ms)
|
setTimeout(() => {
|
||||||
|
resolve()
|
||||||
|
}, ms)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ function invokeServiceWorkerUpdateFlow (registration) {
|
|||||||
|
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
window.addEventListener('load', async () => {
|
window.addEventListener('load', async () => {
|
||||||
const registration = await navigator.serviceWorker.register('service-worker.js')
|
const registration =
|
||||||
|
await navigator.serviceWorker.register('service-worker.js')
|
||||||
// ensure the case when the updatefound event was missed is also handled
|
// ensure the case when the updatefound event was missed is also handled
|
||||||
// by re-invoking the prompt when there's a waiting Service Worker
|
// by re-invoking the prompt when there's a waiting Service Worker
|
||||||
if (registration.waiting) {
|
if (registration.waiting) {
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ export default {
|
|||||||
if (newName) {
|
if (newName) {
|
||||||
value.createdAt = new Date()
|
value.createdAt = new Date()
|
||||||
} else {
|
} else {
|
||||||
var inquiryIndex = myInquiries.findIndex(oldInquiry => oldInquiry.id === inquiryTab.id)
|
var inquiryIndex = myInquiries.findIndex(
|
||||||
|
oldInquiry => oldInquiry.id === inquiryTab.id
|
||||||
|
)
|
||||||
value.createdAt = myInquiries[inquiryIndex].createdAt
|
value.createdAt = myInquiries[inquiryIndex].createdAt
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,8 +65,9 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
renameInquiry({ state, commit }, { inquiryId, newName }) {
|
renameInquiry({ state, commit }, { inquiryId, newName }) {
|
||||||
const renamingInquiry = state.inquiries
|
const renamingInquiry = state.inquiries.find(
|
||||||
.find(inquiry => inquiry.id === inquiryId)
|
inquiry => inquiry.id === inquiryId
|
||||||
|
)
|
||||||
|
|
||||||
renamingInquiry.name = newName
|
renamingInquiry.name = newName
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,24 @@ export default {
|
|||||||
state.currentTabId = id
|
state.currentTabId = id
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id) { tab.id = id }
|
if (id) {
|
||||||
if (name) { tab.name = name }
|
tab.id = id
|
||||||
if (query) { tab.query = query }
|
}
|
||||||
if (viewType) { tab.viewType = viewType }
|
if (name) {
|
||||||
if (viewOptions) { tab.viewOptions = viewOptions }
|
tab.name = name
|
||||||
if (isSaved !== undefined) { tab.isSaved = isSaved }
|
}
|
||||||
|
if (query) {
|
||||||
|
tab.query = query
|
||||||
|
}
|
||||||
|
if (viewType) {
|
||||||
|
tab.viewType = viewType
|
||||||
|
}
|
||||||
|
if (viewOptions) {
|
||||||
|
tab.viewOptions = viewOptions
|
||||||
|
}
|
||||||
|
if (isSaved !== undefined) {
|
||||||
|
tab.isSaved = isSaved
|
||||||
|
}
|
||||||
if (isSaved) {
|
if (isSaved) {
|
||||||
// Saved inquiry is not predefined
|
// Saved inquiry is not predefined
|
||||||
delete tab.isPredefined
|
delete tab.isPredefined
|
||||||
@@ -49,11 +61,13 @@ export default {
|
|||||||
state.currentTabId = id
|
state.currentTabId = id
|
||||||
state.currentTab = state.tabs.find(tab => tab.id === id)
|
state.currentTab = state.tabs.find(tab => tab.id === id)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Can\'t open a tab id:' + id)
|
console.error("Can't open a tab id:" + id)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updatePredefinedInquiries(state, inquiries) {
|
updatePredefinedInquiries(state, inquiries) {
|
||||||
state.predefinedInquiries = Array.isArray(inquiries) ? inquiries : [inquiries]
|
state.predefinedInquiries = Array.isArray(inquiries)
|
||||||
|
? inquiries
|
||||||
|
: [inquiries]
|
||||||
},
|
},
|
||||||
setLoadingPredefinedInquiries(state, value) {
|
setLoadingPredefinedInquiries(state, value) {
|
||||||
state.loadingPredefinedInquiries = value
|
state.loadingPredefinedInquiries = value
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showTooltip(e, tooltipPosition) {
|
showTooltip(e, tooltipPosition) {
|
||||||
const position = tooltipPosition ? tooltipPosition.split('-') : ['top', 'right']
|
const position = tooltipPosition
|
||||||
|
? tooltipPosition.split('-')
|
||||||
|
: ['top', 'right']
|
||||||
const offset = 12
|
const offset = 12
|
||||||
|
|
||||||
if (position[0] === 'top') {
|
if (position[0] === 'top') {
|
||||||
@@ -25,7 +27,8 @@ export default {
|
|||||||
if (position[1] === 'right') {
|
if (position[1] === 'right') {
|
||||||
this.tooltipStyle.left = e.clientX + offset + 'px'
|
this.tooltipStyle.left = e.clientX + offset + 'px'
|
||||||
} else {
|
} else {
|
||||||
this.tooltipStyle.left = e.clientX - offset - this.tooltipElement.offsetWidth + 'px'
|
this.tooltipStyle.left =
|
||||||
|
e.clientX - offset - this.tooltipElement.offsetWidth + 'px'
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tooltipStyle.visibility = 'visible'
|
this.tooltipStyle.visibility = 'visible'
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<logs
|
<logs id="logs" :messages="messages" />
|
||||||
id="logs"
|
|
||||||
:messages="messages"
|
|
||||||
/>
|
|
||||||
<button
|
<button
|
||||||
v-if="hasErrors"
|
v-if="hasErrors"
|
||||||
id="open-workspace-btn"
|
id="open-workspace-btn"
|
||||||
class="secondary"
|
class="secondary"
|
||||||
@click="$router.push('/workspace?hide_schema=1')">
|
@click="$router.push('/workspace?hide_schema=1')"
|
||||||
|
>
|
||||||
Open workspace
|
Open workspace
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -190,7 +188,6 @@ export default {
|
|||||||
#logs {
|
#logs {
|
||||||
margin: 8px auto;
|
margin: 8px auto;
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#open-workspace-btn {
|
#open-workspace-btn {
|
||||||
|
|||||||
@@ -5,11 +5,7 @@
|
|||||||
src="~@/assets/images/info.svg"
|
src="~@/assets/images/info.svg"
|
||||||
@click="$modal.show('app-info')"
|
@click="$modal.show('app-info')"
|
||||||
/>
|
/>
|
||||||
<modal
|
<modal modal-id="app-info" class="dialog" content-class="app-info-modal">
|
||||||
modal-id="app-info"
|
|
||||||
class="dialog"
|
|
||||||
content-class="app-info-modal"
|
|
||||||
>
|
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
App info
|
App info
|
||||||
<close-icon @click="$modal.hide('app-info')" />
|
<close-icon @click="$modal.hide('app-info')" />
|
||||||
|
|||||||
@@ -13,10 +13,18 @@
|
|||||||
<loading-indicator />
|
<loading-indicator />
|
||||||
Loading predefined inquiries...
|
Loading predefined inquiries...
|
||||||
</div>
|
</div>
|
||||||
<div id="my-inquiries-content" ref="my-inquiries-content" v-show="allInquiries.length > 0">
|
<div
|
||||||
|
id="my-inquiries-content"
|
||||||
|
ref="my-inquiries-content"
|
||||||
|
v-show="allInquiries.length > 0"
|
||||||
|
>
|
||||||
<div id="my-inquiries-toolbar">
|
<div id="my-inquiries-toolbar">
|
||||||
<div id="toolbar-buttons">
|
<div id="toolbar-buttons">
|
||||||
<button id="toolbar-btns-import" class="toolbar" @click="importInquiries">
|
<button
|
||||||
|
id="toolbar-btns-import"
|
||||||
|
class="toolbar"
|
||||||
|
@click="importInquiries"
|
||||||
|
>
|
||||||
Import
|
Import
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@@ -37,7 +45,11 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="toolbar-search">
|
<div id="toolbar-search">
|
||||||
<text-field placeholder="Search inquiry by name" width="300px" v-model="filter"/>
|
<text-field
|
||||||
|
placeholder="Search inquiry by name"
|
||||||
|
width="300px"
|
||||||
|
v-model="filter"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -49,15 +61,20 @@
|
|||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<div>
|
<div>
|
||||||
<div class="fixed-header" ref="name-th">
|
<div class="fixed-header" ref="name-th">
|
||||||
<check-box ref="mainCheckBox" theme="light" @click="toggleSelectAll"/>
|
<check-box
|
||||||
|
ref="mainCheckBox"
|
||||||
|
theme="light"
|
||||||
|
@click="toggleSelectAll"
|
||||||
|
/>
|
||||||
<div class="name-th">Name</div>
|
<div class="name-th">Name</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="fixed-header">
|
<div class="fixed-header">Created at</div>
|
||||||
Created at
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div
|
||||||
<div class="table-container" :style="{ 'max-height': `${maxTableHeight}px` }">
|
class="table-container"
|
||||||
|
:style="{ 'max-height': `${maxTableHeight}px` }"
|
||||||
|
>
|
||||||
<table ref="table" class="sqliteviz-table">
|
<table ref="table" class="sqliteviz-table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr
|
<tr
|
||||||
@@ -81,9 +98,13 @@
|
|||||||
@mouseleave="hideTooltip"
|
@mouseleave="hideTooltip"
|
||||||
>
|
>
|
||||||
Predefined
|
Predefined
|
||||||
<span class="icon-tooltip" :style="tooltipStyle" ref="tooltip">
|
<span
|
||||||
Predefined inquiries come from the server.
|
class="icon-tooltip"
|
||||||
These inquiries can’t be deleted or renamed.
|
:style="tooltipStyle"
|
||||||
|
ref="tooltip"
|
||||||
|
>
|
||||||
|
Predefined inquiries come from the server. These
|
||||||
|
inquiries can’t be deleted or renamed.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -106,7 +127,7 @@
|
|||||||
/>
|
/>
|
||||||
<delete-icon
|
<delete-icon
|
||||||
v-if="!inquiry.isPredefined"
|
v-if="!inquiry.isPredefined"
|
||||||
@click="showDeleteDialog((new Set()).add(inquiry.id))"
|
@click="showDeleteDialog(new Set().add(inquiry.id))"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -146,8 +167,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
{{ deleteDialogMsg }}
|
{{ deleteDialogMsg }}
|
||||||
<div v-show="selectedInquiriesCount > selectedNotPredefinedCount" id="note">
|
<div
|
||||||
<img src="~@/assets/images/info.svg">
|
v-show="selectedInquiriesCount > selectedNotPredefinedCount"
|
||||||
|
id="note"
|
||||||
|
>
|
||||||
|
<img src="~@/assets/images/info.svg" />
|
||||||
Note: Predefined inquiries you've selected won't be deleted
|
Note: Predefined inquiries you've selected won't be deleted
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -217,7 +241,8 @@ export default {
|
|||||||
let showedInquiries = this.allInquiries
|
let showedInquiries = this.allInquiries
|
||||||
if (this.filter) {
|
if (this.filter) {
|
||||||
showedInquiries = showedInquiries.filter(
|
showedInquiries = showedInquiries.filter(
|
||||||
inquiry => inquiry.name.toUpperCase().indexOf(this.filter.toUpperCase()) >= 0
|
inquiry =>
|
||||||
|
inquiry.name.toUpperCase().indexOf(this.filter.toUpperCase()) >= 0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return showedInquiries
|
return showedInquiries
|
||||||
@@ -227,21 +252,24 @@ export default {
|
|||||||
return this.predefinedInquiries.concat(this.inquiries)
|
return this.predefinedInquiries.concat(this.inquiries)
|
||||||
},
|
},
|
||||||
processedInquiryIndex() {
|
processedInquiryIndex() {
|
||||||
return this.inquiries.findIndex(inquiry => inquiry.id === this.processedInquiryId)
|
return this.inquiries.findIndex(
|
||||||
|
inquiry => inquiry.id === this.processedInquiryId
|
||||||
|
)
|
||||||
},
|
},
|
||||||
deleteDialogMsg() {
|
deleteDialogMsg() {
|
||||||
if (!this.deleteGroup && (
|
if (
|
||||||
this.processedInquiryIndex === null ||
|
!this.deleteGroup &&
|
||||||
|
(this.processedInquiryIndex === null ||
|
||||||
this.processedInquiryIndex < 0 ||
|
this.processedInquiryIndex < 0 ||
|
||||||
this.processedInquiryIndex > this.inquiries.length
|
this.processedInquiryIndex > this.inquiries.length)
|
||||||
)) {
|
) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteItem = this.deleteGroup
|
const deleteItem = this.deleteGroup
|
||||||
? `${this.selectedNotPredefinedCount} ${this.selectedNotPredefinedCount > 1
|
? `${this.selectedNotPredefinedCount} ${
|
||||||
? 'inquiries'
|
this.selectedNotPredefinedCount > 1 ? 'inquiries' : 'inquiry'
|
||||||
: 'inquiry'}`
|
}`
|
||||||
: `"${this.inquiries[this.processedInquiryIndex].name}"`
|
: `"${this.inquiries[this.processedInquiryIndex].name}"`
|
||||||
|
|
||||||
return `Are you sure you want to delete ${deleteItem}?`
|
return `Are you sure you want to delete ${deleteItem}?`
|
||||||
@@ -250,13 +278,15 @@ export default {
|
|||||||
watch: {
|
watch: {
|
||||||
showedInquiries: {
|
showedInquiries: {
|
||||||
handler() {
|
handler() {
|
||||||
this.selectedInquiriesIds = new Set(this.showedInquiries
|
this.selectedInquiriesIds = new Set(
|
||||||
|
this.showedInquiries
|
||||||
.filter(inquiry => this.selectedInquiriesIds.has(inquiry.id))
|
.filter(inquiry => this.selectedInquiriesIds.has(inquiry.id))
|
||||||
.map(inquiry => inquiry.id)
|
.map(inquiry => inquiry.id)
|
||||||
)
|
)
|
||||||
this.selectedInquiriesCount = this.selectedInquiriesIds.size
|
this.selectedInquiriesCount = this.selectedInquiriesIds.size
|
||||||
this.selectedNotPredefinedCount = ([...this.selectedInquiriesIds]
|
this.selectedNotPredefinedCount = [...this.selectedInquiriesIds].filter(
|
||||||
.filter(id => !this.predefinedInquiriesIds.has(id))).length
|
id => !this.predefinedInquiriesIds.has(id)
|
||||||
|
).length
|
||||||
|
|
||||||
if (this.selectedInquiriesIds.size < this.showedInquiries.length) {
|
if (this.selectedInquiriesIds.size < this.showedInquiries.length) {
|
||||||
if (this.$refs.mainCheckBox) {
|
if (this.$refs.mainCheckBox) {
|
||||||
@@ -269,8 +299,10 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
const loadingPredefinedInquiries = this.$store.state.loadingPredefinedInquiries
|
const loadingPredefinedInquiries =
|
||||||
const predefinedInquiriesLoaded = this.$store.state.predefinedInquiriesLoaded
|
this.$store.state.loadingPredefinedInquiries
|
||||||
|
const predefinedInquiriesLoaded =
|
||||||
|
this.$store.state.predefinedInquiriesLoaded
|
||||||
if (!predefinedInquiriesLoaded && !loadingPredefinedInquiries) {
|
if (!predefinedInquiriesLoaded && !loadingPredefinedInquiries) {
|
||||||
try {
|
try {
|
||||||
this.$store.commit('setLoadingPredefinedInquiries', true)
|
this.$store.commit('setLoadingPredefinedInquiries', true)
|
||||||
@@ -310,11 +342,15 @@ export default {
|
|||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
minute: '2-digit'
|
minute: '2-digit'
|
||||||
}
|
}
|
||||||
return new Date(value).toLocaleDateString('en-GB', dateOptions) + ' ' +
|
return (
|
||||||
|
new Date(value).toLocaleDateString('en-GB', dateOptions) +
|
||||||
|
' ' +
|
||||||
new Date(value).toLocaleTimeString('en-GB', timeOptions)
|
new Date(value).toLocaleTimeString('en-GB', timeOptions)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
calcNameWidth() {
|
calcNameWidth() {
|
||||||
const nameWidth = this.$refs['name-td'] && this.$refs['name-td'][0]
|
const nameWidth =
|
||||||
|
this.$refs['name-td'] && this.$refs['name-td'][0]
|
||||||
? this.$refs['name-td'][0].getBoundingClientRect().width
|
? this.$refs['name-td'][0].getBoundingClientRect().width
|
||||||
: 0
|
: 0
|
||||||
this.$refs['name-th'].style = `width: ${nameWidth}px`
|
this.$refs['name-th'].style = `width: ${nameWidth}px`
|
||||||
@@ -352,7 +388,9 @@ export default {
|
|||||||
this.$modal.hide('rename')
|
this.$modal.hide('rename')
|
||||||
},
|
},
|
||||||
duplicateInquiry(index) {
|
duplicateInquiry(index) {
|
||||||
const newInquiry = storedInquiries.duplicateInquiry(this.showedInquiries[index])
|
const newInquiry = storedInquiries.duplicateInquiry(
|
||||||
|
this.showedInquiries[index]
|
||||||
|
)
|
||||||
this.$store.dispatch('addInquiry', newInquiry)
|
this.$store.dispatch('addInquiry', newInquiry)
|
||||||
},
|
},
|
||||||
showDeleteDialog(idsSet) {
|
showDeleteDialog(idsSet) {
|
||||||
@@ -365,7 +403,10 @@ export default {
|
|||||||
deleteInquiry() {
|
deleteInquiry() {
|
||||||
this.$modal.hide('delete')
|
this.$modal.hide('delete')
|
||||||
if (!this.deleteGroup) {
|
if (!this.deleteGroup) {
|
||||||
this.$store.dispatch('deleteInquiries', new Set().add(this.processedInquiryId))
|
this.$store.dispatch(
|
||||||
|
'deleteInquiries',
|
||||||
|
new Set().add(this.processedInquiryId)
|
||||||
|
)
|
||||||
|
|
||||||
// Clear checkbox
|
// Clear checkbox
|
||||||
if (this.selectedInquiriesIds.has(this.processedInquiryId)) {
|
if (this.selectedInquiriesIds.has(this.processedInquiryId)) {
|
||||||
@@ -383,23 +424,27 @@ export default {
|
|||||||
storedInquiries.export(inquiryList, fileName)
|
storedInquiries.export(inquiryList, fileName)
|
||||||
},
|
},
|
||||||
exportSelectedInquiries() {
|
exportSelectedInquiries() {
|
||||||
const inquiryList = this.allInquiries.filter(
|
const inquiryList = this.allInquiries.filter(inquiry =>
|
||||||
inquiry => this.selectedInquiriesIds.has(inquiry.id)
|
this.selectedInquiriesIds.has(inquiry.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
this.exportToFile(inquiryList, 'My sqliteviz inquiries.json')
|
this.exportToFile(inquiryList, 'My sqliteviz inquiries.json')
|
||||||
},
|
},
|
||||||
|
|
||||||
importInquiries() {
|
importInquiries() {
|
||||||
storedInquiries.importInquiries()
|
storedInquiries.importInquiries().then(importedInquiries => {
|
||||||
.then(importedInquiries => {
|
this.$store.commit(
|
||||||
this.$store.commit('setInquiries', this.inquiries.concat(importedInquiries))
|
'setInquiries',
|
||||||
|
this.inquiries.concat(importedInquiries)
|
||||||
|
)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleSelectAll(checked) {
|
toggleSelectAll(checked) {
|
||||||
this.selectAll = checked
|
this.selectAll = checked
|
||||||
this.$refs.rowCheckBox.forEach(item => { item.checked = checked })
|
this.$refs.rowCheckBox.forEach(item => {
|
||||||
|
item.checked = checked
|
||||||
|
})
|
||||||
|
|
||||||
this.selectedInquiriesIds = checked
|
this.selectedInquiriesIds = checked
|
||||||
? new Set(this.showedInquiries.map(inquiry => inquiry.id))
|
? new Set(this.showedInquiries.map(inquiry => inquiry.id))
|
||||||
@@ -407,8 +452,9 @@ export default {
|
|||||||
|
|
||||||
this.selectedInquiriesCount = this.selectedInquiriesIds.size
|
this.selectedInquiriesCount = this.selectedInquiriesIds.size
|
||||||
this.selectedNotPredefinedCount = checked
|
this.selectedNotPredefinedCount = checked
|
||||||
? ([...this.selectedInquiriesIds].filter(id => !this.predefinedInquiriesIds.has(id)))
|
? [...this.selectedInquiriesIds].filter(
|
||||||
.length
|
id => !this.predefinedInquiriesIds.has(id)
|
||||||
|
).length
|
||||||
: 0
|
: 0
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<nav>
|
<nav>
|
||||||
<div id="nav-links">
|
<div id="nav-links">
|
||||||
<a href="https://sqliteviz.com">
|
<a href="https://sqliteviz.com">
|
||||||
<img src="~@/assets/images/logo_simple.svg">
|
<img src="~@/assets/images/logo_simple.svg" />
|
||||||
</a>
|
</a>
|
||||||
<router-link to="/workspace">Workspace</router-link>
|
<router-link to="/workspace">Workspace</router-link>
|
||||||
<router-link to="/inquiries">Inquiries</router-link>
|
<router-link to="/inquiries">Inquiries</router-link>
|
||||||
@@ -18,11 +18,7 @@
|
|||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button id="create-btn" class="primary" @click="createNewInquiry">
|
||||||
id="create-btn"
|
|
||||||
class="primary"
|
|
||||||
@click="createNewInquiry"
|
|
||||||
>
|
|
||||||
Create
|
Create
|
||||||
</button>
|
</button>
|
||||||
<app-diagnostic-info />
|
<app-diagnostic-info />
|
||||||
@@ -36,9 +32,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
<div v-show="isPredefined" id="save-note">
|
<div v-show="isPredefined" id="save-note">
|
||||||
<img src="~@/assets/images/info.svg">
|
<img src="~@/assets/images/info.svg" />
|
||||||
Note: Predefined inquiries can't be edited.
|
Note: Predefined inquiries can't be edited. That's why your
|
||||||
That's why your modifications will be saved as a new inquiry. Enter the name for it.
|
modifications will be saved as a new inquiry. Enter the name for it.
|
||||||
</div>
|
</div>
|
||||||
<text-field
|
<text-field
|
||||||
label="Inquiry name"
|
label="Inquiry name"
|
||||||
@@ -87,7 +83,10 @@ export default {
|
|||||||
return this.currentInquiry && this.currentInquiry.isPredefined
|
return this.currentInquiry && this.currentInquiry.isPredefined
|
||||||
},
|
},
|
||||||
runDisabled() {
|
runDisabled() {
|
||||||
return this.currentInquiry && (!this.$store.state.db || !this.currentInquiry.query)
|
return (
|
||||||
|
this.currentInquiry &&
|
||||||
|
(!this.$store.state.db || !this.currentInquiry.query)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -126,7 +125,7 @@ export default {
|
|||||||
async saveInquiry() {
|
async saveInquiry() {
|
||||||
const isNeedName = storedInquiries.isTabNeedName(this.currentInquiry)
|
const isNeedName = storedInquiries.isTabNeedName(this.currentInquiry)
|
||||||
if (isNeedName && !this.name) {
|
if (isNeedName && !this.name) {
|
||||||
this.errorMsg = 'Inquiry name can\'t be empty'
|
this.errorMsg = "Inquiry name can't be empty"
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const dataSet = this.currentInquiry.result
|
const dataSet = this.currentInquiry.result
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.table-name, .column {
|
.table-name,
|
||||||
|
.column {
|
||||||
margin-top: 11px;
|
margin-top: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ export default {
|
|||||||
return !this.filter
|
return !this.filter
|
||||||
? this.$store.state.db.schema
|
? this.$store.state.db.schema
|
||||||
: this.$store.state.db.schema.filter(
|
: this.$store.state.db.schema.filter(
|
||||||
table => table.name.toUpperCase().indexOf(this.filter.toUpperCase()) !== -1
|
table =>
|
||||||
|
table.name.toUpperCase().indexOf(this.filter.toUpperCase()) !== -1
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
dbName() {
|
dbName() {
|
||||||
@@ -119,7 +120,8 @@ export default {
|
|||||||
background-image: linear-gradient(white 73%, rgba(255, 255, 255, 0));
|
background-image: linear-gradient(white 73%, rgba(255, 255, 255, 0));
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
.schema, .db-name {
|
.schema,
|
||||||
|
.db-name {
|
||||||
color: var(--color-text-base);
|
color: var(--color-text-base);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-show="visible" class="chart-container" ref="chartContainer">
|
<div v-show="visible" class="chart-container" ref="chartContainer">
|
||||||
<div class="warning chart-warning" v-show="!dataSources && visible">
|
<div class="warning chart-warning" v-show="!dataSources && visible">
|
||||||
There is no data to build a chart. Run your SQL query and make sure the result is not empty.
|
There is no data to build a chart. Run your SQL query and make sure the
|
||||||
|
result is not empty.
|
||||||
</div>
|
</div>
|
||||||
<div class="chart" :style="{ height: !dataSources ? 'calc(100% - 40px)' : '100%' }">
|
<div
|
||||||
|
class="chart"
|
||||||
|
:style="{ height: !dataSources ? 'calc(100% - 40px)' : '100%' }"
|
||||||
|
>
|
||||||
<PlotlyEditor
|
<PlotlyEditor
|
||||||
v-show="visible"
|
v-show="visible"
|
||||||
:data="state.data"
|
:data="state.data"
|
||||||
:layout="state.layout"
|
:layout="state.layout"
|
||||||
:frames="state.frames"
|
:frames="state.frames"
|
||||||
:config="{ editable: true, displaylogo: false, modeBarButtonsToRemove: ['toImage'] }"
|
:config="{
|
||||||
|
editable: true,
|
||||||
|
displaylogo: false,
|
||||||
|
modeBarButtonsToRemove: ['toImage']
|
||||||
|
}"
|
||||||
:dataSources="dataSources"
|
:dataSources="dataSources"
|
||||||
:dataSourceOptions="dataSourceOptions"
|
:dataSourceOptions="dataSourceOptions"
|
||||||
:plotly="plotly"
|
:plotly="plotly"
|
||||||
@@ -38,8 +46,10 @@ import events from '@/lib/utils/events'
|
|||||||
export default {
|
export default {
|
||||||
name: 'Chart',
|
name: 'Chart',
|
||||||
props: [
|
props: [
|
||||||
'dataSources', 'initOptions',
|
'dataSources',
|
||||||
'importToPngEnabled', 'importToSvgEnabled',
|
'initOptions',
|
||||||
|
'importToPngEnabled',
|
||||||
|
'importToSvgEnabled',
|
||||||
'forPivot'
|
'forPivot'
|
||||||
],
|
],
|
||||||
emits: ['update:importToSvgEnabled', 'update', 'loadingImageCompleted'],
|
emits: ['update:importToSvgEnabled', 'update', 'loadingImageCompleted'],
|
||||||
@@ -70,10 +80,13 @@ export default {
|
|||||||
notifyOnLogging: 1
|
notifyOnLogging: 1
|
||||||
})
|
})
|
||||||
this.$watch(
|
this.$watch(
|
||||||
() => this.state && this.state.data && this.state.data
|
() =>
|
||||||
|
this.state &&
|
||||||
|
this.state.data &&
|
||||||
|
this.state.data
|
||||||
.map(trace => `${trace.type}${trace.mode ? '-' + trace.mode : ''}`)
|
.map(trace => `${trace.type}${trace.mode ? '-' + trace.mode : ''}`)
|
||||||
.join(','),
|
.join(','),
|
||||||
(value) => {
|
value => {
|
||||||
events.send('viz_plotly.render', null, {
|
events.send('viz_plotly.render', null, {
|
||||||
type: value,
|
type: value,
|
||||||
pivot: !!this.forPivot
|
pivot: !!this.forPivot
|
||||||
@@ -140,7 +153,10 @@ export default {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
async prepareCopy(type = 'png') {
|
async prepareCopy(type = 'png') {
|
||||||
return await chartHelper.getImageDataUrl(this.$refs.plotlyEditor.$el, type)
|
return await chartHelper.getImageDataUrl(
|
||||||
|
this.$refs.plotlyEditor.$el,
|
||||||
|
type
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,12 @@
|
|||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
import Multiselect from 'vue-multiselect'
|
import Multiselect from 'vue-multiselect'
|
||||||
import PivotSortBtn from './PivotSortBtn'
|
import PivotSortBtn from './PivotSortBtn'
|
||||||
import { renderers, aggregators, zeroValAggregators, twoValAggregators } from '../pivotHelper'
|
import {
|
||||||
|
renderers,
|
||||||
|
aggregators,
|
||||||
|
zeroValAggregators,
|
||||||
|
twoValAggregators
|
||||||
|
} from '../pivotHelper'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'pivotUi',
|
name: 'pivotUi',
|
||||||
@@ -150,16 +155,28 @@ export default {
|
|||||||
PivotSortBtn
|
PivotSortBtn
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const aggregatorName = (this.modelValue && this.modelValue.aggregatorName) || 'Count'
|
const aggregatorName =
|
||||||
const rendererName = (this.modelValue && this.modelValue.rendererName) || 'Table'
|
(this.modelValue && this.modelValue.aggregatorName) || 'Count'
|
||||||
|
const rendererName =
|
||||||
|
(this.modelValue && this.modelValue.rendererName) || 'Table'
|
||||||
return {
|
return {
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
renderer: { name: rendererName, fun: $.pivotUtilities.renderers[rendererName] },
|
renderer: {
|
||||||
aggregator: { name: aggregatorName, fun: $.pivotUtilities.aggregators[aggregatorName] },
|
name: rendererName,
|
||||||
|
fun: $.pivotUtilities.renderers[rendererName]
|
||||||
|
},
|
||||||
|
aggregator: {
|
||||||
|
name: aggregatorName,
|
||||||
|
fun: $.pivotUtilities.aggregators[aggregatorName]
|
||||||
|
},
|
||||||
rows: (this.modelValue && this.modelValue.rows) || [],
|
rows: (this.modelValue && this.modelValue.rows) || [],
|
||||||
cols: (this.modelValue && this.modelValue.cols) || [],
|
cols: (this.modelValue && this.modelValue.cols) || [],
|
||||||
val1: (this.modelValue && this.modelValue.vals && this.modelValue.vals[0]) || '',
|
val1:
|
||||||
val2: (this.modelValue && this.modelValue.vals && this.modelValue.vals[1]) || '',
|
(this.modelValue && this.modelValue.vals && this.modelValue.vals[0]) ||
|
||||||
|
'',
|
||||||
|
val2:
|
||||||
|
(this.modelValue && this.modelValue.vals && this.modelValue.vals[1]) ||
|
||||||
|
'',
|
||||||
colOrder: (this.modelValue && this.modelValue.colOrder) || 'key_a_to_z',
|
colOrder: (this.modelValue && this.modelValue.colOrder) || 'key_a_to_z',
|
||||||
rowOrder: (this.modelValue && this.modelValue.rowOrder) || 'key_a_to_z'
|
rowOrder: (this.modelValue && this.modelValue.rowOrder) || 'key_a_to_z'
|
||||||
}
|
}
|
||||||
@@ -281,7 +298,6 @@ export default {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.switcher:hover {
|
.switcher:hover {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="pivot-container">
|
<div class="pivot-container">
|
||||||
<div class="warning pivot-warning" v-show="!dataSources">
|
<div class="warning pivot-warning" v-show="!dataSources">
|
||||||
There is no data to build a pivot. Run your SQL query and make sure the result is not empty.
|
There is no data to build a pivot. Run your SQL query and make sure the
|
||||||
|
result is not empty.
|
||||||
</div>
|
</div>
|
||||||
<pivot-ui
|
<pivot-ui
|
||||||
:key-names="columns"
|
:key-names="columns"
|
||||||
@@ -37,7 +38,12 @@ import events from '@/lib/utils/events'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'pivot',
|
name: 'pivot',
|
||||||
props: ['dataSources', 'initOptions', 'importToPngEnabled', 'importToSvgEnabled'],
|
props: [
|
||||||
|
'dataSources',
|
||||||
|
'initOptions',
|
||||||
|
'importToPngEnabled',
|
||||||
|
'importToSvgEnabled'
|
||||||
|
],
|
||||||
emits: [
|
emits: [
|
||||||
'loadingImageCompleted',
|
'loadingImageCompleted',
|
||||||
'update',
|
'update',
|
||||||
@@ -163,7 +169,9 @@ export default {
|
|||||||
|
|
||||||
$(this.$refs.pivotOutput).pivot(
|
$(this.$refs.pivotOutput).pivot(
|
||||||
function (callback) {
|
function (callback) {
|
||||||
const rowCount = !this.dataSources ? 0 : this.dataSources[this.columns[0]].length
|
const rowCount = !this.dataSources
|
||||||
|
? 0
|
||||||
|
: this.dataSources[this.columns[0]].length
|
||||||
for (let i = 1; i <= rowCount; i++) {
|
for (let i = 1; i <= rowCount; i++) {
|
||||||
const row = {}
|
const row = {}
|
||||||
this.columns.forEach(col => {
|
this.columns.forEach(col => {
|
||||||
@@ -198,7 +206,9 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
const source = this.viewStandartChart
|
const source = this.viewStandartChart
|
||||||
? await chartHelper.getImageDataUrl(this.$refs.pivotOutput, 'png')
|
? await chartHelper.getImageDataUrl(this.$refs.pivotOutput, 'png')
|
||||||
: (await pivotHelper.getPivotCanvas(this.$refs.pivotOutput)).toDataURL('image/png')
|
: (
|
||||||
|
await pivotHelper.getPivotCanvas(this.$refs.pivotOutput)
|
||||||
|
).toDataURL('image/png')
|
||||||
|
|
||||||
this.$emit('loadingImageCompleted')
|
this.$emit('loadingImageCompleted')
|
||||||
fIo.downloadFromUrl(source, 'pivot')
|
fIo.downloadFromUrl(source, 'pivot')
|
||||||
@@ -219,7 +229,10 @@ export default {
|
|||||||
if (this.viewCustomChart) {
|
if (this.viewCustomChart) {
|
||||||
this.$refs.customChart.saveAsSvg()
|
this.$refs.customChart.saveAsSvg()
|
||||||
} else if (this.viewStandartChart) {
|
} else if (this.viewStandartChart) {
|
||||||
const url = await chartHelper.getImageDataUrl(this.$refs.pivotOutput, 'svg')
|
const url = await chartHelper.getImageDataUrl(
|
||||||
|
this.$refs.pivotOutput,
|
||||||
|
'svg'
|
||||||
|
)
|
||||||
fIo.downloadFromUrl(url, 'pivot')
|
fIo.downloadFromUrl(url, 'pivot')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -69,12 +69,14 @@ export const renderers = Object.keys($.pivotUtilities.renderers).map(key => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const aggregators = Object.keys($.pivotUtilities.aggregators).map(key => {
|
export const aggregators = Object.keys($.pivotUtilities.aggregators).map(
|
||||||
|
key => {
|
||||||
return {
|
return {
|
||||||
name: key,
|
name: key,
|
||||||
fun: $.pivotUtilities.aggregators[key]
|
fun: $.pivotUtilities.aggregators[key]
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
export async function getPivotCanvas(pivotOutput) {
|
export async function getPivotCanvas(pivotOutput) {
|
||||||
const tableElement = pivotOutput.querySelector('.pvtTable')
|
const tableElement = pivotOutput.querySelector('.pvtTable')
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ export default {
|
|||||||
await time.sleep(0)
|
await time.sleep(0)
|
||||||
this.dataToCopy = await this.$refs.viewComponent.prepareCopy()
|
this.dataToCopy = await this.$refs.viewComponent.prepareCopy()
|
||||||
const t1 = performance.now()
|
const t1 = performance.now()
|
||||||
if ((t1 - t0) < 950) {
|
if (t1 - t0 < 950) {
|
||||||
this.$modal.hide('prepareCopy')
|
this.$modal.hide('prepareCopy')
|
||||||
this.copyToClipboard()
|
this.copyToClipboard()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -11,9 +11,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th />
|
<th />
|
||||||
<th>
|
<th>
|
||||||
<div class="cell-data">
|
<div class="cell-data">Row #{{ currentRowIndex + 1 }}</div>
|
||||||
Row #{{ currentRowIndex + 1 }}
|
|
||||||
</div>
|
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -78,8 +76,9 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
const col = this.selectedColumnIndex
|
const col = this.selectedColumnIndex
|
||||||
const row = this.currentRowIndex
|
const row = this.currentRowIndex
|
||||||
const cell = this.$refs.table
|
const cell = this.$refs.table.querySelector(
|
||||||
.querySelector(`td[data-col="${col}"][data-row="${row}"]`)
|
`td[data-col="${col}"][data-row="${row}"]`
|
||||||
|
)
|
||||||
if (cell) {
|
if (cell) {
|
||||||
this.selectCell(cell)
|
this.selectCell(cell)
|
||||||
}
|
}
|
||||||
@@ -152,19 +151,21 @@ export default {
|
|||||||
|
|
||||||
if (this.selectedCellElement && scrollTo) {
|
if (this.selectedCellElement && scrollTo) {
|
||||||
this.selectedCellElement.scrollIntoView()
|
this.selectedCellElement.scrollIntoView()
|
||||||
this.selectedCellElement.closest('.table-container').scrollTo({ left: 0 })
|
this.selectedCellElement
|
||||||
|
.closest('.table-container')
|
||||||
|
.scrollTo({ left: 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$emit('updateSelectedCell', this.selectedCellElement)
|
this.$emit('updateSelectedCell', this.selectedCellElement)
|
||||||
},
|
},
|
||||||
moveFocusInTable(initialCell, direction) {
|
moveFocusInTable(initialCell, direction) {
|
||||||
const currentColIndex = +initialCell.dataset.col
|
const currentColIndex = +initialCell.dataset.col
|
||||||
const newColIndex = direction === 'up'
|
const newColIndex =
|
||||||
? currentColIndex - 1
|
direction === 'up' ? currentColIndex - 1 : currentColIndex + 1
|
||||||
: currentColIndex + 1
|
|
||||||
|
|
||||||
const newCell = this.$refs.table
|
const newCell = this.$refs.table.querySelector(
|
||||||
.querySelector(`td[data-col="${newColIndex}"][data-row="${this.currentRowIndex}"]`)
|
`td[data-col="${newColIndex}"][data-row="${this.currentRowIndex}"]`
|
||||||
|
)
|
||||||
if (newCell) {
|
if (newCell) {
|
||||||
this.selectCell(newCell)
|
this.selectCell(newCell)
|
||||||
}
|
}
|
||||||
@@ -180,7 +181,7 @@ table.sqliteviz-table:focus {
|
|||||||
.sqliteviz-table tbody td:hover {
|
.sqliteviz-table tbody td:hover {
|
||||||
background-color: var(--color-bg-light-3);
|
background-color: var(--color-bg-light-3);
|
||||||
}
|
}
|
||||||
.sqliteviz-table tbody td[aria-selected="true"] {
|
.sqliteviz-table tbody td[aria-selected='true'] {
|
||||||
box-shadow: inset 0 0 0 1px var(--color-accent);
|
box-shadow: inset 0 0 0 1px var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,7 @@
|
|||||||
{{ format.text }}
|
{{ format.text }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button type="button" class="copy" @click="copyToClipboard">Copy</button>
|
||||||
type="button"
|
|
||||||
class="copy"
|
|
||||||
@click="copyToClipboard"
|
|
||||||
>
|
|
||||||
Copy
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="value-body">
|
<div class="value-body">
|
||||||
<codemirror
|
<codemirror
|
||||||
@@ -30,7 +24,8 @@
|
|||||||
<pre
|
<pre
|
||||||
v-if="currentFormat === 'text'"
|
v-if="currentFormat === 'text'"
|
||||||
:class="['text-value', { 'meta-value': isNull || isBlob }]"
|
:class="['text-value', { 'meta-value': isNull || isBlob }]"
|
||||||
>{{ cellText }}</pre>
|
>{{ cellText }}</pre
|
||||||
|
>
|
||||||
<logs
|
<logs
|
||||||
v-if="messages && messages.length > 0"
|
v-if="messages && messages.length > 0"
|
||||||
:messages="messages"
|
:messages="messages"
|
||||||
@@ -117,22 +112,21 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
formatJson(jsonStr) {
|
formatJson(jsonStr) {
|
||||||
try {
|
try {
|
||||||
this.formattedJson = JSON.stringify(
|
this.formattedJson = JSON.stringify(JSON.parse(jsonStr), null, 4)
|
||||||
JSON.parse(jsonStr), null, 4
|
|
||||||
)
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
this.formattedJson = ''
|
this.formattedJson = ''
|
||||||
this.messages = [{
|
this.messages = [
|
||||||
|
{
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: 'Can\'t parse JSON.'
|
message: "Can't parse JSON."
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
copyToClipboard() {
|
copyToClipboard() {
|
||||||
cIo.copyText(this.currentFormat === 'json'
|
cIo.copyText(
|
||||||
? this.formattedJson
|
this.currentFormat === 'json' ? this.formattedJson : this.cellValue,
|
||||||
: this.cellValue,
|
|
||||||
'The value is copied to clipboard.'
|
'The value is copied to clipboard.'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -188,7 +182,7 @@ export default {
|
|||||||
background-color: var(--color-bg-light);
|
background-color: var(--color-bg-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-viewer-toolbar button[aria-selected="true"] {
|
.value-viewer-toolbar button[aria-selected='true'] {
|
||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,16 +8,26 @@
|
|||||||
class="run-result-panel-content"
|
class="run-result-panel-content"
|
||||||
>
|
>
|
||||||
<template #left-pane>
|
<template #left-pane>
|
||||||
<div :id="'run-result-left-pane-'+tab.id" class="result-set-container"/>
|
<div
|
||||||
|
:id="'run-result-left-pane-' + tab.id"
|
||||||
|
class="result-set-container"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div :id="'run-result-result-set-'+tab.id" class="result-set-container"/>
|
<div
|
||||||
|
:id="'run-result-result-set-' + tab.id"
|
||||||
|
class="result-set-container"
|
||||||
|
/>
|
||||||
<template #right-pane v-if="viewValuePanelVisible">
|
<template #right-pane v-if="viewValuePanelVisible">
|
||||||
<div class="value-viewer-container">
|
<div class="value-viewer-container">
|
||||||
<value-viewer
|
<value-viewer
|
||||||
v-show="selectedCell"
|
v-show="selectedCell"
|
||||||
:cellValue="selectedCell
|
:cellValue="
|
||||||
? result.values[result.columns[selectedCell.dataset.col]][selectedCell.dataset.row]
|
selectedCell
|
||||||
: ''"
|
? result.values[result.columns[selectedCell.dataset.col]][
|
||||||
|
selectedCell.dataset.row
|
||||||
|
]
|
||||||
|
: ''
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
<div v-show="!selectedCell" class="table-preview">
|
<div v-show="!selectedCell" class="table-preview">
|
||||||
No cell selected to view
|
No cell selected to view
|
||||||
@@ -80,11 +90,7 @@
|
|||||||
@cancel="cancelCopy"
|
@cancel="cancelCopy"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<teleport
|
<teleport defer :to="resultSetTeleportTarget" :disabled="!enableTeleport">
|
||||||
defer
|
|
||||||
:to="resultSetTeleportTarget"
|
|
||||||
:disabled="!enableTeleport"
|
|
||||||
>
|
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
v-show="result === null && !isGettingResults && !error"
|
v-show="result === null && !isGettingResults && !error"
|
||||||
@@ -192,7 +198,8 @@ export default {
|
|||||||
if (!this.enableTeleport) {
|
if (!this.enableTeleport) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
const base = `#${this.viewValuePanelVisible
|
const base = `#${
|
||||||
|
this.viewValuePanelVisible
|
||||||
? 'run-result-left-pane'
|
? 'run-result-left-pane'
|
||||||
: 'run-result-result-set'
|
: 'run-result-result-set'
|
||||||
}`
|
}`
|
||||||
@@ -236,7 +243,8 @@ export default {
|
|||||||
|
|
||||||
exportToCsv() {
|
exportToCsv() {
|
||||||
if (this.result && this.result.values) {
|
if (this.result && this.result.values) {
|
||||||
events.send('resultset.export',
|
events.send(
|
||||||
|
'resultset.export',
|
||||||
this.result.values[this.result.columns[0]].length,
|
this.result.values[this.result.columns[0]].length,
|
||||||
{ to: 'csv' }
|
{ to: 'csv' }
|
||||||
)
|
)
|
||||||
@@ -247,7 +255,8 @@ export default {
|
|||||||
|
|
||||||
async prepareCopy() {
|
async prepareCopy() {
|
||||||
if (this.result && this.result.values) {
|
if (this.result && this.result.values) {
|
||||||
events.send('resultset.export',
|
events.send(
|
||||||
|
'resultset.export',
|
||||||
this.result.values[this.result.columns[0]].length,
|
this.result.values[this.result.columns[0]].length,
|
||||||
{ to: 'clipboard' }
|
{ to: 'clipboard' }
|
||||||
)
|
)
|
||||||
@@ -261,7 +270,7 @@ export default {
|
|||||||
await time.sleep(0)
|
await time.sleep(0)
|
||||||
this.dataToCopy = csv.serialize(this.result)
|
this.dataToCopy = csv.serialize(this.result)
|
||||||
const t1 = performance.now()
|
const t1 = performance.now()
|
||||||
if ((t1 - t0) < 950) {
|
if (t1 - t0 < 950) {
|
||||||
this.$modal.hide('prepareCSVCopy')
|
this.$modal.hide('prepareCSVCopy')
|
||||||
this.copyToClipboard()
|
this.copyToClipboard()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ export function getHints (cm, options) {
|
|||||||
// Don't show the hint if there is only one option
|
// Don't show the hint if there is only one option
|
||||||
// and the replacingText is already equals to this option
|
// and the replacingText is already equals to this option
|
||||||
const replacedText = cm.getRange(result.from, result.to).toUpperCase()
|
const replacedText = cm.getRange(result.from, result.to).toUpperCase()
|
||||||
if (result.list.length === 1 &&
|
if (
|
||||||
_getHintText(result.list[0]).toUpperCase() === replacedText) {
|
result.list.length === 1 &&
|
||||||
|
_getHintText(result.list[0]).toUpperCase() === replacedText
|
||||||
|
) {
|
||||||
result.list = []
|
result.list = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
runDisabled() {
|
runDisabled() {
|
||||||
return (!this.$store.state.db || !this.query || this.isGettingResults)
|
return !this.$store.state.db || !this.query || this.isGettingResults
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|||||||
@@ -87,7 +87,9 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
topPaneSize: this.tab.maximize
|
topPaneSize: this.tab.maximize
|
||||||
? this.tab.layout[this.tab.maximize] === 'above' ? 100 : 0
|
? this.tab.layout[this.tab.maximize] === 'above'
|
||||||
|
? 100
|
||||||
|
: 0
|
||||||
: 50,
|
: 50,
|
||||||
enableTeleport: this.$store.state.isWorkspaceVisible
|
enableTeleport: this.$store.state.isWorkspaceVisible
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
v-for="(tab, index) in tabs"
|
v-for="(tab, index) in tabs"
|
||||||
:key="index"
|
:key="index"
|
||||||
@click="selectTab(tab.id)"
|
@click="selectTab(tab.id)"
|
||||||
:class="[{'tab-selected': (tab.id === selectedTabId)}, 'tab']"
|
:class="[{ 'tab-selected': tab.id === selectedTabId }, 'tab']"
|
||||||
>
|
>
|
||||||
<div class="tab-name">
|
<div class="tab-name">
|
||||||
<span v-show="!tab.isSaved" class="star">*</span>
|
<span v-show="!tab.isSaved" class="star">*</span>
|
||||||
@@ -13,15 +13,15 @@
|
|||||||
<span v-else class="tab-untitled">{{ tab.tempName }}</span>
|
<span v-else class="tab-untitled">{{ tab.tempName }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<close-icon class="close-icon" :size="10" @click="beforeCloseTab(tab)"/>
|
<close-icon
|
||||||
</div>
|
class="close-icon"
|
||||||
</div>
|
:size="10"
|
||||||
</div>
|
@click="beforeCloseTab(tab)"
|
||||||
<tab
|
|
||||||
v-for="tab in tabs"
|
|
||||||
:key="tab.id"
|
|
||||||
:tab="tab"
|
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<tab v-for="tab in tabs" :key="tab.id" :tab="tab" />
|
||||||
<div v-show="tabs.length === 0" id="start-guide">
|
<div v-show="tabs.length === 0" id="start-guide">
|
||||||
<span class="link" @click="emitCreateTabEvent">Create</span>
|
<span class="link" @click="emitCreateTabEvent">Create</span>
|
||||||
new inquiry from scratch or open one from
|
new inquiry from scratch or open one from
|
||||||
@@ -31,26 +31,33 @@
|
|||||||
<!--Close tab warning dialog -->
|
<!--Close tab warning dialog -->
|
||||||
<modal modal-id="close-warn" class="dialog" content-style="width: 560px;">
|
<modal modal-id="close-warn" class="dialog" content-style="width: 560px;">
|
||||||
<div class="dialog-header">
|
<div class="dialog-header">
|
||||||
Close tab {{
|
Close tab
|
||||||
|
{{
|
||||||
closingTab !== null
|
closingTab !== null
|
||||||
? (closingTab.name || `[${closingTab.tempName}]`)
|
? closingTab.name || `[${closingTab.tempName}]`
|
||||||
: ''
|
: ''
|
||||||
}}
|
}}
|
||||||
<close-icon @click="$modal.hide('close-warn')" />
|
<close-icon @click="$modal.hide('close-warn')" />
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-body">
|
<div class="dialog-body">
|
||||||
You have unsaved changes. Save changes in {{
|
You have unsaved changes. Save changes in
|
||||||
|
{{
|
||||||
closingTab !== null
|
closingTab !== null
|
||||||
? (closingTab.name || `[${closingTab.tempName}]`)
|
? closingTab.name || `[${closingTab.tempName}]`
|
||||||
: ''
|
: ''
|
||||||
}} before closing?
|
}}
|
||||||
|
before closing?
|
||||||
</div>
|
</div>
|
||||||
<div class="dialog-buttons-container">
|
<div class="dialog-buttons-container">
|
||||||
<button class="secondary" @click="closeTab(closingTab)">
|
<button class="secondary" @click="closeTab(closingTab)">
|
||||||
Close without saving
|
Close without saving
|
||||||
</button>
|
</button>
|
||||||
<button class="secondary" @click="$modal.hide('close-warn')">Don't close</button>
|
<button class="secondary" @click="$modal.hide('close-warn')">
|
||||||
<button class="primary" @click="saveAndClose(closingTab)">Save and close</button>
|
Don't close
|
||||||
|
</button>
|
||||||
|
<button class="primary" @click="saveAndClose(closingTab)">
|
||||||
|
Save and close
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</modal>
|
</modal>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ export default {
|
|||||||
},
|
},
|
||||||
async beforeCreate() {
|
async beforeCreate() {
|
||||||
const schema = this.$store.state.db.schema
|
const schema = this.$store.state.db.schema
|
||||||
if ((!schema || schema.length === 0) && this.$store.state.tabs.length === 0) {
|
if (
|
||||||
|
(!schema || schema.length === 0) &&
|
||||||
|
this.$store.state.tabs.length === 0
|
||||||
|
) {
|
||||||
const stmt = [
|
const stmt = [
|
||||||
'/*',
|
'/*',
|
||||||
' * Your database is empty. In order to start building charts',
|
' * Your database is empty. In order to start building charts',
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createVfm, VueFinalModal, useVfm } from 'vue-final-modal'
|
|||||||
|
|
||||||
config.global.plugins = [createVfm()]
|
config.global.plugins = [createVfm()]
|
||||||
config.global.components = {
|
config.global.components = {
|
||||||
'modal': VueFinalModal
|
modal: VueFinalModal
|
||||||
}
|
}
|
||||||
const vfm = useVfm()
|
const vfm = useVfm()
|
||||||
config.global.mocks = {
|
config.global.mocks = {
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ describe('App.vue', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('Gets inquiries', () => {
|
it('Gets inquiries', () => {
|
||||||
sinon.stub(storedInquiries, 'getStoredInquiries').returns([
|
sinon
|
||||||
{ id: 1 }, { id: 2 }, { id: 3 }
|
.stub(storedInquiries, 'getStoredInquiries')
|
||||||
])
|
.returns([{ id: 1 }, { id: 2 }, { id: 3 }])
|
||||||
const state = {
|
const state = {
|
||||||
predefinedInquiries: [],
|
predefinedInquiries: [],
|
||||||
inquiries: []
|
inquiries: []
|
||||||
@@ -33,7 +33,9 @@ describe('App.vue', () => {
|
|||||||
|
|
||||||
it('Updates inquiries when they change in store', async () => {
|
it('Updates inquiries when they change in store', async () => {
|
||||||
sinon.stub(storedInquiries, 'getStoredInquiries').returns([
|
sinon.stub(storedInquiries, 'getStoredInquiries').returns([
|
||||||
{ id: 1, name: 'foo' }, { id: 2, name: 'baz' }, { id: 3, name: 'bar' }
|
{ id: 1, name: 'foo' },
|
||||||
|
{ id: 2, name: 'baz' },
|
||||||
|
{ id: 3, name: 'bar' }
|
||||||
])
|
])
|
||||||
sinon.spy(storedInquiries, 'updateStorage')
|
sinon.spy(storedInquiries, 'updateStorage')
|
||||||
|
|
||||||
@@ -43,8 +45,7 @@ describe('App.vue', () => {
|
|||||||
}
|
}
|
||||||
const store = createStore({ state, mutations })
|
const store = createStore({ state, mutations })
|
||||||
shallowMount(App, {
|
shallowMount(App, {
|
||||||
global: { stubs: ['router-view'],
|
global: { stubs: ['router-view'], plugins: [store] }
|
||||||
plugins: [store] }
|
|
||||||
})
|
})
|
||||||
|
|
||||||
store.state.inquiries.splice(0, 1, { id: 1, name: 'new foo name' })
|
store.state.inquiries.splice(0, 1, { id: 1, name: 'new foo name' })
|
||||||
|
|||||||
@@ -50,9 +50,13 @@ describe('CheckBox', () => {
|
|||||||
props: { disabled: true }
|
props: { disabled: true }
|
||||||
})
|
})
|
||||||
expect(wrapper.find('.checkbox-container').classes()).to.include('disabled')
|
expect(wrapper.find('.checkbox-container').classes()).to.include('disabled')
|
||||||
expect(wrapper.find('.checkbox-container').classes()).to.not.include('checked')
|
expect(wrapper.find('.checkbox-container').classes()).to.not.include(
|
||||||
|
'checked'
|
||||||
|
)
|
||||||
await wrapper.trigger('click')
|
await wrapper.trigger('click')
|
||||||
expect(wrapper.emitted().click).to.equal(undefined)
|
expect(wrapper.emitted().click).to.equal(undefined)
|
||||||
expect(wrapper.find('.checkbox-container').classes()).to.not.include('checked')
|
expect(wrapper.find('.checkbox-container').classes()).to.not.include(
|
||||||
|
'checked'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import CsvJsonImport from '@/components/CsvJsonImport'
|
|||||||
import csv from '@/lib/csv'
|
import csv from '@/lib/csv'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
|
|
||||||
|
|
||||||
describe('CsvJsonImport.vue', () => {
|
describe('CsvJsonImport.vue', () => {
|
||||||
let state = {}
|
let state = {}
|
||||||
let actions = {}
|
let actions = {}
|
||||||
@@ -74,13 +73,15 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
rowCount: 2,
|
rowCount: 2,
|
||||||
messages: [{
|
messages: [
|
||||||
|
{
|
||||||
code: 'UndetectableDelimiter',
|
code: 'UndetectableDelimiter',
|
||||||
message: 'Comma was used as a standart delimiter',
|
message: 'Comma was used as a standart delimiter',
|
||||||
row: 0,
|
row: 0,
|
||||||
type: 'info',
|
type: 'info',
|
||||||
hint: undefined
|
hint: undefined
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
wrapper.vm.preview()
|
wrapper.vm.preview()
|
||||||
@@ -89,24 +90,34 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
|
expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
|
||||||
expect(wrapper.find('.dialog-header').text()).to.equal('CSV import')
|
expect(wrapper.find('.dialog-header').text()).to.equal('CSV import')
|
||||||
expect(wrapper.find('#csv-json-table-name input').element.value).to.equal('my_data')
|
expect(wrapper.find('#csv-json-table-name input').element.value).to.equal(
|
||||||
expect(wrapper.findComponent({ name: 'delimiter-selector' }).props('modelValue')).to.equal('|')
|
'my_data'
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
wrapper.findComponent({ name: 'delimiter-selector' }).props('modelValue')
|
||||||
|
).to.equal('|')
|
||||||
expect(wrapper.find('#quote-char input').element.value).to.equal('"')
|
expect(wrapper.find('#quote-char input').element.value).to.equal('"')
|
||||||
expect(wrapper.find('#escape-char input').element.value).to.equal('"')
|
expect(wrapper.find('#escape-char input').element.value).to.equal('"')
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.checked).to.equal(true)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.checked).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
const rows = wrapper.findAll('tbody tr')
|
const rows = wrapper.findAll('tbody tr')
|
||||||
expect(rows).to.have.lengthOf(2)
|
expect(rows).to.have.lengthOf(2)
|
||||||
expect(rows[0].findAll('td')[0].text()).to.equal('foo')
|
expect(rows[0].findAll('td')[0].text()).to.equal('foo')
|
||||||
expect(rows[0].findAll('td')[1].text()).to.equal('1')
|
expect(rows[0].findAll('td')[1].text()).to.equal('1')
|
||||||
expect(rows[1].findAll('td')[0].text()).to.equal('bar')
|
expect(rows[1].findAll('td')[0].text()).to.equal('bar')
|
||||||
expect(rows[1].findAll('td')[1].text()).to.equal('2')
|
expect(rows[1].findAll('td')[1].text()).to.equal('2')
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).text())
|
expect(wrapper.findComponent({ name: 'logs' }).text()).to.include(
|
||||||
.to.include('Information about row 0. Comma was used as a standart delimiter.')
|
'Information about row 0. Comma was used as a standart delimiter.'
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).text())
|
)
|
||||||
.to.include('Preview parsing is completed in')
|
expect(wrapper.findComponent({ name: 'logs' }).text()).to.include(
|
||||||
|
'Preview parsing is completed in'
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
expect(wrapper.find('#import-start').attributes().disabled).to.equal(undefined)
|
expect(wrapper.find('#import-start').attributes().disabled).to.equal(
|
||||||
|
undefined
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('disables import if no rows found', async () => {
|
it('disables import if no rows found', async () => {
|
||||||
@@ -129,8 +140,9 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
const rows = wrapper.findAll('tbody tr')
|
const rows = wrapper.findAll('tbody tr')
|
||||||
expect(rows).to.have.lengthOf(0)
|
expect(rows).to.have.lengthOf(0)
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).text())
|
expect(wrapper.findComponent({ name: 'logs' }).text()).to.include(
|
||||||
.to.include('No rows to import.')
|
'No rows to import.'
|
||||||
|
)
|
||||||
expect(wrapper.find('.no-data').isVisible()).to.equal(true)
|
expect(wrapper.find('.no-data').isVisible()).to.equal(true)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
@@ -177,8 +189,9 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
expect(rows).to.have.lengthOf(1)
|
expect(rows).to.have.lengthOf(1)
|
||||||
expect(rows[0].findAll('td')[0].text()).to.equal('bar')
|
expect(rows[0].findAll('td')[0].text()).to.equal('bar')
|
||||||
expect(rows[0].findAll('td')[1].text()).to.equal('2')
|
expect(rows[0].findAll('td')[1].text()).to.equal('2')
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).text())
|
expect(wrapper.findComponent({ name: 'logs' }).text()).to.include(
|
||||||
.to.include('Preview parsing is completed in')
|
'Preview parsing is completed in'
|
||||||
|
)
|
||||||
|
|
||||||
parse.onCall(2).resolves({
|
parse.onCall(2).resolves({
|
||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
@@ -191,13 +204,15 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
},
|
},
|
||||||
rowCount: 1,
|
rowCount: 1,
|
||||||
hasErrors: true,
|
hasErrors: true,
|
||||||
messages: [{
|
messages: [
|
||||||
|
{
|
||||||
code: 'MissingQuotes',
|
code: 'MissingQuotes',
|
||||||
message: 'Quote is missed',
|
message: 'Quote is missed',
|
||||||
row: 0,
|
row: 0,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
hint: 'Edit your CSV so that the field has a closing quote char.'
|
hint: 'Edit your CSV so that the field has a closing quote char.'
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
await wrapper.find('#quote-char input').setValue("'")
|
await wrapper.find('#quote-char input').setValue("'")
|
||||||
@@ -207,13 +222,13 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
expect(rows).to.have.lengthOf(1)
|
expect(rows).to.have.lengthOf(1)
|
||||||
expect(rows[0].findAll('td')[0].text()).to.equal('baz')
|
expect(rows[0].findAll('td')[0].text()).to.equal('baz')
|
||||||
expect(rows[0].findAll('td')[1].text()).to.equal('3')
|
expect(rows[0].findAll('td')[1].text()).to.equal('3')
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).text())
|
expect(wrapper.findComponent({ name: 'logs' }).text()).to.contain(
|
||||||
.to.contain(
|
|
||||||
'Error in row 0. Quote is missed. ' +
|
'Error in row 0. Quote is missed. ' +
|
||||||
'Edit your CSV so that the field has a closing quote char.'
|
'Edit your CSV so that the field has a closing quote char.'
|
||||||
)
|
)
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).text())
|
expect(wrapper.findComponent({ name: 'logs' }).text()).to.not.contain(
|
||||||
.to.not.contain('Preview parsing is completed in')
|
'Preview parsing is completed in'
|
||||||
|
)
|
||||||
|
|
||||||
parse.onCall(3).resolves({
|
parse.onCall(3).resolves({
|
||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
@@ -234,8 +249,9 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
expect(rows).to.have.lengthOf(1)
|
expect(rows).to.have.lengthOf(1)
|
||||||
expect(rows[0].findAll('td')[0].text()).to.equal('qux')
|
expect(rows[0].findAll('td')[0].text()).to.equal('qux')
|
||||||
expect(rows[0].findAll('td')[1].text()).to.equal('4')
|
expect(rows[0].findAll('td')[1].text()).to.equal('4')
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).text())
|
expect(wrapper.findComponent({ name: 'logs' }).text()).to.contain(
|
||||||
.to.contain('Preview parsing is completed in')
|
'Preview parsing is completed in'
|
||||||
|
)
|
||||||
|
|
||||||
parse.onCall(4).resolves({
|
parse.onCall(4).resolves({
|
||||||
delimiter: ',',
|
delimiter: ',',
|
||||||
@@ -257,8 +273,9 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
expect(rows[0].findAll('td')[0].text()).to.equal('corge')
|
expect(rows[0].findAll('td')[0].text()).to.equal('corge')
|
||||||
expect(rows[0].findAll('td')[1].text()).to.equal('5')
|
expect(rows[0].findAll('td')[1].text()).to.equal('5')
|
||||||
|
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).text())
|
expect(wrapper.findComponent({ name: 'logs' }).text()).to.include(
|
||||||
.to.include('Preview parsing is completed in')
|
'Preview parsing is completed in'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has proper state before parsing is complete', async () => {
|
it('has proper state before parsing is complete', async () => {
|
||||||
@@ -282,8 +299,10 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
let resolveParsing
|
let resolveParsing
|
||||||
parse.onCall(1).returns(new Promise(resolve => {
|
parse.onCall(1).returns(
|
||||||
resolveParsing = () => resolve({
|
new Promise(resolve => {
|
||||||
|
resolveParsing = () =>
|
||||||
|
resolve({
|
||||||
delimiter: '|',
|
delimiter: '|',
|
||||||
data: {
|
data: {
|
||||||
columns: ['col1', 'col2'],
|
columns: ['col1', 'col2'],
|
||||||
@@ -295,29 +314,40 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
rowCount: 1,
|
rowCount: 1,
|
||||||
messages: []
|
messages: []
|
||||||
})
|
})
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await wrapper.find('#import-start').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
|
|
||||||
// "Parsing CSV..." in the logs
|
// "Parsing CSV..." in the logs
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).findAll('.msg')[1].text())
|
expect(
|
||||||
.to.equal('Parsing CSV...')
|
wrapper.findComponent({ name: 'logs' }).findAll('.msg')[1].text()
|
||||||
|
).to.equal('Parsing CSV...')
|
||||||
|
|
||||||
// After 1 second - loading indicator is shown
|
// After 1 second - loading indicator is shown
|
||||||
await clock.tick(1000)
|
await clock.tick(1000)
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(true)
|
).to.equal(true)
|
||||||
|
|
||||||
// All the dialog controls are disabled
|
// All the dialog controls are disabled
|
||||||
expect(wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled).to.equal(true)
|
expect(
|
||||||
|
wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled
|
||||||
|
).to.equal(true)
|
||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
await resolveParsing()
|
await resolveParsing()
|
||||||
@@ -325,7 +355,10 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
|
|
||||||
// Loading indicator is not shown when parsing is compete
|
// Loading indicator is not shown when parsing is compete
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(false)
|
).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -365,26 +398,35 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
let resolveImport
|
let resolveImport
|
||||||
wrapper.vm.db.addTableFromCsv.onCall(0).returns(new Promise(resolve => {
|
wrapper.vm.db.addTableFromCsv.onCall(0).returns(
|
||||||
|
new Promise(resolve => {
|
||||||
resolveImport = resolve
|
resolveImport = resolve
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await wrapper.find('#import-start').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await csv.parse.returnValues[1]
|
await csv.parse.returnValues[1]
|
||||||
|
|
||||||
// Parsing success in the logs
|
// Parsing success in the logs
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).findAll('.msg')[1].text())
|
expect(
|
||||||
.to.include('2 rows are parsed successfully in')
|
wrapper.findComponent({ name: 'logs' }).findAll('.msg')[1].text()
|
||||||
|
).to.include('2 rows are parsed successfully in')
|
||||||
|
|
||||||
// All the dialog controls are disabled
|
// All the dialog controls are disabled
|
||||||
expect(wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled).to.equal(true)
|
expect(
|
||||||
|
wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled
|
||||||
|
).to.equal(true)
|
||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
await resolveImport()
|
await resolveImport()
|
||||||
@@ -417,18 +459,22 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
},
|
},
|
||||||
rowCount: 2,
|
rowCount: 2,
|
||||||
hasErrors: false,
|
hasErrors: false,
|
||||||
messages: [{
|
messages: [
|
||||||
|
{
|
||||||
code: 'UndetectableDelimiter',
|
code: 'UndetectableDelimiter',
|
||||||
message: 'Comma was used as a standart delimiter',
|
message: 'Comma was used as a standart delimiter',
|
||||||
type: 'info',
|
type: 'info',
|
||||||
hint: undefined
|
hint: undefined
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
let resolveImport
|
let resolveImport
|
||||||
wrapper.vm.db.addTableFromCsv.onCall(0).returns(new Promise(resolve => {
|
wrapper.vm.db.addTableFromCsv.onCall(0).returns(
|
||||||
|
new Promise(resolve => {
|
||||||
resolveImport = resolve
|
resolveImport = resolve
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
wrapper.vm.preview()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
@@ -446,13 +492,19 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
expect(logs[2].text()).to.equals('Comma was used as a standart delimiter.')
|
expect(logs[2].text()).to.equals('Comma was used as a standart delimiter.')
|
||||||
|
|
||||||
// All the dialog controls are disabled
|
// All the dialog controls are disabled
|
||||||
expect(wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled).to.equal(true)
|
expect(
|
||||||
|
wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled
|
||||||
|
).to.equal(true)
|
||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
await resolveImport()
|
await resolveImport()
|
||||||
@@ -485,12 +537,14 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
},
|
},
|
||||||
rowCount: 2,
|
rowCount: 2,
|
||||||
hasErrors: true,
|
hasErrors: true,
|
||||||
messages: [{
|
messages: [
|
||||||
|
{
|
||||||
code: 'Error',
|
code: 'Error',
|
||||||
message: 'Something is wrong',
|
message: 'Something is wrong',
|
||||||
type: 'error',
|
type: 'error',
|
||||||
hint: undefined
|
hint: undefined
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
wrapper.vm.preview()
|
wrapper.vm.preview()
|
||||||
@@ -509,13 +563,19 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
expect(logs[2].text()).to.equals('Something is wrong.')
|
expect(logs[2].text()).to.equals('Something is wrong.')
|
||||||
|
|
||||||
// All the dialog controls are enabled
|
// All the dialog controls are enabled
|
||||||
expect(wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled).to.equal(false)
|
expect(
|
||||||
|
wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled
|
||||||
|
).to.equal(false)
|
||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(
|
||||||
|
false
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
false
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
})
|
})
|
||||||
@@ -551,8 +611,11 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let resolveImport = sinon.stub()
|
let resolveImport = sinon.stub()
|
||||||
wrapper.vm.db.addTableFromCsv = sinon.stub()
|
wrapper.vm.db.addTableFromCsv = sinon.stub().resolves(
|
||||||
.resolves(new Promise(resolve => { resolveImport = resolve }))
|
new Promise(resolve => {
|
||||||
|
resolveImport = resolve
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
wrapper.vm.preview()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
@@ -564,23 +627,33 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
await csv.parse.returnValues[1]
|
await csv.parse.returnValues[1]
|
||||||
|
|
||||||
// Parsing success in the logs
|
// Parsing success in the logs
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).findAll('.msg')[2].text())
|
expect(
|
||||||
.to.equal('Importing CSV into a SQLite database...')
|
wrapper.findComponent({ name: 'logs' }).findAll('.msg')[2].text()
|
||||||
|
).to.equal('Importing CSV into a SQLite database...')
|
||||||
|
|
||||||
// After 1 second - loading indicator is shown
|
// After 1 second - loading indicator is shown
|
||||||
await clock.tick(1000)
|
await clock.tick(1000)
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(true)
|
).to.equal(true)
|
||||||
|
|
||||||
// All the dialog controls are disabled
|
// All the dialog controls are disabled
|
||||||
expect(wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled).to.equal(true)
|
expect(
|
||||||
|
wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled
|
||||||
|
).to.equal(true)
|
||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
expect(wrapper.vm.db.addTableFromCsv.getCall(0).args[0]).to.equal('foo') // table name
|
expect(wrapper.vm.db.addTableFromCsv.getCall(0).args[0]).to.equal('foo') // table name
|
||||||
@@ -589,7 +662,10 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
await resolveImport()
|
await resolveImport()
|
||||||
await wrapper.vm.db.addTableFromCsv.returnValues[0]
|
await wrapper.vm.db.addTableFromCsv.returnValues[0]
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(false)
|
).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -635,16 +711,24 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
// Import success in the logs
|
// Import success in the logs
|
||||||
const logs = wrapper.findComponent({ name: 'logs' }).findAll('.msg')
|
const logs = wrapper.findComponent({ name: 'logs' }).findAll('.msg')
|
||||||
expect(logs).to.have.lengthOf(3)
|
expect(logs).to.have.lengthOf(3)
|
||||||
expect(logs[2].text()).to.contain('Importing CSV into a SQLite database is completed in')
|
expect(logs[2].text()).to.contain(
|
||||||
|
'Importing CSV into a SQLite database is completed in'
|
||||||
|
)
|
||||||
|
|
||||||
// All the dialog controls are enabled
|
// All the dialog controls are enabled
|
||||||
expect(wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled).to.equal(false)
|
expect(
|
||||||
|
wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled
|
||||||
|
).to.equal(false)
|
||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(
|
||||||
|
false
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
false
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -696,13 +780,19 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
expect(logs[3].text()).to.equal('Error: fail.')
|
expect(logs[3].text()).to.equal('Error: fail.')
|
||||||
|
|
||||||
// All the dialog controls are enabled
|
// All the dialog controls are enabled
|
||||||
expect(wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled).to.equal(false)
|
expect(
|
||||||
|
wrapper.findComponent({ name: 'delimiter-selector' }).vm.disabled
|
||||||
|
).to.equal(false)
|
||||||
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#quote-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
expect(wrapper.find('#escape-char input').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'check-box' }).vm.disabled).to.equal(
|
||||||
|
false
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
false
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -733,7 +823,9 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
await clock.tick(100)
|
await clock.tick(100)
|
||||||
expect(actions.addTab.calledOnce).to.equal(true)
|
expect(actions.addTab.calledOnce).to.equal(true)
|
||||||
await actions.addTab.returnValues[0]
|
await actions.addTab.returnValues[0]
|
||||||
expect(mutations.setCurrentTabId.calledOnceWith(state, newTabId)).to.equal(true)
|
expect(mutations.setCurrentTabId.calledOnceWith(state, newTabId)).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
|
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
|
||||||
expect(wrapper.emitted('finish')).to.have.lengthOf(1)
|
expect(wrapper.emitted('finish')).to.have.lengthOf(1)
|
||||||
})
|
})
|
||||||
@@ -764,7 +856,9 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
await wrapper.find('#import-cancel').trigger('click')
|
await wrapper.find('#import-cancel').trigger('click')
|
||||||
await clock.tick(100)
|
await clock.tick(100)
|
||||||
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
|
expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)
|
||||||
expect(wrapper.vm.db.execute.calledOnceWith('DROP TABLE "my_data"')).to.equal(true)
|
expect(
|
||||||
|
wrapper.vm.db.execute.calledOnceWith('DROP TABLE "my_data"')
|
||||||
|
).to.equal(true)
|
||||||
expect(wrapper.vm.db.refreshSchema.calledOnce).to.equal(true)
|
expect(wrapper.vm.db.refreshSchema.calledOnce).to.equal(true)
|
||||||
expect(wrapper.emitted('cancel')).to.have.lengthOf(1)
|
expect(wrapper.emitted('cancel')).to.have.lengthOf(1)
|
||||||
})
|
})
|
||||||
@@ -783,23 +877,31 @@ describe('CsvJsonImport.vue', () => {
|
|||||||
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await clock.tick(400)
|
await clock.tick(400)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(wrapper.find('#csv-json-table-name .text-field-error').text()).to.equal('')
|
expect(
|
||||||
|
wrapper.find('#csv-json-table-name .text-field-error').text()
|
||||||
|
).to.equal('')
|
||||||
|
|
||||||
wrapper.vm.db.validateTableName = sinon.stub().rejects(new Error('this is a bad table name'))
|
wrapper.vm.db.validateTableName = sinon
|
||||||
|
.stub()
|
||||||
|
.rejects(new Error('this is a bad table name'))
|
||||||
await wrapper.find('#csv-json-table-name input').setValue('bar')
|
await wrapper.find('#csv-json-table-name input').setValue('bar')
|
||||||
await clock.tick(400)
|
await clock.tick(400)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(wrapper.find('#csv-json-table-name .text-field-error').text())
|
expect(
|
||||||
.to.equal('this is a bad table name. Try another table name.')
|
wrapper.find('#csv-json-table-name .text-field-error').text()
|
||||||
|
).to.equal('this is a bad table name. Try another table name.')
|
||||||
|
|
||||||
await wrapper.find('#csv-json-table-name input').setValue('')
|
await wrapper.find('#csv-json-table-name input').setValue('')
|
||||||
await clock.tick(400)
|
await clock.tick(400)
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(wrapper.find('#csv-json-table-name .text-field-error').text()).to.equal('')
|
expect(
|
||||||
|
wrapper.find('#csv-json-table-name .text-field-error').text()
|
||||||
|
).to.equal('')
|
||||||
|
|
||||||
await wrapper.find('#import-start').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
expect(wrapper.find('#csv-json-table-name .text-field-error').text())
|
expect(
|
||||||
.to.equal("Table name can't be empty")
|
wrapper.find('#csv-json-table-name .text-field-error').text()
|
||||||
|
).to.equal("Table name can't be empty")
|
||||||
expect(wrapper.vm.db.addTableFromCsv.called).to.equal(false)
|
expect(wrapper.vm.db.addTableFromCsv.called).to.equal(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -812,12 +914,14 @@ describe('CsvJsonImport.vue - json', () => {
|
|||||||
let wrapper
|
let wrapper
|
||||||
const newTabId = 1
|
const newTabId = 1
|
||||||
const file = new File(
|
const file = new File(
|
||||||
[new Blob(
|
[
|
||||||
[JSON.stringify({ foo: [1, 2, 3] }, null, 2)],
|
new Blob([JSON.stringify({ foo: [1, 2, 3] }, null, 2)], {
|
||||||
{ type: 'application/json' }
|
type: 'application/json'
|
||||||
)],
|
})
|
||||||
|
],
|
||||||
'my data.json',
|
'my data.json',
|
||||||
{ type: 'application/json' })
|
{ type: 'application/json' }
|
||||||
|
)
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
clock = sinon.useFakeTimers()
|
clock = sinon.useFakeTimers()
|
||||||
@@ -873,25 +977,25 @@ describe('CsvJsonImport.vue - json', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
|
expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
|
||||||
expect(wrapper.find('.dialog-header').text()).to.equal('JSON import')
|
expect(wrapper.find('.dialog-header').text()).to.equal('JSON import')
|
||||||
expect(wrapper.find('#csv-json-table-name input').element.value).to.equal('my_data')
|
expect(wrapper.find('#csv-json-table-name input').element.value).to.equal(
|
||||||
expect(wrapper.findComponent({ name: 'delimiter-selector' }).exists()).to.equal(false)
|
'my_data'
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
wrapper.findComponent({ name: 'delimiter-selector' }).exists()
|
||||||
|
).to.equal(false)
|
||||||
expect(wrapper.find('#quote-char input').exists()).to.equal(false)
|
expect(wrapper.find('#quote-char input').exists()).to.equal(false)
|
||||||
expect(wrapper.find('#escape-char input').exists()).to.equal(false)
|
expect(wrapper.find('#escape-char input').exists()).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).exists()).to.equal(false)
|
expect(wrapper.findComponent({ name: 'check-box' }).exists()).to.equal(
|
||||||
|
false
|
||||||
|
)
|
||||||
const rows = wrapper.findAll('tbody tr')
|
const rows = wrapper.findAll('tbody tr')
|
||||||
expect(rows).to.have.lengthOf(1)
|
expect(rows).to.have.lengthOf(1)
|
||||||
expect(rows[0].findAll('td')[0].text()).to.equal([
|
expect(rows[0].findAll('td')[0].text()).to.equal(
|
||||||
'{',
|
['{', ' "foo": [', ' 1,', ' 2,', ' 3', ' ]', '}'].join('\n')
|
||||||
' "foo": [',
|
)
|
||||||
' 1,',
|
expect(wrapper.findComponent({ name: 'logs' }).text()).to.include(
|
||||||
' 2,',
|
'Preview parsing is completed in'
|
||||||
' 3',
|
|
||||||
' ]',
|
|
||||||
'}'
|
|
||||||
].join('\n')
|
|
||||||
)
|
)
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).text())
|
|
||||||
.to.include('Preview parsing is completed in')
|
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
})
|
})
|
||||||
@@ -912,8 +1016,10 @@ describe('CsvJsonImport.vue - json', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let resolveParsing
|
let resolveParsing
|
||||||
getJsonParseResult.onCall(1).returns(new Promise(resolve => {
|
getJsonParseResult.onCall(1).returns(
|
||||||
resolveParsing = () => resolve({
|
new Promise(resolve => {
|
||||||
|
resolveParsing = () =>
|
||||||
|
resolve({
|
||||||
delimiter: '|',
|
delimiter: '|',
|
||||||
data: {
|
data: {
|
||||||
columns: ['doc'],
|
columns: ['doc'],
|
||||||
@@ -925,7 +1031,8 @@ describe('CsvJsonImport.vue - json', () => {
|
|||||||
hasErrors: false,
|
hasErrors: false,
|
||||||
messages: []
|
messages: []
|
||||||
})
|
})
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.vm.preview()
|
await wrapper.vm.preview()
|
||||||
await wrapper.vm.open()
|
await wrapper.vm.open()
|
||||||
@@ -937,19 +1044,25 @@ describe('CsvJsonImport.vue - json', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
// "Parsing JSON..." in the logs
|
// "Parsing JSON..." in the logs
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).findAll('.msg')[1].text())
|
expect(
|
||||||
.to.equal('Parsing JSON...')
|
wrapper.findComponent({ name: 'logs' }).findAll('.msg')[1].text()
|
||||||
|
).to.equal('Parsing JSON...')
|
||||||
|
|
||||||
// After 1 second - loading indicator is shown
|
// After 1 second - loading indicator is shown
|
||||||
await clock.tick(1000)
|
await clock.tick(1000)
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(true)
|
).to.equal(true)
|
||||||
|
|
||||||
// All the dialog controls are disabled
|
// All the dialog controls are disabled
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
await resolveParsing()
|
await resolveParsing()
|
||||||
@@ -957,7 +1070,10 @@ describe('CsvJsonImport.vue - json', () => {
|
|||||||
|
|
||||||
// Loading indicator is not shown when parsing is compete
|
// Loading indicator is not shown when parsing is compete
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(false)
|
).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -965,8 +1081,11 @@ describe('CsvJsonImport.vue - json', () => {
|
|||||||
const getJsonParseResult = sinon.spy(wrapper.vm, 'getJsonParseResult')
|
const getJsonParseResult = sinon.spy(wrapper.vm, 'getJsonParseResult')
|
||||||
|
|
||||||
let resolveImport = sinon.stub()
|
let resolveImport = sinon.stub()
|
||||||
wrapper.vm.db.addTableFromCsv = sinon.stub()
|
wrapper.vm.db.addTableFromCsv = sinon.stub().resolves(
|
||||||
.resolves(new Promise(resolve => { resolveImport = resolve }))
|
new Promise(resolve => {
|
||||||
|
resolveImport = resolve
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.vm.preview()
|
await wrapper.vm.preview()
|
||||||
await wrapper.vm.open()
|
await wrapper.vm.open()
|
||||||
@@ -979,19 +1098,25 @@ describe('CsvJsonImport.vue - json', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
// Parsing success in the logs
|
// Parsing success in the logs
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).findAll('.msg')[2].text())
|
expect(
|
||||||
.to.equal('Importing JSON into a SQLite database...')
|
wrapper.findComponent({ name: 'logs' }).findAll('.msg')[2].text()
|
||||||
|
).to.equal('Importing JSON into a SQLite database...')
|
||||||
|
|
||||||
// After 1 second - loading indicator is shown
|
// After 1 second - loading indicator is shown
|
||||||
await clock.tick(1000)
|
await clock.tick(1000)
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(true)
|
).to.equal(true)
|
||||||
|
|
||||||
// All the dialog controls are disabled
|
// All the dialog controls are disabled
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
expect(wrapper.vm.db.addTableFromCsv.getCall(0).args[0]).to.equal('foo') // table name
|
expect(wrapper.vm.db.addTableFromCsv.getCall(0).args[0]).to.equal('foo') // table name
|
||||||
@@ -1000,7 +1125,10 @@ describe('CsvJsonImport.vue - json', () => {
|
|||||||
await resolveImport()
|
await resolveImport()
|
||||||
await wrapper.vm.db.addTableFromCsv.returnValues[0]
|
await wrapper.vm.db.addTableFromCsv.returnValues[0]
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(false)
|
).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1021,12 +1149,16 @@ describe('CsvJsonImport.vue - json', () => {
|
|||||||
// Import success in the logs
|
// Import success in the logs
|
||||||
const logs = wrapper.findComponent({ name: 'logs' }).findAll('.msg')
|
const logs = wrapper.findComponent({ name: 'logs' }).findAll('.msg')
|
||||||
expect(logs).to.have.lengthOf(3)
|
expect(logs).to.have.lengthOf(3)
|
||||||
expect(logs[2].text()).to.contain('Importing JSON into a SQLite database is completed in')
|
expect(logs[2].text()).to.contain(
|
||||||
|
'Importing JSON into a SQLite database is completed in'
|
||||||
|
)
|
||||||
|
|
||||||
// All the dialog controls are enabled
|
// All the dialog controls are enabled
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
false
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -1106,16 +1238,23 @@ describe('CsvJsonImport.vue - ndjson', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
|
expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
|
||||||
expect(wrapper.find('.dialog-header').text()).to.equal('JSON import')
|
expect(wrapper.find('.dialog-header').text()).to.equal('JSON import')
|
||||||
expect(wrapper.find('#csv-json-table-name input').element.value).to.equal('my_data')
|
expect(wrapper.find('#csv-json-table-name input').element.value).to.equal(
|
||||||
expect(wrapper.findComponent({ name: 'delimiter-selector' }).exists()).to.equal(false)
|
'my_data'
|
||||||
|
)
|
||||||
|
expect(
|
||||||
|
wrapper.findComponent({ name: 'delimiter-selector' }).exists()
|
||||||
|
).to.equal(false)
|
||||||
expect(wrapper.find('#quote-char input').exists()).to.equal(false)
|
expect(wrapper.find('#quote-char input').exists()).to.equal(false)
|
||||||
expect(wrapper.find('#escape-char input').exists()).to.equal(false)
|
expect(wrapper.find('#escape-char input').exists()).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'check-box' }).exists()).to.equal(false)
|
expect(wrapper.findComponent({ name: 'check-box' }).exists()).to.equal(
|
||||||
|
false
|
||||||
|
)
|
||||||
const rows = wrapper.findAll('tbody tr')
|
const rows = wrapper.findAll('tbody tr')
|
||||||
expect(rows).to.have.lengthOf(1)
|
expect(rows).to.have.lengthOf(1)
|
||||||
expect(rows[0].findAll('td')[0].text()).to.equal('{ "foo": [ 1, 2, 3 ] }')
|
expect(rows[0].findAll('td')[0].text()).to.equal('{ "foo": [ 1, 2, 3 ] }')
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).text())
|
expect(wrapper.findComponent({ name: 'logs' }).text()).to.include(
|
||||||
.to.include('Preview parsing is completed in')
|
'Preview parsing is completed in'
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
})
|
})
|
||||||
@@ -1139,8 +1278,10 @@ describe('CsvJsonImport.vue - ndjson', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
let resolveParsing
|
let resolveParsing
|
||||||
parse.onCall(1).returns(new Promise(resolve => {
|
parse.onCall(1).returns(
|
||||||
resolveParsing = () => resolve({
|
new Promise(resolve => {
|
||||||
|
resolveParsing = () =>
|
||||||
|
resolve({
|
||||||
delimiter: '|',
|
delimiter: '|',
|
||||||
data: {
|
data: {
|
||||||
columns: ['doc'],
|
columns: ['doc'],
|
||||||
@@ -1151,26 +1292,33 @@ describe('CsvJsonImport.vue - ndjson', () => {
|
|||||||
rowCount: 1,
|
rowCount: 1,
|
||||||
messages: []
|
messages: []
|
||||||
})
|
})
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
await wrapper.find('#csv-json-table-name input').setValue('foo')
|
||||||
await wrapper.find('#import-start').trigger('click')
|
await wrapper.find('#import-start').trigger('click')
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
// "Parsing JSON..." in the logs
|
// "Parsing JSON..." in the logs
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).findAll('.msg')[1].text())
|
expect(
|
||||||
.to.equal('Parsing JSON...')
|
wrapper.findComponent({ name: 'logs' }).findAll('.msg')[1].text()
|
||||||
|
).to.equal('Parsing JSON...')
|
||||||
|
|
||||||
// After 1 second - loading indicator is shown
|
// After 1 second - loading indicator is shown
|
||||||
await clock.tick(1000)
|
await clock.tick(1000)
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(true)
|
).to.equal(true)
|
||||||
|
|
||||||
// All the dialog controls are disabled
|
// All the dialog controls are disabled
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
await resolveParsing()
|
await resolveParsing()
|
||||||
@@ -1178,7 +1326,10 @@ describe('CsvJsonImport.vue - ndjson', () => {
|
|||||||
|
|
||||||
// Loading indicator is not shown when parsing is compete
|
// Loading indicator is not shown when parsing is compete
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(false)
|
).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1211,8 +1362,11 @@ describe('CsvJsonImport.vue - ndjson', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let resolveImport = sinon.stub()
|
let resolveImport = sinon.stub()
|
||||||
wrapper.vm.db.addTableFromCsv = sinon.stub()
|
wrapper.vm.db.addTableFromCsv = sinon.stub().resolves(
|
||||||
.resolves(new Promise(resolve => { resolveImport = resolve }))
|
new Promise(resolve => {
|
||||||
|
resolveImport = resolve
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
wrapper.vm.preview()
|
wrapper.vm.preview()
|
||||||
wrapper.vm.open()
|
wrapper.vm.open()
|
||||||
@@ -1225,19 +1379,25 @@ describe('CsvJsonImport.vue - ndjson', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
// Parsing success in the logs
|
// Parsing success in the logs
|
||||||
expect(wrapper.findComponent({ name: 'logs' }).findAll('.msg')[2].text())
|
expect(
|
||||||
.to.equal('Importing JSON into a SQLite database...')
|
wrapper.findComponent({ name: 'logs' }).findAll('.msg')[2].text()
|
||||||
|
).to.equal('Importing JSON into a SQLite database...')
|
||||||
|
|
||||||
// After 1 second - loading indicator is shown
|
// After 1 second - loading indicator is shown
|
||||||
await clock.tick(1000)
|
await clock.tick(1000)
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(true)
|
).to.equal(true)
|
||||||
|
|
||||||
// All the dialog controls are disabled
|
// All the dialog controls are disabled
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(true)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(true)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(true)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
true
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-start').isVisible()).to.equal(true)
|
||||||
expect(wrapper.vm.db.addTableFromCsv.getCall(0).args[0]).to.equal('foo') // table name
|
expect(wrapper.vm.db.addTableFromCsv.getCall(0).args[0]).to.equal('foo') // table name
|
||||||
@@ -1246,7 +1406,10 @@ describe('CsvJsonImport.vue - ndjson', () => {
|
|||||||
await resolveImport()
|
await resolveImport()
|
||||||
await wrapper.vm.db.addTableFromCsv.returnValues[0]
|
await wrapper.vm.db.addTableFromCsv.returnValues[0]
|
||||||
expect(
|
expect(
|
||||||
wrapper.findComponent({ name: 'logs' }).findComponent({ name: 'LoadingIndicator' }).exists()
|
wrapper
|
||||||
|
.findComponent({ name: 'logs' })
|
||||||
|
.findComponent({ name: 'LoadingIndicator' })
|
||||||
|
.exists()
|
||||||
).to.equal(false)
|
).to.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1291,12 +1454,16 @@ describe('CsvJsonImport.vue - ndjson', () => {
|
|||||||
// Import success in the logs
|
// Import success in the logs
|
||||||
const logs = wrapper.findComponent({ name: 'logs' }).findAll('.msg')
|
const logs = wrapper.findComponent({ name: 'logs' }).findAll('.msg')
|
||||||
expect(logs).to.have.lengthOf(3)
|
expect(logs).to.have.lengthOf(3)
|
||||||
expect(logs[2].text()).to.contain('Importing JSON into a SQLite database is completed in')
|
expect(logs[2].text()).to.contain(
|
||||||
|
'Importing JSON into a SQLite database is completed in'
|
||||||
|
)
|
||||||
|
|
||||||
// All the dialog controls are enabled
|
// All the dialog controls are enabled
|
||||||
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-cancel').element.disabled).to.equal(false)
|
||||||
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
expect(wrapper.find('#import-finish').element.disabled).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(false)
|
expect(wrapper.findComponent({ name: 'close-icon' }).vm.disabled).to.equal(
|
||||||
|
false
|
||||||
|
)
|
||||||
expect(wrapper.find('#import-finish').isVisible()).to.equal(true)
|
expect(wrapper.find('#import-finish').isVisible()).to.equal(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ describe('DelimiterSelector', async () => {
|
|||||||
const wrapper = shallowMount(DelimiterSelector, {
|
const wrapper = shallowMount(DelimiterSelector, {
|
||||||
props: {
|
props: {
|
||||||
modelValue: ',',
|
modelValue: ',',
|
||||||
'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e })
|
'onUpdate:modelValue': e => wrapper.setProps({ modelValue: e })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ describe('DelimiterSelector', async () => {
|
|||||||
const wrapper = mount(DelimiterSelector, {
|
const wrapper = mount(DelimiterSelector, {
|
||||||
props: {
|
props: {
|
||||||
modelValue: '|',
|
modelValue: '|',
|
||||||
'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e })
|
'onUpdate:modelValue': e => wrapper.setProps({ modelValue: e })
|
||||||
},
|
},
|
||||||
attachTo: document.body
|
attachTo: document.body
|
||||||
})
|
})
|
||||||
@@ -68,7 +68,7 @@ describe('DelimiterSelector', async () => {
|
|||||||
const wrapper = mount(DelimiterSelector, {
|
const wrapper = mount(DelimiterSelector, {
|
||||||
props: {
|
props: {
|
||||||
modelValue: '|',
|
modelValue: '|',
|
||||||
'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e })
|
'onUpdate:modelValue': e => wrapper.setProps({ modelValue: e })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ describe('DelimiterSelector', async () => {
|
|||||||
props: {
|
props: {
|
||||||
modelValue: '|',
|
modelValue: '|',
|
||||||
disabled: true,
|
disabled: true,
|
||||||
'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e })
|
'onUpdate:modelValue': e => wrapper.setProps({ modelValue: e })
|
||||||
},
|
},
|
||||||
attachTo: document.body
|
attachTo: document.body
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import fu from '@/lib/utils/fileIo'
|
|||||||
import database from '@/lib/database'
|
import database from '@/lib/database'
|
||||||
import { nextTick } from 'vue'
|
import { nextTick } from 'vue'
|
||||||
|
|
||||||
|
|
||||||
describe('DbUploader.vue', () => {
|
describe('DbUploader.vue', () => {
|
||||||
let state = {}
|
let state = {}
|
||||||
let mutations = {}
|
let mutations = {}
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ describe('LoadingIndicator.vue', () => {
|
|||||||
})
|
})
|
||||||
// The lendth of circle in the component is 50.24. If progress is 50% then resulting arc
|
// The lendth of circle in the component is 50.24. If progress is 50% then resulting arc
|
||||||
// should be 25.12
|
// should be 25.12
|
||||||
expect(wrapper.find('.loader-svg.front').element.style.strokeDasharray)
|
expect(
|
||||||
.to.equal('25.12px, 25.12px')
|
wrapper.find('.loader-svg.front').element.style.strokeDasharray
|
||||||
|
).to.equal('25.12px, 25.12px')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -31,8 +31,9 @@ describe('Logs.vue', () => {
|
|||||||
})
|
})
|
||||||
await nextTick()
|
await nextTick()
|
||||||
const height = wrapper.find('.logs-container').element.scrollHeight
|
const height = wrapper.find('.logs-container').element.scrollHeight
|
||||||
expect(wrapper.find('.logs-container').element.scrollTop)
|
expect(wrapper.find('.logs-container').element.scrollTop).to.equal(
|
||||||
.to.equal(height - viewHeight)
|
height - viewHeight
|
||||||
|
)
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -58,8 +59,9 @@ describe('Logs.vue', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
await nextTick()
|
await nextTick()
|
||||||
const height = wrapper.find('.logs-container').element.scrollHeight
|
const height = wrapper.find('.logs-container').element.scrollHeight
|
||||||
expect(wrapper.find('.logs-container').element.scrollTop)
|
expect(wrapper.find('.logs-container').element.scrollTop).to.equal(
|
||||||
.to.equal(height - viewHeight)
|
height - viewHeight
|
||||||
|
)
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,12 @@ describe('Splitpanes.vue', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(wrapper.findAll('.splitpanes-pane')).to.have.lengthOf(2)
|
expect(wrapper.findAll('.splitpanes-pane')).to.have.lengthOf(2)
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('60%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('40%')
|
'60%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'40%'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders correctly - horizontal', () => {
|
it('renders correctly - horizontal', () => {
|
||||||
@@ -37,8 +41,12 @@ describe('Splitpanes.vue', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
expect(wrapper.findAll('.splitpanes-pane')).to.have.lengthOf(2)
|
expect(wrapper.findAll('.splitpanes-pane')).to.have.lengthOf(2)
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.height).to.equal('60%')
|
expect(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.height).to.equal('40%')
|
wrapper.findAll('.splitpanes-pane')[0].element.style.height
|
||||||
|
).to.equal('60%')
|
||||||
|
expect(
|
||||||
|
wrapper.findAll('.splitpanes-pane')[1].element.style.height
|
||||||
|
).to.equal('40%')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('toggles correctly - no maximized initially', async () => {
|
it('toggles correctly - no maximized initially', async () => {
|
||||||
@@ -55,20 +63,36 @@ describe('Splitpanes.vue', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await wrapper.find('.toggle-btn').trigger('click')
|
await wrapper.find('.toggle-btn').trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('0%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('100%')
|
'0%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'100%'
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.find('.toggle-btn').trigger('click')
|
await wrapper.find('.toggle-btn').trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('60%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('40%')
|
'60%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'40%'
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.findAll('.toggle-btn')[1].trigger('click')
|
await wrapper.findAll('.toggle-btn')[1].trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('100%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('0%')
|
'100%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'0%'
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.find('.toggle-btn').trigger('click')
|
await wrapper.find('.toggle-btn').trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('60%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('40%')
|
'60%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'40%'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('toggles correctly - with maximized initially', async () => {
|
it('toggles correctly - with maximized initially', async () => {
|
||||||
@@ -86,20 +110,36 @@ describe('Splitpanes.vue', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await wrapper.find('.toggle-btn').trigger('click')
|
await wrapper.find('.toggle-btn').trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('20%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('80%')
|
'20%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'80%'
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.findAll('.toggle-btn')[0].trigger('click')
|
await wrapper.findAll('.toggle-btn')[0].trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('0%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('100%')
|
'0%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'100%'
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.find('.toggle-btn').trigger('click')
|
await wrapper.find('.toggle-btn').trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('20%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('80%')
|
'20%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'80%'
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.findAll('.toggle-btn')[1].trigger('click')
|
await wrapper.findAll('.toggle-btn')[1].trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('100%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('0%')
|
'100%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'0%'
|
||||||
|
)
|
||||||
|
|
||||||
wrapper = shallowMount(Splitpanes, {
|
wrapper = shallowMount(Splitpanes, {
|
||||||
slots: {
|
slots: {
|
||||||
@@ -113,20 +153,36 @@ describe('Splitpanes.vue', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await wrapper.find('.toggle-btn').trigger('click')
|
await wrapper.find('.toggle-btn').trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('50%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('50%')
|
'50%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'50%'
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.findAll('.toggle-btn')[0].trigger('click')
|
await wrapper.findAll('.toggle-btn')[0].trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('0%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('100%')
|
'0%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'100%'
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.find('.toggle-btn').trigger('click')
|
await wrapper.find('.toggle-btn').trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('50%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('50%')
|
'50%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'50%'
|
||||||
|
)
|
||||||
|
|
||||||
await wrapper.findAll('.toggle-btn')[1].trigger('click')
|
await wrapper.findAll('.toggle-btn')[1].trigger('click')
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('100%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal('0%')
|
'100%'
|
||||||
|
)
|
||||||
|
expect(wrapper.findAll('.splitpanes-pane')[1].element.style.width).to.equal(
|
||||||
|
'0%'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('drag - vertical', async () => {
|
it('drag - vertical', async () => {
|
||||||
@@ -151,13 +207,17 @@ describe('Splitpanes.vue', () => {
|
|||||||
parent.style.height = '500px'
|
parent.style.height = '500px'
|
||||||
|
|
||||||
await wrapper.find('.splitpanes-splitter').trigger('mousedown')
|
await wrapper.find('.splitpanes-splitter').trigger('mousedown')
|
||||||
document.dispatchEvent(new MouseEvent('mousemove', {
|
document.dispatchEvent(
|
||||||
|
new MouseEvent('mousemove', {
|
||||||
clientX: 300,
|
clientX: 300,
|
||||||
clientY: 80
|
clientY: 80
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
document.dispatchEvent(new MouseEvent('mouseup'))
|
document.dispatchEvent(new MouseEvent('mouseup'))
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('50%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
|
'50%'
|
||||||
|
)
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
root.remove()
|
root.remove()
|
||||||
})
|
})
|
||||||
@@ -186,14 +246,18 @@ describe('Splitpanes.vue', () => {
|
|||||||
parent.style.height = '500px'
|
parent.style.height = '500px'
|
||||||
|
|
||||||
await wrapper.find('.splitpanes-splitter').trigger('mousedown')
|
await wrapper.find('.splitpanes-splitter').trigger('mousedown')
|
||||||
document.dispatchEvent(new MouseEvent('mousemove', {
|
document.dispatchEvent(
|
||||||
|
new MouseEvent('mousemove', {
|
||||||
clientX: 10,
|
clientX: 10,
|
||||||
clientY: 250
|
clientY: 250
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
document.dispatchEvent(new MouseEvent('mouseup'))
|
document.dispatchEvent(new MouseEvent('mouseup'))
|
||||||
await nextTick()
|
await nextTick()
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.height).to.equal('50%')
|
expect(
|
||||||
|
wrapper.findAll('.splitpanes-pane')[0].element.style.height
|
||||||
|
).to.equal('50%')
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
root.remove()
|
root.remove()
|
||||||
})
|
})
|
||||||
@@ -225,16 +289,20 @@ describe('Splitpanes.vue', () => {
|
|||||||
await wrapper.find('.splitpanes-splitter').trigger('touchstart')
|
await wrapper.find('.splitpanes-splitter').trigger('touchstart')
|
||||||
const event = new TouchEvent('touchmove')
|
const event = new TouchEvent('touchmove')
|
||||||
Object.defineProperty(event, 'touches', {
|
Object.defineProperty(event, 'touches', {
|
||||||
value: [{
|
value: [
|
||||||
|
{
|
||||||
clientX: 10,
|
clientX: 10,
|
||||||
clientY: 250
|
clientY: 250
|
||||||
}],
|
}
|
||||||
|
],
|
||||||
writable: true
|
writable: true
|
||||||
})
|
})
|
||||||
document.dispatchEvent(event)
|
document.dispatchEvent(event)
|
||||||
document.dispatchEvent(new MouseEvent('touchend'))
|
document.dispatchEvent(new MouseEvent('touchend'))
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.height).to.equal('50%')
|
expect(
|
||||||
|
wrapper.findAll('.splitpanes-pane')[0].element.style.height
|
||||||
|
).to.equal('50%')
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
root.remove()
|
root.remove()
|
||||||
delete window.ontouchstart
|
delete window.ontouchstart
|
||||||
@@ -265,16 +333,20 @@ describe('Splitpanes.vue', () => {
|
|||||||
await wrapper.find('.splitpanes-splitter').trigger('touchstart')
|
await wrapper.find('.splitpanes-splitter').trigger('touchstart')
|
||||||
const event = new TouchEvent('touchmove')
|
const event = new TouchEvent('touchmove')
|
||||||
Object.defineProperty(event, 'touches', {
|
Object.defineProperty(event, 'touches', {
|
||||||
value: [{
|
value: [
|
||||||
|
{
|
||||||
clientX: 300,
|
clientX: 300,
|
||||||
clientY: 80
|
clientY: 80
|
||||||
}],
|
}
|
||||||
|
],
|
||||||
writable: true
|
writable: true
|
||||||
})
|
})
|
||||||
document.dispatchEvent(event)
|
document.dispatchEvent(event)
|
||||||
document.dispatchEvent(new MouseEvent('touchend'))
|
document.dispatchEvent(new MouseEvent('touchend'))
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal('50%')
|
expect(wrapper.findAll('.splitpanes-pane')[0].element.style.width).to.equal(
|
||||||
|
'50%'
|
||||||
|
)
|
||||||
wrapper.unmount()
|
wrapper.unmount()
|
||||||
root.remove()
|
root.remove()
|
||||||
delete window.ontouchstart
|
delete window.ontouchstart
|
||||||
|
|||||||
@@ -38,7 +38,11 @@ describe('splitter.js', () => {
|
|||||||
|
|
||||||
document.body.appendChild(container)
|
document.body.appendChild(container)
|
||||||
|
|
||||||
const dragPercentage = splitter.getCurrentDragPercentage(event, container, isHorisontal)
|
const dragPercentage = splitter.getCurrentDragPercentage(
|
||||||
|
event,
|
||||||
|
container,
|
||||||
|
isHorisontal
|
||||||
|
)
|
||||||
expect(dragPercentage).to.equal(50)
|
expect(dragPercentage).to.equal(50)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -53,7 +57,11 @@ describe('splitter.js', () => {
|
|||||||
|
|
||||||
document.body.appendChild(container)
|
document.body.appendChild(container)
|
||||||
|
|
||||||
const dragPercentage = splitter.getCurrentDragPercentage(event, container, isHorisontal)
|
const dragPercentage = splitter.getCurrentDragPercentage(
|
||||||
|
event,
|
||||||
|
container,
|
||||||
|
isHorisontal
|
||||||
|
)
|
||||||
expect(dragPercentage).to.equal(25)
|
expect(dragPercentage).to.equal(25)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe('Pager.vue', () => {
|
|||||||
const wrapper = mount(Pager, {
|
const wrapper = mount(Pager, {
|
||||||
props: {
|
props: {
|
||||||
pageCount: 5,
|
pageCount: 5,
|
||||||
'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e })
|
'onUpdate:modelValue': e => wrapper.setProps({ modelValue: e })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -97,7 +97,8 @@ describe('chartHelper.js', () => {
|
|||||||
expect(doc.children[0].src).to.includes('plotly-latest.js')
|
expect(doc.children[0].src).to.includes('plotly-latest.js')
|
||||||
expect(doc.children[1].id).to.have.lengthOf(21)
|
expect(doc.children[1].id).to.have.lengthOf(21)
|
||||||
expect(doc.children[2].innerHTML).to.includes(doc.children[1].id)
|
expect(doc.children[2].innerHTML).to.includes(doc.children[1].id)
|
||||||
expect(doc.children[2].innerHTML)
|
expect(doc.children[2].innerHTML).to.includes(
|
||||||
.to.includes('Plotly.newPlot(el, "plotly data", "plotly layout"')
|
'Plotly.newPlot(el, "plotly data", "plotly layout"'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -62,7 +62,9 @@ describe('_sql.js', () => {
|
|||||||
const data = tempDb.export()
|
const data = tempDb.export()
|
||||||
const sql = await Sql.build()
|
const sql = await Sql.build()
|
||||||
sql.open(data)
|
sql.open(data)
|
||||||
expect(() => { sql.exec() }).to.throw('exec: Missing query string')
|
expect(() => {
|
||||||
|
sql.exec()
|
||||||
|
}).to.throw('exec: Missing query string')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('imports', async () => {
|
it('imports', async () => {
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ describe('_statements.js', () => {
|
|||||||
|
|
||||||
it('getInsertStmt', () => {
|
it('getInsertStmt', () => {
|
||||||
const columns = ['id', 'name']
|
const columns = ['id', 'name']
|
||||||
expect(stmts.getInsertStmt('foo', columns))
|
expect(stmts.getInsertStmt('foo', columns)).to.equal(
|
||||||
.to.equal('INSERT INTO "foo" ("id", "name") VALUES (?, ?);')
|
'INSERT INTO "foo" ("id", "name") VALUES (?, ?);'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('getCreateStatement', () => {
|
it('getCreateStatement', () => {
|
||||||
|
|||||||
@@ -71,10 +71,12 @@ describe('database.js', () => {
|
|||||||
type: 'INTEGER'
|
type: 'INTEGER'
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(schema[1].columns).to.eql([{
|
expect(schema[1].columns).to.eql([
|
||||||
|
{
|
||||||
name: 'amount',
|
name: 'amount',
|
||||||
type: 'INTEGER'
|
type: 'INTEGER'
|
||||||
}])
|
}
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('creates empty db with name database', async () => {
|
it('creates empty db with name database', async () => {
|
||||||
@@ -114,7 +116,9 @@ describe('database.js', () => {
|
|||||||
buffer.name = 'foo.sqlite'
|
buffer.name = 'foo.sqlite'
|
||||||
|
|
||||||
await db.loadDb(buffer)
|
await db.loadDb(buffer)
|
||||||
const result = await db.execute('SELECT * from test limit 1; SELECT * from test;')
|
const result = await db.execute(
|
||||||
|
'SELECT * from test limit 1; SELECT * from test;'
|
||||||
|
)
|
||||||
expect(result.values).to.eql({
|
expect(result.values).to.eql({
|
||||||
id: [1, 2],
|
id: [1, 2],
|
||||||
name: ['Harry Potter', 'Draco Malfoy'],
|
name: ['Harry Potter', 'Draco Malfoy'],
|
||||||
@@ -141,7 +145,9 @@ describe('database.js', () => {
|
|||||||
const buffer = new Blob([data])
|
const buffer = new Blob([data])
|
||||||
buffer.name = 'foo.sqlite'
|
buffer.name = 'foo.sqlite'
|
||||||
await db.loadDb(buffer)
|
await db.loadDb(buffer)
|
||||||
await expect(db.execute('SELECT * from foo')).to.be.rejectedWith(/^no such table: foo$/)
|
await expect(db.execute('SELECT * from foo')).to.be.rejectedWith(
|
||||||
|
/^no such table: foo$/
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('adds table from csv', async () => {
|
it('adds table from csv', async () => {
|
||||||
@@ -186,37 +192,44 @@ describe('database.js', () => {
|
|||||||
}
|
}
|
||||||
const progressHandler = sinon.stub()
|
const progressHandler = sinon.stub()
|
||||||
const progressCounterId = db.createProgressCounter(progressHandler)
|
const progressCounterId = db.createProgressCounter(progressHandler)
|
||||||
await expect(db.addTableFromCsv('foo', data, progressCounterId)).to.be.rejected
|
await expect(db.addTableFromCsv('foo', data, progressCounterId)).to.be
|
||||||
|
.rejected
|
||||||
})
|
})
|
||||||
|
|
||||||
it('progressCounters', () => {
|
it('progressCounters', () => {
|
||||||
const firstHandler = sinon.stub()
|
const firstHandler = sinon.stub()
|
||||||
const firstId = db.createProgressCounter(firstHandler)
|
const firstId = db.createProgressCounter(firstHandler)
|
||||||
db.worker.dispatchEvent(new MessageEvent('message', {
|
db.worker.dispatchEvent(
|
||||||
|
new MessageEvent('message', {
|
||||||
data: {
|
data: {
|
||||||
progress: 50,
|
progress: 50,
|
||||||
id: firstId
|
id: firstId
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
expect(firstHandler.calledOnceWith(50)).to.equal(true)
|
expect(firstHandler.calledOnceWith(50)).to.equal(true)
|
||||||
|
|
||||||
const secondHandler = sinon.stub()
|
const secondHandler = sinon.stub()
|
||||||
const secondId = db.createProgressCounter(secondHandler)
|
const secondId = db.createProgressCounter(secondHandler)
|
||||||
db.worker.dispatchEvent(new MessageEvent('message', {
|
db.worker.dispatchEvent(
|
||||||
|
new MessageEvent('message', {
|
||||||
data: {
|
data: {
|
||||||
progress: 70,
|
progress: 70,
|
||||||
id: secondId
|
id: secondId
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
expect(firstId).to.not.equals(secondId)
|
expect(firstId).to.not.equals(secondId)
|
||||||
expect(secondHandler.calledOnceWith(70)).to.equal(true)
|
expect(secondHandler.calledOnceWith(70)).to.equal(true)
|
||||||
|
|
||||||
db.worker.dispatchEvent(new MessageEvent('message', {
|
db.worker.dispatchEvent(
|
||||||
|
new MessageEvent('message', {
|
||||||
data: {
|
data: {
|
||||||
progress: 80,
|
progress: 80,
|
||||||
id: firstId
|
id: firstId
|
||||||
}
|
}
|
||||||
}))
|
})
|
||||||
|
)
|
||||||
expect(firstHandler.calledTwice).to.equal(true)
|
expect(firstHandler.calledTwice).to.equal(true)
|
||||||
expect(firstHandler.secondCall.calledWith(80)).to.equal(true)
|
expect(firstHandler.secondCall.calledWith(80)).to.equal(true)
|
||||||
|
|
||||||
@@ -268,12 +281,17 @@ describe('database.js', () => {
|
|||||||
|
|
||||||
it('validateTableName', async () => {
|
it('validateTableName', async () => {
|
||||||
await db.execute('CREATE TABLE foo(id)')
|
await db.execute('CREATE TABLE foo(id)')
|
||||||
await expect(db.validateTableName('foo')).to.be.rejectedWith('table "foo" already exists')
|
await expect(db.validateTableName('foo')).to.be.rejectedWith(
|
||||||
await expect(db.validateTableName('1foo'))
|
'table "foo" already exists'
|
||||||
.to.be.rejectedWith("Table name can't start with a digit")
|
)
|
||||||
await expect(db.validateTableName('foo(05.08.2020)'))
|
await expect(db.validateTableName('1foo')).to.be.rejectedWith(
|
||||||
.to.be.rejectedWith('Table name can contain only letters, digits and underscores')
|
"Table name can't start with a digit"
|
||||||
await expect(db.validateTableName('sqlite_foo'))
|
)
|
||||||
.to.be.rejectedWith("Table name can't start with sqlite_")
|
await expect(db.validateTableName('foo(05.08.2020)')).to.be.rejectedWith(
|
||||||
|
'Table name can contain only letters, digits and underscores'
|
||||||
|
)
|
||||||
|
await expect(db.validateTableName('sqlite_foo')).to.be.rejectedWith(
|
||||||
|
"Table name can't start with sqlite_"
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ describe('SQLite extensions', function () {
|
|||||||
'sqrt(square(16))': [16],
|
'sqrt(square(16))': [16],
|
||||||
'ceil(-1.95) + ceil(1.95)': [1],
|
'ceil(-1.95) + ceil(1.95)': [1],
|
||||||
'floor(-1.95) + floor(1.95)': [-1]
|
'floor(-1.95) + floor(1.95)': [-1]
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -452,7 +451,15 @@ describe('SQLite extensions', function () {
|
|||||||
FROM dataset;
|
FROM dataset;
|
||||||
`)
|
`)
|
||||||
expect(actual.values).to.eql({
|
expect(actual.values).to.eql({
|
||||||
xx: [1], xy: [0], xz: [1], yx: [0], yy: [1], yz: [1], zx: [1], zy: [1], zz: [1]
|
xx: [1],
|
||||||
|
xy: [0],
|
||||||
|
xz: [1],
|
||||||
|
yx: [0],
|
||||||
|
yy: [1],
|
||||||
|
yz: [1],
|
||||||
|
zx: [1],
|
||||||
|
zy: [1],
|
||||||
|
zz: [1]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -475,7 +482,10 @@ describe('SQLite extensions', function () {
|
|||||||
|
|
||||||
SELECT lua_inline(1), lua_full(1) - 1 < 0.000001;
|
SELECT lua_inline(1), lua_full(1) - 1 < 0.000001;
|
||||||
`)
|
`)
|
||||||
expect(actual.values).to.eql({ 'lua_inline(1)': [2], 'lua_full(1) - 1 < 0.000001': [1] })
|
expect(actual.values).to.eql({
|
||||||
|
'lua_inline(1)': [2],
|
||||||
|
'lua_full(1) - 1 < 0.000001': [1]
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('supports aggregate Lua functions', async function () {
|
it('supports aggregate Lua functions', async function () {
|
||||||
@@ -534,6 +544,9 @@ describe('SQLite extensions', function () {
|
|||||||
|
|
||||||
SELECT * FROM lua_match('%w+', 'hello world from Lua');
|
SELECT * FROM lua_match('%w+', 'hello world from Lua');
|
||||||
`)
|
`)
|
||||||
expect(actual.values).to.eql({ idx: [1, 2, 3, 4], elm: ['hello', 'world', 'from', 'Lua'] })
|
expect(actual.values).to.eql({
|
||||||
|
idx: [1, 2, 3, 4],
|
||||||
|
elm: ['hello', 'world', 'from', 'Lua']
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ describe('storedInquiries.js', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('getStoredInquiries migrate and returns inquiries of v1', () => {
|
it('getStoredInquiries migrate and returns inquiries of v1', () => {
|
||||||
localStorage.setItem('myQueries', JSON.stringify([
|
localStorage.setItem(
|
||||||
|
'myQueries',
|
||||||
|
JSON.stringify([
|
||||||
{
|
{
|
||||||
id: '123',
|
id: '123',
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
@@ -32,7 +34,8 @@ describe('storedInquiries.js', () => {
|
|||||||
query: 'SELECT * FROM bar',
|
query: 'SELECT * FROM bar',
|
||||||
chart: { here_are: 'bar chart settings' }
|
chart: { here_are: 'bar chart settings' }
|
||||||
}
|
}
|
||||||
]))
|
])
|
||||||
|
)
|
||||||
const inquiries = storedInquiries.getStoredInquiries()
|
const inquiries = storedInquiries.getStoredInquiries()
|
||||||
expect(inquiries).to.eql([
|
expect(inquiries).to.eql([
|
||||||
{
|
{
|
||||||
@@ -53,10 +56,7 @@ describe('storedInquiries.js', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('updateStorage and getStoredInquiries', () => {
|
it('updateStorage and getStoredInquiries', () => {
|
||||||
const data = [
|
const data = [{ id: 1 }, { id: 2 }]
|
||||||
{ id: 1 },
|
|
||||||
{ id: 2 }
|
|
||||||
]
|
|
||||||
storedInquiries.updateStorage(data)
|
storedInquiries.updateStorage(data)
|
||||||
const inquiries = storedInquiries.getStoredInquiries()
|
const inquiries = storedInquiries.getStoredInquiries()
|
||||||
expect(inquiries).to.eql(data)
|
expect(inquiries).to.eql(data)
|
||||||
@@ -77,7 +77,9 @@ describe('storedInquiries.js', () => {
|
|||||||
|
|
||||||
const copy = storedInquiries.duplicateInquiry(base)
|
const copy = storedInquiries.duplicateInquiry(base)
|
||||||
expect(copy).to.have.property('id').which.not.equal(base.id)
|
expect(copy).to.have.property('id').which.not.equal(base.id)
|
||||||
expect(copy).to.have.property('name').which.equal(base.name + ' Copy')
|
expect(copy)
|
||||||
|
.to.have.property('name')
|
||||||
|
.which.equal(base.name + ' Copy')
|
||||||
expect(copy).to.have.property('query').which.equal(base.query)
|
expect(copy).to.have.property('query').which.equal(base.query)
|
||||||
expect(copy).to.have.property('viewType').which.equal(base.viewType)
|
expect(copy).to.have.property('viewType').which.equal(base.viewType)
|
||||||
expect(copy).to.have.property('viewOptions').which.eql(base.viewOptions)
|
expect(copy).to.have.property('viewOptions').which.eql(base.viewOptions)
|
||||||
@@ -197,14 +199,16 @@ describe('storedInquiries.js', () => {
|
|||||||
`
|
`
|
||||||
|
|
||||||
const inquiry = storedInquiries.deserialiseInquiries(str)
|
const inquiry = storedInquiries.deserialiseInquiries(str)
|
||||||
expect(inquiry).to.eql([{
|
expect(inquiry).to.eql([
|
||||||
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
query: 'select * from foo',
|
query: 'select * from foo',
|
||||||
viewType: 'chart',
|
viewType: 'chart',
|
||||||
viewOptions: [],
|
viewOptions: [],
|
||||||
createdAt: '2020-11-03T14:17:49.524Z'
|
createdAt: '2020-11-03T14:17:49.524Z'
|
||||||
}])
|
}
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('deserialiseInquiries generates new id to avoid duplication', () => {
|
it('deserialiseInquiries generates new id to avoid duplication', () => {
|
||||||
@@ -256,14 +260,16 @@ describe('storedInquiries.js', () => {
|
|||||||
sinon.stub(fu, 'importFile').returns(Promise.resolve(str))
|
sinon.stub(fu, 'importFile').returns(Promise.resolve(str))
|
||||||
const inquiries = await storedInquiries.importInquiries()
|
const inquiries = await storedInquiries.importInquiries()
|
||||||
|
|
||||||
expect(inquiries).to.eql([{
|
expect(inquiries).to.eql([
|
||||||
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
query: 'select * from foo',
|
query: 'select * from foo',
|
||||||
viewType: 'chart',
|
viewType: 'chart',
|
||||||
viewOptions: [],
|
viewOptions: [],
|
||||||
createdAt: '2020-11-03T14:17:49.524Z'
|
createdAt: '2020-11-03T14:17:49.524Z'
|
||||||
}])
|
}
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('importInquiries', async () => {
|
it('importInquiries', async () => {
|
||||||
@@ -281,14 +287,16 @@ describe('storedInquiries.js', () => {
|
|||||||
sinon.stub(fu, 'importFile').returns(Promise.resolve(str))
|
sinon.stub(fu, 'importFile').returns(Promise.resolve(str))
|
||||||
const inquiries = await storedInquiries.importInquiries()
|
const inquiries = await storedInquiries.importInquiries()
|
||||||
|
|
||||||
expect(inquiries).to.eql([{
|
expect(inquiries).to.eql([
|
||||||
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'foo',
|
name: 'foo',
|
||||||
query: 'select * from foo',
|
query: 'select * from foo',
|
||||||
viewType: 'chart',
|
viewType: 'chart',
|
||||||
viewOptions: [],
|
viewOptions: [],
|
||||||
createdAt: '2020-11-03T14:17:49.524Z'
|
createdAt: '2020-11-03T14:17:49.524Z'
|
||||||
}])
|
}
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('readPredefinedInquiries old', async () => {
|
it('readPredefinedInquiries old', async () => {
|
||||||
@@ -312,7 +320,8 @@ describe('storedInquiries.js', () => {
|
|||||||
viewType: 'chart',
|
viewType: 'chart',
|
||||||
viewOptions: [],
|
viewOptions: [],
|
||||||
createdAt: '2020-11-03T14:17:49.524Z'
|
createdAt: '2020-11-03T14:17:49.524Z'
|
||||||
}])
|
}
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('readPredefinedInquiries', async () => {
|
it('readPredefinedInquiries', async () => {
|
||||||
@@ -340,6 +349,7 @@ describe('storedInquiries.js', () => {
|
|||||||
viewType: 'chart',
|
viewType: 'chart',
|
||||||
viewOptions: [],
|
viewOptions: [],
|
||||||
createdAt: '2020-11-03T14:17:49.524Z'
|
createdAt: '2020-11-03T14:17:49.524Z'
|
||||||
}])
|
}
|
||||||
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user