Compare commits

...

3 Commits

Author SHA1 Message Date
ByteYue 5257bda4d0 test: fix remaining Windows path issues in test files
- engine.test.ts: use pathToFileURL().href for dynamic import paths
  (path.join produces backslashes on Windows, breaking ES module imports)
- download.test.ts: replace hardcoded '/tmp' with os.tmpdir() + path.join
2026-03-25 10:40:55 +08:00
ByteYue cd338100bb test: replace hardcoded /tmp with os.tmpdir() for Windows compatibility
Fix Windows CI failures caused by hardcoded '/tmp' paths that don't
exist on Windows. Use os.tmpdir() which returns the correct platform-
specific temp directory on all operating systems.

Files fixed:
- src/engine.test.ts: 3 occurrences (mkdtemp, discoverClis path)
- src/plugin.test.ts: 2 occurrences (getCommitHash test, mock condition)
2026-03-25 10:37:51 +08:00
ByteYue 149f6f65d2 ci: add cross-platform matrix (Linux/macOS/Windows) to build, unit-test, adapter-test
Add OS matrix with ubuntu-latest, macos-latest, and windows-latest to
the build, unit-test, and adapter-test CI jobs. This ensures cross-
platform compatibility is verified on every push and PR.

Smoke tests remain Linux-only due to xvfb dependency.

Relates to #392 (Windows plugin path issues).
2026-03-25 10:32:57 +08:00
4 changed files with 27 additions and 13 deletions
+12 -3
View File
@@ -16,7 +16,11 @@ concurrency:
jobs:
# ── Fast gate: typecheck + build ──
build:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v6
@@ -36,10 +40,11 @@ jobs:
# ── Unit tests (vitest shard) ──
unit-test:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: ['20', '22']
shard: [1, 2]
steps:
@@ -57,7 +62,11 @@ jobs:
run: npm test -- --reporter=verbose --shard=${{ matrix.shard }}/2
adapter-test:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
needs: build
steps:
- uses: actions/checkout@v6
+7 -5
View File
@@ -4,16 +4,18 @@ import { executeCommand } from './execution.js';
import { getRegistry, cli, Strategy } from './registry.js';
import { clearAllHooks, onAfterExecute } from './hooks.js';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { pathToFileURL } from 'node:url';
describe('discoverClis', () => {
it('handles non-existent directories gracefully', async () => {
// Should not throw for missing directories
await expect(discoverClis('/tmp/nonexistent-opencli-test-dir')).resolves.not.toThrow();
await expect(discoverClis(path.join(os.tmpdir(), 'nonexistent-opencli-test-dir'))).resolves.not.toThrow();
});
it('imports only CLI command modules during filesystem discovery', async () => {
const tempRoot = await fs.promises.mkdtemp(path.join('/tmp', 'opencli-discovery-'));
const tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'opencli-discovery-'));
const siteDir = path.join(tempRoot, 'temp-site');
const helperPath = path.join(siteDir, 'helper.ts');
const commandPath = path.join(siteDir, 'hello.ts');
@@ -25,7 +27,7 @@ globalThis.__opencli_helper_loaded__ = true;
export const helper = true;
`);
await fs.promises.writeFile(commandPath, `
import { cli, Strategy } from '${path.join(process.cwd(), 'src', 'registry.ts')}';
import { cli, Strategy } from '${pathToFileURL(path.join(process.cwd(), 'src', 'registry.ts')).href}';
cli({
site: 'temp-site',
name: 'hello',
@@ -48,7 +50,7 @@ cli({
});
it('falls back to filesystem discovery when the manifest is invalid', async () => {
const tempBuildRoot = await fs.promises.mkdtemp(path.join('/tmp', 'opencli-manifest-fallback-'));
const tempBuildRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'opencli-manifest-fallback-'));
const distDir = path.join(tempBuildRoot, 'dist');
const siteDir = path.join(distDir, 'fallback-site');
const commandPath = path.join(siteDir, 'hello.ts');
@@ -58,7 +60,7 @@ cli({
await fs.promises.mkdir(siteDir, { recursive: true });
await fs.promises.writeFile(manifestPath, '{ invalid json');
await fs.promises.writeFile(commandPath, `
import { cli, Strategy } from '${path.join(process.cwd(), 'src', 'registry.ts')}';
import { cli, Strategy } from '${pathToFileURL(path.join(process.cwd(), 'src', 'registry.ts')).href}';
cli({
site: 'fallback-site',
name: 'hello',
+5 -3
View File
@@ -1,4 +1,6 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import * as os from 'node:os';
import * as path from 'node:path';
import type { IPage } from '../../types.js';
const { mockHttpDownload, mockYtdlpDownload, mockExportCookiesToNetscape } = vi.hoisted(() => ({
@@ -64,7 +66,7 @@ describe('stepDownload', () => {
page,
{
url: '${{ item.url }}',
dir: '/tmp/opencli-download-test',
dir: path.join(os.tmpdir(), 'opencli-download-test'),
filename: '${{ index }}.txt',
progress: false,
concurrency: 1,
@@ -79,13 +81,13 @@ describe('stepDownload', () => {
expect(mockHttpDownload).toHaveBeenNthCalledWith(
1,
'https://a.example/file-1.txt',
'/tmp/opencli-download-test/0.txt',
path.join(os.tmpdir(), 'opencli-download-test', '0.txt'),
expect.objectContaining({ cookies: 'sid=a.example' }),
);
expect(mockHttpDownload).toHaveBeenNthCalledWith(
2,
'https://b.example/file-2.txt',
'/tmp/opencli-download-test/1.txt',
path.join(os.tmpdir(), 'opencli-download-test', '1.txt'),
expect.objectContaining({ cookies: 'sid=b.example' }),
);
});
+3 -2
View File
@@ -4,6 +4,7 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { PLUGINS_DIR } from './discovery.js';
import type { LockEntry } from './plugin.js';
@@ -177,7 +178,7 @@ describe('getCommitHash', () => {
});
it('returns undefined for non-git directory', () => {
expect(_getCommitHash('/tmp')).toBeUndefined();
expect(_getCommitHash(os.tmpdir())).toBeUndefined();
});
});
@@ -284,7 +285,7 @@ vi.mock('node:child_process', () => {
return {
execFileSync: vi.fn((_cmd, args, opts) => {
if (Array.isArray(args) && args[0] === 'rev-parse' && args[1] === 'HEAD') {
if (opts?.cwd === '/tmp') {
if (opts?.cwd === os.tmpdir()) {
throw new Error('not a git repository');
}
return '1234567890abcdef1234567890abcdef12345678\n';