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
6ea36484fb
commit
1b0c006ac9
|
@ -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);
|
||||
})(Drupal, window.drupalSettings, window.drupalTranslations, window.console, window.Proxy, window.Reflect);
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* @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);
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* 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);
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,6 @@
|
|||
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
|
|
@ -0,0 +1,6 @@
|
|||
deprecation_log:
|
||||
version: VERSION
|
||||
js:
|
||||
js/js_deprecation_log.js: { weight: -100 }
|
||||
dependencies:
|
||||
- core/drupal
|
|
@ -0,0 +1,21 @@
|
|||
<?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;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* @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);
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* 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);
|
|
@ -0,0 +1,6 @@
|
|||
name: 'JS Deprecation test'
|
||||
description: 'Provides deprecated code that can be used for tests'
|
||||
type: module
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
|
@ -0,0 +1,6 @@
|
|||
deprecation_test:
|
||||
version: VERSION
|
||||
js:
|
||||
js/js_deprecation_test.js: {}
|
||||
dependencies:
|
||||
- core/drupal
|
|
@ -0,0 +1,13 @@
|
|||
<?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;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
js_deprecation_test.deprecation:
|
||||
path: '/js_deprecation_test'
|
||||
defaults:
|
||||
_controller: '\Drupal\js_deprecation_test\Controller\JsDeprecationTestController::jsDeprecationTest'
|
||||
_title: 'JsDeprecationTest'
|
||||
requirements:
|
||||
_access: 'TRUE'
|
|
@ -0,0 +1,22 @@
|
|||
<?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']],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
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
|
|
@ -0,0 +1,8 @@
|
|||
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
|
|
@ -0,0 +1,26 @@
|
|||
<?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 @@ abstract class WebDriverTestBase extends BrowserTestBase {
|
|||
* {@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 @@ abstract class WebDriverTestBase extends BrowserTestBase {
|
|||
// 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();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
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,0 +1,23 @@
|
|||
module.exports.assertion = function() {
|
||||
this.message = 'Ensuring no deprecation errors have been triggered';
|
||||
this.expected = '';
|
||||
this.pass = deprecationMessages => deprecationMessages.length === 0;
|
||||
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);
|
||||
};
|
|
@ -46,7 +46,7 @@ exports.command = function drupalCreateUser(
|
|||
})
|
||||
.submitForm('#user-register-form')
|
||||
.assert.containsText(
|
||||
'.messages',
|
||||
'[data-drupal-messages]',
|
||||
'Created a new user account',
|
||||
`User "${name}" was created successfully.`,
|
||||
);
|
||||
|
|
|
@ -25,7 +25,7 @@ exports.command = function drupalInstall({ setupFile = '' } = {}, callback) {
|
|||
: '';
|
||||
const install = execSync(
|
||||
commandAsWebserver(
|
||||
`php ./scripts/test-site.php install ${setupFile} --base-url ${process.env.DRUPAL_TEST_BASE_URL} ${dbOption} --json`,
|
||||
`php ./scripts/test-site.php install ${setupFile} --install-profile nightwatch_testing --base-url ${process.env.DRUPAL_TEST_BASE_URL} ${dbOption} --json`,
|
||||
),
|
||||
);
|
||||
const installData = JSON.parse(install.toString());
|
||||
|
|
|
@ -22,6 +22,7 @@ module.exports = {
|
|||
.drupalRelativeURL('/test-page')
|
||||
.waitForElementVisible('@body', testPage.props.timeout)
|
||||
.assert.containsText('@body', testPage.props.text)
|
||||
.assert.noDeprecationErrors()
|
||||
.drupalLogAndEnd({ onlyOnError: false });
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
module.exports = {
|
||||
'@tags': ['core'],
|
||||
before(browser) {
|
||||
browser.drupalInstall().drupalLoginAsAdmin(() => {
|
||||
browser
|
||||
.drupalRelativeURL('/admin/modules')
|
||||
.setValue('input[type="search"]', 'JS Deprecation test')
|
||||
.waitForElementVisible(
|
||||
'input[name="modules[js_deprecation_test][enable]"]',
|
||||
1000,
|
||||
)
|
||||
.click('input[name="modules[js_deprecation_test][enable]"]')
|
||||
.click('input[type="submit"]'); // Submit module form.
|
||||
});
|
||||
},
|
||||
after(browser) {
|
||||
browser.drupalUninstall();
|
||||
},
|
||||
'Test JavaScript deprecations': browser => {
|
||||
browser
|
||||
.drupalRelativeURL('/js_deprecation_test')
|
||||
.waitForElementVisible('body', 1000)
|
||||
.assert.containsText('h1', 'JsDeprecationTest')
|
||||
.assert.deprecationErrorExists(
|
||||
'This function is deprecated for testing purposes.',
|
||||
)
|
||||
.assert.deprecationErrorExists(
|
||||
'This property is deprecated for testing purposes.',
|
||||
)
|
||||
.drupalLogAndEnd({ onlyOnError: false });
|
||||
},
|
||||
};
|
|
@ -17,7 +17,8 @@ module.exports = {
|
|||
})
|
||||
.drupalLogin({ name: 'user', password: '123' })
|
||||
.drupalRelativeURL('/admin/reports')
|
||||
.expect.element('h1.page-title')
|
||||
.text.to.contain('Reports');
|
||||
.waitForElementVisible('body', 1000)
|
||||
.assert.containsText('h1', 'Reports')
|
||||
.assert.noDeprecationErrors();
|
||||
},
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@ module.exports = {
|
|||
browser
|
||||
.drupalRelativeURL('/form-test/javascript-states-form')
|
||||
.waitForElementVisible('body', 1000)
|
||||
.waitForElementNotVisible('input[name="textfield"]', 1000);
|
||||
.waitForElementNotVisible('input[name="textfield"]', 1000)
|
||||
.assert.noDeprecationErrors();
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue