summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--.gitmodules3
-rw-r--r--Makefile.am2
-rwxr-xr-xautogen.sh4
-rw-r--r--data/assets/category_splash_separator_shadow.pngbin1601 -> 0 bytes
-rw-r--r--data/css/eos-wikipedia-domain.css4
-rw-r--r--data/eos-wikipedia-domain.gresource.xml1
-rw-r--r--endless/eostopbar.c44
-rw-r--r--endless/eoswindow.c42
-rw-r--r--test/Makefile.am.inc (renamed from test/Makefile.am)45
-rw-r--r--test/demos/Makefile.am.inc5
-rw-r--r--test/demos/flexy-grid.c (renamed from test/flexy-grid.c)0
-rw-r--r--test/endless/Makefile.am.inc17
-rw-r--r--test/endless/run-tests.c (renamed from test/run-tests.c)0
-rw-r--r--test/endless/run-tests.h (renamed from test/run-tests.h)0
-rw-r--r--test/endless/test-action-button.c (renamed from test/test-action-button.c)0
-rw-r--r--test/endless/test-action-menu.c (renamed from test/test-action-menu.c)0
-rw-r--r--test/endless/test-application.c (renamed from test/test-application.c)0
-rw-r--r--test/endless/test-flexy-grid.c (renamed from test/test-flexy-grid.c)0
-rw-r--r--test/endless/test-hello.c (renamed from test/test-hello.c)0
-rw-r--r--test/endless/test-init.c (renamed from test/test-init.c)0
-rw-r--r--test/endless/test-page-manager.c (renamed from test/test-page-manager.c)0
-rw-r--r--test/endless/test-splash-page-manager.c (renamed from test/test-splash-page-manager.c)0
-rw-r--r--test/endless/test-window.c (renamed from test/test-window.c)0
m---------test/jasmine0
-rw-r--r--test/smoke-tests/Makefile.am.inc5
-rw-r--r--test/smoke-tests/webhelper/webview.js (renamed from test/webhelper/smoke-tests/webview.js)0
-rw-r--r--test/webhelper/testTranslate.js117
-rw-r--r--test/webhelper/testWebActions.js215
-rw-r--r--test/wikipedia/models/testArticleModel.js172
-rw-r--r--test/wikipedia/models/testCategoryModel.js248
-rw-r--r--test/wikipedia/models/testDomainWikiModel.js346
-rw-r--r--wikipedia/PrebuiltCategoryPage.js7
33 files changed, 785 insertions, 498 deletions
diff --git a/.gitignore b/.gitignore
index c86f245..5b72c1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
# Build products
endless-0.pc
-test/run-tests
-test/flexy-grid
+test/endless/run-tests
+test/demos/flexy-grid
test/smoke-tests/hello
Endless-0.gir
Endless-0.typelib
@@ -26,6 +26,7 @@ tools/eos-json-extractor/eos-json-extractor
.libs
stamp*
*.stamp
+*.trs
/Makefile
/Makefile.in
/aclocal.m4
@@ -45,6 +46,7 @@ stamp*
/m4/ltsugar.m4
/m4/ltversion.m4
/m4/serial-tests.m4
+/test-driver
.dirstamp
# Gettext droppings
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..9e417eb
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "test/jasmine"]
+ path = test/jasmine
+ url = ../../endlessm/eos-jasmine
diff --git a/Makefile.am b/Makefile.am
index e021742..c635f63 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -159,4 +159,4 @@ m4_DATA = \
# # # TESTS # # #
-include $(top_srcdir)/test/Makefile.am
+include $(top_srcdir)/test/Makefile.am.inc
diff --git a/autogen.sh b/autogen.sh
index 6c20886..4f7b9e3 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -20,6 +20,10 @@ if [ -d .git ]; then
chmod +x .git/hooks/commit-msg
fi
+git remote set-url origin http://github.com/endlessm/eos-sdk.git
+git submodule init .
+git submodule update --recursive
+
# GNU gettext automake support doesn't get along with git
# https://bugzilla.gnome.org/show_bug.cgi?id=661128
touch -t 200001010000 $POT_FILE
diff --git a/data/assets/category_splash_separator_shadow.png b/data/assets/category_splash_separator_shadow.png
deleted file mode 100644
index 6ef03e0..0000000
--- a/data/assets/category_splash_separator_shadow.png
+++ /dev/null
Binary files differ
diff --git a/data/css/eos-wikipedia-domain.css b/data/css/eos-wikipedia-domain.css
index d288138..4095d68 100644
--- a/data/css/eos-wikipedia-domain.css
+++ b/data/css/eos-wikipedia-domain.css
@@ -54,8 +54,8 @@ Gjs_ArticleList .scrollbar.slider {
background-color: #babdb6;
}
-.category-page #category_info{
- background-color: rgba(0, 0, 0, 0.5);
+.category-page #category_info {
+ background-color: alpha(#464646, 0.8);
}
.category-page #category_description{
diff --git a/data/eos-wikipedia-domain.gresource.xml b/data/eos-wikipedia-domain.gresource.xml
index 8442d5e..e4672d1 100644
--- a/data/eos-wikipedia-domain.gresource.xml
+++ b/data/eos-wikipedia-domain.gresource.xml
@@ -10,7 +10,6 @@
<file>assets/submenu_separator_shadow_b.png</file>
<file>assets/submenu_hover_arrow.png</file>
<file>assets/submenu_background.jpg</file>
- <file>assets/category_splash_separator_shadow.png</file>
<file>assets/category_hover_arrow.png</file>
<file>assets/image_strip_back_button.png</file>
<file>assets/introduction_back_button_normal.png</file>
diff --git a/endless/eostopbar.c b/endless/eostopbar.c
index 6975e39..6d98090 100644
--- a/endless/eostopbar.c
+++ b/endless/eostopbar.c
@@ -15,7 +15,7 @@
* The #EosTopBar has three different areas that can be managed through this
* class: a left widget, center widget, and action buttons area.
*
- * The action buttons area contain "minimize" and "close" buttons.
+ * The action buttons area contain "minimize", "maximize" and "close" buttons.
*/
#define _EOS_STYLE_CLASS_TOP_BAR "top-bar"
#define _EOS_TOP_BAR_HEIGHT_PX 36
@@ -25,6 +25,7 @@
#define _EOS_TOP_BAR_BUTTON_SEPARATION_PX 8
#define _EOS_TOP_BAR_VERTICAL_BUTTON_MARGIN_PX 6
#define _EOS_TOP_BAR_MINIMIZE_ICON_NAME "window-minimize-symbolic"
+#define _EOS_TOP_BAR_MAXIMIZE_ICON_NAME "face-monkey"
#define _EOS_TOP_BAR_CLOSE_ICON_NAME "window-close-symbolic"
typedef struct {
@@ -37,6 +38,8 @@ typedef struct {
GtkWidget *minimize_button;
GtkWidget *minimize_icon;
+ GtkWidget *maximize_button;
+ GtkWidget *maximize_icon;
GtkWidget *close_button;
GtkWidget *close_icon;
} EosTopBarPrivate;
@@ -46,6 +49,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (EosTopBar, eos_top_bar, GTK_TYPE_EVENT_BOX)
enum {
CLOSE_CLICKED,
MINIMIZE_CLICKED,
+ MAXIMIZE_CLICKED,
LAST_SIGNAL
};
@@ -107,6 +111,17 @@ eos_top_bar_class_init (EosTopBarClass *klass)
G_TYPE_NONE, 0);
/*
+ * Emitted when the maximize button has been activated.
+ */
+ top_bar_signals[MAXIMIZE_CLICKED] =
+ g_signal_new ("maximize-clicked",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ 0,
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /*
* Emitted when the close button has been activated.
*/
top_bar_signals[CLOSE_CLICKED] =
@@ -127,6 +142,14 @@ on_minimize_clicked_cb (GtkButton *button,
}
static void
+on_maximize_clicked_cb (GtkButton *button,
+ gpointer user_data)
+{
+ EosTopBar *self = EOS_TOP_BAR (user_data);
+ g_signal_emit (self, top_bar_signals[MAXIMIZE_CLICKED], 0);
+}
+
+static void
on_close_clicked_cb (GtkButton *button,
gpointer user_data)
{
@@ -179,6 +202,21 @@ eos_top_bar_init (EosTopBar *self)
gtk_container_add (GTK_CONTAINER (priv->minimize_button),
priv->minimize_icon);
+ priv->maximize_button =
+ g_object_new (GTK_TYPE_BUTTON,
+ "halign", GTK_ALIGN_END,
+ "valign", GTK_ALIGN_CENTER,
+ NULL);
+ priv->maximize_icon =
+ gtk_image_new_from_icon_name (_EOS_TOP_BAR_MAXIMIZE_ICON_NAME,
+ GTK_ICON_SIZE_SMALL_TOOLBAR);
+ g_object_set(priv->maximize_icon,
+ "pixel-size", _EOS_TOP_BAR_ICON_SIZE_PX,
+ "margin", _EOS_TOP_BAR_BUTTON_PADDING_PX,
+ NULL);
+ gtk_container_add (GTK_CONTAINER (priv->maximize_button),
+ priv->maximize_icon);
+
priv->close_button =
g_object_new (GTK_TYPE_BUTTON,
"halign", GTK_ALIGN_END,
@@ -201,6 +239,8 @@ eos_top_bar_init (EosTopBar *self)
gtk_container_add (GTK_CONTAINER (priv->actions_grid),
priv->minimize_button);
gtk_container_add (GTK_CONTAINER (priv->actions_grid),
+ priv->maximize_button);
+ gtk_container_add (GTK_CONTAINER (priv->actions_grid),
priv->close_button);
gtk_container_add (GTK_CONTAINER (self), priv->actions_grid);
@@ -210,6 +250,8 @@ eos_top_bar_init (EosTopBar *self)
g_signal_connect (priv->minimize_button, "clicked",
G_CALLBACK (on_minimize_clicked_cb), self);
+ g_signal_connect (priv->maximize_button, "clicked",
+ G_CALLBACK (on_maximize_clicked_cb), self);
g_signal_connect (priv->close_button, "clicked",
G_CALLBACK (on_close_clicked_cb), self);
}
diff --git a/endless/eoswindow.c b/endless/eoswindow.c
index f141c68..8b91919 100644
--- a/endless/eoswindow.c
+++ b/endless/eoswindow.c
@@ -95,6 +95,8 @@ typedef struct {
EosPageManager *page_manager;
+ gboolean maximized;
+
/* For scaling base font-size */
GtkCssProvider *font_size_provider;
gboolean font_scaling_active;
@@ -702,19 +704,39 @@ eos_window_class_init (EosWindowClass *klass)
}
static void
-on_minimize_clicked_cb (GtkWidget* top_bar)
+on_minimize_clicked_cb (GtkWidget *top_bar,
+ EosWindow *self)
+{
+ gtk_window_iconify (GTK_WINDOW (self));
+}
+
+static void
+on_maximize_clicked_cb (GtkWidget *top_bar,
+ EosWindow *self)
{
- GtkWidget *window = gtk_widget_get_toplevel (top_bar);
+ EosWindowPrivate *priv = eos_window_get_instance_private (self);
- gtk_window_iconify (GTK_WINDOW (window));
+ if (priv->maximized)
+ gtk_window_unmaximize (GTK_WINDOW (self));
+ else
+ gtk_window_maximize (GTK_WINDOW (self));
}
static void
-on_close_clicked_cb (GtkWidget* top_bar)
+on_close_clicked_cb (GtkWidget *top_bar,
+ EosWindow *self)
{
- GtkWidget *window = gtk_widget_get_toplevel (top_bar);
+ gtk_window_close (GTK_WINDOW (self));
+}
- gtk_window_close (GTK_WINDOW (window));
+static void
+on_window_state_event_cb (GtkWidget *widget,
+ GdkEventWindowState *event)
+{
+ EosWindow *self = EOS_WINDOW (widget);
+ EosWindowPrivate *priv = eos_window_get_instance_private (self);
+ GdkWindowState window_state = event->new_window_state;
+ priv->maximized = window_state & GDK_WINDOW_STATE_MAXIMIZED;
}
/* Make sure that the edge finishing does not catch input events */
@@ -819,9 +841,13 @@ eos_window_init (EosWindow *self)
gtk_window_set_default_size (GTK_WINDOW (self), DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT);
g_signal_connect (priv->top_bar, "minimize-clicked",
- G_CALLBACK (on_minimize_clicked_cb), NULL);
+ G_CALLBACK (on_minimize_clicked_cb), self);
+ g_signal_connect (priv->top_bar, "maximize-clicked",
+ G_CALLBACK (on_maximize_clicked_cb), self);
g_signal_connect (priv->top_bar, "close-clicked",
- G_CALLBACK (on_close_clicked_cb), NULL);
+ G_CALLBACK (on_close_clicked_cb), self);
+ g_signal_connect (self, "window-state-event",
+ G_CALLBACK (on_window_state_event_cb), NULL);
eos_window_set_page_manager (self,
EOS_PAGE_MANAGER (eos_page_manager_new ()));
diff --git a/test/Makefile.am b/test/Makefile.am.inc
index e5fef17..08dd5e8 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am.inc
@@ -1,39 +1,20 @@
# Copyright 2013 Endless Mobile, Inc.
-noinst_PROGRAMS = \
- test/run-tests \
- test/flexy-grid \
- test/smoke-tests/hello
-
TEST_FLAGS = @EOS_SDK_CFLAGS@ -I$(top_srcdir) -DCOMPILING_EOS_SDK
TEST_LIBS = @EOS_SDK_LIBS@ $(top_builddir)/libendless-@EOS_SDK_API_VERSION@.la
+ENDLESS_TESTS_DIRECTORY = $(top_srcdir)/test
-test_run_tests_SOURCES = \
- test/run-tests.c test/run-tests.h \
- test/test-init.c \
- test/test-hello.c \
- test/test-application.c \
- test/test-page-manager.c \
- test/test-splash-page-manager.c \
- test/test-window.c \
- test/test-action-menu.c \
- test/test-action-button.c \
- test/test-flexy-grid.c \
- $(NULL)
-test_run_tests_CPPFLAGS = $(TEST_FLAGS)
-test_run_tests_LDADD = $(TEST_LIBS)
-
-test_smoke_tests_hello_SOURCES = test/smoke-tests/hello.c
-test_smoke_tests_hello_CPPFLAGS = $(TEST_FLAGS)
-test_smoke_tests_hello_LDADD = $(TEST_LIBS)
+noinst_PROGRAMS = \
+ test/endless/run-tests \
+ test/smoke-tests/hello \
+ test/demos/flexy-grid
-test_flexy_grid_SOURCES = test/flexy-grid.c
-test_flexy_grid_CPPFLAGS = $(TEST_FLAGS)
-test_flexy_grid_LDADD = $(TEST_LIBS)
+include test/endless/Makefile.am.inc
+include test/demos/Makefile.am.inc
+include test/smoke-tests/Makefile.am.inc
javascript_tests = \
- test/tools/eos-run-test/sanitycheck.js \
- test/tools/eos-application-manifest/testInit.js \
+ test/tools/eos-application-manifest/testInit.js \
test/webhelper/testTranslate.js \
test/webhelper/testWebActions.js \
test/wikipedia/models/testCategoryModel.js \
@@ -44,11 +25,10 @@ EXTRA_DIST += $(javascript_tests)
# Run tests when running 'make check'
TESTS = \
- test/run-tests \
+ test/endless/run-tests \
$(javascript_tests) \
$(NULL)
-TEST_EXTENSIONS = .js
-JS_LOG_COMPILER = tools/eos-run-test
+TEST_EXTENSIONS =
AM_JS_LOG_FLAGS = \
--include-path=$(top_srcdir)/webhelper \
--include-path=$(top_srcdir) \
@@ -56,6 +36,9 @@ AM_JS_LOG_FLAGS = \
LOG_COMPILER = gtester
AM_LOG_FLAGS = -k --verbose
+JASMINE_SUBMODULE_PATH = $(top_srcdir)/test/jasmine
+include test/jasmine/Makefile-jasmine.am.inc
+
# Use locally built versions of Endless-0.gir and libraries; this may need to be
# changed to AM_TESTS_ENVIRONMENT in a future version of Automake
# Set XDG_CONFIG_HOME so as to avoid cluttering the user's actual config
diff --git a/test/demos/Makefile.am.inc b/test/demos/Makefile.am.inc
new file mode 100644
index 0000000..797cfd3
--- /dev/null
+++ b/test/demos/Makefile.am.inc
@@ -0,0 +1,5 @@
+# Copyright 2013 Endless Mobile, Inc.
+
+test_demos_flexy_grid_SOURCES = $(ENDLESS_TESTS_DIRECTORY)/demos/flexy-grid.c
+test_demos_flexy_grid_CPPFLAGS = $(TEST_FLAGS)
+test_demos_flexy_grid_LDADD = $(TEST_LIBS)
diff --git a/test/flexy-grid.c b/test/demos/flexy-grid.c
index 2008af4..2008af4 100644
--- a/test/flexy-grid.c
+++ b/test/demos/flexy-grid.c
diff --git a/test/endless/Makefile.am.inc b/test/endless/Makefile.am.inc
new file mode 100644
index 0000000..2835934
--- /dev/null
+++ b/test/endless/Makefile.am.inc
@@ -0,0 +1,17 @@
+# Copyright 2013 Endless Mobile, Inc.
+
+test_endless_run_tests_SOURCES = \
+ $(ENDLESS_TESTS_DIRECTORY)/endless/run-tests.c \
+ $(ENDLESS_TESTS_DIRECTORY)/endless/run-tests.h \
+ $(ENDLESS_TESTS_DIRECTORY)/endless/test-init.c \
+ $(ENDLESS_TESTS_DIRECTORY)/endless/test-hello.c \
+ $(ENDLESS_TESTS_DIRECTORY)/endless/test-application.c \
+ $(ENDLESS_TESTS_DIRECTORY)/endless/test-page-manager.c \
+ $(ENDLESS_TESTS_DIRECTORY)/endless/test-splash-page-manager.c \
+ $(ENDLESS_TESTS_DIRECTORY)/endless/test-window.c \
+ $(ENDLESS_TESTS_DIRECTORY)/endless/test-action-menu.c \
+ $(ENDLESS_TESTS_DIRECTORY)/endless/test-action-button.c \
+ $(ENDLESS_TESTS_DIRECTORY)/endless/test-flexy-grid.c \
+ $(NULL)
+test_endless_run_tests_CPPFLAGS = $(TEST_FLAGS)
+test_endless_run_tests_LDADD = $(TEST_LIBS)
diff --git a/test/run-tests.c b/test/endless/run-tests.c
index db9c289..db9c289 100644
--- a/test/run-tests.c
+++ b/test/endless/run-tests.c
diff --git a/test/run-tests.h b/test/endless/run-tests.h
index 8947a5a..8947a5a 100644
--- a/test/run-tests.h
+++ b/test/endless/run-tests.h
diff --git a/test/test-action-button.c b/test/endless/test-action-button.c
index 7e4ad15..7e4ad15 100644
--- a/test/test-action-button.c
+++ b/test/endless/test-action-button.c
diff --git a/test/test-action-menu.c b/test/endless/test-action-menu.c
index 0450f6c..0450f6c 100644
--- a/test/test-action-menu.c
+++ b/test/endless/test-action-menu.c
diff --git a/test/test-application.c b/test/endless/test-application.c
index 75165d4..75165d4 100644
--- a/test/test-application.c
+++ b/test/endless/test-application.c
diff --git a/test/test-flexy-grid.c b/test/endless/test-flexy-grid.c
index 0c6c3cc..0c6c3cc 100644
--- a/test/test-flexy-grid.c
+++ b/test/endless/test-flexy-grid.c
diff --git a/test/test-hello.c b/test/endless/test-hello.c
index 687f14d..687f14d 100644
--- a/test/test-hello.c
+++ b/test/endless/test-hello.c
diff --git a/test/test-init.c b/test/endless/test-init.c
index 482c079..482c079 100644
--- a/test/test-init.c
+++ b/test/endless/test-init.c
diff --git a/test/test-page-manager.c b/test/endless/test-page-manager.c
index a39fb3f..a39fb3f 100644
--- a/test/test-page-manager.c
+++ b/test/endless/test-page-manager.c
diff --git a/test/test-splash-page-manager.c b/test/endless/test-splash-page-manager.c
index 06105aa..06105aa 100644
--- a/test/test-splash-page-manager.c
+++ b/test/endless/test-splash-page-manager.c
diff --git a/test/test-window.c b/test/endless/test-window.c
index 32639be..32639be 100644
--- a/test/test-window.c
+++ b/test/endless/test-window.c
diff --git a/test/jasmine b/test/jasmine
new file mode 160000
+Subproject 7f9fe7348a1167aee95ac472f4bb7da7701a3e7
diff --git a/test/smoke-tests/Makefile.am.inc b/test/smoke-tests/Makefile.am.inc
new file mode 100644
index 0000000..2c0fba3
--- /dev/null
+++ b/test/smoke-tests/Makefile.am.inc
@@ -0,0 +1,5 @@
+# Copyright 2013 Endless Mobile, Inc.
+
+test_smoke_tests_hello_SOURCES = $(ENDLESS_TESTS_DIRECTORY)/smoke-tests/hello.c
+test_smoke_tests_hello_CPPFLAGS = $(TEST_FLAGS)
+test_smoke_tests_hello_LDADD = $(TEST_LIBS)
diff --git a/test/webhelper/smoke-tests/webview.js b/test/smoke-tests/webhelper/webview.js
index a3b91e5..a3b91e5 100644
--- a/test/webhelper/smoke-tests/webview.js
+++ b/test/smoke-tests/webhelper/webview.js
diff --git a/test/webhelper/testTranslate.js b/test/webhelper/testTranslate.js
index 009efaf..f4d4f68 100644
--- a/test/webhelper/testTranslate.js
+++ b/test/webhelper/testTranslate.js
@@ -5,14 +5,20 @@ const Lang = imports.lang;
const WebHelper = imports.webhelper;
const WebKit = imports.gi.WebKit;
-const TestClass = new Lang.Class({
- Name: 'testclass',
+const WebHelperApplicationWithTranslatableText = new Lang.Class({
+ Name: 'WebHelperApplicationWithTranslatableText',
Extends: WebHelper.Application,
+
+ get_translation_string: function() {
+ return 'Translate Me';
+ },
vfunc_startup: function() {
this.parent();
this.webview = new WebKit.WebView();
- let string = '<html><body><p name="translatable">Translate Me</p></body></html>';
+ let string = '<html><body><p name="translatable">' +
+ this.get_translation_string() +
+ '</p></body></html>';
this.webview.load_string(string, 'text/html', 'UTF-8', 'file://');
this.win = new Endless.Window({
application: this
@@ -36,62 +42,67 @@ const TestClass = new Lang.Class({
}
});
-let app;
+describe("Translation strategy", function() {
+ let app;
-function setUp() {
- // Generate a unique ID for each app instance that we test
- let fake_pid = GLib.random_int();
- // FIXME In this version of GJS there is no Posix module, so fake the PID
- let id_string = 'com.endlessm.webhelper.test' + GLib.get_real_time() + fake_pid;
- app = new TestClass({
- application_id: id_string
+ beforeEach(function() {
+ // FIXME In this version of GJS there is no Posix module, so fake the PID
+ let id_string = 'com.endlessm.webhelper.test' + GLib.get_real_time() + fake_pid;
+ // Generate a unique ID for each app instance that we test
+ let fake_pid = GLib.random_int();
+ app = new WebHelperApplicationWithTranslatableText({
+ application_id: id_string
+ });
});
-}
-
-function testStringIsTranslated() {
- let translationFunctionWasCalled = false;
- let translationFunctionCalledWithString;
- app.set_translation_function(function(s) {
- translationFunctionWasCalled = true;
- translationFunctionCalledWithString = s;
- return s;
+
+ describe("translation function", function() {
+ let translationFunctionSpy;
+ beforeEach(function() {
+ translationFunctionSpy = jasmine.createSpy('translate').and.returnValue('Translated');
+ });
+ it("gets called with string to translate on run", function() {
+ app.set_translation_function(translationFunctionSpy);
+ app.run([]);
+ expect(translationFunctionSpy).toHaveBeenCalledWith(app.get_translation_string());
+ });
});
- app.run([]);
- assertTrue(translationFunctionWasCalled);
- assertEquals('Translate Me', translationFunctionCalledWithString);
-}
-
-// The following test is commented out because GJS cannot catch exceptions
-// across FFI interfaces (e.g. in GObject callbacks.)
-// function testMissingTranslationFunctionIsHandled() {
-// assertRaises(function() {
-// app.run([]);
-// });
-// }
+ it("throws when an incompatible type is set as the translation function", function() {
+ expect(function() {
+ app.set_translation_function({});
+ }).toThrow();
+ });
-function testSetBadTranslationFunction() {
- assertRaises(function() {
- app.set_translation_function("I am not a function");
+ // Can't test this right now as there is no support for propagating exceptions across
+ // GI interfaces
+ xit("throws when there isn't a translation function set", function() {
+ expect(function() {
+ app.run([]);
+ }).toThrow();
});
-}
-function testGetSetTranslationFunction() {
- let translationFunction = function(string) {
- return string;
- };
- app.set_translation_function(translationFunction);
- let actualTranslationFunction = app.get_translation_function();
- assertEquals(translationFunction, actualTranslationFunction);
-}
+ it("has a null translation function by default", function() {
+ expect(app.get_translation_function()).toBe(null);
+ });
-function testTranslationFunctionIsNullByDefault() {
- assertNull(app.get_translation_function());
-}
+ it("stores the expected translation function", function() {
+ let translation = function(str) {
+ return str;
+ };
+
+ app.set_translation_function(translation);
+ expect(app.get_translation_function()).toBe(translation);
+ });
-function testGetSetNullTranslationFunction() {
- app.set_translation_function(function (s) { return s; });
- assertNotNull(app.get_translation_function());
- app.set_translation_function(null);
- assertNull(app.get_translation_function());
-}
+ it("allows us to store a null translation function", function() {
+ let nonNullTranslation = function(str) {
+ return str;
+ }
+
+ // set a non-null translation function first so that we get
+ // the non-default behaviour for get_translation_function
+ app.set_translation_function(nonNullTranslation);
+ app.set_translation_function(null);
+ expect(app.get_translation_function()).toBe(null);
+ });
+});
diff --git a/test/webhelper/testWebActions.js b/test/webhelper/testWebActions.js
index 8c790b2..b2ddaf0 100644
--- a/test/webhelper/testWebActions.js
+++ b/test/webhelper/testWebActions.js
@@ -6,8 +6,8 @@ const Lang = imports.lang;
const WebHelper = imports.webhelper;
const WebKit = imports.gi.WebKit;
-const TestClass = new Lang.Class({
- Name: 'testclass',
+const WebActionTestApplication = new Lang.Class({
+ Name: 'WebActionTestApplication',
Extends: WebHelper.Application,
vfunc_startup: function() {
@@ -33,128 +33,113 @@ const TestClass = new Lang.Class({
}
});
-let app;
+// TODO: These tests depend on a running X Server and Window Manager. That means
+// that they are not runnable in a continuous-integration server
+describe("Web Actions Bindings", function() {
+ let app;
+ let webActionSpy;
+
+ beforeEach(function() {
+ // Generate a unique ID for each app instance that we test
+ let fake_pid = GLib.random_int();
+ // FIXME In this version of GJS there is no Posix module, so fake the PID
+ let id_string = 'com.endlessm.webhelper.test' + GLib.get_real_time() + fake_pid;
+ app = new WebActionTestApplication({
+ application_id: id_string
+ });
+ webActionSpy = jasmine.createSpy('quitAction').and.callFake(function() {
+ app.quit();
+ });
+ });
+
+ let RunApplicationWithWebAction = function(app, action) {
+ app.webActionToTest = action;
+ app.run([]);
+ }
+ it("has a working quitApplication uri upon defining quitApplication as a string", function() {
+ app.define_web_action('quitApplication', webActionSpy);
+ RunApplicationWithWebAction(app, 'endless://quitApplication');
-function setUp() {
- // Generate a unique ID for each app instance that we test
- let fake_pid = GLib.random_int();
- // FIXME In this version of GJS there is no Posix module, so fake the PID
- let id_string = 'com.endlessm.webhelper.test' + GLib.get_real_time() + fake_pid;
- app = new TestClass({
- application_id: id_string
+ expect(webActionSpy).toHaveBeenCalled();
});
-}
-function testWebActionIsCalled() {
- let actionWasCalled = false;
- app.define_web_action('quitApplication', function() {
- actionWasCalled = true;
- app.quit();
+ it("is called with a parameter", function() {
+ app.define_web_action('getParameterAndQuit', webActionSpy);
+ RunApplicationWithWebAction(app, 'endless://getParameterAndQuit?param=value');
+
+ expect(webActionSpy).toHaveBeenCalledWith(new jasmine.ObjectContaining({ param: 'value' }));
});
- app.webActionToTest = 'endless://quitApplication';
- app.run([]);
- assertTrue(actionWasCalled);
-}
-
-function testWebActionIsCalledWithParameter() {
- let actionParameter;
- app.define_web_action('getParameterAndQuit', function(dict) {
- actionParameter = dict['param'];
- app.quit();
+
+ it("can be called with many parameters", function() {
+ app.define_web_action('getParametersAndQuit', webActionSpy);
+ RunApplicationWithWebAction(app, 'endless://getParametersAndQuit?first=thefirst&second=thesecond&third=thethird');
+
+ expect(webActionSpy).toHaveBeenCalledWith(new jasmine.ObjectContaining({
+ first: 'thefirst',
+ second: 'thesecond',
+ third: 'thethird'
+ }));
});
- app.webActionToTest = 'endless://getParameterAndQuit?param=value';
- app.run([]);
- assertEquals('value', actionParameter);
-}
-
-function testWebActionIsCalledWithManyParameters() {
- let firstParameter, secondParameter, thirdParameter;
- app.define_web_action('getParametersAndQuit', function(dict) {
- firstParameter = dict['first'];
- secondParameter = dict['second'];
- thirdParameter = dict['third'];
- app.quit();
+
+ it("decodes parameter URI names", function() {
+ app.define_web_action('getUriDecodedParameterAndQuit', webActionSpy);
+ RunApplicationWithWebAction(app, 'endless://getUriDecodedParameterAndQuit?p%C3%A4r%C3%A4m%F0%9F%92%A9=value');
+
+ expect(webActionSpy).toHaveBeenCalledWith(new jasmine.ObjectContaining({
+ 'päräm💩' : 'value'
+ }));
});
- app.webActionToTest = 'endless://getParametersAndQuit?first=thefirst&second=thesecond&third=thethird';
- app.run([]);
- assertEquals('thefirst', firstParameter);
- assertEquals('thesecond', secondParameter);
- assertEquals('thethird', thirdParameter);
-}
-
-function testParameterNameIsUriDecoded() {
- let expectedParameter = 'päräm💩';
- let parameterWasFound = false;
- app.define_web_action('getUriDecodedParameterAndQuit', function(dict) {
- parameterWasFound = (expectedParameter in dict);
- app.quit();
+
+ it("decodes parameter URI values", function() {
+ app.define_web_action('getUriDecodedParameterValueAndQuit', webActionSpy);
+ RunApplicationWithWebAction(app, 'endless://getUriDecodedParameterValueAndQuit?param=v%C3%A1lu%C3%A9%F0%9F%92%A9');
+
+ expect(webActionSpy).toHaveBeenCalledWith(new jasmine.ObjectContaining({
+ param : 'válué💩'
+ }));
});
- app.webActionToTest = 'endless://getUriDecodedParameterAndQuit?p%C3%A4r%C3%A4m%F0%9F%92%A9=value';
- app.run([]);
- assertTrue(parameterWasFound);
-}
-
-function testParameterValueIsUriDecoded() {
- let expectedValue = 'válué💩';
- let actualValue;
- app.define_web_action('getUriDecodedValueAndQuit', function(dict) {
- actualValue = dict['param'];
- app.quit();
+
+ // We currently can't catch exceptions across GObject-Introspection callbacks
+ xit('bad action is not called', function() {
+ expect(function() { RunApplicationWithWebAction(app, 'endless://nonexistentWebAction') }).toThrow();
});
- app.webActionToTest = 'endless://getUriDecodedValueAndQuit?param=v%C3%A1lu%C3%A9%F0%9F%92%A9';
- app.run([]);
- assertEquals(expectedValue, actualValue);
-}
-
-// This is commented out because GJS cannot catch exceptions across FFI
-// interfaces (e.g. in GObject callbacks.)
-// function testBadActionIsNotCalled() {
-// app.webActionToTest = 'endless://nonexistentAction?param=value';
-// assertRaises(function() { app.run([]); });
-// }
-
-function testWebActionIsCalledWithBlankParameter() {
- let parameterWasFound = false;
- let parameterValue;
- app.define_web_action('getBlankValueAndQuit', function(dict) {
- parameterWasFound = ('param' in dict);
- if(parameterWasFound)
- parameterValue = dict['param'];
- app.quit();
+
+ describe("with blank parameters", function() {
+ beforeEach(function() {
+ app.define_web_action('getBlankValueAndQuit', webActionSpy);
+ RunApplicationWithWebAction(app, 'endless://getBlankValueAndQuit?param=');
+ });
+
+ it("can be called", function() {
+ expect(webActionSpy).toHaveBeenCalled();
+ });
+
+ it("is called with a paramater that is an empty string", function() {
+ expect(webActionSpy).toHaveBeenCalledWith(new jasmine.ObjectContaining({
+ 'param' : ''
+ }));
+ });
});
- app.webActionToTest = 'endless://getBlankValueAndQuit?param=';
- app.run([]);
- assertTrue(parameterWasFound);
- assertNotUndefined(parameterValue);
- assertEquals('', parameterValue);
-}
-
-function testWebActionIsUriDecoded() {
- let actionWasCalled = false;
- app.define_web_action('äction💩Quit', function(dict) {
- actionWasCalled = true;
- app.quit();
+
+ it("URI decodes the action", function() {
+ app.define_web_action('äction💩Quit', webActionSpy);
+ RunApplicationWithWebAction(app, 'endless://%C3%A4ction%F0%9F%92%A9Quit');
+ expect(webActionSpy).toHaveBeenCalled();
});
- app.webActionToTest = 'endless://%C3%A4ction%F0%9F%92%A9Quit';
- app.run([]);
- assertTrue(actionWasCalled);
-}
-
-function testDefineMultipleActionsOverride() {
- let actionWasCalled = false;
- app.define_web_actions({
- quitApplication: function() {
- actionWasCalled = true;
- app.quit();
- }
+
+ it("allows web actions to be defined as object properties", function() {
+ app.define_web_actions({
+ quitApplication: webActionSpy
+ });
+
+ RunApplicationWithWebAction(app, 'endless://quitApplication');
+
+ expect(webActionSpy).toHaveBeenCalled();
});
- app.webActionToTest = 'endless://quitApplication';
- app.run([]);
- assertTrue(actionWasCalled);
-}
-
-function testDefineBadAction() {
- assertRaises(function() {
- app.define_web_action('badAction', 'not a function');
+
+ it("throws an error when trying to define an action that is not a function", function() {
+ expect(function() {
+ app.define_web_action('action', {});
+ }).toThrow();
});
-}
+});
diff --git a/test/wikipedia/models/testArticleModel.js b/test/wikipedia/models/testArticleModel.js
index 655f187..f916369 100644
--- a/test/wikipedia/models/testArticleModel.js
+++ b/test/wikipedia/models/testArticleModel.js
@@ -1,72 +1,110 @@
const ArticleModel = imports.wikipedia.models.article_model;
-let mockJsonData = {
- title: 'Article Title',
- url: 'file:///',
- source: 'Mock data',
- categories: [
- 'Category One',
- 'Category Two'
- ]
-};
-
-function _assertCategoryListHasIds(categoryList, idList) {
- assertEquals(idList.length, categoryList.length);
- idList.forEach(function (id) {
- assertTrue(categoryList.some(function (actualId) {
- return actualId == id;
- }));
- });
-}
-
-function testNewModelFromJson() {
- let model = ArticleModel.newFromJson(mockJsonData);
- assertTrue(model instanceof ArticleModel.ArticleModel);
- assertEquals('Article Title', model.title);
- assertEquals('file:///', model.uri);
- _assertCategoryListHasIds(model.getCategories(),
- ['Category One', 'Category Two']);
-}
-
-function testNewWithProperties() {
- let model = new ArticleModel.ArticleModel({
+describe("Wikipedia article model", function() {
+ let mockJsonData = {
title: 'Article Title',
- uri: 'file:///'
+ url: 'file:///',
+ source: 'Mock data',
+ categories: [
+ 'Category One',
+ 'Category Two'
+ ]
+ };
+
+ describe("from JSON", function() {
+ let model;
+
+ beforeEach(function() {
+ model = ArticleModel.newFromJson(mockJsonData);
+ });
+
+ it("has an article title", function() {
+ expect(model.title).toEqual(mockJsonData.title);
+ });
+
+ it("has a uri", function() {
+ expect(model.uri).toEqual(mockJsonData.url);
+ });
+
+ it("has a list of categories", function() {
+ expect(model.getCategories()).toEqual(mockJsonData.categories);
+ });
+ });
+
+ describe("from properties", function() {
+ let model;
+ beforeEach(function() {
+ model = new ArticleModel.ArticleModel({
+ title: 'Article Title',
+ uri: 'file://'
+ });
+ });
+
+ it("is an instance of an ArticleModel", function() {
+ expect(model instanceof ArticleModel.ArticleModel).toBeTruthy();
+ });
+
+ it("has a title", function() {
+ expect(model.title).toEqual('Article Title');
+ });
+
+ it("has a URI", function() {
+ expect(model.uri).toEqual('file://');
+ });
+
+ it("has no categories", function() {
+ expect(model.getCategories().length).toEqual(0);
+ });
+ });
+
+ describe("setCategories method", function() {
+ let model;
+
+ beforeEach(function() {
+ model = new ArticleModel.ArticleModel();
+ });
+
+ it("adds categories", function() {
+ let expectedCategories = ['One', 'Two', 'Three'];
+ model.setCategories(expectedCategories);
+ expect(model.getCategories()).toEqual(expectedCategories);
+ });
+
+ it("replaces existing categories", function() {
+ model.setCategories(['One', 'Two']);
+ let expectedCategories = ['One', 'Two', 'Three'];
+ model.setCategories(expectedCategories);
+ expect(model.getCategories()).toEqual(expectedCategories);
+ });
});
- assertEquals('Article Title', model.title);
- assertEquals('file:///', model.uri);
- assertEquals(0, model.getCategories().length);
-}
-
-function testSetAndGetCategories() {
- let model = new ArticleModel.ArticleModel();
- let expectedCategories = ['One', 'Two', 'Three'];
- model.setCategories(expectedCategories);
- _assertCategoryListHasIds(model.getCategories(), expectedCategories);
-}
-
-function testSetCategoriesWipesPreviousCategories() {
- let model = new ArticleModel.ArticleModel();
- let firstCategories = ['One', 'Two', 'Three'];
- model.setCategories(firstCategories);
- let expectedCategories = ['A', 'B', 'C', 'D'];
- model.setCategories(expectedCategories);
- _assertCategoryListHasIds(model.getCategories(), expectedCategories);
-}
-
-function testAddAndGetCategories() {
- let model = new ArticleModel.ArticleModel();
- model.addCategory('One');
- model.addCategory('Two');
- model.addCategory('Three');
- _assertCategoryListHasIds(model.getCategories(), ['One', 'Two', 'Three']);
-}
-
-function testHasCategories() {
- let model = new ArticleModel.ArticleModel();
- let expectedCategories = ['One', 'Two', 'Three'];
- model.setCategories(expectedCategories);
- expectedCategories.forEach(function (id) {
- assertTrue(model.hasCategory(id));
+
+ it("appends new categories on addCategory", function() {
+ let model = new ArticleModel.ArticleModel();
+
+ model.addCategory('One');
+ model.addCategory('Two');
+ model.addCategory('Three');
+ expect(model.getCategories()).toEqual(['One', 'Two', 'Three']);
+ });
+ describe("hasCategory method", function() {
+ let model;
+ let expectedCategories = ['One', 'Two', 'Three'];
+
+ beforeEach(function() {
+ model = new ArticleModel.ArticleModel;
+ model.setCategories(expectedCategories);
+ });
+
+ expectedCategories.forEach(function(category) {
+ (function(categoryName) {
+ it("returns true for category named " + categoryName, function() {
+ expect(model.hasCategory(categoryName)).toBeTruthy();
+ });
+ });
+ });
+
+ it("returns false for an unexpected category", function() {
+ expect(model.hasCategory('unexpected')).toBeFalsy();
+ });
});
-}
+});
diff --git a/test/wikipedia/models/testCategoryModel.js b/test/wikipedia/models/testCategoryModel.js
index 8d15665..4ffc1a0 100644
--- a/test/wikipedia/models/testCategoryModel.js
+++ b/test/wikipedia/models/testCategoryModel.js
@@ -1,76 +1,180 @@
const CategoryModel = imports.wikipedia.models.category_model;
-let mockJsonData = {
- category_name: 'Category Name',
- content_text: 'Lorem Ipsum',
- image_file: 'file:///image.jpg',
- image_thumb_uri: 'file:///thumb.jpg',
- is_main_category: false,
- subcategories: [ 'Category Two' ]
-};
-
-function testNewModelFromJson() {
- let model = CategoryModel.newFromJson(mockJsonData);
- assertTrue(model instanceof CategoryModel.CategoryModel);
- assertEquals('Category Name', model.id);
- assertEquals(0, model.getSubcategories().length);
-}
-
-function testNewWithProperties() {
- let model = new CategoryModel.CategoryModel({
- id: 'id',
- title: 'title',
- description: 'description',
- image_uri: 'image-uri',
- image_thumbnail_uri: 'image-thumbnail-uri',
- is_main_category: true,
- has_articles: true
+describe("Category Model", function() {
+ let mockJsonData = {
+ category_name: 'Category Name',
+ content_text: 'Lorem Ipsum',
+ image_file: 'file:///image.jpg',
+ image_thumb_uri: 'file:///thumb.jpg',
+ is_main_category: false,
+ subcategories: [ 'Category Two' ]
+ };
+ describe("from JSON", function() {
+
+ let model;
+ beforeEach(function() {
+ model = CategoryModel.newFromJson(mockJsonData);
+ });
+
+ it("is a CategoryModel", function() {
+ expect(model instanceof CategoryModel.CategoryModel).toBeTruthy();
+ });
+
+ it("has an id", function() {
+ expect(model.id).toEqual(mockJsonData.category_name);
+ });
+
+ it("has no subcategories", function() {
+ expect(model.getSubcategories().length).toEqual(0);
+ });
});
- assertEquals('id', model.id);
- assertEquals('title', model.title);
- assertEquals('description', model.description);
- assertEquals('image-uri', model.image_uri);
- assertEquals('image-thumbnail-uri', model.image_thumbnail_uri);
- assertEquals(true, model.is_main_category);
- assertEquals(true, model.has_articles);
-
- model.has_articles = false;
- assertEquals(false, model.has_articles);
-}
-
-function testGetSubcategoriesEmpty() {
- let model = new CategoryModel.CategoryModel();
- assertEquals(0, model.getSubcategories().length);
-}
-
-function _assertCategoryListContainsCategoryIds(categoryList, idList) {
- assertEquals(idList.length, categoryList.length);
- idList.forEach(function (id) {
- assertTrue(categoryList.some(function (categoryModel) {
- return categoryModel.id == id;
- }));
+
+ describe("from properties", function() {
+ let model;
+
+ beforeEach(function() {
+ model = new CategoryModel.CategoryModel({
+ id: 'id',
+ title: 'title',
+ description: 'description',
+ image_uri: 'image-uri',
+ image_thumbnail_uri: 'image-thumbnail-uri',
+ is_main_category: true,
+ has_articles: true
+ });
+ });
+
+ it("has an id", function() {
+ expect(model.id).toEqual('id');
+ });
+
+ it("has a title", function() {
+ expect(model.title).toEqual('title');
+ });
+
+ it("has a description", function() {
+ expect(model.description).toEqual('description');
+ });
+
+ it("has an image uri", function() {
+ expect(model.image_uri).toEqual('image-uri');
+ });
+
+ it("has an image thumbnail uri", function() {
+ expect(model.image_thumbnail_uri).toEqual('image-thumbnail-uri');
+ });
+
+ it("is a main category", function() {
+ expect(model.is_main_category).toBeTruthy();
+ });
+
+ it("has articles", function() {
+ expect(model.has_articles).toBeTruthy();
+ });
+
+ // FIXME: This seems to be a fairly useless test. Does it actually
+ // test anything?
+ it("does not have articles once the flag is unset", function() {
+ model.has_articles = false;
+ expect(model.has_articles).toBeFalsy();
+ });
+ });
+
+ it("starts with no subcategories", function() {
+ let model = new CategoryModel.CategoryModel();
+
+ expect(model.getSubcategories().length).toEqual(0);
+ });
+
+ describe("in a tree-like structure", function() {
+ let parent;
+
+ beforeEach(function() {
+ jasmine.addMatchers({
+ toContainCategoriesWithNames: function() {
+ return {
+ compare: function(actual, names) {
+ let result = {
+ pass: (function() {
+ let outer_pass = true;
+ names.forEach(function (id) {
+ let categories = actual.getSubcategories();
+ if (!categories.some(function(category) {
+ return category.id == id;
+ })) {
+ outer_pass = false;
+ }
+ });
+ return outer_pass;
+ })(),
+
+ message: (function() {
+ let msg = "Expected categories with the following names\n";
+ names.forEach(function(name) {
+ msg += " " + name + "\n";
+ });
+ msg += "Object actually has the following categories\n";
+ actual.getSubcategories().forEach(function(category) {
+ msg += " " + category.id + "\n";
+ });
+ return msg;
+ })()
+ }
+
+ return result;
+ }
+ }
+ },
+ toHaveOnlyTheFollowingCategoriesInOrder: function() {
+ return {
+ compare: function(actual, names) {
+ let result = {
+ pass: (function() {
+ let categories = actual.getSubcategories();
+ if (categories.length != names.length)
+ return false;
+
+ for (let i = 0; i < categories.length; i++) {
+ if (categories[i].id != names[i])
+ return false;
+ }
+
+ return true;
+ })(),
+
+ message: (function() {
+ let msg = "Expected exactly the following category names\n";
+ names.forEach(function(name) {
+ msg += " " + name + "\n";
+ });
+
+ msg += "Actually had the following category names\n";
+ actual.getSubcategories().forEach(function(category) {
+ msg += " " + category.id + "\n";
+ });
+
+ return msg;
+ })()
+ }
+
+ return result;
+ }
+ }
+ }
+ });
+
+ parent = new CategoryModel.CategoryModel({ id: 'Category One' });
+ parent.addSubcategory(new CategoryModel.CategoryModel({ id: 'Category Two' }));
+ parent.addSubcategory(new CategoryModel.CategoryModel({ id: 'Category Three' }));
+ });
+
+ it("has subcategories", function() {
+ expect(parent).toContainCategoriesWithNames(['Category Two', 'Category Three']);
+ });
+
+ it("silently does not add duplicates", function() {
+ parent.addSubcategory(new CategoryModel.CategoryModel({ id: 'Category Two' }));
+ expect(parent).toHaveOnlyTheFollowingCategoriesInOrder(['Category Two', 'Category Three']);
+ });
});
-}
-
-function testAddAndGetSubcategories() {
- let model1 = new CategoryModel.CategoryModel({ id: 'Category One' });
- let model2 = new CategoryModel.CategoryModel({ id: 'Category Two' });
- let model3 = new CategoryModel.CategoryModel({ id: 'Category Three' });
- model1.addSubcategory(model2);
- model1.addSubcategory(model3);
-
- let categories = model1.getSubcategories();
- _assertCategoryListContainsCategoryIds(categories,
- ['Category Two', 'Category Three']);
-}
-
-function testAddSubcategoryDoesNothingForDuplicate() {
- let model1 = new CategoryModel.CategoryModel({ id: 'Category One' });
- let model2 = new CategoryModel.CategoryModel({ id: 'Category Two' });
- let model3 = new CategoryModel.CategoryModel({ id: 'Category Two' });
- model1.addSubcategory(model2);
- model1.addSubcategory(model3);
-
- let categories = model1.getSubcategories();
- _assertCategoryListContainsCategoryIds(categories, ['Category Two']);
-}
+});
diff --git a/test/wikipedia/models/testDomainWikiModel.js b/test/wikipedia/models/testDomainWikiModel.js
index e4e4d3f..f30d1bd 100644
--- a/test/wikipedia/models/testDomainWikiModel.js
+++ b/test/wikipedia/models/testDomainWikiModel.js
@@ -1,144 +1,208 @@
const DomainWikiModel = imports.wikipedia.models.domain_wiki_model;
-let model;
-
-let mockJsonData = {
- categories: [
- {
- category_name: 'Main Category',
- content_text: 'Lorem Ipsum',
- image_file: 'file:///image.jpg',
- image_thumb_uri: 'file:///image_thumb.jpg',
- is_main_category: true,
- subcategories: [
- 'Category One',
- 'Category Two'
- ]
- },
- {
- category_name: 'Category One',
- content_text: 'Lorem Ipsum',
- image_file: 'file:///image.jpg',
- image_thumb_uri: 'file:///image_thumb.jpg',
- is_main_category: false,
- subcategories: []
- },
- {
- category_name: 'Category Two',
- content_text: 'Lorem Ipsum',
- image_file: 'file:///image.jpg',
- image_thumb_uri: 'file:///image_thumb.jpg',
- is_main_category: false,
- subcategories: [
- 'Category Three'
- ]
- },
- {
- category_name: 'Category Three',
- content_text: 'Lorem Ipsum',
- image_file: 'file:///image.jpg',
- image_thumb_uri: 'file:///image_thumb.jpg',
- is_main_category: false,
- subcategories: []
- },
- ],
- articles: [
- {
- title: 'Article One',
- url: 'file:///article1.html',
- source: 'Mock data',
- categories: [
- 'Category One'
- ]
- },
- {
- title: 'Article Two',
- url: 'file:///article2.html',
- source: 'Mock data',
- categories: [
- 'Category One',
- 'Category Two'
- ]
- },
- {
- title: 'Article Three',
- url: 'file:///article3.html',
- source: 'Mock data',
- categories: [
- 'Category Two'
- ]
- }
- ]
-};
-
-function setUp() {
- model = new DomainWikiModel.DomainWikiModel();
-}
-
-function _assertArticleListContainsArticleTitles(articleList, titleList) {
- assertEquals(titleList.length, articleList.length);
- titleList.forEach(function (title) {
- assertTrue(articleList.some(function (articleModel) {
- return articleModel.title == title;
- }));
+describe('Domain Wiki Model', function () {
+ const mockJsonData = {
+ categories: [
+ {
+ category_name: 'Main Category',
+ content_text: 'Lorem Ipsum',
+ image_file: 'file:///image.jpg',
+ image_thumb_uri: 'file:///image_thumb.jpg',
+ is_main_category: true,
+ subcategories: [
+ 'Category One',
+ 'Category Two'
+ ]
+ },
+ {
+ category_name: 'Category One',
+ content_text: 'Lorem Ipsum',
+ image_file: 'file:///image.jpg',
+ image_thumb_uri: 'file:///image_thumb.jpg',
+ is_main_category: false,
+ subcategories: []
+ },
+ {
+ category_name: 'Category Two',
+ content_text: 'Lorem Ipsum',
+ image_file: 'file:///image.jpg',
+ image_thumb_uri: 'file:///image_thumb.jpg',
+ is_main_category: false,
+ subcategories: [
+ 'Category Three'
+ ]
+ },
+ {
+ category_name: 'Category Three',
+ content_text: 'Lorem Ipsum',
+ image_file: 'file:///image.jpg',
+ image_thumb_uri: 'file:///image_thumb.jpg',
+ is_main_category: false,
+ subcategories: []
+ }
+ ],
+ articles: [
+ {
+ title: 'Article One',
+ url: 'file:///article1.html',
+ source: 'Mock data',
+ categories: [
+ 'Category One'
+ ]
+ },
+ {
+ title: 'Article Two',
+ url: 'file:///article2.html',
+ source: 'Mock data',
+ categories: [
+ 'Category One',
+ 'Category Two'
+ ]
+ },
+ {
+ title: 'Article Three',
+ url: 'file:///article3.html',
+ source: 'Mock data',
+ categories: [
+ 'Category Two'
+ ]
+ }
+ ]
+ };
+ beforeEach(function () {
+ let model = new DomainWikiModel.DomainWikiModel();
+
+ jasmine.addMatchers({
+ toHaveObjectsContainingProperties: function () {
+ return {
+ compare: function (actual, propertyMap) {
+ let result = {
+ pass: (function () {
+ for (let property in propertyMap) {
+ let allValuesListedHaveAMatchForObject = actual.some(function (object) {
+ if (object[property] == 'undefined') {
+ return false;
+ }
+
+ let propertyValueMatchedForObject =
+ propertyMap[property].some(function (value) {
+ return object[property] == value;
+ });
+
+ return propertyValueMatchedForObject;
+ });
+
+ if (!allValuesListedHaveAMatchForObject)
+ return false;
+ }
+
+ return true;
+ })(),
+
+ message: (function () {
+ let msg = 'Expected objects to have the following values for the following properties \n';
+ for (let property in propertyMap) {
+ msg += ' - Property: ' + property + '\n';
+ for (let value in propertyMap[property]) {
+ msg += ' * Value: ' + propertyMap[property][value].toString() + '\n';
+ }
+ }
+
+ msg += 'Object actually has the following toplevel properties\n';
+
+ for (let i = 0; i < actual.length; i++) {
+ let object = actual[i];
+ msg += ' Object in position ' + i + '\n';
+ for (let property in object) {
+ msg += ' - ' + property + ' : ' + object[property] + '\n';
+ }
+ }
+
+ return msg;
+ })()
+ };
+
+ return result;
+ }
+ };
+ }
+ });
+ });
+
+ describe('when loaded from some mock JSON data', function () {
+ let model;
+ beforeEach(function () {
+ model = new DomainWikiModel.DomainWikiModel();
+ model.loadFromJson(mockJsonData);
+ });
+
+ it('returns all articles when getting articles', function () {
+ let articles = model.getArticles();
+ expect(articles).toHaveObjectsContainingProperties({
+ title: [ 'Article One', 'Article Two', 'Article Three' ]
+ });
+ });
+
+ it('can get articles for a category', function () {
+ let articles = model.getArticlesForCategory('Category One');
+ expect(articles).toHaveObjectsContainingProperties({
+ title: [ 'Article One', 'Article Two' ]
+ });
+ });
+
+ it('has no articles on a category that does not have articles', function () {
+ let articles = model.getArticlesForCategory('Main Category');
+ expect(articles.length).toEqual(0);
+ });
+
+ it('has no articles for a category that does not exist', function () {
+ let articles = model.getArticlesForCategory('Nonexistent');
+ expect(articles.length).toEqual(0);
+ });
+
+ it('can check whether or not a category has articles', function () {
+ expect(model._getCategoryHasArticles('Category Two')).toBeTruthy();
+ });
+
+ it('can check whether or not a category does not have articles', function () {
+ expect(model._getCategoryHasArticles('Category Three')).toBeFalsy();
+ });
+
+ it('verifies that a category that does not exist has no articles', function () {
+ expect(model._getCategoryHasArticles('Nonexistent')).toBeFalsy();
+ });
+
+ describe('category fetch', function () {
+ let category;
+
+ beforeEach(function () {
+ category = model.getCategory('Category One');
+ });
+
+ it('actually returns a category', function () {
+ expect(category.__name__).toEqual('CategoryModel');
+ });
+
+ it('returns the right category', function () {
+ expect(category.title).toEqual('Category One');
+ });
+ });
+
+ it("returns an undefined value if we try to get a category that doesn't exist", function () {
+ expect(model.getCategory('Nonexistent')).toBeUndefined();
+ });
+
+ it("returns 'Main Category' when getting the main category", function () {
+ let category = model.getMainCategory();
+
+ expect(category).toEqual(new jasmine.ObjectContaining({
+ 'title' : 'Main Category'
+ }));
+ });
+ });
+
+ it('returns null when the Main Category is unset', function () {
+ let model = new DomainWikiModel.DomainWikiModel();
+ expect(model.getMainCategory()).toBeNull();
});
-}
-
-function testGetArticlesReturnsAllArticles() {
- model.loadFromJson(mockJsonData);
- let articles = model.getArticles();
- _assertArticleListContainsArticleTitles(articles,
- [ 'Article One', 'Article Two', 'Article Three' ]);
-}
-
-function testGetArticlesForCategoryWithArticles() {
- model.loadFromJson(mockJsonData);
- let articles = model.getArticlesForCategory('Category One');
- _assertArticleListContainsArticleTitles(articles,
- [ 'Article One', 'Article Two' ]);
-}
-
-function testGetArticlesForCategoryWithoutArticles() {
- model.loadFromJson(mockJsonData);
- assertEquals(0, model.getArticlesForCategory('Main Category').length);
-}
-
-function testGetArticlesForCategoryWithNonexistentId() {
- assertEquals(0, model.getArticlesForCategory('Nonexistent').length);
-}
-
-function testCategoryHasArticlesReturnsTrue() {
- model.loadFromJson(mockJsonData);
- assertTrue(model._getCategoryHasArticles('Category Two'));
-}
-
-function testCategoryHasArticlesReturnsFalse() {
- model.loadFromJson(mockJsonData);
- assertFalse(model._getCategoryHasArticles('Category Three'));
-}
-
-function testCategoryHasArticlesWithNonexistentId() {
- assertFalse(model._getCategoryHasArticles('Nonexistent'));
-}
-
-function testGetCategory() {
- model.loadFromJson(mockJsonData);
- let category = model.getCategory('Category One');
- assertEquals('CategoryModel', category.__name__);
- assertEquals('Category One', category.title);
-}
-
-function testGetNonexistentCategory() {
- assertUndefined(model.getCategory('Nonexistent'));
-}
-
-function testGetMainCategory() {
- model.loadFromJson(mockJsonData);
- let category = model.getMainCategory();
- assertTrue(category.__name__ == 'CategoryModel');
- assertEquals('Main Category', category.title);
-}
-
-function testGetUnsetMainCategory() {
- assertNull(model.getMainCategory());
-}
+});
diff --git a/wikipedia/PrebuiltCategoryPage.js b/wikipedia/PrebuiltCategoryPage.js
index ca2c783..6ad4be7 100644
--- a/wikipedia/PrebuiltCategoryPage.js
+++ b/wikipedia/PrebuiltCategoryPage.js
@@ -10,8 +10,7 @@ const Config = imports.wikipedia.config;
const FixedSizeTextView = imports.wikipedia.widgets.FixedSizeTextView;
const ScaledImage = imports.wikipedia.widgets.scaled_image;
-const SUBMENU_SEPARATOR_A_URI = "/com/endlessm/wikipedia-domain/assets/submenu_separator_shadow_a.png";
-const SPLASH_SEPARATOR_URI = "/com/endlessm/wikipedia-domain/assets/category_splash_separator_shadow.png";
+const SHADOW_SEPARATOR_RESOURCE_PATH = "/com/endlessm/wikipedia-domain/assets/submenu_separator_shadow_a.png";
const INTRO_TITLE_SEPARATOR_URI = "/com/endlessm/wikipedia-domain/assets/introduction_title_separator.png";
const LEFT_MARGIN_FOR_TEXT = 45;
@@ -80,13 +79,13 @@ const PrebuiltCategoryPage = new Lang.Class({
});
this._submenu_separator = new ScaledImage.ScaledImage({
- resource: SUBMENU_SEPARATOR_A_URI,
+ resource: SHADOW_SEPARATOR_RESOURCE_PATH,
constraint: Gtk.Orientation.VERTICAL,
halign: Gtk.Align.END
});
this._splash_separator = new ScaledImage.ScaledImage({
- resource: SPLASH_SEPARATOR_URI,
+ resource: SHADOW_SEPARATOR_RESOURCE_PATH,
constraint: Gtk.Orientation.VERTICAL,
halign: Gtk.Align.END
});