Skip to content

Commit f28cd6f

Browse files
Merge pull request #674 from deyan-r/main
[typescript/vrotsc] (#673) jest unit testing path transforming
2 parents 6df23c0 + 2923dd9 commit f28cd6f

File tree

3 files changed

+83
-16
lines changed

3 files changed

+83
-16
lines changed

docs/versions/latest/Release.md

+13
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@ When importing we were getting an error that id is expected of type UUID.String
6161

6262
CSPs are no longer fetched prior to updating to set their ID.
6363

64+
### *Transform module paths for jest mocks*
65+
66+
Some module mocking functions of jest get module path parameter, that should be transformed to match vro-test actual file locations
67+
68+
#### Previous Behavior
69+
70+
The path is not transformed and functions fail to find the referenced file
71+
72+
#### New Behavior
73+
74+
During transpilation the paths to be transformed to the actual vro-test file location
75+
*Note*: Path transformation is only supported for paths provided as string literals.
76+
6477
### *Add missing classes to VC plugin*
6578

6679
Add missing classes to VC plugin

typescript/vrotsc/src/compiler/transformer/codeTransformers/modules.ts

+31-16
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import * as ts from "typescript";
1616
import { ScriptTransformationContext, HierarchyFacts } from "../../../types";
1717
import { NodeVisitor } from "../../visitor";
1818
import { SCRIPT_HELPER_MODULE, SCRIPT_VROES_VAR, SCRIPT_LAZY_IMPORT_NAME, SCRIPT_VRO_GLOBAL } from "../helpers/VROES";
19-
import { getIdentifierTextOrNull, hasModifier, isRequireCall, getStringTextOrNull } from "../helpers/node";
19+
import { getIdentifierTextOrNull, hasModifier, isRequireCall, getStringTextOrNull, isJestMethodCallWithModuleName } from "../helpers/node";
2020
import { system } from "../../../system/system";
2121
import { createModulePrologueStatements } from "./prologueStatements";
2222
import { createEpilogueStatements } from "./epilogueStatements";
@@ -263,7 +263,7 @@ export function transformModuleSystem(sourceFile: ts.SourceFile, context: Script
263263
const moduleName = resolveFullModuleName(node.moduleSpecifier.text);
264264
const requireCall = ts.setSourceMapRange(ts.factory.createCallExpression(
265265
ts.factory.createIdentifier("require"),
266-
/*typeArguments*/ undefined,
266+
/*typeArguments*/ undefined,
267267
[ts.factory.createStringLiteral(moduleName)]
268268
), ts.getSourceMapRange(node));
269269
const importVarPrefix = moduleName.substring(1 + Math.max(moduleName.lastIndexOf("."), moduleName.lastIndexOf("/")));
@@ -301,7 +301,7 @@ export function transformModuleSystem(sourceFile: ts.SourceFile, context: Script
301301
const moduleName = resolveFullModuleName(node.moduleSpecifier.text);
302302
const requireCall = ts.setSourceMapRange(ts.factory.createCallExpression(
303303
ts.factory.createIdentifier("require"),
304-
/*typeArguments*/ undefined,
304+
/*typeArguments*/ undefined,
305305
[ts.factory.createStringLiteral(moduleName)]
306306
), ts.getSourceMapRange(node));
307307
if (node.exportClause && ts.isNamespaceExport(node.exportClause)) {
@@ -311,15 +311,15 @@ export function transformModuleSystem(sourceFile: ts.SourceFile, context: Script
311311
ts.factory.createToken(ts.SyntaxKind.EqualsToken),
312312
ts.factory.createCallExpression(
313313
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(tslibVarName), "__importStar"),
314-
/*typeArguments*/ undefined,
314+
/*typeArguments*/ undefined,
315315
[requireCall, ts.factory.createIdentifier("exports")]
316316
),
317317
);
318318
return ts.setSourceMapRange(ts.factory.createExpressionStatement(exportAssignmentNode), ts.getSourceMapRange(node));
319319
} else {
320320
const exportCall = ts.setSourceMapRange(ts.factory.createCallExpression(
321321
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(tslibVarName), "__exportStar"),
322-
/*typeArguments*/ undefined,
322+
/*typeArguments*/ undefined,
323323
[requireCall, ts.factory.createIdentifier("exports")]
324324
), ts.getSourceMapRange(node));
325325
return ts.setSourceMapRange(ts.factory.createExpressionStatement(exportCall), ts.getSourceMapRange(node));
@@ -372,12 +372,12 @@ export function transformModuleSystem(sourceFile: ts.SourceFile, context: Script
372372
const isDefault = hasModifier(node.modifiers, ts.SyntaxKind.DefaultKeyword);
373373
const exportName = isDefault || !node.name ? "default" : node.name.text;
374374
const funcExp = ts.setSourceMapRange(ts.factory.createFunctionExpression(
375-
/*modifiers*/undefined,
376-
/*asteriskToken*/undefined,
377-
/*name*/undefined,
378-
/*typeParameters*/undefined,
375+
/*modifiers*/undefined,
376+
/*asteriskToken*/undefined,
377+
/*name*/undefined,
378+
/*typeParameters*/undefined,
379379
node.parameters,
380-
/*type*/undefined,
380+
/*type*/undefined,
381381
body
382382
), ts.getSourceMapRange(node));
383383
result.push(ts.setSourceMapRange(ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
@@ -389,12 +389,12 @@ export function transformModuleSystem(sourceFile: ts.SourceFile, context: Script
389389
result.push(
390390
ts.factory.updateFunctionDeclaration(
391391
node,
392-
/*modifiers*/undefined,
393-
/*asteriskToken*/undefined,
392+
/*modifiers*/undefined,
393+
/*asteriskToken*/undefined,
394394
node.name,
395-
/*typeParameters*/undefined,
395+
/*typeParameters*/undefined,
396396
node.parameters,
397-
/*type*/undefined,
397+
/*type*/undefined,
398398
body
399399
));
400400
if (isExported) {
@@ -449,6 +449,21 @@ export function transformModuleSystem(sourceFile: ts.SourceFile, context: Script
449449
node = tryUpdateLocalRequireCall(node);
450450
context.file.hierarchyFacts |= HierarchyFacts.ContainsRequire;
451451
}
452+
if (isJestMethodCallWithModuleName(node)) {
453+
const moduleSpecifier = (node.arguments[0] as ts.StringLiteral).text;
454+
const moduleName = system.normalizePath(system.joinPath(context.file.relativeDirPath, moduleSpecifier));
455+
const actionNamespaceDirs = context.actionsNamespace ? context.actionsNamespace.split('.') : [];
456+
const relativeDirPathDirs = context.file.relativeDirPath ? context.file.relativeDirPath.split('/') : [];
457+
const subfolderLevels = 1 + actionNamespaceDirs.length + relativeDirPathDirs.length;
458+
const newPath = "../".repeat(subfolderLevels) + 'src/' + actionNamespaceDirs.join('/') + '/' + moduleName;
459+
460+
const newArguments: ts.Expression[] = [ts.factory.createStringLiteral(newPath)];
461+
for (let i=1; i<node.arguments.length; i++) {
462+
newArguments.push(node.arguments[i]);
463+
}
464+
const newNode = ts.factory.updateCallExpression(node, node.expression, node.typeArguments, newArguments);
465+
return visitor.visitEachChild(newNode);
466+
}
452467
return visitor.visitEachChild(node);
453468
}
454469

@@ -545,7 +560,7 @@ export function transformModuleSystem(sourceFile: ts.SourceFile, context: Script
545560
requireCallNode = ts.factory.updateCallExpression(
546561
requireCallNode,
547562
ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(SCRIPT_VROES_VAR), SCRIPT_LAZY_IMPORT_NAME),
548-
/* typeArguments */ undefined,
563+
/* typeArguments */ undefined,
549564
requireCallNode.arguments);
550565
node = ts.factory.createVariableDeclaration(
551566
node.name,
@@ -606,7 +621,7 @@ export function transformModuleSystem(sourceFile: ts.SourceFile, context: Script
606621
requireCallNode = ts.factory.updateCallExpression(
607622
requireCallNode,
608623
requireCallNode.expression,
609-
/* typeArguments */ undefined,
624+
/* typeArguments */ undefined,
610625
[ts.factory.createStringLiteral(moduleName)]);
611626
}
612627
}

typescript/vrotsc/src/compiler/transformer/helpers/node.ts

+39
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,45 @@ export function isRequireCall(callExpression: ts.Node): boolean {
108108
return true;
109109
}
110110

111+
/**
112+
* Verify if a jest method that requires module name is being called,
113+
* e.g. jest.methodName("path/name"[, args])
114+
*/
115+
export function isJestMethodCallWithModuleName(callExpression: ts.CallExpression): boolean {
116+
if (callExpression.kind !== ts.SyntaxKind.CallExpression) {
117+
return false;
118+
}
119+
if (callExpression.expression.kind !== ts.SyntaxKind.PropertyAccessExpression) {
120+
return false;
121+
}
122+
if (callExpression.arguments.length === 0 || callExpression.arguments[0].kind !== ts.SyntaxKind.StringLiteral) {
123+
return false;
124+
}
125+
126+
const methodExpression = callExpression.expression as ts.PropertyAccessExpression;
127+
if (methodExpression.expression.kind !== ts.SyntaxKind.Identifier || methodExpression.name.kind !== ts.SyntaxKind.Identifier) {
128+
return false;
129+
}
130+
if ((methodExpression.expression as ts.Identifier).escapedText !== "jest") {
131+
return false;
132+
}
133+
134+
const methodName = (methodExpression.name as ts.Identifier).escapedText;
135+
switch(methodName) {
136+
case "createMockFromModule":
137+
case "mock":
138+
case "unmock":
139+
case "deepUnmock":
140+
case "doMock":
141+
case "dontmock":
142+
case "setMock":
143+
case "requireActual":
144+
case "requireMock":
145+
return true;
146+
}
147+
return false;
148+
}
149+
111150
export function getPropertyName(node: ts.PropertyName): string {
112151
switch (node.kind) {
113152
case ts.SyntaxKind.Identifier:

0 commit comments

Comments
 (0)