summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke <luke.wilde@live.co.uk>2020-07-24 21:24:11 +0100
committerAndreas Kling <kling@serenityos.org>2020-07-25 12:35:15 +0200
commit08221139a57ddf941d9ba44f9a700d22e33d6665 (patch)
tree7708ece0e2c0f4b498c5bb3aa6a7aa70a77b57c2
parent1d6a3a5e8fd418c2f36bb002282a2c6192b75f79 (diff)
downloadserenity-08221139a57ddf941d9ba44f9a700d22e33d6665.zip
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.
-rw-r--r--Base/res/html/misc/blank-no-doctype.html7
-rw-r--r--Libraries/LibWeb/Parser/HTMLDocumentParser.cpp6
-rw-r--r--Libraries/LibWeb/Parser/HTMLDocumentParser.h1
-rw-r--r--Libraries/LibWeb/Tests/Document/Doctype.js11
-rw-r--r--Libraries/LibWeb/Tests/Window/Base64.js2
-rw-r--r--Libraries/LibWeb/Tests/test-common.js22
-rw-r--r--Userland/test-web.cpp96
7 files changed, 126 insertions, 19 deletions
diff --git a/Base/res/html/misc/blank-no-doctype.html b/Base/res/html/misc/blank-no-doctype.html
new file mode 100644
index 0000000000..25f1d14c0e
--- /dev/null
+++ b/Base/res/html/misc/blank-no-doctype.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+<title>Blank</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp b/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp
index 83df56146b..1ab8f14d1e 100644
--- a/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp
+++ b/Libraries/LibWeb/Parser/HTMLDocumentParser.cpp
@@ -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()
{
}
diff --git a/Libraries/LibWeb/Parser/HTMLDocumentParser.h b/Libraries/LibWeb/Parser/HTMLDocumentParser.h
index 82f78160d4..dae251fb65 100644
--- a/Libraries/LibWeb/Parser/HTMLDocumentParser.h
+++ b/Libraries/LibWeb/Parser/HTMLDocumentParser.h
@@ -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&);
diff --git a/Libraries/LibWeb/Tests/Document/Doctype.js b/Libraries/LibWeb/Tests/Document/Doctype.js
index 49f67e460b..161da491a3 100644
--- a/Libraries/LibWeb/Tests/Document/Doctype.js
+++ b/Libraries/LibWeb/Tests/Document/Doctype.js
@@ -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);
+ });
});
diff --git a/Libraries/LibWeb/Tests/Window/Base64.js b/Libraries/LibWeb/Tests/Window/Base64.js
index 2cc12ea741..c272d70b74 100644
--- a/Libraries/LibWeb/Tests/Window/Base64.js
+++ b/Libraries/LibWeb/Tests/Window/Base64.js
@@ -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");
diff --git a/Libraries/LibWeb/Tests/test-common.js b/Libraries/LibWeb/Tests/test-common.js
index 7add466d52..54d0b830c6 100644
--- a/Libraries/LibWeb/Tests/test-common.js
+++ b/Libraries/LibWeb/Tests/test-common.js
@@ -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;
})();
diff --git a/Userland/test-web.cpp b/Userland/test-web.cpp
index 3378812d2c..f340d5e736 100644
--- a/Userland/test-web.cpp
+++ b/Userland/test-web.cpp
@@ -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