Skip to content

Commit

Permalink
test: add failing test cases with single file transpilation
Browse files Browse the repository at this point in the history
  • Loading branch information
ahnpnl committed Aug 2, 2024
1 parent 61727ea commit c5a0f81
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 4 deletions.
174 changes: 174 additions & 0 deletions src/compiler/ng-jest-compiler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,5 +475,179 @@ describe('NgJestCompiler', () => {
], MyComp);
`);
});

it.failing('should capture constructor type metadata with `emitDecoratorMetadata` enabled', () => {
const { code, diagnostics } = transformCjs(
`
import {Directive} from '@angular/core';
import {MyOtherClass} from './other-file';
@Directive()
export class MyDir {
constructor(other: MyOtherClass) {}
}
`,
{ emitDecoratorMetadata: true },
);

expect(diagnostics.length).toBe(0);
expect(code).toContain('const other_file_1 = require("./other-file");');
// This is actual output code
// `
// MyDir.ctorParameters = () => [
// { type: other_file_1.MyOtherClass }
// ];
// exports.MyDir = MyDir = tslib_1.__decorate([
// (0, core_1.Directive)(),
// tslib_1.__metadata("design:paramtypes", [typeof (_a = typeof other_file_1.MyOtherClass !== \\"undefined\\" && other_file_1.MyOtherClass) === \\"function\\" ? _a : Object]])
// ], MyDir);
// `
expect(code).toContain(dedent`
MyDir.ctorParameters = () => [
{ type: other_file_1.MyOtherClass }
];
exports.MyDir = MyDir = tslib_1.__decorate([
(0, core_1.Directive)(),
tslib_1.__metadata("design:paramtypes", Object])
], MyDir);
`);
});

it.failing('should properly serialize constructor parameter with external qualified name type', () => {
// This test doesn't pass because `transpileModule` can't resolve `import *`
const { code, diagnostics } = transformCjs(`
import {Directive} from '@angular/core';
import * as externalFile from './other-file';
@Directive()
export class MyDir {
constructor(other: externalFile.MyOtherClass) {}
}
`);

expect(diagnostics.length).toBe(0);
expect(code).toContain('const externalFile = require("./other-file");');
// This is actual output code
// `
// MyDir.ctorParameters = () => [
// { type: undefined }
// ];
// exports.MyDir = MyDir = tslib_1.__decorate([
// (0, core_1.Directive)()
// ], MyDir);
// `
expect(code).toContain(dedent`
MyDir.ctorParameters = () => [
{ type: externalFile.MyOtherClass }
];
exports.MyDir = MyDir = tslib_1.__decorate([
(0, core_1.Directive)()
], MyDir);
`);
});

it.failing('should not capture constructor parameter types when not resolving to a value', () => {
// This test doesn't pass because `transpileModule` can't resolve `import *`
const { code, diagnostics } = transformCjs(`
import {Directive, Inject} from '@angular/core';
import * as angular from './external';
import {IOverlay, KeyCodes} from './external';
import TypeFromDefaultImport from './external';
@Directive()
export class MyDir {
constructor(@Inject('$state') param: angular.IState,
@Inject('$overlay') other: IOverlay,
@Inject('$default') fromDefaultImport: TypeFromDefaultImport,
@Inject('$keyCodes') keyCodes: KeyCodes) {}
}
`);

expect(diagnostics.length).toBe(0);
// This is actual output code
// `
// "\\"use strict\\";
// Object.defineProperty(exports, \\"__esModule\\", { value: true });
// exports.MyDir = void 0;
// const tslib_1 = require(\\"tslib\\");
// const core_1 = require(\\"@angular/core\\");
// const external_1 = require(\\"./external\\");
// const external_2 = tslib_1.__importDefault(require(\\"./external\\"));
// let MyDir = class MyDir {
// constructor(param, other, fromDefaultImport, keyCodes) { }
// };
// exports.MyDir = MyDir;
// MyDir.ctorParameters = () => [
// { type: undefined, decorators: [{ type: core_1.Inject, args: ['$state',] }] },
// { type: external_1.IOverlay, decorators: [{ type: core_1.Inject, args: ['$overlay',] }] },
// { type: external_2.default, decorators: [{ type: core_1.Inject, args: ['$default',] }] },
// { type: external_1.KeyCodes, decorators: [{ type: core_1.Inject, args: ['$keyCodes',] }] }
// ];
// exports.MyDir = MyDir = tslib_1.__decorate([
// (0, core_1.Directive)()
// ], MyDir);
// "
// `
expect(code).not.toContain('external');
expect(code).toContain(dedent`
MyDir.ctorParameters = () => [
{ type: undefined, decorators: [{ type: core_1.Inject, args: ['$state',] }] },
{ type: undefined, decorators: [{ type: core_1.Inject, args: ['$overlay',] }] },
{ type: undefined, decorators: [{ type: core_1.Inject, args: ['$default',] }] },
{ type: undefined, decorators: [{ type: core_1.Inject, args: ['$keyCodes',] }] }
];
exports.MyDir = MyDir = tslib_1.__decorate([
(0, core_1.Directive)()
], MyDir);
`);
});

it.failing(
'should allow for type-only references to be removed with `emitDecoratorMetadata` from custom decorators',
() => {
const { code, diagnostics } = transformCjs(
`
import { ExternalInterface } from './external-interface';
export function CustomDecorator() {
return <T>(target, propertyKey, descriptor: TypedPropertyDescriptor<T>) => {}
}
export class Foo {
@CustomDecorator() static test(): ExternalInterface { return {}; }
}
`,
{ emitDecoratorMetadata: true },
);

expect(diagnostics.length).toBe(0);
// This is actual output code
// `
// "\\"use strict\\";
// var _a;
// Object.defineProperty(exports, \\"__esModule\\", { value: true });
// exports.Foo = void 0;
// exports.CustomDecorator = CustomDecorator;
// const tslib_1 = require(\\"tslib\\");
// const external_interface_1 = require(\\"./external-interface\\");
// function CustomDecorator() {
// return (target, propertyKey, descriptor) => { };
// }
// class Foo {
// static test() { return {}; }
// }
// exports.Foo = Foo;
// tslib_1.__decorate([
// CustomDecorator(),
// tslib_1.__metadata(\\"design:type\\", Function),
// tslib_1.__metadata(\\"design:paramtypes\\", []),
// tslib_1.__metadata(\\"design:returntype\\", typeof (_a = typeof external_interface_1.ExternalInterface !== \\"undefined\\" && external_interface_1.ExternalInterface) === \\"function\\" ? _a : Object)
// ], Foo, \\"test\\", null);
// "
// `
expect(code).not.toContain('ExternalInterface');
expect(code).toContain('metadata("design:returntype", Object)');
},
);
});
});
11 changes: 7 additions & 4 deletions src/compiler/ng-jest-compiler.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import os from 'node:os';
import path from 'node:path';
import os from 'os';
import path from 'path';

import { type TsJestAstTransformer, TsCompiler, type ConfigSet } from 'ts-jest';
import type ts from 'typescript';
import type * as ts from 'typescript';

import { angularJitApplicationTransform } from '../transformers/jit_transform';
import { replaceResources } from '../transformers/replace-resources';
Expand All @@ -11,7 +11,10 @@ export class NgJestCompiler extends TsCompiler {
private readonly _defaultLibDirPath: string;
private readonly _libSourceFileCache = new Map<string, ts.SourceFile>();

constructor(readonly configSet: ConfigSet, readonly jestCacheFS: Map<string, string>) {
constructor(

Check failure on line 14 in src/compiler/ng-jest-compiler.ts

View workflow job for this annotation

GitHub Actions / code_standard_check / lint

Replace `⏎········readonly·configSet:·ConfigSet,⏎········readonly·jestCacheFS:·Map<string,·string>,⏎····` with `readonly·configSet:·ConfigSet,·readonly·jestCacheFS:·Map<string,·string>`
readonly configSet: ConfigSet,
readonly jestCacheFS: Map<string, string>,
) {
super(configSet, jestCacheFS);

this._logger.debug('created NgJestCompiler');
Expand Down

0 comments on commit c5a0f81

Please sign in to comment.