Use sticky positioning for the code review toolbar
[WebKit-https.git] / Websites / bugs.webkit.org / code-review-test.html
1 <div>Tests for some of the easily unittestable parts of code-review.js. You should see a series of "PASS" lines.</div>
2 <div>FIXME: Run these as part of the layout test suite?</div>
3
4 <script>
5 CODE_REVIEW_UNITTEST = true;
6
7 window.onerror = function(errorMsg, url, lineNumber) {
8   log('FAIL: Received an error at line ' + lineNumber);
9   return false;
10 }
11 </script>
12 <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script> 
13 <script src="code-review.js"></script>
14 <pre id="output"></pre>
15 <div id="diff-content"></div>
16 <script>
17 function inherits(childConstructor, parentConstructor) {
18   function tempConstructor() {};
19   tempConstructor.prototype = parentConstructor.prototype;
20   childConstructor.prototype = new tempConstructor();
21   childConstructor.prototype.constructor = childConstructor;
22 }
23
24 function log(msg) {
25     document.getElementById('output').textContent += msg + '\n\n';
26 }
27
28 function ASSERT(msg, assertion) {
29     if (assertion)
30         log('PASS');
31     else
32         log('FAIL: ' + msg);
33 }
34
35 function ASSERT_EQUAL(actual, expected) {
36     if (actual == expected)
37         log('PASS');
38     else
39         log('FAIL:\ngot:\n' + actual + '\nexpected:\n' + expected + '');
40 }
41
42 function testTracLinks() {
43   var links = tracLinks('foo/bar/baz.cpp', '');
44   ASSERT_EQUAL($('<div>').append(links).html(),
45     '<a target="_blank" href="http://trac.webkit.org/log/trunk/foo/bar/baz.h">header</a>' +
46     '<a target="_blank" href="http://trac.webkit.org/browser/trunk/foo/bar/baz.cpp?annotate=blame">annotate</a>' +
47     '<a target="_blank" href="http://trac.webkit.org/log/trunk/foo/bar/baz.cpp">revision log</a>');
48
49   var links = tracLinks('foo/bar.cpp/qux.mm', '');
50   ASSERT_EQUAL($('<div>').append(links).html(),
51     '<a target="_blank" href="http://trac.webkit.org/log/trunk/foo/bar.cpp/qux.h">header</a>' +
52     '<a target="_blank" href="http://trac.webkit.org/browser/trunk/foo/bar.cpp/qux.mm?annotate=blame">annotate</a>' +
53     '<a target="_blank" href="http://trac.webkit.org/log/trunk/foo/bar.cpp/qux.mm">revision log</a>');
54
55   var links = tracLinks('foo/bar.h', '');
56   ASSERT_EQUAL($('<div>').append(links).html(),
57     '<a target="_blank" href="http://trac.webkit.org/browser/trunk/foo/bar.h?annotate=blame">annotate</a>' +
58     '<a target="_blank" href="http://trac.webkit.org/log/trunk/foo/bar.h">revision log</a>');
59
60   var links = tracLinks('foo/bar.gypi', '');
61   ASSERT_EQUAL($('<div>').append(links).html(),
62     '<a target="_blank" href="http://trac.webkit.org/browser/trunk/foo/bar.gypi?annotate=blame">annotate</a>' +
63     '<a target="_blank" href="http://trac.webkit.org/log/trunk/foo/bar.gypi">revision log</a>');
64
65   var links = tracLinks('foo/ChangeLog', '');
66   ASSERT_EQUAL($('<div>').append(links).html(),
67     '<a target="_blank" href="http://trac.webkit.org/browser/trunk/foo/ChangeLog?annotate=blame">annotate</a>' +
68     '<a target="_blank" href="http://trac.webkit.org/log/trunk/foo/ChangeLog">revision log</a>');
69 }
70
71 function testDraftCommentSaver() {
72   function MockLocalStorage() {
73       this.localStorageStore = {};
74       this.log = [];
75
76       this.getItem = function(key) {
77           this.log.push('getItem:' + key);
78           return this.localStorageStore[key];
79       };
80
81       this.setItem = function(key, value) {
82           // For testing sake, consider having more than 2 items to exceed the storage quota.
83           if (Object.keys(this.localStorageStore).length > 2) {
84               this.log.push('QuotaExceeded on setItem:' + key + ',' + value);
85               throw "QuotaExceeded";
86           }
87           this.log.push('setItem:' + key + ',' + value);
88           this.localStorageStore[key] = value;
89       };
90
91       this.removeItem = function(key) {
92           this.log.push('removeItem:' + key);
93           delete this.localStorageStore[key];
94       };
95
96       this.log_string = function() {
97           return this.log.join('\n');
98       };
99
100       this.key = function(i) {
101           return Object.keys(this.localStorageStore)[i];
102       };
103
104       this.__defineGetter__('length', function() {
105           return Object.keys(this.localStorageStore).length;
106       });
107   }
108
109   function MockDraftCommentSaver(attachment_id, opt_localStorage) {
110       DraftCommentSaver.call(this, attachment_id, opt_localStorage);
111   }
112
113   inherits(MockDraftCommentSaver, DraftCommentSaver)
114
115   MockDraftCommentSaver.prototype._json = function() {
116       return "{MOCK JSON}";
117   }
118
119   MockDraftCommentSaver.prototype._should_remove_comments = function(message) {
120       return false;
121   }
122
123   // Basic setItem.
124   var ls = new MockLocalStorage();
125   new MockDraftCommentSaver('1234', ls).save();
126   ASSERT_EQUAL(ls.log_string(), 'setItem:draft-comments-for-attachment-1234,{MOCK JSON}');
127
128   // Exceed quota, but succeed after erasing old reviews.
129   var ls = new MockLocalStorage();
130   ls.localStorageStore = {
131       'draft-comments-for-attachment-1': '{"born-on": 100, "comments":[]}',
132       'draft-comments-for-attachment-2': '{"born-on": 100, "comments":[]}',
133       'draft-comments-for-attachment-3': '{"born-on": 100, "comments":[]}',
134       'draft-comments-for-attachment-4': '{"born-on": ' + (Date.now() - 100) + ', "comments":[]}'
135   };
136   new MockDraftCommentSaver('1234', ls).save();
137   ASSERT_EQUAL(ls.log_string(), 'QuotaExceeded on setItem:draft-comments-for-attachment-1234,{MOCK JSON}\ngetItem:draft-comments-for-attachment-1\ngetItem:draft-comments-for-attachment-2\ngetItem:draft-comments-for-attachment-3\ngetItem:draft-comments-for-attachment-4\nremoveItem:draft-comments-for-attachment-1\nremoveItem:draft-comments-for-attachment-2\nremoveItem:draft-comments-for-attachment-3\nsetItem:draft-comments-for-attachment-1234,{MOCK JSON}');
138
139   // Exceed quota after erasing old reviews and fail after prompt.
140   var ls = new MockLocalStorage();
141   ls.localStorageStore = {
142       'draft-comments-for-attachment-1': '{"born-on": 100, "comments":[]}',
143       'draft-comments-for-attachment-2': '{"born-on": ' + (Date.now() - 100) + ', "comments":[]}',
144       'draft-comments-for-attachment-3': '{"born-on": ' + (Date.now() - 100) + ', "comments":[]}',
145       'draft-comments-for-attachment-4': '{"born-on": ' + (Date.now() - 100) + ', "comments":[]}'
146   };
147   var mockDraftSaver = new MockDraftCommentSaver('1234', ls);
148   mockDraftSaver.save();
149   // Second save to ensure that we stop trying to save when we fail the prompt.
150   mockDraftSaver.save();
151   ASSERT_EQUAL(ls.log_string(), 'QuotaExceeded on setItem:draft-comments-for-attachment-1234,{MOCK JSON}\ngetItem:draft-comments-for-attachment-1\ngetItem:draft-comments-for-attachment-2\ngetItem:draft-comments-for-attachment-3\ngetItem:draft-comments-for-attachment-4\nremoveItem:draft-comments-for-attachment-1\nQuotaExceeded on setItem:draft-comments-for-attachment-1234,{MOCK JSON}');
152
153   // Exceed quota after erasing old reviews, but succeed after prompt.
154   var ls = new MockLocalStorage();
155   ls.localStorageStore = {
156       'draft-comments-for-attachment-1': '{"born-on": 100, "comments":[]}',
157       'draft-comments-for-attachment-2': '{"born-on": ' + (Date.now() - 100) + ', "comments":[]}',
158       'draft-comments-for-attachment-3': '{"born-on": ' + (Date.now() - 100) + ', "comments":[]}',
159       'draft-comments-for-attachment-4': '{"born-on": ' + (Date.now() - 100) + ', "comments":[]}'
160   };
161   var mockDraftSaver = new MockDraftCommentSaver('1234', ls);
162   mockDraftSaver._should_remove_comments = function() { return true; };
163   mockDraftSaver.save();
164   ASSERT_EQUAL(ls.log_string(), 'QuotaExceeded on setItem:draft-comments-for-attachment-1234,{MOCK JSON}\ngetItem:draft-comments-for-attachment-1\ngetItem:draft-comments-for-attachment-2\ngetItem:draft-comments-for-attachment-3\ngetItem:draft-comments-for-attachment-4\nremoveItem:draft-comments-for-attachment-1\nQuotaExceeded on setItem:draft-comments-for-attachment-1234,{MOCK JSON}\nremoveItem:draft-comments-for-attachment-2\nremoveItem:draft-comments-for-attachment-3\nremoveItem:draft-comments-for-attachment-4\nsetItem:draft-comments-for-attachment-1234,{MOCK JSON}');
165
166   // Always exceeds quota, even after erasing all review comments. There should be no setItem calls.
167   var ls = new MockLocalStorage();
168   ls.setItem = function() {
169       this.log.push('QuotaExceeded on setItem:' + key + ',' + value);
170       throw "QuotaExceeded"; 
171   }
172   ls.localStorageStore = {
173       'draft-comments-for-attachment-1': '{"born-on": 100, "comments":[]}',
174       'draft-comments-for-attachment-2': '{"born-on": ' + (Date.now() - 100) + ', "comments":[]}',
175       'draft-comments-for-attachment-3': '{"born-on": ' + (Date.now() - 100) + ', "comments":[]}',
176       'draft-comments-for-attachment-4': '{"born-on": ' + (Date.now() - 100) + ', "comments":[]}'
177   };
178   var mockDraftSaver = new MockDraftCommentSaver('1234', ls);
179   mockDraftSaver._should_remove_comments = function() { return true; };
180   mockDraftSaver.save();
181   // Second save to ensure that we stop trying to save when we fail the prompt.
182   mockDraftSaver.save();
183   ASSERT_EQUAL(ls.log_string(), 'getItem:draft-comments-for-attachment-1\ngetItem:draft-comments-for-attachment-2\ngetItem:draft-comments-for-attachment-3\ngetItem:draft-comments-for-attachment-4\nremoveItem:draft-comments-for-attachment-1\nremoveItem:draft-comments-for-attachment-2\nremoveItem:draft-comments-for-attachment-3\nremoveItem:draft-comments-for-attachment-4');
184
185   var ls = new MockLocalStorage();
186   ls.localStorageStore = {
187       'draft-comments-for-attachment-1': '{"born-on": 100, "comments":[]}',
188       'draft-comments-for-attachment-2': '{"born-on": 100, "comments":[{"start_line_id": 1, "end_line_id": 2, "contents": "DUMMY CONTENTS"}, {"start_line_id": 3, "end_line_id": 4, "contents": "DUMMY CONTENTS 2"}]}'
189   };
190   var comments = new MockDraftCommentSaver('2', ls).saved_comments().comments;
191   ASSERT_EQUAL(comments.length, 2);
192   ASSERT_EQUAL(ls.log_string(), 'getItem:draft-comments-for-attachment-2');
193
194   var ls = new MockLocalStorage();
195   ls.localStorageStore = {
196       'draft-comments-for-attachment-1': 'corrupt comments'
197   };
198   var comments = new MockDraftCommentSaver('1', ls).saved_comments().comments;
199   ASSERT_EQUAL(ls.log_string(), 'getItem:draft-comments-for-attachment-1\nremoveItem:draft-comments-for-attachment-1');
200
201   var ls = new MockLocalStorage();
202   ls.localStorageStore = {
203       'draft-comments-for-attachment-1': '["also corrupt comments"]'
204   };
205   var comments = new MockDraftCommentSaver('1', ls).saved_comments().comments;
206   ASSERT_EQUAL(ls.log_string(), 'getItem:draft-comments-for-attachment-1\nremoveItem:draft-comments-for-attachment-1');
207 }
208
209 function stripBornOn(json) {
210   return json.replace(/\"born\-on\"\:\d+,/, "");
211 }
212
213 function strippedSavedComments() {
214   return stripBornOn(localStorage[g_draftCommentSaver.localStorageKey()]);
215 }
216
217 function syncSlideUp(type, handler) {
218   handler.call(this);
219 }
220
221 function testReaddDiscardedCommentWithPreviousComment() {
222   document.getElementById('diff-content').innerHTML =
223       '<div class="FileDiff">' +
224         '<h1><a href="http://trac.webkit.org/browser/trunk/Source/WebCore/ChangeLog">Source/WebCore/ChangeLog</a></h1>' +
225         '<div class="DiffSection">' +
226           '<div class="DiffBlock">' +
227             '<div class="DiffBlockPart add">' +
228               '<div class="Line LineContainer add">' +
229                 '<span class="from lineNumber">&nbsp;</span><span class="to lineNumber">1</span><span class="text">first diff block first line</span>' +
230               '</div>' +
231               '<div class="Line LineContainer add">' +
232                 '<span class="from lineNumber">&nbsp;</span><span class="to lineNumber">2</span><span class="text"></span>' +
233               '</div>' +
234             '</div>' +
235             '<div class="clear_float"></div>' +
236           '</div>' +
237           '<div class="DiffBlock">' +
238             '<div class="DiffBlockPart shared">' +
239               '<div class="Line LineContainer">' +
240                 '<span class="from lineNumber">1</span><span class="to lineNumber">12</span><span class="text">second diff block first line</span>' +
241               '</div>' +
242               '</div><div class="clear_float">' +
243             '</div>' +
244           '</div>' +
245         '</div>' +
246       '</div>';
247
248   eraseDraftComments();
249   crawlDiff();
250   appendToolbar();
251
252   var line = document.getElementById('line0');
253   var author = "ojan@chromium.org";
254   var comment_text = "This change sux.";
255   addPreviousComment(line, author, comment_text);
256   var previous_comment = document.querySelector('.previousComment');
257   ASSERT("Line with only a previous comment should not have 'data-has-comment' attribute.", !line.getAttribute('data-has-comment'));
258
259   var new_comment = addCommentField(previous_comment);
260   new_comment.find('textarea').val("dummy content");
261   var frozen_comment = acceptComment(new_comment);
262
263   ASSERT_EQUAL(document.querySelectorAll('.comment').length, 1);
264   ASSERT_EQUAL(strippedSavedComments(), '{"comments":[{"start_line_id":"line0","end_line_id":"line0","contents":"dummy content"}],"overall-comments":""}');
265
266   unfreezeComment(frozen_comment);
267   // This is a hack to make slideUp synchronous so that we can keep this test from needing to be async.
268   jQuery.fn.slideUp = syncSlideUp;
269   discardComment(new_comment);
270
271   ASSERT('There should be no draft comments.', !document.querySelector('.comment'));
272   ASSERT_EQUAL(strippedSavedComments(), '{"comments":[],"overall-comments":""}');
273   ASSERT("Line with only a previous comment should not have 'data-has-comment' attribute.", !line.getAttribute('data-has-comment'));
274
275   new_comment = addCommentField(previous_comment);
276   new_comment.find('textarea').val("dummy content");
277   acceptComment(new_comment);
278
279   ASSERT_EQUAL(document.querySelectorAll('.comment').length, 1);
280   ASSERT_EQUAL(strippedSavedComments(), '{"comments":[{"start_line_id":"line0","end_line_id":"line0","contents":"dummy content"}],"overall-comments":""}');
281
282   document.getElementById('diff-content').innerHTML = '';
283 }
284
285 function testSideBySideDiffWithPreviousCommentsOnSharedLine() {
286   document.getElementById('diff-content').innerHTML =
287       '<div class="FileDiff">' +
288         '<h1><a href="http://trac.webkit.org/browser/trunk/Source/WebCore/ChangeLog">Source/WebCore/ChangeLog</a></h1>' +
289         '<div class="DiffSection">' +
290           '<div class="DiffBlock">' +
291             '<div class="DiffBlockPart shared">' +
292               '<div class="Line LineContainer">' +
293               '<span class="from lineNumber">336</span><span class="to lineNumber">338</span><span class="text">    layoutFlexItems(*m_orderIterator, lineContexts);</span>' +
294               '</div>' +
295               '<div class="Line LineContainer">' +
296               '<span class="from lineNumber">337</span><span class="to lineNumber">339</span><span class="text"></span>' +
297               '</div>' +
298               '<div class="Line LineContainer">' +
299               '<span class="from lineNumber">338</span><span class="to lineNumber">340</span><span class="text">    LayoutUnit oldClientAfterEdge = clientLogicalBottom();</span>' +
300               '</div>' +
301             '</div><div class="clear_float">' +
302           '</div>' +
303         '</div>' +
304       '</div>';
305
306   next_line_id = 0;
307   eraseDraftComments();
308   crawlDiff();
309
310   convertAllFileDiffs('sidebyside', $('.FileDiff'));
311
312   displayPreviousComments([{
313     author: 'ojan@chromium.org',
314     file_name: 'Source/WebCore/ChangeLog',
315     line_number: 338,
316     comment_text: 'This change sux.'
317   }]);
318
319   var previous_comment = document.querySelector('.previousComment');
320   ASSERT_EQUAL(previous_comment.getAttribute('data-comment-for'), 'line0');
321
322   var new_comment = addCommentField(previous_comment);
323   ASSERT("New comment should exist and contain a textarea.", new_comment.find('textarea'));
324
325   document.getElementById('diff-content').innerHTML = '';
326 }
327
328 function testIsChangeLog() {
329   ASSERT("Top-level ChangeLog file is a ChangeLog file", isChangeLog("ChangeLog"));
330   ASSERT("Second-level ChangeLog file is a ChangeLog file", isChangeLog("Tools/ChangeLog"));
331   ASSERT("prepare-ChangeLog is not a ChangeLog file", !isChangeLog("Tools/Scripts/prepare-ChangeLog"));
332   ASSERT("ChangeLog-script is not a ChangeLog file", !isChangeLog("Tools/Scripts/ChangeLog-script"));
333 }
334
335 for (var property in window) {
336   if (property.indexOf('test') == 0) {
337     window[property]();
338   }
339 }
340 </script>