Skip to content

Commit

Permalink
feat: project-based test execution
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed Apr 17, 2021
1 parent e6accc3 commit f9236bb
Show file tree
Hide file tree
Showing 70 changed files with 505 additions and 3,329 deletions.
17 changes: 12 additions & 5 deletions core/core/src/jest.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { TestResult } from '@jest/test-result';
export interface JestTestResults {
ancestorTitles: string[];
failureDetails: unknown[];
Expand All @@ -21,10 +22,16 @@ export interface JestCoverage {
branches: CoverageMetrics;
}

export interface JestTestFile {
testFileName: string;
testResults: JestTestResults[];
export interface JestTests {
/**
* test results
*/
results: Pick<
TestResult,
'leaks' | 'memoryUsage' | 'perfStats' | 'testFilePath' | 'testResults'
>[];
/**
* coverage summary data, by file
*/
coverage: Record<string, JestCoverage>;
}

export type JestTests = JestTestFile[];
7 changes: 1 addition & 6 deletions core/instrument/src/babel/extract-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { analyze_components } from './analyze-component';
import { packageInfo } from '../misc/package-info';
import { readSourceFile } from '../misc/source-options';
import { LoadingDocStore, InstrumentOptions } from '../types';
import { extractJestTests } from '../misc/jest-tests';

export interface ComponentParseData {
component?: Component;
Expand All @@ -34,7 +33,7 @@ export const extractComponent = async (
return globalCache[cacheKey];
}
const follow = followImports(componentName, filePath, source, options);
const { components, resolver: resolveOptions, jest } = options || {};
const { components, resolver: resolveOptions } = options || {};

let component: Component;
let componentPackage: PackageInfo | undefined;
Expand Down Expand Up @@ -116,10 +115,6 @@ export const extractComponent = async (
componentName,
follow.filePath,
);
if (jest !== false) {
const testResults = await extractJestTests(component.request, jest);
component.jest = testResults;
}
if (saveSource) {
component.source = saveSource;
component.loc = follow.loc;
Expand Down
2 changes: 2 additions & 0 deletions core/instrument/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import {
export * from './types';
export { getComponentProps } from './misc/props-info';
export { getFileIinfo } from './misc/file-info';
export { extractComponentTests } from './misc/jest-tests';

export { prettify };

type TraverseFn = (
Expand Down
14 changes: 14 additions & 0 deletions core/instrument/src/misc/chached-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ export class CachedFileResource<T> {
private fileKey: string;
private filePath: string;

/**
*
* @param folderKey folder(category) name
* @param fileKey unique (hash) key for the file
* @param filePath full file path and name for the cached file
*/
constructor(folderKey: string, fileKey: string, filePath: string) {
this.folderKey = folderKey;
this.fileKey = fileKey;
Expand All @@ -34,6 +40,10 @@ export class CachedFileResource<T> {
.digest('hex'),
);
};
/**
*
* @returns if the data is in the cache or undefined
*/
get = (): T | undefined => {
const cachedFileName = this.getCachedFile();
if (fs.existsSync(cachedFileName)) {
Expand All @@ -48,6 +58,10 @@ export class CachedFileResource<T> {
return undefined;
};

/**
*
* @param data the data to be saved into the cache
*/
set = (data: T | undefined): void => {
const cachedFileName = this.getCachedFile();
fs.writeFileSync(cachedFileName, JSON.stringify(data || {}));
Expand Down
135 changes: 104 additions & 31 deletions core/instrument/src/misc/jest-tests.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,68 @@
import path from 'path';
import fs from 'fs';
import { createHash } from 'crypto';
import { JestTests } from '@component-controls/core';
import { JestConfig } from '@component-controls/jest-extract';
import {
JestResults,
runTests,
runProjectTests,
findJestConfig,
getRelatedTests,
} from '@component-controls/jest-extract';
import { CachedFileResource } from './chached-file';
import { Components, JestTests } from '@component-controls/core';

export const extractJestTests = async (
filePath: string,
/**
* Separates the files into projects and runs jest tests
* @param files key filePathName pair of all files to test
* @param options jest runCLI options
* @returns return key/value pairs with test results and coverage associated with each file
*/
export const extractTests = async (
files: Record<string, string>,
options?: JestConfig,
): Promise<JestTests | undefined> => {
const testFiles = getRelatedTests(filePath);
const dateModified = fs.statSync(filePath).mtime;
): Promise<Record<string, JestTests>> => {
const tests = Object.keys(files).reduce(
(acc: Record<string, { key: string; files: string[] }[]>, key) => {
const component = files[key];
const projectFolder = findJestConfig(component);
if (!acc[projectFolder]) {
acc[projectFolder] = [];
}
acc[projectFolder].push({ key, files: [component] });
return acc;
},

{},
);
const projects = Object.keys(tests);
const results: Record<string, JestTests> = {};
for (const project of projects) {
const files = tests[project].reduce(
(
acc: {
testFiles: Record<string, string>;
coverageFiles: Record<string, string>;
},
component,
) => {
results[component.key] = { results: [], coverage: {} };
component.files.forEach(filePath =>
getRelatedTests(filePath).forEach(
f =>
(acc.testFiles[`.${path.sep}${path.relative(project, f)}`] =
component.key),
),
);
component.files.forEach(filePath => {
acc.coverageFiles[`.${path.sep}${path.relative(project, filePath)}`] =
component.key;
});
/* const dateModified = fs.statSync(projectFolder).mtime;
const fileDir = path.dirname(filePath);
if (testFiles.length) {
const results: JestTests = [];
for (const testFile of testFiles) {
const fileHash = createHash('md5')
.update(dateModified.toString())
.update(filePath)
.update(projectFolder)
.digest('hex');

const cached = new CachedFileResource<JestResults>(
'jest-tests',
`${testFile}-${fileHash}`,
testFile,
`${componentFilePath}-${fileHash}`,
componentFilePath,
);
const cachedTest = cached.get();
if (cachedTest) {
Expand All @@ -38,18 +71,58 @@ export const extractJestTests = async (
testResults: cachedTest.testResults,
coverage: cachedTest.coverage,
});
}
const result = await runTests(testFile, options);
if (result) {
cached.set(result);
results.push({
testFileName: path.relative(fileDir, testFile),
testResults: result.testResults,
coverage: result.coverage,
});
}
} */
return acc;
},
{ testFiles: {}, coverageFiles: {} },
);
if (Object.keys(files.testFiles).length) {
const testResults = await runProjectTests(
Object.keys(files.testFiles),
project,
{
collectCoverageOnlyFrom: Object.keys(files.coverageFiles),
...options,
},
);
Object.keys(testResults.coverage).forEach(covFile => {
const componentKey = files.coverageFiles[`.${path.sep}${covFile}`];
if (componentKey) {
results[componentKey].coverage[covFile] =
testResults.coverage[covFile];
}
});
testResults.results.forEach(r => {
const componentKey = files.testFiles[`.${path.sep}${r.testFilePath}`];
if (componentKey) {
results[componentKey].results.push(r);
}
});
}
return results.length ? results : undefined;
}
return undefined;
return results;
};

/**
* runs the tests associated with the components and assign to field "jest"
* @param components key/Component list
* @param options jest runCLI options
*/
export const extractComponentTests = async (
components: Components,
options?: JestConfig,
): Promise<void> => {
const mapped = Object.keys(components).reduce(
(acc: Record<string, string>, key) => ({
...acc,
[key]: components[key].request as string,
}),
{},
);
const extracted = await extractTests(mapped, options);
Object.keys(extracted).forEach(key => {
if (components[key]) {
components[key].jest = extracted[key];
}
});
};
Loading

0 comments on commit f9236bb

Please sign in to comment.