Tests/LibWeb: Cover UI-owned session history
Teach test-web to expose the UI-process history dump. Add focused navigation tests for same-document traversal, fallback traversal, and cross-document browser back and forward behavior. The expectations assert document state and the UI-owned history snapshot.
This commit is contained in:
parent
24f37c6732
commit
0aaae1ac76
19 changed files with 440 additions and 37 deletions
|
|
@ -687,7 +687,44 @@ String Internals::dump_session_history()
|
|||
|
||||
String Internals::dump_ui_process_session_history()
|
||||
{
|
||||
return window().associated_document().page().client().page_did_request_ui_process_session_history_for_testing();
|
||||
auto& document = window().associated_document();
|
||||
if (auto navigable = document.navigable()) {
|
||||
if (auto traversable = navigable->traversable_navigable();
|
||||
traversable && document.page().client().should_report_session_history_updates()) {
|
||||
auto session_history_snapshot = traversable->create_session_history_snapshot();
|
||||
document.page().client().page_did_update_session_history(
|
||||
session_history_snapshot.top_level_session_history_entries,
|
||||
session_history_snapshot.used_session_history_steps,
|
||||
session_history_snapshot.current_used_step_index);
|
||||
}
|
||||
}
|
||||
|
||||
return document.page().client().page_did_request_ui_process_session_history_for_testing();
|
||||
}
|
||||
|
||||
GC::Ref<WebIDL::Promise> Internals::flush_session_history_traversal_queue()
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
auto& document = window().associated_document();
|
||||
auto navigable = document.navigable();
|
||||
if (!navigable) {
|
||||
WebIDL::resolve_promise(realm, promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
auto traversable = navigable->traversable_navigable();
|
||||
if (!traversable) {
|
||||
WebIDL::resolve_promise(realm, promise);
|
||||
return promise;
|
||||
}
|
||||
|
||||
traversable->append_session_history_traversal_steps(GC::create_function(heap(), [&realm, promise](NonnullRefPtr<Core::Promise<Empty>> signal) {
|
||||
HTML::TemporaryExecutionContext execution_context { realm };
|
||||
WebIDL::resolve_promise(realm, promise);
|
||||
signal->resolve({});
|
||||
}));
|
||||
return promise;
|
||||
}
|
||||
|
||||
GC::Ptr<DOM::ShadowRoot> Internals::get_shadow_root(GC::Ref<DOM::Element> element)
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ public:
|
|||
String dump_gc_graph();
|
||||
String dump_session_history();
|
||||
String dump_ui_process_session_history();
|
||||
GC::Ref<WebIDL::Promise> flush_session_history_traversal_queue();
|
||||
|
||||
GC::Ptr<DOM::ShadowRoot> get_shadow_root(GC::Ref<DOM::Element>);
|
||||
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ interface Internals {
|
|||
DOMString dumpGCGraph();
|
||||
DOMString dumpSessionHistory();
|
||||
DOMString dumpUIProcessSessionHistory();
|
||||
Promise<undefined> flushSessionHistoryTraversalQueue();
|
||||
|
||||
// Returns the shadow root of the element, if it has one, even if it's not normally accessible to JS.
|
||||
ShadowRoot? getShadowRoot(Element element);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ set(TEST_SOURCES
|
|||
TestMicrosyntax.cpp
|
||||
TestMimeSniff.cpp
|
||||
TestNumbers.cpp
|
||||
TestPage.cpp
|
||||
TestRefCountedTreeNode.cpp
|
||||
TestSecureContexts.cpp
|
||||
TestSourceHighlighter.cpp
|
||||
|
|
@ -30,6 +31,7 @@ target_link_libraries(TestContentBlocker PRIVATE LibURL)
|
|||
target_link_libraries(TestControlMessageQueue PRIVATE LibSync)
|
||||
target_link_libraries(TestFetchResponse PRIVATE LibGC LibHTTP LibJS)
|
||||
target_link_libraries(TestFetchURL PRIVATE LibURL)
|
||||
target_link_libraries(TestPage PRIVATE LibGC LibJS)
|
||||
target_link_libraries(TestSecureContexts PRIVATE LibURL)
|
||||
target_link_libraries(TestSourceHighlighter PRIVATE LibURL LibWebView)
|
||||
|
||||
|
|
|
|||
72
Tests/LibWeb/TestPage.cpp
Normal file
72
Tests/LibWeb/TestPage.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present, the Ladybird developers.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibTest/TestCase.h>
|
||||
#include <LibWeb/Page/Page.h>
|
||||
|
||||
class TestPageClient final : public Web::PageClient {
|
||||
GC_CELL(TestPageClient, Web::PageClient);
|
||||
GC_DECLARE_ALLOCATOR(TestPageClient);
|
||||
|
||||
public:
|
||||
virtual u64 id() const override { return 1; }
|
||||
virtual Web::Page& page() override { return *m_page; }
|
||||
virtual Web::Page const& page() const override { return *m_page; }
|
||||
virtual bool is_connection_open() const override { return true; }
|
||||
virtual Gfx::Palette palette() const override { VERIFY_NOT_REACHED(); }
|
||||
virtual Web::DevicePixelRect screen_rect() const override { return {}; }
|
||||
virtual double zoom_level() const override { return 1; }
|
||||
virtual double device_pixel_ratio() const override { return 1; }
|
||||
virtual double device_pixels_per_css_pixel() const override { return 1; }
|
||||
virtual Web::CSS::PreferredColorScheme preferred_color_scheme() const override { return Web::CSS::PreferredColorScheme::Auto; }
|
||||
virtual Web::CSS::PreferredContrast preferred_contrast() const override { return Web::CSS::PreferredContrast::Auto; }
|
||||
virtual Web::CSS::PreferredMotion preferred_motion() const override { return Web::CSS::PreferredMotion::NoPreference; }
|
||||
virtual size_t screen_count() const override { return 1; }
|
||||
virtual Queue<Web::QueuedInputEvent>& input_event_queue() override { VERIFY_NOT_REACHED(); }
|
||||
virtual void report_finished_handling_input_event(u64, Web::EventResult) override { }
|
||||
virtual void request_frame() override { }
|
||||
virtual void request_file(Web::FileRequest) override { }
|
||||
virtual Web::DisplayListPlayerType display_list_player_type() const override { return Web::DisplayListPlayerType::SkiaCPU; }
|
||||
virtual bool is_headless() const override { return true; }
|
||||
virtual void visit_edges(Visitor& visitor) override
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_page);
|
||||
}
|
||||
|
||||
virtual bool page_did_request_traverse_the_history_by_delta(int delta, Web::HistoryTraversalPrecheck history_traversal_precheck) override
|
||||
{
|
||||
++traversal_request_count;
|
||||
last_traversal_delta = delta;
|
||||
last_history_traversal_precheck = history_traversal_precheck;
|
||||
return accept_traversal_request;
|
||||
}
|
||||
|
||||
bool accept_traversal_request { true };
|
||||
size_t traversal_request_count { 0 };
|
||||
Optional<int> last_traversal_delta;
|
||||
Optional<Web::HistoryTraversalPrecheck> last_history_traversal_precheck;
|
||||
GC::Ptr<Web::Page> m_page;
|
||||
};
|
||||
|
||||
GC_DEFINE_ALLOCATOR(TestPageClient);
|
||||
|
||||
TEST_CASE(browser_traversal_requests_embedder)
|
||||
{
|
||||
auto vm = JS::VM::create();
|
||||
auto client = vm->heap().allocate<TestPageClient>();
|
||||
auto page = Web::Page::create(*vm, client);
|
||||
client->m_page = page.ptr();
|
||||
|
||||
page->traverse_the_history_by_delta(-1);
|
||||
|
||||
EXPECT_EQ(client->traversal_request_count, 1uz);
|
||||
VERIFY(client->last_traversal_delta.has_value());
|
||||
EXPECT_EQ(*client->last_traversal_delta, -1);
|
||||
VERIFY(client->last_history_traversal_precheck.has_value());
|
||||
EXPECT_EQ(*client->last_history_traversal_precheck, Web::HistoryTraversalPrecheck::Needed);
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
location=?second
|
||||
ui-current=?second
|
||||
ui-buttons=true,false
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
after-navigation: location=?second
|
||||
after-navigation: ui-current=?second
|
||||
after-navigation: ui-current-entry=?second*
|
||||
after-navigation: ui-webcontent-known-current-entry=?second*
|
||||
after-navigation: ui-forward-button=false
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
after-back: location=?same-document-back-replaced
|
||||
after-back: ui-current=?same-document-back-replaced
|
||||
after-back: ui-relevant-entries=?same-document-back-replaced*,?same-document-back-pushed
|
||||
after-back: ui-webcontent-known-relevant-entries=?same-document-back-replaced*,?same-document-back-pushed
|
||||
after-back: ui-forward-button=true
|
||||
after-back: ui-uses-ui-step-coordinates=true
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
after-push-state: location=?same-document-pushed
|
||||
after-push-state: ui-current=?same-document-pushed
|
||||
after-push-state: ui-relevant-entries=?same-document-replaced,?same-document-pushed*
|
||||
after-push-state: ui-webcontent-known-relevant-entries=?same-document-replaced,?same-document-pushed*
|
||||
after-push-state: ui-buttons=true,false
|
||||
after-push-state: ui-uses-ui-step-coordinates=true
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<!doctype html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
function queryFromURL(url) {
|
||||
return new URL(url).search || "(none)";
|
||||
}
|
||||
|
||||
function currentUIHistory() {
|
||||
return JSON.parse(internals.dumpUIProcessSessionHistory());
|
||||
}
|
||||
|
||||
asyncTest(async done => {
|
||||
history.replaceState({}, "", "?first");
|
||||
history.pushState({}, "", "?second");
|
||||
await internals.flushSessionHistoryTraversalQueue();
|
||||
|
||||
const initialUIHistory = currentUIHistory();
|
||||
if (location.search !== "?second"
|
||||
|| queryFromURL(initialUIHistory.currentURL) !== "?second"
|
||||
|| !initialUIHistory.backButtonEnabled
|
||||
|| initialUIHistory.forwardButtonEnabled)
|
||||
println(`FAIL: unexpected initial UI history ${JSON.stringify(initialUIHistory)}`);
|
||||
|
||||
let didTraverse = false;
|
||||
window.addEventListener("popstate", () => {
|
||||
didTraverse = true;
|
||||
});
|
||||
|
||||
await new Promise(resolve => {
|
||||
window.addEventListener("message", event => {
|
||||
if (event.data === "sandboxed-history-back-attempted")
|
||||
resolve();
|
||||
}, { once: true });
|
||||
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.setAttribute("sandbox", "allow-scripts");
|
||||
iframe.src = `data:text/html,${encodeURIComponent(`
|
||||
<script>
|
||||
history.back();
|
||||
parent.postMessage("sandboxed-history-back-attempted", "*");
|
||||
<\/script>
|
||||
`)}`;
|
||||
document.body.append(iframe);
|
||||
});
|
||||
|
||||
if (didTraverse || location.search !== "?second") {
|
||||
println(`unexpected traversal: location=${queryFromURL(location.href)}`);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
const uiHistory = currentUIHistory();
|
||||
if (queryFromURL(uiHistory.currentURL) !== "?second"
|
||||
|| !uiHistory.backButtonEnabled
|
||||
|| uiHistory.forwardButtonEnabled)
|
||||
println(`FAIL: unexpected UI history ${JSON.stringify(uiHistory)}`);
|
||||
|
||||
println(`location=${queryFromURL(location.href)}`);
|
||||
println(`ui-current=${queryFromURL(uiHistory.currentURL)}`);
|
||||
println(`ui-buttons=${uiHistory.backButtonEnabled},${uiHistory.forwardButtonEnabled}`);
|
||||
done();
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<!doctype html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
const testKey = "ui-process-session-history-dump-phase";
|
||||
|
||||
function queryFromURL(url) {
|
||||
return new URL(url).search || "(none)";
|
||||
}
|
||||
|
||||
function secondEntryQueries(entries) {
|
||||
return entries
|
||||
.filter(entry => queryFromURL(entry.url) === "?second")
|
||||
.map(entry => `${queryFromURL(entry.url)}${entry.current ? "*" : ""}`)
|
||||
.join(",");
|
||||
}
|
||||
|
||||
function currentUIHistory() {
|
||||
return JSON.parse(internals.dumpUIProcessSessionHistory());
|
||||
}
|
||||
|
||||
function nextTask() {
|
||||
return new Promise(resolve => {
|
||||
const message = `ui-process-session-history-dump-${++nextTask.messageId}`;
|
||||
addEventListener("message", event => {
|
||||
if (event.source === window && event.data === message)
|
||||
resolve();
|
||||
}, { once: true });
|
||||
postMessage(message, "*");
|
||||
});
|
||||
}
|
||||
nextTask.messageId = 0;
|
||||
|
||||
function dump(label, uiHistory) {
|
||||
println(`${label}: location=${queryFromURL(location.href)}`);
|
||||
println(`${label}: ui-current=${queryFromURL(uiHistory.currentURL)}`);
|
||||
println(`${label}: ui-current-entry=${secondEntryQueries(uiHistory.entries)}`);
|
||||
println(`${label}: ui-webcontent-known-current-entry=${secondEntryQueries(uiHistory.webContentKnownEntries)}`);
|
||||
println(`${label}: ui-forward-button=${uiHistory.forwardButtonEnabled}`);
|
||||
}
|
||||
|
||||
asyncTest(async done => {
|
||||
if (location.search === "?second") {
|
||||
await nextTask();
|
||||
const uiHistory = currentUIHistory();
|
||||
if (queryFromURL(uiHistory.currentURL) !== "?second"
|
||||
|| secondEntryQueries(uiHistory.entries) !== "?second*"
|
||||
|| secondEntryQueries(uiHistory.webContentKnownEntries) !== "?second*"
|
||||
|| uiHistory.forwardButtonEnabled
|
||||
|| uiHistory.webContentKnownEntries.length !== 1)
|
||||
println(`FAIL: unexpected UI history ${JSON.stringify(uiHistory)}`);
|
||||
dump("after-navigation", uiHistory);
|
||||
sessionStorage.removeItem(testKey);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
sessionStorage.setItem(testKey, "started");
|
||||
location.href = "?second";
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<!doctype html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
function queryFromURL(url) {
|
||||
return new URL(url).search || "(none)";
|
||||
}
|
||||
|
||||
function relevantEntryQueries(entries, queries) {
|
||||
return entries
|
||||
.filter(entry => queries.includes(queryFromURL(entry.url)))
|
||||
.map(entry => `${queryFromURL(entry.url)}${entry.current ? "*" : ""}`)
|
||||
.join(",");
|
||||
}
|
||||
|
||||
function currentUIHistory() {
|
||||
return JSON.parse(internals.dumpUIProcessSessionHistory());
|
||||
}
|
||||
|
||||
function waitForPopState() {
|
||||
return new Promise(resolve => addEventListener("popstate", resolve, { once: true }));
|
||||
}
|
||||
|
||||
function dump(label, uiHistory) {
|
||||
println(`${label}: location=${queryFromURL(location.href)}`);
|
||||
println(`${label}: ui-current=${queryFromURL(uiHistory.currentURL)}`);
|
||||
println(`${label}: ui-relevant-entries=${relevantEntryQueries(uiHistory.entries, ["?same-document-back-replaced", "?same-document-back-pushed"])}`);
|
||||
println(`${label}: ui-webcontent-known-relevant-entries=${relevantEntryQueries(uiHistory.webContentKnownEntries, ["?same-document-back-replaced", "?same-document-back-pushed"])}`);
|
||||
println(`${label}: ui-forward-button=${uiHistory.forwardButtonEnabled}`);
|
||||
println(`${label}: ui-uses-ui-step-coordinates=${uiHistory.webContentUsesUIStepCoordinates}`);
|
||||
}
|
||||
|
||||
asyncTest(async done => {
|
||||
history.replaceState({ replaced: true }, "", "?same-document-back-replaced");
|
||||
history.pushState({ pushed: true }, "", "?same-document-back-pushed");
|
||||
await internals.flushSessionHistoryTraversalQueue();
|
||||
|
||||
const initialUIHistory = currentUIHistory();
|
||||
if (!internals.dumpSessionHistory().includes("ui-process-session-history-same-document-back.html?same-document-back-pushed (current)")
|
||||
|| queryFromURL(initialUIHistory.currentURL) !== "?same-document-back-pushed"
|
||||
|| relevantEntryQueries(initialUIHistory.entries, ["?same-document-back-replaced", "?same-document-back-pushed"]) !== "?same-document-back-replaced,?same-document-back-pushed*"
|
||||
|| !initialUIHistory.backButtonEnabled
|
||||
|| initialUIHistory.forwardButtonEnabled)
|
||||
println(`FAIL: unexpected initial UI history ${JSON.stringify(initialUIHistory)}`);
|
||||
|
||||
const popStatePromise = waitForPopState();
|
||||
history.back();
|
||||
await popStatePromise;
|
||||
await internals.flushSessionHistoryTraversalQueue();
|
||||
|
||||
const uiHistory = currentUIHistory();
|
||||
if (!internals.dumpSessionHistory().includes("ui-process-session-history-same-document-back.html?same-document-back-replaced (current)")
|
||||
|| queryFromURL(uiHistory.currentURL) !== "?same-document-back-replaced"
|
||||
|| relevantEntryQueries(uiHistory.entries, ["?same-document-back-replaced", "?same-document-back-pushed"]) !== "?same-document-back-replaced*,?same-document-back-pushed"
|
||||
|| !uiHistory.forwardButtonEnabled)
|
||||
println(`FAIL: unexpected UI history ${JSON.stringify(uiHistory)}`);
|
||||
|
||||
dump("after-back", uiHistory);
|
||||
done();
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<!doctype html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
function queryFromURL(url) {
|
||||
return new URL(url).search || "(none)";
|
||||
}
|
||||
|
||||
function relevantEntryQueries(entries, queries) {
|
||||
return entries
|
||||
.filter(entry => queries.includes(queryFromURL(entry.url)))
|
||||
.map(entry => `${queryFromURL(entry.url)}${entry.current ? "*" : ""}`)
|
||||
.join(",");
|
||||
}
|
||||
|
||||
function currentUIHistory() {
|
||||
return JSON.parse(internals.dumpUIProcessSessionHistory());
|
||||
}
|
||||
|
||||
function dump(label, uiHistory) {
|
||||
println(`${label}: location=${queryFromURL(location.href)}`);
|
||||
println(`${label}: ui-current=${queryFromURL(uiHistory.currentURL)}`);
|
||||
println(`${label}: ui-relevant-entries=${relevantEntryQueries(uiHistory.entries, ["?same-document-replaced", "?same-document-pushed"])}`);
|
||||
println(`${label}: ui-webcontent-known-relevant-entries=${relevantEntryQueries(uiHistory.webContentKnownEntries, ["?same-document-replaced", "?same-document-pushed"])}`);
|
||||
println(`${label}: ui-buttons=${uiHistory.backButtonEnabled},${uiHistory.forwardButtonEnabled}`);
|
||||
println(`${label}: ui-uses-ui-step-coordinates=${uiHistory.webContentUsesUIStepCoordinates}`);
|
||||
}
|
||||
|
||||
asyncTest(async done => {
|
||||
history.replaceState({ replaced: true }, "", "?same-document-replaced");
|
||||
history.pushState({ pushed: true }, "", "?same-document-pushed");
|
||||
await internals.flushSessionHistoryTraversalQueue();
|
||||
|
||||
const uiHistory = currentUIHistory();
|
||||
if (!internals.dumpSessionHistory().includes("ui-process-session-history-same-document.html?same-document-pushed (current)")
|
||||
|| queryFromURL(uiHistory.currentURL) !== "?same-document-pushed"
|
||||
|| relevantEntryQueries(uiHistory.entries, ["?same-document-replaced", "?same-document-pushed"]) !== "?same-document-replaced,?same-document-pushed*"
|
||||
|| !uiHistory.backButtonEnabled
|
||||
|| uiHistory.forwardButtonEnabled)
|
||||
println(`FAIL: unexpected UI history ${JSON.stringify(uiHistory)}`);
|
||||
|
||||
dump("after-push-state", uiHistory);
|
||||
done();
|
||||
});
|
||||
</script>
|
||||
|
|
@ -46,6 +46,8 @@ void Application::create_platform_arguments(Core::ArgsParser& args_parser)
|
|||
args_parser.add_option(test_dry_run, "List the tests that would be run, without running them", "dry-run");
|
||||
args_parser.add_option(rebaseline, "Rebaseline any executed layout or text tests", "rebaseline");
|
||||
args_parser.add_option(shuffle, "Shuffle the order of tests before running them", "shuffle", 's');
|
||||
args_parser.add_option(run_ui_process_session_history_tests, "Run tests that require UI-process session history seeding",
|
||||
"run-ui-process-session-history-tests");
|
||||
args_parser.add_option(per_test_timeout_in_seconds, "Per-test timeout (default: 30)", "per-test-timeout", 't', "seconds");
|
||||
|
||||
args_parser.add_option(Core::ArgsParser::Option {
|
||||
|
|
@ -85,6 +87,8 @@ void Application::create_platform_options(WebView::BrowserOptions& browser_optio
|
|||
// Ensure consistent time zone operations across different machine configurations.
|
||||
web_content_options.default_time_zone = "UTC"sv;
|
||||
|
||||
web_content_options.report_session_history_updates_in_test_mode = WebView::ReportSessionHistoryUpdatesInTestMode::Yes;
|
||||
|
||||
if (dump_gc_graph) {
|
||||
// Force all tests to run in serial if we are interested in the GC graph.
|
||||
test_concurrency = 1;
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public:
|
|||
bool test_dry_run { false };
|
||||
bool rebaseline { false };
|
||||
bool shuffle { false };
|
||||
|
||||
bool run_ui_process_session_history_tests { false };
|
||||
int per_test_timeout_in_seconds { 30 };
|
||||
|
||||
u8 verbosity { 0 };
|
||||
|
|
|
|||
|
|
@ -35,7 +35,17 @@ if (BUILD_TESTING)
|
|||
COMMAND $<TARGET_FILE:test-web> --python-executable ${Python3_EXECUTABLE} --per-test-timeout 120 -v -v
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME LibWebUIProcessSessionHistory
|
||||
COMMAND $<TARGET_FILE:test-web> --python-executable ${Python3_EXECUTABLE} --per-test-timeout 120
|
||||
--force-new-process --run-ui-process-session-history-tests -f "Text/input/navigation/*"
|
||||
--results-dir test-dumps/ui-process-session-history-results
|
||||
)
|
||||
|
||||
set_tests_properties(LibWeb PROPERTIES
|
||||
ENVIRONMENT LADYBIRD_SOURCE_DIR=${LADYBIRD_SOURCE_DIR}
|
||||
TIMEOUT_SIGNAL_NAME SIGTERM)
|
||||
set_tests_properties(LibWebUIProcessSessionHistory PROPERTIES
|
||||
ENVIRONMENT LADYBIRD_SOURCE_DIR=${LADYBIRD_SOURCE_DIR}
|
||||
TIMEOUT_SIGNAL_NAME SIGTERM)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include "TestWebView.h"
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
#include <LibCore/AnonymousBuffer.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ShareableBitmap.h>
|
||||
|
|
@ -31,6 +33,11 @@ void TestWebView::clear_content_blockers()
|
|||
client().async_set_content_blockers(m_client_state.page_index, MUST(Core::AnonymousBuffer::create_with_size(0)));
|
||||
}
|
||||
|
||||
NonnullRefPtr<Core::Promise<Empty>> TestWebView::reset_session_history()
|
||||
{
|
||||
return WebView::ViewImplementation::reset_session_history_for_testing();
|
||||
}
|
||||
|
||||
pid_t TestWebView::web_content_pid() const
|
||||
{
|
||||
return client().pid();
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public:
|
|||
static NonnullOwnPtr<TestWebView> create(Core::AnonymousBuffer theme, Web::DevicePixelSize window_size);
|
||||
|
||||
void clear_content_blockers();
|
||||
NonnullRefPtr<Core::Promise<Empty>> reset_session_history();
|
||||
pid_t web_content_pid() const;
|
||||
|
||||
NonnullRefPtr<Core::Promise<RefPtr<Gfx::Bitmap const>>> take_screenshot();
|
||||
|
|
@ -35,7 +36,6 @@ private:
|
|||
TestWebView(Core::AnonymousBuffer theme, Web::DevicePixelSize viewport_size);
|
||||
|
||||
virtual void did_receive_screenshot(Badge<WebView::WebContentClient>, Gfx::ShareableBitmap const& screenshot) override;
|
||||
|
||||
RefPtr<Core::Promise<RefPtr<Gfx::Bitmap const>>> m_pending_screenshot;
|
||||
|
||||
NonnullRefPtr<TestPromise> m_test_promise;
|
||||
|
|
|
|||
|
|
@ -158,6 +158,25 @@ static ErrorOr<void> skip_async_scrolling_tests_unless_enabled(Application const
|
|||
return enumerate_test_files_recursively(path, s_skipped_tests);
|
||||
}
|
||||
|
||||
static ErrorOr<void> skip_ui_process_session_history_tests_unless_enabled(Application const& app)
|
||||
{
|
||||
if (app.run_ui_process_session_history_tests)
|
||||
return {};
|
||||
|
||||
static constexpr Array ui_process_session_history_tests {
|
||||
"Text/input/navigation/ui-process-session-history-dump.html"sv,
|
||||
"Text/input/navigation/ui-process-session-history-same-document-back.html"sv,
|
||||
"Text/input/navigation/ui-process-session-history-same-document.html"sv,
|
||||
};
|
||||
|
||||
for (auto const& test : ui_process_session_history_tests) {
|
||||
auto path = LexicalPath::join(app.test_root_path, test).string();
|
||||
s_skipped_tests.append(TRY(real_path_for_test_input(path)));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static void log_active_test_views(StringView reason)
|
||||
{
|
||||
outln();
|
||||
|
|
@ -872,7 +891,6 @@ static void run_test(TestWebView& view, TestRunContext& context, size_t test_ind
|
|||
};
|
||||
|
||||
// Clear the current document.
|
||||
// FIXME: Implement a debug-request to do this more thoroughly.
|
||||
auto promise = Core::Promise<Empty>::construct();
|
||||
|
||||
view.on_load_finish = [promise](auto const& url) {
|
||||
|
|
@ -887,44 +905,47 @@ static void run_test(TestWebView& view, TestRunContext& context, size_t test_ind
|
|||
view.on_test_finish = {};
|
||||
|
||||
promise->when_resolved([&view, test_index, &app, &context](auto) {
|
||||
auto& test = context.tests[test_index];
|
||||
test.did_start_test = true;
|
||||
view.reset_session_history()->when_resolved([&view, test_index, &app, &context](auto) {
|
||||
auto& test = context.tests[test_index];
|
||||
test.did_start_test = true;
|
||||
|
||||
auto real_path = MUST(FileSystem::real_path(test.input_path));
|
||||
auto headers_path = ByteString::formatted("{}.headers", real_path);
|
||||
auto real_path = MUST(FileSystem::real_path(test.input_path));
|
||||
auto headers_path = ByteString::formatted("{}.headers", real_path);
|
||||
|
||||
Optional<URL::URL> url;
|
||||
if (FileSystem::exists(headers_path) || s_loaded_from_http_server.contains_slow(test.input_path)) {
|
||||
// Some tests need to be served via the echo server so, for example, HTTP headers from .headers files are
|
||||
// sent, or so that the resulting HTML document has a HTTP based origin (e.g for testing cookies).
|
||||
auto echo_server_port = Application::web_content_options().echo_server_port;
|
||||
VERIFY(echo_server_port.has_value());
|
||||
auto relative_path = LexicalPath::relative_path(real_path, app.test_root_path);
|
||||
VERIFY(relative_path.has_value());
|
||||
url = URL::Parser::basic_parse(ByteString::formatted("http://{}:{}/static/{}", unique_localhost_hostname("test-web"sv), echo_server_port.value(), relative_path.value())).release_value();
|
||||
} else {
|
||||
url = URL::create_with_file_scheme(real_path).release_value();
|
||||
}
|
||||
Optional<URL::URL> url;
|
||||
if (FileSystem::exists(headers_path) || s_loaded_from_http_server.contains_slow(test.input_path)) {
|
||||
// Some tests need to be served via the echo server so, for example, HTTP headers from .headers
|
||||
// files are sent, or so that the resulting HTML document has a HTTP based origin (e.g for testing
|
||||
// cookies).
|
||||
auto echo_server_port = Application::web_content_options().echo_server_port;
|
||||
VERIFY(echo_server_port.has_value());
|
||||
auto relative_path = LexicalPath::relative_path(real_path, app.test_root_path);
|
||||
VERIFY(relative_path.has_value());
|
||||
url = URL::Parser::basic_parse(ByteString::formatted("http://{}:{}/static/{}", unique_localhost_hostname("test-web"sv), echo_server_port.value(), relative_path.value())).release_value();
|
||||
} else {
|
||||
url = URL::create_with_file_scheme(real_path).release_value();
|
||||
}
|
||||
|
||||
// Append variant query string if present (variant is "?foo=bar", set_query expects "foo=bar")
|
||||
if (test.variant.has_value())
|
||||
url->set_query(MUST(test.variant->substring_from_byte_offset_with_shared_superstring(1)));
|
||||
// Append variant query string if present (variant is "?foo=bar", set_query expects "foo=bar")
|
||||
if (test.variant.has_value())
|
||||
url->set_query(MUST(test.variant->substring_from_byte_offset_with_shared_superstring(1)));
|
||||
|
||||
switch (test.mode) {
|
||||
case TestMode::Crash:
|
||||
case TestMode::Text:
|
||||
case TestMode::Layout:
|
||||
run_dump_test(view, context, test, *url);
|
||||
return;
|
||||
case TestMode::Ref:
|
||||
run_ref_test(view, context, test, *url);
|
||||
return;
|
||||
case TestMode::Screenshot:
|
||||
run_screenshot_test(view, context, test, *url);
|
||||
return;
|
||||
}
|
||||
switch (test.mode) {
|
||||
case TestMode::Crash:
|
||||
case TestMode::Text:
|
||||
case TestMode::Layout:
|
||||
run_dump_test(view, context, test, *url);
|
||||
return;
|
||||
case TestMode::Ref:
|
||||
run_ref_test(view, context, test, *url);
|
||||
return;
|
||||
case TestMode::Screenshot:
|
||||
run_screenshot_test(view, context, test, *url);
|
||||
return;
|
||||
}
|
||||
|
||||
VERIFY_NOT_REACHED();
|
||||
VERIFY_NOT_REACHED();
|
||||
});
|
||||
});
|
||||
|
||||
view.load(URL::about_blank());
|
||||
|
|
@ -998,6 +1019,7 @@ static ErrorOr<int> run_tests(Core::AnonymousBuffer const& theme, Web::DevicePix
|
|||
|
||||
TRY(load_test_config(app.test_root_path));
|
||||
TRY(skip_async_scrolling_tests_unless_enabled(app));
|
||||
TRY(skip_ui_process_session_history_tests_unless_enabled(app));
|
||||
|
||||
Vector<Test> tests;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue