Skip to content

Commit 24420a2

Browse files
author
Pavel Petroshenko
authored
Merge pull request #9 from electricimp/develop
v1.0.3
2 parents 60d3330 + 6c06e8a commit 24420a2

18 files changed

+241
-191
lines changed

Diff for: CommandsManual.md

+40-39
Large diffs are not rendered by default.

Diff for: DevelopmentGuide.md

+32-26
Large diffs are not rendered by default.

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,8 @@ Interaction with a user is minimal. Only a few commands, such as [delete](#entit
248248

249249
A command’s execution output contains one of the two predefined phrases: `IMPT COMMAND SUCCEEDS` or `IMPT COMMAND FAILS`. Scripts can parse a command’s output to find these standard phrases to detect whether the commmand succeeded or failed. If any command fails, `IMPT COMMAND FAILS` is always the last line of the command’s output. If a command succeeds, `IMPT COMMAND SUCCEEDS` phrase is the last line of the output for the most of the commands. Logging-related commands may have additional `IMPT COMMAND SUCCEEDS` phrases in their output. If the [help option](./CommandsManual.md#the-help-option) is specified for a command, its output does not contain either predefined phrase.
250250

251+
If a command finishes with `IMPT COMMAND FAILS`, it returns non-zero exit code. Otherwise, it returns zero exit code. This can be used by scripts as well.
252+
251253
**Example**
252254

253255
**A successful command execution**

Diff for: TestingGuide.md

+82-61
Large diffs are not rendered by default.

Diff for: bin/impt

-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,5 @@ Yargs
4646
else {
4747
UserInteractor.printErrorWithStatus(msg);
4848
}
49-
process.exit(1);
5049
})
5150
.argv

Diff for: lib/Log.js

+20-36
Original file line numberDiff line numberDiff line change
@@ -72,33 +72,22 @@ class Log {
7272
return Promise.resolve();
7373
}
7474

75-
_exitWithStatus() {
76-
this._closeLogStream().
77-
then(() => {
78-
UserInteractor.printSuccessStatus();
79-
this._exitProcess();
80-
}).
81-
catch(error => {
82-
UserInteractor.processError(error);
83-
this._exitProcess();
84-
});
85-
}
86-
87-
_exit() {
75+
_exitWithStatus(error = null) {
76+
this._readline.close();
8877
this._closeLogStream().
8978
then(() => {
90-
this._exitProcess();
79+
if (error) {
80+
UserInteractor.processError(error);
81+
}
82+
else {
83+
UserInteractor.printSuccessStatus();
84+
}
9185
}).
92-
catch(error => {
93-
this._exitProcess();
86+
catch(err => {
87+
UserInteractor.processError(error ? error : err);
9488
});
9589
}
9690

97-
_exitProcess() {
98-
this._readline.close();
99-
process.exit();
100-
}
101-
10291
// Displays historical logs for the specified Device.
10392
//
10493
// If --device option is missed but the current directory contains
@@ -131,7 +120,7 @@ class Log {
131120
return device.getEntityId().then(() => {
132121
if (options.pageNumber !== undefined) {
133122
return this._printDeviceLogsPage(device.id, options.pageNumber, options.pageSize).
134-
then(() => this._exit());
123+
then(() => this._exitWithStatus());
135124
}
136125
else {
137126
let pageNumber = 1;
@@ -145,8 +134,7 @@ class Log {
145134
}
146135
}
147136
}).catch(error => {
148-
UserInteractor.processError(error);
149-
this._exit();
137+
this._exitWithStatus(error);
150138
});
151139
}
152140

@@ -159,16 +147,16 @@ class Log {
159147
// Returns: Nothing.
160148
_printDeviceLogs(deviceId, pageNumber, pageSize) {
161149
if (this._logsFinished) {
162-
UserInteractor.printMessageWithStatus(UserInteractor.MESSAGES.LOG_GET_FINISHED);
163-
this._exit();
150+
UserInteractor.printMessage(UserInteractor.MESSAGES.LOG_GET_FINISHED);
151+
this._exitWithStatus();
164152
}
165153
else {
166154
this._printDeviceLogsPage(deviceId, pageNumber, pageSize).then(() => {
155+
UserInteractor.printSuccessStatus();
167156
UserInteractor.printMessage('');
168157
UserInteractor.printMessage(UserInteractor.MESSAGES.LOG_GET_NEXT);
169158
}).catch(error => {
170-
UserInteractor.processError(error);
171-
this._exit();
159+
this._exitWithStatus(error);
172160
});
173161
}
174162
}
@@ -180,8 +168,7 @@ class Log {
180168
if (logs.length < pageSize) {
181169
this._logsFinished = true;
182170
}
183-
}).
184-
then(() => UserInteractor.printSuccessStatus());
171+
});
185172
}
186173

187174
// Displays logs from the specified Devices in real-time.
@@ -206,8 +193,7 @@ class Log {
206193
this._setOptions(options).
207194
then(() => this._stream(this._devices)).
208195
catch(error => {
209-
UserInteractor.processError(error);
210-
this._exit();
196+
this._exitWithStatus(error);
211197
});
212198
}
213199

@@ -247,9 +233,8 @@ class Log {
247233
UserInteractor.printJsonData(this._formatLog(JSON.parse(message), true));
248234
}
249235
catch (e) {
250-
UserInteractor.processError(new Errors.InternalLibraryError(
236+
this._exitWithStatus(new Errors.InternalLibraryError(
251237
Util.format(UserInteractor.ERRORS.LOG_STREAM_UNEXPECTED_MSG, message)));
252-
this._exit();
253238
}
254239
}
255240

@@ -263,9 +248,8 @@ class Log {
263248
}
264249
}
265250
catch (e) {
266-
UserInteractor.processError(new Errors.InternalLibraryError(
251+
this._exitWithStatus(new Errors.InternalLibraryError(
267252
Util.format(UserInteractor.ERRORS.LOG_STREAM_UNEXPECTED_MSG, state)));
268-
this._exit();
269253
}
270254
}
271255

Diff for: lib/Test.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -167,20 +167,25 @@ class Test {
167167

168168
run(options) {
169169
UserInteractor.enableSpinner(false);
170-
return this._checkTestConfig().
170+
let testHelper = null;
171+
this._checkTestConfig().
171172
then(() => this._initDeviceGroup(null)).
172173
then(() => this._processBuilderCache(options.clearCache)).
173174
then(() => {
174-
const testHelper = new TestHelper(this._deviceGroup, options);
175+
testHelper = new TestHelper(this._deviceGroup, options);
175176
return testHelper.runTests();
176177
}).
177178
then(() => {
178-
UserInteractor.printSuccessStatus();
179-
process.exit(0);
179+
if (testHelper.success) {
180+
UserInteractor.printSuccessStatus();
181+
process.exit();
182+
}
183+
else {
184+
UserInteractor.printErrorStatus();
185+
}
180186
}).
181187
catch((error) => {
182188
UserInteractor.processError(error);
183-
process.exit(1);
184189
});
185190
}
186191

Diff for: lib/util/ImpCentralApiHelper.js

+1
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ class ImpCentralApiHelper {
424424

425425
_resolveImpCentralApiResponse(result) {
426426
UserInteractor.spinnerStop();
427+
UserInteractor.printResponseMeta(result);
427428
return result;
428429
}
429430

Diff for: lib/util/TestHelper.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ class TestHelper {
7878
this._init(options);
7979
}
8080

81+
get success() {
82+
return this._success;
83+
}
84+
8185
_init(options) {
8286
this.testFrameworkFile = __dirname + '/../impUnit/index.nut';
8387
this.logTiming = true; // enable log timing
@@ -691,7 +695,7 @@ ${'partnerpath' in testFile ? '@include "' + backslashToSlash(testFile.partnerpa
691695

692696
this._error(error.message);
693697
if (error instanceof ImpCentralApi.Errors.ImpCentralApiError) {
694-
UserInteractor._printErrorDetails(error.body);
698+
UserInteractor.printImpCentralApiError(error);
695699
}
696700
this._stopSession = true;
697701

Diff for: lib/util/UserInteractor.js

+38-11
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const JSON_VALUE_MAX_LENGTH = 512;
4343

4444
const SUCCESS_COMMAND_STATUS = 'IMPT COMMAND SUCCEEDS';
4545
const FAIL_COMMAND_STATUS = 'IMPT COMMAND FAILS';
46+
const FAIL_EXIT_CODE = 1;
4647

4748
const _PRODUCT = `${Identifier.ENTITY_TYPE.TYPE_PRODUCT}`;
4849
const _DEVICE_GROUP = `${Identifier.ENTITY_TYPE.TYPE_DEVICE_GROUP}`;
@@ -183,6 +184,7 @@ class UserInteractor {
183184

184185
static printErrorStatus() {
185186
UserInteractor.printMessage(FAIL_COMMAND_STATUS);
187+
process.exit(FAIL_EXIT_CODE);
186188
}
187189

188190
static printMessage(message, ...args) {
@@ -322,8 +324,7 @@ class UserInteractor {
322324
UserInteractor.printErrorStatus();
323325
}
324326
else if (error instanceof ImpCentralApi.Errors.ImpCentralApiError) {
325-
UserInteractor.printError(error.message);
326-
UserInteractor._printErrorDetails(error.body);
327+
UserInteractor.printImpCentralApiError(error);
327328
UserInteractor.printErrorStatus();
328329
}
329330
else if (error instanceof ImpCentralApi.Errors.InvalidDataError) {
@@ -341,27 +342,53 @@ class UserInteractor {
341342
}
342343
}
343344

344-
static _printErrorDetails(errorBody) {
345-
if (Array.isArray(errorBody.errors)) {
346-
errorBody.errors.forEach((error, index) => {
347-
if (index > 0 && error.title && error.detail) {
348-
UserInteractor.printError(error.title + ': ' + error.detail);
345+
static printImpCentralApiError(error) {
346+
if (error.body.hasOwnProperty('errors')) {
347+
UserInteractor._printErrorsAndWarnings(error.body.errors);
348+
}
349+
else {
350+
UserInteractor.printError(error.message);
351+
}
352+
}
353+
354+
static printResponseMeta(response) {
355+
if (response && response.hasOwnProperty('data') && response.data.hasOwnProperty('meta')) {
356+
UserInteractor._printErrorsAndWarnings(response.data.meta);
357+
}
358+
}
359+
360+
static _printErrorsAndWarnings(entities) {
361+
if (Array.isArray(entities)) {
362+
entities.forEach((entity) => {
363+
if (['title', 'detail', 'status'].every(prop => entity.hasOwnProperty(prop))) {
364+
const message = entity.title + ': ' + entity.detail;
365+
if (UserInteractor._isError(entity)) {
366+
UserInteractor.printError(message);
367+
}
368+
else {
369+
UserInteractor.printMessage(message);
370+
}
349371
}
350-
UserInteractor._printErrorMeta(error.meta);
372+
UserInteractor._printMeta(entity.meta);
351373
});
352374
}
353375
}
354376

355-
static _printErrorMeta(errorMeta) {
356-
if (Array.isArray(errorMeta)) {
357-
for (let meta of errorMeta) {
377+
static _printMeta(metaData) {
378+
if (Array.isArray(metaData)) {
379+
for (let meta of metaData) {
358380
if (['row', 'column', 'text', 'type', 'file'].every(prop => meta.hasOwnProperty(prop))) {
359381
UserInteractor.printMessage('%s %s (line %d, column %d): %s', meta.file, meta.type, meta.row, meta.column, meta.text);
360382
}
361383
}
362384
}
363385
}
364386

387+
static _isError(entity) {
388+
const status = parseInt(entity.status);
389+
return (status < 200 || status >= 300);
390+
}
391+
365392
static reloginMessage(authConfig) {
366393
const Options = require('./Options');
367394
const AuthConfig = require('./AuthConfig');

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "imp-central-impt",
3-
"version": "1.0.2",
3+
"version": "1.0.3",
44
"description": "impt - command line tool for the Electric Imp impCentral API",
55
"main": "bin/impt",
66
"engines": {

Diff for: spec/test/test_run_build_api_error.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ describe('impt test run for build-api-error behavior >', () => {
6060
expect(commandOut).toMatch(/Using device test file tests\/2\-device\.test\.nut\n/);
6161

6262
ImptTestCommandsHelper.checkTestFailStatus(commandOut);
63-
ImptTestingHelper.checkSuccessStatus(commandOut);
63+
ImptTestingHelper.checkFailStatus(commandOut);
6464
}).
6565
then(done).
6666
catch(error => done.fail(error));

Diff for: spec/test/test_run_deep_equal.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ describe('impt test run for deep-equal scenario >', () => {
5353
expect(commandOut).toMatch(/Extra slot \[a\.b\.d\] in actual value\n/);
5454
expect(commandOut).toMatch(/At \[a\.b\.c\]: expected \"3\", got \"100\"\n/);
5555
ImptTestCommandsHelper.checkTestFailStatus(commandOut);
56-
ImptTestingHelper.checkSuccessStatus(commandOut);
56+
ImptTestingHelper.checkFailStatus(commandOut);
5757
}).
5858
then(done).
5959
catch(error => done.fail(error));

Diff for: spec/test/test_run_error_before_start.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe('impt test run for error-before-start behavior >', () => {
5858
expect(commandOut).toMatch(/error\] Session startup timeout\n/);
5959
expect(commandOut).not.toMatch(/test\] Device is out of memory\n/);
6060
ImptTestCommandsHelper.checkTestFailStatus(commandOut);
61-
ImptTestingHelper.checkSuccessStatus(commandOut);
61+
ImptTestingHelper.checkFailStatus(commandOut);
6262
}).
6363
then(done).
6464
catch(error => done.fail(error));

Diff for: spec/test/test_run_external_command.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('impt test run for external-command scenario >', () => {
5656
expect(commandOut).toMatch(/\> \"?external command output\"?/);
5757
expect(commandOut).toMatch(/External command failed with exit code 125/);
5858
ImptTestCommandsHelper.checkTestFailStatus(commandOut);
59-
ImptTestingHelper.checkSuccessStatus(commandOut);
59+
ImptTestingHelper.checkFailStatus(commandOut);
6060
})).
6161
then(done).
6262
catch(error => done.fail(error));
@@ -74,7 +74,7 @@ describe('impt test run for external-command scenario >', () => {
7474
expect(commandOut).not.toMatch(/\> external command output/);
7575
expect(commandOut).toMatch(/External command timed out/);
7676
ImptTestCommandsHelper.checkTestFailStatus(commandOut);
77-
ImptTestingHelper.checkSuccessStatus(commandOut);
77+
ImptTestingHelper.checkFailStatus(commandOut);
7878
})).
7979
then(done).
8080
catch(error => done.fail(error));

Diff for: spec/test/test_run_server_error.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ describe('impt test run for test server error scenario >', () => {
8181
expect(commandOut).not.toBeEmptyString();
8282
expect(commandOut).toMatch("the index 'fieldDoesNotExists' does not exist");
8383
ImptTestCommandsHelper.checkTestFailStatus(commandOut);
84-
ImptTestingHelper.checkSuccessStatus(commandOut);
84+
ImptTestingHelper.checkFailStatus(commandOut);
8585
})).
8686
then(done).
8787
catch(error => done.fail(error));
@@ -99,7 +99,7 @@ describe('impt test run for test server error scenario >', () => {
9999
expect(commandOut).not.toBeEmptyString();
100100
expect(commandOut).toMatch("unhandled exception");
101101
ImptTestCommandsHelper.checkTestFailStatus(commandOut);
102-
ImptTestingHelper.checkSuccessStatus(commandOut);
102+
ImptTestingHelper.checkFailStatus(commandOut);
103103
})).
104104
then(done).
105105
catch(error => done.fail(error));

Diff for: spec/test/test_run_stop_on_failure.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ describe('impt test run for stop-on-failure behavior >', () => {
5252
expect(commandOut).not.toBeEmptyString();
5353
expect(commandOut).not.toMatch(/Using device test file tests\/2\.device\.test\.nut\n/);
5454
ImptTestCommandsHelper.checkTestFailStatus(commandOut);
55-
ImptTestingHelper.checkSuccessStatus(commandOut);
55+
ImptTestingHelper.checkFailStatus(commandOut);
5656
})).
5757
then(done).
5858
catch(error => done.fail(error));
@@ -64,7 +64,7 @@ describe('impt test run for stop-on-failure behavior >', () => {
6464
expect(commandOut).not.toBeEmptyString();
6565
expect(commandOut).toMatch(/Using device test file tests\/2\.device\.test\.nut\n/);
6666
ImptTestCommandsHelper.checkTestFailStatus(commandOut);
67-
ImptTestingHelper.checkSuccessStatus(commandOut);
67+
ImptTestingHelper.checkFailStatus(commandOut);
6868
})).
6969
then(done).
7070
catch(error => done.fail(error));

Diff for: spec/test/test_run_timeout.spec.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ describe('impt test run for timeout behavior >', () => {
5252
expect(commandOut).not.toBeEmptyString();
5353
expect(commandOut).toMatch('Test timed out');
5454
ImptTestCommandsHelper.checkTestFailStatus(commandOut);
55-
ImptTestingHelper.checkSuccessStatus(commandOut);
55+
ImptTestingHelper.checkFailStatus(commandOut);
5656
})).
5757
then(done).
5858
catch(error => done.fail(error));

0 commit comments

Comments
 (0)