Files
theia-code-os/packages/filesystem/src/node/parcel-watcher/parcel-filesystem-watcher.spec.ts
mawkone 8bb5110148
Some checks failed
Playwright Tests / Playwright Tests (ubuntu-22.04, Node.js 22.x) (push) Has been cancelled
3PP License Check / 3PP License Check (11, 22.x, ubuntu-22.04) (push) Has been cancelled
Publish packages to NPM / Perform Publishing (push) Has been cancelled
deploy: current vibn theia state
Made-with: Cursor
2026-02-27 12:01:08 -08:00

176 lines
7.0 KiB
TypeScript

// *****************************************************************************
// Copyright (C) 2018 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import * as temp from 'temp';
import * as chai from 'chai';
import * as cp from 'child_process';
import * as fs from '@theia/core/shared/fs-extra';
import * as assert from 'assert';
import URI from '@theia/core/lib/common/uri';
import { FileUri } from '@theia/core/lib/node';
import { ParcelFileSystemWatcherService } from './parcel-filesystem-service';
import { DidFilesChangedParams, FileChange, FileChangeType } from '../../common/filesystem-watcher-protocol';
const expect = chai.expect;
const track = temp.track();
describe('parcel-filesystem-watcher', function (): void {
let root: URI;
let watcherService: ParcelFileSystemWatcherService;
let watcherId: number;
this.timeout(100000);
beforeEach(async () => {
let tempPath = temp.mkdirSync('node-fs-root');
// Sometimes tempPath will use some Windows 8.3 short name in its path. This is a problem
// since parcel always returns paths with long names. We need to convert here.
// See: https://stackoverflow.com/a/34473971/7983255
if (process.platform === 'win32') {
tempPath = cp.execSync(`powershell "(Get-Item -LiteralPath '${tempPath}').FullName"`, {
encoding: 'utf8',
}).trim();
}
root = FileUri.create(fs.realpathSync(tempPath));
watcherService = createParcelFileSystemWatcherService();
watcherId = await watcherService.watchFileChanges(0, root.toString());
await sleep(200);
});
afterEach(async () => {
track.cleanupSync();
watcherService.dispose();
});
it('Should receive file changes events from in the workspace by default.', async function (): Promise<void> {
const actualUris = new Set<string>();
const watcherClient = {
onDidFilesChanged(event: DidFilesChangedParams): void {
event.changes.forEach(c => actualUris.add(c.uri.toString()));
},
onError(): void {
}
};
watcherService.setClient(watcherClient);
const expectedUris = [
root.resolve('foo').toString(),
root.withPath(root.path.join('foo', 'bar')).toString(),
root.withPath(root.path.join('foo', 'bar', 'baz.txt')).toString()
];
fs.mkdirSync(FileUri.fsPath(root.resolve('foo')));
expect(fs.statSync(FileUri.fsPath(root.resolve('foo'))).isDirectory()).to.be.true;
await sleep(200);
fs.mkdirSync(FileUri.fsPath(root.resolve('foo').resolve('bar')));
expect(fs.statSync(FileUri.fsPath(root.resolve('foo').resolve('bar'))).isDirectory()).to.be.true;
await sleep(200);
fs.writeFileSync(FileUri.fsPath(root.resolve('foo').resolve('bar').resolve('baz.txt')), 'baz');
expect(fs.readFileSync(FileUri.fsPath(root.resolve('foo').resolve('bar').resolve('baz.txt')), 'utf8')).to.be.equal('baz');
await sleep(200);
assert.deepStrictEqual([...actualUris], expectedUris);
});
it('Should not receive file changes events from in the workspace by default if unwatched', async function (): Promise<void> {
const actualUris = new Set<string>();
const watcherClient = {
onDidFilesChanged(event: DidFilesChangedParams): void {
event.changes.forEach(c => actualUris.add(c.uri.toString()));
},
onError(): void {
}
};
watcherService.setClient(watcherClient);
/* Unwatch root */
await watcherService.unwatchFileChanges(watcherId);
fs.mkdirSync(FileUri.fsPath(root.resolve('foo')));
expect(fs.statSync(FileUri.fsPath(root.resolve('foo'))).isDirectory()).to.be.true;
await sleep(200);
fs.mkdirSync(FileUri.fsPath(root.resolve('foo').resolve('bar')));
expect(fs.statSync(FileUri.fsPath(root.resolve('foo').resolve('bar'))).isDirectory()).to.be.true;
await sleep(200);
fs.writeFileSync(FileUri.fsPath(root.resolve('foo').resolve('bar').resolve('baz.txt')), 'baz');
expect(fs.readFileSync(FileUri.fsPath(root.resolve('foo').resolve('bar').resolve('baz.txt')), 'utf8')).to.be.equal('baz');
await sleep(200);
assert.deepStrictEqual(actualUris.size, 0);
});
it('Renaming should emit a DELETED and ADDED event', async function (): Promise<void> {
const file_txt = root.resolve('file.txt');
const FILE_txt = root.resolve('FILE.txt');
const changes: FileChange[] = [];
watcherService.setClient({
onDidFilesChanged: event => event.changes.forEach(change => changes.push(change)),
onError: console.error
});
await fs.promises.writeFile(
FileUri.fsPath(file_txt),
'random content\n'
);
await sleep(200);
await fs.promises.rename(
FileUri.fsPath(file_txt),
FileUri.fsPath(FILE_txt)
);
await sleep(200);
// The order of DELETED and ADDED is not deterministic
try {
expect(changes).deep.eq([
// initial file creation change event:
{ type: FileChangeType.ADDED, uri: file_txt.toString() },
// rename change events:
{ type: FileChangeType.DELETED, uri: file_txt.toString() },
{ type: FileChangeType.ADDED, uri: FILE_txt.toString() },
]);
} catch {
expect(changes).deep.eq([
// initial file creation change event:
{ type: FileChangeType.ADDED, uri: file_txt.toString() },
// rename change events:
{ type: FileChangeType.ADDED, uri: FILE_txt.toString() },
{ type: FileChangeType.DELETED, uri: file_txt.toString() },
]);
}
});
function createParcelFileSystemWatcherService(): ParcelFileSystemWatcherService {
return new ParcelFileSystemWatcherService({
verbose: true
});
}
function sleep(time: number): Promise<unknown> {
return new Promise(resolve => setTimeout(resolve, time));
}
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
process.on('unhandledRejection', (reason: any) => {
console.error('Unhandled promise rejection: ' + reason);
});