Skip to content

Commit

Permalink
Add per-project browser config processing (#81)
Browse files Browse the repository at this point in the history
* Add browser-specific configuration helpers to process custom launcher and debugger configurations
Update extension config generation to load from project karma config files

* Fix chrome's debugger flag to avoid it not starting
Add user prefs to configure firefox to allow dev tools connections

* Update browser helper classes to implement an interface, rather than extend an abstract base class
Update the config helper to avoid loading the karma config during configuration
Update the config helper to look for the browser type for custom launch configs to determine if they are supported
Update the karma config loader to use custom launcher configuration from the user's karma config if a matching config was found and it isn't overriden in settings

* Fix vscode test debugging config
Add checks to ensure the custom launcher type is supported when compiling launcher configuration
Update the electron helper to extend the chrome helper
Update extension config tests to cover new browser/custom launcher config processing

* Update to require at least one supported browser for each browser helper
Update to pass config values to the browser helper rather than the whole config store
Update to clone the custom launcher when adding debug configuration, rather than modifying in place

* Update to check incoming browser type when creating a default launcher config in all helpers
Update to check if the custom launcher base is supported as the first step in all custom launchers
  • Loading branch information
BTMorton committed Aug 4, 2023
1 parent 6d0c556 commit 04594f9
Show file tree
Hide file tree
Showing 15 changed files with 1,032 additions and 73 deletions.
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"name": "Debug Unit Tests",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"program": "${workspaceFolder}/node_modules/jest/bin/jest.js",
"args": [
"--runInBand"
],
Expand All @@ -36,7 +36,7 @@
"name": "vscode-jest-tests",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"program": "${workspaceFolder}/node_modules/jest/bin/jest.js",
"args": [
"--runInBand",
"--watchAll=false"
Expand Down
25 changes: 25 additions & 0 deletions src/core/config/browsers/browser-factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { BrowserHelper } from './browser-helper.js';
import { ChromeBrowserHelper } from './chrome-helper.js';
import { ElectronBrowserHelper } from './electron-helper.js';
import { FirefoxBrowserHelper } from './firefox-helper.js';
import { MsEdgeBrowserHelper } from './msedge-helper.js';

export class BrowserHelperFactory {
static BROWSER_HELPER_INSTANCES = [
new ChromeBrowserHelper(),
new FirefoxBrowserHelper(),
new MsEdgeBrowserHelper(),
new ElectronBrowserHelper()
];

public static getBrowserHelper(browserType: string): BrowserHelper {
return (
this.BROWSER_HELPER_INSTANCES.find(helper => helper.isSupportedBrowser(browserType)) ??
this.BROWSER_HELPER_INSTANCES[0]
);
}

public static isSupportedBrowser(browserType: string): boolean {
return this.BROWSER_HELPER_INSTANCES.some(helper => helper.isSupportedBrowser(browserType));
}
}
29 changes: 29 additions & 0 deletions src/core/config/browsers/browser-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { DebugConfiguration } from 'vscode';

import { CustomLauncher } from 'karma';

import { ContainerMode } from '../extension-config.js';

export interface BrowserHelper {
supportedBrowsers: [string, ...string[]];
debuggerType: string;
debuggingPortFlag: string;

getCustomLauncher(
browserType: string,
customLaucher: CustomLauncher | undefined,
configuredContainerMode: ContainerMode | undefined,
isNonHeadlessMode: boolean
): CustomLauncher;

isSupportedBrowser(browserType: string): boolean;

getDefaultDebuggerConfig(): DebugConfiguration;

addCustomLauncherDebugPort(customLaucher: CustomLauncher, debugPort: number | undefined): CustomLauncher | undefined;

getDefaultDebugPort(
customLauncher: Readonly<CustomLauncher>,
debuggerConfig: Readonly<DebugConfiguration>
): number | undefined;
}
110 changes: 110 additions & 0 deletions src/core/config/browsers/chrome-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { DebugConfiguration } from 'vscode';

import { CustomLauncher } from 'karma';

import { isContainerModeEnabled } from '../config-helper.js';
import { ContainerMode } from '../extension-config.js';
import { BrowserHelper } from './browser-helper.js';

export class ChromeBrowserHelper implements BrowserHelper {
public static DEFAULT_DEBUGGING_PORT: number | undefined = 9222;
private static NO_SANDBOX_FLAG: string = '--no-sandbox';
private static HEADLESS_FLAGS: string[] = ['--headless', '--disable-gpu', '--disable-dev-shm-usage'];

public get supportedBrowsers(): [string, ...string[]] {
return ['Chrome', 'Chromium', 'Dartium'];
}
public get debuggerType(): string {
return 'chrome';
}
public get debuggingPortFlag(): string {
return '--remote-debugging-port';
}

public getCustomLauncher(
browserType: string,
customLaucher: CustomLauncher | undefined,
configuredContainerMode: ContainerMode | undefined,
isNonHeadlessMode: boolean
): CustomLauncher {
if (customLaucher && !this.isSupportedBrowser(customLaucher.base)) {
return customLaucher;
}

const configuredLauncher: CustomLauncher = customLaucher ?? {
base: this.isSupportedBrowser(browserType) ? browserType : this.supportedBrowsers[0],
flags: [
...ChromeBrowserHelper.HEADLESS_FLAGS,
`${this.debuggingPortFlag}=${ChromeBrowserHelper.DEFAULT_DEBUGGING_PORT}`
]
};

const isContainerMode = isContainerModeEnabled(configuredContainerMode);
let launcherFlags = (configuredLauncher.flags ??= []);

if (isContainerMode && !launcherFlags.includes(ChromeBrowserHelper.NO_SANDBOX_FLAG)) {
launcherFlags = [...launcherFlags, ChromeBrowserHelper.NO_SANDBOX_FLAG];
}

if (!isContainerMode && !configuredLauncher.base.includes('Headless') && isNonHeadlessMode) {
launcherFlags = launcherFlags.filter(flag => !ChromeBrowserHelper.HEADLESS_FLAGS.includes(flag));
}

const customLauncher: CustomLauncher = { ...configuredLauncher, flags: launcherFlags };
return customLauncher;
}

public isSupportedBrowser(browserType: string): boolean {
return this.supportedBrowsers.some(supportedBrowser => browserType.startsWith(supportedBrowser));
}

public getDefaultDebuggerConfig(): DebugConfiguration {
return {
name: 'Karma Test Explorer Debugging',
type: this.debuggerType,
request: 'attach',
browserAttachLocation: 'workspace',
address: 'localhost',
port: ChromeBrowserHelper.DEFAULT_DEBUGGING_PORT,
timeout: 60000
};
}

public addCustomLauncherDebugPort(
customLaucher: CustomLauncher,
debugPort: number | undefined
): CustomLauncher | undefined {
if (!customLaucher || debugPort === undefined) {
return;
}

return {
...customLaucher,
flags: customLaucher.flags?.map(flag =>
flag.startsWith(this.debuggingPortFlag) ? `${this.debuggingPortFlag}=${debugPort}` : flag
)
};
}

public getDefaultDebugPort(
customLauncher: Readonly<CustomLauncher>,
debuggerConfig: Readonly<DebugConfiguration>
): number | undefined {
const isSupportedLaunchType = this.supportedBrowsers.some(browser => customLauncher.base.startsWith(browser));
const isSupportedDebugConfig = debuggerConfig.type === this.debuggerType;
if (!isSupportedLaunchType || !isSupportedDebugConfig) {
return undefined;
}

let configuredPort: number | undefined;
const browserDebugPortFlag = customLauncher.flags?.find(flag => flag.startsWith(this.debuggingPortFlag));

if (browserDebugPortFlag) {
const portPosition = browserDebugPortFlag.search(/[0-9]+$/g);
const portString = portPosition !== -1 ? browserDebugPortFlag.substring(portPosition) : undefined;
configuredPort = portString ? parseInt(portString, 10) : undefined;
}

return configuredPort ?? ChromeBrowserHelper.DEFAULT_DEBUGGING_PORT;
}
}
37 changes: 37 additions & 0 deletions src/core/config/browsers/electron-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { CustomLauncher } from 'karma';

import { isContainerModeEnabled } from '../config-helper.js';
import { ContainerMode } from '../extension-config.js';
import { ChromeBrowserHelper } from './chrome-helper.js';

export class ElectronBrowserHelper extends ChromeBrowserHelper {
public override get supportedBrowsers(): [string, ...string[]] {
return ['Electron'];
}

public override getCustomLauncher(
browserType: string,
customLaucher: CustomLauncher | undefined,
configuredContainerMode: ContainerMode | undefined,
isNonHeadlessMode: boolean
): CustomLauncher {
if (customLaucher && !this.isSupportedBrowser(customLaucher.base)) {
return customLaucher;
}

const configuredLauncher: CustomLauncher = customLaucher ?? {
base: this.isSupportedBrowser(browserType) ? browserType : this.supportedBrowsers[0],
flags: [`${this.debuggingPortFlag}=${ElectronBrowserHelper.DEFAULT_DEBUGGING_PORT}`]
};

const isContainerMode = isContainerModeEnabled(configuredContainerMode);

if (!isContainerMode && isNonHeadlessMode) {
const browserWindowOptions = ((configuredLauncher as any).browserWindowOptions ??= {});
browserWindowOptions.webPreferences ??= {};
browserWindowOptions.webPreferences.show = true;
}

return configuredLauncher;
}
}
112 changes: 112 additions & 0 deletions src/core/config/browsers/firefox-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { DebugConfiguration } from 'vscode';

import { CustomLauncher } from 'karma';

import { isContainerModeEnabled } from '../config-helper.js';
import { ContainerMode } from '../extension-config.js';
import { BrowserHelper } from './browser-helper.js';

export class FirefoxBrowserHelper implements BrowserHelper {
public static DEFAULT_DEBUGGING_PORT: number | undefined = 9222;
private static HEADLESS_FLAGS: string[] = ['-headless'];

public get supportedBrowsers(): [string, ...string[]] {
return ['Firefox'];
}
public get debuggerType(): string {
return 'firefox';
}
public get debuggingPortFlag(): string {
return '-start-debugger-server';
}

public getCustomLauncher(
browserType: string,
customLaucher: CustomLauncher | undefined,
configuredContainerMode: ContainerMode | undefined,
isNonHeadlessMode: boolean
): CustomLauncher {
if (customLaucher && !this.isSupportedBrowser(customLaucher.base)) {
return customLaucher;
}

const configuredLauncher: CustomLauncher =
customLaucher ??
({
base: this.isSupportedBrowser(browserType) ? browserType : this.supportedBrowsers[0],
flags: [
...FirefoxBrowserHelper.HEADLESS_FLAGS,
`${this.debuggingPortFlag} ${FirefoxBrowserHelper.DEFAULT_DEBUGGING_PORT}`
],
prefs: {
'devtools.debugger.remote-enabled': true,
'devtools.chrome.enabled': true,
'devtools.debugger.prompt-connection': false
}
} as any);

const isContainerMode = isContainerModeEnabled(configuredContainerMode);
let launcherFlags = (configuredLauncher.flags ??= []);

if (!isContainerMode && !configuredLauncher.base.includes('Headless') && isNonHeadlessMode) {
launcherFlags = launcherFlags.filter(flag => !FirefoxBrowserHelper.HEADLESS_FLAGS.includes(flag));
}

const customLauncher: CustomLauncher = { ...configuredLauncher, flags: launcherFlags };
return customLauncher;
}

public isSupportedBrowser(browserType: string): boolean {
return this.supportedBrowsers.some(supportedBrowser => browserType.startsWith(supportedBrowser));
}

public getDefaultDebuggerConfig(): DebugConfiguration {
return {
name: 'Karma Test Explorer Debugging',
type: this.debuggerType,
request: 'attach',
browserAttachLocation: 'workspace',
address: 'localhost',
port: FirefoxBrowserHelper.DEFAULT_DEBUGGING_PORT,
timeout: 60000
};
}

public addCustomLauncherDebugPort(
customLaucher: CustomLauncher,
debugPort: number | undefined
): CustomLauncher | undefined {
if (!customLaucher || debugPort === undefined) {
return undefined;
}

return {
...customLaucher,
flags: customLaucher.flags?.map(flag =>
flag.startsWith(this.debuggingPortFlag) ? `${this.debuggingPortFlag} ${debugPort}` : flag
)
};
}

public getDefaultDebugPort(
customLauncher: Readonly<CustomLauncher>,
debuggerConfig: Readonly<DebugConfiguration>
): number | undefined {
const isSupportedLaunchType = this.supportedBrowsers.some(browser => customLauncher.base.startsWith(browser));
const isSupportedDebugConfig = debuggerConfig.type === this.debuggerType;
if (!isSupportedLaunchType || !isSupportedDebugConfig) {
return undefined;
}

let configuredPort: number | undefined;
const browserDebugPortFlag = customLauncher.flags?.find(flag => flag.startsWith(this.debuggingPortFlag));

if (browserDebugPortFlag) {
const portPosition = browserDebugPortFlag.search(/[0-9]+$/g);
const portString = portPosition !== -1 ? browserDebugPortFlag.substring(portPosition) : undefined;
configuredPort = portString ? parseInt(portString, 10) : undefined;
}

return configuredPort ?? FirefoxBrowserHelper.DEFAULT_DEBUGGING_PORT;
}
}
10 changes: 10 additions & 0 deletions src/core/config/browsers/msedge-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ChromeBrowserHelper } from './chrome-helper.js';

export class MsEdgeBrowserHelper extends ChromeBrowserHelper {
public override get supportedBrowsers(): [string, ...string[]] {
return ['Edge'];
}
public override get debuggerType(): string {
return 'msedge';
}
}
Loading

0 comments on commit 04594f9

Please sign in to comment.