From 3b71c0f64a2d1c4b9845dfc36d9b65720a516a91 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Wed, 21 Aug 2019 09:52:56 +0200 Subject: [PATCH] Check Enum index before value on construct (#1329) --- packages/types/src/codec/Enum.spec.ts | 80 ++++++++++++++++++++++++--- packages/types/src/codec/Enum.ts | 17 +++--- 2 files changed, 80 insertions(+), 17 deletions(-) diff --git a/packages/types/src/codec/Enum.spec.ts b/packages/types/src/codec/Enum.spec.ts index 559fcfa600..1b90cdf8b0 100644 --- a/packages/types/src/codec/Enum.spec.ts +++ b/packages/types/src/codec/Enum.spec.ts @@ -234,20 +234,82 @@ describe('Enum', (): void => { new Enum(['foo', 'bar'], 1).eq('foo') ).toBe(false); }); + + it('has isNone set, with correct index (i.e. no values are used)', (): void => { + const test = new Enum(['foo', 'bar'], 1); + + expect(test.isNone).toBe(true); + expect(test.index).toEqual(1); + }); }); }); - describe('toRawType', (): void => { - it('has a sane output for basic enums', (): void => { - expect( - new Enum(['foo', 'bar']).toRawType() - ).toEqual(JSON.stringify({ _enum: ['foo', 'bar'] })); + describe('index construction', (): void => { + it('creates enum where index is specified', (): void => { + const Test = Enum.with({ + A: U32, + B: U32 + }); + const test = new Test(new U32(123), 1); + + expect(test.type).toEqual('B'); + expect((test.value as U32).toNumber()).toEqual(123); }); - it('has a sane output for types enums', (): void => { - expect( - new Enum({ foo: Text, bar: U32 }).toRawType() - ).toEqual(JSON.stringify({ _enum: { foo: 'Text', bar: 'u32' } })); + it('creates enum when value is an enum', (): void => { + const Test = Enum.with({ + A: U32, + B: U32 + }); + const test = new Test(new Test(123, 1)); + + expect(test.type).toEqual('B'); + expect((test.value as U32).toNumber()).toEqual(123); + }); + + it('creates via enum with nested enums as the value', (): void => { + const Nest = Enum.with({ + C: U32, + D: U32 + }); + const Test = Enum.with({ + A: U32, + B: Nest + }); + const test = new Test(new Nest(123, 1), 1); + + expect(test.type).toEqual('B'); + expect((test.value as Enum).type).toEqual('D'); + expect(((test.value as Enum).value as U32).toNumber()).toEqual(123); + }); + }); + + describe('outputs', (): void => { + describe('toRawType', (): void => { + it('has a sane output for basic enums', (): void => { + expect( + new Enum(['foo', 'bar']).toRawType() + ).toEqual(JSON.stringify({ _enum: ['foo', 'bar'] })); + }); + + it('has a sane output for typed enums', (): void => { + expect( + new Enum({ foo: Text, bar: U32 }).toRawType() + ).toEqual(JSON.stringify({ _enum: { foo: 'Text', bar: 'u32' } })); + }); + }); + + describe('toHex', (): void => { + it('has a proper hex representation & length', (): void => { + const Test = Enum.with({ + A: Text, + B: U32 + }); + const test = new Test(123, 1); + + expect(test.toHex()).toEqual('0x017b000000'); + expect(test.encodedLength).toEqual(1 + 4); + }); }); }); }); diff --git a/packages/types/src/codec/Enum.ts b/packages/types/src/codec/Enum.ts index 04ac4904fd..90a553fcd2 100644 --- a/packages/types/src/codec/Enum.ts +++ b/packages/types/src/codec/Enum.ts @@ -11,7 +11,8 @@ import { mapToTypeMap } from './utils'; import Base from './Base'; import Struct from './Struct'; -interface EnumConstructor { +// export interface, this is used in Enum.with, so required as public by TS +export interface EnumConstructor { new(value?: any, index?: number): T; } @@ -61,7 +62,7 @@ export default class Enum extends Base { private _isBasic: boolean; - public constructor (def: Record | string[], value?: any, index?: number | Enum) { + public constructor (def: Record | string[], value?: any, index?: number) { const defInfo = extractDef(def); const decoded = Enum.decodeEnum(defInfo.def, value, index); @@ -73,13 +74,13 @@ export default class Enum extends Base { this._index = this._indexes.indexOf(decoded.index) || 0; } - private static decodeEnum (def: TypesDef, value?: any, index?: number | Enum): Decoded { - if (value instanceof Enum) { - return Enum.createValue(def, value._index, value.raw); - } else if (index instanceof Enum) { - return Enum.createValue(def, index._index, index.raw); - } else if (isNumber(index)) { + private static decodeEnum (def: TypesDef, value?: any, index?: number): Decoded { + // NOTE We check the index path first, before looking at values - this allows treating + // the optional indexes before anything else, more-specific > less-specific + if (isNumber(index)) { return Enum.createValue(def, index, value); + } else if (value instanceof Enum) { + return Enum.createValue(def, value._index, value.raw); } // Or else, we just look at `value`