Skip to content

Commit

Permalink
fix(vite): pass cli arguments as options to vitest (#22355)
Browse files Browse the repository at this point in the history
  • Loading branch information
thdk committed Apr 12, 2024
1 parent a627ce8 commit 637a004
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 79 deletions.
226 changes: 153 additions & 73 deletions e2e/vite/src/vite-legacy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ import {
rmDist,
runCLI,
runCommand,
runCommandUntil,
runCLIAsync,
tmpProjPath,
uniq,
updateFile,
updateJson,
checkFilesExist,
killProcessAndPorts,
} from '@nx/e2e/utils';
import { join } from 'path';
import { ChildProcess } from 'child_process';

describe('Vite Plugin', () => {
let proj: string;
Expand All @@ -41,18 +44,50 @@ describe('Vite Plugin', () => {

describe('Vite on React apps', () => {
describe('set up new React app with --bundler=vite option', () => {
it('should build application', async () => {
const myApp = uniq('my-app');
let myApp;

beforeAll(() => {
myApp = uniq('my-app');
runCLI(
`generate @nx/react:app ${myApp} --bundler=vite --directory=${myApp} --projectNameAndRootFormat=as-provided`
);
createFile(`${myApp}/public/hello.md`, `# Hello World`);
runCLI(`build ${myApp}`);
expect(readFile(`dist/${myApp}/favicon.ico`)).toBeDefined();
expect(readFile(`dist/${myApp}/hello.md`)).toBeDefined();
expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
});

afterEach(() => {
rmDist();
}, 200_000);
});

describe('build the app', () => {
it('should build application', async () => {
runCLI(`build ${myApp}`);
expect(readFile(`dist/${myApp}/favicon.ico`)).toBeDefined();
expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
}, 200_000);

describe('when the app has static assets', () => {
beforeAll(() => {
createFile(`${myApp}/public/hello.md`, `# Hello World`);
});

afterAll(() => {
removeFile(`${myApp}/public/hello.md`);
});

it('should copy the assets to the output path', async () => {
runCLI(`build ${myApp}`);
expect(readFile(`dist/${myApp}/favicon.ico`)).toBeDefined();
expect(readFile(`dist/${myApp}/hello.md`)).toBeDefined();
expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
}, 200_000);
});
});

describe('test the app', () => {
it('should test application', async () => {
const result = runCLI(`test ${myApp}`);
expect(result).toContain('Successfully ran target test');
}, 200_000);
});
});
});

Expand Down Expand Up @@ -236,32 +271,71 @@ export default App;
});

describe('should be able to create libs that use vitest', () => {
const lib = uniq('my-lib');
beforeEach(() => {
proj = newProject({ name: uniq('vite-proj'), packages: ['@nx/react'] });
});
describe('using default project configuration', () => {
const lib = uniq('my-default-lib');
beforeAll(() => {
proj = newProject({ name: uniq('vite-proj'), packages: ['@nx/react'] });
runCLI(`generate @nx/react:lib ${lib} --unitTestRunner=vitest`);
});

it('should be able to run tests', async () => {
runCLI(`generate @nx/react:lib ${lib} --unitTestRunner=vitest`);
expect(exists(tmpProjPath(`libs/${lib}/vite.config.ts`))).toBeTruthy();
it('should collect coverage when --coverage is set', () => {
const results = runCLI(`test ${lib} --coverage`);
expect(results).toContain(`Coverage report`);
}, 100_000);

const result = await runCLIAsync(`test ${lib}`);
expect(result.combinedOutput).toContain(
`Successfully ran target test for project ${lib}`
);
it('should be able to watch tests', async () => {
let cp: ChildProcess;
try {
cp = await runCommandUntil(`test ${lib} --watch`, (output) => {
return output.includes('Waiting for file changes...');
});
} catch (error) {
console.error(error);
}

if (cp && cp.pid) {
await killProcessAndPorts(cp.pid);
}
}, 100_000);

const nestedResults = await runCLIAsync(`test ${lib} --skip-nx-cache`, {
cwd: `${tmpProjPath()}/libs/${lib}`,
it('should not watch tests when --watch is not set', async () => {
const results = runCLI(`test ${lib}`);

expect(results).not.toContain('Waiting for file changes...');

expect(results).toContain(
`Successfully ran target test for project ${lib}`
);
}, 100_000);
});

describe('using custom project configuration', () => {
const lib = uniq('my-custom-lib');
beforeEach(() => {
proj = newProject({ name: uniq('vite-proj'), packages: ['@nx/react'] });
});
expect(nestedResults.combinedOutput).toContain(
`Successfully ran target test for project ${lib}`
);
}, 100_000);

it('should collect coverage', () => {
runCLI(`generate @nx/react:lib ${lib} --unitTestRunner=vitest`);
updateFile(`libs/${lib}/vite.config.ts`, () => {
return `/// <reference types='vitest' />
it('should be able to run tests', async () => {
runCLI(`generate @nx/react:lib ${lib} --unitTestRunner=vitest`);
expect(exists(tmpProjPath(`libs/${lib}/vite.config.ts`))).toBeTruthy();

const result = await runCLIAsync(`test ${lib}`);
expect(result.combinedOutput).toContain(
`Successfully ran target test for project ${lib}`
);

const nestedResults = await runCLIAsync(`test ${lib} --skip-nx-cache`, {
cwd: `${tmpProjPath()}/libs/${lib}`,
});
expect(nestedResults.combinedOutput).toContain(
`Successfully ran target test for project ${lib}`
);
}, 100_000);

it('should collect coverage', () => {
runCLI(`generate @nx/react:lib ${lib} --unitTestRunner=vitest`);
updateFile(`libs/${lib}/vite.config.ts`, () => {
return `/// <reference types='vitest' />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
Expand Down Expand Up @@ -292,25 +366,26 @@ export default App;
},
});
`;
});
});

const coverageDir = `${tmpProjPath()}/coverage/libs/${lib}`;
const coverageDir = `${tmpProjPath()}/coverage/libs/${lib}`;

const results = runCLI(`test ${lib} --coverage`, { silenceError: true });
expect(results).toContain(
`Running target test for project ${lib} failed`
);
expect(results).toContain(`ERROR: Coverage`);
expect(directoryExists(coverageDir)).toBeTruthy();
}, 100_000);

// TODO: This takes forever and times out everything - find out why
xit('should not delete the project directory when coverage is enabled', async () => {
// when coverage is enabled in the vite.config.ts but reportsDirectory is removed
// from the @nx/vite:test executor options, vite will delete the project root directory
runCLI(`generate @nx/react:lib ${lib} --unitTestRunner=vitest`);
updateFile(`libs/${lib}/vite.config.ts`, () => {
return `import { defineConfig } from 'vite';
const results = runCLI(`test ${lib} --coverage`, {
silenceError: true,
});
expect(results).toContain(
`Running target test for project ${lib} failed`
);
expect(results).toContain(`ERROR: Coverage`);
expect(directoryExists(coverageDir)).toBeTruthy();
}, 100_000);

it('should not delete the project directory when coverage is enabled', async () => {
// when coverage is enabled in the vite.config.ts but reportsDirectory is removed
// from the @nx/vite:test executor options, vite will delete the project root directory
runCLI(`generate @nx/react:lib ${lib} --unitTestRunner=vitest`);
updateFile(`libs/${lib}/vite.config.ts`, () => {
return `import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
Expand Down Expand Up @@ -340,46 +415,51 @@ export default defineConfig({
},
});
`;
});
updateJson(join('libs', lib, 'project.json'), (config) => {
delete config.targets.test.options.reportsDirectory;
return config;
});
});
updateJson(join('libs', lib, 'project.json'), (config) => {
delete config.targets.test.options.reportsDirectory;
return config;
});

const projectRoot = `${tmpProjPath()}/libs/${lib}`;
const projectRoot = `${tmpProjPath()}/libs/${lib}`;

const results = runCLI(`test ${lib}`);
const results = runCLI(`test ${lib}`, {
env: {
CI: 'true', // prevent vitest from watching for file changes and making the process hang
},
});

expect(directoryExists(projectRoot)).toBeTruthy();
expect(results).toContain(
`Successfully ran target test for project ${lib}`
);
expect(results).toContain(`JUNIT report written`);
}, 100_000);
expect(directoryExists(projectRoot)).toBeTruthy();
expect(results).toContain(
`Successfully ran target test for project ${lib}`
);
expect(results).toContain(`JUNIT report written`);
}, 100_000);

it('should be able to run tests with inSourceTests set to true', async () => {
runCLI(
`generate @nx/react:lib ${lib} --unitTestRunner=vitest --inSourceTests`
);
expect(
exists(tmpProjPath(`libs/${lib}/src/lib/${lib}.spec.tsx`))
).toBeFalsy();
it('should be able to run tests with inSourceTests set to true', async () => {
runCLI(
`generate @nx/react:lib ${lib} --unitTestRunner=vitest --inSourceTests`
);
expect(
exists(tmpProjPath(`libs/${lib}/src/lib/${lib}.spec.tsx`))
).toBeFalsy();

updateFile(`libs/${lib}/src/lib/${lib}.tsx`, (content) => {
content += `
updateFile(`libs/${lib}/src/lib/${lib}.tsx`, (content) => {
content += `
if (import.meta.vitest) {
const { expect, it } = import.meta.vitest;
it('should be successful', () => {
expect(1 + 1).toBe(2);
});
}
`;
return content;
});
return content;
});

const result = await runCLIAsync(`test ${lib}`);
expect(result.combinedOutput).toContain(`1 passed`);
}, 100_000);
const result = await runCLIAsync(`test ${lib}`);
expect(result.combinedOutput).toContain(`1 passed`);
}, 100_000);
});
});

describe('ESM-only apps', () => {
Expand Down
10 changes: 6 additions & 4 deletions packages/vite/src/executors/test/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,14 @@ export async function getOptions(

const { parseCLI } = await loadVitestDynamicImport();

const normalizedExtraArgs = parseCLI([
'vitest',
...getOptionsAsArgv(options),
]);
const {
options: { watch, ...normalizedExtraArgs },
} = parseCLI(['vitest', ...getOptionsAsArgv(options)]);

const settings = {
// Explicitly set watch mode to false if not provided otherwise vitest
// will enable watch mode by default for non CI environments
watch: watch ?? false,
...normalizedExtraArgs,
// This should not be needed as it's going to be set in vite.config.ts
// but leaving it here in case someone did not migrate correctly
Expand Down
6 changes: 4 additions & 2 deletions packages/vite/src/executors/test/vitest.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export async function* vitestExecutor(
const resolvedOptions =
(await getOptions(options, context, projectRoot)) ?? {};

const nxReporter = new NxReporter(resolvedOptions['watch']);
const watch = resolvedOptions['watch'] === true;

const nxReporter = new NxReporter(watch);
if (resolvedOptions['reporters'] === undefined) {
resolvedOptions['reporters'] = [];
} else if (typeof resolvedOptions['reporters'] === 'string') {
Expand All @@ -49,7 +51,7 @@ export async function* vitestExecutor(
}
};

if (resolvedOptions['watch'] === true) {
if (watch) {
process.on('SIGINT', processExit);
process.on('SIGTERM', processExit);
process.on('exit', processExit);
Expand Down

0 comments on commit 637a004

Please sign in to comment.