Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add watch support that tracks tests #409

Merged
merged 1 commit into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"url": "https://github.com/microsoft/playwright-vscode/issues"
},
"engines": {
"vscode": "^1.73.0"
"vscode": "^1.78.0"
},
"categories": [
"Testing"
Expand Down Expand Up @@ -87,6 +87,10 @@
"playwright.useTestServer": {
"type": "boolean",
"default": false
},
"playwright.allowWatchingFiles": {
"type": "boolean",
"default": false
}
}
},
Expand Down Expand Up @@ -123,7 +127,7 @@
"@types/glob": "^8.0.0",
"@types/node": "^18.11.9",
"@types/stack-utils": "^2.0.1",
"@types/vscode": "1.73.0",
"@types/vscode": "1.78.0",
"@types/which": "^2.0.1",
"@types/ws": "^8.5.3",
"@typescript-eslint/eslint-plugin": "^5.44.0",
Expand Down
27 changes: 21 additions & 6 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import * as vscodeTypes from './vscodeTypes';
import { WorkspaceChange, WorkspaceObserver } from './workspaceObserver';
import { TraceViewer } from './traceViewer';
import { TestServerController } from './testServerController';
import { type Watch, WatchSupport } from './watchSupport';

const stackUtils = new StackUtils({
cwd: '/ensure_absolute_paths'
Expand Down Expand Up @@ -80,6 +81,7 @@ export class Extension implements RunHooks {
private _traceViewer: TraceViewer;
private _settingsModel: SettingsModel;
private _settingsView!: SettingsView;
private _watchSupport: WatchSupport;
private _filesPendingListTests: {
files: Set<string>,
timer: NodeJS.Timeout,
Expand All @@ -89,6 +91,7 @@ export class Extension implements RunHooks {
private _diagnostics: Record<'configErrors' | 'testErrors', vscodeTypes.DiagnosticCollection>;
private _treeItemObserver: TreeItemObserver;
private _testServerController: TestServerController;
private _watchQueue = Promise.resolve();

constructor(vscode: vscodeTypes.VSCode) {
this._vscode = vscode;
Expand Down Expand Up @@ -127,6 +130,7 @@ export class Extension implements RunHooks {
configErrors: this._vscode.languages.createDiagnosticCollection('pw.configErrors.diagnostic'),
};
this._treeItemObserver = new TreeItemObserver(this._vscode);
this._watchSupport = new WatchSupport(this._vscode, this._playwrightTest, this._testTree, watchData => this._watchesTriggered(watchData));
}

async onWillRunTests(config: TestConfig, debug: boolean) {
Expand Down Expand Up @@ -350,20 +354,21 @@ export class Extension implements RunHooks {
let runProfile = this._runProfiles.get(keyPrefix + ':run');
const projectTag = this._testTree.projectTag(project);
const isDefault = false;
const supportsContinuousRun = this._settingsModel.allowWatchingFiles.get();
if (!runProfile) {
runProfile = this._testController.createRunProfile(`${projectPrefix}${folderName}${path.sep}${configName}`, this._vscode.TestRunProfileKind.Run, this._scheduleTestRunRequest.bind(this, configFile, project.name, false), isDefault, projectTag);
runProfile = this._testController.createRunProfile(`${projectPrefix}${folderName}${path.sep}${configName}`, this._vscode.TestRunProfileKind.Run, this._scheduleTestRunRequest.bind(this, configFile, project.name, false), isDefault, projectTag, supportsContinuousRun);
this._runProfiles.set(keyPrefix + ':run', runProfile);
}
let debugProfile = this._runProfiles.get(keyPrefix + ':debug');
if (!debugProfile) {
debugProfile = this._testController.createRunProfile(`${projectPrefix}${folderName}${path.sep}${configName}`, this._vscode.TestRunProfileKind.Debug, this._scheduleTestRunRequest.bind(this, configFile, project.name, true), isDefault, projectTag);
debugProfile = this._testController.createRunProfile(`${projectPrefix}${folderName}${path.sep}${configName}`, this._vscode.TestRunProfileKind.Debug, this._scheduleTestRunRequest.bind(this, configFile, project.name, true), isDefault, projectTag, supportsContinuousRun);
this._runProfiles.set(keyPrefix + ':debug', debugProfile);
}
}

private _scheduleTestRunRequest(configFile: string, projectName: string, isDebug: boolean, request: vscodeTypes.TestRunRequest) {
private _scheduleTestRunRequest(configFile: string, projectName: string, isDebug: boolean, request: vscodeTypes.TestRunRequest, cancellationToken?: vscodeTypes.CancellationToken) {
// Never run tests concurrently.
if (this._testRun)
if (this._testRun && !request.continuous)
return;

// We can't dispose projects (and bind them to TestProject instances) because otherwise VS Code would forget its selection state.
Expand All @@ -375,6 +380,11 @@ export class Extension implements RunHooks {
if (!project)
return;

if (request.continuous) {
this._watchSupport.addToWatch(project, request.include, cancellationToken!);
return;
}

// VSCode will issue several test run requests (one per enabled run profile). Sometimes
// these profiles belong to the same config and we only want to run tests once per config.
// So we collect all requests and sort them out in the microtask.
Expand All @@ -397,7 +407,7 @@ export class Extension implements RunHooks {
}
}

private async _runMatchingTests(testRunInfos: TestRunInfo[], mode: 'run' | 'debug') {
private async _runMatchingTests(testRunInfos: TestRunInfo[], mode: 'run' | 'debug' | 'watch') {
this._completedSteps.clear();
this._executionLinesChanged();

Expand All @@ -409,7 +419,7 @@ export class Extension implements RunHooks {
// selected test items.
const rootItems: vscodeTypes.TestItem[] = [];
this._testController.items.forEach(item => rootItems.push(item));
const requestWithDeps = new this._vscode.TestRunRequest(rootItems, [], undefined);
const requestWithDeps = new this._vscode.TestRunRequest(rootItems, [], undefined, mode === 'watch');
pavelfeldman marked this conversation as resolved.
Show resolved Hide resolved

// Global errors are attributed to the first test item in the request.
// If the request is global, find the first root test item (folder, file) that has
Expand Down Expand Up @@ -525,6 +535,11 @@ located next to Run / Debug Tests toolbar buttons.`);
// Workspace change can be deferred, make sure editors are
// decorated.
await this._updateVisibleEditorItems();
await this._watchSupport.workspaceChanged(change);
}

private _watchesTriggered(watches: Watch[]) {
this._watchQueue = this._watchQueue.then(() => this._runMatchingTests(watches, 'watch'));
}

private async _runTest(
Expand Down
49 changes: 46 additions & 3 deletions src/playwrightTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { spawn } from 'child_process';
import path from 'path';
import { debugSessionName } from './debugSessionName';
import { ConfigListFilesReport } from './listTests';
import { ConfigFindRelatedTestFilesReport, ConfigListFilesReport } from './listTests';
import type { TestError, Entry, StepBeginParams, StepEndParams, TestBeginParams, TestEndParams } from './oopReporter';
import { ReporterServer } from './reporterServer';
import { findNode, spawnAsync } from './utils';
Expand Down Expand Up @@ -170,8 +170,12 @@ export class PlaywrightTest {
return { entries, errors };
}

private _useTestServer(config: TestConfig) {
return config.version >= 1.43 || this._settingsModel.useTestServer.get();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return config.version >= 1.43 || this._settingsModel.useTestServer.get();
return config.version >= 1.43 && this._settingsModel.useTestServer.get();

}

private async _test(config: TestConfig, locations: string[], mode: 'list' | 'run', options: PlaywrightTestOptions, listener: TestListener, token: vscodeTypes.CancellationToken): Promise<void> {
if (config.version >= 1.43 || this._settingsModel.useTestServer.get())
if (this._useTestServer(config))
await this._testWithServer(config, locations, mode, options, listener, token);
else
await this._testWithCLI(config, locations, mode, options, listener, token);
Expand Down Expand Up @@ -245,8 +249,10 @@ export class PlaywrightTest {
private async _testWithServer(config: TestConfig, locations: string[], mode: 'list' | 'run', options: PlaywrightTestOptions, listener: TestListener, token: vscodeTypes.CancellationToken): Promise<void> {
const reporterServer = new ReporterServer(this._vscode);
const testServer = await this._testServerController.testServerFor(config);
if (!testServer)
if (token?.isCancellationRequested)
return;
if (!testServer)
return this._testWithCLI(config, locations, mode, options, listener, token);
if (token?.isCancellationRequested)
return;
const env = await reporterServer.env({ selfDestruct: false });
Expand All @@ -269,6 +275,43 @@ export class PlaywrightTest {
await reporterServer.wireTestListener(listener, token);
}

async findRelatedTestFiles(config: TestConfig, files: string[]): Promise<ConfigFindRelatedTestFilesReport> {
if (this._useTestServer(config))
return await this._findRelatedTestFilesServer(config, files);
else
return await this._findRelatedTestFilesCLI(config, files);
}

async _findRelatedTestFilesCLI(config: TestConfig, files: string[]): Promise<ConfigFindRelatedTestFilesReport> {
const configFolder = path.dirname(config.configFile);
const configFile = path.basename(config.configFile);
const allArgs = [config.cli, 'find-related-test-files', '-c', configFile, ...files];
{
// For tests.
this._log(`${escapeRegex(path.relative(config.workspaceFolder, configFolder))}> playwright find-related-test-files -c ${configFile}`);
}
try {
const output = await this._runNode(allArgs, configFolder);
const result = JSON.parse(output) as ConfigFindRelatedTestFilesReport;
return result;
} catch (error: any) {
return {
errors: [{
location: { file: configFile, line: 0, column: 0 },
message: error.message,
}],
testFiles: files,
};
}
}

async _findRelatedTestFilesServer(config: TestConfig, files: string[]): Promise<ConfigFindRelatedTestFilesReport> {
const testServer = await this._testServerController.testServerFor(config);
if (!testServer)
return await this._findRelatedTestFilesCLI(config, files);
return await testServer.findRelatedTestFiles({ files });
}

async debugTests(vscode: vscodeTypes.VSCode, config: TestConfig, projectNames: string[], testDirs: string[], settingsEnv: NodeJS.ProcessEnv, locations: string[] | null, listener: TestListener, parametrizedTestTitle: string | undefined, token: vscodeTypes.CancellationToken) {
const configFolder = path.dirname(config.configFile);
const configFile = path.basename(config.configFile);
Expand Down
2 changes: 2 additions & 0 deletions src/settingsModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class SettingsModel implements vscodeTypes.Disposable {
showBrowser: Setting<boolean>;
showTrace: Setting<boolean>;
useTestServer: Setting<boolean>;
allowWatchingFiles: Setting<boolean>;

constructor(vscode: vscodeTypes.VSCode) {
this._vscode = vscode;
Expand All @@ -34,6 +35,7 @@ export class SettingsModel implements vscodeTypes.Disposable {
this.showBrowser = this._createSetting('reuseBrowser');
this.showTrace = this._createSetting('showTrace');
this.useTestServer = this._createSetting('useTestServer');
this.allowWatchingFiles = this._createSetting('allowWatchingFiles');

this.showBrowser.onChange(enabled => {
if (enabled && this.showTrace.get())
Expand Down
5 changes: 5 additions & 0 deletions src/testServerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { BackendClient, BackendServer } from './backend';
import { ConfigFindRelatedTestFilesReport } from './listTests';
import { TestConfig } from './playwrightTest';
import * as vscodeTypes from './vscodeTypes';

Expand Down Expand Up @@ -71,6 +72,10 @@ class TestServer extends BackendClient {
await this.send('list', params);
}

findRelatedTestFiles(params: { files: string[]; }): Promise<ConfigFindRelatedTestFilesReport> {
return this.send('findRelatedTestFiles', params);
}

async test(params: any) {
await this.send('test', params);
}
Expand Down
Loading
Loading