Expose client (browser) logs for integration tests running in browser (#180102) (#181513)

This commit is contained in:
Benjamin Pasero 2023-05-04 15:28:25 +02:00 committed by GitHub
parent d980921bf1
commit c4a46ef344
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 54 additions and 24 deletions

View file

@ -11,6 +11,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import localizedStrings from 'vs/platform/languagePacks/common/localizedStrings';
import { getLogs } from 'vs/platform/log/browser/log';
export class BrowserWindowDriver implements IWindowDriver {
@ -21,22 +22,7 @@ export class BrowserWindowDriver implements IWindowDriver {
}
async getLogs(): Promise<ILogFile[]> {
const result: ILogFile[] = [];
const logs = await this.fileService.resolve(this.environmentService.logsHome);
for (const { name, isDirectory, resource } of logs.children || []) {
if (isDirectory) {
continue;
}
const contents = (await this.fileService.readFile(resource)).value.toString();
if (contents) {
result.push({ name, contents });
}
}
return result;
return getLogs(this.fileService, this.environmentService);
}
async setValue(selector: string, text: string): Promise<void> {

View file

@ -3,11 +3,42 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { AdapterLogger, DEFAULT_LOG_LEVEL, ILogger, LogLevel } from 'vs/platform/log/common/log';
export interface IAutomatedWindow {
codeAutomationLog(type: string, args: any[]): void;
codeAutomationExit(code: number): void;
codeAutomationExit(logs: Array<ILogFile>, code: number): void;
}
export interface ILogFile {
readonly name: string;
readonly contents: string;
}
/**
* Only used in browser contexts where the log files are not stored on disk
* but in IndexedDB. A method to get all logs with their contents so that
* CI automation can persist them.
*/
export async function getLogs(fileService: IFileService, environmentService: IEnvironmentService): Promise<ILogFile[]> {
const result: ILogFile[] = [];
const logs = await fileService.resolve(environmentService.logsHome);
for (const { name, isDirectory, resource } of logs.children || []) {
if (isDirectory) {
continue;
}
const contents = (await fileService.readFile(resource)).value.toString();
if (contents) {
result.push({ name, contents });
}
}
return result;
}
function logLevelToString(level: LogLevel): string {

View file

@ -10,7 +10,7 @@ import { ExtensionIdentifier, ExtensionType, IExtension, IExtensionDescription }
import { IFileService } from 'vs/platform/files/common/files';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IAutomatedWindow } from 'vs/platform/log/browser/log';
import { IAutomatedWindow, getLogs } from 'vs/platform/log/browser/log';
import { ILogService } from 'vs/platform/log/common/log';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IProductService } from 'vs/platform/product/common/productService';
@ -139,13 +139,14 @@ export class ExtensionService extends AbstractExtensionService implements IExten
return new ResolvedExtensions(localExtensions, remoteExtensions, /*hasLocalProcess*/false, /*allowRemoteExtensionsInLocalWebWorker*/true);
}
protected _onExtensionHostExit(code: number): void {
protected async _onExtensionHostExit(code: number): Promise<void> {
// Dispose everything associated with the extension host
this._doStopExtensionHosts();
// If we are running extension tests, forward logs and exit code
const automatedWindow = window as unknown as IAutomatedWindow;
if (typeof automatedWindow.codeAutomationExit === 'function') {
automatedWindow.codeAutomationExit(code);
automatedWindow.codeAutomationExit(await getLogs(this._fileService, this._environmentService), code);
}
}
}

View file

@ -13,6 +13,10 @@ import { URI } from 'vscode-uri';
import * as kill from 'tree-kill';
import * as optimistLib from 'optimist';
import { promisify } from 'util';
import { promises } from 'fs';
const root = path.join(__dirname, '..', '..', '..', '..');
const logsPath = path.join(root, '.build', 'logs', 'integration-tests-browser');
const optimist = optimistLib
.describe('workspacePath', 'path to the workspace (folder or *.code-workspace file) to open in the test').string('workspacePath')
@ -63,7 +67,18 @@ async function runTestsInBrowser(browserType: BrowserType, endpoint: url.UrlWith
console[type](...args);
});
await page.exposeFunction('codeAutomationExit', async (code: number) => {
await page.exposeFunction('codeAutomationExit', async (logs: Array<{ readonly name: string; readonly contents: string }>, code: number) => {
try {
for (const log of logs) {
const absoluteLogsPath = path.join(logsPath, log.name);
await promises.mkdir(path.dirname(absoluteLogsPath), { recursive: true });
await promises.writeFile(absoluteLogsPath, log.contents);
}
} catch (error) {
console.error(`Error saving web client logs (${error})`);
}
try {
await browser.close();
} catch (error) {
@ -123,9 +138,6 @@ async function launchServer(browserType: BrowserType): Promise<{ endpoint: url.U
...process.env
};
const root = path.join(__dirname, '..', '..', '..', '..');
const logsPath = path.join(root, '.build', 'logs', 'integration-tests-browser');
const serverArgs = ['--enable-proposed-api', '--disable-telemetry', '--server-data-dir', userDataDir, '--accept-server-license-terms', '--disable-workspace-trust'];
let serverLocation: string;