forked from yvv/decision
v3 : modules d'ordre et de titre — l'interface, restes mineurs (L11, C25–C27)
Chaque section du lecteur finit par l'ordre de ses articles (inertie 2) : ses articles numérotés, son vote déplié, « Proposer un autre ordre » — une liste où l'on clique un article puis sa place, ses flèches, ou le clavier. Le titre du document (inertie 4) se vote et se propose sous le titre, « traduction à revoir » quand il a changé. Les deux sont dans la liste des propositions, le panier et la chaîne Ğ1 ; un nouvel article se propose dans une section, à sa fin. La liste du groupe compte les propositions ouvertes des modules. Restes mineurs : la participation ramenée se dit en deux temps (la mesure, puis ce que le groupe demande), les lignes « habituelle » des réglages resserrées, le sélecteur d'inertie se parcourt aux flèches (le cran actuel atteignable, jamais choisi), la marque « seed a minima » porte le vrai nombre de votes. Contrôles : C25 (ordre, dont la chaîne et le téléphone), C26 (titre), C27 (participation ramenée) ; C3, C17 (41 modules), C20 mis à jour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,26 @@ describe('the basket of a vote', () => {
|
||||
expect(clearScope(fond, { i1: 'for', e2: 'for', e1k: 'against', e4: 'none' })).toEqual({ i1: 'for', e4: 'none' });
|
||||
});
|
||||
|
||||
it('the modules are voted like articles: the title first, each order at the end of its section', () => {
|
||||
// ADR 0009: the title of the document, the order of each section; an order of one article is left out.
|
||||
const title = { ...unit('T', null, prop('t', 'T', { kind: 'title', status: 'in_force' }), [prop('t2', 'T', { kind: 'title' })]), kind: 'title' as const };
|
||||
const order = (id: string, section: string, moot = false) => ({
|
||||
...unit(id, section, prop(`${id}-v`, id, { kind: 'order', status: 'in_force' }), []),
|
||||
kind: 'order' as const,
|
||||
moot,
|
||||
});
|
||||
const s = sections(DOC, { title, orders: [order('O-intro', 'introduction', true), order('O-fond', 'fondamental'), order('O-tech', 'technique')] });
|
||||
expect(s.map((x) => x.key)).toEqual([null, 'introduction', 'fondamental', 'technique']);
|
||||
expect(s[0]?.slots.map((x) => x.unit.id)).toEqual(['T']);
|
||||
expect(s[0]?.slots[0]?.versions.map((p) => p.id)).toEqual(['t', 't2']);
|
||||
expect(s[1]?.slots.map((x) => x.unit.id)).toEqual(['I1']);
|
||||
expect(s[2]?.slots.map((x) => x.unit.id)).toEqual(['E1', 'E2', 'O-fond']);
|
||||
expect(s[3]?.slots.map((x) => x.unit.id)).toEqual(['E3', 'E4', 'O-tech']);
|
||||
// « Tout pour » a section fills its order too (one version), never the title (two).
|
||||
expect(fill(s[2]?.slots ?? [], 'for', {})).toEqual({ e2: 'for', 'O-fond-v': 'for' });
|
||||
expect(waiting(s.flatMap((x) => x.slots), {}).map((x) => x.unit.id)).toEqual(['T', 'E1', 'E3']);
|
||||
});
|
||||
|
||||
it('the lines sent: a withdrawal is a null choice; a version gone from the document is left out', () => {
|
||||
const known = new Set(['i1', 'e2', 'e4']);
|
||||
expect(lines({ i1: 'for', e2: 'none', e4: 'against', gone: 'for' }, known)).toEqual([
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// The order of a section, proposed with a list one clicks (Yvv, 2026-10-30:
|
||||
// « une simple liste clipable »): an article, then its new place; or its arrows.
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { moved, place, sameOrder, step } from '../../src/lib/order';
|
||||
|
||||
const section = ['T1', 'E1', 'E2', 'E3'];
|
||||
|
||||
describe('the order of a section, by clicks', () => {
|
||||
it('an article chosen takes the place clicked, the others close up', () => {
|
||||
expect(place(section, 'E2', 'T1')).toEqual(['E2', 'T1', 'E1', 'E3']);
|
||||
expect(place(section, 'T1', 'E3')).toEqual(['E1', 'E2', 'E3', 'T1']);
|
||||
expect(place(section, 'E1', 'E2')).toEqual(['T1', 'E2', 'E1', 'E3']);
|
||||
// Its own place, or an article not in the list: nothing moves.
|
||||
expect(place(section, 'E1', 'E1')).toEqual(section);
|
||||
expect(place(section, 'X9', 'E1')).toEqual(section);
|
||||
expect(place(section, 'E1', 'X9')).toEqual(section);
|
||||
});
|
||||
|
||||
it('the arrows move one place, never beyond the ends', () => {
|
||||
expect(step(section, 'E2', -1)).toEqual(['T1', 'E2', 'E1', 'E3']);
|
||||
expect(step(section, 'E2', 1)).toEqual(['T1', 'E1', 'E3', 'E2']);
|
||||
expect(step(section, 'T1', -1)).toEqual(section);
|
||||
expect(step(section, 'E3', 1)).toEqual(section);
|
||||
});
|
||||
|
||||
it('a list never loses nor doubles an article', () => {
|
||||
const once = place(step(place(section, 'E3', 'T1'), 'T1', 1), 'E1', 'E3');
|
||||
expect([...once].sort()).toEqual([...section].sort());
|
||||
});
|
||||
|
||||
it('tells the order in force from another, and what moved', () => {
|
||||
expect(sameOrder(section, [...section])).toBe(true);
|
||||
expect(sameOrder(section, step(section, 'E1', 1))).toBe(false);
|
||||
expect(sameOrder(section, section.slice(1))).toBe(false);
|
||||
expect([...moved(section, ['E1', 'T1', 'E2', 'E3'])].sort()).toEqual(['E1', 'T1']);
|
||||
expect(moved(section, section).size).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -30,8 +30,16 @@ describe('usual participation as shown', () => {
|
||||
const p = { ...base, source: 'reference' as const, median: null, sample: 0, ratio: 0.5, voters: 1, bounded: true };
|
||||
expect(usualShown(p, 2, 1)).toEqual({ voters: 2, ratio: 1 });
|
||||
});
|
||||
it('rounds a median between two numbers of voters', () => {
|
||||
it('rounds a median between two numbers of voters, never its share', () => {
|
||||
const p = { ...base, median: 2.5, ratio: 0.01, voters: 57, bounded: true };
|
||||
expect(usualShown(p, 5745, 50).voters).toBe(3);
|
||||
// Relecture of 71819f9: the share is that of the median itself — 2,5 voters out of 4 is
|
||||
// 62,5 %, not 3 out of 4 (75 %).
|
||||
const q = { ...base, median: 2.5, ratio: 0.5, voters: 2, bounded: true };
|
||||
expect(usualShown(q, 4, 1)).toEqual({ voters: 3, ratio: 0.625 });
|
||||
});
|
||||
it('takes the share of twice C as it is, whatever the rounding', () => {
|
||||
const p = { ...base, source: 'reference' as const, median: null, sample: 0, ratio: 0.5, voters: 2, bounded: true };
|
||||
expect(usualShown(p, 3, 0.75)).toEqual({ voters: 2, ratio: 0.5 });
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user