use Karma as the test runner for the UJS tests

This commit is contained in:
Lachlan Sylvester 2022-10-08 23:34:42 +11:00 committed by Lachlan Sylvester
parent b9aaf99701
commit d9e79ce7f4
31 changed files with 3902 additions and 23354 deletions

@ -112,7 +112,6 @@ end
# Action View
group :view do
gem "blade", require: false, platforms: [:ruby]
gem "sprockets-export", require: false
end

@ -140,19 +140,6 @@ GEM
beaneater (1.1.1)
benchmark-ips (2.9.2)
bindex (0.8.1)
blade (0.7.3)
activesupport (>= 3.0.0)
blade-qunit_adapter (>= 2.0.1)
coffee-script
coffee-script-source
curses (>= 1.4.0)
eventmachine
faye
sprockets (>= 3.0)
thin (>= 1.6.0)
thor (>= 0.19.1)
useragent (>= 0.16.7)
blade-qunit_adapter (2.0.1)
bootsnap (1.9.3)
msgpack (~> 1.0)
builder (3.2.4)
@ -170,20 +157,13 @@ GEM
xpath (~> 3.2)
cgi (0.3.6)
childprocess (4.1.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.12.2)
concurrent-ruby (1.1.9)
connection_pool (2.2.5)
cookiejar (0.3.3)
crack (0.4.5)
rexml
crass (1.0.6)
cssbundling-rails (1.1.0)
railties (>= 6.0.0)
curses (1.4.4)
daemons (1.4.1)
dalli (3.2.0)
dante (0.2.0)
debug (1.4.0)
@ -211,7 +191,6 @@ GEM
et-orbi (1.2.6)
tzinfo
event_emitter (0.2.6)
eventmachine (1.2.7)
execjs (2.8.1)
faraday (1.8.0)
faraday-em_http (~> 1.0)
@ -234,17 +213,6 @@ GEM
faraday-rack (1.0.0)
faraday_middleware (1.2.0)
faraday (~> 1.0)
faye (1.4.0)
cookiejar (>= 0.3.0)
em-http-request (>= 1.1.6)
eventmachine (>= 0.12.0)
faye-websocket (>= 0.11.0)
multi_json (>= 1.0.0)
rack (>= 1.0.0)
websocket-driver (>= 0.5.1)
faye-websocket (0.11.1)
eventmachine (>= 0.12.0)
websocket-driver (>= 0.5.1)
ffi (1.15.4)
fugit (1.5.2)
et-orbi (~> 1.1, >= 1.1.8)
@ -286,7 +254,6 @@ GEM
os (>= 0.9, < 2.0)
signet (>= 0.16, < 2.a)
hashdiff (1.0.1)
http_parser.rb (0.8.0)
httpclient (2.8.3)
i18n (1.8.11)
concurrent-ruby (~> 1.0)
@ -518,10 +485,6 @@ GEM
railties (>= 6.0.0)
terser (1.1.8)
execjs (>= 0.3.0, < 3)
thin (1.8.1)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
thor (1.2.1)
tilt (2.0.10)
timeout (0.3.1)
@ -575,7 +538,6 @@ DEPENDENCIES
backburner
bcrypt (~> 3.1.11)
benchmark-ips
blade
bootsnap (>= 1.4.4)
capybara (>= 3.26)
cgi (>= 0.3.6)

@ -1,4 +1,5 @@
/lib/assets/compiled/
/test/ujs/compiled/
/log/
/test/fixtures/public/absolute/
/test/ujs/log/

@ -1,8 +1,9 @@
== Running UJS tests
Ensure that you can build the project by running:
Run the tests in headless mode by running:
rake test:ujs
To run the tests in a browser, start the Rails UJS server by running:
rake ujs:server
Then run the web tests by visiting the following URL in your browser:
http://localhost:4567

@ -3,6 +3,7 @@
require "rake/testtask"
require "fileutils"
require "open3"
require "base64"
desc "Default Task"
task default: :test
@ -38,15 +39,6 @@ namespace :test do
listen_host = "localhost"
listen_port = "4567"
runner_command = %w(ruby ../ci/qunit-selenium-runner.rb)
if ENV["SELENIUM_DRIVER_URL"]
require "socket"
runner_command += %W(http://#{Socket.gethostname}:#{listen_port}/ #{ENV["SELENIUM_DRIVER_URL"]})
listen_host = "0.0.0.0"
else
runner_command += %W(http://localhost:#{listen_port}/)
end
Dir.mkdir("log")
pid = File.open("log/test.log", "w") do |f|
spawn(*%W(rackup test/ujs/config.ru -o #{listen_host} -p #{listen_port} -s puma), out: f, err: f, pgroup: true)
@ -66,8 +58,7 @@ namespace :test do
sleep 0.2
end
system(*runner_command)
system(Hash[*Base64.decode64(ENV.fetch("ENCODED", "")).split(/[ =]/)], "npm", "test")
status = $?.exitstatus
ensure
Process.kill("KILL", -pid) if pid
@ -101,7 +92,8 @@ end
namespace :ujs do
desc "Starts the test server"
task :server do
system "bundle exec rackup test/ujs/config.ru -p 4567 -s puma"
spawn("bundle exec rackup test/ujs/config.ru -p 4567 -s puma")
system("npm test -- --no-single-run --browsers Chrome")
end
end

64
actionview/karma.conf.js Normal file

@ -0,0 +1,64 @@
const config = {
browsers: ["ChromeHeadless"],
frameworks: ["qunit"],
files: [
"test/ujs/compiled/test.js",
],
client: {
clearContext: false,
qunit: {
showUI: true
}
},
singleRun: true,
autoWatch: false,
captureTimeout: 180000,
browserDisconnectTimeout: 180000,
browserDisconnectTolerance: 3,
browserNoActivityTimeout: 300000,
proxies: {
'/echo': 'http://localhost:4567/echo',
'/error': 'http://localhost:4567/error'
}
}
if (process.env.CI) {
config.customLaunchers = {
sl_chrome: sauce("chrome", "latest", "Windows 10")
}
config.browsers = Object.keys(config.customLaunchers)
config.reporters = ["dots", "saucelabs"]
config.sauceLabs = {
testName: "Rails UJS",
retryLimit: 3,
build: buildId(),
}
function sauce(browserName, version, platform) {
const options = {
base: "SauceLabs",
browserName: browserName.toString(),
version: version.toString(),
}
if (platform) {
options.platform = platform.toString()
}
return options
}
function buildId() {
const { BUILDKITE_JOB_ID } = process.env
return BUILDKITE_JOB_ID
? `Buildkite ${BUILDKITE_JOB_ID}`
: ""
}
}
module.exports = function(karmaConfig) {
karmaConfig.set(config)
}

@ -12,7 +12,8 @@
},
"scripts": {
"build": "rollup --config rollup.config.js",
"test": "echo \"See the README: https://github.com/rails/rails/blob/main/actionview/app/assets/javascripts#how-to-run-tests\" && exit 1",
"pretest": "rollup --config rollup.config.test.js",
"test": "karma start",
"lint": "eslint app/javascript"
},
"repository": {
@ -31,8 +32,16 @@
},
"homepage": "https://rubyonrails.org/",
"devDependencies": {
"@rollup/plugin-commonjs": "^19.0.1",
"@rollup/plugin-node-resolve": "^11.0.1",
"eslint": "^4.19.1",
"eslint-plugin-import": "^2.23.4",
"jquery": "^2.2.0",
"karma": "^3.1.1",
"karma-chrome-launcher": "^2.2.0",
"karma-qunit": "^2.1.0",
"karma-sauce-launcher": "^1.2.0",
"qunit": "^2.8.0",
"rollup": "^2.53.3",
"rollup-plugin-terser": "^7.0.2"
}

@ -0,0 +1,16 @@
import commonjs from "@rollup/plugin-commonjs"
import resolve from "@rollup/plugin-node-resolve"
export default {
input: "test/ujs/src/test.js",
output: {
file: "test/ujs/compiled/test.js",
format: "iife"
},
plugins: [
resolve(),
commonjs()
]
}

@ -0,0 +1,8 @@
document.addEventListener('rails:attachBindings', function() {
window.Rails.linkClickSelector += ', a[data-custom-remote-link]';
// Hijacks link click before ujs binds any handlers
// This is only used for ctrl-clicking test on remote links
window.Rails.delegate(document, '#qunit-fixture a', 'click', function(e) {
e.preventDefault();
});
});

@ -1,4 +1,5 @@
(function() {
import $ from 'jquery'
import Rails from "../../../../app/javascript/rails-ujs/index"
QUnit.module('call-ajax', {
beforeEach: function() {
@ -24,5 +25,3 @@ QUnit.test('call ajax without "ajax:beforeSend"', function(assert) {
link.triggerNative('click')
})
})()

@ -1,4 +1,4 @@
(function() {
import $ from 'jquery'
QUnit.module('call-remote-callbacks', {
beforeEach: function() {
@ -264,5 +264,3 @@ QUnit.test('binding to ajax callbacks via .delegate() triggers handlers properly
})
$('form[data-remote]').triggerNative('submit')
})
})()

@ -1,4 +1,4 @@
(function() {
import $ from 'jquery'
function buildForm(attrs) {
attrs = $.extend({ action: '/echo', 'data-remote': 'true' }, attrs)
@ -352,5 +352,3 @@ QUnit.test('intelligently guesses crossDomain behavior when target URL consists
setTimeout(function() { done() }, 13)
})
})()

@ -1,4 +1,4 @@
(function() {
import $ from 'jquery'
QUnit.module('csrf-refresh', {})
@ -15,9 +15,7 @@ QUnit.test('refresh all csrf tokens', function(assert) {
.append(form)
$.rails.refreshCSRFTokens()
currentToken = $('#qunit-fixture #authenticity_token').val()
var currentToken = $('#qunit-fixture #authenticity_token').val()
assert.equal(currentToken, correctToken)
})
})()

@ -1,4 +1,4 @@
(function() {
import $ from 'jquery'
QUnit.module('csrf-token', {})
@ -7,7 +7,7 @@ QUnit.test('find csrf token', function(assert) {
$('#qunit-fixture').append('<meta name="csrf-token" content="' + correctToken + '"/>')
currentToken = $.rails.csrfToken()
var currentToken = $.rails.csrfToken()
assert.equal(currentToken, correctToken)
})
@ -17,9 +17,7 @@ QUnit.test('find csrf param', function(assert) {
$('#qunit-fixture').append('<meta name="csrf-param" content="' + correctParam + '"/>')
currentParam = $.rails.csrfParam()
var currentParam = $.rails.csrfParam()
assert.equal(currentParam, correctParam)
})
})()

@ -1,3 +1,5 @@
import $ from 'jquery'
QUnit.module('data-confirm', {
beforeEach: function() {
$('#qunit-fixture').append($('<a />', {

@ -1,3 +1,5 @@
import $ from 'jquery'
QUnit.module('data-disable-with', {
beforeEach: function() {
$('#qunit-fixture').append($('<form />', {
@ -326,12 +328,12 @@ QUnit.test('a[data-remote][data-disable-with] re-enables when `ajax:error` event
.bindNative('ajax:beforeSend', function() {
assert.disabledState(link, 'clicking...')
})
.bindNative('ajax:complete', function() {
setTimeout(function() {
assert.enabledState(link, 'Click me')
done()
}, 30) })
.triggerNative('click')
setTimeout(function() {
assert.enabledState(link, 'Click me')
done()
}, 30)
})
QUnit.test('form[data-remote] input|button|textarea[data-disable-with] does not disable when `ajax:beforeSend` event is cancelled', function(assert) {
@ -454,10 +456,11 @@ QUnit.test('button[data-remote][data-disable-with] re-enables when `ajax:error`
.bindNative('ajax:send', function() {
assert.disabledState(button, 'clicking...')
})
.bindNative('ajax:complete', function() {
setTimeout(function() {
assert.enabledState(button, 'Click me')
done()
}, 30)
})
.triggerNative('click')
setTimeout(function() {
assert.enabledState(button, 'Click me')
done()
}, 30)
})

@ -1,3 +1,5 @@
import $ from 'jquery'
QUnit.module('data-disable', {
beforeEach: function() {
$('#qunit-fixture').append($('<form />', {
@ -226,12 +228,13 @@ QUnit.test('a[data-remote][data-disable] re-enables when `ajax:error` event is t
.bindNative('ajax:send', function() {
assert.disabledState(link, 'Click me')
})
.bindNative('ajax:complete', function() {
setTimeout(function() {
assert.enabledState(link, 'Click me')
done()
}, 30)
})
.triggerNative('click')
setTimeout(function() {
assert.enabledState(link, 'Click me')
done()
}, 30)
})
QUnit.test('form[data-remote] input|button|textarea[data-disable] does not disable when `ajax:beforeSend` event is cancelled', function(assert) {
@ -355,12 +358,13 @@ QUnit.test('button[data-remote][data-disable] re-enables when `ajax:error` event
.bindNative('ajax:send', function() {
assert.disabledState(button, 'Click me')
})
.bindNative('ajax:complete', function() {
setTimeout(function() {
assert.enabledState(button, 'Click me')
done()
}, 30)
})
.triggerNative('click')
setTimeout(function() {
assert.enabledState(button, 'Click me')
done()
}, 30)
})
QUnit.test('do not enable elements for XHR redirects', function(assert) {

@ -1,4 +1,4 @@
(function() {
import $ from 'jquery'
QUnit.module('data-method', {
beforeEach: function() {
@ -89,5 +89,3 @@ QUnit.test('link with "data-method" and cross origin', function(assert) {
assert.notEqual(data.authenticity_token, 'cf50faa3fe97702ca1ae')
})
})()

@ -1,4 +1,4 @@
(function() {
import $ from 'jquery'
function buildSelect(attrs) {
attrs = $.extend({
@ -574,5 +574,3 @@ QUnit.test('inputs inside disabled fieldset are not submitted on remote forms',
})
.triggerNative('submit')
})
})()

@ -1,4 +1,4 @@
(function() {
import $ from 'jquery'
var realHref
@ -50,5 +50,3 @@ QUnit.test('including rails-ujs multiple times throws error', function(assert) {
}, 'appending rails.js again throws error')
setTimeout(function() { done() }, 50)
})
})()

@ -1,6 +1,14 @@
import $ from 'jquery';
import Rails from '../../../../app/javascript/rails-ujs/index'
$.rails = Rails
var App = App || {}
var Turbolinks = Turbolinks || {}
window.Turbolinks = Turbolinks
window.jQuery = $
QUnit.assert.callbackInvoked = function(callbackName) {
this.ok(true, callbackName + ' callback should have been invoked')
}
@ -48,7 +56,7 @@ $(document).bind('submit', function(e) {
var form = $(e.target), action = form.attr('action'),
name = 'form-frame' + jQuery.guid++,
iframe = $('<iframe name="' + name + '" />'),
iframeInput = '<input name="iframe" value="true" type="hidden" />'
iframeInput = '<input name="iframe" value="true" type="hidden" />',
targetInput = '<input name="_target" value="' + (form.attr('target') || '') + '" type="hidden" />'
if (action && action.indexOf('iframe') < 0) {

File diff suppressed because it is too large Load Diff

@ -1,525 +0,0 @@
/*!
* QUnit 2.19.1
* https://qunitjs.com/
*
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0;
padding: 0;
}
/* Style our buttons in a simple way, uninfluenced by the styles
the tested app might load. Don't affect buttons in #qunit-fixture!
https://github.com/qunitjs/qunit/pull/1395
https://github.com/qunitjs/qunit/issues/1437 */
#qunit-testrunner-toolbar button,
#qunit-testresult button {
all: unset; /* best effort, modern browsers only */
font: inherit;
color: initial;
border: initial;
background-color: buttonface;
padding: 0 4px;
}
/** Fixed headers with scrollable tests */
@supports (display: flex) or (display: -webkit-box) {
@media (min-height: 500px) {
#qunit {
position: fixed;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
padding: 8px;
display: -webkit-box;
display: flex;
flex-direction: column;
}
#qunit-tests {
overflow: scroll;
}
#qunit-banner {
flex: 5px 0 0;
}
}
}
/** Header (excluding toolbar) */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #C2CCD1;
background-color: #0D3349;
font-size: 1.5em;
line-height: 1em;
font-weight: 400;
border-radius: 5px 5px 0 0;
}
#qunit-header a {
text-decoration: none;
color: inherit;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #FFF;
}
#qunit-banner {
height: 5px;
}
#qunit-filteredTest {
padding: 0.5em 1em 0.5em 1em;
color: #366097;
background-color: #F4FF77;
}
#qunit-userAgent {
padding: 0.5em 1em 0.5em 1em;
color: #FFF;
background-color: #2B81AF;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
/** Toolbar */
#qunit-testrunner-toolbar {
padding: 0.5em 1em 0.5em 1em;
color: #5E740B;
background-color: #EEE;
}
#qunit-testrunner-toolbar .clearfix {
height: 0;
clear: both;
}
#qunit-testrunner-toolbar label {
display: inline-block;
}
#qunit-testrunner-toolbar input[type=checkbox],
#qunit-testrunner-toolbar input[type=radio] {
margin: 3px;
vertical-align: -2px;
}
#qunit-testrunner-toolbar input[type=text] {
box-sizing: border-box;
height: 1.6em;
}
#qunit-testrunner-toolbar button,
#qunit-testresult button {
border-radius: .25em;
border: 1px solid #AAA;
background-color: #F8F8F8;
color: #222;
line-height: 1.6;
cursor: pointer;
}
#qunit-testrunner-toolbar button:hover,
#qunit-testresult button:hover {
border-color: #AAA;
background-color: #FFF;
color: #444;
}
#qunit-testrunner-toolbar button:active,
#qunit-testresult button:active {
border-color: #777;
background-color: #CCC;
color: #000;
}
#qunit-testrunner-toolbar button:focus,
#qunit-testresult button:focus {
border-color: #2F68DA;
/* emulate 2px border without a layout shift */
box-shadow: inset 0 0 0 1px #2F68DA
}
#qunit-testrunner-toolbar button:disabled,
#qunit-testresult button:disabled {
border-color: #CCC;
background-color: #CCC;
color: #FFF;
cursor: default;
}
#qunit-toolbar-filters {
float: right;
/* aligning right avoids overflows and inefficient use of space
around the dropdown menu on narrow viewports */
text-align: right;
}
.qunit-url-config,
.qunit-filter,
#qunit-modulefilter {
display: inline-block;
line-height: 2.1em;
text-align: left;
}
.qunit-filter,
#qunit-modulefilter {
position: relative;
margin-left: 1em;
}
.qunit-url-config label {
margin-right: 0.5em;
}
#qunit-modulefilter-search {
box-sizing: border-box;
min-width: 400px;
min-width: min(400px, 80vw);
}
#qunit-modulefilter-search-container {
position: relative;
}
#qunit-modulefilter-search-container:after {
position: absolute;
right: 0.3em;
bottom: 0;
line-height: 100%;
content: "\25bc";
color: black;
}
#qunit-modulefilter-dropdown {
/* align with #qunit-modulefilter-search */
box-sizing: border-box;
min-width: 400px;
min-width: min(400px, 80vw);
max-width: 80vw;
position: absolute;
right: 0;
top: 100%;
margin-top: 2px;
/* ensure that when on a narrow viewports and having only one result,
that #qunit-modulefilter-actions fall outside the dropdown rectangle. */
min-height: 3em;
border: 1px solid #AAA;
border-top-color: transparent;
border-radius: 0 0 .25em .25em;
color: #0D3349;
background-color: #F5F5F5;
z-index: 99;
}
#qunit-modulefilter-actions {
display: block;
overflow: auto;
/* align with #qunit-modulefilter-dropdown-list */
font: smaller/1.5em sans-serif;
}
@media (min-width: 350px) {
#qunit-modulefilter-actions {
position: absolute;
right: 0;
}
}
#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * {
box-sizing: border-box;
max-height: 2.8em;
display: block;
padding: 0.4em;
}
#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button {
float: right;
margin: 0.25em;
}
#qunit-modulefilter-dropdown-list {
margin: 0;
padding: 0;
font: smaller/1.5em sans-serif;
}
#qunit-modulefilter-dropdown-list li {
list-style: none;
}
#qunit-modulefilter-dropdown-list .clickable {
display: block;
padding: 0.25em 0.50em 0.25em 0.15em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#qunit-modulefilter-dropdown-list .clickable.checked {
font-weight: bold;
background-color: #E2F0F7;
color: #0D3349;
}
#qunit-modulefilter-dropdown .clickable:hover {
background-color: #FFF;
color: #444;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 1em 0.4em 1em;
border-bottom: 1px solid #FFF;
list-style-position: inside;
}
#qunit-tests > li {
display: none;
}
#qunit-tests li.running,
#qunit-tests li.pass,
#qunit-tests li.fail,
#qunit-tests li.skipped,
#qunit-tests li.aborted {
display: list-item;
}
#qunit-tests.hidepass {
position: relative;
}
#qunit-tests.hidepass li.running,
#qunit-tests.hidepass li.pass:not(.todo) {
visibility: hidden;
position: absolute;
width: 0;
height: 0;
padding: 0;
border: 0;
margin: 0;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li.skipped strong {
cursor: default;
}
#qunit-tests li a {
padding: 0.5em;
color: inherit;
text-decoration: underline;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #0D3349;
}
#qunit-tests li .runtime {
float: right;
font-size: smaller;
}
.qunit-assert-list {
margin-top: 0.5em;
padding: 0.5em;
background-color: #FFF;
border-radius: 5px;
}
.qunit-source {
margin: 0.6em 0 0.3em;
}
.qunit-collapsed {
display: none;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: 0.2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 0.5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
color: #374E0C;
background-color: #E0F2BE;
text-decoration: none;
}
#qunit-tests ins {
color: #500;
background-color: #FFCACA;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: #0D3349; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
padding: 5px;
background-color: #FFF;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests .pass {
color: #2F68DA;
background-color: #E2F0F7;
}
#qunit-tests .pass .test-name {
color: #366097;
}
#qunit-tests li li.pass {
color: #3C510C;
background-color: #FFF;
border-left: 10px solid #C6E746;
}
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests .fail {
color: #000;
background-color: #EE5757;
}
#qunit-tests li li.fail {
color: #710909;
background-color: #FFF;
border-left: 10px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 5px 5px;
}
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: #008000; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/*** Aborted tests */
#qunit-tests .aborted { color: #000; background-color: orange; }
/*** Skipped tests */
#qunit-tests .skipped {
background-color: #EBECE9;
}
#qunit-tests .qunit-todo-label,
#qunit-tests .qunit-skipped-label {
background-color: #F4FF77;
display: inline-block;
font-style: normal;
color: #366097;
line-height: 1.8em;
padding: 0 0.5em;
margin: -0.4em 0.4em -0.4em 0;
}
#qunit-tests .qunit-todo-label {
background-color: #EEE;
}
/** Result */
#qunit-testresult {
color: #366097;
background-color: #E2F0F7;
border-bottom: 1px solid #FFF;
}
#qunit-testresult a {
color: #2F68DA;
}
#qunit-testresult .clearfix {
height: 0;
clear: both;
}
#qunit-testresult .module-name {
font-weight: 700;
}
#qunit-testresult-display {
padding: 0.5em 1em 0.5em 1em;
width: 85%;
float:left;
}
#qunit-testresult-controls {
padding: 0.5em 1em 0.5em 1em;
width: 10%;
float:left;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
width: 1000px;
height: 1000px;
}

File diff suppressed because it is too large Load Diff

@ -4,16 +4,13 @@
require "rails"
require "action_controller/railtie"
require "action_view/railtie"
require "blade"
require "json"
module UJS
class Server < Rails::Application
routes.append do
get "/rails-ujs.js" => Blade::Assets.environment
get "/" => "tests#index"
match "/echo" => "tests#echo", via: :all
get "/error" => proc { |env| [403, {}, []] }
get "/error" => proc { |env| [403, { "content-type" => "text/plain" }, []] }
end
config.enable_reloading = true
@ -76,7 +73,7 @@ def echo
html = <<-HTML
<script nonce="#{request.content_security_policy_nonce}">
if (window.top && window.top !== window)
window.top.jQuery.event.trigger('iframe:loaded', #{payload})
window.parent.jQuery.event.trigger('iframe:loaded', #{payload})
</script>
<p>You shouldn't be seeing this. <a href="#{request.env['HTTP_REFERER']}">Go back</a></p>
HTML
@ -88,5 +85,4 @@ def echo
end
end
Blade.initialize!
UJS::Server.initialize!

@ -0,0 +1,17 @@
import "../public/test/attach-bindings"
import Rails from '../../../app/javascript/rails-ujs/index'
import "../public/test/settings"
import "../public/test/data-confirm"
import "../public/test/data-remote"
import "../public/test/data-disable"
import "../public/test/data-disable-with"
import "../public/test/call-remote"
import "../public/test/call-remote-callbacks"
import "../public/test/data-method"
import "../public/test/override"
import "../public/test/csrf-refresh"
import "../public/test/csrf-token"
import "../public/test/call-ajax"

@ -1,26 +0,0 @@
<!DOCTYPE html>
<html id="html">
<head>
<title><%= @title %></title>
<%= csp_meta_tag %>
<link href="/vendor/qunit.css" media="screen" rel="stylesheet" type="text/css" media="screen, projection" />
<script src="/vendor/jquery-2.2.0.js"></script>
<%= javascript_tag nonce: true do %>
// This is for test in override.js.
// Must go before rails-ujs.
document.addEventListener('rails:attachBindings', function() {
window.Rails.linkClickSelector += ', a[data-custom-remote-link]';
// Hijacks link click before ujs binds any handlers
// This is only used for ctrl-clicking test on remote links
window.Rails.delegate(document, '#qunit-fixture a', 'click', function(e) {
e.preventDefault();
});
});
<% end %>
<%= javascript_include_tag "/rails-ujs.js" %>
</head>
<body id="body">
<%= yield %>
</body>
</html>

@ -1,11 +0,0 @@
<% @title = "rails-ujs test" %>
<%= test_to 'data-confirm', 'data-remote', 'data-disable', 'data-disable-with', 'call-remote', 'call-remote-callbacks', 'data-method', 'override', 'csrf-refresh', 'csrf-token', 'call-ajax' %>
<h1 id="qunit-header"><%= @title %></h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture"></div>

@ -1,33 +0,0 @@
# frozen_string_literal: true
require "webdrivers"
require_relative "test_run"
driver = if ARGV[1]
capability = ::Selenium::WebDriver::Remote::Capabilities.chrome
::Selenium::WebDriver.for(:remote, url: ARGV[1], capabilities: [capability])
else
driver_options = Selenium::WebDriver::Chrome::Options.new
driver_options.add_argument("--headless")
driver_options.add_argument("--disable-gpu")
driver_options.add_argument("--no-sandbox")
::Selenium::WebDriver.for(:chrome, capabilities: [driver_options])
end
driver.get(ARGV[0])
result = TestRun.new(driver).tap do |run|
::Selenium::WebDriver::Wait.new(timeout: 60).until do
run.completed?
end
end.result
driver.quit
puts "Time: #{result.duration} seconds, Total: #{result.assertions[:total]}, Passed: #{result.assertions[:passed]}, Failed: #{result.assertions[:failed]}"
if result.tests[:failed] > 0
puts "Qunit output follows. Look for lines that have failures, e.g. (1, n, n) - those are your failing lines\r\n\r\n#{result.raw_output}"
end
exit(result.tests[:failed] > 0 ? 1 : 0)

@ -1,73 +0,0 @@
# frozen_string_literal: true
# The MIT License (MIT)
#
# Copyright (c) 2014 Silvio Montanari
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# This class based on https://github.com/smontanari/qunit-selenium, with a few tweaks to make it easier to read output.
class TestRun
TestResult = Struct.new(:tests, :assertions, :duration, :raw_output)
ID_TESTRESULT = "qunit-testresult"
ID_TESTS = "qunit-tests"
def initialize(driver)
@qunit_testresult = driver[ID_TESTRESULT]
@qunit_tests = driver[ID_TESTS]
end
def completed?
@qunit_testresult.text.include?("tests completed") &&
@qunit_testresult.find_elements(:class, "total").any?
end
def result
assertions = { total: total_assertions, passed: passed_assertions, failed: failed_assertions }
tests = { total: total_tests, passed: pass_tests, failed: fail_tests }
TestResult.new(tests, assertions, duration, raw_output)
end
private
def raw_output
@qunit_tests.text
end
def duration
match = /tests completed in (?<milliseconds>\d+) milliseconds/.match @qunit_testresult.text
match[:milliseconds].to_i / 1000
end
%w(total passed failed).each do |result|
define_method("#{result}_assertions".to_sym) do
@qunit_testresult.find_elements(:class, result).first.text.to_i
end
end
def total_tests
@qunit_tests.find_elements(:css, "##{ID_TESTS} > *").count
end
%w(pass fail).each do |result|
define_method("#{result}_tests".to_sym) do
@qunit_tests.find_elements(:css, "##{ID_TESTS} > .#{result}").count
end
end
end

8738
yarn.lock

File diff suppressed because it is too large Load Diff