From 0a4e28f5aeb6a5bce2102386ff77cc9b2f9b3bb5 Mon Sep 17 00:00:00 2001 From: Vasil Chimev Date: Mon, 18 Nov 2019 11:19:57 +0200 Subject: [PATCH] refactor: testing --- nativescript-core/testing/index.ts | 94 +++ nativescript-core/testing/tk-unit.ts | 328 +--------- nativescript-core/testing/ui-helper.ts | 856 ------------------------- 3 files changed, 96 insertions(+), 1182 deletions(-) create mode 100644 nativescript-core/testing/index.ts delete mode 100644 nativescript-core/testing/ui-helper.ts diff --git a/nativescript-core/testing/index.ts b/nativescript-core/testing/index.ts new file mode 100644 index 000000000..4253d9d3a --- /dev/null +++ b/nativescript-core/testing/index.ts @@ -0,0 +1,94 @@ +// TODO: Remove this and get it from global to decouple builder for angular +import { Builder } from "../ui/builder/builder"; +import { unsetValue, View } from "../ui/core/view/view"; +import { Frame, NavigationEntry } from "../ui/frame/frame"; +import { Page } from "../ui/page/page"; + +import * as TKUnit from "./tk-unit"; + +export let ASYNC = 0.2; +export let MEMORY_ASYNC = 2; + +function navigate(pageFactory: () => Page, navigationContext?: any): Page { + let entry: NavigationEntry = { create: pageFactory, animated: false, context: navigationContext, clearHistory: true }; + + return navigateWithEntry(entry); +} + +function navigateWithHistory(pageFactory: () => Page, navigationContext?: any): Page { + let entry: NavigationEntry = { create: pageFactory, animated: false, context: navigationContext, clearHistory: false }; + + return navigateWithEntry(entry); +} + +export function asdf(moduleName: string, context?: any): Page { + let entry: NavigationEntry = { moduleName: moduleName, context: context, animated: false, clearHistory: true }; + + return navigateWithEntry(entry); +} + +function navigateToModule(moduleName: string, context?: any): Page { + let entry: NavigationEntry = { moduleName: moduleName, context: context, animated: false, clearHistory: true }; + + return navigateWithEntry(entry); +} + +function getCurrentPage(): Page { + return Frame.topmost().currentPage; +} + +function getClearCurrentPage(): Page { + let page = Frame.topmost().currentPage; + page.style.backgroundColor = unsetValue; + page.style.color = unsetValue; + page.bindingContext = unsetValue; + page.className = unsetValue; + page.id = unsetValue; + page.css = ""; + + return page; +} + +function waitUntilNavigatedTo(page: Page, action: Function) { + let completed = false; + function navigatedTo(args) { + args.object.page.off("navigatedTo", navigatedTo); + completed = true; + } + + page.on("navigatedTo", navigatedTo); + action(); + TKUnit.waitUntilReady(() => completed, 5); +} + +function waitUntilNavigatedFrom(action: Function, topFrame?: Frame) { + const currentPage = topFrame ? topFrame.currentPage : Frame.topmost().currentPage; + let completed = false; + function navigatedFrom(args) { + args.object.page.off("navigatedFrom", navigatedFrom); + completed = true; + } + + currentPage.on("navigatedFrom", navigatedFrom); + action(); + TKUnit.waitUntilReady(() => completed); +} + +function waitUntilLayoutReady(view: View): void { + TKUnit.waitUntilReady(() => view.isLayoutValid); +} + +function navigateWithEntry(entry: NavigationEntry, topFrame?: Frame): Page { + const page = Builder.createViewFromEntry(entry) as Page; + entry.moduleName = null; + entry.create = function () { + return page; + }; + + waitUntilNavigatedFrom(() => topFrame ? topFrame.navigate(entry) : Frame.topmost().navigate(entry)); + // navigatedTo; + // waitUntilNavigatedTo(() => topFrame ? topFrame.navigate(entry) : Frame.topmost().navigate(entry)); + + + return page; +} diff --git a/nativescript-core/testing/tk-unit.ts b/nativescript-core/testing/tk-unit.ts index 2c92273bb..ac964f1f2 100644 --- a/nativescript-core/testing/tk-unit.ts +++ b/nativescript-core/testing/tk-unit.ts @@ -12,346 +12,22 @@ */ import * as application from "../application"; -import * as trace from "../trace"; import * as platform from "../platform"; -import * as types from "../utils/types"; import * as timer from "../timer"; +import * as trace from "../trace"; const sdkVersion = parseInt(platform.device.sdkVersion); trace.enable(); -export interface TestInfoEntry { - testFunc: () => void; - instance: Object; - isTest: boolean; - testName: string; - isPassed: boolean; - errorMessage: string; - testTimeout: number; - duration: number; -} - export function time(): number { if (global.android) { - // return java.lang.System.nanoTime() / 1000000; // 1 ms = 1000000 ns + return java.lang.System.nanoTime() / 1000000; // 1 ms = 1000000 ns } else { return CACurrentMediaTime() * 1000; } } -export function write(message: string, type?: number) { - trace.write(message, trace.categories.Test, type); -} - -function runTest(testInfo: TestInfoEntry) { - let start = time(); - let duration; - try { - if (testInfo.instance) { - testInfo.testFunc.apply(testInfo.instance); - } else { - testInfo.testFunc(); - } - - if (testInfo.isTest) { - duration = time() - start; - testInfo.duration = duration; - write(`--- [${testInfo.testName}] OK, duration: ${duration.toFixed(2)}`, trace.messageType.info); - testInfo.isPassed = true; - } - } - catch (e) { - if (testInfo.isTest) { - duration = time() - start; - testInfo.duration = duration; - write(`--- [${testInfo.testName}] FAILED: ${e.message}, Stack: ${e.stack}, duration: ${duration.toFixed(2)}`, trace.messageType.error); - testInfo.isPassed = false; - testInfo.errorMessage = e.message; - } - } -} - -export interface TestFailure { - moduleName: string; - testName: string; - errorMessage: string; -} - -export interface TestModuleRunResult { - name: string; - count: number; - succeeded: number; - failed: Array; -} - -let testsQueue: Array; -const defaultTimeout = 5000; - -function runAsync(testInfo: TestInfoEntry, recursiveIndex: number, testTimeout?: number) { - let error; - let isDone = false; - let handle; - const testStartTime = time(); - //write("--- [" + testInfo.testName + "] Started at: " + testStartTime, trace.messageType.info); - const doneCallback = (e: Error) => { - if (e) { - error = e; - } else { - isDone = true; - } - } - - const timeout = testTimeout || testInfo.testTimeout || defaultTimeout; - - let duration; - const checkFinished = () => { - duration = time() - testStartTime; - testInfo.duration = duration; - if (isDone) { - write(`--- [${testInfo.testName}] OK, duration: ${duration.toFixed(2)}`, trace.messageType.info); - testInfo.isPassed = true; - runTests(testsQueue, recursiveIndex + 1); - } else if (error) { - write(`--- [${testInfo.testName}] FAILED: ${error.message}, duration: ${duration.toFixed(2)}`, trace.messageType.error); - testInfo.errorMessage = error.message; - runTests(testsQueue, recursiveIndex + 1); - } else { - const testEndTime = time(); - if (testEndTime - testStartTime > timeout) { - write(`--- [${testInfo.testName}] TIMEOUT, duration: ${duration.toFixed(2)}`, trace.messageType.error); - testInfo.errorMessage = "Test timeout."; - runTests(testsQueue, recursiveIndex + 1); - } else { - setTimeout(checkFinished, 20); - } - } - } - - try { - if (testInfo.instance) { - testInfo.testFunc.apply(testInfo.instance, [doneCallback]); - } else { - const func: any = testInfo.testFunc; - func(doneCallback); - } - } catch (e) { - doneCallback(e); - } - - setTimeout(checkFinished, 20); -} - -export function runTests(tests: Array, recursiveIndex) { - testsQueue = tests; - - for (let i = recursiveIndex; i < testsQueue.length; i++) { - const testEntry = testsQueue[i]; - - if (testEntry.testFunc.length > 0) { - return runAsync(testEntry, i); - } else { - runTest(testEntry); - } - } -} - -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) { - let equals = false; - if (types.isUndefined(actual) && types.isUndefined(expected)) { - equals = true; - } else if (!types.isNullOrUndefined(actual) && !types.isNullOrUndefined(expected)) { - if (types.isFunction(actual.equals)) { - // Use the equals method - if (actual.equals(expected)) { - equals = true; - } - } else { - equals = actual === expected; - } - } - - if (equals) { - throw new Error(message + " Actual: " + actual + " Not_Expected: " + expected); - } -} - -export function assertEqual(actual: T, expected: T, message: string = '') { - if (!types.isNullOrUndefined(actual) - && !types.isNullOrUndefined(expected) - && types.getClass(actual) === types.getClass(expected) - && types.isFunction(actual.equals)) { - - // Use the equals method - if (!actual.equals(expected)) { - throw new Error(`${message} Actual: <${actual}>(${typeof (actual)}). Expected: <${expected}>(${typeof (expected)})`); - } - } - else if (actual !== expected) { - throw new Error(`${message} Actual: <${actual}>(${typeof (actual)}). Expected: <${expected}>(${typeof (expected)})`); - } -} - -/** - * Assert two json like objects are deep equal. - */ -export function assertDeepEqual(actual, expected, message: string = '', path: any[] = []): void { - let typeofActual: string = typeof actual; - let typeofExpected: string = typeof expected; - if (typeofActual !== typeofExpected) { - throw new Error(message + ' ' + "At /" + path.join("/") + " types of actual " + typeofActual + " and expected " + typeofExpected + " differ."); - } else if (typeofActual === "object" || typeofActual === "array") { - if (expected instanceof Map) { - if (actual instanceof Map) { - expected.forEach((value, key) => { - if (actual.has(key)) { - assertDeepEqual(actual.get(key), value, message, path.concat([key])); - } else { - throw new Error(message + ' ' + "At /" + path.join("/") + " expected Map has key '" + key + "' but actual does not."); - } - }); - actual.forEach((value, key) => { - if (!expected.has(key)) { - throw new Error(message + ' ' + "At /" + path.join("/") + " actual Map has key '" + key + "' but expected does not."); - } - }); - } else { - throw new Error(message + ' ' + "At /" + path.join("/") + " expected is Map but actual is not."); - } - } - if (expected instanceof Set) { - if (actual instanceof Set) { - expected.forEach(i => { - if (!actual.has(i)) { - throw new Error(message + ' ' + "At /" + path.join("/") + " expected Set has item '" + i + "' but actual does not."); - } - }); - actual.forEach(i => { - if (!expected.has(i)) { - throw new Error(message + ' ' + "At /" + path.join("/") + " actual Set has item '" + i + "' but expected does not."); - } - }) - } else { - throw new Error(message + ' ' + "At /" + path.join("/") + " expected is Set but actual is not."); - } - } - for (let key in actual) { - if (!(key in expected)) { - throw new Error(message + ' ' + "At /" + path.join("/") + " found unexpected key " + key + "."); - } - assertDeepEqual(actual[key], expected[key], message, path.concat([key])); - } - for (let key in expected) { - if (!(key in actual)) { - throw new Error(message + ' ' + "At /" + path.join("/") + " expected a key " + key + "."); - } - } - } else if (actual !== expected) { - throw new Error(message + ' ' + "At /" + path.join("/") + " actual: '" + actual + "' and expected: '" + expected + "' differ."); - } -} - -export function assertDeepSuperset(actual, expected, path: any[] = []): void { - let typeofActual: string = typeof actual; - let typeofExpected: string = typeof expected; - if (typeofActual !== typeofExpected) { - throw new Error("At /" + path.join("/") + " types of actual " + typeofActual + " and expected " + typeofExpected + " differ."); - } else if (typeofActual === "object" || typeofActual === "array") { - for (let key in expected) { - if (!(key in actual)) { - throw new Error("At /" + path.join("/") + " expected a key " + key + "."); - } - assertDeepSuperset(actual[key], expected[key], path.concat([key])); - } - } else if (actual !== expected) { - throw new Error("At /" + path.join("/") + " actual: '" + actual + "' and expected: '" + expected + "' differ."); - } -} - -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) { - return false; - } - - 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 assertMatches(actual: string, expected: RegExp, message?: string) { - if (expected.test(actual) !== true) { - throw new Error(`"${actual}" doesn't match "${expected}". ${message}`); - } -} - -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); - } - - 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]); - } - } -} - -export function assertThrows(testFunc: () => void, assertMessage?: string, expectedMessage?: string) { - const re = expectedMessage ? new RegExp(`^${expectedMessage}$`) : null; - return assertThrowsRegExp(testFunc, assertMessage, re); -} - -export function assertThrowsRegExp(testFunc: () => void, assertMessage?: string, expectedMessage?: RegExp) { - let actualError: Error; - try { - testFunc(); - } catch (e) { - actualError = e; - } - - if (!actualError) { - throw new Error("Missing expected exception. " + assertMessage); - } - - if (expectedMessage && !expectedMessage.test(actualError.message)) { - throw new Error("Got unwanted exception. Actual error: " + actualError.message + " Expected to match: " + expectedMessage); - } -} - export function wait(seconds: number): void { waitUntilReady(() => false, seconds, false); } diff --git a/nativescript-core/testing/ui-helper.ts b/nativescript-core/testing/ui-helper.ts deleted file mode 100644 index 2457362f1..000000000 --- a/nativescript-core/testing/ui-helper.ts +++ /dev/null @@ -1,856 +0,0 @@ -import { Color } from "../color"; -import { FormattedString, Span } from "../text/formatted-string"; -import { ActionBar } from "../ui/action-bar"; -// TODO: Remove this and get it from global to decouple builder for angular -import { Builder } from "../ui/builder"; -import { Button } from "../ui/button"; -import { isIOS, unsetValue, View, ViewBase } from "../ui/core/view"; -import { Frame, NavigationEntry } from "../ui/frame"; -import { LayoutBase } from "../ui/layouts/layout-base"; -import { StackLayout } from "../ui/layouts/stack-layout"; -import { Page } from "../ui/page"; -import { TabView, TabViewItem } from "../ui/tab-view"; -import * as utils from "../utils/utils"; - -import * as TKUnit from "./tk-unit"; - -const DELTA = 0.1; - -export let ASYNC = 0.2; -export let MEMORY_ASYNC = 2; - -export function getColor(uiColor: UIColor): Color { - let redRef = new interop.Reference(); - let greenRef = new interop.Reference(); - let blueRef = new interop.Reference(); - let alphaRef = new interop.Reference(); - - uiColor.getRedGreenBlueAlpha(redRef, greenRef, blueRef, alphaRef); - let red = redRef.value * 255; - let green = greenRef.value * 255; - let blue = blueRef.value * 255; - let alpha = alphaRef.value * 255; - - return new Color(alpha, red, green, blue); -} - -export function clearPage(): void { - let newPage = getCurrentPage(); - if (!newPage) { - throw new Error("NO CURRENT PAGE!!!!"); - } - - newPage.style.backgroundColor = unsetValue; - newPage.style.color = unsetValue; - newPage.bindingContext = unsetValue; - newPage.className = unsetValue; - newPage.id = unsetValue; -} - -export function do_PageTest(test: (views: [Page, View, View, View, ActionBar]) => void, content: View, secondView: View, thirdView: View) { - clearPage(); - let newPage = getCurrentPage(); - newPage.content = content; - test([newPage, content, secondView, thirdView, newPage.actionBar]); - newPage.content = null; -} - -export function do_PageTest_WithButton(test: (views: [Page, Button, ActionBar]) => void) { - clearPage(); - let newPage = getCurrentPage(); - let btn = new Button(); - newPage.content = btn; - test([newPage, btn, newPage.actionBar]); - newPage.content = null; -} - -export function do_PageTest_WithStackLayout_AndButton(test: (views: [Page, StackLayout, Button, ActionBar]) => void) { - clearPage(); - let newPage = getCurrentPage(); - let stackLayout = new StackLayout(); - let btn = new Button(); - stackLayout.addChild(btn); - newPage.content = stackLayout; - test([newPage, stackLayout, btn, newPage.actionBar]); - newPage.content = null; -} - -export interface PageOptions { - pageCss?: any; - actionBar?: boolean; - actionBarFlat?: boolean; - actionBarHidden?: boolean; - tabBar?: boolean; -} - -export function buildUIAndRunTest(controlToTest: T, testFunction: (views: [T, Page]) => void, options?: PageOptions) { - clearPage(); - let newPage = getCurrentPage(); - - let testSubject = controlToTest as View; - - if (options) { - if (options.pageCss) { - newPage.css = options.pageCss; - } - - newPage.actionBarHidden = true; - newPage.actionBar.flat = false; - - if (options.actionBar) { - newPage.actionBarHidden = false; - newPage.actionBar.title = "Test ActionBar"; - } - - if (options.actionBarFlat) { - newPage.actionBarHidden = false; - newPage.actionBar.title = "Test ActionBar Flat"; - newPage.actionBar.flat = true; - } - - if (options.actionBarHidden) { - newPage.actionBarHidden = true; - } - - if (options.tabBar) { - const tabView = new TabView(); - const tabEntry = new TabViewItem(); - tabEntry.title = "Test"; - tabEntry.view = controlToTest; - tabView.items = [tabEntry]; - testSubject = tabView; - } - } - - newPage.content = testSubject; - - testFunction([controlToTest, newPage]); - newPage.content = null; - newPage.css = null; -} - -export function buildUIWithWeakRefAndInteract(createFunc: () => T, interactWithViewFunc?: (view: T) => void, done?) { - clearPage(); - const page = getCurrentPage(); - const weakRef = new WeakRef(createFunc()); - page.content = weakRef.get(); - if (interactWithViewFunc) { - interactWithViewFunc(weakRef.get()); - } - page.content = null; - // Give a change for native cleanup (e.g. keyboard close, etc.). - TKUnit.wait(0.001); - if (page.ios) { - /* tslint:disable:no-unused-expression */ - // Could cause GC on the next call. - // NOTE: Don't replace this with forceGC(); - new ArrayBuffer(4 * 1024 * 1024); - - // An additional GC and wait are needed since WebKit upgrade to version 12.0 - // (TEXT-FIELD.testMemoryLeak test started failing sporadically) - utils.GC(); - TKUnit.wait(0.1); - } - utils.GC(); - try { - TKUnit.assert(!weakRef.get(), weakRef.get() + " leaked!"); - done(null); - } - catch (ex) { - done(ex); - } -} - -export function navigateToModuleAndRunTest(moduleName, context, testFunction) { - let page = navigateToModule(moduleName, context); - testFunction(page); -} - -export function navigate(pageFactory: () => Page, navigationContext?: any): Page { - let entry: NavigationEntry = { create: pageFactory, animated: false, context: navigationContext, clearHistory: true }; - - return navigateWithEntry(entry); -} - -export function navigateWithHistory(pageFactory: () => Page, navigationContext?: any): Page { - let entry: NavigationEntry = { create: pageFactory, animated: false, context: navigationContext, clearHistory: false }; - - return navigateWithEntry(entry); -} - -export function navigateToModule(moduleName: string, context?: any): Page { - let entry: NavigationEntry = { moduleName: moduleName, context: context, animated: false, clearHistory: true }; - - return navigateWithEntry(entry); -} - -export function getCurrentPage(): Page { - return Frame.topmost().currentPage; -} - -export function getClearCurrentPage(): Page { - let page = Frame.topmost().currentPage; - page.style.backgroundColor = unsetValue; - page.style.color = unsetValue; - page.bindingContext = unsetValue; - page.className = unsetValue; - page.id = unsetValue; - page.css = ""; - - return page; -} - -export function waitUntilNavigatedTo(page: Page, action: Function) { - let completed = false; - function navigatedTo(args) { - args.object.page.off("navigatedTo", navigatedTo); - completed = true; - } - - page.on("navigatedTo", navigatedTo); - action(); - TKUnit.waitUntilReady(() => completed, 5); -} - -export function waitUntilNavigatedFrom(action: Function, topFrame?: Frame) { - const currentPage = topFrame ? topFrame.currentPage : Frame.topmost().currentPage; - let completed = false; - function navigatedFrom(args) { - args.object.page.off("navigatedFrom", navigatedFrom); - completed = true; - } - - currentPage.on("navigatedFrom", navigatedFrom); - action(); - TKUnit.waitUntilReady(() => completed); -} - -export function waitUntilLayoutReady(view: View): void { - TKUnit.waitUntilReady(() => view.isLayoutValid); -} - -export function navigateWithEntry(entry: NavigationEntry, topFrame?: Frame): Page { - const page = Builder.createViewFromEntry(entry) as Page; - entry.moduleName = null; - entry.create = function () { - return page; - }; - - waitUntilNavigatedFrom(() => topFrame ? topFrame.navigate(entry) : Frame.topmost().navigate(entry)); - - return page; -} - -export function goBack(topFrame?: Frame) { - waitUntilNavigatedFrom(() => topFrame ? topFrame.goBack() : Frame.topmost().goBack()); -} - -export function assertAreClose(actual: number, expected: number, message: string): void { - const density = utils.layout.getDisplayDensity(); - const delta = Math.floor(density) !== density ? 1.1 : DELTA; - - TKUnit.assertAreClose(actual, expected, delta, message); -} - -export function assertViewColor(testView: View, hexColor: string) { - TKUnit.assert(testView.style.color, "Color property not applied correctly. Style value is not defined."); - TKUnit.assertEqual(testView.style.color.hex, hexColor, "color property"); -} - -export function assertViewBackgroundColor(testView: ViewBase, hexColor: string) { - TKUnit.assert(testView.style.backgroundColor, "Background color property not applied correctly. Style value is not defined."); - TKUnit.assertEqual(testView.style.backgroundColor.hex, hexColor, "backgroundColor property"); -} - -export function assertTabSelectedTabTextColor(testView: ViewBase, hexColor: string) { - TKUnit.assert(testView.style.selectedTabTextColor, "selectedTabTextColor property not applied correctly. Style value is not defined."); - TKUnit.assertEqual(testView.style.selectedTabTextColor.hex, hexColor, "selectedTabTextColor property not applied correctly"); -} - -export function forceGC() { - if (isIOS) { - /* tslint:disable:no-unused-expression */ - // Could cause GC on the next call. - new ArrayBuffer(4 * 1024 * 1024); - } - - utils.GC(); - TKUnit.wait(0.001); -} - -export function _generateFormattedString(): FormattedString { - let formattedString = new FormattedString(); - let span: Span; - - span = new Span(); - span.fontFamily = "serif"; - span.fontSize = 10; - span.fontWeight = "bold"; - span.color = new Color("red"); - span.backgroundColor = new Color("blue"); - span.textDecoration = "line-through"; - span.text = "Formatted"; - formattedString.spans.push(span); - - span = new Span(); - span.fontFamily = "sans-serif"; - span.fontSize = 20; - span.fontStyle = "italic"; - span.color = new Color("green"); - span.backgroundColor = new Color("yellow"); - span.textDecoration = "underline"; - span.text = "Text"; - formattedString.spans.push(span); - - return formattedString; -} - -export function nativeView_recycling_test(createNew: () => View, createLayout?: () => LayoutBase, nativeGetters?: Map any>, customSetters?: Map) { - return; - - // if (isIOS) { - // // recycling not implemented yet. - // return; - // } - - // setupSetters(); - // const page = getClearCurrentPage(); - // let layout: LayoutBase = new FlexboxLayout(); - // if (createLayout) { - // // This is done on purpose. We need the constructor of Flexbox - // // to run otherwise some module fileds stays uninitialized. - // layout = createLayout(); - // } - - // page.content = layout; - - // const first = createNew(); - // const test = createNew(); - - // // Make sure we are not reusing a native views. - // first.recycleNativeView = "never"; - // test.recycleNativeView = "never"; - - // page.content = layout; - - // layout.addChild(test); - - // setValue(test.style, cssSetters); - // setValue(test, setters, customSetters); - // // Needed so we can reset formattedText - // test["secure"] = false; - - // const nativeView = test.nativeViewProtected; - // // Mark so we reuse the native views. - // test.recycleNativeView = "always"; - // layout.removeChild(test); - // const newer = createNew(); - // newer.recycleNativeView = "always"; - // layout.addChild(newer); - // layout.addChild(first); - - // if (first.typeName !== "SearchBar") { - // // There are way too many differences in native methods for search-bar. - // // There are too many methods that just throw for newly created views in API lvl 19 and 17 - // if (sdkVersion < 21) { - // TKUnit.waitUntilReady(() => layout.isLayoutValid); - // } - - // compareUsingReflection(newer, first); - // } - - // TKUnit.assertEqual(newer.nativeViewProtected, nativeView, "nativeView not reused."); - // checkDefaults(newer, first, props, nativeGetters || defaultNativeGetters); - // checkDefaults(newer, first, styleProps, nativeGetters || defaultNativeGetters); - - // layout.removeChild(newer); - // layout.removeChild(first); -} - -// function compareUsingReflection(recycledNativeView: View, newNativeView: View): void { -// const recycled: android.view.View = recycledNativeView.nativeViewProtected; -// const newer: android.view.View = newNativeView.nativeViewProtected; -// TKUnit.assertNotEqual(recycled, newer); -// const methods = newer.getClass().getMethods(); -// for (let i = 0, length = methods.length; i < length; i++) { -// const method = methods[i]; -// const returnType = method.getReturnType(); -// const name = method.getName(); -// const skip = name.includes("ViewId") -// || name.includes("Accessibility") -// || name.includes("hashCode") -// || name === "getId" -// || name === "hasFocus" -// || name === "isDirty" -// || name === "getLeft" -// || name === "getTop" -// || name === "getRight" -// || name === "getBottom" -// || name === "getWidth" -// || name === "getHeight" -// || name === "getX" -// || name === "getY" -// || name.includes("getMeasured") -// || name === "toString"; - -// if (skip || method.getParameterTypes().length > 0) { -// continue; -// } - -// if ((java).lang.Comparable.class.isAssignableFrom(returnType)) { -// const defValue = method.invoke(newer, null); -// const currValue = method.invoke(recycled, null); -// if (defValue !== currValue && defValue.compareTo(currValue) !== 0) { -// throw new Error(`Actual: ${currValue}, Expected: ${defValue}, for method: ${method.getName()}`); -// } -// } else if (java.lang.String.class === returnType || -// java.lang.Character.class === returnType || -// (java).lang.CharSequence.class === returnType || -// returnType === java.lang.Byte.TYPE || -// returnType === java.lang.Double.TYPE || -// returnType === java.lang.Float.TYPE || -// returnType === java.lang.Integer.TYPE || -// returnType === java.lang.Long.TYPE || -// returnType === java.lang.Short.TYPE || -// returnType === java.lang.Boolean.TYPE) { - -// const defValue = method.invoke(newer, null); -// const currValue = method.invoke(recycled, null); -// if ((currValue + "") !== (defValue + "")) { -// throw new Error(`Actual: ${currValue}, Expected: ${defValue}, for method: ${method.getName()}`); -// } -// } -// } -// } - -// function checkDefaults(newer: View, first: View, props: Array, nativeGetters: Map any>): void { -// props.forEach(prop => { -// const name = (prop).name; -// if (nativeGetters.has(name)) { -// const getter = nativeGetters.get(name); -// TKUnit.assertDeepEqual(getter(newer), getter(first), name); -// } else if (newer[prop.getDefault]) { -// TKUnit.assertDeepEqual(newer[prop.getDefault](), first[prop.getDefault](), name); -// } else if (newer[prop.setNative]) { -// console.log(`Type: ${newer.typeName} has no getter for ${name} property.`) -// } -// }); -// } - -// function setValue(object: Object, setters: Map, customSetters?: Map): void { -// setters.forEach((value1, key) => { -// let value = customSetters && customSetters.has(key) ? customSetters.get(key) : value1; -// const currentValue = object[key]; -// if (currentValue === value) { -// if (value === "horizontal" && key === "orientation") { -// // wrap-layout.orientation default value is 'horizontal' -// value = "vertical"; -// } else if (value === 2) { -// value = 3; -// } -// } - -// object[key] = value; -// const newValue = object[key]; -// TKUnit.assertNotEqual(newValue, currentValue, `${object} setting ${key} should change current value.`); -// }); -// } - -// function setupSetters(): void { -// if (setters) { -// return; -// } - -// setters = new Map(); -// // view-base -// setters.set("id", "someId"); -// setters.set("className", "someClassName"); -// setters.set("bindingContext", "someBindingContext"); - -// // view -// setters.set("automationText", "automationText"); -// setters.set("originX", 0.2); -// setters.set("originY", 0.2); -// setters.set("isEnabled", false); -// setters.set("isUserInteractionEnabled", false); - -// // action-bar -// setters.set("title", "title"); -// setters.set("text", "text"); -// setters.set("icon", "~/logo.png"); -// setters.set("visibility", "collapse"); - -// // activity-indicator -// setters.set("busy", true); - -// // date-picker -// setters.set("year", "2010"); -// setters.set("month", "2"); -// setters.set("day", "2"); -// setters.set("maxDate", "2100"); -// setters.set("minDate", "2000"); -// setters.set("date", new Date(2011, 3, 3)); - -// // editable-text -// setters.set("keyboardType", "datetime"); -// setters.set("returnKeyType", "done"); -// setters.set("editable", false); -// setters.set("updateTextTrigger", "focusLost"); -// setters.set("autocapitalizationType", "words"); -// setters.set("autocorrect", true); -// setters.set("hint", "hint"); -// setters.set("maxLength", "10"); - -// // html-view -// setters.set("html", ""); - -// // image-view -// setters.set("imageSource", ""); -// setters.set("src", ""); -// setters.set("loadMode", "async"); -// setters.set("isLoading", true); -// setters.set("stretch", "none"); - -// // layout-base -// setters.set("clipToBounds", false); - -// // absolute-layout -// setters.set("left", "20"); -// setters.set("top", "20"); - -// // dock-layout -// setters.set("dock", "top"); -// setters.set("stretchLastChild", false); - -// // grid-layout props -// setters.set("row", "1"); -// setters.set("rowSpan", "2"); -// setters.set("col", "1"); -// setters.set("colSpan", "2"); - -// // stack-layout -// setters.set("orientation", "horizontal"); - -// // wrap-layout -// // custom orientation value -// // setters.set('orientation', 'vertical'); -// setters.set("itemWidth", "50"); -// setters.set("itemHeight", "50"); - -// // list-picker -// setters.set("items", ["1", "2", "3"]); -// setters.set("selectedIndex", "1"); - -// // list-view -// setters.set("items", ["1", "2", "3"]); -// setters.set("itemTemplate", "