mirror of
				https://github.com/mermaid-js/mermaid.git
				synced 2025-11-04 12:54:08 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			563 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			563 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import { imgSnapshotTest, renderGraph } from '../../helpers/util';
 | 
						|
 | 
						|
describe('State diagram', () => {
 | 
						|
  it('v2 should render a simple info', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    info
 | 
						|
      `,
 | 
						|
      { logLevel: 1, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a simple state diagrams', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
 | 
						|
    [*] --> State1
 | 
						|
    State1 --> [*]
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a long descriptions instead of id when available', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
 | 
						|
      [*] --> S1
 | 
						|
      state "Some long name" as S1
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a long descriptions with additional descriptions', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
 | 
						|
      [*] --> S1
 | 
						|
      state "Some long name" as S1: The description
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a single state with short descriptions', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
      state "A long long name" as long1
 | 
						|
      state "A" as longlonglongid
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a transition descriptions with new lines', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
 | 
						|
      [*] --> S1
 | 
						|
      S1 --> S2: long line using<br/>should work
 | 
						|
      S1 --> S3: long line using <br>should work
 | 
						|
      S1 --> S4: long line using \\nshould work
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a state with a note', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
    State1: The state with a note
 | 
						|
    note right of State1
 | 
						|
      Important information! You can write
 | 
						|
      notes.
 | 
						|
    end note
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a state with on the left side when so specified', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
    State1: The state with a note with minus - and plus + in it
 | 
						|
    note left of State1
 | 
						|
      Important information! You can write
 | 
						|
      notes with . and  in them.
 | 
						|
    end note
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a state with a note together with another state', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
    State1: The state with a note +,-
 | 
						|
    note right of State1
 | 
						|
      Important information! You can write +,-
 | 
						|
      notes.
 | 
						|
    end note
 | 
						|
    State1 --> State2 : With +,-
 | 
						|
    note left of State2 : This is the note +,-<br/>
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a note with multiple lines in it', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
    State1: The state with a note
 | 
						|
    note right of State1
 | 
						|
      Important information! You\ncan write
 | 
						|
      notes with multiple lines...
 | 
						|
      Here is another line...
 | 
						|
      And another line...
 | 
						|
    end note
 | 
						|
    `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 should handle multiline notes with different line breaks', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
      State1
 | 
						|
      note right of State1
 | 
						|
      Line1<br>Line2<br/>Line3<br />Line4<br	/>Line5
 | 
						|
      end note
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  it('v2 should render a states with descriptions including multi-line descriptions', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
    State1: This a a single line description
 | 
						|
    State2: This a a multi line description
 | 
						|
    State2: here comes the multi part
 | 
						|
    [*] --> State1
 | 
						|
    State1 --> State2
 | 
						|
    State2 --> [*]
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a simple state diagrams 2', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
    [*] --> State1
 | 
						|
    State1 --> State2
 | 
						|
    State1 --> State3
 | 
						|
    State1 --> [*]
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a simple state diagrams with labels', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
    [*] --> State1
 | 
						|
    State1 --> State2 : Transition 1
 | 
						|
    State1 --> State3 : Transition 2
 | 
						|
    State1 --> State4 : Transition 3
 | 
						|
    State1 --> State5 : Transition 4
 | 
						|
    State2 --> State3 : Transition 5
 | 
						|
    State1 --> [*]
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render state descriptions', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
        state "Long state description" as XState1
 | 
						|
        state "Another Long state description" as XState2
 | 
						|
        XState2 : New line
 | 
						|
        XState1 --> XState2
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render composite states', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
      [*] --> NotShooting: Pacifist
 | 
						|
      NotShooting --> A
 | 
						|
      NotShooting --> B
 | 
						|
      NotShooting --> C
 | 
						|
 | 
						|
      state NotShooting {
 | 
						|
        [*] --> Idle: Yet another long long öong öong öong label
 | 
						|
        Idle --> Configuring : EvConfig
 | 
						|
        Configuring --> Idle : EvConfig  EvConfig EvConfig  EvConfig EvConfig
 | 
						|
      }
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render multiple composite states', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
      [*]-->TV
 | 
						|
 | 
						|
      state TV {
 | 
						|
        [*] --> Off: Off to start with
 | 
						|
        On --> Off : Turn off
 | 
						|
        Off --> On : Turn on
 | 
						|
      }
 | 
						|
 | 
						|
      TV--> Console
 | 
						|
 | 
						|
      state Console {
 | 
						|
        [*] --> Off2: Off to start with
 | 
						|
        On2--> Off2 : Turn off
 | 
						|
        Off2 --> On2 : Turn on
 | 
						|
        On2-->Playing
 | 
						|
 | 
						|
        state Playing {
 | 
						|
          Alive --> Dead
 | 
						|
          Dead-->Alive
 | 
						|
         }
 | 
						|
      }
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 should render forks in composite states', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
      [*]-->TV
 | 
						|
 | 
						|
      state TV {
 | 
						|
        state fork_state <<fork>>
 | 
						|
        [*] --> fork_state
 | 
						|
        fork_state --> State2
 | 
						|
        fork_state --> State3
 | 
						|
 | 
						|
        state join_state <<join>>
 | 
						|
        State2 --> join_state
 | 
						|
        State3 --> join_state
 | 
						|
        join_state --> State4
 | 
						|
        State4 --> [*]
 | 
						|
      }
 | 
						|
      `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 should render forks and joins', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
    state fork_state <<fork>>
 | 
						|
      [*] --> fork_state
 | 
						|
      fork_state --> State2
 | 
						|
      fork_state --> State3
 | 
						|
 | 
						|
      state join_state <<join>>
 | 
						|
      State2 --> join_state
 | 
						|
      State3 --> join_state
 | 
						|
      join_state --> State4
 | 
						|
      State4 --> [*]
 | 
						|
    `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render concurrency states', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
    [*] --> Active
 | 
						|
 | 
						|
    state Active {
 | 
						|
      [*] --> NumLockOff
 | 
						|
      NumLockOff --> NumLockOn : EvNumLockPressed
 | 
						|
      NumLockOn --> NumLockOff : EvNumLockPressed
 | 
						|
      --
 | 
						|
      [*] --> CapsLockOff
 | 
						|
      CapsLockOff --> CapsLockOn : EvCapsLockPressed
 | 
						|
      CapsLockOn --> CapsLockOff : EvCapsLockPressed
 | 
						|
      --
 | 
						|
      [*] --> ScrollLockOff
 | 
						|
      ScrollLockOff --> ScrollLockOn : EvCapsLockPressed
 | 
						|
      ScrollLockOn --> ScrollLockOff : EvCapsLockPressed
 | 
						|
    }
 | 
						|
    `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
    cy.get('svg');
 | 
						|
  });
 | 
						|
  it('v2 should render a state with states in it', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
      state PilotCockpit {
 | 
						|
        state  Parent {
 | 
						|
          C
 | 
						|
        }
 | 
						|
    }
 | 
						|
    `,
 | 
						|
      {
 | 
						|
        logLevel: 0,
 | 
						|
      }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 it should be possibel to use a choice', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
  stateDiagram-v2
 | 
						|
    [*] --> Off
 | 
						|
    Off --> On
 | 
						|
    state MyChoice [[choice]]
 | 
						|
    On --> MyChoice
 | 
						|
    MyChoice --> Washing
 | 
						|
    MyChoice --> Drying
 | 
						|
    Washing --> Finished
 | 
						|
    Finished --> [*]
 | 
						|
    `,
 | 
						|
      {
 | 
						|
        logLevel: 0,
 | 
						|
      }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 A compound state should be able to link to itself', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
stateDiagram
 | 
						|
  state Active {
 | 
						|
    Idle
 | 
						|
  }
 | 
						|
  Inactive --> Idle: ACT
 | 
						|
  Active --> Active: LOG
 | 
						|
    `,
 | 
						|
      {
 | 
						|
        logLevel: 0,
 | 
						|
      }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 width of compond state should grow with title if title is wider', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
stateDiagram-v2
 | 
						|
  state "Long state name 2" as NotShooting {
 | 
						|
    a-->b
 | 
						|
  }
 | 
						|
    `,
 | 
						|
      {
 | 
						|
        logLevel: 0,
 | 
						|
      }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 state label with names in it', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
        Yswsii: Your state with spaces in it
 | 
						|
        [*] --> Yswsii
 | 
						|
    `,
 | 
						|
      {
 | 
						|
        logLevel: 0,
 | 
						|
      }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 Simplest composite state', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
        state  Parent {
 | 
						|
          C
 | 
						|
        }
 | 
						|
    `,
 | 
						|
      {
 | 
						|
        logLevel: 0,
 | 
						|
        fontFamily: 'courier',
 | 
						|
      }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 should handle multiple arrows from one node to another', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
        a --> b: Start
 | 
						|
        a --> b: Stop
 | 
						|
    `,
 | 
						|
      {
 | 
						|
        logLevel: 0,
 | 
						|
        fontFamily: 'courier',
 | 
						|
      }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 should handle multiple notes added to one state', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
stateDiagram-v2
 | 
						|
    MyState
 | 
						|
    note left of MyState : I am a leftie
 | 
						|
    note right of MyState : I am a rightie
 | 
						|
    `,
 | 
						|
      {
 | 
						|
        logLevel: 0,
 | 
						|
        fontFamily: 'courier',
 | 
						|
      }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 should handle different rendering directions in composite states', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
stateDiagram-v2
 | 
						|
  direction LR
 | 
						|
  state A {
 | 
						|
    direction BT
 | 
						|
    a --> b
 | 
						|
  }
 | 
						|
  state C {
 | 
						|
    direction RL
 | 
						|
    c --> d
 | 
						|
  }
 | 
						|
  A --> C
 | 
						|
    `,
 | 
						|
      {
 | 
						|
        logLevel: 0,
 | 
						|
        fontFamily: 'courier',
 | 
						|
      }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 handle transition from one state in a composite state to a composite state', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
stateDiagram-v2
 | 
						|
  state S1 {
 | 
						|
    sub1 -->sub2
 | 
						|
  }
 | 
						|
 | 
						|
  state S2 {
 | 
						|
    sub4
 | 
						|
  }
 | 
						|
  S1 --> S2
 | 
						|
  sub1 --> sub4
 | 
						|
    `,
 | 
						|
      {
 | 
						|
        logLevel: 0,
 | 
						|
        fontFamily: 'courier',
 | 
						|
      }
 | 
						|
    );
 | 
						|
  });
 | 
						|
  it('v2 should render a state diagram when useMaxWidth is true (default)', () => {
 | 
						|
    renderGraph(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
 | 
						|
    [*] --> State1
 | 
						|
    State1 --> [*]
 | 
						|
      `,
 | 
						|
      { state: { useMaxWidth: true } }
 | 
						|
    );
 | 
						|
    cy.get('svg').should((svg) => {
 | 
						|
      expect(svg).to.have.attr('width', '100%');
 | 
						|
      // expect(svg).to.have.attr('height');
 | 
						|
      // const height = parseFloat(svg.attr('height'));
 | 
						|
      // expect(height).to.be.within(177, 178);
 | 
						|
      const style = svg.attr('style');
 | 
						|
      expect(style).to.match(/^max-width: [\d.]+px;$/);
 | 
						|
      const maxWidthValue = parseFloat(style.match(/[\d.]+/g).join(''));
 | 
						|
      // use within because the absolute value can be slightly different depending on the environment ±5%
 | 
						|
      expect(maxWidthValue).to.be.within(65, 85);
 | 
						|
    });
 | 
						|
  });
 | 
						|
  it('v2 should render a state diagram when useMaxWidth is false', () => {
 | 
						|
    renderGraph(
 | 
						|
      `
 | 
						|
    stateDiagram-v2
 | 
						|
 | 
						|
    [*] --> State1
 | 
						|
    State1 --> [*]
 | 
						|
      `,
 | 
						|
      { state: { useMaxWidth: false } }
 | 
						|
    );
 | 
						|
    cy.get('svg').should((svg) => {
 | 
						|
      // const height = parseFloat(svg.attr('height'));
 | 
						|
      const width = parseFloat(svg.attr('width'));
 | 
						|
      // expect(height).to.be.within(177, 178);
 | 
						|
      // use within because the absolute value can be slightly different depending on the environment ±5%
 | 
						|
      expect(width).to.be.within(65, 85);
 | 
						|
      expect(svg).to.not.have.attr('style');
 | 
						|
    });
 | 
						|
  });
 | 
						|
 | 
						|
  it('v2 should render a state diagram and set the correct length of the labels', () => {
 | 
						|
    imgSnapshotTest(
 | 
						|
      `
 | 
						|
      stateDiagram-v2
 | 
						|
      [*] --> 1
 | 
						|
      1 --> 2: test({ foo#colon; 'far' })
 | 
						|
      2 --> [*]
 | 
						|
    `,
 | 
						|
      { logLevel: 0, fontFamily: 'courier' }
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  describe('classDefs and applying classes', () => {
 | 
						|
    it('v2 states can have a class applied', () => {
 | 
						|
      imgSnapshotTest(
 | 
						|
        `
 | 
						|
          stateDiagram-v2
 | 
						|
          [*] --> A
 | 
						|
          A --> B: test({ foo#colon; 'far' })
 | 
						|
          B --> [*]
 | 
						|
            classDef badBadEvent fill:#f00,color:white,font-weight:bold 
 | 
						|
            class B badBadEvent
 | 
						|
           `,
 | 
						|
        { logLevel: 0, fontFamily: 'courier' }
 | 
						|
      );
 | 
						|
    });
 | 
						|
    it('v2 can have multiple classes applied to multiple states', () => {
 | 
						|
      imgSnapshotTest(
 | 
						|
        `
 | 
						|
          stateDiagram-v2
 | 
						|
          classDef notMoving fill:white
 | 
						|
          classDef movement font-style:italic;
 | 
						|
          classDef badBadEvent fill:#f00,color:white,font-weight:bold
 | 
						|
    
 | 
						|
          [*] --> Still
 | 
						|
          Still --> [*]
 | 
						|
          Still --> Moving
 | 
						|
          Moving --> Still
 | 
						|
          Moving --> Crash
 | 
						|
          Crash --> [*]
 | 
						|
    
 | 
						|
          class Still notMoving
 | 
						|
          class Moving, Crash movement
 | 
						|
          class Crash badBadEvent
 | 
						|
        `,
 | 
						|
        { logLevel: 0, fontFamily: 'courier' }
 | 
						|
      );
 | 
						|
    });
 | 
						|
  });
 | 
						|
});
 |