VulpesOS/gaia/apps/system/test/unit/logshake_test.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

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);
});
});
});
});