Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Ability to configure cache folder #305

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion demo/__tests__/Translate.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render, screen, waitFor } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import Translate from '../components/Translate';
import preview from '../../dist/index';

Expand Down
2 changes: 1 addition & 1 deletion demo/components/Translate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function Translate(){
<p>Chinese: 你好</p>
<p>Thai: สวัสดี</p>
</div>
)
);
}

export default Translate;
5 changes: 5 additions & 0 deletions examples/nextjs-nx/apps/app/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {cacheFolder} from "jest-preview";

import {resolve, sep} from "path";

export const jestPreviewCacheFolder = resolve(`${__dirname + `${sep}..`.repeat(4)}${sep}${cacheFolder}`);
1 change: 1 addition & 0 deletions examples/nextjs-nx/apps/app/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const customConfig = {
'^.+\\.[tj]sx?$': ['babel-jest', {presets: ['@nrwl/next/babel']}],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
coverageDirectory: '../../coverage/apps/app',
};

Expand Down
7 changes: 7 additions & 0 deletions examples/nextjs-nx/apps/app/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { jestPreviewConfigure } from 'jest-preview';
import {jestPreviewCacheFolder} from "./constants";

jestPreviewConfigure({
autoPreview: true,
cacheFolder: jestPreviewCacheFolder,
});
16 changes: 11 additions & 5 deletions examples/nextjs-nx/apps/app/specs/index.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import React from 'react';
import { render } from '@testing-library/react';
import {render} from '@testing-library/react';

import Index from '../pages/index';
import preview from "jest-preview";
import {debug} from "./test-utils";

// if you want to debug even if the test does not fail, set this to true
const useDebug = false;

describe('Index', () => {
it('should render successfully', () => {
const { baseElement } = render(<Index />);
const {baseElement} = render(<Index/>);

preview.debug();
if (useDebug) {
debug();
}

expect(baseElement).toBeTruthy();
// change this to false and this will automatically update, thanks to autoPreview feature
expect(baseElement).toEqual(true);
});
});
6 changes: 6 additions & 0 deletions examples/nextjs-nx/apps/app/specs/test-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import preview from "jest-preview";
import {jestPreviewCacheFolder} from "../constants";

export const debug = () => {
preview.debug({cacheFolder: jestPreviewCacheFolder});
};
16 changes: 0 additions & 16 deletions index.html

This file was deleted.

2 changes: 1 addition & 1 deletion scripts/ecosystem-ci.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

const fs = require('fs');
const path = require('path');
const { spawn, spawnSync } = require('child_process');
const { spawnSync } = require('child_process');

// Make sure simulate CI environment when run in local
if (!process.env.CI) {
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/less/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path';
import { processLess } from '../../transform';
import less from 'less';

describe('Less', () => {
it('should compile LESS successfully', () => {
const result = processLess(path.resolve(__dirname, './style.less'));
Expand Down
2 changes: 1 addition & 1 deletion src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ program.parse(process.argv);

// Checks for available update and notify user
const notifier = updateNotifier({
// Built output is at /cli so the relative path is ../package.json
// Built output is at /cli so the relative path is ../package.json
pkg: require('../../package.json'),
updateCheckInterval: 0, // How often to check for updates
shouldNotifyInNpmScript: true, // Allows notification to be shown when running as an npm script
Expand Down
53 changes: 34 additions & 19 deletions src/configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from 'path';
import fs from 'fs';
import chalk from 'chalk';
import { CACHE_FOLDER, SASS_LOAD_PATHS_CONFIG } from './constants';
import { createCacheFolderIfNeeded } from './utils';
import { createCacheFolder } from './utils';
import { debug } from './preview';

interface JestPreviewConfigOptions {
Expand All @@ -13,6 +13,7 @@ interface JestPreviewConfigOptions {
autoPreview?: boolean;
publicFolder?: string;
sassLoadPaths?: string[];
cacheFolder?: string;
}

export function jestPreviewConfigure(
Expand All @@ -21,20 +22,17 @@ export function jestPreviewConfigure(
autoPreview = false,
publicFolder,
sassLoadPaths,
cacheFolder = CACHE_FOLDER,
}: JestPreviewConfigOptions = {
autoPreview: false,
sassLoadPaths: [],
},
) {
if (autoPreview) {
autoRunPreview();
autoRunPreview({ cacheFolder });
}

if (!fs.existsSync(CACHE_FOLDER)) {
fs.mkdirSync(CACHE_FOLDER, {
recursive: true,
});
}
createCacheFolder(cacheFolder);

let sassLoadPathsConfig: string[] = [];
// Save sassLoadPathsConfig to cache, so we can use it in the transformer
Expand All @@ -43,10 +41,10 @@ export function jestPreviewConfigure(
(path) => `${process.cwd()}/${path}`,
);

createCacheFolderIfNeeded();
createCacheFolder(cacheFolder);

fs.writeFileSync(
path.join(CACHE_FOLDER, SASS_LOAD_PATHS_CONFIG),
path.join(cacheFolder, SASS_LOAD_PATHS_CONFIG),
JSON.stringify(sassLoadPathsConfig),
);
}
Expand All @@ -62,9 +60,9 @@ export function jestPreviewConfigure(
});

if (publicFolder) {
createCacheFolderIfNeeded();
createCacheFolder(cacheFolder);
fs.writeFileSync(
path.join(CACHE_FOLDER, 'cache-public.config'),
path.join(cacheFolder, 'cache-public.config'),
publicFolder,
{
encoding: 'utf-8',
Expand All @@ -77,7 +75,14 @@ export function jestPreviewConfigure(
// Omit only, skip, todo, concurrent, each. Couldn't use Omit. Just redeclare for simplicity
type RawIt = (...args: Parameters<jest.It>) => ReturnType<jest.It>;

function patchJestFunction(it: RawIt) {
interface PatchJestFunctionOptions {
cacheFolder?: string;
}

function patchJestFunction(
it: RawIt,
{ cacheFolder }: PatchJestFunctionOptions = {},
) {
const originalIt = it;
const itWithPreview: RawIt = (name, callback, timeout) => {
let callbackWithPreview: jest.ProvidesCallback | undefined;
Expand All @@ -89,9 +94,10 @@ function patchJestFunction(it: RawIt) {
) {
try {
// @ts-expect-error Just forward the args
// eslint-disable-next-line n/no-callback-literal
return await callback(...args);
} catch (error) {
debug();
debug({ cacheFolder });
throw error;
}
};
Expand All @@ -101,20 +107,29 @@ function patchJestFunction(it: RawIt) {
return itWithPreview;
}

function autoRunPreview() {
interface AutoRunPreviewOptions {
cacheFolder?: string;
}

function autoRunPreview({ cacheFolder }: AutoRunPreviewOptions = {}) {
const originalIt = it;
let itWithPreview = patchJestFunction(it) as jest.It;
const itWithPreview = patchJestFunction(it, { cacheFolder }) as jest.It;
itWithPreview.each = originalIt.each;
itWithPreview.only = patchJestFunction(originalIt.only) as jest.It;
itWithPreview.only = patchJestFunction(originalIt.only, {
cacheFolder,
}) as jest.It;
itWithPreview.skip = originalIt.skip;
itWithPreview.todo = originalIt.todo;
itWithPreview.concurrent = patchJestFunction(
originalIt.concurrent,
) as jest.It;
itWithPreview.concurrent = patchJestFunction(originalIt.concurrent, {
cacheFolder,
}) as jest.It;

// Overwrite global it/ test
// Is there any use cases that `it` and `test` is undefined?
// eslint-disable-next-line no-global-assign
it = itWithPreview;
// eslint-disable-next-line no-global-assign
test = itWithPreview;
// eslint-disable-next-line no-global-assign
fit = itWithPreview.only;
}
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { processFile, processFileCRA, processCss } from './transform';
import { debug } from './preview';
import { jestPreviewConfigure } from './configure';
import { configureNextJestPreview } from './next';
import { CACHE_FOLDER } from './constants';

export const cacheFolder = CACHE_FOLDER;

export {
jestPreviewConfigure,
Expand Down
15 changes: 8 additions & 7 deletions src/preview.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import fs from 'fs';
import path from 'path';
import { CACHE_FOLDER } from './constants';
import { createCacheFolder } from './utils';

export function debug(): void {
if (!fs.existsSync(CACHE_FOLDER)) {
fs.mkdirSync(CACHE_FOLDER, {
recursive: true,
});
}
interface DebugOptions {
cacheFolder?: string;
}

export function debug({ cacheFolder = CACHE_FOLDER }: DebugOptions = {}): void {
createCacheFolder(cacheFolder);

fs.writeFileSync(
path.join(CACHE_FOLDER, 'index.html'),
path.join(cacheFolder, 'index.html'),
document.documentElement.outerHTML,
);
}
6 changes: 3 additions & 3 deletions src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import camelcase from 'camelcase';
import slash from 'slash';
import { transform } from '@svgr/core';
import { CACHE_FOLDER, SASS_LOAD_PATHS_CONFIG } from './constants';
import { createCacheFolderIfNeeded } from './utils';
import { createCacheFolder } from './utils';

// https://github.com/vitejs/vite/blob/c29613013ca1c6d9c77b97e2253ed1f07e40a544/packages/vite/src/node/plugins/css.ts#L97-L98
const cssLangs = `\\.(css|less|sass|scss|styl|stylus|pcss|postcss)($|\\?)`;
Expand Down Expand Up @@ -234,7 +234,7 @@ export function processCss(src: string, filename: string): TransformedSource {
// As can be seen, only process or processAsync is mandatory to implement)

// Convert from
//cssModulesExportedTokens||| {"scssModule":"_scssModule_ujx1w_1"}
// cssModulesExportedTokens||| {"scssModule":"_scssModule_ujx1w_1"}
// ---
// css||| ._scssModule_ujx1w_1 {
// color: #ff0000;
Expand All @@ -261,7 +261,7 @@ function parsePostCssExternalOutput(output: string) {
}

function createTempFile(content: string) {
createCacheFolderIfNeeded();
createCacheFolder();
const tempFileName = path.join(
CACHE_FOLDER,
crypto.randomBytes(16).toString('hex'),
Expand Down
7 changes: 3 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import fs from 'fs';
import { CACHE_FOLDER } from './constants';

// Create cache folder if it doesn't exist
export function createCacheFolderIfNeeded() {
if (!fs.existsSync(CACHE_FOLDER)) {
fs.mkdirSync(CACHE_FOLDER, {
export function createCacheFolder(cacheFolder = CACHE_FOLDER) {
if (!fs.existsSync(cacheFolder)) {
fs.mkdirSync(cacheFolder, {
recursive: true,
});
}
Expand Down
77 changes: 77 additions & 0 deletions website/docs/advanced-guides/configure-cache-folder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Customize Cache Folder Location

Sometimes you might want to run single tests right from within your IDE. Moreover, if you are using a monorepo, you
might find yourself with multiple Jest projects with multiple configurations.

## Case #1 Auto-Preview Monorepo setup with single test run from your IDE

In the case of WebStorm/IntelliJ, when you run a single test, it will create an automatic run configuration with the
working directory being to the closest `jest.config.ts` file it can find.

In the case where you want `jest-preview` to always use the same cache folder, you can do the following:

Let's take an example where you have the following monorepo structure:

- `packages/`
- `package-a/`
- `jest.config.ts`
- `jest.setup.ts`
- `package-b/`
- `jest.config.ts`
- `jest.setup.ts`

You can configure, at the package level, or even at the root level (in the case of Nx monorepo) the following auto
preview setup:

```typescript
// package-a/jest.setup.ts

import {cacheFolder} from "jest-preview";

import {resolve, sep} from "path";

// basically, we are targeting {projectRoot}/node_modules/.cache/jest-preview

export const jestPreviewCacheFolder = resolve(`${__dirname + `${sep}..`.repeat(2)}${sep}${cacheFolder}`);

jestPreviewConfigure({
autoPreview: true,
cacheFolder: jestPreviewCacheFolder,
});
```

Do not forget to add `setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],` to your `jest.config.ts` file.

## Case #2 Specify cache folder when using the `debug` function

```typescript
// test-utils.ts

import preview from "jest-preview";

const jestPreviewCacheFolder = resolve(`${__dirname + `${sep}..`.repeat(2)}${sep}${cacheFolder}`);

export const debug = () => {
preview.debug({cacheFolder: jestPreviewCacheFolder});
};
```

In your spec file:

```tsx
import React from 'react';
import {render} from '@testing-library/react';

import Index from '../pages/index';
import {debug} from "./test-utils";

describe('Index', () => {
it('should render successfully', () => {
const {baseElement} = render(<Index/>);

debug();

expect(baseElement).toEqual(true);
});
});
```