chore: add test for webviews
* add webview test * pass browser launcher in from binder * only run webview test on the windows platform with pipeline support for edge * updated webview demo to build a portable release version of WebView2Sample.exe per https://github.com/MicrosoftEdge/WebView2Samples/pull/16 * update webview readme
This commit is contained in:
Родитель
5de1b34c42
Коммит
81693fe224
|
@ -19,4 +19,14 @@ jobs:
|
|||
pool:
|
||||
vmImage: 'vs2017-win2016'
|
||||
steps:
|
||||
- powershell: |
|
||||
# This step can be removed once Edge is included on the test VM image.
|
||||
# The download URI comes from: https://www.microsoftedgeinsider.com/en-us/download
|
||||
$InstallerDownloadURI = "https://go.microsoft.com/fwlink/?linkid=2084649&Channel=Canary&language=en&Consent=1"
|
||||
$InstallerExe = "$ENV:BUILD_ARTIFACTSTAGINGDIRECTORY\MicrosoftEdgeSetup.exe"
|
||||
Invoke-WebRequest -Uri $InstallerDownloadURI -OutFile $InstallerExe
|
||||
Start-Process $InstallerExe -Wait -ArgumentList '/silent /install'
|
||||
# Cleanup installer
|
||||
Remove-Item $InstallerExe
|
||||
displayName: Install Edge Canary
|
||||
- template: common-validation.yml
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
|
|
@ -21,6 +21,5 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="$(MSBuildThisFileDirectory)$(EffectivePlatform)\WebView2Loader.dll" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -43,7 +43,7 @@ const packageJson = require('../../package.json');
|
|||
export interface IBinderDelegate {
|
||||
acquireDap(target: ITarget): Promise<DapConnection>;
|
||||
// Returns whether we should disable child session treatment.
|
||||
initAdapter(debugAdapter: DebugAdapter, target: ITarget): Promise<boolean>;
|
||||
initAdapter(debugAdapter: DebugAdapter, target: ITarget, launcher: ILauncher): Promise<boolean>;
|
||||
releaseDap(target: ITarget): void;
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ export class Binder implements IDisposable {
|
|||
launcher.onTargetListChanged(
|
||||
() => {
|
||||
const targets = this.targetList();
|
||||
this._attachToNewTargets(targets);
|
||||
this._attachToNewTargets(targets, launcher);
|
||||
this._detachOrphanThreads(targets);
|
||||
this._onTargetListChangedEmitter.fire();
|
||||
},
|
||||
|
@ -322,7 +322,7 @@ export class Binder implements IDisposable {
|
|||
return result;
|
||||
}
|
||||
|
||||
async attach(target: ITarget) {
|
||||
async attach(target: ITarget, launcher: ILauncher) {
|
||||
if (!target.canAttach()) return;
|
||||
const cdp = await target.attach();
|
||||
if (!cdp) return;
|
||||
|
@ -357,7 +357,7 @@ export class Binder implements IDisposable {
|
|||
cdp.Runtime.runIfWaitingForDebugger({});
|
||||
return {};
|
||||
};
|
||||
if (await this._delegate.initAdapter(debugAdapter, target)) {
|
||||
if (await this._delegate.initAdapter(debugAdapter, target, launcher)) {
|
||||
startThread();
|
||||
} else {
|
||||
dap.on('attach', startThread);
|
||||
|
@ -386,7 +386,7 @@ export class Binder implements IDisposable {
|
|||
this._releaseTarget(target);
|
||||
}
|
||||
|
||||
_attachToNewTargets(targets: ITarget[]) {
|
||||
_attachToNewTargets(targets: ITarget[], launcher: ILauncher) {
|
||||
for (const target of targets.values()) {
|
||||
if (!target.waitingForDebugger()) {
|
||||
continue;
|
||||
|
@ -394,7 +394,7 @@ export class Binder implements IDisposable {
|
|||
|
||||
const thread = this._threads.get(target);
|
||||
if (!thread) {
|
||||
this.attach(target);
|
||||
this.attach(target, launcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@ import {
|
|||
INodeLaunchConfiguration,
|
||||
nodeAttachConfigDefaults,
|
||||
nodeLaunchConfigDefaults,
|
||||
AnyChromiumLaunchConfiguration,
|
||||
} from '../configuration';
|
||||
import Dap from '../dap/api';
|
||||
import DapConnection from '../dap/connection';
|
||||
import { ChromeLauncher } from '../targets/browser/chromeLauncher';
|
||||
import { ITarget } from '../targets/targets';
|
||||
import { GoldenText } from './goldenText';
|
||||
import { Logger } from './logger';
|
||||
|
@ -32,6 +32,7 @@ import { TelemetryReporter } from '../telemetry/telemetryReporter';
|
|||
import { ILogger } from '../common/logging';
|
||||
import { Logger as AdapterLogger } from '../common/logging/logger';
|
||||
import { createTopLevelSessionContainer, createGlobalContainer } from '../ioc';
|
||||
import { BrowserLauncher } from '../targets/browser/browserLauncher';
|
||||
|
||||
export const kStabilizeNames = ['id', 'threadId', 'sourceReference', 'variablesReference'];
|
||||
|
||||
|
@ -112,7 +113,11 @@ export interface ITestHandle {
|
|||
readonly assertLog: AssertLog;
|
||||
|
||||
load(): Promise<void>;
|
||||
_init(adapter: DebugAdapter, target: ITarget): Promise<boolean>;
|
||||
_init(
|
||||
adapter: DebugAdapter,
|
||||
target: ITarget,
|
||||
launcher: BrowserLauncher<AnyChromiumLaunchConfiguration>,
|
||||
): Promise<boolean>;
|
||||
}
|
||||
|
||||
export class TestP implements ITestHandle {
|
||||
|
@ -196,7 +201,11 @@ export class TestP implements ITestHandle {
|
|||
return this._root.workspacePath(relative);
|
||||
}
|
||||
|
||||
async _init(adapter: DebugAdapter) {
|
||||
async _init(
|
||||
adapter: DebugAdapter,
|
||||
_target: ITarget,
|
||||
launcher: BrowserLauncher<AnyChromiumLaunchConfiguration>,
|
||||
) {
|
||||
adapter.breakpointManager.setPredictorDisabledForTest(true);
|
||||
adapter.sourceContainer.setSourceMapTimeouts({
|
||||
load: 0,
|
||||
|
@ -206,7 +215,8 @@ export class TestP implements ITestHandle {
|
|||
});
|
||||
this._adapter = adapter;
|
||||
|
||||
this._connection = this._root._browserLauncher.connectionForTest()!;
|
||||
this._root._browserLauncher = launcher;
|
||||
this._connection = this._root._browserLauncher?.connectionForTest()!;
|
||||
const result = await this._connection.rootSession().Target.attachToBrowserTarget({});
|
||||
const testSession = this._connection.createSession(result!.sessionId);
|
||||
const { sessionId } = (await testSession.Target.attachToTarget({
|
||||
|
@ -306,7 +316,7 @@ export class TestRoot {
|
|||
private _workerCallback: (session: ITestHandle) => void;
|
||||
private _launchCallback: (session: ITestHandle) => void;
|
||||
|
||||
_browserLauncher: ChromeLauncher;
|
||||
_browserLauncher: BrowserLauncher<AnyChromiumLaunchConfiguration> | undefined;
|
||||
readonly binder: Binder;
|
||||
|
||||
private _onSessionCreatedEmitter = new EventEmitter<ITestHandle>();
|
||||
|
@ -337,7 +347,7 @@ export class TestRoot {
|
|||
const services = createTopLevelSessionContainer(
|
||||
createGlobalContainer({ storagePath, isVsCode: true }),
|
||||
);
|
||||
this._browserLauncher = services.get(ChromeLauncher);
|
||||
|
||||
this.binder = new Binder(
|
||||
this,
|
||||
this._root.adapterConnection,
|
||||
|
@ -359,13 +369,17 @@ export class TestRoot {
|
|||
return p._session.adapterConnection;
|
||||
}
|
||||
|
||||
async initAdapter(adapter: DebugAdapter, target: ITarget): Promise<boolean> {
|
||||
async initAdapter(
|
||||
adapter: DebugAdapter,
|
||||
target: ITarget,
|
||||
launcher: BrowserLauncher<AnyChromiumLaunchConfiguration>,
|
||||
): Promise<boolean> {
|
||||
const p = this._targetToP.get(target);
|
||||
if (!p) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const boot = await p._init(adapter, target);
|
||||
const boot = await p._init(adapter, target, launcher);
|
||||
if (target.parent()) this._workerCallback(p);
|
||||
else this._launchCallback(p);
|
||||
this._onSessionCreatedEmitter.fire(p);
|
||||
|
@ -489,7 +503,7 @@ export class TestRoot {
|
|||
async disconnect(): Promise<void> {
|
||||
return new Promise<void>(cb => {
|
||||
this.initialize.then(() => {
|
||||
const connection = this._browserLauncher.connectionForTest();
|
||||
const connection = this._browserLauncher?.connectionForTest();
|
||||
if (connection) {
|
||||
const disposable = connection.onDisconnected(() => {
|
||||
cb();
|
||||
|
|
|
@ -87,7 +87,16 @@ export async function run(): Promise<void> {
|
|||
runner.addFile(join(__dirname, 'console/consoleAPITest'));
|
||||
runner.addFile(join(__dirname, 'extension/nodeConfigurationProvidersTests'));
|
||||
|
||||
for (const file of glob.sync('**/*.test.js', { cwd: __dirname })) {
|
||||
const options = { cwd: __dirname };
|
||||
const files = glob.sync('**/*.test.js', options);
|
||||
|
||||
// Only run tests on supported platforms
|
||||
// https://nodejs.org/api/process.html#process_process_platform
|
||||
if (process.platform === 'win32') {
|
||||
files.push(...glob.sync('**/*.test.win.js', options));
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
runner.addFile(join(__dirname, file));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
allThreadsStopped : false
|
||||
description : Paused on debugger statement
|
||||
reason : pause
|
||||
threadId : <number>
|
||||
}
|
||||
<anonymous> @ ${workspaceFolder}/web/script.js:10:1
|
||||
{
|
||||
allThreadsStopped : false
|
||||
description : Paused on breakpoint
|
||||
reason : breakpoint
|
||||
threadId : <number>
|
||||
}
|
||||
bar @ ${workspaceFolder}/web/script.js:6:3
|
||||
foo @ ${workspaceFolder}/web/script.js:2:3
|
||||
<anonymous> @ ${workspaceFolder}/web/script.js:11:1
|
|
@ -0,0 +1,39 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
import { ITestHandle } from '../test';
|
||||
import Dap from '../../dap/api';
|
||||
import { itIntegrates } from '../testIntegrationUtils';
|
||||
import { IChromeLaunchConfiguration } from '../../configuration';
|
||||
import { DebugType } from '../../common/contributionUtils';
|
||||
|
||||
describe('webview breakpoints', () => {
|
||||
async function waitForPause(p: ITestHandle, cb?: (threadId: string) => Promise<void>) {
|
||||
const { threadId } = p.log(await p.dap.once('stopped'));
|
||||
await p.logger.logStackTrace(threadId);
|
||||
if (cb) await cb(threadId);
|
||||
return p.dap.continue({ threadId });
|
||||
}
|
||||
|
||||
itIntegrates('launched script', async ({ r }) => {
|
||||
// Breakpoint in separate script set after launch.
|
||||
const p = await r.launchUrl('script.html', ({
|
||||
type: DebugType.Edge,
|
||||
runtimeExecutable: r.workspacePath('webview/win/WebView2Sample.exe'),
|
||||
useWebView: true,
|
||||
// WebView2Sample.exe launches about:blank
|
||||
urlFilter: 'about:blank',
|
||||
// TODO: Test.launchUrl should support AnyLaunchConfiguration
|
||||
} as unknown) as IChromeLaunchConfiguration);
|
||||
p.load();
|
||||
await waitForPause(p, async () => {
|
||||
const source: Dap.Source = {
|
||||
path: p.workspacePath('web/script.js'),
|
||||
};
|
||||
await p.dap.setBreakpoints({ source, breakpoints: [{ line: 6 }] });
|
||||
});
|
||||
await waitForPause(p);
|
||||
p.assertLog();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,2 @@
|
|||
# User directories created by WebView application
|
||||
WebView2Sample.exe.WebView2*/
|
|
@ -0,0 +1,2 @@
|
|||
The WebView2Sample.exe is a Release x64 build.
|
||||
The source code can be found in `demos\webview`.
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Загрузка…
Ссылка в новой задаче