Minor refactoring

This commit is contained in:
Ladd Hoffman 2023-01-29 04:38:28 -06:00
parent e20a864738
commit f475742cbf
26 changed files with 384 additions and 198 deletions

View File

@ -30,4 +30,18 @@ module.exports = {
'no-constant-condition': ['off'],
'no-await-in-loop': ['off'],
},
globals: {
_: 'readonly',
chai: 'readonly',
expect: 'readonly',
mocha: 'readonly',
describe: 'readonly',
context: 'readonly',
it: 'readonly',
specify: 'readonly',
before: 'readonly',
after: 'readonly',
beforeEach: 'readonly',
afterEach: 'readonly',
},
};

View File

View File

@ -1,4 +1,4 @@
import { Graph } from './graph.js';
import { WDAG } from './wdag.js';
class Author {
constructor() {
@ -21,7 +21,7 @@ class PostVertex {
export class ForumView {
constructor() {
this.reputations = new Map();
this.posts = new Graph();
this.posts = new WDAG();
this.authors = new Map();
}
@ -53,8 +53,8 @@ export class ForumView {
const postVertex = new PostVertex(postId, author, stake, content, citations);
console.log('addPost', { id: postId, postContent });
this.posts.addVertex(postId, postVertex);
for (const citation of citations) {
this.posts.addEdge('citation', postId, citation.postId, citation);
for (const { postId: citedPostId, weight } of citations) {
this.posts.addEdge('citation', postId, citedPostId, weight);
}
this.applyNonbindingReputationEffects(postVertex);
}

View File

@ -1,17 +1,20 @@
import { Actor } from './actor.js';
import { Graph } from './graph.js';
import { WDAG } from './wdag.js';
import { Action } from './action.js';
import { CryptoUtil } from './crypto.js';
import params from '../params.js';
import { ReputationHolder } from './reputation-holder.js';
import { displayNumber } from '../util.js';
const CITATION = 'citation';
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 ?? `post_${CryptoUtil.randomUUID()}`;
this.id = postContent.id ?? `post_${CryptoUtil.randomUUID().slice(0, 4)}`;
this.authorPublicKey = authorPublicKey;
this.value = 0;
this.initialValue = 0;
@ -47,7 +50,7 @@ export class Forum extends ReputationHolder {
constructor(name, scene) {
super(`forum_${CryptoUtil.randomUUID()}`, name, scene);
this.id = this.reputationPublicKey;
this.posts = new Graph(scene);
this.posts = new WDAG(scene);
this.actions = {
addPost: new Action('add post', scene),
propagateValue: new Action('propagate value', this.scene),
@ -60,7 +63,7 @@ export class Forum extends ReputationHolder {
await this.actions.addPost.log(this, post);
this.posts.addVertex(post.id, post, post.getLabel());
for (const { postId: citedPostId, weight } of post.citations) {
this.posts.addEdge('citation', post.id, citedPostId, { weight });
this.posts.addEdge(CITATION, post.id, citedPostId, weight);
}
return post.id;
}
@ -88,7 +91,8 @@ export class Forum extends ReputationHolder {
}) {
this.activate();
const initialValue = bench.reputation.valueOf(tokenId);
const post = this.getPost(postId);
const postVertex = this.posts.getVertex(postId);
const post = postVertex.data;
post.setStatus('Validated');
post.initialValue = initialValue;
this.posts.setVertexLabel(post.id, post.getLabel());
@ -97,9 +101,13 @@ export class Forum extends ReputationHolder {
// so that its value can be updated by future validated posts.
post.tokenId = tokenId;
// Compute rewards
const rewardsAccumulator = new Map();
await this.propagateValue(rewardsAccumulator, this, post, initialValue);
// Compute rewards
await this.propagateValue(
{ to: postVertex, from: { data: this } },
{ rewardsAccumulator, increment: initialValue },
);
// Apply computed rewards to update values of tokens
for (const [id, value] of rewardsAccumulator) {
@ -118,46 +126,62 @@ export class Forum extends ReputationHolder {
this.deactivate();
}
async propagateValue(rewardsAccumulator, fromActor, post, increment, depth = 0) {
this.actions.propagateValue.log(fromActor, post, `(${increment})`);
/**
* @param {Edge} edge
* @param {Object} opaqueData
*/
async propagateValue(edge, { rewardsAccumulator, increment, depth = 0 }) {
const postVertex = edge.to;
const post = postVertex?.data;
this.actions.propagateValue.log(edge.from.data, post, `(${increment})`);
console.log('propagateValue start', {
from: edge.from.id,
to: edge.to.id,
depth,
value: post.value,
increment,
});
// Recursively distribute reputation to citations, according to weights
let totalOutboundAmount = 0;
if (params.referenceChainLimit === null || depth <= params.referenceChainLimit) {
for (const { postId: citedPostId, weight } of post.citations) {
const citedPost = this.getPost(citedPostId);
for (const citationEdge of this.posts.getEdges(CITATION, post)) {
const { to: citedPostVertex, data: { weight } } = citationEdge;
const citedPost = citedPostVertex.data;
let outboundAmount = weight * increment;
// If this is a negative citation, it must not bring the target below zero value.
const balance = this.posts.getEdge(BALANCE, postVertex, citedPostVertex)?.data || 0;
console.log('Citation', {
citationEdge, outboundAmount, balance, citedPostValue: citedPost.value,
});
// We need to ensure that we propagate no more reputation than we leached
if (outboundAmount < 0) {
const citedPostTotalCitationWeight = citedPost.citations.reduce((t, { weight: w }) => t += w, 0);
const citedPostCapacity = citedPost.value / (1 - citedPostTotalCitationWeight);
outboundAmount = Math.max(outboundAmount, -citedPostCapacity);
outboundAmount = Math.max(outboundAmount, -citedPost.value);
if (depth > 0) {
outboundAmount = Math.max(outboundAmount, -balance);
}
const refundFromOutbound = await this.propagateValue(
}
increment -= outboundAmount * params.leachingValue;
this.posts.setEdge(BALANCE, postVertex, citedPostVertex, balance + outboundAmount);
await this.propagateValue(citationEdge, {
rewardsAccumulator,
post,
citedPost,
outboundAmount,
depth + 1,
);
totalOutboundAmount += outboundAmount - refundFromOutbound;
increment: outboundAmount,
depth: depth + 1,
});
}
}
// Apply leaching value
const incrementAfterLeaching = increment - totalOutboundAmount * params.leachingValue;
const newValue = post.value + increment;
const rawNewValue = post.value + incrementAfterLeaching;
const newValue = Math.max(0, rawNewValue);
const appliedIncrement = newValue - post.value;
const refundToInbound = increment - appliedIncrement;
console.log('propagateValue end', {
depth,
increment,
newValue,
});
// Award reputation to post author
rewardsAccumulator.set(post.tokenId, appliedIncrement);
rewardsAccumulator.set(post.tokenId, increment);
// Increment the value of the post
await this.setPostValue(post, newValue);
return refundToInbound;
}
}

View File

@ -0,0 +1,9 @@
export class List {
constructor() {
this.items = [];
}
add(item) {
this.items.push(item);
}
}

View File

View File

@ -2,6 +2,7 @@ import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.min.mjs';
import { Actor } from './actor.js';
import { Action } from './action.js';
import { debounce, hexToRGB } from '../util.js';
import { CryptoUtil } from './crypto.js';
class MermaidDiagram {
constructor(box, logBox) {
@ -90,10 +91,11 @@ export class Scene {
this.box.addBox('Spacer').setInnerHTML('&nbsp;');
this.topSection = this.box.addBox('Top section').flex();
this.displayValuesBox = this.topSection.addBox('Values');
this.middleSection = this.box.addBox('Middle section').flex();
this.middleSection = this.box.addBox('Middle section');
this.box.addBox('Spacer').setInnerHTML('&nbsp;');
this.actors = new Set();
this.dateStart = new Date();
this.flowcharts = new Map();
mermaid.mermaidAPI.initialize({
startOnLoad: false,
@ -117,26 +119,49 @@ export class Scene {
withSequenceDiagram() {
const box = this.box.addBox('Sequence diagram');
this.box.addBox('Spacer').setInnerHTML('&nbsp;');
const logBox = this.box.addBox('Sequence diagram text');
const logBox = this.box.addBox('Sequence diagram text').addClass('dim');
this.sequence = new MermaidDiagram(box, logBox);
this.sequence.log('sequenceDiagram', false);
return this;
}
withFlowchart(direction = 'BT') {
const box = this.topSection.addBox('Flowchart');
this.box.addBox('Spacer').setInnerHTML('&nbsp;');
const logBox = this.box.addBox('Flowchart text');
withFlowchart({ direction = 'BT' } = {}) {
const box = this.topSection.addBox('Flowchart').addClass('padded');
const logBox = this.topSection.addBox('Flowchart text').addClass('dim');
this.flowchart = new MermaidDiagram(box, logBox);
this.flowchart.log(`graph ${direction}`, false);
return this;
}
withAdditionalFlowchart({ id, name, direction = 'BT' } = {}) {
const index = this.flowcharts.size;
name = name ?? `Flowchart ${index}`;
id = id ?? `flowchart_${CryptoUtil.randomUUID().slice(0, 4)}`;
const container = this.middleSection.addBox(name).flex();
const box = container.addBox('Flowchart').addClass('padded');
const logBox = container.addBox('Flowchart text').addClass('dim');
const flowchart = new MermaidDiagram(box, logBox);
flowchart.log(`graph ${direction}`, false);
this.flowcharts.set(id, flowchart);
return this;
}
lastFlowchart() {
if (!this.flowcharts.size) {
if (this.flowchart) {
return this.flowchart;
}
throw new Error('lastFlowchart: No additional flowcharts have been added.');
}
const flowcharts = Array.from(this.flowcharts.values());
return flowcharts[flowcharts.length - 1];
}
withTable() {
if (this.table) {
return this;
}
const box = this.middleSection.addBox('Table');
const box = this.middleSection.addBox('Table').addClass('padded');
this.box.addBox('Spacer').setInnerHTML('&nbsp;');
this.table = new Table(box);
return this;

View File

@ -16,20 +16,27 @@ class Vertex {
}
class Edge {
constructor(label, from, to, data) {
constructor(label, from, to, weight) {
this.from = from;
this.to = to;
this.label = label;
this.data = data;
this.weight = weight;
}
}
export class Graph {
export class WDAG {
constructor(scene) {
this.scene = scene;
this.vertices = new Map();
this.edgeLabels = new Map();
this.nextVertexId = 0;
this.flowchart = scene?.flowchart ?? null;
}
withFlowchart() {
this.scene.withAdditionalFlowchart();
this.flowchart = this.scene.lastFlowchart();
return this;
}
addVertex(id, data, label) {
@ -43,16 +50,12 @@ export class Graph {
}
const vertex = new Vertex(id, data);
this.vertices.set(id, vertex);
if (this.scene && this.scene.flowchart) {
this.scene.flowchart.log(`${id}[${label ?? id}]`);
}
this.flowchart?.log(`${id}[${label ?? id}]`);
return this;
}
setVertexLabel(id, label) {
if (this.scene && this.scene.flowchart) {
this.scene.flowchart.log(`${id}[${label}]`);
}
this.flowchart?.log(`${id}[${label}]`);
}
getVertex(id) {
@ -68,36 +71,43 @@ export class Graph {
}
getEdge(label, from, to) {
from = from instanceof Vertex ? from : this.getVertex(from);
to = to instanceof Vertex ? to : this.getVertex(to);
const edges = this.edgeLabels.get(label);
return edges?.get(JSON.stringify({ from, to }));
return edges?.get(JSON.stringify([from.id, to.id]));
}
setEdge(label, from, to, edge) {
from = from instanceof Vertex ? from : this.getVertex(from);
to = to instanceof Vertex ? to : this.getVertex(to);
if (!(edge instanceof Edge)) {
edge = new Edge(edge);
}
let edges = this.edgeLabels.get(label);
if (!edges) {
edges = new Map();
this.edgeLabels.set(label, edges);
}
edges.set(JSON.stringify({ from, to }), edge);
edges.set(JSON.stringify([from.id, to.id]), edge);
}
addEdge(label, from, to, data) {
addEdge(label, from, to, weight) {
from = from instanceof Vertex ? from : this.getVertex(from);
to = to instanceof Vertex ? to : this.getVertex(to);
if (this.getEdge(label, from, to)) {
throw new Error(`Edge ${label} from ${from} to ${to} already exists`);
}
const edge = new Edge(label, from, to, data);
const edge = new Edge(label, from, to, weight);
this.setEdge(label, from, to, edge);
const fromVertex = this.getVertex(from);
fromVertex.edges.from.push(edge);
const toVertex = this.getVertex(to);
toVertex.edges.to.push(edge);
if (this.scene && this.scene.flowchart) {
this.scene.flowchart.log(`${fromVertex.id} -- ${data.weight} --> ${toVertex.id}`);
}
from.edges.from.push(edge);
to.edges.to.push(edge);
this.flowchart?.log(`${from.id} -- ${weight} --> ${to.id}`);
return this;
}
getEdges(label, from, to) {
from = from instanceof Vertex ? from : this.getVertex(from);
to = to instanceof Vertex ? to : this.getVertex(to);
const edgeLabels = label ? [label] : Array.from(this.edgeLabels.keys());
return edgeLabels.flatMap((edgeLabel) => {
const edges = this.edgeLabels.get(edgeLabel);

View File

@ -29,6 +29,12 @@ a:visited {
font-family: monospace;
font-size: 8pt;
}
.dim {
opacity: 0.25;
}
.padded {
padding: 20px;
}
svg {
width: 800px;
}

View File

@ -7,19 +7,19 @@
<h2>Tests</h2>
<h3>Primary</h3>
<ul>
<li><a href="./tests/validation-pool.html">Validation Pool</a></li>
<li><a href="./tests/availability.html">Availability + Business</a></li>
<li><a href="./tests/forum.html">Forum</a></li>
<li><a href="./tests/validation-pool.test.html">Validation Pool</a></li>
<li><a href="./tests/availability.test.html">Availability + Business</a></li>
<li><a href="./tests/forum.test.html">Forum</a></li>
</ul>
<h3>Secondary</h3>
<ul>
<li><a href="./tests/forum-network.html">Forum Network</a></li>
<li><a href="./tests/forum-network.test.html">Forum Network</a></li>
</ul>
<h3>Tertiary</h3>
<ul>
<li><a href="./tests/basic.html">Basic</a></li>
<li><a href="./tests/graph.html">Graph</a></li>
<li><a href="./tests/debounce.html">Debounce</a></li>
<li><a href="./tests/flowchart.html">Flowchart</a></li>
<li><a href="./tests/basic.test.html">Basic Sequencing</a></li>
<li><a href="./tests/wdag.test.html">WDAG</a></li>
<li><a href="./tests/debounce.test.html">Debounce</a></li>
<li><a href="./tests/flowchart.test.html">Flowchart</a></li>
</ul>
</body>

View File

@ -1,9 +0,0 @@
<!DOCTYPE html>
<head>
<title>Forum test</title>
<link type="text/css" rel="stylesheet" href="../index.css" />
</head>
<body>
<div id="forum-test"></div>
</body>
<script type="module" src="./forum/forum.test.js"></script>

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<head>
<title>Forum test</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
<link type="text/css" rel="stylesheet" href="../index.css" />
</head>
<body>
<div id="mocha"></div>
<div id="scene"></div>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/radash/10.7.0/radash.js" integrity="sha512-S207zKWG3iqXqe6msO7/Mr8X3DzzF4u8meFlokHjGtBPTGUhgzVo0lpcqEy0GoiMUdcoct+H+SqzoLsxXbynzg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://unpkg.com/chai/chai.js"></script>
<script src="https://unpkg.com/mocha/mocha.js"></script>
<script class="mocha-init">
mocha.setup({
ui: 'bdd',
globals: ['scene', 'bench', 'forum', 'experts', 'posts', '__REACT_DEVTOOLS_*'],
});
mocha.checkLeaks();
chai.should();
</script>
<script type="module" src="./scripts/forum.test.js"></script>
<script class="mocha-exec">
mocha.run();
</script>

View File

@ -1,79 +0,0 @@
import { Box } from '../../classes/box.js';
import { Scene } from '../../classes/scene.js';
import { Expert } from '../../classes/expert.js';
import { Bench } from '../../classes/bench.js';
import { delay } from '../../util.js';
import { Forum } from '../../classes/forum.js';
import { PostContent } from '../../classes/post-content.js';
import params from '../../params.js';
const DEFAULT_DELAY_INTERVAL = 500;
const rootElement = document.getElementById('forum-test');
const rootBox = new Box('rootBox', rootElement).flex();
const scene = (window.scene = new Scene('Forum test', rootBox));
scene.withSequenceDiagram();
scene.withFlowchart();
scene.withTable();
scene.addDisplayValue('c3. stakeForAuthor').set(params.stakeForAuthor);
scene.addDisplayValue('q2. revaluationLimit').set(params.revaluationLimit);
scene
.addDisplayValue('q3. referenceChainLimit')
.set(params.referenceChainLimit);
scene.addDisplayValue('q4. leachingValue').set(params.leachingValue);
scene.addDisplayValue('&nbsp;');
const experts = (window.experts = []);
const newExpert = async () => {
const index = experts.length;
const name = `Expert${index + 1}`;
const expert = await new Expert(name, scene).initialize();
experts.push(expert);
return expert;
};
const forum = (window.forum = new Forum('Forum', scene));
const bench = (window.bench = new Bench(forum, 'Bench', scene));
const expert1 = await newExpert();
const expert2 = await newExpert();
const expert3 = await newExpert();
bench.addValue('total rep', () => bench.reputation.getTotal());
forum.addValue('total value', () => forum.getTotalValue());
for (const expert of experts) {
expert.addValue('rep', () => bench.reputation.valueOwnedBy(expert.reputationPublicKey));
}
const addPost = async (author, title, fee, citations = []) => {
await scene.startSection();
const postContent = new PostContent({}).setTitle(title);
for (const { postId, weight } of citations) {
postContent.addCitation(postId, weight);
}
const { pool, postId } = await author.submitPostWithFee(
bench,
forum,
postContent,
{
fee,
duration: 1000,
tokenLossRatio: 1,
},
);
await delay(1000);
await pool.evaluateWinningConditions();
await scene.endSection();
await delay(DEFAULT_DELAY_INTERVAL);
return postId;
};
const postId1 = await addPost(expert1, 'Post 1', 20);
const postId2 = await addPost(expert2, 'Post 2', 10, [{ postId: postId1, weight: 0.5 }]);
const postId3 = await addPost(expert3, 'Post 3', 10, [{ postId: postId1, weight: -1 }]);
await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]);
await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]);

View File

@ -1,33 +0,0 @@
<!DOCTYPE html>
<head>
<title>Forum Graph</title>
<link type="text/css" rel="stylesheet" href="../index.css" />
</head>
<body>
<div id="graph-test"></div>
</body>
<script type="module">
import { Box } from '../classes/box.js';
import { Scene } from '../classes/scene.js';
import { Graph } from '../classes/graph.js';
const rootElement = document.getElementById('graph-test');
const rootBox = new Box('rootBox', rootElement).flex();
window.scene = new Scene('Graph test', rootBox);
window.graph = new Graph();
window.v = [];
function addVertex() {
const vertex = window.graph.addVertex({ seq: window.v.length });
window.v.push(vertex);
}
addVertex();
addVertex();
addVertex();
addVertex();
addVertex();
window.graph.addEdge('e1', 0, 1);
</script>

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Mocha Tests</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
</head>
<body>
<div id="mocha"></div>
<div id="scene"></div>
<script src="https://unpkg.com/chai/chai.js"></script>
<script src="https://unpkg.com/mocha/mocha.js"></script>
<script class="mocha-init">
mocha.setup({
ui: 'bdd',
});
mocha.checkLeaks();
chai.should();
</script>
<script src="./scripts/mocha.test.js"></script>
<script class="mocha-exec">
mocha.run();
</script>
</body>
</html>

View File

@ -9,9 +9,9 @@
<script type="module">
import { Box } from '../classes/box.js';
import { Scene } from '../classes/scene.js';
import { ValidationPool } from '../classes/validation-pool.js';
import { TokenHolder } from '../classes/token-holder.js';
import { ReputationToken } from '../classes/reputation-token.js';
// import { ValidationPool } from '../classes/validation-pool.js';
// import { TokenHolder } from '../classes/token-holder.js';
// import { ReputationToken } from '../classes/reputation-token.js';
import { delay } from '../util.js';
const DEFAULT_DELAY_INTERVAL = 500;
@ -23,8 +23,8 @@ const scene = (window.scene = new Scene('Forum test', rootBox));
scene.withSequenceDiagram();
scene.withFlowchart();
const pool = new ValidationPool();
const repToken = new ReputationToken();
// const pool = new ValidationPool();
// const repToken = new ReputationToken();
// const tokenMinter = new TokenHolder('TokenMinter', scene);

View File

@ -0,0 +1,110 @@
import { Box } from '../../classes/box.js';
import { Scene } from '../../classes/scene.js';
import { Expert } from '../../classes/expert.js';
import { Bench } from '../../classes/bench.js';
import { delay } from '../../util.js';
import { Forum } from '../../classes/forum.js';
import { PostContent } from '../../classes/post-content.js';
import params from '../../params.js';
const DEFAULT_DELAY_MS = 1;
const POOL_DURATION_MS = 50;
let rootElement;
let rootBox;
let scene;
let forum;
let bench;
let experts;
let posts;
async function newExpert() {
const index = experts.length;
const name = `Expert${index + 1}`;
const expert = await new Expert(name, scene).initialize();
experts.push(expert);
return expert;
}
async function addPost(author, fee, citations = []) {
const postIndex = posts.length;
const title = `posts[${postIndex}]`;
await scene.startSection();
const postContent = new PostContent({}).setTitle(title);
for (const { postId, weight } of citations) {
postContent.addCitation(postId, weight);
}
const { pool, postId } = await author.submitPostWithFee(
bench,
forum,
postContent,
{
fee,
duration: POOL_DURATION_MS,
tokenLossRatio: 1,
},
);
posts.push(postId);
await delay(POOL_DURATION_MS);
await pool.evaluateWinningConditions();
await scene.endSection();
await delay(DEFAULT_DELAY_MS);
return postId;
}
async function setup() {
rootElement = document.getElementById('scene');
rootBox = new Box('rootBox', rootElement).flex();
scene = (window.scene = new Scene('Forum test', rootBox));
scene.withSequenceDiagram();
scene.addDisplayValue('c3. stakeForAuthor').set(params.stakeForAuthor);
scene.addDisplayValue('q2. revaluationLimit').set(params.revaluationLimit);
scene
.addDisplayValue('q3. referenceChainLimit')
.set(params.referenceChainLimit);
scene.addDisplayValue('q4. leachingValue').set(params.leachingValue);
scene.addDisplayValue('&nbsp;');
forum = (window.forum = new Forum('Forum', scene));
bench = (window.bench = new Bench(forum, 'Bench', scene));
experts = (window.experts = []);
posts = (window.posts = []);
forum.posts.withFlowchart();
scene.withTable();
await newExpert();
// await newExpert();
// await newExpert();
// bench.addValue('total rep', () => bench.reputation.getTotal());
forum.addValue('total value', () => forum.getTotalValue());
// for (const expert of experts) {
// expert.addValue('rep', () => bench.reputation.valueOwnedBy(expert.reputationPublicKey));
// }
}
describe('Negative citation of a negative citation', async () => {
before(async () => {
await setup();
await addPost(experts[0], 10);
await addPost(experts[0], 10, [{ postId: posts[0], weight: -1 }]);
await addPost(experts[0], 10, [{ postId: posts[1], weight: -1 }]);
});
it('Should restore original post to its initial value', () => {
forum.getPost(posts[0]).value.should.equal(10);
});
});
// await addPost(experts[0], 10);
// await addPost(experts[0], 10, [{ postId: posts[3], weight: -1 }]);
// await addPost(experts[0], 10, [{ postId: posts[4], weight: -1 }]);
// await addPost(expert3, 'Post 4', 100, [{ postId: postId2, weight: -1 }]);
// await addPost(expert1, 'Post 5', 100, [{ postId: postId3, weight: -1 }]);

View File

@ -0,0 +1,23 @@
describe('Array', () => {
before(() => {
// ...
});
describe('#indexOf()', () => {
context('when not present', () => {
it('should not throw an error', () => {
(function aFunc() {
[1, 2, 3].indexOf(4);
}.should.not.throw());
});
it('should return -1', () => {
[1, 2, 3].indexOf(4).should.equal(-1);
});
});
context('when present', () => {
it('should return the index where the element first appears in the array', () => {
[1, 2, 3].indexOf(3).should.equal(2);
});
});
});
});

View File

@ -0,0 +1,23 @@
import { Box } from '../../classes/box.js';
import { Scene } from '../../classes/scene.js';
import { WDAG } from '../../classes/wdag.js';
const rootElement = document.getElementById('scene');
const rootBox = new Box('rootBox', rootElement).flex();
window.scene = new Scene('WDAG test', rootBox);
const graph = (window.graph = new WDAG(window.scene).withFlowchart());
const v = (window.v = []);
function addVertex() {
const vertex = graph.addVertex({ seq: window.v.length });
v.push(vertex);
}
addVertex();
addVertex();
addVertex();
addVertex();
addVertex();
graph.addEdge('e1', 0, 1, 1);
graph.addEdge('e1', 2, 1, 0.5);
graph.addEdge('e1', 3, 1, 0.25);
graph.addEdge('e1', 1, 4, 0.125);

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<head>
<title>Forum WDAG</title>
<link type="text/css" rel="stylesheet" href="../index.css" />
</head>
<body>
<div id="scene"></div>
</body>
<script type="module" src="./scripts/wdag.test.js"></script>