/* * Copyright (C) 2019 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ import {expect} from 'chai'; import clone = require('fast-clone'); import {slow, suite, test, timeout} from '@testdeck/mocha'; import {SCThingOriginType, SCThingType, SCThingRemoteOrigin} from '../src/things/abstract/thing'; import {SCBuildingWithoutReferences} from '../src/things/building'; import {SCDish, SCDishMeta} from '../src/things/dish'; import {SCSetting, SCSettingInputType} from '../src/things/setting'; import {SCThingTranslator} from '../src/translator'; const building: SCBuildingWithoutReferences = { address: { addressCountry: 'base-address.addressCountry', addressLocality: 'base-address.addressLocality', postalCode: 'base-address.postalCode', streetAddress: 'base-address.streetAddress', }, categories: ['office', 'education'], floors: ['base-floor0', 'base-floor1'], geo: { point: { coordinates: [12.0, 13.0], type: 'Point', }, }, name: 'base-space-name', translations: { de: { address: { addressCountry: 'de-address.addressCountry', addressLocality: 'de-address.addressLocality', postalCode: 'de-address.postalCode', streetAddress: 'de-address.streetAddress', }, floors: ['de-floor0', 'de-floor1'], name: 'de-space-name', }, }, type: SCThingType.Building, uid: '540862f3-ea30-5b8f-8678-56b4dc217140', }; const dish: SCDish = { categories: ['main dish', 'dessert'], characteristics: [{name: 'base-characteristic0'}, {name: 'base-characteristic1'}], name: 'base-dish-name', offers: [ { availability: 'in stock', inPlace: building, prices: { default: 23.42, }, provider: { name: 'base-provider', type: SCThingType.Organization, uid: '540862f3-ea30-5b8f-8678-56b4dc217141', }, }, ], origin: { indexed: '1970-01-01T00:00:00.000Z', name: 'dish-connector', type: SCThingOriginType.Remote, }, translations: { de: { characteristics: [{name: 'de-characteristic0'}, {name: 'de-characteristic1'}], name: 'de-dish-name', }, }, type: SCThingType.Dish, uid: '540862f3-ea30-5b8f-8678-56b4dc217140', }; const setting: SCSetting = { categories: ['profile'], defaultValue: 'student', description: 'base-description', inputType: SCSettingInputType.SingleChoice, name: 'group', order: 1, origin: { indexed: '2018-11-11T14:30:00Z', name: 'Dummy', type: SCThingOriginType.Remote, }, type: SCThingType.Setting, uid: '2c97aa36-4aa2-43de-bc5d-a2b2cb3a530e', values: ['student', 'employee', true, 42], }; const translator = new SCThingTranslator('de'); const translatorEN = new SCThingTranslator('en'); // this will simulate a translator always utilizing the base language translations const translatorWithFallback = new SCThingTranslator('tt'); const translatedThingDE = translator.translate(dish); const translatedThingFallback = translatorWithFallback.translate(dish); // tslint:disable:max-line-length member-ordering newline-per-chained-call prefer-function-over-method completed-docs TranslationSpecInplace @suite(timeout(10000), slow(5000)) export class TranslationSpecInplace { @test public directEnumSingleValue() { expect(translator.translatedAccess(setting).inputType()).to.equal('einfache Auswahl'); } @test public directStringLiteralType() { expect(translator.translatedAccess(dish).type()).to.equal('Essen'); expect(translatedThingDE.type).to.equal('Essen'); } @test public directStringProperty() { expect(translator.translatedAccess(dish).name()).to.equal('de-dish-name'); expect(translatedThingDE.name).to.equal('de-dish-name'); } @test public directArrayOfString() { expect(translator.translatedAccess(dish).characteristics()).to.deep .equal([{name: 'de-characteristic0'}, {name: 'de-characteristic1'}]); expect(translatedThingDE.characteristics).to.deep .equal([{name: 'de-characteristic0'}, {name: 'de-characteristic1'}]); } @test public directArrayOfStringSubscript() { expect(translator.translatedAccess(dish).characteristics[1]()).to.deep.equal({name: 'de-characteristic1'}); expect(translatedThingDE.characteristics![1]).to.deep.equal({name: 'de-characteristic1'}); } @test public directMetaArrayOfString() { expect(translator.translatedAccess(dish).categories()).to.deep.equal(['Hauptgericht', 'Nachtisch']); expect(translatedThingDE.categories).to.deep.equal(['Hauptgericht', 'Nachtisch']); } @test public directMetaArrayOfStringSubscript() { expect(translator.translatedAccess(dish).categories[1]()).to.equal('Nachtisch'); expect(translatedThingDE.categories[1]).to.equal('Nachtisch'); } @test public nestedStringLiteralType() { expect(translator.translatedAccess(dish).offers[0].inPlace.type()).to.equal('Gebäude'); expect(translatedThingDE.offers![0].inPlace!.type).to.equal('Gebäude'); } @test public nestedStringProperty() { expect(translator.translatedAccess(dish).offers[0].inPlace.name()).to.equal('de-space-name'); expect(translatedThingDE.offers![0].inPlace!.name).to.equal('de-space-name'); } @test public nestedMetaArrayOfString() { expect(translator.translatedAccess(dish).offers[0].inPlace.categories()).to.deep.equal(['Büro', 'Bildung']); expect(translatedThingDE.offers![0].inPlace!.categories).to.deep.equal(['Büro', 'Bildung']); } @test public nestedMetaArrayOfStringSubscript() { expect(translator.translatedAccess(dish).offers[0].inPlace.categories[1]()).to.equal('Bildung'); expect(translatedThingDE.offers![0].inPlace!.categories[1]).to.equal('Bildung'); } @test public directStringLiteralTypeFallback() { expect(translatorWithFallback.translatedAccess(dish).type()).to.equal('dish'); expect(translatedThingFallback.type).to.equal('dish'); } @test public directStringPropertyFallback() { expect(translatorWithFallback.translatedAccess(dish).name()).to.equal('base-dish-name'); expect(translatedThingFallback.name).to.equal('base-dish-name'); } @test public directArrayOfStringSubscriptFallback() { expect(translatorWithFallback.translatedAccess(dish).characteristics[1]()) .to.deep.equal({name: 'base-characteristic1'}); expect(translatedThingFallback.characteristics![1]) .to.deep.equal({name: 'base-characteristic1'}); } @test public directMetaArrayOfStringFallback() { expect(translatorWithFallback.translatedAccess(dish).categories()).to.deep.equal(['main dish', 'dessert']); expect(translatedThingFallback.categories).to.deep.equal(['main dish', 'dessert']); } @test public directMetaArrayOfStringSubscriptFallback() { expect(translatorWithFallback.translatedAccess(dish).categories[1]()).to.equal('dessert'); expect(translatedThingFallback.categories[1]).to.equal('dessert'); } @test public nestedStringLiteralTypeFallback() { expect(translatorWithFallback.translatedAccess(dish).offers[0].inPlace.type()).to.equal('building'); expect(translatedThingFallback.offers![0].inPlace!.type).to.equal('building'); } @test public nestedStringPropertyFallback() { expect(translatorWithFallback.translatedAccess(dish).offers[0].inPlace.name()).to.equal('base-space-name'); expect(translatedThingFallback.offers![0].inPlace!.name).to.equal('base-space-name'); } @test public nestedMetaArrayOfStringFallback() { expect(translatorWithFallback.translatedAccess(dish).offers[0].inPlace.categories()) .to.deep.equal(['office', 'education']); expect(translatedThingFallback.offers![0].inPlace!.categories) .to.deep.equal(['office', 'education']); } @test public nestedMetaArrayOfStringSubscriptFallback() { expect(translatorWithFallback.translatedAccess(dish).offers[0].inPlace.categories[1]()).to.equal('education'); expect(translatedThingFallback.offers![0].inPlace!.categories[1]).to.equal('education'); } @test public directStringLiteralTypeUndefined() { // tslint:disable-next-line:no-eval const undefinedThing = eval('(x) => undefined;'); expect(translator.translatedAccess(undefinedThing())('defaultValue')).to.equal('defaultValue'); expect(translator.translatedAccess(dish).name('defaultValue')).to.not.equal('defaultValue'); } @test public nestedMetaArrayOfStringSubscriptUndefined() { // tslint:disable-next-line: no-eval const workingTranslation = eval('translator.translatedAccess(dish).offers[0].inPlace.categories[1](\'printer\');'); // tslint:disable-next-line: no-eval const defaultValueTranslation = eval('translator.translatedAccess(dish).offers[0].inPlace.categories[1234](\'printer\');'); expect(defaultValueTranslation).to.equal('printer'); expect(workingTranslation).to.not.equal('printer'); } @test public reaccessWithChangedSourceOmitsLRUCache() { const translatorDE = new SCThingTranslator('de'); const dishCopy = clone(dish); const translatedDish = translatorDE.translatedAccess(dish); const distructivelyTranslatedDish = translatorDE.translate(dish); (dishCopy.origin as SCThingRemoteOrigin).name = 'tranlator.spec'; expect(translatorDE.translatedAccess(dishCopy)).not.to.deep.equal(translatedDish); expect(translatorDE.translate(dishCopy)).not.to.equal(distructivelyTranslatedDish); } @test public changingTranslatorLanguageFlushesItsLRUCache() { const translatorDE = new SCThingTranslator('de'); expect(translatorDE.translatedAccess(dish).name()).to.equal('de-dish-name'); expect(translatorDE.translate(dish).name).to.equal('de-dish-name'); translatorDE.language = 'en'; expect(translatorDE.translatedAccess(dish).name()).to.equal('base-dish-name'); expect(translatorDE.translate(dish).name).to.equal('base-dish-name'); } @test public forceTranslatorLRUCacheToOverflow() { const translatorDE = new SCThingTranslator('de'); // Make sure to add more elements to the translator cache than the maximum cache capacity. See Translator.ts for (let i = 0; i < 201; i++) { const anotherDish = Object.assign({}, dish); anotherDish.uid = String(i); expect(translatorDE.translatedAccess(anotherDish).name()).to.equal('de-dish-name'); } } } // tslint:disable:member-ordering no-eval no-unused-expression TranslationSpec @suite(timeout(10000), slow(5000)) export class MetaTranslationSpec { @test public consistencyWithMetaClass() { const dishMetaTranslationsDE = translator.translatedPropertyNames(dish.type); const dishMetaTranslationsEN = translatorEN.translatedPropertyNames(dish.type); expect(dishMetaTranslationsEN).to.not.deep.equal(dishMetaTranslationsDE); expect(dishMetaTranslationsDE).to.deep.equal(SCDishMeta.getInstance().fieldTranslations.de); expect(dishMetaTranslationsEN).to.deep.equal(SCDishMeta.getInstance().fieldTranslations.en); } @test public retrieveTranslatedPropertyValueType() { const dishTypeDE = translator.translatedPropertyValue(dish.type, 'type'); const dishTypeEN = translatorEN.translatedPropertyValue(dish.type, 'type', undefined); const dishTypeBASE = translatorWithFallback.translatedPropertyValue(dish.type, 'type'); expect(dishTypeDE).to.deep.equal(SCDishMeta.getInstance().fieldValueTranslations.de.type); expect(dishTypeEN).to.deep.equal(SCDishMeta.getInstance().fieldValueTranslations.en.type); expect(dishTypeBASE).to.deep.equal(SCDishMeta.getInstance().fieldValueTranslations.en.type); } @test public retrieveTranslatedPropertyValueNested() { const dishTypeDE = translator.translatedPropertyValue(dish.type, 'categories', 'main dish'); const dishTypeEN = translatorEN.translatedPropertyValue(dish.type, 'categories', 'main dish'); const dishTypeBASE = translatorWithFallback.translatedPropertyValue(dish.type, 'categories', 'main dish'); expect(dishTypeDE).to.deep.equal(SCDishMeta.getInstance().fieldValueTranslations.de.categories['main dish']); expect(dishTypeEN).to.deep.equal(dish.categories[0]); expect(dishTypeBASE).to.deep.equal(dish.categories[0]); } @test public thingWithoutMetaClass() { const dishCopy = clone(dish); const typeNonExistant = eval("(x) => x + 'typeNonExistant';"); // this will assign a non existant SCThingType to dishCopy dishCopy.type = typeNonExistant(); const dishMetaTranslationsDE = translator.translatedPropertyNames(dishCopy.type); expect(dishMetaTranslationsDE).to.be.undefined; } }