Files
smartwatch/test/test.platform.bun.ts

116 lines
3.9 KiB
TypeScript

import { tap, expect } from '@git.zone/tstest/tapbundle';
import { NodeWatcher } from '../ts/watchers/watcher.node.js';
import type { IWatchEvent } from '../ts/watchers/interfaces.js';
import * as path from 'path';
import * as fs from 'fs';
// Bun uses NodeWatcher (Node.js compatibility layer).
// This test validates that the chokidar-based watcher works under Bun.
const isBun = typeof (globalThis as any).Bun !== 'undefined';
const TEST_DIR = path.resolve('./test/assets');
const delay = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));
function waitForEvent(
watcher: { events$: { subscribe: Function } },
filter: (e: IWatchEvent) => boolean,
timeoutMs = 5000
): Promise<IWatchEvent> {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
sub.unsubscribe();
reject(new Error(`Timeout waiting for event after ${timeoutMs}ms`));
}, timeoutMs);
const sub = watcher.events$.subscribe((event: IWatchEvent) => {
if (filter(event)) {
clearTimeout(timeout);
sub.unsubscribe();
resolve(event);
}
});
});
}
let watcher: NodeWatcher;
tap.test('BunNodeWatcher: should create and start', async () => {
if (!isBun) { console.log('Skipping: not Bun runtime'); return; }
watcher = new NodeWatcher({
basePaths: [TEST_DIR],
depth: 4,
followSymlinks: false,
debounceMs: 100,
});
expect(watcher.isWatching).toBeFalse();
await watcher.start();
expect(watcher.isWatching).toBeTrue();
await delay(500);
});
tap.test('BunNodeWatcher: should detect file creation', async () => {
if (!isBun) return;
const file = path.join(TEST_DIR, 'bun-add-test.txt');
const eventPromise = waitForEvent(watcher, (e) => e.type === 'add' && e.path.includes('bun-add-test.txt'));
await fs.promises.writeFile(file, 'bun watcher test');
const event = await eventPromise;
expect(event.type).toEqual('add');
expect(event.path).toInclude('bun-add-test.txt');
await fs.promises.unlink(file);
await delay(200);
});
tap.test('BunNodeWatcher: should detect file modification', async () => {
if (!isBun) return;
const file = path.join(TEST_DIR, 'bun-change-test.txt');
await fs.promises.writeFile(file, 'initial');
await delay(300);
const eventPromise = waitForEvent(watcher, (e) => e.type === 'change' && e.path.includes('bun-change-test.txt'));
await fs.promises.writeFile(file, 'modified');
const event = await eventPromise;
expect(event.type).toEqual('change');
await fs.promises.unlink(file);
await delay(200);
});
tap.test('BunNodeWatcher: should detect file deletion', async () => {
if (!isBun) return;
const file = path.join(TEST_DIR, 'bun-unlink-test.txt');
await fs.promises.writeFile(file, 'to delete');
await delay(300);
const eventPromise = waitForEvent(watcher, (e) => e.type === 'unlink' && e.path.includes('bun-unlink-test.txt'));
await fs.promises.unlink(file);
const event = await eventPromise;
expect(event.type).toEqual('unlink');
});
tap.test('BunNodeWatcher: should detect directory creation', async () => {
if (!isBun) return;
const dir = path.join(TEST_DIR, 'bun-test-subdir');
const addDirPromise = waitForEvent(watcher, (e) => e.type === 'addDir' && e.path.includes('bun-test-subdir'));
await fs.promises.mkdir(dir, { recursive: true });
const event = await addDirPromise;
expect(event.type).toEqual('addDir');
await delay(200);
await fs.promises.rmdir(dir);
await delay(200);
});
tap.test('BunNodeWatcher: should not be watching after stop', async () => {
if (!isBun) return;
await watcher.stop();
expect(watcher.isWatching).toBeFalse();
});
tap.test('BunNodeWatcher: cleanup', async () => {
if (!isBun) return;
for (const name of ['bun-add-test.txt', 'bun-change-test.txt', 'bun-unlink-test.txt']) {
try { await fs.promises.unlink(path.join(TEST_DIR, name)); } catch {}
}
try { await fs.promises.rmdir(path.join(TEST_DIR, 'bun-test-subdir')); } catch {}
});
export default tap.start();