// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License.txt in the project root for license information. // /// /// /// module WinJSTests { "use strict"; var previousTracingOptions; // Store edit result, use "recordEditSuccess" callback, clear it before. var editSucceeded = false; function clearLastEdit() { editSucceeded = false; } function recordEditSuccess() { editSucceeded = true; } function verifyLastEditSuccessful() { LiveUnit.Assert.isTrue(editSucceeded, "Edit did not succeed"); } function testEditing(signalTestCaseCompleted, synchronous) { var dataSource = Helper.ItemsManager.simpleAsynchronousDataSource(0), handler = Helper.ItemsManager.simpleListNotificationHandler(), listBinding = dataSource.createListBinding(handler); if (synchronous) { dataSource.testDataAdapter.directives.callMethodsSynchronously = true; } else { Helper.ItemsManager.ensureAllAsynchronousRequestsFulfilled(dataSource); } var state0 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; Helper.ItemsManager.setState(dataSource, state0); // Fetch the first item var itemPromise = listBinding.first(); handler.appendItemPromise(itemPromise); itemPromise.then(function (item) { handler.updateItem(item); handler.verifyItem(item, 0); Helper.ItemsManager.setImmediate(function () { handler.verifyExpectedNotifications([ "beginNotifications", "countChanged", "endNotifications" ]); var promises = []; // Fetch the remaining 9 items for (var i = 1; i < 10; i++) { itemPromise = listBinding.next(); handler.appendItemPromise(itemPromise); (function (i) { promises.push(itemPromise.then(function (item2) { handler.updateItem(item2); handler.verifyItem(item2, i); })); })(i); } WinJS.Promise.join(promises).then(function () { Helper.ItemsManager.verifyRequestCount(dataSource, 0); handler.verifyExpectedNotifications([]); handler.verifyState(state0, dataSource); // Now try an insertion at the start of the list var state1 = [10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; clearLastEdit(); var insertPromise = dataSource.insertAtStart(null, Helper.ItemsManager.simpleItem(10)).then(function () { recordEditSuccess(); }); handler.verifyExpectedNotifications([ "beginNotifications", "inserted", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "countChanged", "endNotifications" ]); handler.verifyState(state1, null, true); insertPromise.then(function (item2) { // After an asynchronous insertions, a changed notification will be sent when the edit // completes. handler.verifyExpectedNotifications(synchronous ? [] : [ "beginNotifications", "changed", "endNotifications" ] ); if (item2) { LiveUnit.Assert.isTrue(typeof item.key === "string"); } // Don't have to tolerate null keys now handler.verifyState(state1); // Force a refresh, and continue the test once that has completed dataSource.invalidateAll().then(function () { verifyLastEditSuccessful(); // No notifications should have been sent handler.verifyExpectedNotifications([]); handler.verifyState(state1, dataSource); // Try other kinds of edits. Don't bother to verify success from now on. var state2 = [10, 0, 1, 3, 4, 5, 6, 7, 8, 9, 11], newData = "A new string"; dataSource.beginEdits(); dataSource.remove("2"); dataSource.change("4", newData); dataSource.insertAtEnd(null, Helper.ItemsManager.simpleItem(11)); handler.verifyExpectedNotifications([ "beginNotifications", "removed", "changed", "inserted", ]); dataSource.endEdits(); handler.verifyExpectedNotifications([ "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "endNotifications" ]); // Change item 4 back, so verifyState finds the expected value var changePromise = dataSource.change("4", Helper.ItemsManager.simpleItem(4)); handler.verifyExpectedNotifications([ "beginNotifications", "changed", "endNotifications" ]); handler.verifyState(state2, null, true); changePromise.then(function (item3) { // After an asynchronous insertions, a changed notification will be sent when the edit // completes. handler.verifyExpectedNotifications(synchronous ? [] : [ "beginNotifications", "changed", "endNotifications" ] ); // Don't have to tolerate null keys now handler.verifyState(state2); var state3 = [13, 12, 9, 10, 1, 3, 4, 5, 6, 0, 7, 11, 8]; // Try all the remaining possible singleton edits dataSource.beginEdits(); dataSource.moveAfter("0", "6"); dataSource.moveToStart("9"); dataSource.insertAfter(null, Helper.ItemsManager.simpleItem("12"), "1").then(function (item4) { dataSource.moveBefore("12", "9"); dataSource.insertBefore(null, Helper.ItemsManager.simpleItem("13"), "12"); var movePromise = dataSource.moveToEnd("8"); dataSource.endEdits(); var expectedNotifications = [ "beginNotifications", "moved", "moved", "inserted", "moved", "inserted", "moved", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "indexChanged", "countChanged", "endNotifications" ]; if (!synchronous) { // After an asynchronous insertion, a changed notification will be sent when // the edit completes. expectedNotifications.splice(4, 0, "changed"); } handler.verifyExpectedNotifications(expectedNotifications); handler.verifyState(state3, null, true); movePromise.then(function (item5) { // After an asynchronous insertion, a changed notification will be sent when // the edit completes. handler.verifyExpectedNotifications(synchronous ? [] : [ "beginNotifications", "changed", "endNotifications" ] ); handler.verifyState(state3); // Force a refresh, and continue the test once that has completed dataSource.invalidateAll().then(function () { // Again, no notifications should have been sent handler.verifyExpectedNotifications([]); handler.verifyState(state3, dataSource); signalTestCaseCompleted(); }); }); }); }); }); }); }); }); }); } export class EditingTests { setUp() { previousTracingOptions = VDSLogging.options; VDSLogging.options = { log: function (message) { LiveUnit.Assert.fail(message); }, include: /createListBinding|_retainItem|_releaseItem|release/, handleTracking: true, logVDS: true, stackTraceLimit: 0 // set this to 100 to get good stack traces if you run into a failure. }; VDSLogging.on(); } tearDown() { VDSLogging.off(); VDSLogging.options = previousTracingOptions; } testEditingAsynchronous(signalTestCaseCompleted) { testEditing(signalTestCaseCompleted, false); } testEditingSynchronous(signalTestCaseCompleted) { testEditing(signalTestCaseCompleted, true); } // this test will verify the edit error code(notPermitted) by simulating readOnly data source testEditErrorCodes_NotPermitted(signalTestCaseCompleted) { var dataSource = Helper.ItemsManager.simpleAsynchronousDataSource(100), handler = Helper.ItemsManager.simpleListNotificationHandler(), listBinding = dataSource.createListBinding(handler); dataSource.testDataAdapter.setProperty("readOnly", true); Helper.ItemsManager.ensureAllAsynchronousRequestsFulfilled(dataSource); // Fetch the first item var itemPromise = listBinding.first(); itemPromise.then(itemPromiseHandler); function itemPromiseHandler(item) { Helper.ItemsManager.verifyItemData(item, 0); Helper.ItemsManager.setImmediate(function () { handler.verifyExpectedNotifications([ "beginNotifications", "countChanged", "endNotifications" ]); }); clearLastEdit(); var newData = "NewData0" dataSource.change("0", newData).then(editSuccess, editError); function editSuccess() { recordEditSuccess(); LiveUnit.Assert.fail("Expecting an exception when trying to edit a read only data source.."); } function editError(e) { LiveUnit.Assert.areEqual("notPermitted", e.name, "Expecting error message while trying to edit a read only data source"); // Change the data source to allow edits now. dataSource.testDataAdapter.setProperty("readOnly", false); dataSource.change("0", newData).then( function () { LiveUnit.LoggingCore.logComment("edit is successful after data source is made editable at run time"); signalTestCaseCompleted(); }, function (error) { LiveUnit.Assert.fail("Edit unsuccessful:" + error.name); signalTestCaseCompleted(); } ); } }; }//end of test function // Testing the edit error code: noLongerMeaningful by changing the deleted item testEditErrorCodes_NoLongerMeaningful(signalTestCaseCompleted) { var dataSource = Helper.ItemsManager.simpleAsynchronousDataSource(100), handler = Helper.ItemsManager.simpleListNotificationHandler(), listBinding = dataSource.createListBinding(handler); Helper.ItemsManager.ensureAllAsynchronousRequestsFulfilled(dataSource); // Fetch the first item var itemPromise = listBinding.first(); itemPromise.then(function (item) { Helper.ItemsManager.verifyItemData(item, 0); handler.verifyExpectedNotifications([ "beginNotifications", "countChanged", "endNotifications" ]); clearLastEdit(); var newData = "NewData0" dataSource.remove("0").then(editSuccess, removeError); function removeError(error) { LiveUnit.Assert.fail("Remove operation failed:" + error.name); } function editSuccess() { recordEditSuccess(); dataSource.testDataAdapter.setProperty("notMeaningfulEdit", true); dataSource.change("0", newData).then( function () { LiveUnit.Assert.fail("Exception is expected for noLongerMeaningful edits"); }, function (e) { LiveUnit.Assert.areEqual("noLongerMeaningful", e.name, "Expected exception is thrown from VDS"); signalTestCaseCompleted(); } ); } //end editSuccess }); }//end of test function // this test will verify the edit error code(noResponse) by simulating DS communication Failure testEditErrorCodes_NoResponse(signalTestCaseCompleted) { var dataSource = Helper.ItemsManager.simpleAsynchronousDataSource(100), handler = Helper.ItemsManager.simpleListNotificationHandler(), listBinding = dataSource.createListBinding(handler); Helper.ItemsManager.ensureAllAsynchronousRequestsFulfilled(dataSource); // Fetch the first item var itemPromise = listBinding.first(); itemPromise.then(function (item) { Helper.ItemsManager.verifyItemData(item, 0); handler.verifyExpectedNotifications([ "beginNotifications", "countChanged", "endNotifications" ]); // This will caue the data adapter to return the error when trying to edit the data. dataSource.testDataAdapter.setProperty("communicationFailure", true); clearLastEdit(); var newData = "NewData0" dataSource.change("0", newData).then(editSuccess, editError); function editSuccess() { recordEditSuccess(); LiveUnit.Assert.fail("Expecting an exception when trying to edit a data source while communication failure occurs"); } function editError(e) { LiveUnit.Assert.areEqual("noResponse", e.name, "Expecting error message while trying to edit a data source when communication to data fails"); // Reestablish data source connection dataSource.testDataAdapter.setProperty("communicationFailure", false); dataSource.change("0", newData).then(function () { LiveUnit.LoggingCore.logComment("edit is successful after data source is made editable at run time"); signalTestCaseCompleted(); }, function (error) { LiveUnit.Assert.fail("Edit unsuccessful:" + error.name); }); } }); }//end of test function noresponse xtestCountError_NoResponse(signalTestCaseCompleted) { var dataSource = Helper.ItemsManager.simpleAsynchronousDataSource(100), handler = Helper.ItemsManager.simpleListNotificationHandler(), listBinding = dataSource.createListBinding(handler); // track how mnay times countChanged notification is thrown var countChanged = 0; Helper.ItemsManager.ensureAllAsynchronousRequestsFulfilled(dataSource); dataSource.testDataAdapter.setProperty("count_NoResponse", true); handler.countChanged = function (newCount, oldCount) { countChanged++; LiveUnit.Assert.fail("CountChanged Handler should not be thrown when count is not returned from the data source as DS cannot be communicated"); }; dataSource.getCount().then(countSuccess, countError); function countSuccess(count) { LiveUnit.Assert.fail("countSuccess handler should not be called when error is returned from the data adapter"); } function countError(e) { LiveUnit.Assert.areEqual("noResponse", e.name, "Wrong Error code."); LiveUnit.Assert.areEqual(0, countChanged, "countChanged notification should not be thrown"); setTimeout(function () { signalTestCaseCompleted(); }, 2000); } }//end of test function fetch-noresponse testCountError_Unknown(signalTestCaseCompleted) { var dataSource = Helper.ItemsManager.simpleAsynchronousDataSource(100), handler = Helper.ItemsManager.simpleListNotificationHandler(), listBinding = dataSource.createListBinding(handler); // track how mnay times countChanged notification is thrown var countChanged = 0; Helper.ItemsManager.ensureAllAsynchronousRequestsFulfilled(dataSource); dataSource.testDataAdapter.setProperty("countUnknown", true); handler.countChanged = function (newCount, oldCount) { countChanged++; LiveUnit.Assert.fail("CountChanged Handler should not be thrown when unknown count is returned"); }; dataSource.getCount().then(countSuccess, countError); function countSuccess(count) { LiveUnit.Assert.fail("countSuccess handler should not be called when error is returned from the data adapter"); } function countError(e) { LiveUnit.Assert.areEqual("unknown", e.name, "Wrong Error code."); LiveUnit.Assert.areEqual(0, countChanged, "countChanged notification should not be thrown"); setTimeout(function () { signalTestCaseCompleted(); }, 2000); } }//end of test function fetch-noresponse }; } // Register the object as a test class by passing in the name LiveUnit.registerTestClass("WinJSTests.EditingTests");