Patch #42521 » 0002-Add-JavaScript-test-environment-And-dialog-controlle.patch
| Gemfile | ||
|---|---|---|
| 94 | 94 | |
| 95 | 95 |
group :development, :test do |
| 96 | 96 |
gem 'debug' |
| 97 |
gem 'importmap_mocha-rails' |
|
| 97 | 98 |
end |
| 98 | 99 | |
| 99 | 100 |
group :development do |
| config/application.rb | ||
|---|---|---|
| 109 | 109 |
if File.exist?(File.join(File.dirname(__FILE__), 'additional_environment.rb')) |
| 110 | 110 |
instance_eval File.read(File.join(File.dirname(__FILE__), 'additional_environment.rb')) |
| 111 | 111 |
end |
| 112 | ||
| 113 |
if Rails.env.local? |
|
| 114 |
config.importmap_mocha_style = 'tdd' |
|
| 115 |
end |
|
| 112 | 116 |
end |
| 113 | 117 |
end |
| test/javascripts/controllers/dialog_controller.test.js | ||
|---|---|---|
| 1 |
/** |
|
| 2 |
* Redmine - project management software |
|
| 3 |
* Copyright (C) 2006- Jean-Philippe Lang |
|
| 4 |
* This code is released under the GNU General Public License. |
|
| 5 |
*/ |
|
| 6 | ||
| 7 |
import { Application } from "@hotwired/stimulus"
|
|
| 8 |
import DialogController from 'controllers/dialog_controller' |
|
| 9 |
import { assert } from 'chai'
|
|
| 10 | ||
| 11 |
const html = ` |
|
| 12 |
<div data-controller="dialog" id="dialog"> |
|
| 13 |
<div data-action="pointerdown->dialog#start pointerup->dialog#end pointercancel->dialog#end pointermove->dialog#move touchstart->dialog#noop dragstart->dialog#noop" data-dialog-target="handler" id="handler">Drag Here</div> |
|
| 14 |
<div id="hide">Close</div> |
|
| 15 |
</div> |
|
| 16 |
<div id="wrapper"></div> |
|
| 17 |
<div id="modal-backdrop"></div>` |
|
| 18 | ||
| 19 |
suite('dialog controller', () => {
|
|
| 20 | ||
| 21 |
let container; |
|
| 22 |
let controller; |
|
| 23 | ||
| 24 |
setup(async () => {
|
|
| 25 |
container = document.getElementById('container')
|
|
| 26 |
const app = Application.start(container); |
|
| 27 |
await app.register('dialog', DialogController);
|
|
| 28 | ||
| 29 |
container.insertAdjacentHTML('afterbegin', html)
|
|
| 30 |
}); |
|
| 31 | ||
| 32 |
teardown(() => {
|
|
| 33 |
const clone = container.cloneNode(false); |
|
| 34 |
container.parentNode.replaceChild(clone, container); |
|
| 35 |
}); |
|
| 36 | ||
| 37 |
test('connect lifecycle method', () => {
|
|
| 38 |
const dialog = document.getElementById('dialog');
|
|
| 39 |
controller = dialog.dialog_controller; |
|
| 40 |
assert.isNotNull(controller); |
|
| 41 |
assert.isNull(controller.dragging); |
|
| 42 |
assert.isNull(controller.backdrop); |
|
| 43 |
}); |
|
| 44 | ||
| 45 |
test('start dragging', async () => {
|
|
| 46 |
const handler = document.getElementById('handler');
|
|
| 47 |
const dialog = document.getElementById('dialog');
|
|
| 48 |
controller = dialog.dialog_controller; |
|
| 49 | ||
| 50 |
const event = new PointerEvent('pointerdown', { button: 0, clientX: 100, clientY: 100 });
|
|
| 51 |
await handler.dispatchEvent(event); |
|
| 52 | ||
| 53 |
assert.isNotNull(controller.dragging); |
|
| 54 |
assert.isDefined(controller.dragging.dx); |
|
| 55 |
assert.isDefined(controller.dragging.dy); |
|
| 56 |
assert.isTrue(handler.classList.contains("dragging"));
|
|
| 57 |
}) |
|
| 58 | ||
| 59 |
test('move dialog', async () => {
|
|
| 60 |
const handler = document.getElementById('handler');
|
|
| 61 |
const dialog = document.getElementById('dialog');
|
|
| 62 |
controller = dialog.dialog_controller; |
|
| 63 | ||
| 64 |
let event = new PointerEvent('pointerdown', { button: 0, clientX: 100, clientY: 100 });
|
|
| 65 |
await handler.dispatchEvent(event); |
|
| 66 | ||
| 67 |
event = new PointerEvent("pointermove", { clientX: 150, clientY: 150 });
|
|
| 68 |
await handler.dispatchEvent(event); |
|
| 69 | ||
| 70 |
const pos = controller.pos; |
|
| 71 |
assert.isDefined(pos.x); |
|
| 72 |
assert.isDefined(pos.y); |
|
| 73 |
assert.notEqual(pos.x, 0); |
|
| 74 |
assert.notEqual(pos.y, 0); |
|
| 75 |
}) |
|
| 76 | ||
| 77 |
test('end dragging', async () => {
|
|
| 78 |
const handler = document.getElementById('handler');
|
|
| 79 |
const dialog = document.getElementById('dialog');
|
|
| 80 |
controller = dialog.dialog_controller; |
|
| 81 | ||
| 82 |
let event = new PointerEvent('pointerdown', { button: 0, clientX: 100, clientY: 100 });
|
|
| 83 |
await handler.dispatchEvent(event); |
|
| 84 | ||
| 85 |
event = new PointerEvent("pointerup");
|
|
| 86 |
await handler.dispatchEvent(event); |
|
| 87 | ||
| 88 |
assert.isNull(controller.dragging); |
|
| 89 |
assert.isFalse(handler.classList.contains("dragging"));
|
|
| 90 |
}); |
|
| 91 | ||
| 92 |
test('show dialog', async () => {
|
|
| 93 |
const dialog = document.getElementById('dialog');
|
|
| 94 |
controller = dialog.dialog_controller; |
|
| 95 |
controller.show({ width: "500px", backdrop: "modal-backdrop" });
|
|
| 96 |
const backdrop = document.getElementById("modal-backdrop");
|
|
| 97 | ||
| 98 |
assert.equal(dialog.style.display, ""); |
|
| 99 |
assert.equal(dialog.style.width, "500px"); |
|
| 100 |
assert.isTrue(backdrop.classList.contains("modal-backdrop-open"));
|
|
| 101 |
}) |
|
| 102 | ||
| 103 |
test('hide dialog', () => {
|
|
| 104 |
const dialog = document.getElementById('dialog');
|
|
| 105 |
controller = dialog.dialog_controller; |
|
| 106 |
controller.show({ backdrop: "modal-backdrop" });
|
|
| 107 |
controller.hide(); |
|
| 108 |
const backdrop = document.getElementById("modal-backdrop");
|
|
| 109 | ||
| 110 |
assert.equal(dialog.style.display, 'none'); |
|
| 111 |
assert.isFalse(backdrop.classList.contains('modal-backdrop-open'));
|
|
| 112 |
}) |
|
| 113 |
}) |
|
| test/javascripts/controllers/dialog_dispatcher_controller.test.js | ||
|---|---|---|
| 1 |
/** |
|
| 2 |
* Redmine - project management software |
|
| 3 |
* Copyright (C) 2006- Jean-Philippe Lang |
|
| 4 |
* This code is released under the GNU General Public License. |
|
| 5 |
*/ |
|
| 6 | ||
| 7 |
import { Application } from "@hotwired/stimulus"
|
|
| 8 |
import DialogDispatcherController from 'controllers/dialog_dispatcher_controller' |
|
| 9 |
import DialogController from 'controllers/dialog_controller' |
|
| 10 |
import { assert } from 'chai'
|
|
| 11 | ||
| 12 |
const html = `<div id="dialog" data-controller="dialog" style="display:none"> |
|
| 13 |
<button data-action="dialog#hide">close</button> |
|
| 14 |
</div> |
|
| 15 |
<div id="dispatcher" data-controller="dialog-dispatcher" data-dialog-dispatcher-dialog-outlet="#dialog"> |
|
| 16 |
<button id="button" data-action="dialog-dispatcher#show" data-dialog-dispatcher-width-param="500px">Show Dialog</button> |
|
| 17 |
</div> |
|
| 18 |
<div id="wrapper"></div> |
|
| 19 |
<div id="modal-backdrop"></div>`; |
|
| 20 | ||
| 21 |
suite('DialogDispatcherController', () => {
|
|
| 22 | ||
| 23 |
let container; |
|
| 24 | ||
| 25 |
setup(async () => {
|
|
| 26 |
container = document.getElementById('container')
|
|
| 27 |
const app = Application.start(container); |
|
| 28 |
await app.register('dialog-dispatcher', DialogDispatcherController);
|
|
| 29 |
await app.register('dialog', DialogController);
|
|
| 30 | ||
| 31 |
container.insertAdjacentHTML('afterbegin', html)
|
|
| 32 |
}); |
|
| 33 | ||
| 34 |
teardown(() => {
|
|
| 35 |
const clone = container.cloneNode(false); |
|
| 36 |
container.parentNode.replaceChild(clone, container); |
|
| 37 |
}); |
|
| 38 | ||
| 39 |
test('show the dialog with the specified width', () => {
|
|
| 40 |
const button = document.getElementById('button')
|
|
| 41 |
button.click(); |
|
| 42 | ||
| 43 |
const dialog = document.getElementById('dialog')
|
|
| 44 |
assert.equal(dialog.style.width, '500px'); |
|
| 45 |
assert.equal(dialog.style.display, ''); |
|
| 46 |
}); |
|
| 47 | ||
| 48 |
test('show the dialog when show target is connected', async () => {
|
|
| 49 |
const html = `<div id="remote-dialog" data-controller="dialog" data-dialog-dispatcher-target="show" style="display:none" data-width="300px"> |
|
| 50 |
<button data-action="dialog#hide">close</button> |
|
| 51 |
</div>` |
|
| 52 | ||
| 53 |
const dispatcher = document.getElementById('dispatcher')
|
|
| 54 |
dispatcher.insertAdjacentHTML('afterbegin', html)
|
|
| 55 | ||
| 56 |
const remotedialog = await document.getElementById('remote-dialog')
|
|
| 57 |
assert.equal(remotedialog.style.width, '300px'); |
|
| 58 |
assert.equal(remotedialog.style.display, ''); |
|
| 59 |
}); |
|
| 60 | ||
| 61 |
test('hide the dialog when hide target is Connected', async () => {
|
|
| 62 |
const button = document.getElementById('button')
|
|
| 63 |
button.click(); |
|
| 64 | ||
| 65 |
const dialog = document.getElementById('dialog')
|
|
| 66 |
const dispatcher = document.getElementById('dispatcher')
|
|
| 67 |
await dispatcher.insertAdjacentHTML('afterbegin', '<template data-dialog-dispatcher-target="hide"></template>')
|
|
| 68 |
assert.equal(dialog.style.display, 'none'); |
|
| 69 | ||
| 70 |
const target = document.querySelector('[data-dialog-dispatcher-target=hide]')
|
|
| 71 |
assert.isNull(target) |
|
| 72 |
}); |
|
| 73 |
}); |
|