Merge remote-tracking branch 'origin/master' into merge-release-in-master

This commit is contained in:
SvetoslavTsenov
2019-01-17 15:49:32 +02:00
77 changed files with 879 additions and 274 deletions

View File

@@ -279,7 +279,7 @@ if (a) return "winning";
```TypeScript
if(condition) {
if (condition) {
console.log("winning");
}
@@ -293,15 +293,15 @@ if (!condition) {
```TypeScript
if(condition === true) {
if (condition === true) {
console.log("losing");
}
if(condition !== true) {
if (condition !== true) {
console.log("losing");
}
if(condition !== false) {
if (condition !== false) {
console.log("losing");
}
@@ -314,7 +314,7 @@ Do not use the **Yoda Conditions** when writing boolean expressions:
```TypeScript
let num;
if(num >= 0) {
if (num >= 0) {
console.log("winning");
}
```
@@ -323,14 +323,14 @@ if(num >= 0) {
```TypeScript
let num;
if(0 <= num) {
if (0 <= num) {
console.log("losing");
}
```
**NOTE** It is OK to use constants on the left when comparing for a range.
```TypeScript
if(0 <= num && num <= 100) {
if (0 <= num && num <= 100) {
console.log("winning");
}
```

View File

@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2015-2018 Progress Software Corporation
Copyright (c) 2015-2019 Progress Software Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@@ -81,6 +81,13 @@ application.on(application.uncaughtErrorEvent, function(args: application.Unhand
console.log("### stack: " + args.error.stack);
});
application.on(application.discardedErrorEvent, function(args: application.DiscardedErrorEventData) {
console.log("### [Discarded] NativeScriptError: " + args.error);
console.log("### [Discarded] nativeException: " + (<any>args.error).nativeException);
console.log("### [Discarded] stackTrace: " + (<any>args.error).stackTrace);
console.log("### [Discarded] stack: " + args.error.stack);
});
application.setCssFileName("ui-tests-app/app.css");
application.start({ moduleName: "ui-tests-app/main-page" });

View File

@@ -1,4 +1,4 @@
--timeout 80000
--timeout 300000
--recursive e2e
--reporter mocha-multi
--reporter-options spec=-,mocha-junit-reporter=test-results.xml

View File

@@ -1,5 +1,5 @@
import { AppiumDriver, createDriver, SearchOptions } from "nativescript-dev-appium";
import { Screen } from "./screen"
import { Screen, driverDefaultWaitTime, elementDefaultWaitTimeInSeconds } from "./screen"
import { assert } from "chai";
const exampleAndroidBackBtnEvents = "Android Back Btn Events";
@@ -10,6 +10,7 @@ describe("android-navigate-back", () => {
before(async () => {
driver = await createDriver();
driver.defaultWaitTime = driverDefaultWaitTime;
screen = new Screen(driver);
const btnShowNestedModalFrame = await driver.findElementByText(exampleAndroidBackBtnEvents);
await btnShowNestedModalFrame.click();
@@ -31,7 +32,7 @@ describe("android-navigate-back", () => {
}
await driver.navBack();
const textElement = await driver.findElementsByText("will cancel next back press: false", SearchOptions.contains, 10);
const textElement = await driver.findElementsByText("will cancel next back press: false", SearchOptions.contains, elementDefaultWaitTimeInSeconds);
assert.isTrue(textElement !== null);
await driver.navBack();
await screen.loadedHome();

View File

@@ -1,5 +1,5 @@
import { AppiumDriver, createDriver } from "nativescript-dev-appium";
import { Screen } from "./screen"
import { Screen, driverDefaultWaitTime } from "./screen"
import {
roots,
modalFrameBackground,
@@ -17,6 +17,7 @@ describe("modal-frame:", () => {
before(async () => {
driver = await createDriver();
driver.defaultWaitTime = driverDefaultWaitTime;
screen = new Screen(driver);
});

View File

@@ -1,5 +1,5 @@
import { AppiumDriver, createDriver } from "nativescript-dev-appium";
import { Screen } from "./screen"
import { Screen, driverDefaultWaitTime } from "./screen"
import {
roots,
modalFrameBackground,
@@ -17,6 +17,7 @@ describe("modal-layout:", () => {
before(async () => {
driver = await createDriver();
driver.defaultWaitTime = driverDefaultWaitTime;
screen = new Screen(driver);
});

View File

@@ -1,5 +1,5 @@
import { AppiumDriver, createDriver } from "nativescript-dev-appium";
import { Screen } from "./screen"
import { Screen, driverDefaultWaitTime } from "./screen"
import {
roots,
modalPageBackground,
@@ -16,6 +16,7 @@ describe("modal-page:", () => {
before(async () => {
driver = await createDriver();
driver.defaultWaitTime = driverDefaultWaitTime;
screen = new Screen(driver);
});

View File

@@ -1,5 +1,5 @@
import { AppiumDriver, createDriver } from "nativescript-dev-appium";
import { Screen } from "./screen"
import { Screen, driverDefaultWaitTime } from "./screen"
import {
roots,
modalFrameBackground,
@@ -19,6 +19,7 @@ describe("modal-tab:", () => {
before(async () => {
driver = await createDriver();
driver.defaultWaitTime = driverDefaultWaitTime;
screen = new Screen(driver);
});

View File

@@ -28,6 +28,9 @@ const closeModalNested = "Close Modal Nested";
const closeModal = "Close Modal";
const goBack = "Go Back";
export const driverDefaultWaitTime = 10000;
export const elementDefaultWaitTimeInSeconds = 10;
export class Screen {
private _driver: AppiumDriver
@@ -37,30 +40,30 @@ export class Screen {
}
loadedHome = async () => {
const lblHome = await this._driver.findElementByText(home);
const lblHome = await this._driver.waitForElement(home);
assert.isTrue(await lblHome.isDisplayed());
console.log(home + " loaded!");
}
resetFrameRootView = async () => {
console.log("Setting frame root ...");
const btnResetFrameRootView = await this._driver.findElementByText(resetFrameRootView);
const btnResetFrameRootView = await this._driver.waitForElement(resetFrameRootView);
await btnResetFrameRootView.tap();
}
resetLayoutRootView = async () => {
console.log("Setting layout root ...");
const btnResetLayoutRootView = await this._driver.findElementByText(resetLayoutRootView);
const btnResetLayoutRootView = await this._driver.waitForElement(resetLayoutRootView);
await btnResetLayoutRootView.tap();
}
resetTabRootView = async () => {
const btnResetTabRootView = await this._driver.findElementByText(resetTabRootView);
const btnResetTabRootView = await this._driver.waitForElement(resetTabRootView);
await btnResetTabRootView.tap();
}
loadedTabRootView = async () => {
const tabFirst = await this._driver.findElementByText(first);
const tabFirst = await this._driver.waitForElement(first);
assert.isTrue(await tabFirst.isDisplayed());
console.log("Tab root view loaded!");
}
@@ -89,29 +92,29 @@ export class Screen {
}
showModalFrame = async () => {
const btnModalFrame = await this._driver.findElementByText(modalFrame);
const btnModalFrame = await this._driver.waitForElement(modalFrame);
await btnModalFrame.tap();
}
loadedModalFrame = async () => {
const lblModal = await this._driver.findElementByText(modal);
const lblModal = await this._driver.waitForElement(modal);
assert.isTrue(await lblModal.isDisplayed());
console.log(modal + " loaded!");
}
showModalPage = async () => {
const btnModalPage = await this._driver.findElementByText(modalPage);
const btnModalPage = await this._driver.waitForElement(modalPage);
await btnModalPage.tap();
}
loadedModalPage = async () => {
const btnShowNestedModalPage = await this._driver.findElementByText(showNestedModalPage);
const btnShowNestedModalPage = await this._driver.waitForElement(showNestedModalPage);
assert.isTrue(await btnShowNestedModalPage.isDisplayed());
console.log("Modal Page loaded!");
}
showModalLayout = async () => {
const btnModalLayout = await this._driver.findElementByText(modalLayout);
const btnModalLayout = await this._driver.waitForElement(modalLayout);
await btnModalLayout.tap();
}
@@ -120,99 +123,99 @@ export class Screen {
}
showModalTabView = async () => {
const btnModalTabView = await this._driver.findElementByText(modalTabView);
const btnModalTabView = await this._driver.waitForElement(modalTabView);
await btnModalTabView.tap();
}
loadedModalTabView = async () => {
const itemModalFirst = await this._driver.findElementByText(modalFirst);
const itemModalFirst = await this._driver.waitForElement(modalFirst);
assert.isTrue(await itemModalFirst.isDisplayed());
console.log("Modal TabView loaded!");
}
navigateToSecondPage = async () => {
const btnNavToSecondPage = await this._driver.findElementByText(navToSecondPage);
const btnNavToSecondPage = await this._driver.waitForElement(navToSecondPage);
await btnNavToSecondPage.tap();
}
showDialogConfirm = async () => {
const btnShowDialogConfirm = await this._driver.findElementByText(showDialog);
const btnShowDialogConfirm = await this._driver.waitForElement(showDialog);
await btnShowDialogConfirm.tap();
}
navigateToFirstItem = async () => {
const itemModalFirst = await this._driver.findElementByText(modalFirst);
const itemModalFirst = await this._driver.waitForElement(modalFirst);
await itemModalFirst.tap();
}
navigateToSecondItem = async () => {
const itemModalSecond = await this._driver.findElementByText(modalSecond);
const itemModalSecond = await this._driver.waitForElement(modalSecond);
await itemModalSecond.tap();
}
loadedConfirmDialog = async () => {
const lblDialogMessage = await this._driver.findElementByText(confirmDialogMessage);
const lblDialogMessage = await this._driver.waitForElement(confirmDialogMessage);
assert.isTrue(await lblDialogMessage.isDisplayed());
console.log(dialogConfirm + " shown!");
}
loadedSecondPage = async () => {
const lblModalSecond = await this._driver.findElementByText(modalSecond);
const lblModalSecond = await this._driver.waitForElement(modalSecond);
assert.isTrue(await lblModalSecond.isDisplayed());
console.log(modalSecond + " loaded!");
}
loadedFirstItem = async () => {
const lblModal = await this._driver.findElementByText(modal);
const lblModal = await this._driver.waitForElement(modal);
assert.isTrue(await lblModal.isDisplayed());
console.log("First Item loaded!");
}
loadedSecondItem = async () => {
const btnGoBack = await this._driver.findElementByText(goBack);
const btnGoBack = await this._driver.waitForElement(goBack);
assert.isTrue(await btnGoBack.isDisplayed());
console.log("Second Item loaded!");
}
closeDialog = async () => {
const btnYesDialog = await this._driver.findElementByText(confirmDialog);
const btnYesDialog = await this._driver.waitForElement(confirmDialog);
await btnYesDialog.tap();
}
goBackFromSecondPage = async () => {
const btnGoBackFromSecondPage = await this._driver.findElementByText(goBack);
const btnGoBackFromSecondPage = await this._driver.waitForElement(goBack);
await btnGoBackFromSecondPage.tap();
}
showNestedModalFrame = async () => {
const btnShowNestedModalFrame = await this._driver.findElementByText(showNestedModalFrame);
const btnShowNestedModalFrame = await this._driver.waitForElement(showNestedModalFrame);
await btnShowNestedModalFrame.tap();
}
loadedNestedModalFrame = async () => {
const lblModalNested = await this._driver.findElementByText(modalNested);
const lblModalNested = await this._driver.waitForElement(modalNested);
assert.isTrue(await lblModalNested.isDisplayed());
console.log(modalNested + " loaded!");
}
closeModalNested = async () => {
const btnCloseNestedModal = await this._driver.findElementByText(closeModalNested);
const btnCloseNestedModal = await this._driver.waitForElement(closeModalNested);
await btnCloseNestedModal.tap();
}
showNestedModalPage = async () => {
const btnShowNestedModalPage = await this._driver.findElementByText(showNestedModalPage);
const btnShowNestedModalPage = await this._driver.waitForElement(showNestedModalPage);
await btnShowNestedModalPage.tap();
}
loadedNestedModalPage = async () => {
const btnCloseModalNested = await this._driver.findElementByText(closeModalNested);
const btnCloseModalNested = await this._driver.waitForElement(closeModalNested);
assert.isTrue(await btnCloseModalNested.isDisplayed());
console.log(closeModalNested + " loaded!");
}
closeModal = async () => {
const btnCloseModal = await this._driver.findElementByText(closeModal);
const btnCloseModal = await this._driver.waitForElement(closeModal);
await btnCloseModal.tap();
}

View File

@@ -21,7 +21,7 @@ export function onNavigateSlide(args: EventData) {
animated: true,
transition: {
name: "slide",
duration: 380,
duration: 300,
curve: "easeIn"
}
});
@@ -34,7 +34,7 @@ export function onNavigateFlip(args: EventData) {
animated: true,
transition: {
name: "flip",
duration: 380,
duration: 300,
curve: "easeIn"
}
});

View File

@@ -21,7 +21,7 @@ export function onNavigateSlide(args: EventData) {
animated: true,
transition: {
name: "slide",
duration: 380,
duration: 300,
curve: "easeIn"
}
});
@@ -34,7 +34,7 @@ export function onNavigateFlip(args: EventData) {
animated: true,
transition: {
name: "flip",
duration: 380,
duration: 300,
curve: "easeIn"
}
});

View File

@@ -1,4 +1,4 @@
<Page codeFile="~/frame-root/frame-home-page" xmlns="http://schemas.nativescript.org/tns.xsd" class="page">
<Page xmlns="http://schemas.nativescript.org/tns.xsd" class="page">
<ActionBar class="action-bar">
<NavigationButton text="frameHomeBack" tap="onBackButtonTap" android.systemIcon="ic_menu_back" />

View File

@@ -3,11 +3,11 @@ import { EventData } from "tns-core-modules/ui/core/view";
import { Button } from "tns-core-modules/ui/button";
export function onNavigateToLayoutFrame(args: EventData) {
application._resetRootView({ moduleName: "layout-root/layout-root-frame" });
application._resetRootView({ moduleName: "layout-root/layout-frame-root" });
}
export function onNavigateToLayoutMultiFrame(args: EventData) {
application._resetRootView({ moduleName: "layout-root/layout-root-multi-frame" });
application._resetRootView({ moduleName: "layout-root/layout-multi-frame-root" });
}
export function onNavigateToPageFrame(args: EventData) {
@@ -31,9 +31,9 @@ export function onNavigateToTabsBottomPage(args: EventData) {
}
export function onNavigateToTabsTopRoot(args: EventData) {
application._resetRootView({ moduleName: "tab-root/tab-root-top" });
application._resetRootView({ moduleName: "tab-root/tab-top-root" });
}
export function onNavigateToTabsBottomRoot(args: EventData) {
application._resetRootView({ moduleName: "tab-root/tab-root-bottom" });
application._resetRootView({ moduleName: "tab-root/tab-bottom-root" });
}

View File

@@ -10,9 +10,9 @@
<Button text="Layout w/ multi frame" tap="onNavigateToLayoutMultiFrame" />
<Button text="Page w/ frame" tap="onNavigateToPageFrame" />
<Button text="Page w/ multi frame" tap="onNavigateToPageMultiFrame" />
<Button ios:visibility="collapsed" text="Page w/ tabs (top)" tap="onNavigateToTabsTopPage" />
<Button text="Page w/ tabs (top)" tap="onNavigateToTabsTopPage" />
<Button text="Page w/ tabs (bottom)" tap="onNavigateToTabsBottomPage" />
<Button ios:visibility="collapsed" text="Root tabs (top)" tap="onNavigateToTabsTopRoot" />
<Button text="Root tabs (top)" tap="onNavigateToTabsTopRoot" />
<Button text="Root tabs (bottom)" tap="onNavigateToTabsBottomRoot" />
</StackLayout>
</GridLayout>

View File

@@ -21,7 +21,7 @@ export function onNavigateSlide(args: EventData) {
animated: true,
transition: {
name: "slide",
duration: 380,
duration: 300,
curve: "easeIn"
}
});
@@ -34,7 +34,7 @@ export function onNavigateFlip(args: EventData) {
animated: true,
transition: {
name: "flip",
duration: 380,
duration: 300,
curve: "easeIn"
}
});

View File

@@ -21,7 +21,7 @@ export function onNavigateSlide(args: EventData) {
animated: true,
transition: {
name: "slide",
duration: 380,
duration: 300,
curve: "easeIn"
}
});
@@ -34,7 +34,7 @@ export function onNavigateFlip(args: EventData) {
animated: true,
transition: {
name: "flip",
duration: 380,
duration: 300,
curve: "easeIn"
}
});

View File

@@ -1,4 +1,4 @@
<GridLayout codeFile="~/layout-root/layout-root-frame" rows="auto, *, *">
<GridLayout rows="auto, *, *">
<Button text="reset app" tap="onReset" />
<GridLayout row="1">
<Frame defaultPage="layout-root/layout-home-page" />

View File

@@ -38,14 +38,14 @@ export function onItemTap(args: ItemEventData) {
case "slide":
entry.transition = {
name: "slide",
duration: 380,
duration: 300,
curve: "easeIn"
};
break;
case "flip":
entry.transition = {
name: "flip",
duration: 380,
duration: 300,
curve: "easeIn"
};
break;

View File

@@ -0,0 +1,46 @@
import { EventData } from "tns-core-modules/ui/page";
import { Button } from "tns-core-modules/ui/button";
export function onNavigate(args: EventData) {
const button = <Button>args.object;
button.page.frame.navigate("some-page/some-page");
}
export function onNavigateNone(args: EventData) {
const button = <Button>args.object;
button.page.frame.navigate({
moduleName: "some-page/some-page",
animated: false
});
}
export function onNavigateSlide(args: EventData) {
const button = <Button>args.object;
button.page.frame.navigate({
moduleName: "some-page/some-page",
animated: true,
transition: {
name: "slide",
duration: 300,
curve: "easeIn"
}
});
}
export function onNavigateFlip(args: EventData) {
const button = <Button>args.object;
button.page.frame.navigate({
moduleName: "some-page/some-page",
animated: true,
transition: {
name: "flip",
duration: 300,
curve: "easeIn"
}
});
}
export function onBackButtonTap(args: EventData): void {
const button = <Button>args.object;
button.page.frame.goBack();
}

View File

@@ -1,4 +1,4 @@
<Page codeFile="~/tab-page/tabs-page" xmlns="http://schemas.nativescript.org/tns.xsd" class="page">
<Page xmlns="http://schemas.nativescript.org/tns.xsd" class="page">
<ActionBar class="action-bar">
<NavigationButton text="tabBottomBack" tap="onBackButtonTap" android.systemIcon="ic_menu_back" />

View File

@@ -0,0 +1,46 @@
import { EventData } from "tns-core-modules/ui/page";
import { Button } from "tns-core-modules/ui/button";
export function onNavigate(args: EventData) {
const button = <Button>args.object;
button.page.frame.navigate("some-page/some-page");
}
export function onNavigateNone(args: EventData) {
const button = <Button>args.object;
button.page.frame.navigate({
moduleName: "some-page/some-page",
animated: false
});
}
export function onNavigateSlide(args: EventData) {
const button = <Button>args.object;
button.page.frame.navigate({
moduleName: "some-page/some-page",
animated: true,
transition: {
name: "slide",
duration: 300,
curve: "easeIn"
}
});
}
export function onNavigateFlip(args: EventData) {
const button = <Button>args.object;
button.page.frame.navigate({
moduleName: "some-page/some-page",
animated: true,
transition: {
name: "flip",
duration: 300,
curve: "easeIn"
}
});
}
export function onBackButtonTap(args: EventData): void {
const button = <Button>args.object;
button.page.frame.goBack();
}

View File

@@ -1,4 +1,4 @@
<Page codeFile="~/tab-page/tabs-page" xmlns="http://schemas.nativescript.org/tns.xsd" class="page">
<Page xmlns="http://schemas.nativescript.org/tns.xsd" class="page">
<ActionBar class="action-bar">
<NavigationButton text="tabTopBack" tap="onBackButtonTap" android.systemIcon="ic_menu_back" />
@@ -13,16 +13,18 @@
<Button text="navigate to some page (flip transition)" tap="onNavigateFlip" />
</StackLayout>
<TabView row="1" androidTabsPosition="top">
<TabViewItem title="Players">
<Frame defaultPage="players/players-items-page" />
</TabViewItem>
<TabViewItem title="Dummy">
<Label text="this is a tab" />
</TabViewItem>
<TabViewItem title="Teams">
<Frame defaultPage="teams/teams-items-page" />
</TabViewItem>
</TabView>
<GridLayout row="1">
<TabView androidTabsPosition="top">
<TabViewItem title="Players">
<Frame defaultPage="players/players-items-page" />
</TabViewItem>
<TabViewItem title="Dummy">
<Label text="this is a tab" />
</TabViewItem>
<TabViewItem title="Teams">
<Frame defaultPage="teams/teams-items-page" />
</TabViewItem>
</TabView>
</GridLayout>
</GridLayout>
</Page>

View File

@@ -0,0 +1,5 @@
import * as application from "tns-core-modules/application";
export function onReset() {
application._resetRootView({ moduleName: "app-root" });
}

View File

@@ -1,4 +1,4 @@
<TabView codeFile="~/tab-root/tab-root" androidTabsPosition="bottom">
<TabView androidTabsPosition="bottom">
<TabViewItem title="Players">
<GridLayout rows="auto, auto, *">
<Label text="tab root bottom home" />

View File

@@ -0,0 +1,5 @@
import * as application from "tns-core-modules/application";
export function onReset() {
application._resetRootView({ moduleName: "app-root" });
}

View File

@@ -1,4 +1,4 @@
<TabView codeFile="~/tab-root/tab-root" androidTabsPosition="top">
<TabView androidTabsPosition="top">
<TabViewItem title="Players">
<GridLayout rows="auto, auto, *">
<Label text="tab root top home" />

View File

@@ -38,14 +38,14 @@ export function onItemTap(args: ItemEventData) {
case "slide":
entry.transition = {
name: "slide",
duration: 380,
duration: 300,
curve: "easeIn"
};
break;
case "flip":
entry.transition = {
name: "flip",
duration: 380,
duration: 300,
curve: "easeIn"
};
break;

View File

@@ -1,4 +1,5 @@
export const suspendTime = 1;
export const appSuspendResume = true;
export const dontKeepActivities = true;
export const transitions = ["Default", "None", "Slide", "Flip"];
// TODO: restore "slide" when https://github.com/NativeScript/NativeScript/issues/6728 is fixed
export const transitions = ["Default", "None", /*"Slide", */"Flip"];

View File

@@ -1,5 +1,6 @@
import { AppiumDriver, createDriver } from "nativescript-dev-appium";
import { Screen, playersData, home, somePage, teamsData } from "./screen";
import { Screen, playersData, home, somePage, teamsData, driverDefaultWaitTime } from "./screen";
import * as shared from "./shared.e2e-spec";
import { suspendTime, appSuspendResume, dontKeepActivities, transitions } from "./config";
@@ -13,6 +14,8 @@ describe("frame-root:", () => {
if (dontKeepActivities) {
await driver.setDontKeepActivities(true);
}
driver.defaultWaitTime = driverDefaultWaitTime;
});
after(async () => {
@@ -76,7 +79,12 @@ describe("frame-root:", () => {
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayersList();
});
@@ -95,7 +103,12 @@ describe("frame-root:", () => {
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayerDetails(playerTwo);
await screen.goBackToPlayersList();
@@ -152,7 +165,12 @@ describe("frame-root:", () => {
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayersList();
});
@@ -171,7 +189,12 @@ describe("frame-root:", () => {
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayerDetails(playerTwo);
await screen.loadedTeamsList(); // assert visible & no changes
@@ -206,7 +229,11 @@ describe("frame-root:", () => {
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
if (appSuspendResume) {
await driver.backgroundApp(suspendTime);
@@ -230,7 +257,12 @@ describe("frame-root:", () => {
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayerDetails(playerTwo); // assert no changes after back navigation
await screen.loadedTeamDetails(teamTwo);

View File

@@ -1,8 +1,10 @@
import { AppiumDriver, createDriver } from "nativescript-dev-appium";
import { Screen, playersData, somePage, teamsData } from "./screen";
import { Screen, playersData, somePage, teamsData, driverDefaultWaitTime } from "./screen";
import * as shared from "./shared.e2e-spec";
import { suspendTime, appSuspendResume, dontKeepActivities, transitions } from "./config";
// NOTE: TabTop is Android only scenario (for iOS we will essentially execute 2x TabBottom)
const roots = ["TabTop", "TabBottom"];
function hyphenate(s: string) {
@@ -19,6 +21,8 @@ describe("frame-tab-root:", () => {
if (dontKeepActivities) {
await driver.setDontKeepActivities(true);
}
driver.defaultWaitTime = driverDefaultWaitTime;
});
after(async () => {
@@ -88,7 +92,12 @@ describe("frame-tab-root:", () => {
await driver.waitForElement(somePage) // wait for some page
}
await driver.navBack();
if (driver.isAndroid) {
await driver.navBack();
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayersList();
});
@@ -107,7 +116,12 @@ describe("frame-tab-root:", () => {
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack();
if (driver.isAndroid) {
await driver.navBack();
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayerDetails(playerTwo);
await screen.goBackToPlayersList();
@@ -151,7 +165,11 @@ describe("frame-tab-root:", () => {
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack();
if (driver.isAndroid) {
await driver.navBack();
} else {
await screen.goBackFromSomePage();
}
if (appSuspendResume) {
await driver.backgroundApp(suspendTime);
@@ -183,7 +201,11 @@ describe("frame-tab-root:", () => {
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack();
if (driver.isAndroid) {
await driver.navBack();
} else {
await screen.goBackFromSomePage();
}
if (appSuspendResume) {
await driver.backgroundApp(suspendTime);

View File

@@ -1,5 +1,6 @@
import { AppiumDriver, createDriver } from "nativescript-dev-appium";
import { Screen, playersData, home, somePage, otherPage, teamsData } from "./screen";
import { Screen, playersData, home, somePage, otherPage, teamsData, driverDefaultWaitTime } from "./screen";
import * as shared from "./shared.e2e-spec";
import { suspendTime, appSuspendResume, dontKeepActivities, transitions } from "./config";
@@ -13,6 +14,8 @@ describe("layout-root:", () => {
if (dontKeepActivities) {
await driver.setDontKeepActivities(true);
}
driver.defaultWaitTime = driverDefaultWaitTime;
});
after(async () => {
@@ -39,16 +42,16 @@ describe("layout-root:", () => {
it("loaded home page", async () => {
await screen.loadedHome();
});
it("loaded layout root with nested frames", async () => {
await screen.navigateToLayoutWithFrame();
await screen.loadedLayoutWithFrame();
});
it("loaded players list", async () => {
await screen.loadedPlayersList();
});
it("loaded player details and go back twice", async () => {
await shared.testPlayerNavigated(playerTwo, screen);
@@ -67,7 +70,7 @@ describe("layout-root:", () => {
await shared.testPlayerNavigated(playerTwo, screen);
await shared.testPlayerNavigatedBack(screen, driver);
});
it("navigate parent frame and go back", async () => {
await shared[`testSomePageNavigated${transition}`](screen);
@@ -76,10 +79,15 @@ describe("layout-root:", () => {
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayersList();
});
it("loaded player details and navigate parent frame and go back", async () => {
await shared.testPlayerNavigated(playerTwo, screen);
@@ -94,14 +102,19 @@ describe("layout-root:", () => {
await driver.backgroundApp(suspendTime);
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayerDetails(playerTwo);
await screen.goBackToPlayersList();
await screen.loadedPlayersList();
});
it("loaded home page again", async () => {
await screen.resetToHome();
await screen.loadedHome();
@@ -111,20 +124,20 @@ describe("layout-root:", () => {
await driver.waitForElement(home); // wait for home page
}
});
it("loaded layout root with multi nested frames", async () => {
await screen.navigateToLayoutWithMultiFrame();
await screen.loadedLayoutWithMultiFrame();
});
it("loaded players list", async () => {
await screen.loadedPlayersList();
});
it("loaded teams list", async () => {
await screen.loadedTeamsList();
});
it("loaded player details and go back twice", async () => {
await shared.testPlayerNavigated(playerTwo, screen);
@@ -139,11 +152,11 @@ describe("layout-root:", () => {
await driver.backgroundApp(suspendTime);
await driver.waitForElement(playerOne.name) // wait for players list
}
await shared.testPlayerNavigated(playerTwo, screen);
await shared.testPlayerNavigatedBack(screen, driver);
});
it("navigate players parent frame and go back", async () => {
await shared[`testSomePageNavigated${transition}`](screen);
@@ -151,11 +164,16 @@ describe("layout-root:", () => {
await driver.backgroundApp(suspendTime);
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayersList();
});
it("loaded players details and navigate parent frame and go back", async () => {
await shared.testPlayerNavigated(playerTwo, screen);
@@ -170,44 +188,53 @@ describe("layout-root:", () => {
await driver.backgroundApp(suspendTime);
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayerDetails(playerTwo);
await screen.goBackToPlayersList();
await screen.loadedPlayersList();
});
it("loaded layout root with multi nested frames again", async () => {
await screen.loadedLayoutWithMultiFrame();
});
it("loaded players list", async () => {
await screen.loadedPlayersList();
});
it("loaded teams list", async () => {
await screen.loadedTeamsList();
});
it ("mix player and team list actions and go back", async () => {
it("mix player and team list actions and go back", async () => {
await shared.testPlayerNavigated(playerTwo, screen);
if (appSuspendResume) {
await driver.backgroundApp(suspendTime);
await driver.waitForElement(playerTwo.name); // wait for player
}
await shared[`testOtherPageNavigated${transition}`](screen); // "teams" parent frame navigation
if (appSuspendResume) {
await driver.backgroundApp(suspendTime);
await driver.waitForElement(otherPage); // wait for other page
}
await screen.loadedPlayerDetails(playerTwo); // assert no changes in the sibling frame
await driver.navBack(); // other page back navigation
if (driver.isAndroid) {
await driver.navBack(); // other page back navigation
} else {
await screen.goBackFromOtherPage();
}
if (appSuspendResume) {
await driver.backgroundApp(suspendTime);
@@ -216,7 +243,7 @@ describe("layout-root:", () => {
await screen.loadedTeamsList();
await screen.loadedPlayerDetails(playerTwo); // assert no changes in the sibling frame
await shared[`testOtherPageNavigated${transition}`](screen);
if (appSuspendResume) {
@@ -232,10 +259,15 @@ describe("layout-root:", () => {
}
await screen.loadedOtherPage(); // assert no changes in the sibling frame
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
await screen.loadedPlayerDetails(playerTwo);
await screen.goBackToPlayersList();
await screen.loadedPlayersList();
@@ -243,7 +275,7 @@ describe("layout-root:", () => {
await driver.backgroundApp(suspendTime);
await driver.waitForElement(playerOne.name); // wait for players list
}
await screen.goBackFromOtherPage();
if (appSuspendResume) {
@@ -269,7 +301,7 @@ describe("layout-root:", () => {
await screen.navigateToLayoutWithFrame();
await screen.loadedLayoutWithFrame();
});
it("loaded players list", async () => {
await screen.loadedPlayersList();
});
@@ -282,7 +314,7 @@ describe("layout-root:", () => {
await driver.waitForElement(playerTwo.name); // wait for player
}
});
it("navigate parent frame and go back", async () => {
await shared.testSomePageNavigatedDefault(screen);
@@ -290,8 +322,12 @@ describe("layout-root:", () => {
await driver.backgroundApp(suspendTime);
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
if (appSuspendResume) {
await driver.backgroundApp(suspendTime);
@@ -324,7 +360,7 @@ describe("layout-root:", () => {
await screen.navigateToLayoutWithFrame();
await screen.loadedLayoutWithFrame();
});
it("loaded players list", async () => {
await screen.loadedPlayersList();
});
@@ -337,7 +373,7 @@ describe("layout-root:", () => {
await driver.waitForElement(playerTwo.name); // wait for player
}
});
it("navigate parent frame and go back", async () => {
await shared.testSomePageNavigatedNone(screen);
@@ -345,8 +381,12 @@ describe("layout-root:", () => {
await driver.backgroundApp(suspendTime);
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
if (appSuspendResume) {
await driver.backgroundApp(suspendTime);
@@ -379,7 +419,7 @@ describe("layout-root:", () => {
await screen.navigateToLayoutWithFrame();
await screen.loadedLayoutWithFrame();
});
it("loaded players list", async () => {
await screen.loadedPlayersList();
});
@@ -392,7 +432,7 @@ describe("layout-root:", () => {
await driver.waitForElement(playerTwo.name); // wait for player
}
});
it("navigate parent frame and go back", async () => {
await shared.testSomePageNavigatedDefault(screen);
@@ -400,8 +440,12 @@ describe("layout-root:", () => {
await driver.backgroundApp(suspendTime);
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
if (appSuspendResume) {
await driver.backgroundApp(suspendTime);
@@ -434,7 +478,7 @@ describe("layout-root:", () => {
await screen.navigateToLayoutWithFrame();
await screen.loadedLayoutWithFrame();
});
it("loaded players list", async () => {
await screen.loadedPlayersList();
});
@@ -447,7 +491,7 @@ describe("layout-root:", () => {
await driver.waitForElement(playerTwo.name); // wait for player
}
});
it("navigate parent frame and go back", async () => {
await shared.testSomePageNavigatedNone(screen);
@@ -455,8 +499,12 @@ describe("layout-root:", () => {
await driver.backgroundApp(suspendTime);
await driver.waitForElement(somePage); // wait for some page
}
await driver.navBack(); // some page back navigation
if (driver.isAndroid) {
await driver.navBack(); // some page back navigation
} else {
await screen.goBackFromSomePage();
}
if (appSuspendResume) {
await driver.backgroundApp(suspendTime);

View File

@@ -36,6 +36,7 @@ const tabTopBack = "tabTopBack";
const tabBottomBack = "tabBottomBack";
const resetApp = "reset app";
export const driverDefaultWaitTime = 10000;
export const home = "Home";
export const somePage = "some page";
export const otherPage = "other page";
@@ -213,7 +214,7 @@ export class Screen {
};
resetToHome = async () => {
const btnReset = await this._driver.findElementByAutomationText(resetApp);
const btnReset = await this._driver.waitForElement(resetApp);
await btnReset.tap();
};
@@ -246,18 +247,18 @@ export class Screen {
}
togglePlayersTab = async () => {
const lblPlayers = await this._driver.findElementByAutomationText(players);
const lblPlayers = await this._driver.waitForElement(players);
await lblPlayers.tap();
}
toggleTeamsTab = async () => {
const lblTeams = await this._driver.findElementByAutomationText(teams);
const lblTeams = await this._driver.waitForElement(teams);
await lblTeams.tap();
}
loadedHome = async () => {
const lblHome = await this._driver.findElementByAutomationText(home);
assert.isTrue(await lblHome.isDisplayed());
const lblHome = await this._driver.waitForElement(home);
assert.isNotNull(lblHome);
console.log(home + " loaded!");
};
@@ -303,8 +304,8 @@ export class Screen {
}
loadedPlayersList = async () => {
const lblPlayerOne = await this._driver.findElementByAutomationText(playersData["playerOneDefault"].name);
assert.isTrue(await lblPlayerOne.isDisplayed());
const lblPlayerOne = await this._driver.waitForElement(playersData["playerOneDefault"].name);
assert.isNotNull(lblPlayerOne);
console.log(players + " loaded!");
}
@@ -313,8 +314,8 @@ export class Screen {
}
loadedTeamsList = async () => {
const lblTeamOne = await this._driver.findElementByAutomationText(teamsData["teamOneDefault"].name);
assert.isTrue(await lblTeamOne.isDisplayed());
const lblTeamOne = await this._driver.waitForElement(teamsData["teamOneDefault"].name);
assert.isNotNull(lblTeamOne);
console.log(teams + " loaded!");
}
@@ -323,33 +324,33 @@ export class Screen {
}
private navigateToPage = async (page: string) => {
const btnPage = await this._driver.findElementByAutomationText(page);
const btnPage = await this._driver.waitForElement(page);
await btnPage.tap();
};
private loadedPage = async (page: string) => {
const lblPage = await this._driver.findElementByAutomationText(page);
assert.isTrue(await lblPage.isDisplayed());
const lblPage = await this._driver.waitForElement(page);
assert.isNotNull(lblPage);
console.log(page + " loaded!");
};
private navigateToItem = async (item: Item) => {
const lblItem = await this._driver.findElementByAutomationText(item.name);
const lblItem = await this._driver.waitForElement(item.name);
await lblItem.tap();
}
private loadedItem = async (item: Item) => {
const lblItemName = await this._driver.findElementByAutomationText(item.name);
assert.isTrue(await lblItemName.isDisplayed());
const lblItemName = await this._driver.waitForElement(item.name);
assert.isNotNull(lblItemName);
const lblItemDescription = await this._driver.findElementByAutomationText(item.description);
assert.isTrue(await lblItemDescription.isDisplayed());
const lblItemDescription = await this._driver.waitForElement(item.description);
assert.isNotNull(lblItemDescription);
console.log(item.name + " loaded!");
}
private goBack = async (accessibilityId: string) => {
const btnBack = await this._driver.findElementByAccessibilityId(accessibilityId);
const btnBack = await this._driver.waitForElement(accessibilityId);
await btnBack.tap();
}
}

View File

@@ -1,13 +1,19 @@
import { Screen, Item } from "./screen";
import { AppiumDriver } from "nativescript-dev-appium";
import { Screen, Item } from "./screen";
export async function testPlayerNavigated(player: Item, screen: Screen) {
await screen.navigateToPlayerDetails(player);
await screen.loadedPlayerDetails(player);
}
export async function testPlayerNavigatedBack(screen: Screen, driver: AppiumDriver) {
await driver.navBack();
if (driver.isAndroid) {
await driver.navBack();
} else {
await screen.goBackToPlayersList();
}
await screen.loadedPlayersList();
}
@@ -37,7 +43,12 @@ export async function testTeamNavigated(team: Item, screen: Screen) {
}
export async function testTeamNavigatedBack(screen: Screen, driver: AppiumDriver) {
await driver.navBack();
if (driver.isAndroid) {
await driver.navBack();
} else {
await screen.goBackToTeamsList();
}
await screen.loadedTeamsList();
}

View File

@@ -1,8 +1,10 @@
import { AppiumDriver, createDriver } from "nativescript-dev-appium";
import { Screen, playersData, teamsData } from "./screen";
import * as shared from "./shared.e2e-spec";
import { suspendTime, appSuspendResume, dontKeepActivities, transitions } from "./config";
// NOTE: TabTop is Android only scenario (for iOS we will essentially execute 2x TabBottom)
const roots = ["TabTop", "TabBottom"];
function hyphenate(s: string) {
@@ -19,6 +21,8 @@ describe("tab-root:", () => {
if (dontKeepActivities) {
await driver.setDontKeepActivities(true);
}
driver.defaultWaitTime = 8000;
});
after(async () => {

View File

@@ -33,4 +33,4 @@
"e2e-watch": "tsc -p e2e --watch",
"clean-e2e": "rimraf 'e2e/**/*.js' 'e2e/**/*.js.map' 'e2e/**/*.map'"
}
}
}

View File

@@ -41,7 +41,7 @@
"time-grunt": "1.3.0",
"tslib": "^1.9.3",
"tslint": "^5.4.3",
"typedoc": "^0.5.10",
"typedoc": "^0.13.0",
"typedoc-plugin-external-module-name": "git://github.com/PanayotCankov/typedoc-plugin-external-module-name.git#with-js",
"typescript": "^3.1.6"
},

View File

@@ -0,0 +1,3 @@
Button, Label {
color: green;
}

View File

@@ -0,0 +1,3 @@
Button, Label {
color: green;
}

View File

@@ -90,6 +90,12 @@ application.on(application.uncaughtErrorEvent, function (args: application.Unhan
console.log((<any>args.error).stackTrace || (<any>args.error).stack);
});
application.on(application.discardedErrorEvent, function (args: application.DiscardedErrorEventData) {
console.log("[Discarded] NativeScriptError: " + args.error);
console.log((<any>args.error).nativeException || (<any>args.error).nativeError);
console.log((<any>args.error).stackTrace || (<any>args.error).stack);
});
// Android activity events
if (application.android) {
application.android.on(application.AndroidApplication.activityCreatedEvent, function (args: application.AndroidActivityBundleEventData) {

View File

@@ -0,0 +1,3 @@
Button, Label {
color: black;
}

View File

@@ -2,6 +2,8 @@
import * as trace from "tns-core-modules/trace";
import * as tests from "../testRunner";
let executeTests = true;
trace.enable();
trace.addCategories(trace.categories.Test + "," + trace.categories.Error);
@@ -21,6 +23,8 @@ function runTests() {
export function onNavigatedTo(args) {
args.object.off(Page.loadedEvent, onNavigatedTo);
runTests();
if (executeTests) {
executeTests = false;
runTests();
}
}

View File

@@ -1,3 +1,3 @@
<Page navigatedTo="onNavigatedTo">
<Label text="Running non-UI tests..." />
<Label id="label" text="Running non-UI tests..." />
</Page>

View File

@@ -0,0 +1,113 @@
import * as app from "tns-core-modules/application/application";
import * as frame from "tns-core-modules/ui/frame";
import * as helper from "../ui/helper";
import * as TKUnit from "../TKUnit";
import { Color } from "tns-core-modules/color";
import { parse } from "tns-core-modules/ui/builder";
import { Page } from "tns-core-modules/ui/page";
const appCssFileName = "./app/application.css";
const appNewCssFileName = "./app/app-new.css";
const appNewScssFileName = "./app/app-new.scss";
const appJsFileName = "./app/app.js";
const appTsFileName = "./app/app.ts";
const mainPageCssFileName = "./app/main-page.css";
const mainPageHtmlFileName = "./app/main-page.html";
const mainPageXmlFileName = "./app/main-page.xml";
const green = new Color("green");
const mainPageTemplate = `
<Page>
<StackLayout>
<Label id="label" text="label"></Label>
</StackLayout>
</Page>`;
const pageTemplate = `
<Page>
<StackLayout>
<Button id="button" text="button"></Button>
</StackLayout>
</Page>`;
export function test_onLiveSync_HmrContext_AppStyle_AppNewCss() {
_test_onLiveSync_HmrContext_AppStyle(appNewCssFileName);
}
export function test_onLiveSync_HmrContext_AppStyle_AppNewScss() {
_test_onLiveSync_HmrContext_AppStyle(appNewScssFileName);
}
export function test_onLiveSync_HmrContext_ContextUndefined() {
_test_onLiveSync_HmrContext({ type: undefined, module: undefined });
}
export function test_onLiveSync_HmrContext_ModuleUndefined() {
_test_onLiveSync_HmrContext({ type: "script", module: undefined });
}
export function test_onLiveSync_HmrContext_Script_AppJs() {
_test_onLiveSync_HmrContext({ type: "script", module: appJsFileName });
}
export function test_onLiveSync_HmrContext_Script_AppTs() {
_test_onLiveSync_HmrContext({ type: "script", module: appTsFileName });
}
export function test_onLiveSync_HmrContext_Style_MainPageCss() {
_test_onLiveSync_HmrContext({ type: "style", module: mainPageCssFileName });
}
export function test_onLiveSync_HmrContext_Markup_MainPageHtml() {
_test_onLiveSync_HmrContext({ type: "markup", module: mainPageHtmlFileName });
}
export function test_onLiveSync_HmrContext_Markup_MainPageXml() {
_test_onLiveSync_HmrContext({ type: "markup", module: mainPageXmlFileName });
}
export function setUpModule() {
const mainPage = <Page>parse(mainPageTemplate);
helper.navigate(() => mainPage);
}
export function tearDown() {
app.setCssFileName(appCssFileName);
}
function _test_onLiveSync_HmrContext_AppStyle(styleFileName: string) {
const pageBeforeNavigation = helper.getCurrentPage();
const page = <Page>parse(pageTemplate);
helper.navigateWithHistory(() => page);
app.setCssFileName(styleFileName);
const pageBeforeLiveSync = helper.getCurrentPage();
global.__onLiveSync({ type: "style", module: styleFileName });
const pageAfterLiveSync = helper.getCurrentPage();
TKUnit.waitUntilReady(() => pageAfterLiveSync.getViewById("button").style.color.toString() === green.toString());
TKUnit.assertTrue(pageAfterLiveSync.frame.canGoBack(), "App styles NOT applied - livesync navigation executed!");
TKUnit.assertEqual(pageAfterLiveSync, pageBeforeLiveSync, "Pages are different - livesync navigation executed!");
TKUnit.assertTrue(pageAfterLiveSync._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version NOT applied!");
helper.goBack();
const pageAfterNavigationBack = helper.getCurrentPage();
TKUnit.assertEqual(pageAfterNavigationBack.getViewById("label").style.color, green, "App styles NOT applied on back navigation!");
TKUnit.assertEqual(pageBeforeNavigation, pageAfterNavigationBack, "Pages are different - livesync navigation executed!");
TKUnit.assertTrue(pageAfterNavigationBack._cssState.isSelectorsLatestVersionApplied(), "Latest selectors version is NOT applied!");
}
function _test_onLiveSync_HmrContext(context: { type, module }) {
const page = <Page>parse(pageTemplate);
helper.navigateWithHistory(() => page);
global.__onLiveSync({ type: context.type, module: context.module });
TKUnit.waitUntilReady(() => !!frame.topmost());
const topmostFrame = frame.topmost();
TKUnit.waitUntilReady(() => topmostFrame.currentPage && topmostFrame.currentPage.isLoaded && !topmostFrame.canGoBack());
TKUnit.assertTrue(topmostFrame.currentPage.getViewById("label").isLoaded);
}

View File

@@ -153,9 +153,6 @@ allTests["STYLE-PROPERTIES"] = stylePropertiesTests;
import * as frameTests from "./ui/frame/frame-tests";
allTests["FRAME"] = frameTests;
import * as tabViewRootTests from "./ui/tab-view/tab-view-root-tests";
allTests["TAB-VIEW-ROOT"] = tabViewRootTests;
import * as viewTests from "./ui/view/view-tests";
allTests["VIEW"] = viewTests;
@@ -255,6 +252,12 @@ allTests["SEARCH-BAR"] = searchBarTests;
import * as navigationTests from "./navigation/navigation-tests";
allTests["NAVIGATION"] = navigationTests;
import * as livesyncTests from "./livesync/livesync-tests";
allTests["LIVESYNC"] = livesyncTests;
import * as tabViewRootTests from "./ui/tab-view/tab-view-root-tests";
allTests["TAB-VIEW-ROOT"] = tabViewRootTests;
import * as resetRootViewTests from "./ui/root-view/reset-root-view-tests";
allTests["RESET-ROOT-VIEW"] = resetRootViewTests;

View File

@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2015-2018 Telerik AD
Copyright (c) 2015-2019 Progress Software Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -198,4 +198,4 @@
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.

View File

@@ -32,9 +32,17 @@ export function hasLaunched(): boolean {
export { Observable };
import { UnhandledErrorEventData, iOSApplication, AndroidApplication, CssChangedEventData, LoadAppCSSEventData } from ".";
import {
AndroidApplication,
CssChangedEventData,
getRootView,
iOSApplication,
LoadAppCSSEventData,
UnhandledErrorEventData,
DiscardedErrorEventData
} from "./application";
export { UnhandledErrorEventData, CssChangedEventData, LoadAppCSSEventData };
export { UnhandledErrorEventData, DiscardedErrorEventData, CssChangedEventData, LoadAppCSSEventData };
export const launchEvent = "launch";
export const suspendEvent = "suspend";
@@ -43,6 +51,7 @@ export const resumeEvent = "resume";
export const exitEvent = "exit";
export const lowMemoryEvent = "lowMemory";
export const uncaughtErrorEvent = "uncaughtError";
export const discardedErrorEvent = "discardedError";
export const orientationChangedEvent = "orientationChanged";
let cssFile: string = "./app.css";
@@ -70,10 +79,22 @@ export function setApplication(instance: iOSApplication | AndroidApplication): v
app = instance;
}
export function livesync() {
export function livesync(context?: HmrContext) {
events.notify(<EventData>{ eventName: "livesync", object: app });
const liveSyncCore = global.__onLiveSyncCore;
if (liveSyncCore) {
let reapplyAppCss = false
if (context) {
const fullFileName = getCssFileName();
const fileName = fullFileName.substring(0, fullFileName.lastIndexOf(".") + 1);
const extensions = ["css", "scss"];
reapplyAppCss = extensions.some(ext => context.module === fileName.concat(ext));
}
const rootView = getRootView();
if (reapplyAppCss && rootView) {
rootView._onCssStateChange();
} else if (liveSyncCore) {
liveSyncCore();
}
}
@@ -92,7 +113,7 @@ export function loadAppCss(): void {
events.notify(<LoadAppCSSEventData>{ eventName: "loadAppCss", object: app, cssFile: getCssFileName() });
} catch (e) {
throw new Error(`The file ${getCssFileName()} couldn't be loaded! ` +
`You may need to register it inside ./app/vendor.ts.`);
`You may need to register it inside ./app/vendor.ts.`);
}
}
@@ -103,3 +124,7 @@ export function addCss(cssText: string): void {
global.__onUncaughtError = function (error: NativeScriptError) {
events.notify(<UnhandledErrorEventData>{ eventName: uncaughtErrorEvent, object: app, android: error, ios: error, error: error });
}
global.__onDiscardedError = function (error: NativeScriptError) {
events.notify(<DiscardedErrorEventData>{ eventName: discardedErrorEvent, object: app, error: error });
}

View File

@@ -212,12 +212,12 @@ export function getNativeApplication(): android.app.Application {
return nativeApp;
}
global.__onLiveSync = function () {
global.__onLiveSync = function __onLiveSync(context?: HmrContext) {
if (androidApp && androidApp.paused) {
return;
}
livesync();
livesync(context);
};
function initLifecycleCallbacks() {
@@ -290,15 +290,10 @@ function initLifecycleCallbacks() {
onActivityResumed: profile("onActivityResumed", function (activity: android.support.v7.app.AppCompatActivity) {
androidApp.foregroundActivity = activity;
if ((<any>activity).isNativeScriptActivity) {
notify(<ApplicationEventData>{ eventName: resumeEvent, object: androidApp, android: activity });
androidApp.paused = false;
}
androidApp.notify(<AndroidActivityEventData>{ eventName: ActivityResumed, object: androidApp, activity: activity });
}),
onActivitySaveInstanceState: profile("onActivityResumed", function (activity: android.support.v7.app.AppCompatActivity, outState: android.os.Bundle) {
onActivitySaveInstanceState: profile("onActivitySaveInstanceState", function (activity: android.support.v7.app.AppCompatActivity, outState: android.os.Bundle) {
androidApp.notify(<AndroidActivityBundleEventData>{ eventName: SaveActivityState, object: androidApp, activity: activity, bundle: outState });
}),

View File

@@ -21,6 +21,11 @@ export var displayedEvent: string;
*/
export var uncaughtErrorEvent: string;
/**
* String value used when hooking to discardedError event.
*/
export var discardedErrorEvent: string;
/**
* String value used when hooking to suspend event.
*/
@@ -103,6 +108,13 @@ export interface UnhandledErrorEventData extends ApplicationEventData {
error: NativeScriptError;
}
/**
* Event data containing information about discarded application errors.
*/
export interface DiscardedErrorEventData extends ApplicationEventData {
error: NativeScriptError;
}
/**
* Event data containing information about application css change.
*/
@@ -137,7 +149,7 @@ export function setResources(res: any): void;
export function setResources(resources: any);
/**
* Sets css file name for the application.
* Sets css file name for the application.
*/
export function setCssFileName(cssFile: string): void;
@@ -199,7 +211,7 @@ export function shouldCreateRootFrame(): boolean;
/**
* A basic method signature to hook an event listener (shortcut alias to the addEventListener method).
* @param eventNames - String corresponding to events (e.g. "onLaunch"). Optionally could be used more events separated by `,` (e.g. "onLaunch", "onSuspend").
* @param eventNames - String corresponding to events (e.g. "onLaunch"). Optionally could be used more events separated by `,` (e.g. "onLaunch", "onSuspend").
* @param callback - Callback function which will be executed when event is raised.
* @param thisArg - An optional parameter which will be used as `this` context for callback execution.
*/
@@ -262,6 +274,11 @@ export function on(event: "lowMemory", callback: (args: ApplicationEventData) =>
*/
export function on(event: "uncaughtError", callback: (args: UnhandledErrorEventData) => void, thisArg?: any);
/**
* This event is raised when an discarded error occurs while the application is running.
*/
export function on(event: "discardedError", callback: (args: DiscardedErrorEventData) => void, thisArg?: any);
/**
* This event is raised the orientation of the current device has changed.
*/
@@ -403,13 +420,13 @@ export class AndroidApplication extends Observable {
/**
* Initialized the android-specific application object with the native android.app.Application instance.
* This is useful when creating custom application types.
* @param nativeApp - the android.app.Application instance that started the app.
* @param nativeApp - the android.app.Application instance that started the app.
*/
init: (nativeApp) => void;
/**
* A basic method signature to hook an event listener (shortcut alias to the addEventListener method).
* @param eventNames - String corresponding to events (e.g. "propertyChange"). Optionally could be used more events separated by `,` (e.g. "propertyChange", "change").
* @param eventNames - String corresponding to events (e.g. "propertyChange"). Optionally could be used more events separated by `,` (e.g. "propertyChange", "change").
* @param callback - Callback function which will be executed when event is raised.
* @param thisArg - An optional parameter which will be used as `this` context for callback execution.
*/
@@ -516,7 +533,7 @@ export class AndroidApplication extends Observable {
public static activityRequestPermissionsEvent: string;
/**
* Register a BroadcastReceiver to be run in the main activity thread. The receiver will be called with any broadcast Intent that matches filter, in the main application thread.
* Register a BroadcastReceiver to be run in the main activity thread. The receiver will be called with any broadcast Intent that matches filter, in the main application thread.
* For more information, please visit 'http://developer.android.com/reference/android/content/Context.html#registerReceiver%28android.content.BroadcastReceiver,%20android.content.IntentFilter%29'
* @param intentFilter A string containing the intent filter.
* @param onReceiveCallback A callback function that will be called each time the receiver receives a broadcast.
@@ -524,7 +541,7 @@ export class AndroidApplication extends Observable {
registerBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: any /* android.content.Context */, intent: any /* android.content.Intent */) => void): void;
/**
* Unregister a previously registered BroadcastReceiver.
* Unregister a previously registered BroadcastReceiver.
* For more information, please visit 'http://developer.android.com/reference/android/content/Context.html#unregisterReceiver(android.content.BroadcastReceiver)'
* @param intentFilter A string containing the intent filter with which the receiver was originally registered.
*/

View File

@@ -161,7 +161,7 @@ class IOSApplication implements IOSApplicationDefinition {
this.setWindowContent(args.root);
} else {
this._window = UIApplication.sharedApplication.delegate.window;
}
}
}
@profile
@@ -373,10 +373,10 @@ function setViewControllerView(view: View): void {
}
}
global.__onLiveSync = function () {
global.__onLiveSync = function __onLiveSync(context?: HmrContext) {
if (!started) {
return;
}
livesync();
}
livesync(context);
}

View File

@@ -52,7 +52,10 @@ function ensureCompleteCallback() {
onComplete: function (result: any, context: any) {
// as a context we will receive the id of the request
onRequestComplete(context, result);
}
},
onError: function (error: string, context: any) {
onRequestError(error, context);
},
});
}
@@ -148,6 +151,14 @@ function onRequestComplete(requestId: number, result: org.nativescript.widgets.A
});
}
function onRequestError(error: string, requestId: number) {
var callbacks = pendingRequests[requestId];
delete pendingRequests[requestId];
if (callbacks) {
callbacks.rejectCallback(new Error(error));
}
}
function buildJavaOptions(options: http.HttpRequestOptions) {
if (typeof options.url !== "string") {
throw new Error("Http request must provide a valid url.");

View File

@@ -3,7 +3,7 @@ declare var global: NodeJS.Global;
interface ModuleResolver {
/**
* A function used to resolve the exports for a module.
* @param uri The name of the module to be resolved.
* @param uri The name of the module to be resolved.
*/
(uri: string): any;
}
@@ -18,7 +18,7 @@ declare namespace NodeJS {
* Register all modules from a webpack context.
* The context is one created using the following webpack utility:
* https://webpack.github.io/docs/context.html
*
*
* The extension map is optional, modules in the webpack context will have their original file extension (e.g. may be ".ts" or ".scss" etc.),
* while the built-in module builders in {N} will look for ".js", ".css" or ".xml" files. Adding a map such as:
* ```
@@ -51,9 +51,10 @@ declare namespace NodeJS {
__native?: any;
__inspector?: any;
__extends: any;
__onLiveSync: () => void;
__onLiveSync: (context?: { type: string, module: string }) => void;
__onLiveSyncCore: () => void;
__onUncaughtError: (error: NativeScriptError) => void;
__onDiscardedError: (error: NativeScriptError) => void;
TNS_WEBPACK?: boolean;
__requireOverride?: (name: string, dir: string) => any;
}
@@ -64,6 +65,27 @@ declare function clearTimeout(timeoutId: number): void;
declare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): number;
declare function clearInterval(intervalId: number): void;
declare enum HmrType {
markup = "markup",
script = "script",
style = "style"
}
/**
* Define a context for Hot Module Replacement.
*/
interface HmrContext {
/**
* The type of module for replacement.
*/
type: HmrType;
/**
* The module for replacement.
*/
module: string;
}
/**
* An extended JavaScript Error which will have the nativeError property initialized in case the error is caused by executing platform-specific code.
*/

View File

@@ -1,7 +1,7 @@
{
"name": "tns-core-modules",
"description": "Telerik NativeScript Core Modules",
"version": "5.1.2",
"version": "5.2.0",
"homepage": "https://www.nativescript.org",
"repository": {
"type": "git",
@@ -26,7 +26,7 @@
"license": "Apache-2.0",
"typings": "tns-core-modules.d.ts",
"dependencies": {
"tns-core-modules-widgets": "5.1.2",
"tns-core-modules-widgets": "next",
"tslib": "^1.9.3"
},
"devDependencies": {
@@ -38,8 +38,8 @@
},
"nativescript": {
"platforms": {
"ios": "5.0.0",
"android": "5.0.0"
"ios": "4.0.0",
"android": "4.0.0"
}
},
"snapshot": {

View File

@@ -6,7 +6,6 @@ import {
layout, Color, traceMissingIcon } from "./action-bar-common";
import { fromFileOrResource } from "../../image-source";
import { ios as iosUtils } from "../../utils/utils";
import { write as traceWrite, categories, messageType } from "../../trace";
export * from "./action-bar-common";

View File

@@ -81,6 +81,12 @@ export interface ShowModalOptions {
*/
presentationStyle: any /* UIModalPresentationStyle */
}
android?: {
/**
* An optional parameter specifying whether the modal view can be dismissed when not in full-screen mode.
*/
cancelable?: boolean
}
}
export abstract class ViewBase extends Observable {

View File

@@ -37,6 +37,7 @@ interface DialogOptions {
owner: View;
fullscreen: boolean;
stretched: boolean;
cancelable: boolean;
shownCallback: () => void;
dismissCallback: () => void;
}
@@ -117,6 +118,7 @@ function initializeDialogFragment() {
public owner: View;
private _fullscreen: boolean;
private _stretched: boolean;
private _cancelable: boolean;
private _shownCallback: () => void;
private _dismissCallback: () => void;
@@ -130,13 +132,20 @@ function initializeDialogFragment() {
const options = getModalOptions(ownerId);
this.owner = options.owner;
this._fullscreen = options.fullscreen;
this._cancelable = options.cancelable;
this._stretched = options.stretched;
this._dismissCallback = options.dismissCallback;
this._shownCallback = options.shownCallback;
this.owner._dialogFragment = this;
this.setStyle(android.support.v4.app.DialogFragment.STYLE_NO_TITLE, 0);
let theme = this.getTheme();
if (this._fullscreen) {
// In fullscreen mode, get the application's theme.
theme = this.getActivity().getApplicationInfo().theme;
}
const dialog = new DialogImpl(this, this.getActivity(), this.getTheme());
const dialog = new DialogImpl(this, this.getActivity(), theme);
// do not override alignment unless fullscreen modal will be shown;
// otherwise we might break component-level layout:
@@ -149,6 +158,7 @@ function initializeDialogFragment() {
this.owner.verticalAlignment = "stretch";
}
dialog.setCanceledOnTouchOutside(this._cancelable);
return dialog;
}
@@ -195,8 +205,17 @@ function initializeDialogFragment() {
public onDestroy(): void {
super.onDestroy();
const owner = this.owner;
owner._isAddedToNativeVisualTree = false;
owner._tearDownUI(true);
if (owner) {
// Android calls onDestroy before onDismiss.
// Make sure we unload first and then call _tearDownUI.
if (owner.isLoaded) {
owner.callUnloaded();
}
owner._isAddedToNativeVisualTree = false;
owner._tearDownUI(true);
}
}
}
@@ -386,7 +405,7 @@ export class View extends ViewCommon {
if (!this.nativeViewProtected || !this.hasGestureObservers()) {
return;
}
// do not set noop listener that handles the event (disabled listener) if IsUserInteractionEnabled is
// false as we might need the ability for the event to pass through to a parent view
initializeTouchListener();
@@ -574,7 +593,7 @@ export class View extends ViewCommon {
return result | (childMeasuredState & layout.MEASURED_STATE_MASK);
}
protected _showNativeModalView(parent: View, options: ShowModalOptions) { //context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean, iosOpts?: any) {
protected _showNativeModalView(parent: View, options: ShowModalOptions) {
super._showNativeModalView(parent, options);
if (!this.backgroundColor) {
this.backgroundColor = new Color("White");
@@ -591,6 +610,7 @@ export class View extends ViewCommon {
owner: this,
fullscreen: !!options.fullscreen,
stretched: !!options.stretched,
cancelable: options.android ? !!options.android.cancelable : true,
shownCallback: () => this._raiseShownModallyEvent(),
dismissCallback: () => this.closeModal()
}

View File

@@ -1,7 +1,6 @@
// Definitions.
import { Point, View as ViewDefinition, dip } from ".";
import { ViewBase } from "../view-base";
import { booleanConverter, Property } from "../view";
import {
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
@@ -371,7 +370,7 @@ export class View extends ViewCommon {
return this._suspendCATransaction || this._suspendNativeUpdatesCount;
}
protected _showNativeModalView(parent: View, options: ShowModalOptions) { //context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean, iosOpts?: any) {
protected _showNativeModalView(parent: View, options: ShowModalOptions) {
const parentWithController = ios.getParentWithViewController(parent);
if (!parentWithController) {
traceWrite(`Could not find parent with viewController for ${parent} while showing modal view.`,

View File

@@ -1,7 +1,7 @@
/**
* iOS specific dialogs functions implementation.
*/
import { View, ios as iosView } from "../core/view";
import { ios as iosView } from "../core/view";
import { ConfirmOptions, PromptOptions, PromptResult, LoginOptions, LoginResult, ActionOptions } from ".";
import { getCurrentPage, getLabelColor, getButtonColors, getTextFieldColor, isDialogOptions, inputType, capitalizationType, ALERT, OK, CONFIRM, CANCEL, PROMPT, parseLoginOptions } from "./dialogs-common";
import { isString, isDefined, isFunction } from "../../utils/types";

View File

@@ -50,6 +50,10 @@ class NativeScriptActivity extends android.support.v7.app.AppCompatActivity {
this._callbacks.onDestroy(this, super.onDestroy);
}
public onPostResume(): void {
this._callbacks.onPostResume(this, super.onPostResume);
}
public onBackPressed(): void {
this._callbacks.onBackPressed(this, super.onBackPressed);
}

View File

@@ -85,10 +85,14 @@ function getAttachListener(): android.view.View.OnAttachStateChangeListener {
export function reloadPage(): void {
const activity = application.android.foregroundActivity;
const callbacks: AndroidActivityCallbacks = activity[CALLBACKS];
const rootView: View = callbacks.getRootView();
if (callbacks) {
const rootView: View = callbacks.getRootView();
if (!rootView || !rootView._onLivesync()) {
callbacks.resetActivityContent(activity);
if (!rootView || !rootView._onLivesync()) {
callbacks.resetActivityContent(activity);
}
} else {
traceError(`${activity}[CALLBACKS] is null or undefined`);
}
}
@@ -469,19 +473,19 @@ export class Frame extends FrameBase {
switch (this.actionBarVisibility) {
case "never":
return false;
case "always":
return true;
default:
if (page.actionBarHidden !== undefined) {
return !page.actionBarHidden;
}
if (this._android && this._android.showActionBar !== undefined) {
return this._android.showActionBar;
}
return true;
}
}
@@ -530,7 +534,7 @@ function restoreAnimatorState(entry: BackstackEntry, snapshot: AnimatorState): v
if (snapshot.enterAnimator) {
expandedEntry.enterAnimator = snapshot.enterAnimator;
}
if (snapshot.exitAnimator) {
expandedEntry.exitAnimator = snapshot.exitAnimator;
}
@@ -538,7 +542,7 @@ function restoreAnimatorState(entry: BackstackEntry, snapshot: AnimatorState): v
if (snapshot.popEnterAnimator) {
expandedEntry.popEnterAnimator = snapshot.popEnterAnimator;
}
if (snapshot.popExitAnimator) {
expandedEntry.popExitAnimator = snapshot.popExitAnimator;
}
@@ -858,14 +862,14 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
// parent while its supposed parent believes it properly removed its children; in order to "force" the child to
// lose its parent we temporarily add it to the parent, and then remove it (addViewInLayout doesn't trigger layout pass)
const nativeView = page.nativeViewProtected;
if (nativeView != null) {
const parentView = nativeView.getParent();
if (nativeView != null) {
const parentView = nativeView.getParent();
if (parentView instanceof android.view.ViewGroup) {
if (parentView.getChildCount() === 0) {
parentView.addViewInLayout(nativeView, -1, new org.nativescript.widgets.CommonLayoutParams());
}
parentView.removeView(nativeView);
parentView.removeView(nativeView);
}
}
@@ -1006,6 +1010,30 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
}
}
@profile
public onPostResume(activity: any, superFunc: Function): void {
superFunc.call(activity);
if (traceEnabled()) {
traceWrite("NativeScriptActivity.onPostResume();", traceCategories.NativeLifecycle);
}
// NOTE: activity.onPostResume() is called when activity resume is complete and we can
// safely raise the application resume event;
// onActivityResumed(...) lifecycle callback registered in application is called too early
// and raising the application resume event there causes issues like
// https://github.com/NativeScript/NativeScript/issues/6708
if ((<any>activity).isNativeScriptActivity) {
const args = <application.ApplicationEventData>{
eventName: application.resumeEvent,
object: application.android,
android: activity
};
application.notify(args);
application.android.paused = false;
}
}
@profile
public onDestroy(activity: any, superFunc: Function): void {
if (traceEnabled()) {
@@ -1216,4 +1244,4 @@ export function setActivityCallbacks(activity: android.support.v7.app.AppCompatA
export function setFragmentCallbacks(fragment: android.support.v4.app.Fragment): void {
fragment[CALLBACKS] = new FragmentCallbacksImplementation();
}
}

View File

@@ -415,6 +415,7 @@ export interface AndroidActivityCallbacks {
onSaveInstanceState(activity: any, outState: any, superFunc: Function): void;
onStart(activity: any, superFunc: Function): void;
onStop(activity: any, superFunc: Function): void;
onPostResume(activity: any, superFunc: Function): void;
onDestroy(activity: any, superFunc: Function): void;
onBackPressed(activity: any, superFunc: Function): void;
onRequestPermissionsResult(activity: any, requestCode: number, permissions: Array<String>, grantResults: Array<number>, superFunc: Function): void;

View File

@@ -6,10 +6,12 @@ export interface DownloadRequest {
url: string;
key: string;
completed?: (image: any, key: string) => void;
error?: (key: string) => void;
}
export class Cache extends observable.Observable implements definition.Cache {
public static downloadedEvent = "downloaded";
public static downloadErrorEvent = "downloadError";
public placeholder: imageSource.ImageSource;
public maxRequests = 5;
@@ -93,6 +95,20 @@ export class Cache extends observable.Observable implements definition.Cache {
else {
existingRequest.completed = newRequest.completed;
}
if (existingRequest.error) {
if (newRequest.error) {
var existingError = existingRequest.error;
var stackError = function (key: string) {
existingError(key);
newRequest.error(key);
}
existingRequest.error = stackError;
}
}
else {
existingRequest.error = newRequest.error;
}
}
public get(key: string): any {
@@ -125,10 +141,7 @@ export class Cache extends observable.Observable implements definition.Cache {
public _onDownloadCompleted(key: string, image: any) {
var request = <DownloadRequest>this._pendingDownloads[key];
if (request.key && image) {
this.set(request.key, image);
}
this.set(request.key, image);
this._currentDownloads--;
if (request.completed) {
@@ -140,7 +153,29 @@ export class Cache extends observable.Observable implements definition.Cache {
eventName: Cache.downloadedEvent,
object: this,
key: key,
image: image
image: image
});
}
delete this._pendingDownloads[request.key];
this._updateQueue();
}
public _onDownloadError(key: string, err: Error) {
var request = <DownloadRequest>this._pendingDownloads[key];
this._currentDownloads--;
if (request.error) {
request.error(request.key);
}
if (this.hasListeners(Cache.downloadErrorEvent)) {
this.notify({
eventName: Cache.downloadErrorEvent,
object: this,
key: key,
error: err
});
}
@@ -185,6 +220,7 @@ export class Cache extends observable.Observable implements definition.Cache {
}
}
export interface Cache {
on(eventNames: string, callback: (args: observable.EventData) => void , thisArg?: any);
on(event: "downloaded", callback: (args: definition.DownloadedData) => void , thisArg?: any);
on(eventNames: string, callback: (args: observable.EventData) => void, thisArg?: any);
on(event: "downloaded", callback: (args: definition.DownloadedData) => void, thisArg?: any);
on(event: "downloadError", callback: (args: definition.DownloadError) => void, thisArg?: any);
}

View File

@@ -1,4 +1,5 @@
import * as common from "./image-cache-common";
import * as trace from "../../trace";
var LruBitmapCacheClass;
function ensureLruBitmapCacheClass() {
@@ -45,7 +46,17 @@ export class Cache extends common.Cache {
onComplete: function (result: any, context: any) {
var instance = that.get();
if (instance) {
instance._onDownloadCompleted(context, result)
if (result) {
instance._onDownloadCompleted(context, result);
} else {
instance._onDownloadError(context, new Error("No result in CompletionCallback"));
}
}
},
onError: function (err: string, context: any) {
var instance = that.get();
if (instance) {
instance._onDownloadError(context, new Error(err));
}
}
});
@@ -61,7 +72,13 @@ export class Cache extends common.Cache {
}
public set(key: string, image: any): void {
this._cache.put(key, image);
try {
if (key && image) {
this._cache.put(key, image);
}
} catch (err) {
trace.write("Cache set error: " + err, trace.categories.Error, trace.messageType.error);
}
}
public remove(key: string): void {

View File

@@ -22,6 +22,10 @@ export interface DownloadRequest {
* An optional function to be called when the download is complete.
*/
completed?: (image: any, key: string) => void;
/**
* An optional function to be called if the download errors.
*/
error?: (key: string) => void;
}
/**
@@ -32,6 +36,10 @@ export class Cache extends observable.Observable {
* String value used when hooking to downloaded event.
*/
public static downloadedEvent: string;
/**
* String value used when hooking to download error event.
*/
public static downloadErrorEvent: string;
/**
* The image to be used to notify for a pending download request - e.g. loading indicator.
*/
@@ -82,12 +90,17 @@ export class Cache extends observable.Observable {
* @param callback - Callback function which will be executed when event is raised.
* @param thisArg - An optional parameter which will be used as `this` context for callback execution.
*/
on(eventNames: string, callback: (args: observable.EventData) => void , thisArg?: any);
on(eventNames: string, callback: (args: observable.EventData) => void, thisArg?: any);
/**
* Raised when the image has been downloaded.
*/
on(event: "downloaded", callback: (args: DownloadedData) => void , thisArg?: any);
on(event: "downloaded", callback: (args: DownloadedData) => void, thisArg?: any);
/**
* Raised if the image download errors.
*/
on(event: "downloadError", callback: (args: DownloadError) => void, thisArg?: any);
//@private
/**
@@ -99,6 +112,11 @@ export class Cache extends observable.Observable {
*/
_onDownloadCompleted(key: string, image: any);
//@endprivate
/**
* @private
*/
_onDownloadError(key: string, err: Error);
//@endprivate
}
/**
@@ -114,3 +132,17 @@ export interface DownloadedData extends observable.EventData {
*/
image: imageSource.ImageSource;
}
/**
* Provides data for download error.
*/
export interface DownloadError extends observable.EventData {
/**
* A string indentifier of the cached image.
*/
key: string;
/**
* Gets the error.
*/
error: Error;
}

View File

@@ -73,7 +73,7 @@ export class Cache extends common.Cache {
super();
this._cache = new NSCache<any, any>();
//this._delegate = NSCacheDelegateImpl.new();
//this._cache.delegate = this._delegate;
@@ -83,11 +83,16 @@ export class Cache extends common.Cache {
public _downloadCore(request: common.DownloadRequest) {
ensureHttpRequest();
var that = this;
httpRequest.request({ url: request.url, method: "GET" })
.then(response => {
var image = UIImage.alloc().initWithData(response.content.raw);
that._onDownloadCompleted(request.key, image);
.then((response) => {
try {
var image = UIImage.alloc().initWithData(response.content.raw);
this._onDownloadCompleted(request.key, image);
} catch (err) {
this._onDownloadError(request.key, err);
}
}, (err) => {
this._onDownloadError(request.key, err);
});
}

View File

@@ -1,5 +1,5 @@
import { ListView as ListViewDefinition, ItemsSource, ItemEventData, TemplatedItemsView } from ".";
import { CoercibleProperty, CssProperty, Style, View, ViewBase, ContainerView, Template, KeyedTemplate, Length, Property, Color, Observable, EventData, CSSType } from "../core/view";
import { CoercibleProperty, CssProperty, Style, View, ContainerView, Template, KeyedTemplate, Length, Property, Color, Observable, EventData, CSSType } from "../core/view";
import { parse, parseMultipleTemplates } from "../builder";
import { Label } from "../label";
import { ObservableArray, ChangedData } from "../../data/observable-array";

View File

@@ -7,7 +7,6 @@ import { StackLayout } from "../layouts/stack-layout";
import { ProxyViewContainer } from "../proxy-view-container";
import { profile } from "../../profiling";
import * as trace from "../../trace";
import { ios as iosUtils } from "../../utils/utils";
export * from "./list-view-common";
@@ -23,7 +22,6 @@ interface ViewItemIndex {
}
type ItemView = View & ViewItemIndex;
const majorVersion = iosUtils.MajorVersion;
class ListViewCell extends UITableViewCell {
public static initWithEmptyBackground(): ListViewCell {

View File

@@ -17,17 +17,17 @@ export class PageBase extends ContentView implements PageDefinition {
public static navigatedToEvent = "navigatedTo";
public static navigatingFromEvent = "navigatingFrom";
public static navigatedFromEvent = "navigatedFrom";
private _navigationContext: any;
private _actionBar: ActionBar;
public _frame: Frame;
public actionBarHidden: boolean;
public enableSwipeBackNavigation: boolean;
public backgroundSpanUnderStatusBar: boolean;
public hasActionBar: boolean;
get navigationContext(): any {
return this._navigationContext;
}
@@ -89,7 +89,7 @@ export class PageBase extends ContentView implements PageDefinition {
const frame = this.parent;
return frame instanceof Frame ? frame : undefined;
}
private createNavigatedData(eventName: string, isBackNavigation: boolean): NavigatedData {
return {
eventName: eventName,
@@ -103,6 +103,13 @@ export class PageBase extends ContentView implements PageDefinition {
public onNavigatingTo(context: any, isBackNavigation: boolean, bindingContext?: any) {
this._navigationContext = context;
if (isBackNavigation && this._styleScope) {
this._styleScope.ensureSelectors();
if (!this._cssState.isSelectorsLatestVersionApplied()) {
this._onCssStateChange();
}
}
//https://github.com/NativeScript/NativeScript/issues/731
if (!isBackNavigation && bindingContext !== undefined && bindingContext !== null) {
this.bindingContext = bindingContext;

View File

@@ -3,9 +3,6 @@ import {
View, layout, ScrollViewBase, scrollBarIndicatorVisibleProperty, isScrollEnabledProperty
} from "./scroll-view-common";
import { ios as iosUtils } from "../../utils/utils";
// HACK: Webpack. Use a fully-qualified import to allow resolve.extensions(.ios.js) to
// kick in. `../utils` doesn't seem to trigger the webpack extensions mechanism.
import * as uiUtils from "tns-core-modules/ui/utils";
export * from "./scroll-view-common";
@@ -193,18 +190,4 @@ export class ScrollView extends ScrollViewBase {
}
}
function getTabBarHeight(scrollView: ScrollView): number {
let parent = scrollView.parent;
while (parent) {
const controller = parent.viewController;
if (controller instanceof UITabBarController) {
return uiUtils.ios.getActualHeight(controller.tabBar);
}
parent = parent.parent;
}
return 0;
}
ScrollView.prototype.recycleNativeView = "auto";

View File

@@ -19,6 +19,11 @@ export class CssState {
* Gets the static selectors that match the view and the dynamic selectors that may potentially match the view.
*/
public changeMap: ChangeMap<ViewBase>;
/**
* Checks whether style scope and CSS state selectors are in sync.
*/
public isSelectorsLatestVersionApplied(): boolean
}
export class StyleScope {
@@ -29,6 +34,9 @@ export class StyleScope {
public static createSelectorsFromImports(tree: SyntaxTree, keyframes: Object): RuleSet[];
public ensureSelectors(): number;
public isApplicationCssSelectorsLatestVersionApplied(): boolean;
public isLocalCssSelectorsLatestVersionApplied(): boolean;
public applySelectors(view: ViewBase): void
public query(options: Node): SelectorCore[];

View File

@@ -271,7 +271,7 @@ export function removeTaggedAdditionalCSS(tag: String | Number): Boolean {
changed = true;
}
}
if (changed) { mergeCssSelectors(); }
if (changed) { mergeCssSelectors(); }
return changed;
}
@@ -343,6 +343,7 @@ export class CssState {
_appliedChangeMap: Readonly<ChangeMap<ViewBase>>;
_appliedPropertyValues: Readonly<{}>;
_appliedAnimations: ReadonlyArray<kam.KeyframeAnimation>;
_appliedSelectorsVersion: number;
_match: SelectorsMatch<ViewBase>;
_matchInvalid: boolean;
@@ -367,6 +368,10 @@ export class CssState {
}
}
public isSelectorsLatestVersionApplied(): boolean {
return this.view._styleScope._getSelectorsVersion() === this._appliedSelectorsVersion;
}
public onLoaded(): void {
if (this._matchInvalid) {
this.updateMatch();
@@ -381,7 +386,12 @@ export class CssState {
@profile
private updateMatch() {
this._match = this.view._styleScope ? this.view._styleScope.matchSelectors(this.view) : CssState.emptyMatch;
if (this.view._styleScope) {
this._appliedSelectorsVersion = this.view._styleScope._getSelectorsVersion();
this._match = this.view._styleScope.matchSelectors(this.view);
} else {
this._match = CssState.emptyMatch;
}
this._matchInvalid = false;
}
@@ -597,8 +607,8 @@ export class StyleScope {
}
public ensureSelectors(): number {
if (this._applicationCssSelectorsAppliedVersion !== applicationCssSelectorVersion ||
this._localCssSelectorVersion !== this._localCssSelectorsAppliedVersion ||
if (!this.isApplicationCssSelectorsLatestVersionApplied() ||
!this.isLocalCssSelectorsLatestVersionApplied() ||
!this._mergedCssSelectors) {
this._createSelectors();
@@ -607,6 +617,14 @@ export class StyleScope {
return this._getSelectorsVersion();
}
public isApplicationCssSelectorsLatestVersionApplied(): boolean {
return this._applicationCssSelectorsAppliedVersion === applicationCssSelectorVersion;
}
public isLocalCssSelectorsLatestVersionApplied(): boolean {
return this._localCssSelectorsAppliedVersion === this._localCssSelectorVersion;
}
@profile
private _createSelectors() {
let toMerge: RuleSet[][] = [];

View File

@@ -127,7 +127,6 @@ export module ios {
}
if (rootViewController.isKindOfClass(UITabBarController.class())) {
let selectedTab = (<UITabBarController>rootViewController).selectedViewController;
return getVisibleViewController(<UITabBarController>rootViewController);
}

View File

@@ -5,10 +5,12 @@
export class CompleteCallback {
constructor(implementation: ICompleteCallback);
onComplete(result: Object, context: Object): void;
onError(error: string, context: Object): void;
}
export interface ICompleteCallback {
onComplete(result: Object, context: Object): void;
onError(error: string, context: Object): void;
}
export module Image {

View File

@@ -1,6 +1,6 @@
{
"name": "tns-platform-declarations",
"version": "5.1.2",
"version": "5.2.0",
"description": "Platform-specific TypeScript declarations for NativeScript for accessing native objects",
"main": "",
"scripts": {