Issue 1:
Context:

Playwright Version: 1.17

Operating System: Mac OS

Node.js version: 16.3

Browser: Chromium

Code Snippet

// wallet.address is a string
testInfo.attachments.push({
name: 'address',
body: wallet.address as any,
contentType: 'text/plain',
})

console.log(wallet.address)

Describe the bug

I'm trying to add an attachment with contentType: 'text/plain' to my html report.

The problem I'm seeing is there are some additional symbols added to my string:

I also tried to use Buffer.from(wallet.address) as the body expects to see an instance of the Buffer but all I get is this:

Issue 2:
Context:

Playwright Version: @playwright/test@1.16.3, ToT (3c1aaa5)

Code Snippet

Currently, if you have the following in your tests:

testInfo.attachments.push({
name: "example.txt",
contentType: "text/plain",
body: Buffer.from("foo"),
});

testInfo.attachments.push({
name: "example.json",
contentType: "application/json",
body: Buffer.from(JSON.stringify({ foo: 1})),
});

console.log("stdout output example");
console.error("stderr output example");

the HTML Reporter renders the example.txt and example.json as b64 encoded text (but, notably inconsistent, not stdout/stderr):

It would be ideal if plaintext attachments were rendered plainly without the encoding so you can easily understand their contents while viewing the HTML Report in the browser. For each of our e2e smoketests, we attach metadata that allows a test reviewer to find items created in the e2e test run in our backend.) Here's what I expected it to look like:

For now, we can log the info to stdout, but it's nice to put it in a standard attachment so it's easy to view.

It looks like stdout/stderr have special treatment, while all other body type attachments are base64 encoded:

playwright/packages/playwright-test/src/reporters/raw.ts

Line 248
in
3c1aaa5

body: attachment.body.toString('base64')

There's a few alternative's I'd like us to consider:

Don't always do the above. If body Buffer is specified AND contentType is plaintext-ish (text/*, application/json), just call toString() instead of toString('base64').

Leave as is, but change _createAttachment like so:

diff --git a/packages/playwright-test/src/reporters/html.ts b/packages/playwright-test/src/reporters/html.ts
index 2768b4cc..565bf6f5 100644
--- a/packages/playwright-test/src/reporters/html.ts
+++ b/packages/playwright-test/src/reporters/html.ts
@@ -390,15 +390,18 @@ class HtmlBuilder {
};
}

- if ((a.name === 'stdout' || a.name === 'stderr') && a.contentType === 'text/plain') {
- if (lastAttachment &&
+ if ((a.name === 'stdout' || a.name === 'stderr')) {
+ if (a.contentType === 'text/plain' && lastAttachment &&
lastAttachment.name === a.name &&
lastAttachment.contentType === a.contentType) {
lastAttachment.body += stripAnsiEscapes(a.body as string);
return null;
}
a.body = stripAnsiEscapes(a.body as string);
+ } else if (a.body && (a.contentType.toLowerCase().startsWith("text/") || a.contentType.toLowerCase() === "application/json")) {
+ a.body = Buffer.from(a.body, "base64").toString();
}

Similar to (2), but instead of in _createAttachment, conditionally decode it in the React Component around this existing line:

playwright/packages/playwright-core/src/web/htmlReport/htmlReport.tsx

Line 326
in
3c1aaa5

return [<div className='attachment-body'>{attachment.body}</div>];

Be more explicit. Change TestInfo.body from Buffer | undefined to Buffer | string | undefined. If the user supplies a string instead of a Buffer, update the following line to just use the raw string as the body:

playwright/packages/playwright-test/src/reporters/raw.ts

Line 248
in
3c1aaa5

body: attachment.body.toString('base64')

As an aside, it might be worth cleaning up the serialized attachments format so it's easy to tell if they've been base64 encoded or not. For instance, some attachments (stderr / stdout) store non-encoded data currently, while user-attachments are base64 encoded, and there's not a programmatic way to tell the difference in this mix—except magically knowing. I think it would make sense to always b64 encode the body (event if it's stderr/stdout) so it's uniform, or introduce an encoding hint (undefined | "base64") in the serialized format.

@pavelfeldman @dgozman : What's your take on the expected behavior/rendering here? If it should be plaintext for my examples above (which would greatly help us when going through our Test Reports), then what are your above thoughts on the correct fix? Happy to contribute an MR for one of the suggestions (or an alternative). Running (2) in a staging environment currently to get around the issue.