// nsDoTestsForAutoCompleteWithComposition tests autocomplete with composition. // Users must include SimpleTest.js and EventUtils.js. function waitForCondition(condition, nextTest) { var tries = 0; var interval = setInterval(function () { if (condition() || tries >= 30) { moveOn(); } tries++; }, 100); var moveOn = function () { clearInterval(interval); nextTest(); }; } function nsDoTestsForAutoCompleteWithComposition( aDescription, aWindow, aTarget, aAutoCompleteController, aIsFunc, aGetTargetValueFunc, aOnFinishFunc ) { this._description = aDescription; this._window = aWindow; this._target = aTarget; this._controller = aAutoCompleteController; this._is = aIsFunc; this._getTargetValue = aGetTargetValueFunc; this._onFinish = aOnFinishFunc; this._target.focus(); this._DefaultCompleteDefaultIndex = this._controller.input.completeDefaultIndex; this._doTests(); } nsDoTestsForAutoCompleteWithComposition.prototype = { _window: null, _target: null, _controller: null, _DefaultCompleteDefaultIndex: false, _description: "", _is: null, _getTargetValue() { return "not initialized"; }, _onFinish: null, _doTests() { if (++this._testingIndex == this._tests.length) { this._controller.input.completeDefaultIndex = this._DefaultCompleteDefaultIndex; this._onFinish(); return; } var test = this._tests[this._testingIndex]; if ( this._controller.input.completeDefaultIndex != test.completeDefaultIndex ) { this._controller.input.completeDefaultIndex = test.completeDefaultIndex; } test.execute(this._window); if (test.popup) { waitForCondition( () => this._controller.input.popupOpen, this._checkResult.bind(this) ); } else { waitForCondition(() => { this._controller.searchStatus >= Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH; }, this._checkResult.bind(this)); } }, _checkResult() { var test = this._tests[this._testingIndex]; this._is( this._getTargetValue(), test.value, this._description + ", " + test.description + ": value" ); this._is( this._controller.searchString, test.searchString, this._description + ", " + test.description + ": searchString" ); this._is( this._controller.input.popupOpen, test.popup, this._description + ", " + test.description + ": popupOpen" ); this._doTests(); }, _testingIndex: -1, _tests: [ // Simple composition when popup hasn't been shown. // The autocomplete popup should not be shown during composition, but // after compositionend, the popup should be shown. { description: "compositionstart shouldn't open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "M", clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 1, length: 0 }, key: { key: "M" }, }, aWindow ); }, popup: false, value: "M", searchString: "", }, { description: "modifying composition string shouldn't open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "Mo", clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 2, length: 0 }, key: { key: "o" }, }, aWindow ); }, popup: false, value: "Mo", searchString: "", }, { description: "compositionend should open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeComposition( { type: "compositioncommitasis", key: { key: "KEY_Enter" } }, aWindow ); }, popup: true, value: "Mo", searchString: "Mo", }, // If composition starts when popup is shown, the compositionstart event // should cause closing the popup. { description: "compositionstart should close the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "z", clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 1, length: 0 }, key: { key: "z" }, }, aWindow ); }, popup: false, value: "Moz", searchString: "Mo", }, { description: "modifying composition string shouldn't reopen the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "zi", clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 2, length: 0 }, key: { key: "i" }, }, aWindow ); }, popup: false, value: "Mozi", searchString: "Mo", }, { description: "compositionend should research the result and open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeComposition( { type: "compositioncommitasis", key: { key: "KEY_Enter" } }, aWindow ); }, popup: true, value: "Mozi", searchString: "Mozi", }, // If composition is cancelled, the value shouldn't be changed. { description: "compositionstart should reclose the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "l", clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 1, length: 0 }, key: { key: "l" }, }, aWindow ); }, popup: false, value: "Mozil", searchString: "Mozi", }, { description: "modifying composition string shouldn't reopen the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "ll", clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 2, length: 0 }, key: { key: "l" }, }, aWindow ); }, popup: false, value: "Mozill", searchString: "Mozi", }, { description: "modifying composition string to empty string shouldn't reopen the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "", clauses: [{ length: 0, attr: 0 }] }, caret: { start: 0, length: 0 }, key: { key: "KEY_Backspace" }, }, aWindow ); }, popup: false, value: "Mozi", searchString: "Mozi", }, { description: "cancled compositionend should reopen the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeComposition( { type: "compositioncommit", data: "", key: { key: "KEY_Escape" } }, aWindow ); }, popup: true, value: "Mozi", searchString: "Mozi", }, // But if composition replaces some characters and canceled, the search // string should be the latest value. { description: "compositionstart with selected string should close the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeKey("VK_LEFT", { shiftKey: true }, aWindow); synthesizeKey("VK_LEFT", { shiftKey: true }, aWindow); synthesizeCompositionChange( { composition: { string: "z", clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 1, length: 0 }, key: { key: "z" }, }, aWindow ); }, popup: false, value: "Moz", searchString: "Mozi", }, { description: "modifying composition string shouldn't reopen the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "zi", clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 2, length: 0 }, key: { key: "i" }, }, aWindow ); }, popup: false, value: "Mozi", searchString: "Mozi", }, { description: "modifying composition string to empty string shouldn't reopen the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "", clauses: [{ length: 0, attr: 0 }] }, caret: { start: 0, length: 0 }, key: { key: "KEY_Backspace" }, }, aWindow ); }, popup: false, value: "Mo", searchString: "Mozi", }, { description: "canceled compositionend should search the result with the latest value", completeDefaultIndex: false, execute(aWindow) { synthesizeComposition( { type: "compositioncommitasis", key: { key: "KEY_Escape" } }, aWindow ); }, popup: true, value: "Mo", searchString: "Mo", }, // If all characters are removed, the popup should be closed. { description: "the value becomes empty by backspace, the popup should be closed", completeDefaultIndex: false, execute(aWindow) { synthesizeKey("KEY_Backspace", {}, aWindow); synthesizeKey("KEY_Backspace", {}, aWindow); }, popup: false, value: "", searchString: "", }, // composition which is canceled shouldn't cause opening the popup. { description: "compositionstart shouldn't open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "M", clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 1, length: 0 }, key: { key: "M" }, }, aWindow ); }, popup: false, value: "M", searchString: "", }, { description: "modifying composition string shouldn't open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "Mo", clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 2, length: 0 }, key: { key: "o" }, }, aWindow ); }, popup: false, value: "Mo", searchString: "", }, { description: "modifying composition string to empty string shouldn't open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "", clauses: [{ length: 0, attr: 0 }] }, caret: { start: 0, length: 0 }, key: { key: "KEY_Backspace" }, }, aWindow ); }, popup: false, value: "", searchString: "", }, { description: "canceled compositionend shouldn't open the popup if it was closed", completeDefaultIndex: false, execute(aWindow) { synthesizeComposition( { type: "compositioncommitasis", key: { key: "KEY_Escape" } }, aWindow ); }, popup: false, value: "", searchString: "", }, // Down key should open the popup even if the editor is empty. { description: "DOWN key should open the popup even if the value is empty", completeDefaultIndex: false, execute(aWindow) { synthesizeKey("KEY_ArrowDown", {}, aWindow); }, popup: true, value: "", searchString: "", }, // If popup is open at starting composition, the popup should be reopened // after composition anyway. { description: "compositionstart shouldn't open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "M", clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 1, length: 0 }, key: { key: "M" }, }, aWindow ); }, popup: false, value: "M", searchString: "", }, { description: "modifying composition string shouldn't open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "Mo", clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 2, length: 0 }, key: { key: "o" }, }, aWindow ); }, popup: false, value: "Mo", searchString: "", }, { description: "modifying composition string to empty string shouldn't open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "", clauses: [{ length: 0, attr: 0 }] }, caret: { start: 0, length: 0 }, key: { key: "KEY_Backspace" }, }, aWindow ); }, popup: false, value: "", searchString: "", }, { description: "canceled compositionend should open the popup if it was opened", completeDefaultIndex: false, execute(aWindow) { synthesizeComposition( { type: "compositioncommitasis", key: { key: "KEY_Escape" } }, aWindow ); }, popup: true, value: "", searchString: "", }, // Type normally, and hit escape, the popup should be closed. { description: "ESCAPE should close the popup after typing something", completeDefaultIndex: false, execute(aWindow) { synthesizeKey("M", {}, aWindow); synthesizeKey("o", {}, aWindow); synthesizeKey("KEY_Escape", {}, aWindow); }, popup: false, value: "Mo", searchString: "Mo", }, // Even if the popup is closed, composition which is canceled should open // the popup if the value isn't empty. // XXX This might not be good behavior, but anyway, this is minor issue... { description: "compositionstart shouldn't open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "z", clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 1, length: 0 }, key: { key: "z" }, }, aWindow ); }, popup: false, value: "Moz", searchString: "Mo", }, { description: "modifying composition string shouldn't open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "zi", clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 2, length: 0 }, key: { key: "i" }, }, aWindow ); }, popup: false, value: "Mozi", searchString: "Mo", }, { description: "modifying composition string to empty string shouldn't open the popup", completeDefaultIndex: false, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "", clauses: [{ length: 0, attr: 0 }] }, caret: { start: 0, length: 0 }, key: { key: "KEY_Backspace" }, }, aWindow ); }, popup: false, value: "Mo", searchString: "Mo", }, { description: "canceled compositionend shouldn't open the popup if the popup was closed", completeDefaultIndex: false, execute(aWindow) { synthesizeComposition( { type: "compositioncommitasis", key: { key: "KEY_Escape" } }, aWindow ); }, popup: true, value: "Mo", searchString: "Mo", }, // House keeping... { description: "house keeping for next tests", completeDefaultIndex: false, execute(aWindow) { synthesizeKey("KEY_Backspace", {}, aWindow); synthesizeKey("KEY_Backspace", {}, aWindow); }, popup: false, value: "", searchString: "", }, // Testing for nsIAutoCompleteInput.completeDefaultIndex being true. { description: "compositionstart shouldn't open the popup (completeDefaultIndex is true)", completeDefaultIndex: true, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "M", clauses: [{ length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 1, length: 0 }, key: { key: "M" }, }, aWindow ); }, popup: false, value: "M", searchString: "", }, { description: "modifying composition string shouldn't open the popup (completeDefaultIndex is true)", completeDefaultIndex: true, execute(aWindow) { synthesizeCompositionChange( { composition: { string: "Mo", clauses: [{ length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }], }, caret: { start: 2, length: 0 }, key: { key: "o" }, }, aWindow ); }, popup: false, value: "Mo", searchString: "", }, { description: "compositionend should open the popup (completeDefaultIndex is true)", completeDefaultIndex: true, execute(aWindow) { synthesizeComposition( { type: "compositioncommitasis", key: { key: "KEY_Enter" } }, aWindow ); }, popup: true, value: "Mozilla", searchString: "Mo", }, // House keeping... { description: "house keeping for next tests", completeDefaultIndex: false, execute(aWindow) { synthesizeKey("KEY_Backspace", {}, aWindow); synthesizeKey("KEY_Backspace", {}, aWindow); synthesizeKey("KEY_Backspace", {}, aWindow); synthesizeKey("KEY_Backspace", {}, aWindow); synthesizeKey("KEY_Backspace", {}, aWindow); synthesizeKey("KEY_Backspace", {}, aWindow); }, popup: false, value: "", searchString: "", }, ], };