Playwright: get console logs without trace (#28972)

* Playwright: get console logs without trace

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add page url to log

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Skip empty logs

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Reset page counter

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2025-01-13 16:15:13 +00:00 committed by GitHub
parent e14a3b64c3
commit 11a8723c73
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 68 additions and 48 deletions

63
playwright/logger.ts Normal file
View File

@ -0,0 +1,63 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { BrowserContext, Page, TestInfo } from "@playwright/test";
import { Readable } from "stream";
import stripAnsi from "strip-ansi";
export class Logger {
private pages: Page[] = [];
private logs: Record<string, string> = {};
public getConsumer(container: string) {
this.logs[container] = "";
return (stream: Readable) => {
stream.on("data", (chunk) => {
this.logs[container] += chunk.toString();
});
stream.on("err", (chunk) => {
this.logs[container] += "ERR " + chunk.toString();
});
};
}
public async onTestStarted(context: BrowserContext) {
this.pages = [];
for (const id in this.logs) {
if (id.startsWith("page-")) {
delete this.logs[id];
} else {
this.logs[id] = "";
}
}
context.on("console", (msg) => {
const page = msg.page();
let pageIdx = this.pages.indexOf(page);
if (pageIdx === -1) {
this.pages.push(page);
pageIdx = this.pages.length - 1;
this.logs[`page-${pageIdx}`] = `Console logs for page with URL: ${page.url()}\n\n`;
}
const type = msg.type();
const text = msg.text();
this.logs[`page-${pageIdx}`] += `${type}: ${text}\n`;
});
}
public async onTestFinished(testInfo: TestInfo) {
if (testInfo.status !== "passed") {
for (const id in this.logs) {
if (!this.logs[id]) continue;
await testInfo.attach(id, {
body: stripAnsi(this.logs[id]),
contentType: "text/plain",
});
}
}
}
}

View File

@ -11,7 +11,7 @@ import { Network, StartedNetwork } from "testcontainers";
import { PostgreSqlContainer, StartedPostgreSqlContainer } from "@testcontainers/postgresql";
import { SynapseConfigOptions, SynapseContainer } from "./testcontainers/synapse.ts";
import { ContainerLogger } from "./testcontainers/utils.ts";
import { Logger } from "./logger.ts";
import { StartedMatrixAuthenticationServiceContainer } from "./testcontainers/mas.ts";
import { HomeserverContainer, StartedHomeserverContainer } from "./testcontainers/HomeserverContainer.ts";
import { MailhogContainer, StartedMailhogContainer } from "./testcontainers/mailhog.ts";
@ -21,7 +21,7 @@ interface TestFixtures {
}
export interface Services {
logger: ContainerLogger;
logger: Logger;
network: StartedNetwork;
postgres: StartedPostgreSqlContainer;
@ -37,7 +37,7 @@ export const test = base.extend<TestFixtures, Services>({
logger: [
// eslint-disable-next-line no-empty-pattern
async ({}, use) => {
const logger = new ContainerLogger();
const logger = new Logger();
await use(logger);
},
{ scope: "worker" },
@ -132,8 +132,8 @@ export const test = base.extend<TestFixtures, Services>({
context: async ({ logger, context, request, homeserver }, use, testInfo) => {
homeserver.setRequest(request);
await logger.testStarted(testInfo);
await logger.onTestStarted(context);
await use(context);
await logger.testFinished(testInfo);
await logger.onTestFinished(testInfo);
},
});

View File

@ -1,43 +0,0 @@
/*
Copyright 2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { TestInfo } from "@playwright/test";
import { Readable } from "stream";
import stripAnsi from "strip-ansi";
export class ContainerLogger {
private logs: Record<string, string> = {};
public getConsumer(container: string) {
this.logs[container] = "";
return (stream: Readable) => {
stream.on("data", (chunk) => {
this.logs[container] += chunk.toString();
});
stream.on("err", (chunk) => {
this.logs[container] += "ERR " + chunk.toString();
});
};
}
public async testStarted(testInfo: TestInfo) {
for (const container in this.logs) {
this.logs[container] = "";
}
}
public async testFinished(testInfo: TestInfo) {
if (testInfo.status !== "passed") {
for (const container in this.logs) {
await testInfo.attach(container, {
body: stripAnsi(this.logs[container]),
contentType: "text/plain",
});
}
}
}
}