diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj
index b8b21c6f6..7712d1427 100644
--- a/CrossPlatformModules.csproj
+++ b/CrossPlatformModules.csproj
@@ -150,6 +150,7 @@
+
time-picker-tests-native.d.ts
@@ -1597,7 +1598,7 @@
False
-
+
\ No newline at end of file
diff --git a/apps/tests/testRunner.ts b/apps/tests/testRunner.ts
index 86a2a72a8..e9dd5c237 100644
--- a/apps/tests/testRunner.ts
+++ b/apps/tests/testRunner.ts
@@ -69,6 +69,7 @@ allTests["DATE-PICKER"] = require("./ui/date-picker/date-picker-tests");
allTests["TIME-PICKER"] = require("./ui/time-picker/time-picker-tests");
allTests["WEB-VIEW"] = require("./ui/web-view/web-view-tests");
allTests["WEAK-EVENTS"] = require("./weak-event-listener-tests");
+allTests["REPEATER"] = require("./ui/repeater/repeater-tests");
if (!isRunningOnEmulator()) {
allTests["LOCATION"] = require("./location-tests");
diff --git a/apps/tests/ui/repeater/repeater-tests.ts b/apps/tests/ui/repeater/repeater-tests.ts
new file mode 100644
index 000000000..75bff24a5
--- /dev/null
+++ b/apps/tests/ui/repeater/repeater-tests.ts
@@ -0,0 +1,375 @@
+import TKUnit = require("../../TKUnit");
+import app = require("application");
+import helper = require("../helper");
+import viewModule = require("ui/core/view");
+import observable = require("data/observable");
+import types = require("utils/types");
+
+//
+// # Repeater
+// Using a Repeater requires the repeater module.
+// ``` JavaScript
+import repeaterModule = require("ui/repeater");
+// ```
+// Other modules which will be used in the code samples in this article:
+// ``` JavaScript
+import observableArray = require("data/observable-array");
+import labelModule = require("ui/label");
+// ```
+
+// ### Binding the Repeater items property to collection in the view-model.
+//```XML
+//
+// {%raw%}{%endraw%}
+//
+//```
+
+// ### Define the Repeater itemTemplate property.
+//```XML
+//
+// {%raw%}
+//
+//
+//
+// {%endraw%}
+//
+//```
+
+//
+
+var ASYNC = 0.2;
+var FEW_ITEMS = [0, 1, 2];
+var MANY_ITEMS = [];
+for (var i = 0; i < 100; i++) {
+ MANY_ITEMS[i] = i;
+}
+
+export function test_set_items_to_array_loads_all_items() {
+ var repeater = new repeaterModule.Repeater();
+
+ function testAction(views: Array) {
+ var indexes = {};
+ //
+ // ### Using Repeater with Array
+ // ``` JavaScript
+ var colors = ["red", "green", "blue"];
+ repeater.items = colors;
+ // ```
+ //
+
+ TKUnit.wait(ASYNC);
+
+ TKUnit.assert(getChildAtText(repeater, 0) === "red", "Item not created for index 0");
+ TKUnit.assert(getChildAtText(repeater, 1) === "green", "Item not created for index 1");
+ TKUnit.assert(getChildAtText(repeater, 2) === "blue", "Item not created for index 2");
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_set_items_to_array_creates_native_views() {
+ var repeater = new repeaterModule.Repeater();
+
+ function testAction(views: Array) {
+ repeater.items = FEW_ITEMS;
+
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), FEW_ITEMS.length, "Native views count.");
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_refresh_after_adding_items_to_array_loads_new_items() {
+
+ var repeater = new repeaterModule.Repeater();
+
+ function testAction(views: Array) {
+ var colors = ["red", "green", "blue"];
+ repeater.items = colors;
+
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), colors.length, "Native views count.");
+ //
+ // > Note, that changing the array after the repeater is shown will not update the UI.
+ // You can force-update the UI using the refresh() method.
+ // ``` JavaScript
+ colors.push("yellow");
+ //// Manually trigger the update so that the new color is shown.
+ repeater.refresh();
+ // ```
+ //
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), colors.length, "Native views count.");
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_refresh_reloads_all_items() {
+ var repeater = new repeaterModule.Repeater();
+
+ function testAction(views: Array) {
+ var indexes = {};
+ var testStarted = false;
+
+ var itemsToBind = >FEW_ITEMS;
+
+ repeater.items = itemsToBind;
+
+ TKUnit.wait(ASYNC);
+ testStarted = true;
+
+ itemsToBind[0] = "red";
+ itemsToBind[1] = "green";
+ itemsToBind[2] = "blue";
+
+ repeater.refresh();
+
+ TKUnit.wait(ASYNC);
+
+ TKUnit.assert(getChildAtText(repeater, 0) === "red", "Item not created for index 0");
+ TKUnit.assert(getChildAtText(repeater, 1) === "green", "Item not created for index 1");
+ TKUnit.assert(getChildAtText(repeater, 2) === "blue", "Item not created for index 2");
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_set_itmes_to_null_clears_native_items() {
+ var repeater = new repeaterModule.Repeater();
+
+ function testAction(views: Array) {
+ repeater.items = FEW_ITEMS;
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), FEW_ITEMS.length, "Native views count.");
+
+ repeater.items = null;
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), 0, "Native views count.");
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_set_itmes_to_undefiend_clears_native_items() {
+ var repeater = new repeaterModule.Repeater();
+
+ function testAction(views: Array) {
+ repeater.items = FEW_ITEMS;
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), FEW_ITEMS.length, "Native views count.");
+
+ repeater.items = undefined;
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), 0, "Native views count.");
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_set_itmes_to_different_source_loads_new_items() {
+ var repeater = new repeaterModule.Repeater();
+
+ function testAction(views: Array) {
+ repeater.items = [1, 2, 3];
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), 3, "Native views count.");
+
+ repeater.items = ["a", "b", "c", "d"];
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), 4, "Native views count.");
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_set_items_to_observable_array_loads_all_items() {
+ var repeater = new repeaterModule.Repeater();
+
+ function testAction(views: Array) {
+ var indexes = {};
+ //
+ // ### Using Repeater with ObservableArray
+ // ``` JavaScript
+ var colors = new observableArray.ObservableArray(["red", "green", "blue"]);
+ repeater.items = colors;
+ // ```
+ //
+
+ TKUnit.assert(getChildAtText(repeater, 0) === "red", "Item not created for index 0");
+ TKUnit.assert(getChildAtText(repeater, 1) === "green", "Item not created for index 1");
+ TKUnit.assert(getChildAtText(repeater, 2) === "blue", "Item not created for index 2");
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_add_to_observable_array_refreshes_the_listview() {
+ var repeater = new repeaterModule.Repeater();
+
+ function testAction(views: Array) {
+ var colors = new observableArray.ObservableArray(["red", "green", "blue"]);
+ repeater.items = colors;
+
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), 3, "getChildrenCount");
+
+ //
+ // > When using ObservableArray the repeater will be automatically updated when items are added or removed form the array.
+ // ``` JavaScript
+ colors.push("yellow");
+ //// The Repeater will be updated automatically.
+ // ```
+ //
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), 4, "getChildrenCount");
+
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_remove_from_observable_array_refreshes_the_listview() {
+ var repeater = new repeaterModule.Repeater();
+ var data = new observableArray.ObservableArray([1, 2, 3]);
+
+ function testAction(views: Array) {
+ repeater.items = data;
+
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), 3, "getChildrenCount");
+
+ data.pop();
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), 2, "getChildrenCount");
+
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_splice_observable_array_refreshes_the_listview() {
+ var repeater = new repeaterModule.Repeater();
+ var data = new observableArray.ObservableArray(["a", "b", "c"]);
+
+ function testAction(views: Array) {
+ repeater.items = data;
+
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), 3, "getChildrenCount");
+
+ // Remove the first 2 elements and add
+ data.splice(0, 2, "d", "e", "f");
+ TKUnit.wait(ASYNC);
+ TKUnit.assertEqual(getChildrenCount(repeater), 4, "getChildrenCount");
+
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_usingAppLevelConvertersInListViewItems() {
+ var repeater = new repeaterModule.Repeater();
+
+ var dateConverter = function (value, format) {
+ var result = format;
+ var day = value.getDate();
+ result = result.replace("DD", month < 10 ? "0" + day : day);
+ var month = value.getMonth() + 1;
+ result = result.replace("MM", month < 10 ? "0" + month : month);
+ result = result.replace("YYYY", value.getFullYear());
+ return result;
+ };
+
+ app.resources["dateConverter"] = dateConverter;
+
+ var data = new observableArray.ObservableArray();
+
+ data.push({ date: new Date() });
+
+ function testAction(views: Array) {
+ repeater.itemTemplate = "";
+ repeater.items = data;
+
+ TKUnit.wait(ASYNC);
+
+ TKUnit.assertEqual(getChildAtText(repeater, 0), dateConverter(new Date(), "DD.MM.YYYY"), "native element");
+ };
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_BindingListViewToASimpleArray() {
+ var repeater = new repeaterModule.Repeater();
+
+ function testAction(views: Array) {
+ repeater.itemTemplate = "";
+ repeater.items = [1, 2, 3];
+
+ TKUnit.wait(ASYNC);
+
+ TKUnit.assertEqual(getChildAtText(repeater, 0), "1", "first element text");
+ TKUnit.assertEqual(getChildAtText(repeater, 1), "2", "second element text");
+ TKUnit.assertEqual(getChildAtText(repeater, 2), "3", "third element text");
+ }
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+
+export function test_BindingListViewToASimpleArrayWithExpression() {
+ var repeater = new repeaterModule.Repeater();
+
+ function testAction(views: Array) {
+ repeater.itemTemplate = "";
+ repeater.items = [1, 2, 3];
+
+ TKUnit.wait(ASYNC);
+
+ TKUnit.assertEqual(getChildAtText(repeater, 0), "1 some static text", "first element text");
+ TKUnit.assertEqual(getChildAtText(repeater, 1), "2 some static text", "second element text");
+ TKUnit.assertEqual(getChildAtText(repeater, 2), "3 some static text", "third element text");
+ }
+
+ helper.buildUIAndRunTest(repeater, testAction);
+}
+/*
+export function test_no_memory_leak_when_items_is_regular_array() {
+ var createFunc = function (): repeaterModule.Repeater {
+ var repeater = new repeaterModule.Repeater();
+ repeater.items = FEW_ITEMS;
+ return repeater;
+ };
+
+ helper.buildUIWithWeakRefAndInteract(createFunc,(list) => {
+ TKUnit.assert(list.isLoaded, "Repeater should be loaded here");
+ });
+}
+
+export function test_no_memory_leak_when_items_is_observable_array() {
+ // Keep the reference to the observable array to test the weakEventListener
+ var colors = new observableArray.ObservableArray(["red", "green", "blue"]);
+
+ var createFunc = function (): repeaterModule.Repeater {
+ var repeater = new repeaterModule.Repeater();
+ repeater.items = colors;
+ return repeater;
+ };
+
+ helper.buildUIWithWeakRefAndInteract(createFunc,(list) => {
+ TKUnit.assert(list.isLoaded, "Repeater should be loaded here");
+ });
+}
+*/
+function getChildrenCount(repeater: repeaterModule.Repeater): number {
+ return repeater.itemsLayout.getChildrenCount();
+}
+
+function getChildAt(repeater: repeaterModule.Repeater, index: number): viewModule.View {
+ return repeater.itemsLayout.getChildAt(index);
+}
+
+function getChildAtText(repeater: repeaterModule.Repeater, index: number): string {
+ return (getChildAt(repeater, index)).text + "";
+}
+