Skip to content

Commit

Permalink
Merge pull request #62 from C0ZEN/feat/support-vitest
Browse files Browse the repository at this point in the history
feat: allow running the tests with vitest
  • Loading branch information
koliveira15 authored Nov 30, 2023
2 parents 0e32083 + 7c738de commit 77759b5
Show file tree
Hide file tree
Showing 6 changed files with 860 additions and 91 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ lcov Paths:

1. Nx workspace
2. SonarQube or Sonar Cloud instance
3. Jest tests & code coverage enabled
3. Jest/Vite tests & code coverage enabled

### Installation

Expand Down
321 changes: 321 additions & 0 deletions packages/nx-sonarqube/src/executors/scan/executor-jest.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
import sonarScanExecutor from './executor';
import {
DependencyType,
ExecutorContext,
ProjectGraph,
readJsonFile,
} from '@nx/devkit';
import * as sonarQubeScanner from 'sonarqube-scanner';
import * as fs from 'fs';
import { determinePaths } from './utils/utils';

let projectGraph: ProjectGraph;
let context: ExecutorContext;

class MockError extends Error {}

jest.mock('@nx/devkit', () => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...jest.requireActual<any>('@nx/devkit'),
readCachedProjectGraph: jest.fn().mockImplementation(() => {
throw new Error('readCachedProjectGraph error');
}),
createProjectGraphAsync: jest
.fn()
.mockImplementation(async () => projectGraph),
readJsonFile: jest.fn().mockImplementation(() => {
throw new MockError('not implemented for this test');
}),
}));

jest.mock('sonarqube-scanner');

describe('Scan Executor', (): void => {
let jestConfig: string;

beforeEach((): void => {
(readJsonFile as jest.MockedFunction<typeof readJsonFile>).mockReset();

context = {
cwd: '',
isVerbose: false,
root: '',
projectName: 'app1',
workspace: {
version: 2,
projects: {
app1: {
root: 'apps/app1',
sourceRoot: 'apps/app1/src',
targets: {
test: {
executor: '',
options: {
jestConfig: 'jest.config.ts',
},
},
},
},
lib1: {
root: 'libs/lib1',
sourceRoot: 'libs/lib1/src',
targets: {
test: {
executor: '@nx/jest:jest',
options: {
jestConfig: 'jest.config.ts',
},
},
},
},
lib2: {
root: 'libs/lib2',
sourceRoot: 'libs/lib2/src',
targets: {
test: {
executor: '@nx/jest:jest',
options: {
jestConfig: 'jest.config.ts',
},
},
},
},
lib3: {
root: 'libs/lib3',
sourceRoot: 'libs/lib3/src',
targets: {
test: {
executor: '@nx/jest:jest',
options: {
jestConfig: 'jest.config.ts',
},
},
},
},
},
},
};

projectGraph = {
dependencies: {
app1: [
{
type: DependencyType.static,
source: 'app1',
target: 'lib1',
},
{
type: DependencyType.static,
source: 'app1',
target: 'lib2',
},
{
type: DependencyType.implicit,
source: 'app1',
target: 'lib3',
},
],
lib1: [
{
type: DependencyType.static,
source: 'lib1',
target: 'lib2',
},
{
type: DependencyType.implicit,
source: 'lib1',
target: 'lib3',
},
],
lib2: [
{
type: DependencyType.static,
source: 'lib2',
target: 'lib3',
},
],
},
nodes: {
app1: {
name: 'app1',
type: 'app',
data: {
root: 'apps/app1',
sourceRoot: 'apps/app1/src',
targets: {
test: {
executor: '',
options: {
jestConfig: 'jest.config.ts',
},
},
},
},
},
lib1: {
name: 'lib1',
type: 'lib',
data: {
root: 'libs/lib1',
sourceRoot: 'libs/lib1/src',
targets: {
test: {
executor: '',
options: {
jestConfig: 'jest.config.ts',
},
},
},
},
},
lib2: {
name: 'lib2',
type: 'lib',
data: {
root: 'libs/lib2',
sourceRoot: 'libs/lib2/src',
targets: {
test: {
executor: '',
options: {
jestConfig: 'jest.config.ts',
},
},
},
},
},
lib3: {
name: 'lib3',
type: 'lib',
data: {
root: 'libs/lib3',
sourceRoot: 'libs/lib3/src',
targets: {
test: {
executor: '',
options: {
jestConfig: 'jest.config.ts',
},
},
},
},
},
},
};

jestConfig = `export default {
displayName: 'app1',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
transform: {
'^.+\\\\.[tj]s$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'js', 'html', 'json'],
coverageDirectory: '../../coverage/apps/app1',
};`;
});

afterEach((): void => {
jest.clearAllMocks();
});


it('should scan project and dependencies & skip projects with no test target', async () => {
jest.spyOn(fs, 'readFileSync').mockReturnValue(jestConfig);
sonarQubeScanner.mockResolvedValue(true);

const newContext = { ...context };
newContext.workspace.projects['app1'].targets = {};

const output = await sonarScanExecutor(
{
hostUrl: 'url',
projectKey: 'key',
qualityGate: true,
},
newContext
);
expect(output.success).toBe(true);
});

it('should scan project and dependencies & skip projects with no jestConfig', async () => {
jest.spyOn(fs, 'readFileSync').mockReturnValue(jestConfig);
sonarQubeScanner.mockResolvedValue(true);

const newContext = { ...context };
newContext.workspace.projects['app1'].targets.test.options = {};

const output = await sonarScanExecutor(
{
hostUrl: 'url',
projectKey: 'key',
qualityGate: true,
},
newContext
);
expect(output.success).toBe(true);
});

it('should scan project and dependencies & skip projects with no coverageDirectory', async () => {
jest.spyOn(fs, 'readFileSync').mockReturnValue('');

sonarQubeScanner.mockResolvedValue(true);

const output = await sonarScanExecutor(
{
hostUrl: 'url',
projectKey: 'key',
qualityGate: true,
},
context
);
expect(output.success).toBe(true);
});

it('should error on sonar scanner issue', async () => {
jest.spyOn(fs, 'readFileSync').mockReturnValue(jestConfig);
sonarQubeScanner.async.mockImplementation(() => {
throw new Error();
});

const output = await sonarScanExecutor(
{
hostUrl: 'url',
projectKey: 'key',
},
context
);
expect(output.success).toBeFalsy();
});

it('should return jest config coverage directory path', async () => {
const paths = await determinePaths(
{
hostUrl: 'url',
projectKey: 'key',
},
context
);
expect(paths.lcovPaths.includes('coverage/apps/app1/lcov.info')).toBe(true);
});

it('should return project test config coverage directory path', async () => {
const testContext = JSON.parse(JSON.stringify(context)) as typeof context;
testContext.workspace.projects.app1.targets.test.options.coverageDirectory =
'coverage/test/apps/app1';
const paths = await determinePaths(
{
hostUrl: 'url',
projectKey: 'key',
},
testContext
);
expect(paths.lcovPaths.includes('coverage/test/apps/app1/lcov.info')).toBe(
true
);
});
});
Loading

0 comments on commit 77759b5

Please sign in to comment.