LibWeb+WebContent+headless-browser: Support async text tests

Previously, we used `on_load_finish` to determine when the text test
was completed. This method did not allow testing of async functions
because there was no way to indicate that the runner should wait for
the async call to end.

This change introduces a function in the `internals` object that is
intended to be called when the text test execution is completed. The
text test runner will now ignore `on_load_finish` which means a test
will timeout if this new function is never called.

`test(f)` function in `include.js` has been modified to automatically
terminate a test once `load` event is fired on `window`.
new `asyncTest(f)` function has been introduces. `f` receives function
that will terminate a test as a first argument.

Every test is expected to call either `test()` or `asyncTest()` to
complete. If not, it will remain hanging until a timeout occurs.
This commit is contained in:
Aliaksandr Kalenik 2023-09-14 19:17:32 +02:00 committed by Andreas Kling
parent c74f7e5f2a
commit 6f8be44c0e
20 changed files with 98 additions and 40 deletions

View file

@ -1 +1,8 @@
<head><link rel="stylesheet" href="style-sheet-with-byte-order-mark.css"></head><div class="pass">PASS
<head>
<link rel="stylesheet" href="style-sheet-with-byte-order-mark.css" />
</head>
<div class="pass">PASS</div>
<script src="../include.js"></script>
<script>
test(() => {});
</script>

View file

@ -12,4 +12,14 @@ document.addEventListener("DOMContentLoaded", function () {
function test(f) {
document.addEventListener("DOMContentLoaded", f);
window.addEventListener("load", () => {
internals.signalTextTestIsDone();
});
}
function asyncTest(f) {
const done = () => internals.signalTextTestIsDone();
document.addEventListener("DOMContentLoaded", () => {
f(done);
});
}

View file

@ -1,17 +1,19 @@
<style>
body {
background: green;
}
body {
background: green;
}
</style>
<script src="include.js"></script>
<script>
let link = document.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute("href", "body-background-color-red.css");
link.setAttribute("media", "print")
document.head.appendChild(link)
test(() => {
let link = document.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute("href", "body-background-color-red.css");
link.setAttribute("media", "print");
document.head.appendChild(link);
window.onload = function() {
println("document background: " + getComputedStyle(document.body).backgroundColor)
}
window.onload = function () {
println("document background: " + getComputedStyle(document.body).backgroundColor);
};
});
</script>

View file

@ -1,9 +1,11 @@
<script src="include.js"></script>
<script>
let link = document.createElement("link");
link.setAttribute("rel", "preload");
link.setAttribute("href", "valid.css");
link.setAttribute("as", "style");
link.setAttribute("onload", "println('link element onload')");
document.head.appendChild(link);
test(() => {
let link = document.createElement("link");
link.setAttribute("rel", "preload");
link.setAttribute("href", "valid.css");
link.setAttribute("as", "style");
link.setAttribute("onload", "println('link element onload')");
document.head.appendChild(link);
});
</script>

View file

@ -1,20 +1,22 @@
<script src="include.js"></script>
<script>
let goodLink = document.createElement("link");
goodLink.setAttribute("rel", "preload");
goodLink.setAttribute("href", "valid.css");
goodLink.setAttribute("as", "style");
goodLink.addEventListener("load", function() {
println("Got load event");
});
document.head.appendChild(goodLink);
test(() => {
let goodLink = document.createElement("link");
goodLink.setAttribute("rel", "preload");
goodLink.setAttribute("href", "valid.css");
goodLink.setAttribute("as", "style");
goodLink.addEventListener("load", function () {
println("Got load event");
});
document.head.appendChild(goodLink);
let badLink = document.createElement("link");
badLink.setAttribute("rel", "preload");
badLink.setAttribute("href", "this-file-does-not-exist-and-so-is-invalid.css");
badLink.setAttribute("as", "style");
badLink.addEventListener("error", function() {
println("Got error event");
let badLink = document.createElement("link");
badLink.setAttribute("rel", "preload");
badLink.setAttribute("href", "this-file-does-not-exist-and-so-is-invalid.css");
badLink.setAttribute("as", "style");
badLink.addEventListener("error", function () {
println("Got error event");
});
document.head.appendChild(badLink);
});
document.head.appendChild(badLink);
</script>

View file

@ -1,7 +1,10 @@
<a id="link-with-query" rel="search" href="https://serenityos.org/search?q=some:query"</a>
<a id="link-with-query" rel="search" href="https://serenityos.org/search?q=some:query"></a>
<script src="include.js"></script>
<script>
window.onload = function() {
println(document.getElementById("link-with-query").search);
}
asyncTest((done) => {
window.onload = function () {
println(document.getElementById("link-with-query").search);
done();
};
});
</script>

View file

@ -1,4 +1,6 @@
<body></body>
<script src="include.js"></script>
<script>
document.body.appendChild(document.createTextNode("PASS"));
test(() => {});
</script>

View file

@ -28,6 +28,13 @@ void Internals::initialize(JS::Realm& realm)
Object::set_prototype(&Bindings::ensure_web_prototype<Bindings::InternalsPrototype>(realm, "Internals"));
}
void Internals::signal_text_test_is_done()
{
if (auto* page = global_object().browsing_context()->page()) {
page->client().page_did_finish_text_test();
}
}
void Internals::gc()
{
vm().heap().collect_garbage();

View file

@ -16,6 +16,8 @@ class Internals final : public Bindings::PlatformObject {
public:
virtual ~Internals() override;
void signal_text_test_is_done();
void gc();
JS::Object* hit_test(double x, double y);

View file

@ -1,5 +1,6 @@
[Exposed=Nobody] interface Internals {
undefined signalTextTestIsDone();
undefined gc();
object hitTest(double x, double y);

View file

@ -241,6 +241,8 @@ public:
// https://html.spec.whatwg.org/multipage/input.html#show-the-picker,-if-applicable
virtual void page_did_request_file_picker(WeakPtr<DOM::EventTarget>, [[maybe_unused]] bool multiple) {};
virtual void page_did_finish_text_test() {};
protected:
virtual ~PageClient() = default;
};

View file

@ -151,6 +151,7 @@ public:
Function<Gfx::IntRect()> on_minimize_window;
Function<Gfx::IntRect()> on_fullscreen_window;
Function<void(bool)> on_finish_handling_input_event;
Function<void()> on_text_test_finish;
virtual Gfx::IntRect viewport_rect() const = 0;
virtual Gfx::IntPoint to_content_position(Gfx::IntPoint widget_position) const = 0;

View file

@ -44,6 +44,12 @@ void WebContentClient::did_finish_loading(AK::URL const& url)
m_view.on_load_finish(url);
}
void WebContentClient::did_finish_text_test()
{
if (m_view.on_text_test_finish)
m_view.on_text_test_finish();
}
void WebContentClient::did_request_navigate_back()
{
if (m_view.on_navigate_back)

View file

@ -83,6 +83,7 @@ private:
virtual Messages::WebContentClient::DidRequestFullscreenWindowResponse did_request_fullscreen_window() override;
virtual void did_request_file(DeprecatedString const& path, i32) override;
virtual void did_finish_handling_input_event(bool event_was_accepted) override;
virtual void did_finish_text_test() override;
ViewImplementation& m_view;
};

View file

@ -291,6 +291,11 @@ void PageHost::page_did_finish_loading(const URL& url)
m_client.async_did_finish_loading(url);
}
void PageHost::page_did_finish_text_test()
{
m_client.async_did_finish_text_test();
}
void PageHost::page_did_request_context_menu(Web::CSSPixelPoint content_position)
{
m_client.async_did_request_context_menu(page().css_to_device_point(content_position).to_type<int>());

View file

@ -112,6 +112,7 @@ private:
virtual void page_did_request_activate_tab() override;
virtual void page_did_close_browsing_context(Web::HTML::BrowsingContext const&) override;
virtual void request_file(Web::FileRequest) override;
virtual void page_did_finish_text_test() override;
explicit PageHost(ConnectionFromClient&);

View file

@ -64,4 +64,6 @@ endpoint WebContentClient
did_output_js_console_message(i32 message_index) =|
did_get_js_console_messages(i32 start_index, Vector<DeprecatedString> message_types, Vector<DeprecatedString> messages) =|
did_finish_text_test() =|
}

View file

@ -196,8 +196,10 @@ static ErrorOr<TestResult> run_dump_test(HeadlessWebContentView& view, StringVie
result = builder.to_string().release_value_but_fixme_should_propagate_errors();
loop.quit(0);
};
view.on_text_test_finish = {};
} else if (mode == TestMode::Text) {
view.on_load_finish = [&](auto const&) {
view.on_load_finish = {};
view.on_text_test_finish = [&]() {
result = view.dump_text().release_value_but_fixme_should_propagate_errors();
loop.quit(0);
};