Skip to content

Commit

Permalink
test(testing-utils): setup testing utils project
Browse files Browse the repository at this point in the history
  • Loading branch information
BioPhoton committed Jan 11, 2025
1 parent 2928bda commit b1dd48e
Show file tree
Hide file tree
Showing 14 changed files with 9,241 additions and 12,297 deletions.
6 changes: 3 additions & 3 deletions nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@
{
"plugin": "@nx/vite/plugin",
"options": {
"testTargetName": "integration"
"testTargetName": "unit-test"
},
"include": ["integration-testing/**"]
"include": ["packages/**", "testing/**"]
},
{
"plugin": "@nx/playwright/plugin",
Expand All @@ -96,7 +96,7 @@
},
{
"plugin": "@nx/js/typescript",
"include": ["packages/**"],
"include": ["packages/**", "testing/*"],
"options": {
"typecheck": {
"targetName": "typecheck"
Expand Down
21,132 changes: 8,838 additions & 12,294 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions testing/utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# testing-utils

This library provides a set of helper functions for testing.

The many content divides into:

- constants
- mock helper
- utils for OS dependent APIs

## Building

Run `nx build testing-utils` to build the library.

## Running unit tests

Run `nx test testing-utils` to execute the unit tests via [Vitest](https://vitest.dev/).
22 changes: 22 additions & 0 deletions testing/utils/eslint.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const baseConfig = require('../../eslint.config.js');

module.exports = [
...baseConfig,
{
files: ['**/*.json'],
rules: {
'@nx/dependency-checks': [
'error',
{
ignoredFiles: [
'{projectRoot}/eslint.config.{js,cjs,mjs}',
'{projectRoot}/vite.config.{js,ts,mjs,mts}',
],
},
],
},
languageOptions: {
parser: require('jsonc-eslint-parser'),
},
},
];
25 changes: 25 additions & 0 deletions testing/utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "@ng-rspack/testing-utils",
"version": "0.0.1",
"dependencies": {
"ts-morph": "^24.0.0"
},
"type": "module",
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"private": true,
"nx": {
"sourceRoot": "testing/utils/src",
"projectType": "library",
"name": "testing-utils"
},
"exports": {
"./package.json": "./package.json",
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"types": "./dist/index.d.ts",
"module": "./dist/index.js"
}
2 changes: 2 additions & 0 deletions testing/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './lib/source-file-from.code.js';
export * from './lib/os-agnostic-paths.js';
95 changes: 95 additions & 0 deletions testing/utils/src/lib/os-agnostic-paths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
const AGNOSTIC_PATH_SEP_REGEX = /[/\\]/g;
const OS_AGNOSTIC_PATH_SEP = '/';
const OS_AGNOSTIC_CWD = `<CWD>`;

/**
* Converts a given file path to an OS-agnostic path by replacing the current working directory with '<CWD>'
* and normalizing path separators to '/'.
*
* @param filePath - The file path to be converted.
* @param separator - The path separator to use for normalization. Defaults to the OS-specific separator.
* @returns The OS-agnostic path.
*
* @example
*
* At CWD on Ubuntu (Linux)
* Input: /home/projects/my-folder/my-file.ts
* Output: <CWD>/my-folder/my-file.ts
*
* At CWD on Windows
* Input: D:\projects\my-folder\my-file.ts
* Output: <CWD>/my-folder/my-file.ts
*
* At CWD on macOS
* Input: /Users/projects/my-folder/my-file.ts
* Output: <CWD>/my-folder/my-file.ts
*
* Out of CWD on all OS
* Input: /Users/projects/../my-folder/my-file.ts
* Output: ../my-folder/my-file.ts
*
* Absolute paths (all OS)
* Input: \\my-folder\\my-file.ts
* Output: /my-folder/my-file.ts
*
* Relative paths (all OS)
* Input: ..\\my-folder\\my-file.ts
* Output: ../my-folder/my-file.ts
*
*/

export function osAgnosticPath(filePath: string): string;
export function osAgnosticPath(): undefined;
export function osAgnosticPath(filePath?: string): string | undefined {
if (filePath == null) {
return filePath;
}
// prepare the path for comparison
// normalize path separators od cwd: "Users\\repo" => "Users/repo"
const osAgnosticCwd = process
.cwd()
.split(AGNOSTIC_PATH_SEP_REGEX)
.join(OS_AGNOSTIC_PATH_SEP);
// normalize path separators => "..\\folder\\repo.ts" => => "../folder/repo.ts"
const osAgnosticFilePath = filePath
.split(AGNOSTIC_PATH_SEP_REGEX)
.join(OS_AGNOSTIC_PATH_SEP);
// remove the current working directory for easier comparison
const osAgnosticPathWithoutCwd = osAgnosticFilePath
.replace(osAgnosticCwd, '')
// consider already agnostic paths
.replace(OS_AGNOSTIC_CWD, '');

// path is outside cwd (Users/repo/../my-folder/my-file.ts)
if (
osAgnosticPathWithoutCwd.startsWith(
`${OS_AGNOSTIC_PATH_SEP}..${OS_AGNOSTIC_PATH_SEP}`
)
) {
return osAgnosticPathWithoutCwd.slice(1); // remove the leading '/'
}

// path is at cwd (Users/repo/my-folder/my-file.ts)
if (
osAgnosticFilePath.startsWith(osAgnosticCwd) ||
osAgnosticFilePath.startsWith(OS_AGNOSTIC_CWD)
) {
// Add a substitute for the current working directory
return `${OS_AGNOSTIC_CWD}${osAgnosticPathWithoutCwd}`;
}

// Notice: I kept the following conditions for documentation purposes

// path is absolute (/my-folder/my-file.ts)
if (osAgnosticPathWithoutCwd.startsWith(OS_AGNOSTIC_PATH_SEP)) {
return osAgnosticPathWithoutCwd;
}

// path is relative (./my-folder/my-file.ts)
if (osAgnosticPathWithoutCwd.startsWith(`.${OS_AGNOSTIC_PATH_SEP}`)) {
return osAgnosticPathWithoutCwd;
}

// path is segment (my-folder/my-file.ts or my-folder/sub-folder)
return osAgnosticPathWithoutCwd;
}
122 changes: 122 additions & 0 deletions testing/utils/src/lib/os-agnostic-paths.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {
type MockInstance,
afterEach,
beforeEach,
describe,
expect,
it,
vi,
} from 'vitest';
import { osAgnosticPath } from './os-agnostic-paths.js';

describe('osAgnosticPath', () => {
const cwdSpy: MockInstance<[], string> = vi.spyOn(process, 'cwd');

it('should forward nullish paths on Linux/macOS and Windows', () => {
expect(osAgnosticPath(undefined)).toBeUndefined();
});

describe('Unix-based systems (Linux/macOS)', () => {
const unixCwd = '/Users/jerry';

beforeEach(() => {
cwdSpy.mockReturnValue(unixCwd);
});

afterEach(() => {
cwdSpy.mockReset();
});

it('should convert a path within the CWD to an OS-agnostic path on Linux/macOS', () => {
expect(
osAgnosticPath(`${unixCwd}/.code-pushup/.code-pushup.config.ts`),
).toBe('<CWD>/.code-pushup/.code-pushup.config.ts');
});

it('should return paths outside of CWD on Linux/macOS', () => {
expect(
osAgnosticPath(`${unixCwd}/../.code-pushup/.code-pushup.config.ts`),
).toBe('../.code-pushup/.code-pushup.config.ts');
});

it('should handle absolute paths correctly on Linux/macOS', () => {
expect(osAgnosticPath('/.code-pushup/.code-pushup.config.ts')).toBe(
'/.code-pushup/.code-pushup.config.ts',
);
});

it('should handle paths with CWD shorthand "." correctly on Linux/macOS', () => {
expect(osAgnosticPath('./.code-pushup/.code-pushup.config.ts')).toBe(
'./.code-pushup/.code-pushup.config.ts',
);
});

it('should handle relative paths correctly on Linux/macOS', () => {
expect(osAgnosticPath('../../.code-pushup/.code-pushup.config.ts')).toBe(
'../../.code-pushup/.code-pushup.config.ts',
);
});

it('should handle path segments correctly on Linux/macOS', () => {
expect(osAgnosticPath('.code-pushup/.code-pushup.config.ts')).toBe(
'.code-pushup/.code-pushup.config.ts',
);
});

it('should NOT modify already OS-agnostic paths on Linux/macOS', () => {
expect(osAgnosticPath('<CWD>/.code-pushup/.code-pushup.config.ts')).toBe(
'<CWD>/.code-pushup/.code-pushup.config.ts',
);
});
});

describe('Windows', () => {
const windowsCWD = String.raw`D:\users\jerry`;

beforeEach(() => {
cwdSpy.mockReturnValue(windowsCWD);
});

afterEach(() => {
cwdSpy.mockReset();
});

it('should return paths outside of CWD on Windows', () => {
expect(
osAgnosticPath(
`${windowsCWD}\\..\\.code-pushup\\.code-pushup.config.ts`,
),
).toBe('../.code-pushup/.code-pushup.config.ts');
});

it('should convert a path within the CWD to an OS-agnostic path on Windows', () => {
expect(
osAgnosticPath(`${windowsCWD}\\.code-pushup\\.code-pushup.config.ts`),
).toBe('<CWD>/.code-pushup/.code-pushup.config.ts');
});

it('should handle absolute paths correctly on Windows', () => {
expect(
osAgnosticPath(String.raw`\.code-pushup\.code-pushup.config.ts`),
).toBe('/.code-pushup/.code-pushup.config.ts');
});

it('should handle paths with CWD shorthand "." correctly on Windows', () => {
expect(
osAgnosticPath(String.raw`.\.code-pushup\.code-pushup.config.ts`),
).toBe('./.code-pushup/.code-pushup.config.ts');
});

it('should handle relative paths correctly on Windows', () => {
expect(
osAgnosticPath(String.raw`..\..\.code-pushup\.code-pushup.config.ts`),
).toBe('../../.code-pushup/.code-pushup.config.ts');
});

it('should handle path segments correctly on Windows', () => {
expect(
osAgnosticPath(String.raw`.code-pushup\.code-pushup.config.ts`),
).toBe('.code-pushup/.code-pushup.config.ts');
});
});
});
12 changes: 12 additions & 0 deletions testing/utils/src/lib/source-file-from.code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Project } from 'ts-morph';

export const sourceFileFromCode = ({
path,
code,
}: {
path?: string;
code: string;
}) => {
const project = new Project({ useInMemoryFileSystem: true });
return project.createSourceFile(path ?? 'cmp.ts', code);
};
13 changes: 13 additions & 0 deletions testing/utils/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
27 changes: 27 additions & 0 deletions testing/utils/tsconfig.lib.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"rootDir": "src",
"outDir": "dist",
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
"emitDeclarationOnly": false,
"types": ["node", "vite/client"]
},
"include": ["src/**/*.ts"],
"references": [],
"exclude": [
"vite.config.ts",
"vite.config.mts",
"vitest.config.ts",
"vitest.config.mts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.test.tsx",
"src/**/*.spec.tsx",
"src/**/*.test.js",
"src/**/*.spec.js",
"src/**/*.test.jsx",
"src/**/*.spec.jsx"
]
}
8 changes: 8 additions & 0 deletions testing/utils/tsconfig.spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"]
},
"include": ["vite.config.ts", "src/**/*.unit.test.ts"]
}
Loading

0 comments on commit b1dd48e

Please sign in to comment.