@parity/product-sdk 0.0.0-dev.312.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/address/index.d.ts +9 -0
- package/dist/address/index.js +3 -0
- package/dist/address/index.js.map +1 -0
- package/dist/chain/index.d.ts +1 -0
- package/dist/chain/index.js +3 -0
- package/dist/chain/index.js.map +1 -0
- package/dist/chunk-BRZNHTLQ.js +89 -0
- package/dist/chunk-BRZNHTLQ.js.map +1 -0
- package/dist/chunk-DHRCWMY5.js +240 -0
- package/dist/chunk-DHRCWMY5.js.map +1 -0
- package/dist/chunk-JVJ552RU.js +3 -0
- package/dist/chunk-JVJ552RU.js.map +1 -0
- package/dist/cloud-storage/index.d.ts +1 -0
- package/dist/cloud-storage/index.js +3 -0
- package/dist/cloud-storage/index.js.map +1 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +3 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/core/index.d.ts +12 -0
- package/dist/core/index.js +5 -0
- package/dist/core/index.js.map +1 -0
- package/dist/crypto/index.d.ts +1 -0
- package/dist/crypto/index.js +3 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/dotns-bswWAmR2.d.ts +136 -0
- package/dist/host/index.d.ts +1 -0
- package/dist/host/index.js +3 -0
- package/dist/host/index.js.map +1 -0
- package/dist/identity/index.d.ts +491 -0
- package/dist/identity/index.js +691 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/individuality/index.d.ts +1 -0
- package/dist/individuality/index.js +3 -0
- package/dist/individuality/index.js.map +1 -0
- package/dist/local-storage/index.d.ts +1 -0
- package/dist/local-storage/index.js +3 -0
- package/dist/local-storage/index.js.map +1 -0
- package/dist/react/index.d.ts +181 -0
- package/dist/react/index.js +198 -0
- package/dist/react/index.js.map +1 -0
- package/dist/testing/index.d.ts +61 -0
- package/dist/testing/index.js +149 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/types-B5Zx4RNq.d.ts +211 -0
- package/dist/wallet/index.d.ts +1 -0
- package/dist/wallet/index.js +3 -0
- package/dist/wallet/index.js.map +1 -0
- package/package.json +118 -0
- package/src/address/index.ts +8 -0
- package/src/chain/index.ts +8 -0
- package/src/cloud-storage/index.ts +8 -0
- package/src/contracts/index.ts +8 -0
- package/src/core/createApp.ts +408 -0
- package/src/core/index.ts +12 -0
- package/src/core/logger.ts +15 -0
- package/src/core/types.ts +233 -0
- package/src/crypto/index.ts +8 -0
- package/src/host/index.ts +8 -0
- package/src/identity/dotns-abis.test.ts +43 -0
- package/src/identity/dotns-abis.ts +274 -0
- package/src/identity/dotns-errors.ts +34 -0
- package/src/identity/dotns-namehash.test.ts +155 -0
- package/src/identity/dotns-namehash.ts +149 -0
- package/src/identity/dotns-owner.test.ts +92 -0
- package/src/identity/dotns-registry.test.ts +1154 -0
- package/src/identity/dotns-registry.ts +870 -0
- package/src/identity/dotns.test.ts +128 -0
- package/src/identity/dotns.ts +221 -0
- package/src/identity/index.ts +61 -0
- package/src/identity/product-account.test.ts +130 -0
- package/src/identity/product-account.ts +115 -0
- package/src/identity/types.ts +87 -0
- package/src/index.ts +61 -0
- package/src/individuality/contract.test.ts +135 -0
- package/src/individuality/index.ts +8 -0
- package/src/local-storage/index.ts +8 -0
- package/src/react/context.ts +35 -0
- package/src/react/index.ts +43 -0
- package/src/react/provider.tsx +78 -0
- package/src/react/useChain.ts +35 -0
- package/src/react/useLocalStorage.ts +127 -0
- package/src/react/useWallet.ts +156 -0
- package/src/testing.ts +297 -0
- package/src/wallet/index.ts +11 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// Copyright 2026 Parity Technologies (UK) Ltd.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* useWallet hook
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { useState, useEffect, useCallback } from "react";
|
|
8
|
+
import { useProductSDK } from "./context.js";
|
|
9
|
+
import type {
|
|
10
|
+
Account,
|
|
11
|
+
DotNsIdentitySignature,
|
|
12
|
+
SignMessageWithDotNsIdentityArgs,
|
|
13
|
+
} from "../core/types.js";
|
|
14
|
+
|
|
15
|
+
/** Wallet hook state */
|
|
16
|
+
export interface UseWalletState {
|
|
17
|
+
/** Whether wallet is connected */
|
|
18
|
+
isConnected: boolean;
|
|
19
|
+
/** Whether connection is in progress */
|
|
20
|
+
isConnecting: boolean;
|
|
21
|
+
/** Available accounts */
|
|
22
|
+
accounts: Account[];
|
|
23
|
+
/** Currently selected account */
|
|
24
|
+
selectedAccount: Account | null;
|
|
25
|
+
/** Last error */
|
|
26
|
+
error: Error | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Wallet hook actions */
|
|
30
|
+
export interface UseWalletActions {
|
|
31
|
+
/** Connect to wallet */
|
|
32
|
+
connect: () => Promise<void>;
|
|
33
|
+
/** Disconnect from wallet */
|
|
34
|
+
disconnect: () => Promise<void>;
|
|
35
|
+
/** Select an account */
|
|
36
|
+
selectAccount: (address: string) => void;
|
|
37
|
+
/** Sign a message */
|
|
38
|
+
signMessage: (message: string | Uint8Array) => Promise<Uint8Array>;
|
|
39
|
+
/** Sign a message with the account that owns a DotNS / People username identity. */
|
|
40
|
+
signMessageWithDotNsIdentity: (
|
|
41
|
+
args: SignMessageWithDotNsIdentityArgs,
|
|
42
|
+
) => Promise<DotNsIdentitySignature>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Return type of useWallet */
|
|
46
|
+
export type UseWalletReturn = UseWalletState & UseWalletActions;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Hook for wallet connection and signing
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```tsx
|
|
53
|
+
* function WalletButton() {
|
|
54
|
+
* const { isConnected, accounts, connect, disconnect, selectAccount } = useWallet();
|
|
55
|
+
*
|
|
56
|
+
* if (!isConnected) {
|
|
57
|
+
* return <button onClick={connect}>Connect Wallet</button>;
|
|
58
|
+
* }
|
|
59
|
+
*
|
|
60
|
+
* return (
|
|
61
|
+
* <div>
|
|
62
|
+
* <select onChange={(e) => selectAccount(e.target.value)}>
|
|
63
|
+
* {accounts.map((a) => (
|
|
64
|
+
* <option key={a.address} value={a.address}>
|
|
65
|
+
* {a.name || a.address}
|
|
66
|
+
* </option>
|
|
67
|
+
* ))}
|
|
68
|
+
* </select>
|
|
69
|
+
* <button onClick={disconnect}>Disconnect</button>
|
|
70
|
+
* </div>
|
|
71
|
+
* );
|
|
72
|
+
* }
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export function useWallet(): UseWalletReturn {
|
|
76
|
+
const app = useProductSDK();
|
|
77
|
+
|
|
78
|
+
const [state, setState] = useState<UseWalletState>({
|
|
79
|
+
isConnected: false,
|
|
80
|
+
isConnecting: false,
|
|
81
|
+
accounts: [],
|
|
82
|
+
selectedAccount: null,
|
|
83
|
+
error: null,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const connect = useCallback(async () => {
|
|
87
|
+
setState((s) => ({ ...s, isConnecting: true, error: null }));
|
|
88
|
+
try {
|
|
89
|
+
const result = await app.wallet.connect();
|
|
90
|
+
setState((s) => ({
|
|
91
|
+
...s,
|
|
92
|
+
isConnected: true,
|
|
93
|
+
isConnecting: false,
|
|
94
|
+
accounts: result.accounts,
|
|
95
|
+
selectedAccount: result.accounts[0] || null,
|
|
96
|
+
}));
|
|
97
|
+
} catch (e) {
|
|
98
|
+
setState((s) => ({
|
|
99
|
+
...s,
|
|
100
|
+
isConnecting: false,
|
|
101
|
+
error: e instanceof Error ? e : new Error(String(e)),
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
}, [app]);
|
|
105
|
+
|
|
106
|
+
const disconnect = useCallback(async () => {
|
|
107
|
+
await app.wallet.disconnect();
|
|
108
|
+
setState({
|
|
109
|
+
isConnected: false,
|
|
110
|
+
isConnecting: false,
|
|
111
|
+
accounts: [],
|
|
112
|
+
selectedAccount: null,
|
|
113
|
+
error: null,
|
|
114
|
+
});
|
|
115
|
+
}, [app]);
|
|
116
|
+
|
|
117
|
+
const selectAccount = useCallback(
|
|
118
|
+
(address: string) => {
|
|
119
|
+
app.wallet.selectAccount(address);
|
|
120
|
+
const account = state.accounts.find((a) => a.address === address) || null;
|
|
121
|
+
setState((s) => ({ ...s, selectedAccount: account }));
|
|
122
|
+
},
|
|
123
|
+
[app, state.accounts],
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const signMessage = useCallback(
|
|
127
|
+
async (message: string | Uint8Array) => {
|
|
128
|
+
return app.wallet.signMessage(message);
|
|
129
|
+
},
|
|
130
|
+
[app],
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const signMessageWithDotNsIdentity = useCallback(
|
|
134
|
+
async (args: SignMessageWithDotNsIdentityArgs) => {
|
|
135
|
+
return app.wallet.signMessageWithDotNsIdentity(args);
|
|
136
|
+
},
|
|
137
|
+
[app],
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// Subscribe to account changes
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
const unsubscribe = app.wallet.onAccountChange((account) => {
|
|
143
|
+
setState((s) => ({ ...s, selectedAccount: account }));
|
|
144
|
+
});
|
|
145
|
+
return unsubscribe;
|
|
146
|
+
}, [app]);
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
...state,
|
|
150
|
+
connect,
|
|
151
|
+
disconnect,
|
|
152
|
+
selectAccount,
|
|
153
|
+
signMessage,
|
|
154
|
+
signMessageWithDotNsIdentity,
|
|
155
|
+
};
|
|
156
|
+
}
|
package/src/testing.ts
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
// Copyright 2026 Parity Technologies (UK) Ltd.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Test fakes for `@parity/product-sdk` (umbrella).
|
|
5
|
+
*
|
|
6
|
+
* `createFakeApp` returns a fake {@link App} — use it directly in logic tests or
|
|
7
|
+
* hand it to `ProductSDKContext.Provider` for React component tests, so code that
|
|
8
|
+
* calls `useProductSDK()` / `useWallet()` runs with no host. This entry also
|
|
9
|
+
* re-exports every package's fakes, for a single meta-package import.
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
import { ProductCloudStorageError } from "@parity/product-sdk-cloud-storage";
|
|
14
|
+
import { createFakeHostLocalStorage } from "@parity/product-sdk-local-storage/testing";
|
|
15
|
+
import { err, ok, unwrapErr, unwrapOk } from "@parity/result";
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
Account,
|
|
19
|
+
App,
|
|
20
|
+
ChainApi,
|
|
21
|
+
CloudStorageApi,
|
|
22
|
+
LocalStorageApi,
|
|
23
|
+
WalletApi,
|
|
24
|
+
} from "./core/types.js";
|
|
25
|
+
|
|
26
|
+
const textEncoder = new TextEncoder();
|
|
27
|
+
|
|
28
|
+
// Re-export the per-package fakes so `@parity/product-sdk/testing` is a one-stop import.
|
|
29
|
+
// statement-store is deliberately absent: the umbrella doesn't wrap that package, so apps
|
|
30
|
+
// using it depend on it directly — import `@parity/product-sdk-statement-store/testing`
|
|
31
|
+
// there instead. Re-exporting its fake here would add a dependency the umbrella otherwise
|
|
32
|
+
// doesn't have and could skew against the version the app actually installs.
|
|
33
|
+
export * from "@parity/product-sdk-local-storage/testing";
|
|
34
|
+
export * from "@parity/product-sdk-signer/testing";
|
|
35
|
+
export * from "@parity/product-sdk-contracts/testing";
|
|
36
|
+
export * from "@parity/product-sdk-host/testing";
|
|
37
|
+
|
|
38
|
+
/** Configurable behavior for the fake {@link WalletApi}. */
|
|
39
|
+
export interface FakeWalletOptions {
|
|
40
|
+
/** Accounts `connect()` / `getAccounts()` expose. Default: none. */
|
|
41
|
+
accounts?: Account[];
|
|
42
|
+
/** Initially selected account. Default: the first account, or `null`. */
|
|
43
|
+
selected?: Account | null;
|
|
44
|
+
/** Bytes returned by `signMessage` / `createProof`. Default: 64 zero bytes. */
|
|
45
|
+
signature?: Uint8Array;
|
|
46
|
+
/** Value returned by `getProductAccount()`. Default: the selected account. */
|
|
47
|
+
productAccount?: Account | null;
|
|
48
|
+
/** Value returned by `getAnonymousAlias()`. Default: `null`. */
|
|
49
|
+
anonymousAlias?: string | null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Options for {@link createFakeApp}. */
|
|
53
|
+
export interface CreateFakeAppOptions {
|
|
54
|
+
/** App name reported by `getAppInfo()`. Default: `"fake-app"`. */
|
|
55
|
+
name?: string;
|
|
56
|
+
/** Configure the fake wallet. */
|
|
57
|
+
wallet?: FakeWalletOptions;
|
|
58
|
+
/** Override the local-storage API. Default: in-memory (backed by `createFakeHostLocalStorage`). */
|
|
59
|
+
localStorage?: LocalStorageApi;
|
|
60
|
+
/** Override the chain API. Default: unconfigured (throws until you provide one). */
|
|
61
|
+
chain?: ChainApi;
|
|
62
|
+
/** Override cloud storage, or pass `null` to disable it. Default: an in-memory fake. */
|
|
63
|
+
cloudStorage?: CloudStorageApi | null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function makeFakeWallet(options?: FakeWalletOptions): WalletApi {
|
|
67
|
+
const accounts = options?.accounts ?? [];
|
|
68
|
+
let selected = options?.selected !== undefined ? options.selected : (accounts[0] ?? null);
|
|
69
|
+
const signature = options?.signature ?? new Uint8Array(64);
|
|
70
|
+
const listeners = new Set<(account: Account | null) => void>();
|
|
71
|
+
const notify = () => {
|
|
72
|
+
for (const l of listeners) l(selected);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
async connect() {
|
|
77
|
+
return { accounts };
|
|
78
|
+
},
|
|
79
|
+
async disconnect() {
|
|
80
|
+
selected = null;
|
|
81
|
+
notify();
|
|
82
|
+
},
|
|
83
|
+
getAccounts() {
|
|
84
|
+
return accounts;
|
|
85
|
+
},
|
|
86
|
+
getSelectedAccount() {
|
|
87
|
+
return selected;
|
|
88
|
+
},
|
|
89
|
+
selectAccount(address) {
|
|
90
|
+
selected = accounts.find((a) => a.address === address) ?? null;
|
|
91
|
+
notify();
|
|
92
|
+
},
|
|
93
|
+
async signMessage() {
|
|
94
|
+
return signature;
|
|
95
|
+
},
|
|
96
|
+
async signMessageWithDotNsIdentity(args) {
|
|
97
|
+
return {
|
|
98
|
+
username: args.username ?? "alice",
|
|
99
|
+
accountId: `0x${"00".repeat(32)}`,
|
|
100
|
+
signature,
|
|
101
|
+
};
|
|
102
|
+
},
|
|
103
|
+
onAccountChange(callback) {
|
|
104
|
+
listeners.add(callback);
|
|
105
|
+
return () => {
|
|
106
|
+
listeners.delete(callback);
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
getProductAccount() {
|
|
110
|
+
return options?.productAccount !== undefined ? options.productAccount : selected;
|
|
111
|
+
},
|
|
112
|
+
getAnonymousAlias() {
|
|
113
|
+
return options?.anonymousAlias ?? null;
|
|
114
|
+
},
|
|
115
|
+
async createProof() {
|
|
116
|
+
return signature;
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function makeFakeLocalStorage(): LocalStorageApi {
|
|
122
|
+
// Compose the local-storage fake; mirror createApp's adapter semantics
|
|
123
|
+
// (get normalizes "" -> null, getJSON normalizes undefined -> null).
|
|
124
|
+
const host = createFakeHostLocalStorage();
|
|
125
|
+
return {
|
|
126
|
+
async get(key) {
|
|
127
|
+
return (await host.readString(key)) || null;
|
|
128
|
+
},
|
|
129
|
+
async set(key, value) {
|
|
130
|
+
await host.writeString(key, value);
|
|
131
|
+
},
|
|
132
|
+
async getJSON<T>(key: string) {
|
|
133
|
+
return ((await host.readJSON(key)) ?? null) as T | null;
|
|
134
|
+
},
|
|
135
|
+
async setJSON<T>(key: string, value: T) {
|
|
136
|
+
await host.writeJSON(key, value);
|
|
137
|
+
},
|
|
138
|
+
async remove(key) {
|
|
139
|
+
await host.clear(key);
|
|
140
|
+
},
|
|
141
|
+
async clear() {
|
|
142
|
+
// Unlike the production adapter (a no-op in container mode), the fake
|
|
143
|
+
// actually empties, so tests get real isolation.
|
|
144
|
+
host.reset();
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function makeFakeCloudStorage(): CloudStorageApi {
|
|
150
|
+
const store = new Map<string, Uint8Array>();
|
|
151
|
+
const toBytes = (data: string | Uint8Array) =>
|
|
152
|
+
typeof data === "string" ? textEncoder.encode(data) : data;
|
|
153
|
+
// Deterministic content-addressed id so upload / computeCid / fetch line up.
|
|
154
|
+
const cidOf = (bytes: Uint8Array) => {
|
|
155
|
+
let h = 0x811c9dc5;
|
|
156
|
+
for (const b of bytes) {
|
|
157
|
+
h ^= b;
|
|
158
|
+
h = Math.imul(h, 0x01000193);
|
|
159
|
+
}
|
|
160
|
+
return `bafyfake${(h >>> 0).toString(16).padStart(8, "0")}`;
|
|
161
|
+
};
|
|
162
|
+
return {
|
|
163
|
+
async upload(data) {
|
|
164
|
+
const bytes = toBytes(data);
|
|
165
|
+
const cid = cidOf(bytes);
|
|
166
|
+
store.set(cid, bytes);
|
|
167
|
+
return ok(cid);
|
|
168
|
+
},
|
|
169
|
+
async fetch(cid) {
|
|
170
|
+
const bytes = store.get(cid);
|
|
171
|
+
if (!bytes) {
|
|
172
|
+
return err(
|
|
173
|
+
new ProductCloudStorageError(
|
|
174
|
+
`fake cloud storage: no object stored for CID ${cid}`,
|
|
175
|
+
),
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
return ok(bytes);
|
|
179
|
+
},
|
|
180
|
+
async computeCid(data) {
|
|
181
|
+
return cidOf(toBytes(data));
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function makeUnconfiguredChain(): ChainApi {
|
|
187
|
+
const fail = (): never => {
|
|
188
|
+
throw new Error(
|
|
189
|
+
"createFakeApp: `chain` is not configured. Pass a `chain` override in options. " +
|
|
190
|
+
"There is no chain-read fake — the host owns RPC selection; test chain-reading " +
|
|
191
|
+
"logic behind your own interface and cover the real wiring in e2e.",
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
// Reads throw (fail fast, no silent undefined); state queries stay quiet.
|
|
195
|
+
return {
|
|
196
|
+
getClient: fail,
|
|
197
|
+
getRawClient: fail,
|
|
198
|
+
connect: fail,
|
|
199
|
+
isConnected: () => false,
|
|
200
|
+
destroyAll: () => {},
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Create a fake {@link App}. Wallet, local-storage, and cloud-storage are working
|
|
206
|
+
* in-memory fakes; `chain` is unconfigured until you override it. Every sub-API
|
|
207
|
+
* is overridable.
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```ts
|
|
211
|
+
* import { createFakeApp } from "@parity/product-sdk/testing";
|
|
212
|
+
*
|
|
213
|
+
* const app = createFakeApp({ wallet: { accounts: [alice, bob], selected: alice } });
|
|
214
|
+
* await app.wallet.connect();
|
|
215
|
+
* expect(app.wallet.getAccounts()).toHaveLength(2);
|
|
216
|
+
* ```
|
|
217
|
+
*
|
|
218
|
+
* For React, feed the fake to the exported context:
|
|
219
|
+
* ```tsx
|
|
220
|
+
* import { ProductSDKContext } from "@parity/product-sdk/react";
|
|
221
|
+
* render(<ProductSDKContext.Provider value={app}><AccountPicker /></ProductSDKContext.Provider>);
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
export function createFakeApp(options?: CreateFakeAppOptions): App {
|
|
225
|
+
const name = options?.name ?? "fake-app";
|
|
226
|
+
return {
|
|
227
|
+
wallet: makeFakeWallet(options?.wallet),
|
|
228
|
+
localStorage: options?.localStorage ?? makeFakeLocalStorage(),
|
|
229
|
+
chain: options?.chain ?? makeUnconfiguredChain(),
|
|
230
|
+
cloudStorage:
|
|
231
|
+
options?.cloudStorage !== undefined ? options.cloudStorage : makeFakeCloudStorage(),
|
|
232
|
+
getAppInfo: () => ({ name }),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (import.meta.vitest) {
|
|
237
|
+
const { describe, test, expect } = import.meta.vitest;
|
|
238
|
+
|
|
239
|
+
const alice: Account = { address: "5Alice", name: "Alice", source: "dev" };
|
|
240
|
+
const bob: Account = { address: "5Bob", name: "Bob", source: "dev" };
|
|
241
|
+
|
|
242
|
+
describe("createFakeApp", () => {
|
|
243
|
+
test("wallet lists, selects, and notifies on account change", async () => {
|
|
244
|
+
const app = createFakeApp({ wallet: { accounts: [alice, bob], selected: alice } });
|
|
245
|
+
expect(app.wallet.getAccounts()).toHaveLength(2);
|
|
246
|
+
expect(app.wallet.getSelectedAccount()).toEqual(alice);
|
|
247
|
+
|
|
248
|
+
let changed: Account | null = null;
|
|
249
|
+
app.wallet.onAccountChange((a) => {
|
|
250
|
+
changed = a;
|
|
251
|
+
});
|
|
252
|
+
app.wallet.selectAccount("5Bob");
|
|
253
|
+
expect(app.wallet.getSelectedAccount()).toEqual(bob);
|
|
254
|
+
expect(changed).toEqual(bob);
|
|
255
|
+
|
|
256
|
+
const { accounts } = await app.wallet.connect();
|
|
257
|
+
expect(accounts).toHaveLength(2);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("localStorage round-trips and clear empties it", async () => {
|
|
261
|
+
const app = createFakeApp();
|
|
262
|
+
await app.localStorage.set("k", "v");
|
|
263
|
+
await app.localStorage.setJSON("o", { n: 1 });
|
|
264
|
+
expect(await app.localStorage.get("k")).toBe("v");
|
|
265
|
+
expect(await app.localStorage.getJSON("o")).toEqual({ n: 1 });
|
|
266
|
+
expect(await app.localStorage.get("missing")).toBeNull();
|
|
267
|
+
|
|
268
|
+
await app.localStorage.clear();
|
|
269
|
+
expect(await app.localStorage.get("k")).toBeNull();
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("cloudStorage round-trips upload -> fetch; computeCid matches upload", async () => {
|
|
273
|
+
const cs = createFakeApp().cloudStorage;
|
|
274
|
+
if (!cs) throw new Error("expected default cloud storage");
|
|
275
|
+
const cid = unwrapOk(await cs.upload("hello"));
|
|
276
|
+
expect(await cs.computeCid("hello")).toBe(cid);
|
|
277
|
+
expect(new TextDecoder().decode(unwrapOk(await cs.fetch(cid)))).toBe("hello");
|
|
278
|
+
expect(unwrapErr(await cs.fetch("bafyfakemissing"))).toBeInstanceOf(
|
|
279
|
+
ProductCloudStorageError,
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("cloudStorage can be disabled with null", () => {
|
|
284
|
+
expect(createFakeApp({ cloudStorage: null }).cloudStorage).toBeNull();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("chain is unconfigured by default", () => {
|
|
288
|
+
const app = createFakeApp();
|
|
289
|
+
expect(app.chain.isConnected({} as never)).toBe(false);
|
|
290
|
+
expect(() => app.chain.getClient({} as never)).toThrow(/not configured/);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("getAppInfo reports the name", () => {
|
|
294
|
+
expect(createFakeApp({ name: "my-app" }).getAppInfo().name).toBe("my-app");
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright 2026 Parity Technologies (UK) Ltd.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* @parity/product-sdk/wallet
|
|
5
|
+
*
|
|
6
|
+
* Re-exports from @parity/product-sdk-signer.
|
|
7
|
+
*
|
|
8
|
+
* Note: The leaf package is named "signer" but exposed here as "wallet"
|
|
9
|
+
* for backwards compatibility with the umbrella API.
|
|
10
|
+
*/
|
|
11
|
+
export * from "@parity/product-sdk-signer";
|