Skip to content
Snippets Groups Projects
Commit 778bdaa6 authored by Ben Mullins's avatar Ben Mullins
Browse files

Issue #3331835 by Spokje, znerol, smustgrave, murilohp, bircher: Some...

Issue #3331835 by Spokje, znerol, smustgrave, murilohp, bircher: Some JavaScript errors are not recorded during test runs
parent d6e9f4d3
No related branches found
No related tags found
28 merge requests!54479.5.x SF update,!5014Issue #3071143: Table Render Array Example Is Incorrect,!4868Issue #1428520: Improve menu parent link selection,!4289Issue #1344552 by marcingy, Niklas Fiekas, Ravi.J, aleevas, Eduardo Morales...,!4114Issue #2707291: Disable body-level scrolling when a dialog is open as a modal,!4100Issue #3249600: Add support for PHP 8.1 Enums as allowed values for list_* data types,!3630Issue #2815301 by Chi, DanielVeza, kostyashupenko, smustgrave: Allow to create...,!3600Issue #3344629: Passing null to parameter #1 ($haystack) of type string is deprecated,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2334Issue #3228209: Add hasRole() method to AccountInterface,!2062Issue #3246454: Add weekly granularity to views date sort,!1591Issue #3199697: Add JSON:API Translation experimental module,!1484Exposed filters get values from URL when Ajax is on,!1255Issue #3238922: Refactor (if feasible) uses of the jQuery serialize function to use vanillaJS,!1162Issue #3100350: Unable to save '/' root path alias,!1105Issue #3025039: New non translatable field on translatable content throws error,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!925Issue #2339235: Remove taxonomy hard dependency on node module,!877Issue #2708101: Default value for link text is not saved,!872Draft: Issue #3221319: Race condition when creating menu links and editing content deletes menu links,!844Resolve #3036010 "Updaters",!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493,!485Sets the autocomplete attribute for username/password input field on login form.,!30Issue #3182188: Updates composer usage to point at ./vendor/bin/composer
Showing
with 106 additions and 30 deletions
......@@ -110,6 +110,7 @@ internal.admin:
- filter/drupal.filter.admin
- core/jquery
- core/once
- core/drupal.announce
internal.admin.specialCharacters:
css:
......
......@@ -652,6 +652,10 @@
'#filter-format-edit-form, #filter-format-add-form',
);
if (!form) {
return;
}
// Get the current stored UI state as an object.
const currentStates = form.hasAttribute('data-drupal-ui-state')
? JSON.parse(form.getAttribute('data-drupal-ui-state'))
......@@ -714,7 +718,9 @@
if (activeTab) {
setTimeout(() => {
const activeTabLink = document.querySelector(activeTab);
activeTabLink.click();
if (activeTabLink) {
activeTabLink.click();
}
// Only change focus on the plugin-settings-wrapper element.
if (id !== 'plugin-settings-wrapper') {
......
......@@ -114,12 +114,14 @@
});
// When the allowed tags list is manually changed, update userTags.
that.$allowedHTMLFormItem.on('change.updateUserTags', function () {
that.userTags = difference(
Object.values(that._parseSetting(this.value)),
Object.values(that.autoTags),
);
});
if (that.autoTags) {
that.$allowedHTMLFormItem.on('change.updateUserTags', function () {
that.userTags = difference(
Object.values(that._parseSetting(this.value)),
Object.values(that.autoTags),
);
});
}
});
},
......
/**
* @file
* Test script for JavaScript errors thrown in async context.
*/
(function () {
window.addEventListener('DOMContentLoaded', () => {
setTimeout(() => {
throw new Error('An error thrown in async context.');
});
});
})();
......@@ -4,3 +4,9 @@ errors_test:
js/js_errors_test.js: {}
dependencies:
- core/drupal
errors_async_test:
version: VERSION
js:
js/js_errors_async_test.js: {}
dependencies:
- core/drupal
......@@ -5,3 +5,10 @@ js_errors_test.errors:
_title: 'JsErrorsTest'
requirements:
_access: 'TRUE'
js_errors_async_test.errors:
path: '/js_errors_async_test'
defaults:
_controller: '\Drupal\js_errors_test\Controller\JsErrorsTestController::jsErrorsAsyncTest'
_title: 'JsErrorsAsyncTest'
requirements:
_access: 'TRUE'
......@@ -19,4 +19,16 @@ public function jsErrorsTest(): array {
];
}
/**
* Renders page that has js_errors_test/errors_async_test library attached.
*
* @return string[][]
* Render array.
*/
public function jsErrorsAsyncTest(): array {
return [
'#attached' => ['library' => ['js_errors_test/errors_async_test']],
];
}
}
......@@ -2,7 +2,7 @@
* @file
* Support code for testing JavaScript error handling in functional tests.
*/
(function (Drupal) {
(function () {
if (typeof console !== 'undefined' && console.warn) {
const originalWarnFunction = console.warn;
console.warn = (warning) => {
......@@ -17,19 +17,17 @@
);
originalWarnFunction(warning);
};
const originalThrowFunction = Drupal.throwError;
Drupal.throwError = (error) => {
const errors = JSON.parse(
sessionStorage.getItem('js_testing_log_test.errors') ||
JSON.stringify([]),
);
errors.push(error.stack);
sessionStorage.setItem(
'js_testing_log_test.errors',
JSON.stringify(errors),
);
originalThrowFunction(error);
};
}
})(Drupal);
window.addEventListener('error', (evt) => {
const errors = JSON.parse(
sessionStorage.getItem('js_testing_log_test.errors') ||
JSON.stringify([]),
);
errors.push(evt.error.stack);
sessionStorage.setItem(
'js_testing_log_test.errors',
JSON.stringify(errors),
);
});
})();
......@@ -2,5 +2,3 @@ deprecation_log:
version: VERSION
js:
js/js_testing_log.js: {}
dependencies:
- core/drupal
......@@ -101,6 +101,11 @@ public function testCurrentPathChange() {
* Tests that overridden CSS files are not added during lazy load.
*/
public function testLazyLoadOverriddenCSS() {
// The test_theme throws a few JavaScript errors. Since we're only
// interested in CSS for this test, we're not letting this test fail on
// those.
$this->failOnJavascriptConsoleErrors = FALSE;
// The test theme overrides js.module.css without an implementation,
// thereby removing it.
\Drupal::service('theme_installer')->install(['test_theme']);
......
......@@ -26,7 +26,6 @@ class FormValuesTest extends WebDriverTestBase {
*/
protected function setUp(): void {
parent::setUp();
$this->drupalLogin($this->drupalCreateUser(['access content']));
}
......@@ -34,7 +33,6 @@ protected function setUp(): void {
* Submits forms with select and checkbox elements via Ajax.
*/
public function testSimpleAjaxFormValue() {
$this->drupalGet('ajax_forms_test_get_form');
$session = $this->getSession();
......@@ -58,10 +56,23 @@ public function testSimpleAjaxFormValue() {
$session->getPage()->uncheckField('checkbox');
$div1 = $this->assertSession()->waitForElement('css', "div#ajax_checkbox_value:contains('unchecked')");
$this->assertNotNull($div1, 'DataCommand updates the DOM as expected when a checkbox is de-selected');
}
/**
* Tests that AJAX elements with invalid callbacks return error code 500.
*/
public function testSimpleInvalidCallbacksAjaxFormValue() {
$this->drupalGet('ajax_forms_test_get_form');
$session = $this->getSession();
// Verify that AJAX elements with invalid callbacks return error code 500.
// Ensure the test error log is empty before these tests.
$this->assertFileDoesNotExist(DRUPAL_ROOT . '/' . $this->siteDirectory . '/error.log');
// We're going to do some invalid requests. The JavaScript errors thrown
// whilst doing so are expected. Do not interpret them as a test failure.
$this->failOnJavascriptConsoleErrors = FALSE;
// We don't need to check for the X-Drupal-Ajax-Token header with these
// invalid requests.
foreach (['null', 'empty', 'nonexistent'] as $key) {
......@@ -74,10 +85,11 @@ public function testSimpleAjaxFormValue() {
// The select element is enabled as the response is received.
$this->assertSession()->waitForElement('css', "select[name=\"$element_name\"]:enabled");
// Not using File API, a potential error must trigger a PHP warning, which
// should be logged in the error.log.
$this->assertFileExists(DRUPAL_ROOT . '/' . $this->siteDirectory . '/error.log');
$this->assertStringContainsString('"The specified #ajax callback is empty or not callable."', file_get_contents(DRUPAL_ROOT . '/' . $this->siteDirectory . '/error.log'));
// The exceptions are expected. Do not interpret them as a test failure.
// Not using File API; a potential error must trigger a PHP warning.
// Remove error.log, so we have a clean slate for the next request.
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
}
// We need to reload the page to kill any unfinished AJAX calls before
......
......@@ -39,6 +39,24 @@ public function testJavascriptErrors(): void {
$this->failOnJavaScriptErrors();
}
/**
* Tests that JavaScript console errors will result in a test failure
* during asynchronous calls.
*/
public function testJavascriptErrorsAsync(): void {
// Visit page that will throw a JavaScript console error in async context.
$this->drupalGet('js_errors_async_test');
// Ensure that errors from previous page loads will be detected.
$this->drupalGet('user');
$this->expectException(AssertionFailedError::class);
$this->expectExceptionMessageMatches('/^Error: An error thrown in async context./');
// Manually call the method under test, as it cannot be caught by PHPUnit
// when triggered from assertPostConditions().
$this->failOnJavaScriptErrors();
}
/**
* Clear the JavaScript error log to prevent this test failing for real.
*
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment