mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-09-24 21:40:48 +02:00
tests(e2e): improve coverage
This commit is contained in:
@@ -11,10 +11,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.11.0",
|
||||
"@tauri-apps/plugin-autostart": "workspace:*",
|
||||
"@tauri-apps/plugin-barcode-scanner": "workspace:*",
|
||||
"@tauri-apps/plugin-biometric": "workspace:*",
|
||||
"@tauri-apps/plugin-cli": "workspace:*",
|
||||
"@tauri-apps/plugin-clipboard-manager": "workspace:*",
|
||||
"@tauri-apps/plugin-deep-link": "workspace:*",
|
||||
"@tauri-apps/plugin-dialog": "workspace:*",
|
||||
"@tauri-apps/plugin-fs": "workspace:*",
|
||||
"@tauri-apps/plugin-geolocation": "workspace:*",
|
||||
@@ -25,11 +27,15 @@
|
||||
"@tauri-apps/plugin-notification": "workspace:*",
|
||||
"@tauri-apps/plugin-opener": "workspace:*",
|
||||
"@tauri-apps/plugin-os": "workspace:*",
|
||||
"@tauri-apps/plugin-positioner": "workspace:*",
|
||||
"@tauri-apps/plugin-process": "workspace:*",
|
||||
"@tauri-apps/plugin-shell": "workspace:*",
|
||||
"@tauri-apps/plugin-sql": "workspace:*",
|
||||
"@tauri-apps/plugin-store": "workspace:*",
|
||||
"@tauri-apps/plugin-stronghold": "workspace:*",
|
||||
"@tauri-apps/plugin-updater": "workspace:*",
|
||||
"@tauri-apps/plugin-upload": "workspace:*",
|
||||
"@tauri-apps/plugin-websocket": "workspace:*",
|
||||
"@zerodevx/svelte-json-view": "2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -39,12 +39,18 @@ tauri-plugin-opener = { path = "../../../plugins/opener", version = "2.5.5" }
|
||||
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.3.6" }
|
||||
tauri-plugin-store = { path = "../../../plugins/store", version = "2.4.5" }
|
||||
tauri-plugin-upload = { path = "../../../plugins/upload", version = "2.3.0" }
|
||||
tauri-plugin-deep-link = { path = "../../../plugins/deep-link", version = "2.4.10" }
|
||||
tauri-plugin-sql = { path = "../../../plugins/sql", version = "2.4.1", features = [
|
||||
"sqlite",
|
||||
] }
|
||||
tauri-plugin-stronghold = { path = "../../../plugins/stronghold", version = "2.3.2" }
|
||||
tauri-plugin-websocket = { path = "../../../plugins/websocket", version = "2.4.3" }
|
||||
|
||||
# WebDriver automation bridge, used by the plugins e2e suite (packages/api-e2e).
|
||||
# Desktop-only and behind the off-by-default `automation` feature so it never ships in a
|
||||
# regular build.
|
||||
[target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies]
|
||||
tauri-plugin-automation = { version = "0.1.4", optional = true }
|
||||
tauri-plugin-automation = { version = "0.2.0", optional = true }
|
||||
|
||||
[dependencies.tauri]
|
||||
workspace = true
|
||||
@@ -61,8 +67,12 @@ features = [
|
||||
]
|
||||
|
||||
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
||||
tauri-plugin-autostart = { path = "../../../plugins/autostart", version = "2.5.1" }
|
||||
tauri-plugin-cli = { path = "../../../plugins/cli", version = "2.4.1" }
|
||||
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut", version = "2.3.2" }
|
||||
tauri-plugin-positioner = { path = "../../../plugins/positioner", version = "2.3.4", features = [
|
||||
"tray-icon",
|
||||
] }
|
||||
tauri-plugin-updater = { path = "../../../plugins/updater", version = "2.12.0" }
|
||||
tauri-plugin-window-state = { path = "../../../plugins/window-state", version = "2.2.0" }
|
||||
|
||||
|
||||
@@ -132,6 +132,17 @@
|
||||
"identifier": "opener:allow-open-path",
|
||||
"allow": [{ "path": "$APPDATA" }, { "path": "$APPDATA/**" }]
|
||||
},
|
||||
"upload:default"
|
||||
"upload:default",
|
||||
"deep-link:default",
|
||||
"deep-link:allow-register",
|
||||
"deep-link:allow-unregister",
|
||||
"deep-link:allow-is-registered",
|
||||
"sql:default",
|
||||
"sql:allow-execute",
|
||||
"stronghold:default",
|
||||
"stronghold:allow-destroy",
|
||||
"stronghold:allow-remove-secret",
|
||||
"stronghold:allow-remove-store-record",
|
||||
"websocket:default"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
"windows": ["main"],
|
||||
"platforms": ["linux", "macOS", "windows"],
|
||||
"permissions": [
|
||||
"autostart:default",
|
||||
"cli:default",
|
||||
"positioner:default",
|
||||
"updater:default",
|
||||
"global-shortcut:allow-unregister",
|
||||
"global-shortcut:allow-register",
|
||||
|
||||
@@ -9,7 +9,7 @@ mod tray;
|
||||
use serde::Serialize;
|
||||
use tauri::{
|
||||
webview::{PageLoadEvent, WebviewWindowBuilder},
|
||||
App, AppHandle, Emitter, Listener, RunEvent, WebviewUrl,
|
||||
App, AppHandle, Emitter, Listener, Manager, RunEvent, WebviewUrl,
|
||||
};
|
||||
|
||||
#[derive(Clone, Serialize)]
|
||||
@@ -55,11 +55,40 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_store::Builder::default().build())
|
||||
.plugin(tauri_plugin_upload::init())
|
||||
.plugin(tauri_plugin_deep_link::init())
|
||||
.plugin(tauri_plugin_websocket::init())
|
||||
.plugin(
|
||||
tauri_plugin_sql::Builder::new()
|
||||
.add_migrations(
|
||||
"sqlite:api.db",
|
||||
vec![tauri_plugin_sql::Migration {
|
||||
version: 1,
|
||||
description: "create_todos_table",
|
||||
sql: "CREATE TABLE todos (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, done INTEGER NOT NULL DEFAULT 0);",
|
||||
kind: tauri_plugin_sql::MigrationKind::Up,
|
||||
}],
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
.setup(move |app| {
|
||||
// the argon2 salt lives next to the snapshots the frontend creates
|
||||
let local_data_dir = app.path().app_local_data_dir()?;
|
||||
std::fs::create_dir_all(&local_data_dir)?;
|
||||
app.handle().plugin(
|
||||
tauri_plugin_stronghold::Builder::with_argon2(&local_data_dir.join("salt.txt"))
|
||||
.build(),
|
||||
)?;
|
||||
|
||||
#[cfg(desktop)]
|
||||
{
|
||||
// registered before the tray, whose events it tracks
|
||||
app.handle().plugin(tauri_plugin_positioner::init())?;
|
||||
tray::create_tray(app.handle())?;
|
||||
app.handle().plugin(tauri_plugin_cli::init())?;
|
||||
app.handle().plugin(tauri_plugin_autostart::init(
|
||||
tauri_plugin_autostart::MacosLauncher::LaunchAgent,
|
||||
None,
|
||||
))?;
|
||||
app.handle()
|
||||
.plugin(tauri_plugin_global_shortcut::Builder::new().build())?;
|
||||
app.handle()
|
||||
|
||||
@@ -107,6 +107,8 @@ pub fn create_tray<R: Runtime>(app: &tauri::AppHandle<R>) -> tauri::Result<()> {
|
||||
_ => {}
|
||||
})
|
||||
.on_tray_icon_event(|tray, event| {
|
||||
// lets the positioner plugin place windows relative to the tray icon
|
||||
tauri_plugin_positioner::on_tray_event(tray.app_handle(), &event);
|
||||
if let TrayIconEvent::Click {
|
||||
button_state: MouseButtonState::Down,
|
||||
button: MouseButton::Left,
|
||||
|
||||
@@ -33,6 +33,12 @@
|
||||
import Geolocation from './views/Geolocation.svelte'
|
||||
import Haptics from './views/Haptics.svelte'
|
||||
import Nfc from './views/Nfc.svelte'
|
||||
import Autostart from './views/Autostart.svelte'
|
||||
import DeepLink from './views/DeepLink.svelte'
|
||||
import Positioner from './views/Positioner.svelte'
|
||||
import Sql from './views/Sql.svelte'
|
||||
import Stronghold from './views/Stronghold.svelte'
|
||||
import WebSocket from './views/WebSocket.svelte'
|
||||
|
||||
import TitleBar from './lib/TitleBar.svelte'
|
||||
|
||||
@@ -131,6 +137,36 @@
|
||||
component: WebRTC,
|
||||
icon: 'i-ph-broadcast'
|
||||
},
|
||||
{
|
||||
label: 'WebSocket',
|
||||
component: WebSocket,
|
||||
icon: 'i-ph-plugs-connected'
|
||||
},
|
||||
{
|
||||
label: 'SQL',
|
||||
component: Sql,
|
||||
icon: 'i-ph-database'
|
||||
},
|
||||
{
|
||||
label: 'Stronghold',
|
||||
component: Stronghold,
|
||||
icon: 'i-ph-lock-key'
|
||||
},
|
||||
{
|
||||
label: 'Deep link',
|
||||
component: DeepLink,
|
||||
icon: 'i-ph-link'
|
||||
},
|
||||
!isMobile && {
|
||||
label: 'Autostart',
|
||||
component: Autostart,
|
||||
icon: 'i-ph-power'
|
||||
},
|
||||
!isMobile && {
|
||||
label: 'Positioner',
|
||||
component: Positioner,
|
||||
icon: 'i-ph-arrows-out-cardinal'
|
||||
},
|
||||
isMobile && {
|
||||
label: 'Scanner',
|
||||
component: Scanner,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<script>
|
||||
import { enable, disable, isEnabled } from '@tauri-apps/plugin-autostart'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
export let onMessage
|
||||
|
||||
let enabled = false
|
||||
|
||||
async function refresh() {
|
||||
enabled = await isEnabled()
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
;(enabled ? disable() : enable())
|
||||
.then(refresh)
|
||||
.then(() => onMessage(`Autostart ${enabled ? 'enabled' : 'disabled'}`))
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
refresh().catch(onMessage)
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<button class="btn" on:click={toggle}>
|
||||
{enabled ? 'Disable' : 'Enable'} autostart
|
||||
</button>
|
||||
<span>Launching at login is {enabled ? 'enabled' : 'disabled'}</span>
|
||||
</div>
|
||||
@@ -0,0 +1,67 @@
|
||||
<script>
|
||||
import {
|
||||
getCurrent,
|
||||
onOpenUrl,
|
||||
register,
|
||||
unregister,
|
||||
isRegistered
|
||||
} from '@tauri-apps/plugin-deep-link'
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
|
||||
export let onMessage
|
||||
|
||||
let current = null
|
||||
let protocol = 'tauri-api'
|
||||
let unlisten
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
current = await getCurrent()
|
||||
unlisten = await onOpenUrl((urls) => {
|
||||
current = urls
|
||||
onMessage(`Opened with ${urls.join(', ')}`)
|
||||
})
|
||||
} catch (error) {
|
||||
onMessage(error)
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
unlisten?.()
|
||||
})
|
||||
|
||||
function registerProtocol() {
|
||||
register(protocol)
|
||||
.then(() => onMessage(`Registered ${protocol}://`))
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function unregisterProtocol() {
|
||||
unregister(protocol)
|
||||
.then(() => onMessage(`Unregistered ${protocol}://`))
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function checkProtocol() {
|
||||
isRegistered(protocol)
|
||||
.then((registered) =>
|
||||
onMessage(
|
||||
`${protocol}:// is ${registered ? '' : 'not '}handled by this app`
|
||||
)
|
||||
)
|
||||
.catch(onMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<div>
|
||||
Current deep link: <code>{current ? current.join(', ') : 'none'}</code>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<input class="input grow" placeholder="Scheme" bind:value={protocol} />
|
||||
<button class="btn" on:click={registerProtocol}>Register</button>
|
||||
<button class="btn" on:click={unregisterProtocol}>Unregister</button>
|
||||
<button class="btn" on:click={checkProtocol}>Is registered?</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,40 @@
|
||||
<script>
|
||||
import {
|
||||
moveWindow,
|
||||
moveWindowConstrained,
|
||||
Position
|
||||
} from '@tauri-apps/plugin-positioner'
|
||||
|
||||
export let onMessage
|
||||
|
||||
// `Position` is a numeric enum, so it also maps the values back to their names
|
||||
const positions = Object.keys(Position).filter((key) => isNaN(Number(key)))
|
||||
|
||||
let position = 'Center'
|
||||
let constrained = false
|
||||
|
||||
function move() {
|
||||
;(constrained ? moveWindowConstrained : moveWindow)(Position[position])
|
||||
.then(() => onMessage(`Moved the window to ${position}`))
|
||||
.catch(onMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<p>
|
||||
The tray positions only resolve once the tray icon has been clicked, which
|
||||
reports its location to the plugin.
|
||||
</p>
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<select class="input" bind:value={position}>
|
||||
{#each positions as name}
|
||||
<option value={name}>{name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={constrained} />
|
||||
Constrain to the tray icon's monitor
|
||||
</label>
|
||||
<button class="btn" on:click={move}>Move window</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,68 @@
|
||||
<script>
|
||||
import Database from '@tauri-apps/plugin-sql'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
export let onMessage
|
||||
|
||||
// the `todos` table is created by the migration the app registers for this database
|
||||
let db
|
||||
let todos = []
|
||||
let title = ''
|
||||
|
||||
async function refresh() {
|
||||
todos = await db.select('SELECT * FROM todos ORDER BY id')
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
db = await Database.load('sqlite:api.db')
|
||||
await refresh()
|
||||
} catch (error) {
|
||||
onMessage(error)
|
||||
}
|
||||
})
|
||||
|
||||
function add() {
|
||||
db.execute('INSERT INTO todos (title) VALUES ($1)', [title])
|
||||
.then((result) => {
|
||||
onMessage(result)
|
||||
title = ''
|
||||
return refresh()
|
||||
})
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function toggle(todo) {
|
||||
db.execute('UPDATE todos SET done = $1 WHERE id = $2', [
|
||||
todo.done ? 0 : 1,
|
||||
todo.id
|
||||
])
|
||||
.then(refresh)
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function remove(todo) {
|
||||
db.execute('DELETE FROM todos WHERE id = $1', [todo.id])
|
||||
.then(refresh)
|
||||
.catch(onMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<form class="flex flex-row gap-2" on:submit|preventDefault={add}>
|
||||
<input class="input grow" placeholder="New todo" bind:value={title} />
|
||||
<button class="btn" type="submit" disabled={!db || !title}>Add</button>
|
||||
</form>
|
||||
|
||||
{#each todos as todo (todo.id)}
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={todo.done === 1}
|
||||
on:change={() => toggle(todo)}
|
||||
/>
|
||||
<span class="grow" class:line-through={todo.done === 1}>{todo.title}</span>
|
||||
<button class="btn" on:click={() => remove(todo)}>Delete</button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
@@ -0,0 +1,71 @@
|
||||
<script>
|
||||
import { Stronghold } from '@tauri-apps/plugin-stronghold'
|
||||
import { appLocalDataDir, join } from '@tauri-apps/api/path'
|
||||
|
||||
export let onMessage
|
||||
|
||||
const clientName = 'api-example'
|
||||
const storeKey = 'secret'
|
||||
|
||||
let password = 'password'
|
||||
let stronghold
|
||||
let store
|
||||
let record = ''
|
||||
|
||||
async function load() {
|
||||
try {
|
||||
const path = await join(await appLocalDataDir(), 'api.stronghold')
|
||||
stronghold = await Stronghold.load(path, password)
|
||||
let client
|
||||
try {
|
||||
client = await stronghold.loadClient(clientName)
|
||||
} catch {
|
||||
client = await stronghold.createClient(clientName)
|
||||
}
|
||||
store = client.getStore()
|
||||
const value = await store.get(storeKey)
|
||||
record = value ? new TextDecoder().decode(value) : ''
|
||||
onMessage('Stronghold loaded')
|
||||
} catch (error) {
|
||||
stronghold = null
|
||||
onMessage(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function save() {
|
||||
try {
|
||||
await store.insert(storeKey, Array.from(new TextEncoder().encode(record)))
|
||||
await stronghold.save()
|
||||
onMessage('Record saved')
|
||||
} catch (error) {
|
||||
onMessage(error)
|
||||
}
|
||||
}
|
||||
|
||||
async function unload() {
|
||||
await stronghold.unload().catch(onMessage)
|
||||
stronghold = null
|
||||
store = null
|
||||
record = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
{#if stronghold}
|
||||
<div class="flex flex-row gap-2">
|
||||
<input class="input grow" placeholder="Secret" bind:value={record} />
|
||||
<button class="btn" on:click={save}>Save</button>
|
||||
<button class="btn" on:click={unload}>Lock</button>
|
||||
</div>
|
||||
{:else}
|
||||
<form class="flex flex-row gap-2" on:submit|preventDefault={load}>
|
||||
<input
|
||||
class="input grow"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
bind:value={password}
|
||||
/>
|
||||
<button class="btn" type="submit">Unlock</button>
|
||||
</form>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,55 @@
|
||||
<script>
|
||||
import WebSocket from '@tauri-apps/plugin-websocket'
|
||||
import { onDestroy } from 'svelte'
|
||||
|
||||
export let onMessage
|
||||
|
||||
let url = 'wss://echo.websocket.org'
|
||||
let message = ''
|
||||
let ws
|
||||
|
||||
async function connect() {
|
||||
try {
|
||||
ws = await WebSocket.connect(url)
|
||||
ws.addListener((received) => {
|
||||
onMessage(received)
|
||||
if (received.type === 'Close') {
|
||||
ws = null
|
||||
}
|
||||
})
|
||||
onMessage(`Connected to ${url}`)
|
||||
} catch (error) {
|
||||
onMessage(error)
|
||||
}
|
||||
}
|
||||
|
||||
function send() {
|
||||
ws.send(message)
|
||||
.then(() => (message = ''))
|
||||
.catch(onMessage)
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
ws.disconnect().catch(onMessage)
|
||||
ws = null
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
ws?.disconnect().catch(() => {})
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
{#if ws}
|
||||
<form class="flex flex-row gap-2" on:submit|preventDefault={send}>
|
||||
<input class="input grow" placeholder="Message" bind:value={message} />
|
||||
<button class="btn" type="submit">Send</button>
|
||||
<button class="btn" type="button" on:click={disconnect}>Disconnect</button>
|
||||
</form>
|
||||
{:else}
|
||||
<form class="flex flex-row gap-2" on:submit|preventDefault={connect}>
|
||||
<input class="input grow" placeholder="ws:// or wss:// URL" bind:value={url} />
|
||||
<button class="btn" type="submit">Connect</button>
|
||||
</form>
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user