/* * Copyright (C) 2018 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 {isLightweightClass, isLightweightEnum, isUnionType} from '@openstapps/core-tools/lib/easy-ast/ast-util'; import {LightweightAliasDefinition} from '@openstapps/core-tools/lib/easy-ast/types/lightweight-alias-definition'; import {LightweightProjectWithIndex} from '@openstapps/core-tools/lib/easy-ast/types/lightweight-project'; import {LightweightType} from '@openstapps/core-tools/lib/easy-ast/types/lightweight-type'; import {LightweightClassDefinition} from '@openstapps/core-tools/src/easy-ast/types/lightweight-class-definition'; import {LightweightDefinition} from '@openstapps/core-tools/src/easy-ast/types/lightweight-definition'; import {LightweightProperty} from '@openstapps/core-tools/src/easy-ast/types/lightweight-property'; import {expect} from 'chai'; import {assign, chain, clone, flatMap, isNil, reduce, reject, some} from 'lodash'; process.on('unhandledRejection', (err) => { throw err; }); describe('Features', () => { let project: LightweightProjectWithIndex; let thingNames: string[]; let things: LightweightClassDefinition[]; let thingsWithoutReferences: LightweightClassDefinition[]; before(function () { this.timeout(15000); this.slow(10000); project = new LightweightProjectWithIndex('src'); const thingsReflection = project.definitions['SCIndexableThings'] as LightweightAliasDefinition; expect(isLightweightEnum(thingsReflection)).to.be.true; expect(isUnionType(thingsReflection.type!)).to.be.true; thingsReflection.type!.specificationTypes!.push({ flags: 524_288, referenceName: 'SCDiff', }); expect(thingsReflection.type?.specificationTypes?.every(it => typeof it.referenceName !== 'undefined')).to.be.true; thingNames = thingsReflection.type?.specificationTypes?.map(type => type.referenceName!) ?? []; things = thingNames .map(it => project.definitions[it]) .filter(isLightweightClass); thingsWithoutReferences = thingNames .map(it => project.definitions[`${it}WithoutReferences`]) .filter(isLightweightClass); }); const inheritedProperties = function (classLike: LightweightClassDefinition): Record | undefined { return reduce( [...(classLike.implementedDefinitions ?? []), ...(classLike.extendedDefinitions ?? [])], (obj, extension) => { const object = project.definitions[extension.referenceName ?? '']; return assign(obj, isLightweightClass(object) ? inheritedProperties(object) : obj); }, clone(classLike.properties) ); }; it('should have an origin', () => { for (const thing of things) { expect(inheritedProperties(thing)?.['origin']).not.to.be.undefined; } }); it('should not have duplicate names', () => { reduce(project.files, (fileResult, file) => reduce(file, (definitionResult, definition: LightweightDefinition) => { expect(definitionResult[definition.name]).to.be.undefined; definitionResult[definition.name] = true; // something that's not undefined return definitionResult; }, fileResult), {} as Record); }); it('should not have properties referencing SCThing', () => { const allPropertyReferenceNames: (property: LightweightProperty) => string[] = property => reject([ property.type.referenceName!, ...flatMap(property.properties, allPropertyReferenceNames), ], isNil); const typeHasSCThingReferences: (type?: LightweightType) => boolean = type => type?.referenceName ? hasSCThingReferences(project.definitions[type.referenceName]) : some(type?.specificationTypes, typeHasSCThingReferences); const hasSCThingReferences: (definition?: LightweightDefinition) => boolean = definition => isLightweightClass(definition) ? chain(inheritedProperties(definition)) .flatMap(it => flatMap(it.properties, allPropertyReferenceNames)) .map(it => project.definitions[it] as LightweightDefinition) .some(it => it.name === 'SCThing' || hasSCThingReferences(it)) .value() : definition ? typeHasSCThingReferences(definition.type) : false; for (const thing of things) { expect(hasSCThingReferences(thing)).to.be.false; } }); function extendsSCThing(definition?: LightweightDefinition): boolean { return isLightweightClass(definition) ? chain([ ...(definition as LightweightClassDefinition).extendedDefinitions ?? [], ...(definition as LightweightClassDefinition).implementedDefinitions ?? [], ]) .map(it => it.referenceName) .reject(isNil) .some(it => it === 'SCThing' || extendsSCThing(project.definitions[it!])) .value() : false; } it('should extend SCThing if it is an SCThing', () => { for (const thing of things) { expect(extendsSCThing(thing)).to.be.true; } }); it('should not extend SCThing if it is an SCThingWithoutReferences', () => { for (const thingWithoutReferences of thingsWithoutReferences) { expect(extendsSCThing(thingWithoutReferences)).to.be.false; } }); });