diff options
| author | Akshay Nair <phenax5@gmail.com> | 2025-07-27 14:54:56 +0530 |
|---|---|---|
| committer | Akshay Nair <phenax5@gmail.com> | 2025-07-27 14:54:56 +0530 |
| commit | 9ece6a733f0c3bfd518ee14f520aed70124fac12 (patch) | |
| tree | 54d045b187d39bf05c6f2653adb78bf2088f45ce /spec | |
| parent | f13a1a1c4eab8bdef3760dd71cd7e2b3cc68a3e8 (diff) | |
| download | null-browser-9ece6a733f0c3bfd518ee14f520aed70124fac12.tar.gz null-browser-9ece6a733f0c3bfd518ee14f520aed70124fac12.zip | |
Add more tests for api + webview + webviewstack
Diffstat (limited to '')
| -rw-r--r-- | spec/BrowserWindowSpec.cpp | 79 | ||||
| -rw-r--r-- | spec/LuaRuntimeApiSpec.cpp | 272 | ||||
| -rw-r--r-- | spec/WebViewSpec.cpp | 100 |
3 files changed, 421 insertions, 30 deletions
diff --git a/spec/BrowserWindowSpec.cpp b/spec/BrowserWindowSpec.cpp new file mode 100644 index 0000000..636b5c2 --- /dev/null +++ b/spec/BrowserWindowSpec.cpp @@ -0,0 +1,79 @@ +#include <QWebEngineNewWindowRequest> +#include <QWebEngineProfile> +#include <qwebengineprofile.h> +#include <vector> + +#include "Configuration.hpp" +#include "LuaRuntime.hpp" +#include "testUtils.h" +#include "widgets/Decorations.hpp" +#include "widgets/WebView.hpp" +#include "widgets/WebViewStack.hpp" + +// NOLINTBEGIN +class BrowserWindowSpec : public QObject { + Q_OBJECT + + // class FakeNewWindowRequest : public QWebEngineNewWindowRequest { + // public: + // FakeNewWindowRequest(DestinationType t, const QRect &r, const QUrl &u, bool b) + // : QWebEngineNewWindowRequest(t, r, u, b, nullptr) {} + // }; + + QWebEngineProfile *profile() { return new QWebEngineProfile(); } + +private slots: + void beforeTestCase() { LuaRuntime::instance().start_event_loop(); } + void cleanupTestCase() { LuaRuntime::instance().stop_event_loop(); } + + void test_initial_state() { + describe("constructor"); + + context("when initialized with some urls"); + it("opens those urls in stack") { + Configuration configuration; + QStringList urls{"https://example1.com", "https://example2.com"}; + + BrowserWindow browser_window(configuration, profile(), urls); + + auto *webview_stack = browser_window.findChild<WebViewStack *>(); + QCOMPARE(webview_stack->count(), 2); + std::vector<QUrl> expected_urls{QUrl("https://example1.com"), QUrl("https://example2.com")}; + QCOMPARE(webview_stack->urls(), expected_urls); + } + + context("when initialized with no urls"); + it("opens configured new_view_url in stack") { + Configuration configuration; + + BrowserWindow browser_window(configuration, profile()); + + auto *webview_stack = browser_window.findChild<WebViewStack *>(); + QCOMPARE(webview_stack->count(), 1); + QCOMPARE(webview_stack->urls(), {QUrl(configuration.new_view_url())}); + } + } + + // void test_window_title_update() { + // context("when title is updated"); + // it("opens configured new_view_url in stack") { + // Configuration configuration; + // BrowserWindow browser_window(configuration, profile(), + // {"https://one.com", "https://two.com"}); + // auto *webview_stack = browser_window.findChild<WebViewStack *>(); + // QSignalSpy current_webview_title_changed(webview_stack, + // &WebViewStack::current_webview_title_changed); + // + // emit webview_stack->current_webview_title_changed(0); + // QVERIFY(current_webview_title_changed.wait(100)); + // + // qDebug() << browser_window.windowTitle(); + // // QCOMPARE(webview_stack->count(), 1); + // // QCOMPARE(webview_stack->urls(), {QUrl(configuration.new_view_url())}); + // } + // } +}; + +QTEST_REGISTER(BrowserWindowSpec) +#include "BrowserWindowSpec.moc" +// NOLINTEND diff --git a/spec/LuaRuntimeApiSpec.cpp b/spec/LuaRuntimeApiSpec.cpp index fe29fcc..123eaa1 100644 --- a/spec/LuaRuntimeApiSpec.cpp +++ b/spec/LuaRuntimeApiSpec.cpp @@ -1,10 +1,15 @@ #include <QtCore> +#include <optional> +#include <unordered_map> +#include <utility> #include <uv.h> +#include <vector> #include "LuaRuntime.hpp" #include "WindowActionRouter.hpp" #include "events/Event.hpp" #include "testUtils.h" +#include "widgets/WebView.hpp" class TestEvent1 : public Event { public: @@ -33,16 +38,16 @@ private slots: auto &lua = LuaRuntime::instance(); QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed); - lua.evaluate(R"( + lua.evaluate(R"LUA( return web.event.add_listener({ 'Hello', 'World' }, { patterns = { 'p1', 'p2' }, callback = function() print("Called") end, }); - )"); + )LUA"); evaluation_completed_spy.wait(); QCOMPARE(evaluation_completed_spy.count(), 1); - QVariant result = evaluation_completed_spy.takeFirst().at(0); + QVariant result = evaluation_completed_spy.first()[0]; QCOMPARE(result, true); } @@ -51,16 +56,16 @@ private slots: auto &lua = LuaRuntime::instance(); QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed); - lua.evaluate(R"( + lua.evaluate(R"LUA( return web.event.add_listener('Hello', { patterns = { 'p1', 'p2' }, callback = function() print("Called") end, }); - )"); + )LUA"); evaluation_completed_spy.wait(); QCOMPARE(evaluation_completed_spy.count(), 1); - QVariant result = evaluation_completed_spy.takeFirst().at(0); + QVariant result = evaluation_completed_spy.takeFirst()[0]; QCOMPARE(result, true); } @@ -69,15 +74,15 @@ private slots: auto &lua = LuaRuntime::instance(); QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed); - lua.evaluate(R"( + lua.evaluate(R"LUA( return web.event.add_listener({ 'Hello', 'World' }, { callback = function() print("Called") end, }); - )"); + )LUA"); evaluation_completed_spy.wait(); QCOMPARE(evaluation_completed_spy.count(), 1); - QVariant result = evaluation_completed_spy.takeFirst().at(0); + QVariant result = evaluation_completed_spy.takeFirst()[0]; QCOMPARE(result, true); } @@ -86,12 +91,12 @@ private slots: auto &lua = LuaRuntime::instance(); QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed); - lua.evaluate(R"( + lua.evaluate(R"LUA( return web.event.add_listener(nil, { patterns = { 'p1', 'p2' }, callback = function() print("Called") end, }); - )"); + )LUA"); evaluation_completed_spy.wait(); QCOMPARE(evaluation_completed_spy.count(), 1); @@ -104,11 +109,11 @@ private slots: auto &lua = LuaRuntime::instance(); QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed); - lua.evaluate(R"( + lua.evaluate(R"LUA( return web.event.add_listener({'Ev'}, { patterns = { 'p1', 'p2' }, }); - )"); + )LUA"); evaluation_completed_spy.wait(); QCOMPARE(evaluation_completed_spy.count(), 1); @@ -118,13 +123,13 @@ private slots: } void test_web_event_dispatching() { - describe("web.event.add_listener (event dispatch)"); + describe("web.event.add_listener (event dispatch)LUA"); - context("when dispatching a registered event (without pattern)"); + context("when dispatching a registered event (without pattern)LUA"); it("calls the registered event handler") { auto &lua = LuaRuntime::instance(); QSignalSpy evaluation_completed_spy(&lua, &LuaRuntime::evaluation_completed); - lua.evaluate(R"( + lua.evaluate(R"LUA( _G.event1_called = false; _G.event1_called_with = nil; web.event.add_listener('TestEvent1', { @@ -137,7 +142,7 @@ private slots: web.event.add_listener('TestEvent2', { callback = function(opts) _G.event2_called = true end, }); - )"); + )LUA"); evaluation_completed_spy.wait(); TestEvent1 event(42); @@ -149,17 +154,17 @@ private slots: } } - void lua_api_view_set_url() { - describe("web.search.set_url"); + void test_lua_api_view_set_url() { + describe("web.view.set_url"); context("when called with a url and view id"); it("emits url_opened for the given view id") { auto &lua = LuaRuntime::instance(); QSignalSpy url_opened(&lua, &LuaRuntime::url_opened); - lua.evaluate(R"( + lua.evaluate(R"LUA( web.view.set_url("https://updated-url.com", 42) - )"); + )LUA"); QVERIFY(url_opened.wait()); QCOMPARE(url_opened.first()[0], "https://updated-url.com"); @@ -172,9 +177,9 @@ private slots: auto &lua = LuaRuntime::instance(); QSignalSpy url_opened(&lua, &LuaRuntime::url_opened); - lua.evaluate(R"( + lua.evaluate(R"LUA( web.view.set_url("https://updated-url.com") - )"); + )LUA"); QVERIFY(url_opened.wait()); QCOMPARE(url_opened.first()[0], "https://updated-url.com"); @@ -187,9 +192,9 @@ private slots: auto &lua = LuaRuntime::instance(); QSignalSpy url_opened(&lua, &LuaRuntime::url_opened); - lua.evaluate(R"( + lua.evaluate(R"LUA( web.view.set_url() - )"); + )LUA"); QVERIFY(url_opened.wait()); QCOMPARE(url_opened.first()[0], ""); @@ -198,7 +203,7 @@ private slots: } } - void lua_api_search_set_text() { + void test_lua_api_search_set_text() { describe("web.search.set_text"); context("when called with just the search text"); @@ -226,7 +231,7 @@ private slots: } } - void lua_api_search_next() { + void test_lua_api_search_next() { describe("web.search.next"); context("when called without view id"); @@ -252,7 +257,7 @@ private slots: } } - void lua_api_search_previous() { + void test_lua_api_search_previous() { describe("web.search.previous"); context("when called without view id"); @@ -278,7 +283,7 @@ private slots: } } - void lua_api_search_get_text() { + void test_lua_api_search_get_text() { describe("web.search.get_text"); it("returns the current search text") { @@ -294,7 +299,7 @@ private slots: } } - void lua_api_view_open_devtools() { + void test_lua_api_view_open_devtools() { describe("web.view.open_devtools"); context("when called with view id"); @@ -308,6 +313,213 @@ private slots: QCOMPARE(devtools_requested.first().first(), 42); } } + + void test_lua_api_view_expose() { + describe("web.view.expose"); + + context("when called without a view id"); + it("defines the rpc for the current webview") { + auto &lua = LuaRuntime::instance(); + QSignalSpy webview_rpc_action_defined(&lua, &LuaRuntime::webview_rpc_action_defined); + + lua.evaluate(R"LUA( + web.view.expose('myfunction', function() end) + )LUA"); + + QVERIFY(webview_rpc_action_defined.wait()); + QCOMPARE(webview_rpc_action_defined.first()[0], "myfunction"); + auto func_raw = webview_rpc_action_defined.first()[1]; + QVERIFY(func_raw.canConvert<RpcFunc>()); + QCOMPARE(webview_rpc_action_defined.first()[2], 0); + } + + context("when called with a view id"); + it("defines the rpc for the given webview") { + auto &lua = LuaRuntime::instance(); + QSignalSpy webview_rpc_action_defined(&lua, &LuaRuntime::webview_rpc_action_defined); + + lua.evaluate(R"LUA( + web.view.expose('myfunction', function() end, { view = 5 }) + )LUA"); + + QVERIFY(webview_rpc_action_defined.wait()); + QCOMPARE(webview_rpc_action_defined.first()[0], "myfunction"); + auto func_raw = webview_rpc_action_defined.first()[1]; + QVERIFY(func_raw.canConvert<RpcFunc>()); + QCOMPARE(webview_rpc_action_defined.first()[2], 5); + } + + context("when the defined function is called with args"); + it("calls the lua function with the given args") { + auto &lua = LuaRuntime::instance(); + QSignalSpy webview_rpc_action_defined(&lua, &LuaRuntime::webview_rpc_action_defined); + lua.evaluate(R"LUA( + web.view.expose('myfunction', function(args) + _G.myfunction_was_called_with = args.prop + end, { view = 5 }) + )LUA"); + QVERIFY(webview_rpc_action_defined.wait()); + + auto func_raw = webview_rpc_action_defined.first()[1]; + QVERIFY(func_raw.canConvert<RpcFunc>()); + RpcArgs args{{"prop", "Test value"}}; + func_raw.value<RpcFunc>()(args); + + QVERIFY(wait_for_lua_to_be_true("return _G.myfunction_was_called_with == 'Test value'")); + } + } + + void test_lua_api_view_set_html() { + describe("web.view.set_html"); + + context("when called without view id"); + it("emits webview_html_set_requested for the given view id") { + auto &lua = LuaRuntime::instance(); + QSignalSpy webview_html_set_requested(&lua, &LuaRuntime::webview_html_set_requested); + + lua.evaluate(R"LUA( + web.view.set_html('<h1>foobar</h1>') + )LUA"); + + QVERIFY(webview_html_set_requested.wait()); + QCOMPARE(webview_html_set_requested.first()[0], "<h1>foobar</h1>"); + QCOMPARE(webview_html_set_requested.first()[1], 0); + } + + context("when called with html and view id"); + it("emits webview_html_set_requested for the given view id") { + auto &lua = LuaRuntime::instance(); + QSignalSpy webview_html_set_requested(&lua, &LuaRuntime::webview_html_set_requested); + + lua.evaluate(R"LUA( + web.view.set_html('<h1>foobar</h1>', { view = 42 }) + )LUA"); + + QVERIFY(webview_html_set_requested.wait()); + QCOMPARE(webview_html_set_requested.first()[0], "<h1>foobar</h1>"); + QCOMPARE(webview_html_set_requested.first()[1], 42); + } + } + + void test_lua_api_decorations_set_enabled() { + describe("web.decorations.*.enable"); + + context("when called without view id"); + it("emits webview_html_set_requested without window id (nullopt)") { + auto &lua = LuaRuntime::instance(); + QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled); + + lua.evaluate(R"LUA( + web.decorations.left.enable() + )LUA"); + + QVERIFY(decorations_set_enabled.wait()); + auto call = decorations_set_enabled.first(); + QCOMPARE(call[0], 3); + QCOMPARE(call[1], true); + QCOMPARE(call[2].value<std::optional<int>>(), std::nullopt); + } + + context("when called with window id"); + it("emits webview_html_set_requested for the given window id") { + auto &lua = LuaRuntime::instance(); + QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled); + + lua.evaluate(R"LUA( + web.decorations.left.enable({ win = 42 }) + )LUA"); + + QVERIFY(decorations_set_enabled.wait()); + auto call = decorations_set_enabled.first(); + QCOMPARE(call[0], 3); + QCOMPARE(call[1], true); + QCOMPARE(call[2].value<std::optional<WindowId>>(), std::make_optional(42)); + } + + context("for all decoration types"); + it("emits webview_html_set_requested for the given window id") { + std::vector<std::pair<int, QString>> decoration_types{ + {1, "top"}, + {2, "bottom"}, + {3, "left"}, + {4, "right"}, + }; + for (auto &dir : decoration_types) { + auto &lua = LuaRuntime::instance(); + QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled); + + lua.evaluate(QString(R"LUA( + web.decorations.%1.enable({ win = 42 }) + )LUA") + .arg(dir.second)); + + QVERIFY(decorations_set_enabled.wait()); + auto call = decorations_set_enabled.first(); + QCOMPARE(call[0], dir.first); + QCOMPARE(call[1], true); + QCOMPARE(call[2].value<std::optional<WindowId>>(), std::make_optional(42)); + } + } + + describe("web.decorations.*.disable"); + + context("when called without view id"); + it("emits webview_html_set_requested without window id (nullopt)") { + auto &lua = LuaRuntime::instance(); + QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled); + + lua.evaluate(R"LUA( + web.decorations.left.disable() + )LUA"); + + QVERIFY(decorations_set_enabled.wait()); + auto call = decorations_set_enabled.first(); + QCOMPARE(call[0], 3); + QCOMPARE(call[1], false); + QCOMPARE(call[2].value<std::optional<int>>(), std::nullopt); + } + + context("when called with window id"); + it("emits webview_html_set_requested for the given window id") { + auto &lua = LuaRuntime::instance(); + QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled); + + lua.evaluate(R"LUA( + web.decorations.left.disable({ win = 42 }) + )LUA"); + + QVERIFY(decorations_set_enabled.wait()); + auto call = decorations_set_enabled.first(); + QCOMPARE(call[0], 3); + QCOMPARE(call[1], false); + QCOMPARE(call[2].value<std::optional<WindowId>>(), std::make_optional(42)); + } + + context("for all decoration types"); + it("emits webview_html_set_requested for the given window id") { + std::vector<std::pair<int, QString>> decoration_types{ + {1, "top"}, + {2, "bottom"}, + {3, "left"}, + {4, "right"}, + }; + for (auto &dir : decoration_types) { + auto &lua = LuaRuntime::instance(); + QSignalSpy decorations_set_enabled(&lua, &LuaRuntime::decoration_set_enabled); + + lua.evaluate(QString(R"LUA( + web.decorations.%1.disable({ win = 42 }) + )LUA") + .arg(dir.second)); + + QVERIFY(decorations_set_enabled.wait()); + auto call = decorations_set_enabled.first(); + QCOMPARE(call[0], dir.first); + QCOMPARE(call[1], false); + QCOMPARE(call[2].value<std::optional<WindowId>>(), std::make_optional(42)); + } + } + } }; QTEST_REGISTER(LuaRuntimeApiSpec) diff --git a/spec/WebViewSpec.cpp b/spec/WebViewSpec.cpp new file mode 100644 index 0000000..3e67f11 --- /dev/null +++ b/spec/WebViewSpec.cpp @@ -0,0 +1,100 @@ +#include <QWebEngineNewWindowRequest> +#include <QWebEngineProfile> +#include <qdeadlinetimer.h> +#include <qsignalspy.h> +#include <qtestcase.h> +#include <qtestsupport_core.h> +#include <qurlquery.h> + +#include "LuaRuntime.hpp" +#include "schemes/NullRpcSchemeHandler.hpp" +#include "testUtils.h" +#include "widgets/WebView.hpp" + +// NOLINTBEGIN +class WebViewSpec : public QObject { + Q_OBJECT + + QWebEngineProfile *profile() { return new QWebEngineProfile(); } + +private slots: + void beforeTestCase() { LuaRuntime::instance().start_event_loop(); } + void cleanupTestCase() { LuaRuntime::instance().stop_event_loop(); } + + void test_rpc_enabled() { + describe("webview rpc enabled"); + + context("when a defined function is called"); + it("calls the function") { + WebView webview(1, profile()); + webview.enable_rpc_api(); + bool my_func_was_called = false; + webview.expose_rpc_function( + "my_func", [&my_func_was_called](RpcArgs /* unused */) { my_func_was_called = true; }); + + auto &nullrpc = NullRPCSchemeHandler::instance(); + NullRPCMessage message{.name = "my_func", .params = QUrlQuery()}; + emit nullrpc.message_received(message); + + QVERIFY(QTest::qWaitFor([&my_func_was_called]() { return my_func_was_called; })); + } + + context("when a defined function is called with arguements"); + it("calls the function with the arguments") { + WebView webview(1, profile()); + webview.enable_rpc_api(); + bool my_func_was_called = false; + QString my_func_arg = ""; + webview.expose_rpc_function("my_func", [&my_func_was_called, &my_func_arg](RpcArgs args) { + my_func_was_called = true; + my_func_arg = args.contains("my_arg") ? args.at("my_arg").toString() : ""; + }); + + auto &nullrpc = NullRPCSchemeHandler::instance(); + NullRPCMessage message{.name = "my_func", .params = QUrlQuery{{"my_arg", "my arg value"}}}; + emit nullrpc.message_received(message); + + QVERIFY(QTest::qWaitFor([&my_func_was_called]() { return my_func_was_called; })); + QCOMPARE(my_func_arg, "my arg value"); + } + + context("when an undefined function is called"); + it("does nothing") { + WebView webview(1, profile()); + webview.enable_rpc_api(); + bool my_func_was_called = false; + webview.expose_rpc_function( + "my_func", [&my_func_was_called](RpcArgs /* unused */) { my_func_was_called = true; }); + + auto &nullrpc = NullRPCSchemeHandler::instance(); + NullRPCMessage message{.name = "my_undefined_func", .params = QUrlQuery()}; + emit nullrpc.message_received(message); + + QVERIFY(NOT QTest::qWaitFor([&my_func_was_called]() { return my_func_was_called; }, + QDeadlineTimer(std::chrono::milliseconds(200)))); + } + } + + void test_rpc_disabled() { + describe("webview rpc disabled"); + + context("when a defined function is called"); + it("does nothing") { + WebView webview(1, profile()); + bool my_func_was_called = false; + webview.expose_rpc_function( + "my_func", [&my_func_was_called](RpcArgs /* unused */) { my_func_was_called = true; }); + + auto &nullrpc = NullRPCSchemeHandler::instance(); + NullRPCMessage message{.name = "my_func", .params = QUrlQuery()}; + emit nullrpc.message_received(message); + + QVERIFY(NOT QTest::qWaitFor([&my_func_was_called]() { return my_func_was_called; }, + QDeadlineTimer(std::chrono::milliseconds(200)))); + } + } +}; + +QTEST_REGISTER(WebViewSpec) +#include "WebViewSpec.moc" +// NOLINTEND |
