From 075e70e336c045032acaf093d2b5b8c059aab62a Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Mon, 7 Aug 2017 17:24:12 +0300 Subject: [PATCH] cache page on forward navigation (#4652) * cache page on forward navigation Still some failing navigation tests * Current page is kept alive when navigating forward Refactoring code and removing all hacks and flags Remove one module circular reference * Disable Page recycling because when there is transition between pages the nativeView stays animated (e.g. when transition is Fade the hidden page nativeView stays with Alpha 0) Disable recycling if there is native anitmation * Fix failing tests on ios & android API17 Fix wrong urls in http tests Made some timer tests async * Animations are not stored in BackstackEntry instead of Fragment because fragments could die (activity die) and recreated and we lose animations. * Fix android crash when activity is recreated. Refactoring transitionListener. --- apps/.vscode/launch.json | 1 + tests/app/TKUnit.ts | 165 ++-- tests/app/app/mainPage.ts | 7 +- tests/app/http/http-tests.ts | 6 +- tests/app/navigation/custom-transition.d.ts | 6 + tests/app/navigation/navigation-tests.ts | 179 ++--- tests/app/navigation/transition-tests.ts | 49 +- tests/app/testRunner.ts | 55 +- tests/app/timer/timer-tests.ts | 42 +- tests/app/ui-test.ts | 14 +- tests/app/ui/animation/animation-tests.ts | 42 +- tests/app/ui/helper.ts | 9 +- tests/app/ui/lifecycle/lifecycle-tests.ts | 4 +- tests/app/ui/list-view/list-view-tests.ts | 12 +- tests/app/ui/page/page-tests-common.ts | 314 ++++---- tests/app/ui/page/page-tests.android.ts | 126 +-- tests/package.json | 2 +- .../ui/core/view-base/view-base.ts | 6 +- .../ui/frame/fragment.transitions.android.ts | 749 ++++++++++++++++++ .../ui/frame/fragment.transitions.d.ts | 36 + .../ui/frame/fragment.transitions.ios.ts | 88 ++ tns-core-modules/ui/frame/frame-common.ts | 55 +- tns-core-modules/ui/frame/frame.android.ts | 404 +++------- tns-core-modules/ui/frame/frame.d.ts | 19 +- tns-core-modules/ui/frame/frame.ios.ts | 2 +- tns-core-modules/ui/page/page-common.ts | 5 +- tns-core-modules/ui/page/page.android.ts | 14 - .../ui/text-field/text-field.ios.ts | 2 - .../ui/transition/flip-transition.d.ts | 9 + .../ui/transition/transition.android.ts | 711 +---------------- .../ui/transition/transition.d.ts | 44 +- .../ui/transition/transition.ios.ts | 110 +-- .../android/android17.d.ts | 48 ++ 33 files changed, 1565 insertions(+), 1770 deletions(-) create mode 100644 tests/app/navigation/custom-transition.d.ts create mode 100644 tns-core-modules/ui/frame/fragment.transitions.android.ts create mode 100644 tns-core-modules/ui/frame/fragment.transitions.d.ts create mode 100644 tns-core-modules/ui/frame/fragment.transitions.ios.ts create mode 100644 tns-core-modules/ui/transition/flip-transition.d.ts diff --git a/apps/.vscode/launch.json b/apps/.vscode/launch.json index a04635572..5faa59444 100644 --- a/apps/.vscode/launch.json +++ b/apps/.vscode/launch.json @@ -30,6 +30,7 @@ "appRoot": "${workspaceRoot}", "sourceMaps": true, "watch": true, + "stopOnEntry": true, "tnsArgs": [ "--sync-all-files" ] diff --git a/tests/app/TKUnit.ts b/tests/app/TKUnit.ts index 46057b5f9..b8787c275 100644 --- a/tests/app/TKUnit.ts +++ b/tests/app/TKUnit.ts @@ -15,6 +15,7 @@ import * as timer from "tns-core-modules/timer"; import * as trace from "tns-core-modules/trace"; import * as types from "tns-core-modules/utils/types"; import * as platform from "tns-core-modules/platform"; +import { topmost } from "tns-core-modules/ui/frame"; import * as utils from "tns-core-modules/utils/utils"; @@ -42,12 +43,12 @@ export function time(): number { } } -export var write = function write(message: string, type?: number) { +export function write(message: string, type?: number) { //console.log(message); trace.write(message, trace.categories.Test, type); } -var runTest = function (testInfo: TestInfoEntry) { +function runTest(testInfo: TestInfoEntry) { let start = time(); let duration; try { @@ -74,7 +75,7 @@ var runTest = function (testInfo: TestInfoEntry) { testInfo.errorMessage = e.message; } } -}; +} export interface TestFailure { moduleName: string; @@ -89,55 +90,45 @@ export interface TestModuleRunResult { failed: Array; } -var testsQueue: Array; -var defaultTimeout = 5000; +let testsQueue: Array; +const defaultTimeout = 5000; -// testInfo : {testFunc: func, testName: string, isTest: boolean, isPassed: boolean, errorMessage: string} function runAsync(testInfo: TestInfoEntry, recursiveIndex: number, testTimeout?: number) { - var error; - var isDone = false; - var handle; - var testStartTime = time(); + let error; + let isDone = false; + let handle; + const testStartTime = time(); //write("--- [" + testInfo.testName + "] Started at: " + testStartTime, trace.messageType.info); - var doneCallback = function (e: Error) { + const doneCallback = (e: Error) => { if (e) { error = e; - } - else { + } else { isDone = true; } } - var testTimeout = testInfo.testTimeout; - if (!testTimeout) { - testTimeout = defaultTimeout; - } + + const timeout = testTimeout || testInfo.testTimeout || defaultTimeout; let duration; - var checkFinished = function () { + const checkFinished = () => { duration = time() - testStartTime; testInfo.duration = duration; if (isDone) { - write("--- [" + testInfo.testName + "] OK, duration: " + duration, trace.messageType.info); - //write("--- [" + testInfo.testName + "] took: " + (new Date().getTime() - testStartTime), trace.messageType.info); + write(`--- [${testInfo.testName}] OK, duration: ${duration}`, trace.messageType.info); testInfo.isPassed = true; runTests(testsQueue, recursiveIndex + 1); - } - else if (error) { - write("--- [" + testInfo.testName + "] FAILED: " + error.message + ", duration: " + duration, trace.messageType.error); - //write("--- [" + testInfo.testName + "] took: " + (new Date().getTime() - testStartTime), trace.messageType.info); + } else if (error) { + write(`--- ["${testInfo.testName}"] FAILED: ${error.message}, duration: ${duration}`, trace.messageType.error); testInfo.errorMessage = error.message; runTests(testsQueue, recursiveIndex + 1); - } - else { - var testEndTime = time(); - if (testEndTime - testStartTime > testTimeout) { - write("--- [" + testInfo.testName + "] TIMEOUT, duration: " + duration, trace.messageType.error); - //write("--- [" + testInfo.testName + "] took: " + (testEndTime - testStartTime), trace.messageType.info); + } else { + const testEndTime = time(); + if (testEndTime - testStartTime > timeout) { + write(`--- ["${testInfo.testName}"] TIMEOUT, duration: ${duration}`, trace.messageType.error); testInfo.errorMessage = "Test timeout."; runTests(testsQueue, recursiveIndex + 1); - } - else { - setTimeout(checkFinished, 10); + } else { + setTimeout(checkFinished, 0); } } } @@ -146,7 +137,7 @@ function runAsync(testInfo: TestInfoEntry, recursiveIndex: number, testTimeout?: if (testInfo.instance) { testInfo.testFunc.apply(testInfo.instance, [doneCallback]); } else { - var func: any = testInfo.testFunc; + const func: any = testInfo.testFunc; func(doneCallback); } } catch (e) { @@ -156,17 +147,15 @@ function runAsync(testInfo: TestInfoEntry, recursiveIndex: number, testTimeout?: setTimeout(checkFinished, 0); } -// tests : Array<{testFunc: func, testName: string, isTest: boolean, isPassed: boolean, errorMessage: string}> -export var runTests = function (tests: Array, recursiveIndex) { +export function runTests(tests: Array, recursiveIndex) { testsQueue = tests; + + for (let i = recursiveIndex; i < testsQueue.length; i++) { + const testEntry = testsQueue[i]; - var i; - for (i = recursiveIndex; i < testsQueue.length; i++) { - var testEntry = testsQueue[i]; if (testEntry.testFunc.length > 0) { return runAsync(testEntry, i); - } - else { + } else { runTest(testEntry); } } @@ -176,35 +165,31 @@ export function assert(test: any, message?: string) { if (!test) { throw new Error(message); } -}; +} export function assertTrue(test: boolean, message?: string) { if (test !== true) { throw new Error(message); } -}; +} export function assertFalse(test: boolean, message?: string) { if (test !== false) { throw new Error(message); } -}; +} export function assertNotEqual(actual: any, expected: any, message?: string) { - - var equals = false; + let equals = false; if (types.isUndefined(actual) && types.isUndefined(expected)) { equals = true; - } - else if (!types.isNullOrUndefined(actual) && !types.isNullOrUndefined(expected)) { + } else if (!types.isNullOrUndefined(actual) && !types.isNullOrUndefined(expected)) { if (types.isFunction(actual.equals)) { - // Use the equals method if (actual.equals(expected)) { equals = true; } - } - else { + } else { equals = actual === expected; } } @@ -228,7 +213,7 @@ export function assertEqual(actual else if (actual !== expected) { throw new Error(`${message} Actual: <${actual}>(${typeof (actual)}). Expected: <${expected}>(${typeof (expected)})`); } -}; +} /** * Assert two json like objects are deep equal. @@ -310,13 +295,13 @@ export function assertNull(actual: any, message?: string) { if (actual !== null && actual !== undefined) { throw new Error(message + " Actual: " + actual + " is not null/undefined"); } -}; +} export function assertNotNull(actual: any, message?: string) { if (actual === null || actual === undefined) { throw new Error(message + " Actual: " + actual + " is null/undefined"); } -}; +} export function areClose(actual: number, expected: number, delta: number): boolean { if (isNaN(actual) || Math.abs(actual - expected) > delta) { @@ -325,18 +310,19 @@ export function areClose(actual: number, expected: number, delta: number): boole return true; } + export function assertAreClose(actual: number, expected: number, delta: number, message?: string) { if (!areClose(actual, expected, delta)) { throw new Error(message + " Numbers are not close enough. Actual: " + actual + " Expected: " + expected + " Delta: " + delta); } -}; +} export function arrayAssert(actual: Array, expected: Array, message?: string) { if (actual.length !== expected.length) { throw new Error(message + " Actual array length: " + actual.length + " Expected array length: " + expected.length); } - var i; - for (i = 0; i < actual.length; i++) { + + for (let i = 0; i < actual.length; i++) { if (actual[i] !== expected[i]) { throw new Error(message + " Actual element at " + i + " is: " + actual[i] + " Expected element is: " + expected[i]); } @@ -344,7 +330,7 @@ export function arrayAssert(actual: Array, expected: Array, message?: } export function assertThrows(testFunc: () => void, assertMessage?: string, expectedMessage?: string) { - var actualError: Error; + let actualError: Error; try { testFunc(); } catch (e) { @@ -360,29 +346,30 @@ export function assertThrows(testFunc: () => void, assertMessage?: string, expec } } -export var wait = function (seconds: number) { - waitUntilReady(function () { - return false; - }, seconds, false); -}; +export function wait(seconds: number): void { + waitUntilReady(() => false, seconds, false); +} -export var waitUntilReady = function (isReady: () => boolean, timeoutSec: number = 3, shouldThrow: boolean = true) { +export function waitUntilReady(isReady: () => boolean, timeoutSec: number = 300, shouldThrow: boolean = true) { if (!isReady) { return; } if (Application.ios) { - const waitTime = 20 / 1000; - var totalWaitTime = 0; + // const waitTime = 1 / 10000; + const timeoutMs = timeoutSec * 1000; + let totalWaitTime = 0; while (true) { - utils.ios.getter(NSRunLoop, NSRunLoop.currentRunLoop).runUntilDate(NSDate.dateWithTimeIntervalSinceNow(waitTime)); - + // const nsDate = NSDate.dateWithTimeIntervalSinceNow(waitTime); + const begin = time(); + const currentRunLoop = utils.ios.getter(NSRunLoop, NSRunLoop.currentRunLoop); + currentRunLoop.limitDateForMode(currentRunLoop.currentMode); if (isReady()) { break; } - totalWaitTime += waitTime; - if (timeoutSec && totalWaitTime >= timeoutSec) { + totalWaitTime += (time() - begin); + if (totalWaitTime >= timeoutMs) { if (shouldThrow) { throw new Error("waitUntilReady Timeout."); } else { @@ -393,26 +380,23 @@ export var waitUntilReady = function (isReady: () => boolean, timeoutSec: number } else if (Application.android) { doModalAndroid(isReady, timeoutSec, shouldThrow); } -}; +} // Setup for the Android modal loop implementation // TODO: If these platform-specific implementations continue to grow, think of per-platform separation (TKUnit.android) -var nextMethod; -var targetField; -var prepared; +let nextMethod; +let targetField; +let prepared; -var prepareModal = function () { +function prepareModal() { if (prepared) { return; } - var clsMsgQueue = java.lang.Class.forName("android.os.MessageQueue"); - var clsMsg = java.lang.Class.forName("android.os.Message"); - - nextMethod; - var methods = clsMsgQueue.getDeclaredMethods(); - var i; - for (i = 0; i < methods.length; i++) { + const clsMsgQueue = java.lang.Class.forName("android.os.MessageQueue"); + const clsMsg = java.lang.Class.forName("android.os.Message"); + const methods = clsMsgQueue.getDeclaredMethods(); + for (let i = 0; i < methods.length; i++) { if (methods[i].getName() === "next") { nextMethod = methods[i]; nextMethod.setAccessible(true); @@ -420,9 +404,8 @@ var prepareModal = function () { } } - targetField; - var fields = clsMsg.getDeclaredFields(); - for (i = 0; i < fields.length; i++) { + const fields = clsMsg.getDeclaredFields(); + for (let i = 0; i < fields.length; i++) { if (fields[i].getName() === "target") { targetField = fields[i]; targetField.setAccessible(true); @@ -433,28 +416,28 @@ var prepareModal = function () { prepared = true; } -var doModalAndroid = function (quitLoop: () => boolean, timeoutSec: number, shouldThrow: boolean = true) { +function doModalAndroid(quitLoop: () => boolean, timeoutSec: number, shouldThrow: boolean = true) { if (!quitLoop) { return; } prepareModal(); - var queue = android.os.Looper.myQueue(); + const queue = android.os.Looper.myQueue(); let quit = false; let timeout = false; - timer.setTimeout(function () { + timer.setTimeout(() => { quit = true; timeout = true; }, timeoutSec * 1000); - var msg; + let msg; while (!quit) { msg = nextMethod.invoke(queue, null); if (msg) { - var target = targetField.get(msg); + const target = targetField.get(msg); if (!target) { quit = true; } else { @@ -474,4 +457,4 @@ var doModalAndroid = function (quitLoop: () => boolean, timeoutSec: number, shou quit = true; } } -}; +} \ No newline at end of file diff --git a/tests/app/app/mainPage.ts b/tests/app/app/mainPage.ts index 7b21f6028..049b79ac1 100644 --- a/tests/app/app/mainPage.ts +++ b/tests/app/app/mainPage.ts @@ -9,14 +9,14 @@ trace.enable(); trace.addCategories(trace.categories.Test + "," + trace.categories.Error); // When debugging -//trace.setCategories(trace.categories.concat( +// trace.setCategories(trace.categories.concat( // trace.categories.Test, // trace.categories.Navigation, // trace.categories.Transition, // trace.categories.NativeLifecycle, // trace.categories.ViewHierarchy, // trace.categories.VisualTreeEvents -//)); +// )); let page = new Page(); page.id = "mainPage"; @@ -57,6 +57,7 @@ function onNavigatedTo(args) { runTests(); } + export function createPage() { return page; -} +} \ No newline at end of file diff --git a/tests/app/http/http-tests.ts b/tests/app/http/http-tests.ts index 98e4a71a2..891feea5e 100644 --- a/tests/app/http/http-tests.ts +++ b/tests/app/http/http-tests.ts @@ -223,7 +223,7 @@ export var test_getFile = function (done) { var result; // >> http-get-urlfile - http.getFile("https://raw.githubusercontent.com/NativeScript/NativeScript/master/apps/tests/logo.png").then(function (r) { + http.getFile("https://raw.githubusercontent.com/NativeScript/NativeScript/master/tests/app/logo.png").then(function (r) { //// Argument (r) is File! // >> (hide) result = r; @@ -273,7 +273,7 @@ export var test_getContentAsFile = function (done) { export var test_getFile_fail = function (done) { var result; - http.getImage({ url: "hgfttp://raw.githubusercontent.com/NativeScript/NativeScript/master/apps/tests/logo.png", method: "GET", timeout: 2000 }).catch(function (e) { + http.getImage({ url: "hgfttp://raw.githubusercontent.com/NativeScript/NativeScript/master/tests/app/logo.png", method: "GET", timeout: 2000 }).catch(function (e) { result = e; try { TKUnit.assert(result instanceof Error, "Result from getFile().catch() should be Error! Current type is " + typeof result); @@ -457,7 +457,7 @@ export var test_request_responseContentToImageShouldReturnCorrectImage = functio export var test_request_responseContentToFileFromUrlShouldReturnCorrectFile = function (done) { var result; - http.request({ url: "https://raw.githubusercontent.com/NativeScript/NativeScript/master/apps/app/ui-tests-app/image-view/red.png", method: "GET" }).then(function (response) { + http.request({ url: "https://raw.githubusercontent.com/NativeScript/NativeScript/master/tests/app/logo.png", method: "GET" }).then(function (response) { result = response.content.toFile(); try { TKUnit.assert(result instanceof fs.File, "Result from toFile() should be valid File object!"); diff --git a/tests/app/navigation/custom-transition.d.ts b/tests/app/navigation/custom-transition.d.ts new file mode 100644 index 000000000..4b14ce81a --- /dev/null +++ b/tests/app/navigation/custom-transition.d.ts @@ -0,0 +1,6 @@ +import { Transition } from "tns-core-modules/ui/transition"; + +export class CustomTransition extends Transition { + constructor(); + constructor(duration: number, curve: any); +} \ No newline at end of file diff --git a/tests/app/navigation/navigation-tests.ts b/tests/app/navigation/navigation-tests.ts index 05cfaf93c..98bb264a5 100644 --- a/tests/app/navigation/navigation-tests.ts +++ b/tests/app/navigation/navigation-tests.ts @@ -1,7 +1,7 @@ import * as TKUnit from "../TKUnit"; -import {Page, NavigatedData} from "tns-core-modules/ui/page"; -import {topmost as topmostFrame, NavigationTransition} from "tns-core-modules/ui/frame"; -import {Color} from "tns-core-modules/color"; +import { Page, NavigatedData } from "tns-core-modules/ui/page"; +import { topmost as topmostFrame, NavigationTransition } from "tns-core-modules/ui/frame"; +import { Color } from "tns-core-modules/color"; import * as helper from "../ui/helper"; // Creates a random colorful page full of meaningless stuff. @@ -14,14 +14,6 @@ let pageFactory = function (): Page { return page; }; -function androidGC() { - let topmost = topmostFrame(); - if (topmost.android) { - gc(); - java.lang.System.gc(); - } -} - function attachEventListeners(page: Page, events: Array) { let argsToString = (args: NavigatedData) => { return `${(args.object).id} ${args.eventName} ${(args.isBackNavigation ? "back" : "forward")}`; @@ -36,36 +28,32 @@ function attachEventListeners(page: Page, events: Array) { function _test_backstackVisible(transition?: NavigationTransition) { let topmost = topmostFrame(); let mainTestPage = topmost.currentPage; - helper.navigateWithEntry({ create: pageFactory, transition: transition, animated: true }); - + topmost.navigate({ create: pageFactory, transition: transition, animated: !!transition }); + TKUnit.waitUntilReady(() => topmost.navigationQueueIsEmpty()); // page1 should not be added to the backstack let page0 = topmost.currentPage; - helper.navigateWithEntry({ create: pageFactory, backstackVisible: false, transition: transition, animated: true }); - - helper.navigateWithEntry({ create: pageFactory, transition: transition, animated: true }); - - helper.goBack(); - + topmost.navigate({ create: pageFactory, backstackVisible: false, transition: transition, animated: !!transition }); + topmost.navigate({ create: pageFactory, transition: transition, animated: !!transition }); + topmost.goBack(); + TKUnit.waitUntilReady(() => topmost.navigationQueueIsEmpty()); // From page2 we have to go directly to page0, skipping page1. - TKUnit.assert(topmost.currentPage === page0, "Page 1 should be skipped when going back."); + TKUnit.assertEqual(topmost.currentPage, page0, "Page 1 should be skipped when going back."); helper.goBack(); TKUnit.assertEqual(topmost.currentPage, mainTestPage, "We should be on the main test page at the end of the test."); } export function test_backstackVisible() { - androidGC(); _test_backstackVisible(); } export function test_backstackVisible_WithTransition() { - androidGC(); - _test_backstackVisible({ name: "fade" }); + _test_backstackVisible({ name: "fade", duration: 10 }); } function _test_backToEntry(transition?: NavigationTransition) { - let topmost = topmostFrame(); - let page = (tag) => () => { + const topmost = topmostFrame(); + const page = (tag) => () => { const p = new Page(); p.actionBarHidden = true; p.id = `NavTestPage${id++}`; @@ -73,114 +61,90 @@ function _test_backToEntry(transition?: NavigationTransition) { return p; }; - let mainTestPage = topmost.currentPage; - let waitFunc = tag => TKUnit.waitUntilReady(() => topmost.currentPage["tag"] === tag, 1); - let navigate = tag => { - topmost.navigate({ create: page(tag), transition: transition, animated: true }); - waitFunc(tag); + const mainTestPage = topmost.currentPage; - }; - - let back = pages => { - topmost.goBack(topmost.backStack[topmost.backStack.length - pages]); - }; - - let currentPageMustBe = tag => { - waitFunc(tag); // TODO: Add a timeout... - TKUnit.assert(topmost.currentPage["tag"] === tag, "Expected current page to be " + tag + " it was " + topmost.currentPage["tag"] + " instead."); - }; + const navigate = tag => topmost.navigate({ create: page(tag), transition: transition, animated: !!transition }); + const back = pages => topmost.goBack(topmost.backStack[topmost.backStack.length - pages]); + const currentPageMustBe = tag => TKUnit.assertEqual(topmost.currentPage["tag"], tag, "Expected current page to be " + tag + " it was " + topmost.currentPage["tag"] + " instead."); navigate("page1"); navigate("page2"); navigate("page3"); navigate("page4"); + TKUnit.waitUntilReady(() => topmost.navigationQueueIsEmpty()); currentPageMustBe("page4"); + back(2); + TKUnit.waitUntilReady(() => topmost.navigationQueueIsEmpty()); + currentPageMustBe("page2"); + back(1); + TKUnit.waitUntilReady(() => topmost.navigationQueueIsEmpty()); + currentPageMustBe("page1"); + navigate("page1.1"); navigate("page1.2"); + TKUnit.waitUntilReady(() => topmost.navigationQueueIsEmpty()); + currentPageMustBe("page1.2"); + back(1); + TKUnit.waitUntilReady(() => topmost.navigationQueueIsEmpty()); + currentPageMustBe("page1.1"); + back(1); + TKUnit.waitUntilReady(() => topmost.navigationQueueIsEmpty()); + currentPageMustBe("page1"); - let page1 = topmost.currentPage; + back(1); - helper.waitUntilNavigatedFrom(page1); + TKUnit.waitUntilReady(() => topmost.navigationQueueIsEmpty()); + TKUnit.assertEqual(topmost.currentPage, mainTestPage, "We should be on the main test page at the end of the test."); } export function test_backToEntry() { - androidGC(); _test_backToEntry(); } export function test_backToEntry_WithTransition() { - androidGC(); - _test_backToEntry({ name: "flip" }); + _test_backToEntry({ name: "fade", duration: 10 }); } function _test_ClearHistory(transition?: NavigationTransition) { let topmost = topmostFrame(); - let mainTestPage = topmost.currentPage; - let mainPageFactory = function (): Page { - return mainTestPage; - }; - - helper.navigateWithEntry({ create: pageFactory, clearHistory: true, transition: transition, animated: true }); + let x = 0; + helper.navigateWithEntry({ create: pageFactory, clearHistory: true, transition: transition, animated: !!transition }); TKUnit.assertEqual(topmost.backStack.length, 0, "1.topmost.backStack.length"); TKUnit.assertEqual(topmost.canGoBack(), false, "1.topmost.canGoBack()."); - helper.navigateWithEntry({ create: pageFactory, transition: transition, animated: true }); + helper.navigateWithEntry({ create: pageFactory, transition: transition, animated: !!transition }); TKUnit.assertEqual(topmost.backStack.length, 1, "2.topmost.backStack.length"); TKUnit.assertEqual(topmost.canGoBack(), true, "2.topmost.canGoBack()."); - helper.navigateWithEntry({ create: pageFactory, transition: transition, animated: true }); + helper.navigateWithEntry({ create: pageFactory, transition: transition, animated: !!transition }); TKUnit.assertEqual(topmost.backStack.length, 2, "3.topmost.backStack.length"); TKUnit.assertEqual(topmost.canGoBack(), true, "3.topmost.canGoBack()."); - helper.navigateWithEntry({ create: pageFactory, clearHistory: true, transition: transition, animated: true }); + helper.navigateWithEntry({ create: pageFactory, clearHistory: true, transition: transition, animated: !!transition }); TKUnit.assertEqual(topmost.backStack.length, 0, "4.topmost.backStack.length"); TKUnit.assertEqual(topmost.canGoBack(), false, "4.topmost.canGoBack()."); - - helper.navigateWithEntry({ create: mainPageFactory, clearHistory: true, animated: false }); - TKUnit.assertEqual(topmost.backStack.length, 0, "5.topmost.backStack.length"); - TKUnit.assertEqual(topmost.canGoBack(), false, "5.topmost.canGoBack()."); - - TKUnit.assertEqual(topmost.currentPage, mainTestPage, "We should be on the main test page at the end of the test."); - TKUnit.assertEqual(topmost.backStack.length, 0, "Back stack should be empty at the end of the test."); } -// Clearing the history messes up the tests app. export function test_ClearHistory() { - androidGC(); _test_ClearHistory(); } export function test_ClearHistory_WithTransition() { - androidGC(); - _test_ClearHistory({ name: "fade" }); -} - -export function test_ClearHistory_WithTransition_WithCachePagesOnNavigate() { - androidGC(); - let topmost = topmostFrame(); - if (!topmost.android) { - return; - } - - let originalCachePagesOnNavigate = topmost.android.cachePagesOnNavigate; - topmostFrame().android.cachePagesOnNavigate = true; - _test_ClearHistory({ name: "fade" }); - topmostFrame().android.cachePagesOnNavigate = originalCachePagesOnNavigate; + _test_ClearHistory({ name: "fade", duration: 10 }); } // Test case for https://github.com/NativeScript/NativeScript/issues/1948 export function test_ClearHistoryWithTransitionDoesNotBreakNavigation() { - androidGC(); let topmost = topmostFrame(); let mainTestPage = topmost.currentPage; let mainPageFactory = function (): Page { @@ -191,14 +155,14 @@ export function test_ClearHistoryWithTransitionDoesNotBreakNavigation() { helper.navigateWithEntry({ create: pageFactory, clearHistory: false, animated: true }); // Go back to main-page with clearHistory - topmost.transition = { name: "fade" }; + topmost.transition = { name: "fade", duration: 10 }; helper.navigateWithEntry({ create: mainPageFactory, clearHistory: true, animated: true }); // Go to details-page AGAIN helper.navigateWithEntry({ create: pageFactory, clearHistory: false, animated: true }); // Go back to main-page with clearHistory - topmost.transition = { name: "fade" }; + topmost.transition = { name: "fade", duration: 10 }; helper.navigateWithEntry({ create: mainPageFactory, clearHistory: true, animated: true }); // Clean up @@ -209,7 +173,6 @@ export function test_ClearHistoryWithTransitionDoesNotBreakNavigation() { } export function test_ClearHistoryWithTransitionDoesNotBreakNavigation_WithLocalTransition() { - androidGC(); const topmost = topmostFrame(); let originalCachePagesOnNavigate: boolean; @@ -224,16 +187,16 @@ export function test_ClearHistoryWithTransitionDoesNotBreakNavigation_WithLocalT }; // Go to 1st page - helper.navigateWithEntry({ create: pageFactory, clearHistory: false, transition: { name: "fade" }, animated: true }); + helper.navigateWithEntry({ create: pageFactory, clearHistory: false, transition: { name: "fade", duration: 10 }, animated: true }); // Go to 2nd page - helper.navigateWithEntry({ create: pageFactory, clearHistory: false, transition: { name: "fade" }, animated: true }); + helper.navigateWithEntry({ create: pageFactory, clearHistory: false, transition: { name: "fade", duration: 10 }, animated: true }); // Go to 3rd page with clearHistory - helper.navigateWithEntry({ create: pageFactory, clearHistory: true, transition: { name: "fade" }, animated: true }); + helper.navigateWithEntry({ create: pageFactory, clearHistory: true, transition: { name: "fade", duration: 10 }, animated: true }); // Go back to main - helper.navigateWithEntry({ create: mainPageFactory, clearHistory: true, transition: { name: "fade" }, animated: true }); + helper.navigateWithEntry({ create: mainPageFactory, clearHistory: true, transition: { name: "fade", duration: 10 }, animated: true }); if (topmost.android) { topmostFrame().android.cachePagesOnNavigate = originalCachePagesOnNavigate; @@ -263,7 +226,7 @@ function _test_NavigationEvents(transition?: NavigationTransition) { }; // Go to other page - helper.navigateWithEntry({ create: secondPageFactory, transition: transition, animated: true }); + helper.navigateWithEntry({ create: secondPageFactory, transition: transition, animated: !!transition }); // Go back to main helper.goBack(); @@ -290,13 +253,11 @@ function _test_NavigationEvents(transition?: NavigationTransition) { } export function test_NavigationEvents() { - androidGC(); _test_NavigationEvents(); } export function test_NavigationEvents_WithTransition() { - androidGC(); - _test_NavigationEvents({ name: "fade" }); + _test_NavigationEvents({ name: "fade", duration: 10 }); } function _test_NavigationEvents_WithBackstackVisibile_False_Forward_Back(transition?: NavigationTransition) { @@ -314,7 +275,7 @@ function _test_NavigationEvents_WithBackstackVisibile_False_Forward_Back(transit }; // Go to other page - helper.navigateWithEntry({ create: secondPageFactory, transition: transition, animated: true, backstackVisible: false }); + helper.navigateWithEntry({ create: secondPageFactory, transition: transition, animated: !!transition, backstackVisible: false }); // Go back to main helper.goBack(); @@ -331,13 +292,11 @@ function _test_NavigationEvents_WithBackstackVisibile_False_Forward_Back(transit } export function test_NavigationEvents_WithBackstackVisibile_False_Forward_Back() { - androidGC(); _test_NavigationEvents_WithBackstackVisibile_False_Forward_Back(); } export function test_NavigationEvents_WithBackstackVisibile_False_Forward_Back_WithTransition() { - androidGC(); - _test_NavigationEvents_WithBackstackVisibile_False_Forward_Back({ name: "fade" }); + _test_NavigationEvents_WithBackstackVisibile_False_Forward_Back({ name: "fade", duration: 10 }); } function _test_NavigationEvents_WithBackstackVisibile_False_Forward_Forward(transition?: NavigationTransition) { @@ -355,10 +314,10 @@ function _test_NavigationEvents_WithBackstackVisibile_False_Forward_Forward(tran }; // Go to other page - helper.navigateWithEntry({ create: secondPageFactory, transition: transition, animated: true, backstackVisible: false }); + helper.navigateWithEntry({ create: secondPageFactory, transition: transition, animated: !!transition, backstackVisible: false }); // Go forward to third page - helper.navigateWithEntry({ create: pageFactory, transition: transition, animated: true }); + helper.navigateWithEntry({ create: pageFactory, transition: transition, animated: !!transition }); // Go back to main helper.goBack(); @@ -375,13 +334,11 @@ function _test_NavigationEvents_WithBackstackVisibile_False_Forward_Forward(tran } export function test_NavigationEvents_WithBackstackVisibile_False_Forward_Forward() { - androidGC(); _test_NavigationEvents_WithBackstackVisibile_False_Forward_Forward(); } export function test_NavigationEvents_WithBackstackVisibile_False_Forward_Forward_WithTransition() { - androidGC(); - _test_NavigationEvents_WithBackstackVisibile_False_Forward_Forward({ name: "fade" }); + _test_NavigationEvents_WithBackstackVisibile_False_Forward_Forward({ name: "fade", duration: 10 }); } function _test_NavigationEvents_WithClearHistory(transition?: NavigationTransition) { @@ -389,12 +346,12 @@ function _test_NavigationEvents_WithClearHistory(transition?: NavigationTransiti const mainTestPage = topmost.currentPage; mainTestPage.id = "main-page"; - let actualMainPageEvents = new Array(); + const actualMainPageEvents = new Array(); attachEventListeners(mainTestPage, actualMainPageEvents); - let actualSecondPageEvents = new Array(); + const actualSecondPageEvents = new Array(); const secondPage = new Page(); - let secondPageFactory = function (): Page { + const secondPageFactory = function (): Page { secondPage.actionBarHidden = true; secondPage.id = "second-page"; attachEventListeners(secondPage, actualSecondPageEvents); @@ -403,29 +360,21 @@ function _test_NavigationEvents_WithClearHistory(transition?: NavigationTransiti }; // Go to second page - helper.navigateWithEntry({ create: secondPageFactory, transition: transition, animated: true, clearHistory: true }); + helper.navigateWithEntry({ create: secondPageFactory, transition: transition, animated: !!transition, clearHistory: true }); - let expectedMainPageEvents = [ - "main-page navigatingFrom forward", - "main-page navigatedFrom forward" - ]; + const expectedMainPageEvents = [ "main-page navigatingFrom forward", "main-page navigatedFrom forward" ]; TKUnit.arrayAssert(actualMainPageEvents, expectedMainPageEvents, "Actual main-page events are different from expected."); - let expectedSecondPageEvents = [ - "second-page navigatingTo forward", - "second-page navigatedTo forward", - ]; + const expectedSecondPageEvents = [ "second-page navigatingTo forward", "second-page navigatedTo forward" ]; TKUnit.arrayAssert(actualSecondPageEvents, expectedSecondPageEvents, "Actual main-page events are different from expected."); TKUnit.assertEqual(topmost.currentPage, secondPage, "We should be on the second page at the end of the test."); } export function test_NavigationEvents_WithClearHistory() { - androidGC(); _test_NavigationEvents_WithClearHistory(); } export function test_NavigationEvents_WithClearHistory_WithTransition() { - androidGC(); - _test_NavigationEvents_WithClearHistory({ name: "fade" }); -} + _test_NavigationEvents_WithClearHistory({ name: "fade", duration: 10 }); +} \ No newline at end of file diff --git a/tests/app/navigation/transition-tests.ts b/tests/app/navigation/transition-tests.ts index db630af26..cdf9b0e37 100644 --- a/tests/app/navigation/transition-tests.ts +++ b/tests/app/navigation/transition-tests.ts @@ -6,6 +6,7 @@ import { Color } from "tns-core-modules/color"; import { NavigationEntry, NavigationTransition, topmost as topmostFrame } from "tns-core-modules/ui/frame"; import { Page } from "tns-core-modules/ui/page"; import { AnimationCurve } from "tns-core-modules/ui/enums" +import { CustomTransition } from "./custom-transition"; function _testTransition(navigationTransition: NavigationTransition) { var testId = `Transition[${JSON.stringify(navigationTransition)}]`; @@ -24,57 +25,39 @@ function _testTransition(navigationTransition: NavigationTransition) { } helper.navigateWithEntry(navigationEntry); - TKUnit.wait(0.100); } -// Extremely slow. Run only if needed. -export var test_Transitions = function () { - let topmost = topmostFrame(); - let mainTestPage = topmost.currentPage; - let mainPageFactory = function (): Page { +export function test_Transitions() { + const topmost = topmostFrame(); + const mainTestPage = topmost.currentPage; + const mainPageFactory = function (): Page { return mainTestPage; }; helper.navigate(() => { - var page = new Page(); + const page = new Page(); page.id = "TransitionsTestPage_MAIN" page.style.backgroundColor = new Color(255, Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255)); return page; }); var transitions; - var testCustomTransition = true; if (platform.device.os === platform.platformNames.ios) { transitions = ["curl"]; - } - else { - var _sdkVersion = parseInt(platform.device.sdkVersion); + } else { + const _sdkVersion = parseInt(platform.device.sdkVersion); transitions = _sdkVersion >= 21 ? ["explode"] : []; - if (_sdkVersion === 23) { - // Apparently, there is some kind of Android 6.0 (API 23) bug when using ObjectAnimators - // http://stackoverflow.com/questions/33188485/resultindex-is-1-the-polygon-must-be-invalid-adter-addview - testCustomTransition = false; - } } - transitions = transitions.concat(["fade", "flip", "slide"]); - var durations = [undefined, 10]; - var curves = [undefined, AnimationCurve.easeIn]; + + transitions = transitions.concat(["fade", "slide"]); + + // Custom transition + _testTransition({ instance: new CustomTransition(), duration: 10 }); // Built-in transitions transitions.forEach(name => { - durations.forEach(duration => { - curves.forEach(curve => { - _testTransition({ name, duration, curve }); - }); - }); + _testTransition({ name, duration: 20, curve: AnimationCurve.easeIn }); }); - // Custom transition - if (testCustomTransition) { - var customTransitionModule = require("./custom-transition"); - var customTransition = new customTransitionModule.CustomTransition(); - _testTransition({ instance: customTransition }); - } - - helper.navigateWithEntry({ create: mainPageFactory, clearHistory: true, animated: false }); -} + // helper.navigateWithEntry({ create: mainPageFactory, clearHistory: true, animated: false }); +} \ No newline at end of file diff --git a/tests/app/testRunner.ts b/tests/app/testRunner.ts index cfbedf229..f98947464 100644 --- a/tests/app/testRunner.ts +++ b/tests/app/testRunner.ts @@ -29,7 +29,7 @@ export function isRunningOnEmulator(): boolean { } } -export var allTests = {}; +export const allTests = {}; import * as domNodeTest from "./debugger/dom-node-tests"; allTests["DOM-NODE"] = domNodeTest; @@ -226,23 +226,19 @@ allTests["ANIMATION"] = animationTests; import * as lifecycle from "./ui/lifecycle/lifecycle-tests"; allTests["LIFECYCLE"] = lifecycle; + import * as cssAnimationTests from "./ui/animation/css-animation-tests"; allTests["CSS-ANIMATION"] = cssAnimationTests; import * as transitionTests from "./navigation/transition-tests"; -// Skip transitions on android emulators with API 23 -if (!(platform.device.os === platform.platformNames.android && parseInt(platform.device.sdkVersion) === 23 && isRunningOnEmulator())) { - allTests["TANSITIONS"] = transitionTests; -} - +allTests["TANSITIONS"] = transitionTests; import * as searchBarTests from "./ui/search-bar/search-bar-tests"; allTests["SEARCH-BAR"] = searchBarTests; import * as navigationTests from "./navigation/navigation-tests"; allTests["NAVIGATION"] = navigationTests; -var testsWithLongDelay = { - test_Transitions: 3 * 60 * 1000, +const testsWithLongDelay = { testLocation: 10000, testLocationOnce: 10000, testLocationOnceMaximumAge: 10000, @@ -257,14 +253,14 @@ var testsWithLongDelay = { test_AnimateBackgroundColor_FromString: 10 * 1000 } -var startTime; -var running = false; -var testsQueue = new Array(); +let startTime; +let running = false; +let testsQueue = new Array(); function printRunTestStats() { const testCases = new Array(); - var failedTestCount = 0; + let failedTestCount = 0; const failedTestInfo = []; const slowTests = new Array(); @@ -285,7 +281,7 @@ function printRunTestStats() { const totalTime = (TKUnit.time() - startTime).toFixed(2); - let finalMessage = `\n`+ + let finalMessage = `\n` + `=== ALL TESTS COMPLETE ===\n` + `${(allTests.length - failedTestCount)} OK, ${failedTestCount} failed\n` + `DURATION: ${totalTime} ms\n`; @@ -335,14 +331,13 @@ function generateTestFile(allTests: TestInfo[]) { } function showReportPage(finalMessage: string) { - let stack = new StackLayout(); - - let btn = new Button(); + const stack = new StackLayout(); + const btn = new Button(); btn.text = "Rerun tests"; btn.on("tap", () => runAll(testsSelector)); stack.addChild(btn); - let messageContainer = new TextView(); + const messageContainer = new TextView(); messageContainer.editable = messageContainer.autocorrect = false; messageContainer.text = finalMessage; stack.addChild(messageContainer); @@ -378,16 +373,16 @@ function log(): void { } let testsSelector: string -export var runAll = function (testSelector?: string) { +export function runAll(testSelector?: string) { testsSelector = testSelector; if (running) { // TODO: We may schedule pending run requests return; } - var singleModuleName, singleTestName; + let singleModuleName, singleTestName; if (testSelector) { - var pair = testSelector.split("."); + const pair = testSelector.split("."); singleModuleName = pair[0]; if (singleModuleName) { if (singleModuleName.length === 0) { @@ -409,17 +404,17 @@ export var runAll = function (testSelector?: string) { console.log("TESTS: " + singleModuleName ? singleModuleName : "" + " " + singleTestName ? singleTestName : ""); - var totalSuccess = 0; - var totalFailed: Array = []; + const totalSuccess = 0; + const totalFailed: Array = []; testsQueue.push(new TestInfo(() => { running = true; startTime = TKUnit.time(); })); - for (var name in allTests) { + for (const name in allTests) { if (singleModuleName && (singleModuleName !== name.toLowerCase())) { continue; } - var testModule = allTests[name]; + const testModule = allTests[name]; - var test = testModule.createTestCase ? testModule.createTestCase() : testModule; + const test = testModule.createTestCase ? testModule.createTestCase() : testModule; test.name = name; testsQueue.push(new TestInfo(startLog, test)); @@ -428,17 +423,17 @@ export var runAll = function (testSelector?: string) { testsQueue.push(new TestInfo(test.setUpModule, test)); } - for (var testName in test) { + for (const testName in test) { if (singleTestName && (singleTestName !== testName.toLowerCase())) { continue; } - var testFunction = test[testName]; + const testFunction = test[testName]; if ((typeof (testFunction) === "function") && (testName.substring(0, 4) == "test")) { if (test.setUp) { testsQueue.push(new TestInfo(test.setUp, test)); } - var testTimeout = testsWithLongDelay[testName]; + const testTimeout = testsWithLongDelay[testName]; testsQueue.push(new TestInfo(testFunction, test, true, name + "." + testName, false, null, testTimeout)); if (test.tearDown) { testsQueue.push(new TestInfo(test.tearDown, test)); @@ -457,8 +452,6 @@ export var runAll = function (testSelector?: string) { TKUnit.runTests(testsQueue, 0); } - - class TestInfo implements TKUnit.TestInfoEntry { testFunc: () => void; instance: any; @@ -479,4 +472,4 @@ class TestInfo implements TKUnit.TestInfoEntry { this.testTimeout = testTimeout; this.duration = duration; } -} +} \ No newline at end of file diff --git a/tests/app/timer/timer-tests.ts b/tests/app/timer/timer-tests.ts index 94c62f2ec..6b1d98fd3 100644 --- a/tests/app/timer/timer-tests.ts +++ b/tests/app/timer/timer-tests.ts @@ -110,51 +110,55 @@ export function test_setTimeout_callbackShouldBeCleared() { TKUnit.assert(!completed, "Callback should be cleared when clearTimeout() is executed for specified id!"); }; -export function test_setInterval_callbackCalledDuringPeriod() { +export function test_setInterval_callbackCalledDuringPeriod(done) { let counter = 0; - let expected = 4; + const expected = 4; + const start = TKUnit.time(); // >> timer-set-expression const id = timer.setInterval(() => { // >> (hide) counter++; + if (counter === 4) { + const end = TKUnit.time(); + timer.clearInterval(id); + done(end - start > 250 ? new Error('setInterval too slow.') : null); + } // << (hide) }, 50); // << timer-set-expression - - TKUnit.waitUntilReady(() => counter >= expected, 0.25, false); - timer.clearInterval(id); - TKUnit.assert(counter >= expected, "Callback should be raised at least" + expected + "times! Callback raised " + counter + " times."); }; -export function test_setInterval_callbackCalledWithExtraArgs() { +export function test_setInterval_callbackCalledWithExtraArgs(done) { let counter: number = 0; - let expected: number = 4; - let rnd: number = Math.random(); + const expected: number = 4; + const rnd: number = Math.random(); + const start = TKUnit.time(); const id = timer.setInterval((arg) => { counter += arg === rnd ? 1 : -1; + if (counter === 4) { + const end = TKUnit.time(); + timer.clearInterval(id); + done(end - start > 250 ? new Error('setInterval too slow.') : null); + } }, 50, rnd); - - TKUnit.waitUntilReady(() => counter >= expected, 0.25, false); - timer.clearInterval(id); - TKUnit.assert(counter >= expected, "Callback raised " + counter + " times with arguments."); }; -export function test_setInterval_callbackShouldBeCleared() { +export function test_setInterval_callbackShouldBeCleared(done) { let counter = 0; + const start = TKUnit.time(); // >> timer-set-interval const id = timer.setInterval(() => { // >> (hide) - counter++; + const end = TKUnit.time(); + timer.clearInterval(id); + done(end - start > 150 ? new Error('setInterval too slow.') : null); // << (hide) timer.clearInterval(id); }, 50); // << timer-set-interval - - TKUnit.wait(0.15); - TKUnit.assertEqual(counter, 1, "Callback should be raised only once!"); }; export function test_clearTimeout_multipleTimes_afterTick() { @@ -205,4 +209,4 @@ export function test_clearTimeout_insideCallback() { TKUnit.waitUntilReady(() => completed, 0.5); TKUnit.assert(completed, "Callback should be called"); -} +} \ No newline at end of file diff --git a/tests/app/ui-test.ts b/tests/app/ui-test.ts index 5d615f34c..b0108ebcb 100644 --- a/tests/app/ui-test.ts +++ b/tests/app/ui-test.ts @@ -22,16 +22,12 @@ export class UITest implements trace.TraceWriter { return this._testView; } - public waitUntilTestElementIsLoaded(timeoutSec?: number): void { - TKUnit.waitUntilReady(() => { - return this.testView.isLoaded; - }, timeoutSec || 1); + public waitUntilTestElementIsLoaded(timeoutSec: number = 1): void { + TKUnit.waitUntilReady(() => this.testView.isLoaded, timeoutSec); } - public waitUntilTestElementLayoutIsValid(timeoutSec?: number): void { - TKUnit.waitUntilReady(() => { - return this.testView.isLayoutValid; - }, timeoutSec || 1); + public waitUntilTestElementLayoutIsValid(timeoutSec: number = 1): void { + TKUnit.waitUntilReady(() => this.testView.isLayoutValid, timeoutSec); } public create(): T { @@ -77,4 +73,4 @@ export class UITest implements trace.TraceWriter { export function createTestCase(): UITest { return null; -} +} \ No newline at end of file diff --git a/tests/app/ui/animation/animation-tests.ts b/tests/app/ui/animation/animation-tests.ts index 235114943..7b4c7af3f 100644 --- a/tests/app/ui/animation/animation-tests.ts +++ b/tests/app/ui/animation/animation-tests.ts @@ -33,8 +33,8 @@ export function test_AnimatingProperties(done) { translate: { x: 100, y: 100 }, scale: { x: 2, y: 2 }, rotate: 180, - duration: 10, - delay: 100, + duration: 5, + delay: 10, iterations: 3, curve: enums.AnimationCurve.easeIn }) @@ -58,7 +58,7 @@ export function test_CancellingAnimation(done) { let label = prepareTest(); // >> animation-cancel - var animation1 = label.createAnimation({ translate: { x: 100, y: 100 }, duration: 20 }); + var animation1 = label.createAnimation({ translate: { x: 100, y: 100 }, duration: 5 }); animation1.play() .then(() => { @@ -87,7 +87,7 @@ export function test_CancellingAnimate(done) { let label = prepareTest(); // >> animation-cancel2 - var animation1 = label.animate({ translate: { x: 100, y: 100 }, duration: 20 }) + var animation1 = label.animate({ translate: { x: 100, y: 100 }, duration: 5 }) .then(() => { //console.log("Animation finished"); // >> (hide) @@ -188,9 +188,9 @@ export function test_AnimatingMultipleViews(done) { // >> animation-multiple-views var animations: Array = [ - { target: label1, translate: { x: 200, y: 200 }, duration: 20, delay: 0 }, - { target: label2, translate: { x: 200, y: 200 }, duration: 20, delay: 7 }, - { target: label3, translate: { x: 200, y: 200 }, duration: 20, delay: 14 }, + { target: label1, translate: { x: 200, y: 200 }, duration: 5, delay: 0 }, + { target: label2, translate: { x: 200, y: 200 }, duration: 5, delay: 2 }, + { target: label3, translate: { x: 200, y: 200 }, duration: 5, delay: 4 }, ]; var a = new animation.Animation(animations); a.play() @@ -215,7 +215,7 @@ export function test_AnimatingMultipleViews(done) { export function test_AnimateOpacity(done) { let label = prepareTest(); - label.animate({ opacity: 0.75, duration: 20 }) + label.animate({ opacity: 0.75, duration: 5 }) .then(() => { TKUnit.assertEqual(label.opacity, 0.75, "label.opacity"); done(); @@ -301,7 +301,7 @@ export function test_AnimateBackgroundColor(done) { let label = prepareTest(); var red = new colorModule.Color("Red"); - label.animate({ backgroundColor: red, duration: 20 }) + label.animate({ backgroundColor: red, duration: 5 }) .then(() => { TKUnit.assert((label.backgroundColor).equals(red)); done(); @@ -316,7 +316,7 @@ export function test_AnimateBackgroundColor_FromString(done) { var expected = "Red"; var clr = new colorModule.Color(expected); - label.animate({ backgroundColor: expected, duration: 20 }) + label.animate({ backgroundColor: expected, duration: 5 }) .then(() => { TKUnit.assert((label.backgroundColor).equals(clr)); done(); @@ -329,7 +329,7 @@ export function test_AnimateBackgroundColor_FromString(done) { export function test_AnimateTranslate(done) { let label = prepareTest(); - label.animate({ translate: { x: 100, y: 200 }, duration: 20 }) + label.animate({ translate: { x: 100, y: 200 }, duration: 5 }) .then(() => { TKUnit.assertEqual(label.translateX, 100, "label.translateX"); TKUnit.assertEqual(label.translateY, 200, "label.translateY"); @@ -344,7 +344,7 @@ export function test_AnimateTranslate(done) { export function test_AnimateScale(done) { let label = prepareTest(); - label.animate({ scale: { x: 2, y: 3 }, duration: 20 }) + label.animate({ scale: { x: 2, y: 3 }, duration: 5 }) .then(() => { TKUnit.assertEqual(label.scaleX, 2, "label.scaleX"); TKUnit.assertEqual(label.scaleY, 3, "label.scaleY"); @@ -359,7 +359,7 @@ export function test_AnimateScale(done) { export function test_AnimateRotate(done) { let label = prepareTest(); - label.animate({ rotate: 123, duration: 20 }) + label.animate({ rotate: 123, duration: 5 }) .then(() => { TKUnit.assertEqual(label.rotate, 123, "label.rotate"); assertIOSNativeTransformIsCorrect(label); @@ -377,7 +377,7 @@ export function test_AnimateTranslateScaleAndRotateSimultaneously(done) { translate: { x: 100, y: 200 }, scale: { x: 2, y: 3 }, rotate: 123, - duration: 20 + duration: 5 }) .then(() => { TKUnit.assertEqual(label.translateX, 100, "label.translateX"); @@ -396,12 +396,12 @@ export function test_AnimateTranslateScaleAndRotateSimultaneously(done) { export function test_AnimateTranslateScaleAndRotateSequentially(done) { let label = prepareTest(); - label.animate({ translate: { x: 100, y: 200 }, duration: 20 }) + label.animate({ translate: { x: 100, y: 200 }, duration: 5 }) .then(() => { TKUnit.assertEqual(label.translateX, 100, "label.translateX"); TKUnit.assertEqual(label.translateY, 200, "label.translateY"); assertIOSNativeTransformIsCorrect(label); - return label.animate({ scale: { x: 2, y: 3 }, duration: 20 }); + return label.animate({ scale: { x: 2, y: 3 }, duration: 5 }); }) .then(() => { TKUnit.assertEqual(label.translateX, 100, "label.translateX"); @@ -409,7 +409,7 @@ export function test_AnimateTranslateScaleAndRotateSequentially(done) { TKUnit.assertEqual(label.scaleX, 2, "label.scaleX"); TKUnit.assertEqual(label.scaleY, 3, "label.scaleY"); assertIOSNativeTransformIsCorrect(label); - return label.animate({ rotate: 123, duration: 20 }); + return label.animate({ rotate: 123, duration: 5 }); }) .then(() => { TKUnit.assertEqual(label.translateX, 100, "label.translateX"); @@ -428,8 +428,8 @@ export function test_AnimateTranslateScaleAndRotateSequentially(done) { export function test_AnimationsAreAlwaysPlayed(done) { let label = prepareTest(); - var animation1 = label.createAnimation({ opacity: 0, duration: 20 }); - var animation2 = label.createAnimation({ opacity: 1, duration: 20 }); + var animation1 = label.createAnimation({ opacity: 0, duration: 5 }); + var animation2 = label.createAnimation({ opacity: 1, duration: 5 }); animation1.play() .then(() => { @@ -449,7 +449,7 @@ export function test_AnimationsAreAlwaysPlayed(done) { export function test_PlayPromiseIsResolvedWhenAnimationFinishes(done) { let label = prepareTest(); - var animation = label.createAnimation({ opacity: 0, duration: 20 }); + var animation = label.createAnimation({ opacity: 0, duration: 5 }); animation.play() .then(function onResolved() { @@ -464,7 +464,7 @@ export function test_PlayPromiseIsResolvedWhenAnimationFinishes(done) { export function test_PlayPromiseIsRejectedWhenAnimationIsCancelled(done) { let label = prepareTest(); - var animation = label.createAnimation({ opacity: 0, duration: 20 }); + var animation = label.createAnimation({ opacity: 0, duration: 5 }); animation.play() .then(function onResolved() { diff --git a/tests/app/ui/helper.ts b/tests/app/ui/helper.ts index 1046e6774..36ff92310 100644 --- a/tests/app/ui/helper.ts +++ b/tests/app/ui/helper.ts @@ -153,7 +153,14 @@ export function getClearCurrentPage(): Page { } export function waitUntilNavigatedFrom(oldPage: Page) { - TKUnit.waitUntilReady(() => getCurrentPage() && getCurrentPage() !== oldPage); + let completed = false; + function navigatedFrom(args) { + args.object.page.off("navigatedFrom", navigatedFrom); + completed = true; + } + + oldPage.on("navigatedFrom", navigatedFrom); + TKUnit.waitUntilReady(() => completed); } export function waitUntilLayoutReady(view: View): void { diff --git a/tests/app/ui/lifecycle/lifecycle-tests.ts b/tests/app/ui/lifecycle/lifecycle-tests.ts index 9dd91855f..c3346c8b0 100644 --- a/tests/app/ui/lifecycle/lifecycle-tests.ts +++ b/tests/app/ui/lifecycle/lifecycle-tests.ts @@ -111,7 +111,9 @@ export function test_navigating_away_does_not_excessively_reset() { const page2 = helper.navigateToModule("ui/lifecycle/pages/page-one"); - helper.waitUntilLayoutReady(page2); + if (!page2.isLayoutValid) { + helper.waitUntilLayoutReady(page2); + } // NOTE: Recycling may mess this up so feel free to change the test, // but ensure a reasonable amount of native setters were called when the views navigate away diff --git a/tests/app/ui/list-view/list-view-tests.ts b/tests/app/ui/list-view/list-view-tests.ts index 23fd0e23f..b6a671841 100644 --- a/tests/app/ui/list-view/list-view-tests.ts +++ b/tests/app/ui/list-view/list-view-tests.ts @@ -639,9 +639,8 @@ export class ListViewTest extends testModule.UITest { } public test_LoadedUnloaded() { - let listView = this.testView; - let count = 10; - let modifier = listView.ios ? 1 : 0; // iOS has one fake measure cell that raises loaded. + const listView = this.testView; + const count = 10; let items = new observableArray.ObservableArray(); for (let i = 0; i < count; i++) { @@ -664,10 +663,15 @@ export class ListViewTest extends testModule.UITest { this.waitUntilListViewReady(); helper.navigateWithHistory(() => new Page()); + for (let i = 0; i < count; i++) { + TKUnit.assertEqual(items.getItem(i).loadedCount, 1, "Loaded Count"); + TKUnit.assertEqual(items.getItem(i).unloadedCount, 1, "Unloaded Count"); + } + helper.goBack(); for (let i = 0; i < count; i++) { - TKUnit.assertEqual(items.getItem(i).loadedCount, 1 + modifier, "Loaded Count"); + TKUnit.assertEqual(items.getItem(i).loadedCount, 2, "Loaded Count"); TKUnit.assertEqual(items.getItem(i).unloadedCount, 1, "Unloaded Count"); } } diff --git a/tests/app/ui/page/page-tests-common.ts b/tests/app/ui/page/page-tests-common.ts index 5ccc57385..9a243003e 100644 --- a/tests/app/ui/page/page-tests-common.ts +++ b/tests/app/ui/page/page-tests-common.ts @@ -1,32 +1,27 @@ // >> article-require-page-module -import * as pageModule from "tns-core-modules/ui/page"; +import { Page, ShownModallyData, NavigatedData } from "tns-core-modules/ui/page"; // FrameModule is needed in order to have an option to navigate to the new page. -import * as frameModule from "tns-core-modules/ui/frame"; +import { topmost, NavigationEntry } from "tns-core-modules/ui/frame"; // << article-require-page-module +import { resolvePageFromEntry } from "tns-core-modules/ui/frame"; + // >> article-set-bindingcontext function pageLoaded(args) { - let page = args.object; + const page = args.object; page.bindingContext = { name: "Some name" }; } exports.pageLoaded = pageLoaded; // << article-set-bindingcontext import * as TKUnit from "../../TKUnit"; -import * as labelModule from "tns-core-modules/ui/label"; -import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout"; import * as helper from "../helper"; -import * as view from "tns-core-modules/ui/core/view"; -import * as observable from "tns-core-modules/data/observable"; -import { Page, ShownModallyData, NavigatedData } from "tns-core-modules/ui/page"; +import { StackLayout } from "tns-core-modules/ui/layouts/stack-layout"; +import { View, PercentLength, Observable, unsetValue, EventData, isIOS } from "tns-core-modules/ui/core/view"; import { Label } from "tns-core-modules/ui/label"; -import { EventData } from "tns-core-modules/data/observable"; -import { PercentLength } from "tns-core-modules/ui/core/view"; -import * as platform from "tns-core-modules/platform"; -import { unsetValue } from "tns-core-modules/ui/core/view"; import { Color } from "tns-core-modules/color"; export function addLabelToPage(page: Page, text?: string) { - let label = new Label(); + const label = new Label(); label.text = text || "The quick brown fox jumps over the lazy dog."; page.content = label; } @@ -40,16 +35,11 @@ export function test_AfterPageLoaded_is_called_NativeInstance_is_created() { let label: Label; let nativeInstanceCreated = false; - let handler = function (data) { - if (label.ios || label.android) { - nativeInstanceCreated = true; - } - } - - let pageFactory = function (): Page { + const handler = (data) => nativeInstanceCreated = !!label.nativeViewProtected; + const pageFactory = () => { page = new Page(); page.id = `page_test_AfterPageLoaded_is_called_NativeInstance_is_created`; - page.on(view.View.loadedEvent, handler); + page.on(Label.loadedEvent, handler); label = new Label(); label.text = "Text"; @@ -60,7 +50,7 @@ export function test_AfterPageLoaded_is_called_NativeInstance_is_created() { helper.navigate(pageFactory); TKUnit.assertTrue(nativeInstanceCreated, "nativeInstanceCreated"); - page.off(view.View.loadedEvent, handler); + page.off(Label.loadedEvent, handler); } export function test_PageLoaded_is_called_once() { @@ -69,11 +59,11 @@ export function test_PageLoaded_is_called_once() { let page2: Page; let loaded = 0; - let handler = function (data) { + const handler = function (data) { loaded++; } - let pageFactory = function (): Page { + const pageFactory = function (): Page { page1 = new Page(); page1.id = `page1_test_PageLoaded_is_called_once`; addLabelToPage(page1, "Page 1"); @@ -83,49 +73,49 @@ export function test_PageLoaded_is_called_once() { helper.navigate(pageFactory); TKUnit.assertEqual(loaded, 0); - let pageFactory2 = function (): Page { + const pageFactory2 = function (): Page { page2 = new Page(); page2.id = `page2_test_PageLoaded_is_called_once`; addLabelToPage(page2, "Page 2"); - page2.on(view.View.loadedEvent, handler); + page2.on(Label.loadedEvent, handler); return page2; }; helper.navigate(pageFactory2); TKUnit.assertEqual(loaded, 1); - page2.off(view.View.loadedEvent, handler); + page2.off(Label.loadedEvent, handler); } export function test_NavigateToNewPage() { - // >> artivle-create-navigate-to-page - let currentPage; - let topFrame = frameModule.topmost(); - currentPage = topFrame.currentPage; + // >> article-create-navigate-to-page + + const topFrame = topmost(); + const currentPage = topFrame.currentPage; let testPage: Page; - let pageFactory = function (): Page { - testPage = new pageModule.Page(); - let label = new labelModule.Label(); + const pageFactory = function (): Page { + testPage = new Page(); + const label = new Label(); label.text = "The quick brown fox jumps over the lazy dog."; testPage.content = label; return testPage; }; - let navEntry = { + const navEntry = { create: pageFactory, animated: false }; topFrame.navigate(navEntry); - // << artivle-create-navigate-to-page + // << article-create-navigate-to-page - TKUnit.waitUntilReady(() => { return testPage.isLayoutValid }); + TKUnit.waitUntilReady(() => testPage.isLayoutValid); // >> article-navigating-backward topFrame.goBack(); // << article-navigating-backward - TKUnit.waitUntilReady(() => { return topFrame.currentPage !== null && topFrame.currentPage === currentPage }); + TKUnit.waitUntilReady(() => topFrame.navigationQueueIsEmpty()); TKUnit.assertNull(testPage.parent, "Page.parent should become undefined after navigating back"); TKUnit.assertNull(testPage._context, "Page._context should become undefined after navigating back"); TKUnit.assertFalse(testPage.isLoaded, "Page.isLoaded should become false after navigating back"); @@ -142,10 +132,11 @@ export function test_PageNavigation_EventSequence_WithoutTransition() { } function _test_PageNavigation_EventSequence(withTransition: boolean) { + const context = { property: "this is the context" }; + const eventSequence = []; + let testPage: Page; - let context = { property: "this is the context" }; - let eventSequence = []; - let pageFactory = function () { + const pageFactory = () => { testPage = new Page(); testPage.id = "testPage_test_PageNavigation_EventSequence"; addLabelToPage(testPage); @@ -155,15 +146,15 @@ function _test_PageNavigation_EventSequence(withTransition: boolean) { TKUnit.assertEqual(data.context, context, "navigatingTo: navigationContext"); }); - testPage.on(Page.loadedEvent, function (data: observable.EventData) { + testPage.on(Page.loadedEvent, function (data: EventData) { eventSequence.push("loaded"); - TKUnit.assertNotEqual(frameModule.topmost().currentPage, data.object); + TKUnit.assertNotEqual(topmost().currentPage, data.object); }); testPage.on(Page.navigatedToEvent, function (data: NavigatedData) { eventSequence.push("navigatedTo"); TKUnit.assertEqual(data.context, context, "navigatedTo : navigationContext"); - TKUnit.assertEqual(frameModule.topmost().currentPage, data.object); + TKUnit.assertEqual(topmost().currentPage, data.object); }); testPage.on(Page.navigatingFromEvent, function (data: NavigatedData) { @@ -183,54 +174,47 @@ function _test_PageNavigation_EventSequence(withTransition: boolean) { return testPage; }; - let navigationEntry: frameModule.NavigationEntry; - if (withTransition) { - navigationEntry = { - create: pageFactory, - context: context, - animated: true, - transition: { - name: "slide", - duration: 100, - } - } - } - else { - navigationEntry = { - create: pageFactory, - context: context, - animated: false - } + const navigationEntry: NavigationEntry = { + create: pageFactory, + context: context, + animated: withTransition, + transition: withTransition ? { + name: "slide", + duration: 10, + } : undefined } + helper.navigateWithEntry(navigationEntry); helper.goBack(); - let expectedEventSequence = ["navigatingTo", "loaded", "navigatedTo", "navigatingFrom", "unloaded", "navigatedFrom"]; + const expectedEventSequence = ["navigatingTo", "loaded", "navigatedTo", "navigatingFrom", "unloaded", "navigatedFrom"]; TKUnit.arrayAssert(eventSequence, expectedEventSequence, "Actual event sequence is not equal to expected. Actual: " + eventSequence + "; Expected: " + expectedEventSequence); } export function test_NavigateTo_WithContext() { - let currentPage = frameModule.topmost().currentPage; + const currentPage = topmost().currentPage; // >> article-pass-data - let testPage: pageModule.Page; - let pageFactory = function (): pageModule.Page { - testPage = new pageModule.Page(); - testPage.on(pageModule.Page.navigatedToEvent, function () { + let testPage: Page; + const pageFactory = function (): Page { + testPage = new Page(); + testPage.on(Page.navigatedToEvent, function () { //console.log(JSON.stringify(context)); }); return testPage; }; - let navEntry = { + + const navEntry = { create: pageFactory, context: "myContext", animated: false }; - let topFrame = frameModule.topmost(); + + const topFrame = topmost(); topFrame.navigate(navEntry); // << article-pass-data - TKUnit.waitUntilReady(() => topFrame.currentPage !== null && topFrame.currentPage !== currentPage && testPage.isLayoutValid); + TKUnit.waitUntilReady(() => topFrame.navigationQueueIsEmpty()); - let actualContextValue = testPage.navigationContext; + const actualContextValue = testPage.navigationContext; TKUnit.assertEqual(actualContextValue, "myContext"); helper.goBack(); @@ -239,34 +223,35 @@ export function test_NavigateTo_WithContext() { //https://github.com/NativeScript/NativeScript/issues/731 export function test_NavigateTo_WithBindingContext() { - let currentPage = frameModule.topmost().currentPage; + const currentPage = topmost().currentPage; let testPage: Page; let bindingContext; - let pageFactory = function (): Page { + const pageFactory = function (): Page { testPage = new Page(); - testPage.on(pageModule.Page.navigatingToEvent, function (args: NavigatedData) { + testPage.on(Page.navigatingToEvent, function (args: NavigatedData) { bindingContext = (args.object).bindingContext; }); return testPage; }; - let navEntry = { + + const navEntry = { create: pageFactory, bindingContext: "bindng context", animated: false }; - let topFrame = frameModule.topmost(); + + const topFrame = topmost(); topFrame.navigate(navEntry); - TKUnit.waitUntilReady(() => topFrame.currentPage !== null && topFrame.currentPage !== currentPage && testPage.isLayoutValid); + TKUnit.waitUntilReady(() => topFrame.navigationQueueIsEmpty()); helper.goBack(); TKUnit.assertEqual(bindingContext, navEntry.bindingContext, "The Page's bindingContext should be equal to the NavigationEntry.bindingContext property when navigating to."); } export function test_FrameBackStack_WhenNavigatingForwardAndBack() { - helper.navigate(() => new Page()); let testPage: Page; - let pageFactory = function () { + const pageFactory = function () { testPage = new Page(); testPage.id = "testPage_test_FrameBackStack_WhenNavigatingForwardAndBack"; addLabelToPage(testPage); @@ -275,7 +260,7 @@ export function test_FrameBackStack_WhenNavigatingForwardAndBack() { helper.navigateWithHistory(pageFactory); - let topFrame = frameModule.topmost(); + const topFrame = topmost(); TKUnit.assertEqual(topFrame.backStack.length, 1); TKUnit.assertTrue(topFrame.canGoBack(), "topFrame.canGoBack() should be true"); @@ -286,38 +271,38 @@ export function test_FrameBackStack_WhenNavigatingForwardAndBack() { } export function test_LoadPageFromModule() { - let topFrame = frameModule.topmost(); + const topFrame = topmost(); helper.navigateToModule("ui/page/test-page-module"); TKUnit.assert(topFrame.currentPage.content instanceof Label, "Content of the test page should be a Label created within test-page-module."); - let testLabel =