UNPKG

@itwin/core-backend

Version:
349 lines • 17.6 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { expect } from "chai"; import { assert, OpenMode } from "@itwin/core-bentley"; import { StandaloneDb } from "../../IModelDb"; import { IModelHost } from "../../IModelHost"; import { SettingsPriority } from "../../workspace/Settings"; import { IModelTestUtils } from "../IModelTestUtils"; import { GeoCoordConfig } from "../../GeoCoordConfig"; describe("Settings", () => { let iModel; before(() => { IModelHost.settingsSchemas.addFile(IModelTestUtils.resolveAssetFile("TestSettings.schema.json")); const seedFileName = IModelTestUtils.resolveAssetFile("CompatibilityTestSeed.bim"); const testFileName = IModelTestUtils.prepareOutputFile("SettingsTest", "SettingsTest.bim"); iModel = IModelTestUtils.createSnapshotFromSeed(testFileName, seedFileName); }); after(() => { iModel.close(); }); const app1 = { description: "", schemaPrefix: "app1", settingDefs: { sub1: { type: "string", enum: ["va1", "alt1"], enumDescriptions: [ "descr1", "descr2", ], default: "val1", description: "the first value", }, sub2: { type: "array", description: "an array", items: { type: "string", }, }, boolVal: { type: "boolean", default: true, description: "boolean defaults to true", }, strVal: { type: "string", default: "default string val", }, intVal: { type: "integer", default: 22, }, obj: { type: "object", properties: { out: { type: "object", properties: { o1: { type: "object", required: ["m1", "m2"], properties: { m1: { type: "string", }, m2: { type: "number", }, }, }, }, }, }, }, databases: { type: "array", items: { type: "object", required: ["name", "dbName", "containerName"], properties: { name: { type: "string", }, dbName: { type: "string", }, containerName: { type: "string", }, b2: { type: "object", properties: { o1: { type: "object", required: ["m1"], properties: { m1: { type: "string", }, m2: { type: "number", }, }, }, }, }, }, }, }, }, }; const app1Settings = { "app1/sub1": "app1 value", "app1/sub2": { arr: ["app1", "app2"], }, "app1/setting2": 1002, "app1/setting3": "app setting 3 val", "app1/setting5": "app setting 5 val", }; const imodel1Settings = { "app1/sub1": "imodel1 value", "app1/sub2": { arr: ["a1", "a2"], }, "app1/setting2": 2, "app1/setting3": "setting 3 val", }; const imodel2Settings = { "app1/sub1": "imodel2 value", "app1/sub2": { arr: ["a21", "a22"], }, }; const iTwinSettings = { "app1/sub1": "val3", "app1/sub2": { arr: ["a31", "a32", "a33"], }, "app2/setting6": "val 6", "app3/obj": { member1: "test2", member2: "test3", member3: { part1: "p1", part2: [ { m1: 0 }, { m1: 2 }, ], }, }, }; it("settings priorities", () => { const settings = iModel.workspace.settings; IModelHost.settingsSchemas.addGroup(app1); IModelHost.appWorkspace.settings.addDictionary({ name: "app1", priority: SettingsPriority.application }, app1Settings); let settingsChanged = 0; settings.onSettingsChanged.addListener(() => settingsChanged++); settings.addDictionary({ name: "iModel1.setting.json", priority: SettingsPriority.iModel }, imodel1Settings); expect(settingsChanged).eq(1); settings.addDictionary({ name: "iModel2.setting.json", priority: SettingsPriority.iModel }, imodel2Settings); expect(settingsChanged).eq(2); settings.addDictionary({ name: "iTwin.setting.json", priority: SettingsPriority.iTwin }, iTwinSettings); expect(settingsChanged).eq(3); expect(() => IModelHost.appWorkspace.settings.addDictionary({ name: "iModel", priority: SettingsPriority.iModel }, imodel1Settings)).to.throw("Use IModelSettings"); expect(settings.getString("app1/sub1")).equals(imodel2Settings["app1/sub1"]); expect(settings.getString("app2/setting6")).equals(iTwinSettings["app2/setting6"]); expect(settings.getString("app1/setting5")).equals(app1Settings["app1/setting5"]); // comes from app settings expect(settings.getSetting("app1/sub2").arr).deep.equals(imodel2Settings["app1/sub2"].arr); const app3obj = settings.getSetting("app3/obj"); expect(app3obj).deep.equals(iTwinSettings["app3/obj"]); app3obj.member3.part2[0].m1 = "bad"; // should modify a copy expect(iTwinSettings["app3/obj"].member3.part2[0].m1).equal(0); expect(settings.getObject("app3/obj")).deep.equals(iTwinSettings["app3/obj"]); // should be original value expect(settings.getBoolean("app1/boolVal")).equals(true); expect(settings.getBoolean("app1/not there", true)).equals(true); expect(settings.getBoolean("app1/not there", false)).equals(false); expect(settings.getString("app1/strVal")).equals(app1.settingDefs.strVal.default); expect(settings.getNumber("app1/intVal")).equals(22); expect(() => settings.getObject("app1/intVal")).throws("app1/intVal"); expect(() => settings.getArray("app1/intVal")).throws("app1/intVal"); expect(() => settings.getString("app1/intVal", "oops")).throws("app1/intVal"); expect(() => settings.getBoolean("app1/intVal", true)).throws("app1/intVal"); expect(settings.getNumber("app1/not there", 33)).equals(33); expect(settings.getSetting("app1/not there")).is.undefined; expect(settings.getString("app2/not there", "fallback")).equals("fallback"); // list of default Gcs databases is in the backend.setting.json5 file loaded on startup const defaultGcs = settings.getArray(GeoCoordConfig.settingName.defaultDatabases); assert(undefined !== defaultGcs); expect(defaultGcs.length).equals(2); expect(defaultGcs[0].baseUri).equal("https://geocoord-workspace.itwinjs.org"); expect(defaultGcs[1].baseUri).equal(defaultGcs[1].baseUri); expect(defaultGcs[0].dbName).equal("base"); expect(defaultGcs[1].dbName).equal("allEarth"); expect(defaultGcs[0].priority).equals(10000); expect(defaultGcs[1].priority).equals(100); expect(defaultGcs[0].prefetch).true; expect(defaultGcs[1].prefetch).undefined; expect(defaultGcs[1].storageType).equals("azure"); iTwinSettings["app2/setting6"] = "new value for 6"; settings.addDictionary({ name: "iTwin.setting.json", priority: SettingsPriority.iTwin }, iTwinSettings); expect(settings.getString("app2/setting6")).equals(iTwinSettings["app2/setting6"]); expect(settingsChanged).eq(4); app1.settingDefs.strVal.default = "new default"; IModelHost.settingsSchemas.addGroup(app1); // after re-registering, the new default should be updated expect(settings.getString("app1/strVal")).equals(app1.settingDefs.strVal.default); expect(settings.dictionaries.length).eq(3); const inspect = Array.from(settings.getSettingEntries("app1/sub1")); expect(inspect.length).equals(5); expect(inspect[0].dictionary.props).to.deep.equal({ name: "iModel2.setting.json", priority: SettingsPriority.iModel }); expect(inspect[0].value).equal("imodel2 value"); expect(inspect[1].dictionary.props).to.deep.equal({ name: "iModel1.setting.json", priority: SettingsPriority.iModel }); expect(inspect[1].value).equal("imodel1 value"); expect(inspect[2].dictionary.props).to.deep.equal({ name: "iTwin.setting.json", priority: SettingsPriority.iTwin }); expect(inspect[2].value).equal("val3"); expect(inspect[3].dictionary.props).to.deep.equal({ name: "app1", priority: SettingsPriority.application }); expect(inspect[3].value).equal("app1 value"); expect(inspect[4].dictionary.props).to.deep.equal({ name: "_default_", priority: 0 }); expect(inspect[4].value).equal("val1"); settings.dropDictionary({ name: "iTwin.setting.json" }); expect(settingsChanged).eq(5); expect(settings.getString("app2/setting6")).is.undefined; // test validation of values vs. setting schemas const workspace = { dbName: "abc", containerId: "123", baseUri: "aab.com" }; const fontListVal = [{ workspace, fontName: "arial" }, { workspace, fontName: "helvetica", fontType: 3 }]; expect(() => IModelHost.settingsSchemas.validateSetting(fontListVal, "testApp/fontList")).throws("required value for \"workspaceLimit\" is missing"); workspace.workspaceLimit = 4; // add missing value expect(() => IModelHost.settingsSchemas.validateSetting(fontListVal, "testApp/fontList")).throws("value for testApp/fontList[1].fontType"); fontListVal[1].fontType = "ttf"; // correct font type to string expect(IModelHost.settingsSchemas.validateSetting(fontListVal, "testApp/fontList")).equal(fontListVal); // should now pass }); it("read settings file", () => { const appSettings = IModelHost.appWorkspace.settings; const iModelSettings = iModel.workspace.settings; const settingFileName = IModelTestUtils.resolveAssetFile("test.setting.json5"); expect(() => appSettings.addFile(settingFileName, SettingsPriority.iTwin)).throws("Use IModelSettings"); appSettings.addFile(settingFileName, SettingsPriority.application); expect(() => iModelSettings.addFile(settingFileName, SettingsPriority.application)).to.throw("Use IModelHost.appSettings"); expect(appSettings.getString("app1/colorTheme")).equals("Light Theme"); expect(iModelSettings.getString("app1/colorTheme")).equals("Light Theme"); const token = appSettings.getSetting("editor/tokenColorCustomizations"); expect(token["Visual Studio Light"].textMateRules[0].settings.foreground).equals("#d16c6c"); expect(token["Default High Contrast"].comments).equals("#FF0000"); expect(appSettings.getArray("editor/enableFiletypes").length).equals(17); appSettings.dropDictionary({ name: settingFileName }); }); it("IModel persistent settings ", () => { const iModelName = IModelTestUtils.prepareOutputFile("IModelSetting", "test.bim"); const iModel2 = IModelTestUtils.createSnapshotFromSeed(iModelName, IModelTestUtils.resolveAssetFile("test.bim")); const setting1 = { "imodel/setting1": "this is from setting1", }; const setting1changed = { "imodel/setting1": "this is changed setting1", }; const gcsDbDict = { "gcs/databases": ["gcs/Usa", "gcs/Canada"], }; iModel2.saveSettingDictionary("gcs-dbs", gcsDbDict); iModel2.saveSettingDictionary("test1", setting1); iModel2.close(); let iModel3 = StandaloneDb.openFile(iModelName, OpenMode.ReadWrite); expect(iModel3.workspace.settings.getObject("gcs/databases")).to.deep.equal(gcsDbDict["gcs/databases"]); expect(iModel3.workspace.settings.getString("imodel/setting1")).equal(setting1["imodel/setting1"]); iModel3.saveSettingDictionary("test1", setting1changed); iModel3.close(); iModel3 = StandaloneDb.openFile(iModelName); expect(iModel3.workspace.settings.getObject("gcs/databases")).to.deep.equal(gcsDbDict["gcs/databases"]); expect(iModel3.workspace.settings.getString("imodel/setting1")).equal(setting1changed["imodel/setting1"]); iModel3.deleteSettingDictionary("test1"); iModel3.close(); iModel3 = StandaloneDb.openFile(iModelName); expect(iModel3.workspace.settings.getObject("gcs/databases")).to.deep.equal(gcsDbDict["gcs/databases"]); expect(iModel3.workspace.settings.getString("imodel/setting1")).to.be.undefined; iModel3.close(); }); describe("combineArray", () => { function addGroup(schemaPrefix, type, combineArray) { const group = { description: "", schemaPrefix, settingDefs: { array: { type: "array", items: { type }, combineArray, }, }, }; IModelHost.settingsSchemas.addGroup(group); } function addArray(schemaPrefix, name, value, priority) { const settings = {}; settings[`${schemaPrefix}/array`] = value; IModelHost.appWorkspace.settings.addDictionary({ name, priority, }, settings); } it("combines arrays only if the combineArray flag is explicitly set to true", () => { addGroup("false", "number", false); addGroup("true", "number", true); addGroup("default", "number", undefined); let dictNum = 0; const prefixes = ["false", "true", "default"]; for (const schemaPrefix of prefixes) { addArray(schemaPrefix, `app${dictNum++}`, [1, 2], SettingsPriority.application); addArray(schemaPrefix, `def${dictNum++}`, [3, 4], SettingsPriority.defaults); } for (const prefix of prefixes) { const settingName = `${prefix}/array`; expect(IModelHost.appWorkspace.settings.getSetting(settingName)).to.deep.equal([1, 2]); const expected = ("true" === prefix ? [1, 2, 3, 4] : [1, 2]); expect(IModelHost.appWorkspace.settings.getArray(settingName)).to.deep.equal(expected); } }); it("orders elements by priority", () => { addGroup("combine", "number", true); function addDictionary(priority) { addArray("combine", priority.toString(), [priority], priority); } addDictionary(100); addDictionary(120); addDictionary(80); addDictionary(150); addDictionary(30); expect(IModelHost.appWorkspace.settings.getArray("combine/array")).to.deep.equal([150, 120, 100, 80, 30]); }); it("ignores duplicates with lower priority", () => { addGroup("numbers", "number", true); addArray("numbers", "a", [4, 8], 101); addArray("numbers", "c", [3, 6, 9, 12], 99); addArray("numbers", "b", [2, 4, 6, 8, 10, 12], 100); expect(IModelHost.appWorkspace.settings.getArray("numbers/array")).to.deep.equal([4, 8, 2, 6, 10, 12, 3, 9]); ; addGroup("points", "object", true); addArray("points", "a", [{ x: 1, y: 1 }, { x: 1, y: 2 }], 101); addArray("points", "b", [{ x: 2, y: 1 }, { x: 1, y: 2 }], 100); addArray("points", "c", [{ y: 1, x: 1 }, { y: 2, x: 1 }, { x: 3, y: 3 }], 99); expect(IModelHost.appWorkspace.settings.getArray("points/array")).to.deep.equal([{ x: 1, y: 1 }, { x: 1, y: 2 }, { x: 2, y: 1 }, { x: 3, y: 3 }]); }); }); }); //# sourceMappingURL=Settings.test.js.map