Skip to content

Commit 205bf59

Browse files
Priyanshu AgrawalCopilot
authored andcommitted
Fix CloneSiteHandler tests: use real temp dirs instead of stubbing fs-extra
fs-extra exports non-configurable properties that cannot be stubbed with sinon. Instead of stubbing ensureDirSync/readdirSync, the tests now: - Let real temp directories be created - Have mock download/clone callbacks create site subfolders to simulate CLI behavior - Clean up temp dirs in afterEach Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent dc77e8e commit 205bf59

1 file changed

Lines changed: 40 additions & 31 deletions

File tree

src/client/test/integration/power-pages/actions-hub/handlers/CloneSiteHandler.test.ts

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import { expect } from 'chai';
77
import * as sinon from 'sinon';
88
import * as vscode from 'vscode';
9+
import * as os from 'os';
910
import * as fs from 'fs-extra';
11+
import path from 'path';
1012
import { cloneSite } from '../../../../../power-pages/actions-hub/handlers/CloneSiteHandler';
1113
import { Constants } from '../../../../../power-pages/actions-hub/Constants';
1214
import { PacTerminal } from '../../../../../lib/PacTerminal';
@@ -29,20 +31,18 @@ describe('CloneSiteHandler', () => {
2931
uploadCodeSiteWithProgress: sinon.SinonStub;
3032
};
3133
let executeCommandStub: sinon.SinonStub;
32-
let readdirSyncStub: sinon.SinonStub;
33-
34-
const mockDirEntry = (name: string) => ({
35-
name,
36-
isDirectory: () => true,
37-
isFile: () => false,
38-
isBlockDevice: () => false,
39-
isCharacterDevice: () => false,
40-
isSymbolicLink: () => false,
41-
isFIFO: () => false,
42-
isSocket: () => false,
43-
path: '',
44-
parentPath: '',
45-
} as unknown as fs.Dirent);
34+
35+
/**
36+
* Simulates what the PAC CLI download/clone commands do:
37+
* create a site subfolder inside the target directory.
38+
*/
39+
const simulateSiteSubfolderCreation = (stub: sinon.SinonStub) => {
40+
stub.callsFake((...args: string[]) => {
41+
const targetDir = args[0];
42+
fs.ensureDirSync(path.join(targetDir, 'test-site'));
43+
return Promise.resolve(true);
44+
});
45+
};
4646

4747
beforeEach(() => {
4848
sandbox = sinon.createSandbox();
@@ -54,11 +54,6 @@ describe('CloneSiteHandler', () => {
5454

5555
executeCommandStub = sandbox.stub(vscode.commands, 'executeCommand').resolves();
5656

57-
// Stub fs-extra operations
58-
sandbox.stub(fs, 'ensureDirSync');
59-
readdirSyncStub = sandbox.stub(fs, 'readdirSync').returns([mockDirEntry('test-site')]);
60-
sandbox.stub(fs, 'remove').resolves();
61-
6257
mockPacTerminal = sandbox.createStubInstance(PacTerminal);
6358

6459
mockPacWrapper = {
@@ -68,11 +63,27 @@ describe('CloneSiteHandler', () => {
6863
uploadSiteWithProgress: sandbox.stub().resolves(true),
6964
uploadCodeSiteWithProgress: sandbox.stub().resolves(true),
7065
};
66+
67+
// Simulate site subfolder creation for download and clone
68+
simulateSiteSubfolderCreation(mockPacWrapper.downloadSiteWithProgress);
69+
simulateSiteSubfolderCreation(mockPacWrapper.downloadCodeSiteWithProgress);
70+
mockPacWrapper.cloneSiteWithProgress.callsFake((_src: string, outputDir: string) => {
71+
fs.ensureDirSync(path.join(outputDir, 'test-site-clone'));
72+
return Promise.resolve(true);
73+
});
74+
7175
mockPacTerminal.getWrapper.returns(mockPacWrapper as unknown as ReturnType<PacTerminal['getWrapper']>);
7276
});
7377

7478
afterEach(() => {
7579
sandbox.restore();
80+
81+
// Clean up any leftover temp dirs
82+
const tmpDir = os.tmpdir();
83+
const entries = fs.readdirSync(tmpDir).filter(e => e.startsWith('pp-clone-'));
84+
for (const entry of entries) {
85+
fs.removeSync(path.join(tmpDir, entry));
86+
}
7687
});
7788

7889
function createMockSiteTreeItem(overrides: Partial<IWebsiteInfo> = {}): SiteTreeItem {
@@ -182,7 +193,7 @@ describe('CloneSiteHandler', () => {
182193
expect(mockPacWrapper.uploadCodeSiteWithProgress.called).to.be.false;
183194
});
184195

185-
it('should use temp directories and find site subfolder for clone and upload', async () => {
196+
it('should pass discovered site subfolder paths to clone and upload', async () => {
186197
sandbox.stub(vscode.window, 'showInputBox').resolves('Copy of Test Site');
187198

188199
const handler = cloneSite(mockPacTerminal as unknown as PacTerminal);
@@ -193,12 +204,10 @@ describe('CloneSiteHandler', () => {
193204
const cloneOutputPath = mockPacWrapper.cloneSiteWithProgress.firstCall.args[1] as string;
194205
const uploadPath = mockPacWrapper.uploadSiteWithProgress.firstCall.args[0] as string;
195206

196-
// Clone source should be inside the download path (the site subfolder)
197-
expect(cloneSourcePath).to.include(downloadPath);
198-
expect(cloneSourcePath).to.include('test-site');
199-
// Upload path should be inside the clone output (the cloned site subfolder)
200-
expect(uploadPath).to.include(cloneOutputPath);
201-
expect(uploadPath).to.include('test-site');
207+
// Clone source should point to the site subfolder inside the download dir
208+
expect(cloneSourcePath).to.equal(path.join(downloadPath, 'test-site'));
209+
// Upload path should point to the site subfolder inside the clone output dir
210+
expect(uploadPath).to.equal(path.join(cloneOutputPath, 'test-site-clone'));
202211
// Paths should be in temp directory
203212
expect(downloadPath).to.include('pp-clone-');
204213
});
@@ -227,8 +236,8 @@ describe('CloneSiteHandler', () => {
227236
sandbox.stub(vscode.window, 'showInputBox').resolves('Clone Name');
228237
const mockShowErrorMessage = sandbox.stub(vscode.window, 'showErrorMessage');
229238

230-
// Return empty directory - no site subfolder
231-
readdirSyncStub.returns([]);
239+
// Override download to NOT create a subfolder
240+
mockPacWrapper.downloadSiteWithProgress.callsFake(() => Promise.resolve(true));
232241

233242
const handler = cloneSite(mockPacTerminal as unknown as PacTerminal);
234243
await handler(createMockSiteTreeItem());
@@ -246,7 +255,7 @@ describe('CloneSiteHandler', () => {
246255
sandbox.stub(vscode.window, 'showInputBox').resolves('Clone Name');
247256
const mockShowErrorMessage = sandbox.stub(vscode.window, 'showErrorMessage');
248257

249-
mockPacWrapper.downloadSiteWithProgress.resolves(false);
258+
mockPacWrapper.downloadSiteWithProgress.callsFake(() => Promise.resolve(false));
250259

251260
const handler = cloneSite(mockPacTerminal as unknown as PacTerminal);
252261
await handler(createMockSiteTreeItem());
@@ -264,7 +273,7 @@ describe('CloneSiteHandler', () => {
264273
sandbox.stub(vscode.window, 'showInputBox').resolves('Clone Name');
265274
const mockShowErrorMessage = sandbox.stub(vscode.window, 'showErrorMessage');
266275

267-
mockPacWrapper.cloneSiteWithProgress.resolves(false);
276+
mockPacWrapper.cloneSiteWithProgress.callsFake(() => Promise.resolve(false));
268277

269278
const handler = cloneSite(mockPacTerminal as unknown as PacTerminal);
270279
await handler(createMockSiteTreeItem());
@@ -300,7 +309,7 @@ describe('CloneSiteHandler', () => {
300309
it('should catch the error and log telemetry', async () => {
301310
sandbox.stub(vscode.window, 'showInputBox').resolves('Clone Name');
302311

303-
mockPacWrapper.downloadSiteWithProgress.throws(new Error('Unexpected error'));
312+
mockPacWrapper.downloadSiteWithProgress.callsFake(() => { throw new Error('Unexpected error'); });
304313

305314
const handler = cloneSite(mockPacTerminal as unknown as PacTerminal);
306315
await handler(createMockSiteTreeItem());

0 commit comments

Comments
 (0)