Bug 794680 - Connect gecko to b2g identity ui. r=benadida

This commit is contained in:
Jed Parsons 2012-10-16 21:34:02 -04:00
parent a03f8d4021
commit 9563e27ce0
13 changed files with 834 additions and 8 deletions

View File

@ -35,6 +35,7 @@ EXTRA_PP_COMPONENTS = \
EXTRA_JS_MODULES = \
TelURIParser.jsm \
SignInToWebsite.jsm \
$(NULL)
TEST_DIRS = \

View File

@ -0,0 +1,168 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
//
// the B2G version of Identity UX
//
"use strict";
const EXPORTED_SYMBOLS = ["SignInToWebsiteController"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
"resource://gre/modules/identity/MinimalIdentity.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Logger",
"resource://gre/modules/identity/LogUtils.jsm");
// JS shim that contains the callback functions that
// live within the identity UI provisioning frame.
// (NOT THE SAME THING AS IdP)
const kIdentityShimFile = "chrome://browser/content/identity.js";
// Type of MozChromEvents to handle payment dialogs.
const kOpenIdentityDialog = "open-id-dialog";
const kCloseIdentityDialog = "close-id-dialog";
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["SignInToWebsiteController"].concat(aMessageArgs));
}
let SignInToWebsiteController = {
init: function SignInToWebsiteController_init() {
log("SignInToWebsiteController: init");
Services.obs.addObserver(this, "identity-controller-watch", false);
Services.obs.addObserver(this, "identity-controller-request", false);
Services.obs.addObserver(this, "identity-controller-logout", false);
},
uninit: function SignInToWebsiteController_uninit() {
Services.obs.removeObserver(this, "identity-controller-watch");
Services.obs.removeObserver(this, "identity-controller-request");
Services.obs.removeObserver(this, "identity-controller-logout");
},
observe: function SignInToWebsiteController_observe(aSubject, aTopic, aData) {
log("observe: received", aTopic, "with", aData, "for", aSubject);
let options = null;
if (aSubject) {
options = aSubject.wrappedJSObject;
}
switch(aTopic) {
case "identity-controller-watch":
this.doWatch(options);
break;
case "identity-controller-request":
this.doRequest(options);
break;
case "identity-controller-logout":
this.doLogout(options);
break;
default:
Logger.reportError("SignInToWebsiteController", "Unknown observer notification:", aTopic);
break;
}
},
getRandomId: function getRandomId() {
return uuidgen.generateUUID().toString();
},
doWatch: function SignInToWebsiteController_doWatch(aOptions) {
// for now, just say we're ready
// this._openDialogAndSendMessage(aOptions.rpId, "identity-delegate-watch", aOptions);
IdentityService.doReady(aOptions.rpId);
},
/**
* The website is requesting login so the user must choose an identity to use.
*/
doRequest: function SignInToWebsiteController_doRequest(aOptions) {
log("request options", aOptions);
this._openDialogAndSendMessage(aOptions.rpId, "identity-delegate-request", aOptions);
},
/*
*
*/
doLogout: function SignInToWebsiteController_doLogout(aOptions) {
log("logout options", aOptions);
IdentityService.doLogout(aOptions.rpId);
},
// FIXME: add a callback when the response from dialog is received
// so watch can call doReady, while request doesn't.
_openDialogAndSendMessage: function SignInToWebsiteController_openDialogAndSendMessage(aRpId, aMessage, aOptions) {
let browser = Services.wm.getMostRecentWindow("navigator:browser");
let content = browser.getContentWindow();
if (!content) {
// aErrorCb.onresult("NO_CONTENT_WINDOW");
return;
}
// prepare the message to be sent to gaia
let id = kOpenIdentityDialog + "-" + this.getRandomId();
let detail = {
type: kOpenIdentityDialog,
id: id
};
log("id before is ", id);
// wait for Gaia to send us a message back
content.addEventListener("mozContentEvent", function getAssertion(evt) {
log("id after is ", id);
let msg = evt.detail;
if (msg.id != id) {
log("mozContentEvent. evt.detail.id != ", id, msg);
content.removeEventListener("mozContentEvent", getAssertion);
return;
}
// Try to load the identity shim file containing the callbacks
// in the content script.
let frame = evt.detail.frame;
let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
let mm = frameLoader.messageManager;
try {
log("about to load frame script");
mm.loadFrameScript(kIdentityShimFile, true);
} catch (e) {
log("Error loading ", kIdentityShimFile, " as a frame script: ", e);
}
mm.addMessageListener("identity-delegate-return-assertion", function(message) {
log("back with assertion", message.json);
if (message.json.assertion)
IdentityService.doLogin(aRpId, message.json.assertion);
IdentityService.doReady(aRpId);
});
// send the options down
log("sending message" , aMessage, aOptions);
mm.sendAsyncMessage(aMessage, aOptions);
log("done load frame script");
content.removeEventListener("mozContentEvent", getAssertion);
});
browser.shell.sendChromeEvent(detail);
log("sent", detail, "to gaia");
}
};

View File

@ -0,0 +1,118 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
// The following boilerplate makes sure that XPCom calls
// that use the profile directory work.
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Identity",
"resource://gre/modules/identity/Identity.jsm",
"Identity");
XPCOMUtils.defineLazyModuleGetter(this,
"Logger",
"resource://gre/modules/identity/LogUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this,
"uuidGenerator",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
const TEST_URL = "https://myfavoriteflan.com";
const TEST_USER = "uumellmahaye1969@hotmail.com";
const TEST_PRIVKEY = "i-am-a-secret";
const TEST_CERT = "i-am-a-cert";
// The following are utility functions for Identity testing
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["test"].concat(aMessageArgs));
}
function partial(fn) {
let args = Array.prototype.slice.call(arguments, 1);
return function() {
return fn.apply(this, args.concat(Array.prototype.slice.call(arguments)));
};
}
function uuid() {
return uuidGenerator.generateUUID().toString();
}
// create a mock "doc" object, which the Identity Service
// uses as a pointer back into the doc object
function mock_doc(aIdentity, aOrigin, aDoFunc) {
let mockedDoc = {};
mockedDoc.id = uuid();
mockedDoc.loggedInEmail = aIdentity;
mockedDoc.origin = aOrigin;
mockedDoc['do'] = aDoFunc;
mockedDoc.doReady = partial(aDoFunc, 'ready');
mockedDoc.doLogin = partial(aDoFunc, 'login');
mockedDoc.doLogout = partial(aDoFunc, 'logout');
mockedDoc.doError = partial(aDoFunc, 'error');
mockedDoc.doCancel = partial(aDoFunc, 'cancel');
mockedDoc.doCoffee = partial(aDoFunc, 'coffee');
return mockedDoc;
}
// mimicking callback funtionality for ease of testing
// this observer auto-removes itself after the observe function
// is called, so this is meant to observe only ONE event.
function makeObserver(aObserveTopic, aObserveFunc) {
let observer = {
// nsISupports provides type management in C++
// nsIObserver is to be an observer
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
observe: function (aSubject, aTopic, aData) {
if (aTopic == aObserveTopic) {
aObserveFunc(aSubject, aTopic, aData);
Services.obs.removeObserver(observer, aObserveTopic);
}
}
};
Services.obs.addObserver(observer, aObserveTopic, false);
}
// set up the ID service with an identity with keypair and all
// when ready, invoke callback with the identity
function setup_test_identity(identity, cert, cb) {
// set up the store so that we're supposed to be logged in
let store = get_idstore();
function keyGenerated(err, kpo) {
store.addIdentity(identity, kpo, cert);
cb();
};
jwcrypto.generateKeyPair("DS160", keyGenerated);
}
// takes a list of functions and returns a function that
// when called the first time, calls the first func,
// then the next time the second, etc.
function call_sequentially() {
let numCalls = 0;
let funcs = arguments;
return function() {
if (!funcs[numCalls]) {
let argString = Array.prototype.slice.call(arguments).join(",");
do_throw("Too many calls: " + argString);
return;
}
funcs[numCalls].apply(funcs[numCalls],arguments);
numCalls += 1;
};
}

View File

@ -0,0 +1,45 @@
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "MinimalIDService",
"resource://gre/modules/identity/MinimalIdentity.jsm",
"IdentityService");
XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsite",
"resource://gre/b2g/components/SignInToWebsite.jsm",
"SignInToWebsite");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["test_signintowebsite"].concat(aMessageArgs));
}
function test_overall() {
do_check_neq(MinimalIDService, null);
run_next_test();
}
function test_mock_doc() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL, function(action, params) {
do_check_eq(action, 'coffee');
do_test_finished();
run_next_test();
});
mockedDoc.doCoffee();
}
// XXX bug 800085 complete these tests - mock the gaia content object
// so we can round trip through the SignInToWebsiteController
let TESTS = [
test_overall,
test_mock_doc,
];
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View File

@ -3,3 +3,9 @@ head =
tail =
[test_bug793310.js]
[test_signintowebsite.js]
head = head_identity.js
tail =

View File

@ -12,13 +12,23 @@ let EXPORTED_SYMBOLS = ["DOMIdentity"];
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
/*
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
"resource://gre/modules/identity/Identity.jsm");
*/
// minimal Identity.jsm
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
"resource://gre/modules/identity/MinimalIdentity.jsm");
XPCOMUtils.defineLazyModuleGetter(this,
"Logger",
"resource://gre/modules/identity/LogUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageListenerManager");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs));
}
@ -118,13 +128,15 @@ RPWatchContext.prototype = {
let DOMIdentity = {
// nsIMessageListener
receiveMessage: function DOMIdentity_receiveMessage(aMessage) {
log("**received message", aMessage);
let msg = aMessage.json;
// Target is the frame message manager that called us and is
// used to send replies back to the proper window.
let targetMM = aMessage.target
/* let targetMM = aMessage.target
.QueryInterface(Ci.nsIFrameLoaderOwner)
.frameLoader.messageManager;
.frameLoader.messageManager;*/
let targetMM = aMessage.target;
switch (aMessage.name) {
// RP
@ -199,8 +211,11 @@ let DOMIdentity = {
let func = aWindow.messageManager[aRegister ? "addMessageListener"
: "removeMessageListener"];
log("in _configureMessages *****", aRegister, this.messages);
for (let message of this.messages) {
func.call(aWindow.messageManager, message, this);
// func.call(aWindow.messageManager, message, this);
ppmm.addMessageListener(message, this);
}
},

View File

@ -17,6 +17,10 @@ const MAX_RP_CALLS = 100;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsIMessageSender");
// This is the child process corresponding to nsIDOMIdentity.
@ -483,10 +487,12 @@ nsDOMIdentityInternal.prototype = {
this._log("init was called from " + aWindow.document.location);
this._mm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
/*this._mm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
*/
this._mm = cpmm;
// Setup listeners for messages from parent process.
this._messages = [

View File

@ -35,6 +35,7 @@ EXTRA_JS_MODULES = \
IdentityStore.jsm \
jwcrypto.jsm \
LogUtils.jsm \
MinimalIdentity.jsm \
RelyingParty.jsm \
Sandbox.jsm \
$(NULL)

View File

@ -0,0 +1,370 @@
/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* this alternate implementation of IdentityService
* provides just the channels for navigator.id,
* leaving the certificate storage to a server-provided app
*/
"use strict";
const EXPORTED_SYMBOLS = ["IdentityService"];
const Cu = Components.utils;
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this,
"jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["minimal core"].concat(aMessageArgs));
}
function reportError(...aMessageArgs) {
Logger.reportError.apply(Logger, ["core"].concat(aMessageArgs));
}
function IDService() {
Services.obs.addObserver(this, "quit-application-granted", false);
Services.obs.addObserver(this, "identity-auth-complete", false);
// simplify, it's one object
this.RP = this;
this.IDP = this;
// keep track of flows
this._rpFlows = {};
this._authFlows = {};
this._provFlows = {};
}
IDService.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
observe: function observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "quit-application-granted":
Services.obs.removeObserver(this, "quit-application-granted");
this.shutdown();
break;
}
},
shutdown: function shutdown() {
log("shutdown");
Services.obs.removeObserver(this, "quit-application-granted");
},
/**
* Parse an email into username and domain if it is valid, else return null
*/
parseEmail: function parseEmail(email) {
var match = email.match(/^([^@]+)@([^@^/]+.[a-z]+)$/);
if (match) {
return {
username: match[1],
domain: match[2]
};
}
return null;
},
// RP stuff
/**
* Register a listener for a given windowID as a result of a call to
* navigator.id.watch().
*
* @param aCaller
* (Object) an object that represents the caller document, and
* is expected to have properties:
* - id (unique, e.g. uuid)
* - loggedInEmail (string or null)
* - origin (string)
*
* and a bunch of callbacks
* - doReady()
* - doLogin()
* - doLogout()
* - doError()
* - doCancel()
*
*/
watch: function watch(aRpCaller) {
log("watch: ", aRpCaller);
// store the caller structure and notify the UI observers
this._rpFlows[aRpCaller.id] = aRpCaller;
let options = {rpId: aRpCaller.id, origin: aRpCaller.origin};
Services.obs.notifyObservers({wrappedJSObject: options},"identity-controller-watch", null);
},
/**
* Initiate a login with user interaction as a result of a call to
* navigator.id.request().
*
* @param aRPId
* (integer) the id of the doc object obtained in .watch()
*
* @param aOptions
* (Object) options including privacyPolicy, termsOfService
*/
request: function request(aRPId, aOptions) {
log("request: rpId:", aRPId);
let rp = this._rpFlows[aRPId];
// Notify UX to display identity picker.
// Pass the doc id to UX so it can pass it back to us later.
let options = {rpId: aRPId, origin: rp.origin};
Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-request", null);
},
/**
* Invoked when a user wishes to logout of a site (for instance, when clicking
* on an in-content logout button).
*
* @param aRpCallerId
* (integer) the id of the doc object obtained in .watch()
*
*/
logout: function logout(aRpCallerId) {
log("logout: RP caller id:", aRpCallerId);
Services.obs.notifyObservers({wrappedJSObject: {rpId: aRpCallerId}},
"identity-controller-logout",
null);
},
// once the UI-and-display-logic components have received notifications, they call back with
// direct invocation of the following.
/**
* make login happen with an assertion
*/
doLogin: function doLogin(aRpCallerId, aAssertion) {
let rp = this._rpFlows[aRpCallerId];
if (!rp)
return;
rp.doLogin(aAssertion);
},
doLogout: function doLogout(aRpCallerId) {
let rp = this._rpFlows[aRpCallerId];
if (!rp)
return;
rp.doLogout();
},
doReady: function doReady(aRpCallerId) {
let rp = this._rpFlows[aRpCallerId];
if (!rp)
return;
rp.doReady();
},
// IdP
/**
* the provisioning iframe sandbox has called navigator.id.beginProvisioning()
*
* @param aCaller
* (object) the iframe sandbox caller with all callbacks and
* other information. Callbacks include:
* - doBeginProvisioningCallback(id, duration_s)
* - doGenKeyPairCallback(pk)
*/
beginProvisioning: function beginProvisioning(aCaller) {
},
/**
* the provisioning iframe sandbox has called
* navigator.id.raiseProvisioningFailure()
*
* @param aProvId
* (int) the identifier of the provisioning flow tied to that sandbox
* @param aReason
*/
raiseProvisioningFailure: function raiseProvisioningFailure(aProvId, aReason) {
reportError("Provisioning failure", aReason);
},
/**
* When navigator.id.genKeyPair is called from provisioning iframe sandbox.
* Generates a keypair for the current user being provisioned.
*
* @param aProvId
* (int) the identifier of the provisioning caller tied to that sandbox
*
* It is an error to call genKeypair without receiving the callback for
* the beginProvisioning() call first.
*/
genKeyPair: function genKeyPair(aProvId) {
},
/**
* When navigator.id.registerCertificate is called from provisioning iframe
* sandbox.
*
* Sets the certificate for the user for which a certificate was requested
* via a preceding call to beginProvisioning (and genKeypair).
*
* @param aProvId
* (integer) the identifier of the provisioning caller tied to that
* sandbox
*
* @param aCert
* (String) A JWT representing the signed certificate for the user
* being provisioned, provided by the IdP.
*/
registerCertificate: function registerCertificate(aProvId, aCert) {
},
/**
* The authentication frame has called navigator.id.beginAuthentication
*
* IMPORTANT: the aCaller is *always* non-null, even if this is called from
* a regular content page. We have to make sure, on every DOM call, that
* aCaller is an expected authentication-flow identifier. If not, we throw
* an error or something.
*
* @param aCaller
* (object) the authentication caller
*
*/
beginAuthentication: function beginAuthentication(aCaller) {
},
/**
* The auth frame has called navigator.id.completeAuthentication
*
* @param aAuthId
* (int) the identifier of the authentication caller tied to that sandbox
*
*/
completeAuthentication: function completeAuthentication(aAuthId) {
},
/**
* The auth frame has called navigator.id.cancelAuthentication
*
* @param aAuthId
* (int) the identifier of the authentication caller
*
*/
cancelAuthentication: function cancelAuthentication(aAuthId) {
},
// methods for chrome and add-ons
/**
* Discover the IdP for an identity
*
* @param aIdentity
* (string) the email we're logging in with
*
* @param aCallback
* (function) callback to invoke on completion
* with first-positional parameter the error.
*/
_discoverIdentityProvider: function _discoverIdentityProvider(aIdentity, aCallback) {
// XXX bug 767610 - validate email address call
// When that is available, we can remove this custom parser
var parsedEmail = this.parseEmail(aIdentity);
if (parsedEmail === null) {
return aCallback("Could not parse email: " + aIdentity);
}
log("_discoverIdentityProvider: identity:", aIdentity, "domain:", parsedEmail.domain);
this._fetchWellKnownFile(parsedEmail.domain, function fetchedWellKnown(err, idpParams) {
// idpParams includes the pk, authorization url, and
// provisioning url.
// XXX bug 769861 follow any authority delegations
// if no well-known at any point in the delegation
// fall back to browserid.org as IdP
return aCallback(err, idpParams);
});
},
/**
* Fetch the well-known file from the domain.
*
* @param aDomain
*
* @param aScheme
b * (string) (optional) Protocol to use. Default is https.
* This is necessary because we are unable to test
* https.
*
* @param aCallback
*
*/
_fetchWellKnownFile: function _fetchWellKnownFile(aDomain, aCallback, aScheme='https') {
// XXX bug 769854 make tests https and remove aScheme option
let url = aScheme + '://' + aDomain + "/.well-known/browserid";
log("_fetchWellKnownFile:", url);
// this appears to be a more successful way to get at xmlhttprequest (which supposedly will close with a window
let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
// XXX bug 769865 gracefully handle being off-line
// XXX bug 769866 decide on how to handle redirects
req.open("GET", url, true);
req.responseType = "json";
req.mozBackgroundRequest = true;
req.onload = function _fetchWellKnownFile_onload() {
if (req.status < 200 || req.status >= 400) {
log("_fetchWellKnownFile", url, ": server returned status:", req.status);
return aCallback("Error");
}
try {
let idpParams = req.response;
// Verify that the IdP returned a valid configuration
if (! (idpParams.provisioning &&
idpParams.authentication &&
idpParams['public-key'])) {
let errStr= "Invalid well-known file from: " + aDomain;
log("_fetchWellKnownFile:", errStr);
return aCallback(errStr);
}
let callbackObj = {
domain: aDomain,
idpParams: idpParams,
};
log("_fetchWellKnownFile result: ", callbackObj);
// Yay. Valid IdP configuration for the domain.
return aCallback(null, callbackObj);
} catch (err) {
reportError("_fetchWellKnownFile", "Bad configuration from", aDomain, err);
return aCallback(err.toString());
}
};
req.onerror = function _fetchWellKnownFile_onerror() {
log("_fetchWellKnownFile", "ERROR:", req.status, req.statusText);
log("ERROR: _fetchWellKnownFile:", err);
return aCallback("Error");
};
req.send(null);
},
};
let IdentityService = new IDService();

View File

@ -13,6 +13,6 @@ include $(DEPTH)/config/autoconf.mk
MODULE = test_identity
XPCSHELL_TESTS = unit
DIRS = chrome mochitest
DIRS = chrome mochitest
include $(topsrcdir)/config/rules.mk

View File

@ -46,8 +46,8 @@ function test_select_identity() {
let mockedDoc = mock_doc(null, TEST_URL, call_sequentially(
function(action, params) {
// ready emitted from first watch() call
do_check_eq(action, 'ready');
do_check_null(params);
do_check_eq(action, 'ready');
do_check_null(params);
},
// first the login call
function(action, params) {

View File

@ -0,0 +1,95 @@
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "MinimalIDService",
"resource://gre/modules/identity/MinimalIdentity.jsm",
"IdentityService");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["test_minimalidentity"].concat(aMessageArgs));
}
function test_overall() {
do_check_neq(MinimalIDService, null);
run_next_test();
}
function test_mock_doc() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL, function(action, params) {
do_check_eq(action, 'coffee');
do_test_finished();
run_next_test();
});
mockedDoc.doCoffee();
}
/*
* Test that the "identity-controller-watch" signal is emitted correctly
*/
function test_watch() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL);
makeObserver("identity-controller-watch", function (aSubject, aTopic, aData) {
do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
do_check_eq(aSubject.wrappedJSObject.origin, TEST_URL);
do_test_finished();
run_next_test();
});
MinimalIDService.RP.watch(mockedDoc);
}
/*
* Test that the "identity-controller-request" signal is emitted correctly
*/
function test_request() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL);
makeObserver("identity-controller-request", function (aSubject, aTopic, aData) {
do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
do_check_eq(aSubject.wrappedJSObject.origin, TEST_URL);
do_test_finished();
run_next_test();
});
MinimalIDService.RP.watch(mockedDoc);
MinimalIDService.RP.request(mockedDoc.id, {});
}
/*
* Test that the "identity-controller-logout" signal is emitted correctly
*/
function test_logout() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL);
makeObserver("identity-controller-logout", function (aSubject, aTopic, aData) {
do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
do_test_finished();
run_next_test();
});
MinimalIDService.RP.watch(mockedDoc);
MinimalIDService.RP.logout(mockedDoc.id, {});
}
let TESTS = [
test_overall,
test_mock_doc,
test_watch,
test_request,
test_logout
];
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View File

@ -4,8 +4,9 @@ tail = tail_identity.js
# Test load modules first so syntax failures are caught early.
[test_load_modules.js]
[test_log_utils.js]
[test_minimalidentity.js]
[test_log_utils.js]
[test_authentication.js]
[test_crypto_service.js]
[test_identity.js]