chore(ui): hide top-level attach actions (#35040)
This commit is contained in:
parent
3773a73855
commit
f44cd3050b
|
@ -0,0 +1,17 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const kTopLevelAttachmentPrefix = '_attach';
|
|
@ -4,3 +4,4 @@
|
||||||
../util.ts
|
../util.ts
|
||||||
../utilBundle.ts
|
../utilBundle.ts
|
||||||
../matchers/**
|
../matchers/**
|
||||||
|
../isomorphic/util.ts
|
||||||
|
|
|
@ -414,14 +414,10 @@ export class TestInfoImpl implements TestInfo {
|
||||||
|
|
||||||
_attach(attachment: TestInfo['attachments'][0], stepId: string | undefined) {
|
_attach(attachment: TestInfo['attachments'][0], stepId: string | undefined) {
|
||||||
const index = this._attachmentsPush(attachment) - 1;
|
const index = this._attachmentsPush(attachment) - 1;
|
||||||
if (stepId) {
|
if (stepId)
|
||||||
this._stepMap.get(stepId)!.attachmentIndices.push(index);
|
this._stepMap.get(stepId)!.attachmentIndices.push(index);
|
||||||
} else {
|
else
|
||||||
// trace viewer has no means of representing attachments outside of a step, so we create an artificial action
|
this._tracing.appendTopLevelAttachment(attachment);
|
||||||
const callId = `attach@${++this._lastStepId}`;
|
|
||||||
this._tracing.appendBeforeActionForStep(callId, this._findLastPredefinedStep(this._steps)?.stepId, 'attach', `attach "${attachment.name}"`, undefined, []);
|
|
||||||
this._tracing.appendAfterActionForStep(callId, undefined, [attachment]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._onAttach({
|
this._onAttach({
|
||||||
testId: this.testId,
|
testId: this.testId,
|
||||||
|
|
|
@ -20,6 +20,7 @@ import path from 'path';
|
||||||
import { ManualPromise, SerializedFS, calculateSha1, createGuid, monotonicTime } from 'playwright-core/lib/utils';
|
import { ManualPromise, SerializedFS, calculateSha1, createGuid, monotonicTime } from 'playwright-core/lib/utils';
|
||||||
import { yauzl, yazl } from 'playwright-core/lib/zipBundle';
|
import { yauzl, yazl } from 'playwright-core/lib/zipBundle';
|
||||||
|
|
||||||
|
import { kTopLevelAttachmentPrefix } from '../isomorphic/util';
|
||||||
import { filteredStackTrace } from '../util';
|
import { filteredStackTrace } from '../util';
|
||||||
|
|
||||||
import type { TestInfoImpl } from './testInfo';
|
import type { TestInfoImpl } from './testInfo';
|
||||||
|
@ -47,6 +48,7 @@ export class TestTracing {
|
||||||
private _tracesDir: string;
|
private _tracesDir: string;
|
||||||
private _contextCreatedEvent: trace.ContextCreatedTraceEvent;
|
private _contextCreatedEvent: trace.ContextCreatedTraceEvent;
|
||||||
private _didFinishTestFunctionAndAfterEachHooks = false;
|
private _didFinishTestFunctionAndAfterEachHooks = false;
|
||||||
|
private _lastActionId = 0;
|
||||||
|
|
||||||
constructor(testInfo: TestInfoImpl, artifactsDir: string) {
|
constructor(testInfo: TestInfoImpl, artifactsDir: string) {
|
||||||
this._testInfo = testInfo;
|
this._testInfo = testInfo;
|
||||||
|
@ -291,6 +293,16 @@ export class TestTracing {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appendTopLevelAttachment(attachment: Attachment) {
|
||||||
|
// trace viewer has no means of representing attachments outside of a step,
|
||||||
|
// so we create an artificial action that's hidden in the UI
|
||||||
|
// the alternative would be to have one hidden step at the end with all top-level attachments,
|
||||||
|
// but that would delay useful information in live traces.
|
||||||
|
const callId = `${kTopLevelAttachmentPrefix}@${++this._lastActionId}`;
|
||||||
|
this.appendBeforeActionForStep(callId, undefined, kTopLevelAttachmentPrefix, `${kTopLevelAttachmentPrefix} "${attachment.name}"`, undefined, []);
|
||||||
|
this.appendAfterActionForStep(callId, undefined, [attachment]);
|
||||||
|
}
|
||||||
|
|
||||||
private _appendTraceEvent(event: trace.TraceEvent) {
|
private _appendTraceEvent(event: trace.TraceEvent) {
|
||||||
this._traceEvents.push(event);
|
this._traceEvents.push(event);
|
||||||
if (this._liveTraceFile)
|
if (this._liveTraceFile)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import type * as trace from '@trace/trace';
|
||||||
import type { ActionTraceEvent } from '@trace/trace';
|
import type { ActionTraceEvent } from '@trace/trace';
|
||||||
import type { ActionEntry, ContextEntry, PageEntry } from '../types/entries';
|
import type { ActionEntry, ContextEntry, PageEntry } from '../types/entries';
|
||||||
import type { StackFrame } from '@protocol/channels';
|
import type { StackFrame } from '@protocol/channels';
|
||||||
|
import { kTopLevelAttachmentPrefix } from '@testIsomorphic/util';
|
||||||
|
|
||||||
const contextSymbol = Symbol('context');
|
const contextSymbol = Symbol('context');
|
||||||
const nextInContextSymbol = Symbol('next');
|
const nextInContextSymbol = Symbol('next');
|
||||||
|
@ -338,6 +339,8 @@ export function buildActionTree(actions: ActionTraceEventInContext[]): { rootIte
|
||||||
|
|
||||||
const rootItem: ActionTreeItem = { id: '', parent: undefined, children: [] };
|
const rootItem: ActionTreeItem = { id: '', parent: undefined, children: [] };
|
||||||
for (const item of itemMap.values()) {
|
for (const item of itemMap.values()) {
|
||||||
|
if (item.action?.apiName.startsWith(kTopLevelAttachmentPrefix))
|
||||||
|
continue;
|
||||||
const parent = item.action!.parentId ? itemMap.get(item.action!.parentId) || rootItem : rootItem;
|
const parent = item.action!.parentId ? itemMap.get(item.action!.parentId) || rootItem : rootItem;
|
||||||
parent.children.push(item);
|
parent.children.push(item);
|
||||||
item.parent = parent;
|
item.parent = parent;
|
||||||
|
|
|
@ -223,7 +223,7 @@ test('should record trace', async ({ runInlineTest }) => {
|
||||||
'After Hooks',
|
'After Hooks',
|
||||||
'fixture: page',
|
'fixture: page',
|
||||||
'fixture: context',
|
'fixture: context',
|
||||||
'attach "_prompt-0"',
|
'_attach "_prompt-0"',
|
||||||
'Worker Cleanup',
|
'Worker Cleanup',
|
||||||
'fixture: browser',
|
'fixture: browser',
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -129,7 +129,6 @@ test('should record api trace', async ({ runInlineTest, server }, testInfo) => {
|
||||||
' fixture: context',
|
' fixture: context',
|
||||||
' fixture: request',
|
' fixture: request',
|
||||||
' apiRequestContext.dispose',
|
' apiRequestContext.dispose',
|
||||||
' attach "_prompt-0"',
|
|
||||||
'Worker Cleanup',
|
'Worker Cleanup',
|
||||||
' fixture: browser',
|
' fixture: browser',
|
||||||
]);
|
]);
|
||||||
|
@ -328,7 +327,6 @@ test('should not override trace file in afterAll', async ({ runInlineTest, serve
|
||||||
'After Hooks',
|
'After Hooks',
|
||||||
' fixture: page',
|
' fixture: page',
|
||||||
' fixture: context',
|
' fixture: context',
|
||||||
' attach "_prompt-0"',
|
|
||||||
' afterAll hook',
|
' afterAll hook',
|
||||||
' fixture: request',
|
' fixture: request',
|
||||||
' apiRequest.newContext',
|
' apiRequest.newContext',
|
||||||
|
@ -671,7 +669,6 @@ test('should show non-expect error in trace', async ({ runInlineTest }, testInfo
|
||||||
'After Hooks',
|
'After Hooks',
|
||||||
' fixture: page',
|
' fixture: page',
|
||||||
' fixture: context',
|
' fixture: context',
|
||||||
' attach "_prompt-0"',
|
|
||||||
'Worker Cleanup',
|
'Worker Cleanup',
|
||||||
' fixture: browser',
|
' fixture: browser',
|
||||||
]);
|
]);
|
||||||
|
@ -734,7 +731,7 @@ test('should not throw when attachment is missing', async ({ runInlineTest }, te
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.passed).toBe(1);
|
expect(result.passed).toBe(1);
|
||||||
const trace = await parseTrace(testInfo.outputPath('test-results', 'a-passes', 'trace.zip'));
|
const trace = await parseTrace(testInfo.outputPath('test-results', 'a-passes', 'trace.zip'));
|
||||||
expect(trace.actionTree).toContain('attach "screenshot"');
|
expect(trace.apiNames).toContain('_attach "screenshot"');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should not throw when screenshot on failure fails', async ({ runInlineTest, server }, testInfo) => {
|
test('should not throw when screenshot on failure fails', async ({ runInlineTest, server }, testInfo) => {
|
||||||
|
@ -983,7 +980,6 @@ test('should record nested steps, even after timeout', async ({ runInlineTest },
|
||||||
' page.setContent',
|
' page.setContent',
|
||||||
' fixture: page',
|
' fixture: page',
|
||||||
' fixture: context',
|
' fixture: context',
|
||||||
' attach "_prompt-0"',
|
|
||||||
' afterAll hook',
|
' afterAll hook',
|
||||||
' fixture: barPage',
|
' fixture: barPage',
|
||||||
' barPage setup',
|
' barPage setup',
|
||||||
|
@ -1043,7 +1039,6 @@ test('should attribute worker fixture teardown to the right test', async ({ runI
|
||||||
expect(trace2.actionTree).toEqual([
|
expect(trace2.actionTree).toEqual([
|
||||||
'Before Hooks',
|
'Before Hooks',
|
||||||
'After Hooks',
|
'After Hooks',
|
||||||
' attach "_prompt-0"',
|
|
||||||
'Worker Cleanup',
|
'Worker Cleanup',
|
||||||
' fixture: foo',
|
' fixture: foo',
|
||||||
' step in foo teardown',
|
' step in foo teardown',
|
||||||
|
@ -1153,7 +1148,6 @@ test('should not corrupt actions when no library trace is present', async ({ run
|
||||||
'After Hooks',
|
'After Hooks',
|
||||||
' fixture: foo',
|
' fixture: foo',
|
||||||
' expect.toBe',
|
' expect.toBe',
|
||||||
' attach "_prompt-0"',
|
|
||||||
'Worker Cleanup',
|
'Worker Cleanup',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -1184,7 +1178,6 @@ test('should record trace for manually created context in a failed test', async
|
||||||
'page.setContent',
|
'page.setContent',
|
||||||
'expect.toBe',
|
'expect.toBe',
|
||||||
'After Hooks',
|
'After Hooks',
|
||||||
' attach "_prompt-0"',
|
|
||||||
'Worker Cleanup',
|
'Worker Cleanup',
|
||||||
' fixture: browser',
|
' fixture: browser',
|
||||||
]);
|
]);
|
||||||
|
@ -1272,7 +1265,6 @@ test('should record trace after fixture teardown timeout', {
|
||||||
'page.evaluate',
|
'page.evaluate',
|
||||||
'After Hooks',
|
'After Hooks',
|
||||||
' fixture: fixture',
|
' fixture: fixture',
|
||||||
' attach "_prompt-0"',
|
|
||||||
'Worker Cleanup',
|
'Worker Cleanup',
|
||||||
' fixture: browser',
|
' fixture: browser',
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -458,7 +458,6 @@ test('attachments tab shows all but top-level .push attachments', async ({ runUI
|
||||||
- treeitem /step/:
|
- treeitem /step/:
|
||||||
- group:
|
- group:
|
||||||
- treeitem /attach \\"foo-attach\\"/
|
- treeitem /attach \\"foo-attach\\"/
|
||||||
- treeitem /attach \\"bar-push\\"/
|
|
||||||
- treeitem /attach \\"bar-attach\\"/
|
- treeitem /attach \\"bar-attach\\"/
|
||||||
`);
|
`);
|
||||||
await page.getByRole('tab', { name: 'Attachments' }).click();
|
await page.getByRole('tab', { name: 'Attachments' }).click();
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"baseUrl": "..",
|
"baseUrl": "..",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@isomorphic/*": ["packages/playwright-core/src/utils/isomorphic/*"],
|
"@isomorphic/*": ["packages/playwright-core/src/utils/isomorphic/*"],
|
||||||
|
"@testIsomorphic/*": ["packages/playwright/src/isomorphic/*"],
|
||||||
"@protocol/*": ["packages/protocol/src/*"],
|
"@protocol/*": ["packages/protocol/src/*"],
|
||||||
"@recorder/*": ["packages/recorder/src/*"],
|
"@recorder/*": ["packages/recorder/src/*"],
|
||||||
"@trace/*": ["packages/trace/src/*"],
|
"@trace/*": ["packages/trace/src/*"],
|
||||||
|
|
Loading…
Reference in New Issue