Skip to content
Merged
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: 2 additions & 0 deletions packages/rstack/rstack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ define.test(async () => {
process.env.NO_COLOR = '1';

return {
// Temporary projects may contain files that match Rstest's test glob.
exclude: ['**/test-temp-*/**'],
extends: withRslibConfig(),
source: {
tsconfigPath: './tests/tsconfig.json',
Expand Down
7 changes: 4 additions & 3 deletions packages/rstack/tests/cli/fmt/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import { RSTACK_BIN_PATH } from '#test-helpers';

let projectPath: string;

const writeProjectFile = (filePath: string, content: string): string => {
const writeProjectFile = (filePath: string, content: string): void => {
const absolutePath = path.join(projectPath, filePath);
mkdirSync(path.dirname(absolutePath), { recursive: true });
writeFileSync(absolutePath, content);
return absolutePath;
};

const readProjectFile = (filePath: string): string =>
Expand All @@ -30,7 +29,9 @@ const runCLI = (args: string[]) => {
const runFmt = (args: string[] = []) => runCLI(['fmt', ...args]);

beforeEach(() => {
projectPath = mkdtempSync(path.join(import.meta.dirname, 'fmt-project-'));
projectPath = mkdtempSync(path.join(import.meta.dirname, 'test-temp-fmt-'));
// Prevent repository-level ignore rules from affecting the fixture.
mkdirSync(path.join(projectPath, '.git'));
writeProjectFile('rstack.config.ts', 'export {};\n');
});

Expand Down
9 changes: 6 additions & 3 deletions packages/rstack/tests/cli/setup/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { spawnSync } from 'node:child_process';
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { afterEach, beforeEach } from 'rstack/test';
import { RSTACK_BIN_PATH, test } from '#test-helpers';
Expand Down Expand Up @@ -30,9 +29,11 @@ const runSetup = (args: string[], runCwd: string = cwd) =>
});

beforeEach(() => {
cwd = mkdtempSync(path.join(tmpdir(), 'rstack setup '));
cwd = mkdtempSync(path.join(import.meta.dirname, 'test-temp-rstack setup '));
env = {
...process.env,
// Keep Git from treating the fixture as part of this repository.
GIT_CEILING_DIRECTORIES: import.meta.dirname,
GIT_CONFIG_GLOBAL: path.join(cwd, 'global.gitconfig'),
GIT_CONFIG_NOSYSTEM: '1',
};
Expand Down Expand Up @@ -106,7 +107,9 @@ test('installs a custom hooks directory from a nested project', ({ execCli, expe
});

test('skips non-Git directories without creating files', ({ execCli, expect }) => {
expect(execCli('setup', { cwd })).toContain('Git hooks setup skipped: not a Git repository.');
expect(execCli('setup', { cwd, env })).toContain(
'Git hooks setup skipped: not a Git repository.',
);
expect(existsSync(path.join(cwd, '.rstack'))).toBe(false);
});

Expand Down
2 changes: 1 addition & 1 deletion packages/rstack/tests/cli/staged/fmt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const runStaged = () =>
});

beforeEach(() => {
projectPath = mkdtempSync(path.join(import.meta.dirname, 'staged-fmt-project-'));
projectPath = mkdtempSync(path.join(import.meta.dirname, 'test-temp-staged-fmt-'));
env = {
...process.env,
GIT_CONFIG_GLOBAL: path.join(projectPath, 'global.gitconfig'),
Expand Down
4 changes: 1 addition & 3 deletions packages/rstack/tests/config/reload-app-config/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { rm, writeFile } from 'node:fs/promises';
import { writeFile } from 'node:fs/promises';
import path from 'node:path';
import { getRandomPort, waitForFile } from '@rstackjs/test-utils';
import { test } from '#test-helpers';
Expand All @@ -11,8 +11,6 @@ test('should restart dev server and reload config when Rstack config changes', a
const dist2 = await prepareDist('dist-2');
const configFile = path.join(import.meta.dirname, 'test-temp-rstack.config.ts');

await rm(configFile, { force: true });

await writeFile(
configFile,
`import { define } from 'rstack';
Expand Down
1 change: 0 additions & 1 deletion packages/rstack/tests/exports/lint-subpath/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ test('should expose lint APIs from `rstack/lint`', async () => {

for (const method of commonLintMethods) {
expect(lint).toHaveProperty(method);
expect(typeof lint[method]).toBeTruthy();
}
});
1 change: 0 additions & 1 deletion packages/rstack/tests/exports/test-subpath/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ test('should expose test APIs from `rstack/test`', async () => {

for (const method of commonTestMethods) {
expect(test).toHaveProperty(method);
expect(typeof test[method]).toBeTruthy();
}
});
33 changes: 7 additions & 26 deletions packages/rstack/tests/fmt/discoverPaths.test.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,14 @@
import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { symlinkSync } from 'node:fs';
import path from 'node:path';
import { expect, test } from 'rstack/test';
import { discoverFmtPaths } from '../../src/fmt/discoverPaths.ts';

const withProject = async (callback: (rootPath: string) => Promise<void>): Promise<void> => {
const rootPath = mkdtempSync(path.join(tmpdir(), 'rstack fmt '));

try {
await callback(rootPath);
} finally {
rmSync(rootPath, { force: true, recursive: true });
}
};

const writeProjectFile = (rootPath: string, filePath: string, content = ''): string => {
const absolutePath = path.join(rootPath, filePath);
mkdirSync(path.dirname(absolutePath), { recursive: true });
writeFileSync(absolutePath, content);
return absolutePath;
};
import { withTempProject, writeProjectFile } from './helpers.ts';

const relativePaths = (rootPath: string, files: string[]): string[] =>
files.map((filePath) => path.relative(rootPath, filePath));

test('discovers non-binary files in stable order and skips hard-ignored paths', async () => {
await withProject(async (rootPath) => {
await withTempProject(async (rootPath) => {
writeProjectFile(rootPath, 'b.ts');
writeProjectFile(rootPath, 'a.js');
writeProjectFile(rootPath, 'folder with spaces/c.ts');
Expand All @@ -50,7 +33,7 @@ test('discovers non-binary files in stable order and skips hard-ignored paths',
});

test('combines files, directories, and globs without duplicates', async () => {
await withProject(async (rootPath) => {
await withTempProject(async (rootPath) => {
const firstFilePath = writeProjectFile(rootPath, 'src/a.ts');
writeProjectFile(rootPath, 'src/b.js');
writeProjectFile(rootPath, 'test/c.ts');
Expand Down Expand Up @@ -90,8 +73,7 @@ test('combines files, directories, and globs without duplicates', async () => {
});

test('applies nested gitignore rules with child negation', async () => {
await withProject(async (rootPath) => {
mkdirSync(path.join(rootPath, '.git'));
await withTempProject(async (rootPath) => {
writeProjectFile(rootPath, '.gitignore', '*.js\ndist/\n');
writeProjectFile(rootPath, 'src/.gitignore', '!keep.js\n');
writeProjectFile(rootPath, 'dist/.gitignore', '!keep.js\n');
Expand All @@ -114,8 +96,7 @@ test('applies nested gitignore rules with child negation', async () => {
});

test('lets explicit files bypass gitignore', async () => {
await withProject(async (rootPath) => {
mkdirSync(path.join(rootPath, '.git'));
await withTempProject(async (rootPath) => {
writeProjectFile(rootPath, '.gitignore', '/generated/\n');
const keepPath = writeProjectFile(rootPath, 'generated/keep.ts');
writeProjectFile(rootPath, 'src/index.ts');
Expand All @@ -132,7 +113,7 @@ test('lets explicit files bypass gitignore', async () => {
});

test.runIf(process.platform !== 'win32')('does not follow file or directory symlinks', async () => {
await withProject(async (rootPath) => {
await withTempProject(async (rootPath) => {
const targetPath = writeProjectFile(rootPath, 'target/index.ts');
symlinkSync(path.join(rootPath, 'target'), path.join(rootPath, 'linked-directory'));
symlinkSync(targetPath, path.join(rootPath, 'linked-file.ts'));
Expand Down
27 changes: 5 additions & 22 deletions packages/rstack/tests/fmt/discovery.test.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,10 @@
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { mkdirSync } from 'node:fs';
import path from 'node:path';
import { expect, test } from 'rstack/test';
import { normalizeFmtConfig } from '../../src/fmt/config.ts';
import { discoverFmtFiles } from '../../src/fmt/discovery.ts';
import type { FmtConfig } from '../../src/fmt/types.ts';

const withProject = async (callback: (rootPath: string) => Promise<void>): Promise<void> => {
const rootPath = mkdtempSync(path.join(tmpdir(), 'rstack fmt '));

try {
await callback(rootPath);
} finally {
rmSync(rootPath, { force: true, recursive: true });
}
};

const writeProjectFile = (rootPath: string, filePath: string, content = ''): string => {
const absolutePath = path.join(rootPath, filePath);
mkdirSync(path.dirname(absolutePath), { recursive: true });
writeFileSync(absolutePath, content);
return absolutePath;
};
import { withTempProject, writeProjectFile } from './helpers.ts';

const discover = async (cwd: string, patterns?: string[], config?: FmtConfig, configRoot = cwd) =>
discoverFmtFiles({
Expand All @@ -34,7 +17,7 @@ const relativePaths = (rootPath: string, files: Awaited<ReturnType<typeof discov
files.map((file) => path.relative(rootPath, file.path));

test('applies config ignore patterns to discovered and explicit files', async () => {
await withProject(async (rootPath) => {
await withTempProject(async (rootPath) => {
const keepPath = writeProjectFile(rootPath, 'generated/keep.ts');
const blockedPath = writeProjectFile(rootPath, 'generated/blocked.ts');
writeProjectFile(rootPath, 'src/index.ts');
Expand All @@ -52,7 +35,7 @@ test('applies config ignore patterns to discovered and explicit files', async ()
});

test('applies config ignore patterns outside the config root', async () => {
await withProject(async (rootPath) => {
await withTempProject(async (rootPath) => {
const configRoot = path.join(rootPath, 'project');
const filePath = writeProjectFile(rootPath, 'shared/index.ts');
mkdirSync(configRoot);
Expand All @@ -64,7 +47,7 @@ test('applies config ignore patterns outside the config root', async () => {
});

test('resolves parsers and accepts unknown extensions with an explicit parser', async () => {
await withProject(async (rootPath) => {
await withTempProject(async (rootPath) => {
writeProjectFile(rootPath, 'index.ts');
writeProjectFile(rootPath, 'source.custom');
writeProjectFile(rootPath, 'unknown.extension');
Expand Down
23 changes: 23 additions & 0 deletions packages/rstack/tests/fmt/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import path from 'node:path';

export const withTempProject = async (
callback: (rootPath: string) => Promise<void>,
): Promise<void> => {
const rootPath = mkdtempSync(path.join(import.meta.dirname, 'test-temp-fmt-'));
// Prevent repository-level ignore rules from affecting the fixture.
mkdirSync(path.join(rootPath, '.git'));

try {
await callback(rootPath);
} finally {
rmSync(rootPath, { force: true, recursive: true });
}
};

export const writeProjectFile = (rootPath: string, filePath: string, content = ''): string => {
const absolutePath = path.join(rootPath, filePath);
mkdirSync(path.dirname(absolutePath), { recursive: true });
writeFileSync(absolutePath, content);
return absolutePath;
};
32 changes: 7 additions & 25 deletions packages/rstack/tests/fmt/runner.test.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,9 @@
import {
chmodSync,
mkdtempSync,
readFileSync,
rmSync,
statSync,
utimesSync,
writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { chmodSync, readFileSync, statSync, utimesSync, writeFileSync } from 'node:fs';
import path from 'node:path';
import { expect, test } from 'rstack/test';
import { runFmtFiles } from '../../src/fmt/runner.ts';
import type { FmtFileRequest, FmtMode } from '../../src/fmt/types.ts';

const withProject = async (callback: (rootPath: string) => Promise<void>): Promise<void> => {
const rootPath = mkdtempSync(path.join(tmpdir(), 'rstack fmt runner '));

try {
await callback(rootPath);
} finally {
rmSync(rootPath, { force: true, recursive: true });
}
};
import { withTempProject } from './helpers.ts';

const createRequest = (filePath: string): FmtFileRequest => ({
path: filePath,
Expand All @@ -40,7 +22,7 @@ const run = (files: FmtFileRequest[], mode: FmtMode = 'write') =>
});

test('does not rewrite unchanged files', async () => {
await withProject(async (rootPath) => {
await withTempProject(async (rootPath) => {
const filePath = path.join(rootPath, 'unchanged.ts');
const timestamp = new Date('2020-01-01T00:00:00.000Z');
writeFileSync(filePath, 'const value = 1;\n');
Expand All @@ -60,7 +42,7 @@ test('does not rewrite unchanged files', async () => {
});

test('writes changed files', async () => {
await withProject(async (rootPath) => {
await withTempProject(async (rootPath) => {
const filePath = path.join(rootPath, 'changed.ts');
writeFileSync(filePath, 'const value=1');

Expand All @@ -75,7 +57,7 @@ test('writes changed files', async () => {
});

test.runIf(process.platform !== 'win32')('preserves file mode when writing', async () => {
await withProject(async (rootPath) => {
await withTempProject(async (rootPath) => {
const filePath = path.join(rootPath, 'executable.ts');
writeFileSync(filePath, 'const value=1');
chmodSync(filePath, 0o744);
Expand All @@ -88,7 +70,7 @@ test.runIf(process.platform !== 'win32')('preserves file mode when writing', asy

for (const mode of ['check', 'list-different'] as const) {
test(`${mode} reports differences without writing`, async () => {
await withProject(async (rootPath) => {
await withTempProject(async (rootPath) => {
const filePath = path.join(rootPath, 'different.ts');
const source = 'const value=1';
writeFileSync(filePath, source);
Expand All @@ -105,7 +87,7 @@ for (const mode of ['check', 'list-different'] as const) {
}

test('continues after a file fails and gives errors exit-code precedence', async () => {
await withProject(async (rootPath) => {
await withTempProject(async (rootPath) => {
const invalidPath = path.join(rootPath, 'invalid.ts');
const validPath = path.join(rootPath, 'valid.ts');
writeFileSync(invalidPath, 'const value = ;');
Expand Down
12 changes: 5 additions & 7 deletions packages/rstack/tests/helpers/cliTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ export type CliTestFixtures = {
type CliTest = ReturnType<typeof baseTest.extend<CliTestFixtures>>;

function makeBox(title: string) {
const header = `-------- Logs from: "${title}" --------`;
const footer = `-------- Logs from: "${title}" --------`;
const border = `-------- Logs from: "${title}" --------`;
return {
header: `\n${header}\n`,
footer: `${footer}\n`,
header: `\n${border}\n`,
footer: `${border}\n`,
};
}

Expand Down Expand Up @@ -114,9 +113,8 @@ export const test: CliTest = baseTest.extend<CliTestFixtures>({
}
},
execCliAsync: async ({ exec }, use) => {
const execCliAsync: Exec = (command, options = {}) => {
return exec(`node "${RSTACK_BIN_PATH}" ${command}`, options);
};
const execCliAsync: Exec = (command, options = {}) =>
exec(`node "${RSTACK_BIN_PATH}" ${command}`, options);
await use(execCliAsync);
},
});
8 changes: 6 additions & 2 deletions packages/rstack/tests/setup/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { type SpawnSyncReturns, spawnSync } from 'node:child_process';
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';

const hooksDir = '.rstack/hooks';
Expand All @@ -22,10 +21,15 @@ export const runGit = (cwd: string, args: string[]): string => {
};

export const withDirectory = (callback: (cwd: string) => void): void => {
const cwd = mkdtempSync(path.join(tmpdir(), 'rstack hooks '));
const cwd = mkdtempSync(path.join(import.meta.dirname, 'test-temp-rstack hooks '));
const gitCeilingDirectories = process.env.GIT_CEILING_DIRECTORIES;
// Keep Git from treating the temporary directory as part of this repository.
process.env.GIT_CEILING_DIRECTORIES = import.meta.dirname;

try {
callback(cwd);
} finally {
restoreEnv('GIT_CEILING_DIRECTORIES', gitCeilingDirectories);
Comment thread
chenjiahan marked this conversation as resolved.
rmSync(cwd, { force: true, recursive: true });
}
};
Expand Down
Loading