Skip to content

Instantly share code, notes, and snippets.

@johnmcase
Last active February 24, 2021 13:03
Show Gist options
  • Save johnmcase/7b0c8544f56c230ceb77cbd244909a07 to your computer and use it in GitHub Desktop.
Save johnmcase/7b0c8544f56c230ceb77cbd244909a07 to your computer and use it in GitHub Desktop.
var glob = require("glob")
Reset = "\x1b[0m"
FgRed = "\x1b[31m"
FgGreen = "\x1b[32m"
FgYellow = "\x1b[33m"
FgWhite = "\x1b[37m"
let specs = glob.sync("src/**/*.spec.ts");
let tests = glob.sync("src/**/*.test.ts");
console.log(FgYellow, `${specs.join('\n')}`, Reset)
if (specs.length) {
console.log(FgRed, specs.length + " slow karma tests")
} else {
console.log(FgGreen, 'Wooooooooooooooooooooo! Jest conversion complete!')
}
console.log(FgWhite, tests.length + " fast jest tests")
console.log(FgGreen, (tests.length * 100 / (tests.length + specs.length)).toFixed(2) + "% complete in switching tests to jest", Reset)
var fs = require('fs')
var filename = process.argv[2]
if (!filename) {
let specs = require('glob').sync("src/**/*.spec.ts");
for (spec of specs) {
if (!spec.includes('pact')) {
convertToJest(spec);
}
}
} else {
convertToJest(filename);
}
function convertToJest(filename) {
if (!filename.startsWith('C:')) {
filename = './' + filename
}
fs.readFile(filename, 'utf8', function (err, data) {
if (err) {
return console.log(err);
}
var result = data;
// result = result.replace(' } from \'@ngneat/spectator\';', ', SpyObject } from \'@ngneat/spectator/jest\';');
// result = result.replace('} from \'@ngneat/spectator\';', ', SpyObject } from \'@ngneat/spectator/jest\';');
result = result.replace(/\.and\.returnValue/g, '.mockReturnValue');
result = result.replace(/\.spec\'/g, '.test');
// result = result.replace(/jasmine\.SpyObj/g, 'SpyObj');
result = result.replace(/spyOn/g, 'jest.spyOn');
result = result.replace(/spyOnProperty/g, 'spyOn');
result = result.replace(/expect\((.*)\.calls\.first\(\)\.args\)\.toEqual\(\[(.*)\]\);/g, 'expect($1).toHaveBeenCalledWith($2);')
result = result.replace(/expect\((.*)\.calls\.any\(\)\)\.toBe\((.*)\);/g, 'expect($1).toHaveBeenCalledWith($2);');
result = result.replace(/expect\((.*)\.calls\.mostRecent\(\)(\.args\[.*\])?\)\.toEqual\((.*)\);/g, 'expect($1).toHaveBeenCalledWith($2);');
result = result.replace(/expect\((.*)\.calls\.count\(\)\)\.toBe\((.*)\);/g, 'expect($1).toHaveBeenCalledTimes($2);');
result = result.replace(/expect\((.*)\.calls\.count\(\)\)\.toEqual\((.*)\);/g, 'expect($1).toHaveBeenCalledTimes($2);');
result = result.replace(/\.calls\.first\(\).args\[(\d+)\]/g, '.mock.calls[0][$1]');
result = result.replace(/\.calls\.first\(\).args/g, '.mock.calls[0].args');
result = result.replace(/.and.callFake/g, '.mockImplementation');
result = result.replace(/.and.callThrough\(\);/g, ';');
result = result.replace(/(.*).calls\.reset\(\);/g, '$1.mockClear();');
result = result.replace(/jasmine.createSpyObj\(/g, 'createSpyObj(');
result = result.replace(/jasmine.createSpy\(\'(.*)\'\);/g, 'jest.fn();');
// result = result.replace(/createService\(/g, 'createServiceFactory(');
// result = result.replace(/createService,/g, 'createServiceFactory,');
if (result.includes('createSpyObj')) {
result = 'import { createSpyObj } from \'src/testing/createSpy\';\r\n' + result;
}
if (result.includes('jasmine.clock().mockDate')) {
result = 'import { advanceBy, advanceTo, clear } from \'jest-date-mock\';\r\n' + result;
}
// result = result.replace('import SpyObj = SpyObj;', '');
// result = result.replace('import Spy = jasmine.Spy;', '');
// result = result.replace('import createSpyObj = createSpyObj;', '');
result = result.replace('import { configureTestSuite } from \'ng-bullet\';', 'import { configureTestSuite } from \'src/testing/jest-bullet\';');
// result = result.replace(/ Spy;/g, ' jest.SpyInstance;');
result = result.replace(/: jasmine.SpyObj</g, ': jest.Mocked<');
result = result.replace(/: jasmine\.Spy</g, ': jest.MockedFunction<');
result = result.replace(/: jasmine\.Spy/g, ': jest.Mock');
// result = result.replace(/configureTestSuite\(\(\)\s*=\>\s*\{(.*?)\}\);/s, 'beforeEach(async(() => {$1}));');
result = result.replace(/(\s)it\(\'/g, '$1test(\'');
result = result.replace(/(\s)it\(\`/g, '$1test(\`');
result = result.replace(/.innerText/g, '.textContent.trim()');
result = result.replace(/jasmine.clock\(\).install\(\)/g, 'jest.useFakeTimers()');
result = result.replace(/jasmine.clock\(\).uninstall\(\)/g, 'jest.useRealTimers()');
result = result.replace(/jasmine.clock\(\).mockDate\((.*)\)/g, 'advanceTo($1)');
// if (!result.includes('@ngneat/spectator') && result.includes('SpyObject')) {
// result = 'import { SpyObject } from \'@ngneat/spectator/jest\';\r\n' + result;
// }
// if (result.includes('MatDialog') && !result.includes('@angular/material/dialog')) {
// result = result.replace(/import \{(.*)MatDialog, (.*)\}/g, 'import {$1$2}');
// result = result.replace(/import \{(.*)MatDialogModule, (.*)\}/g, 'import {$1$2}');
// result = result.replace(/import \{(.*)MatDialogModule(.*)\}/g, 'import {$1$2}');
// result = result.replace(/import \{(.*)MAT_DIALOG_DATA, (.*)\}/g, 'import {$1$2}');
// result = result.replace(/import \{(.*)MatDialogRef, (.*)\}/g, 'import {$1$2}');
// result = 'import { MatDialog, MatDialogModule, MAT_DIALOG_DATA, MatDialogRef } from \'@angular/material/dialog\';\r\n' + result;
// }
// if (result.includes('withArgs')) {
// result = result.replace(/(.*)\.withArgs\((.*)\)\.mockReturnValue\((.*)\)/g, `$1.mockImplementation(flag => {
// switch (flag) {
// case $2:
// return $3;
// }
// })`);
// }
result = result.replace(/jest\.jest/g, 'jest');
let newFile = filename.replace('.spec.ts', '.test.ts');
fs.writeFile(newFile, result, 'utf8', function (err) {
if (err)
return console.log(err);
console.log('Successfully wrote ' + newFile);
if (newFile != filename) {
fs.unlinkSync(filename);
}
});
});
}
export const createSpyObj = <T = any>(_baseName, methodNames?): jest.Mocked<T> => {
const obj: any = {};
for (let i = 0; i < methodNames.length; i++) {
obj[methodNames[i]] = jest.fn();
}
return obj;
};
export const createSpy = (_baseName?) => {
return jest.fn();
};
// This code has been adapted from ng-bullet and modified to work with Jest
// https://www.npmjs.com/package/ng-bullet
import { Type } from '@angular/core';
import { ComponentFixture, getTestBed, TestBed } from '@angular/core/testing';
/**
* Reconfigures current test suit to prevent angular components re-compilation after every test run.
* Forces angular test bed to re-create zone and all injectable services by directly
* setting _instantiated variable to false after every test run.
* Cleanups all the changes and reverts test bed configuration after suite has finished.
*
* @param configureAction an optional delegate which can be used to configure test bed for the current test suite
* directly in the configureTestSuite call (you don't need extra BeforeAll in this case)
*/
export const configureTestSuite = (configureAction?: () => void) => {
const testBedApi: any = getTestBed();
const originReset = TestBed.resetTestingModule;
beforeAll(() => {
TestBed.resetTestingModule();
TestBed.resetTestingModule = () => TestBed;
});
if (configureAction) {
beforeAll((done?: jest.DoneCallback) => (async () => {
configureAction();
await TestBed.compileComponents();
})().then(done ? done : () => {}).catch(done && done.fail ? done.fail : err => console.log(err)));
}
afterEach(() => {
testBedApi._activeFixtures.forEach((fixture: ComponentFixture<any>) => fixture.destroy());
testBedApi._instantiated = false;
});
afterAll(() => {
TestBed.resetTestingModule = originReset;
TestBed.resetTestingModule();
});
};
/**
* A wrapper class around ComponentFixture, which provides useful accessros:
* component - to access component instance of current the fixture
* element - to access underlying native element of the current component
* detectChanges - to run change detections using current fixture
* resolve - to resolve a type using current fixture's injector
*/
export class TestCtx<T> {
constructor(public fixture: ComponentFixture<T>) { }
public get component() { return this.fixture.componentInstance; }
public get element(): HTMLElement { return this.fixture.debugElement.nativeElement; }
public detectChanges() { this.fixture.detectChanges(); }
public resolve(component: Type<any>) { return this.fixture.debugElement.injector.get(component); }
}
/**
* Creates TestCtx instance for the Angular Component which is not initialized yet (no ngOnInit called)
* Use case: you can override Component's providers before hooks are called.
*
* @param component - type of component to create instance of
* **/
export const createTestContext = <T>(component: Type<T>) => {
const fixture = TestBed.createComponent<T>(component);
const testCtx = new TestCtx<T>(fixture);
return testCtx;
};
/**Same as @function createTestContext, but waits till fixture becomes stable */
export const createStableTestContext = async <T>(component: Type<T>) => {
const testCtx = createTestContext(component);
testCtx.detectChanges();
await testCtx.fixture.whenStable();
testCtx.detectChanges();
return testCtx;
};
var preset = require("jest-preset-angular/jest-preset");
module.exports = {
...preset,
preset: "jest-preset-angular",
setupFiles: ["jest-date-mock"],
setupFilesAfterEnv: [
"<rootDir>/node_modules/jest-preset-angular/build/setupJest.js"
],
testMatch: ["**/*.test.ts"],
globals: {
...preset.globals,
"ts-jest": {
...preset.globals["ts-jest"],
tsConfig: "tsconfig.test.json",
isolatedModules: true
}
},
transform: {
"^.+\\.(j|t)sx?$": "ts-jest",
},
transformIgnorePatterns: [
"<rootDir>/node_modules/(?!lodash-es/.*)"
],
coverageReporters: ['html', 'lcovonly', 'text-summary'],
coverageDirectory: '<rootDir>/coverage'
};
import 'jest-preset-angular';
{
"extends": "./tsconfig.json",
"compilerOptions": {
"mapRoot": "./",
"outDir": "./out-tsc/spec",
"types": [
"@jest/types",
"node"
],
"esModuleInterop": true,
"allowJs": true
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment