diff --git a/package-lock.json b/package-lock.json
index 0bfde794..20ce1ae9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1145,6 +1145,11 @@
"strip-eof": "^1.0.0"
}
},
+ "fast-clone": {
+ "version": "1.5.13",
+ "resolved": "https://registry.npmjs.org/fast-clone/-/fast-clone-1.5.13.tgz",
+ "integrity": "sha512-0ez7coyFBQFjZtId+RJqJ+EQs61w9xARfqjqK0AD9vIUkSxWD4HvPt80+5evebZ1tTnv1GYKrPTipx7kOW5ipA=="
+ },
"fast-deep-equal": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
@@ -2264,9 +2269,9 @@
}
},
"minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
"dev": true
},
"minimist-options": {
@@ -2286,6 +2291,14 @@
"dev": true,
"requires": {
"minimist": "0.0.8"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ }
}
},
"mocha": {
@@ -3378,6 +3391,14 @@
"requires": {
"os-tmpdir": "^1.0.0",
"uuid": "^2.0.1"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
+ "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=",
+ "dev": true
+ }
}
},
"test-exclude": {
@@ -3683,12 +3704,6 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
},
- "uuid": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
- "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=",
- "dev": true
- },
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
diff --git a/package.json b/package.json
index 65d6cbd3..e787fbc4 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
"dependencies": {
"@types/geojson": "1.0.6",
"@types/json-patch": "0.0.30",
+ "fast-clone": "1.5.13",
"json-patch": "0.7.0",
"jsonschema": "1.2.4",
"ts-optchain": "0.1.3"
diff --git a/src/core/Translator.ts b/src/core/Translator.ts
index fb71df99..5583868d 100644
--- a/src/core/Translator.ts
+++ b/src/core/Translator.ts
@@ -12,30 +12,33 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see .
*/
-import {SCClasses, SCThingsField} from './Classes';
+import {SCClasses} from './Classes';
import {SCThing, SCThingType} from './Thing';
+import {isThing} from './types/Guards';
import {SCTranslations} from './types/i18n';
+import clone = require('fast-clone');
import {Defined, OCType} from 'ts-optchain';
/**
* SCThingTranslator class
*/
export class SCThingTranslator {
- /**
- * Property representing the translators base language.
- * This means every translation is given for this language.
- */
- private baseLanguage: keyof SCTranslations;
/**
* Property representing the translators target language
*/
- private language: keyof SCTranslations;
+ private _language: keyof SCTranslations;
/**
- * Property provinding a mapping from a SCThingType to its known own meta class.
+ * Property representing the translators base language
+ * This means every translation is given for this language
+ */
+ private cache: LRUCache;
+
+ /**
+ * Property providing a mapping from a SCThingType to its known own meta class
*/
private metaClasses: typeof SCClasses;
@@ -45,42 +48,44 @@ export class SCThingTranslator {
* // returns translator instance for german
* new SCThingTranslator('de');
*/
- constructor(language: keyof SCTranslations, baseLanguage?: keyof SCTranslations) {
- this.baseLanguage = baseLanguage ? baseLanguage : 'en';
- this.language = language;
+ constructor(language: keyof SCTranslations, cacheCapacity: number = 200) {
+ this.cache = new LRUCache(cacheCapacity);
+ this._language = language;
this.metaClasses = SCClasses;
}
+ /**
+ * Getter for language property
+ */
+ get language(): keyof SCTranslations {
+ return this._language;
+ }
+
+ /**
+ * Setter for language property. Also flushes translation cache
+ *
+ * @param language The language the translator instance will use from now on
+ */
+ set language(language: keyof SCTranslations) {
+ if (language !== this._language) {
+ this.cache.flush();
+ }
+ this._language = language;
+ }
+
/**
* Get field value translation recursively
*
- * @param firstObject Top level object that gets passed through the recursion
* @param data The intermediate object / primitive returned by the Proxys get() method
- * @param keyPath The keypath that (in the end) leads to the translatable property (when added to firstObject)
* @returns an OCType object allowing for access to translations or a translated value(s)
*/
- private deeptranslate(firstObject: K, data?: T, keyPath?: string): OCType {
+ private deeptranslate(data?: T): OCType {
const proxy = new Proxy(
((defaultValue?: Defined) => (data == null ? defaultValue : data)) as OCType,
{
get: (target, key) => {
const obj: any = target();
- const extendedKeyPath = [keyPath, key.toString()].filter((e) => e != null).join('.');
- let possiblePrimitive = obj[key];
- // check if obj[key] is an array that contains primitive type (arrays in SCThings are not mixing types)
- if (obj[key] instanceof Array && obj[key].length) {
- possiblePrimitive = obj[key][0];
- }
- if (typeof possiblePrimitive === 'string' ||
- typeof possiblePrimitive === 'number' ||
- typeof possiblePrimitive === 'boolean') {
- // returns final translation for primitive data types
- return this.deeptranslate(firstObject,
- this.getFieldValueTranslation(firstObject, extendedKeyPath),
- extendedKeyPath);
- }
- // recursion to get more calls to the Proxy handler 'get()' (key path not complete)
- return this.deeptranslate(firstObject, obj[key], extendedKeyPath);
+ return this.deeptranslate(obj[key]);
},
},
);
@@ -98,19 +103,19 @@ export class SCThingTranslator {
language: keyof SCTranslations): object | undefined {
const fieldTranslations = {};
const metaClass = this.getMetaClassInstance(thingType);
- if (metaClass === undefined) {
+ if (typeof metaClass === 'undefined') {
return undefined;
}
// Assigns every property in fieldTranslations to the known base language translation
- if (metaClass.fieldTranslations[this.baseLanguage] !== undefined) {
- Object.keys(metaClass.fieldTranslations[this.baseLanguage]).forEach((key) => {
- (fieldTranslations as any)[key] = metaClass.fieldTranslations[this.baseLanguage][key];
+ if (typeof metaClass.fieldTranslations.en !== 'undefined') {
+ Object.keys(metaClass.fieldTranslations.en).forEach((key) => {
+ (fieldTranslations as any)[key] = metaClass.fieldTranslations.en[key];
});
}
// Assigns every property in fieldTranslations to the known translation in given language
- if (metaClass.fieldTranslations[language] !== undefined) {
+ if (typeof metaClass.fieldTranslations[language] !== 'undefined') {
Object.keys(metaClass.fieldTranslations[language]).forEach((key) => {
(fieldTranslations as any)[key] = metaClass.fieldTranslations[language][key];
});
@@ -132,120 +137,34 @@ export class SCThingTranslator {
}
/**
- * Returns property value at a certain (key) path of an object.
- * @example
- * // returns value of dish.offers[0].inPlace.categories[1]
- * const dish: SCDish = {...};
- * this.valueFromPath(dish, 'offers[0].inPlace.categories[1]');
- * @param path Key path to evaluate
- * @param obj Object to evaluate the key path upon
- * @param separator Key path seperation element. Defaults to '.'
- * @returns Property value at at key path
+ * Applies known field value translations of the given SCThings meta class to an instance
+ * Translated values overwrite current values inplace (destructive)
+ *
+ * @param language The language the thing is translated to
+ * @param thing The thing that will be translated
+ * @returns The thing with translated meta field values
*/
- private valueFromPath(path: string, obj: T, separator = '.') {
- path = path.replace(/\[/g, '.');
- path = path.replace(/\]/g, '.');
- path = path.replace(/\.\./g, '.');
- path = path.replace(/\.$/, '');
- const properties = path.split(separator);
- return properties.reduce((prev: any, curr: any) => prev && prev[curr], obj);
- }
-
- /**
- * Get field value translation
- * @example
- * // returns translation of the property (if available) in the language defined when creating the translator object
- * const dish: SCDish = {...};
- * translator.translate(dish, 'offers[0].inPlace.categories[1]');
- * @param thing SCThing to get value translation for
- * @param field Field to get value translation for (keypath allowed)
- * @returns Translated value(s) or value(s) itself
- */
- public getFieldValueTranslation(thing: T,
- field: SCThingsField): string | string[] {
- let translationPath = 'translations.' + this.language + '.' + field;
- const regexTrimProperties = /.*(?:(\..*)(\[\d+\])|(\.[^\d]*$)|(\..*)(\.[\d]*$))/;
-
- const pathMatch = field.match(regexTrimProperties);
-
- // when translation is given in thing
- let translation = this.valueFromPath(translationPath, thing);
- if (translation) {
- return translation;
- } else if (pathMatch && pathMatch[1] && pathMatch[2] || pathMatch && pathMatch[4] && pathMatch[5]) {
- // accessing iteratable of nested thing
- const keyPath = (pathMatch[1] ? pathMatch[1] : pathMatch[4]) + (pathMatch[2] ? pathMatch[2] : pathMatch[5]);
- const redactedField = field.replace(keyPath, '');
-
- // when translation is given in nested thing
- translationPath = `${redactedField}.translations.${this.language}${keyPath}`;
- translation = this.valueFromPath(translationPath, thing);
- if (translation) {
- return translation;
- }
-
- // when translation is given in nested meta thing via iterateable index
- const nestedType = this.valueFromPath(field.replace(keyPath, '.type'), thing) as SCThingType;
- translationPath = `fieldValueTranslations.${this.language}${keyPath}`;
- translation = this.valueFromPath(translationPath.replace(
- /\[(?=[^\[]*$).*|(?=[\d+]*$).*/, '[' + this.valueFromPath(field, thing) + ']'),
- this.getMetaClassInstance(nestedType));
- if (translation) {
- return translation;
- }
-
- } else if (pathMatch && pathMatch[3]) {
- // accessing meta or instance of nested thing primitive value depth > 0
- const keyPath = pathMatch[3];
- const redactedField = field.replace(pathMatch[3], '');
-
- // when translation is given in nested thing
- translationPath = `${redactedField}.translations.${this.language}${keyPath}`;
- if (this.valueFromPath(translationPath, thing)) {
- return this.valueFromPath(translationPath, thing);
- }
-
- // when translation is given in nested meta thing
- const nestedType = this.valueFromPath(field.replace(keyPath, '.type'), thing) as SCThingType;
- translationPath = `fieldValueTranslations.${this.language}${keyPath}`;
- translation = this.valueFromPath(translationPath, this.getMetaClassInstance(nestedType));
- if (translation instanceof Object) { // lookup translated keys in meta thing property
- const translations: string[] = [];
- this.valueFromPath(field, thing).forEach((key: string) => {
- translationPath = `fieldValueTranslations.${this.language}${keyPath}.${key}`;
- translations.push(this.valueFromPath(translationPath, this.getMetaClassInstance(nestedType)));
- });
- return translations;
- }
- if (!translation) { // translation not given, return as is
- return this.valueFromPath(field, thing) as string;
- }
- return translation;
+ private replaceAvailableMetaFieldValueTranslations(instance: any,
+ language: keyof SCTranslations): any {
+ const metaClass = this.getMetaClassInstance(instance.type);
+ if (typeof metaClass === 'undefined') {
+ return instance;
}
- // accessing meta thing primitive value depth = 0
- translationPath = `fieldValueTranslations.${this.language}.${field}`;
- translation = this.valueFromPath(translationPath, this.getMetaClassInstance(thing.type));
- if (translation) {
- if (translation instanceof Object) { // lookup translated keys in meta thing property
- const translations: string[] = [];
- this.valueFromPath(field, thing).forEach((key: string) => {
- translationPath = `fieldValueTranslations.${this.language}.${field}.${key}`;
- translations.push(this.valueFromPath(translationPath, this.getMetaClassInstance(thing.type)));
- });
- return translations;
- }
- return translation;
+ if (typeof metaClass.fieldValueTranslations[language] !== 'undefined') {
+ Object.keys(metaClass.fieldValueTranslations[language]).forEach((key) => {
+ if (metaClass.fieldValueTranslations[language][key] instanceof Object) {
+ // Assigns known translations of subproperties to property in given language (e.g. categories)
+ Object.keys((instance as any)[key]).forEach((subKey) => {
+ (instance as any)[key][subKey] =
+ metaClass.fieldValueTranslations[language][key][(instance as any)[key][subKey]];
+ });
+ } else {
+ // Assigns property to known translation of fieldValueTranslations in given language
+ (instance as any)[key] = metaClass.fieldValueTranslations[language][key];
+ }
+ });
}
-
- // accessing meta thing primitive via iteratable index value depth = 0
- translation = this.valueFromPath(translationPath.replace(
- /\[(?=[^\[]*$).*|(?=[\d+]*$).*/, '[' + this.valueFromPath(field, thing) + ']'),
- this.getMetaClassInstance(thing.type));
- if (translation) {
- return translation;
- }
- // last resort: return as is
- return this.valueFromPath(field, thing) as string;
+ return instance;
}
/**
@@ -261,33 +180,27 @@ export class SCThingTranslator {
* @param data Top level object that gets passed through the recursion
* @returns an OCType object allowing for access to translations or a translated value(s)
*/
- public translate(data?: T): OCType {
+ public translate(data: T): OCType {
return new Proxy(
((defaultValue?: Defined) => (data == null ? defaultValue : data)) as OCType,
{
get: (target, key) => {
const obj: any = target();
- let translatable = obj[key];
- if (obj[key] instanceof Array && obj[key].length) {
- translatable = obj[key][0];
- if (typeof obj[key][0] === 'object' && !obj[key][0].origin) {
- translatable = obj[key][0][Object.keys(obj[key][0])[0]];
- }
+ const objTranslatedFromCache = this.cache.get(data);
+ if (typeof objTranslatedFromCache !== 'undefined') {
+ return this.deeptranslate((objTranslatedFromCache as any)[key]);
}
- if (typeof translatable === 'string') {
- // retrieve final translation
- return this.deeptranslate(data!, this.getFieldValueTranslation(data!, key.toString()), key.toString());
- }
- // recursion to get more calls to the Proxy handler 'get()' (key path not complete)
- return this.deeptranslate(data!, obj[key], key.toString());
+ const objTranslated = this.translateWholeThingDestructively(clone(obj));
+ this.cache.putObject(objTranslated);
+ return this.deeptranslate(objTranslated[key]);
},
},
);
}
/**
- * Given a SCThingType this function returns an object with the same basic structure as the corresponding SCThing.
- * All the values will be set to the known translations of the property/key name.
+ * Given a SCThingType this function returns an object with the same basic structure as the corresponding SCThing
+ * All the values will be set to the known translations of the property/key name
* @example
* const translatedMetaDish = translator.translatedPropertyNames(SCThingType.CourseOfStudies);
* @param language The language the object is translated to
@@ -300,4 +213,129 @@ export class SCThingTranslator {
// return {...{}, ...this.getAllMetaFieldTranslations(thing.type, targetLanguage) as T};
return this.getAllMetaFieldTranslations(thing.type, targetLanguage) as T;
}
+
+ /**
+ * Recursively translates the given object in-place
+ * Translated values overwrite current values (destructive)
+ *
+ * @param language The language the thing is translated to
+ * @param thing The thing that will be translated
+ * @returns The thing translated
+ */
+ public translateWholeThingDestructively(instance: any,
+ language?: keyof SCTranslations): any {
+ const targetLanguage = (language) ? language : this.language;
+ // Recursively call this function on all nested SCThings, arrays and objects
+ Object.keys(instance).forEach((key) => {
+ if (
+ isThing((instance as any)[key]) ||
+ instance[key] instanceof Array ||
+ instance[key] instanceof Object) {
+ instance[key] = this.translateWholeThingDestructively(instance[key], targetLanguage);
+ }
+ });
+
+ // Spread variable translations given by the connector into thing
+ if (typeof instance.translations !== 'undefined') {
+ if (typeof instance.translations![targetLanguage] !== 'undefined') {
+ instance = {...instance, ...instance.translations![targetLanguage]} as typeof instance;
+ }
+ }
+ // Spread known translations from meta classes into (partly) translated thing
+ this.replaceAvailableMetaFieldValueTranslations(instance, targetLanguage);
+ return instance;
+ }
+
+}
+
+/**
+ * LRUCache class
+ * Small last recently used cache intended to get used by SCThingTranslator
+ */
+class LRUCache {
+ /**
+ * Map property that manages cached content
+ */
+ private entries: Map = new Map();
+
+ /**
+ * Property representing cache maximum capacity
+ */
+ private maxEntries: number;
+
+ /**
+ * @constructor
+ * @example
+ * // returns LRUCache instance with a maximum capacity of 500
+ * new LRUCache(500);
+ */
+ constructor(maxEntries: number) {
+ this.maxEntries = maxEntries;
+ }
+
+ /**
+ * Flushes cache / removes all entries
+ */
+ public flush() {
+ this.entries.clear();
+ }
+
+ /**
+ * Get content from cache by key
+ */
+ public get(somethingOrKey: string): T | undefined;
+
+ /**
+ * Get content from cache by another objects uid
+ */
+ public get(something: U): T | undefined;
+
+ /**
+ * Get content from cache by key or by another objects uid
+ *
+ * @param somethingOrKey The key which maps to the cached content or an object for which content has been cached
+ * @returns If available the content connected to the key or somethingOrKey.uid property
+ */
+ public get(somethingOrKey: string | U): T | undefined {
+ let key: string;
+ if (typeof somethingOrKey === 'string') {
+ key = somethingOrKey;
+ } else if (isThing(somethingOrKey)) {
+ key = somethingOrKey.uid;
+ } else {
+ throw new Error(`Passed argument ${somethingOrKey} cannot be key in LRUCache`);
+ }
+
+ const entry = this.entries.get(key);
+ if (entry) {
+ // LRU behavior
+ this.entries.delete(key);
+ this.entries.set(key, entry);
+ }
+ return entry;
+ }
+
+ /**
+ * Place content in cache by key
+ *
+ * @param key The key for which content should be cached
+ * @param content The content that should be cached
+ */
+ public put(key: string, content: T) {
+ if (this.entries.size >= this.maxEntries) {
+ // LRU behavior
+ const keyToDelete = this.entries.keys().next().value;
+ this.entries.delete(keyToDelete);
+ }
+ this.entries.set(key, content);
+ }
+
+ /**
+ * Place content in cache by another objects uid
+ *
+ * @param something The object that should be cached under something.uid
+ */
+ public putObject(something: U) {
+ this.put(something.uid, (something as any) as T);
+ }
}
diff --git a/test/Translator.spec.ts b/test/Translator.spec.ts
index 0f154483..00b7b8cf 100644
--- a/test/Translator.spec.ts
+++ b/test/Translator.spec.ts
@@ -13,11 +13,12 @@
* this program. If not, see .
*/
import {expect} from 'chai';
+import clone = require('fast-clone');
import {slow, suite, test, timeout} from 'mocha-typescript';
+
import {SCThingOriginType, SCThingType} from '../src/core/Thing';
import {SCBuildingWithoutReferences} from '../src/core/things/Building';
import {SCDish, SCDishMeta} from '../src/core/things/Dish';
-import {SCPerson} from '../src/core/things/Person';
import {SCThingTranslator} from '../src/core/Translator';
const building: SCBuildingWithoutReferences = {
@@ -96,29 +97,15 @@ const dish: SCDish = {
uid: '540862f3-ea30-5b8f-8678-56b4dc217140',
};
-const person: SCPerson = {
- familyName: 'base-familyName-name',
- givenName: 'base-givenName-name',
- homeLocations: [building, building, building],
- name : 'base-person-name',
- origin: {
- indexed: '1970',
- name: 'ding',
- type: SCThingOriginType.Remote,
- },
- type: SCThingType.Person,
- uid: '1234',
-};
-
-const translator = new SCThingTranslator('de', 'en');
+const translator = new SCThingTranslator('de');
// tslint:disable-next-line:no-eval
const languageNonExistant = eval("'jp'");
// this will simulate a translator always utilizing the base language translations
const translatorWithFallback = new SCThingTranslator(languageNonExistant);
-// tslint:disable:member-ordering TranslationSpec
+// tslint:disable:member-ordering TranslationSpecInplace
@suite(timeout(10000), slow(5000))
-export class TranslationSpec {
+export class TranslationSpecInplace {
@test
public directStringLiteralType() {
@@ -233,135 +220,6 @@ export class TranslationSpec {
}
}
-// tslint:disable:member-ordering TranslationSpecByString
-@suite(timeout(10000), slow(5000))
-export class TranslationSpecByString {
-
- @test
- public directStringLiteralType() {
- expect(translator.getFieldValueTranslation(dish, 'type')).to.equal('Essen');
- }
-
- @test
- public directStringProperty() {
- expect(translator.getFieldValueTranslation(dish, 'name')).to.equal('de-dish-name');
- }
-
- @test
- public directArrayOfString() {
- expect(translator.getFieldValueTranslation(dish, 'characteristics')).to.deep
- .equal([{name: 'de-characteristic0'}, {name: 'de-characteristic1'}]);
- }
-
- @test
- public directArrayOfStringSubscript() {
- expect(translator.getFieldValueTranslation(dish, 'characteristics[1]'))
- .to.deep.equal({name: 'de-characteristic1'});
- }
-
- @test
- public directMetaArrayOfString() {
- expect(translator.getFieldValueTranslation(dish, 'categories')).to.deep.equal(['Hauptgericht', 'Nachtisch']);
- }
-
- @test
- public directMetaArrayOfStringSubscript() {
- expect(translator.getFieldValueTranslation(dish, 'categories[1]')).to.equal('Nachtisch');
- }
-
- @test
- public nestedStringLiteralType() {
- expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.type')).to.equal('Gebäude');
- }
-
- @test
- public nestedStringProperty() {
- expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.name')).to.equal('de-space-name');
- }
-
- @test
- public nestedMetaArrayOfString() {
- expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.categories'))
- .to.deep.equal(['Büro', 'Bildung']);
- }
-
- @test
- public nestedMetaArrayOfStringSubscript() {
- expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.categories[1]')).to.equal('Bildung');
- }
-
- @test
- public nestedArrayOfStringSubscript() {
- expect(translator.getFieldValueTranslation(dish, 'offers[0].inPlace.floors[1]')).to.equal('de-floor1');
- }
-
- @test
- public directStringLiteralTypeFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(dish, 'type')).to.equal('dish');
- }
-
- @test
- public directStringPropertyFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(dish, 'name')).to.equal('base-dish-name');
- }
-
- @test
- public directArrayOfStringSubscriptFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(dish, 'characteristics[1]'))
- .to.deep.equal({name: 'base-characteristic1'});
- }
-
- @test
- public directMetaArrayOfStringFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(dish, 'categories'))
- .to.deep.equal(['main dish', 'dessert']);
- }
-
- @test
- public directMetaArrayOfStringSubscriptFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(dish, 'categories[1]')).to.equal('dessert');
- }
-
- @test
- public nestedStringLiteralTypeFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.type')).to.equal('building');
- }
-
- @test
- public nestedStringPropertyFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.name')).to.equal('base-space-name');
- }
-
- @test
- public nestedMetaArrayOfStringFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.categories'))
- .to.deep.equal(['office', 'education']);
- }
-
- @test
- public nestedMetaArrayOfStringSubscriptFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.categories[1]'))
- .to.equal('education');
- }
-
- @test
- public nestedArrayOfStringSubscriptFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.floors[1]'))
- .to.equal('base-floor1');
- }
-
- @test
- public nestedArrayOfStringSubscriptUncommonFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(dish, 'offers[0].inPlace.floors.1')).to.equal('base-floor1');
- }
-
- @test
- public nestedNestedMetaArrayOfStringSubscriptUncommonFallback() {
- expect(translatorWithFallback.getFieldValueTranslation(person, 'homeLocations.1.categories.1'))
- .to.equal('education');
- }
-}
-
// tslint:disable:member-ordering no-eval no-unused-expression TranslationSpec
@suite(timeout(10000), slow(5000))
export class MetaTranslationSpec {
@@ -376,7 +234,7 @@ export class MetaTranslationSpec {
@test
public thingWithoutMetaClass() {
- const dishCopy = Object.assign({}, dish);
+ const dishCopy = clone(dish);
const typeNonExistant = eval("(x) => x + 'typeNonExistant';");
// this will assign a non existant SCThingType to dishCopy
dishCopy.type = typeNonExistant();