UNPKG

@eclipse-emfcloud/model-service-theia

Version:
227 lines 9.94 kB
"use strict"; // ***************************************************************************** // Copyright (C) 2023-2024 STMicroelectronics. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: MIT License which is // available at https://opensource.org/licenses/MIT. // // SPDX-License-Identifier: EPL-2.0 OR MIT // ***************************************************************************** var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const model_validation_1 = require("@eclipse-emfcloud/model-validation"); const inversify_1 = require("@theia/core/shared/inversify"); const chai_1 = require("chai"); const sinon_1 = __importDefault(require("sinon")); const frontend_model_hub_1 = require("../frontend-model-hub"); const frontend_model_hub_subscriber_1 = require("../frontend-model-hub-subscriber"); const fake_model_hub_protocol_1 = require("./fake-model-hub-protocol"); const test_module_1 = require("./test-module"); function createTestContainer() { const container = new inversify_1.Container(); container.load(test_module_1.testModule); return container; } const MODEL1_ID = 'test.model1'; const MODEL1 = { name: 'Model 1' }; const MODEL2_ID = 'test.model2'; const MODEL2 = { name: 'Model 2' }; describe('FrontendModelHub', () => { const appContext = 'test-app'; let sandbox; let modelHub; let fake; let provider; beforeEach(async () => { sandbox = sinon_1.default.createSandbox(); const container = createTestContainer(); provider = container.get(frontend_model_hub_1.FrontendModelHubProvider); modelHub = await provider(appContext); const subscriber = container.get(frontend_model_hub_subscriber_1.FrontendModelHubSubscriber); fake = container.get(fake_model_hub_protocol_1.FakeModelHubProtocol); fake.setModel(MODEL1_ID, MODEL1); (0, fake_model_hub_protocol_1.connectClient)(fake, subscriber); }); describe('simple delegators', () => { it('getModel', async () => { const model = await modelHub.getModel(MODEL1_ID); (0, chai_1.expect)(model).to.be.like(MODEL1); }); const delegators = [ 'validateModels', 'getValidationState', 'save', 'isDirty', 'undo', 'redo', 'flush', ]; const error = { message: 'A test error.', path: '/name', severity: 'error', source: 'test', }; const results = [error, error, true, true, true, true, true]; delegators.forEach((methodName, index) => { it(methodName, async () => { const stub = sandbox.stub().resolves(results[index]); const template = {}; template[methodName] = stub; Object.assign(fake, template); const result = await modelHub[methodName](MODEL1_ID); (0, chai_1.expect)(result).to.eql(results[index]); sinon_1.default.assert.calledWithExactly(stub, appContext, MODEL1_ID); }); }); }); describe('subscriptions', () => { let onModelChanged; let onModelDirtyState; let onModelValidated; let onModelLoaded; let onModelUnloaded; let onModelHubDisposed; beforeEach(async () => { onModelChanged = sandbox.stub(); onModelDirtyState = sandbox.stub(); onModelLoaded = sandbox.stub(); onModelUnloaded = sandbox.stub(); onModelValidated = sandbox.stub(); onModelHubDisposed = sandbox.stub(); }); it('notifies model change', async () => { const sub = await modelHub.subscribe(MODEL1_ID); sub.onModelChanged = onModelChanged; const patch = [ { op: 'replace', path: '/name', value: MODEL1.name }, ]; fake.fakeModelChange(MODEL1_ID, patch); await asyncsResolved(); sinon_1.default.assert.calledWithMatch(onModelChanged, MODEL1_ID, MODEL1, patch); }); it('subscription sees consistent model', async () => { const sub = await modelHub.subscribe(); let sawCorrectModelId = false; let sawCorrectModelObject = false; const gatherAssertions = async (modelId, model) => { sawCorrectModelId = modelId === MODEL1_ID; const currentModel2 = await modelHub.getModel(modelId); sawCorrectModelObject = currentModel2 === model; }; let assertions = Promise.reject(new Error('onModelChange not called')); sub.onModelChanged = (modelId, model) => (assertions = gatherAssertions(modelId, model)); const patch = [ { op: 'replace', path: '/name', value: MODEL1.name }, ]; fake.fakeModelChange(MODEL1_ID, patch); await asyncsResolved(); await assertions; (0, chai_1.expect)(sawCorrectModelId, 'incorrect model ID in subscription call-back') .to.be.true; (0, chai_1.expect)(sawCorrectModelObject, 'incorrect model retrieved from hub during subscription call-back').to.be.true; }); it('notifies dirty state', async () => { const sub = await modelHub.subscribe(MODEL1_ID); sub.onModelDirtyState = onModelDirtyState; fake.fakeModelDirtyState(MODEL1_ID, true); await asyncsResolved(); sinon_1.default.assert.calledWithMatch(onModelDirtyState, MODEL1_ID, MODEL1, true); }); it('notifies model validation', async () => { const sub = await modelHub.subscribe(MODEL1_ID); sub.onModelValidated = onModelValidated; const diagnostic = { message: 'This is a test', path: '/name', severity: 'error', source: 'test', }; fake.fakeModelValidated(MODEL1_ID, diagnostic); await asyncsResolved(); sinon_1.default.assert.calledWithMatch(onModelValidated, MODEL1_ID, MODEL1, diagnostic); }); it('notifies model loaded', async () => { const sub = await modelHub.subscribe(MODEL2_ID); sub.onModelLoaded = onModelLoaded; fake.setModel(MODEL2_ID, MODEL2); await asyncsResolved(); sinon_1.default.assert.calledWithExactly(onModelLoaded, MODEL2_ID); }); it('notifies model unloaded', async () => { await modelHub.getModel(MODEL1_ID); const sub = await modelHub.subscribe(MODEL1_ID); sub.onModelUnloaded = onModelUnloaded; fake.removeModel(MODEL1_ID); await asyncsResolved(); sinon_1.default.assert.calledWithMatch(onModelUnloaded, MODEL1_ID, MODEL1); }); it('notifies hub disposal', async () => { const sub = await modelHub.subscribe(); sub.onModelHubDisposed = onModelHubDisposed; fake.fakeModelHubDisposed(); await asyncsResolved(); sinon_1.default.assert.called(onModelHubDisposed); (0, chai_1.expect)(modelHub.isDisposed).to.be.true; }); describe('closes subscriptions', () => { const patch = [ { op: 'replace', path: '/name', value: MODEL1.name }, ]; let sub; beforeEach(async () => { sub = await modelHub.subscribe(MODEL1_ID); sub.onModelChanged = onModelChanged; fake.fakeModelChange(MODEL1_ID, patch); return asyncsResolved(); }); it('from the frontend', async () => { sub.close(); await asyncsResolved(); fake.fakeModelChange(MODEL1_ID, patch); await asyncsResolved(); (0, chai_1.expect)(onModelChanged.callCount).to.be.equal(1); }); it('from the backend', async () => { fake.fakeSubscriptionClosed(MODEL1_ID); await asyncsResolved(); fake.fakeModelChange(MODEL1_ID, patch); await asyncsResolved(); (0, chai_1.expect)(onModelChanged.callCount).to.be.equal(1); }); }); it("doesn't blow up on unassigned call-back", async () => { await modelHub.subscribe(MODEL1_ID); fake.fakeModelChange(MODEL1_ID, []); fake.fakeModelDirtyState(MODEL1_ID, true); fake.fakeModelValidated(MODEL1_ID, (0, model_validation_1.ok)()); await asyncsResolved(); }); }); describe('error cases', () => { it('initialization failure', async () => { const errorStub = sandbox.stub(console, 'error'); await provider('Boom!'); await asyncsResolved(); (0, chai_1.expect)(errorStub).to.have.been.calledWithMatch('Failed to initialize', 'Bomb context'); }); }); }); function asyncsResolved() { // It only takes until the next tick because in the test fixture // the promises involved are all a priori resolved return new Promise((resolve) => { setImmediate(() => { resolve(); }); }); } //# sourceMappingURL=frontend-model-hub.spec.js.map