Skip to content

Commit

Permalink
refactor(angular-output-target): split up component proxy code genera…
Browse files Browse the repository at this point in the history
…tion (#302)
  • Loading branch information
sean-perkins committed Oct 3, 2022
1 parent 5cbfc4a commit 2a001a3
Show file tree
Hide file tree
Showing 10 changed files with 765 additions and 162 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ build/
.idea/
.vscode
.npmrc
*.tgz
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
import { createComponentTypeDefinition, createAngularComponentDefinition } from '../src/generate-angular-component';

describe('createAngularComponentDefinition()', () => {
describe('www output', () => {
it('generates a component', () => {
const component = createAngularComponentDefinition('my-component', [], [], [], false);

expect(component).toEqual(`@ProxyCmp({
})
@Component({
selector: 'my-component',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: [],
})
export class MyComponent {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}`);
});

it('generates a component with inputs', () => {
const component = createAngularComponentDefinition('my-component', ['my-input', 'my-other-input'], [], [], false);
expect(component).toMatch(`@ProxyCmp({
inputs: ['my-input', 'my-other-input']
})
@Component({
selector: 'my-component',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['my-input', 'my-other-input'],
})
export class MyComponent {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}`);
});

it('generates a component with outputs', () => {
const component = createAngularComponentDefinition(
'my-component',
[],
['my-output', 'my-other-output'],
[],
false
);

expect(component).toMatch(`@ProxyCmp({
})
@Component({
selector: 'my-component',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: [],
})
export class MyComponent {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['my-output', 'my-other-output']);
}
}`);
});

it('generates a component with methods', () => {
const component = createAngularComponentDefinition('my-component', [], [], ['myMethod', 'myOtherMethod'], false);

expect(component).toMatch(`@ProxyCmp({
methods: ['myMethod', 'myOtherMethod']
})
@Component({
selector: 'my-component',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: [],
})
export class MyComponent {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}`);
});
});

describe('custom elements output', () => {
it('generates a component', () => {
const component = createAngularComponentDefinition('my-component', [], [], [], true);

expect(component).toEqual(`@ProxyCmp({
defineCustomElementFn: defineMyComponent
})
@Component({
selector: 'my-component',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: [],
})
export class MyComponent {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}`);
});

it('generates a component with inputs', () => {
const component = createAngularComponentDefinition('my-component', ['my-input', 'my-other-input'], [], [], true);

expect(component).toEqual(`@ProxyCmp({
defineCustomElementFn: defineMyComponent,
inputs: ['my-input', 'my-other-input']
})
@Component({
selector: 'my-component',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['my-input', 'my-other-input'],
})
export class MyComponent {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}`);
});

it('generates a component with outputs', () => {
const component = createAngularComponentDefinition(
'my-component',
[],
['my-output', 'my-other-output'],
[],
true
);
expect(component).toMatch(`@ProxyCmp({
defineCustomElementFn: defineMyComponent
})
@Component({
selector: 'my-component',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: [],
})
export class MyComponent {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['my-output', 'my-other-output']);
}
}`);
});

it('generates a component with methods', () => {
const component = createAngularComponentDefinition('my-component', [], [], ['myMethod', 'myOtherMethod'], true);

expect(component).toMatch(`@ProxyCmp({
defineCustomElementFn: defineMyComponent,
methods: ['myMethod', 'myOtherMethod']
})
@Component({
selector: 'my-component',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: [],
})
export class MyComponent {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
}
}`);
});
});
});

describe('createComponentTypeDefinition()', () => {
let testEvents: any[] = [
{
name: 'myEvent',
complexType: {
references: {
MyEvent: {
location: 'import',
},
},
original: 'MyEvent',
resolved: 'MyEvent',
},
docs: {
text: 'This is an example event.',
tags: [
{
text: 'Bar',
name: 'Foo',
},
],
},
},
{
name: 'myOtherEvent',
complexType: {
references: {
MyOtherEvent: {
location: 'import',
},
},
original: 'MyOtherEvent',
},
docs: {
text: 'This is the other event.',
tags: [],
},
},
{
name: 'myDoclessEvent',
complexType: {
references: {
MyDoclessEvent: {
location: 'import',
},
},
original: 'MyDoclessEvent',
},
docs: {
text: '',
tags: [],
},
},
];

describe('www build', () => {
it('creates a type definition', () => {
const definition = createComponentTypeDefinition('MyComponent', testEvents, '@ionic/core', false);

expect(definition).toEqual(
`import type { MyEvent as IMyComponentMyEvent } from '@ionic/core';
import type { MyOtherEvent as IMyComponentMyOtherEvent } from '@ionic/core';
import type { MyDoclessEvent as IMyComponentMyDoclessEvent } from '@ionic/core';
export declare interface MyComponent extends Components.MyComponent {
/**
* This is an example event. @Foo Bar
*/
myEvent: EventEmitter<CustomEvent<IMyComponentMyEvent>>;
/**
* This is the other event.
*/
myOtherEvent: EventEmitter<CustomEvent<IMyComponentMyOtherEvent>>;
myDoclessEvent: EventEmitter<CustomEvent<IMyComponentMyDoclessEvent>>;
}`
);
});
});

describe('custom elements output', () => {
describe('with a custom elements directory provided', () => {
it('creates a type definition', () => {
const definition = createComponentTypeDefinition(
'MyComponent',
testEvents,
'@ionic/core',
true,
'custom-elements'
);

expect(definition).toEqual(
`import type { MyEvent as IMyComponentMyEvent } from '@ionic/core/custom-elements';
import type { MyOtherEvent as IMyComponentMyOtherEvent } from '@ionic/core/custom-elements';
import type { MyDoclessEvent as IMyComponentMyDoclessEvent } from '@ionic/core/custom-elements';
export declare interface MyComponent extends Components.MyComponent {
/**
* This is an example event. @Foo Bar
*/
myEvent: EventEmitter<CustomEvent<IMyComponentMyEvent>>;
/**
* This is the other event.
*/
myOtherEvent: EventEmitter<CustomEvent<IMyComponentMyOtherEvent>>;
myDoclessEvent: EventEmitter<CustomEvent<IMyComponentMyDoclessEvent>>;
}`
);
});
});

describe('without a custom elements directory provided', () => {
it('creates a type definition', () => {
const definition = createComponentTypeDefinition('MyComponent', testEvents, '@ionic/core', true);

expect(definition).toEqual(
`import type { MyEvent as IMyComponentMyEvent } from '@ionic/core/components';
import type { MyOtherEvent as IMyComponentMyOtherEvent } from '@ionic/core/components';
import type { MyDoclessEvent as IMyComponentMyDoclessEvent } from '@ionic/core/components';
export declare interface MyComponent extends Components.MyComponent {
/**
* This is an example event. @Foo Bar
*/
myEvent: EventEmitter<CustomEvent<IMyComponentMyEvent>>;
/**
* This is the other event.
*/
myOtherEvent: EventEmitter<CustomEvent<IMyComponentMyOtherEvent>>;
myDoclessEvent: EventEmitter<CustomEvent<IMyComponentMyDoclessEvent>>;
}`
);
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('generateProxies', () => {
it('should use a relative path to types when a component-library is not provided', () => {
const outputTarget: OutputTargetAngular = {
directivesProxyFile: '../component-library-angular/src/proxies.ts',
};
} as OutputTargetAngular;

const finalText = generateProxies(components, pkgData, outputTarget, rootDir);
expect(finalText.includes(`import { Components } from 'component-library';`)).toBeFalsy();
Expand Down
16 changes: 8 additions & 8 deletions packages/angular-output-target/__tests__/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@ describe('normalizeOutputTarget', () => {

it('should return fail if proxiesFile is not set', () => {
expect(() => {
normalizeOutputTarget({}, {});
normalizeOutputTarget({}, {} as any);
}).toThrow(new Error('rootDir is not set and it should be set by stencil itself'));
});

it('should return fail if proxiesFile is not set', () => {
it('should throw an error if directivesProxyFile is not set', () => {
expect(() => {
normalizeOutputTarget(config, {});
}).toThrow(new Error('directivesProxyFile is required'));
normalizeOutputTarget(config, {} as any);
}).toThrow(new Error('directivesProxyFile is required. Please set it in the Stencil config.'));
});

it('should return defaults for excludeComponents and valueAccessorConfig', () => {
it('should return defaults for excludeComponents and valueAccessorConfigs', () => {
const results: OutputTargetAngular = normalizeOutputTarget(config, {
directivesProxyFile: '/component-library-angular/src/components.ts',
});
} as OutputTargetAngular);

expect(results).toEqual({
directivesProxyFile: '/component-library-angular/src/components.ts',
excludeComponents: [],
valueAccessorConfig: [],
} as OutputTargetAngular);
valueAccessorConfigs: [],
});
});
});

0 comments on commit 2a001a3

Please sign in to comment.