Skip to content

Commit

Permalink
feat: override initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
ala-n committed Apr 7, 2021
1 parent 78de5f5 commit 641da83
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/modules/esl-base-element/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './core/esl-base-element';
export * from './decorators/attr';
export * from './decorators/bool-attr';
export * from './decorators/json-attr';
export * from './decorators/override';
16 changes: 16 additions & 0 deletions src/modules/esl-base-element/decorators/override.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* `@override` is auxiliary decorator to override field that decorated in the parent class.
*
* Typically used to override {@link attr}, {@link boolAttr}, etc
*
* @param [value] - initial property value
* @param [readonly] - make a non writable constant
*/
export function override(value: any = undefined, readonly = false) {
return function (obj: any, name: string): void {
if (Object.hasOwnProperty.call(obj, name)) {
throw new TypeError('Can\'t override own property');
}
Object.defineProperty(obj, name, {value, enumerable: true, writable: !readonly});
};
}
141 changes: 141 additions & 0 deletions src/modules/esl-base-element/test/override.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import '../../../polyfills/es5-target-shim';
import {ESLBaseElement, attr, override, boolAttr, jsonAttr} from '../core';

describe('Decorator: override', () => {
class TestBaseElement extends ESLBaseElement {
@attr()
public field: string;
@boolAttr()
public field2: boolean;
@jsonAttr()
public field3: {a: number};
@attr()
public field4?: string;
@attr({readonly: true})
public readonlyField: string;
}

describe('Overriding @attr', () => {
class TestElement extends TestBaseElement {
@override('test')
public field: string;
@override()
public field4?: string;
@override('test')
public readonlyField: string;
}
customElements.define('attr-override-1', TestElement);

test('should override simple @attr decorator', () => {
const el = new TestElement();
expect(el.field).toBe('test');
});
test('should override readonly @attr decorator', () => {
const el = new TestElement();
expect(el.readonlyField).toBe('test');
});
test('override should be writeable', () => {
const el = new TestElement();
el.field = el.readonlyField = 'hi';
expect(el.field).toBe('hi');
expect(el.readonlyField).toBe('hi');
});
test('original decorator should not be executed', () => {
const el = new TestElement();
el.field = 'hi';
expect(el.getAttribute('field')).toBe(null);
});

test('should have undefined as a default', () => {
const el = new TestElement();
expect('field4' in el).toBe(true);
expect(el.field4).toBe(undefined);
});
});

describe('Overriding @boolAttr', () => {
class TestElement extends TestBaseElement {
@override(true)
public field2: boolean;
}
customElements.define('bool-attr-override-1', TestElement);

test('should override simple @boolAttr decorator', () => {
const el = new TestElement();
expect(el.field2).toBe(true);
});
test('override should be writeable', () => {
const el = new TestElement();
el.field2 = false;
expect(el.field2).toBe(false);
});
test('original decorator should not be executed', () => {
const el = new TestElement();
expect(el.getAttribute('field2')).toBe(null);
});
});

describe('Overriding @jsonAttr', () => {
class TestElement extends TestBaseElement {
@override({a: 2})
public field3: {a: number};
}
customElements.define('json-attr-override-1', TestElement);

test('should override simple @jsonAttr decorator', () => {
const el = new TestElement();
expect(el.field3).toEqual({a: 2});
});
test('override should be writeable', () => {
const el = new TestElement();
el.field3 = {a: 4};
expect(el.field3).toEqual({a: 4});
});
test('original decorator should not be executed', () => {
const el = new TestElement();
expect(el.getAttribute('field3')).toBe(null);
});
});

describe('Overriding @attr with readonly decorator', () => {
class TestElement extends TestBaseElement {
@override('test', true)
public field: string;
}
customElements.define('readonly-attr-override-1', TestElement);

test('should override simple @attr decorator', () => {
const el = new TestElement();
expect(el.field).toBe('test');
});
test('override should be writeable', () => {
const el = new TestElement();
expect(() => el.field ='hi').toThrowError();
expect(el.field).toBe('test');
});
});

describe('Overridden property can be defined through ES initial value ', () => {
class TestElement extends TestBaseElement {
@override()
public field: string = '123';
}
customElements.define('es-initial-attr-override-1', TestElement);

test('should override simple @attr decorator', () => {
const el = new TestElement();
expect(el.field).toBe('123');
});
});

test('Overriding own property produce error', () => {
expect(() => {
class TestElement extends ESLBaseElement {
@override('')
@attr()
public field: string;
}
new TestElement();
}).toThrowError(/own property/);
});
});

0 comments on commit 641da83

Please sign in to comment.