Skip to content

Commit

Permalink
Updated Node Detail Page in Scenegraph Node Inspector and Added Roku …
Browse files Browse the repository at this point in the history
…Test Automation Panel (#499)

* roku automation panel progress commit, standardize event messages to contain fields inside of `context`, add support for webview-ui-toolkit in webviews

* finish up roku automation panel

* remove unneeded code

* progress commit on new node detail page

* increase screenshotOutOfDateTimeOut to 10 seconds

* Move Autorun on deploy out of RokuAutomationView into view header

* Add support for getting persistent keypath in more spots

* Make RokuAutomatView layout responsive

* Add clear button and make run and stop buttons have fixed positions

* move NumberField to shared folder

* automation view improvements

* disable screenshotOutOfDate for now

* More cleanup of NodeDetailPage

* fix warning

* Store autorunOnDeploy in workspaceState

* fix reordering of steps

* Fix colorfield updating properly

* Fix NumberField stepping

* fix failing unit tests

* remove unneeded code

* Improvements discovered while reviewing with Bronley

---------

Co-authored-by: Brian Leighty <bleighty@tubi.tv>
Co-authored-by: Bronley Plumb <bronley@gmail.com>
  • Loading branch information
3 people committed Sep 11, 2023
1 parent 96dcd5a commit 9867d36
Show file tree
Hide file tree
Showing 33 changed files with 1,560 additions and 946 deletions.
966 changes: 435 additions & 531 deletions package-lock.json

Large diffs are not rendered by default.

52 changes: 51 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"pretty-bytes": "^5.6.0",
"roku-debug": "^0.20.3",
"roku-deploy": "^3.10.3",
"roku-test-automation": "^2.0.0-beta.19",
"roku-test-automation": "^2.0.0-beta.20",
"semver": "^7.1.3",
"source-map": "^0.7.3",
"thenby": "^1.3.4",
Expand Down Expand Up @@ -214,6 +214,11 @@
"id": "rokuCommandsView",
"name": "Roku Commands",
"type": "webview"
},
{
"id": "rokuAutomationView",
"name": "Roku Automation",
"type": "webview"
}
]
},
Expand Down Expand Up @@ -290,6 +295,26 @@
"command": "extension.brightscript.rokuDeviceView.disableNodeInspector",
"when": "view == rokuDeviceView && brightscript.isOnDeviceComponentAvailable && brightscript.rokuDeviceView.isInspectingNodes",
"group": "navigation@3"
},
{
"command": "extension.brightscript.rokuAutomationView.startRecording",
"when": "view == rokuAutomationView && !brightscript.rokuAutomationView.isRecording",
"group": "navigation"
},
{
"command": "extension.brightscript.rokuAutomationView.stopRecording",
"when": "view == rokuAutomationView && brightscript.rokuAutomationView.isRecording",
"group": "navigation"
},
{
"command": "extension.brightscript.rokuAutomationView.enableAutorunOnDeploy",
"when": "view == rokuAutomationView && !brightscript.rokuAutomationView.autorunOnDeploy",
"group": "navigation@2"
},
{
"command": "extension.brightscript.rokuAutomationView.disableAutorunOnDeploy",
"when": "view == rokuAutomationView && brightscript.rokuAutomationView.autorunOnDeploy",
"group": "navigation@2"
}
],
"commandPalette": []
Expand Down Expand Up @@ -2676,6 +2701,31 @@
"category": "BrighterScript",
"icon": "$(arrow-up)"
},
{
"command": "extension.brightscript.rokuAutomationView.startRecording",
"title": "Start Recording",
"category": "BrighterScript",
"icon": "$(record)"
},
{
"command": "extension.brightscript.rokuAutomationView.stopRecording",
"title": "Stop Recording",
"category": "BrighterScript",
"icon": "$(debug-stop)"
},

{
"command": "extension.brightscript.rokuAutomationView.enableAutorunOnDeploy",
"title": "Enable Autorun on deploy",
"category": "BrighterScript",
"icon": "$(pass)"
},
{
"command": "extension.brightscript.rokuAutomationView.disableAutorunOnDeploy",
"title": "Disable autorun on deploy",
"category": "BrighterScript",
"icon": "$(pass-filled)"
},
{
"command": "extension.brightscript.languageServer.restart",
"title": "Restart Language Server",
Expand Down
18 changes: 15 additions & 3 deletions src/BrightScriptCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class BrightScriptCommands {

private fileUtils: BrightScriptFileUtils;
private host: string;
private keypressNotifiers = [] as ((key: string, literalCharacter: boolean) => void)[];

public registerCommands() {

Expand Down Expand Up @@ -306,7 +307,15 @@ export class BrightScriptCommands {
}
}

public async sendRemoteCommand(key: string, host?: string) {
public async sendRemoteCommand(key: string, host?: string, literalCharacter = false) {
for (const notifier of this.keypressNotifiers) {
notifier(key, literalCharacter);
}

if (literalCharacter) {
key = 'Lit_' + encodeURIComponent(key);
}

// do we have a temporary override?
if (!host) {
// Get the long lived host ip
Expand Down Expand Up @@ -356,14 +365,17 @@ export class BrightScriptCommands {
}
}

public registerKeypressNotifier(notifier: (key: string, literalCharacter: boolean) => void) {
this.keypressNotifiers.push(notifier);
}

private registerCommand(name: string, callback: (...args: any[]) => any, thisArg?: any) {
const prefix = 'extension.brightscript.';
const commandName = name.startsWith(prefix) ? name : prefix + name;
this.context.subscriptions.push(vscode.commands.registerCommand(commandName, callback, thisArg));
}

private async sendAsciiToDevice(character: string) {
let commandToSend: string = 'Lit_' + encodeURIComponent(character);
await this.sendRemoteCommand(commandToSend);
await this.sendRemoteCommand(character, undefined, true);
}
}
8 changes: 7 additions & 1 deletion src/commands/VscodeCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,11 @@ export enum VscodeCommand {
rokuRegistryExportRegistry = 'extension.brightscript.rokuRegistry.exportRegistry',
rokuRegistryImportRegistry = 'extension.brightscript.rokuRegistry.importRegistry',
rokuRegistryClearRegistry = 'extension.brightscript.rokuRegistry.clearRegistry',
rokuRegistryRefreshRegistry = 'extension.brightscript.rokuRegistry.refreshRegistry'
rokuRegistryRefreshRegistry = 'extension.brightscript.rokuRegistry.refreshRegistry',
rokuAutomationViewEnableAutorunOnDeploy = 'extension.brightscript.rokuAutomationView.enableAutorunOnDeploy',
rokuAutomationViewDisableAutorunOnDeploy = 'extension.brightscript.rokuAutomationView.disableAutorunOnDeploy',
rokuAutomationViewStartRecording = 'extension.brightscript.rokuAutomationView.startRecording',
rokuAutomationViewStopRecording = 'extension.brightscript.rokuAutomationView.stopRecording',
enableRemoteControlMode = 'extension.brightscript.enableRemoteControlMode',
disableRemoteControlMode = 'extension.brightscript.disableRemoteControlMode'
}
28 changes: 7 additions & 21 deletions src/extension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,6 @@ describe('extension', () => {
beforeEach(() => {
sinon.stub(languageServerManager, 'init').returns(Promise.resolve());

context = {
extensionPath: '',
subscriptions: [],
asAbsolutePath: () => { },
globalState: {
get: () => {

},
update: () => {

}
}
};

originalWebviews = extensionInstance['webviews'];
extensionInstance['webviews'] = [];
});
Expand All @@ -54,48 +40,48 @@ describe('extension', () => {
it('registers configuration provider', async () => {
let spy = sinon.spy(vscode.debug, 'registerDebugConfigurationProvider');
expect(spy.calledOnce).to.be.false;
await extension.activate(context);
await extension.activate(vscode.context);
expect(spy.calledOnce).to.be.true;
});

it('registers formatter', async () => {
let spy = sinon.spy(vscode.languages, 'registerDocumentRangeFormattingEditProvider');
expect(spy.getCalls().length).to.equal(0);
await extension.activate(context);
await extension.activate(vscode.context);
expect(spy.getCalls().length).to.be.greaterThan(1);
});

it('registers definition provider', async () => {
let spy = sinon.spy(vscode.languages, 'registerDefinitionProvider');
expect(spy.calledOnce).to.be.false;
await extension.activate(context);
await extension.activate(vscode.context);
expect(spy.callCount).to.be.greaterThan(0);
});

it('registers all commands', async () => {
let stub = sinon.stub(BrightScriptCommands.prototype, 'registerCommands').callsFake(() => { });
await extension.activate(context);
await extension.activate(vscode.context);
expect(stub.callCount).to.equal(1);
});

it('registers onDidStartDebugSession', async () => {
let spy = sinon.spy(vscode.debug, 'onDidStartDebugSession');
expect(spy.calledOnce).to.be.false;
await extension.activate(context);
await extension.activate(vscode.context);
expect(spy.calledOnce).to.be.true;
});

it('registers onDidTerminateDebugSession', async () => {
let spy = sinon.spy(vscode.debug, 'onDidTerminateDebugSession');
expect(spy.calledOnce).to.be.false;
await extension.activate(context);
await extension.activate(vscode.context);
expect(spy.calledOnce).to.be.true;
});

it('registers onDidReceiveDebugSessionCustomEvent', async () => {
let spy = sinon.spy(vscode.debug, 'onDidReceiveDebugSessionCustomEvent');
expect(spy.calledOnce).to.be.false;
await extension.activate(context);
await extension.activate(vscode.context);
expect(spy.getCalls().length).to.be.greaterThan(0);
});
});
2 changes: 1 addition & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class Extension {
);

this.rtaManager = new RtaManager();
this.webviewViewProviderManager = new WebviewViewProviderManager(context, this.rtaManager);
this.webviewViewProviderManager = new WebviewViewProviderManager(context, this.rtaManager, this.brightScriptCommands);
this.rtaManager.setWebviewViewProviderManager(this.webviewViewProviderManager);

//update the tracked version of the extension
Expand Down
6 changes: 6 additions & 0 deletions src/managers/RemoteControlManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from 'vscode';
import { vscodeContextManager } from './VscodeContextManager';
import type { TelemetryManager } from './TelemetryManager';
import { VscodeCommand } from '../commands/VscodeCommand';

export class RemoteControlManager {
constructor(
Expand Down Expand Up @@ -70,6 +71,11 @@ export class RemoteControlManager {
}

public async setRemoteControlMode(isEnabled: boolean, initiator: RemoteControlModeInitiator) {
if (this.isEnabled && !isEnabled) {
// Want to also stop Roku automation recording if it was running
await vscode.commands.executeCommand(VscodeCommand.rokuAutomationViewStopRecording);
}

//only send a telemetry event if we know who initiated the mode. `undefined` usually means our internal system set the value...so don't track that
if (initiator) {
this.telemetryManager.sendSetRemoteControlModeEvent(isEnabled, initiator);
Expand Down
8 changes: 6 additions & 2 deletions src/managers/RtaManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export class RtaManager {

public setupRtaWithConfig(config: { host: string; password: string; logLevel?: string; disableScreenSaver?: boolean; injectRdbOnDeviceComponent?: boolean }) {
const enableDebugging = ['info', 'debug', 'trace'].includes(config.logLevel);
rta.odc.setConfig({
const rtaConfig: rta.ConfigOptions = {
RokuDevice: {
devices: [{
host: config.host,
Expand All @@ -26,7 +26,11 @@ export class RtaManager {
disableTelnet: true,
disableCallOriginationLine: true
}
});
};

rta.odc.setConfig(rtaConfig);

rta.ecp.setConfig(rtaConfig);

this.device = rta.device;

Expand Down
11 changes: 7 additions & 4 deletions src/managers/WebviewViewProviderManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { vscode } from '../mockVscode.spec';
import type { BrightScriptLaunchConfiguration } from '../DebugConfigurationProvider';
import { WebviewViewProviderManager } from './WebviewViewProviderManager';
import { RtaManager } from './RtaManager';
import { BrightScriptCommands } from '../BrightScriptCommands';


const sinon = createSandbox();
Expand All @@ -14,6 +15,7 @@ describe('WebviewViewProviderManager', () => {
const config = {} as BrightScriptLaunchConfiguration;
let webviewViewProviderManager: WebviewViewProviderManager;
let rtaManager: RtaManager;
const brightScriptCommands = new BrightScriptCommands({} as any, {} as any, {} as any, {} as any);

before(() => {
context = {
Expand Down Expand Up @@ -46,16 +48,17 @@ describe('WebviewViewProviderManager', () => {
before(() => {
spy = sinon.spy(vscode.window, 'registerWebviewViewProvider');
rtaManager = new RtaManager();
webviewViewProviderManager = new WebviewViewProviderManager(context, rtaManager);
webviewViewProviderManager = new WebviewViewProviderManager(context, rtaManager, brightScriptCommands);
});

it('initializes webview providers and calls registerWebviewViewProvider for each', () => {
expect(spy.callCount).to.equal(webviewViewProviderManager.getWebviewViewProviders().length);
});

it('assigns RtaManager to each webviewViewProvider', () => {
it('assigns dependencies to each webviewViewProvider', () => {
for (const webviewViewProvider of webviewViewProviderManager.getWebviewViewProviders()) {
expect(webviewViewProvider['rtaManager']).to.equal(rtaManager);
expect(webviewViewProvider['dependencies']['rtaManager']).to.equal(rtaManager);
expect(webviewViewProvider['dependencies']['brightScriptCommands']).to.equal(brightScriptCommands);
}
expect(spy.callCount).to.equal(webviewViewProviderManager.getWebviewViewProviders().length);
});
Expand All @@ -74,7 +77,7 @@ describe('WebviewViewProviderManager', () => {
};

rtaManager = new RtaManager();
webviewViewProviderManager = new WebviewViewProviderManager(context, rtaManager);
webviewViewProviderManager = new WebviewViewProviderManager(context, rtaManager, brightScriptCommands);
rtaManager.setWebviewViewProviderManager(webviewViewProviderManager);
});

Expand Down
28 changes: 18 additions & 10 deletions src/managers/WebviewViewProviderManager.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
import type { ChannelPublishedEvent } from 'roku-debug';
import type { BrightScriptLaunchConfiguration } from '../DebugConfigurationProvider';
import type { RtaManager } from './RtaManager';
import type { BrightScriptCommands } from '../BrightScriptCommands';
import * as vscode from 'vscode';
import { RokuCommandsViewProvider } from '../viewProviders/RokuCommandsViewProvider';
import { RokuDeviceViewViewProvider } from '../viewProviders/RokuDeviceViewViewProvider';
import { RokuRegistryViewProvider } from '../viewProviders/RokuRegistryViewProvider';
import { SceneGraphInspectorViewProvider } from '../viewProviders/SceneGraphInspectorViewProvider';

import { RokuAutomationViewViewProvider } from '../viewProviders/RokuAutomationViewViewProvider';

export class WebviewViewProviderManager {
constructor(context: vscode.ExtensionContext, rtaManager: RtaManager) {
this.rtaManager = rtaManager;
constructor(
private context: vscode.ExtensionContext,
private rtaManager: RtaManager,
private brightScriptCommands: BrightScriptCommands
) {

for (const webview of this.webviewViews) {
if (!webview.provider) {
webview.provider = new webview.constructor(context);
webview.provider = new webview.constructor(context, {
rtaManager: rtaManager,
brightScriptCommands: brightScriptCommands
});
vscode.window.registerWebviewViewProvider(webview.provider.id, webview.provider);

webview.provider.setWebviewViewProviderManager(this);

if (typeof webview.provider.setRtaManager === 'function') {
webview.provider.setRtaManager(this.rtaManager);
}
}
}
}

private rtaManager?: RtaManager;

private webviewViews = [{
constructor: SceneGraphInspectorViewProvider,
provider: undefined as SceneGraphInspectorViewProvider
Expand All @@ -40,6 +41,9 @@ export class WebviewViewProviderManager {
}, {
constructor: RokuDeviceViewViewProvider,
provider: undefined as RokuDeviceViewViewProvider
}, {
constructor: RokuAutomationViewViewProvider,
provider: undefined as RokuAutomationViewViewProvider
}];

public getWebviewViewProviders() {
Expand All @@ -54,6 +58,10 @@ export class WebviewViewProviderManager {
public onChannelPublishedEvent(e: ChannelPublishedEvent) {
const config = e.body.launchConfiguration as BrightScriptLaunchConfiguration;
this.rtaManager.setupRtaWithConfig(config);

for (const webview of this.webviewViews) {
void webview.provider.onChannelPublishedEvent(e);
}
}

// Mainly for communicating between webviews
Expand Down
10 changes: 9 additions & 1 deletion src/mockVscode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,16 @@ export let vscode = {
return this._data[key];
}
} as any,
workspaceState: {
_data: {},
update: function(key: string, value: any) {
this._data[key] = value;
},
get: function(key: string) {
return this._data[key];
}
} as any,
globalStorageUri: undefined as Uri,
workspaceState: {} as any,
environmentVariableCollection: {} as any,
logUri: undefined as Uri,
logPath: '',
Expand Down
Loading

0 comments on commit 9867d36

Please sign in to comment.