Skip to content

Commit

Permalink
fix(core): better eval code to extend es6 classes #5465
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Apr 23, 2023
1 parent 7e90dd1 commit 2dd66cd
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 5 deletions.
14 changes: 9 additions & 5 deletions libs/ng-mocks/src/lib/common/core.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,27 +114,31 @@ export const extractDependency = (deps: any[], set?: Set<any>): void => {

export const extendClassicClass = <I>(base: AnyType<I>): Type<I> => {
let child: any;
const index = ngMocksUniverse.index();

const glb = funcGetGlobal();
glb.ngMocksParent = base;

// First we try to eval es2015 style and if it fails to use es5 transpilation in the catch block.
// The next step is to respect constructor parameters as the parent class via jitReflector.
glb.ngMocksParent = base;
// istanbul ignore next
try {
eval(`
var glb = typeof window === 'undefined' ? global : window;
class MockMiddleware extends glb.ngMocksParent {}
glb.ngMocksResult = MockMiddleware
class MockMiddleware${index} extends glb.ngMocksParent {};
glb.ngMocksResult = MockMiddleware${index};
`);
child = glb.ngMocksResult;
} catch {
class MockMiddleware extends glb.ngMocksParent {}
child = MockMiddleware;
} finally {
glb.ngMocksResult = undefined;
glb.ngMocksParent = undefined;
}
glb.ngMocksParent = undefined;

// A16: adding unique property.
coreDefineProperty(child.prototype, `__ngMocks_index_${ngMocksUniverse.index()}`, undefined, false);
coreDefineProperty(child.prototype, `__ngMocks_index_${index}`, undefined, false);

return child;
};
Expand Down
62 changes: 62 additions & 0 deletions tests/issue-5465/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { NgForOf } from '@angular/common';
import { Component, NgModule, VERSION } from '@angular/core';

import { MockBuilder, MockRender } from 'ng-mocks';

// @see https://github.com/help-me-mom/ng-mocks/issues/5465
// TypeError: Class constructor CommonModule cannot be invoked without 'new'
describe('issue-5465', () => {
if (Number.parseInt(VERSION.major, 10) < 14) {
it('needs a14', () => {
// pending('Need Angular 14+');
expect(true).toBeTruthy();
});

return;
}

@Component({
selector: 'app-ng-for',
template: `
<span *ngFor="let letter of this.test">{{ letter }}</span>
`,
})
class AppNgForComponent {
test = ['a', 'b'];

appNgFor5465() {}
}

@NgModule({
imports: [
NgForOf as never /* TODO: remove after upgrade to a14 */,
],
declarations: [AppNgForComponent],
exports: [AppNgForComponent],
})
class AppNgForModule {}

@Component({
selector: 'app-root',
template: ` <app-ng-for></app-ng-for> `,
})
class AppRootComponent {
appRoot5465() {}
}

@NgModule({
declarations: [AppRootComponent],
imports: [AppNgForModule],
providers: [],
bootstrap: [AppRootComponent],
})
class AppModule {}

beforeEach(() =>
MockBuilder([AppRootComponent], [AppModule, NgForOf]),
);

it('renders AppRootComponent', () => {
expect(() => MockRender(AppRootComponent)).not.toThrow();
});
});

0 comments on commit 2dd66cd

Please sign in to comment.