mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-02 04:20:28 +00:00
test-web: Add ability to change page mid-test
This allows you to not have to write a separate test file for the same thing but in a different situation. This doesn't handle when you change the page with location.href however. Changes the name of the page load handlers to prevent confusion with this.
This commit is contained in:
parent
1d6a3a5e8f
commit
08221139a5
Notes:
sideshowbarker
2024-07-19 04:38:10 +09:00
Author: https://github.com/Lubrsi Commit: https://github.com/SerenityOS/serenity/commit/08221139a57 Pull-request: https://github.com/SerenityOS/serenity/pull/2868 Reviewed-by: https://github.com/awesomekling
7 changed files with 126 additions and 19 deletions
7
Base/res/html/misc/blank-no-doctype.html
Normal file
7
Base/res/html/misc/blank-no-doctype.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Blank</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -117,6 +117,12 @@ HTMLDocumentParser::HTMLDocumentParser(const StringView& input, const String& en
|
|||
m_document = adopt(*new Document);
|
||||
}
|
||||
|
||||
HTMLDocumentParser::HTMLDocumentParser(const StringView& input, const String& encoding, Document& existing_document)
|
||||
: m_tokenizer(input, encoding)
|
||||
, m_document(existing_document)
|
||||
{
|
||||
}
|
||||
|
||||
HTMLDocumentParser::~HTMLDocumentParser()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ RefPtr<Document> parse_html_document(const StringView&, const URL&, const String
|
|||
class HTMLDocumentParser {
|
||||
public:
|
||||
HTMLDocumentParser(const StringView& input, const String& encoding);
|
||||
HTMLDocumentParser(const StringView& input, const String& encoding, Document& existing_document);
|
||||
~HTMLDocumentParser();
|
||||
|
||||
void run(const URL&);
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
loadPage("file:///res/html/misc/blank.html");
|
||||
|
||||
afterPageLoad(() => {
|
||||
afterInitialPageLoad(() => {
|
||||
test("Basic functionality", () => {
|
||||
expect(document.compatMode).toBe("CSS1Compat");
|
||||
expect(document.doctype).toBeDefined();
|
||||
expect(document.doctype).not.toBe(null);
|
||||
expect(document.doctype.name).toBe("html");
|
||||
expect(document.doctype.publicId).toBe("");
|
||||
expect(document.doctype.systemId).toBe("");
|
||||
});
|
||||
|
||||
libweb_tester.changePage("file:///res/html/misc/blank-no-doctype.html");
|
||||
|
||||
test("Quirks mode", () => {
|
||||
expect(document.compatMode).toBe("BackCompat");
|
||||
expect(document.doctype).toBe(null);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
loadPage("file:///res/html/misc/blank.html");
|
||||
|
||||
afterPageLoad(() => {
|
||||
afterInitialPageLoad(() => {
|
||||
test("atob", () => {
|
||||
expect(atob("YQ==")).toBe("a");
|
||||
expect(atob("YWE=")).toBe("aa");
|
||||
|
|
|
@ -1,28 +1,32 @@
|
|||
// NOTE: The tester loads in LibJS's test-common to prevent duplication.
|
||||
|
||||
// NOTE: "window.libweb_tester" is set to a special tester object.
|
||||
// The object currently provides the following functions:
|
||||
// - changePage(url) - change page to given URL. Everything afterwards will refer to the new page.
|
||||
|
||||
let __PageToLoad__;
|
||||
|
||||
// This tells the tester which page to load.
|
||||
// This will only be checked when we look at which page the test wants to use.
|
||||
// Subsequent calls to loadPage in before/after load will be ignored.
|
||||
// Subsequent calls to loadPage in before/after initial load will be ignored.
|
||||
let loadPage;
|
||||
|
||||
let __BeforePageLoad__ = () => {};
|
||||
let __BeforeInitialPageLoad__ = () => {};
|
||||
|
||||
// This function will be run just before loading the page.
|
||||
// This function will be called just before loading the initial page.
|
||||
// This is useful for injecting event listeners.
|
||||
// Defaults to an empty function.
|
||||
let beforePageLoad;
|
||||
let beforeInitialPageLoad;
|
||||
|
||||
let __AfterPageLoad__ = () => {};
|
||||
let __AfterInitialPageLoad__ = () => {};
|
||||
|
||||
// This function will be run just after loading the page.
|
||||
// This function will be called just after loading the initial page.
|
||||
// This is where the main bulk of the tests should be.
|
||||
// Defaults to an empty function.
|
||||
let afterPageLoad;
|
||||
let afterInitialPageLoad;
|
||||
|
||||
(() => {
|
||||
loadPage = (page) => __PageToLoad__ = page;
|
||||
beforePageLoad = (callback) => __BeforePageLoad__ = callback;
|
||||
afterPageLoad = (callback) => __AfterPageLoad__ = callback;
|
||||
beforeInitialPageLoad = (callback) => __BeforeInitialPageLoad__ = callback;
|
||||
afterInitialPageLoad = (callback) => __AfterInitialPageLoad__ = callback;
|
||||
})();
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#include <AK/URL.h>
|
||||
#include <AK/Function.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/QuickSort.h>
|
||||
|
@ -92,6 +93,47 @@ struct JSTestRunnerCounts {
|
|||
int files_total { 0 };
|
||||
};
|
||||
|
||||
Function<void(const URL&)> g_on_page_change;
|
||||
|
||||
class TestRunnerObject final : public JS::Object {
|
||||
JS_OBJECT(TestRunnerObject, JS::Object);
|
||||
|
||||
public:
|
||||
explicit TestRunnerObject(JS::GlobalObject&);
|
||||
virtual void initialize(JS::GlobalObject&) override;
|
||||
virtual ~TestRunnerObject() override;
|
||||
|
||||
private:
|
||||
JS_DECLARE_NATIVE_FUNCTION(change_page);
|
||||
};
|
||||
|
||||
TestRunnerObject::TestRunnerObject(JS::GlobalObject& global_object)
|
||||
: Object(*global_object.object_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void TestRunnerObject::initialize(JS::GlobalObject& global_object)
|
||||
{
|
||||
Object::initialize(global_object);
|
||||
define_native_function("changePage", change_page, 1);
|
||||
}
|
||||
|
||||
TestRunnerObject::~TestRunnerObject()
|
||||
{
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(TestRunnerObject::change_page)
|
||||
{
|
||||
auto url = interpreter.argument(0).to_string(interpreter);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
|
||||
if (g_on_page_change)
|
||||
g_on_page_change(url);
|
||||
|
||||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
class TestRunner {
|
||||
public:
|
||||
TestRunner(String web_test_root, String js_test_root, Web::PageView& page_view, bool print_times)
|
||||
|
@ -163,6 +205,33 @@ Vector<String> get_test_paths(const String& test_root)
|
|||
void TestRunner::run() {
|
||||
size_t progress_counter = 0;
|
||||
auto test_paths = get_test_paths(m_web_test_root);
|
||||
|
||||
g_on_page_change = [this](auto& page_to_load) {
|
||||
if (!page_to_load.is_valid()) {
|
||||
printf("Invalid page URL (%s) on page change", page_to_load.to_string().characters());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ASSERT(m_page_view->document());
|
||||
|
||||
// We want to keep the same document since the interpreter is tied to the document,
|
||||
// and we don't want to lose the test state. So, we just clear the document and
|
||||
// give a new parser the existing document to work on.
|
||||
m_page_view->document()->remove_all_children();
|
||||
|
||||
Web::ResourceLoader::the().load_sync(
|
||||
page_to_load,
|
||||
[&](auto data, auto&) {
|
||||
Web::HTMLDocumentParser parser(data, "utf-8", *m_page_view->document());
|
||||
parser.run(page_to_load);
|
||||
},
|
||||
[page_to_load](auto error) {
|
||||
printf("Failed to load test page: %s (%s)", page_to_load.to_string().characters(), error.characters());
|
||||
exit(1);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
for (auto& path : test_paths) {
|
||||
++progress_counter;
|
||||
print_file_result(run_file_test(path));
|
||||
|
@ -260,21 +329,26 @@ JSFileResult TestRunner::run_file_test(const String& test_path)
|
|||
Web::HTMLDocumentParser parser(data, "utf-8");
|
||||
auto& new_interpreter = parser.document().interpreter();
|
||||
|
||||
// Setup the test environment and call "__BeforePageLoad__"
|
||||
// Setup the test environment and call "__BeforeInitialPageLoad__"
|
||||
new_interpreter.global_object().define_property(
|
||||
"libweb_tester",
|
||||
new_interpreter.heap().allocate<TestRunnerObject>(new_interpreter.global_object(), new_interpreter.global_object()),
|
||||
JS::Attribute::Enumerable | JS::Attribute::Configurable
|
||||
);
|
||||
new_interpreter.run(new_interpreter.global_object(), *m_js_test_common);
|
||||
new_interpreter.run(new_interpreter.global_object(), *m_web_test_common);
|
||||
new_interpreter.run(new_interpreter.global_object(), *file_program.value());
|
||||
|
||||
auto& before_page_load = new_interpreter.get_variable("__BeforePageLoad__", new_interpreter.global_object()).as_function();
|
||||
new_interpreter.call(before_page_load, JS::js_undefined());
|
||||
auto& before_initial_page_load = new_interpreter.get_variable("__BeforeInitialPageLoad__", new_interpreter.global_object()).as_function();
|
||||
new_interpreter.call(before_initial_page_load, JS::js_undefined());
|
||||
|
||||
// Now parse the HTML page.
|
||||
parser.run(page_to_load);
|
||||
m_page_view->set_document(&parser.document());
|
||||
|
||||
// Finally run the test by calling "__AfterPageLoad__"
|
||||
auto& after_page_load = new_interpreter.get_variable("__AfterPageLoad__", new_interpreter.global_object()).as_function();
|
||||
new_interpreter.call(after_page_load, JS::js_undefined());
|
||||
// Finally run the test by calling "__AfterInitialPageLoad__"
|
||||
auto& after_initial_page_load = new_interpreter.get_variable("__AfterInitialPageLoad__", new_interpreter.global_object()).as_function();
|
||||
new_interpreter.call(after_initial_page_load, JS::js_undefined());
|
||||
|
||||
auto test_json = get_test_results(new_interpreter);
|
||||
if (!test_json.has_value()) {
|
||||
|
@ -340,7 +414,7 @@ JSFileResult TestRunner::run_file_test(const String& test_path)
|
|||
m_total_elapsed_time_in_ms += file_result.time_taken;
|
||||
},
|
||||
[page_to_load](auto error) {
|
||||
dbg() << "Failed to load test page: " << error << " (" << page_to_load << ")";
|
||||
printf("Failed to load test page: %s (%s)", page_to_load.to_string().characters(), error.characters());
|
||||
exit(1);
|
||||
}
|
||||
);
|
||||
|
@ -555,9 +629,11 @@ void TestRunner::print_test_results() const
|
|||
int main(int argc, char** argv)
|
||||
{
|
||||
bool print_times = false;
|
||||
bool show_window = false;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.add_option(print_times, "Show duration of each test", "show-time", 't');
|
||||
args_parser.add_option(show_window, "Show window while running tests", "window", 'w');
|
||||
args_parser.parse(argc, argv);
|
||||
|
||||
auto app = GUI::Application::construct(argc, argv);
|
||||
|
@ -569,6 +645,12 @@ int main(int argc, char** argv)
|
|||
|
||||
view.set_document(adopt(*new Web::Document));
|
||||
|
||||
if (show_window) {
|
||||
window->set_title("LibWeb Test Window");
|
||||
window->set_rect(100, 100, 640, 480);
|
||||
window->show();
|
||||
}
|
||||
|
||||
#ifdef __serenity__
|
||||
TestRunner("/home/anon/web-tests", "/home/anon/js-tests", view, print_times).run();
|
||||
#else
|
||||
|
|
Loading…
Reference in a new issue