test(pubky): add headless testing instead of examples

This commit is contained in:
nazeh
2024-07-28 20:10:03 +03:00
parent e0b58451b5
commit 3cc81a5d0e
22 changed files with 85 additions and 648 deletions

42
Cargo.lock generated
View File

@@ -372,16 +372,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if",
"wasm-bindgen",
]
[[package]]
name = "const-oid"
version = "0.9.6"
@@ -1583,7 +1573,6 @@ dependencies = [
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test",
"web-sys",
]
@@ -1890,12 +1879,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -2625,31 +2608,6 @@ version = "0.2.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
[[package]]
name = "wasm-bindgen-test"
version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9bf62a58e0780af3e852044583deee40983e5886da43a271dd772379987667b"
dependencies = [
"console_error_panic_hook",
"js-sys",
"scoped-tls",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "web-sys"
version = "0.3.69"

View File

@@ -1,5 +0,0 @@
.netlify
.parcel*
dist
node_modules
package-lock.json

View File

@@ -1,38 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- <link rel="icon" href="images/favicon.ico" /> -->
<!-- Primary Meta Tags -->
<title>Pubky demo</title>
<meta name="title" content="Pubky demo" />
<meta name="description" content="Pubky is a new thing." />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<!-- <meta property="og:url" content=""> -->
<!-- <meta property="og:title" content=""> -->
<!-- <meta property="og:description" content=""> -->
<!-- <meta property="og:image" content=""> -->
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<!-- <meta property="twitter:url" content=""> -->
<!-- <meta property="twitter:title" content=""> -->
<!-- <meta property="twitter:description" content=""> -->
<!-- <meta property="twitter:image" content=""> -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;700&display=swap"
rel="stylesheet"
/>
<link href="./src/style.css" rel="stylesheet" />
</head>
<body>
<div id="app"></div>
<script type="module" src="src/index.jsx"></script>
</body>
</html>

View File

@@ -1,23 +0,0 @@
{
"private": "true",
"main": "src/index.js",
"type": "module",
"scripts": {
"build": "vite build",
"start": "vite serve",
"preview": "vite preview",
"preinstall": "cargo run --bin bundle_pubky_npm"
},
"dependencies": {
"@synonymdev/pubky": "file:../../../pubky/pkg",
"@solidjs/router": "^0.10.9",
"solid-js": "^1.7.0",
"vite-plugin-solid": "^2.10.2"
},
"devDependencies": {
"typescript": "^4.9.5",
"vite": "^4.1.4",
"vite-plugin-html": "^3.2.0"
}
}

View File

@@ -1,30 +0,0 @@
{
"private": "true",
"type": "module",
"source": "src/index.html",
"browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": {
"start": "parcel -p 7251",
"serve": "npm run start",
"build": "parcel build",
"lint": "standard --fix"
},
"standard": {
"ignore": [
"src/pages",
"src/components",
"dist"
]
},
"dependencies": {
"@solidjs/router": "^0.10.9",
"parcel": "^2.0.1",
"@synonymdev/pubky": "file:../../../pubky/pkg",
"solid-js": "^1.7.0"
},
"devDependencies": {
"@babel/core": "^7.24.9",
"babel-preset-solid": "^1.2.5",
"solid-refresh": "^0.2.2"
}
}

View File

@@ -1,78 +0,0 @@
import { useNavigate } from '@solidjs/router'
import { createSignal } from 'solid-js'
import { crypto } from '@pubky/common'
import { decryptRecoveryFile } from '../sdk/recovery.js'
import store from '../store.js'
/**
* @param {"login" | "signup"} type
*/
const RecoveryFileUpload = ({ type }) => {
const [valid, setValid] = createSignal(false)
const navigate = useNavigate()
const onSubmit = async (e) => {
e.preventDefault()
const form = e.target
const file = form.file.files[0]
const passphrase = form['import-passphrase'].value
const reader = new FileReader()
reader.onload = async function(event) {
const recoveryFile = event.target.result
const seedResult = await decryptRecoveryFile(recoveryFile, passphrase)
if (seedResult.isErr()) return alert(seedResult.error.message)
const action = type === 'signup'
? store.pubkyClient.signup.bind(store.pubkyClient)
: store.pubkyClient.login.bind(store.pubkyClient);
const result = await action(seedResult.value)
crypto.zeroize(seedResult.value)
if (result.isErr()) return alert(result.error.message)
store.setCurrentUser({ id: result.value })
navigate("/", { replace: true })
}
// Read the file as text
reader.readAsText(file)
}
const onUpdate = (e) => {
const form = e.target.parentElement.parentElement
const file = form.file.files[0]
const passphrase = form['import-passphrase'].value
if (passphrase.length > 0 && file) {
setValid(true)
} else {
setValid(false)
}
}
return (
<form onsubmit={onSubmit}>
<label>
Upload Recovery file
<input type='file' name='file' id='file' required onChange={onUpdate} style="margin-top: 1em" />
</label>
<label>
{`Passphrase to ${type === 'signup' ? 'encrypt' : 'decrypt'} your recovery file`}
<input id='import-passphrase' type='password' placeholder='****' required onKeyDown={onUpdate} />
</label>
<input type='submit' className='button primary' disabled={!valid()} />
</form>
)
}
export default RecoveryFileUpload

View File

@@ -1,21 +0,0 @@
const Layout = ({ children }) => {
return (
<>
<header>
<div className='row'>
<h1>Pubky</h1>
</div>
</header>
<main>
{children}
</main>
<footer>
<p>This is a proof of concept for demonstration purposes only.</p>
</footer>
</>
)
}
export default Layout

View File

@@ -1,21 +0,0 @@
import { render } from 'solid-js/web'
import { Router, Route } from '@solidjs/router'
import { PubkyClient } from "@synonymdev/pubky";
let client = new PubkyClient()
console.log(client);
import Home from './pages/Home.jsx'
// import Login from './pages/Login.js'
// import Signup from './pages/Signup.js'
render(() => (
<Router>
<Route path='/' component={Home} />
</Router>
), document.getElementById('app'))
// <Route path='/home' component={Home} />
// <Route path='/signup' component={Signup} />
// <Route path='/login' component={Login} />

View File

@@ -1,43 +0,0 @@
import { useNavigate } from '@solidjs/router'
import store from '../store.js'
import Layout from '../components/Layout'
const Home = () => {
const navigate = useNavigate()
let currentUser = store.getCurrentUser()
if (!currentUser) {
navigate('/login', { replace: true })
}
const logout = async () => {
// await store.pubkyClient.ready()
//
// const logoutResult = await store.pubkyClient.logout(currentUser.id)
// if (logoutResult.isErr()) {
// alert(logoutResult.error.message)
// return
// }
//
// store.removeCurrentUser()
//
// if (window.location.pathname === '/home') {
// navigate('/', { replace: true })
// } else {
// navigate('/home', { replace: true })
// }
}
return (
<Layout>
Home..
<p>Welcome <b>{store.getCurrentUser()?.id}</b></p>
<br />
<button class="button primary" onClick={logout}>Logout</button>
</Layout>
)
}
export default Home

View File

@@ -1,90 +0,0 @@
import { createMutable } from 'solid-js/store'
// import z32 from 'z32'
// import { Result } from '@pubky/common'
// import { Level } from 'level'
// import Client from '@pubky/client'
//
// import { recoveryFile } from './sdk/recovery.js'
//
// // In real application it should be the server's Pkarr Id
// const DEFAULT_HOME_SERVER = 'http://localhost:7259'
// const DEFAULT_RELAY = 'https://relay.pkarr.org'
class Store {
constructor() {
// this.db = new Level('app-db', { keyEncoding: 'utf8', valueEncoding: 'json' })
// this.currentUser = null
//
// this.pubkyClient = new Client(DEFAULT_HOME_SERVER, { relay: DEFAULT_RELAY })
//
// this.DEFAULT_HOME_SERVER = DEFAULT_HOME_SERVER
}
// getUsers() {
// try {
// return JSON.parse(global.localStorage.getItem('users')) || []
// } catch {
// return []
// }
// }
getCurrentUser() {
try {
return JSON.parse(global.localStorage.getItem('currentUser'))
} catch {
return null
}
}
// setCurrentUser(user) {
// const users = this.getUsers()
//
// if (!users?.map(user => user.id).includes(user.id)) {
// global.localStorage.setItem('users', JSON.stringify([
// ...users,
// user
// ]))
// }
//
// global.localStorage.setItem('currentUser', JSON.stringify(user))
// }
//
// removeCurrentUser() {
// global.localStorage.removeItem('currentUser')
// }
//
// /**
// * @param {string} name
// * @param {string} passphrase
// *
// * @returns {Promise<Result<{
// * recoveryFile: Uint8Array,
// * filename: string,
// * signupUrl: string
// * }>>}
// */
// async createAccount(name, passphrase) {
// await this.pubkyClient.ready()
//
// const seed = Client.crypto.generateSeed()
//
// const keypair = Client.crypto.generateKeyPair(seed)
// Client.crypto.zeroize(keypair.secretKey)
//
// const userId = z32.encode(keypair.publicKey)
//
// const recoveryFileAndFilename = await recoveryFile(name, seed, passphrase)
//
// const signedUp = await this.pubkyClient.signup(seed)
// if (signedUp.isErr()) return signedUp
//
// Client.crypto.zeroize(seed)
//
// return Result.Ok({
// userId,
// ...recoveryFileAndFilename
// })
// }
}
export default createMutable(new Store())

View File

@@ -1,137 +0,0 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body,
#app {
width: 100%;
height: 100%;
min-width: 320px;
}
body,
#app {
display: flex;
flex-direction: column;
justify-content: space-between;
max-width: 600px;
margin: 0 auto;
padding: 2rem 1rem 0;
font-family: 'IBM Plex Sans', Helvetica, sans-serif;
}
.button {
background: none;
border: none;
cursor: pointer;
}
.button:disabled {
pointer-events: none;
opacity: 0.4;
}
main {
height: 100%;
margin: 1rem 0;
}
footer {
text-align: center;
color: #666;
padding: 1rem 0;
border-top: 1px solid #ddd;
margin-top: 1rem;
}
footer p,
footer a {
font-size: 0.8rem !important;
padding-bottom: 0.3rem;
}
.row {
display: flex;
justify-content: space-between;
align-items: baseline;
}
a {
color: #000;
font-size: 1rem;
font-weight: 700;
}
h1 {
font-size: 2rem;
font-weight: 700;
}
.small {
font-size: 0.8rem;
}
.button.primary {
background: black;
color: white;
min-width: 90px;
width: 100%;
padding: 0.4rem 0.8rem;
border: 2px solid #000;
}
.button.primary:hover {
background: #222;
}
.divider {
background: #333;
height: 1px;
margin-top: 1rem;
margin-bottom: 1rem;
}
form {
display: flex;
flex-direction: column;
}
label {
font-size: 0.9rem;
font-weight: 700;
margin-bottom: 1rem;
}
form input {
font-size: 1rem;
width: 100%;
height: 2rem;
border: none;
box-shadow: none;
border-radius: 0;
border-bottom: 1px solid #ddd;
}
form input:focus {
outline: none;
border-bottom: 2px solid #000;
}
label.checkbox {
display: flex;
font-weight: normal;
line-height: 1rem;
}
label.checkbox input {
width: 1rem;
margin: 0 0.5rem 0 0;
}
label.checkbox:focus-within {
outline: 1px solid;
}

View File

@@ -1,12 +0,0 @@
import { defineConfig } from "vite";
// import wasmPack from "vite-plugin-wasm-pack";
import solidPlugin from 'vite-plugin-solid';
// import path from "node:path";
export default defineConfig({
// pass your local crate path to the plugin
plugins: [
// wasmPack(path.resolve("../../../pubky")),
solidPlugin()
],
});

View File

@@ -1,3 +0,0 @@
dist
node_modules
package-lock.json

View File

@@ -1,3 +0,0 @@
# No bundler
An example of using Pubky wasm immidiatly in an `index.html` with no bundlers.

View File

@@ -1,46 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- <link rel="icon" href="images/favicon.ico" /> -->
<!-- Primary Meta Tags -->
<title>Pubky demo</title>
<meta name="title" content="Pubky demo" />
<meta name="description" content="Pubky is a new thing." />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<!-- <meta property="og:url" content=""> -->
<!-- <meta property="og:title" content=""> -->
<!-- <meta property="og:description" content=""> -->
<!-- <meta property="og:image" content=""> -->
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<!-- <meta property="twitter:url" content=""> -->
<!-- <meta property="twitter:title" content=""> -->
<!-- <meta property="twitter:description" content=""> -->
<!-- <meta property="twitter:image" content=""> -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;700&display=swap"
rel="stylesheet"
/>
<!-- <link href="./src/style.css" rel="stylesheet" /> -->
</head>
<body>
<div id="app"></div>
<script type="module">
import { PubkyClient } from '@synonymdev/pubky';
let client = new PubkyClient();
console.log(client);
alert("Works fine check the console logs");
</script>
</body>
</html>

View File

@@ -1,18 +0,0 @@
{
"scripts": {
"start": "vite serve",
"preinstall": "cargo run --bin bundle_pubky_npm"
},
"dependencies": {
"@synonymdev/pubky": "file:../../../pubky/pkg",
"@solidjs/router": "^0.10.9",
"solid-js": "^1.7.0"
},
"devDependencies": {
"typescript": "^4.9.5",
"vite": "^4.1.4",
"vite-plugin-html": "^3.2.0",
"vite-plugin-solid": "^2.10.2"
}
}

View File

@@ -43,9 +43,6 @@ web-sys = { version = "0.3.69", features = [
pubky_homeserver = { path = "../pubky-homeserver" }
tokio = "1.37.0"
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3.42"
[features]
[package.metadata.docs.rs]

View File

@@ -1,3 +1,49 @@
# Pubky
JavaScript implementation of [Pubky](https://github.com/pubky/pubky).
## Install
```bash
npm install @synonymdev/pubky
```
## Getting started
```js
import PubkyClient from "@synonymdev/pubky";
// Initialize PubkyClient with Pkarr relay(s).
let client = new PubkyClient();
// Generate a keypair
let keypair = Keypair.random();
// Create a new account
let homeserver = PublicKey.try_from("8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo");
await client.signup(keypair, homeserver)
```
## Test and Development
For test and development, you can run a local homeserver in a test network.
If you don't have Cargo Installed, start by installing it:
```bash
curl https://sh.rustup.rs -sSf | sh
```
Clone the Pubky repository:
```bash
git clone https://github.com/pubky/pubky
cd pubky/
```
Run the testnet server
```bash
cargo run --bin pubky_homeserver -- --testnet
```

View File

@@ -10,7 +10,8 @@
},
"scripts": {
"lint": "standard --fix",
"test": "brittle test/*.js -cov",
"test": "tape test/*.js -cov",
"test-browser": "browserify test/*.js -p esmify | npx tape-run",
"preinstall": "cargo run --bin bundle_pubky_npm",
"prepublishOnly": "npm run lint && npm run test"
},
@@ -33,7 +34,10 @@
"identity"
],
"devDependencies": {
"brittle": "^3.6.1",
"standard": "^17.1.0"
"browser-resolve": "^2.0.0",
"esmify": "^2.1.1",
"standard": "^17.1.0",
"tape": "^5.8.1",
"tape-run": "^11.0.0"
}
}

View File

@@ -1,4 +1,4 @@
import test from 'brittle'
import test from 'tape'
import { PubkyClient, Keypair, PublicKey } from '../index.js'
@@ -7,30 +7,14 @@ test('seed auth', async (t) => {
let client = new PubkyClient();
let keypair = Keypair.random();
let homeserver = PublicKey.try_from("8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo");
let homeserver = PublicKey.try_from("8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo");
await client.signup(keypair, homeserver);
// const client = new Client(
// homeserver.homeserver.pkarr.serverPkarr.publicKey(),
// {
// relay: homeserver.testnet.relay
// }
// )
// await client.ready()
//
// const seed = Client.crypto.generateSeed()
// const keypair = Client.crypto.generateKeyPair(seed)
// const expectedUserId = keypair.public_key().to_string()
//
// const userIdResult = await client.signup(seed)
// t.ok(userIdResult.isOk(), userIdResult.error)
//
// const userId = userIdResult.value
// t.is(userId, expectedUserId)
//
t.ok(true);
// const session = await client.session()
// t.ok(session?.users[userId])
// t.ok(session)
//
// {
// await client.logout(userId)

View File

@@ -1,4 +1,4 @@
import test from 'brittle'
import test from 'tape'
import { Keypair } from '../index.js'

View File

@@ -1,10 +1,15 @@
use pubky_common::auth::AuthnSignature;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use web_sys::RequestMode;
use reqwest::StatusCode;
use pkarr::PkarrRelayClient;
use pubky_common::{auth::AuthnSignature, session::Session};
use crate::Error;
use super::{
keys::{Keypair, PublicKey},
PubkyClient,
@@ -35,18 +40,29 @@ impl PubkyClient {
Ok(())
}
}
#[cfg(test)]
mod tests {
use wasm_bindgen_test::wasm_bindgen_test;
/// Check the current sesison for a given Pubky in its homeserver.
///
/// Returns an [Error::NotSignedIn] if so, or [reqwest::Error] if
/// the response has any other `>=400` status code.
#[wasm_bindgen]
pub async fn session(&self, pubky: &PublicKey) -> Result<Session, JsValue> {
let (homeserver, mut url) = self.resolve_pubky_homeserver(pubky).await?;
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
url.set_path(&format!("/{}/session", pubky));
use super::*;
let res = self.http.get(url).send().await?;
#[wasm_bindgen_test]
async fn basic() {
// let client = PubkyClient::new();
if res.status() == StatusCode::NOT_FOUND {
return Err(Error::NotSignedIn);
}
if !res.status().is_success() {
res.error_for_status_ref()?;
};
let bytes = res.bytes().await?;
Ok(Session::deserialize(&bytes)?)
}
}