Merge branch 'dev' into 'main'

Add support for manually stepping through tests

See merge request dao-governance-framework/science-publishing-dao!2
This commit is contained in:
Ladd Hoffman 2023-03-03 17:22:21 +00:00
commit c4a528283c
62 changed files with 694 additions and 246 deletions

View File

@ -37,6 +37,7 @@ module.exports = {
'no-multi-assign': ['off'], 'no-multi-assign': ['off'],
'no-constant-condition': ['off'], 'no-constant-condition': ['off'],
'no-await-in-loop': ['off'], 'no-await-in-loop': ['off'],
'no-underscore-dangle': ['off'],
}, },
globals: { globals: {
_: 'readonly', _: 'readonly',
@ -44,5 +45,6 @@ module.exports = {
sinon: 'readonly', sinon: 'readonly',
sinonChai: 'readonly', sinonChai: 'readonly',
should: 'readonly', should: 'readonly',
mocha: 'readonly',
}, },
}; };

View File

@ -0,0 +1,101 @@
# Revenue-generating work
## Expert stakes REP to register availability
```mermaid
graph
subgraph EOA
expert(Expert)
end
subgraph Contracts
availability(Availability)
end
expert -- 1. Stake --> availability
```
## Public submits work request with fee
```mermaid
graph
subgraph EOA
expert(Expert)
public(Public)
end
subgraph Contracts
business(Business)
availability(Availability)
end
public -- 1. Request<br />with fee $ --> business
business -- 2. Assign<br />work --> availability
availability -- 3. Transfer<br />staked --> business
availability -- 4. TODO Notify --> expert
```
## Expert submits work evidence
```mermaid
graph
subgraph EOA
expert(Expert)
end
subgraph Contracts
business(Business)
forum(Forum)
pool(Pool)
end
expert -- 1. Work<br />evidence --> business
business -- 2. Post --> forum
business -- 3. Stake --> pool
```
## Peers validate the work evidence
```mermaid
graph
subgraph EOA
peers(Peers)
end
subgraph Contracts
forum(Forum)
pool(Pool)
end
peers -- 8. Stake --> pool
pool -- 9. Validate post,<br />Transfer --> forum
```
## Rewards are distributed
```mermaid
graph
subgraph EOA
expert(Expert)
peers(Peers)
end
subgraph Contracts
pool(Pool)
business(Business)
forum(Forum)
end
pool -- Reward --> peers
forum -- Award --> expert
forum -- Award <br />via citation<br />WDAG --> peers
business -- Award % fee $<br />weighted by --> expert
business -- Award % fee $<br />weighted by --> peers
```

View File

@ -1,43 +0,0 @@
# Primary
## Forum
## ValidationPool
## ReputationToken
## WDAG
# Secondary
## Availability
## Business
## ERC721
## Expert
## Bench
# Tertiary
## Actor
## Action
## Scene
# To Explore
## Exchange
## Storage
## Network
## Wallet
## Agent/UI
## BlockConsensus

View File

@ -0,0 +1,9 @@
## Client/UI
Voting consists of staking operations performed by software operated by owners of EOA.
This software may be referred to as "The UI". It may also be considered "a client".
It will need to be a network-connected application. It will need a certain minimum of RAM,
and for some features disk storage,
and for some features uptime .

View File

@ -0,0 +1,5 @@
# Client Operations
Client must communicate with one or more servers.
Client must build a local view

View File

@ -33,3 +33,114 @@ WHAT is our protocol for evaluating the perspectives offered by peers?
- If there is the HOPE of exact agreement, mistakes and attacks can be costly - If there is the HOPE of exact agreement, mistakes and attacks can be costly
- If there is an EXPECTATION of exact agreement, there must be externalities supporting that agreement, i.e. a common protocol and governance of that protocol. - If there is an EXPECTATION of exact agreement, there must be externalities supporting that agreement, i.e. a common protocol and governance of that protocol.
---
# Internal operations
## Expert starts new DAO
```mermaid
graph
subgraph EOA
expert(Expert)
end
subgraph Contracts
forum(Forum)
pool(Pool)
end
expert -- Post --> forum
expert -- Fee $ --> pool
```
## Expert joins existing DAO
```mermaid
graph
subgraph EOA
expert(Expert)
peers(Peers)
end
subgraph Contracts
forum(Forum)
pool(Pool)
end
expert -- 1. Post --> forum
expert -- 2. Fee $ --> pool
peers -- 3. Stake <br />to approve --> pool
```
## Expert submits governance post
A governance post can be considered one that is respected in some way by the operations of the [Client/UI](./client-or-ui.md).
This is a broad class of posts.
Each type of governance post can be individually (or by category?) handled by the business contract for a given DAO.
```mermaid
graph
subgraph EOA
expert(Expert)
peers(Peers)
end
subgraph Contracts
forum(Forum)
pool(Pool)
business(Business)
end
expert -- 1. Stake on<br />governance post --> business
business -- 2. Post --> forum
business -- 2. Fee $ from<br />internal fund? --> pool
peers -- 3. Stake <br />to approve --> pool
pool -- 4. Validate --> forum
```
Forum usage is open-ended.
DAO protocol consists of core contracts + client behaviors.
Core contracts provide resilience to attacks, since reputation minting is financially backed by future income
Question: What does the DAO do with funds it receives?
Awswer: Distributes the funds to members immediately upon resolution of the reputation effects of a funded validation pool.
---
Before we delve into example use cases, we need to talk about the [Client/UI](./client-or-ui.md), and make sure we have
a sound understanding of how client/ui behaviors interact with the core of the system.
---
The forum, pool, business, and availability contracts all work together to express a single DAO.
Each post in the forum can be its own new DAO
What it would take for that to happen:
The seed of the new DAO becomes the tokens minted by that post DAO when it receives fees.
When will it receive fees? When submitted to its business contract interface.
What happens then? Work is assigned via availability stakes.
Meaning that someone has staked reputation.
Meaning that they had previously been awarded reputation.
The business contract, or the DAO, or the seed post, must be able to accept an initial fee
to mint the reputation of the first expert.
Then, that expert can stake their reputation on availability to perform the work expressed by the post and its associated business contract.
These operations can be consolidated.
When submitting a post to the forum, you may include an optional fee

View File

@ -0,0 +1 @@
Each DAO needs to allocate some of its incoming fees to incentivize development.

View File

@ -0,0 +1,30 @@
Matrix uses rooms to establish contexts.
We can have a forum context,
wherein a few things happen.
One is that the forum will have a root post; equivalently, any post can be the root of a forum.
The context of that post can be preserved.
The forum is thus a collection of posts. Each post MAY have its own internal structure.
A post MAY "replace" a prior post. This consists of on-chain and off-chain elements.
On-chain, a new post replaces a prior post.
---
Reading Matrix spec, https://spec.matrix.org/latest/
> Events are signed by the originating server (the signature includes the parent relations, type, depth and payload hash) and are pushed over federation to the participating servers in a room, currently using full mesh topology. Servers may also request backfill of events over federation from the other servers participating in a room.
> In order to ensure that the mapping from 3PID to user ID is genuine, a globally federated cluster of trusted “identity servers” (IS) are used to verify the 3PID and persist and replicate the mappings.
>
> Usage of an IS is not required in order for a client application to be part of the Matrix ecosystem. However, without one clients will not be able to look up user IDs using 3PIDs.
> Users may publish arbitrary key/value data associated with their account
>
> - such as a human-readable display name, a profile photo URL, contact information (email address, phone numbers, website URLs etc).
> Users may also store arbitrary private key/value data in their account - such as client preferences, or server configuration settings which lack any other dedicated API. The API is symmetrical to managing Profile data.
> The client-server API allows clients to send messages, control rooms and synchronise conversation history. It is designed to support both lightweight clients which store no state and lazy-load data from the server as required - as well as heavyweight clients which maintain a full local persistent copy of server state.

View File

@ -0,0 +1,4 @@
digraph {
layout=neato
}

View File

@ -0,0 +1,56 @@
```mermaid
graph
classDef blue fill:#08f, color:#d8d8d8, stroke-width: 0
classDef yellow fill:#dd0, color:#a0c, stroke-width: 0
classDef green fill:#8c8, color:#333, stroke-width: 0
classDef purple stroke:#c38, stroke-width:2px, fill:#bbf, color:#c38
classDef orange fill:#d60, color:#dff, stroke-width: 0
classDef fuscia fill:#f6c, color:#00c
nodeSpec(Node spec<br />Matrix homeservers):::blue
storageSpec(Storage spec<br />Matrix homeservers):::orange
archiveSpec(Archive spec<br />Weavechain):::green
blockchainSpec(Blockchain spec):::purple
peerProtocolSpec(Peer protocol spec<br />Matrix messaging):::yellow
uiSpec(UI spec<br />Matrix client):::fuscia
nodeSpec --- uiSpec
linkStyle 0 stroke:#08f
nodeSpec --- storageSpec
linkStyle 1 stroke:#08f
nodeSpec --- peerProtocolSpec
linkStyle 2 stroke:#08f
nodeSpec --- archiveSpec
linkStyle 3 stroke:#08f
nodeSpec --- blockchainSpec
linkStyle 4 stroke:#08f
peerProtocolSpec --- storageSpec
linkStyle 5 stroke:#d60
storageSpec --- blockchainSpec
linkStyle 6 stroke:#c38
storageSpec --- archiveSpec
linkStyle 7 stroke:#8c8
archiveSpec --- blockchainSpec
linkStyle 8 stroke:#c38
uiSpec --- blockchainSpec
linkStyle 9 stroke:#c38
uiSpec --- archiveSpec
linkStyle 10 stroke:#8c8
uiSpec --- storageSpec
linkStyle 11 stroke:#d60
```
```mermaid
graph
forum --- pool
availability --- business
business --- forum
business --- pool
```

View File

@ -1,7 +1,7 @@
import { Action } from '../display/action.js'; import { Action } from '../display/action.js';
import { PostMessage } from '../forum-network/message.js'; import { PostMessage } from '../forum-network/message.js';
import { CryptoUtil } from '../util/crypto.js'; import { CryptoUtil } from '../util/crypto.js';
import { ReputationHolder } from './reputation-holder.js'; import { ReputationHolder } from '../reputation/reputation-holder.js';
export class Expert extends ReputationHolder { export class Expert extends ReputationHolder {
constructor(dao, name, scene) { constructor(dao, name, scene) {
@ -47,7 +47,7 @@ export class Expert extends ReputationHolder {
} }
async initiateValidationPool(poolOptions) { async initiateValidationPool(poolOptions) {
// For now, directly call bench.initiateValidationPool(); // For now, make direct call rather than network
poolOptions.reputationPublicKey = this.reputationPublicKey; poolOptions.reputationPublicKey = this.reputationPublicKey;
const pool = await this.dao.initiateValidationPool(poolOptions); const pool = await this.dao.initiateValidationPool(poolOptions);
this.tokens.push(pool.tokenId); this.tokens.push(pool.tokenId);

View File

@ -1,46 +0,0 @@
import { Actor } from '../display/actor.js';
import { displayNumber } from '../../util.js';
import params from '../../params.js';
export class Post extends Actor {
constructor(forum, authorPublicKey, postContent) {
const index = forum.posts.countVertices();
const name = `Post${index + 1}`;
super(name, forum.scene);
this.id = postContent.id ?? name;
this.authorPublicKey = authorPublicKey;
this.value = 0;
this.initialValue = 0;
this.citations = postContent.citations;
this.title = postContent.title;
const leachingTotal = this.citations
.filter(({ weight }) => weight < 0)
.reduce((total, { weight }) => total += -weight, 0);
const donationTotal = this.citations
.filter(({ weight }) => weight > 0)
.reduce((total, { weight }) => total += weight, 0);
if (leachingTotal > params.revaluationLimit) {
throw new Error('Post leaching total exceeds revaluation limit '
+ `(${leachingTotal} > ${params.revaluationLimit})`);
}
if (donationTotal > params.revaluationLimit) {
throw new Error('Post donation total exceeds revaluation limit '
+ `(${donationTotal} > ${params.revaluationLimit})`);
}
if (this.citations.some(({ weight }) => Math.abs(weight) > params.revaluationLimit)) {
throw new Error(`Each citation magnitude must not exceed revaluation limit ${params.revaluationLimit}`);
}
}
getLabel() {
return `${this.name}
<table><tr>
<td>initial</td>
<td>${displayNumber(this.initialValue)}</td>
</tr><tr>
<td>value</td>
<td>${displayNumber(this.value)}</td>
</tr></table>`
.replaceAll(/\n\s*/g, '');
}
}

View File

@ -1,6 +1,6 @@
import params from '../../params.js'; import params from '../../params.js';
import { Forum } from './forum.js'; import { Forum } from './forum.js';
import { ReputationTokenContract } from '../contracts/reputation-token.js'; import { ReputationTokenContract } from '../reputation/reputation-token.js';
import { ValidationPool } from './validation-pool.js'; import { ValidationPool } from './validation-pool.js';
import { Availability } from './availability.js'; import { Availability } from './availability.js';
import { Business } from './business.js'; import { Business } from './business.js';
@ -72,9 +72,4 @@ export class DAO extends Actor {
return pool; return pool;
} }
async submitPost(reputationPublicKey, postContent) {
const post = await this.forum.addPost(reputationPublicKey, postContent);
return post.id;
}
} }

View File

@ -1,13 +1,56 @@
import { WDAG } from '../supporting/wdag.js'; import { WDAG } from '../supporting/wdag.js';
import { Action } from '../display/action.js'; import { Action } from '../display/action.js';
import { Actor } from '../display/actor.js';
import params from '../../params.js'; import params from '../../params.js';
import { ReputationHolder } from './reputation-holder.js'; import { ReputationHolder } from '../reputation/reputation-holder.js';
import { displayNumber, EPSILON } from '../../util.js'; import { displayNumber, EPSILON } from '../../util.js';
import { Post } from './post.js';
const CITATION = 'citation'; const CITATION = 'citation';
const BALANCE = 'balance'; const BALANCE = 'balance';
class Post extends Actor {
constructor(forum, authorPublicKey, postContent) {
const index = forum.posts.countVertices();
const name = `Post${index + 1}`;
super(name, forum.scene);
this.id = postContent.id ?? name;
this.authorPublicKey = authorPublicKey;
this.value = 0;
this.initialValue = 0;
this.citations = postContent.citations;
this.title = postContent.title;
const leachingTotal = this.citations
.filter(({ weight }) => weight < 0)
.reduce((total, { weight }) => total += -weight, 0);
const donationTotal = this.citations
.filter(({ weight }) => weight > 0)
.reduce((total, { weight }) => total += weight, 0);
if (leachingTotal > params.revaluationLimit) {
throw new Error('Post leaching total exceeds revaluation limit '
+ `(${leachingTotal} > ${params.revaluationLimit})`);
}
if (donationTotal > params.revaluationLimit) {
throw new Error('Post donation total exceeds revaluation limit '
+ `(${donationTotal} > ${params.revaluationLimit})`);
}
if (this.citations.some(({ weight }) => Math.abs(weight) > params.revaluationLimit)) {
throw new Error(`Each citation magnitude must not exceed revaluation limit ${params.revaluationLimit}`);
}
}
getLabel() {
return `${this.name}
<table><tr>
<td>initial</td>
<td>${displayNumber(this.initialValue)}</td>
</tr><tr>
<td>value</td>
<td>${displayNumber(this.value)}</td>
</tr></table>`
.replaceAll(/\n\s*/g, '');
}
}
/** /**
* Purpose: * Purpose:
* - Forum: Maintain a directed, acyclic, graph of positively and negatively weighted citations. * - Forum: Maintain a directed, acyclic, graph of positively and negatively weighted citations.

View File

@ -1,4 +1,4 @@
import { ReputationHolder } from './reputation-holder.js'; import { ReputationHolder } from '../reputation/reputation-holder.js';
import { Stake } from '../supporting/stake.js'; import { Stake } from '../supporting/stake.js';
import { Voter } from '../supporting/voter.js'; import { Voter } from '../supporting/voter.js';
import params from '../../params.js'; import params from '../../params.js';

View File

@ -4,6 +4,15 @@ export class Action {
this.scene = scene; this.scene = scene;
} }
/**
*
* @param src
* @param dest
* @param msg
* @param obj
* @param symbol
* @returns {Promise<void>}
*/
async log(src, dest, msg, obj, symbol = '->>') { async log(src, dest, msg, obj, symbol = '->>') {
await this.scene?.sequence?.log( await this.scene?.sequence?.log(
`${src.name} ${symbol} ${dest.name} : ${this.name} ${msg ?? ''} ${ `${src.name} ${symbol} ${dest.name} : ${this.name} ${msg ?? ''} ${

View File

@ -2,16 +2,20 @@ import { DisplayValue } from './display-value.js';
import { randomID } from '../../util.js'; import { randomID } from '../../util.js';
export class Box { export class Box {
constructor(name, parentEl, elementType = 'div') { constructor(name, parentEl, options = {}) {
this.name = name; this.name = name;
this.el = document.createElement(elementType); this.el = document.createElement('div');
this.el.id = `box_${randomID()}`; this.el.id = `box_${randomID()}`;
this.el.classList.add('box'); this.el.classList.add('box');
if (name) { if (name) {
this.el.setAttribute('box-name', name); this.el.setAttribute('box-name', name);
} }
if (parentEl) { if (parentEl) {
parentEl.appendChild(this.el); if (options.prepend) {
parentEl.prepend(this.el);
} else {
parentEl.appendChild(this.el);
}
} }
} }
@ -35,8 +39,8 @@ export class Box {
return this; return this;
} }
addBox(name, elementType) { addBox(name) {
const box = new Box(name, this.el, elementType); const box = new Box(name, this.el);
return box; return box;
} }

View File

@ -0,0 +1,52 @@
class Button {
constructor(innerHTML, onclick) {
this.el = document.createElement('button');
this.el.innerHTML = innerHTML;
this.el.onclick = onclick;
}
}
export class Controls {
constructor(parentBox) {
this.disableAutoplayButton = new Button('Disable Auto-play', () => {
const url = new URL(window.location.href);
url.searchParams.set('auto', 'false');
window.location.href = url.href;
});
this.enableAutoplayButton = new Button('Enable Auto-play', () => {
const url = new URL(window.location.href);
url.searchParams.delete('auto');
window.location.href = url.href;
});
this.stepButton = new Button('Next Action', () => {
console.log('Next Action button clicked');
document.dispatchEvent(new Event('next-action'));
});
if (window.autoPlay) {
parentBox.el.appendChild(this.disableAutoplayButton.el);
} else {
parentBox.el.appendChild(this.enableAutoplayButton.el);
parentBox.el.appendChild(this.stepButton.el);
// Disable `stepButton` when test is complete
setInterval(() => {
this.stepButton.el.disabled = mocha._state !== 'running';
}, 200);
}
}
}
export async function delayOrWait(delayMs) {
if (window.autoPlay) {
await new Promise((resolve) => {
setTimeout(resolve, delayMs);
});
} else {
await new Promise((resolve) => {
document.addEventListener('next-action', () => {
console.log('next-action event received');
resolve();
}, { once: true });
});
}
}

View File

@ -4,6 +4,8 @@ import { MermaidDiagram } from './mermaid.js';
import { SequenceDiagram } from './sequence.js'; import { SequenceDiagram } from './sequence.js';
import { Table } from './table.js'; import { Table } from './table.js';
import { Flowchart } from './flowchart.js'; import { Flowchart } from './flowchart.js';
import { Controls } from './controls.js';
import { Box } from './box.js';
export class Scene { export class Scene {
constructor(name, rootBox) { constructor(name, rootBox) {
@ -24,6 +26,14 @@ export class Scene {
this.options = { this.options = {
edgeNodeColor: '#4d585c', edgeNodeColor: '#4d585c',
}; };
window.autoPlay = new URL(window.location.href).searchParams.get('auto') !== 'false';
if (!window.disableSceneControls) {
this.topRail = new Box('Top rail', document.body, { prepend: true }).addClass('top-rail');
this.controlsBox = this.topRail.addBox('Controls').addClass('controls');
this.controls = new Controls(this.controlsBox);
}
} }
withSequenceDiagram() { withSequenceDiagram() {

View File

@ -42,4 +42,8 @@ export class Table {
cell.innerHTML = typeof value === 'number' ? displayNumber(value) : value ?? ''; cell.innerHTML = typeof value === 'number' ? displayNumber(value) : value ?? '';
} }
} }
listUniqueValueKeys() {
return this.columns; // TODO
}
} }

View File

@ -4,7 +4,7 @@ import {
} from './message.js'; } from './message.js';
import { ForumView } from './forum-view.js'; import { ForumView } from './forum-view.js';
import { NetworkNode } from './network-node.js'; import { NetworkNode } from './network-node.js';
import { randomID } from '../../util.js'; import { randomID } from '../util/util.js';
export class ForumNode extends NetworkNode { export class ForumNode extends NetworkNode {
constructor(name, scene) { constructor(name, scene) {

View File

@ -0,0 +1,45 @@
import { DAO } from '../actors/dao.js';
/**
* An Exchange provides conversion between currencies / facilitates such.
* That means they carry some of the risk of managing these transactions.
*
*/
class Offer {
constructor({
_fromType, _toType, _amount, price,
}) {
this.price = price;
// const from =
}
}
export class Exchange extends DAO {
constructor(name, scene) {
super(name, scene);
this.offersByType = new Map(); // <OfferType, Map<OfferId, Offer>>
}
getOffers(offerType) {
return this.buyOffersByType.get(offerType) ?? new Map(); // <
}
addOffer(offerOptions) {
const offer = new Offer(offerOptions);
const { fromType, toType } = offer;
[fromType, toType].forEach((type) => {
this.offersByType.s();
});
}
requestTransaction({
fromType, toType, amount, price,
}) {
// this.
}
buy(fromType, toType, _priceParameters) {
const buyOffer = {
};
}
}

View File

@ -1,10 +1,6 @@
export class Token { /**
constructor(ownerPublicKey) { * Finance does the work of analysis of the dynamics of reputation and currency exchange
this.ownerPublicKey = ownerPublicKey; */
}
transfer(newOwnerPublicKey) { export class Finance {
// TODO: Current owner must sign this request
this.ownerPublicKey = newOwnerPublicKey;
}
} }

View File

@ -0,0 +1,24 @@
import { randomID } from '../util/util.js';
class Pledge {
constructor({ stake, duration }) {
this.stake = stake;
this.duration = duration;
}
}
/**
* Storage work is providing data availability and integrity.
* It probably makes sense to manage it in pledges of finite duration.
*/
export class Storage {
constructor() {
this.pledges = new Map();
}
pledge(pledgeOptions) {
const id = randomID();
this.pledge.set(id, new Pledge(pledgeOptions));
return id;
}
}

View File

@ -1,4 +1,4 @@
import { ERC721 } from './erc721.js'; import { ERC721 } from '../supporting/erc721.js';
import { EPSILON, randomID } from '../../util.js'; import { EPSILON, randomID } from '../../util.js';
@ -19,7 +19,14 @@ export class ReputationTokenContract extends ERC721 {
this.locks = new Set(); // {tokenId, amount, start, duration} this.locks = new Set(); // {tokenId, amount, start, duration}
} }
mint(to, value, context) { /**
*
* @param to
* @param value
* @param context
* @returns {string}
*/
mint(to, value, context = {}) {
const tokenId = `token_${randomID()}`; const tokenId = `token_${randomID()}`;
super.mint(to, tokenId); super.mint(to, tokenId);
this.values.set(tokenId, value); this.values.set(tokenId, value);

View File

@ -36,6 +36,17 @@ a:visited {
.padded { .padded {
padding: 20px; padding: 20px;
} }
.top-rail {
position: sticky;
top: 0;
left: 0;
width: 100%;
height: 0;
}
.controls {
position: relative;
left: 150px;
}
svg { svg {
width: 800px; width: 800px;
} }
@ -49,3 +60,14 @@ td {
.edge > rect { .edge > rect {
fill: #216262 !important; fill: #216262 !important;
} }
button {
margin: 5px;
margin-top: 1em;
background-color: #c6f4ff;
border-color: #b6b6b6;
border-radius: 5px;
}
button:disabled {
background-color: #2a535e;
color: #919191;
}

View File

@ -12,6 +12,9 @@
<div id="mocha"></div> <div id="mocha"></div>
<div id="scene"></div> <div id="scene"></div>
</body> </body>
<script>
window.disableSceneControls = true;
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js"
integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg==" integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script> crossorigin="anonymous" referrerpolicy="no-referrer"></script>
@ -32,12 +35,6 @@
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['scene', 'dao', 'experts', 'posts', 'vm', 'graph', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
window.should = chai.should(); window.should = chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -26,12 +26,6 @@
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['scene', 'dao', 'experts', 'requestor', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
window.should = chai.should(); window.should = chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -15,18 +15,16 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js"
integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg==" integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script> crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://unpkg.com/mocha/mocha.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/10.2.0/mocha.min.js"
<script src="https://unpkg.com/chai/chai.js"></script> integrity="sha512-jsP/sG70bnt0xNVJt+k9NxQqGYvRrLzWhI+46SSf7oNJeCwdzZlBvoyrAN0zhtVyolGcHNh/9fEgZppG2pH+eA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/4.3.7/chai.min.js"
integrity="sha512-tfLUmTr4u39/6Pykb8v/LjLaQ9u/uSgbHtZXFCtT9bOsZd1ZPZabIrwhif/YzashftTOhwwQUC0cQyrnIC1vEQ=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="module" src="./scripts/business.test.js"></script> <script type="module" src="./scripts/business.test.js"></script>
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['scene', 'dao', 'experts', 'posts', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
window.should = chai.should(); window.should = chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -25,12 +25,6 @@
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['scene', 'dao', 'experts', 'posts', 'vm', 'graph', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
window.should = chai.should(); window.should = chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -10,10 +10,10 @@
<div id="flowchart-test"></div> <div id="flowchart-test"></div>
</body> </body>
<script type="module"> <script type="module">
import { Box } from '../classes/box.js'; import { Box } from '../classes/display/box.js';
import { Scene } from '../classes/scene.js'; import { Scene } from '../classes/display/scene.js';
import { Actor } from '../classes/actor.js'; import { Actor } from '../classes/display/actor.js';
import { Action } from '../classes/action.js'; import { Action } from '../classes/display/action.js';
import { delay } from '../util.js'; import { delay } from '../util.js';
const DEFAULT_DELAY_INTERVAL = 500; const DEFAULT_DELAY_INTERVAL = 500;

View File

@ -16,18 +16,16 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js" <script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js"
integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg==" integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script> crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://unpkg.com/mocha/mocha.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/10.2.0/mocha.min.js"
<script src="https://unpkg.com/chai/chai.js"></script> integrity="sha512-jsP/sG70bnt0xNVJt+k9NxQqGYvRrLzWhI+46SSf7oNJeCwdzZlBvoyrAN0zhtVyolGcHNh/9fEgZppG2pH+eA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/4.3.7/chai.min.js"
integrity="sha512-tfLUmTr4u39/6Pykb8v/LjLaQ9u/uSgbHtZXFCtT9bOsZd1ZPZabIrwhif/YzashftTOhwwQUC0cQyrnIC1vEQ=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script type="module" src="./scripts/forum-network.test.js"></script> <script type="module" src="./scripts/forum-network.test.js"></script>
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['scene', 'dao', 'experts', 'posts', 'vm', 'graph', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
window.should = chai.should(); window.should = chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -21,12 +21,6 @@
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['scene', 'dao', 'experts', 'posts', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
chai.should(); chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -21,12 +21,6 @@
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['scene', 'dao', 'experts', 'posts', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
chai.should(); chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -21,12 +21,6 @@
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['scene', 'dao', 'experts', 'posts', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
chai.should(); chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -21,12 +21,6 @@
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['scene', 'dao', 'experts', 'posts', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
chai.should(); chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -21,12 +21,6 @@
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['scene', 'dao', 'experts', 'posts', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
chai.should(); chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -20,7 +20,6 @@
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
}); });
mocha.checkLeaks();
chai.should(); chai.should();
</script> </script>
<script src="./scripts/mocha.test.js"></script> <script src="./scripts/mocha.test.js"></script>

View File

@ -10,8 +10,8 @@
<div id="scene"></div> <div id="scene"></div>
</body> </body>
<script type="module"> <script type="module">
import { Box } from '../classes/box.js'; import { Box } from '../classes/display/box.js';
import { Scene } from '../classes/scene.js'; import { Scene } from '../classes/display/scene.js';
// import { ValidationPool } from '../classes/validation-pool.js'; // import { ValidationPool } from '../classes/validation-pool.js';
// import { TokenHolder } from '../classes/token-holder.js'; // import { TokenHolder } from '../classes/token-holder.js';
// import { ReputationToken } from '../classes/reputation-token.js'; // import { ReputationToken } from '../classes/reputation-token.js';

View File

@ -1,10 +1,11 @@
import { Box } from '../../classes/display/box.js'; import { Box } from '../../classes/display/box.js';
import { Scene } from '../../classes/display/scene.js'; import { Scene } from '../../classes/display/scene.js';
import { Expert } from '../../classes/actors/expert.js'; import { Expert } from '../../classes/actors/expert.js';
import { delay } from '../../util.js'; import { DAO } from '../../classes/dao/dao.js';
import { DAO } from '../../classes/actors/dao.js';
import { Public } from '../../classes/actors/public.js'; import { Public } from '../../classes/actors/public.js';
import { PostContent } from '../../classes/util/post-content.js'; import { PostContent } from '../../classes/util/post-content.js';
import { delayOrWait } from '../../classes/display/controls.js';
import { mochaRun } from '../../util.js';
const DELAY_INTERVAL = 100; const DELAY_INTERVAL = 100;
const POOL_DURATION = 200; const POOL_DURATION = 200;
@ -43,7 +44,7 @@ const setup = async () => {
await newExpert(); await newExpert();
requestor = new Public('Public', scene); requestor = new Public('Public', scene);
await delay(DELAY_INTERVAL); await delayOrWait(DELAY_INTERVAL);
// Experts gain initial reputation by submitting a post with fee // Experts gain initial reputation by submitting a post with fee
const { postId: postId1, pool: pool1 } = await experts[0].submitPostWithFee( const { postId: postId1, pool: pool1 } = await experts[0].submitPostWithFee(
@ -54,10 +55,10 @@ const setup = async () => {
tokenLossRatio: 1, tokenLossRatio: 1,
}, },
); );
await delay(POOL_DURATION); await delayOrWait(POOL_DURATION);
await pool1.evaluateWinningConditions(); await pool1.evaluateWinningConditions();
await delay(DELAY_INTERVAL); await delayOrWait(DELAY_INTERVAL);
dao.reputation.valueOwnedBy(experts[0].reputationPublicKey).should.equal(10); dao.reputation.valueOwnedBy(experts[0].reputationPublicKey).should.equal(10);
@ -71,10 +72,10 @@ const setup = async () => {
tokenLossRatio: 1, tokenLossRatio: 1,
}, },
); );
await delay(POOL_DURATION); await delayOrWait(POOL_DURATION);
await pool2.evaluateWinningConditions(); await pool2.evaluateWinningConditions();
await delay(DELAY_INTERVAL); await delayOrWait(DELAY_INTERVAL);
dao.reputation.valueOwnedBy(experts[0].reputationPublicKey).should.equal(15); dao.reputation.valueOwnedBy(experts[0].reputationPublicKey).should.equal(15);
dao.reputation.valueOwnedBy(experts[1].reputationPublicKey).should.equal(5); dao.reputation.valueOwnedBy(experts[1].reputationPublicKey).should.equal(5);
@ -106,7 +107,9 @@ const voteForWorkEvidence = async (worker, pool) => {
} }
}; };
describe('Availability + Business', () => { describe('Availability + Business', function tests() {
this.timeout(0);
before(async () => { before(async () => {
await setup(); await setup();
}); });
@ -122,7 +125,7 @@ describe('Availability + Business', () => {
it('Experts can register their availability for some duration', async () => { it('Experts can register their availability for some duration', async () => {
await experts[0].registerAvailability(1, 10000); await experts[0].registerAvailability(1, 10000);
await experts[1].registerAvailability(1, 10000); await experts[1].registerAvailability(1, 10000);
await delay(DELAY_INTERVAL); await delayOrWait(DELAY_INTERVAL);
}); });
it('Public can submit a work request', async () => { it('Public can submit a work request', async () => {
@ -131,7 +134,7 @@ describe('Availability + Business', () => {
{ fee: 100 }, { fee: 100 },
{ please: 'do some work' }, { please: 'do some work' },
); );
await delay(DELAY_INTERVAL); await delayOrWait(DELAY_INTERVAL);
}); });
it('Expert can submit work evidence', async () => { it('Expert can submit work evidence', async () => {
@ -153,11 +156,11 @@ describe('Availability + Business', () => {
await voteForWorkEvidence(worker, pool3); await voteForWorkEvidence(worker, pool3);
// Wait for validation pool duration to elapse // Wait for validation pool duration to elapse
await delay(POOL_DURATION); await delayOrWait(POOL_DURATION);
// Distribute reputation awards and fees // Distribute reputation awards and fees
await pool3.evaluateWinningConditions(); await pool3.evaluateWinningConditions();
await delay(DELAY_INTERVAL); await delayOrWait(DELAY_INTERVAL);
// This should throw an exception since the pool is already resolved // This should throw an exception since the pool is already resolved
try { try {
@ -167,3 +170,5 @@ describe('Availability + Business', () => {
} }
}).timeout(10000); }).timeout(10000);
}); });
mochaRun();

View File

@ -1,8 +1,10 @@
import { Business } from '../../classes/actors/business.js'; import { Business } from '../../classes/dao/business.js';
import { Scene } from '../../classes/display/scene.js'; import { Scene } from '../../classes/display/scene.js';
import { Box } from '../../classes/display/box.js'; import { Box } from '../../classes/display/box.js';
import { mochaRun } from '../../util.js';
describe('Business', () => { describe('Business', function tests() {
this.timeout(0);
let scene; let scene;
before(async () => { before(async () => {
const rootElement = document.getElementById('scene'); const rootElement = document.getElementById('scene');
@ -14,3 +16,5 @@ describe('Business', () => {
should.exist(business); should.exist(business);
}); });
}); });
mochaRun();

View File

@ -2,7 +2,7 @@ import { Box } from '../../classes/display/box.js';
import { Scene } from '../../classes/display/scene.js'; import { Scene } from '../../classes/display/scene.js';
import { Action } from '../../classes/display/action.js'; import { Action } from '../../classes/display/action.js';
import { Actor } from '../../classes/display/actor.js'; import { Actor } from '../../classes/display/actor.js';
import { debounce, delay } from '../../util.js'; import { debounce, delay, mochaRun } from '../../util.js';
describe('Debounce', () => { describe('Debounce', () => {
let scene; let scene;
@ -70,3 +70,5 @@ describe('Debounce', () => {
await scene.sequence.endSection(); await scene.sequence.endSection();
}); });
}); });
mochaRun();

View File

@ -4,9 +4,12 @@ import { PostContent } from '../../classes/util/post-content.js';
import { Expert } from '../../classes/actors/expert.js'; import { Expert } from '../../classes/actors/expert.js';
import { ForumNode } from '../../classes/forum-network/forum-node.js'; import { ForumNode } from '../../classes/forum-network/forum-node.js';
import { Network } from '../../classes/forum-network/network.js'; import { Network } from '../../classes/forum-network/network.js';
import { delay, randomID } from '../../util.js'; import { mochaRun, randomID } from '../../util.js';
import { delayOrWait } from '../../classes/display/controls.js';
describe('Forum Network', function tests() {
this.timeout(0);
describe('Forum Network', () => {
let scene; let scene;
let author1; let author1;
let author2; let author2;
@ -58,19 +61,21 @@ describe('Forum Network', () => {
1.0, 1.0,
); );
await delay(1000); await delayOrWait(1000);
await author1.submitPostViaNetwork( await author1.submitPostViaNetwork(
forumNode1, forumNode1,
post1, post1,
50, 50,
); );
await delay(1000); await delayOrWait(1000);
await author2.submitPostViaNetwork( await author2.submitPostViaNetwork(
forumNode2, forumNode2,
post2, post2,
100, 100,
); );
await delay(1000); await delayOrWait(1000);
}).timeout(10000); }).timeout(10000);
}); });
mochaRun();

View File

@ -2,9 +2,9 @@ import { Box } from '../../../classes/display/box.js';
import { Scene } from '../../../classes/display/scene.js'; import { Scene } from '../../../classes/display/scene.js';
import { Expert } from '../../../classes/actors/expert.js'; import { Expert } from '../../../classes/actors/expert.js';
import { PostContent } from '../../../classes/util/post-content.js'; import { PostContent } from '../../../classes/util/post-content.js';
import { delay } from '../../../util.js';
import params from '../../../params.js'; import params from '../../../params.js';
import { DAO } from '../../../classes/actors/dao.js'; import { DAO } from '../../../classes/dao/dao.js';
import { delayOrWait } from '../../../classes/display/controls.js';
export class ForumTest { export class ForumTest {
constructor(options) { constructor(options) {
@ -38,10 +38,10 @@ export class ForumTest {
}, },
); );
this.posts.push(postId); this.posts.push(postId);
await delay(this.options.poolDurationMs); await delayOrWait(this.options.poolDurationMs);
await pool.evaluateWinningConditions(); await pool.evaluateWinningConditions();
await this.scene.sequence.endSection(); await this.scene.sequence.endSection();
await delay(this.options.defaultDelayMs); await delayOrWait(this.options.defaultDelayMs);
return postId; return postId;
} }

View File

@ -1,6 +1,9 @@
import { mochaRun } from '../../../util.js';
import { ForumTest } from './forum.test-util.js'; import { ForumTest } from './forum.test-util.js';
describe('Forum', () => { describe('Forum', function tests() {
this.timeout(0);
const forumTest = new ForumTest(); const forumTest = new ForumTest();
before(async () => { before(async () => {
@ -37,3 +40,5 @@ describe('Forum', () => {
// await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]); // await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]);
// await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]); // await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]);
mochaRun();

View File

@ -1,6 +1,9 @@
import { mochaRun } from '../../../util.js';
import { ForumTest } from './forum.test-util.js'; import { ForumTest } from './forum.test-util.js';
describe('Forum', () => { describe('Forum', function tests() {
this.timeout(0);
const forumTest = new ForumTest(); const forumTest = new ForumTest();
before(async () => { before(async () => {
@ -37,3 +40,5 @@ describe('Forum', () => {
// await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]); // await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]);
// await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]); // await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]);
mochaRun();

View File

@ -1,6 +1,9 @@
import { mochaRun } from '../../../util.js';
import { ForumTest } from './forum.test-util.js'; import { ForumTest } from './forum.test-util.js';
describe('Forum', () => { describe('Forum', function tests() {
this.timeout(0);
const forumTest = new ForumTest(); const forumTest = new ForumTest();
before(async () => { before(async () => {
@ -40,3 +43,5 @@ describe('Forum', () => {
// await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]); // await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]);
// await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]); // await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]);
mochaRun();

View File

@ -1,6 +1,9 @@
import { mochaRun } from '../../../util.js';
import { ForumTest } from './forum.test-util.js'; import { ForumTest } from './forum.test-util.js';
describe('Forum', () => { describe('Forum', function tests() {
this.timeout(0);
const forumTest = new ForumTest(); const forumTest = new ForumTest();
before(async () => { before(async () => {
@ -68,3 +71,5 @@ describe('Forum', () => {
// await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]); // await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]);
// await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]); // await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]);
mochaRun();

View File

@ -1,6 +1,9 @@
import { mochaRun } from '../../../util.js';
import { ForumTest } from './forum.test-util.js'; import { ForumTest } from './forum.test-util.js';
describe('Forum', () => { describe('Forum', function tests() {
this.timeout(0);
const forumTest = new ForumTest(); const forumTest = new ForumTest();
before(async () => { before(async () => {
@ -57,3 +60,5 @@ describe('Forum', () => {
// await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]); // await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]);
// await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]); // await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]);
mochaRun();

View File

@ -0,0 +1,14 @@
/**
* We want to be able to define tests essentially as sequences of actions by actors,
* with assertions about various values after each action.
*
* We also want the following features:
* - Advance tests automatically, for use as automated regression tests.
* - Advance tests manually, for use as a visual demo on a web page.
* - Nice to have: Manually add/remove/modify action steps.
* - Nice to have: Manually rewind actions to view prior states.
* - Nice to have: Export/import sequences of action steps.
*/
export class Test {
}

View File

@ -2,8 +2,9 @@ import { Box } from '../../classes/display/box.js';
import { Scene } from '../../classes/display/scene.js'; import { Scene } from '../../classes/display/scene.js';
import { Expert } from '../../classes/actors/expert.js'; import { Expert } from '../../classes/actors/expert.js';
import { PostContent } from '../../classes/util/post-content.js'; import { PostContent } from '../../classes/util/post-content.js';
import { delay } from '../../util.js'; import { DAO } from '../../classes/dao/dao.js';
import { DAO } from '../../classes/actors/dao.js'; import { delayOrWait } from '../../classes/display/controls.js';
import { mochaRun } from '../../util.js';
const POOL_DURATION_MS = 100; const POOL_DURATION_MS = 100;
const DEFAULT_DELAY_MS = 100; const DEFAULT_DELAY_MS = 100;
@ -35,10 +36,11 @@ async function setup() {
await newExpert(); await newExpert();
await newExpert(); await newExpert();
await delay(DEFAULT_DELAY_MS); await delayOrWait(DEFAULT_DELAY_MS);
} }
describe('Validation Pool', () => { describe('Validation Pool', function tests() {
this.timeout(0);
before(async () => { before(async () => {
await setup(); await setup();
}); });
@ -73,9 +75,9 @@ describe('Validation Pool', () => {
} }
} }
await scene.sequence.endSection(); await scene.sequence.endSection();
await delay(POOL_DURATION_MS); await delayOrWait(POOL_DURATION_MS);
await pool.evaluateWinningConditions(); // Vote passes await pool.evaluateWinningConditions(); // Vote passes
await delay(DEFAULT_DELAY_MS); await delayOrWait(DEFAULT_DELAY_MS);
}); });
it('Failure example: second expert can not self-approve', async () => { it('Failure example: second expert can not self-approve', async () => {
@ -85,9 +87,9 @@ describe('Validation Pool', () => {
duration: POOL_DURATION_MS, duration: POOL_DURATION_MS,
tokenLossRatio: 1, tokenLossRatio: 1,
}); });
await delay(POOL_DURATION_MS); await delayOrWait(POOL_DURATION_MS);
await pool.evaluateWinningConditions(); // Quorum not met! await pool.evaluateWinningConditions(); // Quorum not met!
await delay(DEFAULT_DELAY_MS); await delayOrWait(DEFAULT_DELAY_MS);
} catch (e) { } catch (e) {
e.message.should.match(/Quorum is not met/); e.message.should.match(/Quorum is not met/);
} }
@ -104,8 +106,10 @@ describe('Validation Pool', () => {
amount: 4, amount: 4,
lockingTime: 0, lockingTime: 0,
}); });
await delay(POOL_DURATION_MS); await delayOrWait(POOL_DURATION_MS);
await pool.evaluateWinningConditions(); // Stake passes await pool.evaluateWinningConditions(); // Stake passes
await delay(DEFAULT_DELAY_MS); await delayOrWait(DEFAULT_DELAY_MS);
}); });
}); });
mochaRun();

View File

@ -2,6 +2,7 @@ import { Actor } from '../../classes/display/actor.js';
import { Box } from '../../classes/display/box.js'; import { Box } from '../../classes/display/box.js';
import { Scene } from '../../classes/display/scene.js'; import { Scene } from '../../classes/display/scene.js';
import { VM } from '../../classes/supporting/vm.js'; import { VM } from '../../classes/supporting/vm.js';
import { mochaRun } from '../../util.js';
const contractIds = ['contract-id-1', 'contract-id-2']; const contractIds = ['contract-id-1', 'contract-id-2'];
@ -29,7 +30,9 @@ class Repeater extends Actor {
} }
} }
describe('VM', () => { describe('VM', function tests() {
this.timeout(0);
let vm; let vm;
let sender; let sender;
let vmHandle; let vmHandle;
@ -68,3 +71,5 @@ describe('VM', () => {
.should.equal('Repeater world: good day'); .should.equal('Repeater world: good day');
}); });
}); });
mochaRun();

View File

@ -1,12 +1,15 @@
import { Box } from '../../classes/display/box.js'; import { Box } from '../../classes/display/box.js';
import { Scene } from '../../classes/display/scene.js'; import { Scene } from '../../classes/display/scene.js';
import { WDAG } from '../../classes/supporting/wdag.js'; import { WDAG } from '../../classes/supporting/wdag.js';
import { mochaRun } from '../../util.js';
const rootElement = document.getElementById('scene'); const rootElement = document.getElementById('scene');
const rootBox = new Box('rootBox', rootElement).flex(); const rootBox = new Box('rootBox', rootElement).flex();
window.scene = new Scene('WDAG test', rootBox); window.scene = new Scene('WDAG test', rootBox);
describe('Query the graph', () => { describe('Query the graph', function tests() {
this.timeout(0);
let graph; let graph;
before(() => { before(() => {
@ -43,3 +46,5 @@ describe('Query the graph', () => {
]); ]);
}); });
}); });
mochaRun();

View File

@ -25,12 +25,6 @@
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['scene', 'dao', 'experts', 'posts', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
chai.should(); chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -21,12 +21,6 @@
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['vm', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
window.should = chai.should(); window.should = chai.should();
</script> </script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -21,13 +21,7 @@
<script defer class="mocha-init"> <script defer class="mocha-init">
mocha.setup({ mocha.setup({
ui: 'bdd', ui: 'bdd',
globals: ['graph', '__REACT_DEVTOOLS_*'],
}); });
mocha.checkLeaks();
chai.should(); chai.should();
</script> </script>
<script type="module" src="./scripts/wdag.test.js"></script> <script type="module" src="./scripts/wdag.test.js"></script>
<script defer class="mocha-exec">
// TODO: Weird race condition -- resolve this in a better way
setTimeout(() => mocha.run(), 1000);
</script>

View File

@ -38,3 +38,9 @@ export const displayNumber = (value, decimals = 2) => (value.toString().length >
: value); : value);
export const randomID = () => CryptoUtil.randomUUID().replaceAll('-', '').slice(0, 8); export const randomID = () => CryptoUtil.randomUUID().replaceAll('-', '').slice(0, 8);
export const mochaRun = () => {
if (mocha._state !== 'running') {
mocha.run();
}
};