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

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

Issue 2435273002: Introduce spellcheck_test (Closed)
Patch Set: spellcheck_test Mon Oct 24 14:09:33 JST 2016 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
« no previous file with comments | « third_party/WebKit/LayoutTests/editing/spelling/spellcheck_test.html ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 // {
yosin_UTC9 2016/10/24 06:17:00 Let's use |spellingMarker()| and |grammarMarker()|
Xiaocheng 2016/10/24 06:37:57 Whoops. Forgot to change this part.
35 // location: 0,
36 // length: 8,
37 // type: 'spelling',
38 // description: 'welcome'
39 // },
40 // 'Mark misspellings and give replacement suggestions after typing.');
41
42 // spellcheck_test(
43 // '<div contentEditable>|</div>',
44 // 'insertText You has the right.',
45 // {
46 // location: 4,
47 // length: 3,
48 // type: 'grammar'
49 // },
50 // 'Mark ungrammatical phrases after typing.');
51 //
52
53 (function() {
54 const Sample = window.Sample;
55
56 class Marker {
57 /**
58 * @public
59 * @param {number} location
60 * @param {number} length
61 * @param {string=} opt_type
62 * @param {string=} opt_description
63 */
64 constructor(location, length, opt_type, opt_description) {
65 /** @type {number} */
66 this.location_ = location;
67 /** @type {number} */
68 this.length_ = length;
69 /** @type {string} */
70 this.type_ = opt_type || 'spelling';
71 /** @type {string|null} */
72 this.description_ = opt_description ? opt_description : null;
73 }
74
75 /** @return {number} */
76 get location() { return this.location_; }
77
78 /** @return {number} */
79 get length() { return this.length_; }
80
81 /** @return {string} */
82 get type() { return this.type_; }
83
84 /** @return {string|null} */
85 get description() { return this.description_; }
86
87 /**
88 * @public
89 */
90 assertValid() {
91 // TODO(xiaochengh): Add proper assert descriptions when needed.
92 assert_true(Number.isInteger(this.location_));
93 assert_greater_than_equal(this.location_, 0);
94 assert_true(Number.isInteger(this.length_));
95 assert_greater_than(this.length_, 0);
96 assert_true(this.type_ === 'spelling' || this.type_ === 'grammar');
97 if (this.description_ === null)
98 return;
99 assert_true(typeof this.description_ === 'string');
100 }
101
102 /**
103 * @public
104 * @param {!Marker} expected
105 */
106 assertMatch(expected) {
107 assert_equals(this.location, expected.location,
108 'Marker locations mismatch.');
109 assert_equals(this.length, expected.length, 'Marker lengths mismatch.');
110 assert_equals(this.type, expected.type, 'Marker types mismatch.');
111 if (expected.description === null)
112 return;
113 assert_equals(this.description, expected.description,
114 'Marker descriptions mismatch');
115 }
116 }
117
118 /**
119 * @param {number} location
120 * @param {number} length
121 * @param {string=} opt_description
122 * @return {!Marker}
123 */
124 function spellingMarker(location, length, opt_description) {
125 return new Marker(location, length, 'spelling', opt_description);
126 }
127
128 /**
129 * @param {number} location
130 * @param {number} length
131 * @param {string=} opt_description
132 * @return {!Marker}
133 */
134 function grammarMarker(location, length, opt_description) {
135 return new Marker(location, length, 'grammar', opt_description);
136 }
137
138 /**
139 * @param {!Array<Marker>} expectedMarkers
140 */
141 function checkExpectedMarkers(expectedMarkers) {
142 expectedMarkers.forEach(marker => marker.assertValid());
143 expectedMarkers.sort((a, b) => a.location - b.location);
144 expectedMarkers.forEach((currentMarker, index) => {
yosin_UTC9 2016/10/24 06:17:00 How about Array#reduce() expectedMarkers.reduce((
Xiaocheng 2016/10/24 06:37:57 Didn't know that. Done.
145 if (index == 0)
146 return;
147 /** @type {!Marker} */
148 const lastMarker = expectedMarkers[index - 1];
149 assert_less_than(
150 lastMarker.location + lastMarker.length, currentMarker.location,
151 'Marker ranges should be disjoint.');
152 });
153 }
154
155 /**
156 * @param {!Node} node
157 * @param {string} type
158 * @param {!Array<Marker>} markers
yosin_UTC9 2016/10/24 06:17:00 s/!Array<Marker>/!Array<!Marker>/
Xiaocheng 2016/10/24 06:37:57 Done.
159 */
160 function extractMarkersOfType(node, type, markers) {
161 /** @type {!HTMLBodyElement} */
162 const body = node.ownerDocument.body;
163 /** @type {number} */
164 const markerCount = window.internals.markerCountForNode(node, type);
165 for (var i = 0; i < markerCount; ++i) {
166 /** @type {!Range} */
167 const markerRange = window.internals.markerRangeForNode(node, type, i);
168 /** @type {string} */
169 const description = window.internals.markerDescriptionForNode(node, type, i) ;
170 /** @type {number} */
171 const location = window.internals.locationFromRange(body, markerRange);
172 /** @type {number} */
173 const length = window.internals.lengthFromRange(body, markerRange);
174
175 markers.push(new Marker(location, length, type, description));
176 }
177 }
178
179 /**
180 * @param {!Node} node
181 * @param {!Array<Marker>} markers
182 */
183 function extractAllMarkersRecursivelyTo(node, markers) {
184 extractMarkersOfType(node, 'spelling', markers);
185 extractMarkersOfType(node, 'grammar', markers);
186 node.childNodes.forEach(
187 child => extractAllMarkersRecursivelyTo(child, markers));
188 }
189
190 /**
191 * @param {!Document} doc
192 * @return {!Array<Marker>}
193 */
194 function extractAllMarkers(doc) {
195 /** @type {!Array<Marker>} */
196 const markers = [];
197 extractAllMarkersRecursivelyTo(doc.body, markers);
198 markers.sort((a, b) => a.location - b.location);
199 return markers;
200 }
201
202 /**
203 * @param {!Test} testObject
204 * @param {!Sample} sample,
205 * @param {!Array<Marker>} expectedMarkers
206 * @param {number} remainingRetry
207 * @param {number} retryInterval
208 */
209 function verifyMarkers(
210 testObject, sample, expectedMarkers, remainingRetry, retryInterval) {
211 assert_not_equals(
212 window.internals, undefined,
213 'window.internals is required for running automated spellcheck tests.');
214
215 /** @type {!Array<Marker>} */
216 const actualMarkers = extractAllMarkers(sample.document);
217 try {
218 assert_equals(actualMarkers.length, expectedMarkers.length,
219 'Number of markers mismatch.');
220 actualMarkers.forEach(
221 (marker, index) => marker.assertMatch(expectedMarkers[index]));
222 testObject.done();
223 sample.remove();
224 } catch (e) {
225 if (remainingRetry > 0) {
226 // Force invoking idle time spellchecker if it was not run yet.
227 if (window.testRunner)
228 window.testRunner.runIdleTasks(() => {});
229
230 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger
231 // something in JavaScript (e.g., a |Promise|), so that we can actively
232 // know the completion of spellchecking instead of passively waiting for
233 // markers to appear or disappear.
234 testObject.step_timeout(
235 () => verifyMarkers(testObject, sample, expectedMarkers,
236 remainingRetry - 1, retryInterval),
237 retryInterval);
238 } else {
239 throw e;
240 }
241 }
242 }
243
244 /**
245 * @param {string} inputText
246 * @param {function(!Document)|string} tester
247 * @param {!Marker|!Array<Marker>} expectedMarkers
248 * @param {string=} opt_title
249 */
250 function spellcheckTest(inputText, tester, expectedMarkers, opt_title) {
251 /** @type {!Test} */
252 const testObject = async_test(opt_title);
253
254 if (!(expectedMarkers instanceof Array))
255 expectedMarkers = [expectedMarkers]
256 testObject.step(() => checkExpectedMarkers(expectedMarkers));
257
258 if (window.testRunner)
259 window.testRunner.setMockSpellCheckerEnabled(true);
260
261 /** @type {!Sample} */
262 const sample = new Sample(inputText);
263 if (typeof(tester) === 'function') {
264 tester.call(window, sample.document);
265 } else if (typeof(tester) === 'string') {
266 const strings = tester.split(/ (.+)/);
267 sample.document.execCommand(strings[0], false, strings[1]);
268 } else {
269 testObject.step(() => assert_unreached(`Invalid tester: ${tester}`));
270 }
271
272 /** @type {number} */
273 const maxRetry = 10;
274 /** @type {number} */
275 const retryInterval = 50;
276
277 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger
278 // something in JavaScript (e.g., a |Promise|), so that we can actively know
279 // the completion of spellchecking instead of passively waiting for markers to
280 // appear or disappear.
281 testObject.step_timeout(
282 () => verifyMarkers(testObject, sample, expectedMarkers,
283 maxRetry, retryInterval),
284 retryInterval);
285 }
286
287 // Export symbols
288 window.Marker = Marker;
289 window.spellingMarker = spellingMarker;
290 window.grammarMarker = grammarMarker;
291 window.spellcheck_test = spellcheckTest;
292 })();
OLDNEW
« no previous file with comments | « third_party/WebKit/LayoutTests/editing/spelling/spellcheck_test.html ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698