Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const previewSite = async (siteTreeItem: SiteTreeItem) => {
try {
await PreviewSite.clearCache(siteTreeItem.siteInfo.websiteUrl);

await PreviewSite.launchBrowserAndDevToolsWithinVsCode(siteTreeItem.siteInfo.websiteUrl, siteTreeItem.siteInfo.dataModelVersion, siteTreeItem.siteInfo.siteVisibility);
await PreviewSite.launchSitePreviewWithinVsCode(siteTreeItem.siteInfo.websiteUrl, siteTreeItem.siteInfo.dataModelVersion, siteTreeItem.siteInfo.siteVisibility);
} catch (error) {
traceError(Constants.EventNames.ACTIONS_HUB_PREVIEW_SITE_FAILED, error as Error, { methodName: previewSite.name });
}
Expand Down
46 changes: 38 additions & 8 deletions src/client/power-pages/preview-site/PreviewSite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { SiteVisibility } from '../actions-hub/models/SiteVisibility';
import { uploadSite } from '../actions-hub/handlers/UploadSiteHandler';

export const SITE_PREVIEW_COMMAND_ID = "microsoft.powerplatform.pages.preview-site";
const INTEGRATED_BROWSER_COMMAND_ID = 'workbench.action.browser.open';
const EDGE_DEVTOOLS_COMMAND_ID = 'vscode-edge-devtools.launch';

export class PreviewSite {
private static _websiteDetails: IWebsiteDetails | undefined = undefined;
Expand Down Expand Up @@ -126,24 +128,21 @@ export class PreviewSite {
}
}

public static async launchBrowserAndDevToolsWithinVsCode(webSitePreviewURL: string | undefined, dataModelVersion: 1 | 2, siteVisibility: SiteVisibility | undefined): Promise<void> {
public static async launchSitePreviewWithinVsCode(webSitePreviewURL: string | undefined, dataModelVersion: 1 | 2, siteVisibility: SiteVisibility | undefined): Promise<void> {
if (!webSitePreviewURL || webSitePreviewURL === "" || !siteVisibility) {
return;
}

const edgeToolsExtension = vscode.extensions.getExtension(EDGE_TOOLS_EXTENSION_ID);

if (!edgeToolsExtension) {
await PreviewSite.promptInstallEdgeTools();
const previewBrowser = await PreviewSite.getSitePreviewBrowser();
if (!previewBrowser) {
return;
}

await showProgressWithNotification(
Messages.OPENING_SITE_PREVIEW,
false,
async () => {
PreviewSite.closeExistingPreview();
await vscode.commands.executeCommand('vscode-edge-devtools.launch', { launchUrl: webSitePreviewURL });
await PreviewSite.openSitePreviewBrowser(webSitePreviewURL, previewBrowser);
}
);

Expand All @@ -153,6 +152,37 @@ export class PreviewSite {
}
}

private static async getSitePreviewBrowser(): Promise<'integratedBrowser' | 'edgeTools' | undefined> {
const commands = await vscode.commands.getCommands(true);

// VS Code's integrated browser is available through a workbench command in
// newer builds. Older builds fall back to the existing Edge Tools path because
// Simple Browser does not fully render Power Pages sites.
// See https://github.com/microsoft/vscode/blob/main/extensions/simple-browser/src/extension.ts
if (commands.includes(INTEGRATED_BROWSER_COMMAND_ID)) {
return 'integratedBrowser';
}

const edgeToolsExtension = vscode.extensions.getExtension(EDGE_TOOLS_EXTENSION_ID);

if (!edgeToolsExtension) {
await PreviewSite.promptInstallEdgeTools();
return undefined;
}

return 'edgeTools';
}

private static async openSitePreviewBrowser(webSitePreviewURL: string, previewBrowser: 'integratedBrowser' | 'edgeTools'): Promise<void> {
if (previewBrowser === 'integratedBrowser') {
await vscode.commands.executeCommand(INTEGRATED_BROWSER_COMMAND_ID, webSitePreviewURL);
return;
}

PreviewSite.closeExistingPreview();
await vscode.commands.executeCommand(EDGE_DEVTOOLS_COMMAND_ID, { launchUrl: webSitePreviewURL });
}

private static async showUploadWarning(websitePath: string, dataModelVersion: 1 | 2, siteVisibility: SiteVisibility) {
const pendingChangesResult = await PreviewSite._pacTerminal.getWrapper().pendingChanges(websitePath, dataModelVersion);

Expand Down Expand Up @@ -219,7 +249,7 @@ export class PreviewSite {

await PreviewSite.clearCache(PreviewSite._websiteDetails.websiteUrl);

await PreviewSite.launchBrowserAndDevToolsWithinVsCode(
await PreviewSite.launchSitePreviewWithinVsCode(
PreviewSite._websiteDetails.websiteUrl,
PreviewSite._websiteDetails.dataModel === WebsiteDataModel.Standard ? 1 : 2,
PreviewSite._websiteDetails.siteVisibility
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ describe('PreviewSiteHandler', () => {

describe('previewSite', () => {
let mockPreviewSiteClearCache: sinon.SinonStub;
let mockLaunchBrowserAndDevTools: sinon.SinonStub;
let mockLaunchSitePreview: sinon.SinonStub;
let mockSiteInfo: IWebsiteInfo;

beforeEach(() => {
mockPreviewSiteClearCache = sandbox.stub(PreviewSite, 'clearCache');
mockLaunchBrowserAndDevTools = sandbox.stub(PreviewSite, 'launchBrowserAndDevToolsWithinVsCode');
mockLaunchSitePreview = sandbox.stub(PreviewSite, 'launchSitePreviewWithinVsCode');
mockSiteInfo = {
name: "Test Site",
websiteId: "test-id",
Expand All @@ -53,13 +53,13 @@ describe('PreviewSiteHandler', () => {
};
});

it('should clear cache and launch browser with dev tools', async () => {
it('should clear cache and launch site preview', async () => {
const siteTreeItem = new SiteTreeItem(mockSiteInfo);

await previewSite(siteTreeItem);

expect(mockPreviewSiteClearCache.calledOnceWith('https://test-site.com')).to.be.true;
expect(mockLaunchBrowserAndDevTools.calledOnceWith('https://test-site.com', 1)).to.be.true;
expect(mockLaunchSitePreview.calledOnceWith('https://test-site.com', 1)).to.be.true;
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Events } from '../../../../power-pages/preview-site/Constants';
import ArtemisContext from '../../../../ArtemisContext';
import PacContext from '../../../../pac/PacContext';
import * as sinon from 'sinon';
import { SiteVisibility } from '../../../../power-pages/actions-hub/models/SiteVisibility';

describe('PreviewSite', () => {
let sandbox: sinon.SinonSandbox;
Expand Down Expand Up @@ -182,4 +183,44 @@ describe('PreviewSite', () => {
expect(getWebSiteUrlStub.calledOnceWith(mockWorkspaceFolders)).to.be.true;
});
});

describe('launchSitePreviewWithinVsCode', () => {
let executeCommandStub: sinon.SinonStub;

beforeEach(() => {
executeCommandStub = sandbox.stub(vscode.commands, 'executeCommand');
sandbox.stub(vscode.window, 'withProgress').callsFake(async (_options, task) => {
return await task({ report: sandbox.stub() }, {} as vscode.CancellationToken);
});
sandbox.stub(PreviewSite as unknown as { showUploadWarning: () => Promise<void> }, 'showUploadWarning').resolves();
});

it('should open the integrated browser when the VS Code command is available', async () => {
sandbox.stub(vscode.commands, 'getCommands').resolves(['workbench.action.browser.open']);

await PreviewSite.launchSitePreviewWithinVsCode('https://test-site.com', 1, SiteVisibility.Public);

expect(executeCommandStub.calledOnceWith('workbench.action.browser.open', 'https://test-site.com')).to.be.true;
});

it('should fall back to Edge Tools when the integrated browser command is not available', async () => {
sandbox.stub(vscode.commands, 'getCommands').resolves(['vscode-edge-devtools.launch']);
sandbox.stub(vscode.extensions, 'getExtension').withArgs('ms-edgedevtools.vscode-edge-devtools').returns({} as vscode.Extension<unknown>);

await PreviewSite.launchSitePreviewWithinVsCode('https://test-site.com', 1, SiteVisibility.Public);

expect(executeCommandStub.calledOnceWith('vscode-edge-devtools.launch', { launchUrl: 'https://test-site.com' })).to.be.true;
});

it('should prompt to install Edge Tools when no preview browser is available', async () => {
sandbox.stub(vscode.commands, 'getCommands').resolves([]);
sandbox.stub(vscode.extensions, 'getExtension').withArgs('ms-edgedevtools.vscode-edge-devtools').returns(undefined);
const showWarningMessageStub = sandbox.stub(vscode.window, 'showWarningMessage');

await PreviewSite.launchSitePreviewWithinVsCode('https://test-site.com', 1, SiteVisibility.Public);

expect(showWarningMessageStub.calledOnce).to.be.true;
expect(executeCommandStub.called).to.be.false;
});
});
});
13 changes: 11 additions & 2 deletions src/client/test/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@
*/

import * as path from "path";
import * as fs from "fs";
import * as os from "os";

import { runTests } from "@vscode/test-electron";

async function main() {
// VS Code creates IPC sockets under the user-data directory. Keeping that
// directory in the system temp path avoids macOS Unix socket path limits when
// tests run from long worktree paths.
const userDataDir = fs.mkdtempSync(path.join(os.tmpdir(), "pp-vscode-client-"));

try {
// The folder containing the Extension Manifest package.json
// Passed to `--extensionDevelopmentPath`
Expand All @@ -24,11 +31,13 @@ async function main() {
// version: 'insiders',
extensionDevelopmentPath,
extensionTestsPath,
launchArgs: ['--no-sandbox', '--disable-gpu']
launchArgs: ['--no-sandbox', '--disable-gpu', `--user-data-dir=${userDataDir}`]
});
} catch (err) {
console.error("Failed to run tests");
process.exit(1);
throw err;
} finally {
fs.rmSync(userDataDir, { recursive: true, force: true });
}
}

Expand Down
Loading