Skip to content

Commit

Permalink
write tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Jan 29, 2025
1 parent c0675aa commit 1bb89a6
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 48 deletions.
26 changes: 16 additions & 10 deletions demos/_unit-test/element-remove-observer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use Atk4\Ui\App;
use Atk4\Ui\Button;
use Atk4\Ui\Form\Control\Line;
use Atk4\Ui\Js\JsExpression;
use Atk4\Ui\View;

Expand All @@ -16,14 +15,23 @@
$app->layout->js(true, new JsExpression('atk.i = 0'));
$reloadUrlArgs = ['i' => new JsExpression('++atk.i')];

$addBoxFx = static function ($owner, string $name): View {
$log = View::addTo($app, ['element' => 'input', 'name' => 'log']);
$log->setStyle('width', '100%');

$addBoxFx = static function ($owner, string $name) use ($log) {
$box = View::addTo($owner);
$box->setStyle('padding', '5px');
$box->setStyle('margin', '5px');
$box->setStyle('border', '1px solid black');

$box->set($name . (int) $box->getApp()->tryGetRequestQueryParam('i'));

$box->js(true, new JsExpression(<<<'JS'
var target = document.querySelector([target]);
var logInput = document.querySelector([log]);
logInput.value = (logInput.value + ' ').trimStart() + target.lastChild.textContent;
JS, ['target' => $box, 'log' => $log]));

return $box;
};

Expand All @@ -33,19 +41,17 @@
$viewU = $addBoxFx($viewI, 'U');
$viewV = $addBoxFx($viewI, 'V');

$log = Line::addTo($app);

$makeAddHandlerJsExpressionFx = static function (View $view) use ($log) {
$makeAddHandlerJsFx = static function (View $view) use ($log) {
return new JsExpression(<<<'JS'
const target = document.querySelector([target]);
const logInput = document.querySelector([log] + ' > input');
atk.elementRemoveObserver.addHandler(target, () => logInput.value = (logInput.value + ' ').trimStart() + target.lastChild.textContent);
const logInput = document.querySelector([log]);
atk.elementRemoveObserver.addHandler(target, () => logInput.value = (logInput.value + ' ').trimStart() + 'h' + target.lastChild.textContent);
JS, ['target' => $view, 'log' => $log]);
};

Button::addTo($app, ['Add A handler'])->on('click', $makeAddHandlerJsExpressionFx($viewA));
Button::addTo($app, ['Add U handler'])->on('click', $makeAddHandlerJsExpressionFx($viewU));
Button::addTo($app, ['Add V handler'])->on('click', $makeAddHandlerJsExpressionFx($viewV));
Button::addTo($app, ['Add A handler'])->on('click', $makeAddHandlerJsFx($viewA));
Button::addTo($app, ['Add I handler'])->on('click', $makeAddHandlerJsFx($viewI));
Button::addTo($app, ['Add V handler'])->on('click', $makeAddHandlerJsFx($viewV));

Button::addTo($app, ['Reload I'])->on('click', $viewI->jsReload($reloadUrlArgs));
Button::addTo($app, ['Reload V'])->on('click', $viewV->jsReload($reloadUrlArgs));
41 changes: 22 additions & 19 deletions public/js/atkjs-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,13 @@ __webpack_require__.r(__webpack_exports__);

const observerByElement = new Map();
const observedChildrenByElement = new Map();
const elementByObservedChild = new Map();
const removeHandlersByElement = new Map();
function handleElementRemove(elem) {
const observedChildren = observedChildrenByElement.get(elem) ?? [];
const removeHandlers = removeHandlersByElement.get(elem) ?? [];
removeObserverIfUnused(elem, true);
removeHandlersByElement.delete(elem);
removeObserverIfUnused(elem);
for (const child of observedChildren) {
handleElementRemove(child);
}
Expand Down Expand Up @@ -171,34 +173,35 @@ function addObserverToParentElement(elem) {
observerByElement.set(parentElem, observer);
observedChildrenByElement.set(parentElem, new Set());
}
observedChildrenByElement.get(parentElem).add(elem);
if (!observedChildrenByElement.get(parentElem).has(elem)) {
observedChildrenByElement.get(parentElem).add(elem);
elementByObservedChild.set(elem, parentElem);
}
}
function removeObserverIfUnused(elem) {
let force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
let updated = false;
if (removeHandlersByElement.has(elem)) {
if (!force && removeHandlersByElement.get(elem).size !== 0) {
if (removeHandlersByElement.get(elem).size !== 0) {
return;
}
removeHandlersByElement.delete(elem);
updated = true;
}
if (observerByElement.has(elem)) {
if (!force && observedChildrenByElement.get(elem).size !== 0) {
return;
}
const observer = observerByElement.get(elem);
observer.disconnect();
observerByElement.delete(elem);
observedChildrenByElement.delete(elem);
updated = true;
return;
}
if (updated) {
const parentElem = elem.parentElement;
if (parentElem !== null) {
removeObserverIfUnused(parentElem);
}
const parentElem = elementByObservedChild.get(elem);
if (parentElem === null || !observerByElement.has(parentElem)) {
return;
}
observedChildrenByElement.get(parentElem).delete(elem);
elementByObservedChild.delete(elem);
if (observedChildrenByElement.get(parentElem).size !== 0) {
return;
}
const observer = observerByElement.get(parentElem);
observer.disconnect();
observerByElement.delete(parentElem);
observedChildrenByElement.delete(parentElem);
removeObserverIfUnused(parentElem);
}
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({
/**
Expand Down
2 changes: 1 addition & 1 deletion public/js/atkjs-ui.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/js/atkjs-ui.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/js/atkjs-ui.min.js.map

Large diffs are not rendered by default.

49 changes: 33 additions & 16 deletions tests-behat/element-remove-observer.feature
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,38 @@ Feature: Element remove observer

Scenario:
Given I am on "_unit-test/element-remove-observer.php"
When I press button "First"
Then I should see "TestName"
When I press Modal button "Save"
Then Toast display should contain text "Save"
Then I should not see "TestName"
Then I check if input value for "#log" match text "A0 I0 U0 V0 J0"
When I press button "Reload V"
Then I check if input value for "#log" match text "A0 I0 U0 V0 J0 V1"
When I press button "Reload V"
Then I check if input value for "#log" match text "A0 I0 U0 V0 J0 V1 V2"
When I press button "Reload I"
Then I check if input value for "#log" match text "A0 I0 U0 V0 J0 V1 V2 I3 U3 V3"
When I fill field using "#log" with ""
When I press button "Add A handler"
When I press button "Reload V"
Then I check if input value for "#log" match text "V4"
When I press button "Add V handler"
When I press button "Reload V"
Then I check if input value for "#log" match text "V4 V5 hV4"
When I press button "Reload V"
Then I check if input value for "#log" match text "V4 V5 hV4 V6"

Scenario:
Scenario: multiple handlers
When I fill field using "#log" with ""
When I press button "Add V handler"
When I press button "Add V handler"
When I press button "Reload V"
Then I check if input value for "#log" match text "V7 hV6 hV6"

Scenario: handler for child must be called first
Given I am on "_unit-test/element-remove-observer.php"
When I press button "Add I handler"
When I press button "Add V handler"
When I press button "Reload I"
Then I check if input value for "#log" match text "A0 I0 U0 V0 J0 I1 U1 V1 hV0 hI0"
Given I am on "_unit-test/element-remove-observer.php"
When I press button "Load1"
Then I should see "Loader-1"
When I press button "Load2"
Then I should see "Loader-2"
Then I should see "Loader-3"
When I click paginator page "2"
When I click using selector "(//div.ui.atk-test.button)[1]"
Then Modal is open with text "Edit Country"
When I press Modal button "Save"
Then Toast display should contain text 'Country action "edit" with "Andorra" entity was executed.'
When I press button "Add V handler"
When I press button "Add I handler"
When I press button "Reload I"
Then I check if input value for "#log" match text "A0 I0 U0 V0 J0 I1 U1 V1 hV0 hI0"

0 comments on commit 1bb89a6

Please sign in to comment.