Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(60)

Side by Side Diff: third_party/WebKit/LayoutTests/editing/spelling/spellcheck_test.js

Issue 2435273002: Introduce spellcheck_test (Closed)
Patch Set: Introduce Marker class Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 'use strict';
6
7 // This file provides
8 // |spellcheck_test(sample, tester, expectedMarkers, opt_title)| asynchronous
9 // test to W3C test harness for easier writing of editing test cases.
10 //
11 // |sample| is an HTML fragment text which is inserted as |innerHTML|. It should
12 // have at least one focus boundary point marker "|" and at most one anchor
13 // boundary point marker "^" indicating the initial selection.
14 //
15 // |tester| is either name with parameter of execCommand or function taking
16 // one parameter |Document|.
17 //
18 // |expectedMarkers| is either a |Marker| or a |Marker| array, where each
19 // |Marker| is an |Object| with the following properties:
20 // - |location| and |length| are integers indicating the range of the marked
21 // text. It must hold that |location >= 0| and |length > 0|.
22 // - |type| is an optional string indicating the marker type. When present, it
23 // must be equal to either "spelling" or "grammer". When absent, it is
24 // regarded as "spelling".
25 // - |description| is an optional string indicating the description of a marker.
26 //
27 // |opt_title| is an optional string giving the title of the test case.
28 //
29 // Examples:
30 //
31 // spellcheck_test(
32 // '<div contentEditable>|</div>',
33 // 'insertText wellcome.',
34 // {
35 // location: 0,
36 // length: 8,
37 // type: 'spelling',
38 // description: 'welcome'
39 // },
40 // 'Mark misspellings and give replacement suggestions after simple typing.'
41 // );
42
43 // spellcheck_test(
44 // '<div contentEditable>|</div>',
45 // 'insertText You has the right.',
46 // {
47 // location: 4,
48 // length: 3,
49 // type: 'grammar'
50 // },
51 // 'Mark ungrammatical phrases after simple typing.'
52 // );
53 //
54
55 (function() {
56 const Sample = window.Sample;
57
58 class Marker {
59 /**
60 * @public
61 * @param {number} location
62 * @param {number} length
63 * @param {string=} opt_type
64 * @param {string=} opt_description
65 */
66 constructor(location, length, opt_type, opt_description) {
67 this.location_ = location;
68 this.length_ = length;
69 this.type_ = opt_type || 'spelling';
70 this.description_ = opt_description;
71 }
72
73 /** @return {!number} */
74 get location() { return this.location_; }
75
76 /** @return {!number} */
77 get length() { return this.length_; }
78
79 /** @return {!string} */
80 get type() { return this.type_; }
81
82 /** @return {string=} */
83 get description() { return this.description_; }
84
85 /**
86 * @public
87 */
88 assertValid() {
89 // TODO(xiaochengh): Add proper assert descriptions when needed.
90 assert_true(Number.isInteger(this.location_));
91 assert_greater_than_equal(this.location_, 0);
92 assert_true(Number.isInteger(this.length_));
93 assert_greater_than(this.length_, 0);
94 assert_true(this.type_ === 'spelling' || this.type_ === 'grammar');
95 if (this.description_ !== undefined)
yosin_UTC9 2016/10/24 04:40:08 nit: We prefer early-return style to reduce indent
Xiaocheng 2016/10/24 05:11:37 Done.
96 assert_true(typeof this.description_ === 'string');
97 }
98
99 /**
100 * @public
101 * @param {Marker} expected
102 */
103 assertMatch(expected) {
104 assert_equals(this.location, expected.location,
105 'Marker locations mismatch.');
106 assert_equals(this.length, expected.length, 'Marker lengths mismatch.');
107 assert_equals(this.type, expected.type, 'Marker types mismatch.');
108 if (expected.description !== undefined) {
yosin_UTC9 2016/10/24 04:40:08 nit: We prefer early-return style.
Xiaocheng 2016/10/24 05:11:37 Done.
109 assert_equals(this.description, expected.description,
110 'Marker descriptions mismatch');
111 }
112 }
113 }
114
115 /**
116 * @param {number} location
117 * @param {number} length
118 * @param {string=} opt_description
119 * @return {!Marker}
120 */
121 function spellingMarker(location, length, opt_description) {
122 return new Marker(location, length, 'spelling', opt_description);
123 }
124
125 /**
126 * @param {number} location
127 * @param {number} length
128 * @param {string=} opt_description
129 * @return {!Marker}
130 */
131 function grammarMarker(location, length, opt_description) {
132 return new Marker(location, length, 'grammar', opt_description);
133 }
134
135 /**
136 * @param {Marker[]} expectedMarkers
137 */
138 function checkExpectedMarkers(expectedMarkers) {
139 expectedMarkers.forEach(marker => marker.assertValid());
140 expectedMarkers.sort((a, b) => a.location - b.location);
141 for (var i = 1; i < expectedMarkers.length; ++i) {
yosin_UTC9 2016/10/24 04:40:08 Let's use Array#forEach. We can write: expectedMa
Xiaocheng 2016/10/24 05:11:37 Didn't know |forEach| takes a second parameter. D
142 assert_less_than(
143 expectedMarkers[i - 1].location + expectedMarkers[i - 1].length,
144 expectedMarkers[i].location,
145 'Marker ranges should be disjoint.');
146 }
147 }
148
149 /**
150 * @param {Node} node
yosin_UTC9 2016/10/24 04:40:08 nit: s/Node/!Node/ It think passing |null| to |ex
Xiaocheng 2016/10/24 05:11:37 Done.
151 * @param {string} type
152 * @param {Marker[]} markers
153 */
154 function extractMarkersOfType(node, type, markers) {
155 /** @type {HTMLBodyElement} */
156 const body = node.ownerDocument.body;
157 /** @type {number} */
158 const markerCount = internals.markerCountForNode(node, type);
159 for (var i = 0; i < markerCount; ++i) {
160 /** @type {Range} */
161 const markerRange = internals.markerRangeForNode(node, type, i);
162 /** @type {string} */
163 const description = internals.markerDescriptionForNode(node, type, i);
164 /** @type {number} */
165 const location = internals.locationFromRange(body, markerRange);
166 /** @type {number} */
167 const length = internals.lengthFromRange(body, markerRange);
168
169 markers.push(new Marker(location, length, type, description));
170 }
171 }
172
173 /**
174 * @param {Node} node
yosin_UTC9 2016/10/24 04:40:08 nit: s/Node/!Node/
Xiaocheng 2016/10/24 05:11:37 Done.
175 * @param {Marker[]} markers
yosin_UTC9 2016/10/24 04:40:08 s/Marker[]/!Array<!Marker>/
Xiaocheng 2016/10/24 05:11:37 Done.
176 */
177 function extractAllMarkersRecursivelyTo(node, markers) {
178 extractMarkersOfType(node, 'spelling', markers);
179 extractMarkersOfType(node, 'grammar', markers);
180 node.childNodes.forEach(
181 child => extractAllMarkersRecursivelyTo(child, markers));
182 }
183
184 /**
185 * @param {Document} doc
yosin_UTC9 2016/10/24 04:40:08 nit: s/Document/!Document/
Xiaocheng 2016/10/24 05:11:37 Done.
186 * @return {Marker[]}
187 */
188 function extractAllMarkers(doc) {
189 /** @type {Marker[]} */
190 const markers = [];
191 extractAllMarkersRecursivelyTo(doc.body, markers);
192 markers.sort((a, b) => a.location - b.location);
193 return markers;
194 }
195
196 /**
197 * @param {Test} testObject
198 * @param {Sample} sample,
199 * @param {Marker[]} expectedMarkers
200 * @param {number} remainingRetry
201 * @param {number} retryInterval
202 */
203 function verifyMarkers(
204 testObject, sample, expectedMarkers, remainingRetry, retryInterval) {
205 assert_not_equals(
206 window.internals, undefined,
207 'window.internals is required for running automated spellcheck tests.');
208
209 /** @type {Marker[]} */
210 const actualMarkers = extractAllMarkers(sample.document);
211 try {
212 assert_equals(
213 actualMarkers.length, expectedMarkers.length,
214 'Number of markers mismatch.');
215 for (var i = 0; i < expectedMarkers.length; ++i)
216 actualMarkers[i].assertMatch(expectedMarkers[i]);
217
218 testObject.done();
219 sample.remove();
220 } catch (e) {
221 if (remainingRetry > 0) {
222 // Force invoking idle time spellchecker if it was not run yet.
223 if (window.testRunner)
224 testRunner.runIdleTasks(() => {});
225
226 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger
227 // something in JavaScript (e.g., a |Promise|), so that we can actively
228 // know the completion of spellchecking instead of passively waiting for
229 // markers to appear or disappear.
230 testObject.step_timeout(
231 () => verifyMarkers(testObject, sample, expectedMarkers,
232 remainingRetry - 1, retryInterval),
233 retryInterval);
234 } else {
235 throw e;
236 }
237 }
238 }
239
240 /**
241 * @param {string} inputText
242 * @param {function(!Document)|string}
243 * @param {Marker|Marker[]} expectedMarkers
244 * @param {string=} opt_title
245 */
246 function spellcheckTest(inputText, tester, expectedMarkers, opt_title) {
247 /** @type {Test} */
248 const testObject = async_test(opt_title);
249
250 if (!(expectedMarkers instanceof Array))
251 expectedMarkers = [expectedMarkers]
252 testObject.step(() => checkExpectedMarkers(expectedMarkers));
253
254 if (window.testRunner)
255 testRunner.setMockSpellCheckerEnabled(true);
256
257 /** @type {Sample} */
258 const sample = new Sample(inputText);
259 if (typeof(tester) === 'function') {
260 tester.call(window, sample.document);
261 } else if (typeof(tester) === 'string') {
262 const strings = tester.split(/ (.+)/);
263 sample.document.execCommand(strings[0], false, strings[1]);
264 } else {
265 testObject.step(() => assert_unreached(`Invalid tester: ${tester}`));
266 }
267
268 /** @type {number} */
269 const maxRetry = 10;
270 /** @type {number} */
271 const retryInterval = 50;
272
273 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger
274 // something in JavaScript (e.g., a |Promise|), so that we can actively know
275 // the completion of spellchecking instead of passively waiting for markers to
276 // appear or disappear.
277 testObject.step_timeout(
278 () => verifyMarkers(testObject, sample, expectedMarkers,
279 maxRetry, retryInterval),
280 retryInterval);
281 }
282
283 // Export symbols
284 window.Marker = Marker;
285 window.spellingMarker = spellingMarker;
286 window.grammarMarker = grammarMarker;
287 window.spellcheck_test = spellcheckTest;
288 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698