Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
610 lines
19 KiB
JavaScript
Executable file
610 lines
19 KiB
JavaScript
Executable file
'use strict';
|
|
|
|
/* global LogShake,
|
|
DOMError,
|
|
MockDOMRequest,
|
|
MockModalDialog,
|
|
MockMozActivity,
|
|
MockNavigatorGetDeviceStorage,
|
|
MockGetDeviceStorages,
|
|
MockNotification,
|
|
MockNotificationHelper,
|
|
MockL10n,
|
|
MockService,
|
|
MocksHelper
|
|
*/
|
|
|
|
require('/js/devtools/logshake.js');
|
|
require('/shared/test/unit/mocks/mock_l10n.js');
|
|
require('/shared/test/unit/mocks/mock_notification.js');
|
|
require('/shared/test/unit/mocks/mock_notification_helper.js');
|
|
require('/shared/test/unit/mocks/mock_event_target.js');
|
|
require('/shared/test/unit/mocks/mock_dom_request.js');
|
|
require('/shared/test/unit/mocks/mock_service.js');
|
|
require('/shared/test/unit/mocks/mock_navigator_getdevicestorage.js');
|
|
require('/shared/test/unit/mocks/mock_navigator_getdevicestorages.js');
|
|
require('/test/unit/mock_activity.js');
|
|
require('/test/unit/mock_modal_dialog.js');
|
|
require('/test/unit/mock_navigator_get_device_storage.js');
|
|
|
|
var mocksForLogshake = new MocksHelper([
|
|
'Service'
|
|
]).init();
|
|
|
|
/**
|
|
* Test shake-to-log functionality.
|
|
* Borrows heavily from screenshot_test.js
|
|
*/
|
|
suite('system/LogShake', function() {
|
|
var realL10n;
|
|
var realDOMRequest;
|
|
var realModalDialog;
|
|
var realNavigatorGetDeviceStorage;
|
|
var realNavigatorGetDeviceStorages;
|
|
var realNotification;
|
|
var realNotificationHelper;
|
|
var realMozActivity;
|
|
|
|
var logshake;
|
|
var logTagBase = 'logshake:';
|
|
var expectedLogTag = logTagBase + '1';
|
|
|
|
mocksForLogshake.attachTestHelpers();
|
|
|
|
setup(function() {
|
|
// XXX: Use screenshot's hack until system2 rolls around
|
|
if (window.logshake) {
|
|
window.logshake.stop();
|
|
window.logshake = null;
|
|
}
|
|
|
|
realNotificationHelper = window.NotificationHelper;
|
|
window.NotificationHelper = MockNotificationHelper;
|
|
|
|
realDOMRequest = window.DOMRequest;
|
|
window.DOMRequest = MockDOMRequest;
|
|
|
|
realModalDialog = window.ModalDialog;
|
|
window.ModalDialog = MockModalDialog;
|
|
|
|
realNavigatorGetDeviceStorage = navigator.getDeviceStorage;
|
|
navigator.getDeviceStorage = MockNavigatorGetDeviceStorage;
|
|
|
|
realNavigatorGetDeviceStorages = navigator.getDeviceStorages;
|
|
navigator.getDeviceStorages = MockGetDeviceStorages;
|
|
|
|
realMozActivity = window.MozActivity;
|
|
window.MozActivity = MockMozActivity;
|
|
|
|
realL10n = navigator.mozL10n;
|
|
navigator.mozL10n = MockL10n;
|
|
|
|
realNotification = window.Notification;
|
|
window.Notification = MockNotification;
|
|
|
|
logshake = new LogShake();
|
|
logshake.start();
|
|
logshake._shakeId = 1;
|
|
});
|
|
|
|
teardown(function() {
|
|
logshake._shakeId = null;
|
|
logshake.stop();
|
|
|
|
window.DOMRequest = realDOMRequest;
|
|
window.ModalDialog = realModalDialog;
|
|
navigator.getDeviceStorage = realNavigatorGetDeviceStorage;
|
|
navigator.getDeviceStorages = realNavigatorGetDeviceStorages;
|
|
navigator.mozL10n = realL10n;
|
|
window.NotificationHelper = realNotificationHelper;
|
|
window.Notification = realNotification;
|
|
window.MozActivity = realMozActivity;
|
|
|
|
// XXX: Do not restore window's logshake, its time is over
|
|
});
|
|
|
|
suite('_notify sends proper notifications >', function() {
|
|
var notifSpy;
|
|
|
|
setup(function() {
|
|
notifSpy = this.sinon.spy(window.NotificationHelper, 'send');
|
|
});
|
|
|
|
test('notification basics', function() {
|
|
logshake._notify('title', 'body');
|
|
sinon.assert.calledOnce(notifSpy);
|
|
|
|
var args = notifSpy.firstCall.args;
|
|
assert.equal(args[0], 'title');
|
|
assert.equal(args[1].bodyL10n, 'body');
|
|
assert.equal(args[1].tag, 'logshake:1');
|
|
assert.equal(args[1].data.systemMessageTarget, 'logshake');
|
|
});
|
|
|
|
test('without notification click handler', function() {
|
|
logshake._notify('title', 'body');
|
|
sinon.assert.calledOnce(notifSpy);
|
|
|
|
var notif = notifSpy.firstCall.returnValue;
|
|
assert.isUndefined(notif.onclick);
|
|
});
|
|
|
|
test('with notification click handler', function(done) {
|
|
logshake._notify('title', 'body', function() {});
|
|
sinon.assert.calledOnce(notifSpy);
|
|
|
|
var notifPromise = notifSpy.firstCall.returnValue;
|
|
notifPromise.then(notif => {
|
|
assert.isDefined(notif.onclick);
|
|
}).then(done, done);
|
|
});
|
|
|
|
test('with payload', function() {
|
|
var payload = { error: 'BADBAD' };
|
|
logshake._notify('title', 'body', undefined, payload);
|
|
sinon.assert.calledOnce(notifSpy);
|
|
|
|
var data = notifSpy.firstCall.args[1].data;
|
|
assert.isDefined(data.logshakePayload);
|
|
assert.equal(data.logshakePayload, payload);
|
|
});
|
|
});
|
|
|
|
suite('Capture start handling', function() {
|
|
test('Create notification after capture-logs-start event', function() {
|
|
var notificationSpy = this.sinon.spy(window.NotificationHelper, 'send');
|
|
|
|
assert.equal(1, logshake._shakeId);
|
|
|
|
window.dispatchEvent(
|
|
new CustomEvent('capture-logs-start', { detail: {} }));
|
|
|
|
assert.isNotNull(logshake._shakeId);
|
|
assert.notEqual(1, logshake._shakeId);
|
|
|
|
// LogShake should dispatch a notification of some kind
|
|
assert.isTrue(notificationSpy.calledOnce,
|
|
'Notification should be called');
|
|
assert.equal(notificationSpy.firstCall.args[0],
|
|
'logsSaving');
|
|
assert.equal(logshake._shakeId, parseInt(logshake._shakeId));
|
|
assert.equal(notificationSpy.firstCall.args[1].tag,
|
|
logTagBase + logshake._shakeId);
|
|
});
|
|
});
|
|
|
|
|
|
suite('Uncompressed LogShake', function() {
|
|
var logFilenames = ['log.log', 'bog.log'];
|
|
var logPrefix = 'logs/2014-06-03-00-00/';
|
|
var logPaths = logFilenames.map(function(filename) {
|
|
return logPrefix + filename;
|
|
});
|
|
var compressed = false;
|
|
var logBlobs = logPaths.map(function(path) {
|
|
return {
|
|
type: 'text/plain',
|
|
name: path
|
|
};
|
|
});
|
|
|
|
suite('Capture success handling',
|
|
captureSuccessSuite(logFilenames, logPaths, compressed, logBlobs));
|
|
});
|
|
|
|
suite('Compressed LogShake', function() {
|
|
var logFilenames = ['log.log', 'bog.log'];
|
|
var zipPath = 'logs/2014-06-03-00-00-logs.zip';
|
|
var compressed = true;
|
|
var zipBlob = {
|
|
type: 'application/zip',
|
|
name: zipPath
|
|
};
|
|
|
|
suite('Capture success handling',
|
|
captureSuccessSuite(logFilenames, [zipPath], compressed, [zipBlob]));
|
|
|
|
});
|
|
|
|
function captureSuccessSuite(logFilenames, logPaths, compressed, mockBlobs) {
|
|
var notificationSpy;
|
|
|
|
setup(function() {
|
|
notificationSpy = this.sinon.spy(window.NotificationHelper, 'send');
|
|
|
|
window.dispatchEvent(new CustomEvent('capture-logs-success',
|
|
{ detail: { logFilenames: logFilenames, logPaths: logPaths,
|
|
compressed: compressed } }));
|
|
});
|
|
|
|
test('Notification sent', function() {
|
|
assert.isNull(logshake._shakeId);
|
|
assert.isTrue(notificationSpy.calledOnce);
|
|
assert.equal(notificationSpy.firstCall.args[0],
|
|
'logsSavedHeader');
|
|
assert.equal(notificationSpy.firstCall.args[1].bodyL10n,
|
|
'logsSavedBody');
|
|
assert.equal(notificationSpy.firstCall.args[1].tag,
|
|
expectedLogTag);
|
|
});
|
|
|
|
test('Clicking notification', function(done) {
|
|
var mockDeviceStorage = MockNavigatorGetDeviceStorage();
|
|
var storageSpy = this.sinon.spy(navigator, 'getDeviceStorages');
|
|
var getSpy = this.sinon.spy(mockDeviceStorage, 'get');
|
|
|
|
var notifPromise = notificationSpy.firstCall.returnValue;
|
|
notifPromise.then(notif => {
|
|
notif.onclick();
|
|
|
|
assert.isTrue(storageSpy.callCount > 0,
|
|
'getDeviceStorage should be called at least once');
|
|
assert.equal(getSpy.callCount, logPaths.length,
|
|
'.get() should be called for all log paths');
|
|
for (var i = 0; i < getSpy.callCount; i++) {
|
|
assert.equal(getSpy.getCall(i).args[0], logPaths[i],
|
|
'.get() should have been called with filename from event');
|
|
}
|
|
}).then(done, done);
|
|
});
|
|
|
|
suite('MozActivity handling', function() {
|
|
var expectedActivity, mockDeviceStorage, notification, getSpy;
|
|
var activityStub, closeSpy, closeSystemSpy;
|
|
|
|
setup(function(done) {
|
|
expectedActivity = {
|
|
name: 'share',
|
|
data: {
|
|
type: 'application/vnd.moz-systemlog',
|
|
blobs: mockBlobs,
|
|
filenames: logFilenames
|
|
}
|
|
};
|
|
|
|
mockDeviceStorage = MockNavigatorGetDeviceStorage();
|
|
mockDeviceStorage._freeSpace = Number.MAX_VALUE;
|
|
|
|
getSpy = this.sinon.spy(mockDeviceStorage, 'get');
|
|
activityStub = this.sinon.stub(window, 'MozActivity', function() {
|
|
return new MockDOMRequest();
|
|
});
|
|
|
|
var notifPromise = notificationSpy.firstCall.returnValue;
|
|
notifPromise.then(notif => {
|
|
notification = notif;
|
|
closeSpy = notif.close;
|
|
|
|
closeSystemSpy =
|
|
this.sinon.spy(logshake, 'closeSystemMessageNotification');
|
|
}).then(done, done);
|
|
});
|
|
|
|
function triggerGetSuccess() {
|
|
// Simulate success of reading file
|
|
for (var i = 0; i < getSpy.callCount; i++) {
|
|
var getRequest = getSpy.getCall(i).returnValue;
|
|
getRequest.fireSuccess(mockBlobs[i]);
|
|
}
|
|
}
|
|
|
|
function triggerActivitySuccess() {
|
|
for (var i = 0; i < activityStub.callCount; i++) {
|
|
var activity = activityStub.getCall(i).returnValue;
|
|
activity.fireSuccess();
|
|
}
|
|
}
|
|
|
|
function triggerActivityError() {
|
|
for (var i = 0; i < activityStub.callCount; i++) {
|
|
var activity = activityStub.getCall(i).returnValue;
|
|
activity.fireError();
|
|
}
|
|
}
|
|
|
|
test('triggerShareLogs launches MozActivity', function() {
|
|
logshake.triggerShareLogs(logPaths);
|
|
|
|
triggerGetSuccess();
|
|
|
|
sinon.assert.calledWith(activityStub, expectedActivity);
|
|
});
|
|
|
|
test('close notification with MozActivity success', function() {
|
|
logshake.triggerShareLogs(logPaths, notification);
|
|
|
|
triggerGetSuccess();
|
|
triggerActivitySuccess();
|
|
|
|
sinon.assert.calledOnce(closeSpy);
|
|
});
|
|
|
|
test('calls closeSystemMessageNotification with MozActivity success',
|
|
function() {
|
|
// Use an empty object since we just want one without 'close()'
|
|
logshake.triggerShareLogs(logPaths, {});
|
|
|
|
triggerGetSuccess();
|
|
triggerActivitySuccess();
|
|
|
|
sinon.assert.calledOnce(closeSystemSpy);
|
|
});
|
|
|
|
test('keep notification with MozActivity error', function() {
|
|
logshake.triggerShareLogs(logPaths, notification);
|
|
|
|
triggerGetSuccess();
|
|
triggerActivityError();
|
|
|
|
sinon.assert.notCalled(closeSpy);
|
|
});
|
|
|
|
test('dont call closeSystemMessageNotification with MozActivity error',
|
|
function() {
|
|
// Use an empty object since we just want one without 'close()'
|
|
logshake.triggerShareLogs(logPaths, {});
|
|
|
|
triggerGetSuccess();
|
|
triggerActivityError();
|
|
|
|
sinon.assert.notCalled(closeSystemSpy);
|
|
});
|
|
});
|
|
}
|
|
|
|
suite('Capture error handling >', function() {
|
|
var notificationSpy, errorMessage,
|
|
errorUnixSharedSD, errorUnixGeneric,
|
|
errorNotFoundError, errorNotFoundErrorExpectedBody,
|
|
errorUnixExpectedBody;
|
|
|
|
function sendError(e) {
|
|
assert.isNotNull(logshake._shakeId);
|
|
window.dispatchEvent(new CustomEvent('capture-logs-error',
|
|
{ detail: { error: e } }));
|
|
assert.isNull(logshake._shakeId);
|
|
}
|
|
|
|
function notificationAsserts(spy) {
|
|
assert.isTrue(spy.calledOnce, 'Notification should be called');
|
|
assert.isTrue(spy.calledWithNew(), 'Notification created with new');
|
|
assert.equal(spy.firstCall.args[0], 'logsSaveError');
|
|
assert.equal(spy.firstCall.args[1].tag, expectedLogTag);
|
|
}
|
|
|
|
function assertBody(expected) {
|
|
assert.equal(notificationSpy.firstCall.args[1].body, expected);
|
|
}
|
|
|
|
setup(function() {
|
|
notificationSpy = this.sinon.spy(window, 'Notification');
|
|
errorMessage = 'error error error';
|
|
errorUnixSharedSD = {
|
|
operation: 'makeDir',
|
|
unixErrno: 30 // EROFS
|
|
};
|
|
errorUnixGeneric = {
|
|
operation: 'makeDir',
|
|
unixErrno: 0
|
|
};
|
|
errorNotFoundError = new DOMError('NotFoundError');
|
|
errorUnixExpectedBody =
|
|
'logsOperationFailed{"operation":"makeDir"}';
|
|
errorNotFoundErrorExpectedBody = 'logsNotFoundError';
|
|
});
|
|
|
|
test('Handling capture-logs-error event with string error', function() {
|
|
sendError(errorMessage);
|
|
notificationAsserts(notificationSpy);
|
|
assertBody(errorMessage);
|
|
});
|
|
|
|
suite('Handling capture-logs-error event with errno >', function() {
|
|
test('Handling known errno', function() {
|
|
sendError(errorUnixSharedSD);
|
|
notificationAsserts(notificationSpy);
|
|
assertBody(errorUnixExpectedBody);
|
|
});
|
|
|
|
test('Handling unknown errno', function() {
|
|
sendError(errorUnixGeneric);
|
|
notificationAsserts(notificationSpy);
|
|
assertBody(errorUnixExpectedBody);
|
|
});
|
|
|
|
test('Handling NotFoundError', function() {
|
|
sendError(errorNotFoundError);
|
|
notificationAsserts(notificationSpy);
|
|
assertBody(errorNotFoundErrorExpectedBody);
|
|
});
|
|
});
|
|
|
|
suite('formatError behavior >', function() {
|
|
test('formatError() with a string', function() {
|
|
var error = 'Unknown error';
|
|
var msg = logshake.formatError(error);
|
|
assert.equal(msg, error);
|
|
});
|
|
|
|
test('formatError() with an object', function() {
|
|
var error = {
|
|
operation: 'makeDir',
|
|
unixErrno: 30 // EROFS
|
|
};
|
|
var msg = logshake.formatError(error);
|
|
assert.equal(msg, 'logsOperationFailed{"operation":"makeDir"}');
|
|
});
|
|
|
|
test('formatError() with a NotFoundError', function() {
|
|
var msg = logshake.formatError(errorNotFoundError);
|
|
assert.equal(msg, 'logsNotFoundError');
|
|
});
|
|
});
|
|
|
|
suite('Clicking error notification >', function() {
|
|
var notification, closeSpy, modalSpy;
|
|
|
|
setup(function() {
|
|
modalSpy = this.sinon.spy(MockModalDialog, 'alert');
|
|
});
|
|
|
|
test('Known errno', function() {
|
|
sendError(errorUnixSharedSD);
|
|
notification = notificationSpy.firstCall.thisValue;
|
|
closeSpy = this.sinon.spy(notification, 'close');
|
|
notification.onclick();
|
|
assert.isTrue(modalSpy.calledWith('logsSaveError',
|
|
'logsSDCardMaybeShared',
|
|
{ title: 'ok' }));
|
|
sinon.assert.calledOnce(closeSpy);
|
|
});
|
|
|
|
test('Unknown errno', function() {
|
|
sendError(errorUnixGeneric);
|
|
notification = notificationSpy.firstCall.thisValue;
|
|
closeSpy = this.sinon.spy(notification, 'close');
|
|
notification.onclick();
|
|
assert.isTrue(modalSpy.calledWith('logsSaveError',
|
|
'logsGenericError',
|
|
{ title: 'ok' }));
|
|
sinon.assert.calledOnce(closeSpy);
|
|
});
|
|
|
|
test('NotFoundError DOMError', function() {
|
|
sendError(errorNotFoundError);
|
|
notification = notificationSpy.firstCall.thisValue;
|
|
closeSpy = this.sinon.spy(notification, 'close');
|
|
notification.onclick();
|
|
assert.isTrue(modalSpy.calledWith('logsShareError',
|
|
'logsNotFoundError',
|
|
{ title: 'ok' }));
|
|
sinon.assert.calledOnce(closeSpy);
|
|
});
|
|
});
|
|
});
|
|
|
|
suite('Use sdcard device storage', function() {
|
|
var sdcard, sdcard1, extsdcard;
|
|
var expected = 'sdcard';
|
|
|
|
suiteSetup(function() {
|
|
sdcard = { storageName: 'sdcard' };
|
|
sdcard1 = { storageName: 'sdcard1' };
|
|
extsdcard = { storageName: 'extsdcard' };
|
|
});
|
|
|
|
suite('only one device storage', function() {
|
|
setup(function() {
|
|
this.sinon.stub(navigator, 'getDeviceStorages')
|
|
.withArgs('sdcard').returns([sdcard]);
|
|
});
|
|
|
|
test('device storage name is sdcard', function() {
|
|
assert.equal(expected, logshake.getDeviceStorage().storageName);
|
|
});
|
|
});
|
|
|
|
suite('multiple device storages', function() {
|
|
setup(function() {
|
|
this.sinon.stub(navigator, 'getDeviceStorages')
|
|
.withArgs('sdcard').returns([sdcard, sdcard1, extsdcard]);
|
|
});
|
|
|
|
test('device storage name is sdcard', function() {
|
|
assert.equal(expected, logshake.getDeviceStorage().storageName);
|
|
});
|
|
});
|
|
});
|
|
|
|
suite('System message notification >', function() {
|
|
var serviceSpy;
|
|
|
|
var notification = {
|
|
body: 'fake',
|
|
tag: 'logshake',
|
|
data: {
|
|
systemMessageTarget: 'logshake'
|
|
},
|
|
close: function() {}
|
|
};
|
|
|
|
suite('start/stop conditions', function() {
|
|
setup(function() {
|
|
logshake.stop();
|
|
});
|
|
|
|
teardown(function() {
|
|
logshake.start();
|
|
});
|
|
|
|
test('.start() requests handleSystemMessageNotification service',
|
|
function() {
|
|
serviceSpy = this.sinon.spy(MockService, 'request');
|
|
logshake.start();
|
|
assert.isTrue(serviceSpy.calledOnce);
|
|
assert.isTrue(serviceSpy.calledWith(
|
|
'handleSystemMessageNotification', 'logshake', logshake));
|
|
logshake.stop();
|
|
});
|
|
|
|
test('.stop() requests unhandleSystemMessageNotification service',
|
|
function() {
|
|
logshake.start();
|
|
serviceSpy = this.sinon.spy(MockService, 'request');
|
|
logshake.stop();
|
|
assert.isTrue(serviceSpy.calledOnce);
|
|
assert.isTrue(serviceSpy.calledWith(
|
|
'unhandleSystemMessageNotification', 'logshake', logshake));
|
|
});
|
|
});
|
|
|
|
suite('handleSystemMessageNotification behavior', function() {
|
|
test('calls triggerShareLogs for success cases', function() {
|
|
var triggerShareLogsSpy = this.sinon.spy(logshake, 'triggerShareLogs');
|
|
notification.data.logshakePayload = { logPaths: [], logFilenames: [],
|
|
compressed: false };
|
|
logshake.handleSystemMessageNotification(notification);
|
|
assert.isTrue(triggerShareLogsSpy.calledOnce);
|
|
assert.isTrue(
|
|
triggerShareLogsSpy.calledWith(
|
|
notification.data.logshakePayload.logPaths));
|
|
delete notification.data.logshakePayload;
|
|
});
|
|
|
|
test('calls showErrorMessage for error cases', function() {
|
|
var showErrorMessageSpy = this.sinon.spy(logshake, 'showErrorMessage');
|
|
notification.data.logshakePayload = { error: '' };
|
|
logshake.handleSystemMessageNotification(notification);
|
|
assert.isTrue(showErrorMessageSpy.calledOnce);
|
|
assert.isTrue(
|
|
showErrorMessageSpy.calledWith(
|
|
notification.data.logshakePayload.error));
|
|
delete notification.data.logshakePayload;
|
|
});
|
|
});
|
|
|
|
suite('closeSystemMessageNotification behavior', function() {
|
|
var notifCloseSpy, notificationGetStub;
|
|
|
|
setup(function() {
|
|
notifCloseSpy = this.sinon.spy(notification, 'close');
|
|
notificationGetStub = function notificationGet() {
|
|
return {
|
|
then: function(cb) {
|
|
cb && cb([ notification ]);
|
|
}
|
|
};
|
|
};
|
|
this.sinon.stub(window.Notification, 'get', notificationGetStub);
|
|
});
|
|
|
|
test('closes notification by tag', function() {
|
|
logshake.closeSystemMessageNotification(notification);
|
|
assert.isTrue(window.Notification.get.calledOnce);
|
|
assert.isTrue(window.Notification.get.calledWith(
|
|
{ tag: notification.tag}));
|
|
assert.isTrue(notifCloseSpy.calledOnce);
|
|
});
|
|
});
|
|
});
|
|
});
|