feat: add better testing

This commit is contained in:
Samuel 2024-12-26 12:11:37 +01:00
parent e43dd2c336
commit b59f40830b
18 changed files with 1441 additions and 197 deletions

29
test/.gitignore vendored Normal file
View file

@ -0,0 +1,29 @@
dist
.solid
.output
.vercel
.netlify
.vinxi
app.config.timestamp_*.js
# Environment
.env
.env*.local
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
*.launch
.settings/
# Temp
gitignore
# System Files
.DS_Store
Thumbs.db

View file

@ -1,15 +1,36 @@
# test
## Usage
To install dependencies:
Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.
This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template.
```bash
bun install
$ npm install # or pnpm install or yarn install
```
To run:
### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
```bash
bun run index.js
```
## Available Scripts
This project was created using `bun init` in bun v1.1.20. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
In the project directory, you can run:
### `npm run dev` or `npm start`
Runs the app in the development mode.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br>
### `npm run build`
Builds the app for production to the `dist` folder.<br>
It correctly bundles Solid in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.<br>
Your app is ready to be deployed!
## Deployment
You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
## This project was created with the [Solid CLI](https://solid-cli.netlify.app)

21
test/dist/index.html vendored
View file

@ -1,21 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<!-- <link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" /> -->
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
<form id="test-form">
<input type="password" id="passphrase-input" />
<input type="file" id="backup-input" />
</form>
</div>
<script src="./index.js" type="module"></script>
</body>
</html>

100
test/dist/index.js vendored
View file

@ -1,100 +0,0 @@
import init, { BackupDecryptor } from "../pkg/signal_decrypt_backup_wasm.js";
let isInitialized = false;
async function initialize() {
if (!isInitialized) {
await init().catch((err) =>
console.error("Failed to initialize WASM module: ", err),
);
isInitialized = true;
}
}
export async function decryptBackup(file, passphrase, progressCallback) {
await initialize();
const fileSize = file.size;
console.log("Starting decryption of file size:", fileSize);
const decryptor = new BackupDecryptor();
decryptor.set_progress_callback(fileSize, (percentage) =>
console.info(`${percentage}% done`),
);
const chunkSize = 1024 * 1024 * 40; // 40MB chunks
let offset = 0;
try {
while (offset < file.size) {
// console.log(`Processing chunk at offset ${offset}`);
const chunk = file.slice(offset, offset + chunkSize);
const arrayBuffer = await chunk.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
decryptor.feed_data(uint8Array);
let done = false;
while (!done) {
try {
done = await decryptor.process_chunk(passphrase, progressCallback);
} catch (e) {
console.error("Error processing chunk:", e);
throw e;
}
}
offset += chunkSize;
// console.log(`Completed chunk, new offset: ${offset}`);
// if (performance.memory) {
// const memoryInfo = performance.memory;
// console.log(`Total JS Heap Size: ${memoryInfo.totalJSHeapSize} bytes`);
// console.log(`Used JS Heap Size: ${memoryInfo.usedJSHeapSize} bytes`);
// console.log(`JS Heap Size Limit: ${memoryInfo.jsHeapSizeLimit} bytes`);
// } else {
// console.log("Memory information is not available in this environment.");
// }
}
// console.log("All chunks processed, finishing up");
const result = decryptor.finish();
decryptor.free();
return result;
} catch (e) {
console.error("Decryption failed:", e);
throw e;
}
}
async function decrypt(file, passphrase) {
try {
const result = await decryptBackup(file, passphrase);
console.log(result, result.database_bytes);
// console.log("Database bytes length:", result.databaseBytes.length);
console.log(
"Database bytes as string (partly)",
new TextDecoder().decode(result.database_bytes.slice(0, 1024 * 50)),
);
// console.log("Preferences:", result.preferences);
// console.log("Key values:", result.keyValues);
} catch (error) {
console.error("Decryption failed:", error);
}
}
/**
* @type {HTMLInputElement}
*/
const passphraseInput = document.querySelector("#passphrase-input");
/**
* @type {HTMLInputElement}
*/
const backupInput = document.querySelector("#backup-input");
backupInput.addEventListener("change", (event) => {
const file = event.currentTarget.files[0];
decrypt(file, passphraseInput.value);
});

16
test/index.html Normal file
View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" />
<title>Solid App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="/src/index.tsx" type="module"></script>
</body>
</html>

View file

@ -1,34 +0,0 @@
const server = Bun.serve({
static: {
"/": new Response(await Bun.file("./dist/index.html").bytes(), {
headers: {
"Content-Type": "text/html",
},
}),
"/index.js": new Response(await Bun.file("./dist/index.js").bytes(), {
headers: {
"Content-Type": "text/javascript",
},
}),
"/pkg/signal_decrypt_backup_wasm.js": new Response(
await Bun.file("../pkg/signal_decrypt_backup_wasm.js").bytes(),
{
headers: {
"Content-Type": "text/javascript",
},
},
),
"/pkg/signal_decrypt_backup_wasm_bg.wasm": new Response(
await Bun.file("../pkg/signal_decrypt_backup_wasm_bg.wasm").bytes(),
{
headers: {
"Content-Type": "application/wasm",
},
},
),
},
fetch(req) {
console.log(req);
return new Response("404!");
},
});

View file

@ -1,27 +0,0 @@
{
"compilerOptions": {
// Enable latest features
"lib": ["ESNext", "DOM"],
"target": "ESNext",
"module": "ESNext",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
}

View file

@ -1,11 +1,24 @@
{
"name": "test",
"module": "index.js",
"name": "vite-template-solid",
"version": "0.0.0",
"description": "",
"type": "module",
"devDependencies": {
"@types/bun": "latest"
"scripts": {
"start": "vite",
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"peerDependencies": {
"typescript": "^5.0.0"
"license": "MIT",
"devDependencies": {
"@duskflower/signal-decrypt-backup-wasm": "../pkg",
"typescript": "^5.7.2",
"vite": "^6.0.0",
"vite-plugin-solid": "^2.11.0",
"vite-plugin-wasm": "^3.4.1"
},
"dependencies": {
"@sqlite.org/sqlite-wasm": "3.47.2-build1",
"solid-js": "^1.9.3"
}
}

1089
test/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load diff

33
test/src/App.module.css Normal file
View file

@ -0,0 +1,33 @@
.App {
text-align: center;
}
.logo {
animation: logo-spin infinite 20s linear;
height: 40vmin;
pointer-events: none;
}
.header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.link {
color: #b318f0;
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

144
test/src/App.tsx Normal file
View file

@ -0,0 +1,144 @@
import { createEffect, createSignal, on, type Component } from "solid-js";
import { BackupDecryptor } from "@duskflower/signal-decrypt-backup-wasm";
import { db } from "./db";
export async function decryptBackup(
file: File,
passphrase: string,
progressCallback: (progres: number) => void,
) {
const fileSize = file.size;
console.log("Starting decryption of file size:", fileSize);
const decryptor = new BackupDecryptor();
decryptor.set_progress_callback(fileSize, (percentage: number) =>
console.info(`${percentage}% done`),
);
const chunkSize = 1024 * 1024 * 40; // 40MB chunks
let offset = 0;
try {
while (offset < file.size) {
// console.log(`Processing chunk at offset ${offset}`);
const chunk = file.slice(offset, offset + chunkSize);
const arrayBuffer = await chunk.arrayBuffer();
const uint8Array = new Uint8Array(arrayBuffer);
decryptor.feed_data(uint8Array);
let done = false;
while (!done) {
try {
done = decryptor.process_chunk(passphrase);
} catch (e) {
console.error("Error processing chunk:", e);
throw e;
}
}
offset += chunkSize;
// console.log(`Completed chunk, new offset: ${offset}`);
// if (performance.memory) {
// const memoryInfo = performance.memory;
// console.log(`Total JS Heap Size: ${memoryInfo.totalJSHeapSize} bytes`);
// console.log(`Used JS Heap Size: ${memoryInfo.usedJSHeapSize} bytes`);
// console.log(`JS Heap Size Limit: ${memoryInfo.jsHeapSizeLimit} bytes`);
// } else {
// console.log("Memory information is not available in this environment.");
// }
}
// console.log("All chunks processed, finishing up");
const result = decryptor.finish();
// decryptor.free();
return result;
} catch (e) {
console.error("Decryption failed:", e);
throw e;
}
}
async function decrypt(file: File, passphrase: string) {
try {
const result = await decryptBackup(file, passphrase, console.info);
// console.log(result, result.database_bytes);
// console.log("Database bytes length:", result.databaseBytes.length);
// console.log(
// "Database bytes as string (partly)",
// new TextDecoder().decode(result.database_bytes.slice(0, 1024 * 50)),
// );
// console.log(result.database_statements);
return result;
// console.log("Preferences:", result.preferences);
// console.log("Key values:", result.keyValues);
} catch (error) {
console.error("Decryption failed:", error);
}
}
const App: Component = () => {
const [passphrase, setPassphrase] = createSignal("");
const [backupFile, setBackupFile] = createSignal<File>();
createEffect(
on(
backupFile,
(currentBackupFile) => {
if (currentBackupFile) {
decrypt(currentBackupFile, passphrase()).then((result) => {
if (result) {
for (const statement of result.database_statements) {
try {
console.log("executing");
db.exec(statement);
} catch (e) {
throw new Error(`statement failed: ${statement}`, {
cause: e,
});
}
}
console.log("All statements executed");
console.log(
db.exec("SELECT * from message", {
returnValue: "resultRows",
}),
);
}
});
}
},
{
defer: true,
},
),
);
return (
<form id="test-form">
<input
type="password"
id="passphrase-input"
onChange={(event) => {
setPassphrase(event.currentTarget.value);
}}
/>
<input
type="file"
id="backup-input"
onChange={(event) => {
setBackupFile(event.currentTarget.files?.[0]);
}}
/>
</form>
);
};
export default App;

BIN
test/src/assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

21
test/src/db.ts Normal file
View file

@ -0,0 +1,21 @@
import sqlite3InitModule, {
type Database,
type OpfsDatabase,
} from "@sqlite.org/sqlite-wasm";
export let db: OpfsDatabase | Database;
const sqlite3 = (await sqlite3InitModule()).oo1;
if (!sqlite3) {
throw new Error("fail to load sqlite");
}
const path = "/signal.db";
if (sqlite3.OpfsDb) {
console.log("support OPFS");
db = new sqlite3.OpfsDb(path);
}
console.log("doesn't support OPFS");
db = new sqlite3.DB(path);

13
test/src/index.css Normal file
View file

@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
'Helvetica Neue', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

15
test/src/index.tsx Normal file
View file

@ -0,0 +1,15 @@
/* @refresh reload */
import { render } from 'solid-js/web';
import './index.css';
import App from './App';
const root = document.getElementById('root');
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
throw new Error(
'Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?',
);
}
render(() => <App />, root!);

1
test/src/logo.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 166 155.3"><path d="M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z" fill="#76b3e1"/><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="27.5" y1="3" x2="152" y2="63.5"><stop offset=".1" stop-color="#76b3e1"/><stop offset=".3" stop-color="#dcf2fd"/><stop offset="1" stop-color="#76b3e1"/></linearGradient><path d="M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z" opacity=".3" fill="url(#a)"/><path d="M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z" fill="#518ac8"/><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="95.8" y1="32.6" x2="74" y2="105.2"><stop offset="0" stop-color="#76b3e1"/><stop offset=".5" stop-color="#4377bb"/><stop offset="1" stop-color="#1f3b77"/></linearGradient><path d="M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z" opacity=".3" fill="url(#b)"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="18.4" y1="64.2" x2="144.3" y2="149.8"><stop offset="0" stop-color="#315aa9"/><stop offset=".5" stop-color="#518ac8"/><stop offset="1" stop-color="#315aa9"/></linearGradient><path d="M134 80a45 45 0 00-48-15L24 85 4 120l112 19 20-36c4-7 3-15-2-23z" fill="url(#c)"/><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="75.2" y1="74.5" x2="24.4" y2="260.8"><stop offset="0" stop-color="#4377bb"/><stop offset=".5" stop-color="#1a336b"/><stop offset="1" stop-color="#1a336b"/></linearGradient><path d="M114 115a45 45 0 00-48-15L4 120s53 40 94 30l3-1c17-5 23-21 13-34z" fill="url(#d)"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

15
test/tsconfig.json Normal file
View file

@ -0,0 +1,15 @@
{
"compilerOptions": {
"strict": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"types": ["vite/client"],
"noEmit": true,
"isolatedModules": true
}
}

16
test/vite.config.ts Normal file
View file

@ -0,0 +1,16 @@
import { defineConfig } from "vite";
import solidPlugin from "vite-plugin-solid";
import wasm from "vite-plugin-wasm";
export default defineConfig({
plugins: [solidPlugin(), wasm()],
optimizeDeps: {
exclude: ["signal-decrypt-backup-wasm", "@sqlite.org/sqlite-wasm"],
},
server: {
port: 3000,
},
build: {
target: "esnext",
},
});