Switch to chrome headless mode instead of phantomjs.
This commit is contained in:
parent
30ddc1f222
commit
6a4f391e38
3
Brewfile
3
Brewfile
|
@ -14,6 +14,3 @@ brew 'postgresql'
|
|||
|
||||
# install the Redis datastore
|
||||
brew 'redis'
|
||||
|
||||
# install headless Javascript testing library
|
||||
brew 'phantomjs'
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 <URL> <timeout>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (args.length !== 1) {
|
||||
console.log("Usage: " + phantom.scriptName + " <URL>");
|
||||
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<moduleErrors.length; idx++) {
|
||||
console.error(moduleErrors[idx]+"\n");
|
||||
}
|
||||
}
|
||||
|
||||
// display summary
|
||||
console.log("\n");
|
||||
console.log("Finished in " + (context.runtime / 1000) + " seconds");
|
||||
var color = context.failed > 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();
|
||||
|
|
|
@ -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|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
});
|
|
@ -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'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 <URL> <timeout>");
|
||||
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<moduleErrors.length; idx++) {
|
||||
console.error(moduleErrors[idx]+"\n");
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
|
@ -1,66 +1,104 @@
|
|||
// PhantomJS QUnit Test Runner
|
||||
// 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 QUnit phantom*/
|
||||
/* globals Promise */
|
||||
|
||||
var system = require("system"),
|
||||
args = phantom.args;
|
||||
|
||||
if (args === undefined) {
|
||||
args = system.args;
|
||||
args.shift();
|
||||
}
|
||||
var args = process.argv.slice(2);
|
||||
|
||||
if (args.length < 1 || args.length > 2) {
|
||||
console.log("Usage: " + phantom.scriptName + " <URL> <timeout>");
|
||||
phantom.exit(1);
|
||||
console.log("Usage: node run-qunit.js <URL> <timeout>");
|
||||
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();
|
||||
|
|
Loading…
Reference in New Issue