All files / collaborative-prosemirror/src/__tests__ fuzzer.ts

100% Statements 48/48
100% Branches 16/16
100% Functions 12/12
100% Lines 42/42

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97  3x 3x                               3x   3x 449x   449x   449x 449x     63823x 60927x       17457x 17457x 17457x 5833x   11624x         22854x 22854x 22854x 36302x 18845x 18845x 18845x   17457x     22854x       5987x 5987x 13681x 13681x   5987x       4586x 4586x 3267x 3267x 3267x   4586x       6824x 6824x   6824x 5397x       6857x 6857x           449x      
import type {Node} from 'prosemirror-model';
import {Fuzzer} from '@jsonjoy.com/util/lib/Fuzzer';
import {
  doc,
  blockquote,
  ul,
  ol,
  li,
  p,
  pre,
  h1,
  h2,
  h3,
  em,
  strong,
  type MarkBuilder,
  a,
} from 'prosemirror-test-builder';
import {RandomJson} from '@jsonjoy.com/json-random';
 
export class NodeToViewRangeFuzzer {
  public static readonly doc = () => new NodeToViewRangeFuzzer().createDocumentNode();
 
  private fuzzer = new Fuzzer();
 
  private nodeCount = 0;
  private maxNodeCount = 100;
 
  doContinue(percent = 50): boolean {
    if (this.nodeCount >= this.maxNodeCount) return false;
    return this.fuzzer.randomInt(1, 100) <= percent;
  }
 
  createInlineNode(): ReturnType<MarkBuilder> {
    const builder = this.fuzzer.pick([em, strong, a]);
    this.nodeCount++;
    if (builder === a) {
      return a({href: RandomJson.genString(4)}, ...this.createInlineFragment());
    } else {
      return builder(...this.createInlineFragment());
    }
  }
 
  createInlineFragment(percent = 90): (ReturnType<MarkBuilder> | string)[] {
    const nodes: (ReturnType<MarkBuilder> | string)[] = [];
    const count = Fuzzer.randomInt(1, 5);
    for (let i = 0; i < count; i++) {
      if (!this.doContinue()) {
        this.nodeCount++;
        nodes.push(RandomJson.genString(5));
        break;
      } else {
        nodes.push(this.createInlineNode());
      }
    }
    return nodes;
  }
 
  createBlockFragment(percent = 60): Node[] {
    const nodes: Node[] = [];
    while (this.doContinue(percent)) {
      const node = this.fuzzer.pick([() => this.createLeafBlockNode(), () => this.createContainerBlockNode()]);
      nodes.push(node());
    }
    return nodes;
  }
 
  createListFragment(percent = 50): Node[] {
    const nodes: Node[] = [];
    while (this.doContinue(percent)) {
      percent = Math.max(0, percent - 10);
      this.nodeCount++;
      nodes.push(li(...this.createBlockFragment()));
    }
    return nodes;
  }
 
  createLeafBlockNode(): Node {
    const builder = this.fuzzer.pick([p, pre, h1, h2, h3]);
    this.nodeCount++;
    // code_block (pre) disallows marks — use plain text only.
    if (builder === pre) return pre(RandomJson.genString(8));
    return builder(...this.createInlineFragment());
  }
 
  createContainerBlockNode(): Node {
    const builder = this.fuzzer.pick([blockquote, ul, ol]);
    return builder === ul || builder === ol
      ? builder(...this.createListFragment())
      : builder(...this.createBlockFragment());
  }
 
  createDocumentNode(): Node {
    return doc(...this.createBlockFragment(100));
  }
}