commonTests('Services', function (oasis) { test("service is notified about ports created for a sandbox", function() { expect(2); oasis.register({ url: "fixtures/index.js", capabilities: ['testData'] }); stop(2); var DataService = Oasis.Service.extend({ initialize: function(port, capability) { start(); equal(this.sandbox, sandbox, "Data service had `sandbox` set correctly."); equal(capability, 'testData', "Data service constructor is told what capability it fulfills."); } }); var sandbox = oasis.createSandbox({ url: "fixtures/index.js", services: { testData: DataService } }); sandbox.waitForLoad().then( function() { start(); }); sandbox.start(); }); test("The `destroy` hook is called on each service when terminating the sandbox", function() { expect(1); oasis.register({ url: "fixtures/index.js", capabilities: ['assertions'] }); stop(); var AssertionsService = Oasis.Service.extend({ destroy: function() { ok(true, "The destroy hook is called"); } }); var sandbox = oasis.createSandbox({ url: "fixtures/index.js", services: { assertions: AssertionsService } }); sandbox.start(); sandbox.waitForLoad().then( function() { sandbox.terminate(); start(); }); }); });
commonTests('Requests', function (oasis) { test("environment can request a value from a sandbox", function() { expect(1); oasis.register({ url: "fixtures/simple_value.js", capabilities: ['pong'] }); stop(); var PingPongService = Oasis.Service.extend({ initialize: function(port, capability) { port.request('ping').then(function(data) { start(); equal(data, 'pong', "promise was resolved with expected value"); }); } }); var sandbox = oasis.createSandbox({ url: 'fixtures/simple_value.js', services: { pong: PingPongService } }); sandbox.start(); }); test("environment can request a value from a sandbox with arguments", function() { expect(1); oasis.register({ url: "fixtures/simple_value_with_args.js", capabilities: ['pong'] }); stop(); var PingPongService = Oasis.Service.extend({ initialize: function(port) { port.request('ping', "first", "second").then(function(data) { start(); equal(data, 'pong', "promise was resolved with expected value"); }); } }); var sandbox = oasis.createSandbox({ url: 'fixtures/simple_value_with_args.js', services: { pong: PingPongService } }); sandbox.start(); }); test("sandbox can request a value from the environment", function() { expect(1); oasis.register({ url: "fixtures/request_from_sandbox.js", capabilities: ['pong'] }); stop(); var PingPongService = Oasis.Service.extend({ requests: { ping: function() { return 'pong'; } }, events: { testResolvedToSatisfaction: function() { start(); ok(true, "test was resolved to sandbox's satisfaction"); } } }); var sandbox = oasis.createSandbox({ url: 'fixtures/request_from_sandbox.js', services: { pong: PingPongService } }); sandbox.start(); }); test("sandbox can request a value from the environment with arguments", function() { expect(1); oasis.register({ url: "fixtures/request_from_sandbox_with_args.js", capabilities: ['pong'] }); stop(); var PingPongService = Oasis.Service.extend({ requests: { ping: function(firstArg, secondArg) { if (firstArg === 'first' && secondArg === 'second') { return 'pong'; } } }, events: { testResolvedToSatisfaction: function() { start(); ok(true, "test was resolved to sandbox's satisfaction"); } } }); var sandbox = oasis.createSandbox({ url: 'fixtures/request_from_sandbox_with_args.js', services: { pong: PingPongService } }); sandbox.start(); }); test("environment can respond to a sandbox request with a promise that resolves", function() { expect(1); oasis.register({ url: "fixtures/request_from_sandbox.js", capabilities: ['pong'] }); stop(); var PingPongPromiseService = Oasis.Service.extend({ requests: { ping: function() { return new Oasis.RSVP.Promise(function (resolve, reject) { setTimeout( function () { resolve('pong'); }, 1); }); } }, events: { testResolvedToSatisfaction: function() { start(); ok(true, "test was resolved to sandbox's satisfaction"); } } }); var sandbox = oasis.createSandbox({ url: 'fixtures/request_from_sandbox.js', services: { pong: PingPongPromiseService } }); sandbox.start(); }); test("sandbox can respond to an environment request with a promise that resolves", function() { expect(1); oasis.register({ url: "fixtures/promise.js", capabilities: ['promisepong'] }); stop(); var PingPongPromiseService = Oasis.Service.extend({ initialize: function(port, capability) { this.sandbox.pingPongPort = port; } }); var sandbox = oasis.createSandbox({ url: 'fixtures/promise.js', services: { promisepong: PingPongPromiseService } }); sandbox.waitForLoad().then( function() { sandbox.pingPongPort.request('ping').then(function(data) { start(); equal(data, 'pong', "promise was resolved with expected value"); }); }); sandbox.start(); }); test("environment can respond to a sandbox request with a promise that rejects", function() { expect(1); oasis.register({ url: "fixtures/request_from_sandbox.js", capabilities: ['pong'] }); stop(); var PingPongPromiseService = Oasis.Service.extend({ requests: { ping: function() { return new Oasis.RSVP.Promise(function (resolve, reject) { setTimeout( function () { try { throw new Error('badpong'); } catch (error) { reject(error); } }, 1); }); } }, events: { testResolvedToSatisfaction: function() { start(); ok(true, "test was resolved to sandbox's satisfaction"); } } }); var sandbox = oasis.createSandbox({ url: 'fixtures/request_from_sandbox.js', services: { pong: PingPongPromiseService } }); sandbox.start(); }); test("sandbox can respond to an environment request with a promise that rejects", function() { expect(errorsHaveStacks ? 2 : 1); oasis.register({ url: "fixtures/rejected_request_from_environment.js", capabilities: ['pong'] }); stop(); var PingPongPromiseService = Oasis.Service.extend({ initialize: function(port, capability) { port.request('ping').then(null, function(error) { start(); equal(error.message, 'badpong', "promise was rejected with expected error"); if (errorsHaveStacks) { equal(typeof error.stack, 'string', "promise was rejected with error that included stack"); } }); } }); var sandbox = oasis.createSandbox({ url: 'fixtures/rejected_request_from_environment.js', services: { pong: PingPongPromiseService } }); sandbox.start(); }); test("requests whose values are undefined are treated as failures", function() { expect(1); oasis.register({ url: "fixtures/request_value_is_undefined.js", capabilities: ['pong'] }); stop(); var PingPongService = Oasis.Service.extend({ initialize: function(port, capability) { port.request('ping').then(null, function(error) { start(); ok(/did not return a value/.test(error), "undefined request values are treated as errors"); }); } }); var sandbox = oasis.createSandbox({ url: 'fixtures/request_value_is_undefined.js', services: { pong: PingPongService } }); sandbox.start(); }); });
commonTests('Consumer', function (createSandbox, adapter) { test("Consumers instances are saved on the Oasis global", function() { stop(); oasis.register({ url: "fixtures/consumer.js", capabilities: ['assertions'] }); var AssertionsService = Oasis.Service.extend({ events: { ok: function() { start(); ok(true, "Consumer was accessed"); } } }); var sandbox = createSandbox({ url: 'fixtures/consumer.js', services: { assertions: AssertionsService } }); sandbox.start(); }); test("Consumers do not process events until connect() has been called", function() { oasis.register({ url: 'fixtures/delayed_connect.js', capabilities: ['assertions'] }); stop(); var AssertionsService = Oasis.Service.extend({ initialize: function(port) { port.send('ping'); }, events: { pong: function() { start(); ok(true, "The child card acknowledged the ping"); } } }); var sandbox = createSandbox({ url: 'fixtures/delayed_connect.js', services: { assertions: AssertionsService } }); sandbox.start(); }); });
commonTests('Sandbox', function (createSandbox, adapter) { test("assertion: must register package", function() { expect(1); raises(function() { createSandbox({ url: "fixtures/index.js" }); }, Error, "Creating a sandbox from an unregistered package fails"); }); test("assertion: must provide capabilities when registering a package", function() { expect(1); raises(function() { oasis.register({ url: 'fixtures/index.js' }); }, Error, "Registering a package without capabilities fails"); }); test("sandboxes have access to ports & services via `sandbox.capabilities`", function() { expect(2); stop(2); var sandbox = createSandbox({ url: 'fixtures/sandbox_ports.js', capabilities: ['assertions', 'something', 'somethingElse'], services: { assertions: Oasis.Service.extend({ events: { somethingOkay: function () { start(); ok(true, "sandbox.capabilities could be used to send messages to named services"); }, somethingElseOkay: function () { start(); ok(true, "sandbox.capabilities could be used to send messages to named services"); } } }), something: Oasis.Service, somethingElse: Oasis.Service } }); sandbox.waitForLoad().then(function () { sandbox.capabilities.something.send('go'); sandbox.capabilities.somethingElse.send('go'); }); sandbox.start(); }); test("sandboxes have access to services via `sandbox.capabilities`", function() { expect(2); stop(); var LocalService = Oasis.Service.extend({ }), channel = adapter.createChannel(oasis), port = channel.port2; var sandbox = createSandbox({ url: 'fixtures/index.js', capabilities: ['serviceCapability', 'portCapability'], services: { serviceCapability: LocalService, portCapability: port } }); sandbox.waitForLoad().then(function () { var capabilities = sandbox.capabilities; ok(capabilities.serviceCapability instanceof LocalService, "The capability has an associated service"); equal(capabilities.portCapability, port, "The capability has an associated port"); start(); }); sandbox.start(); }); test("Sandboxes should have promises that are resolved when the sandbox has finished initializing", function() { expect(3); oasis.register({ url: 'fixtures/index.js', capabilities: ['assertions'] }); var serviceInitialized = false; var AssertionsService = Oasis.Service.extend({ initialize: function(port) { this.sandbox.assertionsPort = port; serviceInitialized = true; } }); var sandbox = createSandbox({ url: 'fixtures/index.js', services: { assertions: AssertionsService } }); equal(serviceInitialized, false, "The service is not immediately initialized"); stop(); sandbox.waitForLoad().then(function() { start(); ok(sandbox.assertionsPort, "The promise was not resolved until the service has been initialized"); equal(serviceInitialized, true, "The service has been initialized once the promise is resolved"); }); sandbox.start(); }); test("Sandboxes can ask for ports directly via portFor", function() { expect(3); stop(2); var AssertionService = Oasis.Service.extend({ events: { blackRaven: function (message) { equal(message, "dark words", "Card connected to port and sent a message."); }, whiteRaven: function (message) { start(); equal(message, "winter is coming", "Card retrieved port via `portFor`."); }, redRaven: function (message) { start(); equal(message, "no such port", "`portFor` throws an exception when asked to retrieve a port for an unsupplied capability"); } } }); var sandbox = createSandbox({ url: 'fixtures/port_for.js', capabilities: ['assertions'], services: { assertions: AssertionService } }); sandbox.start(); }); test("Multiple sandboxes can be created in the same environment", function() { expect(2); var sandbox1, sandbox2; oasis.register({ url: "fixtures/multiple_url_1.js", capabilities: ['assertions'] }); oasis.register({ url: "fixtures/multiple_url_2.js", capabilities: ['assertions2'] }); stop(2); var AssertionsService = Oasis.Service.extend({ events: { ok1: function() { ok(true, "First event was fired"); start(); sandbox2.start(); sandbox1.waitForLoad().then( function() { sandbox1.terminate(); }); }, ok2: function() { ok(true, "Second event was fired"); start(); } } }); sandbox1 = createSandbox({ url: 'fixtures/multiple_url_1.js', services: { assertions: AssertionsService } }); sandbox2 = createSandbox({ url: "fixtures/multiple_url_2.js", services: { assertions2: AssertionsService } }); sandbox1.start(); }); test("Oasis' bootloader can be loaded from a default URL", function() { expect(2); oasis.configure('allowSameOrigin', true); oasis.register({ url: "fixtures/assertions.js", capabilities: ['assertions'] }); stop(); var AssertionsService = Oasis.Service.extend({ initialize: function(port, capability) { equal(capability, 'assertions', "precond - capability is the assertions service"); port.on('ok', function(data) { start(); equal(data, 'success', "The sandbox was able to communicate back"); }); } }); var sandbox = createSandbox({ url: "fixtures/assertions.js", services: { assertions: AssertionsService } }, true); sandbox.start(); }); });
commonTests('Wiretapping', function (createSandbox, adapter) { test("Sandboxes can be wiretapped to listen to events being sent and received", function() { expect(4); oasis.register({ url: 'fixtures/wiretapping.js', capabilities: ['assertions', 'otherStuff'] }); var AssertionsService = Oasis.Service.extend({ initialize: function(port) { port.send('sentAssertion'); } }); var OtherStuffService = Oasis.Service.extend({ initialize: function(port) { port.send('sentOther'); } }); var sandbox = createSandbox({ url: 'fixtures/wiretapping.js', services: { assertions: AssertionsService, otherStuff: OtherStuffService } }); stop(4); var expectedEvents = ['sentAssertion', 'sentOther', 'receivedAssertion', 'receivedOther']; sandbox.wiretap(function(service, event) { start(); var loc = a_indexOf.call(expectedEvents, event.type); if (loc > -1) { ok(true, "received expected message " + event.type); expectedEvents.splice(loc, 1); } }); sandbox.start(); }); });
commonTests('Nested Sandbox', function (createSandbox, adapter) { // TODO: Get inception adapters working in web workers if (iframeAdapter === adapter) { test("ports sent to a sandbox can be passed to its child sandboxes", function() { expect(1); oasis.register({ url: "fixtures/inception_parent.js", capabilities: ['inception'] }); stop(); var InceptionService = Oasis.Service.extend({ initialize: function(port) { port.onRequest('kick', function() { return 'kick1'; }); port.on('workPlacement', function(value) { start(); equal(value, 'kick1', "messages between deeply nested sandboxes are sent"); }); } }); var sandbox = createSandbox({ url: 'fixtures/inception_parent.js', services: { inception: InceptionService } }); sandbox.start(); }); test("ports sent to a sandbox can be passed to its child sandboxes while supporting a shorthand", function() { expect(3); oasis.register({ url: "fixtures/inception_parent.js", capabilities: ['inception'] }); stop(); var InceptionService = Oasis.Service.extend({ requests: { kick: function() { ok(this instanceof InceptionService, "The callback gets the service instance as `this`"); return 'kick2'; } }, events: { workPlacement: function(value) { start(); equal(value, 'kick2', "messages between deeply nested sandboxes are sent"); ok(this instanceof InceptionService, "The callback gets the service instance as `this`"); } } }); var sandbox = createSandbox({ url: 'fixtures/inception_parent.js', services: { inception: InceptionService } }); sandbox.start(); }); } });
commonTests('Ports', function (oasis) { test("sandbox can communicate with the environment through a port", function() { expect(2); oasis.register({ url: "fixtures/assertions.js", capabilities: ['assertions'] }); stop(); var AssertionsService = Oasis.Service.extend({ initialize: function(port, capability) { equal(capability, 'assertions', "precond - capability is the assertions service"); port.on('ok', function(data) { start(); equal(data, 'success', "The card was able to communicate back"); }); } }); var sandbox = oasis.createSandbox({ url: "fixtures/assertions.js", services: { assertions: AssertionsService } }); sandbox.start(); }); test("shorthand - sandbox can communicate with the environment through a port", function() { expect(1); stop(); var sandbox = oasis.createSandbox({ url: "fixtures/assertions.js", capabilities: ['assertions'] }); sandbox.connect('assertions').then(function(port) { port.on('ok', function(data) { start(); equal(data, 'success', "The card was able to communicate back"); }); }); sandbox.start(); }); test("when closing a port, no messages are received", function() { expect(1); var testPort; oasis.register({ url: "fixtures/close_service.js", capabilities: ['close'] }); stop(); var CloseService = Oasis.Service.extend({ events: { sandboxInitialized: function(data) { ok(true, "sandbox initialized"); this.send('ping'); this.port.close(); // We try to test the lack of message setTimeout( function() { start(); }, 200); }, pong: function() { ok(false, "Succesfully sent events from event shorthand function"); } } }); var sandbox = oasis.createSandbox({ url: "fixtures/close_service.js", services: { close: CloseService } }); sandbox.start(); }); test("environment can communicate with the sandbox through a port", function() { expect(2); oasis.register({ url: "fixtures/to_environment.js", capabilities: ['pingpong'] }); stop(); var PingPongService = Oasis.Service.extend({ initialize: function(port, capability) { equal(capability, 'pingpong', "precond - capability is the pingpong service"); port.on('pong', function(data) { start(); equal(data, "PONG", "Got pong from the child"); }); port.send('ping', "PONG"); } }); var sandbox = oasis.createSandbox({ url: 'fixtures/to_environment.js', services: { pingpong: PingPongService } }); sandbox.start(); }); test("environment can communicate with the sandbox through a port with a shorthand", function() { expect(1); oasis.register({ url: "fixtures/to_environment.js", capabilities: ['pingpong'] }); stop(); var PingPongService = Oasis.Service.extend({ events: { pong: function(data) { start(); equal(data, "PONG", "Got pong from the child"); } }, initialize: function(port) { port.send('ping', "PONG"); } }); var sandbox = oasis.createSandbox({ url: 'fixtures/to_environment.js', services: { pingpong: PingPongService } }); sandbox.start(); }); });