forked from yvv/decision
- Le vote se déplie sous l'article, façon accordéon, l'URL pour état (?vote=) :
niveau d'eau, totaux, seuil et voix manquantes, ton vote et où il en est
(enregistré, inscrit sur la chaîne, en attente, à reconfirmer, non compté),
inertie et formule. Le reste d'une version (coquilles, traductions,
signalements, historique) passe dans l'onglet « Détails » du panneau.
- Le panier : article par article, « tout pour / tout contre » par section ou
pour tout le document, jamais pour un article à plusieurs versions ; un seul
geste (une écriture, ou une transaction).
- La signature : le portefeuille de la page (injectedWeb3), polkadot-api chargé
au moment de signer ; sans portefeuille, Ğecko par lien et QR ; la page
attend la confirmation. Un bulletin que le portefeuille n'a pas signé est
lâché aussitôt (DELETE /api/ballots/{id}).
- Pastilles d'ambiance : flèches dans le groupe, anneau de focus pointillé à
la couleur d'accent de la page (3:1 au moins), distinct du choix.
- e2e : chaîne simulée (indexeur et nœud), faux portefeuille ; C21–C24, et
C4, C5, C6, C11, C17, C19, C20, M1 mis à jour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
42 lines
2.2 KiB
TypeScript
42 lines
2.2 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { amountText, geckoLink } from '../../src/lib/wallet/gecko';
|
|
import { failure, injectedNames, testExtrinsic } from '../../src/lib/wallet/injected';
|
|
|
|
const ACCOUNT = 'g1L5EYvvSBaYuQCPW78bHxYcvHfmWAqHYe3Gsrp9SV4kWZaEM';
|
|
const COMMENT = `ld1:${'ab'.repeat(32)}`;
|
|
|
|
describe('Ğecko', () => {
|
|
it('the link and the QR code carry the account, the amount in Ğ1 and the sealed comment', () => {
|
|
expect(geckoLink(ACCOUNT, 10, COMMENT)).toBe(`g1://${ACCOUNT}?amount=0.1&comment=${COMMENT}`);
|
|
// Ğ1nkgo keeps [A-Za-z0-9 .;:!?()-_@&<>%]: the comment passes untouched.
|
|
expect(/^[A-Za-z0-9 .;:!?()\-_@&<>%]+$/.test(COMMENT)).toBe(true);
|
|
});
|
|
|
|
it('amounts in cents read as Ğ1', () => {
|
|
expect([amountText(10), amountText(100), amountText(125), amountText(1)]).toEqual(['0.1', '1', '1.25', '0.01']);
|
|
});
|
|
});
|
|
|
|
describe('wallets of the page', () => {
|
|
it('every extension offering the standard interface is found, the one for Ğ1 first', () => {
|
|
const win = { injectedWeb3: { 'polkadot-js': { enable: async () => ({}) }, 'duniter-connect': { enable: async () => ({}) }, broken: {} } };
|
|
expect(injectedNames(win)).toEqual(['duniter-connect', 'polkadot-js']);
|
|
expect(injectedNames({})).toEqual([]);
|
|
});
|
|
|
|
it('what a wallet answers is told in words', () => {
|
|
expect(failure(new Error('Cancelled'))).toBe('cancelled');
|
|
expect(failure(new Error('Rejected by user'))).toBe('cancelled');
|
|
expect(failure({ type: 'Invalid', value: { type: 'Payment' } })).toBe('funds');
|
|
expect(failure(new Error('Inability to pay some fees (e.g. account balance too low)'))).toBe('funds');
|
|
expect(failure(new Error('socket hang up'))).toBe('wallet');
|
|
});
|
|
|
|
it('in tests, the fake node receives what was signed, readable', () => {
|
|
const hex = testExtrinsic({ address: '5Grw', dest: ACCOUNT, amount: '10', remark: COMMENT, signature: '0x01' });
|
|
expect(hex.startsWith('0x')).toBe(true);
|
|
const back = JSON.parse(new TextDecoder().decode(Uint8Array.from((hex.slice(2).match(/../g) ?? []).map((b) => parseInt(b, 16)))));
|
|
expect(back).toEqual({ address: '5Grw', dest: ACCOUNT, amount: '10', remark: COMMENT, signature: '0x01' });
|
|
});
|
|
});
|