1// Copyright 2014 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
5GEN_INCLUDE(['../testing/chromevox_unittest_base.js']);
6
7/**
8 * Base class for walker test fixtures.
9 * @constructor
10 * @extends {ChromeVoxUnitTestBase}
11 */
12function CvoxWalkerUnitTestBase() {}
13
14CvoxWalkerUnitTestBase.prototype = {
15  __proto__: ChromeVoxUnitTestBase.prototype,
16
17  /** @override */
18  closureModuleDeps: [
19    'cvox.CursorSelection',
20    'cvox.TestMsgs'
21  ],
22
23  /**
24   * Common set up for all walker test cases.
25   */
26  setUp: function() {
27    // Needed for getDescription and getGranularityMsg.
28    cvox.ChromeVox.msgs = new cvox.TestMsgs();
29
30    // Delete all nodes in the body.
31    while (document.body.hasChildNodes()) {
32      document.body.removeChild(document.body.lastChild);
33    }
34
35    this.walker = this.newWalker();
36  },
37
38  /**
39   * Returns a new walker appropriate for the child test.
40   * @return {!cvox.AbstractWalker} The walker instance.
41   */
42  newWalker: goog.abstractMethod,
43
44  /**
45   * Makes testing much less verbose. Executes the command on the
46   * selection, then asserts that for all the parameters passed in desc,
47   * the new selection matches. Returns the new selections if assertion passes.
48   * NOTE: If you change the parameters here, you should also change the
49   * whitelist above.
50   * @param {!cvox.CursorSelection} sel The selection.
51   * @param {!string|!cvox.CursorSelection} opt_cmdOrDest The command to
52   *  execute, or the override returned selection.
53   * @param {{selText: string=,
54   *          selNodeId: string=,
55   *          selParentNodeId: string=,
56   *          selStartIndex: number=,
57   *          selEndIndex: number=,
58   *          selReversed: boolean=,
59   *          descText: string=,
60   *          descContext: string=,
61   *          descAnnotation: string=,
62   *          descUserValue: string=,
63   *          descPersonality: string=}} desc The parameters to assert.
64   *    selText: The text in the new selection matches for both start and end.
65   *    selNodeId: The node in the new selection matches for both start and end.
66   *    selParentNodeId: The parent node of both start and end matches.
67   *    selStartIndex: The index of the absolute start.
68   *    selEndIndex: The index of the absolute end.
69   *    selReversed: True if should be reversed.
70   *    descText: The text in the NavDescription when getDescription is called.
71   *    descContext: The context in the NavDescription
72   *    descAnnotation: The annotation in the NavDescription
73   *    descUserValue: The userValue in the NavDescription
74   *    descPersonality: The personality in the NavDescription
75   * @return {cvox.CursorSelection} The new selection.
76   */
77  go: function(sel, opt_cmdOrDest, desc) {
78    if (opt_cmdOrDest instanceof cvox.CursorSelection) {
79      var ret = opt_cmdOrDest;
80    } else {
81      if (CvoxWalkerUnitTestBase.CMD_WHITELIST.indexOf(opt_cmdOrDest) == -1) {
82        // Intentionally fail the test if there's a typo.
83        throw 'Got an invalid command: "' + opt_cmdOrDest + '".';
84      }
85
86      var ret = this.walker[opt_cmdOrDest](sel);
87    }
88
89    if (ret == null) {
90      assertEquals(null, desc);
91      return;
92    }
93    if (desc == null) {
94      assertEquals(null, ret);
95      return;
96    }
97
98    for (var key in desc) {
99      if (CvoxWalkerUnitTestBase.DESC_WHITELIST.indexOf(key) == -1) {
100        throw 'Invalid key in desc parameter: "' + key + '".';
101      }
102    }
103
104    // Intentionally only check one-to-one and not onto. This allows us to
105    // write tests that just ignore everything except what we care about.
106    if (desc.hasOwnProperty('selText')) {
107      assertEquals(desc.selText, ret.start.text);
108      assertEquals(desc.selText, ret.end.text);
109    }
110    if (desc.hasOwnProperty('selNodeId')) {
111      assertEquals(desc.selNodeId, ret.start.node.id);
112      assertEquals(desc.selNodeId, ret.end.node.id);
113    }
114    if (desc.hasOwnProperty('selParentNodeId')) {
115      assertEquals(desc.selParentNodeId, ret.start.node.parentNode.id);
116      assertEquals(desc.selParentNodeId, ret.end.node.parentNode.id);
117    }
118    if (desc.hasOwnProperty('selStartIndex')) {
119      assertEquals(desc.selStartIndex, ret.absStart().index);
120    }
121    if (desc.hasOwnProperty('selEndIndex')) {
122      assertEquals(desc.selEndIndex, ret.absEnd().index);
123    }
124    if (desc.hasOwnProperty('selReversed')) {
125      assertEquals(desc.selReversed, ret.isReversed());
126    }
127
128    var trueDesc = this.walker.getDescription(sel, ret)[0];
129    if (desc.hasOwnProperty('descText')) {
130      assertEquals(desc.descText, trueDesc.text);
131    }
132    if (desc.hasOwnProperty('descContext')) {
133      assertEquals(desc.descContext, trueDesc.context);
134    }
135    if (desc.hasOwnProperty('descAnnotation')) {
136      assertEquals(desc.descAnnotation, trueDesc.annotation);
137    }
138    if (desc.hasOwnProperty('descUserValue')) {
139      assertEquals(desc.descUserValue, trueDesc.userValue);
140    }
141    if (desc.hasOwnProperty('descPersonality')) {
142      assertEquals(desc.descPersonality, trueDesc.personality);
143    }
144
145    return ret;
146  },
147};
148
149/**
150 * Whitelist for the commands that are allowed to be executed with go().
151 * @type {Array.string}
152 * @const
153 */
154CvoxWalkerUnitTestBase.CMD_WHITELIST = ['next', 'sync', 'nextRow', 'nextCol'];
155
156/**
157 * Whitelist for the properties that can be asserted with go().
158 * @type {Array.string}
159 * @const
160 */
161CvoxWalkerUnitTestBase.DESC_WHITELIST = ['selText', 'selNodeId',
162'selParentNodeId', 'selStartIndex', 'selEndIndex', 'selReversed', 'descText',
163'descContext', 'descAnnotation', 'descUserValue', 'descPersonality'];
164
165/**
166 * Adds common walker tests
167 * @param {string} testFixture Name of the test fixture class.
168 */
169CvoxWalkerUnitTestBase.addCommonTests = function(testFixture) {
170  /**
171   * Ensures that syncing to the beginning and ends of the page return
172   * not null.
173   */
174  TEST_F(testFixture, 'testSyncToPage', function() {
175    this.loadDoc(function() {/*!
176      <div><p id="a">a</p></div>
177    */});
178    var ret = this.walker.begin();
179    assertNotEquals(null, ret);
180    ret = this.walker.begin({reversed: true});
181    assertNotEquals(null, ret);
182  });
183
184  /**
185   * Ensures that sync(sync(sel)) = sync(sel)
186   * TODO (stoarca): The interfaces are not frozen yet. In particular,
187   * for TableWalker, sync can return null. Override if it doesn't work yet.
188   */
189  TEST_F(testFixture, 'testSyncInvariant', function() {
190    this.loadDoc(function() {/*!
191      <div id="outer">
192        <p id="a">a</p>
193        <p id="b">b</p>
194        <p id="c">c</p>
195        <p id="d">d</p>
196        <h1 id="A">h1</h1>
197        <p id="e">e</p>
198        <h1 id="B">h1</h1>
199      </div>
200    */});
201    var sel = cvox.CursorSelection.fromNode(
202        $('outer').firstChild);
203    var sync = this.walker.sync(sel);
204    var syncsync = this.walker.sync(sync);
205    assertEquals(true, sync.equals(syncsync));
206
207    sel = cvox.CursorSelection.fromNode($('a'));
208    sync = this.walker.sync(sel);
209    syncsync = this.walker.sync(sync);
210    assertEquals(true, sync.equals(syncsync));
211
212    sel = cvox.CursorSelection.fromNode($('e'));
213    sync = this.walker.sync(sel);
214    syncsync = this.walker.sync(sync);
215    assertEquals(true, sync.equals(syncsync));
216
217    sel = cvox.CursorSelection.fromBody();
218    sync = this.walker.sync(sel);
219    syncsync = this.walker.sync(sync);
220    assertEquals(true, sync.equals(syncsync));
221  });
222
223  /**
224   * Ensures that all operations work on an empty body.
225   */
226  TEST_F(testFixture, 'testEmptyBody', function() {
227    var sel = cvox.CursorSelection.fromBody();
228
229    // Testing for infinite loop. If one exists, this test will fail by timing
230    // out.
231    var sync = this.walker.sync(sel);
232    var next = this.walker.next(sel);
233  });
234};
235