Skip to content

Commit a359559

Browse files
author
Fatme
authored
Merge pull request #4089 from NativeScript/release-5.0.1
Release 5.0.1
2 parents a9b5644 + 2e45034 commit a359559

38 files changed

+540
-199
lines changed

lib/bootstrap.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ $injector.require("usbLiveSyncService", "./services/livesync/livesync-service");
134134
$injector.require("previewAppLiveSyncService", "./services/livesync/playground/preview-app-livesync-service");
135135
$injector.require("previewAppPluginsService", "./services/livesync/playground/preview-app-plugins-service");
136136
$injector.require("previewSdkService", "./services/livesync/playground/preview-sdk-service");
137-
$injector.require("playgroundQrCodeGenerator", "./services/livesync/playground/qr-code-generator");
137+
$injector.requirePublicClass("previewDevicesService", "./services/livesync/playground/devices/preview-devices-service");
138+
$injector.requirePublic("previewQrCodeService", "./services/livesync/playground/preview-qr-code-service");
138139
$injector.requirePublic("sysInfo", "./sys-info");
139140

140141
$injector.require("iOSNotificationService", "./services/ios-notification-service");

lib/commands/preview.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ export class PreviewCommand implements ICommand {
33
private static MIN_SUPPORTED_WEBPACK_VERSION = "0.17.0";
44

55
constructor(private $bundleValidatorHelper: IBundleValidatorHelper,
6+
private $errors: IErrors,
67
private $liveSyncService: ILiveSyncService,
78
private $networkConnectivityValidator: INetworkConnectivityValidator,
89
private $projectData: IProjectData,
910
private $options: IOptions,
10-
private $playgroundQrCodeGenerator: IPlaygroundQrCodeGenerator) { }
11+
private $previewQrCodeService: IPreviewQrCodeService) { }
1112

1213
public async execute(): Promise<void> {
1314
await this.$liveSyncService.liveSync([], {
@@ -23,10 +24,14 @@ export class PreviewCommand implements ICommand {
2324
useHotModuleReload: this.$options.hmr
2425
});
2526

26-
await this.$playgroundQrCodeGenerator.generateQrCode({ useHotModuleReload: this.$options.hmr, link: this.$options.link });
27+
await this.$previewQrCodeService.printLiveSyncQrCode({ useHotModuleReload: this.$options.hmr, link: this.$options.link });
2728
}
2829

2930
public async canExecute(args: string[]): Promise<boolean> {
31+
if (args && args.length) {
32+
this.$errors.fail(`The arguments '${args.join(" ")}' are not valid for the preview command.`);
33+
}
34+
3035
await this.$networkConnectivityValidator.validate();
3136
this.$bundleValidatorHelper.validate(PreviewCommand.MIN_SUPPORTED_WEBPACK_VERSION);
3237
return true;

lib/commands/test-init.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,10 @@ class TestInitCommand implements ICommand {
9393
await this.$pluginsService.add('nativescript-unit-test-runner', this.$projectData);
9494

9595
const testsDir = path.join(this.$projectData.appDirectoryPath, 'tests');
96+
const relativeTestsDir = path.relative(this.$projectData.projectDir, testsDir);
9697
let shouldCreateSampleTests = true;
9798
if (this.$fs.exists(testsDir)) {
98-
this.$logger.info('app/tests/ directory already exists, will not create an example test project.');
99+
this.$logger.info(`${relativeTestsDir} directory already exists, will not create an example test project.`);
99100
shouldCreateSampleTests = false;
100101
}
101102

@@ -104,18 +105,19 @@ class TestInitCommand implements ICommand {
104105
const frameworks = [frameworkToInstall].concat(this.karmaConfigAdditionalFrameworks[frameworkToInstall] || [])
105106
.map(fw => `'${fw}'`)
106107
.join(', ');
108+
const testFiles = `'${relativeTestsDir}/**/*.js'`;
107109
const karmaConfTemplate = this.$resources.readText('test/karma.conf.js');
108-
const karmaConf = _.template(karmaConfTemplate)({ frameworks });
110+
const karmaConf = _.template(karmaConfTemplate)({ frameworks, testFiles });
109111

110112
this.$fs.writeFile(path.join(projectDir, 'karma.conf.js'), karmaConf);
111113

112114
const exampleFilePath = this.$resources.resolvePath(`test/example.${frameworkToInstall}.js`);
113115

114116
if (shouldCreateSampleTests && this.$fs.exists(exampleFilePath)) {
115117
this.$fs.copyFile(exampleFilePath, path.join(testsDir, 'example.js'));
116-
this.$logger.info('\nExample test file created in app/tests/'.yellow);
118+
this.$logger.info(`\nExample test file created in ${relativeTestsDir}`.yellow);
117119
} else {
118-
this.$logger.info('\nPlace your test files under app/tests/'.yellow);
120+
this.$logger.info(`\nPlace your test files under ${relativeTestsDir}`.yellow);
119121
}
120122

121123
this.$logger.info('Run your tests using the "$ tns test <platform>" command.'.yellow);

lib/common/constants.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,5 +155,5 @@ export class AndroidVirtualDevice {
155155
static TIMEOUT_SECONDS = 120;
156156
static GENYMOTION_DEFAULT_STDERR_STRING = "Logging activities to file";
157157

158-
static UNABLE_TO_START_EMULATOR_MESSAGE = "Cannot run your app in the native emulator. Increase the timeout of the operation with the --timeout option or try to restart your adb server with 'adb kill-server' command. Alternatively, run the Android Virtual Device manager and increase the allocated RAM for the virtual device.";
158+
static UNABLE_TO_START_EMULATOR_MESSAGE = "Cannot run the app in the selected native emulator. Try to restart the adb server by running the `adb kill-server` command in the Command Prompt, or increase the allocated RAM of the virtual device through the Android Virtual Device manager. NativeScript CLI users can try to increase the timeout of the operation by adding the `--timeout` flag.";
159159
}

lib/common/declarations.d.ts

+15
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,21 @@ interface IQrCodeGenerator {
969969
generateDataUri(data: string): Promise<string>;
970970
}
971971

972+
interface IQrCodeImageData {
973+
/**
974+
* The original URL used for generating QR code image.
975+
*/
976+
originalUrl: string;
977+
/**
978+
* The shorten URL used for generating QR code image.
979+
*/
980+
shortenUrl: string;
981+
/**
982+
* Base64 encoded data used for generating QR code image.
983+
*/
984+
imageData: string;
985+
}
986+
972987
interface IDynamicHelpProvider {
973988
/**
974989
* Checks if current project's framework is one of the specified as arguments.

lib/common/definitions/mobile.d.ts

+23-5
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,6 @@ declare module Mobile {
143143
--predicate 'eventType == logEvent and subsystem contains "com.example.my_subsystem"'
144144
*/
145145
predicate?: string;
146-
/**
147-
* If set to true, device's log will not be displayed on the console.
148-
*/
149-
muted?: boolean;
150146
}
151147

152148
interface IDeviceAppData extends IPlatform, IConnectTimeoutOption {
@@ -221,12 +217,18 @@ declare module Mobile {
221217
* @param {string} projectName The project name of the currently running application for which we need the logs.
222218
*/
223219
setProjectNameForDevice(deviceIdentifier: string, projectName: string): void;
220+
221+
/**
222+
* Disables logs on the specified device and does not print any logs on the console.
223+
* @param {string} deviceIdentifier The unique identifier of the device.
224+
*/
225+
muteLogsForDevice(deviceIdentifier: string): void;
224226
}
225227

226228
/**
227229
* Describes different options for filtering device logs.
228230
*/
229-
interface IDeviceLogOptions extends IStringDictionary {
231+
interface IDeviceLogOptions extends IDictionary<string | boolean> {
230232
/**
231233
* Process id of the application on the device.
232234
*/
@@ -241,6 +243,11 @@ declare module Mobile {
241243
* The project name.
242244
*/
243245
projectName?: string;
246+
247+
/**
248+
* Specifies if the logs will be printed on the console.
249+
*/
250+
muteLogs?: boolean;
244251
}
245252

246253
/**
@@ -730,6 +737,12 @@ declare module Mobile {
730737
* @returns {Promise<IStartEmulatorOutput>} Starts the emulator and returns the errors if some error occurs.
731738
*/
732739
startEmulator(options: Mobile.IStartEmulatorOptions): Promise<IStartEmulatorOutput>;
740+
741+
/**
742+
* Called when emulator is lost. Its purpose is to clean any resources used by the instance.
743+
* @returns {void}
744+
*/
745+
detach?(deviceInfo: Mobile.IDeviceInfo): void;
733746
}
734747

735748
interface IStartEmulatorOutput {
@@ -772,6 +785,11 @@ declare module Mobile {
772785
* @param imageIdentifier - The imagerIdentifier of the emulator.
773786
*/
774787
startEmulatorArgs(imageIdentifier: string): string[];
788+
/**
789+
* Called when emulator is lost. Its purpose is to clean any resources used by the instance.
790+
* @returns {void}
791+
*/
792+
detach?(deviceInfo: Mobile.IDeviceInfo): void;
775793
}
776794

777795
interface IVirtualBoxService {

lib/common/mobile/android/android-device.ts

+6
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,12 @@ export class AndroidDevice implements Mobile.IAndroidDevice {
121121
}
122122
}
123123

124+
public detach(): void {
125+
if (this.isEmulator) {
126+
this.$androidEmulatorServices.detach(this.deviceInfo);
127+
}
128+
}
129+
124130
private async getDeviceDetails(shellCommandArgs: string[]): Promise<IAndroidDeviceDetails> {
125131
const parsedDetails: any = {};
126132

lib/common/mobile/android/android-emulator-services.ts

+4
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ export class AndroidEmulatorServices implements Mobile.IEmulatorPlatformService
5959
};
6060
}
6161

62+
public detach(deviceInfo: Mobile.IDeviceInfo) {
63+
this.$androidVirtualDeviceService.detach(deviceInfo);
64+
}
65+
6266
private async startEmulatorCore(options: Mobile.IAndroidStartEmulatorOptions): Promise<{runningEmulator: Mobile.IDeviceInfo, errors: string[], endTimeEpoch: number}> {
6367
const timeout = options.timeout || AndroidVirtualDevice.TIMEOUT_SECONDS;
6468
const endTimeEpoch = getCurrentEpochTime() + this.$utils.getMilliSecondsTimeout(timeout);

lib/common/mobile/android/android-virtual-device-service.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,16 @@ export class AndroidVirtualDeviceService implements Mobile.IAndroidVirtualDevice
142142
});
143143
}
144144

145+
public detach(deviceInfo: Mobile.IDeviceInfo) {
146+
if (this.mapEmulatorIdToImageIdentifier[deviceInfo.identifier]) {
147+
delete this.mapEmulatorIdToImageIdentifier[deviceInfo.identifier];
148+
}
149+
}
150+
145151
private async getEmulatorImagesCore(): Promise<Mobile.IEmulatorImagesOutput> {
146152
let result: ISpawnResult = null;
147153
let devices: Mobile.IDeviceInfo[] = [];
154+
let errors: string[] = [];
148155

149156
if (this.pathToAvdManagerExecutable && this.$fs.exists(this.pathToAvdManagerExecutable)) {
150157
result = await this.$childProcess.trySpawnFromCloseEvent(this.pathToAvdManagerExecutable, ["list", "avds"]);
@@ -154,11 +161,12 @@ export class AndroidVirtualDeviceService implements Mobile.IAndroidVirtualDevice
154161

155162
if (result && result.stdout) {
156163
devices = this.parseListAvdsOutput(result.stdout);
164+
errors = result && result.stderr ? [result.stderr] : [];
157165
} else {
158166
devices = this.listAvdsFromDirectory();
159167
}
160168

161-
return { devices, errors: result && result.stderr ? [result.stderr] : [] };
169+
return { devices, errors };
162170
}
163171

164172
private async getRunningEmulatorData(runningEmulatorId: string, availableEmulators: Mobile.IDeviceInfo[]): Promise<Mobile.IDeviceInfo> {

lib/common/mobile/android/logcat-helper.ts

+14-14
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,6 @@ export class LogcatHelper implements Mobile.ILogcatHelper {
5858
}
5959
}
6060

61-
private async getLogcatStream(deviceIdentifier: string, pid?: string) {
62-
const device = await this.$devicesService.getDevice(deviceIdentifier);
63-
const minAndroidWithLogcatPidSupport = "7.0.0";
64-
const isLogcatPidSupported = semver.gte(semver.coerce(device.deviceInfo.version), minAndroidWithLogcatPidSupport);
65-
const adb: Mobile.IDeviceAndroidDebugBridge = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: deviceIdentifier });
66-
const logcatCommand = ["logcat"];
67-
68-
if (pid && isLogcatPidSupported) {
69-
logcatCommand.push(`--pid=${pid}`);
70-
}
71-
const logcatStream = await adb.executeCommand(logcatCommand, { returnChildProcess: true });
72-
return logcatStream;
73-
}
74-
7561
public async dump(deviceIdentifier: string): Promise<void> {
7662
const adb: Mobile.IDeviceAndroidDebugBridge = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: deviceIdentifier });
7763
const logcatDumpStream = await adb.executeCommand(["logcat", "-d"], { returnChildProcess: true });
@@ -101,6 +87,20 @@ export class LogcatHelper implements Mobile.ILogcatHelper {
10187
delete this.mapDevicesLoggingData[deviceIdentifier];
10288
}
10389
}
90+
91+
private async getLogcatStream(deviceIdentifier: string, pid?: string) {
92+
const device = await this.$devicesService.getDevice(deviceIdentifier);
93+
const minAndroidWithLogcatPidSupport = "7.0.0";
94+
const isLogcatPidSupported = !!device.deviceInfo.version && semver.gte(semver.coerce(device.deviceInfo.version), minAndroidWithLogcatPidSupport);
95+
const adb: Mobile.IDeviceAndroidDebugBridge = this.$injector.resolve(DeviceAndroidDebugBridge, { identifier: deviceIdentifier });
96+
const logcatCommand = ["logcat"];
97+
98+
if (pid && isLogcatPidSupported) {
99+
logcatCommand.push(`--pid=${pid}`);
100+
}
101+
const logcatStream = await adb.executeCommand(logcatCommand, { returnChildProcess: true });
102+
return logcatStream;
103+
}
104104
}
105105

106106
$injector.register("logcatHelper", LogcatHelper);

lib/common/mobile/device-log-provider-base.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ export abstract class DeviceLogProviderBase extends EventEmitter implements Mobi
2626
this.setLogLevel(logLevel, deviceIdentifier);
2727
}
2828

29+
public muteLogsForDevice(deviceIdentifier: string): void {
30+
this.setDeviceLogOptionsProperty(deviceIdentifier, (deviceLogOptions: Mobile.IDeviceLogOptions) => deviceLogOptions.muteLogs, true);
31+
}
32+
2933
protected getApplicationPidForDevice(deviceIdentifier: string): string {
3034
return this.devicesLogOptions[deviceIdentifier] && this.devicesLogOptions[deviceIdentifier].applicationPid;
3135
}
@@ -39,7 +43,7 @@ export abstract class DeviceLogProviderBase extends EventEmitter implements Mobi
3943
return this.devicesLogOptions[deviceIdentifier];
4044
}
4145

42-
protected setDeviceLogOptionsProperty(deviceIdentifier: string, propNameFunction: Function, propertyValue: string): void {
46+
protected setDeviceLogOptionsProperty(deviceIdentifier: string, propNameFunction: Function, propertyValue: string | boolean): void {
4347
const propertyName = getPropertyName(propNameFunction);
4448

4549
if (propertyName) {

lib/common/mobile/device-log-provider.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,19 @@ export class DeviceLogProvider extends DeviceLogProviderBase {
1111
const loggingOptions = this.getDeviceLogOptionsForDevice(deviceIdentifier);
1212
const data = this.$logFilter.filterData(platform, lineText, loggingOptions);
1313
if (data) {
14-
this.$logger.write(data);
14+
this.logDataCore(data, loggingOptions);
1515
this.emit(DEVICE_LOG_EVENT_NAME, lineText, deviceIdentifier, platform);
1616
}
1717
}
1818

1919
public setLogLevel(logLevel: string, deviceIdentifier?: string): void {
2020
this.$logFilter.loggingLevel = logLevel.toUpperCase();
2121
}
22+
23+
private logDataCore(data: string, loggingOptions: Mobile.IDeviceLogOptions): void {
24+
if (!loggingOptions || (loggingOptions && !loggingOptions.muteLogs)) {
25+
this.$logger.write(data);
26+
}
27+
}
2228
}
2329
$injector.register("deviceLogProvider", DeviceLogProvider);

lib/common/test/unit-tests/mobile/android-virtual-device-service.ts

+20-3
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,13 @@ describe("androidVirtualDeviceService", () => {
176176
assert.deepEqual(result.devices, []);
177177
assert.deepEqual(result.errors, []);
178178
});
179-
it("should return an empty array when `avdmanager list avds` command fails", async () => {
179+
it("should return an empty array and no errors when `avdmanager list avds` command fails", async () => {
180180
const avdManagerError = "some error while executing avdmanager list avds";
181181
const avdService = mockAvdService({ avdManagerError });
182182
const result = await avdService.getEmulatorImages([]);
183183
assert.lengthOf(result.devices, 0);
184184
assert.deepEqual(result.devices, []);
185-
assert.lengthOf(result.errors, 1);
186-
assert.deepEqual(result.errors, [avdManagerError]);
185+
assert.lengthOf(result.errors, 0);
187186
});
188187
it("should return all emulators when there are available emulators and no running emulators", async () => {
189188
const avdService = mockAvdService({
@@ -214,6 +213,24 @@ describe("androidVirtualDeviceService", () => {
214213
assert.deepEqual(result[1], getAvailableEmulatorData({ displayName: "Nexus_5X_API_28", imageIdentifier: "Nexus_5X_API_28", version: "9.0.0", model: "Nexus 5X" }));
215214
assert.deepEqual(result[2], getAvailableEmulatorData({ displayName: "Nexus_6P_API_28", imageIdentifier: "Nexus_6P_API_28", version: "9.0.0", model: "Nexus 6P" }));
216215
});
216+
// In this case we should fallback to list avd directory and should't report errors from avdmanager
217+
it("should return devices and no errors when there is an error on avdmanager's stderr", async () => {
218+
const iniFilesData = getIniFilesData();
219+
const testInjector = createTestInjector({
220+
avdManagerOutput: "",
221+
avdManagerError: "my test error",
222+
iniFilesData
223+
});
224+
225+
const fs = testInjector.resolve("fs");
226+
fs.readDirectory = () => _.keys(iniFilesData);
227+
228+
const avdService = testInjector.resolve("androidVirtualDeviceService");
229+
const result = await avdService.getEmulatorImages(["emulator-5554 device"]);
230+
231+
assert.deepEqual(result.devices.length, 3);
232+
assert.deepEqual(result.errors.length, 0);
233+
});
217234
});
218235

219236
describe("when avdmanager is not found", () => {

lib/common/test/unit-tests/stubs.ts

+2
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,6 @@ export class DeviceLogProviderStub extends EventEmitter implements Mobile.IDevic
171171
setProjectNameForDevice(deviceIdentifier: string, projectName: string): void {
172172
this.currentDeviceProjectNames[deviceIdentifier] = projectName;
173173
}
174+
175+
muteLogsForDevice(deviceIdentifier: string): void { }
174176
}

lib/declarations.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ interface IiOSNotification extends NodeJS.EventEmitter {
709709
}
710710

711711
interface IiOSSocketRequestExecutor {
712-
executeLaunchRequest(deviceIdentifier: string, timeout: number, readyForAttachTimeout: number, projectId: string, shouldBreak?: boolean): Promise<void>;
712+
executeLaunchRequest(deviceIdentifier: string, timeout: number, readyForAttachTimeout: number, projectId: string, debugOptions: IDebugOptions): Promise<void>;
713713
executeAttachRequest(device: Mobile.IiOSDevice, timeout: number, projectId: string): Promise<void>;
714714
}
715715

lib/definitions/debug.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ interface IDebugOptions {
9595
* The sdk version of the emulator.
9696
*/
9797
sdk?: string;
98+
/**
99+
* Defines if the handshake(AppLaunching notification) between CLI and runtime should be executed. The handshake is not needed when CLI retries to attach to the debugger.
100+
*/
101+
skipHandshake?: boolean;
98102
}
99103

100104
/**

0 commit comments

Comments
 (0)