Skip to content
Snippets Groups Projects
Verified Commit 1b0c006a authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #3070521 by lauriii, tedbow, Wim Leers, larowlan, justafish, zrpnr,...

Issue #3070521 by lauriii, tedbow, Wim Leers, larowlan, justafish, zrpnr, alexpott, catch: Trigger deprecation notifications on JavaScript deprecations to notify developers that deprecated code is being used
parent 6ea36484
No related branches found
No related tags found
No related merge requests found
Showing
with 359 additions and 7 deletions
......@@ -50,6 +50,8 @@ drupal:
dependencies:
- core/domready
- core/drupalSettings
drupalSettings:
suppressDeprecationErrors: true
drupalSettings:
version: VERSION
......
......@@ -42,7 +42,7 @@ window.Drupal = { behaviors: {}, locale: {} };
// JavaScript should be made compatible with libraries other than jQuery by
// wrapping it in an anonymous closure.
(function(Drupal, drupalSettings, drupalTranslations) {
(function(Drupal, drupalSettings, drupalTranslations, console, Proxy, Reflect) {
/**
* Helper to rethrow errors asynchronously.
*
......@@ -541,6 +541,61 @@ window.Drupal = { behaviors: {}, locale: {} };
return window.encodeURIComponent(item).replace(/%2F/g, '/');
};
/**
* Triggers deprecation error.
*
* Deprecation errors are only triggered if deprecation errors haven't
* been suppressed.
*
* @param {Object} deprecation
* The deprecation options.
* @param {string} deprecation.message
* The deprecation message.
*
* @see https://www.drupal.org/core/deprecation#javascript
*/
Drupal.deprecationError = ({ message }) => {
if (
drupalSettings.suppressDeprecationErrors === false &&
typeof console !== 'undefined' &&
console.warn
) {
console.warn(`[Deprecation] ${message}`);
}
};
/**
* Triggers deprecation error when object property is being used.
*
* @param {Object} deprecation
* The deprecation options.
* @param {Object} deprecation.target
* The targeted object.
* @param {string} deprecation.deprecatedProperty
* A key of the deprecated property.
* @param {string} deprecation.message
* The deprecation message.
* @returns {Object}
*
* @see https://www.drupal.org/core/deprecation#javascript
*/
Drupal.deprecatedProperty = ({ target, deprecatedProperty, message }) => {
// Proxy and Reflect are not supported by all browsers. Unsupported browsers
// are ignored since this is a development feature.
if (!Proxy || !Reflect) {
return target;
}
return new Proxy(target, {
get: (target, key, ...rest) => {
if (key === deprecatedProperty) {
Drupal.deprecationError({ message });
}
return Reflect.get(target, key, ...rest);
},
});
};
/**
* Generates the themed representation of a Drupal object.
*
......@@ -583,4 +638,11 @@ window.Drupal = { behaviors: {}, locale: {} };
Drupal.theme.placeholder = function(str) {
return `<em class="placeholder">${Drupal.checkPlain(str)}</em>`;
};
})(Drupal, window.drupalSettings, window.drupalTranslations);
})(
Drupal,
window.drupalSettings,
window.drupalTranslations,
window.console,
window.Proxy,
window.Reflect,
);
......@@ -7,7 +7,7 @@
window.Drupal = { behaviors: {}, locale: {} };
(function (Drupal, drupalSettings, drupalTranslations) {
(function (Drupal, drupalSettings, drupalTranslations, console, Proxy, Reflect) {
Drupal.throwError = function (error) {
setTimeout(function () {
throw error;
......@@ -173,12 +173,43 @@ window.Drupal = { behaviors: {}, locale: {} };
return window.encodeURIComponent(item).replace(/%2F/g, '/');
};
Drupal.deprecationError = function (_ref) {
var message = _ref.message;
if (drupalSettings.suppressDeprecationErrors === false && typeof console !== 'undefined' && console.warn) {
console.warn('[Deprecation] ' + message);
}
};
Drupal.deprecatedProperty = function (_ref2) {
var target = _ref2.target,
deprecatedProperty = _ref2.deprecatedProperty,
message = _ref2.message;
if (!Proxy || !Reflect) {
return target;
}
return new Proxy(target, {
get: function get(target, key) {
for (var _len = arguments.length, rest = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
rest[_key - 2] = arguments[_key];
}
if (key === deprecatedProperty) {
Drupal.deprecationError({ message: message });
}
return Reflect.get.apply(Reflect, [target, key].concat(rest));
}
});
};
Drupal.theme = function (func) {
if (func in Drupal.theme) {
var _Drupal$theme;
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
return (_Drupal$theme = Drupal.theme)[func].apply(_Drupal$theme, args);
......@@ -188,4 +219,4 @@ window.Drupal = { behaviors: {}, locale: {} };
Drupal.theme.placeholder = function (str) {
return '<em class="placeholder">' + Drupal.checkPlain(str) + '</em>';
};
})(Drupal, window.drupalSettings, window.drupalTranslations);
\ No newline at end of file
})(Drupal, window.drupalSettings, window.drupalTranslations, window.console, window.Proxy, window.Reflect);
\ No newline at end of file
/**
* @file
* Testing tools for deprecating JavaScript functions and class properties.
*/
(function() {
if (typeof console !== 'undefined' && console.warn) {
const originalWarnFunction = console.warn;
console.warn = warning => {
const warnings = JSON.parse(
sessionStorage.getItem('js_deprecation_log_test.warnings') ||
JSON.stringify([]),
);
warnings.push(warning);
sessionStorage.setItem(
'js_deprecation_log_test.warnings',
JSON.stringify(warnings),
);
originalWarnFunction(warning);
};
}
})();
/**
* DO NOT EDIT THIS FILE.
* See the following change record for more information,
* https://www.drupal.org/node/2815083
* @preserve
**/
(function () {
if (typeof console !== 'undefined' && console.warn) {
var originalWarnFunction = console.warn;
console.warn = function (warning) {
var warnings = JSON.parse(sessionStorage.getItem('js_deprecation_log_test.warnings') || JSON.stringify([]));
warnings.push(warning);
sessionStorage.setItem('js_deprecation_log_test.warnings', JSON.stringify(warnings));
originalWarnFunction(warning);
};
}
})();
\ No newline at end of file
name: 'JS Deprecation log test'
description: 'Stores all JS deprecation calls to allow JS tests to determine if they have been called.'
type: module
package: Testing
version: VERSION
core: 8.x
deprecation_log:
version: VERSION
js:
js/js_deprecation_log.js: { weight: -100 }
dependencies:
- core/drupal
<?php
/**
* @file
* Helper module for the JavaScript deprecation tests.
*/
/**
* Implements hook_page_attachments().
*/
function js_deprecation_log_test_page_attachments(array &$attachments) {
// Unconditionally attach an asset to the page.
$attachments['#attached']['library'][] = 'js_deprecation_log_test/deprecation_log';
}
/**
* Implements hook_js_settings_alter().
*/
function js_deprecation_log_test_js_settings_alter(&$settings) {
$settings['suppressDeprecationErrors'] = FALSE;
}
/**
* @file
* Testing tools for deprecating JavaScript functions and class properties.
*/
(function({ deprecationError, deprecatedProperty, behaviors }) {
const deprecatedFunction = () => {
deprecationError({
message: 'This function is deprecated for testing purposes.',
});
};
const objectWithDeprecatedProperty = deprecatedProperty({
target: { deprecatedProperty: 'Kitten' },
deprecatedProperty: 'deprecatedProperty',
message: 'This property is deprecated for testing purposes.',
});
behaviors.testDeprecations = {
attach: () => {
deprecatedFunction();
const deprecatedProperty =
objectWithDeprecatedProperty.deprecatedProperty;
},
};
})(Drupal);
/**
* DO NOT EDIT THIS FILE.
* See the following change record for more information,
* https://www.drupal.org/node/2815083
* @preserve
**/
(function (_ref) {
var deprecationError = _ref.deprecationError,
deprecatedProperty = _ref.deprecatedProperty,
behaviors = _ref.behaviors;
var deprecatedFunction = function deprecatedFunction() {
deprecationError({
message: 'This function is deprecated for testing purposes.'
});
};
var objectWithDeprecatedProperty = deprecatedProperty({
target: { deprecatedProperty: 'Kitten' },
deprecatedProperty: 'deprecatedProperty',
message: 'This property is deprecated for testing purposes.'
});
behaviors.testDeprecations = {
attach: function attach() {
deprecatedFunction();
var deprecatedProperty = objectWithDeprecatedProperty.deprecatedProperty;
}
};
})(Drupal);
\ No newline at end of file
name: 'JS Deprecation test'
description: 'Provides deprecated code that can be used for tests'
type: module
package: Testing
version: VERSION
core: 8.x
deprecation_test:
version: VERSION
js:
js/js_deprecation_test.js: {}
dependencies:
- core/drupal
<?php
/**
* @file
* Helper module for the JavaScript deprecation tests.
*/
/**
* Implements hook_js_settings_alter().
*/
function js_deprecation_test_js_settings_alter(&$settings) {
$settings['suppressDeprecationErrors'] = FALSE;
}
js_deprecation_test.deprecation:
path: '/js_deprecation_test'
defaults:
_controller: '\Drupal\js_deprecation_test\Controller\JsDeprecationTestController::jsDeprecationTest'
_title: 'JsDeprecationTest'
requirements:
_access: 'TRUE'
<?php
namespace Drupal\js_deprecation_test\Controller;
/**
* Test Controller to show message links.
*/
class JsDeprecationTestController {
/**
* Renders page that has js_deprecation_test/deprecation library attached.
*
* @return array
* Render array.
*/
public function jsDeprecationTest() {
return [
'#attached' => ['library' => ['js_deprecation_test/deprecation_test']],
];
}
}
cache_strings: true
javascript:
directory: languages
translation:
use_source: remote_and_local
default_filename: '%project-%version.%language.po'
default_server_pattern: 'http://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po'
overwrite_customized: false
overwrite_not_customized: true
update_interval_days: 0
path: ''
import_enabled: false
name: Nightwatch Testing
type: profile
description: 'Minimal profile for running Nightwatch tests. Includes absolutely required modules only.'
version: VERSION
core: 8.x
hidden: true
install:
- js_deprecation_log_test
<?php
namespace Drupal\FunctionalJavascriptTests;
/**
* Tests Javascript deprecation notices.
*
* @group javascript
* @group legacy
*/
class JavascriptDeprecationTest extends WebDriverTestBase {
public static $modules = ['js_deprecation_test'];
/**
* @expectedDeprecation Javascript Deprecation: This function is deprecated for testing purposes.
* @expectedDeprecation Javascript Deprecation: This property is deprecated for testing purposes.
*/
public function testJavascriptDeprecation() {
$this->drupalGet('js_deprecation_test');
// Ensure that deprecation message from previous page loads will be
// detected.
$this->drupalGet('user');
}
}
......@@ -76,8 +76,9 @@ protected function initMink() {
* {@inheritdoc}
*/
protected function installModulesFromClassProperty(ContainerInterface $container) {
self::$modules = ['js_deprecation_log_test'];
if ($this->disableCssAnimations) {
self::$modules = ['css_disable_transitions_test'];
self::$modules[] = 'css_disable_transitions_test';
}
parent::installModulesFromClassProperty($container);
}
......@@ -108,6 +109,13 @@ protected function tearDown() {
// explaining what the problem is.
throw new \RuntimeException('Unfinished AJAX requests while tearing down a test');
}
$warnings = $this->getSession()->evaluateScript("JSON.parse(sessionStorage.getItem('js_deprecation_log_test.warnings') || JSON.stringify([]))");
foreach ($warnings as $warning) {
if (strpos($warning, '[Deprecation]') === 0) {
@trigger_error('Javascript Deprecation:' . substr($warning, 13), E_USER_DEPRECATED);
}
}
}
parent::tearDown();
}
......
module.exports.assertion = function(expected) {
this.message = `Testing if "${expected}" deprecation error has been triggered`;
this.expected = expected;
this.pass = deprecationMessages => deprecationMessages.includes(expected);
this.value = result => {
const sessionStorageEntries = JSON.parse(result.value);
const deprecationMessages =
sessionStorageEntries !== null
? sessionStorageEntries.filter(message =>
new RegExp('[Deprecation]').test(message),
)
: [];
return deprecationMessages.map(message =>
message.replace('[Deprecation] ', ''),
);
};
this.command = callback =>
// eslint-disable-next-line prefer-arrow-callback
this.api.execute(function() {
return window.sessionStorage.getItem('js_deprecation_log_test.warnings');
}, callback);
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment