Skip to content

Commit

Permalink
chore: use uimode's test server api
Browse files Browse the repository at this point in the history
  • Loading branch information
pavelfeldman committed Mar 22, 2024
1 parent fc12a00 commit 3bccf2f
Show file tree
Hide file tree
Showing 23 changed files with 515 additions and 335 deletions.
84 changes: 45 additions & 39 deletions src/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,59 +20,35 @@ import * as vscodeTypes from './vscodeTypes';
import EventEmitter from 'events';
import { WebSocketTransport } from './transport';

export type BackendServerOptions<T extends BackendClient> = {
export type BackendServerOptions = {
args: string[],
cwd: string,
envProvider: () => NodeJS.ProcessEnv,
clientFactory: () => T,
dumpIO?: boolean,
};

export class BackendServer<T extends BackendClient> {
private _vscode: vscodeTypes.VSCode;
private _options: BackendServerOptions<T>;
private _options: BackendServerOptions;
private _clientFactory: () => T;

constructor(vscode: vscodeTypes.VSCode, options: BackendServerOptions<T>) {
constructor(vscode: vscodeTypes.VSCode, clientFactory: () => T, options: BackendServerOptions) {
this._vscode = vscode;
this._clientFactory = clientFactory;
this._options = options;
}

async start(): Promise<T | null> {
const node = await findNode(this._vscode, this._options.cwd);
const serverProcess = spawn(node, this._options.args, {
cwd: this._options.cwd,
stdio: 'pipe',
env: {
...process.env,
...this._options.envProvider(),
},
});

const client = this._options.clientFactory();

serverProcess.stderr?.on('data', data => {
if (this._options.dumpIO)
console.log('[server err]', data.toString());
});
serverProcess.on('error', error => {
client._onErrorEvent.fire(error);
});
return new Promise(fulfill => {
serverProcess.stdout?.on('data', async data => {
if (this._options.dumpIO)
console.log('[server out]', data.toString());
const match = data.toString().match(/Listening on (.*)/);
if (!match)
return;
const wse = match[1];
await client._connect(wse);
fulfill(client);
});
serverProcess.on('exit', () => {
fulfill(null);
client._onCloseEvent.fire();
});
async startAndConnect(): Promise<T | null> {
const client = this._clientFactory();
const wsEndpoint = await startBackend(this._vscode, {
...this._options,
onError: error => client._onErrorEvent.fire(error),
onClose: () => client._onCloseEvent.fire(),
});
if (!wsEndpoint)
return null;
await client._connect(wsEndpoint);
return client;
}
}

Expand Down Expand Up @@ -143,3 +119,33 @@ export class BackendClient extends EventEmitter {
this._transport.close();
}
}

export async function startBackend(vscode: vscodeTypes.VSCode, options: BackendServerOptions & { onError: (error: Error) => void, onClose: () => void }): Promise<string | null> {
const node = await findNode(vscode, options.cwd);
const serverProcess = spawn(node, options.args, {
cwd: options.cwd,
stdio: 'pipe',
env: {
...process.env,
...options.envProvider(),
},
});
serverProcess.stderr?.on('data', data => {
if (options.dumpIO)
console.log('[server err]', data.toString());
});
serverProcess.on('error', options.onError);
serverProcess.on('close', options.onClose);
return new Promise(fulfill => {
serverProcess.stdout?.on('data', async data => {
if (options.dumpIO)
console.log('[server out]', data.toString());
const match = data.toString().match(/Listening on (.*)/);
if (!match)
return;
const wse = match[1];
fulfill(wse);
});
serverProcess.on('exit', () => fulfill(null));
});
}
2 changes: 1 addition & 1 deletion src/debugHighlight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { locatorForSourcePosition, pruneAstCaches } from './babelHighlightUtil';
import { debugSessionName } from './debugSessionName';
import { replaceActionWithLocator, locatorMethodRegex } from './methodNames';
import type { Location } from './reporter';
import type { Location } from './upstream/reporter';
import { ReusedBrowser } from './reusedBrowser';
import * as vscodeTypes from './vscodeTypes';

Expand Down
92 changes: 29 additions & 63 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ import path from 'path';
import StackUtils from 'stack-utils';
import { DebugHighlight } from './debugHighlight';
import { installBrowsers, installPlaywright } from './installer';
import { MultiMap } from './multimap';
import { RunHooks, TestConfig, getPlaywrightInfo } from './playwrightTest';
import * as reporterTypes from './reporter';
import * as reporterTypes from './upstream/reporter';
import { ReusedBrowser } from './reusedBrowser';
import { SettingsModel } from './settingsModel';
import { SettingsView } from './settingsView';
Expand Down Expand Up @@ -85,7 +84,7 @@ export class Extension implements RunHooks {
promise: Promise<void>,
finishedCallback: () => void
} | undefined;
private _diagnostics: Record<'configErrors' | 'testErrors', vscodeTypes.DiagnosticCollection>;
private _diagnostics: vscodeTypes.DiagnosticCollection;
private _treeItemObserver: TreeItemObserver;
private _testServerController: TestServerController;
private _watchQueue = Promise.resolve();
Expand Down Expand Up @@ -128,10 +127,7 @@ export class Extension implements RunHooks {
this._debugHighlight = new DebugHighlight(vscode, this._reusedBrowser);
this._debugHighlight.onErrorInDebugger(e => this._errorInDebugger(e.error, e.location));
this._workspaceObserver = new WorkspaceObserver(this._vscode, changes => this._workspaceChanged(changes));
this._diagnostics = {
testErrors: this._vscode.languages.createDiagnosticCollection('pw.testErrors.diagnostic'),
configErrors: this._vscode.languages.createDiagnosticCollection('pw.configErrors.diagnostic'),
};
this._diagnostics = this._vscode.languages.createDiagnosticCollection('pw.testErrors.diagnostic');
this._treeItemObserver = new TreeItemObserver(this._vscode);
this._watchSupport = new WatchSupport(this._vscode, this._testTree, watchData => this._watchesTriggered(watchData));
}
Expand Down Expand Up @@ -229,7 +225,7 @@ export class Extension implements RunHooks {
this._debugProfile,
this._workspaceObserver,
this._reusedBrowser,
...Object.values(this._diagnostics),
this._diagnostics,
this._treeItemObserver,
this._testServerController,
registerTerminalLinkProvider(this._vscode),
Expand Down Expand Up @@ -315,44 +311,11 @@ export class Extension implements RunHooks {
private _modelsUpdated() {
this._workspaceObserver.reset();
this._updateVisibleEditorItems();
this._reportConfigErrors();
this._updateDiagnostics();
for (const testDir of this._models.testDirs())
this._workspaceObserver.addWatchFolder(testDir);
}

private _reportConfigErrors() {
const configErrors = new MultiMap<string, reporterTypes.TestError>();
for (const model of this._models.enabledModels()) {
const error = model.takeConfigError();
if (error)
configErrors.set(model.config.configFile, error);
}

this._updateDiagnostics('configErrors', configErrors);
if (!configErrors.size)
return;
(async () => {
const showDetails = this._vscode.l10n.t('Show details');
const choice = await this._vscode.window.showErrorMessage(this._vscode.l10n.t('There are errors in Playwright configuration files.'), showDetails);
if (choice === showDetails) {
// Show the document to the user.
const document = await this._vscode.workspace.openTextDocument([...configErrors.keys()][0]);
await this._vscode.window.showTextDocument(document);
const error = [...configErrors.values()][0];
// Reveal the error line.
if (error?.location) {
const range = new this._vscode.Range(
new this._vscode.Position(Math.max(error.location.line - 4, 0), 0),
new this._vscode.Position(Math.max(error.location.line - 1, 0), Math.max(error.location.column - 1, 0)),
);
this._vscode.window.activeTextEditor?.revealRange(range);
}
// Focus problems view.
await this._vscode.commands.executeCommand('workbench.action.problems.focus');
}
})();
}

private _envProvider(): NodeJS.ProcessEnv {
const env = this._vscode.workspace.getConfiguration('playwright').get('env', {});
return Object.fromEntries(Object.entries(env).map(entry => {
Expand Down Expand Up @@ -637,24 +600,13 @@ export class Extension implements RunHooks {

const timer = setTimeout(async () => {
delete this._filesPendingListTests;
const allErrors = new Set<string>();
const errorsByFile = new MultiMap<string, reporterTypes.TestError>();
for (const model of this._models.enabledModels()) {
const filteredFiles = model.narrowDownFilesToEnabledProjects(files);
if (!filteredFiles.size)
continue;
const errors = await model.ensureTests([...filteredFiles]).catch(e => console.log(e)) || [];
for (const error of errors) {
if (!error.location || !error.message)
continue;
const key = error.location.file + ':' + error.location.line + ':' + error.message;
if (allErrors.has(key))
continue;
allErrors.add(key);
errorsByFile.set(error.location?.file, error);
}
await model.ensureTests([...filteredFiles]).catch(e => console.log(e));
}
this._updateDiagnostics('testErrors', errorsByFile);
this._updateDiagnostics();
finishedCallback();
}, 0);

Expand All @@ -672,21 +624,35 @@ export class Extension implements RunHooks {
return this._filesPendingListTests.promise;
}

private _updateDiagnostics(diagnosticsType: 'configErrors' | 'testErrors' , errorsByFile: MultiMap<string, reporterTypes.TestError>) {
const diagnosticsCollection = this._diagnostics[diagnosticsType]!;
diagnosticsCollection.clear();
for (const [file, errors] of errorsByFile) {
const diagnostics: vscodeTypes.Diagnostic[] = [];
for (const error of errors) {
diagnostics.push({
private _updateDiagnostics() {
this._diagnostics.clear();
const diagnosticsByFile = new Map<string, Map<string, vscodeTypes.Diagnostic>>();

const addError = (error: reporterTypes.TestError) => {
if (!error.location)
return;
let diagnostics = diagnosticsByFile.get(error.location.file);
if (!diagnostics) {
diagnostics = new Map();
diagnosticsByFile.set(error.location.file, diagnostics);
}
const key = `${error.location?.line}:${error.location?.column}:${error.message}`;
if (!diagnostics.has(key)) {
diagnostics.set(key, {
severity: this._vscode.DiagnosticSeverity.Error,
source: 'playwright',
range: new this._vscode.Range(Math.max(error.location!.line - 1, 0), Math.max(error.location!.column - 1, 0), error.location!.line, 0),
message: error.message!,
});
}
diagnosticsCollection.set(this._vscode.Uri.file(file), diagnostics);
};

for (const model of this._models.enabledModels()) {
for (const error of model.errors().values())
addError(error);
}
for (const [file, diagnostics] of diagnosticsByFile)
this._diagnostics.set(this._vscode.Uri.file(file), [...diagnostics.values()]);
}

private _errorInDebugger(errorStack: string, location: reporterTypes.Location) {
Expand Down
2 changes: 1 addition & 1 deletion src/listTests.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

import type { TestError } from './reporter';
import type { TestError } from './upstream/reporter';

// This matches the structs in packages/playwright-test/src/runner/runner.ts.

Expand Down
2 changes: 1 addition & 1 deletion src/oopReporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

import { TeleReporterEmitter } from './upstream/teleEmitter';
import { WebSocketTransport } from './transport';
import { FullResult } from './reporter';
import { FullResult } from './upstream/reporter';

class TeleReporter extends TeleReporterEmitter {
private _hasSender: boolean;
Expand Down
Loading

0 comments on commit 3bccf2f

Please sign in to comment.