From d7fb9b8d9e3b9ba330fa6265b2179dcfa674af0d Mon Sep 17 00:00:00 2001 From: Pedro Teran Gezn Date: Wed, 20 Mar 2019 05:18:48 -0400 Subject: [PATCH 1/3] fix(http): ensure httpcontent.toFile() creates intermediate directories (#6451) --- tests/app/http/http-tests.ts | 28 +++++-- tests/package.json | 4 +- .../http/http-request/http-request.android.ts | 80 +++++++------------ .../http/http-request/http-request.ios.ts | 78 +++++++++--------- 4 files changed, 92 insertions(+), 98 deletions(-) diff --git a/tests/app/http/http-tests.ts b/tests/app/http/http-tests.ts index a1f257f66..0eb045721 100644 --- a/tests/app/http/http-tests.ts +++ b/tests/app/http/http-tests.ts @@ -158,9 +158,9 @@ export var test_getJSON_fail_when_result_is_not_JSONP = function (done) { export var test_gzip_request_explicit = function(done) { var result; - http.request({ - url: "https://postman-echo.com/gzip", - method: "GET", + http.request({ + url: "https://postman-echo.com/gzip", + method: "GET", headers: { "Accept-Encoding": "gzip" }}).then(function (r) { @@ -180,8 +180,8 @@ export var test_gzip_request_explicit = function(done) { export var test_gzip_request_implicit = function(done) { var result; - http.request({ - url: "https://postman-echo.com/gzip", + http.request({ + url: "https://postman-echo.com/gzip", method: "GET"}).then(function (r) { result = r; try { @@ -524,6 +524,24 @@ export var test_request_responseContentToFileFromUrlShouldReturnCorrectFile = fu done(e); }); }; +export var test_request_responseContentToFileFromUrlShouldReturnCorrectFileAndCreateDirPathIfNecesary = function (done) { + var result; + + http.request({ url: "https://raw.githubusercontent.com/NativeScript/NativeScript/master/tests/app/logo.png", method: "GET" }).then(function (response) { + const filePath = fs.path.join(fs.knownFolders.temp().path, "test", "some", "path", "logo.png"); + result = response.content.toFile(filePath); + try { + TKUnit.assert(result instanceof fs.File, "Result from toFile() should be valid File object!"); + TKUnit.assert(result.size > 0, "result from to file should be greater than 0 in size"); + done(null); + } + catch (err) { + done(err); + } + }, function (e) { + done(e); + }); +}; export var test_request_responseContentToFileFromContentShouldReturnCorrectFile = function (done) { var result; diff --git a/tests/package.json b/tests/package.json index a36530bc3..b821ee2b6 100644 --- a/tests/package.json +++ b/tests/package.json @@ -6,10 +6,10 @@ "nativescript": { "id": "org.nativescript.UnitTestApp", "tns-ios": { - "version": "5.1.0" + "version": "5.2.0" }, "tns-android": { - "version": "5.1.0" + "version": "5.2.1" } }, "dependencies": { diff --git a/tns-core-modules/http/http-request/http-request.android.ts b/tns-core-modules/http/http-request/http-request.android.ts index ff986956f..a6a1f04bc 100644 --- a/tns-core-modules/http/http-request/http-request.android.ts +++ b/tns-core-modules/http/http-request/http-request.android.ts @@ -1,9 +1,9 @@ /** * Android specific http request implementation. */ -import * as imageSourceModule from "../../image-source"; -import * as platformModule from "../../platform"; -import * as fsModule from "../../file-system"; +import { fromNativeSource } from "../../image-source"; +import { screen } from "../../platform"; +import { File } from "../../file-system"; import { getFilenameFromUrl } from "./http-request-common"; // this is imported for definition purposes only @@ -17,7 +17,7 @@ export enum HttpResponseEncoding { } function parseJSON(source: string): any { - var src = source.trim(); + const src = source.trim(); if (src.lastIndexOf(")") === src.length - 1) { return JSON.parse(src.substring(src.indexOf("(") + 1, src.lastIndexOf(")"))); } @@ -25,24 +25,10 @@ function parseJSON(source: string): any { return JSON.parse(src); } -var requestIdCounter = 0; -var pendingRequests = {}; +let requestIdCounter = 0; +const pendingRequests = {}; -var imageSource: typeof imageSourceModule; -function ensureImageSource() { - if (!imageSource) { - imageSource = require("image-source"); - } -} - -var platform: typeof platformModule; -function ensurePlatform() { - if (!platform) { - platform = require("platform"); - } -} - -var completeCallback: org.nativescript.widgets.Async.CompleteCallback; +let completeCallback: org.nativescript.widgets.Async.CompleteCallback; function ensureCompleteCallback() { if (completeCallback) { return; @@ -60,7 +46,7 @@ function ensureCompleteCallback() { } function onRequestComplete(requestId: number, result: org.nativescript.widgets.Async.Http.RequestResult) { - var callbacks = pendingRequests[requestId]; + const callbacks = pendingRequests[requestId]; delete pendingRequests[requestId]; if (result.error) { @@ -69,13 +55,12 @@ function onRequestComplete(requestId: number, result: org.nativescript.widgets.A } // read the headers - var headers: http.Headers = {}; + const headers: http.Headers = {}; if (result.headers) { - var jHeaders = result.headers; - var length = jHeaders.size(); - var i; - var pair: org.nativescript.widgets.Async.Http.KeyValuePair; - for (i = 0; i < length; i++) { + const jHeaders = result.headers; + const length = jHeaders.size(); + let pair: org.nativescript.widgets.Async.Http.KeyValuePair; + for (let i = 0; i < length; i++) { pair = jHeaders.get(i); addHeader(headers, pair.key, pair.value); } @@ -112,11 +97,9 @@ function onRequestComplete(requestId: number, result: org.nativescript.widgets.A return parseJSON(str); }, toImage: () => { - ensureImageSource(); - return new Promise((resolveImage, rejectImage) => { if (result.responseAsImage != null) { - resolveImage(imageSource.fromNativeSource(result.responseAsImage)); + resolveImage(fromNativeSource(result.responseAsImage)); } else { rejectImage(new Error("Response content may not be converted to an Image")); @@ -124,17 +107,19 @@ function onRequestComplete(requestId: number, result: org.nativescript.widgets.A }); }, toFile: (destinationFilePath: string) => { - var fs: typeof fsModule = require("file-system"); - if (!destinationFilePath) { destinationFilePath = getFilenameFromUrl(callbacks.url); } - var stream: java.io.FileOutputStream; + let stream: java.io.FileOutputStream; try { - var javaFile = new java.io.File(destinationFilePath); + // ensure destination path exists by creating any missing parent directories + const file = File.fromPath(destinationFilePath); + + const javaFile = new java.io.File(destinationFilePath); stream = new java.io.FileOutputStream(javaFile); stream.write(result.raw.toByteArray()); - return fs.File.fromPath(destinationFilePath); + + return file; } catch (exception) { throw new Error(`Cannot save file with path: ${destinationFilePath}.`); @@ -152,7 +137,7 @@ function onRequestComplete(requestId: number, result: org.nativescript.widgets.A } function onRequestError(error: string, requestId: number) { - var callbacks = pendingRequests[requestId]; + const callbacks = pendingRequests[requestId]; delete pendingRequests[requestId]; if (callbacks) { callbacks.rejectCallback(new Error(error)); @@ -164,7 +149,7 @@ function buildJavaOptions(options: http.HttpRequestOptions) { throw new Error("Http request must provide a valid url."); } - var javaOptions = new org.nativescript.widgets.Async.Http.RequestOptions(); + const javaOptions = new org.nativescript.widgets.Async.Http.RequestOptions(); javaOptions.url = options.url; @@ -182,22 +167,19 @@ function buildJavaOptions(options: http.HttpRequestOptions) { } if (options.headers) { - var arrayList = new java.util.ArrayList(); - var pair = org.nativescript.widgets.Async.Http.KeyValuePair; + const arrayList = new java.util.ArrayList(); + const pair = org.nativescript.widgets.Async.Http.KeyValuePair; - for (var key in options.headers) { + for (let key in options.headers) { arrayList.add(new pair(key, options.headers[key] + "")); } javaOptions.headers = arrayList; } - ensurePlatform(); - // pass the maximum available image size to the request options in case we need a bitmap conversion - var screen = platform.screen.mainScreen; - javaOptions.screenWidth = screen.widthPixels; - javaOptions.screenHeight = screen.heightPixels; + javaOptions.screenWidth = screen.mainScreen.widthPixels; + javaOptions.screenHeight = screen.mainScreen.heightPixels; return javaOptions; } @@ -211,7 +193,7 @@ export function request(options: http.HttpRequestOptions): Promise((resolve, reject) => { try { // initialize the options - var javaOptions = buildJavaOptions(options); + const javaOptions = buildJavaOptions(options); // send request data to network debugger if (global.__inspector && global.__inspector.isConnected) { @@ -219,7 +201,7 @@ export function request(options: http.HttpRequestOptions): Promiseheaders[key]).push(value); } else { - let values: string[] = [headers[key]]; + const values: string[] = [headers[key]]; values.push(value); headers[key] = values; } diff --git a/tns-core-modules/http/http-request/http-request.ios.ts b/tns-core-modules/http/http-request/http-request.ios.ts index 5b9b6716d..6e02247b2 100644 --- a/tns-core-modules/http/http-request/http-request.ios.ts +++ b/tns-core-modules/http/http-request/http-request.ios.ts @@ -2,10 +2,10 @@ * iOS specific http request implementation. */ -import * as http from "../../http"; +import { HttpRequestOptions, HttpResponse, Headers } from "../../http"; import * as types from "../../utils/types"; -import * as imageSourceModule from "../../image-source"; -import * as fsModule from "../../file-system"; +import { fromNativeSource } from "../../image-source"; +import { File } from "../../file-system"; import * as utils from "../../utils/utils"; import getter = utils.ios.getter; @@ -18,18 +18,18 @@ export enum HttpResponseEncoding { GBK } -var currentDevice = utils.ios.getter(UIDevice, UIDevice.currentDevice); -var device = currentDevice.userInterfaceIdiom === UIUserInterfaceIdiom.Phone ? "Phone" : "Pad"; -var osVersion = currentDevice.systemVersion; +const currentDevice = getter(UIDevice, UIDevice.currentDevice); +const device = currentDevice.userInterfaceIdiom === UIUserInterfaceIdiom.Phone ? "Phone" : "Pad"; +const osVersion = currentDevice.systemVersion; -var GET = "GET"; -var USER_AGENT_HEADER = "User-Agent"; -var USER_AGENT = `Mozilla/5.0 (i${device}; CPU OS ${osVersion.replace(".", "_")} like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/${osVersion} Mobile/10A5355d Safari/8536.25`; -var sessionConfig = getter(NSURLSessionConfiguration, NSURLSessionConfiguration.defaultSessionConfiguration); -var queue = getter(NSOperationQueue, NSOperationQueue.mainQueue); +const GET = "GET"; +const USER_AGENT_HEADER = "User-Agent"; +const USER_AGENT = `Mozilla/5.0 (i${device}; CPU OS ${osVersion.replace(".", "_")} like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/${osVersion} Mobile/10A5355d Safari/8536.25`; +const sessionConfig = getter(NSURLSessionConfiguration, NSURLSessionConfiguration.defaultSessionConfiguration); +const queue = getter(NSOperationQueue, NSOperationQueue.mainQueue); function parseJSON(source: string): any { - var src = source.trim(); + const src = source.trim(); if (src.lastIndexOf(")") === src.length - 1) { return JSON.parse(src.substring(src.indexOf("(") + 1, src.lastIndexOf(")"))); } @@ -43,31 +43,24 @@ class NSURLSessionTaskDelegateImpl extends NSObject implements NSURLSessionTaskD completionHandler(null); } } -var sessionTaskDelegateInstance: NSURLSessionTaskDelegateImpl = NSURLSessionTaskDelegateImpl.new(); +const sessionTaskDelegateInstance: NSURLSessionTaskDelegateImpl = NSURLSessionTaskDelegateImpl.new(); -var defaultSession; +let defaultSession; function ensureDefaultSession() { if (!defaultSession) { defaultSession = NSURLSession.sessionWithConfigurationDelegateDelegateQueue(sessionConfig, null, queue); } } -var sessionNotFollowingRedirects; +let sessionNotFollowingRedirects; function ensureSessionNotFollowingRedirects() { if (!sessionNotFollowingRedirects) { sessionNotFollowingRedirects = NSURLSession.sessionWithConfigurationDelegateDelegateQueue(sessionConfig, sessionTaskDelegateInstance, queue); } } -var imageSource: typeof imageSourceModule; -function ensureImageSource() { - if (!imageSource) { - imageSource = require("image-source"); - } -} - -export function request(options: http.HttpRequestOptions): Promise { - return new Promise((resolve, reject) => { +export function request(options: HttpRequestOptions): Promise { + return new Promise((resolve, reject) => { if (!options.url) { reject(new Error("Request url was empty.")); @@ -75,10 +68,10 @@ export function request(options: http.HttpRequestOptions): Promise { addHeader(headers, key, value); @@ -125,7 +118,7 @@ export function request(options: http.HttpRequestOptions): Promise NSDataToString(data, encoding), toJSON: (encoding?: any) => parseJSON(NSDataToString(data, encoding)), toImage: () => { - ensureImageSource(); return new Promise((resolve, reject) => { (UIImage).tns_decodeImageWithDataCompletion(data, image => { if (image) { - resolve(imageSource.fromNativeSource(image)) + resolve(fromNativeSource(image)); } else { reject(new Error("Response content may not be converted to an Image")); } }); }); }, - toFile: (destinationFilePath?: string) => { - var fs: typeof fsModule = require("file-system"); - + toFile: (destinationFilePath?: string) => { if (!destinationFilePath) { destinationFilePath = getFilenameFromUrl(options.url); } if (data instanceof NSData) { + // ensure destination path exists by creating any missing parent directories + const file = File.fromPath(destinationFilePath); + data.writeToFileAtomically(destinationFilePath, true); - return fs.File.fromPath(destinationFilePath); + + return file; } else { reject(new Error(`Cannot save file with path: ${destinationFilePath}.`)); } @@ -175,7 +169,7 @@ export function request(options: http.HttpRequestOptions): Promiseheaders[key]).push(value); } else { - let values: string[] = [headers[key]]; + const values: string[] = [headers[key]]; values.push(value); headers[key] = values; } From f9e008fd54d655c37375cdfd8a592d7d909b156d Mon Sep 17 00:00:00 2001 From: Vasil Chimev Date: Wed, 20 Mar 2019 14:42:50 +0200 Subject: [PATCH 2/3] fix(android): throw new Error if no activity callbacks (#7044) --- tns-core-modules/application/application.android.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tns-core-modules/application/application.android.ts b/tns-core-modules/application/application.android.ts index 07247a9eb..79f0cc56f 100644 --- a/tns-core-modules/application/application.android.ts +++ b/tns-core-modules/application/application.android.ts @@ -163,6 +163,9 @@ export function _resetRootView(entry?: NavigationEntry | string) { createRootFrame.value = false; mainEntry = typeof entry === "string" ? { moduleName: entry } : entry; const callbacks: AndroidActivityCallbacks = activity[CALLBACKS]; + if (!callbacks) { + throw new Error("Cannot find android activity callbacks."); + } callbacks.resetActivityContent(activity); } From 2efafc3a4fba66ce269ba74ac91aa3b5233a663b Mon Sep 17 00:00:00 2001 From: Svetoslav Date: Wed, 20 Mar 2019 18:15:48 +0200 Subject: [PATCH 3/3] test: update test samples (#7016) * test: update test samples --- apps/app/ui-tests-app/action-bar/icons.xml | 2 +- .../ui-tests-app/action-bar/local-icons.xml | 2 +- apps/app/ui-tests-app/intent/main-page.ts | 17 ------ apps/app/ui-tests-app/intent/main-page.xml | 6 -- apps/app/ui-tests-app/intent/open-file.xml | 5 -- apps/app/ui-tests-app/issues/main-page.ts | 1 + .../open-file.ts => issues/open-file-6895.ts} | 0 .../ui-tests-app/issues/open-file-6895.xml | 5 ++ .../ui-tests-app/layouts/passThroughParent.ts | 51 +++++++++++------ .../layouts/passThroughParent.xml | 36 ++++++------ apps/app/ui-tests-app/main-page.ts | 1 - .../scroll-view/layout-outside-scroll.xml | 8 +-- .../tab-view/icon-title-placement.xml | 6 +- apps/app/ui-tests-app/tab-view/issue-5470.xml | 4 +- .../app/home/home-page.ts | 5 ++ .../app/home/home-page.xml | 1 + .../e2e/issues.e2e.spec.ts | 55 +++++++++++++++++++ e2e/nested-frame-navigation/e2e/screen.ts | 14 ++--- 18 files changed, 139 insertions(+), 80 deletions(-) delete mode 100644 apps/app/ui-tests-app/intent/main-page.ts delete mode 100644 apps/app/ui-tests-app/intent/main-page.xml delete mode 100644 apps/app/ui-tests-app/intent/open-file.xml rename apps/app/ui-tests-app/{intent/open-file.ts => issues/open-file-6895.ts} (100%) create mode 100644 apps/app/ui-tests-app/issues/open-file-6895.xml create mode 100644 e2e/nested-frame-navigation/e2e/issues.e2e.spec.ts diff --git a/apps/app/ui-tests-app/action-bar/icons.xml b/apps/app/ui-tests-app/action-bar/icons.xml index 7862fe83b..cde351035 100644 --- a/apps/app/ui-tests-app/action-bar/icons.xml +++ b/apps/app/ui-tests-app/action-bar/icons.xml @@ -8,7 +8,7 @@ - - - \ No newline at end of file diff --git a/apps/app/ui-tests-app/issues/main-page.ts b/apps/app/ui-tests-app/issues/main-page.ts index 349b9634c..7afaa8783 100644 --- a/apps/app/ui-tests-app/issues/main-page.ts +++ b/apps/app/ui-tests-app/issues/main-page.ts @@ -30,6 +30,7 @@ export function loadExamples() { examples.set("ng-repo-1599", "issues/issue-ng-repo-1599"); examples.set("ng-repo-1626", "issues/issue-ng-repo-1626"); examples.set("6439", "issues/issue-6439"); + examples.set("open-file-6895", "issues/open-file-6895") return examples; } \ No newline at end of file diff --git a/apps/app/ui-tests-app/intent/open-file.ts b/apps/app/ui-tests-app/issues/open-file-6895.ts similarity index 100% rename from apps/app/ui-tests-app/intent/open-file.ts rename to apps/app/ui-tests-app/issues/open-file-6895.ts diff --git a/apps/app/ui-tests-app/issues/open-file-6895.xml b/apps/app/ui-tests-app/issues/open-file-6895.xml new file mode 100644 index 000000000..fc938d3ff --- /dev/null +++ b/apps/app/ui-tests-app/issues/open-file-6895.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/app/ui-tests-app/layouts/passThroughParent.ts b/apps/app/ui-tests-app/layouts/passThroughParent.ts index 1080e0b3c..2406d170c 100644 --- a/apps/app/ui-tests-app/layouts/passThroughParent.ts +++ b/apps/app/ui-tests-app/layouts/passThroughParent.ts @@ -1,19 +1,36 @@ -export function onOuterWrapLayoutTap() { - console.log("on outer wrap layout tap"); -} +import { EventData, Page } from "tns-core-modules/ui/page/page"; +import { Label } from "tns-core-modules/ui/label/label"; -export function onStackLayoutThrowTap() { - throw new Error("Should not tap layout with IsPassThroughParentEnabled=true"); -} - -export function onUserInteractionDisabledTap() { - throw new Error("Should not tap button with IsUserInteractionEnabled=false"); -} - -export function onDisabledThrowTap() { - throw new Error("Should not tap button with IsEnabled=false"); -} - -export function onTap() { +const setLabelTextAndLog = (args, text: string) => { + const page = args.object.page; + const label =