remove the watch mode concept, combine both watchers

This commit is contained in:
Simon Knott 2024-07-26 16:52:12 +02:00
parent c7b8a7b766
commit 52b0811c55
No known key found for this signature in database
GPG key ID: 8CEDC00028084AEC
3 changed files with 35 additions and 36 deletions

View file

@ -61,7 +61,7 @@ export async function runDevServer(config: FullConfigInternal): Promise<() => Pr
projectOutputs.add(p.project.outputDir); projectOutputs.add(p.project.outputDir);
} }
const globalWatcher = new Watcher('deep', async () => { const globalWatcher = new Watcher(async () => {
const registry: ComponentRegistry = new Map(); const registry: ComponentRegistry = new Map();
await populateComponentsFromTests(registry); await populateComponentsFromTests(registry);
// compare componentRegistry to registry key sets. // compare componentRegistry to registry key sets.

View file

@ -21,26 +21,24 @@ export type FSEvent = { event: 'add' | 'addDir' | 'change' | 'unlink' | 'unlinkD
export class Watcher { export class Watcher {
private _onChange: (events: FSEvent[]) => void; private _onChange: (events: FSEvent[]) => void;
private _watchedFiles: string[] = []; private _watchedPaths: string[] = [];
private _ignoredFolders: string[] = []; private _ignoredFolders: string[] = [];
private _collector: FSEvent[] = []; private _collector: FSEvent[] = [];
private _fsWatcher: FSWatcher | undefined; private _fsWatcher: FSWatcher | undefined;
private _throttleTimer: NodeJS.Timeout | undefined; private _throttleTimer: NodeJS.Timeout | undefined;
private _mode: 'flat' | 'deep';
constructor(mode: 'flat' | 'deep', onChange: (events: FSEvent[]) => void) { constructor(onChange: (events: FSEvent[]) => void) {
this._mode = mode;
this._onChange = onChange; this._onChange = onChange;
} }
update(watchedFiles: string[], ignoredFolders: string[], reportPending: boolean) { update(watchedPaths: string[], ignoredFolders: string[], reportPending: boolean) {
if (JSON.stringify([this._watchedFiles, this._ignoredFolders]) === JSON.stringify(watchedFiles, ignoredFolders)) if (JSON.stringify([this._watchedPaths, this._ignoredFolders]) === JSON.stringify(watchedPaths, ignoredFolders))
return; return;
if (reportPending) if (reportPending)
this._reportEventsIfAny(); this._reportEventsIfAny();
this._watchedFiles = watchedFiles; this._watchedPaths = watchedPaths;
this._ignoredFolders = ignoredFolders; this._ignoredFolders = ignoredFolders;
void this._fsWatcher?.close(); void this._fsWatcher?.close();
this._fsWatcher = undefined; this._fsWatcher = undefined;
@ -48,17 +46,13 @@ export class Watcher {
clearTimeout(this._throttleTimer); clearTimeout(this._throttleTimer);
this._throttleTimer = undefined; this._throttleTimer = undefined;
if (!this._watchedFiles.length) if (!this._watchedPaths.length)
return; return;
const ignored = [...this._ignoredFolders, '**/node_modules/**']; const ignored = [...this._ignoredFolders, '**/node_modules/**'];
this._fsWatcher = chokidar.watch(watchedFiles, { ignoreInitial: true, ignored }).on('all', async (event, file) => { this._fsWatcher = chokidar.watch(watchedPaths, { ignoreInitial: true, ignored }).on('all', async (event, file) => {
if (this._throttleTimer) if (this._throttleTimer)
clearTimeout(this._throttleTimer); clearTimeout(this._throttleTimer);
if (this._mode === 'flat' && event !== 'add' && event !== 'change')
return;
if (this._mode === 'deep' && event !== 'add' && event !== 'change' && event !== 'unlink' && event !== 'addDir' && event !== 'unlinkDir')
return;
this._collector.push({ event, file }); this._collector.push({ event, file });
this._throttleTimer = setTimeout(() => this._reportEventsIfAny(), 250); this._throttleTimer = setTimeout(() => this._reportEventsIfAny(), 250);
}); });

View file

@ -27,7 +27,7 @@ import { TestRun, createTaskRunnerForList, createTaskRunnerForTestServer, create
import { open } from 'playwright-core/lib/utilsBundle'; import { open } from 'playwright-core/lib/utilsBundle';
import ListReporter from '../reporters/list'; import ListReporter from '../reporters/list';
import { SigIntWatcher } from './sigIntWatcher'; import { SigIntWatcher } from './sigIntWatcher';
import { type FSEvent, Watcher } from '../fsWatcher'; import { Watcher } from '../fsWatcher';
import type { ReportEntry, TestServerInterface, TestServerInterfaceEventEmitters } from '../isomorphic/testServerInterface'; import type { ReportEntry, TestServerInterface, TestServerInterfaceEventEmitters } from '../isomorphic/testServerInterface';
import { Runner } from './runner'; import { Runner } from './runner';
import type { ConfigCLIOverrides } from '../common/ipc'; import type { ConfigCLIOverrides } from '../common/ipc';
@ -64,8 +64,12 @@ class TestServer {
class TestServerDispatcher implements TestServerInterface { class TestServerDispatcher implements TestServerInterface {
private _configLocation: ConfigLocation; private _configLocation: ConfigLocation;
private _globalWatcher: Watcher;
private _testWatcher: Watcher; private _watcher: Watcher;
private _watchedProjectDirs = new Set<string>();
private _ignoredProjectOutputs = new Set<string>();
private _watchedTestDependencies = new Set<string>();
private _testRun: { run: Promise<reporterTypes.FullResult['status']>, stop: ManualPromise<void> } | undefined; private _testRun: { run: Promise<reporterTypes.FullResult['status']>, stop: ManualPromise<void> } | undefined;
readonly transport: Transport; readonly transport: Transport;
private _queue = Promise.resolve(); private _queue = Promise.resolve();
@ -86,17 +90,14 @@ class TestServerDispatcher implements TestServerInterface {
gracefullyProcessExitDoNotHang(0); gracefullyProcessExitDoNotHang(0);
}, },
}; };
this._globalWatcher = new Watcher('deep', events => this._checkForChangedTestFiles(events)); this._watcher = new Watcher(events => {
this._testWatcher = new Watcher('flat', events => this._checkForChangedTestFiles(events)); const collector = new Set<string>();
events.forEach(f => collectAffectedTestFiles(f.file, collector));
this._dispatchEvent('testFilesChanged', { testFiles: [...collector] });
});
this._dispatchEvent = (method, params) => this.transport.sendEvent?.(method, params); this._dispatchEvent = (method, params) => this.transport.sendEvent?.(method, params);
} }
private _checkForChangedTestFiles(events: FSEvent[]) {
const collector = new Set<string>();
events.forEach(f => collectAffectedTestFiles(f.file, collector));
this._dispatchEvent('testFilesChanged', { testFiles: [...collector] });
}
private async _wireReporter(messageSink: (message: any) => void) { private async _wireReporter(messageSink: (message: any) => void) {
return await createReporterForTestServer(this._serializer, messageSink); return await createReporterForTestServer(this._serializer, messageSink);
} }
@ -281,24 +282,28 @@ class TestServerDispatcher implements TestServerInterface {
await taskRunner.reporter.onEnd({ status }); await taskRunner.reporter.onEnd({ status });
await taskRunner.reporter.onExit(); await taskRunner.reporter.onExit();
const projectDirs = new Set<string>(); this._watchedProjectDirs = new Set();
const projectOutputs = new Set<string>(); this._ignoredProjectOutputs = new Set();
for (const p of config.projects) { for (const p of config.projects) {
projectDirs.add(p.project.testDir); this._watchedProjectDirs.add(p.project.testDir);
projectOutputs.add(p.project.outputDir); this._ignoredProjectOutputs.add(p.project.outputDir);
} }
const result = await resolveCtDirs(config); const result = await resolveCtDirs(config);
if (result) { if (result) {
projectDirs.add(result.templateDir); this._watchedProjectDirs.add(result.templateDir);
projectOutputs.add(result.outDir); this._ignoredProjectOutputs.add(result.outDir);
} }
if (this._watchTestDirs) if (this._watchTestDirs)
this._globalWatcher.update([...projectDirs], [...projectOutputs], false); this.updateWatcher(false);
return { report, status }; return { report, status };
} }
private updateWatcher(reportPending: boolean) {
this._watcher.update([...this._watchedProjectDirs, ...this._watchedTestDependencies], [...this._ignoredProjectOutputs], reportPending);
}
async runTests(params: Parameters<TestServerInterface['runTests']>[0]): ReturnType<TestServerInterface['runTests']> { async runTests(params: Parameters<TestServerInterface['runTests']>[0]): ReturnType<TestServerInterface['runTests']> {
let result: Awaited<ReturnType<TestServerInterface['runTests']>> = { status: 'passed' }; let result: Awaited<ReturnType<TestServerInterface['runTests']>> = { status: 'passed' };
this._queue = this._queue.then(async () => { this._queue = this._queue.then(async () => {
@ -366,12 +371,12 @@ class TestServerDispatcher implements TestServerInterface {
} }
async watch(params: { fileNames: string[]; }) { async watch(params: { fileNames: string[]; }) {
const files = new Set<string>(); this._watchedTestDependencies = new Set();
for (const fileName of params.fileNames) { for (const fileName of params.fileNames) {
files.add(fileName); this._watchedTestDependencies.add(fileName);
dependenciesForTestFile(fileName).forEach(file => files.add(file)); dependenciesForTestFile(fileName).forEach(file => this._watchedTestDependencies.add(file));
} }
this._testWatcher.update([...files], [], true); this.updateWatcher(true);
} }
async findRelatedTestFiles(params: Parameters<TestServerInterface['findRelatedTestFiles']>[0]): ReturnType<TestServerInterface['findRelatedTestFiles']> { async findRelatedTestFiles(params: Parameters<TestServerInterface['findRelatedTestFiles']>[0]): ReturnType<TestServerInterface['findRelatedTestFiles']> {