From 6a4f391e38414a400dd3e1440097dde65d0ff989 Mon Sep 17 00:00:00 2001 From: Guo Xiang Tan Date: Tue, 19 Dec 2017 15:59:41 +0800 Subject: [PATCH] Switch to chrome headless mode instead of phantomjs. --- Brewfile | 3 - docs/DEVELOPER-ADVANCED.md | 6 +- docs/DEVELOPMENT-OSX-NATIVE.md | 6 +- lib/autospec/qunit_runner.rb | 15 +- lib/autospec/run-qunit.js | 284 ++++++++-------- lib/tasks/qunit.rake | 22 +- lib/tasks/smoke_test.rake | 19 +- spec/phantom_js/smoke_test.js | 307 ------------------ .../topic-notifications-button-test.js.es6 | 11 +- test/javascripts/lib/url-test.js.es6 | 2 +- vendor/assets/javascripts/babel.js | 11 +- vendor/assets/javascripts/run-qunit-chrome.js | 171 ---------- vendor/assets/javascripts/run-qunit.js | 148 ++++++--- 13 files changed, 284 insertions(+), 721 deletions(-) delete mode 100644 spec/phantom_js/smoke_test.js delete mode 100644 vendor/assets/javascripts/run-qunit-chrome.js diff --git a/Brewfile b/Brewfile index ee7378b71c6..ba4f913899d 100644 --- a/Brewfile +++ b/Brewfile @@ -14,6 +14,3 @@ brew 'postgresql' # install the Redis datastore brew 'redis' - -# install headless Javascript testing library -brew 'phantomjs' diff --git a/docs/DEVELOPER-ADVANCED.md b/docs/DEVELOPER-ADVANCED.md index 9b417e5d99b..5bd9792d06c 100644 --- a/docs/DEVELOPER-ADVANCED.md +++ b/docs/DEVELOPER-ADVANCED.md @@ -38,7 +38,7 @@ To get your Ubuntu 16.04 LTS install up and running to develop Discourse and Dis nvm install node nvm alias default node - npm install -g svgo phantomjs-prebuilt + npm install -g svgo If everything goes alright, let's clone Discourse and start hacking: @@ -54,10 +54,10 @@ If everything goes alright, let's clone Discourse and start hacking: # time to create the database and run migrations bundle exec rake db:create db:migrate RAILS_ENV=test bundle exec rake db:create db:migrate - + # run the specs (optional) bundle exec rake autospec # CTRL + C to stop - + # launch discourse bundle exec rails s -b 0.0.0.0 # open browser on http://localhost:3000 and you should see Discourse diff --git a/docs/DEVELOPMENT-OSX-NATIVE.md b/docs/DEVELOPMENT-OSX-NATIVE.md index d93732266b2..389d7e46ccf 100644 --- a/docs/DEVELOPMENT-OSX-NATIVE.md +++ b/docs/DEVELOPMENT-OSX-NATIVE.md @@ -183,11 +183,11 @@ You should not need to alter `/usr/local/var/postgres/pg_hba.conf` That's about it. -## PhantomJS +## Google Chrome 59+ -Homebrew loves you. +Chrome is used for running QUnit tests in headless mode. - brew install phantomjs +Download from https://www.google.com/chrome/index.html ## ImageMagick diff --git a/lib/autospec/qunit_runner.rb b/lib/autospec/qunit_runner.rb index 5c570643704..051fcb0409c 100644 --- a/lib/autospec/qunit_runner.rb +++ b/lib/autospec/qunit_runner.rb @@ -24,10 +24,10 @@ module Autospec require "socket" - class PhantomJsNotInstalled < StandardError; end + class ChromeNotInstalled < StandardError; end def initialize - ensure_phantomjs_is_installed + ensure_chrome_is_installed end def start @@ -66,7 +66,7 @@ module Autospec end end - cmd = "phantomjs #{Rails.root}/lib/autospec/run-qunit.js \"#{qunit_url}\"" + cmd = "node #{Rails.root}/lib/autospec/run-qunit.js \"#{qunit_url}\"" @pid = Process.spawn(cmd) _, status = Process.wait2(@pid) @@ -96,7 +96,6 @@ module Autospec end def stop - # kill phantomjs first abort stop_rails_server @running = false @@ -104,8 +103,12 @@ module Autospec private - def ensure_phantomjs_is_installed - raise PhantomJsNotInstalled.new unless system("command -v phantomjs >/dev/null;") + def ensure_chrome_is_installed + raise ChromeNotInstalled.new unless system("command -v google-chrome >/dev/null;") + + if Gem::Version.new(`$(command -v google-chrome) --version`.match(/[\d\.]+/)[0]) < Gem::Version.new("59") + raise "Chrome 59 or higher is required" + end end def port_available?(port) diff --git a/lib/autospec/run-qunit.js b/lib/autospec/run-qunit.js index fcf10df5c61..2c2bd860734 100644 --- a/lib/autospec/run-qunit.js +++ b/lib/autospec/run-qunit.js @@ -1,182 +1,182 @@ -/*jshint devel:true, phantom:true */ -/*globals QUnit ANSI */ +// Chrome QUnit Test Runner +// Author: David Taylor +// Requires chrome-launcher and chrome-remote-interface from npm +// An up-to-date version of chrome is also required -// THIS FILE IS CALLED BY "qunit_runner.rb" IN AUTOSPEC +/* globals Promise */ -var system = require("system"), - args = phantom.args; +var args = process.argv.slice(2); -if (args === undefined) { - args = system.args; - args.shift(); +if (args.length < 1 || args.length > 2) { + console.log("Usage: node run-qunit.js "); + process.exit(1); } -if (args.length !== 1) { - console.log("Usage: " + phantom.scriptName + " "); - phantom.exit(1); -} +const chromeLauncher = require('chrome-launcher'); +const CDP = require('chrome-remote-interface'); +const fs = require('fs'); +const QUNIT_RESULT = "./tmp/qunit_result"; -var fs = require('fs'), - page = require('webpage').create(), - QUNIT_RESULT = "./tmp/qunit_result"; +(async () => { + await fs.stat(QUNIT_RESULT, (err, stats) => { + if (stats && stats.isFile()) fs.unlink(QUNIT_RESULT); + }); +})(); -if (fs.exists(QUNIT_RESULT) && fs.isFile(QUNIT_RESULT)) { fs.remove(QUNIT_RESULT); } +(function() { -page.onConsoleMessage = function (message) { - // filter out Ember's debug messages - if (message.slice(0, 8) === "WARNING:") { return; } - if (message.slice(0, 6) === "DEBUG:") { return; } - - console.log(message); -}; - -page.onCallback = function (message) { - // write to the result file - if (message.slice(0, 5) === "FAIL:") { - fs.write(QUNIT_RESULT, message.slice(6) + "\n", "a"); + function launchChrome() { + return chromeLauncher.launch({ + chromeFlags: [ + '--disable-gpu', + '--headless', + '--no-sandbox' + ] + }); } - // forward the message to the standard output - if (message.slice(0, 6) === "PRINT:") { system.stdout.write(message.slice(7)); } -}; -page.start = new Date(); + launchChrome().then(chrome => { + CDP({ + port: chrome.port + }).then(protocol => { + const {Page, Runtime} = protocol; + Promise.all([Page.enable(), Runtime.enable()]).then(()=>{ -// -----------------------------------WARNING -------------------------------------- -// calling "console.log" BELOW this line will go through the "page.onConsoleMessage" -// -----------------------------------WARNING -------------------------------------- -page.open(args[0], function (status) { - if (status !== "success") { - console.log("\nNO NETWORK :(\n"); - phantom.exit(1); - } else { - console.log("QUnit loaded in " + (new Date() - page.start) + " ms"); + Runtime.consoleAPICalled((response) => { + const message = response['args'][0].value; - page.evaluate(colorizer); - page.evaluate(logQUnit); - - // wait up to 600 seconds for QUnit to finish - var timeout = 600 * 1000, - start = Date.now(); - - var interval = setInterval(function() { - if (Date.now() - start > timeout) { - console.error("\nTIME OUT :(\n"); - phantom.exit(1); - } else { - var qunitResult = page.evaluate(function() { return window.qunitResult; }); - if (qunitResult) { - clearInterval(interval); - if (qunitResult.failed > 0) { - phantom.exit(1); + // If it's a simple test result, write without newline + if (message === "." || message === "F") { + process.stdout.write(message); + } else if (message.startsWith("AUTOSPEC:")) { + fs.appendFileSync(QUNIT_RESULT, `${message.slice(10)}\n`); } else { - phantom.exit(0); + console.log(message); } - } - } - }, 250); - } -}); + }); -// https://github.com/jquery/qunit/pull/470 -function colorizer() { - window.ANSI = { - colorMap: { - "red": "\u001b[31m", - "green": "\u001b[32m", - "blue": "\u001b[34m", - "end": "\u001b[0m" - }, - highlightMap: { - "red": "\u001b[41m\u001b[37m", // change 37 to 30 for black text - "green": "\u001b[42m\u001b[30m", - "blue": "\u001b[44m\u001b[37m", - "end": "\u001b[0m" - }, + Page.navigate({ + url: args[0] + }); - highlight: function (text, color) { - var colorCode = this.highlightMap[color], - colorEnd = this.highlightMap.end; + Page.loadEventFired(() => { - return colorCode + text + colorEnd; - }, + Runtime.evaluate({ + expression: `(${qunit_script})()` + }).then(() => { + const timeout = parseInt(args[1] || 300000, 10); + var start = Date.now(); - colorize: function (text, color) { - var colorCode = this.colorMap[color], - colorEnd = this.colorMap.end; + var interval = setInterval(() => { + if (Date.now() > start + timeout) { + console.error("Tests timed out"); - return colorCode + text + colorEnd; - } + protocol.close(); + chrome.kill(); + process.exit(124); + } else { + + Runtime.evaluate({ + expression: `(${check_script})()` + }).then(numFails => { + if (numFails.result.type !== 'undefined') { + clearInterval(interval); + protocol.close(); + chrome.kill(); + + if (numFails.result.value > 0) { + process.exit(1); + } else { + process.exit(); + } + } + }).catch(error); + } + }, 250); + }).catch(error(1)); + }); + }).catch(error(3)); + }).catch(error(4)); + }).catch(error(5)); +})(); + +function error(code){ + return function(){ + console.log("A promise failed to resolve code:"+code); + process.exit(1); }; } - +// The following functions are converted to strings +// And then sent to chrome to be evalaluated function logQUnit() { - // keep track of error messages - var errors = {}; + var moduleErrors = []; + var testErrors = []; + var assertionErrors = []; - QUnit.begin(function () { - console.log("BEGIN"); - }); + console.log("\nRunning: " + JSON.stringify(QUnit.urlParams) + "\n"); - QUnit.log(function (context) { - if (!context.result) { - var module = context.module, - test = context.name; + QUnit.config.testTimeout = 10000; - var assertion = { - message: context.message, - expected: context.expected, - actual: context.actual - }; - - if (!errors[module]) { errors[module] = {}; } - if (!errors[module][test]) { errors[module][test] = []; } - errors[module][test].push(assertion); - - window.callPhantom("FAIL: " + context.module + ":::" + context.testId + ":::" + context.name); + QUnit.moduleDone(function(context) { + if (context.failed) { + var msg = "Module Failed: " + context.name + "\n" + testErrors.join("\n"); + moduleErrors.push(msg); + testErrors = []; } }); - QUnit.testDone(function (context) { - if (context.failed > 0) { - window.callPhantom("PRINT: " + ANSI.colorize("F", "red")); + QUnit.testDone(function(context) { + if (context.failed) { + var msg = " Test Failed: " + context.name + assertionErrors.join(" "); + console.log(`AUTOSPEC: ${context.module}:::${context.testId}:::${context.name}`); + testErrors.push(msg); + assertionErrors = []; + console.log("F"); } else { - window.callPhantom("PRINT: " + ANSI.colorize(".", "green")); + console.log("."); } }); - QUnit.done(function (context) { + QUnit.log(function(context) { + if (context.result) { return; } + + var msg = "\n Assertion Failed:"; + if (context.message) { + msg += " " + context.message; + } + + if (context.expected) { + msg += "\n Expected: " + context.expected + ", Actual: " + context.actual; + } + + assertionErrors.push(msg); + }); + + QUnit.done(function(context) { console.log("\n"); - // display failures - if (Object.keys(errors).length > 0) { - console.log("Failures:\n"); - for (var m in errors) { - var module = errors[m]; - console.log("Module Failed: " + ANSI.highlight(m, "red")); - for (var t in module) { - var test = module[t]; - console.log(" Test Failed: " + t); - for (var a = 0; a < test.length; a++) { - var assertion = test[a]; - console.log(" Assertion Failed: " + (assertion.message || "")); - if (assertion.expected) { - console.log(" Expected: " + assertion.expected); - console.log(" Actual: " + assertion.actual); - } - } - } + if (moduleErrors.length > 0) { + for (var idx=0; idx 0 ? "red" : "green"; - console.log(ANSI.colorize(context.total + " examples, " + context.failed + " failures", color)); - - // we're done - window.qunitResult = context; + var stats = [ + "Time: " + context.runtime + "ms", + "Total: " + context.total, + "Passed: " + context.passed, + "Failed: " + context.failed + ]; + console.log(stats.join(", ")); + window.qunitDone = context; }); - } +const qunit_script = logQUnit.toString(); + +function check() { + if(window.qunitDone){ + return window.qunitDone.failed; + } +} +const check_script = check.toString(); diff --git a/lib/tasks/qunit.rake b/lib/tasks/qunit.rake index 98d69c8403e..db7c19e7724 100644 --- a/lib/tasks/qunit.rake +++ b/lib/tasks/qunit.rake @@ -5,10 +5,20 @@ task "qunit:test", [:timeout, :qunit_path] => :environment do |_, args| require "rack" require "socket" - unless %x{which phantomjs > /dev/null 2>&1} || ENV["USE_CHROME"] - abort "PhantomJS is not installed. Download from http://phantomjs.org" + unless system("command -v google-chrome >/dev/null;") + abort "Chrome is not installed. Download from https://www.google.com/chrome/browser/desktop/index.html" end + if Gem::Version.new(`$(command -v google-chrome) --version`.match(/[\d\.]+/)[0]) < Gem::Version.new("59") + abort "Chrome 59 or higher is required to run tests in headless mode." + end + + unless system("command -v yarn >/dev/null;") + abort "Yarn is not installed. Download from https://yarnpkg.com/lang/en/docs/install/" + end + + system("yarn install --dev") + # ensure we have this port available def port_available?(port) server = TCPServer.open port @@ -36,13 +46,7 @@ task "qunit:test", [:timeout, :qunit_path] => :environment do |_, args| success = true test_path = "#{Rails.root}/vendor/assets/javascripts" qunit_path = args[:qunit_path] || "/qunit" - - if ENV["USE_CHROME"] - cmd = "node #{test_path}/run-qunit-chrome.js http://localhost:#{port}#{qunit_path}" - else - cmd = "phantomjs #{test_path}/run-qunit.js http://localhost:#{port}#{qunit_path}" - end - + cmd = "node #{test_path}/run-qunit.js http://localhost:#{port}#{qunit_path}" options = {} %w{module filter qunit_skip_core qunit_single_plugin}.each do |arg| diff --git a/lib/tasks/smoke_test.rake b/lib/tasks/smoke_test.rake index e5e4473c8c3..61f0a74ce92 100644 --- a/lib/tasks/smoke_test.rake +++ b/lib/tasks/smoke_test.rake @@ -1,8 +1,12 @@ -desc "run phantomjs based smoke tests on current build" +desc "run chrome headless smoke tests on current build" task "smoke:test" do - phantom_path = File.expand_path('~/phantomjs/bin/phantomjs') - phantom_path = nil unless File.exists?(phantom_path) - phantom_path = phantom_path || 'phantomjs' + unless system("command -v google-chrome >/dev/null;") + abort "Chrome is not installed. Download from https://www.google.com/chrome/browser/desktop/index.html" + end + + if Gem::Version.new(`$(command -v google-chrome) --version`.match(/[\d\.]+/)[0]) < Gem::Version.new("59") + abort "Chrome 59 or higher is required to run smoke tests in headless mode." + end url = ENV["URL"] if !url @@ -32,12 +36,7 @@ task "smoke:test" do results = "" - command = - if ENV["USE_CHROME"] - "node #{Rails.root}/test/smoke_test.js #{url}" - else - "#{phantom_path} #{Rails.root}/spec/phantom_js/smoke_test.js #{url}" - end + "node #{Rails.root}/test/smoke_test.js #{url}" IO.popen(command).each do |line| puts line diff --git a/spec/phantom_js/smoke_test.js b/spec/phantom_js/smoke_test.js deleted file mode 100644 index 55e48cd9a8f..00000000000 --- a/spec/phantom_js/smoke_test.js +++ /dev/null @@ -1,307 +0,0 @@ -/*global phantom:true */ - -console.log("Starting Discourse Smoke Test"); - -var system = require("system"); - -if (system.args.length !== 2) { - console.log("Expecting: phantomjs {smoke_test.js} {url}"); - phantom.exit(1); -} - -var TIMEOUT = 25000; -var page = require("webpage").create(); - -if (system.env["AUTH_USER"] && system.env["AUTH_PASSWORD"]) { - page.settings.userName = system.env["AUTH_USER"]; - page.settings.password = system.env["AUTH_PASSWORD"]; -} - -page.viewportSize = { - width: 1366, - height: 768 -}; - -// In the browser, when the cookies are disabled, it also disables the localStorage -// Here, we're mocking that behavior and making sure the application doesn't blow up -page.onInitialized = function() { - page.evaluate(function() { - localStorage["disableLocalStorage"] = true; - }); -}; - -page.onConsoleMessage = function(msg) { - console.log(msg); -}; - -page.waitFor = function(desc, fn, cb) { - var start = +new Date(); - - var check = function() { - var r; - - try { r = page.evaluate(fn); } catch (err) { } - - var diff = (+new Date()) - start; - - if (r) { - console.log("PASSED: " + desc + " - " + diff + "ms"); - cb(true); - } else { - if (diff > TIMEOUT) { - console.log("FAILED: " + desc + " - " + diff + "ms"); - page.render('/tmp/failed.png'); - console.log('Content:' + page.content); - cb(false); - } else { - setTimeout(check, 25); - } - } - }; - - check(); -}; - - -var actions = []; - -function test(desc, fn) { - actions.push({ test: fn, desc: desc }); -}; - -// function wait(delay) { -// actions.push({ wait: delay }); -// } - -function exec(desc, fn) { - actions.push({ exec: fn, desc: desc }); -}; - -function execAsync(desc, delay, fn) { - actions.push({ execAsync: fn, delay: delay, desc: desc }); -}; - -// function upload(input, path) { -// actions.push({ upload: path, input: input }); -// }; - -// function screenshot(filename) { -// actions.push({ screenshot: filename }); -// } - -function run() { - var allPassed = true; - - var done = function() { - console.log(allPassed ? "ALL PASSED" : "SMOKE TEST FAILED"); - phantom.exit(); - }; - - var performNextAction = function() { - if (!allPassed || actions.length === 0) { - done(); - } else { - var action = actions[0]; - actions = actions.splice(1); - if (action.test) { - page.waitFor(action.desc, action.test, function(success) { - allPassed = allPassed && success; - performNextAction(); - }); - } else if (action.exec) { - console.log("EXEC: " + action.desc); - page.evaluate(action.exec, system); - performNextAction(); - } else if (action.execAsync) { - console.log("EXEC ASYNC: " + action.desc + " - " + action.delay + "ms"); - setTimeout(function() { - page.evaluate(action.execAsync); - performNextAction(); - }, action.delay); - } else if (action.upload) { - console.log("UPLOAD: " + action.upload); - page.uploadFile(action.input, action.upload); - performNextAction(); - } else if (action.screenshot) { - console.log("SCREENSHOT: " + action.screenshot); - page.render(action.screenshot); - performNextAction(); - } else if (action.wait) { - console.log("WAIT: " + action.wait + "ms"); - setTimeout(function() { - performNextAction(); - }, action.wait); - } - } - }; - - performNextAction(); -}; - -var runTests = function() { - - test("expect a log in button in the header", function() { - return $("header .login-button").length; - }); - - execAsync("go to latest page", 500, function(){ - window.location = "/latest"; - }); - - test("at least one topic shows up", function() { - return $(".topic-list tbody tr").length; - }); - - execAsync("go to categories page", 500, function(){ - window.location = "/categories"; - }); - - test("can see categories on the page", function() { - return $('.category-list').length; - }); - - execAsync("navigate to 1st topic", 500, function() { - $(".main-link a.title:first").click(); - }); - - test("at least one post body", function() { - return $(".topic-post").length; - }); - - execAsync("click on the 1st user", 500, function() { - // remove the popup action for testing - $(".topic-meta-data a:first").data("ember-action", ""); - $(".topic-meta-data a:first").focus().click(); - }); - - test("user has details", function() { - return $("#user-card .names").length; - }); - - if (!system.env["READONLY_TESTS"]) { - exec("open login modal", function() { - $(".login-button").click(); - }); - - test("login modal is open", function() { - return $(".login-modal").length; - }); - - exec("type in credentials & log in", function(system) { - $("#login-account-name").val(system.env['DISCOURSE_USERNAME'] || 'smoke_user').trigger("change"); - $("#login-account-password").val(system.env["DISCOURSE_PASSWORD"] || 'P4ssw0rd').trigger("change"); - $(".login-modal .btn-primary").click(); - }); - - test("is logged in", function() { - return $(".current-user").length; - }); - - exec("go home", function() { - if ($('#site-logo').length) $('#site-logo').click(); - if ($('#site-text-logo').length) $('#site-text-logo').click(); - }); - - test("it shows a topic list", function() { - return $(".topic-list").length; - }); - - test('we have a create topic button', function() { - return $("#create-topic").length; - }); - - exec("open composer", function() { - $("#create-topic").click(); - }); - - test('the editor is visible', function() { - return $(".d-editor").length; - }); - - exec("compose new topic", function() { - var date = " (" + (+new Date()) + ")", - title = "This is a new topic" + date, - post = "I can write a new topic inside the smoke test!" + date + "\n\n"; - - $("#reply-title").val(title).trigger("change"); - $("#reply-control .d-editor-input").val(post).trigger("change"); - $("#reply-control .d-editor-input").focus()[0].setSelectionRange(post.length, post.length); - }); - - test("updates preview", function() { - return $(".d-editor-preview p").length; - }); - - exec("open upload modal", function() { - $(".d-editor-button-bar .upload").click(); - }); - - test("upload modal is open", function() { - return $("#filename-input").length; - }); - - // TODO: Looks like PhantomJS 2.0.0 has a bug with `uploadFile` - // which breaks this code. - - // upload("#filename-input", "spec/fixtures/images/large & unoptimized.png"); - // test("the file is inserted into the input", function() { - // return document.getElementById('filename-input').files.length - // }); - // screenshot('/tmp/upload-modal.png'); - // - // test("upload modal is open", function() { - // return document.querySelector("#filename-input"); - // }); - // - // exec("click upload button", function() { - // $(".modal .btn-primary").click(); - // }); - // - // test("image is uploaded", function() { - // return document.querySelector(".cooked img"); - // }); - - exec("submit the topic", function() { - $("#reply-control .create").click(); - }); - - test("topic is created", function() { - return $(".fancy-title").length; - }); - - exec("click reply button", function() { - $(".post-controls:first .create").click(); - }); - - test("composer is open", function() { - return $("#reply-control .d-editor-input").length; - }); - - exec("compose reply", function() { - var post = "I can even write a reply inside the smoke test ;) (" + (+new Date()) + ")"; - $("#reply-control .d-editor-input").val(post).trigger("change"); - }); - - test("waiting for the preview", function() { - return $(".d-editor-preview").text().trim().indexOf("I can even write") === 0; - }); - - execAsync("submit the reply", 6000, function() { - $("#reply-control .create").click(); - }); - - test("reply is created", function() { - return !document.querySelector(".saving-text") - && $(".topic-post").length === 2; - }); - } - - run(); -}; - -phantom.clearCookies(); -page.open(system.args[1], function() { - page.evaluate(function() { localStorage.clear(); }); - console.log("OPENED: " + system.args[1]); - runTests(); -}); diff --git a/test/javascripts/acceptance/topic-notifications-button-test.js.es6 b/test/javascripts/acceptance/topic-notifications-button-test.js.es6 index 7a0934ffc57..f8295808457 100644 --- a/test/javascripts/acceptance/topic-notifications-button-test.js.es6 +++ b/test/javascripts/acceptance/topic-notifications-button-test.js.es6 @@ -38,11 +38,10 @@ QUnit.test("Updating topic notification level", assert => { "it should display the right notification level" ); - // TODO: tgxworld I can't figure out why the topic timeline doesn't show when - // running the tests in phantomjs - // ok( - // exists(".timeline-footer-controls .notifications-button .watching"), - // 'it should display the right notification level in topic timeline' - // ); + assert.equal( + find(`.timeline-footer-controls .select-kit-header`).data().name, + 'Watching', + 'it should display the right notification level in topic timeline' + ); }); }); diff --git a/test/javascripts/lib/url-test.js.es6 b/test/javascripts/lib/url-test.js.es6 index 71a95467a70..15f760d92d7 100644 --- a/test/javascripts/lib/url-test.js.es6 +++ b/test/javascripts/lib/url-test.js.es6 @@ -37,4 +37,4 @@ QUnit.test("userPath with BaseUri", assert => { assert.equal(userPath(), '/forum/u'); assert.equal(userPath('eviltrout'), '/forum/u/eviltrout'); assert.equal(userPath('hp.json'), '/forum/u/hp.json'); -}); \ No newline at end of file +}); diff --git a/vendor/assets/javascripts/babel.js b/vendor/assets/javascripts/babel.js index 8a341e11749..4c54562fcdc 100644 --- a/vendor/assets/javascripts/babel.js +++ b/vendor/assets/javascripts/babel.js @@ -2357,7 +2357,7 @@ return /******/ (function(modules) { // webpackBootstrap $export.B = 16; // bind $export.W = 32; // wrap $export.U = 64; // safe - $export.R = 128; // real proto method for `library` + $export.R = 128; // real proto method for `library` module.exports = $export; /***/ }), @@ -62209,13 +62209,6 @@ return /******/ (function(modules) { // webpackBootstrap "test": false, "throws": false }, - "phantomjs": { - "console": true, - "exports": true, - "phantom": true, - "require": true, - "WebPage": true - }, "couch": { "emit": false, "exports": false, @@ -62843,4 +62836,4 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }) /******/ ]))) }); -; \ No newline at end of file +; diff --git a/vendor/assets/javascripts/run-qunit-chrome.js b/vendor/assets/javascripts/run-qunit-chrome.js deleted file mode 100644 index 5e988e628f1..00000000000 --- a/vendor/assets/javascripts/run-qunit-chrome.js +++ /dev/null @@ -1,171 +0,0 @@ -// Chrome QUnit Test Runner -// Author: David Taylor -// Requires chrome-launcher and chrome-remote-interface from npm -// An up-to-date version of chrome is also required - -/* globals Promise */ - -var args = process.argv.slice(2); - -if (args.length < 1 || args.length > 2) { - console.log("Usage: node run-qunit-chrome.js "); - process.exit(1); -} - -const chromeLauncher = require('chrome-launcher'); -const CDP = require('chrome-remote-interface'); - -(function() { - - function launchChrome() { - return chromeLauncher.launch({ - chromeFlags: [ - '--disable-gpu', - '--headless', - '--no-sandbox' - ] - }); - } - - launchChrome().then(chrome => { - CDP({ - port: chrome.port - }).then(protocol => { - const {Page, Runtime} = protocol; - Promise.all([Page.enable(), Runtime.enable()]).then(()=>{ - - Runtime.consoleAPICalled((response) => { - const message = response['args'][0].value; - - // If it's a simple test result, write without newline - if(message === "." || message === "F"){ - process.stdout.write(message); - }else{ - console.log(message); - } - }); - - Page.navigate({ - url: args[0] - }); - - Page.loadEventFired(() => { - - Runtime.evaluate({ - expression: `(${qunit_script})()` - }).then(() => { - const timeout = parseInt(args[1] || 300000, 10); - var start = Date.now(); - - var interval = setInterval(() => { - if (Date.now() > start + timeout) { - console.error("Tests timed out"); - - protocol.close(); - chrome.kill(); - process.exit(124); - } else { - - Runtime.evaluate({ - expression: `(${check_script})()` - }).then(numFails => { - if (numFails.result.type !== 'undefined') { - clearInterval(interval); - protocol.close(); - chrome.kill(); - - if (numFails.result.value > 0) { - process.exit(1); - } else { - process.exit(); - } - } - }).catch(error); - } - }, 250); - }).catch(error(1)); - }); - }).catch(error(3)); - }).catch(error(4)); - }).catch(error(5)); -})(); - -function error(code){ - return function(){ - console.log("A promise failed to resolve code:"+code); - process.exit(1); - }; -} - -// The following functions are converted to strings -// And then sent to chrome to be evalaluated -function logQUnit() { - var moduleErrors = []; - var testErrors = []; - var assertionErrors = []; - - console.log("\nRunning: " + JSON.stringify(QUnit.urlParams) + "\n"); - - QUnit.config.testTimeout = 10000; - - QUnit.moduleDone(function(context) { - if (context.failed) { - var msg = "Module Failed: " + context.name + "\n" + testErrors.join("\n"); - moduleErrors.push(msg); - testErrors = []; - } - }); - - QUnit.testDone(function(context) { - if (context.failed) { - var msg = " Test Failed: " + context.name + assertionErrors.join(" "); - testErrors.push(msg); - assertionErrors = []; - console.log("F"); - } else { - console.log("."); - } - }); - - QUnit.log(function(context) { - if (context.result) { return; } - - var msg = "\n Assertion Failed:"; - if (context.message) { - msg += " " + context.message; - } - - if (context.expected) { - msg += "\n Expected: " + context.expected + ", Actual: " + context.actual; - } - - assertionErrors.push(msg); - }); - - QUnit.done(function(context) { - console.log("\n"); - - if (moduleErrors.length > 0) { - for (var idx=0; idx 2) { - console.log("Usage: " + phantom.scriptName + " "); - phantom.exit(1); + console.log("Usage: node run-qunit.js "); + process.exit(1); } -var page = require("webpage").create(); +const chromeLauncher = require('chrome-launcher'); +const CDP = require('chrome-remote-interface'); -page.onConsoleMessage = function(msg) { - if (msg.slice(0, 8) === "WARNING:") { return; } - if (msg.slice(0, 6) === "DEBUG:") { return; } +(function() { - console.log(msg); -}; + function launchChrome() { + return chromeLauncher.launch({ + chromeFlags: [ + '--disable-gpu', + '--headless', + '--no-sandbox' + ] + }); + } -page.onCallback = function (message) { - // forward the message to the standard output - system.stdout.write(message); -}; + launchChrome().then(chrome => { + CDP({ + port: chrome.port + }).then(protocol => { + const {Page, Runtime} = protocol; + Promise.all([Page.enable(), Runtime.enable()]).then(()=>{ -page.open(args[0], function(status) { - if (status !== "success") { - console.error("Unable to access network"); - phantom.exit(1); - } else { - page.evaluate(logQUnit); + Runtime.consoleAPICalled((response) => { + const message = response['args'][0].value; - var timeout = parseInt(args[1] || 300000, 10), - start = Date.now(); - - var interval = setInterval(function() { - if (Date.now() > start + timeout) { - console.error("Tests timed out"); - phantom.exit(124); - } else { - var qunitDone = page.evaluate(function() { - return window.qunitDone; + // If it's a simple test result, write without newline + if(message === "." || message === "F"){ + process.stdout.write(message); + }else{ + console.log(message); + } }); - if (qunitDone) { - clearInterval(interval); - if (qunitDone.failed > 0) { - phantom.exit(1); - } else { - phantom.exit(); - } - } - } - }, 250); - } -}); + Page.navigate({ + url: args[0] + }); + Page.loadEventFired(() => { + + Runtime.evaluate({ + expression: `(${qunit_script})()` + }).then(() => { + const timeout = parseInt(args[1] || 300000, 10); + var start = Date.now(); + + var interval = setInterval(() => { + if (Date.now() > start + timeout) { + console.error("Tests timed out"); + + protocol.close(); + chrome.kill(); + process.exit(124); + } else { + + Runtime.evaluate({ + expression: `(${check_script})()` + }).then(numFails => { + if (numFails.result.type !== 'undefined') { + clearInterval(interval); + protocol.close(); + chrome.kill(); + + if (numFails.result.value > 0) { + process.exit(1); + } else { + process.exit(); + } + } + }).catch(error); + } + }, 250); + }).catch(error(1)); + }); + }).catch(error(3)); + }).catch(error(4)); + }).catch(error(5)); +})(); + +function error(code){ + return function(){ + console.log("A promise failed to resolve code:"+code); + process.exit(1); + }; +} + +// The following functions are converted to strings +// And then sent to chrome to be evalaluated function logQUnit() { var moduleErrors = []; var testErrors = []; @@ -83,9 +121,9 @@ function logQUnit() { var msg = " Test Failed: " + context.name + assertionErrors.join(" "); testErrors.push(msg); assertionErrors = []; - window.callPhantom("F"); + console.log("F"); } else { - window.callPhantom("."); + console.log("."); } }); @@ -123,3 +161,11 @@ function logQUnit() { window.qunitDone = context; }); } +const qunit_script = logQUnit.toString(); + +function check() { + if(window.qunitDone){ + return window.qunitDone.failed; + } +} +const check_script = check.toString();