mirror of https://github.com/rails/rails
Merge pull request #46206 from lsylvester/karma-ujs
use Karma as the test runner for the UJS tests
This commit is contained in:
commit
18b2964dea
1
Gemfile
1
Gemfile
|
@ -112,7 +112,6 @@ end
|
|||
|
||||
# Action View
|
||||
group :view do
|
||||
gem "blade", require: false, platforms: [:ruby]
|
||||
gem "sprockets-export", require: false
|
||||
end
|
||||
|
||||
|
|
38
Gemfile.lock
38
Gemfile.lock
|
@ -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.38)
|
||||
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,9 @@ namespace :test do
|
|||
|
||||
sleep 0.2
|
||||
end
|
||||
|
||||
system(*runner_command)
|
||||
# Decode the obfuscate environment variables
|
||||
decoded_environment_variables = Hash[*Base64.decode64(ENV.fetch("ENCODED", "")).split(/[ =]/)]
|
||||
system(decoded_environment_variables, "npm", "test")
|
||||
status = $?.exitstatus
|
||||
ensure
|
||||
Process.kill("KILL", -pid) if pid
|
||||
|
@ -101,7 +94,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
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
// Karma configuration for running the UJS tests
|
||||
|
||||
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,18 @@
|
|||
// Rollup configuration for compiling the UJS tests
|
||||
|
||||
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()
|
||||
]
|
||||
}
|
|
@ -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,13 @@ QUnit.test('a[data-remote][data-disable-with] re-enables when `ajax:error` event
|
|||
.bindNative('ajax:beforeSend', function() {
|
||||
assert.disabledState(link, 'clicking...')
|
||||
})
|
||||
.triggerNative('click')
|
||||
|
||||
.bindNative('ajax:complete', function() {
|
||||
setTimeout(function() {
|
||||
assert.enabledState(link, 'Click me')
|
||||
done()
|
||||
}, 30)
|
||||
})
|
||||
.triggerNative('click')
|
||||
})
|
||||
|
||||
QUnit.test('form[data-remote] input|button|textarea[data-disable-with] does not disable when `ajax:beforeSend` event is cancelled', function(assert) {
|
||||
|
@ -454,10 +457,11 @@ QUnit.test('button[data-remote][data-disable-with] re-enables when `ajax:error`
|
|||
.bindNative('ajax:send', function() {
|
||||
assert.disabledState(button, 'clicking...')
|
||||
})
|
||||
.triggerNative('click')
|
||||
|
||||
.bindNative('ajax:complete', function() {
|
||||
setTimeout(function() {
|
||||
assert.enabledState(button, 'Click me')
|
||||
done()
|
||||
}, 30)
|
||||
})
|
||||
.triggerNative('click')
|
||||
})
|
||||
|
|
|
@ -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')
|
||||
})
|
||||
.triggerNative('click')
|
||||
|
||||
.bindNative('ajax:complete', function() {
|
||||
setTimeout(function() {
|
||||
assert.enabledState(link, 'Click me')
|
||||
done()
|
||||
}, 30)
|
||||
})
|
||||
.triggerNative('click')
|
||||
})
|
||||
|
||||
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')
|
||||
})
|
||||
.triggerNative('click')
|
||||
|
||||
.bindNative('ajax:complete', function() {
|
||||
setTimeout(function() {
|
||||
assert.enabledState(button, 'Click me')
|
||||
done()
|
||||
}, 30)
|
||||
})
|
||||
.triggerNative('click')
|
||||
})
|
||||
|
||||
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 "rack"
|
|||
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 @@ class TestsController < ActionController::Base
|
|||
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 @@ class TestsController < ActionController::Base
|
|||
end
|
||||
end
|
||||
|
||||
Blade.initialize!
|
||||
UJS::Server.initialize!
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// Must go before rails-ujs.
|
||||
document.addEventListener('rails:attachBindings', function() {
|
||||
// This is for test in override.js.
|
||||
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();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
import "./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
|
Loading…
Reference in New Issue