Browse Source

Merge pull request #4418 from rstm-sf/feature/add_handling_input_message_previewer

Add handling input events in web previewer
pull/4990/head
Nikita Tsukanov 6 years ago
committed by GitHub
parent
commit
7fdaa97ca2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      .editorconfig
  2. 4
      build.ps1
  3. 16
      nukebuild/Build.cs
  4. 2
      nukebuild/BuildParameters.cs
  5. 84
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs
  6. 41
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx
  7. 11
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputEventMessageBase.ts
  8. 9
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputModifiers.ts
  9. 6
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseButton.ts
  10. 39
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts
  11. 13
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts
  12. 12
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts
  13. 17
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts
  14. 17
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts
  15. 17
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage.ts
  16. 6
      src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts
  17. 3
      tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/.gitignore
  18. 94
      tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/Models/InputEventTests.ts
  19. 2414
      tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json
  20. 26
      tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json
  21. 12
      tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/tsconfig.json

3
.editorconfig

@ -156,6 +156,9 @@ indent_size = 2
[*.{props,targets,config,nuspec}] [*.{props,targets,config,nuspec}]
indent_size = 2 indent_size = 2
[*.json]
indent_size = 2
# Shell scripts # Shell scripts
[*.sh] [*.sh]
end_of_line = lf end_of_line = lf

4
build.ps1

@ -43,7 +43,7 @@ if (Test-Path $DotNetGlobalFile) {
} }
# If dotnet is installed locally, and expected version is not set or installation matches the expected version # If dotnet is installed locally, and expected version is not set or installation matches the expected version
if ((Get-Command "dotnet" -ErrorAction SilentlyContinue) -ne $null -and ` if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and `
(!(Test-Path variable:DotNetVersion) -or $(& dotnet --version) -eq $DotNetVersion)) { (!(Test-Path variable:DotNetVersion) -or $(& dotnet --version) -eq $DotNetVersion)) {
$env:DOTNET_EXE = (Get-Command "dotnet").Path $env:DOTNET_EXE = (Get-Command "dotnet").Path
} }
@ -53,7 +53,7 @@ else {
# Download install script # Download install script
$DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1"
md -force $TempDirectory > $null mkdir -force $TempDirectory > $null
(New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile)
# Install by channel or version # Install by channel or version

16
nukebuild/Build.cs

@ -233,6 +233,21 @@ partial class Build : NukeBuild
} }
} }
Target RunHtmlPreviewerTests => _ => _
.DependsOn(CompileHtmlPreviewer)
.OnlyWhenStatic(() => !(Parameters.SkipPreviewer || Parameters.SkipTests))
.Executes(() =>
{
var webappTestDir = RootDirectory / "tests" / "Avalonia.DesignerSupport.Tests" / "Remote" / "HtmlTransport" / "webapp";
NpmTasks.NpmInstall(c => c
.SetWorkingDirectory(webappTestDir)
.SetArgumentConfigurator(a => a.Add("--silent")));
NpmTasks.NpmRun(c => c
.SetWorkingDirectory(webappTestDir)
.SetCommand("test"));
});
Target RunCoreLibsTests => _ => _ Target RunCoreLibsTests => _ => _
.OnlyWhenStatic(() => !Parameters.SkipTests) .OnlyWhenStatic(() => !Parameters.SkipTests)
.DependsOn(Compile) .DependsOn(Compile)
@ -332,6 +347,7 @@ partial class Build : NukeBuild
.DependsOn(RunCoreLibsTests) .DependsOn(RunCoreLibsTests)
.DependsOn(RunRenderTests) .DependsOn(RunRenderTests)
.DependsOn(RunDesignerTests) .DependsOn(RunDesignerTests)
.DependsOn(RunHtmlPreviewerTests)
.DependsOn(RunLeakTests); .DependsOn(RunLeakTests);
Target Package => _ => _ Target Package => _ => _

2
nukebuild/BuildParameters.cs

@ -62,7 +62,7 @@ public partial class Build
public AbsolutePath ZipTargetControlCatalogDesktopDir { get; } public AbsolutePath ZipTargetControlCatalogDesktopDir { get; }
public BuildParameters(Build b) public BuildParameters(Build b)
{ {
// ARGUMENTS // ARGUMENTS
Configuration = b.Configuration ?? "Release"; Configuration = b.Configuration ?? "Release";

84
src/Avalonia.DesignerSupport/Remote/HtmlTransport/HtmlTransport.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq; using System.Linq;
@ -9,6 +10,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Remote.Protocol; using Avalonia.Remote.Protocol;
using Avalonia.Remote.Protocol.Viewport; using Avalonia.Remote.Protocol.Viewport;
using InputProtocol = Avalonia.Remote.Protocol.Input;
namespace Avalonia.DesignerSupport.Remote.HtmlTransport namespace Avalonia.DesignerSupport.Remote.HtmlTransport
{ {
@ -115,14 +117,11 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
while (true) while (true)
{ {
var msg = await socket.ReceiveMessage().ConfigureAwait(false); var msg = await socket.ReceiveMessage().ConfigureAwait(false);
if(msg == null) if(msg != null && msg.IsText)
return;
if (msg.IsText)
{ {
var s = Encoding.UTF8.GetString(msg.Data); var message = ParseMessage(msg.AsString());
var parts = s.Split(':'); if (message != null)
if (parts[0] == "frame-received") _onMessage?.Invoke(this, message);
_onMessage?.Invoke(this, new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) });
} }
} }
} }
@ -181,7 +180,6 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
_pendingSocket?.Dispose(); _pendingSocket?.Dispose();
_simpleServer.Dispose(); _simpleServer.Dispose();
} }
public Task Send(object data) public Task Send(object data)
{ {
@ -264,5 +262,75 @@ namespace Avalonia.DesignerSupport.Remote.HtmlTransport
_onException?.Invoke(this, ex); _onException?.Invoke(this, ex);
} }
#endregion #endregion
private static object ParseMessage(string message)
{
var parts = message.Split(':');
var key = parts[0];
if (key.Equals("frame-received", StringComparison.OrdinalIgnoreCase))
{
return new FrameReceivedMessage { SequenceId = long.Parse(parts[1]) };
}
else if (key.Equals("pointer-released", StringComparison.OrdinalIgnoreCase))
{
return new InputProtocol.PointerReleasedEventMessage
{
Modifiers = ParseInputModifiers(parts[1]),
X = ParseDouble(parts[2]),
Y = ParseDouble(parts[3]),
Button = ParseMouseButton(parts[4]),
};
}
else if (key.Equals("pointer-pressed", StringComparison.OrdinalIgnoreCase))
{
return new InputProtocol.PointerPressedEventMessage
{
Modifiers = ParseInputModifiers(parts[1]),
X = ParseDouble(parts[2]),
Y = ParseDouble(parts[3]),
Button = ParseMouseButton(parts[4]),
};
}
else if (key.Equals("pointer-moved", StringComparison.OrdinalIgnoreCase))
{
return new InputProtocol.PointerMovedEventMessage
{
Modifiers = ParseInputModifiers(parts[1]),
X = ParseDouble(parts[2]),
Y = ParseDouble(parts[3]),
};
}
else if (key.Equals("scroll", StringComparison.OrdinalIgnoreCase))
{
return new InputProtocol.ScrollEventMessage
{
Modifiers = ParseInputModifiers(parts[1]),
X = ParseDouble(parts[2]),
Y = ParseDouble(parts[3]),
DeltaX = ParseDouble(parts[4]),
DeltaY = ParseDouble(parts[5]),
};
}
return null;
}
private static InputProtocol.InputModifiers[] ParseInputModifiers(string modifiersText) =>
string.IsNullOrWhiteSpace(modifiersText)
? null
: modifiersText
.Split(',')
.Select(x => (InputProtocol.InputModifiers)Enum.Parse(
typeof(InputProtocol.InputModifiers), x, true))
.ToArray();
private static InputProtocol.MouseButton ParseMouseButton(string buttonText) =>
string.IsNullOrWhiteSpace(buttonText)
? InputProtocol.MouseButton.None
: (InputProtocol.MouseButton)Enum.Parse(
typeof(InputProtocol.MouseButton), buttonText, true);
private static double ParseDouble(string text) =>
double.Parse(text, NumberStyles.Float, CultureInfo.InvariantCulture);
} }
} }

41
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/FramePresenter.tsx

@ -1,5 +1,9 @@
import {PreviewerFrame, PreviewerServerConnection} from "src/PreviewerServerConnection";
import * as React from "react"; import * as React from "react";
import {PreviewerFrame, PreviewerServerConnection} from "src/PreviewerServerConnection";
import {PointerPressedEventMessage} from "src/Models/Input/PointerPressedEventMessage";
import {PointerReleasedEventMessage} from "src/Models/Input/PointerReleasedEventMessage";
import {PointerMovedEventMessage} from "src/Models/Input/PointerMovedEventMessage";
import {ScrollEventMessage} from "src/Models/Input/ScrollEventMessage";
interface PreviewerPresenterProps { interface PreviewerPresenterProps {
conn: PreviewerServerConnection; conn: PreviewerServerConnection;
@ -15,6 +19,11 @@ export class PreviewerPresenter extends React.Component<PreviewerPresenterProps>
this.componentDidUpdate({ this.componentDidUpdate({
conn: null! conn: null!
}, this.state); }, this.state);
this.handleMouseDown = this.handleMouseDown.bind(this);
this.handleMouseUp = this.handleMouseUp.bind(this);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.handleWheel = this.handleWheel.bind(this);
} }
componentDidMount(): void { componentDidMount(): void {
@ -51,7 +60,35 @@ export class PreviewerPresenter extends React.Component<PreviewerPresenterProps>
} }
} }
handleMouseDown(e: React.MouseEvent) {
e.preventDefault();
const pointerPressedEventMessage = new PointerPressedEventMessage(e);
this.props.conn.sendMouseEvent(pointerPressedEventMessage);
}
handleMouseUp(e: React.MouseEvent) {
e.preventDefault();
const pointerReleasedEventMessage = new PointerReleasedEventMessage(e);
this.props.conn.sendMouseEvent(pointerReleasedEventMessage);
}
handleMouseMove(e: React.MouseEvent) {
e.preventDefault();
const pointerMovedEventMessage = new PointerMovedEventMessage(e);
this.props.conn.sendMouseEvent(pointerMovedEventMessage);
}
handleWheel(e: React.WheelEvent) {
e.preventDefault();
const scrollEventMessage = new ScrollEventMessage(e);
this.props.conn.sendMouseEvent(scrollEventMessage);
}
render() { render() {
return <canvas ref={this.canvasRef}/> return <canvas ref={this.canvasRef}
onMouseDown={this.handleMouseDown}
onMouseUp={this.handleMouseUp}
onMouseMove={this.handleMouseMove}
onWheel={this.handleWheel} />
} }
} }

11
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputEventMessageBase.ts

@ -0,0 +1,11 @@
import * as React from "react";
import {InputModifiers} from "./InputModifiers";
import {getModifiers} from "./MouseEventHelpers";
export abstract class InputEventMessageBase {
public readonly modifiers : Array<InputModifiers>;
protected constructor(e: React.MouseEvent) {
this.modifiers = getModifiers(e);
}
}

9
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputModifiers.ts

@ -0,0 +1,9 @@
export enum InputModifiers {
Alt,
Control,
Shift,
Windows,
LeftMouseButton,
RightMouseButton,
MiddleMouseButton,
}

6
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseButton.ts

@ -0,0 +1,6 @@
export enum MouseButton {
None,
Left,
Right,
Middle,
}

39
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers.ts

@ -0,0 +1,39 @@
import * as React from "react";
import {InputModifiers} from "./InputModifiers";
import {MouseButton} from "./MouseButton";
export function getModifiers(e: React.MouseEvent): Array<InputModifiers> {
let modifiers : Array<InputModifiers> = [];
if (e.altKey)
modifiers.push(InputModifiers.Alt);
if (e.ctrlKey)
modifiers.push(InputModifiers.Control);
if (e.shiftKey)
modifiers.push(InputModifiers.Shift);
if (e.metaKey)
modifiers.push(InputModifiers.Windows);
if (e.buttons != 0) {
if ((e.buttons & 1) != 0)
modifiers.push(InputModifiers.LeftMouseButton);
if ((e.buttons & 2) != 0)
modifiers.push(InputModifiers.RightMouseButton);
if ((e.buttons & 4) != 0)
modifiers.push(InputModifiers.MiddleMouseButton);
}
return modifiers;
}
export function getMouseButton(e: React.MouseEvent) : MouseButton {
if (e.button == 0) {
return MouseButton.Left;
} else if (e.button == 1) {
return MouseButton.Middle;
} else if (e.button == 2) {
return MouseButton.Right;
} else {
return MouseButton.None;
}
}

13
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerEventMessageBase.ts

@ -0,0 +1,13 @@
import * as React from "react";
import {InputEventMessageBase} from "./InputEventMessageBase";
export abstract class PointerEventMessageBase extends InputEventMessageBase {
public readonly x: number;
public readonly y: number;
protected constructor(e: React.MouseEvent) {
super(e);
this.x = e.clientX;
this.y = e.clientY;
}
}

12
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage.ts

@ -0,0 +1,12 @@
import * as React from "react";
import {PointerEventMessageBase} from "./PointerEventMessageBase";
export class PointerMovedEventMessage extends PointerEventMessageBase {
constructor(e: React.MouseEvent) {
super(e);
}
public toString = () : string => {
return `pointer-moved:${this.modifiers}:${this.x}:${this.y}`;
}
}

17
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage.ts

@ -0,0 +1,17 @@
import * as React from "react";
import {PointerEventMessageBase} from "./PointerEventMessageBase";
import {MouseButton} from "./MouseButton";
import {getMouseButton} from "./MouseEventHelpers";
export class PointerPressedEventMessage extends PointerEventMessageBase {
public readonly button: MouseButton
constructor(e: React.MouseEvent) {
super(e);
this.button = getMouseButton(e);
}
public toString = () : string => {
return `pointer-pressed:${this.modifiers}:${this.x}:${this.y}:${this.button}`;
}
}

17
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage.ts

@ -0,0 +1,17 @@
import * as React from "react";
import {PointerEventMessageBase} from "./PointerEventMessageBase";
import {MouseButton} from "./MouseButton";
import {getMouseButton} from "./MouseEventHelpers";
export class PointerReleasedEventMessage extends PointerEventMessageBase {
public readonly button: MouseButton
constructor(e: React.MouseEvent) {
super(e);
this.button = getMouseButton(e);
}
public toString = () : string => {
return `pointer-released:${this.modifiers}:${this.x}:${this.y}:${this.button}`;
}
}

17
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage.ts

@ -0,0 +1,17 @@
import * as React from "react";
import {PointerEventMessageBase} from "./PointerEventMessageBase";
export class ScrollEventMessage extends PointerEventMessageBase {
public readonly deltaX: number;
public readonly deltaY: number;
constructor(e: React.WheelEvent) {
super(e);
this.deltaX = -e.deltaX;
this.deltaY = -e.deltaY;
}
public toString = () : string => {
return `scroll:${this.modifiers}:${this.x}:${this.y}:${this.deltaX}:${this.deltaY}`;
}
}

6
src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/PreviewerServerConnection.ts

@ -1,3 +1,5 @@
import { InputEventMessageBase } from "src/Models/Input/InputEventMessageBase";
export interface PreviewerFrame { export interface PreviewerFrame {
data: ImageData; data: ImageData;
dpiX: number; dpiX: number;
@ -28,6 +30,10 @@ export class PreviewerServerConnection {
this.handlers.delete(listener); this.handlers.delete(listener);
} }
public sendMouseEvent(message: InputEventMessageBase) {
this.conn.send(message.toString());
}
constructor(uri: string) { constructor(uri: string) {
this.currentFrame = null; this.currentFrame = null;
var conn = this.conn = new WebSocket(uri); var conn = this.conn = new WebSocket(uri);

3
tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/.gitignore

@ -0,0 +1,3 @@
build
node_modules
.nyc_output

94
tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/Models/InputEventTests.ts

@ -0,0 +1,94 @@
import { describe } from 'mocha';
import { expect } from 'chai';
import { Mock } from "moq.ts";
import { MouseEvent, WheelEvent } from "react";
import { InputModifiers } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/InputModifiers";
import { MouseButton } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseButton";
import { PointerMovedEventMessage } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerMovedEventMessage";
import { PointerPressedEventMessage } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerPressedEventMessage";
import { PointerReleasedEventMessage } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/PointerReleasedEventMessage";
import { ScrollEventMessage } from "../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/ScrollEventMessage";
import { getModifiers, getMouseButton } from '../../../../../../src/Avalonia.DesignerSupport/Remote/HtmlTransport/webapp/src/Models/Input/MouseEventHelpers';
describe("Input event tests", () => {
describe("Helpers", () => {
it("getModifiers", () => {
const event = new Mock<MouseEvent>()
.setup(x => x.altKey).returns(false)
.setup(x => x.ctrlKey).returns(true)
.setup(x => x.shiftKey).returns(false)
.setup(x => x.metaKey).returns(false)
.setup(x => x.buttons).returns(1)
.object()
var actual = getModifiers(event)
expect(actual)
.eql([InputModifiers.Control, InputModifiers.LeftMouseButton])
})
it("getMouseButton", () => {
const event = new Mock<MouseEvent>()
.setup(x => x.button).returns(1)
.object()
var actual = getMouseButton(event)
expect(actual)
.equal(MouseButton.Middle)
})
})
describe("Messages", () => {
const x = .3
const y = .42
const modifiers = "0,1,2,3,4,5,6"
const button = "1"
const deltaX = -3.
const deltaY = -3.
const mouseEvent = new Mock<MouseEvent>()
.setup(x => x.altKey).returns(true)
.setup(x => x.ctrlKey).returns(true)
.setup(x => x.shiftKey).returns(true)
.setup(x => x.metaKey).returns(true)
.setup(x => x.buttons).returns(7)
.setup(x => x.button).returns(0)
.setup(x => x.clientX).returns(x)
.setup(x => x.clientY).returns(y)
.object()
it("PointerMovedEventMessage", () => {
const message = new PointerMovedEventMessage(mouseEvent)
expect(message.toString())
.equal(`pointer-moved:${modifiers}:${x}:${y}`)
})
it("PointerPressedEventMessage", () => {
const message = new PointerPressedEventMessage(mouseEvent)
expect(message.toString())
.equal(`pointer-pressed:${modifiers}:${x}:${y}:${button}`)
})
it("PointerReleasedEventMessage", () => {
const message = new PointerReleasedEventMessage(mouseEvent)
expect(message.toString())
.equal(`pointer-released:${modifiers}:${x}:${y}:${button}`)
})
it("ScrollEventMessage", () => {
const wheelEvent = new Mock<WheelEvent>()
.setup(x => x.altKey).returns(true)
.setup(x => x.ctrlKey).returns(true)
.setup(x => x.shiftKey).returns(true)
.setup(x => x.metaKey).returns(true)
.setup(x => x.buttons).returns(7)
.setup(x => x.clientX).returns(x)
.setup(x => x.clientY).returns(y)
.setup(x => x.deltaX).returns(-deltaX)
.setup(x => x.deltaY).returns(-deltaY)
.object()
const message = new ScrollEventMessage(wheelEvent)
expect(message.toString())
.equal(`scroll:${modifiers}:${x}:${y}:${deltaX}:${deltaY}`)
})
})
})

2414
tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package-lock.json

File diff suppressed because it is too large

26
tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/package.json

@ -0,0 +1,26 @@
{
"name": "simple-test",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "mocha -r ts-node/register ./**/*Tests.ts",
"coverage": "nyc -r text -e .ts -x \"./*Tests.ts\" npm run test"
},
"type": "module",
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {},
"devDependencies": {
"@types/chai": "4.2.12",
"@types/mocha": "8.0.3",
"@types/react": "^16.3.14",
"chai": "^4.2.0",
"mocha": "^8.1.3",
"moq.ts": "^6.4.0",
"nyc": "^15.1.0",
"react": "^16.3.2",
"ts-node": "^9.0.0",
"typescript": "^4.0.2"
}
}

12
tests/Avalonia.DesignerSupport.Tests/Remote/HtmlTransport/webapp/tsconfig.json

@ -0,0 +1,12 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"sourceMap": true,
"strict": true,
"esModuleInterop": true
},
"exclude": [
"node_modules"
]
}
Loading…
Cancel
Save