追蹤檢視器
簡介
Playwright Trace Viewer 是一個 GUI 工具,可以幫助您在腳本執行後探索記錄的 Playwright 追蹤。追蹤是在 CI 上測試失敗時進行偵錯的好方法。您可以在本地或在瀏覽器上 trace.playwright.dev 打開追蹤。
開啟 Trace Viewer
您可以使用 Playwright CLI 或在瀏覽器 trace.playwright.dev 中開啟已儲存的追蹤。請確保加入 trace.zip
檔案所在位置的完整路徑。
pwsh bin/Debug/netX/playwright.ps1 show-trace trace.zip
使用 trace.playwright.dev
trace.playwright.dev 是 Trace Viewer 的靜態託管版本。您可以使用拖放功能或透過 Select file(s)
按鈕上傳追蹤檔案。
Trace Viewer 會將追蹤完全載入您的瀏覽器中,不會對外傳輸任何資料。

查看遠端追蹤
您可以使用其 URL 直接開啟遠端追蹤。這讓您可以輕鬆查看遠端追蹤,而無需手動從 CI 執行中下載檔案,例如。
pwsh bin/Debug/netX/playwright.ps1 show-trace https://example.com/trace.zip
當使用 trace.playwright.dev 時,您也可以將上傳追蹤的 URL 作為查詢參數傳遞到某個可存取的儲存位置(例如在您的 CI 內)。可能會適用 CORS(跨來源資源共享)規則。
https://trace.playwright.dev/?trace=https://demo.playwright.dev/reports/todomvc/data/fa874b0d59cdedec675521c21124e93161d66533.zip
錄製追蹤
追蹤可以使用 BrowserContext.Tracing API 如下記錄:
- MSTest
- NUnit
- xUnit
- xUnit v3
namespace PlaywrightTests;
[Parallelizable(ParallelScope.Self)]
[TestFixture]
public class Tests : PageTest
{
[SetUp]
public async Task Setup()
{
await Context.Tracing.StartAsync(new()
{
Title = TestContext.CurrentContext.Test.ClassName + "." + TestContext.CurrentContext.Test.Name,
Screenshots = true,
Snapshots = true,
Sources = true
});
}
[TearDown]
public async Task TearDown()
{
// This will produce e.g.:
// bin/Debug/net8.0/playwright-traces/PlaywrightTests.Tests.Test1.zip
await Context.Tracing.StopAsync(new()
{
Path = Path.Combine(
TestContext.CurrentContext.WorkDirectory,
"playwright-traces",
$"{TestContext.CurrentContext.Test.ClassName}.{TestContext.CurrentContext.Test.Name}.zip"
)
});
}
[Test]
public async Task TestYourOnlineShop()
{
// ..
}
}
using System.Text.RegularExpressions;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
namespace PlaywrightTestsMSTest;
[TestClass]
public class UnitTest1 : PageTest
{
[TestInitialize]
public async Task TestInitialize()
{
await Context.Tracing.StartAsync(new()
{
Title = TestContext.TestName,
Screenshots = true,
Snapshots = true,
Sources = true
});
}
[TestCleanup]
public async Task TestCleanup()
{
// This will produce e.g.:
// bin/Debug/net8.0/playwright-traces/PlaywrightTests.UnitTest1.zip
await Context.Tracing.StopAsync(new()
{
Path = Path.Combine(
Environment.CurrentDirectory,
"playwright-traces",
$"{TestContext.FullyQualifiedTestClassName}.zip"
)
});
}
[TestMethod]
public async Task TestYourOnlineShop()
{
// ...
}
}
using System.Reflection;
using Microsoft.Playwright;
using Microsoft.Playwright.Xunit;
using Xunit.Sdk;
namespace PlaywrightTests;
[WithTestName]
public class UnitTest1 : PageTest
{
public override async Task InitializeAsync()
{
await base.InitializeAsync().ConfigureAwait(false);
await Context.Tracing.StartAsync(new()
{
Title = $"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}",
Screenshots = true,
Snapshots = true,
Sources = true
});
}
public override async Task DisposeAsync()
{
await Context.Tracing.StopAsync(new()
{
Path = Path.Combine(
Environment.CurrentDirectory,
"playwright-traces",
$"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}.zip"
)
});
await base.DisposeAsync().ConfigureAwait(false);
}
[Fact]
public async Task GetStartedLink()
{
// ...
await Page.GotoAsync("https://playwright.dev/dotnet/docs/intro");
}
}
public class WithTestNameAttribute : BeforeAfterTestAttribute
{
public static string CurrentTestName = string.Empty;
public static string CurrentClassName = string.Empty;
public override void Before(MethodInfo methodInfo)
{
CurrentTestName = methodInfo.Name;
CurrentClassName = methodInfo.DeclaringType!.Name;
}
public override void After(MethodInfo methodInfo)
{
}
}
using System.Reflection;
using Microsoft.Playwright;
using Microsoft.Playwright.Xunit.v3;
using Xunit.Sdk;
namespace PlaywrightTests;
[WithTestName]
public class UnitTest1 : PageTest
{
public override async Task InitializeAsync()
{
await base.InitializeAsync().ConfigureAwait(false);
await Context.Tracing.StartAsync(new()
{
Title = $"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}",
Screenshots = true,
Snapshots = true,
Sources = true
});
}
public override async Task DisposeAsync()
{
await Context.Tracing.StopAsync(new()
{
Path = Path.Combine(
Environment.CurrentDirectory,
"playwright-traces",
$"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}.zip"
)
});
await base.DisposeAsync().ConfigureAwait(false);
}
[Fact]
public async Task GetStartedLink()
{
// ...
await Page.GotoAsync("https://playwright.dev/dotnet/docs/intro");
}
}
public class WithTestNameAttribute : BeforeAfterTestAttribute
{
public static string CurrentTestName = string.Empty;
public static string CurrentClassName = string.Empty;
public override void Before(MethodInfo methodInfo)
{
CurrentTestName = methodInfo.Name;
CurrentClassName = methodInfo.DeclaringType!.Name;
}
public override void After(MethodInfo methodInfo)
{
}
}
這將記錄追蹤並將其放置到 bin/Debug/net8.0/playwright-traces/
目錄中。
僅在失敗時執行追蹤
設定您的測試僅在測試失敗時記錄追蹤:
- MSTest
- NUnit
- xUnit
- xUnit v3
namespace PlaywrightTests;
[Parallelizable(ParallelScope.Self)]
[TestFixture]
public class ExampleTest : PageTest
{
[SetUp]
public async Task Setup()
{
await Context.Tracing.StartAsync(new()
{
Title = $"{TestContext.CurrentContext.Test.ClassName}.{TestContext.CurrentContext.Test.Name}",
Screenshots = true,
Snapshots = true,
Sources = true
});
}
[TearDown]
public async Task TearDown()
{
var failed = TestContext.CurrentContext.Result.Outcome == NUnit.Framework.Interfaces.ResultState.Error
|| TestContext.CurrentContext.Result.Outcome == NUnit.Framework.Interfaces.ResultState.Failure;
await Context.Tracing.StopAsync(new()
{
Path = failed ? Path.Combine(
TestContext.CurrentContext.WorkDirectory,
"playwright-traces",
$"{TestContext.CurrentContext.Test.ClassName}.{TestContext.CurrentContext.Test.Name}.zip"
) : null,
});
}
[Test]
public async Task GetStartedLink()
{
// ..
}
}
using System.Text.RegularExpressions;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
namespace PlaywrightTests;
[TestClass]
public class ExampleTest : PageTest
{
[TestInitialize]
public async Task TestInitialize()
{
await Context.Tracing.StartAsync(new()
{
Title = $"{TestContext.FullyQualifiedTestClassName}.{TestContext.TestName}",
Screenshots = true,
Snapshots = true,
Sources = true
});
}
[TestCleanup]
public async Task TestCleanup()
{
var failed = new[] { UnitTestOutcome.Failed, UnitTestOutcome.Error, UnitTestOutcome.Timeout, UnitTestOutcome.Aborted }.Contains(TestContext.CurrentTestOutcome);
await Context.Tracing.StopAsync(new()
{
Path = failed ? Path.Combine(
Environment.CurrentDirectory,
"playwright-traces",
$"{TestContext.FullyQualifiedTestClassName}.{TestContext.TestName}.zip"
) : null,
});
}
[TestMethod]
public async Task GetStartedLink()
{
// ...
}
}
using System.Reflection;
using Microsoft.Playwright;
using Microsoft.Playwright.Xunit;
using Xunit.Sdk;
namespace PlaywrightTests;
[WithTestName]
public class UnitTest1 : PageTest
{
public override async Task InitializeAsync()
{
await base.InitializeAsync().ConfigureAwait(false);
await Context.Tracing.StartAsync(new()
{
Title = $"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}",
Screenshots = true,
Snapshots = true,
Sources = true
});
}
public override async Task DisposeAsync()
{
await Context.Tracing.StopAsync(new()
{
Path = !TestOk ? Path.Combine(
Environment.CurrentDirectory,
"playwright-traces",
$"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}.zip"
) : null
});
await base.DisposeAsync().ConfigureAwait(false);
}
[Fact]
public async Task GetStartedLink()
{
// ...
await Page.GotoAsync("https://playwright.dev/dotnet/docs/intro");
}
}
public class WithTestNameAttribute : BeforeAfterTestAttribute
{
public static string CurrentTestName = string.Empty;
public static string CurrentClassName = string.Empty;
public override void Before(MethodInfo methodInfo)
{
CurrentTestName = methodInfo.Name;
CurrentClassName = methodInfo.DeclaringType!.Name;
}
public override void After(MethodInfo methodInfo)
{
}
}
using System.Reflection;
using Microsoft.Playwright;
using Microsoft.Playwright.Xunit.v3;
using Xunit.Sdk;
namespace PlaywrightTests;
[WithTestName]
public class UnitTest1 : PageTest
{
public override async Task InitializeAsync()
{
await base.InitializeAsync().ConfigureAwait(false);
await Context.Tracing.StartAsync(new()
{
Title = $"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}",
Screenshots = true,
Snapshots = true,
Sources = true
});
}
public override async Task DisposeAsync()
{
await Context.Tracing.StopAsync(new()
{
Path = !TestOk ? Path.Combine(
Environment.CurrentDirectory,
"playwright-traces",
$"{WithTestNameAttribute.CurrentClassName}.{WithTestNameAttribute.CurrentTestName}.zip"
) : null
});
await base.DisposeAsync().ConfigureAwait(false);
}
[Fact]
public async Task GetStartedLink()
{
// ...
await Page.GotoAsync("https://playwright.dev/dotnet/docs/intro");
}
}
public class WithTestNameAttribute : BeforeAfterTestAttribute
{
public static string CurrentTestName = string.Empty;
public static string CurrentClassName = string.Empty;
public override void Before(MethodInfo methodInfo)
{
CurrentTestName = methodInfo.Name;
CurrentClassName = methodInfo.DeclaringType!.Name;
}
public override void After(MethodInfo methodInfo)
{
}
}
Trace Viewer 功能
Actions
在 Actions 分頁中,您可以看到每個動作使用的定位器以及每個動作執行的時間。懸停在測試的每個動作上,視覺化地查看 DOM 快照的變化。前後移動時間並點擊動作進行檢查和偵錯。使用 Before 和 After 分頁視覺化地查看動作前後發生的情況。
選擇每個動作會顯示:
- 動作快照
- 動作日誌
- 原始程式碼位置
螢幕截圖
當使用 Screenshots 選項開啟追蹤時(預設),每個追蹤會記錄螢幕錄影並將其呈現為膠片條。您可以懸停在膠片條上查看每個動作和狀態的放大影像,這有助於您輕鬆找到要檢查的動作。
雙擊動作可查看該動作的時間範圍。您可以使用時間軸中的滑桿增加選擇的動作,這些動作將顯示在 Actions 分頁中,所有主控台日誌和網路日誌都會被篩選為僅顯示所選動作的日誌。
快照
當使用 Snapshots 選項開啟追蹤時(預設),Playwright 會為每個動作捕獲一組完整的 DOM 快照。根據動作的類型,它將捕獲:
Type | Description |
---|---|
Before | 動作呼叫時的快照。 |
Action | 執行輸入時刻的快照。此類型的快照特別有用於探索 Playwright 確切點擊的位置。 |
After | 動作後的快照。 |
這是典型的 Action 快照外觀:
注意它如何同時突出顯示 DOM Node 以及確切的點擊位置。
Source
當您點擊側邊欄中的動作時,該動作的程式碼行會在原始碼面板中突出顯示。
Call
call 分頁顯示有關動作的資訊,例如執行時間、使用的定位器、是否在嚴格模式下以及使用的鍵。
Log
查看完整的測試日誌,以更好地了解 Playwright 在幕後執行的操作,例如滾動到檢視中、等待元素可見、啟用和穩定,以及執行點擊、填寫、按壓等動作。
Errors
如果測試失敗,您將在 Errors 分頁中看到每個測試的錯誤訊息。時間軸也會顯示一條紅線,突出顯示錯誤發生的位置。您也可以點擊 source 分頁查看錯誤出現在原始程式碼的哪一行。
Console
請查看來自瀏覽器以及測試的主控台日誌。會顯示不同的圖示來表示主控台日誌是來自瀏覽器還是測試檔案。
在動作側邊欄中雙擊測試中的動作。這將篩選主控台以僅顯示該動作期間產生的日誌。點擊 Show all 按鈕可再次查看所有主控台日誌。
使用時間軸篩選動作,方法是點擊起始點並拖動到結束點。主控台分頁也會被篩選為僅顯示所選動作期間產生的日誌。
Network
Network 分頁顯示測試期間產生的所有網路請求。您可以按請求的不同類型、狀態碼、方法、請求、內容類型、持續時間和大小進行排序。點擊請求可查看更多相關資訊,例如請求標頭、回應標頭、請求內文和回應內文。
在動作側邊欄中雙擊測試中的動作。這將篩選網路請求以僅顯示該動作期間產生的請求。點擊 Show all 按鈕可再次查看所有網路請求。
使用時間軸篩選動作,方法是點擊起始點並拖動到結束點。網路分頁也會被篩選為僅顯示所選動作期間產生的網路請求。
Metadata
在 Actions 分頁旁邊,您會找到 Metadata 分頁,該分頁會顯示更多有關測試的資訊,例如瀏覽器、檢視區大小、測試持續時間等。