Join 34,000+ subscribers and receive articles from our blog about software quality, testing, QA and security.
 

[UI Script] Enhance Markdown formatting options for code blocks when large amounts of code need to be formatted


#1

My team needs the ability to paste sizeable code snippets into our test cases and have them be formatted appropriately. I know standard markdown uses four spaces before each line (which TestRail supports) as well as ‘```’ to format blocks of code. Being able to wrap our pre-existing blocks of code with some kind of tag would be more efficient than having to add spaces before every line. Is it possible to use UI scripts to parse text blocks and properly format them (for example, on the ^cases/view) page?


#2

So the following UI Script I have written handles the ^cases/view as far as displaying code blocks in a pretty manner by replacing all html <code> tags with <pre><code> dynamically on page load:

$(document).ready (
  function() {
    jQuery.fn.outerHTML = function() {
       return (this[0]) ? this[0].outerHTML : ''
     };
    
    $(".markdown").each( function() {
  		$( this ).html( $( this ).outerHTML().replace(/<code>/g, '<pre><code>').replace(/<\/code>/g, '</code></pre>'))
      }
    );
  }
);

However, this is very hacky, and does not actually change the format that the code block is stored in on the backend, so printing the test case results in the old inline code. Looking into ways to programmatically handle code blocks on the ^cases/edit page now. Going to see if it is possible to make a ui script fire on save and format all text between predetermined tags with 4 spaces.


#3

Found a solution to the above problem that involves two scripts: 1) adding a button to the UI, 2) text parsing script to execute on all ‘textarea’ elements when the button is clicked. The parsing script will look for “```” and treat them as tags, so as long as the code blocks are surrounded in these, the lines of text inbetween will be formatted with four spaces before each line. Posting the code below in case it will ever be useful to others:

Add button to top of ^cases(add|edit) pages:

js:
$(document).ready(
    function() {
    var $input = $('<input id="customFormatCodeBtn" type="button" value="Format Code Blocks" />');
    $input.appendTo($("div.content-header"));
    }
);

css:
input#customFormatCodeBtn {
    left: 150px;
    padding: 3px;
    position: relative;
}

Script to convert code blocks to markdown on ^cases/(add|edit) pages:

js:
$(document).ready(

function() {
    $( "#customFormatCodeBtn" ).click(function() {

    //Gets the indices of substrings matching the target text search
    function getIndicesOf(searchStr, str, caseSensitive) {
        var searchStrLen = searchStr.length;
        if (searchStrLen == 0) {
            return [];
        }
        var startIndex = 0, index, indices = [];
        if (!caseSensitive) {
            str = str.toLowerCase();
            searchStr = searchStr.toLowerCase();
        }
        while ((index = str.indexOf(searchStr, startIndex)) > -1) {
            indices.push(index);
            startIndex = index + searchStrLen;
        }
        return indices;
    }
      
      //Track whether a parse error has been encountered
      var parseError = false;
      //Loop over all the textarea elements and parse code blocks
      $("textarea").each( function(key) {
        
        if(!parseError) {
        var regex = "```", result, indices = [];
        //While there are still string matches and no parsing errors
        //continue formatting
        while ($( this ).val().includes(regex) && !parseError) {
          var str = $( this ).val();
          var indices = [];
          indices = getIndicesOf(regex, str, true);
          //Only continue parsing if ``` are on their own line and not
          //succeeded or preceded by other characters. Else, throw parse error
          //in browser alert
          if(str.match(/```\ *[\x21-\x7E]/g) == null && str.match(/[\x21-\x7E] *\```/g) == null) {
          
           //Only execute parsing if there are an even number of tags.
           //Else, throw a parsing error because an uneven number of tags
           //indicates that not all tags open/close correctly
           if(((indices.length)%2) == 0) {
            //Parse text and format
            var begIndex = indices[0];
            var endIndex = indices[1] + 3;
            console.log( $( this ).val().substring(begIndex, endIndex));
            var formatTxt = $( this ).val().substring(begIndex, endIndex);
            formatTxt = formatTxt.replace(/\n/g, "\n    ").replace(/```/g, "");
            $( this ).val($( this ).val().replace($( this ).val().substring(begIndex, endIndex), formatTxt));
          } else {
        $( this ).focus();
            //Print alert to user that they did not open/close all tags correctly
             alert("Improper code formatting detected in textbox " + (key + 1) +
                   ".\nMake sure that all code blocks are preceded and succeeded by the ``` tag." + 
            "\nCurrently there are an odd number of tags in the target textbox.\n" +
            "\nFollow this format:\n\n" +
            "```\n" +
            "Some code here();\n" +
            "Some more code here();\n" +
            "```\n");
            //Notify script to stop execution due to parse error
            parseError = true;
          }
          } else {
         $( this ).focus();
            //Print alert to user they they do not have tags on their own line
            alert("Improper code formatting detected in textbox " + (key + 1) +
                  ".\nMake sure that all ``` tags are on their own line, and do not have spaces before" +
                  " or after them. The target text box contains one of these errors.\n\nFollow this format:\n\n" +
                  "```\n" +
                  "Some code here();\n" +
                  "Some more code here();\n" +
                  "```\n");
                  //Notify script to stop execution due to parse error
                  parseError = true;
          }
          
        }} else {
          //If parse error was found, exit out of loop execution of page elements
          //NOTE: if this is removed, then an alert box will show up for every text
          //box on the page that has a syntax error which could easily become overwhelming
          //if the test has a lot of steps.
            return false;
          }
        });
});
}    

);

UI Scripts needed