/* * 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; } }); });