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     64174x 61306x       17261x 17261x 17261x 5812x   11449x         22751x 22751x 22751x 36009x 18748x 18748x 18748x   17261x     22751x       6184x 6184x 13918x 13918x   6184x       4674x 4674x 3389x 3389x 3389x   4674x       6898x 6898x   6898x 5490x       7020x 7020x           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));
  }
}