UNPKG

office-addin-mock

Version:
237 lines (209 loc) 7.83 kB
import { OfficeApp } from "office-addin-manifest"; import { getHostType } from "./host"; import { isValidError, PossibleErrors } from "./possibleErrors"; import { ObjectData } from "./objectData"; /** * Creates an office-js mockable object * @param object Object structure to provide initial values for the mock object (Optional) * @param host Host tested by the object (Optional) */ export class OfficeMockObject { constructor(object?: ObjectData, host?: OfficeApp | undefined) { this._properties = new Map<string, OfficeMockObject>(); this._loaded = false; if (host) { this._host = host; } else { this._host = getHostType(object); } this.resetValue(undefined); if (object) { this.populate(object); } } /** * Mock replacement of the load method in the Office.js API * @param propertyArgument Argument of the load call. Will load any properties in the argument */ load(propertyArgument: string | string[] | ObjectData) { if (this._host === OfficeApp.Outlook) { return; } let properties: string[] = []; if (propertyArgument === undefined) { // an empty load call mean load all properties properties = ["*"]; } else if (typeof propertyArgument === "string") { properties = Array(propertyArgument); } else if (Array.isArray(propertyArgument)) { properties = propertyArgument; } else { properties = this.parseObjectPropertyIntoArray(propertyArgument); } properties.forEach((property: string) => { this.loadMultipleProperties(property); }); return this; } /** * Mock replacement for the sync method in the Office.js API */ async sync() { this._properties.forEach(async (property: OfficeMockObject, key: string) => { await property.sync(); this.updatePropertyCall(key); }); if (this._loaded) { this._value = this._valueBeforeLoaded; } } /** * addMock(name) will add a property named “name”, with a new OfficeMockObject as its value, to the object * @param objectName Object name of the object to be added */ private addMock(objectName: string) { if (this[objectName] !== undefined) { throw new Error("Mock object already exists"); } const officeMockObject = new OfficeMockObject(undefined, this._host); officeMockObject._isObject = true; this._properties.set(objectName, officeMockObject); this[objectName] = this._properties.get(objectName); } private loadAllProperties() { this._properties.forEach((property, propertyName: string) => { property.loadCalled(); this.updatePropertyCall(propertyName); }); } private loadCalled() { if (!this._loaded) { this._loaded = true; this._value = PossibleErrors.notSync; } } private loadMultipleProperties(properties: string) { if (properties === "*") { this.loadAllProperties(); } else { properties .replace(/\s/g, "") .split(",") .forEach((completeProperties: string) => { this.loadNavigational(completeProperties); }); } } private loadNavigational(completePropertyName: string) { const properties: Array<string> = completePropertyName.split("/"); let navigationalOfficeMockObject: OfficeMockObject = this; // Iterating through navigational properties for (let i = 0; i < properties.length - 1; i++) { const property = properties[i]; const retrievedProperty: OfficeMockObject | undefined = navigationalOfficeMockObject._properties.get(property); if (retrievedProperty) { navigationalOfficeMockObject = retrievedProperty; } else { throw new Error( `Navigational property ${property} needs to be present in object model before load is called.` ); } } const scalarProperty: string = properties[properties.length - 1]; navigationalOfficeMockObject.loadScalar(scalarProperty); } private loadScalar(scalarPropertyName: string) { if (this._properties.has(scalarPropertyName)) { this._properties.get(scalarPropertyName)?.loadCalled(); this.updatePropertyCall(scalarPropertyName); this._properties .get(scalarPropertyName) ?._properties.forEach((property: OfficeMockObject) => { property.loadCalled(); }); } else { throw new Error( `Property ${scalarPropertyName} needs to be present in object model before load is called.` ); } } private parseObjectPropertyIntoArray(objectData: ObjectData): string[] { let composedProperties: string[] = []; Object.keys(objectData).forEach((propertyName: string) => { const property: OfficeMockObject | undefined = this._properties.get(propertyName); if (property) { const propertyValue: ObjectData = objectData[propertyName]; if (property._isObject) { const composedProperty: string[] = property.parseObjectPropertyIntoArray(propertyValue); if (composedProperty.length !== 0) { composedProperty.forEach((prop: string) => { composedProperties = composedProperties.concat(propertyName + "/" + prop); }); } } else if (propertyValue) { composedProperties = composedProperties.concat(propertyName); } } else { throw new Error( `Property ${propertyName} needs to be present in object model before load is called.` ); } }); return composedProperties; } private populate(objectData: ObjectData) { Object.keys(objectData).forEach((propertyName: string) => { const property = objectData[propertyName]; const dataType: string = typeof property; if (dataType === "object" && !Array.isArray(property) && !(property instanceof Date)) { this.addMock(propertyName); this[propertyName].populate(property); } else { this.setValue(propertyName, property); } }); } private resetValue(value: unknown) { if (this._host === OfficeApp.Outlook) { this._value = value; } else { this._value = PossibleErrors.notLoaded; this._valueBeforeLoaded = value; this._loaded = false; } } /** * Sets a property of any type or function to the object * @param propertyName Property name to the property to be added * @param value Value this added property will have */ private setValue(propertyName: string, value: any) { if (typeof value === "function") { this[propertyName] = value; } else { if (!this._properties.has(propertyName)) { const officeMockObject = new OfficeMockObject(undefined, this._host); officeMockObject._isObject = false; this._properties.set(propertyName, officeMockObject); } this._properties.get(propertyName)?.resetValue(value); this[propertyName] = this._properties.get(propertyName)?._value; } } private updatePropertyCall(propertyName: string) { if (this._properties.get(propertyName)?._isObject) { this[propertyName] = this._properties.get(propertyName); } else if (isValidError(this[propertyName])) { // It is a known error this[propertyName] = this._properties.get(propertyName)?._value; } } private _properties: Map<string, OfficeMockObject>; private _loaded: boolean; private _value: unknown; private _valueBeforeLoaded: unknown; private _isObject: boolean | undefined; private _host: OfficeApp | undefined; [key: string]: any; }