204 lines
5.5 KiB
JavaScript
204 lines
5.5 KiB
JavaScript
/**
|
|
* Helper to test CodeMirror highlighting modes. It pretty prints output of the
|
|
* highlighter and can check against expected styles.
|
|
*
|
|
* See test.html in the stex mode for examples.
|
|
*/
|
|
ModeTest = {};
|
|
|
|
ModeTest.modeOptions = {};
|
|
ModeTest.modeName = CodeMirror.defaults.mode;
|
|
|
|
/* keep track of results for printSummary */
|
|
ModeTest.testCount = 0;
|
|
ModeTest.passes = 0;
|
|
|
|
/**
|
|
* Run a test; prettyprints the results using document.write().
|
|
*
|
|
* @param name Name of test
|
|
* @param text String to highlight.
|
|
* @param expected Expected styles and tokens: Array(style, token, [style, token,...])
|
|
* @param modeName
|
|
* @param modeOptions
|
|
* @param expectedFail
|
|
*/
|
|
ModeTest.testMode = function(name, text, expected, modeName, modeOptions, expectedFail) {
|
|
ModeTest.testCount += 1;
|
|
|
|
if (!modeName) modeName = ModeTest.modeName;
|
|
|
|
if (!modeOptions) modeOptions = ModeTest.modeOptions;
|
|
|
|
var mode = CodeMirror.getMode(modeOptions, modeName);
|
|
|
|
if (expected.length < 0) {
|
|
throw "must have text for test (" + name + ")";
|
|
}
|
|
if (expected.length % 2 != 0) {
|
|
throw "must have text for test (" + name + ") plus expected (style, token) pairs";
|
|
}
|
|
return test(
|
|
modeName + "_" + name,
|
|
function(){
|
|
return ModeTest.compare(text, expected, mode);
|
|
},
|
|
expectedFail
|
|
);
|
|
|
|
}
|
|
|
|
ModeTest.compare = function (text, arguments, mode) {
|
|
|
|
var expectedOutput = [];
|
|
for (var i = 0; i < arguments.length; i += 2) {
|
|
arguments[i] = (arguments[i] != null ? arguments[i].split(' ').sort().join(' ') : arguments[i]);
|
|
expectedOutput.push([arguments[i],arguments[i + 1]]);
|
|
}
|
|
|
|
var observedOutput = ModeTest.highlight(text, mode)
|
|
|
|
var pass, passStyle = "";
|
|
if (expectedOutput.length > 0) {
|
|
pass = ModeTest.highlightOutputsEqual(expectedOutput, observedOutput);
|
|
passStyle = pass ? 'mt-pass' : 'mt-fail';
|
|
ModeTest.passes += pass ? 1 : 0;
|
|
}
|
|
|
|
var s = '';
|
|
if (pass || expectedOutput.length == 0) {
|
|
s += '<div class="mt-test ' + passStyle + '">';
|
|
s += '<pre>' + ModeTest.htmlEscape(text) + '</pre>';
|
|
s += '<div class="cm-s-default">';
|
|
s += ModeTest.prettyPrintOutputTable(observedOutput);
|
|
s += '</div>';
|
|
s += '</div>';
|
|
return s;
|
|
} else {
|
|
s += '<div class="mt-test ' + passStyle + '">';
|
|
s += '<pre>' + ModeTest.htmlEscape(text) + '</pre>';
|
|
s += '<div class="cm-s-default">';
|
|
s += 'expected:';
|
|
s += ModeTest.prettyPrintOutputTable(expectedOutput);
|
|
s += 'observed:';
|
|
s += ModeTest.prettyPrintOutputTable(observedOutput);
|
|
s += '</div>';
|
|
s += '</div>';
|
|
throw s;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Emulation of CodeMirror's internal highlight routine for testing. Multi-line
|
|
* input is supported.
|
|
*
|
|
* @param string to highlight
|
|
*
|
|
* @param mode the mode that will do the actual highlighting
|
|
*
|
|
* @return array of [style, token] pairs
|
|
*/
|
|
ModeTest.highlight = function(string, mode) {
|
|
var state = mode.startState()
|
|
|
|
var lines = string.replace(/\r\n/g,'\n').split('\n');
|
|
var output = [];
|
|
for (var i = 0; i < lines.length; ++i) {
|
|
var line = lines[i];
|
|
var stream = new CodeMirror.StringStream(line);
|
|
if (line == "" && mode.blankLine) mode.blankLine(state);
|
|
var pos = 0;
|
|
var st = [];
|
|
/* Start copied code from CodeMirror.highlight */
|
|
while (!stream.eol()) {
|
|
var style = mode.token(stream, state), substr = stream.current();
|
|
stream.start = stream.pos;
|
|
if (pos && st[pos-1] == style) {
|
|
st[pos-2] += substr;
|
|
} else if (substr) {
|
|
st[pos++] = substr; st[pos++] = style;
|
|
}
|
|
// Give up when line is ridiculously long
|
|
if (stream.pos > 5000) {
|
|
st[pos++] = this.text.slice(stream.pos); st[pos++] = null;
|
|
break;
|
|
}
|
|
}
|
|
/* End copied code from CodeMirror.highlight */
|
|
for (var x = 0; x < st.length; x += 2) {
|
|
st[x + 1] = (st[x + 1] != null ? st[x + 1].split(' ').sort().join(' ') : st[x + 1]);
|
|
output.push([st[x + 1], st[x]]);
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/**
|
|
* Compare two arrays of output from ModeTest.highlight.
|
|
*
|
|
* @param o1 array of [style, token] pairs
|
|
*
|
|
* @param o2 array of [style, token] pairs
|
|
*
|
|
* @return boolean; true iff outputs equal
|
|
*/
|
|
ModeTest.highlightOutputsEqual = function(o1, o2) {
|
|
var eq = (o1.length == o2.length);
|
|
if (eq) {
|
|
for (var j in o1) {
|
|
eq = eq &&
|
|
o1[j].length == 2 && o1[j][0] == o2[j][0] && o1[j][1] == o2[j][1];
|
|
}
|
|
}
|
|
return eq;
|
|
}
|
|
|
|
/**
|
|
* Print tokens and corresponding styles in a table. Spaces in the token are
|
|
* replaced with 'interpunct' dots (·).
|
|
*
|
|
* @param output array of [style, token] pairs
|
|
*
|
|
* @return html string
|
|
*/
|
|
ModeTest.prettyPrintOutputTable = function(output) {
|
|
var s = '<table class="mt-output">';
|
|
s += '<tr>';
|
|
for (var i = 0; i < output.length; ++i) {
|
|
var token = output[i];
|
|
s +=
|
|
'<td class="mt-token">' +
|
|
'<span class="cm-' + String(token[0]).replace(/ +/g, " cm-") + '">' +
|
|
ModeTest.htmlEscape(token[1]).replace(/ /g,'·') +
|
|
'</span>' +
|
|
'</td>';
|
|
}
|
|
s += '</tr><tr>';
|
|
for (var i = 0; i < output.length; ++i) {
|
|
var token = output[i];
|
|
s +=
|
|
'<td class="mt-style"><span>' + token[0] + '</span></td>';
|
|
}
|
|
s += '</table>';
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Print how many tests have run so far and how many of those passed.
|
|
*/
|
|
ModeTest.printSummary = function() {
|
|
ModeTest.runTests(ModeTest.displayTest);
|
|
document.write(ModeTest.passes + ' passes for ' + ModeTest.testCount + ' tests');
|
|
}
|
|
|
|
/**
|
|
* Basic HTML escaping.
|
|
*/
|
|
ModeTest.htmlEscape = function(str) {
|
|
str = str.toString();
|
|
return str.replace(/[<&]/g,
|
|
function(str) {return str == "&" ? "&" : "<";});
|
|
}
|
|
|