Skip to content

Commit

Permalink
Show "submit issue" button, silence telnet after 2 clicks
Browse files Browse the repository at this point in the history
  • Loading branch information
TwitchBronBron committed Nov 6, 2023
1 parent 33fc5b8 commit 5814d18
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 39 deletions.
49 changes: 43 additions & 6 deletions src/DebugConfigurationProvider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type { RokuDeviceDetails } from './ActiveDeviceManager';
import { ActiveDeviceManager } from './ActiveDeviceManager';
import { rokuDeploy } from 'roku-deploy';
import { GlobalStateManager } from './GlobalStateManager';
import { util } from './util';

const sinon = createSandbox();
const Module = require('module');
Expand Down Expand Up @@ -464,7 +465,7 @@ describe('BrightScriptConfigurationProvider', () => {
let value: string;
let stub: SinonStub;
beforeEach(() => {
stub = sinon.stub(vscode.window, 'showWarningMessage').callsFake(() => {
stub = sinon.stub(vscode.window, 'showInformationMessage').callsFake(() => {
return Promise.resolve(value) as any;
});
});
Expand All @@ -477,14 +478,18 @@ describe('BrightScriptConfigurationProvider', () => {

it('sets true and flips global state when clicked "okay"', async () => {
value = `Okay (and dont warn again)`;
expect(globalStateManager.suppressDebugProtocolAutoEnabledMessage).to.eql(false);
expect(globalStateManager.debugProtocolPopupSnoozeUntilDate).to.eql(undefined);
const config = await configProvider['processEnableDebugProtocolParameter']({} as any, { softwareVersion: '12.5.0' });
expect(config.enableDebugProtocol).to.eql(true);
expect(globalStateManager.suppressDebugProtocolAutoEnabledMessage).to.eql(true);
//2 weeks after now
expect(
globalStateManager.debugProtocolPopupSnoozeUntilDate.getTime()
).closeTo(Date.now() + (14 * 24 * 60 * 60 * 1000), 1000);
expect(globalStateManager.debugProtocolPopupSnoozeValue).to.eql(true);
});

it('sets false when clicked "No, use the telnet debugger"', async () => {
value = 'No, use the telnet debugger';
value = 'Use telnet';
const config = await configProvider['processEnableDebugProtocolParameter']({} as any, { softwareVersion: '12.5.0' });
expect(config.enableDebugProtocol).to.eql(false);
});
Expand All @@ -500,12 +505,44 @@ describe('BrightScriptConfigurationProvider', () => {
expect(ex?.message).to.eql('Debug session cancelled');
});

it('sets to true and does not prompt when "dont show again" was clicked"', async () => {
it('sets to true and does not prompt when "dont show again" was clicked', async () => {
value = `Okay (and dont warn again)`;
globalStateManager.suppressDebugProtocolAutoEnabledMessage = true;
globalStateManager.debugProtocolPopupSnoozeUntilDate = new Date(Date.now() + (60 * 1000));
globalStateManager.debugProtocolPopupSnoozeValue = true;
let config = await configProvider['processEnableDebugProtocolParameter']({} as any, { softwareVersion: '12.5.0' });
expect(config.enableDebugProtocol).to.eql(true);
expect(stub.called).to.be.false;
});

it('shows the alternate telnet prompt after 2 debug sessions', async () => {
value = `Use telnet`;

await configProvider['processEnableDebugProtocolParameter']({} as any, { softwareVersion: '12.5.0' });
expect(stub.getCall(stub.callCount - 1).args[4]).to.eql('Use telnet');

await configProvider['processEnableDebugProtocolParameter']({} as any, { softwareVersion: '12.5.0' });
expect(stub.getCall(stub.callCount - 1).args[4]).to.eql('Use telnet');

value = 'Use telnet (and ask less often)';
await configProvider['processEnableDebugProtocolParameter']({} as any, { softwareVersion: '12.5.0' });
expect(stub.getCall(stub.callCount - 1).args[4]).to.eql('Use telnet (and ask less often)');
});

it('shows the issue picker when selected', async () => {
value = `Report an issue`;
const reportStub = sinon.stub(util, 'openIssueReporter').returns(Promise.resolve());

try {
await configProvider['processEnableDebugProtocolParameter']({} as any, { softwareVersion: '12.5.0' });
} catch (e) { }

expect(reportStub.called).to.be.true;
});

it('turns truthy values into true', async () => {
value = `Report an issue`;
const config = await configProvider['processEnableDebugProtocolParameter']({ enableDebugProtocol: {} } as any, { softwareVersion: '12.5.0' });
expect(config.enableDebugProtocol).to.be.true;
});
});
});
47 changes: 35 additions & 12 deletions src/DebugConfigurationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,21 @@ export class BrightScriptDebugConfigurationProvider implements DebugConfiguratio

private configDefaults: any;

/**
* A counter used to track how often the user clicks the "use telnet" button in the popup
*/
private useTelnetCounter = 0;

/**
* Massage a debug configuration just before a debug session is being launched,
* e.g. add all missing attributes to the debug configuration.
*/
public async resolveDebugConfiguration(folder: WorkspaceFolder | undefined, config: BrightScriptLaunchConfiguration, token?: CancellationToken): Promise<BrightScriptLaunchConfiguration> {
let deviceInfo: DeviceInfo;
let result: BrightScriptLaunchConfiguration;
try {
// merge user and workspace settings into the config
let result = this.processUserWorkspaceSettings(config);
result = this.processUserWorkspaceSettings(config);

//force a specific staging folder path because sometimes this conflicts with bsconfig.json
result.stagingFolderPath = path.join('${outDir}/.roku-deploy-staging');
Expand Down Expand Up @@ -126,6 +132,7 @@ export class BrightScriptDebugConfigurationProvider implements DebugConfiguratio
//send telemetry about this debug session (don't worry, it gets sanitized...we're just checking if certain features are being used)
this.telemetryManager?.sendStartDebugSessionEvent(
this.processUserWorkspaceSettings(config) as any,
result,
deviceInfo
);
}
Expand All @@ -137,26 +144,42 @@ export class BrightScriptDebugConfigurationProvider implements DebugConfiguratio
return config;
}

if (this.globalStateManager.suppressDebugProtocolAutoEnabledMessage) {
config.enableDebugProtocol = true;
//auto-pick a value if user chose to snooze the popup
if (this.globalStateManager.debugProtocolPopupSnoozeUntilDate && this.globalStateManager.debugProtocolPopupSnoozeUntilDate > new Date()) {
config.enableDebugProtocol = this.globalStateManager.debugProtocolPopupSnoozeValue;
return config;
}

//enable the debug protocol by default if the user hasn't defined this prop, and the target RokuOS is 12.5 or greater
const result = await vscode.window.showWarningMessage(`We have just auto-enabled Roku's new debug protocol for this debug session. The debug protocol will soon become the default option without warning, so please be sure to notify us about any issues you encounter while using this feature during this testing phase.`, {
modal: true
}, 'Okay', `Okay (and dont warn again)`, 'No, use the telnet debugger');
//cancel
if (result === undefined) {
throw new Error('Debug session cancelled');
} else if (result === 'Okay') {
const result = await vscode.window.showInformationMessage('New Debug Protocol Enabled', {
modal: true,
detail: `We've activated Roku's debug protocol for this session. This will become the default choice in the future and may be implemented without additional notice. Your feedback during this testing phase is invaluable.`
}, 'Okay', `Okay (and dont warn again)`, this.useTelnetCounter < 2 ? 'Use telnet' : 'Use telnet (and ask less often)', 'Report an issue');


if (result === 'Okay') {
config.enableDebugProtocol = true;
} else if (result === `Okay (and dont warn again)`) {
config.enableDebugProtocol = true;
this.globalStateManager.suppressDebugProtocolAutoEnabledMessage = true;
} else if (result === 'No, use the telnet debugger') {
this.globalStateManager.debugProtocolPopupSnoozeValue = config.enableDebugProtocol;
//snooze for 2 weeks
this.globalStateManager.debugProtocolPopupSnoozeUntilDate = new Date(Date.now() + (14 * 24 * 60 * 60 * 1000));
} else if (result === 'Use telnet') {
this.useTelnetCounter++;
config.enableDebugProtocol = false;
} else if (result === 'Use telnet (and ask less often)') {
this.useTelnetCounter = 0;
config.enableDebugProtocol = false;
this.globalStateManager.debugProtocolPopupSnoozeValue = config.enableDebugProtocol;
//snooze for 12 hours
this.globalStateManager.debugProtocolPopupSnoozeUntilDate = new Date(Date.now() + (12 * 60 * 60 * 1000));
} else if (result === 'Report an issue') {
await util.openIssueReporter({ deviceInfo: deviceInfo });
throw new Error('Debug session cancelled');
} else {
throw new Error('Debug session cancelled');
}

return config;
}

Expand Down
27 changes: 19 additions & 8 deletions src/GlobalStateManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export class GlobalStateManager {
lastRunExtensionVersion: 'lastRunExtensionVersion',
lastSeenReleaseNotesVersion: 'lastSeenReleaseNotesVersion',
sendRemoteTextHistory: 'sendRemoteTextHistory',
suppressDebugProtocolAutoEnabledMessage: 'suppressDebugProtocolAutoEnabledMessage'
debugProtocolPopupSnoozeUntilDate: 'debugProtocolPopupSnoozeUntilDate',
debugProtocolPopupSnoozeValue: 'debugProtocolPopupSnoozeValue'
};
private remoteTextHistoryLimit: number;
private remoteTextHistoryEnabled: boolean;
Expand All @@ -37,16 +38,26 @@ export class GlobalStateManager {
void this.context.globalState.update(this.keys.lastSeenReleaseNotesVersion, value);
}

/**
* Should the "we auto-enabled the debug protocol for you" message be suppressed? Defaults to false.
*/
public get suppressDebugProtocolAutoEnabledMessage() {
return this.context.globalState.get<boolean>(this.keys.suppressDebugProtocolAutoEnabledMessage) === true;

public get debugProtocolPopupSnoozeUntilDate(): Date {
const epoch = this.context.globalState.get<number>(this.keys.debugProtocolPopupSnoozeUntilDate);
if (epoch) {
return new Date(epoch);
}
}
public set suppressDebugProtocolAutoEnabledMessage(value: boolean) {
void this.context.globalState.update(this.keys.suppressDebugProtocolAutoEnabledMessage, value);
public set debugProtocolPopupSnoozeUntilDate(value: Date) {
void this.context.globalState.update(this.keys.debugProtocolPopupSnoozeUntilDate, value?.getTime());
}


public get debugProtocolPopupSnoozeValue(): boolean {
return this.context.globalState.get<boolean>(this.keys.debugProtocolPopupSnoozeValue);
}
public set debugProtocolPopupSnoozeValue(value: boolean) {
void this.context.globalState.update(this.keys.debugProtocolPopupSnoozeValue, value);
}


public get sendRemoteTextHistory(): string[] {
return this.context.globalState.get(this.keys.sendRemoteTextHistory) ?? [];
}
Expand Down
8 changes: 8 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as fsExtra from 'fs-extra';

export let ROKU_DEBUG_VERSION: string;
try {
ROKU_DEBUG_VERSION = fsExtra.readJsonSync(__dirname + '/../node_modules/roku-debug/package.json').version;
} catch (e) { }

export const EXTENSION_ID = 'RokuCommunity.brightscript';
3 changes: 1 addition & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ import { RtaManager } from './managers/RtaManager';
import { WebviewViewProviderManager } from './managers/WebviewViewProviderManager';
import { ViewProviderId } from './viewProviders/ViewProviderId';
import { DiagnosticManager } from './managers/DiagnosticManager';

const EXTENSION_ID = 'RokuCommunity.brightscript';
import { EXTENSION_ID } from './constants';

export class Extension {
public outputChannel: vscode.OutputChannel;
Expand Down
33 changes: 22 additions & 11 deletions src/managers/TelemetryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,29 @@ export class TelemetryManager implements Disposable {
/**
* Track when a debug session has been started
*/
public sendStartDebugSessionEvent(event: BrightScriptLaunchConfiguration & { preLaunchTask: string }, deviceInfo?: DeviceInfo) {
public sendStartDebugSessionEvent(initialConfig: BrightScriptLaunchConfiguration & { preLaunchTask: string }, finalConfig: BrightScriptLaunchConfiguration, deviceInfo?: DeviceInfo) {
let debugConnectionType: 'debugProtocol' | 'telnet';
let enableDebugProtocol = finalConfig?.enableDebugProtocol ?? initialConfig?.enableDebugProtocol;
if (enableDebugProtocol === true) {
debugConnectionType = 'debugProtocol';
} else if (enableDebugProtocol === false) {
debugConnectionType = 'telnet';
} else {
debugConnectionType = undefined;
}

this.reporter.sendTelemetryEvent('startDebugSession', {
enableDebugProtocol: boolToString(event.enableDebugProtocol),
retainDeploymentArchive: boolToString(event.retainDeploymentArchive),
retainStagingFolder: boolToString(event.retainStagingFolder),
injectRaleTrackerTask: boolToString(event.injectRaleTrackerTask),
isFilesDefined: isDefined(event.files),
isPreLaunchTaskDefined: isDefined(event.preLaunchTask),
isComponentLibrariesDefined: isDefined(event.componentLibraries),
isDeepLinkUrlDefined: isDefined(event.deepLinkUrl),
isStagingFolderPathDefined: isDefined(event.stagingFolderPath),
isLogfilePathDefined: isDefined(event.logfilePath),
enableDebugProtocol: boolToString(initialConfig.enableDebugProtocol),
debugConnectionType: debugConnectionType?.toString(),
retainDeploymentArchive: boolToString(initialConfig.retainDeploymentArchive),
retainStagingFolder: boolToString(initialConfig.retainStagingFolder),
injectRaleTrackerTask: boolToString(initialConfig.injectRaleTrackerTask),
isFilesDefined: isDefined(initialConfig.files),
isPreLaunchTaskDefined: isDefined(initialConfig.preLaunchTask),
isComponentLibrariesDefined: isDefined(initialConfig.componentLibraries),
isDeepLinkUrlDefined: isDefined(initialConfig.deepLinkUrl),
isStagingFolderPathDefined: isDefined(initialConfig.stagingFolderPath),
isLogfilePathDefined: isDefined(initialConfig.logfilePath),
isExtensionLogfilePathDefined: isDefined(
vscode.workspace.getConfiguration('brightscript').get<string>('extensionLogfilePath')
),
Expand Down
3 changes: 3 additions & 0 deletions src/mockVscode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ export let vscode = {
} as OutputChannel;
},
registerTreeDataProvider: function(viewId: string, treeDataProvider: TreeDataProvider<any>) { },
showInformationMessage: function(message: string) {

},
showWarningMessage: function(message: string) {

},
Expand Down
28 changes: 28 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import * as url from 'url';
import { debounce } from 'debounce';
import * as vscode from 'vscode';
import { Cache } from 'brighterscript/dist/Cache';
import undent from 'undent';
import { EXTENSION_ID, ROKU_DEBUG_VERSION } from './constants';
import type { DeviceInfo } from 'roku-deploy';

class Util {
public async readDir(dirPath: string) {
Expand Down Expand Up @@ -381,6 +384,31 @@ class Util {
public escapeRegex(text: string) {
return text?.toString().replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}

public async openIssueReporter(options: { title?: string; body?: string; deviceInfo?: DeviceInfo }) {
if (!options.body) {
options.body = undent`
Please describe the issue you are experiencing:
Steps to reproduce:
Additional feedback:
`;
}
options.body += `\n\nroku-debug version: ${ROKU_DEBUG_VERSION}`;
if (options.deviceInfo) {
options.body += '\n' + undent`
Device firmware: ${options.deviceInfo.softwareVersion}.${options.deviceInfo.softwareBuild}
Debug protocol version: ${options.deviceInfo.brightscriptDebuggerVersion}
Device model: ${options.deviceInfo.modelNumber}
`;
}
await vscode.commands.executeCommand('vscode.openIssueReporter', {
extensionId: EXTENSION_ID,
issueTitle: options.title ?? 'Problem with Debug Protocol',
issueBody: options.body
});
}
}

const util = new Util();
Expand Down

0 comments on commit 5814d18

Please sign in to comment.