1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/sourceeditor/test/cm_doc_test.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,348 @@ 1.4 +(function() { 1.5 + // A minilanguage for instantiating linked CodeMirror instances and Docs 1.6 + function instantiateSpec(spec, place, opts) { 1.7 + var names = {}, pos = 0, l = spec.length, editors = []; 1.8 + while (spec) { 1.9 + var m = spec.match(/^(\w+)(\*?)(?:='([^\']*)'|<(~?)(\w+)(?:\/(\d+)-(\d+))?)\s*/); 1.10 + var name = m[1], isDoc = m[2], cur; 1.11 + if (m[3]) { 1.12 + cur = isDoc ? CodeMirror.Doc(m[3]) : CodeMirror(place, clone(opts, {value: m[3]})); 1.13 + } else { 1.14 + var other = m[5]; 1.15 + if (!names.hasOwnProperty(other)) { 1.16 + names[other] = editors.length; 1.17 + editors.push(CodeMirror(place, opts)); 1.18 + } 1.19 + var doc = editors[names[other]].linkedDoc({ 1.20 + sharedHist: !m[4], 1.21 + from: m[6] ? Number(m[6]) : null, 1.22 + to: m[7] ? Number(m[7]) : null 1.23 + }); 1.24 + cur = isDoc ? doc : CodeMirror(place, clone(opts, {value: doc})); 1.25 + } 1.26 + names[name] = editors.length; 1.27 + editors.push(cur); 1.28 + spec = spec.slice(m[0].length); 1.29 + } 1.30 + return editors; 1.31 + } 1.32 + 1.33 + function clone(obj, props) { 1.34 + if (!obj) return; 1.35 + clone.prototype = obj; 1.36 + var inst = new clone(); 1.37 + if (props) for (var n in props) if (props.hasOwnProperty(n)) 1.38 + inst[n] = props[n]; 1.39 + return inst; 1.40 + } 1.41 + 1.42 + function eqAll(val) { 1.43 + var end = arguments.length, msg = null; 1.44 + if (typeof arguments[end-1] == "string") 1.45 + msg = arguments[--end]; 1.46 + if (i == end) throw new Error("No editors provided to eqAll"); 1.47 + for (var i = 1; i < end; ++i) 1.48 + eq(arguments[i].getValue(), val, msg) 1.49 + } 1.50 + 1.51 + function testDoc(name, spec, run, opts, expectFail) { 1.52 + if (!opts) opts = {}; 1.53 + 1.54 + return test("doc_" + name, function() { 1.55 + var place = document.getElementById("testground"); 1.56 + var editors = instantiateSpec(spec, place, opts); 1.57 + var successful = false; 1.58 + 1.59 + try { 1.60 + run.apply(null, editors); 1.61 + successful = true; 1.62 + } finally { 1.63 + if (!successful || verbose) { 1.64 + place.style.visibility = "visible"; 1.65 + } else { 1.66 + for (var i = 0; i < editors.length; ++i) 1.67 + if (editors[i] instanceof CodeMirror) 1.68 + place.removeChild(editors[i].getWrapperElement()); 1.69 + } 1.70 + } 1.71 + }, expectFail); 1.72 + } 1.73 + 1.74 + var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); 1.75 + 1.76 + function testBasic(a, b) { 1.77 + eqAll("x", a, b); 1.78 + a.setValue("hey"); 1.79 + eqAll("hey", a, b); 1.80 + b.setValue("wow"); 1.81 + eqAll("wow", a, b); 1.82 + a.replaceRange("u\nv\nw", Pos(0, 3)); 1.83 + b.replaceRange("i", Pos(0, 4)); 1.84 + b.replaceRange("j", Pos(2, 1)); 1.85 + eqAll("wowui\nv\nwj", a, b); 1.86 + } 1.87 + 1.88 + testDoc("basic", "A='x' B<A", testBasic); 1.89 + testDoc("basicSeparate", "A='x' B<~A", testBasic); 1.90 + 1.91 + testDoc("sharedHist", "A='ab\ncd\nef' B<A", function(a, b) { 1.92 + a.replaceRange("x", Pos(0)); 1.93 + b.replaceRange("y", Pos(1)); 1.94 + a.replaceRange("z", Pos(2)); 1.95 + eqAll("abx\ncdy\nefz", a, b); 1.96 + a.undo(); 1.97 + a.undo(); 1.98 + eqAll("abx\ncd\nef", a, b); 1.99 + a.redo(); 1.100 + eqAll("abx\ncdy\nef", a, b); 1.101 + b.redo(); 1.102 + eqAll("abx\ncdy\nefz", a, b); 1.103 + a.undo(); b.undo(); a.undo(); a.undo(); 1.104 + eqAll("ab\ncd\nef", a, b); 1.105 + }, null, ie_lt8); 1.106 + 1.107 + testDoc("undoIntact", "A='ab\ncd\nef' B<~A", function(a, b) { 1.108 + a.replaceRange("x", Pos(0)); 1.109 + b.replaceRange("y", Pos(1)); 1.110 + a.replaceRange("z", Pos(2)); 1.111 + a.replaceRange("q", Pos(0)); 1.112 + eqAll("abxq\ncdy\nefz", a, b); 1.113 + a.undo(); 1.114 + a.undo(); 1.115 + eqAll("abx\ncdy\nef", a, b); 1.116 + b.undo(); 1.117 + eqAll("abx\ncd\nef", a, b); 1.118 + a.redo(); 1.119 + eqAll("abx\ncd\nefz", a, b); 1.120 + a.redo(); 1.121 + eqAll("abxq\ncd\nefz", a, b); 1.122 + a.undo(); a.undo(); a.undo(); a.undo(); 1.123 + eqAll("ab\ncd\nef", a, b); 1.124 + b.redo(); 1.125 + eqAll("ab\ncdy\nef", a, b); 1.126 + }); 1.127 + 1.128 + testDoc("undoConflict", "A='ab\ncd\nef' B<~A", function(a, b) { 1.129 + a.replaceRange("x", Pos(0)); 1.130 + a.replaceRange("z", Pos(2)); 1.131 + // This should clear the first undo event in a, but not the second 1.132 + b.replaceRange("y", Pos(0)); 1.133 + a.undo(); a.undo(); 1.134 + eqAll("abxy\ncd\nef", a, b); 1.135 + a.replaceRange("u", Pos(2)); 1.136 + a.replaceRange("v", Pos(0)); 1.137 + // This should clear both events in a 1.138 + b.replaceRange("w", Pos(0)); 1.139 + a.undo(); a.undo(); 1.140 + eqAll("abxyvw\ncd\nefu", a, b); 1.141 + }); 1.142 + 1.143 + testDoc("doubleRebase", "A='ab\ncd\nef\ng' B<~A C<B", function(a, b, c) { 1.144 + c.replaceRange("u", Pos(3)); 1.145 + a.replaceRange("", Pos(0, 0), Pos(1, 0)); 1.146 + c.undo(); 1.147 + eqAll("cd\nef\ng", a, b, c); 1.148 + }); 1.149 + 1.150 + testDoc("undoUpdate", "A='ab\ncd\nef' B<~A", function(a, b) { 1.151 + a.replaceRange("x", Pos(2)); 1.152 + b.replaceRange("u\nv\nw\n", Pos(0, 0)); 1.153 + a.undo(); 1.154 + eqAll("u\nv\nw\nab\ncd\nef", a, b); 1.155 + a.redo(); 1.156 + eqAll("u\nv\nw\nab\ncd\nefx", a, b); 1.157 + a.undo(); 1.158 + eqAll("u\nv\nw\nab\ncd\nef", a, b); 1.159 + b.undo(); 1.160 + a.redo(); 1.161 + eqAll("ab\ncd\nefx", a, b); 1.162 + a.undo(); 1.163 + eqAll("ab\ncd\nef", a, b); 1.164 + }); 1.165 + 1.166 + testDoc("undoKeepRanges", "A='abcdefg' B<A", function(a, b) { 1.167 + var m = a.markText(Pos(0, 1), Pos(0, 3), {className: "foo"}); 1.168 + b.replaceRange("x", Pos(0, 0)); 1.169 + eqPos(m.find().from, Pos(0, 2)); 1.170 + b.replaceRange("yzzy", Pos(0, 1), Pos(0)); 1.171 + eq(m.find(), null); 1.172 + b.undo(); 1.173 + eqPos(m.find().from, Pos(0, 2)); 1.174 + b.undo(); 1.175 + eqPos(m.find().from, Pos(0, 1)); 1.176 + }); 1.177 + 1.178 + testDoc("longChain", "A='uv' B<A C<B D<C", function(a, b, c, d) { 1.179 + a.replaceSelection("X"); 1.180 + eqAll("Xuv", a, b, c, d); 1.181 + d.replaceRange("Y", Pos(0)); 1.182 + eqAll("XuvY", a, b, c, d); 1.183 + }); 1.184 + 1.185 + testDoc("broadCast", "B<A C<A D<A E<A", function(a, b, c, d, e) { 1.186 + b.setValue("uu"); 1.187 + eqAll("uu", a, b, c, d, e); 1.188 + a.replaceRange("v", Pos(0, 1)); 1.189 + eqAll("uvu", a, b, c, d, e); 1.190 + }); 1.191 + 1.192 + // A and B share a history, C and D share a separate one 1.193 + testDoc("islands", "A='x\ny\nz' B<A C<~A D<C", function(a, b, c, d) { 1.194 + a.replaceRange("u", Pos(0)); 1.195 + d.replaceRange("v", Pos(2)); 1.196 + b.undo(); 1.197 + eqAll("x\ny\nzv", a, b, c, d); 1.198 + c.undo(); 1.199 + eqAll("x\ny\nz", a, b, c, d); 1.200 + a.redo(); 1.201 + eqAll("xu\ny\nz", a, b, c, d); 1.202 + d.redo(); 1.203 + eqAll("xu\ny\nzv", a, b, c, d); 1.204 + }); 1.205 + 1.206 + testDoc("unlink", "B<A C<A D<B", function(a, b, c, d) { 1.207 + a.setValue("hi"); 1.208 + b.unlinkDoc(a); 1.209 + d.setValue("aye"); 1.210 + eqAll("hi", a, c); 1.211 + eqAll("aye", b, d); 1.212 + a.setValue("oo"); 1.213 + eqAll("oo", a, c); 1.214 + eqAll("aye", b, d); 1.215 + }); 1.216 + 1.217 + testDoc("bareDoc", "A*='foo' B*<A C<B", function(a, b, c) { 1.218 + is(a instanceof CodeMirror.Doc); 1.219 + is(b instanceof CodeMirror.Doc); 1.220 + is(c instanceof CodeMirror); 1.221 + eqAll("foo", a, b, c); 1.222 + a.replaceRange("hey", Pos(0, 0), Pos(0)); 1.223 + c.replaceRange("!", Pos(0)); 1.224 + eqAll("hey!", a, b, c); 1.225 + b.unlinkDoc(a); 1.226 + b.setValue("x"); 1.227 + eqAll("x", b, c); 1.228 + eqAll("hey!", a); 1.229 + }); 1.230 + 1.231 + testDoc("swapDoc", "A='a' B*='b' C<A", function(a, b, c) { 1.232 + var d = a.swapDoc(b); 1.233 + d.setValue("x"); 1.234 + eqAll("x", c, d); 1.235 + eqAll("b", a, b); 1.236 + }); 1.237 + 1.238 + testDoc("docKeepsScroll", "A='x' B*='y'", function(a, b) { 1.239 + addDoc(a, 200, 200); 1.240 + a.scrollIntoView(Pos(199, 200)); 1.241 + var c = a.swapDoc(b); 1.242 + a.swapDoc(c); 1.243 + var pos = a.getScrollInfo(); 1.244 + is(pos.left > 0, "not at left"); 1.245 + is(pos.top > 0, "not at top"); 1.246 + }); 1.247 + 1.248 + testDoc("copyDoc", "A='u'", function(a) { 1.249 + var copy = a.getDoc().copy(true); 1.250 + a.setValue("foo"); 1.251 + copy.setValue("bar"); 1.252 + var old = a.swapDoc(copy); 1.253 + eq(a.getValue(), "bar"); 1.254 + a.undo(); 1.255 + eq(a.getValue(), "u"); 1.256 + a.swapDoc(old); 1.257 + eq(a.getValue(), "foo"); 1.258 + eq(old.historySize().undo, 1); 1.259 + eq(old.copy(false).historySize().undo, 0); 1.260 + }); 1.261 + 1.262 + testDoc("docKeepsMode", "A='1+1'", function(a) { 1.263 + var other = CodeMirror.Doc("hi", "text/x-markdown"); 1.264 + a.setOption("mode", "text/javascript"); 1.265 + var old = a.swapDoc(other); 1.266 + eq(a.getOption("mode"), "text/x-markdown"); 1.267 + eq(a.getMode().name, "markdown"); 1.268 + a.swapDoc(old); 1.269 + eq(a.getOption("mode"), "text/javascript"); 1.270 + eq(a.getMode().name, "javascript"); 1.271 + }); 1.272 + 1.273 + testDoc("subview", "A='1\n2\n3\n4\n5' B<~A/1-3", function(a, b) { 1.274 + eq(b.getValue(), "2\n3"); 1.275 + eq(b.firstLine(), 1); 1.276 + b.setCursor(Pos(4)); 1.277 + eqPos(b.getCursor(), Pos(2, 1)); 1.278 + a.replaceRange("-1\n0\n", Pos(0, 0)); 1.279 + eq(b.firstLine(), 3); 1.280 + eqPos(b.getCursor(), Pos(4, 1)); 1.281 + a.undo(); 1.282 + eqPos(b.getCursor(), Pos(2, 1)); 1.283 + b.replaceRange("oyoy\n", Pos(2, 0)); 1.284 + eq(a.getValue(), "1\n2\noyoy\n3\n4\n5"); 1.285 + b.undo(); 1.286 + eq(a.getValue(), "1\n2\n3\n4\n5"); 1.287 + }); 1.288 + 1.289 + testDoc("subviewEditOnBoundary", "A='11\n22\n33\n44\n55' B<~A/1-4", function(a, b) { 1.290 + a.replaceRange("x\nyy\nz", Pos(0, 1), Pos(2, 1)); 1.291 + eq(b.firstLine(), 2); 1.292 + eq(b.lineCount(), 2); 1.293 + eq(b.getValue(), "z3\n44"); 1.294 + a.replaceRange("q\nrr\ns", Pos(3, 1), Pos(4, 1)); 1.295 + eq(b.firstLine(), 2); 1.296 + eq(b.getValue(), "z3\n4q"); 1.297 + eq(a.getValue(), "1x\nyy\nz3\n4q\nrr\ns5"); 1.298 + a.execCommand("selectAll"); 1.299 + a.replaceSelection("!"); 1.300 + eqAll("!", a, b); 1.301 + }); 1.302 + 1.303 + 1.304 + testDoc("sharedMarker", "A='ab\ncd\nef\ngh' B<A C<~A/1-2", function(a, b, c) { 1.305 + var mark = b.markText(Pos(0, 1), Pos(3, 1), 1.306 + {className: "cm-searching", shared: true}); 1.307 + var found = a.findMarksAt(Pos(0, 2)); 1.308 + eq(found.length, 1); 1.309 + eq(found[0], mark); 1.310 + eq(c.findMarksAt(Pos(1, 1)).length, 1); 1.311 + eqPos(mark.find().from, Pos(0, 1)); 1.312 + eqPos(mark.find().to, Pos(3, 1)); 1.313 + b.replaceRange("x\ny\n", Pos(0, 0)); 1.314 + eqPos(mark.find().from, Pos(2, 1)); 1.315 + eqPos(mark.find().to, Pos(5, 1)); 1.316 + var cleared = 0; 1.317 + CodeMirror.on(mark, "clear", function() {++cleared;}); 1.318 + b.operation(function(){mark.clear();}); 1.319 + eq(a.findMarksAt(Pos(3, 1)).length, 0); 1.320 + eq(b.findMarksAt(Pos(3, 1)).length, 0); 1.321 + eq(c.findMarksAt(Pos(3, 1)).length, 0); 1.322 + eq(mark.find(), null); 1.323 + eq(cleared, 1); 1.324 + }); 1.325 + 1.326 + testDoc("sharedBookmark", "A='ab\ncd\nef\ngh' B<A C<~A/1-2", function(a, b, c) { 1.327 + var mark = b.setBookmark(Pos(1, 1), {shared: true}); 1.328 + var found = a.findMarksAt(Pos(1, 1)); 1.329 + eq(found.length, 1); 1.330 + eq(found[0], mark); 1.331 + eq(c.findMarksAt(Pos(1, 1)).length, 1); 1.332 + eqPos(mark.find(), Pos(1, 1)); 1.333 + b.replaceRange("x\ny\n", Pos(0, 0)); 1.334 + eqPos(mark.find(), Pos(3, 1)); 1.335 + var cleared = 0; 1.336 + CodeMirror.on(mark, "clear", function() {++cleared;}); 1.337 + b.operation(function() {mark.clear();}); 1.338 + eq(a.findMarks(Pos(0, 0), Pos(5)).length, 0); 1.339 + eq(b.findMarks(Pos(0, 0), Pos(5)).length, 0); 1.340 + eq(c.findMarks(Pos(0, 0), Pos(5)).length, 0); 1.341 + eq(mark.find(), null); 1.342 + eq(cleared, 1); 1.343 + }); 1.344 + 1.345 + testDoc("undoInSubview", "A='line 0\nline 1\nline 2\nline 3\nline 4' B<A/1-4", function(a, b) { 1.346 + b.replaceRange("x", Pos(2, 0)); 1.347 + a.undo(); 1.348 + eq(a.getValue(), "line 0\nline 1\nline 2\nline 3\nline 4"); 1.349 + eq(b.getValue(), "line 1\nline 2\nline 3"); 1.350 + }); 1.351 +})();