Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/transformer/genericDeclaration/genericDeclaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as ts from 'typescript';
import { GetDescriptor } from '../descriptor/descriptor';
import { TypescriptHelper } from '../descriptor/helper/helper';
import { TypescriptCreator } from '../helper/creator';
import { TransformerLogger } from '../logger/transformerLogger';
import { MockDefiner } from '../mockDefiner/mockDefiner';
import { MockIdentifierGenericParameterIds, MockIdentifierGenericParameterValue } from '../mockIdentifier/mockIdentifier';
import { Scope } from '../scope/scope';
import { IGenericDeclaration } from './genericDeclaration.interface';
Expand Down Expand Up @@ -86,6 +88,16 @@ export function GenericDeclaration(scope: Scope): IGenericDeclaration {

if (ts.isTypeReferenceNode(genericNode)) {
const typeParameterDeclaration: ts.Declaration = TypescriptHelper.GetDeclarationFromNode(genericNode.typeName);

const isExtendingItself: boolean = MockDefiner.instance.getDeclarationKeyMap(typeParameterDeclaration) === declarationKey;
if (isExtendingItself) {
// FIXME: Currently, circular generics aren't supported. See
// https://github.com/Typescript-TDD/ts-auto-mock/pull/312 for more
// details.
TransformerLogger().circularGenericNotSupported(genericNode.getText());
return acc;
}

if (ts.isTypeParameterDeclaration(typeParameterDeclaration)) {
addGenericParameterToExisting(
extensionDeclarationTypeParameters[index],
Expand Down
7 changes: 7 additions & 0 deletions src/transformer/logger/transformerLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ILogger } from '../../logger/logger.interface';
let logger: ILogger;

export interface TransformerLogger {
circularGenericNotSupported(nodeName: string): void;
unexpectedCreateMock(mockFileName: string, expectedFileName: string): void;
typeNotSupported(type: string): void;
typeOfFunctionCallNotFound(node: string): void;
Expand All @@ -13,6 +14,12 @@ export function TransformerLogger(): TransformerLogger {
logger = logger || Logger('Transformer');

return {
circularGenericNotSupported(nodeName: string): void {
logger.warning(
`Found a circular generic of \`${nodeName}' and such generics are currently not supported. ` +
'The generated mock will be incomplete.',
);
},
unexpectedCreateMock(mockFileName: string, expectedFileName: string): void {
logger.warning(`I\'ve found a mock creator but it comes from a different folder
found: ${mockFileName}
Expand Down
3 changes: 2 additions & 1 deletion src/transformer/mockFactoryCall/mockFactoryCall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ function addFromDeclarationExtensions(declaration: GenericDeclarationSupported,
declarationKey,
extensionDeclaration as GenericDeclarationSupported,
extensionDeclarationKey,
extension);
extension,
);

addFromDeclarationExtensions(extensionDeclaration as GenericDeclarationSupported, extensionDeclarationKey, genericDeclaration);
});
Expand Down
12 changes: 12 additions & 0 deletions test/transformer/descriptor/generic/extends.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,4 +244,16 @@ describe('for generic', () => {
});
});

describe('with circular', () => {
interface A extends ClassWithGenerics<A> {
b: number;
}

it('should avoid infinite extension', () => {
const properties: A = createMock<A>();
expect(properties.a).toBeDefined();
expect(properties.b).toBe(0);
});
});

});
28 changes: 27 additions & 1 deletion ui/src/views/types-not-supported.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,30 @@ Unfortunately this functionality doesnt work yet because when getting properties
a mapped type typescript returns a different type of property that is difficult to mock.

There is a branch created with a working version but it needs more investigation because the implementation is not readable and it may cause more issues
[link](https://github.com/Typescript-TDD/ts-auto-mock/tree/feature/extends-mapped-type)
[link](https://github.com/Typescript-TDD/ts-auto-mock/tree/feature/extends-mapped-type)


## Circular Generics

```ts
class C<T> {
public propC: T
public test: string
}

class A extends C<A> {
public propA: number
}
const a: A = createMock<A>();

// This will fail because we will not support generics of the same type.
expect(a.propC.propC.test).toBe("");
```

These are discussed here:
[link](https://github.com/Typescript-TDD/ts-auto-mock/pull/312). As of this
writing, the problem with circular generics is that the generated AST will
circle `A` over and over, and result in an infinite nested tree of declaration
references. The intended behavior is to have the first back-reference stored
elsewhere in the generated output and let it reference itself, making the
runtime a lazy-evaluated sequence of getters.