diff --git a/src/parsers/attribute-parser.js b/src/parsers/attribute-parser.js index 2531f80..ade50d4 100644 --- a/src/parsers/attribute-parser.js +++ b/src/parsers/attribute-parser.js @@ -167,7 +167,7 @@ export class AttributeParser { let value = null; // we don't need to serialize the value for arrays - const serializer = !array && this.typeSerializerMap.get(type); + const serializer = !array && this.typeSerializerMap.get(typeName); if (serializer) { try { value = serializer(node.initializer ?? node, this.typeChecker); @@ -192,8 +192,8 @@ export class AttributeParser { let members = []; // Check if there's a type annotation directly on the variable declaration - if (ts.isVariableDeclaration(node) && node.type) { - typeNode = node.type; + if (node.type) { + typeNode = node; } else { // Check for JSDoc annotations const jsDocs = ts.getJSDocTags(node); @@ -203,10 +203,13 @@ export class AttributeParser { } } - if (typeNode && ts.isTypeReferenceNode(typeNode.type)) { + // Also consider the elementType + const type = typeNode && (typeNode.type.elementType ?? typeNode.type); + + if (typeNode && ts.isTypeReferenceNode(type)) { // resolve the symbol of the type - let symbol = this.typeChecker.getSymbolAtLocation(typeNode.type.typeName); + let symbol = this.typeChecker.getSymbolAtLocation(type.typeName); // Resolve aliases, which are common with imports if (symbol && symbol.flags & ts.SymbolFlags.Alias) { @@ -222,7 +225,7 @@ export class AttributeParser { // Check if the declaration is a TypeScript enum if (ts.isEnumDeclaration(declaration)) { - members = declaration.members.map(member => member.name.getText()); + members = declaration.members.map(member => ({ [member.name.getText()]: member.initializer.text })); } // Additionally check for JSDoc enum tag diff --git a/src/parsers/script-parser.js b/src/parsers/script-parser.js index 0197c0f..f6bb31b 100644 --- a/src/parsers/script-parser.js +++ b/src/parsers/script-parser.js @@ -147,7 +147,8 @@ const SUPPORTED_BLOCK_TAGS = new Map([ ['precision', 'number'], ['size', 'number'], ['step', 'number'], - ['title', 'string'] + ['title', 'string'], + ['default', 'any'] ]); /** @@ -195,7 +196,7 @@ const mapAttributesToOutput = (attribute) => { } // set the default value - if (attribute.value !== undefined) attribute.default = attribute.value; + if (attribute.value !== undefined) attribute.default = attribute.default ?? attribute.value; // Curve Attributes specifically should not expose a default value if it's an empty array if (attribute.type === 'curve' && Array.isArray(attribute.value) && attribute.value.length === 0) { @@ -251,7 +252,7 @@ export class ScriptParser { const typeName = this.typeChecker.typeToString(type); const serializer = SUPPORTED_INITIALIZABLE_TYPE_NAMES.get(typeName); if (serializer) { - this.typeSerializerMap.set(type, serializer); + this.typeSerializerMap.set(typeName, serializer); } }); diff --git a/src/utils/ts-utils.js b/src/utils/ts-utils.js index 93e8aee..56689e6 100644 --- a/src/utils/ts-utils.js +++ b/src/utils/ts-utils.js @@ -208,7 +208,7 @@ function getSuperClasses(node, typeChecker) { export function getJSDocCommentRanges(node, text, typeChecker) { const commentRanges = []; - if (ts.isClassDeclaration(node)) { + if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node)) { // get an array of the class an all parent classed const heritageChain = getSuperClasses(node, typeChecker); @@ -216,7 +216,7 @@ export function getJSDocCommentRanges(node, text, typeChecker) { heritageChain.forEach((classNode) => { // for each class iterate over it's class members classNode.members.forEach((member) => { - if (ts.isPropertyDeclaration(member) || ts.isSetAccessor(member)) { + if (ts.isPropertyDeclaration(member) || ts.isSetAccessor(member) || ts.isPropertySignature(member)) { const memberName = member.name && ts.isIdentifier(member.name) ? member.name.text : 'unnamed'; const ranges = getLeadingBlockCommentRanges(member, text); if (ranges.length > 0) { @@ -310,6 +310,31 @@ export function isEnum(node) { return false; } +/** + * Determines the primitive type for enums, or falls back to the actual type name. + * + * @param {ts.Type} type - The type to inspect. + * @param {ts.TypeChecker} typeChecker - The TypeScript type checker. + * @returns {'string' | 'boolean' | 'number' | null} - The primitive type of the enum or the type's name. + */ +export function getPrimitiveEnumType(type, typeChecker) { + // Check if the type is an enum type + if (!type.symbol?.declarations?.some(decl => ts.isEnumDeclaration(decl))) return null; + + // Get the type of enum members + const enumMembers = type.symbol.declarations[0].members; + const firstMemberValue = typeChecker.getConstantValue(enumMembers[0]); + + const validEnumType = [ + 'number', + 'string', + 'boolean' + ]; + + const typeOf = typeof firstMemberValue; + return validEnumType.includes(typeOf) ? typeOf : null; +} + /** * Gets the inferred type of a TypeScript node. * @@ -322,7 +347,7 @@ export function getType(node, typeChecker) { const type = typeChecker.getTypeAtLocation(node); const array = typeChecker.isArrayType(type); const actualType = array ? typeChecker.getElementTypeOfArrayType(type) : type; - const name = typeChecker.typeToString(actualType); + const name = getPrimitiveEnumType(actualType, typeChecker) ?? typeChecker.typeToString(actualType); return { type: actualType, name, array }; } @@ -489,10 +514,15 @@ const resolvePropertyAccess = (node, typeChecker) => { if (ts.isPropertyAssignment(declaration) && declaration.initializer) { return getLiteralValue(declaration.initializer, typeChecker); } + if (ts.isVariableDeclaration(declaration) && declaration.initializer) { return getLiteralValue(declaration.initializer, typeChecker); } - // Handle other kinds of declarations if needed + + if (ts.isEnumMember(declaration)) { + return declaration.initializer ? getLiteralValue(declaration.initializer, typeChecker) : declaration.name.getText(); + } + } } diff --git a/test/fixtures/asset.invalid.ts b/test/fixtures/asset.invalid.ts new file mode 100644 index 0000000..5fc7cbf --- /dev/null +++ b/test/fixtures/asset.invalid.ts @@ -0,0 +1,24 @@ +// eslint-disable-next-line +import { Script, Asset } from 'playcanvas'; + +class Example extends Script { + /** + * @attribute + * @resource nothing + */ + a: Asset; + + /** + * @attribute + * @resource 1 + */ + b : Asset; + + /** + * @attribute + * @resource + */ + c : Asset; +} + +export { Example }; diff --git a/test/fixtures/asset.valid.ts b/test/fixtures/asset.valid.ts new file mode 100644 index 0000000..800b321 --- /dev/null +++ b/test/fixtures/asset.valid.ts @@ -0,0 +1,21 @@ +// eslint-disable-next-line +import { Script, Asset } from 'playcanvas'; + +class Example extends Script { + /** @attribute */ + a : Asset; + + /** + * @attribute + * @resource texture + */ + b : Asset; + + /** + * @attribute + * @resource container + */ + c : Asset[]; +} + +export { Example }; diff --git a/test/fixtures/enum.valid.js b/test/fixtures/enum.valid.js index 7cb11e3..8c75042 100644 --- a/test/fixtures/enum.valid.js +++ b/test/fixtures/enum.valid.js @@ -1,4 +1,4 @@ -import { Script, Vec3 } from 'playcanvas'; +import { Script } from 'playcanvas'; /** * @enum {number} @@ -19,16 +19,6 @@ const StringEnum = { C: 'c' }; -/** - * @enum {Vec3} - */ -// eslint-disable-next-line -const Vec3Enum = { - A: new Vec3(1, 2, 3), - B: new Vec3(4, 5, 6), - C: new Vec3(7, 8, 9) -}; - /** * @enum {NumberEnum} */ @@ -65,17 +55,11 @@ class Example extends Script { */ h; - /** - * @attribute - * @type {Vec3Enum} - */ - i; - /** * @attribute * @type {NumberNumberEnum} */ - j = 2; + i = 2; } export { Example }; diff --git a/test/fixtures/enum.valid.ts b/test/fixtures/enum.valid.ts new file mode 100644 index 0000000..a110c22 --- /dev/null +++ b/test/fixtures/enum.valid.ts @@ -0,0 +1,49 @@ +import { Script, Vec3 } from 'playcanvas'; + +enum NumberEnum { + A = 13, + B = 14, + C = 23 +}; + +enum StringEnum { + A = 'a', + B = 'b', + C = 'c' +}; + +enum NumberNumberEnum { + A = NumberEnum.A, + B = NumberEnum.B, + C = NumberEnum.C +}; + +class Example extends Script { + /** + * @attribute + */ + e : NumberEnum = NumberEnum.A; + + /** + * @attribute + */ + f : NumberEnum = 1; + + /** + * @attribute + * @size 2 + */ + g : NumberEnum[]; + + /** + * @attribute + */ + h : StringEnum; + + /** + * @attribute + */ + i : NumberNumberEnum = 2; +} + +export { Example }; diff --git a/test/fixtures/json.valid.ts b/test/fixtures/json.valid.ts new file mode 100644 index 0000000..a28b66e --- /dev/null +++ b/test/fixtures/json.valid.ts @@ -0,0 +1,72 @@ +import { Script, Vec3 } from 'playcanvas'; + +interface Folder { + /** + * @attribute + */ + a: boolean; + + /** + * @attribute + * @default 10 + */ + b: number; + + /** + * @attribute + * @default hello + */ + c : string; + + /** + * @attribute + */ + d : Vec3[]; +} + +interface NestedFolder { + /** + * @attribute + */ + x : Folder; +} + +/** + * @interface + */ +class Example extends Script { + /** + * @attribute + */ + f : Folder; + + /** + * @attribute + * @size 2 + */ + g : Folder[]; + + /** + * @attribute + */ + h : NestedFolder; + + /** + * @attribute + */ + i = { a: true, b: 10, c: 'hello', d: [new Vec3(1, 2, 3)] }; + + /** + * @attribute + */ + l : { x: number, y: number }; + + /** + * @attribute + */ + m : Example; + + n = 10; +} + +export { Example }; diff --git a/test/tests/valid/asset.test.js b/test/tests/valid/asset.test.js index f43a23a..190fbf9 100644 --- a/test/tests/valid/asset.test.js +++ b/test/tests/valid/asset.test.js @@ -3,48 +3,57 @@ import { describe, it, before } from 'mocha'; import { parseAttributes } from '../../utils.js'; -describe('VALID: Asset attribute', function () { - let data; - before(async function () { - data = await parseAttributes('./asset.valid.js'); - }); - - it('only results should exist', function () { - expect(data).to.exist; - expect(data[0]).to.not.be.empty; - expect(data[1]).to.be.empty; - }); - - it('Example: should exist without errors', function () { - expect(data[0]?.example).to.exist; - expect(data[0].example.attributes).to.not.be.empty; - expect(data[0].example.errors).to.be.empty; - }); - - it('a: should be a asset attribute', function () { - expect(data[0].example.attributes.a).to.exist; - expect(data[0].example.attributes.a.name).to.equal('a'); - expect(data[0].example.attributes.a.type).to.equal('asset'); - expect(data[0].example.attributes.a.array).to.equal(false); - expect(data[0].example.attributes.a.default).to.equal(null); - }); - - it('b: should be a asset attribute with a texture resource', function () { - expect(data[0].example.attributes.b).to.exist; - expect(data[0].example.attributes.b.name).to.equal('b'); - expect(data[0].example.attributes.b.type).to.equal('asset'); - expect(data[0].example.attributes.b.array).to.equal(false); - expect(data[0].example.attributes.b.default).to.equal(null); - expect(data[0].example.attributes.b.resource).to.equal('texture'); - }); +function runTests(fileName) { + + const isTS = fileName.endsWith('.ts'); + const script = isTS ? 'TS' : 'JS'; + + describe(`${script}: VALID: Asset attribute`, function () { + let data; + before(async function () { + data = await parseAttributes(fileName); + }); + + it('only results should exist', function () { + expect(data).to.exist; + expect(data[0]).to.not.be.empty; + expect(data[1]).to.be.empty; + }); + + it('Example: should exist without errors', function () { + expect(data[0]?.example).to.exist; + expect(data[0].example.attributes).to.not.be.empty; + expect(data[0].example.errors).to.be.empty; + }); + + it('a: should be a asset attribute', function () { + expect(data[0].example.attributes.a).to.exist; + expect(data[0].example.attributes.a.name).to.equal('a'); + expect(data[0].example.attributes.a.type).to.equal('asset'); + expect(data[0].example.attributes.a.array).to.equal(false); + expect(data[0].example.attributes.a.default).to.equal(null); + }); + + it('b: should be a asset attribute with a texture resource', function () { + expect(data[0].example.attributes.b).to.exist; + expect(data[0].example.attributes.b.name).to.equal('b'); + expect(data[0].example.attributes.b.type).to.equal('asset'); + expect(data[0].example.attributes.b.array).to.equal(false); + expect(data[0].example.attributes.b.default).to.equal(null); + expect(data[0].example.attributes.b.resource).to.equal('texture'); + }); + + it('c: should be a asset attribute array with a container resource', function () { + expect(data[0].example.attributes.c).to.exist; + expect(data[0].example.attributes.c.name).to.equal('c'); + expect(data[0].example.attributes.c.type).to.equal('asset'); + expect(data[0].example.attributes.c.array).to.equal(true); + expect(data[0].example.attributes.c.default).to.equal(null); + expect(data[0].example.attributes.c.resource).to.equal('container'); + }); - it('c: should be a asset attribute array with a container resource', function () { - expect(data[0].example.attributes.c).to.exist; - expect(data[0].example.attributes.c.name).to.equal('c'); - expect(data[0].example.attributes.c.type).to.equal('asset'); - expect(data[0].example.attributes.c.array).to.equal(true); - expect(data[0].example.attributes.c.default).to.equal(null); - expect(data[0].example.attributes.c.resource).to.equal('container'); }); +} -}); +runTests('./asset.valid.js'); +runTests('./asset.valid.ts'); diff --git a/test/tests/valid/checkbox.test.js b/test/tests/valid/checkbox.test.js index 6bac811..fd8dba3 100644 --- a/test/tests/valid/checkbox.test.js +++ b/test/tests/valid/checkbox.test.js @@ -3,47 +3,55 @@ import { describe, it, before } from 'mocha'; import { parseAttributes } from '../../utils.js'; -describe('VALID: Checkbox attribute', function () { - let data; - before(async function () { - data = await parseAttributes('./checkbox.valid.js'); - }); - - it('only results should exist', function () { - expect(data).to.exist; - expect(data[0]).to.not.be.empty; - expect(data[1]).to.be.empty; - }); - - it('Example: should exist without errors', function () { - expect(data[0]?.example).to.exist; - expect(data[0].example.attributes).to.not.be.empty; - expect(data[0].example.errors).to.be.empty; - }); - - it('a: should be a checkbox attribute', function () { - expect(data[0].example.attributes.a).to.exist; - expect(data[0].example.attributes.a.name).to.equal('a'); - expect(data[0].example.attributes.a.type).to.equal('boolean'); - expect(data[0].example.attributes.a.array).to.equal(false); - expect(data[0].example.attributes.a.default).to.equal(false); - }); - - it('b: should be a checkbox attribute with a default value', function () { - expect(data[0].example.attributes.b).to.exist; - expect(data[0].example.attributes.b.name).to.equal('b'); - expect(data[0].example.attributes.b.type).to.equal('boolean'); - expect(data[0].example.attributes.b.array).to.equal(false); - expect(data[0].example.attributes.b.default).to.equal(true); - }); +function runTests(fileName) { + + const isTS = fileName.endsWith('.ts'); + const script = isTS ? 'TS' : 'JS'; + + describe(`${script}: VALID: Checkbox attribute`, function () { + let data; + before(async function () { + data = await parseAttributes(fileName); + }); + + it('only results should exist', function () { + expect(data).to.exist; + expect(data[0]).to.not.be.empty; + expect(data[1]).to.be.empty; + }); + + it('Example: should exist without errors', function () { + expect(data[0]?.example).to.exist; + expect(data[0].example.attributes).to.not.be.empty; + expect(data[0].example.errors).to.be.empty; + }); + + it('a: should be a checkbox attribute', function () { + expect(data[0].example.attributes.a).to.exist; + expect(data[0].example.attributes.a.name).to.equal('a'); + expect(data[0].example.attributes.a.type).to.equal('boolean'); + expect(data[0].example.attributes.a.array).to.equal(false); + expect(data[0].example.attributes.a.default).to.equal(false); + }); + + it('b: should be a checkbox attribute with a default value', function () { + expect(data[0].example.attributes.b).to.exist; + expect(data[0].example.attributes.b.name).to.equal('b'); + expect(data[0].example.attributes.b.type).to.equal('boolean'); + expect(data[0].example.attributes.b.array).to.equal(false); + expect(data[0].example.attributes.b.default).to.equal(true); + }); + + it('c: should be a checkbox attribute array with a size', function () { + expect(data[0].example.attributes.c).to.exist; + expect(data[0].example.attributes.c.name).to.equal('c'); + expect(data[0].example.attributes.c.type).to.equal('boolean'); + expect(data[0].example.attributes.c.array).to.equal(true); + expect(data[0].example.attributes.c.size).to.equal(2); + expect(data[0].example.attributes.c.default).equal(null); + }); - it('c: should be a checkbox attribute array with a size', function () { - expect(data[0].example.attributes.c).to.exist; - expect(data[0].example.attributes.c.name).to.equal('c'); - expect(data[0].example.attributes.c.type).to.equal('boolean'); - expect(data[0].example.attributes.c.array).to.equal(true); - expect(data[0].example.attributes.c.size).to.equal(2); - expect(data[0].example.attributes.c.default).equal(null); }); +} -}); +runTests('./checkbox.valid.js'); diff --git a/test/tests/valid/enum.test.js b/test/tests/valid/enum.test.js index 656c2f7..f91bbcb 100644 --- a/test/tests/valid/enum.test.js +++ b/test/tests/valid/enum.test.js @@ -3,75 +3,76 @@ import { describe, it, before } from 'mocha'; import { parseAttributes } from '../../utils.js'; -describe('VALID: Enum attribute', function () { - let data; - before(async function () { - data = await parseAttributes('./enum.valid.js'); - }); +function runTests(fileName) { - it('only results should exist', function () { - expect(data).to.exist; - expect(data[0]).to.not.be.empty; - expect(data[1]).to.be.empty; - }); + const isTS = fileName.endsWith('.ts'); + const script = isTS ? 'TS' : 'JS'; - it('Example: should exist without errors', function () { - expect(data[0]?.example).to.exist; - expect(data[0].example.attributes).to.not.be.empty; - expect(data[0].example.errors).to.be.empty; - }); + describe(`${script}: VALID: Enum attribute`, function () { + let data; + before(async function () { + data = await parseAttributes(fileName); + }); - it('e: should be a enum attribute', function () { - expect(data[0].example.attributes.e).to.exist; - expect(data[0].example.attributes.e.name).to.equal('e'); - expect(data[0].example.attributes.e.type).to.equal('number'); - expect(data[0].example.attributes.e.array).to.equal(false); - expect(data[0].example.attributes.e.default).to.equal(13); - }); + it('only results should exist', function () { + expect(data).to.exist; + expect(data[0]).to.not.be.empty; + expect(data[1]).to.be.empty; + }); - it('f: should be a enum attribute with a default value', function () { - expect(data[0].example.attributes.f).to.exist; - expect(data[0].example.attributes.f.name).to.equal('f'); - expect(data[0].example.attributes.f.type).to.equal('number'); - expect(data[0].example.attributes.f.array).to.equal(false); - expect(data[0].example.attributes.f.default).to.equal(1); - }); + it('Example: should exist without errors', function () { + expect(data[0]?.example).to.exist; + expect(data[0].example.errors).to.be.empty; + expect(data[0].example.attributes).to.not.be.empty; + }); - it('g: should be a enum attribute array with a size', function () { - expect(data[0].example.attributes.g).to.exist; - expect(data[0].example.attributes.g.name).to.equal('g'); - expect(data[0].example.attributes.g.type).to.equal('number'); - expect(data[0].example.attributes.g.array).to.equal(true); - expect(data[0].example.attributes.g.size).to.equal(2); - expect(data[0].example.attributes.g.default).equal(null); - }); + it('e: should be a enum attribute', function () { + expect(data[0].example.attributes.e).to.exist; + expect(data[0].example.attributes.e.name).to.equal('e'); + expect(data[0].example.attributes.e.type).to.equal('number'); + expect(data[0].example.attributes.e.array).to.equal(false); + expect(data[0].example.attributes.e.default).to.equal(13); + }); - it('h: should be a enum attribute', function () { - expect(data[0].example.attributes.h).to.exist; - expect(data[0].example.attributes.h.name).to.equal('h'); - expect(data[0].example.attributes.h.type).to.equal('string'); - expect(data[0].example.attributes.h.array).to.equal(false); - expect(data[0].example.attributes.h.enum).to.be.an('array').with.lengthOf(3); - expect(data[0].example.attributes.h.enum[0]).to.deep.equal({ A: 'a' }); - expect(data[0].example.attributes.h.enum[1]).to.deep.equal({ B: 'b' }); - expect(data[0].example.attributes.h.enum[2]).to.deep.equal({ C: 'c' }); - expect(data[0].example.attributes.h.default).to.equal(''); - }); + it('f: should be a enum attribute with a default value', function () { + expect(data[0].example.attributes.f).to.exist; + expect(data[0].example.attributes.f.name).to.equal('f'); + expect(data[0].example.attributes.f.type).to.equal('number'); + expect(data[0].example.attributes.f.array).to.equal(false); + expect(data[0].example.attributes.f.default).to.equal(1); + }); - it('i: should be a enum attribute with a default value', function () { - expect(data[0].example.attributes.i).to.exist; - expect(data[0].example.attributes.i.name).to.equal('i'); - expect(data[0].example.attributes.i.type).to.equal('vec3'); - expect(data[0].example.attributes.i.array).to.equal(false); - expect(data[0].example.attributes.i.default).to.eql([0, 0, 0]); - }); + it('g: should be a enum attribute array with a size', function () { + expect(data[0].example.attributes.g).to.exist; + expect(data[0].example.attributes.g.name).to.equal('g'); + expect(data[0].example.attributes.g.type).to.equal('number'); + expect(data[0].example.attributes.g.array).to.equal(true); + expect(data[0].example.attributes.g.size).to.equal(2); + expect(data[0].example.attributes.g.default).equal(null); + }); + + it('h: should be a enum attribute', function () { + expect(data[0].example.attributes.h).to.exist; + expect(data[0].example.attributes.h.name).to.equal('h'); + expect(data[0].example.attributes.h.type).to.equal('string'); + expect(data[0].example.attributes.h.array).to.equal(false); + expect(data[0].example.attributes.h.enum).to.be.an('array').with.lengthOf(3); + expect(data[0].example.attributes.h.enum[0]).to.deep.equal({ A: 'a' }); + expect(data[0].example.attributes.h.enum[1]).to.deep.equal({ B: 'b' }); + expect(data[0].example.attributes.h.enum[2]).to.deep.equal({ C: 'c' }); + expect(data[0].example.attributes.h.default).to.equal(''); + }); + + it('j: should be a enum attribute array with a size', function () { + expect(data[0].example.attributes.i).to.exist; + expect(data[0].example.attributes.i.name).to.equal('i'); + expect(data[0].example.attributes.i.type).to.equal('number'); + expect(data[0].example.attributes.i.array).to.equal(false); + expect(data[0].example.attributes.i.default).equal(2); + }); - it('j: should be a enum attribute array with a size', function () { - expect(data[0].example.attributes.j).to.exist; - expect(data[0].example.attributes.j.name).to.equal('j'); - expect(data[0].example.attributes.j.type).to.equal('number'); - expect(data[0].example.attributes.j.array).to.equal(false); - expect(data[0].example.attributes.j.default).equal(2); }); +} -}); +runTests('./enum.valid.js'); +runTests('./enum.valid.ts'); diff --git a/test/tests/valid/json.test.js b/test/tests/valid/json.test.js index 4e2356a..0f6baa9 100644 --- a/test/tests/valid/json.test.js +++ b/test/tests/valid/json.test.js @@ -3,7 +3,8 @@ import { describe, it, before } from 'mocha'; import { parseAttributes } from '../../utils.js'; -describe('VALID: JSON attribute', function () { +describe('JS: VALID: Asset attribute', function () { + let data; before(async function () { data = await parseAttributes('./json.valid.js'); @@ -147,3 +148,130 @@ describe('VALID: JSON attribute', function () { expect(data[0].example.attributes.o).to.not.exist; }); }); + + +describe('TS: VALID: Asset attribute', function () { + + let data; + before(async function () { + data = await parseAttributes('./json.valid.ts'); + }); + + it('only results should exist', function () { + expect(data).to.exist; + expect(data[0]).to.not.be.empty; + expect(data[1]).to.be.empty; + }); + + it('Example: should exist without errors', function () { + expect(data[0]?.example).to.exist; + expect(data[0].example.attributes).to.not.be.empty; + expect(data[0].example.errors).to.be.empty; + }); + + it('f: should be a json attribute', function () { + expect(data[0].example.attributes.f).to.exist; + expect(data[0].example.attributes.f.name).to.equal('f'); + expect(data[0].example.attributes.f.type).to.equal('json'); + expect(data[0].example.attributes.f.array).to.equal(false); + expect(data[0].example.attributes.f.default).to.equal(null); + + expect(data[0].example.attributes.f.schema).to.exist; + }); + + it('f[a]: should be a boolean attribute', function () { + expect(data[0].example.attributes.f.schema[0].name).to.equal('a'); + expect(data[0].example.attributes.f.schema[0].type).to.equal('boolean'); + expect(data[0].example.attributes.f.schema[0].array).to.equal(false); + expect(data[0].example.attributes.f.schema[0].default).to.equal(false); + }); + + it('f[b]: should be a numeric attribute', function () { + expect(data[0].example.attributes.f.schema[1].name).to.equal('b'); + expect(data[0].example.attributes.f.schema[1].type).to.equal('number'); + expect(data[0].example.attributes.f.schema[1].array).to.equal(false); + expect(data[0].example.attributes.f.schema[1].default).to.equal(10); + }); + + it('f[c]: should be a string attribute', function () { + expect(data[0].example.attributes.f.schema[2].name).to.equal('c'); + expect(data[0].example.attributes.f.schema[2].type).to.equal('string'); + expect(data[0].example.attributes.f.schema[2].array).to.equal(false); + expect(data[0].example.attributes.f.schema[2].default).to.equal('hello'); + }); + + it('f[d]: should be a vec3 attribute array', function () { + expect(data[0].example.attributes.f.schema[3].name).to.equal('d'); + expect(data[0].example.attributes.f.schema[3].type).to.equal('vec3'); + expect(data[0].example.attributes.f.schema[3].array).to.equal(true); + expect(data[0].example.attributes.f.schema[3].default).to.eql(null); + }); + + it('g: should be a json attribute array with a size', function () { + expect(data[0].example.attributes.g).to.exist; + expect(data[0].example.attributes.g.name).to.equal('g'); + expect(data[0].example.attributes.g.type).to.equal('json'); + expect(data[0].example.attributes.g.array).to.equal(true); + expect(data[0].example.attributes.g.size).to.equal(2); + expect(data[0].example.attributes.g.default).to.eql(null); + + expect(data[0].example.attributes.g.schema).to.exist; + }); + + it('h: should be a nested json attribute (parent)', function () { + expect(data[0].example.attributes.h).to.exist; + expect(data[0].example.attributes.h.name).to.equal('h'); + expect(data[0].example.attributes.h.type).to.equal('json'); + expect(data[0].example.attributes.h.array).to.equal(false); + expect(data[0].example.attributes.h.default).to.eql(null); + + expect(data[0].example.attributes.h.schema).to.exist; + }); + + it('h[x]: should be a nested json attribute (child)', function () { + expect(data[0].example.attributes.h.schema[0].name).to.equal('x'); + expect(data[0].example.attributes.h.schema[0].type).to.equal('json'); + expect(data[0].example.attributes.h.schema[0].array).to.equal(false); + expect(data[0].example.attributes.h.schema[0].default).to.eql(null); + + expect(data[0].example.attributes.h.schema[0].schema).to.exist; + }); + + it('i: should be an inline default json attribute', function () { + expect(data[0].example.attributes.i).to.exist; + expect(data[0].example.attributes.i.name).to.equal('i'); + expect(data[0].example.attributes.i.type).to.equal('json'); + expect(data[0].example.attributes.i.array).to.equal(false); + expect(data[0].example.attributes.i.default).to.eql(null); + + expect(data[0].example.attributes.i.schema).to.exist; + }); + + it('l: should be an inline type json attribute', function () { + expect(data[0].example.attributes.l).to.exist; + expect(data[0].example.attributes.l.name).to.equal('l'); + expect(data[0].example.attributes.l.type).to.equal('json'); + expect(data[0].example.attributes.l.array).to.equal(false); + expect(data[0].example.attributes.l.default).to.eql(null); + + expect(data[0].example.attributes.l.schema).to.exist; + }); + + it('m: should be a self reference json attribute', function () { + expect(data[0].example.attributes.m).to.exist; + expect(data[0].example.attributes.m.name).to.equal('m'); + expect(data[0].example.attributes.m.type).to.equal('json'); + expect(data[0].example.attributes.m.array).to.equal(false); + expect(data[0].example.attributes.m.default).to.eql(null); + + expect(data[0].example.attributes.m.schema).to.exist; + }); + + it('n: should not be recognised as an attribute (no attribute tag)', function () { + expect(data[0].example.attributes.n).to.not.exist; + }); + + it('o: should not be recognised as an attribute (type not an interface)', function () { + expect(data[0].example.attributes.o).to.not.exist; + }); +});