From 2b9be5376e5d48350eee0f52df1468f7286272a8 Mon Sep 17 00:00:00 2001 From: Hannah Wolfe Date: Wed, 31 Jul 2013 08:33:28 +0100 Subject: [PATCH] Added functional tests for Ghost Admin UI using Casperjs Hacky implementation of a suite of casper tests. This is here so that we can start to build up some tests. Main thing missing is being able to simulate keypresses for CodeMirror Making the tests run nicely with grunt, travis and be independent rather than interdependent can all come later. - See tests/functional/base.js for full usage instructions & implementation notes --- Gruntfile.js | 6 +-- core/test/functional/admin/01_login_test.js | 47 +++++++++++++++++ .../functional/admin/02_dashboard_test.js | 30 +++++++++++ core/test/functional/admin/03_editor_test.js | 50 +++++++++++++++++++ core/test/functional/admin/04_content_test.js | 39 +++++++++++++++ .../test/functional/admin/05_settings_test.js | 21 ++++++++ core/test/functional/base.js | 38 ++++++++++++++ .../{ghost => unit}/api_permissions_spec.js | 0 core/test/{ghost => unit}/api_posts_spec.js | 0 .../test/{ghost => unit}/api_settings_spec.js | 0 core/test/{ghost => unit}/api_users_spec.js | 0 .../{ghost => unit}/errorHandling_spec.js | 0 core/test/{ghost => unit}/export_spec.js | 0 core/test/{ghost => unit}/fixtures/test.hbs | 0 .../frontend_helpers_index_spec.js | 0 core/test/{ghost => unit}/ghost_spec.js | 2 +- core/test/{ghost => unit}/helpers.js | 0 core/test/{ghost => unit}/import_spec.js | 0 core/test/{ghost => unit}/permissions_spec.js | 0 19 files changed, 229 insertions(+), 4 deletions(-) create mode 100644 core/test/functional/admin/01_login_test.js create mode 100644 core/test/functional/admin/02_dashboard_test.js create mode 100644 core/test/functional/admin/03_editor_test.js create mode 100644 core/test/functional/admin/04_content_test.js create mode 100644 core/test/functional/admin/05_settings_test.js create mode 100644 core/test/functional/base.js rename core/test/{ghost => unit}/api_permissions_spec.js (100%) rename core/test/{ghost => unit}/api_posts_spec.js (100%) rename core/test/{ghost => unit}/api_settings_spec.js (100%) rename core/test/{ghost => unit}/api_users_spec.js (100%) rename core/test/{ghost => unit}/errorHandling_spec.js (100%) rename core/test/{ghost => unit}/export_spec.js (100%) rename core/test/{ghost => unit}/fixtures/test.hbs (100%) rename core/test/{ghost => unit}/frontend_helpers_index_spec.js (100%) rename core/test/{ghost => unit}/ghost_spec.js (98%) rename core/test/{ghost => unit}/helpers.js (100%) rename core/test/{ghost => unit}/import_spec.js (100%) rename core/test/{ghost => unit}/permissions_spec.js (100%) diff --git a/Gruntfile.js b/Gruntfile.js index 05fbdff39b..8df664acbe 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -98,15 +98,15 @@ var path = require('path'), }, all: { - src: ['core/test/**/*_spec.js'] + src: ['core/test/unit/**/*_spec.js'] }, api: { - src: ['core/test/**/api*_spec.js'] + src: ['core/test/unit/**/api*_spec.js'] }, perm: { - src: ['core/test/**/permissions_spec.js'] + src: ['core/test/unit/**/permissions_spec.js'] } }, diff --git a/core/test/functional/admin/01_login_test.js b/core/test/functional/admin/01_login_test.js new file mode 100644 index 0000000000..5434b788d2 --- /dev/null +++ b/core/test/functional/admin/01_login_test.js @@ -0,0 +1,47 @@ +/*globals casper, __utils__, url, user */ +casper.test.begin("Ghost admin will load login page", 2, function suite(test) { + + casper.test.filename = "admin_test.png"; + + casper.start(url + "ghost", function testTitleAndUrl() { + test.assertTitle("", "Ghost admin has no title"); + test.assertEquals(this.getCurrentUrl(), url + "ghost/login/", "Ghost requires login"); + }).viewport(1280, 1024); + + casper.run(function () { + test.done(); + }); +}); + + +casper.test.begin("Can login to Ghost", 3, function suite(test) { + + casper.test.filename = "login_test.png"; + + casper.start(url + "ghost/login/", function testTitle() { + test.assertTitle("", "Ghost admin has no title"); + }).viewport(1280, 1024); + + casper.waitFor(function checkOpaque() { + return this.evaluate(function () { + var loginBox = document.querySelector('.login-box'); + return window.getComputedStyle(loginBox).getPropertyValue('display') === "block" + && window.getComputedStyle(loginBox).getPropertyValue('opacity') === "1"; + }); + }, function then() { + this.fill("#login", user, true); + }); + + casper.wait(1000, function doneWait() { + this.echo("I've waited for 1 seconds."); + }); + + casper.then(function testForDashboard() { + this.test.assertExists("#global-header", "Global admin header is present"); + this.test.assertExists(".dashboard", "We're now on the dashboard"); + }); + + casper.run(function () { + test.done(); + }); +}); \ No newline at end of file diff --git a/core/test/functional/admin/02_dashboard_test.js b/core/test/functional/admin/02_dashboard_test.js new file mode 100644 index 0000000000..86063ce00a --- /dev/null +++ b/core/test/functional/admin/02_dashboard_test.js @@ -0,0 +1,30 @@ +/*globals casper, __utils__, url */ + +casper.test.begin("Ghost dashboard is correct", 13, function suite(test) { + + casper.test.filename = "dashboard_test.png"; + + casper.start(url + "ghost", function testTitleAndUrl() { + test.assertTitle("", "Ghost admin has no title"); + test.assertEquals(this.getCurrentUrl(), url + "ghost/", "Ghost doesn't require login this time"); + test.assertExists("#ghost", "Ghost is present"); + }).viewport(1280, 1024); + + casper.then(function testMenus() { + test.assertExists("#main-menu", "Main menu is present"); + test.assertSelectorHasText("#main-menu .dashboard a", "Dashboard"); + test.assertSelectorHasText("#main-menu .content a", "Content"); + test.assertSelectorHasText("#main-menu .editor a", "New Post"); + test.assertSelectorHasText("#main-menu .settings a", "Settings"); + + test.assertExists("#usermenu", "User menu is present"); + test.assertSelectorHasText("#usermenu .usermenu-profile a", "Your Profile"); + test.assertSelectorHasText("#usermenu .usermenu-help a", "Help / Support"); + test.assertSelectorHasText("#usermenu .usermenu-shortcuts a", "Keyboard Shortcuts"); + test.assertSelectorHasText("#usermenu .usermenu-signout a", "Sign Out"); + }); + + casper.run(function () { + test.done(); + }); +}); diff --git a/core/test/functional/admin/03_editor_test.js b/core/test/functional/admin/03_editor_test.js new file mode 100644 index 0000000000..84c47f0c25 --- /dev/null +++ b/core/test/functional/admin/03_editor_test.js @@ -0,0 +1,50 @@ +/*globals casper, __utils__, url, testPost */ + +casper.test.begin("Ghost editor is correct", 7, function suite(test) { + + casper.test.filename = "editor_test.png"; + + casper.start(url + "ghost/editor", function testTitleAndUrl() { + test.assertTitle("", "Ghost admin has no title"); + test.assertEquals(casper.getCurrentUrl(), url + "ghost/editor", "Ghost doesn't require login this time"); + test.assertExists(".entry-markdown", "Ghost editor is present"); + test.assertExists(".entry-preview", "Ghost preview is present"); + }).viewport(1280, 1024); + + function handleResource(resource) { + if (resource.url === 'http://localhost:2368/api/v0.1/posts') { + casper.removeListener('resource.received', handleResource); + casper.test.assertEquals(resource.status, 200, "Received correct response"); + } + } + + casper.then(function testCreatePost() { + // bind to resource events so we can get the API response + casper.on('resource.received', handleResource); + + casper.sendKeys('#entry-title', testPost.title); + casper.evaluate(function () { + var txt = document.querySelector('.CodeMirror-wrap textarea'); + txt.focus(); + // TODO: finish figuring out codemirror this works in chrome console.. but not in evaluate? + txt.value = "abcd"; + }); + + casper.click('.button-save'); + }); + + casper.wait(1000, function doneWait() { + this.echo("I've waited for 1 seconds."); + }); + + casper.then(function checkPostWasCreated() { + var urlRegExp = new RegExp("^" + url + "ghost\/editor\/[0-9]*"); + test.assertUrlMatch(urlRegExp, 'got an id on our URL'); + test.assertExists('.notification-success', 'got success notification'); + }); + + casper.run(function () { + casper.removeListener('resource.received', handleResource); + test.done(); + }); +}); diff --git a/core/test/functional/admin/04_content_test.js b/core/test/functional/admin/04_content_test.js new file mode 100644 index 0000000000..bbc60a5770 --- /dev/null +++ b/core/test/functional/admin/04_content_test.js @@ -0,0 +1,39 @@ +/*globals casper, __utils__, url, testPost */ + +casper.test.begin("Content screen is correct", 9, function suite(test) { + + casper.test.filename = "content_test.png"; + + casper.start(url + "ghost/content/", function testTitleAndUrl() { + test.assertTitle("", "Ghost admin has no title"); + test.assertEquals(this.getCurrentUrl(), url + "ghost/content/", "Ghost doesn't require login this time"); + }).viewport(1280, 1024); + + casper.then(function testViews() { + test.assertExists(".content-view-container", "Content main view is present"); + test.assertExists(".content-list-content", "Content list view is present"); + test.assertExists(".content-list-content li .entry-title", "Content list view has at least one item"); + test.assertExists(".content-preview", "Content preview is present"); + test.assertSelectorHasText(".content-list-content li:first-child h3", testPost.title, "first item is the post we created"); + }); + + casper.then(function testActiveItem() { + casper.test.assertEquals(casper.evaluate(function () { + return document.querySelector('.content-list-content li').className; + }), "active", "first item is active"); + + }).thenClick(".content-list-content li:nth-child(2) a", function then() { + casper.test.assertEquals(casper.evaluate(function () { + return document.querySelectorAll('.content-list-content li')[1].className; + }), "active", "second item is active"); + }); + + // TODO: finish testing delete +// casper.then(function testDeletePost() { +// casper.clickLabel(testPost.title, "h3"); +// }); + + casper.run(function () { + test.done(); + }); +}); \ No newline at end of file diff --git a/core/test/functional/admin/05_settings_test.js b/core/test/functional/admin/05_settings_test.js new file mode 100644 index 0000000000..8f41461806 --- /dev/null +++ b/core/test/functional/admin/05_settings_test.js @@ -0,0 +1,21 @@ +/*globals casper, __utils__, url */ + +casper.test.begin("Settings screen is correct", 3, function suite(test) { + + casper.test.filename = "settings_test.png"; + + casper.start(url + "ghost/settings", function testTitleAndUrl() { + test.assertTitle("", "Ghost admin has no title"); + test.assertEquals(this.getCurrentUrl(), url + "ghost/settings/general", "Ghost doesn't require login this time"); + }).viewport(1280, 1024); + + casper.then(function testViews() { + test.assertExists(".wrapper", "Settings main view is present"); + + // TODO: real settings tests + }); + + casper.run(function () { + test.done(); + }); +}); \ No newline at end of file diff --git a/core/test/functional/base.js b/core/test/functional/base.js new file mode 100644 index 0000000000..904bf139e9 --- /dev/null +++ b/core/test/functional/base.js @@ -0,0 +1,38 @@ +/*globals casper, __utils__ */ + +/** + * Casper Tests + * + * Functional browser tests for checking that the Ghost Admin UI is working as expected + * The setup of these tests is a little hacky for now, which is why they are not wired in to grunt + * Requires that you are running Ghost locally and have already registered a single user + * + * Usage (from test/functional): + * + * casperjs test admin/ --includes=base.js [--host=localhost --port=2368 --email=ghost@tryghost.org --password=Sl1m3r] + * + * --host - your local host address e.g. localhost or local.tryghost.org + * --port - port number of your local Ghost + * --email - the email address your admin user is registered with + * --password - the password your admin user is registered with + * + * Requirements: + * you must have phantomjs 1.9.1 and casperjs 1.1.0-DEV installed in order for these tests to work + */ + +var host = casper.cli.options.url || 'localhost', + port = casper.cli.options.port || '2368', + email = casper.cli.options.email || 'ghost@tryghost.org', + password = casper.cli.options.password || 'Sl1m3r', + url = "http://" + host + ":" + port + "/", + user = { + email: email, + password: password + }, + testPost = {title: "A post title", content: "I am a post \n #With some content"}; + + +casper.test.on("fail", function captureFailure(failure) { + var filename = casper.test.filename || "casper_test_fail.png"; + casper.capture(new Date().getTime() + '_' + filename); +}); \ No newline at end of file diff --git a/core/test/ghost/api_permissions_spec.js b/core/test/unit/api_permissions_spec.js similarity index 100% rename from core/test/ghost/api_permissions_spec.js rename to core/test/unit/api_permissions_spec.js diff --git a/core/test/ghost/api_posts_spec.js b/core/test/unit/api_posts_spec.js similarity index 100% rename from core/test/ghost/api_posts_spec.js rename to core/test/unit/api_posts_spec.js diff --git a/core/test/ghost/api_settings_spec.js b/core/test/unit/api_settings_spec.js similarity index 100% rename from core/test/ghost/api_settings_spec.js rename to core/test/unit/api_settings_spec.js diff --git a/core/test/ghost/api_users_spec.js b/core/test/unit/api_users_spec.js similarity index 100% rename from core/test/ghost/api_users_spec.js rename to core/test/unit/api_users_spec.js diff --git a/core/test/ghost/errorHandling_spec.js b/core/test/unit/errorHandling_spec.js similarity index 100% rename from core/test/ghost/errorHandling_spec.js rename to core/test/unit/errorHandling_spec.js diff --git a/core/test/ghost/export_spec.js b/core/test/unit/export_spec.js similarity index 100% rename from core/test/ghost/export_spec.js rename to core/test/unit/export_spec.js diff --git a/core/test/ghost/fixtures/test.hbs b/core/test/unit/fixtures/test.hbs similarity index 100% rename from core/test/ghost/fixtures/test.hbs rename to core/test/unit/fixtures/test.hbs diff --git a/core/test/ghost/frontend_helpers_index_spec.js b/core/test/unit/frontend_helpers_index_spec.js similarity index 100% rename from core/test/ghost/frontend_helpers_index_spec.js rename to core/test/unit/frontend_helpers_index_spec.js diff --git a/core/test/ghost/ghost_spec.js b/core/test/unit/ghost_spec.js similarity index 98% rename from core/test/ghost/ghost_spec.js rename to core/test/unit/ghost_spec.js index 9d55549aff..5f7077a502 100644 --- a/core/test/ghost/ghost_spec.js +++ b/core/test/unit/ghost_spec.js @@ -7,7 +7,7 @@ var should = require('should'), Ghost = require('../../ghost'); describe("Ghost API", function () { - var testTemplatePath = 'core/test/ghost/fixtures/', + var testTemplatePath = 'core/test/unit/fixtures/', ghost; beforeEach(function () { diff --git a/core/test/ghost/helpers.js b/core/test/unit/helpers.js similarity index 100% rename from core/test/ghost/helpers.js rename to core/test/unit/helpers.js diff --git a/core/test/ghost/import_spec.js b/core/test/unit/import_spec.js similarity index 100% rename from core/test/ghost/import_spec.js rename to core/test/unit/import_spec.js diff --git a/core/test/ghost/permissions_spec.js b/core/test/unit/permissions_spec.js similarity index 100% rename from core/test/ghost/permissions_spec.js rename to core/test/unit/permissions_spec.js