mirror of
https://github.com/lana-k/sqliteviz.git
synced 2025-12-07 18:48:55 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a9f4b3c0a | ||
|
|
77468d34ae | ||
|
|
a0577ec0ce | ||
|
|
e7d1398546 | ||
|
|
aa52048d51 | ||
|
|
33913f8f5c | ||
|
|
51eb7a543c | ||
|
|
a3fb38b23c | ||
|
|
3bb40b4eb7 | ||
|
|
6864bf84f8 | ||
|
|
9f1b3823f6 | ||
|
|
7574f529c3 | ||
|
|
653f8eff7b |
17
.github/workflows/config.grenrc.js
vendored
Normal file
17
.github/workflows/config.grenrc.js
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
module.exports = {
|
||||||
|
dataSource: 'milestones',
|
||||||
|
ignoreIssuesWith: [
|
||||||
|
'wontfix',
|
||||||
|
'duplicate'
|
||||||
|
],
|
||||||
|
milestoneMatch: 'v{{tag_name}}',
|
||||||
|
template: {
|
||||||
|
issue: '- {{name}} [{{text}}]({{url}})',
|
||||||
|
changelogTitle: "## Release notes\n\n",
|
||||||
|
release: "{{body}}",
|
||||||
|
},
|
||||||
|
groupBy: {
|
||||||
|
'Enhancements:': ["enhancement", "internal"],
|
||||||
|
'Bug fixes:': ["bug"]
|
||||||
|
}
|
||||||
|
}
|
||||||
8
.github/workflows/main.yml
vendored
8
.github/workflows/main.yml
vendored
@@ -25,11 +25,19 @@ jobs:
|
|||||||
cd dist
|
cd dist
|
||||||
zip -9 -r dist.zip . -x "js/*.map"
|
zip -9 -r dist.zip . -x "js/*.map"
|
||||||
|
|
||||||
|
- name: Create Release Notes
|
||||||
|
run: |
|
||||||
|
npm install github-release-notes@0.16.0 -g
|
||||||
|
gren changelog --generate --config="/.github/workflows/config.grenrc.js"
|
||||||
|
env:
|
||||||
|
GREN_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Create release
|
- name: Create release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
artifacts: "dist/dist.zip"
|
artifacts: "dist/dist.zip"
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
bodyFile: "CHANGELOG.md"
|
||||||
|
|
||||||
- name: Deploy 🚀
|
- name: Deploy 🚀
|
||||||
uses: JamesIves/github-pages-deploy-action@4.1.1
|
uses: JamesIves/github-pages-deploy-action@4.1.1
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ With sqliteviz you can:
|
|||||||
- export a modified SQLite database
|
- export a modified SQLite database
|
||||||
- use it offline from your OS application menu like any other desktop app
|
- use it offline from your OS application menu like any other desktop app
|
||||||
|
|
||||||
|
https://user-images.githubusercontent.com/24638357/117355518-fa332680-aeb2-11eb-8a69-fbcea4f7aeb0.mp4
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
The latest release of sqliteviz is deployed on GitHub Pages at [lana-k.github.io/sqliteviz][6].
|
The latest release of sqliteviz is deployed on GitHub Pages at [lana-k.github.io/sqliteviz][6].
|
||||||
|
|
||||||
@@ -36,4 +38,4 @@ It is built on top of [react-chart-editor][3], [sql.js][4] and [Vue-Codemirror][
|
|||||||
[8]: https://github.com/surmon-china/vue-codemirror#readme
|
[8]: https://github.com/surmon-china/vue-codemirror#readme
|
||||||
[9]: https://www.papaparse.com/
|
[9]: https://www.papaparse.com/
|
||||||
[10]: https://github.com/lana-k/sqliteviz/wiki/Predefined-queries
|
[10]: https://github.com/lana-k/sqliteviz/wiki/Predefined-queries
|
||||||
[11]: https://github.com/plotly/plotly.js
|
[11]: https://github.com/plotly/plotly.js
|
||||||
|
|||||||
@@ -26,7 +26,8 @@
|
|||||||
ref="fileImg"
|
ref="fileImg"
|
||||||
:class="{
|
:class="{
|
||||||
'swing': state === 'dragover',
|
'swing': state === 'dragover',
|
||||||
'fly': state === 'drop'
|
'fly': state === 'dropping',
|
||||||
|
'hidden': state === 'dropped'
|
||||||
}"
|
}"
|
||||||
:src="require('@/assets/images/file.png')"
|
:src="require('@/assets/images/file.png')"
|
||||||
/>
|
/>
|
||||||
@@ -127,7 +128,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import fu from '@/lib/utils/fileIo'
|
import fIo from '@/lib/utils/fileIo'
|
||||||
import csv from './csv'
|
import csv from './csv'
|
||||||
import CloseIcon from '@/components/svg/close'
|
import CloseIcon from '@/components/svg/close'
|
||||||
import TextField from '@/components/TextField'
|
import TextField from '@/components/TextField'
|
||||||
@@ -139,15 +140,6 @@ import ChangeDbIcon from '@/components/svg/changeDb'
|
|||||||
import time from '@/lib/utils/time'
|
import time from '@/lib/utils/time'
|
||||||
import database from '@/lib/database'
|
import database from '@/lib/database'
|
||||||
|
|
||||||
const csvMimeTypes = [
|
|
||||||
'text/csv',
|
|
||||||
'text/x-csv',
|
|
||||||
'application/x-csv',
|
|
||||||
'application/csv',
|
|
||||||
'text/x-comma-separated-values',
|
|
||||||
'text/comma-separated-values'
|
|
||||||
]
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DbUploader',
|
name: 'DbUploader',
|
||||||
props: {
|
props: {
|
||||||
@@ -196,6 +188,7 @@ export default {
|
|||||||
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'
|
||||||
resolve()
|
resolve()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -387,8 +380,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async checkFile (file) {
|
async checkFile (file) {
|
||||||
this.state = 'drop'
|
this.state = 'dropping'
|
||||||
if (csvMimeTypes.includes(file.type)) {
|
if (fIo.isDatabase(file)) {
|
||||||
|
this.loadDb(file)
|
||||||
|
} else {
|
||||||
this.file = file
|
this.file = file
|
||||||
this.header = true
|
this.header = true
|
||||||
this.quoteChar = '"'
|
this.quoteChar = '"'
|
||||||
@@ -398,12 +393,10 @@ export default {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
this.$modal.show('parse')
|
this.$modal.show('parse')
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
this.loadDb(file)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
browse () {
|
browse () {
|
||||||
fu.getFileFromUser('.db,.sqlite,.sqlite3,.csv')
|
fIo.getFileFromUser('.db,.sqlite,.sqlite3,.csv')
|
||||||
.then(this.checkFile)
|
.then(this.checkFile)
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -511,13 +504,19 @@ export default {
|
|||||||
#file-img.fly {
|
#file-img.fly {
|
||||||
animation: fly ease-in-out 1s 1 normal;
|
animation: fly ease-in-out 1s 1 normal;
|
||||||
transform-origin: center center;
|
transform-origin: center center;
|
||||||
top: 183px;
|
|
||||||
left: 225px;
|
|
||||||
transition: top 1s ease-in-out, left 1s ease-in-out;
|
|
||||||
}
|
}
|
||||||
@keyframes fly {
|
@keyframes fly {
|
||||||
100% { transform: rotate(360deg) scale(0.5); }
|
100% {
|
||||||
|
transform: rotate(360deg) scale(0.5);
|
||||||
|
top: 183px;
|
||||||
|
left: 225px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#file-img.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Parse CSV dialog */
|
/* Parse CSV dialog */
|
||||||
.chars {
|
.chars {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<svg :class="animationClass" height="20" width="20" viewBox="0 0 20 20">
|
<svg :class="animationClass" :height="size" :width="size" :viewBox="`0 0 ${size} ${size}`">
|
||||||
<circle
|
<circle
|
||||||
class="loader-svg bg"
|
class="loader-svg bg"
|
||||||
cx="10"
|
:style="{ strokeWidth }"
|
||||||
cy="10"
|
:cx="size / 2"
|
||||||
r="8"
|
:cy="size / 2"
|
||||||
|
:r="radius"
|
||||||
/>
|
/>
|
||||||
<circle
|
<circle
|
||||||
class="loader-svg front"
|
class="loader-svg front"
|
||||||
:style="{ strokeDasharray: circleProgress }"
|
:style="{ strokeDasharray: circleProgress, strokeDashoffset: offset, strokeWidth }"
|
||||||
cx="10"
|
:cx="size / 2"
|
||||||
cy="10"
|
:cy="size / 2"
|
||||||
r="8"
|
:r="radius"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
@@ -19,15 +20,35 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'LoadingIndicator',
|
name: 'LoadingIndicator',
|
||||||
props: ['progress'],
|
props: {
|
||||||
|
progress: {
|
||||||
|
type: Number,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 20
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
circleProgress () {
|
circleProgress () {
|
||||||
const dash = (50.24 * this.progress) / 100
|
const circle = this.radius * 3.14 * 2
|
||||||
const space = 50.24 - dash
|
const dash = this.progress ? (circle * this.progress) / 100 : circle * 1 / 3
|
||||||
|
const space = circle - dash
|
||||||
return `${dash}px, ${space}px`
|
return `${dash}px, ${space}px`
|
||||||
},
|
},
|
||||||
animationClass () {
|
animationClass () {
|
||||||
return this.progress === undefined ? 'loading' : 'progress'
|
return this.progress === undefined ? 'loading' : 'progress'
|
||||||
|
},
|
||||||
|
radius () {
|
||||||
|
return this.size / 2 - this.strokeWidth
|
||||||
|
},
|
||||||
|
offset () {
|
||||||
|
return this.radius * 3.14 / 2
|
||||||
|
},
|
||||||
|
strokeWidth () {
|
||||||
|
return this.size / 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,7 +59,6 @@ export default {
|
|||||||
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: 2px;
|
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
stroke: var(--color-accent);
|
stroke: var(--color-accent);
|
||||||
}
|
}
|
||||||
@@ -48,27 +68,30 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.loading .loader-svg.front {
|
.loading .loader-svg.front {
|
||||||
stroke-dasharray: 40.24px;
|
will-change: transform;
|
||||||
animation: fill-animation-loading 1s cubic-bezier(1,1,1,1) 0s infinite;
|
animation: fill-animation-loading 1s cubic-bezier(1,1,1,1) 0s infinite;
|
||||||
|
transform-origin: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
We can't change anything in loading animation except transform, opacity and filter. Because in
|
||||||
|
our case the Main Thread can be busy and animation will be frozen (e. g. getting a result set
|
||||||
|
from the web-worker after query execution).
|
||||||
|
But transform, opacity and filter trigger changes only in the Composite Layer stage in rendering
|
||||||
|
waterfall. Hence they can be processed only with Compositor Thread while the Main Thread
|
||||||
|
processes something else.
|
||||||
|
https://www.viget.com/articles/animation-performance-101-browser-under-the-hood/
|
||||||
|
*/
|
||||||
@keyframes fill-animation-loading {
|
@keyframes fill-animation-loading {
|
||||||
0% {
|
0% {
|
||||||
stroke-dasharray: 10px 40.24px;
|
transform: rotate(0deg);
|
||||||
stroke-dashoffset: 0;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
stroke-dasharray: 25.12px;
|
|
||||||
stroke-dashoffset: 25.12px;
|
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
stroke-dasharray: 10px 40.24px;
|
transform: rotate(360deg);
|
||||||
stroke-dashoffset: 50.24px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress .loader-svg.front {
|
.progress .loader-svg.front {
|
||||||
stroke-dashoffset: 12.56;
|
|
||||||
transition: stroke-dasharray 0.2s;
|
transition: stroke-dasharray 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ export default {
|
|||||||
border: 1px solid var(--color-border-light);
|
border: 1px solid var(--color-border-light);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
color: var(--color-text-base);
|
||||||
}
|
}
|
||||||
.msg {
|
.msg {
|
||||||
padding: 16px 7px;
|
padding: 16px 7px;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
<div class="table-footer-count">
|
<div class="table-footer-count">
|
||||||
{{ dataSet.values.length}} {{dataSet.values.length === 1 ? 'row' : 'rows'}} retrieved
|
{{ dataSet.values.length}} {{dataSet.values.length === 1 ? 'row' : 'rows'}} retrieved
|
||||||
<span v-if="preview">for preview</span>
|
<span v-if="preview">for preview</span>
|
||||||
|
<span v-if="time">in {{ time }}</span>
|
||||||
</div>
|
</div>
|
||||||
<pager v-show="pageCount > 1" :page-count="pageCount" v-model="currentPage" />
|
<pager v-show="pageCount > 1" :page-count="pageCount" v-model="currentPage" />
|
||||||
</div>
|
</div>
|
||||||
@@ -53,7 +54,7 @@ import Pager from './Pager'
|
|||||||
export default {
|
export default {
|
||||||
name: 'SqlTable',
|
name: 'SqlTable',
|
||||||
components: { Pager },
|
components: { Pager },
|
||||||
props: ['dataSet', 'height', 'preview'],
|
props: ['dataSet', 'time', 'height', 'preview'],
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
header: null,
|
header: null,
|
||||||
|
|||||||
@@ -127,10 +127,10 @@ function getAst (sql) {
|
|||||||
// It throws an error if tokenizer has an arguments:
|
// It throws an error if tokenizer has an arguments:
|
||||||
// https://github.com/codeschool/sqlite-parser/issues/59
|
// https://github.com/codeschool/sqlite-parser/issues/59
|
||||||
const fixedSql = sql
|
const fixedSql = sql
|
||||||
.replace(/(?<=tokenize=.+)"tokenchars=.+"/, '')
|
.replace(/(tokenize=[^,]+)"tokenchars=.+?"/, '$1')
|
||||||
.replace(/(?<=tokenize=.+)"remove_diacritics=.+"/, '')
|
.replace(/(tokenize=[^,]+)"remove_diacritics=.+?"/, '$1')
|
||||||
.replace(/(?<=tokenize=.+)"separators=.+"/, '')
|
.replace(/(tokenize=[^,]+)"separators=.+?"/, '$1')
|
||||||
.replace(/tokenize=.+(?=(,|\)))/, 'tokenize=unicode61')
|
.replace(/tokenize=.+?(,|\))/, 'tokenize=unicode61$1')
|
||||||
|
|
||||||
return sqliteParser(fixedSql)
|
return sqliteParser(fixedSql)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
export default {
|
export default {
|
||||||
|
isDatabase (file) {
|
||||||
|
const dbTypes = ['application/vnd.sqlite3', 'application/x-sqlite3']
|
||||||
|
return file.type
|
||||||
|
? dbTypes.includes(file.type)
|
||||||
|
: /\.(db|sqlite(3)?)+$/.test(file.name)
|
||||||
|
},
|
||||||
|
|
||||||
exportToFile (str, fileName, type = 'octet/stream') {
|
exportToFile (str, fileName, type = 'octet/stream') {
|
||||||
// Create downloader
|
// Create downloader
|
||||||
const downloader = document.createElement('a')
|
const downloader = document.createElement('a')
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export default {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100px;
|
height: 100px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-image: linear-gradient(white 73%, transparent);;
|
background-image: linear-gradient(white 73%, rgba(255, 255, 255, 0));
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
.schema, .db-name {
|
.schema, .db-name {
|
||||||
|
|||||||
@@ -21,7 +21,8 @@
|
|||||||
>
|
>
|
||||||
Run your query and get results here
|
Run your query and get results here
|
||||||
</div>
|
</div>
|
||||||
<div v-show="isGettingResults" class="table-preview result-in-progress">
|
<div v-if="isGettingResults" class="table-preview result-in-progress">
|
||||||
|
<loading-indicator :size="30"/>
|
||||||
Fetching results...
|
Fetching results...
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@@ -30,10 +31,8 @@
|
|||||||
>
|
>
|
||||||
No rows retrieved according to your query
|
No rows retrieved according to your query
|
||||||
</div>
|
</div>
|
||||||
<div v-show="error" class="table-preview error">
|
<logs v-if="error" :messages="[error]"/>
|
||||||
{{ error }}
|
<sql-table v-if="result" :data-set="result" :time="time" :height="tableViewHeight" />
|
||||||
</div>
|
|
||||||
<sql-table v-if="result" :data-set="result" :height="tableViewHeight" />
|
|
||||||
</div>
|
</div>
|
||||||
<chart
|
<chart
|
||||||
:visible="view === 'chart'"
|
:visible="view === 'chart'"
|
||||||
@@ -51,9 +50,12 @@
|
|||||||
<script>
|
<script>
|
||||||
import SqlTable from '@/components/SqlTable'
|
import SqlTable from '@/components/SqlTable'
|
||||||
import Splitpanes from '@/components/Splitpanes'
|
import Splitpanes from '@/components/Splitpanes'
|
||||||
|
import LoadingIndicator from '@/components/LoadingIndicator'
|
||||||
import SqlEditor from './SqlEditor'
|
import SqlEditor from './SqlEditor'
|
||||||
import ViewSwitcher from './ViewSwitcher'
|
import ViewSwitcher from './ViewSwitcher'
|
||||||
import Chart from './Chart'
|
import Chart from './Chart'
|
||||||
|
import Logs from '@/components/Logs'
|
||||||
|
import time from '@/lib/utils/time'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Tab',
|
name: 'Tab',
|
||||||
@@ -63,7 +65,9 @@ export default {
|
|||||||
SqlTable,
|
SqlTable,
|
||||||
Splitpanes,
|
Splitpanes,
|
||||||
ViewSwitcher,
|
ViewSwitcher,
|
||||||
Chart
|
Chart,
|
||||||
|
LoadingIndicator,
|
||||||
|
Logs
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@@ -73,7 +77,8 @@ export default {
|
|||||||
tableViewHeight: 0,
|
tableViewHeight: 0,
|
||||||
isGettingResults: false,
|
isGettingResults: false,
|
||||||
error: null,
|
error: null,
|
||||||
resizeObserver: null
|
resizeObserver: null,
|
||||||
|
time: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -110,11 +115,16 @@ export default {
|
|||||||
this.error = null
|
this.error = null
|
||||||
const state = this.$store.state
|
const state = this.$store.state
|
||||||
try {
|
try {
|
||||||
|
const start = new Date()
|
||||||
this.result = await state.db.execute(this.query + ';')
|
this.result = await state.db.execute(this.query + ';')
|
||||||
|
this.time = time.getPeriod(start, new Date())
|
||||||
const schema = await state.db.getSchema(state.dbName)
|
const schema = await state.db.getSchema(state.dbName)
|
||||||
this.$store.commit('saveSchema', schema)
|
this.$store.commit('saveSchema', schema)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.error = err
|
this.error = {
|
||||||
|
type: 'error',
|
||||||
|
message: err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.isGettingResults = false
|
this.isGettingResults = false
|
||||||
},
|
},
|
||||||
@@ -183,11 +193,32 @@ export default {
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-preview.error {
|
.result-in-progress {
|
||||||
color: var(--color-text-error);
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
will-change: opacity;
|
||||||
|
/*
|
||||||
|
We need to show loader in 1 sec after starting query execution. We can't do that with
|
||||||
|
setTimeout because the main thread can be busy by getting a result set from the web worker.
|
||||||
|
But we can use CSS animation for opacity. Opacity triggers changes only in the Composite Layer
|
||||||
|
stage in rendering waterfall. Hence it can be processed only with Compositor Thread while
|
||||||
|
the Main Thread processes a result set.
|
||||||
|
https://www.viget.com/articles/animation-performance-101-browser-under-the-hood/
|
||||||
|
*/
|
||||||
|
animation: show-loader 1s linear 0s 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-preview.error::first-letter {
|
@keyframes show-loader {
|
||||||
text-transform: capitalize;
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
99% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<router-link to="/editor">Editor</router-link>
|
<router-link to="/editor">Editor</router-link>
|
||||||
<router-link to="/my-queries">My queries</router-link>
|
<router-link to="/my-queries">My queries</router-link>
|
||||||
|
<a href="https://github.com/lana-k/sqliteviz/wiki" target="_blank">Help</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ describe('DbUploader.vue', () => {
|
|||||||
|
|
||||||
it('loads db on click and redirects to /editor', async () => {
|
it('loads db on click and redirects to /editor', async () => {
|
||||||
// mock getting a file from user
|
// mock getting a file from user
|
||||||
const file = {}
|
const file = { name: 'test.db' }
|
||||||
sinon.stub(fu, 'getFileFromUser').resolves(file)
|
sinon.stub(fu, 'getFileFromUser').resolves(file)
|
||||||
|
|
||||||
// mock db loading
|
// mock db loading
|
||||||
@@ -90,7 +90,7 @@ describe('DbUploader.vue', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// mock a file dropped by a user
|
// mock a file dropped by a user
|
||||||
const file = {}
|
const file = { name: 'test.db' }
|
||||||
const dropData = { dataTransfer: new DataTransfer() }
|
const dropData = { dataTransfer: new DataTransfer() }
|
||||||
Object.defineProperty(dropData.dataTransfer, 'files', {
|
Object.defineProperty(dropData.dataTransfer, 'files', {
|
||||||
value: [file],
|
value: [file],
|
||||||
@@ -109,7 +109,7 @@ describe('DbUploader.vue', () => {
|
|||||||
|
|
||||||
it("doesn't redirect if already on /editor", async () => {
|
it("doesn't redirect if already on /editor", async () => {
|
||||||
// mock getting a file from user
|
// mock getting a file from user
|
||||||
const file = {}
|
const file = { name: 'test.db' }
|
||||||
sinon.stub(fu, 'getFileFromUser').resolves(file)
|
sinon.stub(fu, 'getFileFromUser').resolves(file)
|
||||||
|
|
||||||
// mock db loading
|
// mock db loading
|
||||||
|
|||||||
@@ -105,4 +105,27 @@ describe('fileIo.js', () => {
|
|||||||
const blob = new Blob(['foo'])
|
const blob = new Blob(['foo'])
|
||||||
await expect(fu.readAsArrayBuffer(blob)).to.be.rejectedWith('Problem parsing input file.')
|
await expect(fu.readAsArrayBuffer(blob)).to.be.rejectedWith('Problem parsing input file.')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('isDatabase', () => {
|
||||||
|
let file = { type: 'application/vnd.sqlite3' }
|
||||||
|
expect(fu.isDatabase(file)).to.equal(true)
|
||||||
|
|
||||||
|
file = { type: 'application/x-sqlite3' }
|
||||||
|
expect(fu.isDatabase(file)).to.equal(true)
|
||||||
|
|
||||||
|
file = { type: '', name: 'test.db' }
|
||||||
|
expect(fu.isDatabase(file)).to.equal(true)
|
||||||
|
|
||||||
|
file = { type: '', name: 'test.sqlite' }
|
||||||
|
expect(fu.isDatabase(file)).to.equal(true)
|
||||||
|
|
||||||
|
file = { type: '', name: 'test.sqlite3' }
|
||||||
|
expect(fu.isDatabase(file)).to.equal(true)
|
||||||
|
|
||||||
|
file = { type: '', name: 'test.csv' }
|
||||||
|
expect(fu.isDatabase(file)).to.equal(false)
|
||||||
|
|
||||||
|
file = { type: 'text', name: 'test.db' }
|
||||||
|
expect(fu.isDatabase(file)).to.equal(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -202,9 +202,9 @@ describe('Tab.vue', () => {
|
|||||||
|
|
||||||
await wrapper.vm.execute()
|
await wrapper.vm.execute()
|
||||||
expect(wrapper.find('.table-view .result-before').isVisible()).to.equal(false)
|
expect(wrapper.find('.table-view .result-before').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('.table-view .result-in-progress').isVisible()).to.equal(false)
|
expect(wrapper.find('.table-view .result-in-progress').exists()).to.equal(false)
|
||||||
expect(wrapper.find('.table-preview.error').isVisible()).to.equal(true)
|
expect(wrapper.findComponent({ name: 'logs' }).isVisible()).to.equal(true)
|
||||||
expect(wrapper.find('.table-preview.error').text()).to.include('There is no table foo')
|
expect(wrapper.findComponent({ name: 'logs' }).text()).to.include('There is no table foo')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Passes result to sql-table component', async () => {
|
it('Passes result to sql-table component', async () => {
|
||||||
@@ -242,8 +242,8 @@ describe('Tab.vue', () => {
|
|||||||
|
|
||||||
await wrapper.vm.execute()
|
await wrapper.vm.execute()
|
||||||
expect(wrapper.find('.table-view .result-before').isVisible()).to.equal(false)
|
expect(wrapper.find('.table-view .result-before').isVisible()).to.equal(false)
|
||||||
expect(wrapper.find('.table-view .result-in-progress').isVisible()).to.equal(false)
|
expect(wrapper.find('.table-view .result-in-progress').exists()).to.equal(false)
|
||||||
expect(wrapper.find('.table-preview.error').isVisible()).to.equal(false)
|
expect(wrapper.findComponent({ name: 'logs' }).exists()).to.equal(false)
|
||||||
expect(wrapper.findComponent({ name: 'SqlTable' }).vm.dataSet).to.eql(result)
|
expect(wrapper.findComponent({ name: 'SqlTable' }).vm.dataSet).to.eql(result)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user