From ea75488842205a599426a158bc02e3ed2ebef114 Mon Sep 17 00:00:00 2001
From: Matthew Grill
Date: Fri, 8 Dec 2017 11:48:35 -0800
Subject: [PATCH 001/232] nightwatch
---
core/js_tests/index.js | 13 ++
core/nightwatch.json | 38 ++++
core/package.json | 6 +-
core/yarn.lock | 503 +++++++++++++++++++++++++++++++++++++++--
4 files changed, 546 insertions(+), 14 deletions(-)
create mode 100644 core/js_tests/index.js
create mode 100644 core/nightwatch.json
diff --git a/core/js_tests/index.js b/core/js_tests/index.js
new file mode 100644
index 000000000000..4481c72acd8c
--- /dev/null
+++ b/core/js_tests/index.js
@@ -0,0 +1,13 @@
+module.exports = {
+ 'Demo test Google' : function (browser) {
+ browser
+ .url('http://www.google.com')
+ .waitForElementVisible('body', 1000)
+ .setValue('input[type=text]', 'nightwatch')
+ .waitForElementVisible('button[name=btnG]', 1000)
+ .click('button[name=btnG]')
+ .pause(1000)
+ .assert.containsText('#main', 'Night Watch')
+ .end();
+ }
+};
diff --git a/core/nightwatch.json b/core/nightwatch.json
new file mode 100644
index 000000000000..5de1bddc351a
--- /dev/null
+++ b/core/nightwatch.json
@@ -0,0 +1,38 @@
+{
+ "src_folders" : ["js_tests"],
+ "output_folder" : "reports",
+ "custom_commands_path" : "",
+ "custom_assertions_path" : "",
+ "page_objects_path" : "",
+ "globals_path" : "",
+ "selenium" : {
+ "start_process" : false,
+ "server_path" : "",
+ "log_path" : "",
+ "port" : 4444,
+ "cli_args" : {
+ "webdriver.chrome.driver" : ""
+ }
+ },
+ "test_settings" : {
+ "default" : {
+ "launch_url" : "http://localhost",
+ "selenium_port" : 4444,
+ "selenium_host" : "localhost",
+ "silent": true,
+ "screenshots" : {
+ "enabled" : false,
+ "path" : ""
+ },
+ "desiredCapabilities": {
+ "browserName": "firefox",
+ "marionette": true
+ }
+ },
+ "chrome" : {
+ "desiredCapabilities": {
+ "browserName": "chrome"
+ }
+ }
+ }
+}
diff --git a/core/package.json b/core/package.json
index b33263a3a4f2..f894d177e311 100644
--- a/core/package.json
+++ b/core/package.json
@@ -12,7 +12,8 @@
"lint:core-js-passing": "node ./node_modules/eslint/bin/eslint.js --quiet --config=.eslintrc.passing.json --ext=.es6.js . || exit 0",
"lint:core-js-stats": "node ./node_modules/eslint/bin/eslint.js --format=./scripts/js/eslint-stats-by-type.js --ext=.es6.js . || exit 0",
"lint:css": "stylelint \"**/*.css\" || exit 0",
- "lint:css-checkstyle": "stylelint \"**/*.css\" --custom-formatter ./node_modules/stylelint-checkstyle-formatter/index.js || exit 0"
+ "lint:css-checkstyle": "stylelint \"**/*.css\" --custom-formatter ./node_modules/stylelint-checkstyle-formatter/index.js || exit 0",
+ "test:js": "./node_modules/.bin/nightwatch"
},
"devDependencies": {
"babel-core": "6.24.1",
@@ -52,5 +53,8 @@
}
]
]
+ },
+ "dependencies": {
+ "nightwatch": "^0.9.19"
}
}
diff --git a/core/yarn.lock b/core/yarn.lock
index d4bab4acd4ae..860daca44171 100644
--- a/core/yarn.lock
+++ b/core/yarn.lock
@@ -27,6 +27,13 @@ acorn@^5.0.1:
version "5.0.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.0.3.tgz#c460df08491463f028ccb82eab3730bf01087b3d"
+agent-base@2:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.1.1.tgz#d6de10d5af6132d5bd692427d46fc538539094c7"
+ dependencies:
+ extend "~3.0.0"
+ semver "~5.0.1"
+
ajv-keywords@^1.0.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
@@ -139,10 +146,18 @@ assert-plus@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
+assertion-error@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.0.tgz#c7f85438fdd466bc7ca16ab90c81513797a5d23b"
+
ast-types-flow@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
+ast-types@0.x.x:
+ version "0.10.1"
+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.10.1.tgz#f52fca9715579a14f841d67d7f8d25432ab6a3dd"
+
async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
@@ -672,6 +687,10 @@ braces@^1.8.2:
preserve "^0.2.0"
repeat-element "^1.1.2"
+browser-stdout@1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f"
+
browserslist@^1.1.1, browserslist@^1.1.3, browserslist@^1.4.0, browserslist@^1.7.6:
version "1.7.7"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
@@ -687,6 +706,10 @@ builtin-modules@^1.0.0, builtin-modules@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
+bytes@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+
caller-path@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
@@ -716,6 +739,13 @@ caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+chai-nightwatch@~0.1.x:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/chai-nightwatch/-/chai-nightwatch-0.1.1.tgz#1ca56de768d3c0868fe7fc2f4d32c2fe894e6be9"
+ dependencies:
+ assertion-error "1.0.0"
+ deep-eql "0.1.3"
+
chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -774,6 +804,10 @@ co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+co@~3.0.6:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/co/-/co-3.0.6.tgz#1445f226c5eb956138e68c9ac30167ea7d2e6bda"
+
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
@@ -803,6 +837,12 @@ combined-stream@^1.0.5, combined-stream@~1.0.5:
dependencies:
delayed-stream "~1.0.0"
+commander@2.9.0:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
+ dependencies:
+ graceful-readlink ">= 1.0.0"
+
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
@@ -910,6 +950,16 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
+data-uri-to-buffer@1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835"
+
+debug@2:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ dependencies:
+ ms "2.0.0"
+
debug@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
@@ -926,6 +976,12 @@ decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+deep-eql@0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2"
+ dependencies:
+ type-detect "0.1.1"
+
deep-extend@~0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253"
@@ -941,6 +997,14 @@ define-properties@^1.1.2:
foreach "^2.0.5"
object-keys "^1.0.8"
+degenerator@~1.0.2:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095"
+ dependencies:
+ ast-types "0.x.x"
+ escodegen "1.x.x"
+ esprima "3.x.x"
+
del@^2.0.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
@@ -961,12 +1025,20 @@ delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
+depd@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
+
detect-indent@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
dependencies:
repeating "^2.0.0"
+diff@1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf"
+
doctrine@1.5.0, doctrine@^1.2.2:
version "1.5.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa"
@@ -1014,6 +1086,10 @@ ecc-jsbn@~0.1.1:
dependencies:
jsbn "~0.1.0"
+ejs@2.5.7:
+ version "2.5.7"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a"
+
electron-to-chromium@^1.2.7:
version "1.3.8"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.8.tgz#b2c8a2c79bb89fbbfd3724d9555e15095b5f5fb6"
@@ -1097,10 +1173,21 @@ es6-weak-map@^2.0.1:
es6-iterator "^2.0.1"
es6-symbol "^3.1.1"
-escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+escodegen@1.x.x:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.0.tgz#9811a2f265dc1cd3894420ee3717064b632b8852"
+ dependencies:
+ esprima "^3.1.3"
+ estraverse "^4.2.0"
+ esutils "^2.0.2"
+ optionator "^0.8.1"
+ optionalDependencies:
+ source-map "~0.5.6"
+
escope@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3"
@@ -1218,7 +1305,7 @@ espree@^3.4.0:
acorn "^5.0.1"
acorn-jsx "^3.0.0"
-esprima@^3.1.1:
+esprima@3.x.x, esprima@^3.1.1, esprima@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
@@ -1276,7 +1363,7 @@ expand-range@^1.8.1:
dependencies:
fill-range "^2.1.0"
-extend@~3.0.0:
+extend@3, extend@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
@@ -1308,6 +1395,10 @@ file-entry-cache@^2.0.0:
flat-cache "^1.2.1"
object-assign "^4.0.1"
+file-uri-to-path@1:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
+
filename-regex@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
@@ -1396,6 +1487,13 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2:
mkdirp ">=0.5 0"
rimraf "2"
+ftp@~0.3.10:
+ version "0.3.10"
+ resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d"
+ dependencies:
+ readable-stream "1.1.x"
+ xregexp "2.0.0"
+
function-bind@^1.0.2, function-bind@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
@@ -1435,6 +1533,17 @@ get-stdin@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398"
+get-uri@2:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.1.tgz#dbdcacacd8c608a38316869368117697a1631c59"
+ dependencies:
+ data-uri-to-buffer "1"
+ debug "2"
+ extend "3"
+ file-uri-to-path "1"
+ ftp "~0.3.10"
+ readable-stream "2"
+
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
@@ -1454,6 +1563,17 @@ glob-parent@^2.0.0:
dependencies:
is-glob "^2.0.0"
+glob@7.0.5:
+ version "7.0.5"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95"
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.2"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5:
version "7.1.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
@@ -1498,6 +1618,14 @@ graceful-fs@^4.1.2:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
+"graceful-readlink@>= 1.0.0":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
+
+growl@1.9.2:
+ version "1.9.2"
+ resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
+
har-schema@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
@@ -1557,6 +1685,23 @@ html-tags@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-1.1.1.tgz#869f43859f12d9bdc3892419e494a628aa1b204e"
+http-errors@1.6.2:
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
+ dependencies:
+ depd "1.1.1"
+ inherits "2.0.3"
+ setprototypeof "1.0.3"
+ statuses ">= 1.3.1 < 2"
+
+http-proxy-agent@1:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a"
+ dependencies:
+ agent-base "2"
+ debug "2"
+ extend "3"
+
http-signature@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf"
@@ -1565,6 +1710,18 @@ http-signature@~1.1.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
+https-proxy-agent@1:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6"
+ dependencies:
+ agent-base "2"
+ debug "2"
+ extend "3"
+
+iconv-lite@0.4.19:
+ version "0.4.19"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
+
ignore@^3.2.0:
version "3.2.7"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.7.tgz#4810ca5f1d8eca5595213a34b94f2eb4ed926bbd"
@@ -1590,7 +1747,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1:
+inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
@@ -1630,6 +1787,14 @@ invert-kv@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
+ip@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/ip/-/ip-1.0.1.tgz#c7e356cdea225ae71b36d70f2e71a92ba4e42590"
+
+ip@^1.1.4:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
+
irregular-plurals@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.2.0.tgz#38f299834ba8c00c30be9c554e137269752ff3ac"
@@ -1854,6 +2019,10 @@ json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+json3@3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
+
json5@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
@@ -1932,10 +2101,127 @@ load-json-file@^1.0.0:
pinkie-promise "^2.0.0"
strip-bom "^2.0.0"
+lodash._arraycopy@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1"
+
+lodash._arrayeach@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz#bab156b2a90d3f1bbd5c653403349e5e5933ef9e"
+
+lodash._baseassign@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
+ dependencies:
+ lodash._basecopy "^3.0.0"
+ lodash.keys "^3.0.0"
+
+lodash._baseclone@^3.0.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz#303519bf6393fe7e42f34d8b630ef7794e3542b7"
+ dependencies:
+ lodash._arraycopy "^3.0.0"
+ lodash._arrayeach "^3.0.0"
+ lodash._baseassign "^3.0.0"
+ lodash._basefor "^3.0.0"
+ lodash.isarray "^3.0.0"
+ lodash.keys "^3.0.0"
+
+lodash._baseclone@^4.0.0:
+ version "4.5.7"
+ resolved "https://registry.yarnpkg.com/lodash._baseclone/-/lodash._baseclone-4.5.7.tgz#ce42ade08384ef5d62fa77c30f61a46e686f8434"
+
+lodash._basecopy@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
+
+lodash._basecreate@^3.0.0:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821"
+
+lodash._basefor@^3.0.0:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz#7550b4e9218ef09fad24343b612021c79b4c20c2"
+
+lodash._bindcallback@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
+
+lodash._getnative@^3.0.0:
+ version "3.9.1"
+ resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
+
+lodash._isiterateecall@^3.0.0:
+ version "3.0.9"
+ resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c"
+
+lodash._stack@^4.0.0:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/lodash._stack/-/lodash._stack-4.1.3.tgz#751aa76c1b964b047e76d14fc72a093fcb5e2dd0"
+
+lodash.clone@3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-3.0.3.tgz#84688c73d32b5a90ca25616963f189252a997043"
+ dependencies:
+ lodash._baseclone "^3.0.0"
+ lodash._bindcallback "^3.0.0"
+ lodash._isiterateecall "^3.0.0"
+
lodash.cond@^4.3.0:
version "4.5.2"
resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5"
+lodash.create@3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.create/-/lodash.create-3.1.1.tgz#d7f2849f0dbda7e04682bb8cd72ab022461debe7"
+ dependencies:
+ lodash._baseassign "^3.0.0"
+ lodash._basecreate "^3.0.0"
+ lodash._isiterateecall "^3.0.0"
+
+lodash.defaultsdeep@4.3.2:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.3.2.tgz#6c1a586e6c5647b0e64e2d798141b8836158be8a"
+ dependencies:
+ lodash._baseclone "^4.0.0"
+ lodash._stack "^4.0.0"
+ lodash.isplainobject "^4.0.0"
+ lodash.keysin "^4.0.0"
+ lodash.mergewith "^4.0.0"
+ lodash.rest "^4.0.0"
+
+lodash.isarguments@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
+
+lodash.isarray@^3.0.0:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
+
+lodash.isplainobject@^4.0.0:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+
+lodash.keys@^3.0.0:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
+ dependencies:
+ lodash._getnative "^3.0.0"
+ lodash.isarguments "^3.0.0"
+ lodash.isarray "^3.0.0"
+
+lodash.keysin@^4.0.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-4.2.0.tgz#8cc3fb35c2d94acc443a1863e02fa40799ea6f28"
+
+lodash.mergewith@^4.0.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
+
+lodash.rest@^4.0.0:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/lodash.rest/-/lodash.rest-4.0.5.tgz#954ef75049262038c96d1fc98b28fdaf9f0772aa"
+
lodash@^3.0.0:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
@@ -1970,6 +2256,10 @@ lru-cache@^4.0.1:
pseudomap "^1.0.1"
yallist "^2.0.0"
+lru-cache@~2.6.5:
+ version "2.6.5"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5"
+
map-obj@^1.0.0, map-obj@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
@@ -2017,7 +2307,7 @@ mime-types@^2.1.12, mime-types@~2.1.7:
dependencies:
mime-db "~1.27.0"
-minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3:
+minimatch@3.0.3, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774"
dependencies:
@@ -2031,12 +2321,36 @@ minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
-"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1:
+minimist@~0.0.1:
+ version "0.0.10"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
+
+mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
minimist "0.0.8"
+mkpath@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/mkpath/-/mkpath-1.0.0.tgz#ebb3a977e7af1c683ae6fda12b545a6ba6c5853d"
+
+mocha-nightwatch@3.2.2:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/mocha-nightwatch/-/mocha-nightwatch-3.2.2.tgz#91bcb9b3bde057dd7677c78125e491e58d66647c"
+ dependencies:
+ browser-stdout "1.3.0"
+ commander "2.9.0"
+ debug "2.2.0"
+ diff "1.4.0"
+ escape-string-regexp "1.0.5"
+ glob "7.0.5"
+ growl "1.9.2"
+ json3 "3.3.2"
+ lodash.create "3.1.1"
+ mkdirp "0.5.1"
+ supports-color "3.1.2"
+
ms@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
@@ -2045,6 +2359,10 @@ ms@0.7.3:
version "0.7.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.3.tgz#708155a5e44e33f5fd0fc53e81d0d40a91be1fff"
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+
multimatch@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b"
@@ -2066,6 +2384,25 @@ natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+netmask@~1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
+
+nightwatch@^0.9.19:
+ version "0.9.19"
+ resolved "https://registry.yarnpkg.com/nightwatch/-/nightwatch-0.9.19.tgz#4bd9757273d30b845f04847a98b71be9bb7c4b3b"
+ dependencies:
+ chai-nightwatch "~0.1.x"
+ ejs "2.5.7"
+ lodash.clone "3.0.3"
+ lodash.defaultsdeep "4.3.2"
+ minimatch "3.0.3"
+ mkpath "1.0.0"
+ mocha-nightwatch "3.2.2"
+ optimist "0.6.1"
+ proxy-agent "2.0.0"
+ q "1.4.1"
+
node-pre-gyp@^0.6.29:
version "0.6.34"
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz#94ad1c798a11d7fc67381b50d47f8cc18d9799f7"
@@ -2168,7 +2505,14 @@ onetime@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
-optionator@^0.8.2:
+optimist@0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
+ dependencies:
+ minimist "~0.0.1"
+ wordwrap "~0.0.2"
+
+optionator@^0.8.1, optionator@^0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64"
dependencies:
@@ -2200,6 +2544,30 @@ osenv@^0.1.4:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
+pac-proxy-agent@1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d"
+ dependencies:
+ agent-base "2"
+ debug "2"
+ extend "3"
+ get-uri "2"
+ http-proxy-agent "1"
+ https-proxy-agent "1"
+ pac-resolver "~2.0.0"
+ raw-body "2"
+ socks-proxy-agent "2"
+
+pac-resolver@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-2.0.0.tgz#99b88d2f193fbdeefc1c9a529c1f3260ab5277cd"
+ dependencies:
+ co "~3.0.6"
+ degenerator "~1.0.2"
+ ip "1.0.1"
+ netmask "~1.0.4"
+ thunkify "~2.1.1"
+
parse-glob@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
@@ -2367,6 +2735,19 @@ progress@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
+proxy-agent@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499"
+ dependencies:
+ agent-base "2"
+ debug "2"
+ extend "3"
+ http-proxy-agent "1"
+ https-proxy-agent "1"
+ lru-cache "~2.6.5"
+ pac-proxy-agent "1"
+ socks-proxy-agent "2"
+
pseudomap@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
@@ -2375,6 +2756,10 @@ punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+q@1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
+
qs@~6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
@@ -2386,6 +2771,15 @@ randomatic@^1.1.3:
is-number "^2.0.2"
kind-of "^3.0.2"
+raw-body@2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
+ dependencies:
+ bytes "3.0.0"
+ http-errors "1.6.2"
+ iconv-lite "0.4.19"
+ unpipe "1.0.0"
+
rc@^1.1.7:
version "1.2.1"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
@@ -2416,18 +2810,30 @@ read-pkg@^1.0.0:
normalize-package-data "^2.3.2"
path-type "^1.0.0"
-"readable-stream@>=1.0.33-1 <1.1.0-0":
- version "1.0.34"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
+readable-stream@1.1.x, readable-stream@^1.0.33, readable-stream@~1.1.9:
+ version "1.1.14"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.1"
isarray "0.0.1"
string_decoder "~0.10.x"
-readable-stream@^1.0.33, readable-stream@~1.1.9:
- version "1.1.14"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
+readable-stream@2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.3"
+ isarray "~1.0.0"
+ process-nextick-args "~1.0.6"
+ safe-buffer "~5.1.1"
+ string_decoder "~1.0.3"
+ util-deprecate "~1.0.1"
+
+"readable-stream@>=1.0.33-1 <1.1.0-0":
+ version "1.0.34"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.1"
@@ -2614,10 +3020,18 @@ safe-buffer@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
+safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
+
"semver@2 || 3 || 4 || 5", semver@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
+semver@~5.0.1:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
+
set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
@@ -2626,6 +3040,10 @@ set-immediate-shim@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+setprototypeof@1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
+
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
@@ -2656,12 +3074,31 @@ slice-ansi@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
+smart-buffer@^1.0.13:
+ version "1.1.15"
+ resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16"
+
sntp@1.x.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
dependencies:
hoek "2.x.x"
+socks-proxy-agent@2:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3"
+ dependencies:
+ agent-base "2"
+ extend "3"
+ socks "~1.1.5"
+
+socks@~1.1.5:
+ version "1.1.10"
+ resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a"
+ dependencies:
+ ip "^1.1.4"
+ smart-buffer "^1.0.13"
+
source-map-support@^0.4.2:
version "0.4.15"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
@@ -2678,6 +3115,10 @@ source-map@^0.5.0, source-map@^0.5.6:
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+source-map@~0.5.6:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+
spdx-correct@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40"
@@ -2721,6 +3162,10 @@ sshpk@^1.7.0:
jsbn "~0.1.0"
tweetnacl "~0.14.0"
+"statuses@>= 1.3.1 < 2":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
+
stream-combiner@^0.2.1:
version "0.2.2"
resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.2.2.tgz#aec8cbac177b56b6f4fa479ced8c1912cee52858"
@@ -2753,6 +3198,12 @@ string_decoder@~1.0.0:
dependencies:
buffer-shims "~1.0.0"
+string_decoder@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab"
+ dependencies:
+ safe-buffer "~5.1.0"
+
stringstream@~0.0.4:
version "0.0.5"
resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
@@ -2868,6 +3319,12 @@ sugarss@^0.2.0:
dependencies:
postcss "^5.2.4"
+supports-color@3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
+ dependencies:
+ has-flag "^1.0.0"
+
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -2946,6 +3403,10 @@ through2@^0.6.1, through2@^0.6.3, through2@~0.6.1:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+thunkify@~2.1.1:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d"
+
to-fast-properties@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320"
@@ -2984,6 +3445,10 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
+type-detect@0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822"
+
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
@@ -2996,6 +3461,10 @@ uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
+unpipe@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+
user-home@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f"
@@ -3039,6 +3508,10 @@ window-size@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876"
+wordwrap@~0.0.2:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
+
wordwrap@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
@@ -3064,6 +3537,10 @@ write@^0.2.1:
dependencies:
mkdirp "^0.5.1"
+xregexp@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
+
"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
From e7553325d70894a10429399b47a47ed185e26f2a Mon Sep 17 00:00:00 2001
From: Matthew Grill
Date: Fri, 8 Dec 2017 13:06:29 -0800
Subject: [PATCH 002/232] nightwatch updates
---
core/js_tests/index.js | 13 --
core/nightwatch.json | 49 +++---
core/package.json | 7 +-
core/tests/Drupal/Nightwatch/index.js | 9 ++
core/tests/nightwatch_bootstrap.js | 11 ++
core/yarn.lock | 224 +++++++++++++++++++++++++-
6 files changed, 258 insertions(+), 55 deletions(-)
delete mode 100644 core/js_tests/index.js
create mode 100644 core/tests/Drupal/Nightwatch/index.js
create mode 100644 core/tests/nightwatch_bootstrap.js
diff --git a/core/js_tests/index.js b/core/js_tests/index.js
deleted file mode 100644
index 4481c72acd8c..000000000000
--- a/core/js_tests/index.js
+++ /dev/null
@@ -1,13 +0,0 @@
-module.exports = {
- 'Demo test Google' : function (browser) {
- browser
- .url('http://www.google.com')
- .waitForElementVisible('body', 1000)
- .setValue('input[type=text]', 'nightwatch')
- .waitForElementVisible('button[name=btnG]', 1000)
- .click('button[name=btnG]')
- .pause(1000)
- .assert.containsText('#main', 'Night Watch')
- .end();
- }
-};
diff --git a/core/nightwatch.json b/core/nightwatch.json
index 5de1bddc351a..439f5b5ae7fa 100644
--- a/core/nightwatch.json
+++ b/core/nightwatch.json
@@ -1,37 +1,24 @@
{
- "src_folders" : ["js_tests"],
- "output_folder" : "reports",
- "custom_commands_path" : "",
- "custom_assertions_path" : "",
- "page_objects_path" : "",
- "globals_path" : "",
- "selenium" : {
- "start_process" : false,
- "server_path" : "",
- "log_path" : "",
- "port" : 4444,
- "cli_args" : {
- "webdriver.chrome.driver" : ""
- }
+ "src_folders": ["tests/Drupal/Nightwatch"],
+ "output_folder": "reports",
+ "custom_commands_path": "",
+ "custom_assertions_path": "",
+ "page_objects_path": "",
+ "globals_path": "tests/nightwatch_bootstrap.js",
+ "selenium": {
+ "start_process": false
},
- "test_settings" : {
- "default" : {
- "launch_url" : "http://localhost",
- "selenium_port" : 4444,
- "selenium_host" : "localhost",
- "silent": true,
- "screenshots" : {
- "enabled" : false,
- "path" : ""
- },
- "desiredCapabilities": {
- "browserName": "firefox",
- "marionette": true
- }
- },
- "chrome" : {
+ "test_settings": {
+ "default": {
+ "selenium_port": 9515,
+ "selenium_host": "localhost",
+ "default_path_prefix": "",
"desiredCapabilities": {
- "browserName": "chrome"
+ "browserName": "chrome",
+ "chromeOptions": {
+ "args": ["--no-sandbox"]
+ },
+ "acceptSslCerts": true
}
}
}
diff --git a/core/package.json b/core/package.json
index f894d177e311..2e13a539fa48 100644
--- a/core/package.json
+++ b/core/package.json
@@ -13,6 +13,7 @@
"lint:core-js-stats": "node ./node_modules/eslint/bin/eslint.js --format=./scripts/js/eslint-stats-by-type.js --ext=.es6.js . || exit 0",
"lint:css": "stylelint \"**/*.css\" || exit 0",
"lint:css-checkstyle": "stylelint \"**/*.css\" --custom-formatter ./node_modules/stylelint-checkstyle-formatter/index.js || exit 0",
+ "test:js:ci": "./node_modules/.bin/nightwatch -o $NIGHTWATCH_OUTPUT -r junit",
"test:js": "./node_modules/.bin/nightwatch"
},
"devDependencies": {
@@ -21,6 +22,7 @@
"babel-preset-env": "1.4.0",
"chalk": "^1.1.3",
"chokidar": "1.6.1",
+ "chromedriver": "^2.33.2",
"cross-env": "^4.0.0",
"eslint": "3.19.0",
"eslint-config-airbnb": "14.1.0",
@@ -29,6 +31,7 @@
"eslint-plugin-react": "6.10.3",
"glob": "7.1.1",
"minimist": "^1.2.0",
+ "nightwatch": "^0.9.19",
"stylelint": "^7.10.1",
"stylelint-checkstyle-formatter": "^0.1.0",
"stylelint-config-standard": "^16.0.0",
@@ -54,7 +57,5 @@
]
]
},
- "dependencies": {
- "nightwatch": "^0.9.19"
- }
+ "dependencies": {}
}
diff --git a/core/tests/Drupal/Nightwatch/index.js b/core/tests/Drupal/Nightwatch/index.js
new file mode 100644
index 000000000000..b94607280c3b
--- /dev/null
+++ b/core/tests/Drupal/Nightwatch/index.js
@@ -0,0 +1,9 @@
+module.exports = {
+ 'Demo Drupal.org' : function (browser) {
+ browser
+ .url('https://www.drupal.org/')
+ .waitForElementVisible('body', 1000)
+ .assert.containsText('body', 'Launch, manage, and scale ambitious digital experiences—with the flexibility to build great websites or push beyond the browser. Proudly open source.')
+ .end();
+ }
+};
diff --git a/core/tests/nightwatch_bootstrap.js b/core/tests/nightwatch_bootstrap.js
new file mode 100644
index 000000000000..dcaa13bba6ad
--- /dev/null
+++ b/core/tests/nightwatch_bootstrap.js
@@ -0,0 +1,11 @@
+const chromedriver = require('chromedriver');
+module.exports = {
+ before: function(done) {
+ chromedriver.start();
+ done();
+ },
+ after: function(done) {
+ chromedriver.stop();
+ done();
+ }
+};
diff --git a/core/yarn.lock b/core/yarn.lock
index 860daca44171..af9c48c2eb0c 100644
--- a/core/yarn.lock
+++ b/core/yarn.lock
@@ -45,6 +45,15 @@ ajv@^4.7.0, ajv@^4.9.1:
co "^4.6.0"
json-stable-stringify "^1.0.1"
+ajv@^5.1.0:
+ version "5.5.1"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.1.tgz#b38bb8876d9e86bee994956a04e721e88b248eb2"
+ dependencies:
+ co "^4.6.0"
+ fast-deep-equal "^1.0.0"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.3.0"
+
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
@@ -181,7 +190,11 @@ aws-sign2@~0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
-aws4@^1.2.1:
+aws-sign2@~0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+
+aws4@^1.2.1, aws4@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
@@ -672,6 +685,18 @@ boom@2.x.x:
dependencies:
hoek "2.x.x"
+boom@4.x.x:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
+ dependencies:
+ hoek "4.x.x"
+
+boom@5.x.x:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02"
+ dependencies:
+ hoek "4.x.x"
+
brace-expansion@^1.0.0:
version "1.1.7"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59"
@@ -771,6 +796,16 @@ chokidar@1.6.1:
optionalDependencies:
fsevents "^1.0.0"
+chromedriver@^2.33.2:
+ version "2.33.2"
+ resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-2.33.2.tgz#8fc779d54b6e45bef55d264a1eceed52427a9b49"
+ dependencies:
+ del "^3.0.0"
+ extract-zip "^1.6.5"
+ kew "^0.7.0"
+ mkdirp "^0.5.1"
+ request "^2.83.0"
+
circular-json@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d"
@@ -847,7 +882,7 @@ concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
-concat-stream@^1.5.2:
+concat-stream@1.6.0, concat-stream@^1.5.2:
version "1.6.0"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
dependencies:
@@ -908,6 +943,12 @@ cryptiles@2.x.x:
dependencies:
boom "2.x.x"
+cryptiles@3.x.x:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe"
+ dependencies:
+ boom "5.x.x"
+
css-color-names@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.3.tgz#de0cef16f4d8aa8222a320d5b6d7e9bbada7b9f6"
@@ -954,7 +995,7 @@ data-uri-to-buffer@1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835"
-debug@2:
+debug@2, debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
@@ -1017,6 +1058,17 @@ del@^2.0.2:
pinkie-promise "^2.0.0"
rimraf "^2.2.8"
+del@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5"
+ dependencies:
+ globby "^6.1.0"
+ is-path-cwd "^1.0.0"
+ is-path-in-cwd "^1.0.0"
+ p-map "^1.1.1"
+ pify "^3.0.0"
+ rimraf "^2.2.8"
+
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
@@ -1363,7 +1415,7 @@ expand-range@^1.8.1:
dependencies:
fill-range "^2.1.0"
-extend@3, extend@~3.0.0:
+extend@3, extend@~3.0.0, extend@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
@@ -1373,14 +1425,37 @@ extglob@^0.3.1:
dependencies:
is-extglob "^1.0.0"
+extract-zip@^1.6.5:
+ version "1.6.6"
+ resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c"
+ dependencies:
+ concat-stream "1.6.0"
+ debug "2.6.9"
+ mkdirp "0.5.0"
+ yauzl "2.4.1"
+
extsprintf@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550"
+fast-deep-equal@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
+
fast-levenshtein@~2.0.4:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+fd-slicer@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
+ dependencies:
+ pend "~1.2.0"
+
figures@^1.3.5:
version "1.7.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
@@ -1459,6 +1534,14 @@ form-data@~2.1.1:
combined-stream "^1.0.5"
mime-types "^2.1.12"
+form-data@~2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf"
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.5"
+ mime-types "^2.1.12"
+
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@@ -1600,7 +1683,7 @@ globby@^5.0.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
-globby@^6.0.0:
+globby@^6.0.0, globby@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
dependencies:
@@ -1630,6 +1713,10 @@ har-schema@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
+har-schema@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+
har-validator@~4.2.1:
version "4.2.1"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
@@ -1637,6 +1724,13 @@ har-validator@~4.2.1:
ajv "^4.9.1"
har-schema "^1.0.5"
+har-validator@~5.0.3:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
+ dependencies:
+ ajv "^5.1.0"
+ har-schema "^2.0.0"
+
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@@ -1666,10 +1760,23 @@ hawk@~3.1.3:
hoek "2.x.x"
sntp "1.x.x"
+hawk@~6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038"
+ dependencies:
+ boom "4.x.x"
+ cryptiles "3.x.x"
+ hoek "4.x.x"
+ sntp "2.x.x"
+
hoek@2.x.x:
version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
+hoek@4.x.x:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
+
home-or-tmp@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
@@ -1710,6 +1817,14 @@ http-signature@~1.1.0:
jsprim "^1.2.2"
sshpk "^1.7.0"
+http-signature@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+ dependencies:
+ assert-plus "^1.0.0"
+ jsprim "^1.2.2"
+ sshpk "^1.7.0"
+
https-proxy-agent@1:
version "1.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6"
@@ -2005,6 +2120,10 @@ jsesc@~0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+json-schema-traverse@^0.3.0:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
+
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
@@ -2061,6 +2180,10 @@ jsx-ast-utils@^1.0.0, jsx-ast-utils@^1.3.4:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1"
+kew@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b"
+
kind-of@^3.0.2:
version "3.2.0"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.0.tgz#b58abe4d5c044ad33726a8c1525b48cf891bff07"
@@ -2301,12 +2424,22 @@ mime-db@~1.27.0:
version "1.27.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1"
+mime-db@~1.30.0:
+ version "1.30.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
+
mime-types@^2.1.12, mime-types@~2.1.7:
version "2.1.15"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed"
dependencies:
mime-db "~1.27.0"
+mime-types@~2.1.17:
+ version "2.1.17"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
+ dependencies:
+ mime-db "~1.30.0"
+
minimatch@3.0.3, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774"
@@ -2325,6 +2458,12 @@ minimist@~0.0.1:
version "0.0.10"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
+mkdirp@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12"
+ dependencies:
+ minimist "0.0.8"
+
mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@@ -2464,7 +2603,7 @@ number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
-oauth-sign@~0.8.1:
+oauth-sign@~0.8.1, oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
@@ -2544,6 +2683,10 @@ osenv@^0.1.4:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
+p-map@^1.1.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b"
+
pac-proxy-agent@1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d"
@@ -2609,14 +2752,26 @@ path-type@^1.0.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
+pend@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
+
performance-now@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
+performance-now@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+
pify@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+pify@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
+
pinkie-promise@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa"
@@ -2764,6 +2919,10 @@ qs@~6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
+qs@~6.5.1:
+ version "6.5.1"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
+
randomatic@^1.1.3:
version "1.1.6"
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb"
@@ -2968,6 +3127,33 @@ request@^2.81.0:
tunnel-agent "^0.6.0"
uuid "^3.0.0"
+request@^2.83.0:
+ version "2.83.0"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
+ dependencies:
+ aws-sign2 "~0.7.0"
+ aws4 "^1.6.0"
+ caseless "~0.12.0"
+ combined-stream "~1.0.5"
+ extend "~3.0.1"
+ forever-agent "~0.6.1"
+ form-data "~2.3.1"
+ har-validator "~5.0.3"
+ hawk "~6.0.2"
+ http-signature "~1.2.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.17"
+ oauth-sign "~0.8.2"
+ performance-now "^2.1.0"
+ qs "~6.5.1"
+ safe-buffer "^5.1.1"
+ stringstream "~0.0.5"
+ tough-cookie "~2.3.3"
+ tunnel-agent "^0.6.0"
+ uuid "^3.1.0"
+
require-from-string@^1.1.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418"
@@ -3020,7 +3206,7 @@ safe-buffer@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
-safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
@@ -3084,6 +3270,12 @@ sntp@1.x.x:
dependencies:
hoek "2.x.x"
+sntp@2.x.x:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8"
+ dependencies:
+ hoek "4.x.x"
+
socks-proxy-agent@2:
version "2.1.1"
resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3"
@@ -3204,7 +3396,7 @@ string_decoder@~1.0.3:
dependencies:
safe-buffer "~5.1.0"
-stringstream@~0.0.4:
+stringstream@~0.0.4, stringstream@~0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
@@ -3417,6 +3609,12 @@ tough-cookie@~2.3.0:
dependencies:
punycode "^1.4.1"
+tough-cookie@~2.3.3:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561"
+ dependencies:
+ punycode "^1.4.1"
+
trim-newlines@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@@ -3479,6 +3677,10 @@ uuid@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1"
+uuid@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
+
validate-npm-package-license@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
@@ -3568,3 +3770,9 @@ yargs@^3.5.4:
string-width "^1.0.1"
window-size "^0.1.4"
y18n "^3.2.0"
+
+yauzl@2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005"
+ dependencies:
+ fd-slicer "~1.0.1"
From 188c81e150f4390e9dcfbaaa912f43ecc1019481 Mon Sep 17 00:00:00 2001
From: Matthew Grill
Date: Fri, 8 Dec 2017 13:39:22 -0800
Subject: [PATCH 003/232] do not start chromedriver on ci env
---
core/nightwatch.conf.js | 33 ++++++++++++++++++++++++++++++
core/nightwatch.json | 25 ----------------------
core/package.json | 4 ++--
core/tests/nightwatch_bootstrap.js | 32 +++++++++++++++++++++--------
4 files changed, 58 insertions(+), 36 deletions(-)
create mode 100644 core/nightwatch.conf.js
delete mode 100644 core/nightwatch.json
diff --git a/core/nightwatch.conf.js b/core/nightwatch.conf.js
new file mode 100644
index 000000000000..04cdf4ff5efa
--- /dev/null
+++ b/core/nightwatch.conf.js
@@ -0,0 +1,33 @@
+const testingMode = process.env.TESTING_MODE || 'local';
+
+const options = {
+ src_folders: ['tests/Drupal/Nightwatch'],
+ output_folder: false,
+ custom_commands_path: '',
+ custom_assertions_path: '',
+ page_objects_path: '',
+ globals_path: 'tests/nightwatch_bootstrap.js',
+ selenium: {
+ start_process: false,
+ },
+ test_settings: {
+ default: {
+ selenium_port: 9515,
+ selenium_host: 'localhost',
+ default_path_prefix: '',
+ desiredCapabilities: {
+ browserName: 'chrome',
+ chromeOptions: {
+ args: ['--no-sandbox'],
+ },
+ acceptSslCerts: true,
+ },
+ },
+ },
+};
+
+if (testingMode === 'ci') {
+ options.output_folder = process.env.NIGHTWATCH_OUTPUT;
+}
+
+module.exports = options;
diff --git a/core/nightwatch.json b/core/nightwatch.json
deleted file mode 100644
index 439f5b5ae7fa..000000000000
--- a/core/nightwatch.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "src_folders": ["tests/Drupal/Nightwatch"],
- "output_folder": "reports",
- "custom_commands_path": "",
- "custom_assertions_path": "",
- "page_objects_path": "",
- "globals_path": "tests/nightwatch_bootstrap.js",
- "selenium": {
- "start_process": false
- },
- "test_settings": {
- "default": {
- "selenium_port": 9515,
- "selenium_host": "localhost",
- "default_path_prefix": "",
- "desiredCapabilities": {
- "browserName": "chrome",
- "chromeOptions": {
- "args": ["--no-sandbox"]
- },
- "acceptSslCerts": true
- }
- }
- }
-}
diff --git a/core/package.json b/core/package.json
index 2e13a539fa48..76707e7bc454 100644
--- a/core/package.json
+++ b/core/package.json
@@ -13,8 +13,8 @@
"lint:core-js-stats": "node ./node_modules/eslint/bin/eslint.js --format=./scripts/js/eslint-stats-by-type.js --ext=.es6.js . || exit 0",
"lint:css": "stylelint \"**/*.css\" || exit 0",
"lint:css-checkstyle": "stylelint \"**/*.css\" --custom-formatter ./node_modules/stylelint-checkstyle-formatter/index.js || exit 0",
- "test:js:ci": "./node_modules/.bin/nightwatch -o $NIGHTWATCH_OUTPUT -r junit",
- "test:js": "./node_modules/.bin/nightwatch"
+ "test:js:ci": "TESTING_MODE=ci ./node_modules/.bin/nightwatch -r junit",
+ "test:js": "TESTING_MODE=local ./node_modules/.bin/nightwatch"
},
"devDependencies": {
"babel-core": "6.24.1",
diff --git a/core/tests/nightwatch_bootstrap.js b/core/tests/nightwatch_bootstrap.js
index dcaa13bba6ad..cdedae862d6c 100644
--- a/core/tests/nightwatch_bootstrap.js
+++ b/core/tests/nightwatch_bootstrap.js
@@ -1,11 +1,25 @@
const chromedriver = require('chromedriver');
-module.exports = {
- before: function(done) {
- chromedriver.start();
- done();
- },
- after: function(done) {
- chromedriver.stop();
- done();
+const testingMode = process.env.TESTING_MODE || 'local';
+
+if (testingMode === 'local') {
+ module.exports = {
+ before: function(done) {
+ chromedriver.start();
+ done();
+ },
+ after: function(done) {
+ chromedriver.stop();
+ done();
+ }
+ };
+}
+else {
+ module.exports = {
+ before: function (done) {
+ done();
+ },
+ after: function (done) {
+ done();
+ }
}
-};
+}
From 65c809ca4ea672ed8af968943bb68661355aacd6 Mon Sep 17 00:00:00 2001
From: Sally Young
Date: Fri, 8 Dec 2017 22:16:57 +0000
Subject: [PATCH 004/232] Shift files around and have a separate environment
for the testbot
---
core/.gitignore | 3 +
core/nightwatch.conf.js | 48 ++++++
core/nightwatch.json | 25 ---
core/package.json | 11 +-
.../Drupal/Nightwatch/{ => Tests}/index.js | 0
.../Nightwatch/globals.js} | 0
core/tests/README.md | 16 ++
core/yarn.lock | 158 +++++++++++++++++-
8 files changed, 231 insertions(+), 30 deletions(-)
create mode 100644 core/nightwatch.conf.js
delete mode 100644 core/nightwatch.json
rename core/tests/Drupal/Nightwatch/{ => Tests}/index.js (100%)
rename core/tests/{nightwatch_bootstrap.js => Drupal/Nightwatch/globals.js} (100%)
diff --git a/core/.gitignore b/core/.gitignore
index be42d48852f0..db29d420b98a 100644
--- a/core/.gitignore
+++ b/core/.gitignore
@@ -9,3 +9,6 @@ phpunit.xml
# Ignore package-lock.json that is automatically created when adding
# dependencies by users of NPMv5.
package-lock.json
+
+# Ignore test reports
+reports
diff --git a/core/nightwatch.conf.js b/core/nightwatch.conf.js
new file mode 100644
index 000000000000..91d0193f0f4b
--- /dev/null
+++ b/core/nightwatch.conf.js
@@ -0,0 +1,48 @@
+const chromeArgs = ['--disable-notifications'];
+if (!process.env.HEADLESS_CHROME_DISABLED) {
+ chromeArgs.push('--headless');
+}
+
+const outputFolder = process.env.NIGHTWATCH_OUTPUT ? process.env.NIGHTWATCH_OUTPUT : 'reports/nightwatch';
+
+module.exports = {
+ 'src_folders': ['tests/Drupal/Nightwatch/Tests'],
+ 'output_folder': outputFolder,
+ 'custom_commands_path': '',
+ 'custom_assertions_path': '',
+ 'page_objects_path': '',
+ 'globals_path': 'tests/Drupal/Nightwatch/globals.js',
+ 'selenium': {
+ 'start_process': false,
+ },
+ 'test_settings': {
+ 'default': {
+ 'selenium_port': 9515,
+ 'selenium_host': 'localhost',
+ 'default_path_prefix': '',
+ 'desiredCapabilities': {
+ 'browserName': 'chrome',
+ 'acceptSslCerts': true,
+ 'args': [
+ '--headless',
+ '--disable-notifications',
+ ]
+ },
+ 'screenshots' : {
+ 'enabled' : true,
+ 'on_failure' : true,
+ 'on_error' : true,
+ 'path' : 'core/reports/nightwatch/screenshots'
+ },
+ },
+ 'testbot': {
+ 'desiredCapabilities': {
+ 'browserName': 'chrome',
+ 'chromeOptions': {
+ 'args': [ ...chromeArgs, '--no-sandbox'],
+ },
+ 'acceptSslCerts': true
+ }
+ }
+ }
+};
diff --git a/core/nightwatch.json b/core/nightwatch.json
deleted file mode 100644
index 439f5b5ae7fa..000000000000
--- a/core/nightwatch.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "src_folders": ["tests/Drupal/Nightwatch"],
- "output_folder": "reports",
- "custom_commands_path": "",
- "custom_assertions_path": "",
- "page_objects_path": "",
- "globals_path": "tests/nightwatch_bootstrap.js",
- "selenium": {
- "start_process": false
- },
- "test_settings": {
- "default": {
- "selenium_port": 9515,
- "selenium_host": "localhost",
- "default_path_prefix": "",
- "desiredCapabilities": {
- "browserName": "chrome",
- "chromeOptions": {
- "args": ["--no-sandbox"]
- },
- "acceptSslCerts": true
- }
- }
- }
-}
diff --git a/core/package.json b/core/package.json
index 2e13a539fa48..e9f51781ea67 100644
--- a/core/package.json
+++ b/core/package.json
@@ -3,6 +3,10 @@
"description": "Drupal is an open source content management platform powering millions of websites and applications.",
"license": "GPL-2.0",
"private": true,
+ "engines": {
+ "yarn": "1.x",
+ "node": "8.x"
+ },
"scripts": {
"build:js": "node ./scripts/js/babel-es6-build.js",
"build:js-dev": "cross-env NODE_ENV=development node ./scripts/js/babel-es6-build.js",
@@ -13,13 +17,16 @@
"lint:core-js-stats": "node ./node_modules/eslint/bin/eslint.js --format=./scripts/js/eslint-stats-by-type.js --ext=.es6.js . || exit 0",
"lint:css": "stylelint \"**/*.css\" || exit 0",
"lint:css-checkstyle": "stylelint \"**/*.css\" --custom-formatter ./node_modules/stylelint-checkstyle-formatter/index.js || exit 0",
- "test:js:ci": "./node_modules/.bin/nightwatch -o $NIGHTWATCH_OUTPUT -r junit",
- "test:js": "./node_modules/.bin/nightwatch"
+ "nightwatch": "node $NODE_DEBUG_OPTION -r babel-register ./node_modules/.bin/nightwatch",
+ "test": "if [ \"$NODE_ENV\" = \"testbot\" ] ; then yarn test:testbot; else yarn test:default; fi",
+ "test:default": "yarn nightwatch",
+ "test:testbot": "yarn nightwatch --env testbot"
},
"devDependencies": {
"babel-core": "6.24.1",
"babel-plugin-add-header-comment": "1.0.3",
"babel-preset-env": "1.4.0",
+ "babel-register": "^6.26.0",
"chalk": "^1.1.3",
"chokidar": "1.6.1",
"chromedriver": "^2.33.2",
diff --git a/core/tests/Drupal/Nightwatch/index.js b/core/tests/Drupal/Nightwatch/Tests/index.js
similarity index 100%
rename from core/tests/Drupal/Nightwatch/index.js
rename to core/tests/Drupal/Nightwatch/Tests/index.js
diff --git a/core/tests/nightwatch_bootstrap.js b/core/tests/Drupal/Nightwatch/globals.js
similarity index 100%
rename from core/tests/nightwatch_bootstrap.js
rename to core/tests/Drupal/Nightwatch/globals.js
diff --git a/core/tests/README.md b/core/tests/README.md
index 5dd6cc63944e..5a601f191e98 100644
--- a/core/tests/README.md
+++ b/core/tests/README.md
@@ -46,3 +46,19 @@ export SIMPLETEST_BASE_URL='http://d8.dev'
sudo -u www-data -E ./vendor/bin/phpunit -c core --testsuite functional
sudo -u www-data -E ./vendor/bin/phpunit -c core --testsuite functional-javascript
```
+
+## Nightwatch tests
+
+- Install [Node.js](https://nodejs.org/en/download/) and [yarn](https://yarnpkg.com/en/docs/install). The versions required are specificed inside core/package.json in the `engines` field
+- Install [Google Chrome](https://www.google.com/chrome/browser/desktop/index.html)
+- Inside the `core` folder, run `yarn install`
+- Again inside the `core` folder, run `yarn nightwatch` to run the tests. By default this will output reports to `core/reports`
+
+Some settings can be overridden with environment variables:
+
+| Variable | Default Value | Description |
+|------------|---------------|-------------|
+| `HEADLESS_CHROME_DISABLED` | `false` | If set to `true`, this will remove the `--headless` option passed to Chrome, allowing you to see tests running in realtime in the browser |
+| `NIGHTWATCH_OUTPUT` | `reports/nightwatch` | This will output the test results into `core/reports/nightwatch`. Passing a value here is relative to the `core` directory |
+| `NODE_ENV` | none | Setting this to `testbot` will cause Nightwatch to run with the settings specified in the `testbot` environment in `core/nightwatch.conf.js` |
+
diff --git a/core/yarn.lock b/core/yarn.lock
index af9c48c2eb0c..4830783c259e 100644
--- a/core/yarn.lock
+++ b/core/yarn.lock
@@ -206,6 +206,14 @@ babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
esutils "^2.0.2"
js-tokens "^3.0.0"
+babel-code-frame@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
+ dependencies:
+ chalk "^1.1.3"
+ esutils "^2.0.2"
+ js-tokens "^3.0.2"
+
babel-core@6.24.1, babel-core@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83"
@@ -230,6 +238,30 @@ babel-core@6.24.1, babel-core@^6.24.1:
slash "^1.0.0"
source-map "^0.5.0"
+babel-core@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8"
+ dependencies:
+ babel-code-frame "^6.26.0"
+ babel-generator "^6.26.0"
+ babel-helpers "^6.24.1"
+ babel-messages "^6.23.0"
+ babel-register "^6.26.0"
+ babel-runtime "^6.26.0"
+ babel-template "^6.26.0"
+ babel-traverse "^6.26.0"
+ babel-types "^6.26.0"
+ babylon "^6.18.0"
+ convert-source-map "^1.5.0"
+ debug "^2.6.8"
+ json5 "^0.5.1"
+ lodash "^4.17.4"
+ minimatch "^3.0.4"
+ path-is-absolute "^1.0.1"
+ private "^0.1.7"
+ slash "^1.0.0"
+ source-map "^0.5.6"
+
babel-generator@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944d52aa07c5d9497"
@@ -243,6 +275,19 @@ babel-generator@^6.24.1:
source-map "^0.5.0"
trim-right "^1.0.1"
+babel-generator@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5"
+ dependencies:
+ babel-messages "^6.23.0"
+ babel-runtime "^6.26.0"
+ babel-types "^6.26.0"
+ detect-indent "^4.0.0"
+ jsesc "^1.3.0"
+ lodash "^4.17.4"
+ source-map "^0.5.6"
+ trim-right "^1.0.1"
+
babel-helper-builder-binary-assignment-operator-visitor@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664"
@@ -615,6 +660,18 @@ babel-register@^6.24.1:
mkdirp "^0.5.1"
source-map-support "^0.4.2"
+babel-register@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
+ dependencies:
+ babel-core "^6.26.0"
+ babel-runtime "^6.26.0"
+ core-js "^2.5.0"
+ home-or-tmp "^2.0.0"
+ lodash "^4.17.4"
+ mkdirp "^0.5.1"
+ source-map-support "^0.4.15"
+
babel-runtime@^6.18.0, babel-runtime@^6.22.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
@@ -622,6 +679,13 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0:
core-js "^2.4.0"
regenerator-runtime "^0.10.0"
+babel-runtime@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+ dependencies:
+ core-js "^2.4.0"
+ regenerator-runtime "^0.11.0"
+
babel-template@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333"
@@ -632,6 +696,16 @@ babel-template@^6.24.1:
babylon "^6.11.0"
lodash "^4.2.0"
+babel-template@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
+ dependencies:
+ babel-runtime "^6.26.0"
+ babel-traverse "^6.26.0"
+ babel-types "^6.26.0"
+ babylon "^6.18.0"
+ lodash "^4.17.4"
+
babel-traverse@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695"
@@ -646,6 +720,20 @@ babel-traverse@^6.24.1:
invariant "^2.2.0"
lodash "^4.2.0"
+babel-traverse@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
+ dependencies:
+ babel-code-frame "^6.26.0"
+ babel-messages "^6.23.0"
+ babel-runtime "^6.26.0"
+ babel-types "^6.26.0"
+ babylon "^6.18.0"
+ debug "^2.6.8"
+ globals "^9.18.0"
+ invariant "^2.2.2"
+ lodash "^4.17.4"
+
babel-types@^6.19.0, babel-types@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975"
@@ -655,14 +743,31 @@ babel-types@^6.19.0, babel-types@^6.24.1:
lodash "^4.2.0"
to-fast-properties "^1.0.1"
+babel-types@^6.26.0:
+ version "6.26.0"
+ resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
+ dependencies:
+ babel-runtime "^6.26.0"
+ esutils "^2.0.2"
+ lodash "^4.17.4"
+ to-fast-properties "^1.0.3"
+
babylon@^6.11.0, babylon@^6.15.0:
version "6.17.0"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.0.tgz#37da948878488b9c4e3c4038893fa3314b3fc932"
+babylon@^6.18.0:
+ version "6.18.0"
+ resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
+
balanced-match@^0.4.0, balanced-match@^0.4.1:
version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
+balanced-match@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+
bcrypt-pbkdf@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
@@ -704,6 +809,13 @@ brace-expansion@^1.0.0:
balanced-match "^0.4.1"
concat-map "0.0.1"
+brace-expansion@^1.1.7:
+ version "1.1.8"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
braces@^1.8.2:
version "1.8.5"
resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
@@ -902,10 +1014,18 @@ convert-source-map@^1.1.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
+convert-source-map@^1.5.0:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
+
core-js@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
+core-js@^2.5.0:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
+
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -995,7 +1115,7 @@ data-uri-to-buffer@1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835"
-debug@2, debug@2.6.9:
+debug@2, debug@2.6.9, debug@^2.6.8:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
@@ -1672,6 +1792,10 @@ globals@^9.0.0, globals@^9.14.0:
version "9.17.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.17.0.tgz#0c0ca696d9b9bb694d2e5470bd37777caad50286"
+globals@^9.18.0:
+ version "9.18.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
+
globby@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
@@ -2101,6 +2225,10 @@ js-tokens@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
+js-tokens@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
+
js-yaml@^3.4.3, js-yaml@^3.5.1:
version "3.8.3"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.3.tgz#33a05ec481c850c8875929166fe1beb61c728766"
@@ -2142,7 +2270,7 @@ json3@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
-json5@^0.5.0:
+json5@^0.5.0, json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
@@ -2446,6 +2574,12 @@ minimatch@3.0.3, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3:
dependencies:
brace-expansion "^1.0.0"
+minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ dependencies:
+ brace-expansion "^1.1.7"
+
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
@@ -2732,7 +2866,7 @@ path-exists@^2.0.0:
dependencies:
pinkie-promise "^2.0.0"
-path-is-absolute@^1.0.0:
+path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -2882,6 +3016,10 @@ private@^0.1.6:
version "0.1.7"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"
+private@^0.1.7:
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
+
process-nextick-args@~1.0.6:
version "1.0.7"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
@@ -3049,6 +3187,10 @@ regenerator-runtime@^0.10.0:
version "0.10.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
+regenerator-runtime@^0.11.0:
+ version "0.11.1"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+
regenerator-transform@0.9.11:
version "0.9.11"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283"
@@ -3291,6 +3433,12 @@ socks@~1.1.5:
ip "^1.1.4"
smart-buffer "^1.0.13"
+source-map-support@^0.4.15:
+ version "0.4.18"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
+ dependencies:
+ source-map "^0.5.6"
+
source-map-support@^0.4.2:
version "0.4.15"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
@@ -3603,6 +3751,10 @@ to-fast-properties@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320"
+to-fast-properties@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
+
tough-cookie@~2.3.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
From 86d511fe8b24aea1f90f27be65b6d871fa89292e Mon Sep 17 00:00:00 2001
From: Matthew Grill
Date: Fri, 8 Dec 2017 14:40:22 -0800
Subject: [PATCH 005/232] linting cleanup
---
core/.eslintignore | 2 +
core/nightwatch.conf.js | 64 ++++++++++-----------
core/tests/Drupal/Nightwatch/Tests/index.js | 4 +-
core/tests/Drupal/Nightwatch/globals.js | 15 ++---
4 files changed, 44 insertions(+), 41 deletions(-)
diff --git a/core/.eslintignore b/core/.eslintignore
index fae394892bb7..874d687495a3 100644
--- a/core/.eslintignore
+++ b/core/.eslintignore
@@ -4,3 +4,5 @@ node_modules/**/*
*.js
!*.es6.js
modules/locale/tests/locale_test.es6.js
+!nightwatch.conf.js
+!tests/Drupal/Nightwatch/**/*.js
diff --git a/core/nightwatch.conf.js b/core/nightwatch.conf.js
index 91d0193f0f4b..c659e63e6baf 100644
--- a/core/nightwatch.conf.js
+++ b/core/nightwatch.conf.js
@@ -6,43 +6,43 @@ if (!process.env.HEADLESS_CHROME_DISABLED) {
const outputFolder = process.env.NIGHTWATCH_OUTPUT ? process.env.NIGHTWATCH_OUTPUT : 'reports/nightwatch';
module.exports = {
- 'src_folders': ['tests/Drupal/Nightwatch/Tests'],
- 'output_folder': outputFolder,
- 'custom_commands_path': '',
- 'custom_assertions_path': '',
- 'page_objects_path': '',
- 'globals_path': 'tests/Drupal/Nightwatch/globals.js',
- 'selenium': {
- 'start_process': false,
+ src_folders: ['tests/Drupal/Nightwatch/Tests'],
+ output_folder: outputFolder,
+ custom_commands_path: '',
+ custom_assertions_path: '',
+ page_objects_path: '',
+ globals_path: 'tests/Drupal/Nightwatch/globals.js',
+ selenium: {
+ start_process: false,
},
- 'test_settings': {
- 'default': {
- 'selenium_port': 9515,
- 'selenium_host': 'localhost',
- 'default_path_prefix': '',
- 'desiredCapabilities': {
- 'browserName': 'chrome',
- 'acceptSslCerts': true,
- 'args': [
+ test_settings: {
+ defaul: {
+ selenium_port: 9515,
+ selenium_host: 'localhost',
+ default_path_prefix: '',
+ desiredCapabilities: {
+ browserName: 'chrome',
+ acceptSslCerts: true,
+ arg: [
'--headless',
'--disable-notifications',
- ]
+ ],
},
- 'screenshots' : {
- 'enabled' : true,
- 'on_failure' : true,
- 'on_error' : true,
- 'path' : 'core/reports/nightwatch/screenshots'
+ screenshots: {
+ enabled: true,
+ on_failure: true,
+ on_error: true,
+ path: 'core/reports/nightwatch/screenshots',
},
},
- 'testbot': {
- 'desiredCapabilities': {
- 'browserName': 'chrome',
- 'chromeOptions': {
- 'args': [ ...chromeArgs, '--no-sandbox'],
+ testbot: {
+ desiredCapabilities: {
+ browserName: 'chrome',
+ chromeOptions: {
+ args: [...chromeArgs, '--no-sandbox'],
},
- 'acceptSslCerts': true
- }
- }
- }
+ acceptSslCerts: true,
+ },
+ },
+ },
};
diff --git a/core/tests/Drupal/Nightwatch/Tests/index.js b/core/tests/Drupal/Nightwatch/Tests/index.js
index b94607280c3b..4c9aa83b9538 100644
--- a/core/tests/Drupal/Nightwatch/Tests/index.js
+++ b/core/tests/Drupal/Nightwatch/Tests/index.js
@@ -1,9 +1,9 @@
module.exports = {
- 'Demo Drupal.org' : function (browser) {
+ 'Demo Drupal.org': (browser) => {
browser
.url('https://www.drupal.org/')
.waitForElementVisible('body', 1000)
.assert.containsText('body', 'Launch, manage, and scale ambitious digital experiences—with the flexibility to build great websites or push beyond the browser. Proudly open source.')
.end();
- }
+ },
};
diff --git a/core/tests/Drupal/Nightwatch/globals.js b/core/tests/Drupal/Nightwatch/globals.js
index cdedae862d6c..0385469e1b38 100644
--- a/core/tests/Drupal/Nightwatch/globals.js
+++ b/core/tests/Drupal/Nightwatch/globals.js
@@ -1,25 +1,26 @@
const chromedriver = require('chromedriver');
+
const testingMode = process.env.TESTING_MODE || 'local';
if (testingMode === 'local') {
module.exports = {
- before: function(done) {
+ before: (done) => {
chromedriver.start();
done();
},
- after: function(done) {
+ after: (done) => {
chromedriver.stop();
done();
- }
+ },
};
}
else {
module.exports = {
- before: function (done) {
+ before: (done) => {
done();
},
- after: function (done) {
+ after: (done) => {
done();
- }
- }
+ },
+ };
}
From ff614af4b9086bb724d1d3ad5200622cbb35140d Mon Sep 17 00:00:00 2001
From: Sally Young
Date: Fri, 8 Dec 2017 22:53:54 +0000
Subject: [PATCH 006/232] Doesn't need a separate ci command as nightwatch is
already running in a container
---
core/nightwatch.conf.js | 13 ++-------
core/package.json | 7 ++---
core/tests/Drupal/Nightwatch/globals.js | 36 +++++++++----------------
core/tests/README.md | 2 +-
4 files changed, 18 insertions(+), 40 deletions(-)
diff --git a/core/nightwatch.conf.js b/core/nightwatch.conf.js
index c659e63e6baf..5234f6a76e2e 100644
--- a/core/nightwatch.conf.js
+++ b/core/nightwatch.conf.js
@@ -16,7 +16,7 @@ module.exports = {
start_process: false,
},
test_settings: {
- defaul: {
+ default: {
selenium_port: 9515,
selenium_host: 'localhost',
default_path_prefix: '',
@@ -32,16 +32,7 @@ module.exports = {
enabled: true,
on_failure: true,
on_error: true,
- path: 'core/reports/nightwatch/screenshots',
- },
- },
- testbot: {
- desiredCapabilities: {
- browserName: 'chrome',
- chromeOptions: {
- args: [...chromeArgs, '--no-sandbox'],
- },
- acceptSslCerts: true,
+ path: `${outputFolder}/core/reports/nightwatch/screenshots`,
},
},
},
diff --git a/core/package.json b/core/package.json
index e9f51781ea67..2515d61853ff 100644
--- a/core/package.json
+++ b/core/package.json
@@ -18,9 +18,7 @@
"lint:css": "stylelint \"**/*.css\" || exit 0",
"lint:css-checkstyle": "stylelint \"**/*.css\" --custom-formatter ./node_modules/stylelint-checkstyle-formatter/index.js || exit 0",
"nightwatch": "node $NODE_DEBUG_OPTION -r babel-register ./node_modules/.bin/nightwatch",
- "test": "if [ \"$NODE_ENV\" = \"testbot\" ] ; then yarn test:testbot; else yarn test:default; fi",
- "test:default": "yarn nightwatch",
- "test:testbot": "yarn nightwatch --env testbot"
+ "test": "yarn nightwatch"
},
"devDependencies": {
"babel-core": "6.24.1",
@@ -63,6 +61,5 @@
}
]
]
- },
- "dependencies": {}
+ }
}
diff --git a/core/tests/Drupal/Nightwatch/globals.js b/core/tests/Drupal/Nightwatch/globals.js
index 0385469e1b38..e69b8d184a9c 100644
--- a/core/tests/Drupal/Nightwatch/globals.js
+++ b/core/tests/Drupal/Nightwatch/globals.js
@@ -1,26 +1,16 @@
-const chromedriver = require('chromedriver');
+import 'chromedriver' from chromedriver;
-const testingMode = process.env.TESTING_MODE || 'local';
-
-if (testingMode === 'local') {
- module.exports = {
- before: (done) => {
+module.exports = {
+ before: (done) => {
+ if (process.env.NODE_ENV !== 'testbot') {
chromedriver.start();
- done();
- },
- after: (done) => {
+ }
+ done();
+ },
+ after: (done) => {
+ if (process.env.NODE_ENV !== 'testbot') {
chromedriver.stop();
- done();
- },
- };
-}
-else {
- module.exports = {
- before: (done) => {
- done();
- },
- after: (done) => {
- done();
- },
- };
-}
+ }
+ done();
+ },
+};
diff --git a/core/tests/README.md b/core/tests/README.md
index 5a601f191e98..7723a6680d7a 100644
--- a/core/tests/README.md
+++ b/core/tests/README.md
@@ -60,5 +60,5 @@ Some settings can be overridden with environment variables:
|------------|---------------|-------------|
| `HEADLESS_CHROME_DISABLED` | `false` | If set to `true`, this will remove the `--headless` option passed to Chrome, allowing you to see tests running in realtime in the browser |
| `NIGHTWATCH_OUTPUT` | `reports/nightwatch` | This will output the test results into `core/reports/nightwatch`. Passing a value here is relative to the `core` directory |
-| `NODE_ENV` | none | Setting this to `testbot` will cause Nightwatch to run with the settings specified in the `testbot` environment in `core/nightwatch.conf.js` |
+| `NODE_ENV` | none | Setting this to `testbot` will cause Nightwatch to run with settings specific to the Drupal.org testbot |
From a54317ee64f883744bf37d2658d5139eb0f89236 Mon Sep 17 00:00:00 2001
From: Sally Young
Date: Fri, 8 Dec 2017 22:55:32 +0000
Subject: [PATCH 007/232] fix path
---
core/nightwatch.conf.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/nightwatch.conf.js b/core/nightwatch.conf.js
index 5234f6a76e2e..dbc799ade19c 100644
--- a/core/nightwatch.conf.js
+++ b/core/nightwatch.conf.js
@@ -32,7 +32,7 @@ module.exports = {
enabled: true,
on_failure: true,
on_error: true,
- path: `${outputFolder}/core/reports/nightwatch/screenshots`,
+ path: `${outputFolder}/screenshots`,
},
},
},
From df054bf83e12b55a8c05ad33f13cc56dfc5d0b52 Mon Sep 17 00:00:00 2001
From: Sally Young
Date: Fri, 8 Dec 2017 23:09:20 +0000
Subject: [PATCH 008/232] fight me
---
core/package.json | 2 +-
core/tests/Drupal/Nightwatch/globals.js | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/core/package.json b/core/package.json
index 2515d61853ff..277e4d568de8 100644
--- a/core/package.json
+++ b/core/package.json
@@ -47,7 +47,7 @@
[
"env",
{
- "modules": false,
+ "modules": "commonjs",
"targets": {
"browsers": [
"ie >= 9",
diff --git a/core/tests/Drupal/Nightwatch/globals.js b/core/tests/Drupal/Nightwatch/globals.js
index e69b8d184a9c..6880cc31248b 100644
--- a/core/tests/Drupal/Nightwatch/globals.js
+++ b/core/tests/Drupal/Nightwatch/globals.js
@@ -1,4 +1,4 @@
-import 'chromedriver' from chromedriver;
+import chromedriver from 'chromedriver';
module.exports = {
before: (done) => {
From c5d30c2abe236b7cc964b9fcc01487c00820d3ab Mon Sep 17 00:00:00 2001
From: Sally Young
Date: Fri, 8 Dec 2017 23:10:49 +0000
Subject: [PATCH 009/232] srsly
---
core/package.json | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/core/package.json b/core/package.json
index 277e4d568de8..cd96731a933c 100644
--- a/core/package.json
+++ b/core/package.json
@@ -56,7 +56,8 @@
"opera >= 12",
"safari >= 5",
"chrome >= 56"
- ]
+ ],
+ "node": "current"
}
}
]
From 9b587cab805af5f7830593b05ae1e16778691bf7 Mon Sep 17 00:00:00 2001
From: Daniel Wehner
Date: Fri, 8 Dec 2017 23:28:27 +0000
Subject: [PATCH 010/232] add a way to configure the hostname
---
core/nightwatch.conf.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/core/nightwatch.conf.js b/core/nightwatch.conf.js
index dbc799ade19c..b66447aa34a9 100644
--- a/core/nightwatch.conf.js
+++ b/core/nightwatch.conf.js
@@ -4,6 +4,7 @@ if (!process.env.HEADLESS_CHROME_DISABLED) {
}
const outputFolder = process.env.NIGHTWATCH_OUTPUT ? process.env.NIGHTWATCH_OUTPUT : 'reports/nightwatch';
+const hostname = process.env.NIGHTWATCH_HOSTNAME ? process.env.NIGHTWATCH_HOSTNAME : 'localhost';
module.exports = {
src_folders: ['tests/Drupal/Nightwatch/Tests'],
@@ -18,7 +19,7 @@ module.exports = {
test_settings: {
default: {
selenium_port: 9515,
- selenium_host: 'localhost',
+ selenium_host: hostname,
default_path_prefix: '',
desiredCapabilities: {
browserName: 'chrome',
From 8b6b087eb2464958a1c63f4c7ce8eeb4ee007483 Mon Sep 17 00:00:00 2001
From: Sally Young
Date: Fri, 8 Dec 2017 23:32:09 +0000
Subject: [PATCH 011/232] add babel environments
---
core/package.json | 63 ++++++++++++++++++++++++++++++-----------------
1 file changed, 40 insertions(+), 23 deletions(-)
diff --git a/core/package.json b/core/package.json
index cd96731a933c..dac158b20a9e 100644
--- a/core/package.json
+++ b/core/package.json
@@ -8,10 +8,10 @@
"node": "8.x"
},
"scripts": {
- "build:js": "node ./scripts/js/babel-es6-build.js",
- "build:js-dev": "cross-env NODE_ENV=development node ./scripts/js/babel-es6-build.js",
- "watch:js": "node ./scripts/js/babel-es6-watch.js",
- "watch:js-dev": "cross-env NODE_ENV=development node ./scripts/js/babel-es6-watch.js",
+ "build:js": "BABEL_ENV=legacy node ./scripts/js/babel-es6-build.js",
+ "build:js-dev": "cross-env NODE_ENV=development node BABEL_ENV=legacy ./scripts/js/babel-es6-build.js",
+ "watch:js": "BABEL_ENV=legacy node ./scripts/js/babel-es6-watch.js",
+ "watch:js-dev": "cross-env NODE_ENV=development BABEL_ENV=legacy node ./scripts/js/babel-es6-watch.js",
"lint:core-js": "node ./node_modules/eslint/bin/eslint.js --ext=.es6.js . || exit 0",
"lint:core-js-passing": "node ./node_modules/eslint/bin/eslint.js --quiet --config=.eslintrc.passing.json --ext=.es6.js . || exit 0",
"lint:core-js-stats": "node ./node_modules/eslint/bin/eslint.js --format=./scripts/js/eslint-stats-by-type.js --ext=.es6.js . || exit 0",
@@ -42,25 +42,42 @@
"stylelint-config-standard": "^16.0.0",
"stylelint-no-browser-hacks": "^1.0.2"
},
+ "//": "'development is the default environment, and legacy is for transpiling the old jQuery codebase",
"babel": {
- "presets": [
- [
- "env",
- {
- "modules": "commonjs",
- "targets": {
- "browsers": [
- "ie >= 9",
- "edge >= 13",
- "firefox >= 5",
- "opera >= 12",
- "safari >= 5",
- "chrome >= 56"
- ],
- "node": "current"
- }
- }
- ]
- ]
+ "env": {
+ "development": {
+ "presets": [
+ [
+ "env",
+ {
+ "modules": "commonjs",
+ "targets": {
+ "node": "current"
+ }
+ }
+ ]
+ ]
+ },
+ "legacy": {
+ "presets": [
+ [
+ "env",
+ {
+ "modules": "commonjs",
+ "targets": {
+ "browsers": [
+ "ie >= 9",
+ "edge >= 13",
+ "firefox >= 5",
+ "opera >= 12",
+ "safari >= 5",
+ "chrome >= 56"
+ ]
+ }
+ }
+ ]
+ ]
+ }
+ }
}
}
From 53824a431e05c316ba52cdf4ea8b2f806e4752f9 Mon Sep 17 00:00:00 2001
From: Matthew Grill
Date: Fri, 8 Dec 2017 15:40:01 -0800
Subject: [PATCH 012/232] do not adjust old codebase with commonjs modules
settings
---
core/package.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/core/package.json b/core/package.json
index dac158b20a9e..b088b716c4ec 100644
--- a/core/package.json
+++ b/core/package.json
@@ -8,9 +8,9 @@
"node": "8.x"
},
"scripts": {
- "build:js": "BABEL_ENV=legacy node ./scripts/js/babel-es6-build.js",
+ "build:js": "cross-env BABEL_ENV=legacy node ./scripts/js/babel-es6-build.js",
"build:js-dev": "cross-env NODE_ENV=development node BABEL_ENV=legacy ./scripts/js/babel-es6-build.js",
- "watch:js": "BABEL_ENV=legacy node ./scripts/js/babel-es6-watch.js",
+ "watch:js": "cross-env BABEL_ENV=legacy node ./scripts/js/babel-es6-watch.js",
"watch:js-dev": "cross-env NODE_ENV=development BABEL_ENV=legacy node ./scripts/js/babel-es6-watch.js",
"lint:core-js": "node ./node_modules/eslint/bin/eslint.js --ext=.es6.js . || exit 0",
"lint:core-js-passing": "node ./node_modules/eslint/bin/eslint.js --quiet --config=.eslintrc.passing.json --ext=.es6.js . || exit 0",
@@ -63,7 +63,7 @@
[
"env",
{
- "modules": "commonjs",
+ "modules": false,
"targets": {
"browsers": [
"ie >= 9",
From b7de174f62b723ce3dc6f39f27d3db2301ed3a26 Mon Sep 17 00:00:00 2001
From: Matthew Grill
Date: Mon, 11 Dec 2017 07:01:02 -0800
Subject: [PATCH 013/232] temporarily remove babel-register
---
core/package.json | 6 +-
core/tests/Drupal/Nightwatch/globals.js | 2 +-
core/yarn.lock | 158 +-----------------------
3 files changed, 6 insertions(+), 160 deletions(-)
diff --git a/core/package.json b/core/package.json
index b088b716c4ec..e4a41317061c 100644
--- a/core/package.json
+++ b/core/package.json
@@ -17,14 +17,12 @@
"lint:core-js-stats": "node ./node_modules/eslint/bin/eslint.js --format=./scripts/js/eslint-stats-by-type.js --ext=.es6.js . || exit 0",
"lint:css": "stylelint \"**/*.css\" || exit 0",
"lint:css-checkstyle": "stylelint \"**/*.css\" --custom-formatter ./node_modules/stylelint-checkstyle-formatter/index.js || exit 0",
- "nightwatch": "node $NODE_DEBUG_OPTION -r babel-register ./node_modules/.bin/nightwatch",
- "test": "yarn nightwatch"
+ "test:js": "./node_modules/.bin/nightwatch"
},
"devDependencies": {
"babel-core": "6.24.1",
"babel-plugin-add-header-comment": "1.0.3",
"babel-preset-env": "1.4.0",
- "babel-register": "^6.26.0",
"chalk": "^1.1.3",
"chokidar": "1.6.1",
"chromedriver": "^2.33.2",
@@ -50,7 +48,7 @@
[
"env",
{
- "modules": "commonjs",
+ "modules": "false",
"targets": {
"node": "current"
}
diff --git a/core/tests/Drupal/Nightwatch/globals.js b/core/tests/Drupal/Nightwatch/globals.js
index 6880cc31248b..3786eeaa5c9e 100644
--- a/core/tests/Drupal/Nightwatch/globals.js
+++ b/core/tests/Drupal/Nightwatch/globals.js
@@ -1,4 +1,4 @@
-import chromedriver from 'chromedriver';
+const chromedriver = require('chromedriver');
module.exports = {
before: (done) => {
diff --git a/core/yarn.lock b/core/yarn.lock
index 4830783c259e..af9c48c2eb0c 100644
--- a/core/yarn.lock
+++ b/core/yarn.lock
@@ -206,14 +206,6 @@ babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
esutils "^2.0.2"
js-tokens "^3.0.0"
-babel-code-frame@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
- dependencies:
- chalk "^1.1.3"
- esutils "^2.0.2"
- js-tokens "^3.0.2"
-
babel-core@6.24.1, babel-core@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.24.1.tgz#8c428564dce1e1f41fb337ec34f4c3b022b5ad83"
@@ -238,30 +230,6 @@ babel-core@6.24.1, babel-core@^6.24.1:
slash "^1.0.0"
source-map "^0.5.0"
-babel-core@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8"
- dependencies:
- babel-code-frame "^6.26.0"
- babel-generator "^6.26.0"
- babel-helpers "^6.24.1"
- babel-messages "^6.23.0"
- babel-register "^6.26.0"
- babel-runtime "^6.26.0"
- babel-template "^6.26.0"
- babel-traverse "^6.26.0"
- babel-types "^6.26.0"
- babylon "^6.18.0"
- convert-source-map "^1.5.0"
- debug "^2.6.8"
- json5 "^0.5.1"
- lodash "^4.17.4"
- minimatch "^3.0.4"
- path-is-absolute "^1.0.1"
- private "^0.1.7"
- slash "^1.0.0"
- source-map "^0.5.6"
-
babel-generator@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.24.1.tgz#e715f486c58ded25649d888944d52aa07c5d9497"
@@ -275,19 +243,6 @@ babel-generator@^6.24.1:
source-map "^0.5.0"
trim-right "^1.0.1"
-babel-generator@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5"
- dependencies:
- babel-messages "^6.23.0"
- babel-runtime "^6.26.0"
- babel-types "^6.26.0"
- detect-indent "^4.0.0"
- jsesc "^1.3.0"
- lodash "^4.17.4"
- source-map "^0.5.6"
- trim-right "^1.0.1"
-
babel-helper-builder-binary-assignment-operator-visitor@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664"
@@ -660,18 +615,6 @@ babel-register@^6.24.1:
mkdirp "^0.5.1"
source-map-support "^0.4.2"
-babel-register@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
- dependencies:
- babel-core "^6.26.0"
- babel-runtime "^6.26.0"
- core-js "^2.5.0"
- home-or-tmp "^2.0.0"
- lodash "^4.17.4"
- mkdirp "^0.5.1"
- source-map-support "^0.4.15"
-
babel-runtime@^6.18.0, babel-runtime@^6.22.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
@@ -679,13 +622,6 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0:
core-js "^2.4.0"
regenerator-runtime "^0.10.0"
-babel-runtime@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
- dependencies:
- core-js "^2.4.0"
- regenerator-runtime "^0.11.0"
-
babel-template@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.24.1.tgz#04ae514f1f93b3a2537f2a0f60a5a45fb8308333"
@@ -696,16 +632,6 @@ babel-template@^6.24.1:
babylon "^6.11.0"
lodash "^4.2.0"
-babel-template@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
- dependencies:
- babel-runtime "^6.26.0"
- babel-traverse "^6.26.0"
- babel-types "^6.26.0"
- babylon "^6.18.0"
- lodash "^4.17.4"
-
babel-traverse@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.24.1.tgz#ab36673fd356f9a0948659e7b338d5feadb31695"
@@ -720,20 +646,6 @@ babel-traverse@^6.24.1:
invariant "^2.2.0"
lodash "^4.2.0"
-babel-traverse@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
- dependencies:
- babel-code-frame "^6.26.0"
- babel-messages "^6.23.0"
- babel-runtime "^6.26.0"
- babel-types "^6.26.0"
- babylon "^6.18.0"
- debug "^2.6.8"
- globals "^9.18.0"
- invariant "^2.2.2"
- lodash "^4.17.4"
-
babel-types@^6.19.0, babel-types@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.24.1.tgz#a136879dc15b3606bda0d90c1fc74304c2ff0975"
@@ -743,31 +655,14 @@ babel-types@^6.19.0, babel-types@^6.24.1:
lodash "^4.2.0"
to-fast-properties "^1.0.1"
-babel-types@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
- dependencies:
- babel-runtime "^6.26.0"
- esutils "^2.0.2"
- lodash "^4.17.4"
- to-fast-properties "^1.0.3"
-
babylon@^6.11.0, babylon@^6.15.0:
version "6.17.0"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.0.tgz#37da948878488b9c4e3c4038893fa3314b3fc932"
-babylon@^6.18.0:
- version "6.18.0"
- resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
-
balanced-match@^0.4.0, balanced-match@^0.4.1:
version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
-balanced-match@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
-
bcrypt-pbkdf@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d"
@@ -809,13 +704,6 @@ brace-expansion@^1.0.0:
balanced-match "^0.4.1"
concat-map "0.0.1"
-brace-expansion@^1.1.7:
- version "1.1.8"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
- dependencies:
- balanced-match "^1.0.0"
- concat-map "0.0.1"
-
braces@^1.8.2:
version "1.8.5"
resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7"
@@ -1014,18 +902,10 @@ convert-source-map@^1.1.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
-convert-source-map@^1.5.0:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
-
core-js@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
-core-js@^2.5.0:
- version "2.5.1"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
-
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -1115,7 +995,7 @@ data-uri-to-buffer@1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835"
-debug@2, debug@2.6.9, debug@^2.6.8:
+debug@2, debug@2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
@@ -1792,10 +1672,6 @@ globals@^9.0.0, globals@^9.14.0:
version "9.17.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.17.0.tgz#0c0ca696d9b9bb694d2e5470bd37777caad50286"
-globals@^9.18.0:
- version "9.18.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
-
globby@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
@@ -2225,10 +2101,6 @@ js-tokens@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
-js-tokens@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
-
js-yaml@^3.4.3, js-yaml@^3.5.1:
version "3.8.3"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.3.tgz#33a05ec481c850c8875929166fe1beb61c728766"
@@ -2270,7 +2142,7 @@ json3@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
-json5@^0.5.0, json5@^0.5.1:
+json5@^0.5.0:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
@@ -2574,12 +2446,6 @@ minimatch@3.0.3, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3:
dependencies:
brace-expansion "^1.0.0"
-minimatch@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
- dependencies:
- brace-expansion "^1.1.7"
-
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
@@ -2866,7 +2732,7 @@ path-exists@^2.0.0:
dependencies:
pinkie-promise "^2.0.0"
-path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
+path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -3016,10 +2882,6 @@ private@^0.1.6:
version "0.1.7"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"
-private@^0.1.7:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
-
process-nextick-args@~1.0.6:
version "1.0.7"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
@@ -3187,10 +3049,6 @@ regenerator-runtime@^0.10.0:
version "0.10.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
-regenerator-runtime@^0.11.0:
- version "0.11.1"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
-
regenerator-transform@0.9.11:
version "0.9.11"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283"
@@ -3433,12 +3291,6 @@ socks@~1.1.5:
ip "^1.1.4"
smart-buffer "^1.0.13"
-source-map-support@^0.4.15:
- version "0.4.18"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
- dependencies:
- source-map "^0.5.6"
-
source-map-support@^0.4.2:
version "0.4.15"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
@@ -3751,10 +3603,6 @@ to-fast-properties@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320"
-to-fast-properties@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
-
tough-cookie@~2.3.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
From 899414a5cafed0ec5bfad1f708b472439d3e35cc Mon Sep 17 00:00:00 2001
From: Matthew Grill
Date: Mon, 11 Dec 2017 08:27:11 -0800
Subject: [PATCH 014/232] adjust args to actually pass correctly.
---
core/nightwatch.conf.js | 16 +++++++---------
core/package.json | 2 +-
2 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/core/nightwatch.conf.js b/core/nightwatch.conf.js
index b66447aa34a9..4e699de7fcc0 100644
--- a/core/nightwatch.conf.js
+++ b/core/nightwatch.conf.js
@@ -1,8 +1,3 @@
-const chromeArgs = ['--disable-notifications'];
-if (!process.env.HEADLESS_CHROME_DISABLED) {
- chromeArgs.push('--headless');
-}
-
const outputFolder = process.env.NIGHTWATCH_OUTPUT ? process.env.NIGHTWATCH_OUTPUT : 'reports/nightwatch';
const hostname = process.env.NIGHTWATCH_HOSTNAME ? process.env.NIGHTWATCH_HOSTNAME : 'localhost';
@@ -24,10 +19,13 @@ module.exports = {
desiredCapabilities: {
browserName: 'chrome',
acceptSslCerts: true,
- arg: [
- '--headless',
- '--disable-notifications',
- ],
+ chromeOptions: {
+ args: [
+ '--headless',
+ '--disable-gpu',
+ '--disable-notifications',
+ ],
+ },
},
screenshots: {
enabled: true,
diff --git a/core/package.json b/core/package.json
index e4a41317061c..cf87761f7ce7 100644
--- a/core/package.json
+++ b/core/package.json
@@ -17,7 +17,7 @@
"lint:core-js-stats": "node ./node_modules/eslint/bin/eslint.js --format=./scripts/js/eslint-stats-by-type.js --ext=.es6.js . || exit 0",
"lint:css": "stylelint \"**/*.css\" || exit 0",
"lint:css-checkstyle": "stylelint \"**/*.css\" --custom-formatter ./node_modules/stylelint-checkstyle-formatter/index.js || exit 0",
- "test:js": "./node_modules/.bin/nightwatch"
+ "test:js": "./node_modules/.bin/nightwatch "
},
"devDependencies": {
"babel-core": "6.24.1",
From 05a1417229bb69a8d3e9b0b4eb95a922e11eec35 Mon Sep 17 00:00:00 2001
From: Matthew Grill
Date: Mon, 11 Dec 2017 08:34:43 -0800
Subject: [PATCH 015/232] actually use chromeArgs array
---
core/nightwatch.conf.js | 11 ++++++-----
core/package.json | 2 +-
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/core/nightwatch.conf.js b/core/nightwatch.conf.js
index 4e699de7fcc0..46de26f1fd93 100644
--- a/core/nightwatch.conf.js
+++ b/core/nightwatch.conf.js
@@ -1,3 +1,8 @@
+const chromeArgs = ['--disable-gpu'];
+if (!process.env.HEADLESS_CHROME_DISABLED) {
+ chromeArgs.push('--headless');
+}
+
const outputFolder = process.env.NIGHTWATCH_OUTPUT ? process.env.NIGHTWATCH_OUTPUT : 'reports/nightwatch';
const hostname = process.env.NIGHTWATCH_HOSTNAME ? process.env.NIGHTWATCH_HOSTNAME : 'localhost';
@@ -20,11 +25,7 @@ module.exports = {
browserName: 'chrome',
acceptSslCerts: true,
chromeOptions: {
- args: [
- '--headless',
- '--disable-gpu',
- '--disable-notifications',
- ],
+ args: chromeArgs,
},
},
screenshots: {
diff --git a/core/package.json b/core/package.json
index cf87761f7ce7..e4a41317061c 100644
--- a/core/package.json
+++ b/core/package.json
@@ -17,7 +17,7 @@
"lint:core-js-stats": "node ./node_modules/eslint/bin/eslint.js --format=./scripts/js/eslint-stats-by-type.js --ext=.es6.js . || exit 0",
"lint:css": "stylelint \"**/*.css\" || exit 0",
"lint:css-checkstyle": "stylelint \"**/*.css\" --custom-formatter ./node_modules/stylelint-checkstyle-formatter/index.js || exit 0",
- "test:js": "./node_modules/.bin/nightwatch "
+ "test:js": "./node_modules/.bin/nightwatch"
},
"devDependencies": {
"babel-core": "6.24.1",
From 20877b539cfd0453d8839027f25bc6daacb3146d Mon Sep 17 00:00:00 2001
From: Matthew Grill
Date: Mon, 11 Dec 2017 10:46:05 -0800
Subject: [PATCH 016/232] custom command for adding baseurl prefix
---
core/nightwatch.conf.js | 2 +-
core/tests/Drupal/Nightwatch/Commands/drupalURL.js | 8 ++++++++
core/tests/Drupal/Nightwatch/Tests/index.js | 4 ++--
core/tests/README.md | 2 +-
4 files changed, 12 insertions(+), 4 deletions(-)
create mode 100644 core/tests/Drupal/Nightwatch/Commands/drupalURL.js
diff --git a/core/nightwatch.conf.js b/core/nightwatch.conf.js
index 46de26f1fd93..081a9e013876 100644
--- a/core/nightwatch.conf.js
+++ b/core/nightwatch.conf.js
@@ -9,7 +9,7 @@ const hostname = process.env.NIGHTWATCH_HOSTNAME ? process.env.NIGHTWATCH_HOSTNA
module.exports = {
src_folders: ['tests/Drupal/Nightwatch/Tests'],
output_folder: outputFolder,
- custom_commands_path: '',
+ custom_commands_path: ['tests/Drupal/Nightwatch/Commands'],
custom_assertions_path: '',
page_objects_path: '',
globals_path: 'tests/Drupal/Nightwatch/globals.js',
diff --git a/core/tests/Drupal/Nightwatch/Commands/drupalURL.js b/core/tests/Drupal/Nightwatch/Commands/drupalURL.js
new file mode 100644
index 000000000000..29e4b39b454c
--- /dev/null
+++ b/core/tests/Drupal/Nightwatch/Commands/drupalURL.js
@@ -0,0 +1,8 @@
+exports.command = function baseurl(relativeURL) {
+ if (!process.env.BASE_URL || process.env.BASE_URL === '') {
+ throw new Error('Missing a BASE_URL environment variable.');
+ }
+ this
+ .url(`${process.env.BASE_URL}${relativeURL}`);
+ return this;
+};
diff --git a/core/tests/Drupal/Nightwatch/Tests/index.js b/core/tests/Drupal/Nightwatch/Tests/index.js
index 4c9aa83b9538..32732d8398e7 100644
--- a/core/tests/Drupal/Nightwatch/Tests/index.js
+++ b/core/tests/Drupal/Nightwatch/Tests/index.js
@@ -1,9 +1,9 @@
module.exports = {
'Demo Drupal.org': (browser) => {
browser
- .url('https://www.drupal.org/')
+ .drupalURL('/community')
.waitForElementVisible('body', 1000)
- .assert.containsText('body', 'Launch, manage, and scale ambitious digital experiences—with the flexibility to build great websites or push beyond the browser. Proudly open source.')
+ .assert.containsText('body', 'Where is the Drupal Community?')
.end();
},
};
diff --git a/core/tests/README.md b/core/tests/README.md
index 7723a6680d7a..d2bb1c6c8b53 100644
--- a/core/tests/README.md
+++ b/core/tests/README.md
@@ -61,4 +61,4 @@ Some settings can be overridden with environment variables:
| `HEADLESS_CHROME_DISABLED` | `false` | If set to `true`, this will remove the `--headless` option passed to Chrome, allowing you to see tests running in realtime in the browser |
| `NIGHTWATCH_OUTPUT` | `reports/nightwatch` | This will output the test results into `core/reports/nightwatch`. Passing a value here is relative to the `core` directory |
| `NODE_ENV` | none | Setting this to `testbot` will cause Nightwatch to run with settings specific to the Drupal.org testbot |
-
+| `BASE_URL` | none | Set this to the base URL for your Drupal install. Do not include the trailing slash. |
From 802011db0dfcace831fe6236bd45616395604a6f Mon Sep 17 00:00:00 2001
From: Matthew Grill
Date: Mon, 11 Dec 2017 11:58:21 -0800
Subject: [PATCH 017/232] use relativeURL over drupalURL
---
.../Drupal/Nightwatch/Commands/drupalURL.js | 8 --------
.../Drupal/Nightwatch/Commands/relativeURL.js | 18 ++++++++++++++++++
core/tests/Drupal/Nightwatch/Tests/index.js | 2 +-
3 files changed, 19 insertions(+), 9 deletions(-)
delete mode 100644 core/tests/Drupal/Nightwatch/Commands/drupalURL.js
create mode 100644 core/tests/Drupal/Nightwatch/Commands/relativeURL.js
diff --git a/core/tests/Drupal/Nightwatch/Commands/drupalURL.js b/core/tests/Drupal/Nightwatch/Commands/drupalURL.js
deleted file mode 100644
index 29e4b39b454c..000000000000
--- a/core/tests/Drupal/Nightwatch/Commands/drupalURL.js
+++ /dev/null
@@ -1,8 +0,0 @@
-exports.command = function baseurl(relativeURL) {
- if (!process.env.BASE_URL || process.env.BASE_URL === '') {
- throw new Error('Missing a BASE_URL environment variable.');
- }
- this
- .url(`${process.env.BASE_URL}${relativeURL}`);
- return this;
-};
diff --git a/core/tests/Drupal/Nightwatch/Commands/relativeURL.js b/core/tests/Drupal/Nightwatch/Commands/relativeURL.js
new file mode 100644
index 000000000000..082c8913a409
--- /dev/null
+++ b/core/tests/Drupal/Nightwatch/Commands/relativeURL.js
@@ -0,0 +1,18 @@
+/**
+ * Concatenate a BASE_URL variable and a pathname.
+ *
+ * This provides a custom command, .relativeURL()
+ *
+ * @param {string} pathname
+ * The relative path to append to BASE_URL
+ * @return {object}
+ * The 'browser' object.
+ */
+exports.command = function relativeURL(pathname) {
+ if (!process.env.BASE_URL || process.env.BASE_URL === '') {
+ throw new Error('Missing a BASE_URL environment variable.');
+ }
+ this
+ .url(`${process.env.BASE_URL}${pathname}`);
+ return this;
+};
diff --git a/core/tests/Drupal/Nightwatch/Tests/index.js b/core/tests/Drupal/Nightwatch/Tests/index.js
index 32732d8398e7..6ef292acdb03 100644
--- a/core/tests/Drupal/Nightwatch/Tests/index.js
+++ b/core/tests/Drupal/Nightwatch/Tests/index.js
@@ -1,7 +1,7 @@
module.exports = {
'Demo Drupal.org': (browser) => {
browser
- .drupalURL('/community')
+ .relativeURL('/community')
.waitForElementVisible('body', 1000)
.assert.containsText('body', 'Where is the Drupal Community?')
.end();
From c06cd72b50d94285b434e91624821963a964deaa Mon Sep 17 00:00:00 2001
From: Matthew Grill
Date: Mon, 11 Dec 2017 14:06:39 -0800
Subject: [PATCH 018/232] use nightwatch.settings.json for config. needs docs
---
core/.gitignore | 3 +++
core/nightwatch.conf.js | 19 +++++++++----------
core/nightwatch.settings.json.default | 7 +++++++
.../Drupal/Nightwatch/Commands/relativeURL.js | 10 +++++++---
4 files changed, 26 insertions(+), 13 deletions(-)
create mode 100644 core/nightwatch.settings.json.default
diff --git a/core/.gitignore b/core/.gitignore
index db29d420b98a..bc921b6d8396 100644
--- a/core/.gitignore
+++ b/core/.gitignore
@@ -12,3 +12,6 @@ package-lock.json
# Ignore test reports
reports
+
+# Ignore local Nightwatch settings
+nightwatch.settings.json
diff --git a/core/nightwatch.conf.js b/core/nightwatch.conf.js
index 081a9e013876..2a9747232ee8 100644
--- a/core/nightwatch.conf.js
+++ b/core/nightwatch.conf.js
@@ -1,14 +1,13 @@
-const chromeArgs = ['--disable-gpu'];
-if (!process.env.HEADLESS_CHROME_DISABLED) {
- chromeArgs.push('--headless');
-}
+const settings = require('./nightwatch.settings.json');
-const outputFolder = process.env.NIGHTWATCH_OUTPUT ? process.env.NIGHTWATCH_OUTPUT : 'reports/nightwatch';
-const hostname = process.env.NIGHTWATCH_HOSTNAME ? process.env.NIGHTWATCH_HOSTNAME : 'localhost';
+const args = ['--disable-gpu', ...settings.CHROME_ARGS];
+if (settings.HEADLESS_CHROME) {
+ args.push('--headless');
+}
module.exports = {
src_folders: ['tests/Drupal/Nightwatch/Tests'],
- output_folder: outputFolder,
+ output_folder: settings.NIGHTWATCH_OUTPUT,
custom_commands_path: ['tests/Drupal/Nightwatch/Commands'],
custom_assertions_path: '',
page_objects_path: '',
@@ -19,20 +18,20 @@ module.exports = {
test_settings: {
default: {
selenium_port: 9515,
- selenium_host: hostname,
+ selenium_host: settings.NIGHTWATCH_HOSTNAME,
default_path_prefix: '',
desiredCapabilities: {
browserName: 'chrome',
acceptSslCerts: true,
chromeOptions: {
- args: chromeArgs,
+ args,
},
},
screenshots: {
enabled: true,
on_failure: true,
on_error: true,
- path: `${outputFolder}/screenshots`,
+ path: `${settings.NIGHTWATCH_OUTPUT}/screenshots`,
},
},
},
diff --git a/core/nightwatch.settings.json.default b/core/nightwatch.settings.json.default
new file mode 100644
index 000000000000..3be8c5ab1ee2
--- /dev/null
+++ b/core/nightwatch.settings.json.default
@@ -0,0 +1,7 @@
+{
+ "BASE_URL": "",
+ "NIGHTWATCH_OUTPUT": "reports/nightwatch",
+ "NIGHTWATCH_HOSTNAME": "",
+ "HEADLESS_CHROME": true,
+ "CHROME_ARGS": []
+}
diff --git a/core/tests/Drupal/Nightwatch/Commands/relativeURL.js b/core/tests/Drupal/Nightwatch/Commands/relativeURL.js
index 082c8913a409..b07c9b5dc378 100644
--- a/core/tests/Drupal/Nightwatch/Commands/relativeURL.js
+++ b/core/tests/Drupal/Nightwatch/Commands/relativeURL.js
@@ -1,3 +1,5 @@
+const settings = require('../../../../nightwatch.settings.json');
+
/**
* Concatenate a BASE_URL variable and a pathname.
*
@@ -9,10 +11,12 @@
* The 'browser' object.
*/
exports.command = function relativeURL(pathname) {
- if (!process.env.BASE_URL || process.env.BASE_URL === '') {
- throw new Error('Missing a BASE_URL environment variable.');
+ if (
+ (!settings.BASE_URL || settings.BASE_URL === '') &&
+ (!process.env.SIMPLETEST_BASE_URL || process.env.SIMPLETEST_BASE_URL === '')) {
+ throw new Error('Missing a BASE_URL or SIMPLETEST_BASE_URL configuration item.');
}
this
- .url(`${process.env.BASE_URL}${pathname}`);
+ .url(`${settings.BASE_URL !== '' ? settings.BASE_URL : process.env.SIMPLETEST_BASE_URL}${pathname}`);
return this;
};
From c9a5dac7a5e3e4a0ddc04665b30a296190ea2839 Mon Sep 17 00:00:00 2001
From: Daniel Wehner
Date: Mon, 11 Dec 2017 21:43:44 +0000
Subject: [PATCH 019/232] Apply 2926633
---
core/scripts/setup-drupal-test.php | 31 +++
.../SetupDrupalTestScriptTest.php | 29 +++
.../TestInstallationSetupApplication.php | 39 ++++
.../Commands/TestInstallationSetupCommand.php | 43 ++++
core/tests/Drupal/Setup/ExampleTestSetup.php | 22 ++
.../Drupal/Setup/TestInstallationSetup.php | 195 ++++++++++++++++++
.../tests/Drupal/Setup/TestSetupInterface.php | 23 +++
core/tests/bootstrap.php | 1 +
8 files changed, 383 insertions(+)
create mode 100644 core/scripts/setup-drupal-test.php
create mode 100644 core/tests/Drupal/FunctionalTests/SetupDrupalTestScriptTest.php
create mode 100644 core/tests/Drupal/Setup/Commands/TestInstallationSetupApplication.php
create mode 100644 core/tests/Drupal/Setup/Commands/TestInstallationSetupCommand.php
create mode 100644 core/tests/Drupal/Setup/ExampleTestSetup.php
create mode 100644 core/tests/Drupal/Setup/TestInstallationSetup.php
create mode 100644 core/tests/Drupal/Setup/TestSetupInterface.php
diff --git a/core/scripts/setup-drupal-test.php b/core/scripts/setup-drupal-test.php
new file mode 100644
index 000000000000..44b0f9eaf1fd
--- /dev/null
+++ b/core/scripts/setup-drupal-test.php
@@ -0,0 +1,31 @@
+#!/usr/bin/env php
+getAppRoot());
+
+Settings::initialize(dirname(dirname(__DIR__)),
+ DrupalKernel::findSitePath($request), $autoloader);
+
+require_once __DIR__ . '/../tests/bootstrap.php';
+
+(new TestInstallationSetupApplication())
+ ->run();
diff --git a/core/tests/Drupal/FunctionalTests/SetupDrupalTestScriptTest.php b/core/tests/Drupal/FunctionalTests/SetupDrupalTestScriptTest.php
new file mode 100644
index 000000000000..3a0487092cf0
--- /dev/null
+++ b/core/tests/Drupal/FunctionalTests/SetupDrupalTestScriptTest.php
@@ -0,0 +1,29 @@
+find();
+
+ $db_url = getenv('SIMPLETEST_DB');
+ $base_url = getenv('SIMPLETEST_BASE_URL');
+ $process = new Process("$php {$this->root}/core/scripts/setup-drupal-test.php --db_url={$db_url} --base_url={$base_url}");
+ $process->run(function ($type, $data) {
+ // @todo How does one test an async API?
+ // This code might happen after the test function is done.
+ });
+ }
+
+}
diff --git a/core/tests/Drupal/Setup/Commands/TestInstallationSetupApplication.php b/core/tests/Drupal/Setup/Commands/TestInstallationSetupApplication.php
new file mode 100644
index 000000000000..312d3db757d8
--- /dev/null
+++ b/core/tests/Drupal/Setup/Commands/TestInstallationSetupApplication.php
@@ -0,0 +1,39 @@
+setName('setup-drupal-test')
+ ->addOption('setup_file', NULL, InputOption::VALUE_OPTIONAL)
+ ->addOption('db_url', NULL, InputOption::VALUE_OPTIONAL, '', getenv('SIMPLETEST_DB'))
+ ->addOption('base_url', NULL, InputOption::VALUE_OPTIONAL, '', getenv('SIMPLETEST_BASE_URL'));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $test = new TestInstallationSetup();
+ $test->setup('testing', $input->getOption('setup_file'));
+
+ $db_url = $input->getOption('db_url');
+ $base_url = $input->getOption('base_url');
+ putenv("SIMPLETEST_DB=$db_url");
+ putenv("SIMPLETEST_BASE_URL=$base_url");
+
+ $output->writeln(drupal_generate_test_ua($test->getDatabasePrefix()));
+ }
+
+}
diff --git a/core/tests/Drupal/Setup/ExampleTestSetup.php b/core/tests/Drupal/Setup/ExampleTestSetup.php
new file mode 100644
index 000000000000..ed94c18778aa
--- /dev/null
+++ b/core/tests/Drupal/Setup/ExampleTestSetup.php
@@ -0,0 +1,22 @@
+install(['node']);
+
+ Node::create(['type' => 'page', 'title' => 'Test tile'])
+ ->save();
+ }
+
+}
diff --git a/core/tests/Drupal/Setup/TestInstallationSetup.php b/core/tests/Drupal/Setup/TestInstallationSetup.php
new file mode 100644
index 000000000000..c0bde3851f21
--- /dev/null
+++ b/core/tests/Drupal/Setup/TestInstallationSetup.php
@@ -0,0 +1,195 @@
+profile = $profile;
+ $this->setupBaseUrl();
+ $this->prepareEnvironment();
+ $this->installDrupal();
+
+ if ($setup_file) {
+ $this->executeSetupFile($setup_file);
+ }
+ }
+
+ /**
+ * Gets the database prefix.
+ *
+ * @return string
+ */
+ public function getDatabasePrefix() {
+ return $this->databasePrefix;
+ }
+
+ /**
+ * Installs Drupal into the Simpletest site.
+ */
+ protected function installDrupal() {
+ $this->initUserSession();
+ $this->prepareSettings();
+ $this->doInstall();
+ $this->initSettings();
+ $container = $this->initKernel(\Drupal::request());
+ $this->initConfig($container);
+ $this->installModulesFromClassProperty($container);
+ $this->rebuildAll();
+ }
+
+ /**
+ * Uses the setup file to configure Drupal.
+ *
+ * @param string $setup_file
+ * The setup file.
+ */
+ protected function executeSetupFile($setup_file) {
+ $classes = static::fileGetPhpClasses($setup_file);
+
+ if (empty($classes)) {
+ throw new \InvalidArgumentException(sprintf('You need to define a class implementing \Drupal\Setup\TestSetupInterface'));
+ }
+ if (count($classes) > 1) {
+ throw new \InvalidArgumentException(sprintf('You need to define a single class implementing \Drupal\Setup\TestSetupInterface'));
+ }
+ if (!is_subclass_of($classes[0], TestSetupInterface::class)) {
+ throw new \InvalidArgumentException(sprintf('You need to define a class implementing \Drupal\Setup\TestSetupInterface'));
+ }
+
+ require_once $setup_file;
+
+ /** @var \Drupal\Setup\TestSetupInterface $instance */
+ $instance = new $classes[0];
+ $instance->setup();
+ }
+
+ /**
+ * Gets the PHP classes contained in a php file.
+ *
+ * @param string $filepath
+ * The file path.
+ *
+ * @return string[]
+ * An array of PHP classes.
+ */
+ protected static function fileGetPhpClasses($filepath) {
+ $php_code = file_get_contents($filepath);
+ $classes = static::extractClassesFromPhp($php_code);
+ return $classes;
+ }
+
+ /**
+ * @param string $php_code
+ * PHP code to parse.
+ *
+ * @return string[]
+ * An array of PHP classes.
+ */
+ protected static function extractClassesFromPhp($php_code) {
+ $classes = array();
+ $tokens = token_get_all($php_code);
+ $count = count($tokens);
+ for ($i = 2; $i < $count; $i++) {
+ if ($tokens[$i - 2][0] == T_CLASS
+ && $tokens[$i - 1][0] == T_WHITESPACE
+ && $tokens[$i][0] == T_STRING
+ ) {
+
+ $class_name = $tokens[$i][1];
+ $classes[] = $class_name;
+ }
+ }
+ return $classes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function installParameters() {
+ $connection_info = Database::getConnectionInfo();
+ $driver = $connection_info['default']['driver'];
+ $connection_info['default']['prefix'] = $connection_info['default']['prefix']['default'];
+ unset($connection_info['default']['driver']);
+ unset($connection_info['default']['namespace']);
+ unset($connection_info['default']['pdo']);
+ unset($connection_info['default']['init_commands']);
+ $parameters = [
+ 'interactive' => FALSE,
+ 'parameters' => [
+ 'profile' => $this->profile,
+ 'langcode' => 'en',
+ ],
+ 'forms' => [
+ 'install_settings_form' => [
+ 'driver' => $driver,
+ $driver => $connection_info['default'],
+ ],
+ 'install_configure_form' => [
+ 'site_name' => 'Drupal',
+ 'site_mail' => 'simpletest@example.com',
+ 'account' => [
+ 'name' => $this->rootUser->name,
+ 'mail' => $this->rootUser->getEmail(),
+ 'pass' => [
+ 'pass1' => $this->rootUser->pass_raw,
+ 'pass2' => $this->rootUser->pass_raw,
+ ],
+ ],
+ // form_type_checkboxes_value() requires NULL instead of FALSE values
+ // for programmatic form submissions to disable a checkbox.
+ 'enable_update_status_module' => NULL,
+ 'enable_update_status_emails' => NULL,
+ ],
+ ],
+ ];
+ return $parameters;
+ }
+
+}
diff --git a/core/tests/Drupal/Setup/TestSetupInterface.php b/core/tests/Drupal/Setup/TestSetupInterface.php
new file mode 100644
index 000000000000..8bf2c2e96acf
--- /dev/null
+++ b/core/tests/Drupal/Setup/TestSetupInterface.php
@@ -0,0 +1,23 @@
+install(['my_module'])
+ * @endcode
+ *
+ * Check out 'core/tests/Drupal/Setup/ExampleTestSetup.php' for an example.
+ */
+ public function setup();
+
+}
diff --git a/core/tests/bootstrap.php b/core/tests/bootstrap.php
index f78b69ff0498..77b270fe232a 100644
--- a/core/tests/bootstrap.php
+++ b/core/tests/bootstrap.php
@@ -127,6 +127,7 @@ function drupal_phpunit_populate_class_loader() {
// Start with classes in known locations.
$loader->add('Drupal\\Tests', __DIR__);
+ $loader->add('Drupal\\Setup', __DIR__);
$loader->add('Drupal\\KernelTests', __DIR__);
$loader->add('Drupal\\FunctionalTests', __DIR__);
$loader->add('Drupal\\FunctionalJavascriptTests', __DIR__);
From 168484060197a89f1607d3945dd109cb1540b25d Mon Sep 17 00:00:00 2001
From: Daniel Wehner
Date: Mon, 11 Dec 2017 22:04:50 +0000
Subject: [PATCH 020/232] Fix test to actually work against a drupal site
---
core/tests/Drupal/Nightwatch/Tests/index.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/core/tests/Drupal/Nightwatch/Tests/index.js b/core/tests/Drupal/Nightwatch/Tests/index.js
index 6ef292acdb03..41c4a8833a52 100644
--- a/core/tests/Drupal/Nightwatch/Tests/index.js
+++ b/core/tests/Drupal/Nightwatch/Tests/index.js
@@ -1,9 +1,9 @@
module.exports = {
'Demo Drupal.org': (browser) => {
browser
- .relativeURL('/community')
+ .relativeURL('/user/login')
.waitForElementVisible('body', 1000)
- .assert.containsText('body', 'Where is the Drupal Community?')
+ .assert.containsText('body', 'Powered by Drupal')
.end();
},
};
From 84f21dab4b2e676dd98cc0bc944d7d5d8a16af2c Mon Sep 17 00:00:00 2001
From: Daniel Wehner
Date: Mon, 11 Dec 2017 22:32:24 +0000
Subject: [PATCH 021/232] Improve initial setup
---
core/tests/Drupal/Nightwatch/globals.js | 2 ++
core/tests/README.md | 3 ++-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/core/tests/Drupal/Nightwatch/globals.js b/core/tests/Drupal/Nightwatch/globals.js
index 3786eeaa5c9e..61c956af95af 100644
--- a/core/tests/Drupal/Nightwatch/globals.js
+++ b/core/tests/Drupal/Nightwatch/globals.js
@@ -2,6 +2,8 @@ const chromedriver = require('chromedriver');
module.exports = {
before: (done) => {
+ // Setting up
+ process.env.SIMPLETEST_BASE_URL = process.env.SIMPLETEST_BASE_URL || process.env.BASE_URL;
if (process.env.NODE_ENV !== 'testbot') {
chromedriver.start();
}
diff --git a/core/tests/README.md b/core/tests/README.md
index d2bb1c6c8b53..36390b88cdf2 100644
--- a/core/tests/README.md
+++ b/core/tests/README.md
@@ -52,7 +52,8 @@ sudo -u www-data -E ./vendor/bin/phpunit -c core --testsuite functional-javascri
- Install [Node.js](https://nodejs.org/en/download/) and [yarn](https://yarnpkg.com/en/docs/install). The versions required are specificed inside core/package.json in the `engines` field
- Install [Google Chrome](https://www.google.com/chrome/browser/desktop/index.html)
- Inside the `core` folder, run `yarn install`
-- Again inside the `core` folder, run `yarn nightwatch` to run the tests. By default this will output reports to `core/reports`
+- Configure the nightwatch settings by copying `nightwatch.settings.json.default` to `nightwatch.settings.json`
+- Again inside the `core` folder, run `yarn test:js` to run the tests. By default this will output reports to `core/reports`
Some settings can be overridden with environment variables:
From 58c9c80683a722e2bed687bb730f085d4c7f70a9 Mon Sep 17 00:00:00 2001
From: Daniel Wehner
Date: Mon, 11 Dec 2017 22:38:21 +0000
Subject: [PATCH 022/232] Use the install command
---
.../Nightwatch/Commands/installDrupal.js | 39 +++++++++++++++++++
core/tests/Drupal/Nightwatch/Tests/index.js | 3 +-
2 files changed, 41 insertions(+), 1 deletion(-)
create mode 100644 core/tests/Drupal/Nightwatch/Commands/installDrupal.js
diff --git a/core/tests/Drupal/Nightwatch/Commands/installDrupal.js b/core/tests/Drupal/Nightwatch/Commands/installDrupal.js
new file mode 100644
index 000000000000..ef703d7ab53f
--- /dev/null
+++ b/core/tests/Drupal/Nightwatch/Commands/installDrupal.js
@@ -0,0 +1,39 @@
+const exec = require('child_process').exec;
+
+/**
+ * @param browser
+ * @param cookieValue
+ * @returns {*}
+ */
+const setupCookie = function (browser, cookieValue, done) {
+ const matches = process.env.BASE_URL.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);
+ const domain = matches[1];
+ const path = matches[2];
+
+ return browser
+ // See https://bugs.chromium.org/p/chromedriver/issues/detail?id=728#c10
+ .url(BASE_URL)
+ .setCookie({
+ name: 'SIMPLETEST_USER_AGENT',
+ // Colons needs to be URL encoded to be valid.
+ value: encodeURIComponent(cookieValue),
+ path: path,
+ domain: domain,
+ }, done);
+};
+
+exports.command = function installDrupal(profile = 'testing', setupFile = '', done) {
+ const self = this;
+
+ exec(`php ./scripts/setup-drupal-test.php ${profile} ${setupFile}`, (err, simpleTestCookie) => {
+ if (err) {
+ console.error(err);
+ return done(err);
+ }
+
+ setupCookie(self, simpleTestCookie, simpleTestCookie);
+ });
+
+
+ return this;
+};
diff --git a/core/tests/Drupal/Nightwatch/Tests/index.js b/core/tests/Drupal/Nightwatch/Tests/index.js
index 41c4a8833a52..aee9920e246d 100644
--- a/core/tests/Drupal/Nightwatch/Tests/index.js
+++ b/core/tests/Drupal/Nightwatch/Tests/index.js
@@ -1,6 +1,7 @@
module.exports = {
- 'Demo Drupal.org': (browser) => {
+ 'Demo Drupal.org': (browser, done) => {
browser
+ .installDrupal()
.relativeURL('/user/login')
.waitForElementVisible('body', 1000)
.assert.containsText('body', 'Powered by Drupal')
From 4f6a28e2d9053c94722c5143604a9d2c583fdab6 Mon Sep 17 00:00:00 2001
From: Daniel Wehner
Date: Mon, 11 Dec 2017 22:54:09 +0000
Subject: [PATCH 023/232] Wire everything together
---
core/tests/Drupal/Nightwatch/Commands/installDrupal.js | 4 ++--
core/tests/Drupal/Nightwatch/Commands/relativeURL.js | 8 +-------
core/tests/Drupal/Nightwatch/Tests/index.js | 2 +-
core/tests/Drupal/Nightwatch/globals.js | 9 ++++++++-
4 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/core/tests/Drupal/Nightwatch/Commands/installDrupal.js b/core/tests/Drupal/Nightwatch/Commands/installDrupal.js
index ef703d7ab53f..f62297b8c9bd 100644
--- a/core/tests/Drupal/Nightwatch/Commands/installDrupal.js
+++ b/core/tests/Drupal/Nightwatch/Commands/installDrupal.js
@@ -12,7 +12,7 @@ const setupCookie = function (browser, cookieValue, done) {
return browser
// See https://bugs.chromium.org/p/chromedriver/issues/detail?id=728#c10
- .url(BASE_URL)
+ .url(process.env.BASE_URL)
.setCookie({
name: 'SIMPLETEST_USER_AGENT',
// Colons needs to be URL encoded to be valid.
@@ -31,7 +31,7 @@ exports.command = function installDrupal(profile = 'testing', setupFile = '', do
return done(err);
}
- setupCookie(self, simpleTestCookie, simpleTestCookie);
+ setupCookie(self, simpleTestCookie, done);
});
diff --git a/core/tests/Drupal/Nightwatch/Commands/relativeURL.js b/core/tests/Drupal/Nightwatch/Commands/relativeURL.js
index b07c9b5dc378..867f9825e194 100644
--- a/core/tests/Drupal/Nightwatch/Commands/relativeURL.js
+++ b/core/tests/Drupal/Nightwatch/Commands/relativeURL.js
@@ -1,4 +1,3 @@
-const settings = require('../../../../nightwatch.settings.json');
/**
* Concatenate a BASE_URL variable and a pathname.
@@ -11,12 +10,7 @@ const settings = require('../../../../nightwatch.settings.json');
* The 'browser' object.
*/
exports.command = function relativeURL(pathname) {
- if (
- (!settings.BASE_URL || settings.BASE_URL === '') &&
- (!process.env.SIMPLETEST_BASE_URL || process.env.SIMPLETEST_BASE_URL === '')) {
- throw new Error('Missing a BASE_URL or SIMPLETEST_BASE_URL configuration item.');
- }
this
- .url(`${settings.BASE_URL !== '' ? settings.BASE_URL : process.env.SIMPLETEST_BASE_URL}${pathname}`);
+ .url(`${process.env.BASE_URL}${pathname}`);
return this;
};
diff --git a/core/tests/Drupal/Nightwatch/Tests/index.js b/core/tests/Drupal/Nightwatch/Tests/index.js
index aee9920e246d..c600b8033974 100644
--- a/core/tests/Drupal/Nightwatch/Tests/index.js
+++ b/core/tests/Drupal/Nightwatch/Tests/index.js
@@ -4,7 +4,7 @@ module.exports = {
.installDrupal()
.relativeURL('/user/login')
.waitForElementVisible('body', 1000)
- .assert.containsText('body', 'Powered by Drupal')
+ .assert.containsText('body', 'Skip to main content')
.end();
},
};
diff --git a/core/tests/Drupal/Nightwatch/globals.js b/core/tests/Drupal/Nightwatch/globals.js
index 61c956af95af..b5e53b41c1fa 100644
--- a/core/tests/Drupal/Nightwatch/globals.js
+++ b/core/tests/Drupal/Nightwatch/globals.js
@@ -1,9 +1,16 @@
const chromedriver = require('chromedriver');
+const settings = require('../../../nightwatch.settings.json');
module.exports = {
before: (done) => {
// Setting up
- process.env.SIMPLETEST_BASE_URL = process.env.SIMPLETEST_BASE_URL || process.env.BASE_URL;
+ const baseUrl = settings.BASE_URL || process.env.SIMPLETEST_BASE_URL || process.env.BASE_URL;
+
+ if (baseUrl === undefined) {
+ throw new Error('Missing a BASE_URL or SIMPLETEST_BASE_URL configuration item.');
+ }
+
+ process.env.BASE_URL = process.env.SIMPLETEST_BASE_URL = baseUrl;
if (process.env.NODE_ENV !== 'testbot') {
chromedriver.start();
}
From e06f20c7a2b510836a0d9615145690f4e734dd94 Mon Sep 17 00:00:00 2001
From: Daniel Wehner
Date: Mon, 11 Dec 2017 23:25:53 +0000
Subject: [PATCH 024/232] Create a setup file
---
.../Drupal/Nightwatch/Commands/installDrupal.js | 2 +-
core/tests/Drupal/Nightwatch/Tests/index.js | 8 ++++----
core/tests/Drupal/Nightwatch/Tests/index.setup.php | 12 ++++++++++++
3 files changed, 17 insertions(+), 5 deletions(-)
create mode 100644 core/tests/Drupal/Nightwatch/Tests/index.setup.php
diff --git a/core/tests/Drupal/Nightwatch/Commands/installDrupal.js b/core/tests/Drupal/Nightwatch/Commands/installDrupal.js
index f62297b8c9bd..96de8a83e930 100644
--- a/core/tests/Drupal/Nightwatch/Commands/installDrupal.js
+++ b/core/tests/Drupal/Nightwatch/Commands/installDrupal.js
@@ -25,7 +25,7 @@ const setupCookie = function (browser, cookieValue, done) {
exports.command = function installDrupal(profile = 'testing', setupFile = '', done) {
const self = this;
- exec(`php ./scripts/setup-drupal-test.php ${profile} ${setupFile}`, (err, simpleTestCookie) => {
+ exec(`php ./scripts/setup-drupal-test.php ${profile} --setup_file ${setupFile}`, (err, simpleTestCookie) => {
if (err) {
console.error(err);
return done(err);
diff --git a/core/tests/Drupal/Nightwatch/Tests/index.js b/core/tests/Drupal/Nightwatch/Tests/index.js
index c600b8033974..acdcaedb40fa 100644
--- a/core/tests/Drupal/Nightwatch/Tests/index.js
+++ b/core/tests/Drupal/Nightwatch/Tests/index.js
@@ -1,10 +1,10 @@
module.exports = {
- 'Demo Drupal.org': (browser, done) => {
+ 'Test page': (browser, done) => {
browser
- .installDrupal()
- .relativeURL('/user/login')
+ .installDrupal('testing', __dirname + '/index.setup.php')
+ .relativeURL('/test-page')
.waitForElementVisible('body', 1000)
- .assert.containsText('body', 'Skip to main content')
+ .assert.containsText('body', 'Test page text')
.end();
},
};
diff --git a/core/tests/Drupal/Nightwatch/Tests/index.setup.php b/core/tests/Drupal/Nightwatch/Tests/index.setup.php
new file mode 100644
index 000000000000..6aa0cf230f23
--- /dev/null
+++ b/core/tests/Drupal/Nightwatch/Tests/index.setup.php
@@ -0,0 +1,12 @@
+install(['test_page_test']);
+ }
+
+}
From 0c1cc1b601e1009999de4f96d051b037958ccb55 Mon Sep 17 00:00:00 2001
From: Daniel Wehner
Date: Mon, 11 Dec 2017 23:26:12 +0000
Subject: [PATCH 025/232] fix the test setup code
---
core/tests/Drupal/Setup/TestInstallationSetup.php | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/core/tests/Drupal/Setup/TestInstallationSetup.php b/core/tests/Drupal/Setup/TestInstallationSetup.php
index c0bde3851f21..714b2815e4fe 100644
--- a/core/tests/Drupal/Setup/TestInstallationSetup.php
+++ b/core/tests/Drupal/Setup/TestInstallationSetup.php
@@ -99,12 +99,13 @@ protected function executeSetupFile($setup_file) {
if (count($classes) > 1) {
throw new \InvalidArgumentException(sprintf('You need to define a single class implementing \Drupal\Setup\TestSetupInterface'));
}
+
+ require_once $setup_file;
+
if (!is_subclass_of($classes[0], TestSetupInterface::class)) {
throw new \InvalidArgumentException(sprintf('You need to define a class implementing \Drupal\Setup\TestSetupInterface'));
}
- require_once $setup_file;
-
/** @var \Drupal\Setup\TestSetupInterface $instance */
$instance = new $classes[0];
$instance->setup();
From de9e2d7061c2fe2303ca926250817d4e125b91f9 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Tue, 12 Dec 2017 19:29:54 +1000
Subject: [PATCH 026/232] Issue #2865184 by chr.fritsch, phenaproxima,
larowlan, marcoscano, xjm: Allow MediaSource plugins provide default field
form/view display settings
---
core/modules/media/src/MediaSourceBase.php | 16 ++++
.../media/src/MediaSourceInterface.php | 38 ++++++++++
core/modules/media/src/MediaTypeForm.php | 23 ++----
.../schema/media_test_source.schema.yml | 8 ++
.../media/Source/TestDifferentDisplays.php | 46 ++++++++++++
.../Source/TestWithHiddenSourceField.php | 42 +++++++++++
.../tests/src/Kernel/MediaSourceTest.php | 75 +++++++++++++++++++
7 files changed, 231 insertions(+), 17 deletions(-)
create mode 100644 core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php
create mode 100644 core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithHiddenSourceField.php
diff --git a/core/modules/media/src/MediaSourceBase.php b/core/modules/media/src/MediaSourceBase.php
index dea01402609c..4f8301fbee75 100644
--- a/core/modules/media/src/MediaSourceBase.php
+++ b/core/modules/media/src/MediaSourceBase.php
@@ -3,6 +3,8 @@
namespace Drupal\media;
use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
+use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
@@ -317,4 +319,18 @@ protected function getSourceFieldName() {
return $id;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display) {
+ $display->setComponent($this->getSourceFieldDefinition($type)->getName());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display) {
+ $display->setComponent($this->getSourceFieldDefinition($type)->getName());
+ }
+
}
diff --git a/core/modules/media/src/MediaSourceInterface.php b/core/modules/media/src/MediaSourceInterface.php
index 723e0b9a8e05..43dd8aa73380 100644
--- a/core/modules/media/src/MediaSourceInterface.php
+++ b/core/modules/media/src/MediaSourceInterface.php
@@ -4,6 +4,8 @@
use Drupal\Component\Plugin\ConfigurablePluginInterface;
use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
+use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Plugin\PluginFormInterface;
/**
@@ -139,4 +141,40 @@ public function getSourceFieldDefinition(MediaTypeInterface $type);
*/
public function createSourceField(MediaTypeInterface $type);
+ /**
+ * Prepares the media type fields for this source in the view display.
+ *
+ * This method should normally call
+ * \Drupal\Core\Entity\Display\EntityDisplayInterface::setComponent() or
+ * \Drupal\Core\Entity\Display\EntityDisplayInterface::removeComponent() to
+ * configure the media type fields in the view display.
+ *
+ * @param \Drupal\media\MediaTypeInterface $type
+ * The media type which is using this source.
+ * @param \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display
+ * The display which should be prepared.
+ *
+ * @see \Drupal\Core\Entity\Display\EntityDisplayInterface::setComponent()
+ * @see \Drupal\Core\Entity\Display\EntityDisplayInterface::removeComponent()
+ */
+ public function prepareViewDisplay(MediaTypeInterface $type, EntityViewDisplayInterface $display);
+
+ /**
+ * Prepares the media type fields for this source in the form display.
+ *
+ * This method should normally call
+ * \Drupal\Core\Entity\Display\EntityDisplayInterface::setComponent() or
+ * \Drupal\Core\Entity\Display\EntityDisplayInterface::removeComponent() to
+ * configure the media type fields in the form display.
+ *
+ * @param \Drupal\media\MediaTypeInterface $type
+ * The media type which is using this source.
+ * @param \Drupal\Core\Entity\Display\EntityFormDisplayInterface $display
+ * The display which should be prepared.
+ *
+ * @see \Drupal\Core\Entity\Display\EntityDisplayInterface::setComponent()
+ * @see \Drupal\Core\Entity\Display\EntityDisplayInterface::removeComponent()
+ */
+ public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display);
+
}
diff --git a/core/modules/media/src/MediaTypeForm.php b/core/modules/media/src/MediaTypeForm.php
index 5f301d58ed0b..04ac4ba88ccf 100644
--- a/core/modules/media/src/MediaTypeForm.php
+++ b/core/modules/media/src/MediaTypeForm.php
@@ -327,30 +327,19 @@ public function save(array $form, FormStateInterface $form_state) {
// Add the new field to the default form and view displays for this
// media type.
- $field_name = $source_field->getName();
- $field_type = $source_field->getType();
-
if ($source_field->isDisplayConfigurable('form')) {
- // Use the default widget and settings.
- $component = \Drupal::service('plugin.manager.field.widget')
- ->prepareConfiguration($field_type, []);
-
// @todo Replace entity_get_form_display() when #2367933 is done.
// https://www.drupal.org/node/2872159.
- entity_get_form_display('media', $media_type->id(), 'default')
- ->setComponent($field_name, $component)
- ->save();
+ $display = entity_get_form_display('media', $media_type->id(), 'default');
+ $source->prepareFormDisplay($media_type, $display);
+ $display->save();
}
if ($source_field->isDisplayConfigurable('view')) {
- // Use the default formatter and settings.
- $component = \Drupal::service('plugin.manager.field.formatter')
- ->prepareConfiguration($field_type, []);
-
// @todo Replace entity_get_display() when #2367933 is done.
// https://www.drupal.org/node/2872159.
- entity_get_display('media', $media_type->id(), 'default')
- ->setComponent($field_name, $component)
- ->save();
+ $display = entity_get_display('media', $media_type->id(), 'default');
+ $source->prepareViewDisplay($media_type, $display);
+ $display->save();
}
}
diff --git a/core/modules/media/tests/modules/media_test_source/config/schema/media_test_source.schema.yml b/core/modules/media/tests/modules/media_test_source/config/schema/media_test_source.schema.yml
index 089aeac35f02..ab025640b400 100644
--- a/core/modules/media/tests/modules/media_test_source/config/schema/media_test_source.schema.yml
+++ b/core/modules/media/tests/modules/media_test_source/config/schema/media_test_source.schema.yml
@@ -13,3 +13,11 @@ media.source.test_translation:
media.source.test_constraints:
type: media.source.test
label: 'Test media source with constraints configuration'
+
+media.source.test_hidden_source_field:
+ type: media.source.test
+ label: 'Test media source with hidden source field'
+
+media.source.test_different_displays:
+ type: media.source.test
+ label: 'Test media source with different source field displays'
diff --git a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php
new file mode 100644
index 000000000000..19dde8b3ac30
--- /dev/null
+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestDifferentDisplays.php
@@ -0,0 +1,46 @@
+setComponent($this->getSourceFieldDefinition($type)->getName(), [
+ 'type' => 'entity_reference_entity_id',
+ ]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display) {
+ $display->setComponent($this->getSourceFieldDefinition($type)->getName(), [
+ 'type' => 'entity_reference_autocomplete_tags',
+ ]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getSourceFieldName() {
+ return 'field_media_different_display';
+ }
+
+}
diff --git a/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithHiddenSourceField.php b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithHiddenSourceField.php
new file mode 100644
index 000000000000..e5ac525eb0e6
--- /dev/null
+++ b/core/modules/media/tests/modules/media_test_source/src/Plugin/media/Source/TestWithHiddenSourceField.php
@@ -0,0 +1,42 @@
+removeComponent($this->getSourceFieldDefinition($type)->getName());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function prepareFormDisplay(MediaTypeInterface $type, EntityFormDisplayInterface $display) {
+ $display->removeComponent($this->getSourceFieldDefinition($type)->getName());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getSourceFieldName() {
+ return 'field_media_hidden';
+ }
+
+}
diff --git a/core/modules/media/tests/src/Kernel/MediaSourceTest.php b/core/modules/media/tests/src/Kernel/MediaSourceTest.php
index fc30326fb1dd..e985303566c2 100644
--- a/core/modules/media/tests/src/Kernel/MediaSourceTest.php
+++ b/core/modules/media/tests/src/Kernel/MediaSourceTest.php
@@ -442,4 +442,79 @@ public function testSourceConfigurationSubmit() {
$this->assertEquals($expected, $source->getConfiguration(), 'Submitted values were saved correctly.');
}
+ /**
+ * Tests different display options for the source field.
+ */
+ public function testDifferentSourceFieldDisplays() {
+ $id = 'test_different_displays';
+ $field_name = 'field_media_different_display';
+
+ $this->createMediaTypeViaForm($id, $field_name);
+
+ // Source field not in displays.
+ $display = entity_get_display('media', $id, 'default');
+ $components = $display->getComponents();
+ $this->assertArrayHasKey($field_name, $components);
+ $this->assertSame('entity_reference_entity_id', $components[$field_name]['type']);
+
+ $display = entity_get_form_display('media', $id, 'default');
+ $components = $display->getComponents();
+ $this->assertArrayHasKey($field_name, $components);
+ $this->assertSame('entity_reference_autocomplete_tags', $components[$field_name]['type']);
+ }
+
+ /**
+ * Tests hidden source field in media type.
+ */
+ public function testHiddenSourceField() {
+ $id = 'test_hidden_source_field';
+ $field_name = 'field_media_hidden';
+
+ $this->createMediaTypeViaForm($id, $field_name);
+
+ // Source field not in displays.
+ $display = entity_get_display('media', $id, 'default');
+ $this->assertArrayNotHasKey($field_name, $display->getComponents());
+
+ $display = entity_get_form_display('media', $id, 'default');
+ $this->assertArrayNotHasKey($field_name, $display->getComponents());
+ }
+
+ /**
+ * Creates a media type via form submit.
+ *
+ * @param string $source_plugin_id
+ * Source plugin ID.
+ * @param string $field_name
+ * Source field name.
+ */
+ protected function createMediaTypeViaForm($source_plugin_id, $field_name) {
+ /** @var \Drupal\media\MediaTypeInterface $type */
+ $type = MediaType::create(['source' => $source_plugin_id]);
+
+ $form = $this->container->get('entity_type.manager')
+ ->getFormObject('media_type', 'add')
+ ->setEntity($type);
+
+ $form_state = new FormState();
+ $form_state->setValues([
+ 'label' => 'Test type',
+ 'id' => $source_plugin_id,
+ 'op' => t('Save'),
+ ]);
+
+ /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $field_manager */
+ $field_manager = \Drupal::service('entity_field.manager');
+
+ // Source field not created yet.
+ $fields = $field_manager->getFieldDefinitions('media', $source_plugin_id);
+ $this->assertArrayNotHasKey($field_name, $fields);
+
+ \Drupal::formBuilder()->submitForm($form, $form_state);
+
+ // Source field exists now.
+ $fields = $field_manager->getFieldDefinitions('media', $source_plugin_id);
+ $this->assertArrayHasKey($field_name, $fields);
+ }
+
}
From b5b3949b1454d535f876e29b8d0453d31416179c Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Tue, 12 Dec 2017 19:41:20 +1000
Subject: [PATCH 027/232] Issue #2928256 by marcoscano, seanB: Users shouldn't
be able to change the media source plugin after the media type is created
---
core/modules/media/src/MediaTypeForm.php | 11 ++++++++++-
.../FunctionalJavascript/MediaTypeCreationTest.php | 4 ++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/core/modules/media/src/MediaTypeForm.php b/core/modules/media/src/MediaTypeForm.php
index 04ac4ba88ccf..170a34fa5c2e 100644
--- a/core/modules/media/src/MediaTypeForm.php
+++ b/core/modules/media/src/MediaTypeForm.php
@@ -118,14 +118,23 @@ public function form(array $form, FormStateInterface $form_state) {
'#attributes' => ['id' => 'source-dependent'],
];
+ if ($source) {
+ $source_description = $this->t('The media source cannot be changed after the media type is created.');
+ }
+ else {
+ $source_description = $this->t('Media source that is responsible for additional logic related to this media type.');
+ }
$form['source_dependent']['source'] = [
'#type' => 'select',
'#title' => $this->t('Media source'),
'#default_value' => $source ? $source->getPluginId() : NULL,
'#options' => $options,
- '#description' => $this->t('Media source that is responsible for additional logic related to this media type.'),
+ '#description' => $source_description,
'#ajax' => ['callback' => '::ajaxHandlerData'],
'#required' => TRUE,
+ // Once the media type is created, its source plugin cannot be changed
+ // anymore.
+ '#disabled' => !empty($source),
];
if (!$source) {
diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaTypeCreationTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaTypeCreationTest.php
index efbe28849b19..652aca8c6be1 100644
--- a/core/modules/media/tests/src/FunctionalJavascript/MediaTypeCreationTest.php
+++ b/core/modules/media/tests/src/FunctionalJavascript/MediaTypeCreationTest.php
@@ -49,6 +49,10 @@ public function testMediaTypeCreationFormWithDefaultField() {
$this->drupalGet("admin/structure/media/manage/{$mediaTypeMachineName}");
$assert_session->pageTextContains('Test source field is used to store the essential information about the media item.');
+
+ // Check that the plugin cannot be changed after it is set on type creation.
+ $assert_session->fieldDisabled('Media source');
+ $assert_session->pageTextContains('The media source cannot be changed after the media type is created.');
}
/**
From f320141ddab659fdfe497aaa684ddfb50b1aa3a3 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Wed, 13 Dec 2017 06:40:44 +1000
Subject: [PATCH 028/232] Issue #2795317 by hswong3i, alexpott, Lendude,
bircher, dawehner, martin107, Jo Fitzgerald, mondrake: Allow PHPUnit 6+
support for object mocking
---
.../tests/src/Unit/UpdateFetcherTest.php | 2 +-
.../Drupal/KernelTests/KernelTestBase.php | 2 +
core/tests/Drupal/Tests/BrowserTestBase.php | 1 +
.../Tests/PhpunitCompatibilityTrait.php | 142 ++++++++++++++++++
.../Tests/PhpunitCompatibilityTraitTest.php | 115 ++++++++++++++
core/tests/Drupal/Tests/UnitTestCase.php | 12 +-
6 files changed, 268 insertions(+), 6 deletions(-)
create mode 100644 core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php
create mode 100644 core/tests/Drupal/Tests/PhpunitCompatibilityTraitTest.php
diff --git a/core/modules/update/tests/src/Unit/UpdateFetcherTest.php b/core/modules/update/tests/src/Unit/UpdateFetcherTest.php
index c3e447d1fcfb..61c767deda67 100644
--- a/core/modules/update/tests/src/Unit/UpdateFetcherTest.php
+++ b/core/modules/update/tests/src/Unit/UpdateFetcherTest.php
@@ -28,7 +28,7 @@ class UpdateFetcherTest extends UnitTestCase {
*/
protected function setUp() {
$config_factory = $this->getConfigFactoryStub(['update.settings' => ['fetch_url' => 'http://www.example.com']]);
- $http_client_mock = $this->getMock('\GuzzleHttp\ClientInterface');
+ $http_client_mock = $this->createMock('\GuzzleHttp\ClientInterface');
$this->updateFetcher = new UpdateFetcher($config_factory, $http_client_mock);
}
diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php
index 32c97b63b555..ca1a517d6abc 100644
--- a/core/tests/Drupal/KernelTests/KernelTestBase.php
+++ b/core/tests/Drupal/KernelTests/KernelTestBase.php
@@ -20,6 +20,7 @@
use Drupal\simpletest\AssertContentTrait;
use Drupal\Tests\AssertHelperTrait;
use Drupal\Tests\ConfigTestTrait;
+use Drupal\Tests\PhpunitCompatibilityTrait;
use Drupal\Tests\RandomGeneratorTrait;
use Drupal\Tests\TestRequirementsTrait;
use Drupal\simpletest\TestServiceProvider;
@@ -76,6 +77,7 @@ abstract class KernelTestBase extends TestCase implements ServiceProviderInterfa
use RandomGeneratorTrait;
use ConfigTestTrait;
use TestRequirementsTrait;
+ use PhpunitCompatibilityTrait;
/**
* {@inheritdoc}
diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php
index b12ba60b8a45..93df3dbf1319 100644
--- a/core/tests/Drupal/Tests/BrowserTestBase.php
+++ b/core/tests/Drupal/Tests/BrowserTestBase.php
@@ -66,6 +66,7 @@ abstract class BrowserTestBase extends TestCase {
createUser as drupalCreateUser;
}
use XdebugRequestTrait;
+ use PhpunitCompatibilityTrait;
/**
* The database prefix of this test run.
diff --git a/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php b/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php
new file mode 100644
index 000000000000..5cf020a8df6b
--- /dev/null
+++ b/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php
@@ -0,0 +1,142 @@
+supports('getMock')) {
+ $mock = $this->getMockBuilder($originalClassName)
+ ->setMethods($methods)
+ ->setConstructorArgs($arguments)
+ ->setMockClassName($mockClassName)
+ ->setProxyTarget($proxyTarget);
+ if ($callOriginalConstructor) {
+ $mock->enableOriginalConstructor();
+ }
+ else {
+ $mock->disableOriginalConstructor();
+ }
+ if ($callOriginalClone) {
+ $mock->enableOriginalClone();
+ }
+ else {
+ $mock->disableOriginalClone();
+ }
+ if ($callAutoload) {
+ $mock->enableAutoload();
+ }
+ else {
+ $mock->disableAutoload();
+ }
+ if ($cloneArguments) {
+ $mock->enableArgumentCloning();
+ }
+ else {
+ $mock->disableArgumentCloning();
+ }
+ if ($callOriginalMethods) {
+ $mock->enableProxyingToOriginalMethods();
+ }
+ else {
+ $mock->disableProxyingToOriginalMethods();
+ }
+ return $mock->getMock();
+ }
+ else {
+ return parent::getMock($originalClassName, $methods, $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods, $proxyTarget);
+ }
+ }
+
+ /**
+ * Returns a mock object for the specified class using the available method.
+ *
+ * The createMock method does not exist in PHPUnit 4. To provide forward
+ * compatibility this trait provides the createMock method and uses createMock
+ * if this method is available on the parent class or falls back to getMock if
+ * it isn't.
+ *
+ * @param string $originalClassName
+ * Name of the class to mock.
+ *
+ * @see \PHPUnit_Framework_TestCase::getMock
+ *
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ public function createMock($originalClassName) {
+ if ($this->supports('createMock')) {
+ return parent::createMock($originalClassName);
+ }
+ else {
+ return $this->getMock($originalClassName, [], [], '', FALSE, FALSE);
+ }
+ }
+
+ /**
+ * Checks if the trait is used in a class that has a method.
+ *
+ * @param string $method
+ * Method to check.
+ *
+ * @return bool
+ * TRUE if the method is supported, FALSE if not.
+ */
+ private function supports($method) {
+ // Get the parent class of the currently running test class.
+ $parent = get_parent_class($this);
+ // Ensure that the method_exists() check on the createMock method is carried
+ // out on the first parent of $this that does not have access to this
+ // trait's methods. This is because the trait also has a method called
+ // createMock(). Most often the check will be made on
+ // \PHPUnit\Framework\TestCase.
+ while (method_exists($parent, 'supports')) {
+ $parent = get_parent_class($parent);
+ }
+ return method_exists($parent, $method);
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/PhpunitCompatibilityTraitTest.php b/core/tests/Drupal/Tests/PhpunitCompatibilityTraitTest.php
new file mode 100644
index 000000000000..145980bfe531
--- /dev/null
+++ b/core/tests/Drupal/Tests/PhpunitCompatibilityTraitTest.php
@@ -0,0 +1,115 @@
+assertSame($expected, $class->getMock($this->randomMachineName()));
+ }
+
+ /**
+ * Tests that createMock is available and calls the correct parent method.
+ *
+ * @covers ::createMock
+ * @dataProvider providerMockVersions
+ */
+ public function testCreateMock($className, $expected) {
+ $class = new $className();
+ $this->assertSame($expected, $class->createMock($this->randomMachineName()));
+ }
+
+ /**
+ * Returns the class names and the string they return.
+ *
+ * @return array
+ */
+ public function providerMockVersions() {
+ return [
+ [UnitTestCasePhpunit4TestClass::class, 'PHPUnit 4'],
+ [UnitTestCasePhpunit4TestClassExtends::class, 'PHPUnit 4'],
+ [UnitTestCasePhpunit6TestClass::class, 'PHPUnit 6'],
+ [UnitTestCasePhpunit6TestClassExtends::class, 'PHPUnit 6'],
+ ];
+ }
+
+}
+
+/**
+ * Test class for \PHPUnit\Framework\TestCase in PHPUnit 4.
+ */
+class Phpunit4TestClass {
+ public function getMock($originalClassName) {
+ return 'PHPUnit 4';
+ }
+
+}
+
+/**
+ * Test class for \PHPUnit\Framework\TestCase in PHPUnit 6.
+ */
+class Phpunit6TestClass {
+ public function createMock($originalClassName) {
+ return 'PHPUnit 6';
+ }
+
+ public function getMockbuilder() {
+ return new Mockbuilder();
+ }
+
+}
+
+/**
+ * Test double for PHPUnit_Framework_MockObject_MockBuilder.
+ */
+class Mockbuilder {
+ public function __call($name, $arguments) {
+ return $this;
+ }
+
+ public function getMock() {
+ return 'PHPUnit 6';
+ }
+
+}
+
+/**
+ * Test class for \Drupal\Tests\UnitTestCase with PHPUnit 4.
+ */
+class UnitTestCasePhpunit4TestClass extends Phpunit4TestClass {
+ use PhpunitCompatibilityTrait;
+
+}
+
+/**
+ * Test class for \Drupal\Tests\UnitTestCase with PHPUnit 4.
+ */
+class UnitTestCasePhpunit4TestClassExtends extends UnitTestCasePhpunit4TestClass {
+}
+
+/**
+ * Test class for \Drupal\Tests\UnitTestCase with PHPUnit 6.
+ */
+class UnitTestCasePhpunit6TestClass extends Phpunit6TestClass {
+ use PhpunitCompatibilityTrait;
+
+}
+
+/**
+ * Test class for \Drupal\Tests\UnitTestCase with PHPUnit 6.
+ */
+class UnitTestCasePhpunit6TestClassExtends extends UnitTestCasePhpunit6TestClass {
+}
diff --git a/core/tests/Drupal/Tests/UnitTestCase.php b/core/tests/Drupal/Tests/UnitTestCase.php
index 24ad6801bf8e..58173e540ff8 100644
--- a/core/tests/Drupal/Tests/UnitTestCase.php
+++ b/core/tests/Drupal/Tests/UnitTestCase.php
@@ -17,6 +17,8 @@
*/
abstract class UnitTestCase extends TestCase {
+ use PhpunitCompatibilityTrait;
+
/**
* The random generator.
*
@@ -135,7 +137,7 @@ public function getConfigFactoryStub(array $configs = []) {
}
// Construct a config factory with the array of configuration object stubs
// as its return map.
- $config_factory = $this->getMock('Drupal\Core\Config\ConfigFactoryInterface');
+ $config_factory = $this->createMock('Drupal\Core\Config\ConfigFactoryInterface');
$config_factory->expects($this->any())
->method('get')
->will($this->returnValueMap($config_get_map));
@@ -157,7 +159,7 @@ public function getConfigFactoryStub(array $configs = []) {
* A mocked config storage.
*/
public function getConfigStorageStub(array $configs) {
- $config_storage = $this->getMock('Drupal\Core\Config\NullStorage');
+ $config_storage = $this->createMock('Drupal\Core\Config\NullStorage');
$config_storage->expects($this->any())
->method('listAll')
->will($this->returnValue(array_keys($configs)));
@@ -211,7 +213,7 @@ protected function getBlockMockWithMachineName($machine_name) {
* A mock translation object.
*/
public function getStringTranslationStub() {
- $translation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface');
+ $translation = $this->createMock('Drupal\Core\StringTranslation\TranslationInterface');
$translation->expects($this->any())
->method('translate')
->willReturnCallback(function ($string, array $args = [], array $options = []) use ($translation) {
@@ -241,7 +243,7 @@ public function getStringTranslationStub() {
* The container with the cache tags invalidator service.
*/
protected function getContainerWithCacheTagsInvalidator(CacheTagsInvalidatorInterface $cache_tags_validator) {
- $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
+ $container = $this->createMock('Symfony\Component\DependencyInjection\ContainerInterface');
$container->expects($this->any())
->method('get')
->with('cache_tags.invalidator')
@@ -258,7 +260,7 @@ protected function getContainerWithCacheTagsInvalidator(CacheTagsInvalidatorInte
* The class resolver stub.
*/
protected function getClassResolverStub() {
- $class_resolver = $this->getMock('Drupal\Core\DependencyInjection\ClassResolverInterface');
+ $class_resolver = $this->createMock('Drupal\Core\DependencyInjection\ClassResolverInterface');
$class_resolver->expects($this->any())
->method('getInstanceFromDefinition')
->will($this->returnCallback(function ($class) {
From 89e64c1fe0ef2bf6395453b372546ae58df9b59b Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Wed, 13 Dec 2017 06:55:11 +1000
Subject: [PATCH 029/232] Issue #2928249 by alexpott, mondrake: Introduce a
PHPUnit 6+ compatibility layer for Drupal\Tests\Listeners classes
---
core/modules/simpletest/src/WebTestBase.php | 4 +-
core/phpunit.xml.dist | 9 +-
...tener.php => DeprecationListenerTrait.php} | 12 ++-
...p => DrupalComponentTestListenerTrait.php} | 18 ++--
.../Drupal/Tests/Listeners/DrupalListener.php | 36 ++++++++
...r.php => DrupalStandardsListenerTrait.php} | 59 ++++++++++---
.../Tests/Listeners/HtmlOutputPrinter.php | 87 +++++++------------
.../Listeners/HtmlOutputPrinterTrait.php | 72 +++++++++++++++
.../Tests/Listeners/Legacy/DrupalListener.php | 29 +++++++
.../Listeners/Legacy/HtmlOutputPrinter.php | 33 +++++++
.../Tests/TestSuites/TestSuiteBaseTest.php | 10 ++-
core/tests/TestSuites/TestSuiteBase.php | 10 ++-
12 files changed, 290 insertions(+), 89 deletions(-)
rename core/tests/Drupal/Tests/Listeners/{DeprecationListener.php => DeprecationListenerTrait.php} (97%)
rename core/tests/Drupal/Tests/Listeners/{DrupalComponentTestListener.php => DrupalComponentTestListenerTrait.php} (50%)
create mode 100644 core/tests/Drupal/Tests/Listeners/DrupalListener.php
rename core/tests/Drupal/Tests/Listeners/{DrupalStandardsListener.php => DrupalStandardsListenerTrait.php} (78%)
create mode 100644 core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php
create mode 100644 core/tests/Drupal/Tests/Listeners/Legacy/DrupalListener.php
create mode 100644 core/tests/Drupal/Tests/Listeners/Legacy/HtmlOutputPrinter.php
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index 4b6f421e5524..441b2398f4b9 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -18,7 +18,7 @@
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\Tests\EntityViewTrait;
use Drupal\Tests\block\Traits\BlockCreationTrait as BaseBlockCreationTrait;
-use Drupal\Tests\Listeners\DeprecationListener;
+use Drupal\Tests\Listeners\DeprecationListenerTrait;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
use Drupal\Tests\node\Traits\NodeCreationTrait;
use Drupal\Tests\Traits\Core\CronRunTrait;
@@ -698,7 +698,7 @@ protected function curlHeaderCallback($curlHandler, $header) {
if ($parameters[1] === 'User deprecated function') {
if (getenv('SYMFONY_DEPRECATIONS_HELPER') !== 'disabled') {
$message = (string) $parameters[0];
- if (!in_array($message, DeprecationListener::getSkippedDeprecations())) {
+ if (!in_array($message, DeprecationListenerTrait::getSkippedDeprecations())) {
call_user_func_array([&$this, 'error'], $parameters);
}
}
diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist
index bfe74673b251..750c6e2b502e 100644
--- a/core/phpunit.xml.dist
+++ b/core/phpunit.xml.dist
@@ -49,16 +49,11 @@
-
+
-
+
-
-
-
-
diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListener.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
similarity index 97%
rename from core/tests/Drupal/Tests/Listeners/DeprecationListener.php
rename to core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
index 80b3d31c97e1..c73b6e59df40 100644
--- a/core/tests/Drupal/Tests/Listeners/DeprecationListener.php
+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
@@ -9,12 +9,18 @@
* This class will be removed once all the deprecation notices have been
* fixed.
*/
-class DeprecationListener extends \PHPUnit_Framework_BaseTestListener {
+trait DeprecationListenerTrait {
/**
- * {@inheritdoc}
+ * Reacts to the end of a test.
+ *
+ * @param \PHPUnit\Framework\Test|\PHPUnit_Framework_Test $test
+ * The test object that has ended its test run.
+ * @param float $time
+ * The time the test took.
*/
- public function endTest(\PHPUnit_Framework_Test $test, $time) {
+ protected function deprecationEndTest($test, $time) {
+ /** @var \PHPUnit\Framework\Test $test */
// Need to edit the file of deprecations.
if ($file = getenv('SYMFONY_DEPRECATIONS_SERIALIZE')) {
$deprecations = file_get_contents($file);
diff --git a/core/tests/Drupal/Tests/Listeners/DrupalComponentTestListener.php b/core/tests/Drupal/Tests/Listeners/DrupalComponentTestListenerTrait.php
similarity index 50%
rename from core/tests/Drupal/Tests/Listeners/DrupalComponentTestListener.php
rename to core/tests/Drupal/Tests/Listeners/DrupalComponentTestListenerTrait.php
index c3496773057b..70fa23604141 100644
--- a/core/tests/Drupal/Tests/Listeners/DrupalComponentTestListener.php
+++ b/core/tests/Drupal/Tests/Listeners/DrupalComponentTestListenerTrait.php
@@ -5,20 +5,28 @@
use Drupal\KernelTests\KernelTestBase;;
use Drupal\Tests\BrowserTestBase;;
use Drupal\Tests\UnitTestCase;
-use PHPUnit\Framework\BaseTestListener;
+use PHPUnit\Framework\AssertionFailedError;
/**
* Ensures that no component tests are extending a core test base class.
+ *
+ * @internal
*/
-class DrupalComponentTestListener extends BaseTestListener {
+trait DrupalComponentTestListenerTrait {
/**
- * {@inheritdoc}
+ * Reacts to the end of a test.
+ *
+ * @param \PHPUnit\Framework\Test|\PHPUnit_Framework_Test $test
+ * The test object that has ended its test run.
+ * @param float $time
+ * The time the test took.
*/
- public function endTest(\PHPUnit_Framework_Test $test, $time) {
+ protected function componentEndTest($test, $time) {
+ /** @var \PHPUnit\Framework\Test $test */
if (substr($test->toString(), 0, 22) == 'Drupal\Tests\Component') {
if ($test instanceof BrowserTestBase || $test instanceof KernelTestBase || $test instanceof UnitTestCase) {
- $error = new \PHPUnit_Framework_AssertionFailedError('Component tests should not extend a core test base class.');
+ $error = new AssertionFailedError('Component tests should not extend a core test base class.');
$test->getTestResultObject()->addFailure($test, $error, $time);
}
}
diff --git a/core/tests/Drupal/Tests/Listeners/DrupalListener.php b/core/tests/Drupal/Tests/Listeners/DrupalListener.php
new file mode 100644
index 000000000000..9ed976f76c44
--- /dev/null
+++ b/core/tests/Drupal/Tests/Listeners/DrupalListener.php
@@ -0,0 +1,36 @@
+deprecationEndTest($test, $time);
+ $this->componentEndTest($test, $time);
+ $this->standardsEndTest($test, $time);
+ }
+
+ }
+}
diff --git a/core/tests/Drupal/Tests/Listeners/DrupalStandardsListener.php b/core/tests/Drupal/Tests/Listeners/DrupalStandardsListenerTrait.php
similarity index 78%
rename from core/tests/Drupal/Tests/Listeners/DrupalStandardsListener.php
rename to core/tests/Drupal/Tests/Listeners/DrupalStandardsListenerTrait.php
index fd15f8218bc1..2676c46a4fe6 100644
--- a/core/tests/Drupal/Tests/Listeners/DrupalStandardsListener.php
+++ b/core/tests/Drupal/Tests/Listeners/DrupalStandardsListenerTrait.php
@@ -2,15 +2,18 @@
namespace Drupal\Tests\Listeners;
-use PHPUnit\Framework\BaseTestListener;
+use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\TestSuite;
/**
* Listens for PHPUnit tests and fails those with invalid coverage annotations.
*
* Enforces various coding standards within test runs.
+ *
+ * @internal
*/
-class DrupalStandardsListener extends BaseTestListener {
+trait DrupalStandardsListenerTrait {
/**
* Signals a coding standards failure to the user.
@@ -21,10 +24,10 @@ class DrupalStandardsListener extends BaseTestListener {
* The message to add to the failure notice. The test class name and test
* name will be appended to this message automatically.
*/
- protected function fail(TestCase $test, $message) {
+ private function fail(TestCase $test, $message) {
// Add the report to the test's results.
$message .= ': ' . get_class($test) . '::' . $test->getName();
- $fail = new \PHPUnit_Framework_AssertionFailedError($message);
+ $fail = new AssertionFailedError($message);
$result = $test->getTestResultObject();
$result->addFailure($test, $fail, 0);
}
@@ -38,7 +41,7 @@ protected function fail(TestCase $test, $message) {
* @return bool
* TRUE if the class exists, FALSE otherwise.
*/
- protected function classExists($class) {
+ private function classExists($class) {
return class_exists($class, TRUE) || trait_exists($class, TRUE) || interface_exists($class, TRUE);
}
@@ -50,7 +53,7 @@ protected function classExists($class) {
* @param \PHPUnit\Framework\TestCase $test
* The test to examine.
*/
- public function checkValidCoversForTest(TestCase $test) {
+ private function checkValidCoversForTest(TestCase $test) {
// If we're generating a coverage report already, don't do anything here.
if ($test->getTestResultObject() && $test->getTestResultObject()->getCollectCodeCoverageInformation()) {
return;
@@ -141,7 +144,7 @@ public function checkValidCoversForTest(TestCase $test) {
}
/**
- * {@inheritdoc}
+ * Reacts to the end of a test.
*
* We must mark this method as belonging to the special legacy group because
* it might trigger an E_USER_DEPRECATED error during coverage annotation
@@ -151,22 +154,58 @@ public function checkValidCoversForTest(TestCase $test) {
*
* @group legacy
*
+ * @param \PHPUnit\Framework\Test|\PHPUnit_Framework_Test $test
+ * The test object that has ended its test run.
+ * @param float $time
+ * The time the test took.
+ *
* @see http://symfony.com/doc/current/components/phpunit_bridge.html#mark-tests-as-legacy
*/
- public function endTest(\PHPUnit_Framework_Test $test, $time) {
+ private function doEndTest($test, $time) {
// \PHPUnit_Framework_Test does not have any useful methods of its own for
// our purpose, so we have to distinguish between the different known
// subclasses.
if ($test instanceof TestCase) {
$this->checkValidCoversForTest($test);
}
- elseif ($test instanceof \PHPUnit_Framework_TestSuite) {
+ elseif ($this->isTestSuite($test)) {
foreach ($test->getGroupDetails() as $tests) {
foreach ($tests as $test) {
- $this->endTest($test, $time);
+ $this->doEndTest($test, $time);
}
}
}
}
+ /**
+ * Determine if a test object is a test suite regardless of PHPUnit version.
+ *
+ * @param \PHPUnit\Framework\Test|\PHPUnit_Framework_Test $test
+ * The test object to test if it is a test suite.
+ *
+ * @return bool
+ * TRUE if it is a test suite, FALSE if not.
+ */
+ private function isTestSuite($test) {
+ if (class_exists('\PHPUnit_Framework_TestSuite') && $test instanceof \PHPUnit_Framework_TestSuite) {
+ return TRUE;
+ }
+ if (class_exists('PHPUnit\Framework\TestSuite') && $test instanceof TestSuite) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Reacts to the end of a test.
+ *
+ * @param \PHPUnit\Framework\Test|\PHPUnit_Framework_Test $test
+ * The test object that has ended its test run.
+ * @param float $time
+ * The time the test took.
+ */
+ protected function standardsEndTest($test, $time) {
+ $this->doEndTest($test, $time);
+ }
+
}
diff --git a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php
index ac22072d1636..80219898682f 100644
--- a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php
+++ b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinter.php
@@ -2,70 +2,41 @@
namespace Drupal\Tests\Listeners;
-/**
- * Defines a class for providing html output results for functional tests.
- */
-class HtmlOutputPrinter extends \PHPUnit_TextUI_ResultPrinter {
-
+use PHPUnit\Framework\TestResult;
+use PHPUnit\TextUI\ResultPrinter;
+
+if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) {
+ class_alias('Drupal\Tests\Listeners\Legacy\HtmlOutputPrinter', 'Drupal\Tests\Listeners\HtmlOutputPrinter');
+ // Using an early return instead of a else does not work when using the
+ // PHPUnit phar due to some weird PHP behavior (the class gets defined without
+ // executing the code before it and so the definition is not properly
+ // conditional).
+}
+else {
/**
- * File to write html links to.
+ * Defines a class for providing html output results for functional tests.
*
- * @var string
+ * @internal
*/
- protected $browserOutputFile;
-
- /**
- * {@inheritdoc}
- */
- public function __construct($out, $verbose, $colors, $debug, $numberOfColumns) {
- parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns);
- if ($html_output_directory = getenv('BROWSERTEST_OUTPUT_DIRECTORY')) {
- // Initialize html output debugging.
- $html_output_directory = rtrim($html_output_directory, '/');
-
- // Check if directory exists.
- if (!is_dir($html_output_directory) || !is_writable($html_output_directory)) {
- $this->writeWithColor('bg-red, fg-black', "HTML output directory $html_output_directory is not a writable directory.");
- }
- else {
- // Convert to a canonicalized absolute pathname just in case the current
- // working directory is changed.
- $html_output_directory = realpath($html_output_directory);
- $this->browserOutputFile = tempnam($html_output_directory, 'browser_output_');
- if ($this->browserOutputFile) {
- touch($this->browserOutputFile);
- }
- else {
- $this->writeWithColor('bg-red, fg-black', "Unable to create a temporary file in $html_output_directory.");
- }
- }
- }
-
- if ($this->browserOutputFile) {
- putenv('BROWSERTEST_OUTPUT_FILE=' . $this->browserOutputFile);
- }
- else {
- // Remove any environment variable.
- putenv('BROWSERTEST_OUTPUT_FILE');
+ class HtmlOutputPrinter extends ResultPrinter {
+ use HtmlOutputPrinterTrait;
+ /**
+ * {@inheritdoc}
+ */
+ public function __construct($out = NULL, $verbose = FALSE, $colors = self::COLOR_DEFAULT, $debug = FALSE, $numberOfColumns = 80, $reverse = FALSE) {
+ parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse);
+
+ $this->setUpHtmlOutput();
}
- }
- /**
- * {@inheritdoc}
- */
- public function printResult(\PHPUnit_Framework_TestResult $result) {
- parent::printResult($result);
+ /**
+ * {@inheritdoc}
+ */
+ public function printResult(TestResult $result) {
+ parent::printResult($result);
- if ($this->browserOutputFile) {
- $contents = file_get_contents($this->browserOutputFile);
- if ($contents) {
- $this->writeNewLine();
- $this->writeWithColor('bg-yellow, fg-black', 'HTML output was generated');
- $this->write($contents);
- }
- // No need to keep the file around any more.
- unlink($this->browserOutputFile);
+ $this->printHtmlOutput();
}
- }
+ }
}
diff --git a/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php
new file mode 100644
index 000000000000..1dd67eb9e9a3
--- /dev/null
+++ b/core/tests/Drupal/Tests/Listeners/HtmlOutputPrinterTrait.php
@@ -0,0 +1,72 @@
+writeWithColor('bg-red, fg-black', "HTML output directory $html_output_directory is not a writable directory.");
+ }
+ else {
+ // Convert to a canonicalized absolute pathname just in case the current
+ // working directory is changed.
+ $html_output_directory = realpath($html_output_directory);
+ $this->browserOutputFile = tempnam($html_output_directory, 'browser_output_');
+ if ($this->browserOutputFile) {
+ touch($this->browserOutputFile);
+ }
+ else {
+ $this->writeWithColor('bg-red, fg-black', "Unable to create a temporary file in $html_output_directory.");
+ }
+ }
+ }
+
+ if ($this->browserOutputFile) {
+ putenv('BROWSERTEST_OUTPUT_FILE=' . $this->browserOutputFile);
+ }
+ else {
+ // Remove any environment variable.
+ putenv('BROWSERTEST_OUTPUT_FILE');
+ }
+ }
+
+ /**
+ * Prints the list of HTML output generated during the test.
+ */
+ protected function printHtmlOutput() {
+ if ($this->browserOutputFile) {
+ $contents = file_get_contents($this->browserOutputFile);
+ if ($contents) {
+ $this->writeNewLine();
+ $this->writeWithColor('bg-yellow, fg-black', 'HTML output was generated');
+ $this->write($contents);
+ }
+ // No need to keep the file around any more.
+ unlink($this->browserOutputFile);
+ }
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Listeners/Legacy/DrupalListener.php b/core/tests/Drupal/Tests/Listeners/Legacy/DrupalListener.php
new file mode 100644
index 000000000000..f7c2c76668cb
--- /dev/null
+++ b/core/tests/Drupal/Tests/Listeners/Legacy/DrupalListener.php
@@ -0,0 +1,29 @@
+deprecationEndTest($test, $time);
+ $this->componentEndTest($test, $time);
+ $this->standardsEndTest($test, $time);
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Listeners/Legacy/HtmlOutputPrinter.php b/core/tests/Drupal/Tests/Listeners/Legacy/HtmlOutputPrinter.php
new file mode 100644
index 000000000000..7c1f45e38f90
--- /dev/null
+++ b/core/tests/Drupal/Tests/Listeners/Legacy/HtmlOutputPrinter.php
@@ -0,0 +1,33 @@
+setUpHtmlOutput();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function printResult(\PHPUnit_Framework_TestResult $result) {
+ parent::printResult($result);
+
+ $this->printHtmlOutput();
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/TestSuites/TestSuiteBaseTest.php b/core/tests/Drupal/Tests/TestSuites/TestSuiteBaseTest.php
index 4c289e4d78bb..41a98419ee03 100644
--- a/core/tests/Drupal/Tests/TestSuites/TestSuiteBaseTest.php
+++ b/core/tests/Drupal/Tests/TestSuites/TestSuiteBaseTest.php
@@ -34,6 +34,13 @@ protected function getFilesystem() {
],
'Tests' => [
'CoreUnitTest.php' => ' [
+ 'Listener.php' => ' [
+ 'Listener.php' => 'invokeArgs($stub, [vfsStream::url('root'), $suite_namespace]);
// Determine if we loaded the expected test files.
- $this->assertNotEmpty($stub->testFiles);
- $this->assertEmpty(array_diff_assoc($expected_tests, $stub->testFiles));
+ $this->assertEquals($expected_tests, $stub->testFiles);
}
/**
diff --git a/core/tests/TestSuites/TestSuiteBase.php b/core/tests/TestSuites/TestSuiteBase.php
index 82a13ba25574..e5925debb901 100644
--- a/core/tests/TestSuites/TestSuiteBase.php
+++ b/core/tests/TestSuites/TestSuiteBase.php
@@ -3,11 +3,12 @@
namespace Drupal\Tests\TestSuites;
use Drupal\simpletest\TestDiscovery;
+use PHPUnit\Framework\TestSuite;
/**
* Base class for Drupal test suites.
*/
-abstract class TestSuiteBase extends \PHPUnit_Framework_TestSuite {
+abstract class TestSuiteBase extends TestSuite {
/**
* Finds extensions in a Drupal installation.
@@ -40,7 +41,12 @@ protected function addTestsBySuiteNamespace($root, $suite_namespace) {
// always inside of core/tests/Drupal/${suite_namespace}Tests. The exception
// to this is Unit tests for historical reasons.
if ($suite_namespace == 'Unit') {
- $this->addTestFiles(TestDiscovery::scanDirectory("Drupal\\Tests\\", "$root/core/tests/Drupal/Tests"));
+ $tests = TestDiscovery::scanDirectory("Drupal\\Tests\\", "$root/core/tests/Drupal/Tests");
+ $tests = array_filter($tests, function ($test) use ($root) {
+ // The Listeners directory does not contain tests.
+ return !preg_match("@^$root/core/tests/Drupal/Tests/Listeners(/|$)@", dirname($test));
+ });
+ $this->addTestFiles($tests);
}
else {
$this->addTestFiles(TestDiscovery::scanDirectory("Drupal\\${suite_namespace}Tests\\", "$root/core/tests/Drupal/${suite_namespace}Tests"));
From 8c44f2085ebcdd488aaeace5d6183e80ded1b0b2 Mon Sep 17 00:00:00 2001
From: Francesco Placella
Date: Wed, 13 Dec 2017 00:17:41 +0100
Subject: [PATCH 030/232] Issue #2930197 by mondrake, amateescu:
EntityDefinitionUpdateTest fails with contrib db driver (again)
---
.../Core/Entity/EntityDefinitionUpdateTest.php | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
index 4842149dacbd..fa55935bbad4 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
@@ -465,7 +465,9 @@ public function testBaseFieldDeleteWithExistingData($entity_type_id, $create_ent
// Only one row will be created for non-revisionable base fields.
$this->assertCount($base_field_revisionable ? 2 : 1, $result);
- $this->assertSame([
+ // Use assertEquals and not assertSame here to prevent that a different
+ // sequence of the columns in the table will affect the check.
+ $this->assertEquals([
'bundle' => $entity->bundle(),
'deleted' => '1',
'entity_id' => $entity->id(),
@@ -477,7 +479,9 @@ public function testBaseFieldDeleteWithExistingData($entity_type_id, $create_ent
// Two rows only exist if the base field is revisionable.
if ($base_field_revisionable) {
- $this->assertSame([
+ // Use assertEquals and not assertSame here to prevent that a different
+ // sequence of the columns in the table will affect the check.
+ $this->assertEquals([
'bundle' => $entity->bundle(),
'deleted' => '1',
'entity_id' => $entity->id(),
From 4d629c4d811f305dbfebadc3dfdf34cd9630ad1d Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Wed, 13 Dec 2017 11:47:56 +0000
Subject: [PATCH 031/232] Issue #2627512 by gambry, jhedstrom, mpdonadio, Jo
Fitzgerald, jibran, tedbow, bkosborne, alexpott, vprocessor, xjm: Datetime
Views plugins don't support timezones
---
core/modules/datetime/datetime.views.inc | 2 +
.../src/Plugin/views/argument/Date.php | 31 ++-
.../datetime/src/Plugin/views/filter/Date.php | 82 ++++++-
.../datetime/src/Plugin/views/sort/Date.php | 32 ++-
.../src/Kernel/Views/ArgumentDateTimeTest.php | 25 ++
.../Kernel/Views/DateTimeHandlerTestBase.php | 15 ++
.../tests/src/Kernel/Views/FilterDateTest.php | 231 +++++++++++++-----
.../src/Kernel/Views/FilterDateTimeTest.php | 3 +
.../Plugin/views/query/DateSqlInterface.php | 62 +++++
.../src/Plugin/views/query/MysqlDateSql.php | 93 +++++++
.../Plugin/views/query/PostgresqlDateSql.php | 93 +++++++
.../Plugin/views/query/QueryPluginBase.php | 38 ++-
.../views/src/Plugin/views/query/Sql.php | 186 +++-----------
.../src/Plugin/views/query/SqliteDateSql.php | 122 +++++++++
.../src/Plugin/views/query/QueryTest.php | 5 +
.../tests/src/Unit/Plugin/query/SqlTest.php | 28 ++-
.../Plugin/views/query/MysqlDateSqlTest.php | 97 ++++++++
.../views/query/PostgresqlDateSqlTest.php | 97 ++++++++
.../Plugin/views/query/SqliteDateSqlTest.php | 98 ++++++++
core/modules/views/views.services.yml | 13 +
20 files changed, 1115 insertions(+), 238 deletions(-)
create mode 100644 core/modules/views/src/Plugin/views/query/DateSqlInterface.php
create mode 100644 core/modules/views/src/Plugin/views/query/MysqlDateSql.php
create mode 100644 core/modules/views/src/Plugin/views/query/PostgresqlDateSql.php
create mode 100644 core/modules/views/src/Plugin/views/query/SqliteDateSql.php
create mode 100644 core/modules/views/tests/src/Unit/Plugin/views/query/MysqlDateSqlTest.php
create mode 100644 core/modules/views/tests/src/Unit/Plugin/views/query/PostgresqlDateSqlTest.php
create mode 100644 core/modules/views/tests/src/Unit/Plugin/views/query/SqliteDateSqlTest.php
diff --git a/core/modules/datetime/datetime.views.inc b/core/modules/datetime/datetime.views.inc
index d3b0d18617d0..93d6cd4d3038 100644
--- a/core/modules/datetime/datetime.views.inc
+++ b/core/modules/datetime/datetime.views.inc
@@ -39,6 +39,8 @@ function datetime_field_views_data(FieldStorageConfigInterface $field_storage) {
'argument' => [
'field' => $field_storage->getName() . '_value',
'id' => 'datetime_' . $argument_type,
+ 'entity_type' => $field_storage->getTargetEntityTypeId(),
+ 'field_name' => $field_storage->getName(),
],
'group' => $group,
];
diff --git a/core/modules/datetime/src/Plugin/views/argument/Date.php b/core/modules/datetime/src/Plugin/views/argument/Date.php
index 3e7d461adff5..4b8caa57c6aa 100644
--- a/core/modules/datetime/src/Plugin/views/argument/Date.php
+++ b/core/modules/datetime/src/Plugin/views/argument/Date.php
@@ -2,6 +2,9 @@
namespace Drupal\datetime\Plugin\views\argument;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
+use Drupal\views\FieldAPIHandlerTrait;
use Drupal\views\Plugin\views\argument\Date as NumericDate;
/**
@@ -22,12 +25,36 @@
*/
class Date extends NumericDate {
+ use FieldAPIHandlerTrait;
+
+ /**
+ * Determines if the timezone offset is calculated.
+ *
+ * @var bool
+ */
+ protected $calculateOffset = TRUE;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition, $route_match);
+
+ $definition = $this->getFieldStorageDefinition();
+ if ($definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
+ // Timezone offset calculation is not applicable to dates that are stored
+ // as date-only.
+ $this->calculateOffset = FALSE;
+ }
+ }
+
/**
* {@inheritdoc}
*/
public function getDateField() {
- // Return the real field, since it is already in string format.
- return "$this->tableAlias.$this->realField";
+ // Use string date storage/formatting since datetime fields are stored as
+ // strings rather than UNIX timestamps.
+ return $this->query->getDateField("$this->tableAlias.$this->realField", TRUE, $this->calculateOffset);
}
/**
diff --git a/core/modules/datetime/src/Plugin/views/filter/Date.php b/core/modules/datetime/src/Plugin/views/filter/Date.php
index 378f33fe84c8..14215206c58e 100644
--- a/core/modules/datetime/src/Plugin/views/filter/Date.php
+++ b/core/modules/datetime/src/Plugin/views/filter/Date.php
@@ -2,6 +2,7 @@
namespace Drupal\datetime\Plugin\views\filter;
+use Drupal\Component\Datetime\DateTimePlus;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
@@ -41,6 +42,13 @@ class Date extends NumericDate implements ContainerFactoryPluginInterface {
*/
protected $dateFormat = DateTimeItemInterface::DATETIME_STORAGE_FORMAT;
+ /**
+ * Determines if the timezone offset is calculated.
+ *
+ * @var bool
+ */
+ protected $calculateOffset = TRUE;
+
/**
* The request stack used to determin current time.
*
@@ -67,10 +75,13 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
$this->dateFormatter = $date_formatter;
$this->requestStack = $request_stack;
- // Date format depends on field storage format.
$definition = $this->getFieldStorageDefinition();
if ($definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
+ // Date format depends on field storage format.
$this->dateFormat = DateTimeItemInterface::DATE_STORAGE_FORMAT;
+ // Timezone offset calculation is not applicable to dates that are stored
+ // as date-only.
+ $this->calculateOffset = FALSE;
}
}
@@ -91,20 +102,23 @@ public static function create(ContainerInterface $container, array $configuratio
* Override parent method, which deals with dates as integers.
*/
protected function opBetween($field) {
- $origin = ($this->value['type'] == 'offset') ? $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME') : 0;
- $a = intval(strtotime($this->value['min'], $origin));
- $b = intval(strtotime($this->value['max'], $origin));
+ $timezone = $this->getTimezone();
+ $origin_offset = $this->getOffset($this->value['min'], $timezone);
- // Formatting will vary on date storage.
+ // Although both 'min' and 'max' values are required, default empty 'min'
+ // value as UNIX timestamp 0.
+ $min = (!empty($this->value['min'])) ? $this->value['min'] : '@0';
// Convert to ISO format and format for query. UTC timezone is used since
// dates are stored in UTC.
- $a = $this->query->getDateFormat("'" . $this->dateFormatter->format($a, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", $this->dateFormat, TRUE);
- $b = $this->query->getDateFormat("'" . $this->dateFormatter->format($b, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", $this->dateFormat, TRUE);
+ $a = new DateTimePlus($min, new \DateTimeZone($timezone));
+ $a = $this->query->getDateFormat($this->query->getDateField("'" . $this->dateFormatter->format($a->getTimestamp() + $origin_offset, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
+ $b = new DateTimePlus($this->value['max'], new \DateTimeZone($timezone));
+ $b = $this->query->getDateFormat($this->query->getDateField("'" . $this->dateFormatter->format($b->getTimestamp() + $origin_offset, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
// This is safe because we are manually scrubbing the values.
$operator = strtoupper($this->operator);
- $field = $this->query->getDateFormat($field, $this->dateFormat, TRUE);
+ $field = $this->query->getDateFormat($this->query->getDateField($field, TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
$this->query->addWhereExpression($this->options['group'], "$field $operator $a AND $b");
}
@@ -112,15 +126,57 @@ protected function opBetween($field) {
* Override parent method, which deals with dates as integers.
*/
protected function opSimple($field) {
- $origin = (!empty($this->value['type']) && $this->value['type'] == 'offset') ? $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME') : 0;
- $value = intval(strtotime($this->value['value'], $origin));
+ $timezone = $this->getTimezone();
+ $origin_offset = $this->getOffset($this->value['value'], $timezone);
- // Convert to ISO. UTC is used since dates are stored in UTC.
- $value = $this->query->getDateFormat("'" . $this->dateFormatter->format($value, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", $this->dateFormat, TRUE);
+ // Convert to ISO. UTC timezone is used since dates are stored in UTC.
+ $value = new DateTimePlus($this->value['value'], new \DateTimeZone($timezone));
+ $value = $this->query->getDateFormat($this->query->getDateField("'" . $this->dateFormatter->format($value->getTimestamp() + $origin_offset, 'custom', DateTimeItemInterface::DATETIME_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE) . "'", TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
// This is safe because we are manually scrubbing the value.
- $field = $this->query->getDateFormat($field, $this->dateFormat, TRUE);
+ $field = $this->query->getDateFormat($this->query->getDateField($field, TRUE, $this->calculateOffset), $this->dateFormat, TRUE);
$this->query->addWhereExpression($this->options['group'], "$field $this->operator $value");
}
+ /**
+ * Get the proper time zone to use in computations.
+ *
+ * Date-only fields do not have a time zone associated with them, so the
+ * filter input needs to use UTC for reference. Otherwise, use the time zone
+ * for the current user.
+ *
+ * @return string
+ * The time zone name.
+ */
+ protected function getTimezone() {
+ return $this->dateFormat === DateTimeItemInterface::DATE_STORAGE_FORMAT
+ ? DateTimeItemInterface::STORAGE_TIMEZONE
+ : drupal_get_user_timezone();
+ }
+
+ /**
+ * Get the proper offset from UTC to use in computations.
+ *
+ * @param string $time
+ * A date/time string compatible with \DateTime. It is used as the
+ * reference for computing the offset, which can vary based on the time
+ * zone rules.
+ * @param string $timezone
+ * The time zone that $time is in.
+ *
+ * @return int
+ * The computed offset in seconds.
+ */
+ protected function getOffset($time, $timezone) {
+ // Date-only fields do not have a time zone or offset from UTC associated
+ // with them. For relative (i.e. 'offset') comparisons, we need to compute
+ // the user's offset from UTC for use in the query.
+ $origin_offset = 0;
+ if ($this->dateFormat === DateTimeItemInterface::DATE_STORAGE_FORMAT && $this->value['type'] === 'offset') {
+ $origin_offset = $origin_offset + timezone_offset_get(new \DateTimeZone(drupal_get_user_timezone()), new \DateTime($time, new \DateTimeZone($timezone)));
+ }
+
+ return $origin_offset;
+ }
+
}
diff --git a/core/modules/datetime/src/Plugin/views/sort/Date.php b/core/modules/datetime/src/Plugin/views/sort/Date.php
index 2c8338ad2599..0049e867feb7 100644
--- a/core/modules/datetime/src/Plugin/views/sort/Date.php
+++ b/core/modules/datetime/src/Plugin/views/sort/Date.php
@@ -2,6 +2,8 @@
namespace Drupal\datetime\Plugin\views\sort;
+use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
+use Drupal\views\FieldAPIHandlerTrait;
use Drupal\views\Plugin\views\sort\Date as NumericDate;
/**
@@ -14,12 +16,38 @@
*/
class Date extends NumericDate {
+ use FieldAPIHandlerTrait;
+
/**
+ * Determines if the timezone offset is calculated.
+ *
+ * @var bool
+ */
+ protected $calculateOffset = TRUE;
+
+ /**
+ * {@inheritdoc}
+ */
+ public function __construct(array $configuration, $plugin_id, $plugin_definition) {
+ parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+ $definition = $this->getFieldStorageDefinition();
+ if ($definition->getSetting('datetime_type') === DateTimeItem::DATETIME_TYPE_DATE) {
+ // Timezone offset calculation is not applicable to dates that are stored
+ // as date-only.
+ $this->calculateOffset = FALSE;
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
* Override to account for dates stored as strings.
*/
public function getDateField() {
- // Return the real field, since it is already in string format.
- return "$this->tableAlias.$this->realField";
+ // Use string date storage/formatting since datetime fields are stored as
+ // strings rather than UNIX timestamps.
+ return $this->query->getDateField("$this->tableAlias.$this->realField", TRUE, $this->calculateOffset);
}
/**
diff --git a/core/modules/datetime/tests/src/Kernel/Views/ArgumentDateTimeTest.php b/core/modules/datetime/tests/src/Kernel/Views/ArgumentDateTimeTest.php
index 614548395876..8261b27793f8 100644
--- a/core/modules/datetime/tests/src/Kernel/Views/ArgumentDateTimeTest.php
+++ b/core/modules/datetime/tests/src/Kernel/Views/ArgumentDateTimeTest.php
@@ -28,6 +28,9 @@ protected function setUp($import_test_views = TRUE) {
'2000-10-10',
'2001-10-10',
'2002-01-01',
+ // Add a date that is the year 2002 in UTC, but 2003 in the site's time
+ // zone (Australia/Sydney).
+ '2002-12-31T23:00:00',
];
foreach ($dates as $date) {
$node = Node::create([
@@ -64,6 +67,25 @@ public function testDatetimeArgumentYear() {
$expected[] = ['nid' => $this->nodes[2]->id()];
$this->assertIdenticalResultset($view, $expected, $this->map);
$view->destroy();
+
+ $view->setDisplay('default');
+ $this->executeView($view, ['2003']);
+ $expected = [];
+ $expected[] = ['nid' => $this->nodes[3]->id()];
+ $this->assertIdenticalResultset($view, $expected, $this->map);
+ $view->destroy();
+
+ // Tests different system timezone with the same nodes.
+ $this->setSiteTimezone('America/Vancouver');
+
+ $view->setDisplay('default');
+ $this->executeView($view, ['2002']);
+ $expected = [];
+ // Only the 3rd node is returned here since UTC 2002-01-01T00:00:00 is still
+ // in 2001 for this user timezone.
+ $expected[] = ['nid' => $this->nodes[3]->id()];
+ $this->assertIdenticalResultset($view, $expected, $this->map);
+ $view->destroy();
}
/**
@@ -87,6 +109,7 @@ public function testDatetimeArgumentMonth() {
$this->executeView($view, ['01']);
$expected = [];
$expected[] = ['nid' => $this->nodes[2]->id()];
+ $expected[] = ['nid' => $this->nodes[3]->id()];
$this->assertIdenticalResultset($view, $expected, $this->map);
$view->destroy();
}
@@ -112,6 +135,7 @@ public function testDatetimeArgumentDay() {
$this->executeView($view, ['01']);
$expected = [];
$expected[] = ['nid' => $this->nodes[2]->id()];
+ $expected[] = ['nid' => $this->nodes[3]->id()];
$this->assertIdenticalResultset($view, $expected, $this->map);
$view->destroy();
}
@@ -157,6 +181,7 @@ public function testDatetimeArgumentWeek() {
$this->executeView($view, ['01']);
$expected = [];
$expected[] = ['nid' => $this->nodes[2]->id()];
+ $expected[] = ['nid' => $this->nodes[3]->id()];
$this->assertIdenticalResultset($view, $expected, $this->map);
$view->destroy();
}
diff --git a/core/modules/datetime/tests/src/Kernel/Views/DateTimeHandlerTestBase.php b/core/modules/datetime/tests/src/Kernel/Views/DateTimeHandlerTestBase.php
index f0c9c786144a..20a3542319f8 100644
--- a/core/modules/datetime/tests/src/Kernel/Views/DateTimeHandlerTestBase.php
+++ b/core/modules/datetime/tests/src/Kernel/Views/DateTimeHandlerTestBase.php
@@ -41,6 +41,7 @@ abstract class DateTimeHandlerTestBase extends ViewsKernelTestBase {
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
+ $this->installSchema('node', 'node_access');
$this->installEntitySchema('node');
$this->installEntitySchema('user');
@@ -76,4 +77,18 @@ protected function setUp($import_test_views = TRUE) {
ViewTestData::createTestViews(get_class($this), ['datetime_test']);
}
+ /**
+ * Sets the site timezone to a given timezone.
+ *
+ * @param string $timezone
+ * The timezone identifier to set.
+ */
+ protected function setSiteTimezone($timezone) {
+ // Set an explicit site timezone, and disallow per-user timezones.
+ $this->config('system.date')
+ ->set('timezone.user.configurable', 0)
+ ->set('timezone.default', $timezone)
+ ->save();
+ }
+
}
diff --git a/core/modules/datetime/tests/src/Kernel/Views/FilterDateTest.php b/core/modules/datetime/tests/src/Kernel/Views/FilterDateTest.php
index 23f40948e868..f4a6342956a7 100644
--- a/core/modules/datetime/tests/src/Kernel/Views/FilterDateTest.php
+++ b/core/modules/datetime/tests/src/Kernel/Views/FilterDateTest.php
@@ -2,8 +2,8 @@
namespace Drupal\Tests\datetime\Kernel\Views;
+use Drupal\Component\Datetime\DateTimePlus;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
-use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\node\Entity\Node;
use Drupal\views\Views;
@@ -21,9 +21,26 @@ class FilterDateTest extends DateTimeHandlerTestBase {
public static $testViews = ['test_filter_datetime'];
/**
- * For offset tests, set to the current time.
+ * An array of timezone extremes to test.
+ *
+ * @var string[]
*/
- protected static $date;
+ protected static $timezones = [
+ // UTC-12, no DST.
+ 'Pacific/Kwajalein',
+ // UTC-11, no DST.
+ 'Pacific/Midway',
+ // UTC-7, no DST.
+ 'America/Phoenix',
+ // UTC.
+ 'UTC',
+ // UTC+5:30, no DST.
+ 'Asia/Kolkata',
+ // UTC+12, no DST.
+ 'Pacific/Funafuti',
+ // UTC+13, no DST.
+ 'Pacific/Tongatapu',
+ ];
/**
* {@inheritdoc}
@@ -33,30 +50,24 @@ class FilterDateTest extends DateTimeHandlerTestBase {
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
- // Set to 'today'.
- static::$date = REQUEST_TIME;
-
// Change field storage to date-only.
$storage = FieldStorageConfig::load('node.' . static::$field_name);
$storage->setSetting('datetime_type', DateTimeItem::DATETIME_TYPE_DATE);
$storage->save();
- $dates = [
- // Tomorrow.
- \Drupal::service('date.formatter')->format(static::$date + 86400, 'custom', DateTimeItemInterface::DATE_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE),
- // Today.
- \Drupal::service('date.formatter')->format(static::$date, 'custom', DateTimeItemInterface::DATE_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE),
- // Yesterday.
- \Drupal::service('date.formatter')->format(static::$date - 86400, 'custom', DateTimeItemInterface::DATE_STORAGE_FORMAT, DateTimeItemInterface::STORAGE_TIMEZONE),
- ];
+ // Retrieve tomorrow, today and yesterday dates just to create the nodes.
+ $timestamp = $this->getUTCEquivalentOfUserNowAsTimestamp();
+ $dates = $this->getRelativeDateValuesFromTimestamp($timestamp);
+ // Clean the nodes on setUp.
+ $this->nodes = [];
foreach ($dates as $date) {
$node = Node::create([
'title' => $this->randomMachineName(8),
'type' => 'page',
'field_date' => [
'value' => $date,
- ]
+ ],
]);
$node->save();
$this->nodes[] = $node;
@@ -70,48 +81,156 @@ public function testDateOffsets() {
$view = Views::getView('test_filter_datetime');
$field = static::$field_name . '_value';
- // Test simple operations.
- $view->initHandlers();
-
- // A greater than or equal to 'now', should return the 'today' and
- // the 'tomorrow' node.
- $view->filter[$field]->operator = '>=';
- $view->filter[$field]->value['type'] = 'offset';
- $view->filter[$field]->value['value'] = 'now';
- $view->setDisplay('default');
- $this->executeView($view);
- $expected_result = [
- ['nid' => $this->nodes[0]->id()],
- ['nid' => $this->nodes[1]->id()],
- ];
- $this->assertIdenticalResultset($view, $expected_result, $this->map);
- $view->destroy();
-
- // Only dates in the past.
- $view->initHandlers();
- $view->filter[$field]->operator = '<';
- $view->filter[$field]->value['type'] = 'offset';
- $view->filter[$field]->value['value'] = 'now';
- $view->setDisplay('default');
- $this->executeView($view);
- $expected_result = [
- ['nid' => $this->nodes[2]->id()],
- ];
- $this->assertIdenticalResultset($view, $expected_result, $this->map);
- $view->destroy();
-
- // Test offset for between operator. Only the 'tomorrow' node should appear.
- $view->initHandlers();
- $view->filter[$field]->operator = 'between';
- $view->filter[$field]->value['type'] = 'offset';
- $view->filter[$field]->value['max'] = '+2 days';
- $view->filter[$field]->value['min'] = '+1 day';
- $view->setDisplay('default');
- $this->executeView($view);
- $expected_result = [
- ['nid' => $this->nodes[0]->id()],
+ foreach (static::$timezones as $timezone) {
+
+ $this->setSiteTimezone($timezone);
+ $timestamp = $this->getUTCEquivalentOfUserNowAsTimestamp();
+ $dates = $this->getRelativeDateValuesFromTimestamp($timestamp);
+ $this->updateNodesDateFieldsValues($dates);
+
+ // Test simple operations.
+ $view->initHandlers();
+
+ // A greater than or equal to 'now', should return the 'today' and the
+ // 'tomorrow' node.
+ $view->filter[$field]->operator = '>=';
+ $view->filter[$field]->value['type'] = 'offset';
+ $view->filter[$field]->value['value'] = 'now';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[0]->id()],
+ ['nid' => $this->nodes[1]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ // Only dates in the past.
+ $view->initHandlers();
+ $view->filter[$field]->operator = '<';
+ $view->filter[$field]->value['type'] = 'offset';
+ $view->filter[$field]->value['value'] = 'now';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[2]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ // Test offset for between operator. Only 'tomorrow' node should appear.
+ $view->initHandlers();
+ $view->filter[$field]->operator = 'between';
+ $view->filter[$field]->value['type'] = 'offset';
+ $view->filter[$field]->value['max'] = '+2 days';
+ $view->filter[$field]->value['min'] = '+1 day';
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[0]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+ }
+ }
+
+ /**
+ * Test date filter with date-only fields.
+ */
+ public function testDateIs() {
+ $view = Views::getView('test_filter_datetime');
+ $field = static::$field_name . '_value';
+
+ foreach (static::$timezones as $timezone) {
+
+ $this->setSiteTimezone($timezone);
+ $timestamp = $this->getUTCEquivalentOfUserNowAsTimestamp();
+ $dates = $this->getRelativeDateValuesFromTimestamp($timestamp);
+ $this->updateNodesDateFieldsValues($dates);
+
+ // Test simple operations.
+ $view->initHandlers();
+
+ // Filtering with nodes date-only values (format: Y-m-d) to test UTC
+ // conversion does NOT change the day.
+ $view->filter[$field]->operator = '=';
+ $view->filter[$field]->value['type'] = 'date';
+ $view->filter[$field]->value['value'] = $this->nodes[2]->field_date->first()->getValue()['value'];
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[2]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+
+ // Test offset for between operator. Only 'today' and 'tomorrow' nodes
+ // should appear.
+ $view->initHandlers();
+ $view->filter[$field]->operator = 'between';
+ $view->filter[$field]->value['type'] = 'date';
+ $view->filter[$field]->value['max'] = $this->nodes[0]->field_date->first()->getValue()['value'];
+ $view->filter[$field]->value['min'] = $this->nodes[1]->field_date->first()->getValue()['value'];
+ $view->setDisplay('default');
+ $this->executeView($view);
+ $expected_result = [
+ ['nid' => $this->nodes[0]->id()],
+ ['nid' => $this->nodes[1]->id()],
+ ];
+ $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ $view->destroy();
+ }
+ }
+
+ /**
+ * Returns UTC timestamp of user's TZ 'now'.
+ *
+ * The date field stores date_only values without conversion, considering them
+ * already as UTC. This method returns the UTC equivalent of user's 'now' as a
+ * unix timestamp, so they match using Y-m-d format.
+ *
+ * @return int
+ * Unix timestamp.
+ */
+ protected function getUTCEquivalentOfUserNowAsTimestamp() {
+ $user_now = new DateTimePlus('now', new \DateTimeZone(drupal_get_user_timezone()));
+ $utc_equivalent = new DateTimePlus($user_now->format('Y-m-d H:i:s'), new \DateTimeZone(DATETIME_STORAGE_TIMEZONE));
+
+ return $utc_equivalent->getTimestamp();
+ }
+
+ /**
+ * Returns an array formatted date_only values.
+ *
+ * @param int $timestamp
+ * Unix Timestamp equivalent to user's "now".
+ *
+ * @return array
+ * An array of DATETIME_DATE_STORAGE_FORMAT date values. In order tomorrow,
+ * today and yesterday.
+ */
+ protected function getRelativeDateValuesFromTimestamp($timestamp) {
+ return [
+ // Tomorrow.
+ \Drupal::service('date.formatter')->format($timestamp + 86400, 'custom', DATETIME_DATE_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE),
+ // Today.
+ \Drupal::service('date.formatter')->format($timestamp, 'custom', DATETIME_DATE_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE),
+ // Yesterday.
+ \Drupal::service('date.formatter')->format($timestamp - 86400, 'custom', DATETIME_DATE_STORAGE_FORMAT, DATETIME_STORAGE_TIMEZONE),
];
- $this->assertIdenticalResultset($view, $expected_result, $this->map);
+ }
+
+ /**
+ * Updates tests nodes date fields values.
+ *
+ * @param array $dates
+ * An array of DATETIME_DATE_STORAGE_FORMAT date values.
+ */
+ protected function updateNodesDateFieldsValues(array $dates) {
+ foreach ($dates as $index => $date) {
+ $this->nodes[$index]->{static::$field_name}->value = $date;
+ $this->nodes[$index]->save();
+ }
}
}
diff --git a/core/modules/datetime/tests/src/Kernel/Views/FilterDateTimeTest.php b/core/modules/datetime/tests/src/Kernel/Views/FilterDateTimeTest.php
index 5282da33ef77..7cb0fa040ab8 100644
--- a/core/modules/datetime/tests/src/Kernel/Views/FilterDateTimeTest.php
+++ b/core/modules/datetime/tests/src/Kernel/Views/FilterDateTimeTest.php
@@ -40,6 +40,9 @@ protected function setUp($import_test_views = TRUE) {
// Set the timezone.
date_default_timezone_set(static::$timezone);
+ $this->config('system.date')
+ ->set('timezone.default', static::$timezone)
+ ->save();
// Add some basic test nodes.
$dates = [
diff --git a/core/modules/views/src/Plugin/views/query/DateSqlInterface.php b/core/modules/views/src/Plugin/views/query/DateSqlInterface.php
new file mode 100644
index 000000000000..caa741939b30
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/query/DateSqlInterface.php
@@ -0,0 +1,62 @@
+ '%Y',
+ 'y' => '%y',
+ 'M' => '%b',
+ 'm' => '%m',
+ 'n' => '%c',
+ 'F' => '%M',
+ 'D' => '%a',
+ 'd' => '%d',
+ 'l' => '%W',
+ 'j' => '%e',
+ 'W' => '%v',
+ 'H' => '%H',
+ 'h' => '%h',
+ 'i' => '%i',
+ 's' => '%s',
+ 'A' => '%p',
+ ];
+
+ /**
+ * Constructs the MySQL-specific date sql class.
+ *
+ * @param \Drupal\Core\Database\Connection $database
+ * The database connection.
+ */
+ public function __construct(Connection $database) {
+ $this->database = $database;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDateField($field, $string_date) {
+ if ($string_date) {
+ return $field;
+ }
+
+ // Base date field storage is timestamp, so the date to be returned here is
+ // epoch + stored value (seconds from epoch).
+ return "DATE_ADD('19700101', INTERVAL $field SECOND)";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDateFormat($field, $format) {
+ $format = strtr($format, static::$replace);
+ return "DATE_FORMAT($field, '$format')";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setTimezoneOffset($offset) {
+ $this->database->query("SET @@session.time_zone = '$offset'");
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setFieldTimezoneOffset(&$field, $offset) {
+ if (!empty($offset)) {
+ $field = "($field + INTERVAL $offset SECOND)";
+ }
+ }
+
+}
diff --git a/core/modules/views/src/Plugin/views/query/PostgresqlDateSql.php b/core/modules/views/src/Plugin/views/query/PostgresqlDateSql.php
new file mode 100644
index 000000000000..c03c416456a8
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/query/PostgresqlDateSql.php
@@ -0,0 +1,93 @@
+ 'YYYY',
+ 'y' => 'YY',
+ 'M' => 'Mon',
+ 'm' => 'MM',
+ // No format for Numeric representation of a month, without leading zeros.
+ 'n' => 'MM',
+ 'F' => 'Month',
+ 'D' => 'Dy',
+ 'd' => 'DD',
+ 'l' => 'Day',
+ // No format for Day of the month without leading zeros.
+ 'j' => 'DD',
+ 'W' => 'IW',
+ 'H' => 'HH24',
+ 'h' => 'HH12',
+ 'i' => 'MI',
+ 's' => 'SS',
+ 'A' => 'AM',
+ ];
+
+ /**
+ * Constructs the PostgreSQL-specific date sql class.
+ *
+ * @param \Drupal\Core\Database\Connection $database
+ * The database connection.
+ */
+ public function __construct(Connection $database) {
+ $this->database = $database;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDateField($field, $string_date) {
+ if ($string_date) {
+ // Ensures compatibility with field offset operation below.
+ return "TO_TIMESTAMP($field, 'YYYY-MM-DD HH24:MI:SS')";
+ }
+ return "TO_TIMESTAMP($field)";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDateFormat($field, $format) {
+ $format = strtr($format, static::$replace);
+ return "TO_CHAR($field, '$format')";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setFieldTimezoneOffset(&$field, $offset) {
+ $field = "($field + INTERVAL '$offset SECONDS')";
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setTimezoneOffset($offset) {
+ $this->database->query("SET TIME ZONE INTERVAL '$offset' HOUR TO MINUTE");
+ }
+
+}
diff --git a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php
index 35453c1172dd..83e0bcfa2403 100644
--- a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php
+++ b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php
@@ -206,11 +206,17 @@ public function loadEntities(&$results) {}
*
* @param string $field
* The query field that will be used in the expression.
+ * @param bool $string_date
+ * For certain databases, date format functions vary depending on string or
+ * numeric storage.
+ * @param bool $calculate_offset
+ * If set to TRUE, the timezone offset will be included in the returned
+ * field.
*
* @return string
* An expression representing a timestamp with time zone.
*/
- public function getDateField($field) {
+ public function getDateField($field, $string_date = FALSE, $calculate_offset = TRUE) {
return $field;
}
@@ -346,6 +352,36 @@ public function getCacheTags() {
return [];
}
+ /**
+ * Applies a timezone offset to the given field.
+ *
+ * @param string &$field
+ * The date field, in string format.
+ * @param int $offset
+ * The timezone offset to apply to the field.
+ */
+ public function setFieldTimezoneOffset(&$field, $offset) {
+ // No-op. Timezone offsets are implementation-specific and should implement
+ // this method as needed.
+ }
+
+ /**
+ * Get the timezone offset in seconds.
+ *
+ * @return int
+ * The offset, in seconds, for the timezone being used.
+ */
+ public function getTimezoneOffset() {
+ $timezone = $this->setupTimezone();
+ $offset = 0;
+ if ($timezone) {
+ $dtz = new \DateTimeZone($timezone);
+ $dt = new \DateTime('now', $dtz);
+ $offset = $dtz->getOffset($dt);
+ }
+ return $offset;
+ }
+
}
/**
diff --git a/core/modules/views/src/Plugin/views/query/Sql.php b/core/modules/views/src/Plugin/views/query/Sql.php
index 7df6521b1cbb..2771a3d7c035 100644
--- a/core/modules/views/src/Plugin/views/query/Sql.php
+++ b/core/modules/views/src/Plugin/views/query/Sql.php
@@ -123,6 +123,13 @@ class Sql extends QueryPluginBase {
*/
protected $entityTypeManager;
+ /**
+ * The database-specific date handler.
+ *
+ * @var \Drupal\views\Plugin\views\query\DateSqlInterface
+ */
+ protected $dateSql;
+
/**
* Constructs a Sql object.
*
@@ -134,11 +141,14 @@ class Sql extends QueryPluginBase {
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
+ * @param \Drupal\views\Plugin\views\query\DateSqlInterface $date_sql
+ * The database-specific date handler.
*/
- public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, DateSqlInterface $date_sql) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
+ $this->dateSql = $date_sql;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
@@ -146,7 +156,8 @@ public static function create(ContainerInterface $container, array $configuratio
$configuration,
$plugin_id,
$plugin_definition,
- $container->get('entity_type.manager')
+ $container->get('entity_type.manager'),
+ $container->get('views.date_sql')
);
}
@@ -1762,175 +1773,40 @@ public function aggregationMethodDistinct($group_type, $field) {
/**
* {@inheritdoc}
*/
- public function getDateField($field) {
- $db_type = Database::getConnection()->databaseType();
- $offset = $this->setupTimezone();
- if (isset($offset) && !is_numeric($offset)) {
- $dtz = new \DateTimeZone($offset);
- $dt = new \DateTime('now', $dtz);
- $offset_seconds = $dtz->getOffset($dt);
- }
-
- switch ($db_type) {
- case 'mysql':
- $field = "DATE_ADD('19700101', INTERVAL $field SECOND)";
- if (!empty($offset)) {
- $field = "($field + INTERVAL $offset_seconds SECOND)";
- }
- break;
- case 'pgsql':
- $field = "TO_TIMESTAMP($field)";
- if (!empty($offset)) {
- $field = "($field + INTERVAL '$offset_seconds SECONDS')";
- }
- break;
- case 'sqlite':
- if (!empty($offset)) {
- $field = "($field + $offset_seconds)";
- }
- break;
+ public function getDateField($field, $string_date = FALSE, $calculate_offset = TRUE) {
+ $field = $this->dateSql->getDateField($field, $string_date);
+ if ($calculate_offset && $offset = $this->getTimezoneOffset()) {
+ $this->setFieldTimezoneOffset($field, $offset);
}
-
return $field;
}
/**
* {@inheritdoc}
*/
- public function setupTimezone() {
- $timezone = drupal_get_user_timezone();
-
- // set up the database timezone
- $db_type = Database::getConnection()->databaseType();
- if (in_array($db_type, ['mysql', 'pgsql'])) {
- $offset = '+00:00';
- static $already_set = FALSE;
- if (!$already_set) {
- if ($db_type == 'pgsql') {
- Database::getConnection()->query("SET TIME ZONE INTERVAL '$offset' HOUR TO MINUTE");
- }
- elseif ($db_type == 'mysql') {
- Database::getConnection()->query("SET @@session.time_zone = '$offset'");
- }
+ public function setFieldTimezoneOffset(&$field, $offset) {
+ $this->dateSql->setFieldTimezoneOffset($field, $offset);
+ }
- $already_set = TRUE;
- }
+ /**
+ * {@inheritdoc}
+ */
+ public function setupTimezone() {
+ // Set the database timezone offset.
+ static $already_set = FALSE;
+ if (!$already_set) {
+ $this->dateSql->setTimezoneOffset('+00:00');
+ $already_set = TRUE;
}
- return $timezone;
+ return parent::setupTimezone();
}
/**
* {@inheritdoc}
*/
public function getDateFormat($field, $format, $string_date = FALSE) {
- $db_type = Database::getConnection()->databaseType();
- switch ($db_type) {
- case 'mysql':
- $replace = [
- 'Y' => '%Y',
- 'y' => '%y',
- 'M' => '%b',
- 'm' => '%m',
- 'n' => '%c',
- 'F' => '%M',
- 'D' => '%a',
- 'd' => '%d',
- 'l' => '%W',
- 'j' => '%e',
- 'W' => '%v',
- 'H' => '%H',
- 'h' => '%h',
- 'i' => '%i',
- 's' => '%s',
- 'A' => '%p',
- ];
- $format = strtr($format, $replace);
- return "DATE_FORMAT($field, '$format')";
- case 'pgsql':
- $replace = [
- 'Y' => 'YYYY',
- 'y' => 'YY',
- 'M' => 'Mon',
- 'm' => 'MM',
- // No format for Numeric representation of a month, without leading
- // zeros.
- 'n' => 'MM',
- 'F' => 'Month',
- 'D' => 'Dy',
- 'd' => 'DD',
- 'l' => 'Day',
- // No format for Day of the month without leading zeros.
- 'j' => 'DD',
- 'W' => 'IW',
- 'H' => 'HH24',
- 'h' => 'HH12',
- 'i' => 'MI',
- 's' => 'SS',
- 'A' => 'AM',
- ];
- $format = strtr($format, $replace);
- if (!$string_date) {
- return "TO_CHAR($field, '$format')";
- }
- // In order to allow for partials (eg, only the year), transform to a
- // date, back to a string again.
- return "TO_CHAR(TO_TIMESTAMP($field, 'YYYY-MM-DD HH24:MI:SS'), '$format')";
- case 'sqlite':
- $replace = [
- 'Y' => '%Y',
- // No format for 2 digit year number.
- 'y' => '%Y',
- // No format for 3 letter month name.
- 'M' => '%m',
- 'm' => '%m',
- // No format for month number without leading zeros.
- 'n' => '%m',
- // No format for full month name.
- 'F' => '%m',
- // No format for 3 letter day name.
- 'D' => '%d',
- 'd' => '%d',
- // No format for full day name.
- 'l' => '%d',
- // no format for day of month number without leading zeros.
- 'j' => '%d',
- 'W' => '%W',
- 'H' => '%H',
- // No format for 12 hour hour with leading zeros.
- 'h' => '%H',
- 'i' => '%M',
- 's' => '%S',
- // No format for AM/PM.
- 'A' => '',
- ];
- $format = strtr($format, $replace);
-
- // Don't use the 'unixepoch' flag for string date comparisons.
- $unixepoch = $string_date ? '' : ", 'unixepoch'";
-
- // SQLite does not have a ISO week substitution string, so it needs
- // special handling.
- // @see http://wikipedia.org/wiki/ISO_week_date#Calculation
- // @see http://stackoverflow.com/a/15511864/1499564
- if ($format === '%W') {
- $expression = "((strftime('%j', date(strftime('%Y-%m-%d', $field" . $unixepoch . "), '-3 days', 'weekday 4')) - 1) / 7 + 1)";
- }
- else {
- $expression = "strftime('$format', $field" . $unixepoch . ")";
- }
- // The expression yields a string, but the comparison value is an
- // integer in case the comparison value is a float, integer, or numeric.
- // All of the above SQLite format tokens only produce integers. However,
- // the given $format may contain 'Y-m-d', which results in a string.
- // @see \Drupal\Core\Database\Driver\sqlite\Connection::expandArguments()
- // @see http://www.sqlite.org/lang_datefunc.html
- // @see http://www.sqlite.org/lang_expr.html#castexpr
- if (preg_match('/^(?:%\w)+$/', $format)) {
- $expression = "CAST($expression AS NUMERIC)";
- }
- return $expression;
- }
+ return $this->dateSql->getDateFormat($field, $format);
}
}
diff --git a/core/modules/views/src/Plugin/views/query/SqliteDateSql.php b/core/modules/views/src/Plugin/views/query/SqliteDateSql.php
new file mode 100644
index 000000000000..628e1c68faec
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/query/SqliteDateSql.php
@@ -0,0 +1,122 @@
+ '%Y',
+ // No format for 2 digit year number.
+ 'y' => '%Y',
+ // No format for 3 letter month name.
+ 'M' => '%m',
+ 'm' => '%m',
+ // No format for month number without leading zeros.
+ 'n' => '%m',
+ // No format for full month name.
+ 'F' => '%m',
+ // No format for 3 letter day name.
+ 'D' => '%d',
+ 'd' => '%d',
+ // No format for full day name.
+ 'l' => '%d',
+ // no format for day of month number without leading zeros.
+ 'j' => '%d',
+ 'W' => '%W',
+ 'H' => '%H',
+ // No format for 12 hour hour with leading zeros.
+ 'h' => '%H',
+ 'i' => '%M',
+ 's' => '%S',
+ // No format for AM/PM.
+ 'A' => '',
+ ];
+
+ /**
+ * Constructs the SQLite-specific date sql class.
+ *
+ * @param \Drupal\Core\Database\Connection $database
+ * The database connection.
+ */
+ public function __construct(Connection $database) {
+ $this->database = $database;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDateField($field, $string_date) {
+ if ($string_date) {
+ $field = "strftime('%s', $field)";
+ }
+ return $field;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDateFormat($field, $format) {
+ $format = strtr($format, static::$replace);
+
+ // SQLite does not have a ISO week substitution string, so it needs special
+ // handling.
+ // @see http://wikipedia.org/wiki/ISO_week_date#Calculation
+ // @see http://stackoverflow.com/a/15511864/1499564
+ if ($format === '%W') {
+ $expression = "((strftime('%j', date(strftime('%Y-%m-%d', $field, 'unixepoch'), '-3 days', 'weekday 4')) - 1) / 7 + 1)";
+ }
+ else {
+ $expression = "strftime('$format', $field, 'unixepoch')";
+ }
+ // The expression yields a string, but the comparison value is an integer in
+ // case the comparison value is a float, integer, or numeric. All of the
+ // above SQLite format tokens only produce integers. However, the given
+ // $format may contain 'Y-m-d', which results in a string.
+ // @see \Drupal\Core\Database\Driver\sqlite\Connection::expandArguments()
+ // @see http://www.sqlite.org/lang_datefunc.html
+ // @see http://www.sqlite.org/lang_expr.html#castexpr
+ if (preg_match('/^(?:%\w)+$/', $format)) {
+ $expression = "CAST($expression AS NUMERIC)";
+ }
+ return $expression;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setTimezoneOffset($offset) {
+ // Nothing to do here.
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setFieldTimezoneOffset(&$field, $offset, $string_date = FALSE) {
+ if (!empty($offset)) {
+ $field = "($field + $offset)";
+ }
+ }
+
+}
diff --git a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/query/QueryTest.php b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/query/QueryTest.php
index cf430af25c09..8868f75de667 100644
--- a/core/modules/views/tests/modules/views_test_data/src/Plugin/views/query/QueryTest.php
+++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/query/QueryTest.php
@@ -151,4 +151,9 @@ public function calculateDependencies() {
];
}
+ /**
+ * {@inheritdoc}
+ */
+ public function setFieldTimezoneOffset(&$field, $offset) {}
+
}
diff --git a/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php b/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php
index bae90240f005..80e8a334ef6a 100644
--- a/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php
@@ -7,6 +7,7 @@
use Drupal\Core\Entity\EntityType;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Tests\UnitTestCase;
+use Drupal\views\Plugin\views\query\DateSqlInterface;
use Drupal\views\Plugin\views\query\Sql;
use Drupal\views\Plugin\views\relationship\RelationshipPluginBase;
use Drupal\views\ResultRow;
@@ -29,8 +30,9 @@ class SqlTest extends UnitTestCase {
public function testGetCacheTags() {
$view = $this->prophesize('Drupal\views\ViewExecutable')->reveal();
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
+ $date_sql = $this->prophesize(DateSqlInterface::class);
- $query = new Sql([], 'sql', [], $entity_type_manager->reveal());
+ $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal());
$query->view = $view;
$result = [];
@@ -75,8 +77,9 @@ public function testGetCacheTags() {
public function testGetCacheMaxAge() {
$view = $this->prophesize('Drupal\views\ViewExecutable')->reveal();
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
+ $date_sql = $this->prophesize(DateSqlInterface::class);
- $query = new Sql([], 'sql', [], $entity_type_manager->reveal());
+ $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal());
$query->view = $view;
$view->result = [];
@@ -249,8 +252,9 @@ public function testLoadEntitiesWithEmptyResult() {
$view->storage = $view_entity->reveal();
$entity_type_manager = $this->setupEntityTypes();
+ $date_sql = $this->prophesize(DateSqlInterface::class);
- $query = new Sql([], 'sql', [], $entity_type_manager->reveal());
+ $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal());
$query->view = $view;
$result = [];
@@ -277,8 +281,9 @@ public function testLoadEntitiesWithNoRelationshipAndNoRevision() {
],
];
$entity_type_manager = $this->setupEntityTypes($entities);
+ $date_sql = $this->prophesize(DateSqlInterface::class);
- $query = new Sql([], 'sql', [], $entity_type_manager->reveal());
+ $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal());
$query->view = $view;
$result = [];
@@ -340,8 +345,9 @@ public function testLoadEntitiesWithRelationship() {
],
];
$entity_type_manager = $this->setupEntityTypes($entities);
+ $date_sql = $this->prophesize(DateSqlInterface::class);
- $query = new Sql([], 'sql', [], $entity_type_manager->reveal());
+ $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal());
$query->view = $view;
$result = [];
@@ -394,8 +400,9 @@ public function testLoadEntitiesWithNonEntityRelationship() {
],
];
$entity_type_manager = $this->setupEntityTypes($entities);
+ $date_sql = $this->prophesize(DateSqlInterface::class);
- $query = new Sql([], 'sql', [], $entity_type_manager->reveal());
+ $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal());
$query->view = $view;
$result = [];
@@ -444,8 +451,9 @@ public function testLoadEntitiesWithRevision() {
],
];
$entity_type_manager = $this->setupEntityTypes([], $entity_revisions);
+ $date_sql = $this->prophesize(DateSqlInterface::class);
- $query = new Sql([], 'sql', [], $entity_type_manager->reveal());
+ $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal());
$query->view = $view;
$result = [];
@@ -497,8 +505,9 @@ public function testLoadEntitiesWithRevisionOfSameEntityType() {
],
];
$entity_type_manager = $this->setupEntityTypes($entity, $entity_revisions);
+ $date_sql = $this->prophesize(DateSqlInterface::class);
- $query = new Sql([], 'sql', [], $entity_type_manager->reveal());
+ $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal());
$query->view = $view;
$result = [];
@@ -554,8 +563,9 @@ public function testLoadEntitiesWithRelationshipAndRevision() {
],
];
$entity_type_manager = $this->setupEntityTypes($entities, $entity_revisions);
+ $date_sql = $this->prophesize(DateSqlInterface::class);
- $query = new Sql([], 'sql', [], $entity_type_manager->reveal());
+ $query = new Sql([], 'sql', [], $entity_type_manager->reveal(), $date_sql->reveal());
$query->view = $view;
$result = [];
diff --git a/core/modules/views/tests/src/Unit/Plugin/views/query/MysqlDateSqlTest.php b/core/modules/views/tests/src/Unit/Plugin/views/query/MysqlDateSqlTest.php
new file mode 100644
index 000000000000..575cc58cf690
--- /dev/null
+++ b/core/modules/views/tests/src/Unit/Plugin/views/query/MysqlDateSqlTest.php
@@ -0,0 +1,97 @@
+database = $this->prophesize(Connection::class)->reveal();
+ }
+
+ /**
+ * Tests the getDateField method.
+ *
+ * @covers ::getDateField
+ */
+ public function testGetDateField() {
+ $date_sql = new MysqlDateSql($this->database);
+
+ $expected = 'foo.field';
+ $this->assertEquals($expected, $date_sql->getDateField('foo.field', TRUE));
+
+ $expected = "DATE_ADD('19700101', INTERVAL foo.field SECOND)";
+ $this->assertEquals($expected, $date_sql->getDateField('foo.field', FALSE));
+ }
+
+ /**
+ * Tests date formatting replacement.
+ *
+ * @covers ::getDateFormat
+ *
+ * @dataProvider providerTestGetDateFormat
+ */
+ public function testGetDateFormat($field, $format, $expected_format) {
+ $date_sql = new MysqlDateSql($this->database);
+
+ $this->assertEquals("DATE_FORMAT($field, '$expected_format')", $date_sql->getDateFormat($field, $format));
+ }
+
+ /**
+ * Provider for date formatting test.
+ */
+ public function providerTestGetDateFormat() {
+ return [
+ ['foo.field', 'Y-y-M-m', '%Y-%y-%b-%m'],
+ ['bar.field', 'n-F D d l', '%c-%M %a %d %W'],
+ ['baz.bar_field', 'j/W/H-h i s A', '%e/%v/%H-%h %i %s %p'],
+ ];
+ }
+
+ /**
+ * Tests timezone offset formatting.
+ *
+ * @covers ::setFieldTimezoneOffset
+ */
+ public function testSetFieldTimezoneOffset() {
+ $date_sql = new MysqlDateSql($this->database);
+
+ $field = 'foobar.field';
+ $date_sql->setFieldTimezoneOffset($field, 42);
+ $this->assertEquals("(foobar.field + INTERVAL 42 SECOND)", $field);
+ }
+
+ /**
+ * Tests setting the database offset.
+ *
+ * @covers ::setTimezoneOffset
+ */
+ public function testSetTimezoneOffset() {
+ $database = $this->prophesize(Connection::class);
+ $database->query("SET @@session.time_zone = '42'")->shouldBeCalledTimes(1);
+ $date_sql = new MysqlDateSql($database->reveal());
+ $date_sql->setTimezoneOffset(42);
+ }
+
+}
diff --git a/core/modules/views/tests/src/Unit/Plugin/views/query/PostgresqlDateSqlTest.php b/core/modules/views/tests/src/Unit/Plugin/views/query/PostgresqlDateSqlTest.php
new file mode 100644
index 000000000000..14f367d261dc
--- /dev/null
+++ b/core/modules/views/tests/src/Unit/Plugin/views/query/PostgresqlDateSqlTest.php
@@ -0,0 +1,97 @@
+database = $this->prophesize(Connection::class)->reveal();
+ }
+
+ /**
+ * Tests the getDateField method.
+ *
+ * @covers ::getDateField
+ */
+ public function testGetDateField() {
+ $date_sql = new PostgresqlDateSql($this->database);
+
+ $expected = "TO_TIMESTAMP(foo.field, 'YYYY-MM-DD HH24:MI:SS')";
+ $this->assertEquals($expected, $date_sql->getDateField('foo.field', TRUE));
+
+ $expected = 'TO_TIMESTAMP(foo.field)';
+ $this->assertEquals($expected, $date_sql->getDateField('foo.field', FALSE));
+ }
+
+ /**
+ * Tests date formatting replacement.
+ *
+ * @covers ::getDateFormat
+ *
+ * @dataProvider providerTestGetDateFormat
+ */
+ public function testGetDateFormat($field, $format, $expected_format) {
+ $date_sql = new PostgresqlDateSql($this->database);
+
+ $this->assertEquals("TO_CHAR($field, '$expected_format')", $date_sql->getDateFormat($field, $format));
+ }
+
+ /**
+ * Provider for date formatting test.
+ */
+ public function providerTestGetDateFormat() {
+ return [
+ ['foo.field', 'Y-y-M-m', 'YYYY-YY-Mon-MM'],
+ ['bar.field', 'n-F D d l', 'MM-Month Dy DD Day'],
+ ['baz.bar_field', 'j/W/H-h i s A', 'DD/IW/HH24-HH12 MI SS AM'],
+ ];
+ }
+
+ /**
+ * Tests timezone offset formatting.
+ *
+ * @covers ::setFieldTimezoneOffset
+ */
+ public function testSetFieldTimezoneOffset() {
+ $date_sql = new PostgresqlDateSql($this->database);
+
+ $field = 'foobar.field';
+ $date_sql->setFieldTimezoneOffset($field, 42);
+ $this->assertEquals("(foobar.field + INTERVAL '42 SECONDS')", $field);
+ }
+
+ /**
+ * Tests setting the database offset.
+ *
+ * @covers ::setTimezoneOffset
+ */
+ public function testSetTimezoneOffset() {
+ $database = $this->prophesize(Connection::class);
+ $database->query("SET TIME ZONE INTERVAL '42' HOUR TO MINUTE")->shouldBeCalledTimes(1);
+ $date_sql = new PostgresqlDateSql($database->reveal());
+ $date_sql->setTimezoneOffset(42);
+ }
+
+}
diff --git a/core/modules/views/tests/src/Unit/Plugin/views/query/SqliteDateSqlTest.php b/core/modules/views/tests/src/Unit/Plugin/views/query/SqliteDateSqlTest.php
new file mode 100644
index 000000000000..f29650796c56
--- /dev/null
+++ b/core/modules/views/tests/src/Unit/Plugin/views/query/SqliteDateSqlTest.php
@@ -0,0 +1,98 @@
+database = $this->prophesize(Connection::class)->reveal();
+ }
+
+ /**
+ * Tests the getDateField method.
+ *
+ * @covers ::getDateField
+ */
+ public function testGetDateField() {
+ $date_sql = new SqliteDateSql($this->database);
+
+ $expected = "strftime('%s', foo.field)";
+ $this->assertEquals($expected, $date_sql->getDateField('foo.field', TRUE));
+
+ $expected = 'foo.field';
+ $this->assertEquals($expected, $date_sql->getDateField('foo.field', FALSE));
+ }
+
+ /**
+ * Tests date formatting replacement.
+ *
+ * @covers ::getDateFormat
+ *
+ * @dataProvider providerTestGetDateFormat
+ */
+ public function testGetDateFormat($field, $format, $expected) {
+ $date_sql = new SqliteDateSql($this->database);
+
+ $this->assertEquals($expected, $date_sql->getDateFormat($field, $format));
+ }
+
+ /**
+ * Provider for date formatting test.
+ */
+ public function providerTestGetDateFormat() {
+ return [
+ ['foo.field', 'Y-y-M-m', "strftime('%Y-%Y-%m-%m', foo.field, 'unixepoch')"],
+ ['bar.field', 'n-F D d l', "strftime('%m-%m %d %d %d', bar.field, 'unixepoch')"],
+ ['baz.bar_field', 'j/W/H-h i s A', "strftime('%d/%W/%H-%H %M %S ', baz.bar_field, 'unixepoch')"],
+ ['foo.field', 'W', "CAST(((strftime('%j', date(strftime('%Y-%m-%d', foo.field, 'unixepoch'), '-3 days', 'weekday 4')) - 1) / 7 + 1) AS NUMERIC)"]
+ ];
+ }
+
+ /**
+ * Tests timezone offset formatting.
+ *
+ * @covers ::setFieldTimezoneOffset
+ */
+ public function testSetFieldTimezoneOffset() {
+ $date_sql = new SqliteDateSql($this->database);
+
+ $field = 'foobar.field';
+ $date_sql->setFieldTimezoneOffset($field, 42);
+ $this->assertEquals("(foobar.field + 42)", $field);
+ }
+
+ /**
+ * Tests setting the database offset.
+ *
+ * @covers ::setTimezoneOffset
+ */
+ public function testSetTimezoneOffset() {
+ $database = $this->prophesize(Connection::class);
+ $database->query()->shouldNotBeCalled();
+ $date_sql = new SqliteDateSql($database->reveal());
+ $date_sql->setTimezoneOffset(42);
+ }
+
+}
diff --git a/core/modules/views/views.services.yml b/core/modules/views/views.services.yml
index 28f8d0d333d3..ffed39460f83 100644
--- a/core/modules/views/views.services.yml
+++ b/core/modules/views/views.services.yml
@@ -80,3 +80,16 @@ services:
arguments: ['@entity.manager']
tags:
- { name: 'event_subscriber' }
+ views.date_sql:
+ class: Drupal\views\Plugin\views\query\MysqlDateSql
+ arguments: ['@database']
+ tags:
+ - { name: backend_overridable }
+ pgsql.views.date_sql:
+ class: Drupal\views\Plugin\views\query\PostgresqlDateSql
+ arguments: ['@database']
+ public: false
+ sqlite.views.date_sql:
+ class: Drupal\views\Plugin\views\query\SqliteDateSql
+ arguments: ['@database']
+ public: false
From 0b67173a1f67cc8d87425298ccc1911b9e14c018 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Wed, 13 Dec 2017 16:00:18 +0000
Subject: [PATCH 032/232] Issue #2929464 by tedbow, mpdonadio: Tests under
"core/modules/ckeditor/tests/modules/src/Kernel" are in the wrong folder and
do not get tested
---
.../tests/{modules => }/src/Kernel/CKEditorPluginManagerTest.php | 0
.../ckeditor/tests/{modules => }/src/Kernel/CKEditorTest.php | 0
2 files changed, 0 insertions(+), 0 deletions(-)
rename core/modules/ckeditor/tests/{modules => }/src/Kernel/CKEditorPluginManagerTest.php (100%)
rename core/modules/ckeditor/tests/{modules => }/src/Kernel/CKEditorTest.php (100%)
diff --git a/core/modules/ckeditor/tests/modules/src/Kernel/CKEditorPluginManagerTest.php b/core/modules/ckeditor/tests/src/Kernel/CKEditorPluginManagerTest.php
similarity index 100%
rename from core/modules/ckeditor/tests/modules/src/Kernel/CKEditorPluginManagerTest.php
rename to core/modules/ckeditor/tests/src/Kernel/CKEditorPluginManagerTest.php
diff --git a/core/modules/ckeditor/tests/modules/src/Kernel/CKEditorTest.php b/core/modules/ckeditor/tests/src/Kernel/CKEditorTest.php
similarity index 100%
rename from core/modules/ckeditor/tests/modules/src/Kernel/CKEditorTest.php
rename to core/modules/ckeditor/tests/src/Kernel/CKEditorTest.php
From 0ee253116f68ba970f8650f837f2c100087399b4 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Thu, 14 Dec 2017 11:26:56 +0000
Subject: [PATCH 033/232] Issue #2928778 by plach: Exception when trying to
save a new revision after manually setting the original revision ID
---
.../Drupal/Core/Entity/ContentEntityBase.php | 6 +++
.../Functional/Entity/EntityRevisionsTest.php | 46 +++++++++++++++++++
2 files changed, 52 insertions(+)
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 5376644c548c..3effe8b3c259 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -745,6 +745,12 @@ public function onChange($name) {
elseif (isset($this->translatableEntityKeys[$key][$this->activeLangcode])) {
unset($this->translatableEntityKeys[$key][$this->activeLangcode]);
}
+ // If the revision identifier field is being populated with the original
+ // value, we need to make sure the "new revision" flag is reset
+ // accordingly.
+ if ($key === 'revision' && $this->getRevisionId() == $this->getLoadedRevisionId()) {
+ $this->newRevision = FALSE;
+ }
}
}
diff --git a/core/modules/system/tests/src/Functional/Entity/EntityRevisionsTest.php b/core/modules/system/tests/src/Functional/Entity/EntityRevisionsTest.php
index 8b544c4abde3..863f95b332d9 100644
--- a/core/modules/system/tests/src/Functional/Entity/EntityRevisionsTest.php
+++ b/core/modules/system/tests/src/Functional/Entity/EntityRevisionsTest.php
@@ -212,4 +212,50 @@ public function testEntityRevisionParamConverter() {
$this->assertNoText('pending revision - en');
}
+ /**
+ * Tests manual revert of the revision ID value.
+ *
+ * @covers \Drupal\Core\Entity\ContentEntityBase::getRevisionId
+ * @covers \Drupal\Core\Entity\ContentEntityBase::getLoadedRevisionId
+ * @covers \Drupal\Core\Entity\ContentEntityBase::setNewRevision
+ * @covers \Drupal\Core\Entity\ContentEntityBase::isNewRevision
+ */
+ public function testNewRevisionRevert() {
+ $entity = EntityTestMulRev::create(['name' => 'EntityLoadedRevisionTest']);
+ $entity->save();
+
+ // Check that revision ID field is reset while the loaded revision ID is
+ // preserved when flagging a new revision.
+ $revision_id = $entity->getRevisionId();
+ $entity->setNewRevision();
+ $this->assertNull($entity->getRevisionId());
+ $this->assertEquals($revision_id, $entity->getLoadedRevisionId());
+ $this->assertTrue($entity->isNewRevision());
+
+ // Check that after manually restoring the original revision ID, the entity
+ // is stored without creating a new revision.
+ $key = $entity->getEntityType()->getKey('revision');
+ $entity->set($key, $revision_id);
+ $entity->save();
+ $this->assertEquals($revision_id, $entity->getRevisionId());
+ $this->assertEquals($revision_id, $entity->getLoadedRevisionId());
+
+ // Check that manually restoring the original revision ID causes the "new
+ // revision" state to be reverted.
+ $entity->setNewRevision();
+ $this->assertNull($entity->getRevisionId());
+ $this->assertEquals($revision_id, $entity->getLoadedRevisionId());
+ $this->assertTrue($entity->isNewRevision());
+ $entity->set($key, $revision_id);
+ $this->assertFalse($entity->isNewRevision());
+ $this->assertEquals($revision_id, $entity->getRevisionId());
+ $this->assertEquals($revision_id, $entity->getLoadedRevisionId());
+
+ // Check that flagging a new revision again works correctly.
+ $entity->setNewRevision();
+ $this->assertNull($entity->getRevisionId());
+ $this->assertEquals($revision_id, $entity->getLoadedRevisionId());
+ $this->assertTrue($entity->isNewRevision());
+ }
+
}
From 4a338bf83338c1810c279580d4f608b4bbda5582 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Thu, 14 Dec 2017 13:05:53 +0000
Subject: [PATCH 034/232] Issue #2928522 by yo30, dawehner:
\Drupal\FunctionalJavascriptTests\WebDriverWebAssert misses some deprecations
---
.../WebDriverWebAssert.php | 52 +++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/core/tests/Drupal/FunctionalJavascriptTests/WebDriverWebAssert.php b/core/tests/Drupal/FunctionalJavascriptTests/WebDriverWebAssert.php
index 16b1281d4d8b..e621c1a0725c 100644
--- a/core/tests/Drupal/FunctionalJavascriptTests/WebDriverWebAssert.php
+++ b/core/tests/Drupal/FunctionalJavascriptTests/WebDriverWebAssert.php
@@ -55,4 +55,56 @@ public function responseHeaderNotEquals($name, $value) {
parent::responseHeaderNotEquals($name, $value);
}
+ /**
+ * The use of responseHeaderContains() is not available.
+ *
+ * @param string $name
+ * The name of the header.
+ * @param string $value
+ * The value to check the header against.
+ */
+ public function responseHeaderContains($name, $value) {
+ @trigger_error('Support for responseHeaderContains is to be dropped from Javascript tests. See https://www.drupal.org/node/2857562.');
+ parent::responseHeaderContains($name, $value);
+ }
+
+ /**
+ * The use of responseHeaderNotContains() is not available.
+ *
+ * @param string $name
+ * The name of the header.
+ * @param string $value
+ * The value to check the header against.
+ */
+ public function responseHeaderNotContains($name, $value) {
+ @trigger_error('Support for responseHeaderNotContains is to be dropped from Javascript tests. See https://www.drupal.org/node/2857562.');
+ parent::responseHeaderNotContains($name, $value);
+ }
+
+ /**
+ * The use of responseHeaderMatches() is not available.
+ *
+ * @param string $name
+ * The name of the header.
+ * @param string $regex
+ * The value to check the header against.
+ */
+ public function responseHeaderMatches($name, $regex) {
+ @trigger_error('Support for responseHeaderMatches is to be dropped from Javascript tests. See https://www.drupal.org/node/2857562.');
+ parent::responseHeaderMatches($name, $regex);
+ }
+
+ /**
+ * The use of responseHeaderNotMatches() is not available.
+ *
+ * @param string $name
+ * The name of the header.
+ * @param string $regex
+ * The value to check the header against.
+ */
+ public function responseHeaderNotMatches($name, $regex) {
+ @trigger_error('Support for responseHeaderNotMatches is to be dropped from Javascript tests. See https://www.drupal.org/node/2857562.');
+ parent::responseHeaderNotMatches($name, $regex);
+ }
+
}
From 6f6abec9641609669544b6cbf43f392d31ad136a Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Fri, 15 Dec 2017 09:56:46 +0000
Subject: [PATCH 035/232] Issue #2894014 by adriancid, Cottser: Additional
space in a field setting schema label
---
.../block_content/config/optional/views.view.block_content.yml | 2 +-
core/modules/comment/config/schema/comment.schema.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/core/modules/block_content/config/optional/views.view.block_content.yml b/core/modules/block_content/config/optional/views.view.block_content.yml
index 20fe0bf4f83f..1be5a0417c12 100644
--- a/core/modules/block_content/config/optional/views.view.block_content.yml
+++ b/core/modules/block_content/config/optional/views.view.block_content.yml
@@ -445,7 +445,7 @@ display:
admin_label: ''
empty: true
tokenize: false
- content: 'There are no custom blocks available. '
+ content: 'There are no custom blocks available.'
plugin_id: text_custom
block_content_listing_empty:
admin_label: ''
diff --git a/core/modules/comment/config/schema/comment.schema.yml b/core/modules/comment/config/schema/comment.schema.yml
index 045e9ad5448e..e4a5b75d4af3 100644
--- a/core/modules/comment/config/schema/comment.schema.yml
+++ b/core/modules/comment/config/schema/comment.schema.yml
@@ -105,7 +105,7 @@ field.field_settings.comment:
label: 'Mode'
form_location:
type: boolean
- label: ' Allow comment title'
+ label: 'Allow comment title'
preview:
type: integer
label: 'Preview comment'
From 8fb5919181c6e426a9dd061c1f211236b41acbd1 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Fri, 15 Dec 2017 20:08:48 +1000
Subject: [PATCH 036/232] Issue #2880445 by pjcdawkins, japerry, gargsuchi,
q0rban: Config sync should not throw a warning when not being writable
---
core/modules/config/config.install | 32 -------------------
.../src/Functional/ConfigInstallWebTest.php | 5 ---
2 files changed, 37 deletions(-)
delete mode 100644 core/modules/config/config.install
diff --git a/core/modules/config/config.install b/core/modules/config/config.install
deleted file mode 100644
index c971ae6b5d22..000000000000
--- a/core/modules/config/config.install
+++ /dev/null
@@ -1,32 +0,0 @@
- t('Configuration directory: %type', ['%type' => CONFIG_SYNC_DIRECTORY]),
- 'description' => t('The directory %directory is not writable.', ['%directory' => $directory]),
- 'severity' => REQUIREMENT_WARNING,
- ];
- }
- return $requirements;
-}
diff --git a/core/modules/config/tests/src/Functional/ConfigInstallWebTest.php b/core/modules/config/tests/src/Functional/ConfigInstallWebTest.php
index 5dc3119dfb8f..5ac93bb8abc1 100644
--- a/core/modules/config/tests/src/Functional/ConfigInstallWebTest.php
+++ b/core/modules/config/tests/src/Functional/ConfigInstallWebTest.php
@@ -204,11 +204,6 @@ public function testConfigModuleRequirements() {
file_unmanaged_delete_recursive($directory);
$this->drupalGet('/admin/reports/status');
$this->assertRaw(t('The directory %directory does not exist.', ['%directory' => $directory]));
-
- file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
- \Drupal::service('file_system')->chmod($directory, 0555);
- $this->drupalGet('/admin/reports/status');
- $this->assertRaw(t('The directory %directory is not writable.', ['%directory' => $directory]));
}
}
From ee22a47cd2d85212426f31e30961491534d33ebf Mon Sep 17 00:00:00 2001
From: webchick
Date: Fri, 15 Dec 2017 11:58:10 -0800
Subject: [PATCH 037/232] Issue #2928702 by Wim Leers, borisson_, tedbow: Make
EntityResourceTestBase's field_rest_test_multivalue test field less invasive:
omit it from normalizations
---
.../tests/modules/rest_test/rest_test.module | 17 +++++++-
.../EntityResource/EntityResourceTestBase.php | 42 ++-----------------
2 files changed, 18 insertions(+), 41 deletions(-)
diff --git a/core/modules/rest/tests/modules/rest_test/rest_test.module b/core/modules/rest/tests/modules/rest_test/rest_test.module
index 7df286370295..8897fb98116b 100644
--- a/core/modules/rest/tests/modules/rest_test/rest_test.module
+++ b/core/modules/rest/tests/modules/rest_test/rest_test.module
@@ -14,19 +14,32 @@ use Drupal\Core\Access\AccessResult;
* Implements hook_entity_field_access().
*
* @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::setUp()
- * @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testPost()
*/
function rest_test_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
+ // @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testPost()
+ // @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testPatch()
if ($field_definition->getName() === 'field_rest_test') {
switch ($operation) {
case 'view':
- // Never ever allow this field to be viewed: this lets EntityResourceTestBase::testGet() test in a "vanilla" way.
+ // Never ever allow this field to be viewed: this lets
+ // EntityResourceTestBase::testGet() test in a "vanilla" way.
return AccessResult::forbidden();
case 'edit':
return AccessResult::forbidden();
}
}
+ // @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testGet()
+ // @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testPatch()
+ if ($field_definition->getName() === 'field_rest_test_multivalue') {
+ switch ($operation) {
+ case 'view':
+ // Never ever allow this field to be viewed: this lets
+ // EntityResourceTestBase::testGet() test in a "vanilla" way.
+ return AccessResult::forbidden();
+ }
+ }
+
// No opinion.
return AccessResult::neutral();
}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
index 5416d308e2ff..2962ef380ab2 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -451,18 +451,6 @@ public function testGet() {
// for the keys with the array order the same (it needs to match with
// identical comparison).
$expected = $this->getExpectedNormalizedEntity();
- if ($this->entity instanceof FieldableEntityInterface) {
- $expected += [
- 'field_rest_test_multivalue' => [
- 0 => [
- 'value' => 'One',
- ],
- 1 => [
- 'value' => 'Two',
- ],
- ]
- ];
- }
static::recursiveKSort($expected);
$actual = $this->serializer->decode((string) $response->getBody(), static::$format);
static::recursiveKSort($actual);
@@ -533,18 +521,6 @@ public function testGet() {
// normalized entity's values to strings. This ensures the BC layer for
// bc_primitives_as_strings works as expected.
$expected = $this->getExpectedNormalizedEntity();
- if ($this->entity instanceof FieldableEntityInterface) {
- $expected += [
- 'field_rest_test_multivalue' => [
- 0 => [
- 'value' => 'One',
- ],
- 1 => [
- 'value' => 'Two',
- ],
- ]
- ];
- }
// Config entities are not affected.
// @see \Drupal\serialization\Normalizer\ConfigEntityNormalizer::normalize()
$expected = static::castToString($expected);
@@ -578,18 +554,6 @@ public function testGet() {
// ::formatExpectedTimestampValue() to generate the timestamp value. This
// will take into account the above config setting.
$expected = $this->getExpectedNormalizedEntity();
- if ($this->entity instanceof FieldableEntityInterface) {
- $expected += [
- 'field_rest_test_multivalue' => [
- 0 => [
- 'value' => 'One',
- ],
- 1 => [
- 'value' => 'Two',
- ],
- ]
- ];
- }
// Config entities are not affected.
// @see \Drupal\serialization\Normalizer\ConfigEntityNormalizer::normalize()
static::recursiveKSort($expected);
@@ -1088,13 +1052,13 @@ public function testPatch() {
// Multi-value field: remove item 0. Then item 1 becomes item 0.
$normalization_multi_value_tests = $this->getNormalizedPatchEntity();
- $normalization_multi_value_tests['field_rest_test_multivalue'] = $updated_entity_normalization['field_rest_test_multivalue'];
+ $normalization_multi_value_tests['field_rest_test_multivalue'] = $this->entity->get('field_rest_test_multivalue')->getValue();
$normalization_remove_item = $normalization_multi_value_tests;
unset($normalization_remove_item['field_rest_test_multivalue'][0]);
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization_remove_item, static::$format);
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
- $this->assertSame([0 => ['value' => 'Two']], $this->serializer->decode((string) $response->getBody(), static::$format)['field_rest_test_multivalue']);
+ $this->assertSame([0 => ['value' => 'Two']], $this->entityStorage->loadUnchanged($this->entity->id())->get('field_rest_test_multivalue')->getValue());
// Multi-value field: add one item before the existing one, and one after.
$normalization_add_items = $normalization_multi_value_tests;
@@ -1102,7 +1066,7 @@ public function testPatch() {
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization_add_items, static::$format);
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
- $this->assertSame([0 => ['value' => 'One'], 1 => ['value' => 'Two'], 2 => ['value' => 'Three']], $this->serializer->decode((string) $response->getBody(), static::$format)['field_rest_test_multivalue']);
+ $this->assertSame([0 => ['value' => 'One'], 1 => ['value' => 'Two'], 2 => ['value' => 'Three']], $this->entityStorage->loadUnchanged($this->entity->id())->get('field_rest_test_multivalue')->getValue());
// BC: rest_update_8203().
$this->config('rest.settings')->set('bc_entity_resource_permissions', TRUE)->save(TRUE);
From 66d19eaa71a661a78873a59b73100ebce7cb2d5c Mon Sep 17 00:00:00 2001
From: webchick
Date: Fri, 15 Dec 2017 11:59:49 -0800
Subject: [PATCH 038/232] Issue #2927566 by Wim Leers: Unit test
EntityReferenceFieldItemNormalizerTest mocks incorrectly
---
.../EntityReferenceFieldItemNormalizerTest.php | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
index e9df564c5fd5..5cc6467e8b6e 100644
--- a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
+++ b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
@@ -70,7 +70,7 @@ protected function setUp() {
$this->serializer = $this->prophesize(Serializer::class);
// Set up the serializer to return an entity property.
$this->serializer->normalize(Argument::cetera())
- ->willReturn(['value' => 'test']);
+ ->willReturn('test');
$this->normalizer->setSerializer($this->serializer->reveal());
@@ -131,7 +131,7 @@ public function testNormalize() {
$normalized = $this->normalizer->normalize($this->fieldItem->reveal());
$expected = [
- 'target_id' => ['value' => 'test'],
+ 'target_id' => 'test',
'target_type' => 'test_type',
'target_uuid' => '080e3add-f9d5-41ac-9821-eea55b7b42fb',
'url' => $test_url,
@@ -159,7 +159,7 @@ public function testNormalizeWithNoEntity() {
$normalized = $this->normalizer->normalize($this->fieldItem->reveal());
$expected = [
- 'target_id' => ['value' => 'test'],
+ 'target_id' => 'test',
];
$this->assertSame($expected, $normalized);
}
@@ -169,7 +169,7 @@ public function testNormalizeWithNoEntity() {
*/
public function testDenormalizeWithTypeAndUuid() {
$data = [
- 'target_id' => ['value' => 'test'],
+ 'target_id' => 'test',
'target_type' => 'test_type',
'target_uuid' => '080e3add-f9d5-41ac-9821-eea55b7b42fb',
];
@@ -193,7 +193,7 @@ public function testDenormalizeWithTypeAndUuid() {
*/
public function testDenormalizeWithUuidWithoutType() {
$data = [
- 'target_id' => ['value' => 'test'],
+ 'target_id' => 'test',
'target_uuid' => '080e3add-f9d5-41ac-9821-eea55b7b42fb',
];
@@ -218,7 +218,7 @@ public function testDenormalizeWithUuidWithIncorrectType() {
$this->setExpectedException(UnexpectedValueException::class, 'The field "field_reference" property "target_type" must be set to "test_type" or omitted.');
$data = [
- 'target_id' => ['value' => 'test'],
+ 'target_id' => 'test',
'target_type' => 'wrong_type',
'target_uuid' => '080e3add-f9d5-41ac-9821-eea55b7b42fb',
];
@@ -238,7 +238,7 @@ public function testDenormalizeWithTypeWithIncorrectUuid() {
$this->setExpectedException(InvalidArgumentException::class, 'No "test_type" entity found with UUID "unique-but-none-non-existent" for field "field_reference"');
$data = [
- 'target_id' => ['value' => 'test'],
+ 'target_id' => 'test',
'target_type' => 'test_type',
'target_uuid' => 'unique-but-none-non-existent',
];
@@ -261,7 +261,7 @@ public function testDenormalizeWithEmtpyUuid() {
$this->setExpectedException(InvalidArgumentException::class, 'If provided "target_uuid" cannot be empty for field "test_type".');
$data = [
- 'target_id' => ['value' => 'test'],
+ 'target_id' => 'test',
'target_type' => 'test_type',
'target_uuid' => '',
];
@@ -278,7 +278,7 @@ public function testDenormalizeWithEmtpyUuid() {
*/
public function testDenormalizeWithId() {
$data = [
- 'target_id' => ['value' => 'test'],
+ 'target_id' => 'test',
];
$this->fieldItem->setValue($data)->shouldBeCalled();
From 5ea62f1d3e218ed31b8e7480caf40891b3b802d2 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Sat, 16 Dec 2017 08:36:36 +1000
Subject: [PATCH 039/232] Issue #2876085 by heddn, maxocub, phenaproxima, Jo
Fitzgerald, vasi, quietone, yoroy, masipila, larowlan, neclimdul,
krystalcode, catch: Before upgrading, audit for potential ID conflicts
---
.../migrations/d6_aggregator_feed.yml | 1 +
.../migrations/d6_aggregator_item.yml | 1 +
.../migrations/d7_aggregator_feed.yml | 1 +
.../migrations/d7_aggregator_item.yml | 1 +
.../migrations/d6_custom_block.yml | 1 +
.../migrations/d7_custom_block.yml | 1 +
.../modules/comment/migrations/d6_comment.yml | 1 +
.../modules/comment/migrations/d7_comment.yml | 1 +
core/modules/file/migrations/d6_file.yml | 1 +
core/modules/file/migrations/d7_file.yml | 1 +
.../file/migrations/d7_file_private.yml | 1 +
.../migrations/d6_menu_links.yml | 1 +
.../migrations/d7_menu_links.yml | 1 +
.../migrate/src/Audit/AuditException.php | 27 +++
.../modules/migrate/src/Audit/AuditResult.php | 146 +++++++++++++
.../migrate/src/Audit/AuditorInterface.php | 42 ++++
.../migrate/src/Audit/HighestIdInterface.php | 26 +++
core/modules/migrate/src/Audit/IdAuditor.php | 62 ++++++
core/modules/migrate/src/Plugin/Migration.php | 11 +
.../src/Plugin/migrate/destination/Entity.php | 4 +
.../migrate/destination/EntityContentBase.php | 22 +-
.../migrate/destination/EntityRevision.php | 30 +++
.../migrate/src/Plugin/migrate/id_map/Sql.php | 70 ++++++-
.../destination/EntityContentBaseTest.php | 19 +-
.../Unit/destination/EntityRevisionTest.php | 7 +
.../Kernel/d6/MigrateDrupal6AuditIdsTest.php | 196 ++++++++++++++++++
.../Kernel/d7/MigrateDrupal7AuditIdsTest.php | 195 +++++++++++++++++
.../Traits/CreateTestContentEntitiesTrait.php | 131 ++++++++++++
.../src/Form/MigrateUpgradeForm.php | 154 ++++++++++++++
.../src/Functional/MigrateUpgradeTestBase.php | 50 +++--
.../src/Functional/d6/MigrateUpgrade6Test.php | 6 +-
.../src/Functional/d7/MigrateUpgrade7Test.php | 4 +-
core/modules/node/migrations/d6_node.yml | 1 +
.../node/migrations/d6_node_revision.yml | 1 +
core/modules/node/migrations/d7_node.yml | 1 +
.../node/migrations/d7_node_revision.yml | 1 +
.../taxonomy/migrations/d6_taxonomy_term.yml | 1 +
.../migrations/d6_term_node_revision.yml | 1 +
.../taxonomy/migrations/d7_taxonomy_term.yml | 1 +
core/modules/user/migrations/d6_user.yml | 1 +
core/modules/user/migrations/d7_user.yml | 1 +
.../Plugin/migrate/destination/EntityUser.php | 14 ++
42 files changed, 1206 insertions(+), 32 deletions(-)
create mode 100644 core/modules/migrate/src/Audit/AuditException.php
create mode 100644 core/modules/migrate/src/Audit/AuditResult.php
create mode 100644 core/modules/migrate/src/Audit/AuditorInterface.php
create mode 100644 core/modules/migrate/src/Audit/HighestIdInterface.php
create mode 100644 core/modules/migrate/src/Audit/IdAuditor.php
create mode 100644 core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php
create mode 100644 core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php
create mode 100644 core/modules/migrate_drupal/tests/src/Traits/CreateTestContentEntitiesTrait.php
diff --git a/core/modules/aggregator/migrations/d6_aggregator_feed.yml b/core/modules/aggregator/migrations/d6_aggregator_feed.yml
index cad155374a86..8689d5bcc8aa 100644
--- a/core/modules/aggregator/migrations/d6_aggregator_feed.yml
+++ b/core/modules/aggregator/migrations/d6_aggregator_feed.yml
@@ -1,5 +1,6 @@
id: d6_aggregator_feed
label: Aggregator feeds
+audit: true
migration_tags:
- Drupal 6
source:
diff --git a/core/modules/aggregator/migrations/d6_aggregator_item.yml b/core/modules/aggregator/migrations/d6_aggregator_item.yml
index e14dbd60ed59..7c991eb76116 100644
--- a/core/modules/aggregator/migrations/d6_aggregator_item.yml
+++ b/core/modules/aggregator/migrations/d6_aggregator_item.yml
@@ -1,5 +1,6 @@
id: d6_aggregator_item
label: Aggregator items
+audit: true
migration_tags:
- Drupal 6
source:
diff --git a/core/modules/aggregator/migrations/d7_aggregator_feed.yml b/core/modules/aggregator/migrations/d7_aggregator_feed.yml
index 5dbeb25eaf40..48eb29de8cf9 100644
--- a/core/modules/aggregator/migrations/d7_aggregator_feed.yml
+++ b/core/modules/aggregator/migrations/d7_aggregator_feed.yml
@@ -1,5 +1,6 @@
id: d7_aggregator_feed
label: Aggregator feeds
+audit: true
migration_tags:
- Drupal 7
source:
diff --git a/core/modules/aggregator/migrations/d7_aggregator_item.yml b/core/modules/aggregator/migrations/d7_aggregator_item.yml
index 054ba439f5db..342c5c8cbbc6 100644
--- a/core/modules/aggregator/migrations/d7_aggregator_item.yml
+++ b/core/modules/aggregator/migrations/d7_aggregator_item.yml
@@ -1,5 +1,6 @@
id: d7_aggregator_item
label: Aggregator items
+audit: true
migration_tags:
- Drupal 7
source:
diff --git a/core/modules/block_content/migrations/d6_custom_block.yml b/core/modules/block_content/migrations/d6_custom_block.yml
index 55fbcb5c9dc8..071e4de19ac5 100644
--- a/core/modules/block_content/migrations/d6_custom_block.yml
+++ b/core/modules/block_content/migrations/d6_custom_block.yml
@@ -1,5 +1,6 @@
id: d6_custom_block
label: Custom blocks
+audit: true
migration_tags:
- Drupal 6
source:
diff --git a/core/modules/block_content/migrations/d7_custom_block.yml b/core/modules/block_content/migrations/d7_custom_block.yml
index ca06cf04f91a..1a9ea1978c48 100644
--- a/core/modules/block_content/migrations/d7_custom_block.yml
+++ b/core/modules/block_content/migrations/d7_custom_block.yml
@@ -1,5 +1,6 @@
id: d7_custom_block
label: Custom blocks
+audit: true
migration_tags:
- Drupal 7
source:
diff --git a/core/modules/comment/migrations/d6_comment.yml b/core/modules/comment/migrations/d6_comment.yml
index 161820eeaacb..afab0cb4ca87 100644
--- a/core/modules/comment/migrations/d6_comment.yml
+++ b/core/modules/comment/migrations/d6_comment.yml
@@ -1,5 +1,6 @@
id: d6_comment
label: Comments
+audit: true
migration_tags:
- Drupal 6
source:
diff --git a/core/modules/comment/migrations/d7_comment.yml b/core/modules/comment/migrations/d7_comment.yml
index dff4b64ab91d..0837df91b7e2 100644
--- a/core/modules/comment/migrations/d7_comment.yml
+++ b/core/modules/comment/migrations/d7_comment.yml
@@ -1,5 +1,6 @@
id: d7_comment
label: Comments
+audit: true
migration_tags:
- Drupal 7
source:
diff --git a/core/modules/file/migrations/d6_file.yml b/core/modules/file/migrations/d6_file.yml
index 6544d7d2f6fa..5b8b67201874 100644
--- a/core/modules/file/migrations/d6_file.yml
+++ b/core/modules/file/migrations/d6_file.yml
@@ -2,6 +2,7 @@
# migration as an optional dependency.
id: d6_file
label: Public files
+audit: true
migration_tags:
- Drupal 6
source:
diff --git a/core/modules/file/migrations/d7_file.yml b/core/modules/file/migrations/d7_file.yml
index b63f13e9e06f..7e05a28aeb5a 100644
--- a/core/modules/file/migrations/d7_file.yml
+++ b/core/modules/file/migrations/d7_file.yml
@@ -2,6 +2,7 @@
# migration as an optional dependency.
id: d7_file
label: Public files
+audit: true
migration_tags:
- Drupal 7
source:
diff --git a/core/modules/file/migrations/d7_file_private.yml b/core/modules/file/migrations/d7_file_private.yml
index 197c7010313f..51de5338676a 100644
--- a/core/modules/file/migrations/d7_file_private.yml
+++ b/core/modules/file/migrations/d7_file_private.yml
@@ -1,5 +1,6 @@
id: d7_file_private
label: Private files
+audit: true
migration_tags:
- Drupal 7
source:
diff --git a/core/modules/menu_link_content/migrations/d6_menu_links.yml b/core/modules/menu_link_content/migrations/d6_menu_links.yml
index 2c8ad4a45a03..e05efee4a75b 100644
--- a/core/modules/menu_link_content/migrations/d6_menu_links.yml
+++ b/core/modules/menu_link_content/migrations/d6_menu_links.yml
@@ -1,5 +1,6 @@
id: d6_menu_links
label: Menu links
+audit: true
migration_tags:
- Drupal 6
source:
diff --git a/core/modules/menu_link_content/migrations/d7_menu_links.yml b/core/modules/menu_link_content/migrations/d7_menu_links.yml
index 200a79204791..81d4eb8530b3 100644
--- a/core/modules/menu_link_content/migrations/d7_menu_links.yml
+++ b/core/modules/menu_link_content/migrations/d7_menu_links.yml
@@ -1,5 +1,6 @@
id: d7_menu_links
label: Menu links
+audit: true
migration_tags:
- Drupal 7
source:
diff --git a/core/modules/migrate/src/Audit/AuditException.php b/core/modules/migrate/src/Audit/AuditException.php
new file mode 100644
index 000000000000..d2ef2eb5d9aa
--- /dev/null
+++ b/core/modules/migrate/src/Audit/AuditException.php
@@ -0,0 +1,27 @@
+id(), $message);
+ parent::__construct($message, 0, $previous);
+ }
+
+}
diff --git a/core/modules/migrate/src/Audit/AuditResult.php b/core/modules/migrate/src/Audit/AuditResult.php
new file mode 100644
index 000000000000..d5260775e1d4
--- /dev/null
+++ b/core/modules/migrate/src/Audit/AuditResult.php
@@ -0,0 +1,146 @@
+migration = $migration;
+ $this->status = $status;
+ array_walk($reasons, [$this, 'addReason']);
+ }
+
+ /**
+ * Returns the audited migration.
+ *
+ * @return \Drupal\migrate\Plugin\MigrationInterface
+ * The audited migration.
+ */
+ public function getMigration() {
+ return $this->migration;
+ }
+
+ /**
+ * Returns the boolean result of the audit.
+ *
+ * @return bool
+ * The result of the audit. TRUE if the migration passed the audit, FALSE
+ * otherwise.
+ */
+ public function passed() {
+ return $this->status;
+ }
+
+ /**
+ * Adds a reason why the migration passed or failed the audit.
+ *
+ * @param string|object $reason
+ * The reason to add. Can be a string or a string-castable object.
+ *
+ * @return $this
+ */
+ public function addReason($reason) {
+ array_push($this->reasons, (string) $reason);
+ return $this;
+ }
+
+ /**
+ * Creates a passing audit result for a migration.
+ *
+ * @param \Drupal\migrate\Plugin\MigrationInterface $migration
+ * The audited migration.
+ * @param string[] $reasons
+ * (optional) The reasons why the migration passed the audit.
+ *
+ * @return static
+ */
+ public static function pass(MigrationInterface $migration, array $reasons = []) {
+ return new static($migration, TRUE, $reasons);
+ }
+
+ /**
+ * Creates a failing audit result for a migration.
+ *
+ * @param \Drupal\migrate\Plugin\MigrationInterface $migration
+ * The audited migration.
+ * @param array $reasons
+ * (optional) The reasons why the migration failed the audit.
+ *
+ * @return static
+ */
+ public static function fail(MigrationInterface $migration, array $reasons = []) {
+ return new static($migration, FALSE, $reasons);
+ }
+
+ /**
+ * Implements \Countable::count() for Twig template compatibility.
+ *
+ * @return int
+ *
+ * @see \Drupal\Component\Render\MarkupInterface
+ */
+ public function count() {
+ return count($this->reasons);
+ }
+
+ /**
+ * Returns the reasons the migration passed or failed, as a string.
+ *
+ * @return string
+ *
+ * @see \Drupal\Component\Render\MarkupInterface
+ */
+ public function __toString() {
+ return implode("\n", $this->reasons);
+ }
+
+ /**
+ * Returns the reasons the migration passed or failed, for JSON serialization.
+ *
+ * @return string[]
+ */
+ public function jsonSerialize() {
+ return $this->reasons;
+ }
+
+}
diff --git a/core/modules/migrate/src/Audit/AuditorInterface.php b/core/modules/migrate/src/Audit/AuditorInterface.php
new file mode 100644
index 000000000000..a61b792cfef8
--- /dev/null
+++ b/core/modules/migrate/src/Audit/AuditorInterface.php
@@ -0,0 +1,42 @@
+getPluginDefinition();
+
+ // If the migration does not opt into auditing, it passes.
+ // @todo Use $migration->isAuditable() when
+ // https://www.drupal.org/project/drupal/issues/2930832 is in.
+ if (empty($plugin_definition['audit'])) {
+ return AuditResult::pass($migration);
+ }
+
+ $interface = HighestIdInterface::class;
+
+ $destination = $migration->getDestinationPlugin();
+ if (!$destination instanceof HighestIdInterface) {
+ throw new AuditException($migration, "Destination does not implement $interface");
+ }
+
+ $id_map = $migration->getIdMap();
+ if (!$id_map instanceof HighestIdInterface) {
+ throw new AuditException($migration, "ID map does not implement $interface");
+ }
+
+ if ($destination->getHighestId() > $id_map->getHighestId()) {
+ return AuditResult::fail($migration, [
+ $this->t('The destination system contains data which was not created by a migration.'),
+ ]);
+ }
+ return AuditResult::pass($migration);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function auditMultiple(array $migrations) {
+ $conflicts = [];
+
+ foreach ($migrations as $migration) {
+ $migration_id = $migration->getPluginId();
+ $conflicts[$migration_id] = $this->audit($migration);
+ }
+ ksort($conflicts);
+ return $conflicts;
+ }
+
+}
diff --git a/core/modules/migrate/src/Plugin/Migration.php b/core/modules/migrate/src/Plugin/Migration.php
index 3bbe38ad7425..92f7864d5a80 100644
--- a/core/modules/migrate/src/Plugin/Migration.php
+++ b/core/modules/migrate/src/Plugin/Migration.php
@@ -154,6 +154,17 @@ class Migration extends PluginBase implements MigrationInterface, RequirementsIn
*/
protected $migration_tags = [];
+ /**
+ * Whether the migration is auditable.
+ *
+ * If set to TRUE, the migration's IDs will be audited. This means that, if
+ * the highest destination ID is greater than the highest source ID, a warning
+ * will be displayed that entities might be overwritten.
+ *
+ * @var bool
+ */
+ protected $audit = FALSE;
+
/**
* These migrations, if run, must be executed before this migration.
*
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/Entity.php b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
index 12b0ed6d31d2..0cebbd07c824 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
@@ -94,6 +94,10 @@ abstract class Entity extends DestinationBase implements ContainerFactoryPluginI
* The list of bundles this entity type has.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles) {
+ $plugin_definition += [
+ 'label' => $storage->getEntityType()->getPluralLabel(),
+ ];
+
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration);
$this->storage = $storage;
$this->bundles = $bundles;
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
index cf3ae1dedb79..f8d4caeb4197 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
@@ -9,6 +9,7 @@
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\Core\TypedData\TranslatableInterface;
use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\migrate\Audit\HighestIdInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\MigrateException;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
@@ -18,7 +19,7 @@
/**
* The destination class for all content entities lacking a specific class.
*/
-class EntityContentBase extends Entity {
+class EntityContentBase extends Entity implements HighestIdInterface {
/**
* Entity manager.
@@ -111,12 +112,9 @@ protected function save(ContentEntityInterface $entity, array $old_destination_i
}
/**
- * Get whether this destination is for translations.
- *
- * @return bool
- * Whether this destination is for translations.
+ * {@inheritdoc}
*/
- protected function isTranslationDestination() {
+ public function isTranslationDestination() {
return !empty($this->configuration['translations']);
}
@@ -294,4 +292,16 @@ protected function getDefinitionFromEntity($key) {
] + $field_definition->getSettings();
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getHighestId() {
+ $values = $this->storage->getQuery()
+ ->accessCheck(FALSE)
+ ->sort($this->getKey('id'), 'DESC')
+ ->range(0, 1)
+ ->execute();
+ return (int) current($values);
+ }
+
}
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
index b0db4769877c..06c158ae1bd2 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityRevision.php
@@ -3,7 +3,12 @@
namespace Drupal\migrate\Plugin\migrate\destination;
use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\migrate\MigrateException;
+use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
/**
@@ -16,6 +21,16 @@
*/
class EntityRevision extends EntityContentBase {
+ /**
+ * {@inheritdoc}
+ */
+ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager) {
+ $plugin_definition += [
+ 'label' => new TranslatableMarkup('@entity_type revisions', ['@entity_type' => $storage->getEntityType()->getSingularLabel()]),
+ ];
+ parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
+ }
+
/**
* {@inheritdoc}
*/
@@ -78,4 +93,19 @@ public function getIds() {
throw new MigrateException('This entity type does not support revisions.');
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getHighestId() {
+ $values = $this->storage->getQuery()
+ ->accessCheck(FALSE)
+ ->allRevisions()
+ ->sort($this->getKey('revision'), 'DESC')
+ ->range(0, 1)
+ ->execute();
+ // The array keys are the revision IDs.
+ // The array contains only one entry, so we can use key().
+ return (int) key($values);
+ }
+
}
diff --git a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
index 6bdd51ebf0b3..8bc17f19f502 100644
--- a/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
+++ b/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php
@@ -7,6 +7,7 @@
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Plugin\PluginBase;
use Drupal\migrate\MigrateMessage;
+use Drupal\migrate\Audit\HighestIdInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Event\MigrateIdMapMessageEvent;
use Drupal\migrate\MigrateException;
@@ -27,7 +28,7 @@
*
* @PluginID("sql")
*/
-class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryPluginInterface {
+class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryPluginInterface, HighestIdInterface {
/**
* Column name of hashed source id values.
@@ -152,6 +153,8 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP
* The configuration for the plugin.
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
* The migration to do.
+ * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
+ * The event dispatcher.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EventDispatcherInterface $event_dispatcher) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
@@ -925,4 +928,69 @@ public function valid() {
return $this->currentRow !== FALSE;
}
+ /**
+ * Returns the migration plugin manager.
+ *
+ * @todo Inject as a dependency in https://www.drupal.org/node/2919158.
+ *
+ * @return \Drupal\migrate\Plugin\MigrationPluginManagerInterface
+ * The migration plugin manager.
+ */
+ protected function getMigrationPluginManager() {
+ return \Drupal::service('plugin.manager.migration');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getHighestId() {
+ array_filter(
+ $this->migration->getDestinationPlugin()->getIds(),
+ function (array $id) {
+ if ($id['type'] !== 'integer') {
+ throw new \LogicException('Cannot determine the highest migrated ID without an integer ID column');
+ }
+ }
+ );
+
+ // List of mapping tables to look in for the highest ID.
+ $map_tables = [
+ $this->migration->id() => $this->mapTableName(),
+ ];
+
+ // If there's a bundle, it means we have a derived migration and we need to
+ // find all the mapping tables from the related derived migrations.
+ if ($base_id = substr($this->migration->id(), 0, strpos($this->migration->id(), static::DERIVATIVE_SEPARATOR))) {
+ $migration_manager = $this->getMigrationPluginManager();
+ $migrations = $migration_manager->getDefinitions();
+ foreach ($migrations as $migration_id => $migration) {
+ if ($migration['id'] === $base_id) {
+ // Get this derived migration's mapping table and add it to the list
+ // of mapping tables to look in for the highest ID.
+ $stub = $migration_manager->createInstance($migration_id);
+ $map_tables[$migration_id] = $stub->getIdMap()->mapTableName();
+ }
+ }
+ }
+
+ // Get the highest id from the list of map tables.
+ $ids = [0];
+ foreach ($map_tables as $map_table) {
+ if (!$this->getDatabase()->schema()->tableExists($map_table)) {
+ break;
+ }
+
+ $query = $this->getDatabase()->select($map_table, 'map')
+ ->fields('map', $this->destinationIdFields())
+ ->range(0, 1);
+ foreach (array_values($this->destinationIdFields()) as $order_field) {
+ $query->orderBy($order_field, 'DESC');
+ }
+ $ids[] = $query->execute()->fetchField();
+ }
+
+ // Return the highest of all the mapped IDs.
+ return (int) max($ids);
+ }
+
}
diff --git a/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php
index 4fa08fed0a20..9f435235608e 100644
--- a/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php
+++ b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityContentBaseTest.php
@@ -8,9 +8,9 @@
namespace Drupal\Tests\migrate\Unit\Plugin\migrate\destination;
use Drupal\Core\Entity\ContentEntityInterface;
-use Drupal\Core\Entity\ContentEntityType;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
use Drupal\migrate\MigrateException;
@@ -38,6 +38,11 @@ class EntityContentBaseTest extends UnitTestCase {
*/
protected $storage;
+ /**
+ * @var \Drupal\Core\Entity\EntityTypeInterface
+ */
+ protected $entityType;
+
/**
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
@@ -51,6 +56,11 @@ protected function setUp() {
$this->migration = $this->prophesize(MigrationInterface::class);
$this->storage = $this->prophesize(EntityStorageInterface::class);
+
+ $this->entityType = $this->prophesize(EntityTypeInterface::class);
+ $this->entityType->getPluralLabel()->willReturn('wonkiness');
+ $this->storage->getEntityType()->willReturn($this->entityType->reveal());
+
$this->entityManager = $this->prophesize(EntityManagerInterface::class);
}
@@ -104,14 +114,11 @@ public function testImportEntityLoadFailure() {
*/
public function testUntranslatable() {
// An entity type without a language.
- $entity_type = $this->prophesize(ContentEntityType::class);
- $entity_type->getKey('langcode')->willReturn('');
- $entity_type->getKey('id')->willReturn('id');
+ $this->entityType->getKey('langcode')->willReturn('');
+ $this->entityType->getKey('id')->willReturn('id');
$this->entityManager->getBaseFieldDefinitions('foo')
->willReturn(['id' => BaseFieldDefinitionTest::create('integer')]);
- $this->storage->getEntityType()->willReturn($entity_type->reveal());
-
$destination = new EntityTestDestination(
['translations' => TRUE],
'',
diff --git a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
index f68c2bea1fb3..8a6fe9a43dbc 100644
--- a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
+++ b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
@@ -9,6 +9,7 @@
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Plugin\migrate\destination\EntityRevision as RealEntityRevision;
use Drupal\migrate\Row;
@@ -48,6 +49,12 @@ protected function setUp() {
// Setup mocks to be used when creating a revision destination.
$this->migration = $this->prophesize(MigrationInterface::class);
$this->storage = $this->prophesize('\Drupal\Core\Entity\EntityStorageInterface');
+
+ $entity_type = $this->prophesize(EntityTypeInterface::class);
+ $entity_type->getSingularLabel()->willReturn('crazy');
+ $entity_type->getPluralLabel()->willReturn('craziness');
+ $this->storage->getEntityType()->willReturn($entity_type->reveal());
+
$this->entityManager = $this->prophesize('\Drupal\Core\Entity\EntityManagerInterface');
$this->fieldTypeManager = $this->prophesize('\Drupal\Core\Field\FieldTypePluginManagerInterface');
}
diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php
new file mode 100644
index 000000000000..a0bb591c4c85
--- /dev/null
+++ b/core/modules/migrate_drupal/tests/src/Kernel/d6/MigrateDrupal6AuditIdsTest.php
@@ -0,0 +1,196 @@
+coreModuleListDataProvider());
+ parent::setUp();
+
+ // Install required entity schemas.
+ $this->installEntitySchemas();
+
+ // Install required schemas.
+ $this->installSchema('book', ['book']);
+ $this->installSchema('dblog', ['watchdog']);
+ $this->installSchema('forum', ['forum_index']);
+ $this->installSchema('node', ['node_access']);
+ $this->installSchema('search', ['search_dataset']);
+ $this->installSchema('tracker', ['tracker_node', 'tracker_user']);
+
+ // Enable content moderation for nodes of type page.
+ $this->installEntitySchema('content_moderation_state');
+ $this->installConfig('content_moderation');
+ NodeType::create(['type' => 'page'])->save();
+ $workflow = Workflow::load('editorial');
+ $workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'page');
+ $workflow->save();
+ }
+
+ /**
+ * Tests multiple migrations to the same destination with no ID conflicts.
+ */
+ public function testMultipleMigrationWithoutIdConflicts() {
+ // Create a node of type page.
+ $node = Node::create(['type' => 'page', 'title' => 'foo']);
+ $node->moderation_state->value = 'published';
+ $node->save();
+
+ // Insert data in the d6_node:page migration mappping table to simulate a
+ // previously migrated node.
+ $table_name = $this->getMigration('d6_node:page')->getIdMap()->mapTableName();
+ $this->container->get('database')->insert($table_name)
+ ->fields([
+ 'source_ids_hash' => 1,
+ 'sourceid1' => 1,
+ 'destid1' => 1,
+ ])
+ ->execute();
+
+ // Audit the IDs of the d6_node migrations for the page & article node type.
+ // There should be no conflicts since the highest destination ID should be
+ // equal to the highest migrated ID, as found in the aggregated mapping
+ // tables of the two node migrations.
+ $migrations = [
+ $this->getMigration('d6_node:page'),
+ $this->getMigration('d6_node:article'),
+ ];
+
+ $results = (new IdAuditor())->auditMultiple($migrations);
+ /** @var \Drupal\migrate\Audit\AuditResult $result */
+ foreach ($results as $result) {
+ $this->assertInstanceOf(AuditResult::class, $result);
+ $this->assertTrue($result->passed());
+ }
+ }
+
+ /**
+ * Tests all migrations with no ID conflicts.
+ */
+ public function testAllMigrationsWithNoIdConflicts() {
+ $migrations = $this->container
+ ->get('plugin.manager.migration')
+ ->createInstancesByTag('Drupal 6');
+
+ // Audit all Drupal 6 migrations that support it. There should be no
+ // conflicts since no content has been created.
+ $results = (new IdAuditor())->auditMultiple($migrations);
+ /** @var \Drupal\migrate\Audit\AuditResult $result */
+ foreach ($results as $result) {
+ $this->assertInstanceOf(AuditResult::class, $result);
+ $this->assertTrue($result->passed());
+ }
+ }
+
+ /**
+ * Tests all migrations with ID conflicts.
+ */
+ public function testAllMigrationsWithIdConflicts() {
+ // Get all Drupal 6 migrations.
+ $migrations = $this->container
+ ->get('plugin.manager.migration')
+ ->createInstancesByTag('Drupal 6');
+
+ // Create content.
+ $this->createContent();
+
+ // Audit the IDs of all migrations. There should be conflicts since content
+ // has been created.
+ $conflicts = array_map(
+ function (AuditResult $result) {
+ return $result->passed() ? NULL : $result->getMigration()->getBaseId();
+ },
+ (new IdAuditor())->auditMultiple($migrations)
+ );
+
+ $expected = [
+ 'd6_aggregator_feed',
+ 'd6_aggregator_item',
+ 'd6_comment',
+ 'd6_custom_block',
+ 'd6_file',
+ 'd6_menu_links',
+ 'd6_node',
+ 'd6_node_revision',
+ 'd6_taxonomy_term',
+ 'd6_term_node_revision',
+ 'd6_user',
+ ];
+ $this->assertEmpty(array_diff(array_filter($conflicts), $expected));
+ }
+
+ /**
+ * Tests draft revisions ID conflicts.
+ */
+ public function testDraftRevisionIdConflicts() {
+ // Create a published node of type page.
+ $node = Node::create(['type' => 'page', 'title' => 'foo']);
+ $node->moderation_state->value = 'published';
+ $node->save();
+
+ // Create a draft revision.
+ $node->moderation_state->value = 'draft';
+ $node->setNewRevision(TRUE);
+ $node->save();
+
+ // Insert data in the d6_node_revision:page migration mappping table to
+ // simulate a previously migrated node revison.
+ $table_name = $this->getMigration('d6_node_revision:page')->getIdMap()->mapTableName();
+ $this->container->get('database')->insert($table_name)
+ ->fields([
+ 'source_ids_hash' => 1,
+ 'sourceid1' => 1,
+ 'destid1' => 1,
+ ])
+ ->execute();
+
+ // Audit the IDs of the d6_node_revision migration. There should be
+ // conflicts since a draft revision has been created.
+ /** @var \Drupal\migrate\Audit\AuditResult $result */
+ $result = (new IdAuditor())->audit($this->getMigration('d6_node_revision:page'));
+ $this->assertInstanceOf(AuditResult::class, $result);
+ $this->assertFalse($result->passed());
+ }
+
+ /**
+ * Tests ID conflicts for inaccessible nodes.
+ */
+ public function testNodeGrantsIdConflicts() {
+ // Enable the node_test module to restrict access to page nodes.
+ $this->enableModules(['node_test']);
+
+ // Create a published node of type page.
+ $node = Node::create(['type' => 'page', 'title' => 'foo']);
+ $node->moderation_state->value = 'published';
+ $node->save();
+
+ // Audit the IDs of the d6_node migration. There should be conflicts
+ // even though the new node is not accessible.
+ /** @var \Drupal\migrate\Audit\AuditResult $result */
+ $result = (new IdAuditor())->audit($this->getMigration('d6_node:page'));
+ $this->assertInstanceOf(AuditResult::class, $result);
+ $this->assertFalse($result->passed());
+ }
+
+}
diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php
new file mode 100644
index 000000000000..1d7442383825
--- /dev/null
+++ b/core/modules/migrate_drupal/tests/src/Kernel/d7/MigrateDrupal7AuditIdsTest.php
@@ -0,0 +1,195 @@
+coreModuleListDataProvider());
+ parent::setUp();
+
+ // Install required entity schemas.
+ $this->installEntitySchemas();
+
+ // Install required schemas.
+ $this->installSchema('book', ['book']);
+ $this->installSchema('dblog', ['watchdog']);
+ $this->installSchema('forum', ['forum_index']);
+ $this->installSchema('node', ['node_access']);
+ $this->installSchema('search', ['search_dataset']);
+ $this->installSchema('tracker', ['tracker_node', 'tracker_user']);
+
+ // Enable content moderation for nodes of type page.
+ $this->installEntitySchema('content_moderation_state');
+ $this->installConfig('content_moderation');
+ NodeType::create(['type' => 'page'])->save();
+ $workflow = Workflow::load('editorial');
+ $workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'page');
+ $workflow->save();
+ }
+
+ /**
+ * Tests multiple migrations to the same destination with no ID conflicts.
+ */
+ public function testMultipleMigrationWithoutIdConflicts() {
+ // Create a node of type page.
+ $node = Node::create(['type' => 'page', 'title' => 'foo']);
+ $node->moderation_state->value = 'published';
+ $node->save();
+
+ // Insert data in the d7_node:page migration mappping table to simulate a
+ // previously migrated node.
+ $table_name = $this->getMigration('d7_node:page')->getIdMap()->mapTableName();
+ $this->container->get('database')->insert($table_name)
+ ->fields([
+ 'source_ids_hash' => 1,
+ 'sourceid1' => 1,
+ 'destid1' => 1,
+ ])
+ ->execute();
+
+ // Audit the IDs of the d7_node migrations for the page & article node type.
+ // There should be no conflicts since the highest destination ID should be
+ // equal to the highest migrated ID, as found in the aggregated mapping
+ // tables of the two node migrations.
+ $migrations = [
+ $this->getMigration('d7_node:page'),
+ $this->getMigration('d7_node:article'),
+ ];
+
+ $results = (new IdAuditor())->auditMultiple($migrations);
+ /** @var \Drupal\migrate\Audit\AuditResult $result */
+ foreach ($results as $result) {
+ $this->assertInstanceOf(AuditResult::class, $result);
+ $this->assertTrue($result->passed());
+ }
+ }
+
+ /**
+ * Tests all migrations with no ID conflicts.
+ */
+ public function testAllMigrationsWithNoIdConflicts() {
+ $migrations = $this->container
+ ->get('plugin.manager.migration')
+ ->createInstancesByTag('Drupal 7');
+
+ // Audit the IDs of all Drupal 7 migrations. There should be no conflicts
+ // since no content has been created.
+ $results = (new IdAuditor())->auditMultiple($migrations);
+ /** @var \Drupal\migrate\Audit\AuditResult $result */
+ foreach ($results as $result) {
+ $this->assertInstanceOf(AuditResult::class, $result);
+ $this->assertTrue($result->passed());
+ }
+ }
+
+ /**
+ * Tests all migrations with ID conflicts.
+ */
+ public function testAllMigrationsWithIdConflicts() {
+ $migrations = $this->container
+ ->get('plugin.manager.migration')
+ ->createInstancesByTag('Drupal 7');
+
+ // Create content.
+ $this->createContent();
+
+ // Audit the IDs of all Drupal 7 migrations. There should be conflicts since
+ // content has been created.
+ $conflicts = array_map(
+ function (AuditResult $result) {
+ return $result->passed() ? NULL : $result->getMigration()->getBaseId();
+ },
+ (new IdAuditor())->auditMultiple($migrations)
+ );
+
+ $expected = [
+ 'd7_aggregator_feed',
+ 'd7_aggregator_item',
+ 'd7_comment',
+ 'd7_custom_block',
+ 'd7_file',
+ 'd7_file_private',
+ 'd7_menu_links',
+ 'd7_node',
+ 'd7_node_revision',
+ 'd7_taxonomy_term',
+ 'd7_user',
+ ];
+ $this->assertEmpty(array_diff(array_filter($conflicts), $expected));
+ }
+
+ /**
+ * Tests draft revisions ID conflicts.
+ */
+ public function testDraftRevisionIdConflicts() {
+ // Create a published node of type page.
+ $node = Node::create(['type' => 'page', 'title' => 'foo']);
+ $node->moderation_state->value = 'published';
+ $node->save();
+
+ // Create a draft revision.
+ $node->moderation_state->value = 'draft';
+ $node->setNewRevision(TRUE);
+ $node->save();
+
+ // Insert data in the d7_node_revision:page migration mappping table to
+ // simulate a previously migrated node revison.
+ $table_name = $this->getMigration('d7_node_revision:page')->getIdMap()->mapTableName();
+ $this->container->get('database')->insert($table_name)
+ ->fields([
+ 'source_ids_hash' => 1,
+ 'sourceid1' => 1,
+ 'destid1' => 1,
+ ])
+ ->execute();
+
+ // Audit the IDs of the d7_node_revision migration. There should be
+ // conflicts since a draft revision has been created.
+ /** @var \Drupal\migrate\Audit\AuditResult $result */
+ $result = (new IdAuditor())->audit($this->getMigration('d7_node_revision:page'));
+ $this->assertInstanceOf(AuditResult::class, $result);
+ $this->assertFalse($result->passed());
+ }
+
+ /**
+ * Tests ID conflicts for inaccessible nodes.
+ */
+ public function testNodeGrantsIdConflicts() {
+ // Enable the node_test module to restrict access to page nodes.
+ $this->enableModules(['node_test']);
+
+ // Create a published node of type page.
+ $node = Node::create(['type' => 'page', 'title' => 'foo']);
+ $node->moderation_state->value = 'published';
+ $node->save();
+
+ // Audit the IDs of the d7_node migration. There should be conflicts
+ // even though the new node is not accessible.
+ /** @var \Drupal\migrate\Audit\AuditResult $result */
+ $result = (new IdAuditor())->audit($this->getMigration('d7_node:page'));
+ $this->assertInstanceOf(AuditResult::class, $result);
+ $this->assertFalse($result->passed());
+ }
+
+}
diff --git a/core/modules/migrate_drupal/tests/src/Traits/CreateTestContentEntitiesTrait.php b/core/modules/migrate_drupal/tests/src/Traits/CreateTestContentEntitiesTrait.php
new file mode 100644
index 000000000000..db720e8cb786
--- /dev/null
+++ b/core/modules/migrate_drupal/tests/src/Traits/CreateTestContentEntitiesTrait.php
@@ -0,0 +1,131 @@
+installEntitySchema('aggregator_feed');
+ $this->installEntitySchema('aggregator_item');
+ $this->installEntitySchema('block_content');
+ $this->installEntitySchema('comment');
+ $this->installEntitySchema('file');
+ $this->installEntitySchema('menu_link_content');
+ $this->installEntitySchema('node');
+ $this->installEntitySchema('taxonomy_term');
+ $this->installEntitySchema('user');
+ }
+
+ /**
+ * Create several pieces of generic content.
+ */
+ protected function createContent() {
+ // Create an aggregator feed.
+ $feed = Feed::create([
+ 'title' => 'feed',
+ 'url' => 'http://www.example.com',
+ ]);
+ $feed->save();
+
+ // Create an aggregator feed item.
+ $item = Item::create([
+ 'title' => 'feed item',
+ 'fid' => $feed->id(),
+ 'link' => 'http://www.example.com',
+ ]);
+ $item->save();
+
+ // Create a block content.
+ $block = BlockContent::create([
+ 'info' => 'block',
+ 'type' => 'block',
+ ]);
+ $block->save();
+
+ // Create a node.
+ $node = Node::create([
+ 'type' => 'page',
+ 'title' => 'page',
+ ]);
+ $node->save();
+
+ // Create a comment.
+ $comment = Comment::create([
+ 'comment_type' => 'comment',
+ 'field_name' => 'comment',
+ 'entity_type' => 'node',
+ 'entity_id' => $node->id(),
+ ]);
+ $comment->save();
+
+ // Create a file.
+ $file = File::create([
+ 'uri' => 'public://example.txt',
+ ]);
+ $file->save();
+
+ // Create a menu link.
+ $menu_link = MenuLinkContent::create([
+ 'title' => 'menu link',
+ 'link' => ['uri' => 'http://www.example.com'],
+ 'menu_name' => 'tools',
+ ]);
+ $menu_link->save();
+
+ // Create a taxonomy term.
+ $term = Term::create([
+ 'name' => 'term',
+ 'vid' => 'term',
+ ]);
+ $term->save();
+
+ // Create a user.
+ $user = User::create([
+ 'uid' => 2,
+ 'name' => 'user',
+ 'mail' => 'user@example.com',
+ ]);
+ $user->save();
+ }
+
+}
diff --git a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
index 6bfaf42f1dcb..898a8a5568cd 100644
--- a/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
+++ b/core/modules/migrate_drupal_ui/src/Form/MigrateUpgradeForm.php
@@ -9,6 +9,8 @@
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Url;
+use Drupal\migrate\Audit\IdAuditor;
+use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface;
use Drupal\migrate_drupal_ui\Batch\MigrateUpgradeImportBatch;
@@ -124,6 +126,9 @@ public function buildForm(array $form, FormStateInterface $form_state) {
case 'credentials':
return $this->buildCredentialForm($form, $form_state);
+ case 'confirm_id_conflicts':
+ return $this->buildIdConflictForm($form, $form_state);
+
case 'confirm':
return $this->buildConfirmForm($form, $form_state);
@@ -457,6 +462,155 @@ public function validateCredentialForm(array &$form, FormStateInterface $form_st
*/
public function submitCredentialForm(array &$form, FormStateInterface $form_state) {
// Indicate the next step is confirmation.
+ $form_state->set('step', 'confirm_id_conflicts');
+ $form_state->setRebuild();
+ }
+
+ /**
+ * Confirmation form for ID conflicts.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ *
+ * @return array
+ * The form structure.
+ */
+ public function buildIdConflictForm(array &$form, FormStateInterface $form_state) {
+ // Check if there are conflicts. If none, just skip this form!
+ $migration_ids = array_keys($form_state->get('migrations'));
+ $migrations = $this->pluginManager->createInstances($migration_ids);
+
+ $translated_content_conflicts = $content_conflicts = [];
+
+ $results = (new IdAuditor())->auditMultiple($migrations);
+
+ /** @var \Drupal\migrate\Audit\AuditResult $result */
+ foreach ($results as $result) {
+ $destination = $result->getMigration()->getDestinationPlugin();
+ if ($destination instanceof EntityContentBase && $destination->isTranslationDestination()) {
+ // Translations are not yet supperted by the audit system. For now, we
+ // only warn the user to be cautious when migrating translated content.
+ // I18n support should be added in https://www.drupal.org/node/2905759.
+ $translated_content_conflicts[] = $result;
+ }
+ elseif (!$result->passed()) {
+ $content_conflicts[] = $result;
+ }
+
+ }
+ if (empty($content_conflicts) && empty($translated_content_conflicts)) {
+ $form_state->set('step', 'confirm');
+ return $this->buildForm($form, $form_state);
+ }
+
+ drupal_set_message($this->t('WARNING: Content may be overwritten on your new site.'), 'warning');
+
+ $form = parent::buildForm($form, $form_state);
+ $form['actions']['submit']['#submit'] = ['::submitConfirmIdConflictForm'];
+ $form['actions']['submit']['#value'] = $this->t('I acknowledge I may lose data. Continue anyway.');
+
+ if ($content_conflicts) {
+ $form = $this->conflictsForm($form, $form_state, $content_conflicts);
+ }
+ if ($translated_content_conflicts) {
+ $form = $this->i18nWarningForm($form, $form_state, $translated_content_conflicts);
+ }
+ return $form;
+ }
+
+ /**
+ * Build the markup for conflict warnings.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ * @param \Drupal\migrate\Audit\AuditResult[] $conflicts
+ * The failing audit results.
+ *
+ * @return array
+ * The form structure.
+ */
+ protected function conflictsForm(array &$form, FormStateInterface $form_state, array $conflicts) {
+ $form['conflicts'] = [
+ '#title' => $this->t('There is conflicting content of these types:'),
+ '#theme' => 'item_list',
+ '#items' => $this->formatConflicts($conflicts),
+ ];
+
+ $form['warning'] = [
+ '#type' => 'markup',
+ '#markup' => '
' . $this->t('It looks like you have content on your new site which may be overwritten if you continue to run this upgrade. The upgrade should be performed on a clean Drupal 8 installation. For more information see the upgrade handbook.', [':id-conflicts-handbook' => 'https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#id_conflicts']) . '
',
+ ];
+
+ return $form;
+ }
+
+ /**
+ * Formats a set of failing audit results as strings.
+ *
+ * Each string is the label of the destination plugin of the migration that
+ * failed the audit, keyed by the destination plugin ID in order to prevent
+ * duplication.
+ *
+ * @param \Drupal\migrate\Audit\AuditResult[] $conflicts
+ * The failing audit results.
+ *
+ * @return string[]
+ * The formatted audit results.
+ */
+ protected function formatConflicts(array $conflicts) {
+ $items = [];
+
+ foreach ($conflicts as $conflict) {
+ $definition = $conflict->getMigration()->getDestinationPlugin()->getPluginDefinition();
+ $id = $definition['id'];
+ $items[$id] = $definition['label'];
+ }
+ sort($items, SORT_STRING);
+
+ return $items;
+ }
+
+ /**
+ * Build the markup for i18n warnings.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ * @param \Drupal\migrate\Audit\AuditResult[] $conflicts
+ * The failing audit results.
+ *
+ * @return array
+ * The form structure.
+ */
+ protected function i18nWarningForm(array &$form, FormStateInterface $form_state, array $conflicts) {
+ $form['i18n'] = [
+ '#title' => $this->t('There is translated content of these types:'),
+ '#theme' => 'item_list',
+ '#items' => $this->formatConflicts($conflicts),
+ ];
+
+ $form['i18n_warning'] = [
+ '#type' => 'markup',
+ '#markup' => '
' . $this->t('It looks like you are migrating translated content from your old site. Possible ID conflicts for translations are not automatically detected in the current version of Drupal. Refer to the upgrade handbook for instructions on how to avoid ID conflicts with translated content.', [':id-conflicts-handbook' => 'https://www.drupal.org/docs/8/upgrade/known-issues-when-upgrading-from-drupal-6-or-7-to-drupal-8#id_conflicts']) . '
',
+ ];
+
+ return $form;
+ }
+
+ /**
+ * Submission handler for the confirmation form.
+ *
+ * @param array $form
+ * An associative array containing the structure of the form.
+ * @param \Drupal\Core\Form\FormStateInterface $form_state
+ * The current state of the form.
+ */
+ public function submitConfirmIdConflictForm(array &$form, FormStateInterface $form_state) {
$form_state->set('step', 'confirm');
$form_state->setRebuild();
}
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
index d5cc93908a8e..98c05aaef431 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeTestBase.php
@@ -6,12 +6,15 @@
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate_drupal\MigrationConfigurationTrait;
use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait;
/**
* Provides a base class for testing migration upgrades in the UI.
*/
abstract class MigrateUpgradeTestBase extends BrowserTestBase {
+
use MigrationConfigurationTrait;
+ use CreateTestContentEntitiesTrait;
/**
* Use the Standard profile to test help implementations of many core modules.
@@ -54,6 +57,9 @@ protected function setUp() {
// Log in as user 1. Migrations in the UI can only be performed as user 1.
$this->drupalLogin($this->rootUser);
+
+ // Create content.
+ $this->createContent();
}
/**
@@ -116,7 +122,8 @@ protected function tearDown() {
public function testMigrateUpgrade() {
$connection_options = $this->sourceDatabase->getConnectionOptions();
$this->drupalGet('/upgrade');
- $this->assertSession()->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
+ $session = $this->assertSession();
+ $session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$this->drupalPostForm(NULL, [], t('Continue'));
$this->assertText('Provide credentials for the database of the Drupal site you want to upgrade.');
@@ -153,37 +160,52 @@ public function testMigrateUpgrade() {
$this->assertText('Resolve the issue below to continue the upgrade.');
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
+ $session->pageTextContains('WARNING: Content may be overwritten on your new site.');
+ $session->pageTextContains('There is conflicting content of these types:');
+ $session->pageTextContains('aggregator feed entities');
+ $session->pageTextContains('aggregator feed item entities');
+ $session->pageTextContains('custom block entities');
+ $session->pageTextContains('custom menu link entities');
+ $session->pageTextContains('file entities');
+ $session->pageTextContains('taxonomy term entities');
+ $session->pageTextContains('user entities');
+ $session->pageTextContains('comments');
+ $session->pageTextContains('content item revisions');
+ $session->pageTextContains('content items');
+ $session->pageTextContains('There is translated content of these types:');
+ $this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
$this->assertResponse(200);
$this->assertText('Upgrade analysis report');
// Ensure we get errors about missing modules.
- $this->assertSession()->pageTextContains(t('Source module not found for migration_provider_no_annotation.'));
- $this->assertSession()->pageTextContains(t('Source module not found for migration_provider_test.'));
- $this->assertSession()->pageTextContains(t('Destination module not found for migration_provider_test'));
+ $session->pageTextContains(t('Source module not found for migration_provider_no_annotation.'));
+ $session->pageTextContains(t('Source module not found for migration_provider_test.'));
+ $session->pageTextContains(t('Destination module not found for migration_provider_test'));
// Uninstall the module causing the missing module error messages.
$this->container->get('module_installer')->uninstall(['migration_provider_test'], TRUE);
// Restart the upgrade process.
$this->drupalGet('/upgrade');
- $this->assertSession()->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
+ $session->responseContains('Upgrade a site by importing its files and the data from its database into a clean and empty new install of Drupal 8.');
$this->drupalPostForm(NULL, [], t('Continue'));
- $this->assertSession()->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
- $this->assertSession()->fieldExists('mysql[host]');
+ $session->pageTextContains('Provide credentials for the database of the Drupal site you want to upgrade.');
+ $session->fieldExists('mysql[host]');
$this->drupalPostForm(NULL, $edits, t('Review upgrade'));
- $this->assertSession()->statusCodeEquals(200);
- $this->assertSession()->pageTextContains('Upgrade analysis report');
+ $session->pageTextContains('WARNING: Content may be overwritten on your new site.');
+ $this->drupalPostForm(NULL, [], t('I acknowledge I may lose data. Continue anyway.'));
+ $session->statusCodeEquals(200);
+ $session->pageTextContains('Upgrade analysis report');
// Ensure there are no errors about the missing modules from the test module.
- $this->assertSession()->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.'));
- $this->assertSession()->pageTextNotContains(t('Source module not found for migration_provider_test.'));
- $this->assertSession()->pageTextNotContains(t('Destination module not found for migration_provider_test'));
+ $session->pageTextNotContains(t('Source module not found for migration_provider_no_annotation.'));
+ $session->pageTextNotContains(t('Source module not found for migration_provider_test.'));
+ $session->pageTextNotContains(t('Destination module not found for migration_provider_test'));
// Ensure there are no errors about any other missing migration providers.
- $this->assertSession()->pageTextNotContains(t('module not found'));
+ $session->pageTextNotContains(t('module not found'));
// Test the available migration paths.
$all_available = $this->getAvailablePaths();
- $session = $this->assertSession();
foreach ($all_available as $available) {
$session->elementExists('xpath', "//span[contains(@class, 'checked') and text() = '$available']");
$session->elementNotExists('xpath', "//span[contains(@class, 'warning') and text() = '$available']");
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php
index 3717606cd846..432b7036d72c 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php
@@ -35,7 +35,7 @@ protected function getSourceBasePath() {
protected function getEntityCounts() {
return [
'aggregator_item' => 1,
- 'aggregator_feed' => 1,
+ 'aggregator_feed' => 2,
'block' => 35,
'block_content' => 2,
'block_content_type' => 1,
@@ -48,7 +48,7 @@ protected function getEntityCounts() {
'editor' => 2,
'field_config' => 84,
'field_storage_config' => 58,
- 'file' => 7,
+ 'file' => 8,
'filter_format' => 7,
'image_style' => 5,
'language_content_settings' => 2,
@@ -68,7 +68,7 @@ protected function getEntityCounts() {
'tour' => 4,
'user' => 7,
'user_role' => 6,
- 'menu_link_content' => 4,
+ 'menu_link_content' => 5,
'view' => 16,
'date_format' => 11,
'entity_form_display' => 29,
diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php
index 3cfe4bc6d711..608967c75411 100644
--- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php
+++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php
@@ -39,7 +39,7 @@ protected function getSourceBasePath() {
*/
protected function getEntityCounts() {
return [
- 'aggregator_item' => 10,
+ 'aggregator_item' => 11,
'aggregator_feed' => 1,
'block' => 25,
'block_content' => 1,
@@ -72,7 +72,7 @@ protected function getEntityCounts() {
'tour' => 4,
'user' => 4,
'user_role' => 3,
- 'menu_link_content' => 7,
+ 'menu_link_content' => 8,
'view' => 16,
'date_format' => 11,
'entity_form_display' => 17,
diff --git a/core/modules/node/migrations/d6_node.yml b/core/modules/node/migrations/d6_node.yml
index 56d0459a8126..84a4bf18720f 100644
--- a/core/modules/node/migrations/d6_node.yml
+++ b/core/modules/node/migrations/d6_node.yml
@@ -1,5 +1,6 @@
id: d6_node
label: Nodes
+audit: true
migration_tags:
- Drupal 6
deriver: Drupal\node\Plugin\migrate\D6NodeDeriver
diff --git a/core/modules/node/migrations/d6_node_revision.yml b/core/modules/node/migrations/d6_node_revision.yml
index f4ff3011c41b..74a42d430773 100644
--- a/core/modules/node/migrations/d6_node_revision.yml
+++ b/core/modules/node/migrations/d6_node_revision.yml
@@ -1,5 +1,6 @@
id: d6_node_revision
label: Node revisions
+audit: true
migration_tags:
- Drupal 6
deriver: Drupal\node\Plugin\migrate\D6NodeDeriver
diff --git a/core/modules/node/migrations/d7_node.yml b/core/modules/node/migrations/d7_node.yml
index 359be81cc429..80367971a3a5 100644
--- a/core/modules/node/migrations/d7_node.yml
+++ b/core/modules/node/migrations/d7_node.yml
@@ -1,5 +1,6 @@
id: d7_node
label: Nodes
+audit: true
migration_tags:
- Drupal 7
deriver: Drupal\node\Plugin\migrate\D7NodeDeriver
diff --git a/core/modules/node/migrations/d7_node_revision.yml b/core/modules/node/migrations/d7_node_revision.yml
index c6081ef11044..18c90b6f49d3 100644
--- a/core/modules/node/migrations/d7_node_revision.yml
+++ b/core/modules/node/migrations/d7_node_revision.yml
@@ -1,5 +1,6 @@
id: d7_node_revision
label: Node revisions
+audit: true
migration_tags:
- Drupal 7
deriver: Drupal\node\Plugin\migrate\D7NodeDeriver
diff --git a/core/modules/taxonomy/migrations/d6_taxonomy_term.yml b/core/modules/taxonomy/migrations/d6_taxonomy_term.yml
index e3c3e3d3420b..9eafee544cbf 100644
--- a/core/modules/taxonomy/migrations/d6_taxonomy_term.yml
+++ b/core/modules/taxonomy/migrations/d6_taxonomy_term.yml
@@ -1,5 +1,6 @@
id: d6_taxonomy_term
label: Taxonomy terms
+audit: true
migration_tags:
- Drupal 6
source:
diff --git a/core/modules/taxonomy/migrations/d6_term_node_revision.yml b/core/modules/taxonomy/migrations/d6_term_node_revision.yml
index 91c8362e63b8..c3ebe3059faf 100644
--- a/core/modules/taxonomy/migrations/d6_term_node_revision.yml
+++ b/core/modules/taxonomy/migrations/d6_term_node_revision.yml
@@ -1,5 +1,6 @@
id: d6_term_node_revision
label: Term/node relationship revisions
+audit: true
migration_tags:
- Drupal 6
deriver: Drupal\taxonomy\Plugin\migrate\D6TermNodeDeriver
diff --git a/core/modules/taxonomy/migrations/d7_taxonomy_term.yml b/core/modules/taxonomy/migrations/d7_taxonomy_term.yml
index 46f9f20427f7..6033d4f875ec 100644
--- a/core/modules/taxonomy/migrations/d7_taxonomy_term.yml
+++ b/core/modules/taxonomy/migrations/d7_taxonomy_term.yml
@@ -1,5 +1,6 @@
id: d7_taxonomy_term
label: Taxonomy terms
+audit: true
migration_tags:
- Drupal 7
deriver: Drupal\taxonomy\Plugin\migrate\D7TaxonomyTermDeriver
diff --git a/core/modules/user/migrations/d6_user.yml b/core/modules/user/migrations/d6_user.yml
index d58607b1507c..35d31da875ad 100644
--- a/core/modules/user/migrations/d6_user.yml
+++ b/core/modules/user/migrations/d6_user.yml
@@ -1,5 +1,6 @@
id: d6_user
label: User accounts
+audit: true
migration_tags:
- Drupal 6
source:
diff --git a/core/modules/user/migrations/d7_user.yml b/core/modules/user/migrations/d7_user.yml
index 54c880587515..ee70db782e11 100644
--- a/core/modules/user/migrations/d7_user.yml
+++ b/core/modules/user/migrations/d7_user.yml
@@ -1,5 +1,6 @@
id: d7_user
label: User accounts
+audit: true
migration_tags:
- Drupal 7
class: Drupal\user\Plugin\migrate\User
diff --git a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
index aa74b84a437e..b5317beb8ceb 100644
--- a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
+++ b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
@@ -127,4 +127,18 @@ protected function processStubRow(Row $row) {
}
}
+ /**
+ * {@inheritdoc}
+ */
+ public function getHighestId() {
+ $highest_id = parent::getHighestId();
+
+ // Every Drupal site must have a user with UID of 1 and it's normal for
+ // migrations to overwrite this user.
+ if ($highest_id === 1) {
+ return 0;
+ }
+ return $highest_id;
+ }
+
}
From 40856ccd26233c2ad5345c623d55f44f149784b9 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Sat, 16 Dec 2017 08:41:56 +1000
Subject: [PATCH 040/232] Issue #2760167 by kim.pepper, markcarver, jibran,
dawehner, znerol, Wim Leers, larowlan, xjm, tim.plunkett: Add
\Drupal\Core\Messenger\Messenger
---
core/core.services.yml | 4 +-
core/includes/bootstrap.inc | 23 +-
core/lib/Drupal.php | 34 ++-
.../Drupal/Core/Messenger/LegacyMessenger.php | 260 ++++++++----------
core/lib/Drupal/Core/Messenger/Messenger.php | 112 ++++++++
.../Core/Messenger/MessengerInterface.php | 9 +-
.../src/Controller/SystemTestController.php | 24 +-
.../Core/Common/DrupalSetMessageTest.php | 6 -
.../Core/Messenger/LegacyMessengerTest.php | 84 ++++++
.../Listeners/DeprecationListenerTrait.php | 3 +
10 files changed, 394 insertions(+), 165 deletions(-)
create mode 100644 core/lib/Drupal/Core/Messenger/Messenger.php
create mode 100644 core/tests/Drupal/KernelTests/Core/Messenger/LegacyMessengerTest.php
diff --git a/core/core.services.yml b/core/core.services.yml
index 459503e44ceb..49b27089b52d 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1646,5 +1646,5 @@ services:
tags:
- { name: event_subscriber }
messenger:
- class: Drupal\Core\Messenger\LegacyMessenger
- arguments: ['@page_cache_kill_switch']
+ class: Drupal\Core\Messenger\Messenger
+ arguments: ['@session.flash_bag', '@page_cache_kill_switch']
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index d05298d4bf44..ae2f8915c3d4 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -466,11 +466,17 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia
*
* @see drupal_get_messages()
* @see status-messages.html.twig
+ * @see https://www.drupal.org/node/2774931
+ *
+ * @deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0.
+ * Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead.
*/
function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) {
- /* @var \Drupal\Core\Messenger\MessengerInterface $messenger */
- $messenger = \Drupal::service('messenger');
- $messenger->addMessage($message, $type, $repeat);
+ @trigger_error('drupal_set_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead. See https://www.drupal.org/node/2774931', E_USER_DEPRECATED);
+ $messenger = \Drupal::messenger();
+ if (isset($message)) {
+ $messenger->addMessage($message, $type, $repeat);
+ }
return $messenger->all();
}
@@ -498,11 +504,16 @@ function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE)
*
* @see drupal_set_message()
* @see status-messages.html.twig
+ * @see https://www.drupal.org/node/2774931
+ *
+ * @deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0.
+ * Use \Drupal\Core\Messenger\MessengerInterface::all() or
+ * \Drupal\Core\Messenger\MessengerInterface::messagesByType() instead.
*/
function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
- /** @var \Drupal\Core\Messenger\MessengerInterface $messenger */
- $messenger = \Drupal::hasService('messenger') ? \Drupal::service('messenger') : NULL;
- if ($messenger && ($messages = $messenger->all())) {
+ @trigger_error('drupal_get_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::all() or \Drupal\Core\Messenger\MessengerInterface::messagesByType() instead. See https://www.drupal.org/node/2774931', E_USER_DEPRECATED);
+ $messenger = \Drupal::messenger();
+ if ($messages = $messenger->all()) {
if ($type) {
if ($clear_queue) {
$messenger->deleteByType($type);
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 07dc12f1f314..8b98ccd1dc71 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -6,8 +6,9 @@
*/
use Drupal\Core\DependencyInjection\ContainerNotInitializedException;
-use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Messenger\LegacyMessenger;
use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Static Service Container wrapper.
@@ -100,6 +101,22 @@ class Drupal {
*/
protected static $container;
+ /**
+ * The LegacyMessenger instance.
+ *
+ * Note: this is merely used to ensure that the instance survives when
+ * \Drupal::messenger() is invoked. It is required to ensure that messages
+ * are properly transferred to the Messenger service once the container has
+ * been initialized. Do not store the Messenger service here.
+ *
+ * @todo Remove once LegacyMessenger has been removed before 9.0.0.
+ *
+ * @see https://www.drupal.org/node/2928994
+ *
+ * @var \Drupal\Core\Messenger\LegacyMessenger|null
+ */
+ protected static $legacyMessenger;
+
/**
* Sets a new global container.
*
@@ -757,4 +774,19 @@ public static function time() {
return static::getContainer()->get('datetime.time');
}
+ /**
+ * Returns the messenger.
+ *
+ * @return \Drupal\Core\Messenger\MessengerInterface
+ * The messenger.
+ */
+ public static function messenger() {
+ // @todo Replace with service once LegacyMessenger is removed in 9.0.0.
+ // @see https://www.drupal.org/node/2928994
+ if (!isset(static::$legacyMessenger)) {
+ static::$legacyMessenger = new LegacyMessenger();
+ }
+ return static::$legacyMessenger;
+ }
+
}
diff --git a/core/lib/Drupal/Core/Messenger/LegacyMessenger.php b/core/lib/Drupal/Core/Messenger/LegacyMessenger.php
index 8c9751f3d820..bdc66ffe39bc 100644
--- a/core/lib/Drupal/Core/Messenger/LegacyMessenger.php
+++ b/core/lib/Drupal/Core/Messenger/LegacyMessenger.php
@@ -3,38 +3,64 @@
namespace Drupal\Core\Messenger;
use Drupal\Component\Render\MarkupInterface;
-use Drupal\Core\PageCache\ResponsePolicy\KillSwitch;
use Drupal\Core\Render\Markup;
/**
- * A legacy implementation of the messenger interface.
+ * Provides a LegacyMessenger implementation.
*
- * @internal
+ * This implementation is for handling messages in a backwards compatible way
+ * using core's previous $_SESSION storage method.
+ *
+ * You should not instantiate a new instance of this class directly. Instead,
+ * you should inject the "messenger" service into your own services or use
+ * \Drupal::messenger() in procedural functions.
+ *
+ * @see https://www.drupal.org/node/2774931
+ * @see https://www.drupal.org/node/2928994
+ *
+ * @deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0.
+ * Use \Drupal\Core\Messenger\Messenger instead.
*/
class LegacyMessenger implements MessengerInterface {
/**
- * The page cache kill switch.
+ * The messages.
*
- * @var \Drupal\Core\PageCache\ResponsePolicy\KillSwitch
+ * @var array
*/
- protected $killSwitch;
+ protected $messages;
/**
- * LegacyMessenger constructor.
- *
- * @param \Drupal\Core\PageCache\ResponsePolicy\KillSwitch $killSwitch
- * (optional) The page cache kill switch.
+ * {@inheritdoc}
*/
- public function __construct(KillSwitch $killSwitch) {
- $this->killSwitch = $killSwitch;
+ public function addError($message, $repeat = FALSE) {
+ return $this->addMessage($message, static::TYPE_ERROR);
}
/**
* {@inheritdoc}
*/
public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE) {
- $this->setMessage($message, $type, $repeat);
+ // Proxy to the Messenger service, if it exists.
+ if ($messenger = $this->getMessengerService()) {
+ return $messenger->addMessage($message, $type, $repeat);
+ }
+
+ if (!isset($this->messages[$type])) {
+ $this->messages[$type] = [];
+ }
+
+ if (!($message instanceof Markup) && $message instanceof MarkupInterface) {
+ $message = Markup::create((string) $message);
+ }
+
+ // Do not use strict type checking so that equivalent string and
+ // MarkupInterface objects are detected.
+ if ($repeat || !in_array($message, $this->messages[$type])) {
+ $this->messages[$type][] = $message;
+ }
+
+ return $this;
}
/**
@@ -44,13 +70,6 @@ public function addStatus($message, $repeat = FALSE) {
return $this->addMessage($message, static::TYPE_STATUS);
}
- /**
- * {@inheritdoc}
- */
- public function addError($message, $repeat = FALSE) {
- return $this->addMessage($message, static::TYPE_ERROR);
- }
-
/**
* {@inheritdoc}
*/
@@ -62,151 +81,104 @@ public function addWarning($message, $repeat = FALSE) {
* {@inheritdoc}
*/
public function all() {
- return $this->getMessages(NULL, FALSE);
- }
+ // Proxy to the Messenger service, if it exists.
+ if ($messenger = $this->getMessengerService()) {
+ return $messenger->all();
+ }
- /**
- * {@inheritdoc}
- */
- public function messagesByType($type) {
- return $this->getMessages($type, FALSE);
+ return $this->messages;
}
/**
- * {@inheritdoc}
+ * Returns the Messenger service.
+ *
+ * @return \Drupal\Core\Messenger\MessengerInterface|null
+ * The Messenger service.
*/
- public function deleteAll() {
- return $this->getMessages(NULL, TRUE);
+ protected function getMessengerService() {
+ // Use the Messenger service, if it exists.
+ if (\Drupal::hasService('messenger')) {
+ // Note: because the container has the potential to be rebuilt during
+ // requests, this service cannot be directly stored on this class.
+ $messenger = \Drupal::service('messenger');
+
+ // Transfer any messages into the service.
+ if (isset($this->messages)) {
+ foreach ($this->messages as $type => $messages) {
+ foreach ($messages as $message) {
+ $messenger->addMessage($message, $type);
+ }
+ }
+ unset($this->messages);
+ }
+
+ return $messenger;
+ }
+
+ // Otherwise, trigger an error.
+ @trigger_error('Adding or retrieving messages prior to the container being initialized was deprecated in Drupal 8.5.0 and this functionality will be removed before Drupal 9.0.0. Please report this usage at https://www.drupal.org/node/2928994.', E_USER_DEPRECATED);
+
+ // Prematurely creating $_SESSION['messages'] in this class' constructor
+ // causes issues when the container attempts to initialize its own session
+ // later down the road. This can only be done after it has been determined
+ // the Messenger service is not available (i.e. no container). It is also
+ // reasonable to assume that if the container becomes available in a
+ // subsequent request, a new instance of this class will be created and
+ // this code will never be reached. This is merely for BC purposes.
+ if (!isset($this->messages)) {
+ // A "session" was already created, perhaps to simply allow usage of
+ // the previous method core used to store messages, use it.
+ if (isset($_SESSION)) {
+ if (!isset($_SESSION['messages'])) {
+ $_SESSION['messages'] = [];
+ }
+ $this->messages = &$_SESSION['messages'];
+ }
+ // Otherwise, just set an empty array.
+ else {
+ $this->messages = [];
+ }
+ }
}
/**
* {@inheritdoc}
*/
- public function deleteByType($type) {
- return $this->getMessages($type, TRUE);
+ public function messagesByType($type) {
+ // Proxy to the Messenger service, if it exists.
+ if ($messenger = $this->getMessengerService()) {
+ return $messenger->messagesByType($type);
+ }
+
+ return $this->messages[$type];
}
/**
- * Sets a message to display to the user.
- *
- * Messages are stored in a session variable and displayed in the page template
- * via the $messages theme variable.
- *
- * Example usage:
- * @code
- * drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
- * @endcode
- *
- * @param string|\Drupal\Component\Render\MarkupInterface $message
- * (optional) The translated message to be displayed to the user. For
- * consistency with other messages, it should begin with a capital letter and
- * end with a period.
- * @param string $type
- * (optional) The message's type. Defaults to 'status'. These values are
- * supported:
- * - 'status'
- * - 'warning'
- * - 'error'
- * @param bool $repeat
- * (optional) If this is FALSE and the message is already set, then the
- * message won't be repeated. Defaults to FALSE.
- *
- * @return array|null
- * A multidimensional array with keys corresponding to the set message types.
- * The indexed array values of each contain the set messages for that type,
- * and each message is an associative array with the following format:
- * - safe: Boolean indicating whether the message string has been marked as
- * safe. Non-safe strings will be escaped automatically.
- * - message: The message string.
- * So, the following is an example of the full return array structure:
- * @code
- * array(
- * 'status' => array(
- * array(
- * 'safe' => TRUE,
- * 'message' => 'A safe markup string.',
- * ),
- * array(
- * 'safe' => FALSE,
- * 'message' => "$arbitrary_user_input to escape.",
- * ),
- * ),
- * );
- * @endcode
- * If there are no messages set, the function returns NULL.
- *
- * @internal
+ * {@inheritdoc}
*/
- private function setMessage($message = NULL, $type = 'status', $repeat = FALSE) {
- if (isset($message)) {
- if (!isset($_SESSION['messages'][$type])) {
- $_SESSION['messages'][$type] = [];
- }
-
- // Convert strings which are safe to the simplest Markup objects.
- if (!($message instanceof Markup) && $message instanceof MarkupInterface) {
- $message = Markup::create((string) $message);
- }
-
- // Do not use strict type checking so that equivalent string and
- // MarkupInterface objects are detected.
- if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
- $_SESSION['messages'][$type][] = $message;
- }
-
- // Mark this page as being uncacheable.
- $this->killSwitch->trigger();
+ public function deleteAll() {
+ // Proxy to the Messenger service, if it exists.
+ if ($messenger = $this->getMessengerService()) {
+ return $messenger->deleteAll();
}
- // Messages not set when DB connection fails.
- return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
+ $messages = $this->messages;
+ unset($this->messages);
+ return $messages;
}
/**
- * Returns all messages that have been set with drupal_set_message().
- *
- * @param string $type
- * (optional) Limit the messages returned by type. Defaults to NULL, meaning
- * all types. These values are supported:
- * - NULL
- * - 'status'
- * - 'warning'
- * - 'error'
- * @param bool $clear_queue
- * (optional) If this is TRUE, the queue will be cleared of messages of the
- * type specified in the $type parameter. Otherwise the queue will be left
- * intact. Defaults to TRUE.
- *
- * @return array
- * An associative, nested array of messages grouped by message type, with
- * the top-level keys as the message type. The messages returned are
- * limited to the type specified in the $type parameter, if any. If there
- * are no messages of the specified type, an empty array is returned. See
- * drupal_set_message() for the array structure of individual messages.
- *
- * @see drupal_set_message()
- * @see status-messages.html.twig
- *
- * @internal
+ * {@inheritdoc}
*/
- private function getMessages($type = NULL, $clear_queue = TRUE) {
- if ($messages = $this->setMessage()) {
- if ($type) {
- if ($clear_queue) {
- unset($_SESSION['messages'][$type]);
- }
- if (isset($messages[$type])) {
- return [$type => $messages[$type]];
- }
- }
- else {
- if ($clear_queue) {
- unset($_SESSION['messages']);
- }
- return $messages;
- }
+ public function deleteByType($type) {
+ // Proxy to the Messenger service, if it exists.
+ if ($messenger = $this->getMessengerService()) {
+ return $messenger->messagesByType($type);
}
- return [];
+
+ $messages = $this->messages[$type];
+ unset($this->messages[$type]);
+ return $messages;
}
}
diff --git a/core/lib/Drupal/Core/Messenger/Messenger.php b/core/lib/Drupal/Core/Messenger/Messenger.php
new file mode 100644
index 000000000000..b8b6c3ddf369
--- /dev/null
+++ b/core/lib/Drupal/Core/Messenger/Messenger.php
@@ -0,0 +1,112 @@
+flashBag = $flash_bag;
+ $this->killSwitch = $killSwitch;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addError($message, $repeat = FALSE) {
+ return $this->addMessage($message, static::TYPE_ERROR);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE) {
+ if (!($message instanceof Markup) && $message instanceof MarkupInterface) {
+ $message = Markup::create((string) $message);
+ }
+
+ // Do not use strict type checking so that equivalent string and
+ // MarkupInterface objects are detected.
+ if ($repeat || !in_array($message, $this->flashBag->peek($type))) {
+ $this->flashBag->add($type, $message);
+ }
+
+ // Mark this page as being uncacheable.
+ $this->killSwitch->trigger();
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addStatus($message, $repeat = FALSE) {
+ return $this->addMessage($message, static::TYPE_STATUS);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function addWarning($message, $repeat = FALSE) {
+ return $this->addMessage($message, static::TYPE_WARNING);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all() {
+ return $this->flashBag->peekAll();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteAll() {
+ return $this->flashBag->clear();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteByType($type) {
+ // Flash bag gets and clears flash messages from the stack.
+ return $this->flashBag->get($type);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function messagesByType($type) {
+ return $this->flashBag->peek($type);
+ }
+
+}
diff --git a/core/lib/Drupal/Core/Messenger/MessengerInterface.php b/core/lib/Drupal/Core/Messenger/MessengerInterface.php
index 216835bcea5e..0d5e63ac95f0 100644
--- a/core/lib/Drupal/Core/Messenger/MessengerInterface.php
+++ b/core/lib/Drupal/Core/Messenger/MessengerInterface.php
@@ -6,8 +6,6 @@
* Stores runtime messages sent out to individual users on the page.
*
* An example for these messages is for example: "Content X got saved".
- *
- * @internal
*/
interface MessengerInterface {
@@ -109,11 +107,15 @@ public function all();
* or self::TYPE_ERROR.
*
* @return string[]|\Drupal\Component\Render\MarkupInterface[]
+ * The messages of given type.
*/
public function messagesByType($type);
/**
* Deletes all messages.
+ *
+ * @return string[]|\Drupal\Component\Render\MarkupInterface[]
+ * The deleted messages.
*/
public function deleteAll();
@@ -123,6 +125,9 @@ public function deleteAll();
* @param string $type
* The messages' type. Either self::TYPE_STATUS, self::TYPE_WARNING, or
* self::TYPE_ERROR.
+ *
+ * @return string[]|\Drupal\Component\Render\MarkupInterface[]
+ * The deleted messages of given type.
*/
public function deleteByType($type);
diff --git a/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
index 37eb87585d2f..e350bc2eaf37 100644
--- a/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
+++ b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
@@ -5,6 +5,7 @@
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\CacheableResponse;
use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Render\Markup;
use Drupal\Core\Session\AccountInterface;
@@ -48,6 +49,13 @@ class SystemTestController extends ControllerBase {
*/
protected $renderer;
+ /**
+ * The messenger service.
+ *
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
/**
* Constructs the SystemTestController.
*
@@ -59,12 +67,15 @@ class SystemTestController extends ControllerBase {
* The current user.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
+ * @param \Drupal\Core\Messenger\MessengerInterface $messenger
+ * The messenger service.
*/
- public function __construct(LockBackendInterface $lock, LockBackendInterface $persistent_lock, AccountInterface $current_user, RendererInterface $renderer) {
+ public function __construct(LockBackendInterface $lock, LockBackendInterface $persistent_lock, AccountInterface $current_user, RendererInterface $renderer, MessengerInterface $messenger) {
$this->lock = $lock;
$this->persistentLock = $persistent_lock;
$this->currentUser = $current_user;
$this->renderer = $renderer;
+ $this->messenger = $messenger;
}
/**
@@ -75,7 +86,8 @@ public static function create(ContainerInterface $container) {
$container->get('lock'),
$container->get('lock.persistent'),
$container->get('current_user'),
- $container->get('renderer')
+ $container->get('renderer'),
+ $container->get('messenger')
);
}
@@ -99,9 +111,13 @@ public function drupalSetMessageTest() {
// Set two messages.
drupal_set_message('First message (removed).');
drupal_set_message(t('Second message with markup! (not removed).'));
-
+ $messages = $this->messenger->deleteByType('status');
// Remove the first.
- unset($_SESSION['messages']['status'][0]);
+ unset($messages[0]);
+
+ foreach ($messages as $message) {
+ $this->messenger->addStatus($message);
+ }
// Duplicate message check.
drupal_set_message('Non Duplicated message', 'status', FALSE);
diff --git a/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php b/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php
index 59470e656306..7a15fc3e04ac 100644
--- a/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Common/DrupalSetMessageTest.php
@@ -20,10 +20,4 @@ public function testDrupalSetMessage() {
$this->assertEquals('A message: bar', (string) $messages['status'][0]);
}
- protected function tearDown() {
- // Clear session to prevent global leakage.
- unset($_SESSION['messages']);
- parent::tearDown();
- }
-
}
diff --git a/core/tests/Drupal/KernelTests/Core/Messenger/LegacyMessengerTest.php b/core/tests/Drupal/KernelTests/Core/Messenger/LegacyMessengerTest.php
new file mode 100644
index 000000000000..13f10e9c2eb0
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Messenger/LegacyMessengerTest.php
@@ -0,0 +1,84 @@
+setAccessible(TRUE);
+ return $method->invoke($legacy_messenger);
+ }
+
+ /**
+ * @covers \Drupal::messenger
+ * @covers ::getMessengerService
+ * @covers ::all
+ * @covers ::addMessage
+ * @covers ::addError
+ * @covers ::addStatus
+ * @covers ::addWarning
+ */
+ public function testMessages() {
+ // Save the current container for later use.
+ $container = \Drupal::getContainer();
+
+ // Unset the container to mimic not having one.
+ \Drupal::unsetContainer();
+
+ /** @var \Drupal\Core\Messenger\LegacyMessenger $messenger */
+ // Verify that the Messenger service doesn't exists.
+ $messenger = \Drupal::messenger();
+ $this->assertNull($this->getMessengerService($messenger));
+
+ // Add messages.
+ $messenger->addMessage('Foobar');
+ $messenger->addError('Foo');
+
+ // Verify that retrieving another instance and adding more messages works.
+ $messenger = \Drupal::messenger();
+ $messenger->addStatus('Bar');
+ $messenger->addWarning('Fiz');
+
+ // Restore the container.
+ \Drupal::setContainer($container);
+
+ // Verify that the Messenger service exists.
+ $messenger = \Drupal::messenger();
+ $this->assertInstanceOf(Messenger::class, $this->getMessengerService($messenger));
+
+ // Add more messages.
+ $messenger->addMessage('Platypus');
+ $messenger->addError('Rhinoceros');
+ $messenger->addStatus('Giraffe');
+ $messenger->addWarning('Cheetah');
+
+ // Verify that all the messages are present and accounted for.
+ $messages = $messenger->all();
+ $this->assertContains('Foobar', $messages[MessengerInterface::TYPE_STATUS]);
+ $this->assertContains('Foo', $messages[MessengerInterface::TYPE_ERROR]);
+ $this->assertContains('Bar', $messages[MessengerInterface::TYPE_STATUS]);
+ $this->assertContains('Fiz', $messages[MessengerInterface::TYPE_WARNING]);
+ $this->assertContains('Platypus', $messages[MessengerInterface::TYPE_STATUS]);
+ $this->assertContains('Rhinoceros', $messages[MessengerInterface::TYPE_ERROR]);
+ $this->assertContains('Giraffe', $messages[MessengerInterface::TYPE_STATUS]);
+ $this->assertContains('Cheetah', $messages[MessengerInterface::TYPE_WARNING]);
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
index c73b6e59df40..094fcf10174d 100644
--- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
@@ -119,6 +119,9 @@ public static function getSkippedDeprecations() {
'Automatically creating the first item for computed fields is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\TypedData\ComputedItemListTrait instead.',
'"\Drupal\Core\Entity\ContentEntityStorageBase::doLoadRevisionFieldItems()" is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. "\Drupal\Core\Entity\ContentEntityStorageBase::doLoadMultipleRevisionsFieldItems()" should be implemented instead. See https://www.drupal.org/node/2924915.',
'Passing a single revision ID to "\Drupal\Core\Entity\Sql\SqlContentEntityStorage::buildQuery()" is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. An array of revision IDs should be given instead. See https://www.drupal.org/node/2924915.',
+ 'drupal_set_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead. See https://www.drupal.org/node/2774931',
+ 'drupal_get_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::all() or \Drupal\Core\Messenger\MessengerInterface::messagesByType() instead. See https://www.drupal.org/node/2774931',
+ 'Adding or retrieving messages prior to the container being initialized was deprecated in Drupal 8.5.0 and this functionality will be removed before Drupal 9.0.0. Please report this usage at https://www.drupal.org/node/2928994.',
];
}
From 968da88d76bec562783be77df1055762d28a6e42 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Mon, 18 Dec 2017 13:39:09 +0000
Subject: [PATCH 041/232] Issue #2928846 by alexpott, Berdir: [PHP 7.2] count()
parameter must be an array or an object that implements Countable
---
.../BigPipeResponseAttachmentsProcessorTest.php | 4 ++--
.../Functional/ConfigTranslationOverviewTest.php | 4 ++--
.../EntityReference/EntityReferenceAdminTest.php | 14 ++++++++++++--
.../tour/tests/src/Functional/TourTestBase.php | 5 ++---
4 files changed, 18 insertions(+), 9 deletions(-)
diff --git a/core/modules/big_pipe/tests/src/Unit/Render/BigPipeResponseAttachmentsProcessorTest.php b/core/modules/big_pipe/tests/src/Unit/Render/BigPipeResponseAttachmentsProcessorTest.php
index a4cbacdf9e65..ba69bfee603f 100644
--- a/core/modules/big_pipe/tests/src/Unit/Render/BigPipeResponseAttachmentsProcessorTest.php
+++ b/core/modules/big_pipe/tests/src/Unit/Render/BigPipeResponseAttachmentsProcessorTest.php
@@ -101,8 +101,8 @@ public function attachmentsProvider() {
'random attachment type (unofficial), with random assigned value, to prove BigPipeResponseAttachmentsProcessor is a perfect decorator' => [$random_attachments],
];
- $big_pipe_placeholder_attachments = ['big_pipe_placeholders' => $this->randomMachineName()];
- $big_pipe_nojs_placeholder_attachments = ['big_pipe_nojs_placeholders' => $this->randomMachineName()];
+ $big_pipe_placeholder_attachments = ['big_pipe_placeholders' => [$this->randomMachineName()]];
+ $big_pipe_nojs_placeholder_attachments = ['big_pipe_nojs_placeholders' => [$this->randomMachineName()]];
$big_pipe_cases = [
'only big_pipe_placeholders' => [$big_pipe_placeholder_attachments],
'only big_pipe_nojs_placeholders' => [$big_pipe_nojs_placeholder_attachments],
diff --git a/core/modules/config_translation/tests/src/Functional/ConfigTranslationOverviewTest.php b/core/modules/config_translation/tests/src/Functional/ConfigTranslationOverviewTest.php
index dff627024594..9ccbb135d2f2 100644
--- a/core/modules/config_translation/tests/src/Functional/ConfigTranslationOverviewTest.php
+++ b/core/modules/config_translation/tests/src/Functional/ConfigTranslationOverviewTest.php
@@ -77,7 +77,7 @@ public function testMapperListPage() {
// Make sure there is only a single operation for each dropbutton, either
// 'List' or 'Translate'.
foreach ($this->cssSelect('ul.dropbutton') as $i => $dropbutton) {
- $this->assertIdentical(1, count($dropbutton->find('xpath', 'li')));
+ $this->assertIdentical(1, count($dropbutton->findAll('xpath', 'li')));
$this->assertTrue(($dropbutton->getText() === 'Translate') || ($dropbutton->getText() === 'List'));
}
@@ -103,7 +103,7 @@ public function testMapperListPage() {
// Make sure there is only a single 'Translate' operation for each
// dropbutton.
foreach ($this->cssSelect('ul.dropbutton') as $i => $dropbutton) {
- $this->assertIdentical(1, count($dropbutton->find('xpath', 'li')));
+ $this->assertIdentical(1, count($dropbutton->findAll('xpath', 'li')));
$this->assertIdentical('Translate', $dropbutton->getText());
}
diff --git a/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php b/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php
index 72a2160e2233..9bacb07d9642 100644
--- a/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php
+++ b/core/modules/field/src/Tests/EntityReference/EntityReferenceAdminTest.php
@@ -332,6 +332,12 @@ public function testFieldAdminHandler() {
$this->drupalPostForm(NULL, $edit, t('Save field settings'));
$this->drupalGet($bundle_path . '/fields/' . $field_path);
$term_name = $this->randomString();
+ $result = \Drupal::entityQuery('taxonomy_term')
+ ->condition('name', $term_name)
+ ->condition('vid', 'tags')
+ ->accessCheck(FALSE)
+ ->execute();
+ $this->assertIdentical(0, count($result), "No taxonomy terms exist with the name '$term_name'.");
$edit = [
// This must be set before new entities will be auto-created.
'settings[handler_settings][auto_create]' => 1,
@@ -344,8 +350,12 @@ public function testFieldAdminHandler() {
];
$this->drupalPostForm(NULL, $edit, t('Save settings'));
// The term should now exist.
- $term = taxonomy_term_load_multiple_by_name($term_name, 'tags')[1];
- $this->assertIdentical(1, count($term), 'Taxonomy term was auto created when set as field default.');
+ $result = \Drupal::entityQuery('taxonomy_term')
+ ->condition('name', $term_name)
+ ->condition('vid', 'tags')
+ ->accessCheck(FALSE)
+ ->execute();
+ $this->assertIdentical(1, count($result), 'Taxonomy term was auto created when set as field default.');
}
/**
diff --git a/core/modules/tour/tests/src/Functional/TourTestBase.php b/core/modules/tour/tests/src/Functional/TourTestBase.php
index 522e8e6f3648..a8487def61ee 100644
--- a/core/modules/tour/tests/src/Functional/TourTestBase.php
+++ b/core/modules/tour/tests/src/Functional/TourTestBase.php
@@ -50,14 +50,13 @@ public function assertTourTips($tips = []) {
// Check for corresponding page elements.
$total = 0;
$modals = 0;
- $raw_content = $this->getSession()->getPage()->getContent();
foreach ($tips as $tip) {
if (!empty($tip['data-id'])) {
- $elements = \PHPUnit_Util_XML::cssSelect('#' . $tip['data-id'], TRUE, $raw_content, TRUE);
+ $elements = $this->getSession()->getPage()->findAll('css', '#' . $tip['data-id']);
$this->assertTrue(!empty($elements) && count($elements) === 1, format_string('Found corresponding page element for tour tip with id #%data-id', ['%data-id' => $tip['data-id']]));
}
elseif (!empty($tip['data-class'])) {
- $elements = \PHPUnit_Util_XML::cssSelect('.' . $tip['data-class'], TRUE, $raw_content, TRUE);
+ $elements = $this->getSession()->getPage()->findAll('css', '.' . $tip['data-class']);
$this->assertFalse(empty($elements), format_string('Found corresponding page element for tour tip with class .%data-class', ['%data-class' => $tip['data-class']]));
}
else {
From 522ed00526a604b21a951483e776889332d0b8c4 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Mon, 18 Dec 2017 14:08:10 +0000
Subject: [PATCH 042/232] Issue #2914938 by timmillwood, RajabNatshah, xjm,
Manuel Garcia, amateescu, Wim Leers: Preview of content - Notice: Undefined
offset: 0 in _quickedit_entity_is_latest_revision() (line 196 of
core/modules/quickedit/quickedit.module)
---
core/modules/quickedit/quickedit.module | 16 +++++++---------
.../quickedit/src/Tests/QuickEditLoadingTest.php | 8 ++++++++
2 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/core/modules/quickedit/quickedit.module b/core/modules/quickedit/quickedit.module
index 3d270500789f..ad08fc1e0df6 100644
--- a/core/modules/quickedit/quickedit.module
+++ b/core/modules/quickedit/quickedit.module
@@ -180,18 +180,16 @@ function quickedit_entity_view_alter(&$build, EntityInterface $entity, EntityVie
* @internal
*/
function _quickedit_entity_is_latest_revision(ContentEntityInterface $entity) {
- $entity_type_manager = \Drupal::entityTypeManager();
- $entity_definition = $entity_type_manager->getDefinition($entity->getEntityTypeId());
- if (!$entity_definition->isRevisionable()) {
+ if (!$entity->getEntityType()->isRevisionable() || $entity->isNew()) {
return TRUE;
}
- $revision_ids = $entity_type_manager
+
+ $latest_revision = \Drupal::entityTypeManager()
->getStorage($entity->getEntityTypeId())
->getQuery()
- ->allRevisions()
- ->condition($entity_definition->getKey('id'), $entity->id())
- ->sort($entity_definition->getKey('revision'), 'DESC')
- ->range(0, 1)
+ ->latestRevision()
+ ->condition($entity->getEntityType()->getKey('id'), $entity->id())
->execute();
- return $entity->getLoadedRevisionId() == array_keys($revision_ids)[0];
+
+ return !empty($latest_revision) && $entity->getLoadedRevisionId() == key($latest_revision) ? TRUE : FALSE;
}
diff --git a/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php b/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php
index 9ca558a37bcd..6945610db1f6 100644
--- a/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php
+++ b/core/modules/quickedit/src/Tests/QuickEditLoadingTest.php
@@ -330,6 +330,13 @@ public function testUserWithPermission() {
public function testWithPendingRevision() {
$this->drupalLogin($this->editorUser);
+ // Verify that the preview is loaded correctly.
+ $this->drupalPostForm('node/add/article', ['title[0][value]' => 'foo'], 'Preview');
+ $this->assertResponse(200);
+ // Verify that quickedit is not active on preview.
+ $this->assertNoRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"');
+ $this->assertNoRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"');
+
$this->drupalGet('node/' . $this->testNode->id());
$this->assertRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"');
$this->assertRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"');
@@ -340,6 +347,7 @@ public function testWithPendingRevision() {
$this->testNode->save();
$this->drupalGet('node/' . $this->testNode->id());
+ $this->assertResponse(200);
$this->assertNoRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"');
$this->assertNoRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"');
}
From a44a23e64fcf517b5b6a5e0286bf5cccd723b45f Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Mon, 18 Dec 2017 20:20:52 +0000
Subject: [PATCH 043/232] Issue #1489692 by Liam Morland, pfrenssen, YesCT,
geekinpink, sudishth, josmera01, David_Rothstein: Incorrect handling of file
upload limit exceeded - file widget disappears
---
.../EventSubscriber/FormAjaxSubscriber.php | 4 +-
.../src/Functional/FileFieldCreationTrait.php | 86 +++++++++++++
.../src/Functional/FileFieldTestBase.php | 72 +----------
.../MaximumFileSizeExceededUploadTest.php | 119 ++++++++++++++++++
.../FormAjaxSubscriberTest.php | 2 +-
.../Drupal/Tests/TestFileCreationTrait.php | 3 +-
6 files changed, 212 insertions(+), 74 deletions(-)
create mode 100644 core/modules/file/tests/src/Functional/FileFieldCreationTrait.php
create mode 100644 core/modules/file/tests/src/FunctionalJavascript/MaximumFileSizeExceededUploadTest.php
diff --git a/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php b/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php
index cae43980274a..6c9c1788f233 100644
--- a/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php
+++ b/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php
@@ -3,7 +3,7 @@
namespace Drupal\Core\Form\EventSubscriber;
use Drupal\Core\Ajax\AjaxResponse;
-use Drupal\Core\Ajax\ReplaceCommand;
+use Drupal\Core\Ajax\PrependCommand;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\Core\Form\Exception\BrokenPostRequestException;
use Drupal\Core\Form\FormAjaxException;
@@ -78,7 +78,7 @@ public function onException(GetResponseForExceptionEvent $event) {
$this->drupalSetMessage($this->t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', ['@size' => $this->formatSize($exception->getSize())]), 'error');
$response = new AjaxResponse();
$status_messages = ['#type' => 'status_messages'];
- $response->addCommand(new ReplaceCommand(NULL, $status_messages));
+ $response->addCommand(new PrependCommand(NULL, $status_messages));
$response->headers->set('X-Status-Code', 200);
$event->setResponse($response);
return;
diff --git a/core/modules/file/tests/src/Functional/FileFieldCreationTrait.php b/core/modules/file/tests/src/Functional/FileFieldCreationTrait.php
new file mode 100644
index 000000000000..89c713e0779d
--- /dev/null
+++ b/core/modules/file/tests/src/Functional/FileFieldCreationTrait.php
@@ -0,0 +1,86 @@
+ $entity_type,
+ 'field_name' => $name,
+ 'type' => 'file',
+ 'settings' => $storage_settings,
+ 'cardinality' => !empty($storage_settings['cardinality']) ? $storage_settings['cardinality'] : 1,
+ ]);
+ $field_storage->save();
+
+ $this->attachFileField($name, $entity_type, $bundle, $field_settings, $widget_settings);
+ return $field_storage;
+ }
+
+ /**
+ * Attaches a file field to an entity.
+ *
+ * @param string $name
+ * The name of the new field (all lowercase), exclude the "field_" prefix.
+ * @param string $entity_type
+ * The entity type this field will be added to.
+ * @param string $bundle
+ * The bundle this field will be added to.
+ * @param array $field_settings
+ * A list of field settings that will be added to the defaults.
+ * @param array $widget_settings
+ * A list of widget settings that will be added to the widget defaults.
+ */
+ public function attachFileField($name, $entity_type, $bundle, $field_settings = [], $widget_settings = []) {
+ $field = [
+ 'field_name' => $name,
+ 'label' => $name,
+ 'entity_type' => $entity_type,
+ 'bundle' => $bundle,
+ 'required' => !empty($field_settings['required']),
+ 'settings' => $field_settings,
+ ];
+ FieldConfig::create($field)->save();
+
+ entity_get_form_display($entity_type, $bundle, 'default')
+ ->setComponent($name, [
+ 'type' => 'file_generic',
+ 'settings' => $widget_settings,
+ ])
+ ->save();
+ // Assign display settings.
+ entity_get_display($entity_type, $bundle, 'default')
+ ->setComponent($name, [
+ 'label' => 'hidden',
+ 'type' => 'file_default',
+ ])
+ ->save();
+ }
+
+}
diff --git a/core/modules/file/tests/src/Functional/FileFieldTestBase.php b/core/modules/file/tests/src/Functional/FileFieldTestBase.php
index 937bf212a239..f3d1c22b460a 100644
--- a/core/modules/file/tests/src/Functional/FileFieldTestBase.php
+++ b/core/modules/file/tests/src/Functional/FileFieldTestBase.php
@@ -13,6 +13,8 @@
*/
abstract class FileFieldTestBase extends BrowserTestBase {
+ use FileFieldCreationTrait;
+
/**
* Modules to enable.
*
@@ -57,76 +59,6 @@ public function getLastFileId() {
return (int) db_query('SELECT MAX(fid) FROM {file_managed}')->fetchField();
}
- /**
- * Creates a new file field.
- *
- * @param string $name
- * The name of the new field (all lowercase), exclude the "field_" prefix.
- * @param string $entity_type
- * The entity type.
- * @param string $bundle
- * The bundle that this field will be added to.
- * @param array $storage_settings
- * A list of field storage settings that will be added to the defaults.
- * @param array $field_settings
- * A list of instance settings that will be added to the instance defaults.
- * @param array $widget_settings
- * A list of widget settings that will be added to the widget defaults.
- */
- public function createFileField($name, $entity_type, $bundle, $storage_settings = [], $field_settings = [], $widget_settings = []) {
- $field_storage = FieldStorageConfig::create([
- 'entity_type' => $entity_type,
- 'field_name' => $name,
- 'type' => 'file',
- 'settings' => $storage_settings,
- 'cardinality' => !empty($storage_settings['cardinality']) ? $storage_settings['cardinality'] : 1,
- ]);
- $field_storage->save();
-
- $this->attachFileField($name, $entity_type, $bundle, $field_settings, $widget_settings);
- return $field_storage;
- }
-
- /**
- * Attaches a file field to an entity.
- *
- * @param string $name
- * The name of the new field (all lowercase), exclude the "field_" prefix.
- * @param string $entity_type
- * The entity type this field will be added to.
- * @param string $bundle
- * The bundle this field will be added to.
- * @param array $field_settings
- * A list of field settings that will be added to the defaults.
- * @param array $widget_settings
- * A list of widget settings that will be added to the widget defaults.
- */
- public function attachFileField($name, $entity_type, $bundle, $field_settings = [], $widget_settings = []) {
- $field = [
- 'field_name' => $name,
- 'label' => $name,
- 'entity_type' => $entity_type,
- 'bundle' => $bundle,
- 'required' => !empty($field_settings['required']),
- 'settings' => $field_settings,
- ];
- FieldConfig::create($field)->save();
-
- entity_get_form_display($entity_type, $bundle, 'default')
- ->setComponent($name, [
- 'type' => 'file_generic',
- 'settings' => $widget_settings,
- ])
- ->save();
- // Assign display settings.
- entity_get_display($entity_type, $bundle, 'default')
- ->setComponent($name, [
- 'label' => 'hidden',
- 'type' => 'file_default',
- ])
- ->save();
- }
-
/**
* Updates an existing file field with new settings.
*/
diff --git a/core/modules/file/tests/src/FunctionalJavascript/MaximumFileSizeExceededUploadTest.php b/core/modules/file/tests/src/FunctionalJavascript/MaximumFileSizeExceededUploadTest.php
new file mode 100644
index 000000000000..7ccd2b218737
--- /dev/null
+++ b/core/modules/file/tests/src/FunctionalJavascript/MaximumFileSizeExceededUploadTest.php
@@ -0,0 +1,119 @@
+fileSystem = $this->container->get('file_system');
+
+ // Create the Article node type.
+ $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
+
+ // Attach a file field to the node type.
+ $field_settings = ['file_extensions' => 'txt'];
+ $this->createFileField('field_file', 'node', 'article', [], $field_settings);
+
+ // Log in as a content author who can create Articles.
+ $this->user = $this->drupalCreateUser([
+ 'access content',
+ 'create article content',
+ ]);
+ $this->drupalLogin($this->user);
+
+ // Disable the displaying of errors, so that the AJAX responses are not
+ // contaminated with error messages about exceeding the maximum POST size.
+ // @todo Remove this when issue #2905597 is fixed.
+ // @see https://www.drupal.org/node/2905597
+ $this->originalDisplayErrorsValue = ini_set('display_errors', '0');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function tearDown() {
+ // Restore the displaying of errors to the original value.
+ // @todo Remove this when issue #2905597 is fixed.
+ // @see https://www.drupal.org/node/2905597
+ ini_set('display_errors', $this->originalDisplayErrorsValue);
+
+ parent::tearDown();
+ }
+
+ /**
+ * Tests that uploading files exceeding maximum size are handled correctly.
+ */
+ public function testUploadFileExceedingMaximumFileSize() {
+ $session = $this->getSession();
+
+ // Create a test file that exceeds the maximum POST size with 1 kilobyte.
+ $post_max_size = Bytes::toInt(ini_get('post_max_size'));
+ $invalid_file = $this->generateFile('exceeding_post_max_size', ceil(($post_max_size + 1024) / 1024), 1024);
+
+ // Go to the node creation form and try to upload the test file.
+ $this->drupalGet('node/add/article');
+ $page = $session->getPage();
+ $page->attachFileToField("files[field_file_0]", $this->fileSystem->realpath($invalid_file));
+
+ // An error message should appear informing the user that the file exceeded
+ // the maximum file size.
+ $this->assertSession()->waitForElement('css', '.messages--error');
+ // The error message includes the actual file size limit which depends on
+ // the current environment, so we check for a part of the message.
+ $this->assertSession()->pageTextContains('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size');
+
+ // Now upload a valid file and check that the error message disappears.
+ $valid_file = $this->generateFile('not_exceeding_post_max_size', 8, 8);
+ $page->attachFileToField("files[field_file_0]", $this->fileSystem->realpath($valid_file));
+ $this->assertSession()->waitForElement('named', ['id_or_name', 'field_file_0_remove_button']);
+ $this->assertSession()->elementNotExists('css', '.messages--error');
+ }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php b/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php
index 050d74526508..278ec0641668 100644
--- a/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php
@@ -181,7 +181,7 @@ public function testOnExceptionBrokenPostRequest() {
$this->assertSame(200, $actual_response->headers->get('X-Status-Code'));
$expected_commands[] = [
'command' => 'insert',
- 'method' => 'replaceWith',
+ 'method' => 'prepend',
'selector' => NULL,
'data' => $rendered_output,
'settings' => NULL,
diff --git a/core/tests/Drupal/Tests/TestFileCreationTrait.php b/core/tests/Drupal/Tests/TestFileCreationTrait.php
index 5bb5394be996..a2f119b65ca4 100644
--- a/core/tests/Drupal/Tests/TestFileCreationTrait.php
+++ b/core/tests/Drupal/Tests/TestFileCreationTrait.php
@@ -164,7 +164,8 @@ public static function generateFile($filename, $width, $lines, $type = 'binary-t
}
// Create filename.
- file_put_contents('public://' . $filename . '.txt', $text);
+ $filename = 'public://' . $filename . '.txt';
+ file_put_contents($filename, $text);
return $filename;
}
From dad8d64eed13328445a58c1f570c66119406e0f3 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Tue, 19 Dec 2017 16:39:06 +0000
Subject: [PATCH 044/232] Issue #2913864 by Jo Fitzgerald, chiranjeeb2410,
matslats, phenaproxima: badly constructred link in drupal_set_message
---
core/modules/migrate_drupal_ui/migrate_drupal_ui.install | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/modules/migrate_drupal_ui/migrate_drupal_ui.install b/core/modules/migrate_drupal_ui/migrate_drupal_ui.install
index 3ad813844721..c6925b9b8df5 100644
--- a/core/modules/migrate_drupal_ui/migrate_drupal_ui.install
+++ b/core/modules/migrate_drupal_ui/migrate_drupal_ui.install
@@ -11,6 +11,6 @@ use Drupal\Core\Url;
* Implements hook_install().
*/
function migrate_drupal_ui_install() {
- $url = Url::fromUri('base:upgrade')->toString();
+ $url = Url::fromRoute('migrate_drupal_ui.upgrade')->toString();
drupal_set_message(t('The Migrate Drupal UI module has been enabled. Proceed to the upgrade form.', [':url' => $url]));
}
From 3cf0815a5471af6dcc1791e42b10dbaacd3ed8ad Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Tue, 19 Dec 2017 17:23:03 +0000
Subject: [PATCH 045/232] Issue #2930072 by vaplas, Lendude: Module: Convert
system functional tests to phpunit
---
.../src/Functional}/Module/DependencyTest.php | 2 +-
.../src/Functional}/Module/HookRequirementsTest.php | 2 +-
.../src/Functional}/Module/InstallUninstallTest.php | 2 +-
.../src/Functional}/Module/PrepareUninstallTest.php | 8 ++++----
.../src/Functional}/Module/RequiredTest.php | 2 +-
.../Tests => tests/src/Functional}/Module/VersionTest.php | 4 ++--
6 files changed, 10 insertions(+), 10 deletions(-)
rename core/modules/system/{src/Tests => tests/src/Functional}/Module/DependencyTest.php (99%)
rename core/modules/system/{src/Tests => tests/src/Functional}/Module/HookRequirementsTest.php (94%)
rename core/modules/system/{src/Tests => tests/src/Functional}/Module/InstallUninstallTest.php (99%)
rename core/modules/system/{src/Tests => tests/src/Functional}/Module/PrepareUninstallTest.php (97%)
rename core/modules/system/{src/Tests => tests/src/Functional}/Module/RequiredTest.php (95%)
rename core/modules/system/{src/Tests => tests/src/Functional}/Module/VersionTest.php (91%)
diff --git a/core/modules/system/src/Tests/Module/DependencyTest.php b/core/modules/system/tests/src/Functional/Module/DependencyTest.php
similarity index 99%
rename from core/modules/system/src/Tests/Module/DependencyTest.php
rename to core/modules/system/tests/src/Functional/Module/DependencyTest.php
index 173380ebd632..0fc78caa953a 100644
--- a/core/modules/system/src/Tests/Module/DependencyTest.php
+++ b/core/modules/system/tests/src/Functional/Module/DependencyTest.php
@@ -1,6 +1,6 @@
drupalGet('admin/modules');
$checkbox = $this->xpath('//input[@id="edit-modules-module-test-enable"]');
- $this->assertEqual(!empty($checkbox[0]['disabled']), $i % 2, $dependencies[$i]);
+ $this->assertEqual(!empty($checkbox[0]->getAttribute('disabled')), $i % 2, $dependencies[$i]);
}
}
From 2e16f2a35d87540f53eec8b13a9a7c25e03db07e Mon Sep 17 00:00:00 2001
From: xjm
Date: Tue, 19 Dec 2017 09:25:18 -1000
Subject: [PATCH 046/232] Issue #2926914 by tim.plunkett, xjm, larowlan,
tedbow, EclipseGc: Rewrite \Drupal\layout_builder\Section to represent the
entire section, not just the block info
---
.../src/Controller/AddSectionController.php | 9 +-
.../Controller/LayoutBuilderController.php | 22 +-
.../src/Controller/MoveBlockController.php | 21 +-
.../src/Field/LayoutSectionItemInterface.php | 40 ---
.../src/Field/LayoutSectionItemList.php | 61 +++-
.../Field/LayoutSectionItemListInterface.php | 46 ---
.../layout_builder/src/Form/AddBlockForm.php | 3 +-
.../src/Form/ConfigureBlockFormBase.php | 9 +-
.../src/Form/ConfigureSectionForm.php | 26 +-
.../src/Form/RemoveBlockForm.php | 8 +-
.../src/Form/UpdateBlockForm.php | 13 +-
.../src/LayoutSectionBuilder.php | 106 +-----
.../src/Plugin/DataType/SectionData.php | 36 ++
.../FieldFormatter/LayoutSectionFormatter.php | 6 +-
.../Field/FieldType/LayoutSectionItem.php | 48 +--
core/modules/layout_builder/src/Section.php | 301 ++++++++++++-----
.../layout_builder/src/SectionComponent.php | 314 ++++++++++++++++++
.../src/SectionStorageInterface.php | 71 ++++
.../src/Functional/LayoutSectionTest.php | 139 +++-----
...outBuilderFieldLayoutCompatibilityTest.php | 9 +-
.../src/Kernel/LayoutSectionItemListTest.php | 39 +++
.../src/Kernel/LayoutSectionItemTest.php | 89 -----
.../src/Kernel/SectionStorageTestBase.php | 156 +++++++++
.../src/Unit/LayoutSectionBuilderTest.php | 122 ++-----
.../tests/src/Unit/SectionTest.php | 271 ++++++---------
25 files changed, 1142 insertions(+), 823 deletions(-)
delete mode 100644 core/modules/layout_builder/src/Field/LayoutSectionItemInterface.php
delete mode 100644 core/modules/layout_builder/src/Field/LayoutSectionItemListInterface.php
create mode 100644 core/modules/layout_builder/src/Plugin/DataType/SectionData.php
create mode 100644 core/modules/layout_builder/src/SectionComponent.php
create mode 100644 core/modules/layout_builder/src/SectionStorageInterface.php
create mode 100644 core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemListTest.php
delete mode 100644 core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemTest.php
create mode 100644 core/modules/layout_builder/tests/src/Kernel/SectionStorageTestBase.php
diff --git a/core/modules/layout_builder/src/Controller/AddSectionController.php b/core/modules/layout_builder/src/Controller/AddSectionController.php
index d6771082382b..fa522b547224 100644
--- a/core/modules/layout_builder/src/Controller/AddSectionController.php
+++ b/core/modules/layout_builder/src/Controller/AddSectionController.php
@@ -6,6 +6,7 @@
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
+use Drupal\layout_builder\Section;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -63,13 +64,9 @@ public static function create(ContainerInterface $container) {
* The controller response.
*/
public function build(EntityInterface $entity, $delta, $plugin_id) {
- /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
+ /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
$field_list = $entity->layout_builder__layout;
- $field_list->addItem($delta, [
- 'layout' => $plugin_id,
- 'layout_settings' => [],
- 'section' => [],
- ]);
+ $field_list->insertSection($delta, new Section($plugin_id));
$this->layoutTempstoreRepository->set($entity);
diff --git a/core/modules/layout_builder/src/Controller/LayoutBuilderController.php b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php
index a4163a40a821..959ce1d5b414 100644
--- a/core/modules/layout_builder/src/Controller/LayoutBuilderController.php
+++ b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php
@@ -10,8 +10,8 @@
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\layout_builder\LayoutSectionBuilder;
-use Drupal\layout_builder\Field\LayoutSectionItemInterface;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
+use Drupal\layout_builder\Section;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -111,20 +111,20 @@ public function layout(EntityInterface $entity, $is_rebuilding = FALSE) {
$entity_id = $entity->id();
$entity_type_id = $entity->getEntityTypeId();
- /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
+ /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
$field_list = $entity->layout_builder__layout;
// For a new layout override, begin with a single section of one column.
- if (!$is_rebuilding && $field_list->isEmpty()) {
- $field_list->addItem(0, ['layout' => 'layout_onecol']);
+ if (!$is_rebuilding && $field_list->count() === 0) {
+ $field_list->appendSection(new Section('layout_onecol'));
$this->layoutTempstoreRepository->set($entity);
}
$output = [];
$count = 0;
- foreach ($field_list as $item) {
+ foreach ($field_list->getSections() as $section) {
$output[] = $this->buildAddSectionLink($entity_type_id, $entity_id, $count);
- $output[] = $this->buildAdministrativeSection($item, $entity, $count);
+ $output[] = $this->buildAdministrativeSection($section, $entity, $count);
$count++;
}
$output[] = $this->buildAddSectionLink($entity_type_id, $entity_id, $count);
@@ -179,8 +179,8 @@ protected function buildAddSectionLink($entity_type_id, $entity_id, $delta) {
/**
* Builds the render array for the layout section while editing.
*
- * @param \Drupal\layout_builder\Field\LayoutSectionItemInterface $item
- * The layout section item.
+ * @param \Drupal\layout_builder\Section $section
+ * The layout section.
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity.
* @param int $delta
@@ -189,12 +189,12 @@ protected function buildAddSectionLink($entity_type_id, $entity_id, $delta) {
* @return array
* The render array for a given section.
*/
- protected function buildAdministrativeSection(LayoutSectionItemInterface $item, EntityInterface $entity, $delta) {
+ protected function buildAdministrativeSection(Section $section, EntityInterface $entity, $delta) {
$entity_type_id = $entity->getEntityTypeId();
$entity_id = $entity->id();
- $layout = $this->layoutManager->createInstance($item->layout, $item->layout_settings);
- $build = $this->builder->buildSectionFromLayout($layout, $item->section);
+ $layout = $section->getLayout();
+ $build = $section->toRenderArray();
$layout_definition = $layout->getPluginDefinition();
foreach ($layout_definition->getRegions() as $region => $info) {
diff --git a/core/modules/layout_builder/src/Controller/MoveBlockController.php b/core/modules/layout_builder/src/Controller/MoveBlockController.php
index d648416d9e41..7a841a252d77 100644
--- a/core/modules/layout_builder/src/Controller/MoveBlockController.php
+++ b/core/modules/layout_builder/src/Controller/MoveBlockController.php
@@ -69,32 +69,29 @@ public static function create(ContainerInterface $container) {
* An AJAX response.
*/
public function build(EntityInterface $entity, $delta_from, $delta_to, $region_from, $region_to, $block_uuid, $preceding_block_uuid = NULL) {
- /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
- $field = $entity->layout_builder__layout->get($delta_from);
- $section = $field->getSection();
+ /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
+ $field_list = $entity->layout_builder__layout;
+ $section = $field_list->getSection($delta_from);
- $block = $section->getBlock($region_from, $block_uuid);
- $section->removeBlock($region_from, $block_uuid);
+ $component = $section->getComponent($block_uuid);
+ $section->removeComponent($block_uuid);
// If the block is moving from one section to another, update the original
// section and load the new one.
if ($delta_from !== $delta_to) {
- $field->updateFromSection($section);
- $field = $entity->layout_builder__layout->get($delta_to);
- $section = $field->getSection();
+ $section = $field_list->getSection($delta_to);
}
// If a preceding block was specified, insert after that. Otherwise add the
// block to the front.
+ $component->setRegion($region_to);
if (isset($preceding_block_uuid)) {
- $section->insertBlock($region_to, $block_uuid, $block, $preceding_block_uuid);
+ $section->insertAfterComponent($preceding_block_uuid, $component);
}
else {
- $section->addBlock($region_to, $block_uuid, $block);
+ $section->appendComponent($component);
}
- $field->updateFromSection($section);
-
$this->layoutTempstoreRepository->set($entity);
return $this->rebuildLayout($entity);
}
diff --git a/core/modules/layout_builder/src/Field/LayoutSectionItemInterface.php b/core/modules/layout_builder/src/Field/LayoutSectionItemInterface.php
deleted file mode 100644
index 786b6b4b19ac..000000000000
--- a/core/modules/layout_builder/src/Field/LayoutSectionItemInterface.php
+++ /dev/null
@@ -1,40 +0,0 @@
-get($index)) {
- $start = array_slice($this->list, 0, $index);
- $end = array_slice($this->list, $index);
- $item = $this->createItem($index, $value);
+ public function insertSection($delta, Section $section) {
+ if ($this->get($delta)) {
+ /** @var \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem $item */
+ $item = $this->createItem($delta);
+ $item->section = $section;
+
+ $start = array_slice($this->list, 0, $delta);
+ $end = array_slice($this->list, $delta);
$this->list = array_merge($start, [$item], $end);
}
else {
- $item = $this->appendItem($value);
+ $this->appendSection($section);
+ }
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function appendSection(Section $section) {
+ $this->appendItem()->section = $section;
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSections() {
+ $sections = [];
+ /** @var \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem $item */
+ foreach ($this->list as $delta => $item) {
+ $sections[$delta] = $item->section;
}
- return $item;
+ return $sections;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getSection($delta) {
+ /** @var \Drupal\layout_builder\Plugin\Field\FieldType\LayoutSectionItem $item */
+ if (!$item = $this->get($delta)) {
+ throw new \OutOfBoundsException(sprintf('Invalid delta "%s" for the "%s" entity', $delta, $this->getEntity()->label()));
+ }
+
+ return $item->section;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeSection($delta) {
+ $this->removeItem($delta);
+ return $this;
}
}
diff --git a/core/modules/layout_builder/src/Field/LayoutSectionItemListInterface.php b/core/modules/layout_builder/src/Field/LayoutSectionItemListInterface.php
deleted file mode 100644
index 81839d17747b..000000000000
--- a/core/modules/layout_builder/src/Field/LayoutSectionItemListInterface.php
+++ /dev/null
@@ -1,46 +0,0 @@
-addBlock($region, $uuid, $configuration);
+ $section->appendComponent(new SectionComponent($uuid, $region, $configuration));
}
}
diff --git a/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php b/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php
index 7356ef4ccdf1..08ff3174167c 100644
--- a/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php
+++ b/core/modules/layout_builder/src/Form/ConfigureBlockFormBase.php
@@ -246,11 +246,10 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
$configuration = $this->block->getConfiguration();
- /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
- $field = $this->entity->layout_builder__layout->get($this->delta);
- $section = $field->getSection();
- $this->submitBlock($section, $this->region, $configuration['uuid'], ['block' => $configuration]);
- $field->updateFromSection($section);
+ /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
+ $field_list = $this->entity->layout_builder__layout;
+ $section = $field_list->getSection($this->delta);
+ $this->submitBlock($section, $this->region, $configuration['uuid'], $configuration);
$this->layoutTempstoreRepository->set($this->entity);
$form_state->setRedirectUrl($this->entity->toUrl('layout-builder'));
diff --git a/core/modules/layout_builder/src/Form/ConfigureSectionForm.php b/core/modules/layout_builder/src/Form/ConfigureSectionForm.php
index 17913237d51d..6993d218be50 100644
--- a/core/modules/layout_builder/src/Form/ConfigureSectionForm.php
+++ b/core/modules/layout_builder/src/Form/ConfigureSectionForm.php
@@ -14,6 +14,7 @@
use Drupal\Core\Plugin\PluginWithFormsInterface;
use Drupal\layout_builder\Controller\LayoutRebuildTrait;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
+use Drupal\layout_builder\Section;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -121,14 +122,15 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
$this->delta = $delta;
$this->isUpdate = is_null($plugin_id);
- $configuration = [];
if ($this->isUpdate) {
- /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
- $field = $this->entity->layout_builder__layout->get($this->delta);
- $plugin_id = $field->layout;
- $configuration = $field->layout_settings;
+ /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
+ $field_list = $this->entity->layout_builder__layout;
+ $section = $field_list->getSection($this->delta);
}
- $this->layout = $this->layoutManager->createInstance($plugin_id, $configuration);
+ else {
+ $section = new Section($plugin_id);
+ }
+ $this->layout = $section->getLayout();
$form['#tree'] = TRUE;
$form['layout_settings'] = [];
@@ -166,19 +168,13 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
$plugin_id = $this->layout->getPluginId();
$configuration = $this->layout->getConfiguration();
- /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
+ /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
$field_list = $this->entity->layout_builder__layout;
if ($this->isUpdate) {
- $field = $field_list->get($this->delta);
- $field->layout = $plugin_id;
- $field->layout_settings = $configuration;
+ $field_list->getSection($this->delta)->setLayoutSettings($configuration);
}
else {
- $field_list->addItem($this->delta, [
- 'layout' => $plugin_id,
- 'layout_settings' => $configuration,
- 'section' => [],
- ]);
+ $field_list->insertSection($this->delta, new Section($plugin_id, $configuration));
}
$this->layoutTempstoreRepository->set($this->entity);
diff --git a/core/modules/layout_builder/src/Form/RemoveBlockForm.php b/core/modules/layout_builder/src/Form/RemoveBlockForm.php
index 139186af6654..9c7eb2084dc5 100644
--- a/core/modules/layout_builder/src/Form/RemoveBlockForm.php
+++ b/core/modules/layout_builder/src/Form/RemoveBlockForm.php
@@ -60,11 +60,9 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
* {@inheritdoc}
*/
protected function handleEntity(EntityInterface $entity, FormStateInterface $form_state) {
- /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
- $field = $entity->layout_builder__layout->get($this->delta);
- $section = $field->getSection();
- $section->removeBlock($this->region, $this->uuid);
- $field->updateFromSection($section);
+ /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
+ $field_list = $this->entity->layout_builder__layout;
+ $field_list->getSection($this->delta)->removeComponent($this->uuid);
}
}
diff --git a/core/modules/layout_builder/src/Form/UpdateBlockForm.php b/core/modules/layout_builder/src/Form/UpdateBlockForm.php
index 2f2aa600e44c..3cc36585a11a 100644
--- a/core/modules/layout_builder/src/Form/UpdateBlockForm.php
+++ b/core/modules/layout_builder/src/Form/UpdateBlockForm.php
@@ -40,14 +40,11 @@ public function getFormId() {
* The form array.
*/
public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $entity = NULL, $delta = NULL, $region = NULL, $uuid = NULL) {
- /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface $field */
- $field = $entity->layout_builder__layout->get($delta);
- $block = $field->getSection()->getBlock($region, $uuid);
- if (empty($block['block']['id'])) {
- throw new \InvalidArgumentException('Invalid UUID specified');
- }
+ /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
+ $field_list = $entity->layout_builder__layout;
+ $plugin = $field_list->getSection($delta)->getComponent($uuid)->getPlugin();
- return parent::buildForm($form, $form_state, $entity, $delta, $region, $block['block']['id'], $block['block']);
+ return parent::buildForm($form, $form_state, $entity, $delta, $region, $plugin->getPluginId(), $plugin->getConfiguration());
}
/**
@@ -61,7 +58,7 @@ protected function submitLabel() {
* {@inheritdoc}
*/
protected function submitBlock(Section $section, $region, $uuid, array $configuration) {
- $section->updateBlock($region, $uuid, $configuration);
+ $section->getComponent($uuid)->setConfiguration($configuration);
}
}
diff --git a/core/modules/layout_builder/src/LayoutSectionBuilder.php b/core/modules/layout_builder/src/LayoutSectionBuilder.php
index 1682974f1134..525849d9cef3 100644
--- a/core/modules/layout_builder/src/LayoutSectionBuilder.php
+++ b/core/modules/layout_builder/src/LayoutSectionBuilder.php
@@ -2,14 +2,11 @@
namespace Drupal\layout_builder;
-use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Block\BlockManagerInterface;
-use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Layout\LayoutInterface;
use Drupal\Core\Layout\LayoutPluginManagerInterface;
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
-use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
@@ -17,6 +14,8 @@
* Builds the UI for layout sections.
*
* @internal
+ *
+ * @todo Remove in https://www.drupal.org/project/drupal/issues/2928450.
*/
class LayoutSectionBuilder {
@@ -84,37 +83,21 @@ public function __construct(AccountInterface $account, LayoutPluginManagerInterf
*
* @param \Drupal\Core\Layout\LayoutInterface $layout
* The ID of the layout.
- * @param array $section
- * An array of configuration, keyed first by region and then by block UUID.
+ * @param \Drupal\layout_builder\SectionComponent[] $components
+ * An array of components.
*
* @return array
* The render array for a given section.
*/
- public function buildSectionFromLayout(LayoutInterface $layout, array $section) {
- $cacheability = CacheableMetadata::createFromRenderArray([]);
-
+ public function buildSectionFromLayout(LayoutInterface $layout, array $components) {
$regions = [];
- $weight = 0;
- foreach ($section as $region => $blocks) {
- if (!is_array($blocks)) {
- throw new \InvalidArgumentException(sprintf('The "%s" region in the "%s" layout has invalid configuration', $region, $layout->getPluginId()));
- }
-
- foreach ($blocks as $uuid => $configuration) {
- if (!is_array($configuration) || !isset($configuration['block'])) {
- throw new \InvalidArgumentException(sprintf('The block with UUID of "%s" has invalid configuration', $uuid));
- }
-
- if ($block_output = $this->buildBlock($uuid, $configuration['block'], $cacheability)) {
- $block_output['#weight'] = $weight++;
- $regions[$region][$uuid] = $block_output;
- }
+ foreach ($components as $component) {
+ if ($output = $component->toRenderArray()) {
+ $regions[$component->getRegion()][$component->getUuid()] = $output;
}
}
- $result = $layout->build($regions);
- $cacheability->applyTo($result);
- return $result;
+ return $layout->build($regions);
}
/**
@@ -124,78 +107,15 @@ public function buildSectionFromLayout(LayoutInterface $layout, array $section)
* The ID of the layout.
* @param array $layout_settings
* The configuration for the layout.
- * @param array $section
- * An array of configuration, keyed first by region and then by block UUID.
+ * @param \Drupal\layout_builder\SectionComponent[] $components
+ * An array of components.
*
* @return array
* The render array for a given section.
*/
- public function buildSection($layout_id, array $layout_settings, array $section) {
+ public function buildSection($layout_id, array $layout_settings, array $components) {
$layout = $this->layoutPluginManager->createInstance($layout_id, $layout_settings);
- return $this->buildSectionFromLayout($layout, $section);
- }
-
- /**
- * Builds the render array for a given block.
- *
- * @param string $uuid
- * The UUID of this block instance.
- * @param array $configuration
- * An array of configuration relevant to the block instance. Must contain
- * the plugin ID with the key 'id'.
- * @param \Drupal\Core\Cache\CacheableMetadata $cacheability
- * The cacheability metadata.
- *
- * @return array|null
- * The render array representing this block, if accessible. NULL otherwise.
- */
- protected function buildBlock($uuid, array $configuration, CacheableMetadata $cacheability) {
- $block = $this->getBlock($uuid, $configuration);
-
- $access = $block->access($this->account, TRUE);
- $cacheability->addCacheableDependency($access);
-
- $block_output = NULL;
- if ($access->isAllowed()) {
- $block_output = [
- '#theme' => 'block',
- '#configuration' => $block->getConfiguration(),
- '#plugin_id' => $block->getPluginId(),
- '#base_plugin_id' => $block->getBaseId(),
- '#derivative_plugin_id' => $block->getDerivativeId(),
- 'content' => $block->build(),
- ];
- $cacheability->addCacheableDependency($block);
- }
- return $block_output;
- }
-
- /**
- * Gets a block instance.
- *
- * @param string $uuid
- * The UUID of this block instance.
- * @param array $configuration
- * An array of configuration relevant to the block instance. Must contain
- * the plugin ID with the key 'id'.
- *
- * @return \Drupal\Core\Block\BlockPluginInterface
- * The block instance.
- *
- * @throws \Drupal\Component\Plugin\Exception\PluginException
- * Thrown when the configuration parameter does not contain 'id'.
- */
- protected function getBlock($uuid, array $configuration) {
- if (!isset($configuration['id'])) {
- throw new PluginException(sprintf('No plugin ID specified for block with "%s" UUID', $uuid));
- }
-
- $block = $this->blockManager->createInstance($configuration['id'], $configuration);
- if ($block instanceof ContextAwarePluginInterface) {
- $contexts = $this->contextRepository->getRuntimeContexts(array_values($block->getContextMapping()));
- $this->contextHandler->applyContextMapping($block, $contexts);
- }
- return $block;
+ return $this->buildSectionFromLayout($layout, $components);
}
}
diff --git a/core/modules/layout_builder/src/Plugin/DataType/SectionData.php b/core/modules/layout_builder/src/Plugin/DataType/SectionData.php
new file mode 100644
index 000000000000..353c53fe7cca
--- /dev/null
+++ b/core/modules/layout_builder/src/Plugin/DataType/SectionData.php
@@ -0,0 +1,36 @@
+getName()));
+ }
+ parent::setValue($value, $notify);
+ }
+
+}
diff --git a/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php b/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php
index 4951d01c4b9b..c321585fdf55 100644
--- a/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php
+++ b/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php
@@ -78,9 +78,9 @@ public static function create(ContainerInterface $container, array $configuratio
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
- /** @var \Drupal\layout_builder\Field\LayoutSectionItemInterface[] $items */
- foreach ($items as $delta => $item) {
- $elements[$delta] = $this->builder->buildSection($item->layout, $item->layout_settings, $item->section);
+ /** @var \Drupal\layout_builder\SectionStorageInterface $items */
+ foreach ($items->getSections() as $delta => $section) {
+ $elements[$delta] = $section->toRenderArray();
}
return $elements;
diff --git a/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
index fc1c63413fa1..2001d1b5ff19 100644
--- a/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
+++ b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
@@ -7,8 +7,6 @@
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\TypedData\DataDefinition;
-use Drupal\Core\TypedData\MapDataDefinition;
-use Drupal\layout_builder\Field\LayoutSectionItemInterface;
use Drupal\layout_builder\Section;
/**
@@ -25,22 +23,16 @@
* no_ui = TRUE,
* cardinality = \Drupal\Core\Field\FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
* )
+ *
+ * @property \Drupal\layout_builder\Section section
*/
-class LayoutSectionItem extends FieldItemBase implements LayoutSectionItemInterface {
+class LayoutSectionItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
- // Prevent early t() calls by using the TranslatableMarkup.
- $properties['layout'] = DataDefinition::create('string')
- ->setLabel(new TranslatableMarkup('Layout'))
- ->setSetting('case_sensitive', FALSE)
- ->setRequired(TRUE);
- $properties['layout_settings'] = MapDataDefinition::create('map')
- ->setLabel(new TranslatableMarkup('Layout Settings'))
- ->setRequired(FALSE);
- $properties['section'] = MapDataDefinition::create('map')
+ $properties['section'] = DataDefinition::create('layout_section')
->setLabel(new TranslatableMarkup('Layout Section'))
->setRequired(FALSE);
@@ -73,17 +65,6 @@ public static function mainPropertyName() {
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = [
'columns' => [
- 'layout' => [
- 'type' => 'varchar',
- 'length' => '255',
- 'binary' => FALSE,
- ],
- 'layout_settings' => [
- 'type' => 'blob',
- 'size' => 'normal',
- // @todo Address in https://www.drupal.org/node/2914503.
- 'serialize' => TRUE,
- ],
'section' => [
'type' => 'blob',
'size' => 'normal',
@@ -100,10 +81,8 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
- $values['layout'] = 'layout_onecol';
- $values['layout_settings'] = [];
// @todo Expand this in https://www.drupal.org/node/2912331.
- $values['section'] = [];
+ $values['section'] = new Section('layout_onecol');
return $values;
}
@@ -111,22 +90,7 @@ public static function generateSampleValue(FieldDefinitionInterface $field_defin
* {@inheritdoc}
*/
public function isEmpty() {
- return empty($this->layout);
- }
-
- /**
- * {@inheritdoc}
- */
- public function getSection() {
- return new Section($this->section);
- }
-
- /**
- * {@inheritdoc}
- */
- public function updateFromSection(Section $section) {
- $this->section = $section->getValue();
- return $this;
+ return empty($this->section);
}
}
diff --git a/core/modules/layout_builder/src/Section.php b/core/modules/layout_builder/src/Section.php
index f5e19003b58a..55204ddc9eda 100644
--- a/core/modules/layout_builder/src/Section.php
+++ b/core/modules/layout_builder/src/Section.php
@@ -5,158 +5,303 @@
/**
* Provides a domain object for layout sections.
*
- * A section is a multi-dimensional array, keyed first by region machine name,
- * then by block UUID, containing block configuration values.
+ * A section consists of three parts:
+ * - The layout plugin ID for the layout applied to the section (for example,
+ * 'layout_onecol').
+ * - An array of settings for the layout plugin.
+ * - An array of components that can be rendered in the section.
+ *
+ * @internal
+ * Layout Builder is currently experimental and should only be leveraged by
+ * experimental modules and development releases of contributed modules.
+ * See https://www.drupal.org/core/experimental for more information.
+ *
+ * @see \Drupal\Core\Layout\LayoutDefinition
+ * @see \Drupal\layout_builder\SectionComponent
+ *
+ * @todo Determine whether an interface will be provided for this in
+ * https://www.drupal.org/project/drupal/issues/2930334.
*/
class Section {
/**
- * The section data.
+ * The layout plugin ID.
+ *
+ * @var string
+ */
+ protected $layoutId;
+
+ /**
+ * The layout plugin settings.
*
* @var array
*/
- protected $section;
+ protected $layoutSettings = [];
+
+ /**
+ * An array of components, keyed by UUID.
+ *
+ * @var \Drupal\layout_builder\SectionComponent[]
+ */
+ protected $components = [];
/**
* Constructs a new Section.
*
- * @param array $section
- * The section data.
+ * @param string $layout_id
+ * The layout plugin ID.
+ * @param array $layout_settings
+ * (optional) The layout plugin settings.
+ * @param \Drupal\layout_builder\SectionComponent[] $components
+ * (optional) The components.
*/
- public function __construct(array $section) {
- $this->section = $section;
+ public function __construct($layout_id, array $layout_settings = [], array $components = []) {
+ $this->layoutId = $layout_id;
+ $this->layoutSettings = $layout_settings;
+ foreach ($components as $component) {
+ $this->setComponent($component);
+ }
}
/**
- * Returns the value of the section.
+ * Returns the renderable array for this section.
*
* @return array
- * The section data.
+ * A renderable array representing the content of the section.
*/
- public function getValue() {
- return $this->section;
+ public function toRenderArray() {
+ $regions = [];
+ foreach ($this->getComponents() as $component) {
+ if ($output = $component->toRenderArray()) {
+ $regions[$component->getRegion()][$component->getUuid()] = $output;
+ }
+ }
+
+ return $this->getLayout()->build($regions);
}
/**
- * Gets the configuration of a given block from a region.
+ * Gets the layout plugin for this section.
*
- * @param string $region
- * The region name.
- * @param string $uuid
- * The UUID of the block to retrieve.
+ * @return \Drupal\Core\Layout\LayoutInterface
+ * The layout plugin.
+ */
+ public function getLayout() {
+ return $this->layoutPluginManager()->createInstance($this->getLayoutId(), $this->getLayoutSettings());
+ }
+
+ /**
+ * Gets the layout plugin ID for this section.
*
- * @return array
- * The block configuration.
+ * @return string
+ * The layout plugin ID.
*
- * @throws \InvalidArgumentException
- * Thrown when the expected region or UUID do not exist.
+ * @internal
+ * This method should only be used by code responsible for storing the data.
*/
- public function getBlock($region, $uuid) {
- if (!isset($this->section[$region])) {
- throw new \InvalidArgumentException('Invalid region');
- }
+ public function getLayoutId() {
+ return $this->layoutId;
+ }
- if (!isset($this->section[$region][$uuid])) {
- throw new \InvalidArgumentException('Invalid UUID');
- }
+ /**
+ * Gets the layout plugin settings for this section.
+ *
+ * @return mixed[]
+ * The layout plugin settings.
+ *
+ * @internal
+ * This method should only be used by code responsible for storing the data.
+ */
+ public function getLayoutSettings() {
+ return $this->layoutSettings;
+ }
+
+ /**
+ * Sets the layout plugin settings for this section.
+ *
+ * @param mixed[] $layout_settings
+ * The layout plugin settings.
+ *
+ * @return $this
+ */
+ public function setLayoutSettings(array $layout_settings) {
+ $this->layoutSettings = $layout_settings;
+ return $this;
+ }
- return $this->section[$region][$uuid];
+ /**
+ * Returns the components of the section.
+ *
+ * @return \Drupal\layout_builder\SectionComponent[]
+ * The components.
+ */
+ public function getComponents() {
+ return $this->components;
}
/**
- * Updates the configuration of a given block from a region.
+ * Gets the component for a given UUID.
*
- * @param string $region
- * The region name.
* @param string $uuid
- * The UUID of the block to retrieve.
- * @param array $configuration
- * The block configuration.
+ * The UUID of the component to retrieve.
*
- * @return $this
+ * @return \Drupal\layout_builder\SectionComponent
+ * The component.
*
* @throws \InvalidArgumentException
- * Thrown when the expected region or UUID do not exist.
+ * Thrown when the expected UUID does not exist.
*/
- public function updateBlock($region, $uuid, array $configuration) {
- if (!isset($this->section[$region])) {
- throw new \InvalidArgumentException('Invalid region');
+ public function getComponent($uuid) {
+ if (!isset($this->components[$uuid])) {
+ throw new \InvalidArgumentException(sprintf('Invalid UUID "%s"', $uuid));
}
- if (!isset($this->section[$region][$uuid])) {
- throw new \InvalidArgumentException('Invalid UUID');
- }
-
- $this->section[$region][$uuid] = $configuration;
+ return $this->components[$uuid];
+ }
+ /**
+ * Helper method to set a component.
+ *
+ * @param \Drupal\layout_builder\SectionComponent $component
+ * The component.
+ *
+ * @return $this
+ */
+ protected function setComponent(SectionComponent $component) {
+ $this->components[$component->getUuid()] = $component;
return $this;
}
/**
- * Removes a given block from a region.
+ * Removes a given component from a region.
*
- * @param string $region
- * The region name.
* @param string $uuid
- * The UUID of the block to remove.
+ * The UUID of the component to remove.
*
* @return $this
*/
- public function removeBlock($region, $uuid) {
- unset($this->section[$region][$uuid]);
- $this->section = array_filter($this->section);
+ public function removeComponent($uuid) {
+ unset($this->components[$uuid]);
return $this;
}
/**
- * Adds a block to the front of a region.
+ * Appends a component to the end of a region.
*
- * @param string $region
- * The region name.
- * @param string $uuid
- * The UUID of the block to add.
- * @param array $configuration
- * The block configuration.
+ * @param \Drupal\layout_builder\SectionComponent $component
+ * The component being appended.
*
* @return $this
*/
- public function addBlock($region, $uuid, array $configuration) {
- $this->section += [$region => []];
- $this->section[$region] = array_merge([$uuid => $configuration], $this->section[$region]);
+ public function appendComponent(SectionComponent $component) {
+ $component->setWeight($this->getNextHighestWeight($component->getRegion()));
+ $this->setComponent($component);
return $this;
}
/**
- * Inserts a block after a specified existing block in a region.
+ * Returns the next highest weight of the component in a region.
*
* @param string $region
* The region name.
- * @param string $uuid
- * The UUID of the block to insert.
- * @param array $configuration
- * The block configuration.
+ *
+ * @return int
+ * A number higher than the highest weight of the component in the region.
+ */
+ protected function getNextHighestWeight($region) {
+ $components = $this->getComponentsByRegion($region);
+ $weights = array_map(function (SectionComponent $component) {
+ return $component->getWeight();
+ }, $components);
+ return $weights ? max($weights) + 1 : 0;
+ }
+
+ /**
+ * Gets the components for a specific region.
+ *
+ * @param string $region
+ * The region name.
+ *
+ * @return \Drupal\layout_builder\SectionComponent[]
+ * An array of components in the specified region, sorted by weight.
+ */
+ protected function getComponentsByRegion($region) {
+ $components = array_filter($this->getComponents(), function (SectionComponent $component) use ($region) {
+ return $component->getRegion() === $region;
+ });
+ uasort($components, function (SectionComponent $a, SectionComponent $b) {
+ return $a->getWeight() > $b->getWeight() ? 1 : -1;
+ });
+ return $components;
+ }
+
+ /**
+ * Inserts a component after a specified existing component.
+ *
* @param string $preceding_uuid
- * The UUID of the existing block to insert after.
+ * The UUID of the existing component to insert after.
+ * @param \Drupal\layout_builder\SectionComponent $component
+ * The component being inserted.
*
* @return $this
*
* @throws \InvalidArgumentException
- * Thrown when the expected region does not exist.
+ * Thrown when the expected UUID does not exist.
+ */
+ public function insertAfterComponent($preceding_uuid, SectionComponent $component) {
+ // Find the delta of the specified UUID.
+ $uuids = array_keys($this->getComponentsByRegion($component->getRegion()));
+ $delta = array_search($preceding_uuid, $uuids, TRUE);
+ if ($delta === FALSE) {
+ throw new \InvalidArgumentException(sprintf('Invalid preceding UUID "%s"', $preceding_uuid));
+ }
+ return $this->insertComponent($delta + 1, $component);
+ }
+
+ /**
+ * Inserts a component at a specified delta.
+ *
+ * @param int $delta
+ * The zero-based delta in which to insert the component.
+ * @param \Drupal\layout_builder\SectionComponent $new_component
+ * The component being inserted.
+ *
+ * @return $this
+ *
+ * @throws \OutOfBoundsException
+ * Thrown when the specified delta is invalid.
*/
- public function insertBlock($region, $uuid, array $configuration, $preceding_uuid) {
- if (!isset($this->section[$region])) {
- throw new \InvalidArgumentException('Invalid region');
+ public function insertComponent($delta, SectionComponent $new_component) {
+ $components = $this->getComponentsByRegion($new_component->getRegion());
+ $count = count($components);
+ if ($delta > $count) {
+ throw new \OutOfBoundsException(sprintf('Invalid delta "%s" for the "%s" component', $delta, $new_component->getUuid()));
}
- $slice_id = array_search($preceding_uuid, array_keys($this->section[$region]));
- if ($slice_id === FALSE) {
- throw new \InvalidArgumentException('Invalid preceding UUID');
+ // If the delta is the end of the list, append the component instead.
+ if ($delta === $count) {
+ return $this->appendComponent($new_component);
}
- $before = array_slice($this->section[$region], 0, $slice_id + 1);
- $after = array_slice($this->section[$region], $slice_id + 1);
- $this->section[$region] = array_merge($before, [$uuid => $configuration], $after);
+ // Find the weight of the component that exists at the specified delta.
+ $weight = array_values($components)[$delta]->getWeight();
+ $this->setComponent($new_component->setWeight($weight++));
+
+ // Increase the weight of every subsequent component.
+ foreach (array_slice($components, $delta) as $component) {
+ $component->setWeight($weight++);
+ }
return $this;
}
+ /**
+ * Wraps the layout plugin manager.
+ *
+ * @return \Drupal\Core\Layout\LayoutPluginManagerInterface
+ * The layout plugin manager.
+ */
+ protected function layoutPluginManager() {
+ return \Drupal::service('plugin.manager.core.layout');
+ }
+
}
diff --git a/core/modules/layout_builder/src/SectionComponent.php b/core/modules/layout_builder/src/SectionComponent.php
new file mode 100644
index 000000000000..caad0a2b61db
--- /dev/null
+++ b/core/modules/layout_builder/src/SectionComponent.php
@@ -0,0 +1,314 @@
+uuid = $uuid;
+ $this->region = $region;
+ $this->configuration = $configuration;
+ $this->additional = $additional;
+ }
+
+ /**
+ * Returns the renderable array for this component.
+ *
+ * @return array
+ * A renderable array representing the content of the component.
+ */
+ public function toRenderArray() {
+ $output = [];
+
+ $plugin = $this->getPlugin();
+ // @todo Figure out the best way to unify fields and blocks and components
+ // in https://www.drupal.org/node/1875974.
+ if ($plugin instanceof BlockPluginInterface) {
+ $access = $plugin->access($this->currentUser(), TRUE);
+ $cacheability = CacheableMetadata::createFromObject($access);
+
+ if ($access->isAllowed()) {
+ $cacheability->addCacheableDependency($plugin);
+ // @todo Move this to BlockBase in https://www.drupal.org/node/2931040.
+ $output = [
+ '#theme' => 'block',
+ '#configuration' => $plugin->getConfiguration(),
+ '#plugin_id' => $plugin->getPluginId(),
+ '#base_plugin_id' => $plugin->getBaseId(),
+ '#derivative_plugin_id' => $plugin->getDerivativeId(),
+ '#weight' => $this->getWeight(),
+ 'content' => $plugin->build(),
+ ];
+ }
+ $cacheability->applyTo($output);
+ }
+ return $output;
+ }
+
+ /**
+ * Gets any arbitrary property for the component.
+ *
+ * @param string $property
+ * The property to retrieve.
+ *
+ * @return mixed
+ * The value for that property, or NULL if the property does not exist.
+ */
+ public function get($property) {
+ if (property_exists($this, $property)) {
+ $value = isset($this->{$property}) ? $this->{$property} : NULL;
+ }
+ else {
+ $value = isset($this->additional[$property]) ? $this->additional[$property] : NULL;
+ }
+ return $value;
+ }
+
+ /**
+ * Sets a value to an arbitrary property for the component.
+ *
+ * @param string $property
+ * The property to use for the value.
+ * @param mixed $value
+ * The value to set.
+ *
+ * @return $this
+ */
+ public function set($property, $value) {
+ if (property_exists($this, $property)) {
+ $this->{$property} = $value;
+ }
+ else {
+ $this->additional[$property] = $value;
+ }
+ return $this;
+ }
+
+ /**
+ * Gets the region for the component.
+ *
+ * @return string
+ * The region.
+ */
+ public function getRegion() {
+ return $this->region;
+ }
+
+ /**
+ * Sets the region for the component.
+ *
+ * @param string $region
+ * The region.
+ *
+ * @return $this
+ */
+ public function setRegion($region) {
+ $this->region = $region;
+ return $this;
+ }
+
+ /**
+ * Gets the weight of the component.
+ *
+ * @return int
+ * The zero-based weight of the component.
+ *
+ * @throws \UnexpectedValueException
+ * Thrown if the weight was never set.
+ */
+ public function getWeight() {
+ return $this->weight;
+ }
+
+ /**
+ * Sets the weight of the component.
+ *
+ * @param int $weight
+ * The zero-based weight of the component.
+ *
+ * @return $this
+ */
+ public function setWeight($weight) {
+ $this->weight = $weight;
+ return $this;
+ }
+
+ /**
+ * Gets the component plugin configuration.
+ *
+ * @return mixed[]
+ * The component plugin configuration.
+ */
+ protected function getConfiguration() {
+ return $this->configuration;
+ }
+
+ /**
+ * Sets the plugin configuration.
+ *
+ * @param mixed[] $configuration
+ * The plugin configuration.
+ *
+ * @return $this
+ */
+ public function setConfiguration(array $configuration) {
+ $this->configuration = $configuration;
+ return $this;
+ }
+
+ /**
+ * Gets the plugin ID.
+ *
+ * @return string
+ * The plugin ID.
+ *
+ * @throws \Drupal\Component\Plugin\Exception\PluginException
+ * Thrown if the plugin ID cannot be found.
+ */
+ protected function getPluginId() {
+ if (empty($this->configuration['id'])) {
+ throw new PluginException(sprintf('No plugin ID specified for component with "%s" UUID', $this->uuid));
+ }
+ return $this->configuration['id'];
+ }
+
+ /**
+ * Gets the UUID for this component.
+ *
+ * @return string
+ * The UUID.
+ */
+ public function getUuid() {
+ return $this->uuid;
+ }
+
+ /**
+ * Gets the plugin for this component.
+ *
+ * @return \Drupal\Component\Plugin\PluginInspectionInterface
+ * The plugin.
+ */
+ public function getPlugin() {
+ $plugin = $this->pluginManager()->createInstance($this->getPluginId(), $this->getConfiguration());
+ if ($plugin instanceof ContextAwarePluginInterface) {
+ $contexts = $this->contextRepository()->getRuntimeContexts(array_values($plugin->getContextMapping()));
+ $this->contextHandler()->applyContextMapping($plugin, $contexts);
+ }
+ return $plugin;
+ }
+
+ /**
+ * Wraps the component plugin manager.
+ *
+ * @return \Drupal\Core\Block\BlockManagerInterface
+ * The plugin manager.
+ */
+ protected function pluginManager() {
+ return \Drupal::service('plugin.manager.block');
+ }
+
+ /**
+ * Wraps the context repository.
+ *
+ * @return \Drupal\Core\Plugin\Context\ContextRepositoryInterface
+ * The context repository.
+ */
+ protected function contextRepository() {
+ return \Drupal::service('context.repository');
+ }
+
+ /**
+ * Wraps the context handler.
+ *
+ * @return \Drupal\Core\Plugin\Context\ContextHandlerInterface
+ * The context handler.
+ */
+ protected function contextHandler() {
+ return \Drupal::service('context.handler');
+ }
+
+ /**
+ * Wraps the current user.
+ *
+ * @return \Drupal\Core\Session\AccountInterface
+ * The current user.
+ */
+ protected function currentUser() {
+ return \Drupal::currentUser();
+ }
+
+}
diff --git a/core/modules/layout_builder/src/SectionStorageInterface.php b/core/modules/layout_builder/src/SectionStorageInterface.php
new file mode 100644
index 000000000000..c4da487eba6b
--- /dev/null
+++ b/core/modules/layout_builder/src/SectionStorageInterface.php
@@ -0,0 +1,71 @@
+ 'layout_onecol',
- 'section' => [
- 'content' => [
- 'baz' => [
- 'block' => [
- 'id' => 'test_context_aware',
- 'context_mapping' => [
- 'user' => '@user.current_user_context:current_user',
- ],
- ],
+ 'section' => new Section('layout_onecol', [], [
+ 'baz' => new SectionComponent('baz', 'content', [
+ 'id' => 'test_context_aware',
+ 'context_mapping' => [
+ 'user' => '@user.current_user_context:current_user',
],
- ],
- ],
+ ]),
+ ]),
],
],
[
@@ -86,16 +83,11 @@ public function providerTestLayoutSectionFormatter() {
$data['single_section_single_block'] = [
[
[
- 'layout' => 'layout_onecol',
- 'section' => [
- 'content' => [
- 'baz' => [
- 'block' => [
- 'id' => 'system_powered_by_block',
- ],
- ],
- ],
- ],
+ 'section' => new Section('layout_onecol', [], [
+ 'baz' => new SectionComponent('baz', 'content', [
+ 'id' => 'system_powered_by_block',
+ ]),
+ ]),
],
],
'.layout--onecol',
@@ -107,37 +99,23 @@ public function providerTestLayoutSectionFormatter() {
$data['multiple_sections'] = [
[
[
- 'layout' => 'layout_onecol',
- 'section' => [
- 'content' => [
- 'baz' => [
- 'block' => [
- 'id' => 'system_powered_by_block',
- ],
- ],
- ],
- ],
+ 'section' => new Section('layout_onecol', [], [
+ 'baz' => new SectionComponent('baz', 'content', [
+ 'id' => 'system_powered_by_block',
+ ]),
+ ]),
],
[
- 'layout' => 'layout_twocol',
- 'section' => [
- 'first' => [
- 'foo' => [
- 'block' => [
- 'id' => 'test_block_instantiation',
- 'display_message' => 'foo text',
- ],
- ],
- ],
- 'second' => [
- 'bar' => [
- 'block' => [
- 'id' => 'test_block_instantiation',
- 'display_message' => 'bar text',
- ],
- ],
- ],
- ],
+ 'section' => new Section('layout_twocol', [], [
+ 'foo' => new SectionComponent('foo', 'first', [
+ 'id' => 'test_block_instantiation',
+ 'display_message' => 'foo text',
+ ]),
+ 'bar' => new SectionComponent('bar', 'second', [
+ 'id' => 'test_block_instantiation',
+ 'display_message' => 'bar text',
+ ]),
+ ]),
],
],
[
@@ -177,16 +155,11 @@ public function testLayoutSectionFormatter($layout_data, $expected_selector, $ex
public function testLayoutSectionFormatterAccess() {
$node = $this->createSectionNode([
[
- 'layout' => 'layout_onecol',
- 'section' => [
- 'content' => [
- 'baz' => [
- 'block' => [
- 'id' => 'test_access',
- ],
- ],
- ],
- ],
+ 'section' => new Section('layout_onecol', [], [
+ 'baz' => new SectionComponent('baz', 'content', [
+ 'id' => 'test_access',
+ ]),
+ ]),
],
]);
@@ -216,41 +189,27 @@ public function testMultilingualLayoutSectionFormatter() {
$entity = $this->createSectionNode([
[
- 'layout' => 'layout_onecol',
- 'section' => [
- 'content' => [
- 'baz' => [
- 'block' => [
- 'id' => 'system_powered_by_block',
- ],
- ],
- ],
- ],
+ 'section' => new Section('layout_onecol', [], [
+ 'baz' => new SectionComponent('baz', 'content', [
+ 'id' => 'system_powered_by_block',
+ ]),
+ ]),
],
]);
$entity->addTranslation('es', [
'title' => 'Translated node title',
$this->fieldName => [
[
- 'layout' => 'layout_twocol',
- 'section' => [
- 'first' => [
- 'foo' => [
- 'block' => [
- 'id' => 'test_block_instantiation',
- 'display_message' => 'foo text',
- ],
- ],
- ],
- 'second' => [
- 'bar' => [
- 'block' => [
- 'id' => 'test_block_instantiation',
- 'display_message' => 'bar text',
- ],
- ],
- ],
- ],
+ 'section' => new Section('layout_twocol', [], [
+ 'foo' => new SectionComponent('foo', 'first', [
+ 'id' => 'test_block_instantiation',
+ 'display_message' => 'foo text',
+ ]),
+ 'bar' => new SectionComponent('bar', 'second', [
+ 'id' => 'test_block_instantiation',
+ 'display_message' => 'bar text',
+ ]),
+ ]),
],
],
]);
diff --git a/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php b/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php
index c3a23c78710f..c1b340dea411 100644
--- a/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php
+++ b/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php
@@ -8,6 +8,7 @@
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\KernelTests\KernelTestBase;
+use Drupal\layout_builder\Section;
/**
* Ensures that Layout Builder and Field Layout are compatible with each other.
@@ -108,13 +109,9 @@ public function testCompatibility() {
$this->assertSame($original_markup, $new_markup);
// Add a layout override.
- /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
+ /** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
$field_list = $entity->layout_builder__layout;
- $field_list->appendItem([
- 'layout' => 'layout_onecol',
- 'layout_settings' => [],
- 'section' => [],
- ]);
+ $field_list->appendSection(new Section('layout_onecol'));
$entity->save();
// The rendered entity has now changed. The non-configurable field is shown
diff --git a/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemListTest.php b/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemListTest.php
new file mode 100644
index 000000000000..4c8ae2e6c9a8
--- /dev/null
+++ b/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemListTest.php
@@ -0,0 +1,39 @@
+installEntitySchema('entity_test_base_field_display');
+ layout_builder_add_layout_section_field('entity_test_base_field_display', 'entity_test_base_field_display');
+
+ $entity = EntityTestBaseFieldDisplay::create([
+ 'name' => 'The test entity',
+ 'layout_builder__layout' => $section_data,
+ ]);
+ $entity->save();
+ return $entity->get('layout_builder__layout');
+ }
+
+}
diff --git a/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemTest.php b/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemTest.php
deleted file mode 100644
index 0e13471c1558..000000000000
--- a/core/modules/layout_builder/tests/src/Kernel/LayoutSectionItemTest.php
+++ /dev/null
@@ -1,89 +0,0 @@
-layout_builder__layout;
-
- // Test sample item generation.
- $field_list->generateSampleItems();
- $this->entityValidateAndSave($entity);
-
- $field = $field_list->get(0);
- $this->assertInstanceOf(LayoutSectionItemInterface::class, $field);
- $this->assertInstanceOf(FieldItemInterface::class, $field);
- $this->assertSame('section', $field->mainPropertyName());
- $this->assertSame('layout_onecol', $field->layout);
- $this->assertSame([], $field->layout_settings);
- $this->assertSame([], $field->section);
- }
-
- /**
- * {@inheritdoc}
- */
- public function testLayoutSectionItemList() {
- layout_builder_add_layout_section_field('entity_test', 'entity_test');
-
- $entity = EntityTest::create();
- /** @var \Drupal\layout_builder\Field\LayoutSectionItemListInterface $field_list */
- $field_list = $entity->layout_builder__layout;
- $this->assertInstanceOf(LayoutSectionItemListInterface::class, $field_list);
- $this->assertInstanceOf(FieldItemListInterface::class, $field_list);
- $entity->save();
-
- $field_list->appendItem(['layout' => 'layout_twocol']);
- $field_list->appendItem(['layout' => 'layout_onecol']);
- $field_list->appendItem(['layout' => 'layout_threecol_25_50_25']);
- $this->assertSame([
- ['layout' => 'layout_twocol'],
- ['layout' => 'layout_onecol'],
- ['layout' => 'layout_threecol_25_50_25'],
- ], $field_list->getValue());
-
- $field_list->addItem(1, ['layout' => 'layout_threecol_33_34_33']);
- $this->assertSame([
- ['layout' => 'layout_twocol'],
- ['layout' => 'layout_threecol_33_34_33'],
- ['layout' => 'layout_onecol'],
- ['layout' => 'layout_threecol_25_50_25'],
- ], $field_list->getValue());
-
- $field_list->addItem($field_list->count(), ['layout' => 'layout_twocol_bricks']);
- $this->assertSame([
- ['layout' => 'layout_twocol'],
- ['layout' => 'layout_threecol_33_34_33'],
- ['layout' => 'layout_onecol'],
- ['layout' => 'layout_threecol_25_50_25'],
- ['layout' => 'layout_twocol_bricks'],
- ], $field_list->getValue());
- }
-
-}
diff --git a/core/modules/layout_builder/tests/src/Kernel/SectionStorageTestBase.php b/core/modules/layout_builder/tests/src/Kernel/SectionStorageTestBase.php
new file mode 100644
index 000000000000..b8e8b3462c35
--- /dev/null
+++ b/core/modules/layout_builder/tests/src/Kernel/SectionStorageTestBase.php
@@ -0,0 +1,156 @@
+ new Section('layout_test_plugin', [], [
+ 'first-uuid' => new SectionComponent('first-uuid', 'content'),
+ ]),
+ ],
+ [
+ 'section' => new Section('layout_test_plugin', ['setting_1' => 'bar'], [
+ 'second-uuid' => new SectionComponent('second-uuid', 'content'),
+ ]),
+ ],
+ ];
+ $this->sectionStorage = $this->getEntity($section_data);
+ }
+
+ /**
+ * Sets up the section storage entity.
+ *
+ * @param array $section_data
+ * An array of section data.
+ *
+ * @return \Drupal\Core\Entity\EntityInterface
+ * The entity.
+ */
+ abstract protected function getEntity(array $section_data);
+
+ /**
+ * @covers ::getSections
+ */
+ public function testGetSections() {
+ $expected = [
+ new Section('layout_test_plugin', [], [
+ 'first-uuid' => new SectionComponent('first-uuid', 'content'),
+ ]),
+ new Section('layout_test_plugin', ['setting_1' => 'bar'], [
+ 'second-uuid' => new SectionComponent('second-uuid', 'content'),
+ ]),
+ ];
+ $this->assertSections($expected);
+ }
+
+ /**
+ * @covers ::getSection
+ */
+ public function testGetSection() {
+ $this->assertInstanceOf(Section::class, $this->sectionStorage->getSection(0));
+ }
+
+ /**
+ * @covers ::getSection
+ */
+ public function testGetSectionInvalidDelta() {
+ $this->setExpectedException(\OutOfBoundsException::class, 'Invalid delta "2" for the "The test entity"');
+ $this->sectionStorage->getSection(2);
+ }
+
+ /**
+ * @covers ::insertSection
+ */
+ public function testInsertSection() {
+ $expected = [
+ new Section('layout_test_plugin', [], [
+ 'first-uuid' => new SectionComponent('first-uuid', 'content'),
+ ]),
+ new Section('setting_1'),
+ new Section('layout_test_plugin', ['setting_1' => 'bar'], [
+ 'second-uuid' => new SectionComponent('second-uuid', 'content'),
+ ]),
+ ];
+
+ $this->sectionStorage->insertSection(1, new Section('setting_1'));
+ $this->assertSections($expected);
+ }
+
+ /**
+ * @covers ::appendSection
+ */
+ public function testAppendSection() {
+ $expected = [
+ new Section('layout_test_plugin', [], [
+ 'first-uuid' => new SectionComponent('first-uuid', 'content'),
+ ]),
+ new Section('layout_test_plugin', ['setting_1' => 'bar'], [
+ 'second-uuid' => new SectionComponent('second-uuid', 'content'),
+ ]),
+ new Section('foo'),
+ ];
+
+ $this->sectionStorage->appendSection(new Section('foo'));
+ $this->assertSections($expected);
+ }
+
+ /**
+ * @covers ::removeSection
+ */
+ public function testRemoveSection() {
+ $expected = [
+ new Section('layout_test_plugin', ['setting_1' => 'bar'], [
+ 'second-uuid' => new SectionComponent('second-uuid', 'content'),
+ ]),
+ ];
+
+ $this->sectionStorage->removeSection(0);
+ $this->assertSections($expected);
+ }
+
+ /**
+ * Asserts that the field list has the expected sections.
+ *
+ * @param \Drupal\layout_builder\Section[] $expected
+ * The expected sections.
+ */
+ protected function assertSections(array $expected) {
+ $result = $this->sectionStorage->getSections();
+ $this->assertEquals($expected, $result);
+ $this->assertSame(array_keys($expected), array_keys($result));
+ }
+
+}
diff --git a/core/modules/layout_builder/tests/src/Unit/LayoutSectionBuilderTest.php b/core/modules/layout_builder/tests/src/Unit/LayoutSectionBuilderTest.php
index 3e5b2ffad148..28f22557e75b 100644
--- a/core/modules/layout_builder/tests/src/Unit/LayoutSectionBuilderTest.php
+++ b/core/modules/layout_builder/tests/src/Unit/LayoutSectionBuilderTest.php
@@ -7,6 +7,8 @@
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Cache\Cache;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Layout\LayoutDefinition;
use Drupal\Core\Layout\LayoutInterface;
use Drupal\Core\Layout\LayoutPluginManagerInterface;
use Drupal\Core\Plugin\Context\ContextHandlerInterface;
@@ -14,6 +16,7 @@
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\layout_builder\LayoutSectionBuilder;
+use Drupal\layout_builder\SectionComponent;
use Drupal\Tests\UnitTestCase;
use Prophecy\Argument;
@@ -86,7 +89,16 @@ protected function setUp() {
$this->layoutSectionBuilder = new LayoutSectionBuilder($this->account->reveal(), $this->layoutPluginManager->reveal(), $this->blockManager->reveal(), $this->contextHandler->reveal(), $this->contextRepository->reveal());
$this->layout = $this->prophesize(LayoutInterface::class);
+ $this->layout->getPluginDefinition()->willReturn(new LayoutDefinition([]));
+ $this->layout->build(Argument::type('array'))->willReturnArgument(0);
$this->layoutPluginManager->createInstance('layout_onecol', [])->willReturn($this->layout->reveal());
+
+ $container = new ContainerBuilder();
+ $container->set('current_user', $this->account->reveal());
+ $container->set('plugin.manager.block', $this->blockManager->reveal());
+ $container->set('context.handler', $this->contextHandler->reveal());
+ $container->set('context.repository', $this->contextRepository->reveal());
+ \Drupal::setContainer($container);
}
/**
@@ -102,8 +114,12 @@ public function testBuildSection() {
'#base_plugin_id' => 'block_plugin_id',
'#derivative_plugin_id' => NULL,
'content' => $block_content,
+ '#cache' => [
+ 'contexts' => [],
+ 'tags' => [],
+ 'max-age' => -1,
+ ],
];
- $this->layout->build(['content' => ['some_uuid' => $render_array]])->willReturnArgument(0);
$block = $this->prophesize(BlockPluginInterface::class);
$this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
@@ -120,20 +136,9 @@ public function testBuildSection() {
$block->getConfiguration()->willReturn([]);
$section = [
- 'content' => [
- 'some_uuid' => [
- 'block' => [
- 'id' => 'block_plugin_id',
- ],
- ],
- ],
+ new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
];
$expected = [
- '#cache' => [
- 'contexts' => [],
- 'tags' => [],
- 'max-age' => -1,
- ],
'content' => [
'some_uuid' => $render_array,
],
@@ -146,7 +151,6 @@ public function testBuildSection() {
* @covers ::buildSection
*/
public function testBuildSectionAccessDenied() {
- $this->layout->build([])->willReturn([]);
$block = $this->prophesize(BlockPluginInterface::class);
$this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
@@ -156,21 +160,19 @@ public function testBuildSectionAccessDenied() {
$block->build()->shouldNotBeCalled();
$section = [
+ new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
+ ];
+ $expected = [
'content' => [
'some_uuid' => [
- 'block' => [
- 'id' => 'block_plugin_id',
+ '#cache' => [
+ 'contexts' => [],
+ 'tags' => [],
+ 'max-age' => -1,
],
],
],
];
- $expected = [
- '#cache' => [
- 'contexts' => [],
- 'tags' => [],
- 'max-age' => -1,
- ],
- ];
$result = $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
$this->assertEquals($expected, $result);
}
@@ -179,23 +181,14 @@ public function testBuildSectionAccessDenied() {
* @covers ::buildSection
*/
public function testBuildSectionEmpty() {
- $this->layout->build([])->willReturn([]);
-
$section = [];
- $expected = [
- '#cache' => [
- 'contexts' => [],
- 'tags' => [],
- 'max-age' => -1,
- ],
- ];
+ $expected = [];
$result = $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
$this->assertEquals($expected, $result);
}
/**
* @covers ::buildSection
- * @covers ::getBlock
*/
public function testContextAwareBlock() {
$render_array = [
@@ -206,8 +199,12 @@ public function testContextAwareBlock() {
'#base_plugin_id' => 'block_plugin_id',
'#derivative_plugin_id' => NULL,
'content' => [],
+ '#cache' => [
+ 'contexts' => [],
+ 'tags' => [],
+ 'max-age' => -1,
+ ],
];
- $this->layout->build(['content' => ['some_uuid' => $render_array]])->willReturnArgument(0);
$block = $this->prophesize(BlockPluginInterface::class)->willImplement(ContextAwarePluginInterface::class);
$this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
@@ -228,20 +225,9 @@ public function testContextAwareBlock() {
$this->contextHandler->applyContextMapping($block->reveal(), [])->shouldBeCalled();
$section = [
- 'content' => [
- 'some_uuid' => [
- 'block' => [
- 'id' => 'block_plugin_id',
- ],
- ],
- ],
+ new SectionComponent('some_uuid', 'content', ['id' => 'block_plugin_id']),
];
$expected = [
- '#cache' => [
- 'contexts' => [],
- 'tags' => [],
- 'max-age' => -1,
- ],
'content' => [
'some_uuid' => $render_array,
],
@@ -252,50 +238,10 @@ public function testContextAwareBlock() {
/**
* @covers ::buildSection
- * @covers ::getBlock
*/
public function testBuildSectionMissingPluginId() {
- $section = [
- 'content' => [
- 'some_uuid' => [
- 'block' => [],
- ],
- ],
- ];
- $this->setExpectedException(PluginException::class, 'No plugin ID specified for block with "some_uuid" UUID');
- $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
- }
-
- /**
- * @covers ::buildSection
- *
- * @dataProvider providerTestBuildSectionMalformedData
- */
- public function testBuildSectionMalformedData($section, $message) {
- $this->layout->build(Argument::type('array'))->willReturnArgument(0);
- $this->layout->getPluginId()->willReturn('the_plugin_id');
- $this->setExpectedException(\InvalidArgumentException::class, $message);
- $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
- }
-
- /**
- * Provides test data for ::testBuildSectionMalformedData().
- */
- public function providerTestBuildSectionMalformedData() {
- $data = [];
- $data['invalid_region'] = [
- ['content' => 'bar'],
- 'The "content" region in the "the_plugin_id" layout has invalid configuration',
- ];
- $data['invalid_configuration'] = [
- ['content' => ['some_uuid' => 'bar']],
- 'The block with UUID of "some_uuid" has invalid configuration',
- ];
- $data['invalid_blocks'] = [
- ['content' => ['some_uuid' => []]],
- 'The block with UUID of "some_uuid" has invalid configuration',
- ];
- return $data;
+ $this->setExpectedException(PluginException::class, 'No plugin ID specified for component with "some_uuid" UUID');
+ $this->layoutSectionBuilder->buildSection('layout_onecol', [], [new SectionComponent('some_uuid', 'content')]);
}
}
diff --git a/core/modules/layout_builder/tests/src/Unit/SectionTest.php b/core/modules/layout_builder/tests/src/Unit/SectionTest.php
index 33a338706e27..eff239507d2b 100644
--- a/core/modules/layout_builder/tests/src/Unit/SectionTest.php
+++ b/core/modules/layout_builder/tests/src/Unit/SectionTest.php
@@ -3,6 +3,7 @@
namespace Drupal\Tests\layout_builder\Unit;
use Drupal\layout_builder\Section;
+use Drupal\layout_builder\SectionComponent;
use Drupal\Tests\UnitTestCase;
/**
@@ -24,242 +25,158 @@ class SectionTest extends UnitTestCase {
protected function setUp() {
parent::setUp();
- $this->section = new Section([
- 'empty-region' => [],
- 'some-region' => [
- 'existing-uuid' => [
- 'block' => [
- 'id' => 'existing-block-id',
- ],
- ],
- ],
- 'ordered-region' => [
- 'first-uuid' => [
- 'block' => [
- 'id' => 'first-block-id',
- ],
- ],
- 'second-uuid' => [
- 'block' => [
- 'id' => 'second-block-id',
- ],
- ],
- ],
+ $this->section = new Section('layout_onecol', [], [
+ new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']),
+ (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
+ (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
]);
}
/**
* @covers ::__construct
- * @covers ::getValue
+ * @covers ::setComponent
+ * @covers ::getComponents
*/
- public function testGetValue() {
+ public function testGetComponents() {
$expected = [
- 'empty-region' => [],
- 'some-region' => [
- 'existing-uuid' => [
- 'block' => [
- 'id' => 'existing-block-id',
- ],
- ],
- ],
- 'ordered-region' => [
- 'first-uuid' => [
- 'block' => [
- 'id' => 'first-block-id',
- ],
- ],
- 'second-uuid' => [
- 'block' => [
- 'id' => 'second-block-id',
- ],
- ],
- ],
+ 'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+ 'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
+ 'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
];
- $result = $this->section->getValue();
- $this->assertSame($expected, $result);
- }
- /**
- * @covers ::getBlock
- */
- public function testGetBlockInvalidRegion() {
- $this->setExpectedException(\InvalidArgumentException::class, 'Invalid region');
- $this->section->getBlock('invalid-region', 'existing-uuid');
+ $this->assertComponents($expected, $this->section);
}
/**
- * @covers ::getBlock
+ * @covers ::getComponent
*/
- public function testGetBlockInvalidUuid() {
- $this->setExpectedException(\InvalidArgumentException::class, 'Invalid UUID');
- $this->section->getBlock('some-region', 'invalid-uuid');
+ public function testGetComponentInvalidUuid() {
+ $this->setExpectedException(\InvalidArgumentException::class, 'Invalid UUID "invalid-uuid"');
+ $this->section->getComponent('invalid-uuid');
}
/**
- * @covers ::getBlock
+ * @covers ::getComponent
*/
- public function testGetBlock() {
- $expected = ['block' => ['id' => 'existing-block-id']];
+ public function testGetComponent() {
+ $expected = new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']);
- $block = $this->section->getBlock('some-region', 'existing-uuid');
- $this->assertSame($expected, $block);
+ $this->assertEquals($expected, $this->section->getComponent('existing-uuid'));
}
/**
- * @covers ::removeBlock
+ * @covers ::removeComponent
+ * @covers ::getComponentsByRegion
*/
- public function testRemoveBlock() {
- $this->section->removeBlock('some-region', 'existing-uuid');
+ public function testRemoveComponent() {
$expected = [
- 'ordered-region' => [
- 'first-uuid' => [
- 'block' => [
- 'id' => 'first-block-id',
- ],
- ],
- 'second-uuid' => [
- 'block' => [
- 'id' => 'second-block-id',
- ],
- ],
- ],
+ 'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+ 'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
];
- $this->assertSame($expected, $this->section->getValue());
+
+ $this->section->removeComponent('first-uuid');
+ $this->assertComponents($expected, $this->section);
}
/**
- * @covers ::addBlock
+ * @covers ::appendComponent
+ * @covers ::getNextHighestWeight
+ * @covers ::getComponentsByRegion
*/
- public function testAddBlock() {
- $this->section->addBlock('some-region', 'new-uuid', []);
+ public function testAppendComponent() {
$expected = [
- 'empty-region' => [],
- 'some-region' => [
- 'new-uuid' => [],
- 'existing-uuid' => [
- 'block' => [
- 'id' => 'existing-block-id',
- ],
- ],
- ],
- 'ordered-region' => [
- 'first-uuid' => [
- 'block' => [
- 'id' => 'first-block-id',
- ],
- ],
- 'second-uuid' => [
- 'block' => [
- 'id' => 'second-block-id',
- ],
- ],
- ],
+ 'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+ 'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
+ 'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
+ 'new-uuid' => (new SectionComponent('new-uuid', 'some-region', []))->setWeight(1),
];
- $this->assertSame($expected, $this->section->getValue());
+
+ $this->section->appendComponent(new SectionComponent('new-uuid', 'some-region'));
+ $this->assertComponents($expected, $this->section);
}
/**
- * @covers ::insertBlock
+ * @covers ::insertAfterComponent
*/
- public function testInsertBlock() {
- $this->section->insertBlock('ordered-region', 'new-uuid', [], 'first-uuid');
+ public function testInsertAfterComponent() {
$expected = [
- 'empty-region' => [],
- 'some-region' => [
- 'existing-uuid' => [
- 'block' => [
- 'id' => 'existing-block-id',
- ],
- ],
- ],
- 'ordered-region' => [
- 'first-uuid' => [
- 'block' => [
- 'id' => 'first-block-id',
- ],
- ],
- 'new-uuid' => [],
- 'second-uuid' => [
- 'block' => [
- 'id' => 'second-block-id',
- ],
- ],
- ],
+ 'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+ 'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(4),
+ 'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
+ 'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(3),
];
- $this->assertSame($expected, $this->section->getValue());
+
+ $this->section->insertAfterComponent('first-uuid', new SectionComponent('new-uuid', 'ordered-region'));
+ $this->assertComponents($expected, $this->section);
}
/**
- * @covers ::insertBlock
+ * @covers ::insertAfterComponent
*/
- public function testInsertBlockInvalidRegion() {
- $this->setExpectedException(\InvalidArgumentException::class, 'Invalid region');
- $this->section->insertBlock('invalid-region', 'new-uuid', [], 'first-uuid');
+ public function testInsertAfterComponentValidUuidRegionMismatch() {
+ $this->setExpectedException(\InvalidArgumentException::class, 'Invalid preceding UUID "existing-uuid"');
+ $this->section->insertAfterComponent('existing-uuid', new SectionComponent('new-uuid', 'ordered-region'));
}
/**
- * @covers ::insertBlock
+ * @covers ::insertAfterComponent
*/
- public function testInsertBlockInvalidUuid() {
- $this->setExpectedException(\InvalidArgumentException::class, 'Invalid preceding UUID');
- $this->section->insertBlock('ordered-region', 'new-uuid', [], 'invalid-uuid');
+ public function testInsertAfterComponentInvalidUuid() {
+ $this->setExpectedException(\InvalidArgumentException::class, 'Invalid preceding UUID "invalid-uuid"');
+ $this->section->insertAfterComponent('invalid-uuid', new SectionComponent('new-uuid', 'ordered-region'));
}
/**
- * @covers ::updateBlock
+ * @covers ::insertComponent
+ * @covers ::getComponentsByRegion
*/
- public function testUpdateBlock() {
- $this->section->updateBlock('some-region', 'existing-uuid', [
- 'block' => [
- 'id' => 'existing-block-id',
- 'settings' => [
- 'foo' => 'bar',
- ],
- ],
- ]);
+ public function testInsertComponent() {
+ $expected = [
+ 'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+ 'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(4),
+ 'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(3),
+ 'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(2),
+ ];
+
+ $this->section->insertComponent(0, new SectionComponent('new-uuid', 'ordered-region'));
+ $this->assertComponents($expected, $this->section);
+ }
+ /**
+ * @covers ::insertComponent
+ */
+ public function testInsertComponentAppend() {
$expected = [
- 'empty-region' => [],
- 'some-region' => [
- 'existing-uuid' => [
- 'block' => [
- 'id' => 'existing-block-id',
- 'settings' => [
- 'foo' => 'bar',
- ],
- ],
- ],
- ],
- 'ordered-region' => [
- 'first-uuid' => [
- 'block' => [
- 'id' => 'first-block-id',
- ],
- ],
- 'second-uuid' => [
- 'block' => [
- 'id' => 'second-block-id',
- ],
- ],
- ],
+ 'existing-uuid' => (new SectionComponent('existing-uuid', 'some-region', ['id' => 'existing-block-id']))->setWeight(0),
+ 'second-uuid' => (new SectionComponent('second-uuid', 'ordered-region', ['id' => 'second-block-id']))->setWeight(3),
+ 'first-uuid' => (new SectionComponent('first-uuid', 'ordered-region', ['id' => 'first-block-id']))->setWeight(2),
+ 'new-uuid' => (new SectionComponent('new-uuid', 'ordered-region', []))->setWeight(4),
];
- $this->assertSame($expected, $this->section->getValue());
+
+ $this->section->insertComponent(2, new SectionComponent('new-uuid', 'ordered-region'));
+ $this->assertComponents($expected, $this->section);
}
/**
- * @covers ::updateBlock
+ * @covers ::insertComponent
*/
- public function testUpdateBlockInvalidRegion() {
- $this->setExpectedException(\InvalidArgumentException::class, 'Invalid region');
- $this->section->updateBlock('invalid-region', 'new-uuid', []);
+ public function testInsertComponentInvalidDelta() {
+ $this->setExpectedException(\OutOfBoundsException::class, 'Invalid delta "7" for the "new-uuid" component');
+ $this->section->insertComponent(7, new SectionComponent('new-uuid', 'ordered-region'));
}
/**
- * @covers ::updateBlock
+ * Asserts that the section has the expected components.
+ *
+ * @param \Drupal\layout_builder\SectionComponent[] $expected
+ * The expected sections.
+ * @param \Drupal\layout_builder\Section $section
+ * The section storage to check.
*/
- public function testUpdateBlockInvalidUuid() {
- $this->setExpectedException(\InvalidArgumentException::class, 'Invalid UUID');
- $this->section->updateBlock('ordered-region', 'new-uuid', []);
+ protected function assertComponents(array $expected, Section $section) {
+ $result = $section->getComponents();
+ $this->assertEquals($expected, $result);
+ $this->assertSame(array_keys($expected), array_keys($result));
}
}
From 8e9f1b61208087991afb783bd7d920f1e39fbcae Mon Sep 17 00:00:00 2001
From: effulgentsia
Date: Wed, 20 Dec 2017 09:53:54 -0800
Subject: [PATCH 047/232] Issue #2929496 by plach, hchonov, timmillwood, catch,
Wim Leers, amateescu, Calystod, andypost: Add dedicated interfaces to group
methods dealing with revision translation and clean up the related
documentation
---
.../Core/Entity/ContentEntityInterface.php | 67 +------------------
.../Entity/ContentEntityStorageInterface.php | 19 +-----
.../Core/Entity/Entity/EntityViewDisplay.php | 4 +-
.../Drupal/Core/Entity/EntityRepository.php | 4 +-
.../Drupal/Core/Entity/EntityViewBuilder.php | 4 +-
.../Core/Entity/TranslatableInterface.php | 20 ++++++
.../TranslatableRevisionableInterface.php | 65 ++++++++++++++++++
...anslatableRevisionableStorageInterface.php | 9 +++
.../Entity/TranslatableStorageInterface.php | 30 +++++++++
core/lib/Drupal/Core/Entity/entity.api.php | 66 +++++++++++++++---
10 files changed, 187 insertions(+), 101 deletions(-)
create mode 100644 core/lib/Drupal/Core/Entity/TranslatableInterface.php
create mode 100644 core/lib/Drupal/Core/Entity/TranslatableRevisionableInterface.php
create mode 100644 core/lib/Drupal/Core/Entity/TranslatableRevisionableStorageInterface.php
create mode 100644 core/lib/Drupal/Core/Entity/TranslatableStorageInterface.php
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
index 3053d23694d9..f43bc3b453d2 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php
@@ -2,8 +2,6 @@
namespace Drupal\Core\Entity;
-use Drupal\Core\TypedData\TranslatableInterface;
-
/**
* Defines a common interface for all content entity objects.
*
@@ -20,70 +18,7 @@
*
* @ingroup entity_api
*/
-interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, RevisionableInterface, TranslatableInterface {
-
- /**
- * Determines if the current translation of the entity has unsaved changes.
- *
- * @return bool
- * TRUE if the current translation of the entity has changes.
- */
- public function hasTranslationChanges();
-
- /**
- * Marks the current revision translation as affected.
- *
- * Setting the revision translation affected flag through the setter or
- * through the field directly will always enforce it, which will be used by
- * the entity storage to determine if the flag should be recomputed or the set
- * value should be used instead.
- * @see \Drupal\Core\Entity\ContentEntityStorageBase::populateAffectedRevisionTranslations()
- *
- * @param bool|null $affected
- * The flag value. A NULL value can be specified to reset the current value
- * and make sure a new value will be computed by the system.
- *
- * @return $this
- */
- public function setRevisionTranslationAffected($affected);
-
- /**
- * Checks whether the current translation is affected by the current revision.
- *
- * @return bool
- * TRUE if the entity object is affected by the current revision, FALSE
- * otherwise.
- */
- public function isRevisionTranslationAffected();
-
- /**
- * Checks if the revision translation affected flag value has been enforced.
- *
- * @return bool
- * TRUE if revision translation affected flag is enforced, FALSE otherwise.
- *
- * @internal
- */
- public function isRevisionTranslationAffectedEnforced();
-
- /**
- * Enforces the revision translation affected flag value.
- *
- * Note that this method call will not have any influence on the storage if
- * the value of the revision translation affected flag is NULL which is used
- * as an indication for the storage to recompute the flag.
- * @see \Drupal\Core\Entity\ContentEntityInterface::setRevisionTranslationAffected()
- *
- * @param bool $enforced
- * If TRUE, the value of the revision translation affected flag will be
- * enforced so that on entity save the entity storage will not recompute it.
- * Otherwise the storage will recompute it.
- *
- * @return $this
- *
- * @internal
- */
- public function setRevisionTranslationAffectedEnforced($enforced);
+interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, TranslatableRevisionableInterface {
/**
* Gets the loaded Revision ID of the entity.
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
index 678937bdc004..9b35a84bef19 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageInterface.php
@@ -5,24 +5,7 @@
/**
* A storage that supports content entity types.
*/
-interface ContentEntityStorageInterface extends EntityStorageInterface, RevisionableStorageInterface {
-
- /**
- * Constructs a new entity translation object, without permanently saving it.
- *
- * @param \Drupal\Core\Entity\ContentEntityInterface $entity
- * The entity object being translated.
- * @param string $langcode
- * The translation language code.
- * @param array $values
- * (optional) An associative array of initial field values keyed by field
- * name. If none is provided default values will be applied.
- *
- * @return \Drupal\Core\Entity\ContentEntityInterface
- * A new entity translation object.
- */
- public function createTranslation(ContentEntityInterface $entity, $langcode, array $values = []);
-
+interface ContentEntityStorageInterface extends EntityStorageInterface, TranslatableRevisionableStorageInterface {
/**
* Creates an entity with sample field values.
diff --git a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
index 1604e311132d..7c7d62e04da3 100644
--- a/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
+++ b/core/lib/Drupal/Core/Entity/Entity/EntityViewDisplay.php
@@ -8,7 +8,7 @@
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Entity\EntityDisplayBase;
-use Drupal\Core\TypedData\TranslatableInterface;
+use Drupal\Core\TypedData\TranslatableInterface as TranslatableDataInterface;
/**
* Configuration entity that contains display options for all components of a
@@ -253,7 +253,7 @@ public function buildMultiple(array $entities) {
// those values using:
// - the entity language if the entity is translatable,
// - the current "content language" otherwise.
- if ($entity instanceof TranslatableInterface && $entity->isTranslatable()) {
+ if ($entity instanceof TranslatableDataInterface && $entity->isTranslatable()) {
$view_langcode = $entity->language()->getId();
}
else {
diff --git a/core/lib/Drupal/Core/Entity/EntityRepository.php b/core/lib/Drupal/Core/Entity/EntityRepository.php
index 986cc50b5433..37a89d793ebd 100644
--- a/core/lib/Drupal/Core/Entity/EntityRepository.php
+++ b/core/lib/Drupal/Core/Entity/EntityRepository.php
@@ -5,7 +5,7 @@
use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Core\TypedData\TranslatableInterface;
+use Drupal\Core\TypedData\TranslatableInterface as TranslatableDataInterface;
/**
* Provides several mechanisms for retrieving entities.
@@ -82,7 +82,7 @@ public function loadEntityByConfigTarget($entity_type_id, $target) {
public function getTranslationFromContext(EntityInterface $entity, $langcode = NULL, $context = []) {
$translation = $entity;
- if ($entity instanceof TranslatableInterface && count($entity->getTranslationLanguages()) > 1) {
+ if ($entity instanceof TranslatableDataInterface && count($entity->getTranslationLanguages()) > 1) {
if (empty($langcode)) {
$langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
$entity->addCacheContexts(['languages:' . LanguageInterface::TYPE_CONTENT]);
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index e95c01b2c843..faeb1473df48 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -11,7 +11,7 @@
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Theme\Registry;
-use Drupal\Core\TypedData\TranslatableInterface;
+use Drupal\Core\TypedData\TranslatableInterface as TranslatableDataInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -189,7 +189,7 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode) {
'bin' => $this->cacheBin,
];
- if ($entity instanceof TranslatableInterface && count($entity->getTranslationLanguages()) > 1) {
+ if ($entity instanceof TranslatableDataInterface && count($entity->getTranslationLanguages()) > 1) {
$build['#cache']['keys'][] = $entity->language()->getId();
}
}
diff --git a/core/lib/Drupal/Core/Entity/TranslatableInterface.php b/core/lib/Drupal/Core/Entity/TranslatableInterface.php
new file mode 100644
index 000000000000..573310b6c2cb
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/TranslatableInterface.php
@@ -0,0 +1,20 @@
+
Date: Wed, 20 Dec 2017 20:49:05 +0000
Subject: [PATCH 048/232] Issue #2551259 by Mile23, gaurav.kapoor, Jo
Fitzgerald, googletorp, Jeremy, xjm, webchick: Deprecate dead code
locale_translation_manual_status()
---
core/modules/locale/locale.pages.inc | 7 ++++-
.../src/Kernel/LocaleDeprecationsTest.php | 30 +++++++++++++++++++
2 files changed, 36 insertions(+), 1 deletion(-)
create mode 100644 core/modules/locale/tests/src/Kernel/LocaleDeprecationsTest.php
diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc
index d8d7659bf9ca..2f8957cc8814 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -13,9 +13,14 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
*
* Manually checks the translation status without the use of cron.
*
- * @see locale_menu()
+ * @deprecated in Drupal 8.5.0 and will be removed before 9.0.0. It is unused by
+ * Drupal core. Duplicate this function in your own extension if you need its
+ * behavior.
+ *
+ * @see https://www.drupal.org/node/2931188
*/
function locale_translation_manual_status() {
+ @trigger_error('locale_translation_manual_status() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. It is unused by Drupal core. Duplicate this function in your own extension if you need its behavior.', E_USER_DEPRECATED);
module_load_include('compare.inc', 'locale');
// Check the translation status of all translatable projects in all languages.
diff --git a/core/modules/locale/tests/src/Kernel/LocaleDeprecationsTest.php b/core/modules/locale/tests/src/Kernel/LocaleDeprecationsTest.php
new file mode 100644
index 000000000000..b98bc0552a7a
--- /dev/null
+++ b/core/modules/locale/tests/src/Kernel/LocaleDeprecationsTest.php
@@ -0,0 +1,30 @@
+assertNotNull(\locale_translation_manual_status());
+ }
+
+}
From be64202643a512a694c3bcb48d6fc6d38970abe3 Mon Sep 17 00:00:00 2001
From: effulgentsia
Date: Wed, 20 Dec 2017 14:14:36 -0800
Subject: [PATCH 049/232] Issue #2825487 by damiankloip, Wim Leers, garphy,
cburschka, tedbow, dpovshed, tstoeckler, Munavijayalakshmi, Berdir, dawehner,
e0ipso: Fix normalization of File entities: file entities should expose the
file URL as a computed property on the 'uri' base field
---
core/modules/file/file.module | 10 ++
core/modules/file/src/ComputedFileUrl.php | 47 ++++++++++
core/modules/file/src/Entity/File.php | 2 +-
.../Field/FieldFormatter/FileUriFormatter.php | 3 +-
.../Plugin/Field/FieldType/FileUriItem.php | 39 ++++++++
.../tests/src/Kernel/ComputedFileUrlTest.php | 91 +++++++++++++++++++
.../file/tests/src/Kernel/FileUriItemTest.php | 40 ++++++++
.../hal/config/install/hal.settings.yml | 8 ++
core/modules/hal/config/schema/hal.schema.yml | 3 +
core/modules/hal/hal.install | 12 +++
core/modules/hal/hal.services.yml | 3 +-
.../src/Normalizer/FileEntityNormalizer.php | 24 ++++-
.../File/FileHalJsonAnonTest.php | 38 +++++++-
.../Media/MediaHalJsonAnonTest.php | 18 ++--
.../src/Functional/FileDenormalizeTest.php | 14 +++
.../tests/src/Kernel/FileNormalizeTest.php | 5 +-
.../EntityResource/EntityResourceTestBase.php | 9 +-
.../File/FileResourceTestBase.php | 1 +
.../Listeners/DeprecationListenerTrait.php | 1 +
19 files changed, 348 insertions(+), 20 deletions(-)
create mode 100644 core/modules/file/src/ComputedFileUrl.php
create mode 100644 core/modules/file/src/Plugin/Field/FieldType/FileUriItem.php
create mode 100644 core/modules/file/tests/src/Kernel/ComputedFileUrlTest.php
create mode 100644 core/modules/file/tests/src/Kernel/FileUriItemTest.php
diff --git a/core/modules/file/file.module b/core/modules/file/file.module
index 88bc2990fcd9..013201119a71 100644
--- a/core/modules/file/file.module
+++ b/core/modules/file/file.module
@@ -48,6 +48,16 @@ function file_help($route_name, RouteMatchInterface $route_match) {
}
}
+/**
+ * Implements hook_field_widget_info_alter().
+ */
+function file_field_widget_info_alter(array &$info) {
+ // Allows using the 'uri' widget for the 'file_uri' field type, which uses it
+ // as the default widget.
+ // @see \Drupal\file\Plugin\Field\FieldType\FileUriItem
+ $info['uri']['field_types'][] = 'file_uri';
+}
+
/**
* Loads file entities from the database.
*
diff --git a/core/modules/file/src/ComputedFileUrl.php b/core/modules/file/src/ComputedFileUrl.php
new file mode 100644
index 000000000000..2eb012edc659
--- /dev/null
+++ b/core/modules/file/src/ComputedFileUrl.php
@@ -0,0 +1,47 @@
+url !== NULL) {
+ return $this->url;
+ }
+
+ assert($this->getParent()->getEntity() instanceof FileInterface);
+
+ $uri = $this->getParent()->getEntity()->getFileUri();
+ $this->url = file_url_transform_relative(file_create_url($uri));
+
+ return $this->url;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setValue($value, $notify = TRUE) {
+ $this->url = $value;
+
+ // Notify the parent of any changes.
+ if ($notify && isset($this->parent)) {
+ $this->parent->onChange($this->name);
+ }
+ }
+
+}
diff --git a/core/modules/file/src/Entity/File.php b/core/modules/file/src/Entity/File.php
index a9ade9e7d6ee..4060b773eed1 100644
--- a/core/modules/file/src/Entity/File.php
+++ b/core/modules/file/src/Entity/File.php
@@ -243,7 +243,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
->setLabel(t('Filename'))
->setDescription(t('Name of the file with no path components.'));
- $fields['uri'] = BaseFieldDefinition::create('uri')
+ $fields['uri'] = BaseFieldDefinition::create('file_uri')
->setLabel(t('URI'))
->setDescription(t('The URI to access the file (either local or remote).'))
->setSetting('max_length', 255)
diff --git a/core/modules/file/src/Plugin/Field/FieldFormatter/FileUriFormatter.php b/core/modules/file/src/Plugin/Field/FieldFormatter/FileUriFormatter.php
index 055e32dc8b12..0facb7e03388 100644
--- a/core/modules/file/src/Plugin/Field/FieldFormatter/FileUriFormatter.php
+++ b/core/modules/file/src/Plugin/Field/FieldFormatter/FileUriFormatter.php
@@ -13,7 +13,8 @@
* id = "file_uri",
* label = @Translation("File URI"),
* field_types = {
- * "uri"
+ * "uri",
+ * "file_uri",
* }
* )
*/
diff --git a/core/modules/file/src/Plugin/Field/FieldType/FileUriItem.php b/core/modules/file/src/Plugin/Field/FieldType/FileUriItem.php
new file mode 100644
index 000000000000..33bc15ef03f1
--- /dev/null
+++ b/core/modules/file/src/Plugin/Field/FieldType/FileUriItem.php
@@ -0,0 +1,39 @@
+setLabel(t('Root-relative file URL'))
+ ->setComputed(TRUE)
+ ->setInternal(FALSE)
+ ->setClass(ComputedFileUrl::class);
+
+ return $properties;
+ }
+
+}
diff --git a/core/modules/file/tests/src/Kernel/ComputedFileUrlTest.php b/core/modules/file/tests/src/Kernel/ComputedFileUrlTest.php
new file mode 100644
index 000000000000..d0f1e616ecc3
--- /dev/null
+++ b/core/modules/file/tests/src/Kernel/ComputedFileUrlTest.php
@@ -0,0 +1,91 @@
+prophesize(FileInterface::class);
+ $entity->getFileUri()
+ ->willReturn($this->testUrl);
+
+ $parent = $this->prophesize(FieldItemInterface::class);
+ $parent->getEntity()
+ ->shouldBeCalledTimes(2)
+ ->willReturn($entity->reveal());
+
+ $definition = $this->prophesize(DataDefinitionInterface::class);
+
+ $typed_data = new ComputedFileUrl($definition->reveal(), $this->randomMachineName(), $parent->reveal());
+
+ $expected = base_path() . $this->siteDirectory . '/files/druplicon.txt';
+
+ $this->assertSame($expected, $typed_data->getValue());
+ // Do this a second time to confirm the same value is returned but the value
+ // isn't retrieved from the parent entity again.
+ $this->assertSame($expected, $typed_data->getValue());
+ }
+
+ /**
+ * @covers ::setValue
+ */
+ public function testSetValue() {
+ $name = $this->randomMachineName();
+ $parent = $this->prophesize(FieldItemInterface::class);
+ $parent->onChange($name)
+ ->shouldBeCalled();
+
+ $definition = $this->prophesize(DataDefinitionInterface::class);
+ $typed_data = new ComputedFileUrl($definition->reveal(), $name, $parent->reveal());
+
+ // Setting the value explicitly should mean the parent entity is never
+ // called into.
+ $typed_data->setValue($this->testUrl);
+
+ $this->assertSame($this->testUrl, $typed_data->getValue());
+ // Do this a second time to confirm the same value is returned but the value
+ // isn't retrieved from the parent entity again.
+ $this->assertSame($this->testUrl, $typed_data->getValue());
+ }
+
+ /**
+ * @covers ::setValue
+ */
+ public function testSetValueNoNotify() {
+ $name = $this->randomMachineName();
+ $parent = $this->prophesize(FieldItemInterface::class);
+ $parent->onChange($name)
+ ->shouldNotBeCalled();
+
+ $definition = $this->prophesize(DataDefinitionInterface::class);
+ $typed_data = new ComputedFileUrl($definition->reveal(), $name, $parent->reveal());
+
+ // Setting the value should explicitly should mean the parent entity is
+ // never called into.
+ $typed_data->setValue($this->testUrl, FALSE);
+
+ $this->assertSame($this->testUrl, $typed_data->getValue());
+ }
+
+}
diff --git a/core/modules/file/tests/src/Kernel/FileUriItemTest.php b/core/modules/file/tests/src/Kernel/FileUriItemTest.php
new file mode 100644
index 000000000000..d67eba07070c
--- /dev/null
+++ b/core/modules/file/tests/src/Kernel/FileUriItemTest.php
@@ -0,0 +1,40 @@
+ 1,
+ 'filename' => 'druplicon.txt',
+ 'uri' => $uri,
+ 'filemime' => 'text/plain',
+ 'status' => FILE_STATUS_PERMANENT,
+ ]);
+ file_put_contents($file->getFileUri(), 'hello world');
+
+ $file->save();
+
+ $this->assertSame($uri, $file->uri->value);
+ $expected_url = base_path() . $this->siteDirectory . '/files/druplicon.txt';
+ $this->assertSame($expected_url, $file->uri->url);
+ }
+
+}
diff --git a/core/modules/hal/config/install/hal.settings.yml b/core/modules/hal/config/install/hal.settings.yml
index 67107af00d34..c62cb6a04819 100644
--- a/core/modules/hal/config/install/hal.settings.yml
+++ b/core/modules/hal/config/install/hal.settings.yml
@@ -1,3 +1,11 @@
# Set the domain for HAL type and relation links.
# If left blank, the site's domain will be used.
link_domain: ~
+# Before Drupal 8.5, the File entity 'uri' field value was overridden to return
+# the absolute file URL instead of the actual (stream wrapper) URI. The default
+# for new sites is now to return the actual URI as well as a root-relative file
+# URL. Enable this setting to use the previous behavior. For existing sites,
+# the previous behavior is kept by default.
+# @see hal_update_8501()
+# @see https://www.drupal.org/node/2925783
+bc_file_uri_as_url_normalizer: false
diff --git a/core/modules/hal/config/schema/hal.schema.yml b/core/modules/hal/config/schema/hal.schema.yml
index 3192d6758052..cad1bdb722b8 100644
--- a/core/modules/hal/config/schema/hal.schema.yml
+++ b/core/modules/hal/config/schema/hal.schema.yml
@@ -6,3 +6,6 @@ hal.settings:
link_domain:
type: string
label: 'Domain of the relation'
+ bc_file_uri_as_url_normalizer:
+ type: boolean
+ label: 'Whether to retain pre Drupal 8.5 behavior of normalizing the File entity "uri" field value to an absolute URL.'
diff --git a/core/modules/hal/hal.install b/core/modules/hal/hal.install
index 78810e37f534..87944844325e 100644
--- a/core/modules/hal/hal.install
+++ b/core/modules/hal/hal.install
@@ -31,3 +31,15 @@ function hal_update_8301() {
$hal_settings->set('link_domain', $link_domain);
$hal_settings->save(TRUE);
}
+
+/**
+ * Add hal.settings::bc_file_uri_as_url_normalizer configuration.
+ */
+function hal_update_8501() {
+ $config_factory = \Drupal::configFactory();
+ $config_factory->getEditable('hal.settings')
+ ->set('bc_file_uri_as_url_normalizer', TRUE)
+ ->save(TRUE);
+
+ return t('Backwards compatibility mode has been enabled for File entities\' HAL normalization of the "uri" field. Like before, it will continue to return only the absolute file URL. If you want the new behavior, which returns both the stored URI and a root-relative file URL, read the change record to learn how to opt in.');
+}
diff --git a/core/modules/hal/hal.services.yml b/core/modules/hal/hal.services.yml
index b2c898fc5608..a877163c08de 100644
--- a/core/modules/hal/hal.services.yml
+++ b/core/modules/hal/hal.services.yml
@@ -14,9 +14,10 @@ services:
- { name: normalizer, priority: 10 }
serializer.normalizer.file_entity.hal:
class: Drupal\hal\Normalizer\FileEntityNormalizer
+ deprecated: 'The "%service_id%" normalizer service is deprecated: it is obsolete, it only remains available for backwards compatibility.'
+ arguments: ['@entity.manager', '@http_client', '@hal.link_manager', '@module_handler', '@config.factory']
tags:
- { name: normalizer, priority: 20 }
- arguments: ['@entity.manager', '@http_client', '@hal.link_manager', '@module_handler']
serializer.normalizer.timestamp_item.hal:
class: Drupal\hal\Normalizer\TimestampItemNormalizer
tags:
diff --git a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php
index ec870e9e14c6..5186f8b25594 100644
--- a/core/modules/hal/src/Normalizer/FileEntityNormalizer.php
+++ b/core/modules/hal/src/Normalizer/FileEntityNormalizer.php
@@ -2,6 +2,7 @@
namespace Drupal\hal\Normalizer;
+use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\hal\LinkManager\LinkManagerInterface;
@@ -9,6 +10,8 @@
/**
* Converts the Drupal entity object structure to a HAL array structure.
+ *
+ * @deprecated in Drupal 8.5.0, to be removed before Drupal 9.0.0.
*/
class FileEntityNormalizer extends ContentEntityNormalizer {
@@ -26,6 +29,13 @@ class FileEntityNormalizer extends ContentEntityNormalizer {
*/
protected $httpClient;
+ /**
+ * The HAL settings config.
+ *
+ * @var \Drupal\Core\Config\ImmutableConfig
+ */
+ protected $halSettings;
+
/**
* Constructs a FileEntityNormalizer object.
*
@@ -37,11 +47,14 @@ class FileEntityNormalizer extends ContentEntityNormalizer {
* The hypermedia link manager.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
+ * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
+ * The config factory.
*/
- public function __construct(EntityManagerInterface $entity_manager, ClientInterface $http_client, LinkManagerInterface $link_manager, ModuleHandlerInterface $module_handler) {
+ public function __construct(EntityManagerInterface $entity_manager, ClientInterface $http_client, LinkManagerInterface $link_manager, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory) {
parent::__construct($link_manager, $entity_manager, $module_handler);
$this->httpClient = $http_client;
+ $this->halSettings = $config_factory->get('hal.settings');
}
/**
@@ -49,8 +62,13 @@ public function __construct(EntityManagerInterface $entity_manager, ClientInterf
*/
public function normalize($entity, $format = NULL, array $context = []) {
$data = parent::normalize($entity, $format, $context);
- // Replace the file url with a full url for the file.
- $data['uri'][0]['value'] = $this->getEntityUri($entity);
+
+ $this->addCacheableDependency($context, $this->halSettings);
+
+ if ($this->halSettings->get('bc_file_uri_as_url_normalizer')) {
+ // Replace the file url with a full url for the file.
+ $data['uri'][0]['value'] = $this->getEntityUri($entity);
+ }
return $data;
}
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php
index ff89f745903f..f3036a9de868 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/File/FileHalJsonAnonTest.php
@@ -2,6 +2,7 @@
namespace Drupal\Tests\hal\Functional\EntityResource\File;
+use Drupal\Core\Cache\Cache;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\File\FileResourceTestBase;
@@ -38,7 +39,11 @@ protected function getExpectedNormalizedEntity() {
$normalization = $this->applyHalFieldNormalization($default_normalization);
$url = file_create_url($this->entity->getFileUri());
- $normalization['uri'][0]['value'] = $url;
+ // @see \Drupal\Tests\hal\Functional\EntityResource\File\FileHalJsonAnonTest::testGetBcUriField()
+ if ($this->config('hal.settings')->get('bc_file_uri_as_url_normalizer')) {
+ $normalization['uri'][0]['value'] = $url;
+ }
+
$uid = $this->author->id();
return $normalization + [
@@ -90,6 +95,13 @@ protected function getNormalizedPostEntity() {
];
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheTags() {
+ return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:hal.settings']);
+ }
+
/**
* {@inheritdoc}
*/
@@ -100,6 +112,30 @@ protected function getExpectedCacheContexts() {
];
}
+ /**
+ * @see hal_update_8501()
+ */
+ public function testGetBcUriField() {
+ $this->config('hal.settings')->set('bc_file_uri_as_url_normalizer', TRUE)->save(TRUE);
+
+ $this->initAuthentication();
+ $url = $this->getEntityResourceUrl();
+ $url->setOption('query', ['_format' => static::$format]);
+ $request_options = $this->getAuthenticationRequestOptions('GET');
+ $this->provisionEntityResource();
+ $this->setUpAuthorization('GET');
+ $response = $this->request('GET', $url, $request_options);
+ $expected = $this->getExpectedNormalizedEntity();
+ static::recursiveKSort($expected);
+ $actual = $this->serializer->decode((string) $response->getBody(), static::$format);
+ static::recursiveKSort($actual);
+ $this->assertSame($expected, $actual);
+
+ // Explicitly assert that $file->uri->value is an absolute file URL, unlike
+ // the default normalization.
+ $this->assertSame($this->baseUrl . '/' . $this->siteDirectory . '/files/drupal.txt', $actual['uri'][0]['value']);
+ }
+
/**
* {@inheritdoc}
*/
diff --git a/core/modules/hal/tests/src/Functional/EntityResource/Media/MediaHalJsonAnonTest.php b/core/modules/hal/tests/src/Functional/EntityResource/Media/MediaHalJsonAnonTest.php
index c71b54e6e6c8..c9ee7733cfa3 100644
--- a/core/modules/hal/tests/src/Functional/EntityResource/Media/MediaHalJsonAnonTest.php
+++ b/core/modules/hal/tests/src/Functional/EntityResource/Media/MediaHalJsonAnonTest.php
@@ -2,6 +2,7 @@
namespace Drupal\Tests\hal\Functional\EntityResource\Media;
+use Drupal\Core\Cache\Cache;
use Drupal\file\Entity\File;
use Drupal\Tests\hal\Functional\EntityResource\HalEntityNormalizationTrait;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
@@ -86,11 +87,6 @@ protected function getExpectedNormalizedEntity() {
],
],
'lang' => 'en',
- 'uri' => [
- [
- 'value' => $file->url(),
- ],
- ],
'uuid' => [
[
'value' => $file->uuid(),
@@ -126,11 +122,6 @@ protected function getExpectedNormalizedEntity() {
],
],
'lang' => 'en',
- 'uri' => [
- [
- 'value' => $thumbnail->url(),
- ],
- ],
'uuid' => [
[
'value' => $thumbnail->uuid(),
@@ -173,4 +164,11 @@ protected function getNormalizedPostEntity() {
];
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheTags() {
+ return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:hal.settings']);
+ }
+
}
diff --git a/core/modules/hal/tests/src/Functional/FileDenormalizeTest.php b/core/modules/hal/tests/src/Functional/FileDenormalizeTest.php
index 05ee23489394..9c00902988d4 100644
--- a/core/modules/hal/tests/src/Functional/FileDenormalizeTest.php
+++ b/core/modules/hal/tests/src/Functional/FileDenormalizeTest.php
@@ -20,6 +20,20 @@ class FileDenormalizeTest extends BrowserTestBase {
*/
public static $modules = ['hal', 'file', 'node'];
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+
+ // @todo Remove this work-around in https://www.drupal.org/node/1927648.
+ // @see hal_update_8501()
+ \Drupal::configFactory()
+ ->getEditable('hal.settings')
+ ->set('bc_file_uri_as_url_normalizer', TRUE)
+ ->save(TRUE);
+ }
+
/**
* Tests file entity denormalization.
*/
diff --git a/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php b/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php
index c4b67f54e345..355c09d1a67e 100644
--- a/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php
+++ b/core/modules/hal/tests/src/Kernel/FileNormalizeTest.php
@@ -44,7 +44,10 @@ public function testNormalize() {
$expected_array = [
'uri' => [
- ['value' => file_create_url($file->getFileUri())],
+ [
+ 'value' => $file->getFileUri(),
+ 'url' => file_url_transform_relative(file_create_url($file->getFileUri())),
+ ],
],
];
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
index 2962ef380ab2..6d0d0bd18070 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -461,8 +461,13 @@ public function testGet() {
// Note: deserialization of the XML format is not supported, so only test
// this for other formats.
if (static::$format !== 'xml') {
- $unserialized = $this->serializer->deserialize((string) $response->getBody(), get_class($this->entity), static::$format);
- $this->assertSame($unserialized->uuid(), $this->entity->uuid());
+ // @todo Work-around for HAL's FileEntityNormalizer::denormalize() being
+ // broken, being fixed in https://www.drupal.org/node/1927648, where this
+ // if-test should be removed.
+ if (!(static::$entityTypeId === 'file' && static::$format === 'hal_json')) {
+ $unserialized = $this->serializer->deserialize((string) $response->getBody(), get_class($this->entity), static::$format);
+ $this->assertSame($unserialized->uuid(), $this->entity->uuid());
+ }
}
// Finally, assert that the expected 'Link' headers are present.
if ($this->entity->getEntityType()->getLinkTemplates()) {
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php
index c63853e0bf43..1fa5a3856bb7 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/File/FileResourceTestBase.php
@@ -154,6 +154,7 @@ protected function getExpectedNormalizedEntity() {
],
'uri' => [
[
+ 'url' => base_path() . $this->siteDirectory . '/files/drupal.txt',
'value' => 'public://drupal.txt',
],
],
diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
index 094fcf10174d..b7445fb89965 100644
--- a/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
@@ -122,6 +122,7 @@ public static function getSkippedDeprecations() {
'drupal_set_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead. See https://www.drupal.org/node/2774931',
'drupal_get_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::all() or \Drupal\Core\Messenger\MessengerInterface::messagesByType() instead. See https://www.drupal.org/node/2774931',
'Adding or retrieving messages prior to the container being initialized was deprecated in Drupal 8.5.0 and this functionality will be removed before Drupal 9.0.0. Please report this usage at https://www.drupal.org/node/2928994.',
+ 'The "serializer.normalizer.file_entity.hal" normalizer service is deprecated: it is obsolete, it only remains available for backwards compatibility.',
];
}
From 4851f311e25ef59dc0b0742fe05e7560e4b1b79d Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Thu, 21 Dec 2017 10:49:53 +0000
Subject: [PATCH 050/232] Issue #2862671 by masipila, Jo Fitzgerald, kleog,
phenaproxima, quietone: Add documentation to SqlBase source plugin
---
.../src/Plugin/migrate/source/SqlBase.php | 63 +++++++++++--------
1 file changed, 38 insertions(+), 25 deletions(-)
diff --git a/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php b/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php
index 35ab8abd859e..08653b2332b6 100644
--- a/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php
+++ b/core/modules/migrate/src/Plugin/migrate/source/SqlBase.php
@@ -17,35 +17,48 @@
/**
* Sources whose data may be fetched via a database connection.
*
- * Database configuration, which may appear either within the source plugin
- * configuration or in state, is structured as follows:
+ * Available configuration keys:
+ * - database_state_key: (optional) Name of the state key which contains an
+ * array with database connection information.
+ * - key: (optional) The database key name. Defaults to 'migrate'.
+ * - target: (optional) The database target name. Defaults to 'default'.
+ * - batch_size: (optional) Number of records to fetch from the database during
+ * each batch. If omitted, all records are fetched in a single query.
+ * - ignore_map: (optional) Source data is joined to the map table by default.
+ * If set to TRUE, the map table will not be joined.
*
- * 'key' - The database key name (defaults to 'migrate').
- * 'target' - The database target name (defaults to 'default').
- * 'database' - Database connection information as accepted by
- * Database::addConnectionInfo(). If not present, the key/target is assumed
- * to already be defined (e.g., in settings.php).
+ * For other optional configuration keys inherited from the parent class, refer
+ * to \Drupal\migrate\Plugin\migrate\source\SourcePluginBase.
*
- * This configuration info is obtained in the following order:
+ * About the source database determination:
+ * - If the source plugin configuration contains 'database_state_key', its value
+ * is taken as the name of a state key which contains an array with the
+ * database configuration.
+ * - Otherwise, if the source plugin configuration contains 'key', the database
+ * configuration with that name is used.
+ * - If both 'database_state_key' and 'key' are omitted in the source plugin
+ * configuration, the database connection named 'migrate' is used by default.
+ * - If all of the above steps fail, RequirementsException is thrown.
*
- * 1. If the source plugin configuration contains a key 'database_state_key',
- * its value is taken as the name of a state key which contains an array
- * with the above database configuration.
- * 2. Otherwise, if the source plugin configuration contains 'key', the above
- * database configuration is obtained directly from the plugin configuration.
- * 3. Otherwise, if the state 'migrate.fallback_state_key' exists, its value is
- * taken as the name of a state key which contains an array with the above
- * database configuration.
- * 4. Otherwise, if a connection named 'migrate' exists, that is used as the
- * database connection.
- * 5. Otherwise, RequirementsException is thrown.
+ * Drupal Database API supports multiple database connections. The connection
+ * parameters are defined in $databases array in settings.php or
+ * settings.local.php. It is also possible to modify the $databases array in
+ * runtime. For example, Migrate Drupal, which provides the migrations from
+ * Drupal 6 / 7, asks for the source database connection parameters in the UI
+ * and then adds the $databases['migrate'] connection in runtime before the
+ * migrations are executed.
*
- * It is strongly recommended that database connections be explicitly defined
- * via 'database_state_key' or in the source plugin configuration. Defining
- * migrate.fallback_state_key or a 'migrate' connection affects not only any
- * migrations intended to use that particular connection, but all
- * SqlBase-derived source plugins which do not have explicit database
- * configuration.
+ * As described above, the default source database is $databases['migrate']. If
+ * the source plugin needs another source connection, the database connection
+ * parameters should be added to the $databases array as, for instance,
+ * $databases['foo']. The source plugin can then use this connection by setting
+ * 'key' to 'foo' in its configuration.
+ *
+ * For a complete example on migrating data from an SQL source, refer to
+ * https://www.drupal.org/docs/8/api/migrate-api/migrating-data-from-sql-source
+ *
+ * @see https://www.drupal.org/docs/8/api/database-api
+ * @see \Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase
*/
abstract class SqlBase extends SourcePluginBase implements ContainerFactoryPluginInterface, RequirementsInterface {
From aee6bea5cd929088daa019639fae1b9ddda452f1 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Thu, 21 Dec 2017 10:52:32 +0000
Subject: [PATCH 051/232] Issue #2921033 by Jo Fitzgerald, masipila,
phenaproxima, xjm, Wim Leers: Improve API documentation of DrupalSqlBase
source plugin
---
.../Plugin/migrate/source/DrupalSqlBase.php | 23 +++++++++++++------
1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php
index 445441befd60..c8b44f894e46 100644
--- a/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/DrupalSqlBase.php
@@ -13,10 +13,19 @@
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
- * A base source class for Drupal migrate sources.
+ * A base class for source plugins using a Drupal database as a source.
*
- * Mainly to let children retrieve information from the origin system in an
- * easier way.
+ * Provides general purpose helper methods that are commonly needed
+ * when writing source plugins that use a Drupal database as a source, for
+ * example:
+ * - Check if the given module exists in the source database.
+ * - Read Drupal configuration variables from the source database.
+ *
+ * For a full list, refer to the methods of this class.
+ *
+ * For available configuration keys, refer to the parent classes:
+ * @see \Drupal\migrate\Plugin\migrate\source\SqlBase
+ * @see \Drupal\migrate\Plugin\migrate\source\SourcePluginBase
*/
abstract class DrupalSqlBase extends SqlBase implements ContainerFactoryPluginInterface, DependentPluginInterface {
@@ -52,7 +61,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
}
/**
- * Retrieves all system data information from origin system.
+ * Retrieves all system data information from the source Drupal database.
*
* @return array
* List of system table information keyed by type and name.
@@ -109,7 +118,7 @@ public function checkRequirements() {
}
/**
- * Get a module schema_version value in the source installation.
+ * Retrieves a module schema_version from the source Drupal database.
*
* @param string $module
* Name of module.
@@ -124,7 +133,7 @@ protected function getModuleSchemaVersion($module) {
}
/**
- * Check to see if a given module is enabled in the source installation.
+ * Checks if a given module is enabled in the source Drupal database.
*
* @param string $module
* Name of module to check.
@@ -138,7 +147,7 @@ protected function moduleExists($module) {
}
/**
- * Read a variable from a Drupal database.
+ * Reads a variable from a source Drupal database.
*
* @param $name
* Name of the variable.
From 6ac6325024a2e7bcf996115db4f8a16474c55c36 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Thu, 21 Dec 2017 10:55:00 +0000
Subject: [PATCH 052/232] Issue #2931339 by manuel.adan: Unused variable
$admin_permission in EntityAccessControlHandler::checkAccess
---
core/lib/Drupal/Core/Entity/EntityAccessControlHandler.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/lib/Drupal/Core/Entity/EntityAccessControlHandler.php b/core/lib/Drupal/Core/Entity/EntityAccessControlHandler.php
index ac364115ebd6..c67c2ed3c30f 100644
--- a/core/lib/Drupal/Core/Entity/EntityAccessControlHandler.php
+++ b/core/lib/Drupal/Core/Entity/EntityAccessControlHandler.php
@@ -158,7 +158,7 @@ protected function checkAccess(EntityInterface $entity, $operation, AccountInter
return AccessResult::forbidden()->addCacheableDependency($entity);
}
if ($admin_permission = $this->entityType->getAdminPermission()) {
- return AccessResult::allowedIfHasPermission($account, $this->entityType->getAdminPermission());
+ return AccessResult::allowedIfHasPermission($account, $admin_permission);
}
else {
// No opinion.
From 93b9f5842664971cc82920a219503a28b0a69096 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Thu, 21 Dec 2017 10:57:43 +0000
Subject: [PATCH 053/232] Issue #2932044 by alexpott: Remove
\PHPUnit_Util_XML::cssSelect() from \Drupal\tour\Tests\TourTestBase
---
core/modules/tour/src/Tests/TourTest.php | 83 ++++++++++++++++++++
core/modules/tour/src/Tests/TourTestBase.php | 4 +-
2 files changed, 85 insertions(+), 2 deletions(-)
create mode 100644 core/modules/tour/src/Tests/TourTest.php
diff --git a/core/modules/tour/src/Tests/TourTest.php b/core/modules/tour/src/Tests/TourTest.php
new file mode 100644
index 000000000000..738bae8e2ae9
--- /dev/null
+++ b/core/modules/tour/src/Tests/TourTest.php
@@ -0,0 +1,83 @@
+ [
+ 'data-id' => 'tour-test-1',
+ 'data-class' => 'tour-test-1',
+ ],
+ ];
+
+ /**
+ * An admin user with administrative permissions for tour.
+ *
+ * @var \Drupal\user\UserInterface
+ */
+ protected $adminUser;
+
+ /**
+ * The permissions required for a logged in user to test tour tips.
+ *
+ * @var array
+ * A list of permissions.
+ */
+ protected $permissions = ['access tour'];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+
+ // Make sure we are using distinct default and administrative themes for
+ // the duration of these tests.
+ $this->container->get('theme_handler')->install(['bartik', 'seven']);
+ $this->config('system.theme')
+ ->set('default', 'bartik')
+ ->set('admin', 'seven')
+ ->save();
+
+ $this->permissions[] = 'view the administration theme';
+
+ // Create an admin user to view tour tips.
+ $this->adminUser = $this->drupalCreateUser($this->permissions);
+ $this->drupalLogin($this->adminUser);
+
+ $this->drupalPlaceBlock('local_actions_block', [
+ 'theme' => 'seven',
+ 'region' => 'content'
+ ]);
+ }
+
+ /**
+ * A simple tip test.
+ */
+ public function testTips() {
+ foreach ($this->tips as $path => $attributes) {
+ $this->drupalGet($path);
+ $this->assertTourTips($attributes);
+ }
+ }
+
+}
diff --git a/core/modules/tour/src/Tests/TourTestBase.php b/core/modules/tour/src/Tests/TourTestBase.php
index e13965070678..eca2ef8a1574 100644
--- a/core/modules/tour/src/Tests/TourTestBase.php
+++ b/core/modules/tour/src/Tests/TourTestBase.php
@@ -55,11 +55,11 @@ public function assertTourTips($tips = []) {
$modals = 0;
foreach ($tips as $tip) {
if (!empty($tip['data-id'])) {
- $elements = \PHPUnit_Util_XML::cssSelect('#' . $tip['data-id'], TRUE, $this->content, TRUE);
+ $elements = $this->xpath('//*[@id="' . $tip['data-id'] . '"]');
$this->assertTrue(!empty($elements) && count($elements) === 1, format_string('Found corresponding page element for tour tip with id #%data-id', ['%data-id' => $tip['data-id']]));
}
elseif (!empty($tip['data-class'])) {
- $elements = \PHPUnit_Util_XML::cssSelect('.' . $tip['data-class'], TRUE, $this->content, TRUE);
+ $elements = $this->xpath('//*[contain(@class, "' . $tip['data-id'] . '")]');
$this->assertFalse(empty($elements), format_string('Found corresponding page element for tour tip with class .%data-class', ['%data-class' => $tip['data-class']]));
}
else {
From 962122efc7d524ffb288791b33f18c22870334d9 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Thu, 21 Dec 2017 11:09:36 +0000
Subject: [PATCH 054/232] Issue #2884675 by rodrigoaguilera, joelpittet, TR:
Remove twig uses of Twig_Node::getLine to Twig_Node::getTemplateLine
---
core/lib/Drupal/Core/Template/TwigNodeTrans.php | 7 +++++--
core/lib/Drupal/Core/Template/TwigNodeVisitor.php | 5 +++--
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/core/lib/Drupal/Core/Template/TwigNodeTrans.php b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
index 264a511172bf..c0691dbca6ad 100644
--- a/core/lib/Drupal/Core/Template/TwigNodeTrans.php
+++ b/core/lib/Drupal/Core/Template/TwigNodeTrans.php
@@ -157,7 +157,7 @@ protected function compileString(\Twig_Node $body) {
if (!is_null($args)) {
$argName = $args->getAttribute('name');
}
- $expr = new \Twig_Node_Expression_Name($argName, $n->getLine());
+ $expr = new \Twig_Node_Expression_Name($argName, $n->getTemplateLine());
}
$placeholder = sprintf('%s%s', $argPrefix, $argName);
$text .= $placeholder;
@@ -176,7 +176,10 @@ protected function compileString(\Twig_Node $body) {
$text = $body->getAttribute('data');
}
- return [new \Twig_Node([new \Twig_Node_Expression_Constant(trim($text), $body->getLine())]), $tokens];
+ return [
+ new \Twig_Node([new \Twig_Node_Expression_Constant(trim($text), $body->getTemplateLine())]),
+ $tokens,
+ ];
}
}
diff --git a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
index 1ebfa57de92a..8a5dc05d7508 100644
--- a/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
+++ b/core/lib/Drupal/Core/Template/TwigNodeVisitor.php
@@ -33,7 +33,7 @@ protected function doLeaveNode(\Twig_Node $node, \Twig_Environment $env) {
return $node;
}
$class = get_class($node);
- $line = $node->getLine();
+ $line = $node->getTemplateLine();
return new $class(
new \Twig_Node_Expression_Function('render_var', new \Twig_Node([$node->getNode('expr')]), $line),
$line
@@ -46,7 +46,8 @@ protected function doLeaveNode(\Twig_Node $node, \Twig_Environment $env) {
// Use our own escape filter that is SafeMarkup aware.
$node->getNode('filter')->setAttribute('value', 'drupal_escape');
- // Store that we have a filter active already that knows how to deal with render arrays.
+ // Store that we have a filter active already that knows
+ // how to deal with render arrays.
$this->skipRenderVarFunction = TRUE;
}
}
From 4e5b4b09bde155d50ed3b84ee1fa399347bb83ef Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Thu, 21 Dec 2017 11:49:17 +0000
Subject: [PATCH 055/232] Issue #2931368 by opdavies: Add missing param
documentation for hook_migrate_prepare_row, document
hook_migrate_MIGRATION_ID_prepare_row
---
core/modules/migrate/migrate.api.php | 29 ++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/core/modules/migrate/migrate.api.php b/core/modules/migrate/migrate.api.php
index c6130334d09e..117c5e406205 100644
--- a/core/modules/migrate/migrate.api.php
+++ b/core/modules/migrate/migrate.api.php
@@ -98,6 +98,13 @@
*
* hook_migrate_MIGRATION_ID_prepare_row() is also available.
*
+ * @param \Drupal\migrate\Row $row
+ * The row being imported.
+ * @param \Drupal\migrate\Plugin\MigrateSourceInterface $source
+ * The source migration.
+ * @param \Drupal\migrate\Plugin\MigrationInterface $migration
+ * The current migration.
+ *
* @ingroup migration
*/
function hook_migrate_prepare_row(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) {
@@ -109,6 +116,28 @@ function hook_migrate_prepare_row(Row $row, MigrateSourceInterface $source, Migr
}
}
+/**
+ * Allows adding data to a row for a migration with the specified ID.
+ *
+ * This provides the same functionality as hook_migrate_prepare_row() but
+ * removes the need to check the value of $migration->id().
+ *
+ * @param \Drupal\migrate\Row $row
+ * The row being imported.
+ * @param \Drupal\migrate\Plugin\MigrateSourceInterface $source
+ * The source migration.
+ * @param \Drupal\migrate\Plugin\MigrationInterface $migration
+ * The current migration.
+ *
+ * @ingroup migration
+ */
+function hook_migrate_MIGRATION_ID_prepare_row(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) {
+ $value = $source->getDatabase()->query('SELECT value FROM {variable} WHERE name = :name', [':name' => 'mymodule_filter_foo_' . $row->getSourceProperty('format')])->fetchField();
+ if ($value) {
+ $row->setSourceProperty('settings:mymodule:foo', unserialize($value));
+ }
+}
+
/**
* Allows altering the list of discovered migration plugins.
*
From 03fd77c842e3606da859e5bb7c04b2da0b85c720 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Fri, 22 Dec 2017 12:47:46 +0000
Subject: [PATCH 056/232] Issue #2927806 by alexpott, mondrake, jibran, Mile23:
Use PHPUnit 6 for testing when PHP version >= 7.2
---
composer.json | 6 +-
core/composer.json | 2 +-
core/lib/Drupal/Core/Composer/Composer.php | 26 ++++
.../DisplayVariant/BlockPageVariantTest.php | 4 +-
.../tests/src/Unit/CommentLinkBuilderTest.php | 2 +-
.../tests/src/Unit/MailHandlerTest.php | 2 +-
.../EntityStateChangeValidationTest.php | 4 +-
.../Kernel/Migrate/process/d6/CckFileTest.php | 1 +
.../Plugin/migrate/process/d6/CckFileTest.php | 2 +
.../migrate/cckfield/d7/LinkCckTest.php | 2 +
.../Plugin/migrate/cckfield/LinkCckTest.php | 2 +
.../tests/src/Kernel/process/DownloadTest.php | 7 --
.../tests/src/Unit/MigrateTestCase.php | 5 -
.../PathBasedBreadcrumbBuilderTest.php | 2 -
.../{LegacyTest.php => EarlyDateTest.php} | 4 +-
...mUpdateTest.php => BulkFormUpdateTest.php} | 2 +-
.../src/Unit/Plugin/Block/ViewsBlockTest.php | 2 +-
core/phpunit.xml.dist | 5 +-
core/scripts/run-tests.sh | 6 +-
.../FunctionalTests/BrowserTestBaseTest.php | 12 +-
...acyMessengerTest.php => MessengerTest.php} | 2 +-
core/tests/Drupal/Tests/BrowserTestBase.php | 5 +
.../Component/Datetime/DateTimePlusTest.php | 37 +++++-
.../Tests/Component/Datetime/TimeTest.php | 3 +-
.../DependencyInjection/ContainerTest.php | 112 +++++++++++++++---
.../Dumper/OptimizedPhpArrayDumperTest.php | 28 ++++-
.../Component/Diff/Engine/DiffOpTest.php | 8 +-
.../Discovery/YamlDirectoryDiscoveryTest.php | 16 ++-
.../ContainerAwareEventDispatcherTest.php | 4 +-
.../FileCache/FileCacheFactoryTest.php | 8 +-
.../Component/PhpStorage/FileStorageTest.php | 10 +-
.../Component/Plugin/Context/ContextTest.php | 14 ++-
.../Component/Plugin/DefaultFactoryTest.php | 56 +++++++--
.../Plugin/Discovery/DiscoveryTraitTest.php | 14 ++-
.../StaticDiscoveryDecoratorTest.php | 7 +-
.../Plugin/Factory/ReflectionFactoryTest.php | 7 +-
.../Component/Serialization/YamlPeclTest.php | 7 +-
.../Serialization/YamlSymfonyTest.php | 15 ++-
.../Utility/ArgumentsResolverTest.php | 32 +++--
.../Tests/Component/Utility/ColorTest.php | 7 +-
.../Tests/Component/Utility/CryptTest.php | 7 +-
.../Tests/Component/Utility/HtmlTest.php | 7 +-
.../Tests/Component/Utility/RandomTest.php | 14 ++-
.../Tests/Component/Utility/RectangleTest.php | 14 ++-
.../Component/Utility/SafeMarkupTest.php | 2 +-
.../Tests/Component/Utility/UnicodeTest.php | 7 +-
.../Tests/Component/Utility/UrlHelperTest.php | 7 +-
...ityFormDisplayAccessControlHandlerTest.php | 4 -
.../Core/Entity/EntityListBuilderTest.php | 3 -
.../KeyValueEntityStorageTest.php | 2 +-
...DrupalStandardsListenerDeprecationTest.php | 3 +
.../Tests/Core/Logger/LoggerChannelTest.php | 6 +-
.../Core/PathProcessor/PathProcessorTest.php | 6 -
.../Tests/Core/Plugin/Context/ContextTest.php | 1 +
.../Core/Render/BubbleableMetadataTest.php | 1 +
.../Tests/PhpunitCompatibilityTrait.php | 25 ++++
core/tests/bootstrap.php | 17 +++
57 files changed, 485 insertions(+), 131 deletions(-)
rename core/modules/taxonomy/tests/src/Functional/{LegacyTest.php => EarlyDateTest.php} (95%)
rename core/modules/views/tests/src/Functional/Update/{LegacyBulkFormUpdateTest.php => BulkFormUpdateTest.php} (94%)
rename core/tests/Drupal/KernelTests/Core/Messenger/{LegacyMessengerTest.php => MessengerTest.php} (98%)
diff --git a/composer.json b/composer.json
index 71dcc9605cc5..3bfb0c777948 100644
--- a/composer.json
+++ b/composer.json
@@ -49,11 +49,11 @@
},
"scripts": {
"pre-autoload-dump": "Drupal\\Core\\Composer\\Composer::preAutoloadDump",
- "post-autoload-dump": [
- "Drupal\\Core\\Composer\\Composer::ensureHtaccess"
- ],
+ "post-autoload-dump": "Drupal\\Core\\Composer\\Composer::ensureHtaccess",
"post-package-install": "Drupal\\Core\\Composer\\Composer::vendorTestCodeCleanup",
"post-package-update": "Drupal\\Core\\Composer\\Composer::vendorTestCodeCleanup",
+ "post-install-cmd": "Drupal\\Core\\Composer\\Composer::upgradePHPUnit",
+ "drupal-phpunit-upgrade": "@composer update phpunit/phpunit --with-dependencies --no-progress",
"phpcs": "phpcs --standard=core/phpcs.xml.dist --runtime-set installed_paths $($COMPOSER_BINARY config vendor-dir)/drupal/coder/coder_sniffer --",
"phpcbf": "phpcbf --standard=core/phpcs.xml.dist --runtime-set installed_paths $($COMPOSER_BINARY config vendor-dir)/drupal/coder/coder_sniffer --"
},
diff --git a/core/composer.json b/core/composer.json
index 31bc51a0fa84..c4bb384b92a5 100644
--- a/core/composer.json
+++ b/core/composer.json
@@ -44,7 +44,7 @@
"jcalderonzumba/gastonjs": "^1.0.2",
"jcalderonzumba/mink-phantomjs-driver": "^0.3.1",
"mikey179/vfsStream": "^1.2",
- "phpunit/phpunit": ">=4.8.35 <5",
+ "phpunit/phpunit": "^4.8.35 || ^6.1",
"phpspec/prophecy": "^1.4",
"symfony/css-selector": "~3.2.8",
"symfony/phpunit-bridge": "^3.4.0@beta"
diff --git a/core/lib/Drupal/Core/Composer/Composer.php b/core/lib/Drupal/Core/Composer/Composer.php
index 016f93a34856..5f3a4a92ab89 100644
--- a/core/lib/Drupal/Core/Composer/Composer.php
+++ b/core/lib/Drupal/Core/Composer/Composer.php
@@ -143,6 +143,32 @@ public static function ensureHtaccess(Event $event) {
}
}
+ /**
+ * Fires the drupal-phpunit-upgrade script event if necessary.
+ *
+ * @param \Composer\Script\Event $event
+ */
+ public static function upgradePHPUnit(Event $event) {
+ $repository = $event->getComposer()->getRepositoryManager()->getLocalRepository();
+ // This is, essentially, a null constraint. We only care whether the package
+ // is present in the vendor directory yet, but findPackage() requires it.
+ $constraint = new Constraint('>', '');
+ $phpunit_package = $repository->findPackage('phpunit/phpunit', $constraint);
+ if (!$phpunit_package) {
+ // There is nothing to do. The user is probably installing using the
+ // --no-dev flag.
+ return;
+ }
+
+ // If the PHP version is 7.2 or above and PHPUnit is less than version 6
+ // call the drupal-phpunit-upgrade script to upgrade PHPUnit.
+ if (version_compare(PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, '7.2') >= 0 && version_compare($phpunit_package->getVersion(), '6.1') < 0) {
+ $event->getComposer()
+ ->getEventDispatcher()
+ ->dispatchScript('drupal-phpunit-upgrade');
+ }
+ }
+
/**
* Remove possibly problematic test files from vendored projects.
*
diff --git a/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php b/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
index 15694113534b..5d3a20b6a6d4 100644
--- a/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
+++ b/core/modules/block/tests/src/Unit/Plugin/DisplayVariant/BlockPageVariantTest.php
@@ -49,6 +49,7 @@ public function setUpDisplayVariant($configuration = [], $definition = []) {
$container = new Container();
$cache_context_manager = $this->getMockBuilder('Drupal\Core\Cache\CacheContextsManager')
->disableOriginalConstructor()
+ ->setMethods(['assertValidTokens'])
->getMock();
$container->set('cache_contexts_manager', $cache_context_manager);
$cache_context_manager->expects($this->any())
@@ -209,9 +210,6 @@ public function testBuild(array $blocks_config, $visible_block_count, array $exp
$title_block_plugin = $this->getMock('Drupal\Core\Block\TitleBlockPluginInterface');
foreach ($blocks_config as $block_id => $block_config) {
$block = $this->getMock('Drupal\block\BlockInterface');
- $block->expects($this->any())
- ->method('getContexts')
- ->willReturn([]);
$block->expects($this->atLeastOnce())
->method('getPlugin')
->willReturn($block_config[1] ? $main_content_block_plugin : ($block_config[2] ? $messages_block_plugin : ($block_config[3] ? $title_block_plugin : $block_plugin)));
diff --git a/core/modules/comment/tests/src/Unit/CommentLinkBuilderTest.php b/core/modules/comment/tests/src/Unit/CommentLinkBuilderTest.php
index 8a0a0f5871f2..8aaae7560981 100644
--- a/core/modules/comment/tests/src/Unit/CommentLinkBuilderTest.php
+++ b/core/modules/comment/tests/src/Unit/CommentLinkBuilderTest.php
@@ -269,7 +269,7 @@ public function getLinkCombinations() {
*/
protected function getMockNode($has_field, $comment_status, $form_location, $comment_count) {
$node = $this->getMock('\Drupal\node\NodeInterface');
- $node->expects($this->once())
+ $node->expects($this->any())
->method('hasField')
->willReturn($has_field);
diff --git a/core/modules/contact/tests/src/Unit/MailHandlerTest.php b/core/modules/contact/tests/src/Unit/MailHandlerTest.php
index 04a12b86df58..a96b0c69e124 100644
--- a/core/modules/contact/tests/src/Unit/MailHandlerTest.php
+++ b/core/modules/contact/tests/src/Unit/MailHandlerTest.php
@@ -367,7 +367,7 @@ protected function getAuthenticatedMockMessage($copy_sender = FALSE) {
$recipient->expects($this->once())
->method('getEmail')
->willReturn('user2@drupal.org');
- $recipient->expects($this->once())
+ $recipient->expects($this->any())
->method('getDisplayName')
->willReturn('user2');
$recipient->expects($this->once())
diff --git a/core/modules/content_moderation/tests/src/Kernel/EntityStateChangeValidationTest.php b/core/modules/content_moderation/tests/src/Kernel/EntityStateChangeValidationTest.php
index 2fefc0148c56..d72000b868be 100644
--- a/core/modules/content_moderation/tests/src/Kernel/EntityStateChangeValidationTest.php
+++ b/core/modules/content_moderation/tests/src/Kernel/EntityStateChangeValidationTest.php
@@ -217,7 +217,7 @@ public function testInvalidStateMultilingual() {
/**
* Tests that content without prior moderation information can be moderated.
*/
- public function testLegacyContent() {
+ public function testExistingContentWithNoModeration() {
$node_type = NodeType::create([
'type' => 'example',
]);
@@ -251,7 +251,7 @@ public function testLegacyContent() {
/**
* Tests that content without prior moderation information can be translated.
*/
- public function testLegacyMultilingualContent() {
+ public function testExistingMultilingualContentWithNoModeration() {
// Enable French.
ConfigurableLanguage::createFromLangcode('fr')->save();
diff --git a/core/modules/file/tests/src/Kernel/Migrate/process/d6/CckFileTest.php b/core/modules/file/tests/src/Kernel/Migrate/process/d6/CckFileTest.php
index e0e193450285..0ab8ba44fd9b 100644
--- a/core/modules/file/tests/src/Kernel/Migrate/process/d6/CckFileTest.php
+++ b/core/modules/file/tests/src/Kernel/Migrate/process/d6/CckFileTest.php
@@ -20,6 +20,7 @@ class CckFileTest extends MigrateDrupalTestBase {
* Tests configurability of file migration name.
*
* @covers ::__construct
+ * @expectedDeprecation CckFile is deprecated in Drupal 8.3.x and will be be removed before Drupal 9.0.x. Use \Drupal\file\Plugin\migrate\process\d6\FieldFile instead.
*/
public function testConfigurableFileMigration() {
$migration = Migration::create($this->container, [], 'custom_migration', []);
diff --git a/core/modules/file/tests/src/Unit/Plugin/migrate/process/d6/CckFileTest.php b/core/modules/file/tests/src/Unit/Plugin/migrate/process/d6/CckFileTest.php
index 5bad279c0dce..8781ef11b470 100644
--- a/core/modules/file/tests/src/Unit/Plugin/migrate/process/d6/CckFileTest.php
+++ b/core/modules/file/tests/src/Unit/Plugin/migrate/process/d6/CckFileTest.php
@@ -17,6 +17,8 @@ class CckFileTest extends UnitTestCase {
/**
* Tests that alt and title attributes are included in transformed values.
+ *
+ * @expectedDeprecation CckFile is deprecated in Drupal 8.3.x and will be be removed before Drupal 9.0.x. Use \Drupal\file\Plugin\migrate\process\d6\FieldFile instead.
*/
public function testTransformAltTitle() {
$executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
diff --git a/core/modules/link/tests/src/Kernel/Plugin/migrate/cckfield/d7/LinkCckTest.php b/core/modules/link/tests/src/Kernel/Plugin/migrate/cckfield/d7/LinkCckTest.php
index 896c15c9fca0..14345a60ef28 100644
--- a/core/modules/link/tests/src/Kernel/Plugin/migrate/cckfield/d7/LinkCckTest.php
+++ b/core/modules/link/tests/src/Kernel/Plugin/migrate/cckfield/d7/LinkCckTest.php
@@ -53,6 +53,8 @@ protected function setUp() {
/**
* @covers ::processCckFieldValues
+ * @expectedDeprecation CckFieldPluginBase is deprecated in Drupal 8.3.x and will be be removed before Drupal 9.0.x. Use \Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase instead.
+ * @expectedDeprecation MigrateCckFieldInterface is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.x. Use \Drupal\migrate_drupal\Annotation\MigrateField instead.
*/
public function testProcessCckFieldValues() {
$this->plugin->processFieldInstance($this->migration);
diff --git a/core/modules/link/tests/src/Unit/Plugin/migrate/cckfield/LinkCckTest.php b/core/modules/link/tests/src/Unit/Plugin/migrate/cckfield/LinkCckTest.php
index 7029d72412e9..dc3fe7731ee4 100644
--- a/core/modules/link/tests/src/Unit/Plugin/migrate/cckfield/LinkCckTest.php
+++ b/core/modules/link/tests/src/Unit/Plugin/migrate/cckfield/LinkCckTest.php
@@ -46,6 +46,8 @@ protected function setUp() {
/**
* @covers ::processCckFieldValues
+ * @expectedDeprecation CckFieldPluginBase is deprecated in Drupal 8.3.x and will be be removed before Drupal 9.0.x. Use \Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase instead.
+ * @expectedDeprecation MigrateCckFieldInterface is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.x. Use \Drupal\migrate_drupal\Annotation\MigrateField instead.
*/
public function testProcessCckFieldValues() {
$this->plugin->processCckFieldValues($this->migration, 'somefieldname', []);
diff --git a/core/modules/migrate/tests/src/Kernel/process/DownloadTest.php b/core/modules/migrate/tests/src/Kernel/process/DownloadTest.php
index 576539eb3fed..ee56e32c525d 100644
--- a/core/modules/migrate/tests/src/Kernel/process/DownloadTest.php
+++ b/core/modules/migrate/tests/src/Kernel/process/DownloadTest.php
@@ -9,7 +9,6 @@
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\Row;
use GuzzleHttp\Client;
-use GuzzleHttp\Psr7\Response;
/**
* Tests the download process plugin.
@@ -100,14 +99,8 @@ public function testWriteProtectedDestination() {
* The local URI of the downloaded file.
*/
protected function doTransform($destination_uri, $configuration = []) {
- // The HTTP client will return a file with contents 'It worked!'
- $body = fopen('data://text/plain;base64,SXQgd29ya2VkIQ==', 'r');
-
// Prepare a mock HTTP client.
$this->container->set('http_client', $this->getMock(Client::class));
- $this->container->get('http_client')
- ->method('get')
- ->willReturn(new Response(200, [], $body));
// Instantiate the plugin statically so it can pull dependencies out of
// the container.
diff --git a/core/modules/migrate/tests/src/Unit/MigrateTestCase.php b/core/modules/migrate/tests/src/Unit/MigrateTestCase.php
index 20d7662d2901..558cecb94af0 100644
--- a/core/modules/migrate/tests/src/Unit/MigrateTestCase.php
+++ b/core/modules/migrate/tests/src/Unit/MigrateTestCase.php
@@ -78,11 +78,6 @@ protected function getMigration() {
$configuration = &$this->migrationConfiguration;
- $migration->method('getHighWaterProperty')
- ->willReturnCallback(function () use ($configuration) {
- return isset($configuration['high_water_property']) ? $configuration['high_water_property'] : '';
- });
-
$migration->method('set')
->willReturnCallback(function ($argument, $value) use (&$configuration) {
$configuration[$argument] = $value;
diff --git a/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php b/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
index 2fe4a8335d0a..a24313f4affb 100644
--- a/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
+++ b/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
@@ -134,8 +134,6 @@ protected function setUp() {
->disableOriginalConstructor()
->getMock();
$cache_contexts_manager->method('assertValidTokens')->willReturn(TRUE);
- $cache_contexts_manager->expects($this->any())
- ->method('validate_tokens');
$container = new Container();
$container->set('cache_contexts_manager', $cache_contexts_manager);
\Drupal::setContainer($container);
diff --git a/core/modules/taxonomy/tests/src/Functional/LegacyTest.php b/core/modules/taxonomy/tests/src/Functional/EarlyDateTest.php
similarity index 95%
rename from core/modules/taxonomy/tests/src/Functional/LegacyTest.php
rename to core/modules/taxonomy/tests/src/Functional/EarlyDateTest.php
index 667ed5542fc6..e4490f3431aa 100644
--- a/core/modules/taxonomy/tests/src/Functional/LegacyTest.php
+++ b/core/modules/taxonomy/tests/src/Functional/EarlyDateTest.php
@@ -11,7 +11,7 @@
*
* @group taxonomy
*/
-class LegacyTest extends TaxonomyTestBase {
+class EarlyDateTest extends TaxonomyTestBase {
/**
* Modules to enable.
@@ -51,7 +51,7 @@ protected function setUp() {
/**
* Test taxonomy functionality with nodes prior to 1970.
*/
- public function testTaxonomyLegacyNode() {
+ public function testTaxonomyEarlyDateNode() {
// Posts an article with a taxonomy term and a date prior to 1970.
$date = new DrupalDateTime('1969-01-01 00:00:00');
$edit = [];
diff --git a/core/modules/views/tests/src/Functional/Update/LegacyBulkFormUpdateTest.php b/core/modules/views/tests/src/Functional/Update/BulkFormUpdateTest.php
similarity index 94%
rename from core/modules/views/tests/src/Functional/Update/LegacyBulkFormUpdateTest.php
rename to core/modules/views/tests/src/Functional/Update/BulkFormUpdateTest.php
index 75e2b54b65ec..5db54d2c67ec 100644
--- a/core/modules/views/tests/src/Functional/Update/LegacyBulkFormUpdateTest.php
+++ b/core/modules/views/tests/src/Functional/Update/BulkFormUpdateTest.php
@@ -10,7 +10,7 @@
*
* @group views
*/
-class LegacyBulkFormUpdateTest extends UpdatePathTestBase {
+class BulkFormUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
diff --git a/core/modules/views/tests/src/Unit/Plugin/Block/ViewsBlockTest.php b/core/modules/views/tests/src/Unit/Plugin/Block/ViewsBlockTest.php
index 9323b04438b2..f9bd6d2f1b72 100644
--- a/core/modules/views/tests/src/Unit/Plugin/Block/ViewsBlockTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/Block/ViewsBlockTest.php
@@ -70,7 +70,7 @@ protected function setUp() {
$this->executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
->disableOriginalConstructor()
- ->setMethods(['buildRenderable', 'setDisplay', 'setItemsPerPage'])
+ ->setMethods(['buildRenderable', 'setDisplay', 'setItemsPerPage', 'getShowAdminLinks'])
->getMock();
$this->executable->expects($this->any())
->method('setDisplay')
diff --git a/core/phpunit.xml.dist b/core/phpunit.xml.dist
index 750c6e2b502e..963d921a7e82 100644
--- a/core/phpunit.xml.dist
+++ b/core/phpunit.xml.dist
@@ -8,8 +8,7 @@
+ beStrictAboutChangesToGlobalState="true">
-
+
diff --git a/core/scripts/run-tests.sh b/core/scripts/run-tests.sh
index 5ebf9f0c9fba..407d87a34f6c 100755
--- a/core/scripts/run-tests.sh
+++ b/core/scripts/run-tests.sh
@@ -793,7 +793,11 @@ function simpletest_script_run_one_test($test_id, $test_class) {
putenv('SYMFONY_DEPRECATIONS_HELPER=disabled');
}
else {
- putenv('SYMFONY_DEPRECATIONS_HELPER=strict');
+ // Prevent deprecations caused by vendor code calling deprecated code.
+ // This also prevents mock objects in PHPUnit 6 triggering silenced
+ // deprecations from breaking the test suite. We should consider changing
+ // this to 'strict' once PHPUnit 4 is no longer used.
+ putenv('SYMFONY_DEPRECATIONS_HELPER=weak_vendors');
}
if (is_subclass_of($test_class, TestCase::class)) {
$status = simpletest_script_run_phpunit($test_id, $test_class);
diff --git a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
index 95ee39fd3a78..38e610c3b566 100644
--- a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
+++ b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
@@ -188,7 +188,7 @@ public function testInvalidLinkNotExistsExact() {
/**
* Tests legacy text asserts.
*/
- public function testLegacyTextAsserts() {
+ public function testTextAsserts() {
$this->drupalGet('test-encoded');
$dangerous = 'Bad html ';
$sanitized = Html::escape($dangerous);
@@ -202,7 +202,7 @@ public function testLegacyTextAsserts() {
/**
* Tests legacy field asserts which use xpath directly.
*/
- public function testLegacyXpathAsserts() {
+ public function testXpathAsserts() {
$this->drupalGet('test-field-xpath');
$this->assertFieldsByValue($this->xpath("//h1[@class = 'page-title']"), NULL);
$this->assertFieldsByValue($this->xpath('//table/tbody/tr[2]/td[1]'), 'one');
@@ -245,7 +245,7 @@ public function testLegacyXpathAsserts() {
/**
* Tests legacy field asserts using textfields.
*/
- public function testLegacyFieldAssertsForTextfields() {
+ public function testFieldAssertsForTextfields() {
$this->drupalGet('test-field-xpath');
// *** 1. assertNoField().
@@ -387,7 +387,7 @@ public function testLegacyFieldAssertsForTextfields() {
/**
* Tests legacy field asserts for options field type.
*/
- public function testLegacyFieldAssertsForOptions() {
+ public function testFieldAssertsForOptions() {
$this->drupalGet('test-field-xpath');
// Option field type.
@@ -443,7 +443,7 @@ public function testLegacyFieldAssertsForOptions() {
/**
* Tests legacy field asserts for button field type.
*/
- public function testLegacyFieldAssertsForButton() {
+ public function testFieldAssertsForButton() {
$this->drupalGet('test-field-xpath');
$this->assertFieldById('edit-save', NULL);
@@ -485,7 +485,7 @@ public function testLegacyFieldAssertsForButton() {
/**
* Tests legacy field asserts for checkbox field type.
*/
- public function testLegacyFieldAssertsForCheckbox() {
+ public function testFieldAssertsForCheckbox() {
$this->drupalGet('test-field-xpath');
// Part 1 - Test by name.
diff --git a/core/tests/Drupal/KernelTests/Core/Messenger/LegacyMessengerTest.php b/core/tests/Drupal/KernelTests/Core/Messenger/MessengerTest.php
similarity index 98%
rename from core/tests/Drupal/KernelTests/Core/Messenger/LegacyMessengerTest.php
rename to core/tests/Drupal/KernelTests/Core/Messenger/MessengerTest.php
index 13f10e9c2eb0..362069756eae 100644
--- a/core/tests/Drupal/KernelTests/Core/Messenger/LegacyMessengerTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Messenger/MessengerTest.php
@@ -11,7 +11,7 @@
* @group Messenger
* @coversDefaultClass \Drupal\Core\Messenger\LegacyMessenger
*/
-class LegacyMessengerTest extends KernelTestBase {
+class MessengerTest extends KernelTestBase {
/**
* Retrieves the Messenger service from LegacyMessenger.
diff --git a/core/tests/Drupal/Tests/BrowserTestBase.php b/core/tests/Drupal/Tests/BrowserTestBase.php
index 93df3dbf1319..3260f6ea016b 100644
--- a/core/tests/Drupal/Tests/BrowserTestBase.php
+++ b/core/tests/Drupal/Tests/BrowserTestBase.php
@@ -497,6 +497,11 @@ protected function setUp() {
if ($disable_gc) {
gc_enable();
}
+
+ // Ensure that the test is not marked as risky because of no assertions. In
+ // PHPUnit 6 tests that only make assertions using $this->assertSession()
+ // can be marked as risky.
+ $this->addToAssertionCount(1);
}
/**
diff --git a/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php b/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
index 297125bf63ed..b89d351c755c 100644
--- a/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
+++ b/core/tests/Drupal/Tests/Component/Datetime/DateTimePlusTest.php
@@ -87,7 +87,13 @@ public function testDateDiff($input1, $input2, $absolute, \DateInterval $expecte
* @dataProvider providerTestInvalidDateDiff
*/
public function testInvalidDateDiff($input1, $input2, $absolute) {
- $this->setExpectedException(\BadMethodCallException::class, 'Method Drupal\Component\Datetime\DateTimePlus::diff expects parameter 1 to be a \DateTime or \Drupal\Component\Datetime\DateTimePlus object');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\BadMethodCallException::class);
+ $this->expectExceptionMessage('Method Drupal\Component\Datetime\DateTimePlus::diff expects parameter 1 to be a \DateTime or \Drupal\Component\Datetime\DateTimePlus object');
+ }
+ else {
+ $this->setExpectedException(\BadMethodCallException::class, 'Method Drupal\Component\Datetime\DateTimePlus::diff expects parameter 1 to be a \DateTime or \Drupal\Component\Datetime\DateTimePlus object');
+ }
$interval = $input1->diff($input2, $absolute);
}
@@ -104,7 +110,12 @@ public function testInvalidDateDiff($input1, $input2, $absolute) {
* @dataProvider providerTestInvalidDateArrays
*/
public function testInvalidDateArrays($input, $timezone, $class) {
- $this->setExpectedException($class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException($class);
+ }
+ else {
+ $this->setExpectedException($class);
+ }
$this->assertInstanceOf(
'\Drupal\Component\DateTimePlus',
DateTimePlus::createFromArray($input, $timezone)
@@ -242,7 +253,12 @@ public function testDateFormat($input, $timezone, $format, $format_date, $expect
* @dataProvider providerTestInvalidDates
*/
public function testInvalidDates($input, $timezone, $format, $message, $class) {
- $this->setExpectedException($class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException($class);
+ }
+ else {
+ $this->setExpectedException($class);
+ }
DateTimePlus::createFromFormat($format, $input, $timezone);
}
@@ -800,7 +816,12 @@ public function testValidateFormat() {
// Parse the same date with ['validate_format' => TRUE] and make sure we
// get the expected exception.
- $this->setExpectedException(\UnexpectedValueException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\UnexpectedValueException::class);
+ }
+ else {
+ $this->setExpectedException(\UnexpectedValueException::class);
+ }
$date = DateTimePlus::createFromFormat('Y-m-d H:i:s', '11-03-31 17:44:00', 'UTC', ['validate_format' => TRUE]);
}
@@ -859,7 +880,13 @@ public function testChainableNonChainable() {
* @covers ::__call
*/
public function testChainableNonCallable() {
- $this->setExpectedException(\BadMethodCallException::class, 'Call to undefined method Drupal\Component\Datetime\DateTimePlus::nonexistent()');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\BadMethodCallException::class);
+ $this->expectExceptionMessage('Call to undefined method Drupal\Component\Datetime\DateTimePlus::nonexistent()');
+ }
+ else {
+ $this->setExpectedException(\BadMethodCallException::class, 'Call to undefined method Drupal\Component\Datetime\DateTimePlus::nonexistent()');
+ }
$date = new DateTimePlus('now', 'Australia/Sydney');
$date->setTimezone(new \DateTimeZone('America/New_York'))->nonexistent();
}
diff --git a/core/tests/Drupal/Tests/Component/Datetime/TimeTest.php b/core/tests/Drupal/Tests/Component/Datetime/TimeTest.php
index 4a5fa80205c2..ee0a2af36f26 100644
--- a/core/tests/Drupal/Tests/Component/Datetime/TimeTest.php
+++ b/core/tests/Drupal/Tests/Component/Datetime/TimeTest.php
@@ -37,8 +37,7 @@ class TimeTest extends TestCase {
protected function setUp() {
parent::setUp();
- $this->requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack');
-
+ $this->requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->getMock();
$this->time = new Time($this->requestStack);
}
diff --git a/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php b/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php
index 0e6a1472cdc5..1b5b23f347e4 100644
--- a/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php
+++ b/core/tests/Drupal/Tests/Component/DependencyInjection/ContainerTest.php
@@ -70,7 +70,12 @@ protected function setUp() {
public function testConstruct() {
$container_definition = $this->getMockContainerDefinition();
$container_definition['machine_format'] = !$this->machineFormat;
- $this->setExpectedException(InvalidArgumentException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(InvalidArgumentException::class);
+ }
+ else {
+ $this->setExpectedException(InvalidArgumentException::class);
+ }
$container = new $this->containerClass($container_definition);
}
@@ -93,7 +98,12 @@ public function testGetParameter() {
* @covers ::getAlternatives
*/
public function testGetParameterIfNotFound() {
- $this->setExpectedException(ParameterNotFoundException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(ParameterNotFoundException::class);
+ }
+ else {
+ $this->setExpectedException(ParameterNotFoundException::class);
+ }
$this->container->getParameter('parameter_that_does_not_exist');
}
@@ -103,7 +113,12 @@ public function testGetParameterIfNotFound() {
* @covers ::getParameter
*/
public function testGetParameterIfNotFoundBecauseNull() {
- $this->setExpectedException(ParameterNotFoundException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(ParameterNotFoundException::class);
+ }
+ else {
+ $this->setExpectedException(ParameterNotFoundException::class);
+ }
$this->container->getParameter(NULL);
}
@@ -137,7 +152,12 @@ public function testSetParameterWithUnfrozenContainer() {
*/
public function testSetParameterWithFrozenContainer() {
$this->container = new $this->containerClass($this->containerDefinition);
- $this->setExpectedException(LogicException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(LogicException::class);
+ }
+ else {
+ $this->setExpectedException(LogicException::class);
+ }
$this->container->setParameter('some_config', 'new_value');
}
@@ -242,7 +262,12 @@ public function testHasForAliasedService() {
* @covers ::createService
*/
public function testGetForCircularServices() {
- $this->setExpectedException(ServiceCircularReferenceException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(ServiceCircularReferenceException::class);
+ }
+ else {
+ $this->setExpectedException(ServiceCircularReferenceException::class);
+ }
$this->container->get('circular_dependency');
}
@@ -255,7 +280,12 @@ public function testGetForCircularServices() {
* @covers ::getServiceAlternatives
*/
public function testGetForNonExistantService() {
- $this->setExpectedException(ServiceNotFoundException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(ServiceNotFoundException::class);
+ }
+ else {
+ $this->setExpectedException(ServiceNotFoundException::class);
+ }
$this->container->get('service_not_exists');
}
@@ -304,7 +334,12 @@ public function testGetForParameterDependencyWithExceptionOnSecondCall() {
// Reset the service.
$this->container->set('service_parameter_not_exists', NULL);
- $this->setExpectedException(InvalidArgumentException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(InvalidArgumentException::class);
+ }
+ else {
+ $this->setExpectedException(InvalidArgumentException::class);
+ }
$this->container->get('service_parameter_not_exists');
}
@@ -316,7 +351,12 @@ public function testGetForParameterDependencyWithExceptionOnSecondCall() {
* @covers ::resolveServicesAndParameters
*/
public function testGetForNonExistantParameterDependencyWithException() {
- $this->setExpectedException(InvalidArgumentException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(InvalidArgumentException::class);
+ }
+ else {
+ $this->setExpectedException(InvalidArgumentException::class);
+ }
$this->container->get('service_parameter_not_exists');
}
@@ -341,7 +381,12 @@ public function testGetForNonExistantServiceDependency() {
* @covers ::getAlternatives
*/
public function testGetForNonExistantServiceDependencyWithException() {
- $this->setExpectedException(ServiceNotFoundException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(ServiceNotFoundException::class);
+ }
+ else {
+ $this->setExpectedException(ServiceNotFoundException::class);
+ }
$this->container->get('service_dependency_not_exists');
}
@@ -361,7 +406,12 @@ public function testGetForNonExistantServiceWhenUsingNull() {
* @covers ::createService
*/
public function testGetForNonExistantNULLService() {
- $this->setExpectedException(ServiceNotFoundException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(ServiceNotFoundException::class);
+ }
+ else {
+ $this->setExpectedException(ServiceNotFoundException::class);
+ }
$this->container->get(NULL);
}
@@ -387,7 +437,12 @@ public function testGetForNonExistantServiceMultipleTimes() {
*/
public function testGetForNonExistantServiceWithExceptionOnSecondCall() {
$this->assertNull($this->container->get('service_not_exists', ContainerInterface::NULL_ON_INVALID_REFERENCE), 'Not found service does nto throw exception.');
- $this->setExpectedException(ServiceNotFoundException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(ServiceNotFoundException::class);
+ }
+ else {
+ $this->setExpectedException(ServiceNotFoundException::class);
+ }
$this->container->get('service_not_exists');
}
@@ -423,7 +478,12 @@ public function testGetForSyntheticService() {
* @covers ::createService
*/
public function testGetForSyntheticServiceWithException() {
- $this->setExpectedException(RuntimeException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(RuntimeException::class);
+ }
+ else {
+ $this->setExpectedException(RuntimeException::class);
+ }
$this->container->get('synthetic');
}
@@ -462,7 +522,12 @@ public function testGetForInstantiationWithVariousArgumentLengths() {
* @covers ::createService
*/
public function testGetForWrongFactory() {
- $this->setExpectedException(RuntimeException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(RuntimeException::class);
+ }
+ else {
+ $this->setExpectedException(RuntimeException::class);
+ }
$this->container->get('wrong_factory');
}
@@ -500,7 +565,12 @@ public function testGetForFactoryClass() {
* @covers ::createService
*/
public function testGetForConfiguratorWithException() {
- $this->setExpectedException(InvalidArgumentException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(InvalidArgumentException::class);
+ }
+ else {
+ $this->setExpectedException(InvalidArgumentException::class);
+ }
$this->container->get('configurable_service_exception');
}
@@ -598,7 +668,12 @@ public function testResolveServicesAndParametersForOptionalServiceDependencies()
* @covers ::resolveServicesAndParameters
*/
public function testResolveServicesAndParametersForInvalidArgument() {
- $this->setExpectedException(InvalidArgumentException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(InvalidArgumentException::class);
+ }
+ else {
+ $this->setExpectedException(InvalidArgumentException::class);
+ }
$this->container->get('invalid_argument_service');
}
@@ -612,7 +687,12 @@ public function testResolveServicesAndParametersForInvalidArgument() {
public function testResolveServicesAndParametersForInvalidArguments() {
// In case the machine-optimized format is not used, we need to simulate the
// test failure.
- $this->setExpectedException(InvalidArgumentException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(InvalidArgumentException::class);
+ }
+ else {
+ $this->setExpectedException(InvalidArgumentException::class);
+ }
if (!$this->machineFormat) {
throw new InvalidArgumentException('Simulating the test failure.');
}
diff --git a/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php b/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php
index 3c447527487c..c1b70954d11b 100644
--- a/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php
+++ b/core/tests/Drupal/Tests/Component/DependencyInjection/Dumper/OptimizedPhpArrayDumperTest.php
@@ -545,7 +545,12 @@ public function testGetServiceDefinitionForDecoratedService() {
$services['bar'] = $bar_definition;
$this->containerBuilder->getDefinitions()->willReturn($services);
- $this->setExpectedException(InvalidArgumentException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(InvalidArgumentException::class);
+ }
+ else {
+ $this->setExpectedException(InvalidArgumentException::class);
+ }
$this->dumper->getArray();
}
@@ -562,7 +567,12 @@ public function testGetServiceDefinitionForExpression() {
$services['bar'] = $bar_definition;
$this->containerBuilder->getDefinitions()->willReturn($services);
- $this->setExpectedException(RuntimeException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(RuntimeException::class);
+ }
+ else {
+ $this->setExpectedException(RuntimeException::class);
+ }
$this->dumper->getArray();
}
@@ -579,7 +589,12 @@ public function testGetServiceDefinitionForObject() {
$services['bar'] = $bar_definition;
$this->containerBuilder->getDefinitions()->willReturn($services);
- $this->setExpectedException(RuntimeException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(RuntimeException::class);
+ }
+ else {
+ $this->setExpectedException(RuntimeException::class);
+ }
$this->dumper->getArray();
}
@@ -596,7 +611,12 @@ public function testGetServiceDefinitionForResource() {
$services['bar'] = $bar_definition;
$this->containerBuilder->getDefinitions()->willReturn($services);
- $this->setExpectedException(RuntimeException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(RuntimeException::class);
+ }
+ else {
+ $this->setExpectedException(RuntimeException::class);
+ }
$this->dumper->getArray();
}
diff --git a/core/tests/Drupal/Tests/Component/Diff/Engine/DiffOpTest.php b/core/tests/Drupal/Tests/Component/Diff/Engine/DiffOpTest.php
index 1a649ae510c7..dbbb6ec08176 100644
--- a/core/tests/Drupal/Tests/Component/Diff/Engine/DiffOpTest.php
+++ b/core/tests/Drupal/Tests/Component/Diff/Engine/DiffOpTest.php
@@ -4,6 +4,7 @@
use Drupal\Component\Diff\Engine\DiffOp;
use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\Error\Error;
/**
* Test DiffOp base class.
@@ -24,7 +25,12 @@ class DiffOpTest extends TestCase {
* @covers ::reverse
*/
public function testReverse() {
- $this->setExpectedException(\PHPUnit_Framework_Error::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(Error::class);
+ }
+ else {
+ $this->setExpectedException(\PHPUnit_Framework_Error::class);
+ }
$op = new DiffOp();
$result = $op->reverse();
}
diff --git a/core/tests/Drupal/Tests/Component/Discovery/YamlDirectoryDiscoveryTest.php b/core/tests/Drupal/Tests/Component/Discovery/YamlDirectoryDiscoveryTest.php
index 86134a7bdf10..9ac807d744d0 100644
--- a/core/tests/Drupal/Tests/Component/Discovery/YamlDirectoryDiscoveryTest.php
+++ b/core/tests/Drupal/Tests/Component/Discovery/YamlDirectoryDiscoveryTest.php
@@ -124,7 +124,13 @@ public function testDiscoveryAlternateId() {
* @covers ::getIdentifier
*/
public function testDiscoveryNoIdException() {
- $this->setExpectedException(DiscoveryException::class, 'The vfs://modules/test_1/item_1.test.yml contains no data in the identifier key \'id\'');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(DiscoveryException::class);
+ $this->expectExceptionMessage('The vfs://modules/test_1/item_1.test.yml contains no data in the identifier key \'id\'');
+ }
+ else {
+ $this->setExpectedException(DiscoveryException::class, 'The vfs://modules/test_1/item_1.test.yml contains no data in the identifier key \'id\'');
+ }
vfsStream::setup('modules', NULL, [
'test_1' => [
'item_1.test.yml' => "",
@@ -144,7 +150,13 @@ public function testDiscoveryNoIdException() {
* @covers ::findAll
*/
public function testDiscoveryInvalidYamlException() {
- $this->setExpectedException(DiscoveryException::class, 'The vfs://modules/test_1/item_1.test.yml contains invalid YAML');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(DiscoveryException::class);
+ $this->expectExceptionMessage('The vfs://modules/test_1/item_1.test.yml contains invalid YAML');
+ }
+ else {
+ $this->setExpectedException(DiscoveryException::class, 'The vfs://modules/test_1/item_1.test.yml contains invalid YAML');
+ }
vfsStream::setup('modules', NULL, [
'test_1' => [
'item_1.test.yml' => "id: invalid\nfoo : [bar}",
diff --git a/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php b/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php
index ea0685a7171e..a750ecaf097b 100644
--- a/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php
+++ b/core/tests/Drupal/Tests/Component/EventDispatcher/ContainerAwareEventDispatcherTest.php
@@ -38,7 +38,7 @@ public function testGetListenersWithCallables()
// When passing in callables exclusively as listeners into the event
// dispatcher constructor, the event dispatcher must not attempt to
// resolve any services.
- $container = $this->getMock(ContainerInterface::class);
+ $container = $this->getMockBuilder(ContainerInterface::class)->getMock();
$container->expects($this->never())->method($this->anything());
$firstListener = new CallableClass();
@@ -73,7 +73,7 @@ public function testDispatchWithCallables()
// When passing in callables exclusively as listeners into the event
// dispatcher constructor, the event dispatcher must not attempt to
// resolve any services.
- $container = $this->getMock(ContainerInterface::class);
+ $container = $this->getMockBuilder(ContainerInterface::class)->getMock();
$container->expects($this->never())->method($this->anything());
$firstListener = new CallableClass();
diff --git a/core/tests/Drupal/Tests/Component/FileCache/FileCacheFactoryTest.php b/core/tests/Drupal/Tests/Component/FileCache/FileCacheFactoryTest.php
index 995f5fc8510e..f9195984341a 100644
--- a/core/tests/Drupal/Tests/Component/FileCache/FileCacheFactoryTest.php
+++ b/core/tests/Drupal/Tests/Component/FileCache/FileCacheFactoryTest.php
@@ -59,7 +59,13 @@ public function testGet() {
*/
public function testGetNoPrefix() {
FileCacheFactory::setPrefix(NULL);
- $this->setExpectedException(\InvalidArgumentException::class, 'Required prefix configuration is missing');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\InvalidArgumentException::class);
+ $this->expectExceptionMessage('Required prefix configuration is missing');
+ }
+ else {
+ $this->setExpectedException(\InvalidArgumentException::class, 'Required prefix configuration is missing');
+ }
FileCacheFactory::get('test_foo_settings', []);
}
diff --git a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php
index 52e1b83c032e..168d1603e3ce 100644
--- a/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php
+++ b/core/tests/Drupal/Tests/Component/PhpStorage/FileStorageTest.php
@@ -5,7 +5,7 @@
use Drupal\Component\PhpStorage\FileStorage;
use Drupal\Component\Utility\Random;
use org\bovigo\vfs\vfsStreamDirectory;
-use PHPUnit_Framework_Error_Warning;
+use PHPUnit\Framework\Error\Warning;
/**
* @coversDefaultClass \Drupal\Component\PhpStorage\FileStorage
@@ -99,7 +99,13 @@ public function testCreateDirectoryFailWarning() {
'bin' => 'test',
]);
$code = "setExpectedException(PHPUnit_Framework_Error_Warning::class, 'mkdir(): Permission Denied');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(Warning::class);
+ $this->expectExceptionMessage('mkdir(): Permission Denied');
+ }
+ else {
+ $this->setExpectedException(\PHPUnit_Framework_Error_Warning::class, 'mkdir(): Permission Denied');
+ }
$storage->save('subdirectory/foo.php', $code);
}
diff --git a/core/tests/Drupal/Tests/Component/Plugin/Context/ContextTest.php b/core/tests/Drupal/Tests/Component/Plugin/Context/ContextTest.php
index 6a2cf4667321..aee8d0931893 100644
--- a/core/tests/Drupal/Tests/Component/Plugin/Context/ContextTest.php
+++ b/core/tests/Drupal/Tests/Component/Plugin/Context/ContextTest.php
@@ -71,10 +71,16 @@ public function testGetContextValue($expected, $context_value, $is_required, $da
// Set expectation for exception.
if ($is_required) {
- $this->setExpectedException(
- 'Drupal\Component\Plugin\Exception\ContextException',
- sprintf("The %s context is required and not present.", $data_type)
- );
+ if (method_exists($this, 'expectException')) {
+ $this->expectException('Drupal\Component\Plugin\Exception\ContextException');
+ $this->expectExceptionMessage(sprintf("The %s context is required and not present.", $data_type));
+ }
+ else {
+ $this->setExpectedException(
+ 'Drupal\Component\Plugin\Exception\ContextException',
+ sprintf("The %s context is required and not present.", $data_type)
+ );
+ }
}
// Exercise getContextValue().
diff --git a/core/tests/Drupal/Tests/Component/Plugin/DefaultFactoryTest.php b/core/tests/Drupal/Tests/Component/Plugin/DefaultFactoryTest.php
index b6bb32c84154..de24172904be 100644
--- a/core/tests/Drupal/Tests/Component/Plugin/DefaultFactoryTest.php
+++ b/core/tests/Drupal/Tests/Component/Plugin/DefaultFactoryTest.php
@@ -35,7 +35,7 @@ public function testGetPluginClassWithValidArrayPluginDefinition() {
*/
public function testGetPluginClassWithValidObjectPluginDefinition() {
$plugin_class = Cherry::class;
- $plugin_definition = $this->getMock(PluginDefinitionInterface::class);
+ $plugin_definition = $this->getMockBuilder(PluginDefinitionInterface::class)->getMock();
$plugin_definition->expects($this->atLeastOnce())
->method('getClass')
->willReturn($plugin_class);
@@ -50,7 +50,13 @@ public function testGetPluginClassWithValidObjectPluginDefinition() {
* @covers ::getPluginClass
*/
public function testGetPluginClassWithMissingClassWithArrayPluginDefinition() {
- $this->setExpectedException(PluginException::class, 'The plugin (cherry) did not specify an instance class.');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(PluginException::class);
+ $this->expectExceptionMessage('The plugin (cherry) did not specify an instance class.');
+ }
+ else {
+ $this->setExpectedException(PluginException::class, 'The plugin (cherry) did not specify an instance class.');
+ }
DefaultFactory::getPluginClass('cherry', []);
}
@@ -60,8 +66,14 @@ public function testGetPluginClassWithMissingClassWithArrayPluginDefinition() {
* @covers ::getPluginClass
*/
public function testGetPluginClassWithMissingClassWithObjectPluginDefinition() {
- $plugin_definition = $this->getMock(PluginDefinitionInterface::class);
- $this->setExpectedException(PluginException::class, 'The plugin (cherry) did not specify an instance class.');
+ $plugin_definition = $this->getMockBuilder(PluginDefinitionInterface::class)->getMock();
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(PluginException::class);
+ $this->expectExceptionMessage('The plugin (cherry) did not specify an instance class.');
+ }
+ else {
+ $this->setExpectedException(PluginException::class, 'The plugin (cherry) did not specify an instance class.');
+ }
DefaultFactory::getPluginClass('cherry', $plugin_definition);
}
@@ -71,7 +83,13 @@ public function testGetPluginClassWithMissingClassWithObjectPluginDefinition() {
* @covers ::getPluginClass
*/
public function testGetPluginClassWithNotExistingClassWithArrayPluginDefinition() {
- $this->setExpectedException(PluginException::class, 'Plugin (kiwifruit) instance class "\Drupal\plugin_test\Plugin\plugin_test\fruit\Kiwifruit" does not exist.');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(PluginException::class);
+ $this->expectExceptionMessage('Plugin (kiwifruit) instance class "\Drupal\plugin_test\Plugin\plugin_test\fruit\Kiwifruit" does not exist.');
+ }
+ else {
+ $this->setExpectedException(PluginException::class, 'Plugin (kiwifruit) instance class "\Drupal\plugin_test\Plugin\plugin_test\fruit\Kiwifruit" does not exist.');
+ }
DefaultFactory::getPluginClass('kiwifruit', ['class' => '\Drupal\plugin_test\Plugin\plugin_test\fruit\Kiwifruit']);
}
@@ -82,11 +100,16 @@ public function testGetPluginClassWithNotExistingClassWithArrayPluginDefinition(
*/
public function testGetPluginClassWithNotExistingClassWithObjectPluginDefinition() {
$plugin_class = '\Drupal\plugin_test\Plugin\plugin_test\fruit\Kiwifruit';
- $plugin_definition = $this->getMock(PluginDefinitionInterface::class);
+ $plugin_definition = $this->getMockBuilder(PluginDefinitionInterface::class)->getMock();
$plugin_definition->expects($this->atLeastOnce())
->method('getClass')
->willReturn($plugin_class);
- $this->setExpectedException(PluginException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(PluginException::class);
+ }
+ else {
+ $this->setExpectedException(PluginException::class);
+ }
DefaultFactory::getPluginClass('kiwifruit', $plugin_definition);
}
@@ -109,7 +132,7 @@ public function testGetPluginClassWithInterfaceWithArrayPluginDefinition() {
*/
public function testGetPluginClassWithInterfaceWithObjectPluginDefinition() {
$plugin_class = Cherry::class;
- $plugin_definition = $this->getMock(PluginDefinitionInterface::class);
+ $plugin_definition = $this->getMockBuilder(PluginDefinitionInterface::class)->getMock();
$plugin_definition->expects($this->atLeastOnce())
->method('getClass')
->willReturn($plugin_class);
@@ -125,7 +148,13 @@ public function testGetPluginClassWithInterfaceWithObjectPluginDefinition() {
*/
public function testGetPluginClassWithInterfaceAndInvalidClassWithArrayPluginDefinition() {
$plugin_class = Kale::class;
- $this->setExpectedException(PluginException::class, 'Plugin "cherry" (Drupal\plugin_test\Plugin\plugin_test\fruit\Kale) must implement interface Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface.');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(PluginException::class);
+ $this->expectExceptionMessage('Plugin "cherry" (Drupal\plugin_test\Plugin\plugin_test\fruit\Kale) must implement interface Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface.');
+ }
+ else {
+ $this->setExpectedException(PluginException::class, 'Plugin "cherry" (Drupal\plugin_test\Plugin\plugin_test\fruit\Kale) must implement interface Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface.');
+ }
DefaultFactory::getPluginClass('cherry', ['class' => $plugin_class, 'provider' => 'core'], FruitInterface::class);
}
@@ -136,11 +165,16 @@ public function testGetPluginClassWithInterfaceAndInvalidClassWithArrayPluginDef
*/
public function testGetPluginClassWithInterfaceAndInvalidClassWithObjectPluginDefinition() {
$plugin_class = Kale::class;
- $plugin_definition = $this->getMock(PluginDefinitionInterface::class);
+ $plugin_definition = $this->getMockBuilder(PluginDefinitionInterface::class)->getMock();
$plugin_definition->expects($this->atLeastOnce())
->method('getClass')
->willReturn($plugin_class);
- $this->setExpectedException(PluginException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(PluginException::class);
+ }
+ else {
+ $this->setExpectedException(PluginException::class);
+ }
DefaultFactory::getPluginClass('cherry', $plugin_definition, FruitInterface::class);
}
diff --git a/core/tests/Drupal/Tests/Component/Plugin/Discovery/DiscoveryTraitTest.php b/core/tests/Drupal/Tests/Component/Plugin/Discovery/DiscoveryTraitTest.php
index c37a4b5a18d3..1a5c36e7b854 100644
--- a/core/tests/Drupal/Tests/Component/Plugin/Discovery/DiscoveryTraitTest.php
+++ b/core/tests/Drupal/Tests/Component/Plugin/Discovery/DiscoveryTraitTest.php
@@ -69,7 +69,12 @@ public function testDoGetDefinitionException($expected, $definitions, $plugin_id
$method_ref = new \ReflectionMethod($trait, 'doGetDefinition');
$method_ref->setAccessible(TRUE);
// Call doGetDefinition, with $exception_on_invalid always TRUE.
- $this->setExpectedException(PluginNotFoundException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(PluginNotFoundException::class);
+ }
+ else {
+ $this->setExpectedException(PluginNotFoundException::class);
+ }
$method_ref->invoke($trait, $definitions, $plugin_id, TRUE);
}
@@ -106,7 +111,12 @@ public function testGetDefinitionException($expected, $definitions, $plugin_id)
->method('getDefinitions')
->willReturn($definitions);
// Call getDefinition(), with $exception_on_invalid always TRUE.
- $this->setExpectedException(PluginNotFoundException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(PluginNotFoundException::class);
+ }
+ else {
+ $this->setExpectedException(PluginNotFoundException::class);
+ }
$trait->getDefinition($plugin_id, TRUE);
}
diff --git a/core/tests/Drupal/Tests/Component/Plugin/Discovery/StaticDiscoveryDecoratorTest.php b/core/tests/Drupal/Tests/Component/Plugin/Discovery/StaticDiscoveryDecoratorTest.php
index a44721bd9b4e..5b010930922a 100644
--- a/core/tests/Drupal/Tests/Component/Plugin/Discovery/StaticDiscoveryDecoratorTest.php
+++ b/core/tests/Drupal/Tests/Component/Plugin/Discovery/StaticDiscoveryDecoratorTest.php
@@ -100,7 +100,12 @@ public function testGetDefinition($expected, $has_register_definitions, $excepti
$ref_decorated->setValue($mock_decorator, $mock_decorated);
if ($exception_on_invalid) {
- $this->setExpectedException('Drupal\Component\Plugin\Exception\PluginNotFoundException');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException('Drupal\Component\Plugin\Exception\PluginNotFoundException');
+ }
+ else {
+ $this->setExpectedException('Drupal\Component\Plugin\Exception\PluginNotFoundException');
+ }
}
// Exercise getDefinition(). It calls parent::getDefinition().
diff --git a/core/tests/Drupal/Tests/Component/Plugin/Factory/ReflectionFactoryTest.php b/core/tests/Drupal/Tests/Component/Plugin/Factory/ReflectionFactoryTest.php
index 7e8fbefd1d1c..20d2f76edfc5 100644
--- a/core/tests/Drupal/Tests/Component/Plugin/Factory/ReflectionFactoryTest.php
+++ b/core/tests/Drupal/Tests/Component/Plugin/Factory/ReflectionFactoryTest.php
@@ -123,7 +123,12 @@ public function testGetInstanceArguments($expected, $reflector_name, $plugin_id,
// us to use one data set for this test method as well as
// testCreateInstance().
if ($plugin_id == 'arguments_no_constructor') {
- $this->setExpectedException('\ReflectionException');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException('\ReflectionException');
+ }
+ else {
+ $this->setExpectedException('\ReflectionException');
+ }
}
// Finally invoke getInstanceArguments() on our mocked factory.
diff --git a/core/tests/Drupal/Tests/Component/Serialization/YamlPeclTest.php b/core/tests/Drupal/Tests/Component/Serialization/YamlPeclTest.php
index c2e0a0d888ee..5a71f4838101 100644
--- a/core/tests/Drupal/Tests/Component/Serialization/YamlPeclTest.php
+++ b/core/tests/Drupal/Tests/Component/Serialization/YamlPeclTest.php
@@ -87,7 +87,12 @@ public function testGetFileExtension() {
* @covers ::errorHandler
*/
public function testError() {
- $this->setExpectedException(InvalidDataTypeException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(InvalidDataTypeException::class);
+ }
+ else {
+ $this->setExpectedException(InvalidDataTypeException::class);
+ }
YamlPecl::decode('foo: [ads');
}
diff --git a/core/tests/Drupal/Tests/Component/Serialization/YamlSymfonyTest.php b/core/tests/Drupal/Tests/Component/Serialization/YamlSymfonyTest.php
index 86c818c18eaa..d857d097a525 100644
--- a/core/tests/Drupal/Tests/Component/Serialization/YamlSymfonyTest.php
+++ b/core/tests/Drupal/Tests/Component/Serialization/YamlSymfonyTest.php
@@ -59,7 +59,12 @@ public function testGetFileExtension() {
* @covers ::decode
*/
public function testError() {
- $this->setExpectedException(InvalidDataTypeException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(InvalidDataTypeException::class);
+ }
+ else {
+ $this->setExpectedException(InvalidDataTypeException::class);
+ }
YamlSymfony::decode('foo: [ads');
}
@@ -69,7 +74,13 @@ public function testError() {
* @covers ::encode
*/
public function testObjectSupportDisabled() {
- $this->setExpectedException(InvalidDataTypeException::class, 'Object support when dumping a YAML file has been disabled.');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(InvalidDataTypeException::class);
+ $this->expectExceptionMessage('Object support when dumping a YAML file has been disabled.');
+ }
+ else {
+ $this->setExpectedException(InvalidDataTypeException::class, 'Object support when dumping a YAML file has been disabled.');
+ }
$object = new \stdClass();
$object->foo = 'bar';
YamlSymfony::encode([$object]);
diff --git a/core/tests/Drupal/Tests/Component/Utility/ArgumentsResolverTest.php b/core/tests/Drupal/Tests/Component/Utility/ArgumentsResolverTest.php
index f15f14b85a4c..9099d6fa582a 100644
--- a/core/tests/Drupal/Tests/Component/Utility/ArgumentsResolverTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/ArgumentsResolverTest.php
@@ -96,9 +96,9 @@ public function testGetWildcardArgument() {
* Tests getArgument() with a Route, Request, and Account object.
*/
public function testGetArgumentOrder() {
- $a1 = $this->getMock('\Drupal\Tests\Component\Utility\Test1Interface');
- $a2 = $this->getMock('\Drupal\Tests\Component\Utility\TestClass');
- $a3 = $this->getMock('\Drupal\Tests\Component\Utility\Test2Interface');
+ $a1 = $this->getMockBuilder('\Drupal\Tests\Component\Utility\Test1Interface')->getMock();
+ $a2 = $this->getMockBuilder('\Drupal\Tests\Component\Utility\TestClass')->getMock();
+ $a3 = $this->getMockBuilder('\Drupal\Tests\Component\Utility\Test2Interface')->getMock();
$objects = [
't1' => $a1,
@@ -123,12 +123,18 @@ public function testGetArgumentOrder() {
* Without the typehint, the wildcard object will not be passed to the callable.
*/
public function testGetWildcardArgumentNoTypehint() {
- $a = $this->getMock('\Drupal\Tests\Component\Utility\Test1Interface');
+ $a = $this->getMockBuilder('\Drupal\Tests\Component\Utility\Test1Interface')->getMock();
$wildcards = [$a];
$resolver = new ArgumentsResolver([], [], $wildcards);
$callable = function ($route) {};
- $this->setExpectedException(\RuntimeException::class, 'requires a value for the "$route" argument.');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\RuntimeException::class);
+ $this->expectExceptionMessage('requires a value for the "$route" argument.');
+ }
+ else {
+ $this->setExpectedException(\RuntimeException::class, 'requires a value for the "$route" argument.');
+ }
$resolver->getArguments($callable);
}
@@ -156,7 +162,13 @@ public function testHandleNotUpcastedArgument() {
$resolver = new ArgumentsResolver($scalars, $objects, []);
$callable = function (\stdClass $foo) {};
- $this->setExpectedException(\RuntimeException::class, 'requires a value for the "$foo" argument.');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\RuntimeException::class);
+ $this->expectExceptionMessage('requires a value for the "$foo" argument.');
+ }
+ else {
+ $this->setExpectedException(\RuntimeException::class, 'requires a value for the "$foo" argument.');
+ }
$resolver->getArguments($callable);
}
@@ -167,7 +179,13 @@ public function testHandleNotUpcastedArgument() {
*/
public function testHandleUnresolvedArgument($callable) {
$resolver = new ArgumentsResolver([], [], []);
- $this->setExpectedException(\RuntimeException::class, 'requires a value for the "$foo" argument.');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\RuntimeException::class);
+ $this->expectExceptionMessage('requires a value for the "$foo" argument.');
+ }
+ else {
+ $this->setExpectedException(\RuntimeException::class, 'requires a value for the "$foo" argument.');
+ }
$resolver->getArguments($callable);
}
diff --git a/core/tests/Drupal/Tests/Component/Utility/ColorTest.php b/core/tests/Drupal/Tests/Component/Utility/ColorTest.php
index cbb9d7e8eb91..aea1779841e6 100644
--- a/core/tests/Drupal/Tests/Component/Utility/ColorTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/ColorTest.php
@@ -26,7 +26,12 @@ class ColorTest extends TestCase {
*/
public function testHexToRgb($value, $expected, $invalid = FALSE) {
if ($invalid) {
- $this->setExpectedException('InvalidArgumentException');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException('InvalidArgumentException');
+ }
+ else {
+ $this->setExpectedException('InvalidArgumentException');
+ }
}
$this->assertSame($expected, Color::hexToRgb($value));
}
diff --git a/core/tests/Drupal/Tests/Component/Utility/CryptTest.php b/core/tests/Drupal/Tests/Component/Utility/CryptTest.php
index c87628f75cda..80208ef294f5 100644
--- a/core/tests/Drupal/Tests/Component/Utility/CryptTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/CryptTest.php
@@ -77,7 +77,12 @@ public function testHmacBase64($data, $key, $expected_hmac) {
* Key to use in hashing process.
*/
public function testHmacBase64Invalid($data, $key) {
- $this->setExpectedException(\InvalidArgumentException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException('InvalidArgumentException');
+ }
+ else {
+ $this->setExpectedException('InvalidArgumentException');
+ }
Crypt::hmacBase64($data, $key);
}
diff --git a/core/tests/Drupal/Tests/Component/Utility/HtmlTest.php b/core/tests/Drupal/Tests/Component/Utility/HtmlTest.php
index 1860e041050b..a8a8af0e6c35 100644
--- a/core/tests/Drupal/Tests/Component/Utility/HtmlTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/HtmlTest.php
@@ -343,7 +343,12 @@ public function testTransformRootRelativeUrlsToAbsolute($html, $scheme_and_host,
* @dataProvider providerTestTransformRootRelativeUrlsToAbsoluteAssertion
*/
public function testTransformRootRelativeUrlsToAbsoluteAssertion($scheme_and_host) {
- $this->setExpectedException(\AssertionError::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\AssertionError::class);
+ }
+ else {
+ $this->setExpectedException(\AssertionError::class);
+ }
Html::transformRootRelativeUrlsToAbsolute('', $scheme_and_host);
}
diff --git a/core/tests/Drupal/Tests/Component/Utility/RandomTest.php b/core/tests/Drupal/Tests/Component/Utility/RandomTest.php
index 64f0eaac1bd4..7523c8be0670 100644
--- a/core/tests/Drupal/Tests/Component/Utility/RandomTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/RandomTest.php
@@ -62,7 +62,12 @@ public function testRandomNameException() {
// There are fewer than 100 possibilities so an exception should occur to
// prevent infinite loops.
$random = new Random();
- $this->setExpectedException(\RuntimeException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\RuntimeException::class);
+ }
+ else {
+ $this->setExpectedException(\RuntimeException::class);
+ }
for ($i = 0; $i <= 100; $i++) {
$str = $random->name(1, TRUE);
$names[$str] = TRUE;
@@ -78,7 +83,12 @@ public function testRandomStringException() {
// There are fewer than 100 possibilities so an exception should occur to
// prevent infinite loops.
$random = new Random();
- $this->setExpectedException(\RuntimeException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\RuntimeException::class);
+ }
+ else {
+ $this->setExpectedException(\RuntimeException::class);
+ }
for ($i = 0; $i <= 100; $i++) {
$str = $random->string(1, TRUE);
$names[$str] = TRUE;
diff --git a/core/tests/Drupal/Tests/Component/Utility/RectangleTest.php b/core/tests/Drupal/Tests/Component/Utility/RectangleTest.php
index 49d08333ef4c..ef46b7963af3 100644
--- a/core/tests/Drupal/Tests/Component/Utility/RectangleTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/RectangleTest.php
@@ -17,7 +17,12 @@ class RectangleTest extends TestCase {
* @covers ::rotate
*/
public function testWrongWidth() {
- $this->setExpectedException(\InvalidArgumentException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\InvalidArgumentException::class);
+ }
+ else {
+ $this->setExpectedException(\InvalidArgumentException::class);
+ }
$rect = new Rectangle(-40, 20);
}
@@ -27,7 +32,12 @@ public function testWrongWidth() {
* @covers ::rotate
*/
public function testWrongHeight() {
- $this->setExpectedException(\InvalidArgumentException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\InvalidArgumentException::class);
+ }
+ else {
+ $this->setExpectedException(\InvalidArgumentException::class);
+ }
$rect = new Rectangle(40, 0);
}
diff --git a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
index 7f249d9bea14..f811c16c15d8 100644
--- a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
@@ -37,7 +37,7 @@ protected function tearDown() {
* @covers ::isSafe
*/
public function testIsSafe() {
- $safe_string = $this->getMock('\Drupal\Component\Render\MarkupInterface');
+ $safe_string = $this->getMockBuilder('\Drupal\Component\Render\MarkupInterface')->getMock();
$this->assertTrue(SafeMarkup::isSafe($safe_string));
$string_object = new SafeMarkupTestString('test');
$this->assertFalse(SafeMarkup::isSafe($string_object));
diff --git a/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php b/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php
index 6bfc6cb0d2f7..ba1757ffc5ee 100644
--- a/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/UnicodeTest.php
@@ -33,7 +33,12 @@ protected function setUp() {
*/
public function testStatus($value, $expected, $invalid = FALSE) {
if ($invalid) {
- $this->setExpectedException('InvalidArgumentException');
+ if (method_exists($this, 'expectException')) {
+ $this->expectException('InvalidArgumentException');
+ }
+ else {
+ $this->setExpectedException('InvalidArgumentException');
+ }
}
Unicode::setStatus($value);
$this->assertEquals($expected, Unicode::getStatus());
diff --git a/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php b/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
index db78cd9458a5..d185219c9a2a 100644
--- a/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/UrlHelperTest.php
@@ -578,7 +578,12 @@ public function providerTestExternalIsLocal() {
* @dataProvider providerTestExternalIsLocalInvalid
*/
public function testExternalIsLocalInvalid($url, $base_url) {
- $this->setExpectedException(\InvalidArgumentException::class);
+ if (method_exists($this, 'expectException')) {
+ $this->expectException(\InvalidArgumentException::class);
+ }
+ else {
+ $this->setExpectedException(\InvalidArgumentException::class);
+ }
UrlHelper::externalIsLocal($url, $base_url);
}
diff --git a/core/tests/Drupal/Tests/Core/Entity/Access/EntityFormDisplayAccessControlHandlerTest.php b/core/tests/Drupal/Tests/Core/Entity/Access/EntityFormDisplayAccessControlHandlerTest.php
index 1ee804d5398a..a92d76abb70d 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Access/EntityFormDisplayAccessControlHandlerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Access/EntityFormDisplayAccessControlHandlerTest.php
@@ -164,10 +164,6 @@ protected function setUp() {
->willReturnMap([
['entity_display', $storage_access_control_handler],
]);
- $entity_type_manager
- ->expects($this->any())
- ->method('getFieldDefinitions')
- ->willReturn([]);
$entity_type_manager
->expects($this->any())
->method('getDefinition')
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php
index ad68e2df68e4..55928b98a6e2 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityListBuilderTest.php
@@ -123,9 +123,6 @@ public function testGetOperations() {
$url = $this->getMockBuilder('\Drupal\Core\Url')
->disableOriginalConstructor()
->getMock();
- $url->expects($this->any())
- ->method('toArray')
- ->will($this->returnValue([]));
$url->expects($this->atLeastOnce())
->method('mergeOptions')
->with(['query' => ['destination' => '/foo/bar']]);
diff --git a/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php
index 4a2dac5653e0..846ce0a59f2f 100644
--- a/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/KeyValueStore/KeyValueEntityStorageTest.php
@@ -361,7 +361,7 @@ public function testSaveConfigEntity() {
$this->assertSame('foo', $entity->getOriginalId());
$expected = ['id' => 'foo'];
- $entity->expects($this->once())
+ $entity->expects($this->atLeastOnce())
->method('toArray')
->will($this->returnValue($expected));
diff --git a/core/tests/Drupal/Tests/Core/Listeners/DrupalStandardsListenerDeprecationTest.php b/core/tests/Drupal/Tests/Core/Listeners/DrupalStandardsListenerDeprecationTest.php
index 61b62118af14..ad3930440b89 100644
--- a/core/tests/Drupal/Tests/Core/Listeners/DrupalStandardsListenerDeprecationTest.php
+++ b/core/tests/Drupal/Tests/Core/Listeners/DrupalStandardsListenerDeprecationTest.php
@@ -21,6 +21,7 @@
* would trigger another deprecation error.
*
* @group Listeners
+ * @group legacy
*
* @coversDefaultClass \Drupal\deprecation_test\Deprecation\FixtureDeprecatedClass
*/
@@ -29,6 +30,8 @@ class DrupalStandardsListenerDeprecationTest extends UnitTestCase {
/**
* Exercise DrupalStandardsListener's coverage validation.
*
+ * @expectedDeprecation Drupal\deprecation_test\Deprecation\FixtureDeprecatedClass is deprecated.
+ *
* @covers ::testFunction
*/
public function testDeprecation() {
diff --git a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php
index 2512602afaf2..0f934ebf8e67 100644
--- a/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php
+++ b/core/tests/Drupal/Tests/Core/Logger/LoggerChannelTest.php
@@ -102,12 +102,12 @@ public function testSortLoggers() {
*/
public function providerTestLog() {
$account_mock = $this->getMock('Drupal\Core\Session\AccountInterface');
- $account_mock->expects($this->exactly(2))
+ $account_mock->expects($this->any())
->method('id')
->will($this->returnValue(1));
- $request_mock = $this->getMock('Symfony\Component\HttpFoundation\Request');
- $request_mock->expects($this->exactly(2))
+ $request_mock = $this->getMock('Symfony\Component\HttpFoundation\Request', ['getClientIp']);
+ $request_mock->expects($this->any())
->method('getClientIp')
->will($this->returnValue('127.0.0.1'));
$request_mock->headers = $this->getMock('Symfony\Component\HttpFoundation\ParameterBag');
diff --git a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
index 929930a5216b..e6215caac834 100644
--- a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
+++ b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
@@ -76,12 +76,6 @@ protected function setUp() {
$language_manager->expects($this->any())
->method('getLanguageTypes')
->will($this->returnValue([LanguageInterface::TYPE_INTERFACE]));
- $language_manager->expects($this->any())
- ->method('getNegotiationMethods')
- ->will($this->returnValue($method_definitions));
- $language_manager->expects($this->any())
- ->method('getNegotiationMethodInstance')
- ->will($this->returnValue($method_instance));
$method_instance->setLanguageManager($language_manager);
$this->languageManager = $language_manager;
diff --git a/core/tests/Drupal/Tests/Core/Plugin/Context/ContextTest.php b/core/tests/Drupal/Tests/Core/Plugin/Context/ContextTest.php
index 2190d5391878..7dead3d32e23 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/Context/ContextTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/Context/ContextTest.php
@@ -104,6 +104,7 @@ public function testSetContextValueCacheableDependency() {
$container = new Container();
$cache_context_manager = $this->getMockBuilder('Drupal\Core\Cache\CacheContextsManager')
->disableOriginalConstructor()
+ ->setMethods(['validateTokens'])
->getMock();
$container->set('cache_contexts_manager', $cache_context_manager);
$cache_context_manager->expects($this->any())
diff --git a/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php b/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php
index 1fca8179a832..a881e7c2ce72 100644
--- a/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/BubbleableMetadataTest.php
@@ -38,6 +38,7 @@ public function testMerge(BubbleableMetadata $a, CacheableMetadata $b, Bubbleabl
if (!$b instanceof BubbleableMetadata) {
$renderer = $this->getMockBuilder('Drupal\Core\Render\Renderer')
->disableOriginalConstructor()
+ ->setMethods(['mergeAttachments'])
->getMock();
$renderer->expects($this->never())
->method('mergeAttachments');
diff --git a/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php b/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php
index 5cf020a8df6b..ee14f005a6f8 100644
--- a/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php
+++ b/core/tests/Drupal/Tests/PhpunitCompatibilityTrait.php
@@ -116,6 +116,31 @@ public function createMock($originalClassName) {
}
}
+ /**
+ * Compatibility layer for PHPUnit 6 to support PHPUnit 4 code.
+ *
+ * @param mixed $class
+ * The expected exception class.
+ * @param string $message
+ * The expected exception message.
+ * @param int $exception_code
+ * The expected exception code.
+ */
+ public function setExpectedException($class, $message = '', $exception_code = NULL) {
+ if (method_exists($this, 'expectException')) {
+ $this->expectException($class);
+ if (!empty($message)) {
+ $this->expectExceptionMessage($message);
+ }
+ if ($exception_code !== NULL) {
+ $this->expectExceptionCode($exception_code);
+ }
+ }
+ else {
+ parent::setExpectedException($class, $message, $exception_code);
+ }
+ }
+
/**
* Checks if the trait is used in a class that has a method.
*
diff --git a/core/tests/bootstrap.php b/core/tests/bootstrap.php
index f78b69ff0498..50fef61ee685 100644
--- a/core/tests/bootstrap.php
+++ b/core/tests/bootstrap.php
@@ -8,6 +8,7 @@
*/
use Drupal\Component\Assertion\Handle;
+use PHPUnit\Runner\Version;
/**
* Finds all valid extension directories recursively within a given directory.
@@ -166,3 +167,19 @@ function drupal_phpunit_populate_class_loader() {
// make PHP 5 and 7 handle assertion failures the same way, but this call does
// not turn runtime assertions on if they weren't on already.
Handle::register();
+
+// PHPUnit 4 to PHPUnit 6 bridge. Tests written for PHPUnit 4 need to work on
+// PHPUnit 6 with a minimum of fuss.
+if (class_exists('PHPUnit\Runner\Version') && version_compare(Version::id(), '6.1', '>=')) {
+ class_alias('\PHPUnit\Framework\AssertionFailedError', '\PHPUnit_Framework_AssertionFailedError');
+ class_alias('\PHPUnit\Framework\Constraint\Count', '\PHPUnit_Framework_Constraint_Count');
+ class_alias('\PHPUnit\Framework\Error\Error', '\PHPUnit_Framework_Error');
+ class_alias('\PHPUnit\Framework\Error\Warning', '\PHPUnit_Framework_Error_Warning');
+ class_alias('\PHPUnit\Framework\ExpectationFailedException', '\PHPUnit_Framework_ExpectationFailedException');
+ class_alias('\PHPUnit\Framework\Exception', '\PHPUnit_Framework_Exception');
+ class_alias('\PHPUnit\Framework\MockObject\Matcher\InvokedRecorder', '\PHPUnit_Framework_MockObject_Matcher_InvokedRecorder');
+ class_alias('\PHPUnit\Framework\SkippedTestError', '\PHPUnit_Framework_SkippedTestError');
+ class_alias('\PHPUnit\Framework\TestCase', '\PHPUnit_Framework_TestCase');
+ class_alias('\PHPUnit\Util\Test', '\PHPUnit_Util_Test');
+ class_alias('\PHPUnit\Util\XML', '\PHPUnit_Util_XML');
+}
From a83b06a46b228eea09f19a1636b14609dd22652b Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Fri, 22 Dec 2017 13:15:21 +0000
Subject: [PATCH 057/232] Issue #2923015 by cburschka, amateescu, tstoeckler,
alexpott, hchonov, mondrake, pfrenssen: [PHP 7.2] Incompatible method
declarations
---
core/lib/Drupal/Core/Entity/EntityDisplayBase.php | 4 ++--
core/lib/Drupal/Core/Field/FieldItemList.php | 12 ------------
.../src/Plugin/Menu/LocalTask/UnapprovedComments.php | 3 ++-
.../comment/src/Tests/Views/CommentTestBase.php | 4 ++--
.../ConfigTranslationContextualLink.php | 3 ++-
.../Menu/LocalTask/ConfigTranslationLocalTask.php | 3 ++-
core/modules/datetime/src/DateTimeComputed.php | 2 +-
.../src/Tests/Views/DateTimeHandlerTestBase.php | 4 ++--
core/modules/field/src/Tests/Views/FieldTestBase.php | 4 ++--
core/modules/field/src/Tests/Views/FieldUITest.php | 4 ++--
.../field/src/Tests/Views/HandlerFieldFieldTest.php | 4 ++--
.../src/Tests/Views/RelationshipUserFileDataTest.php | 4 ++--
core/modules/forum/src/Form/Overview.php | 3 ++-
.../Tests/Views/RelationshipUserImageDataTest.php | 4 ++--
.../layout_builder/src/Form/UpdateBlockForm.php | 4 +++-
.../link/src/Tests/Views/LinkViewsTokensTest.php | 4 ++--
.../src/Plugin/migrate/source/EmbeddedDataSource.php | 2 +-
.../src/Plugin/migrate/source/EmptySource.php | 2 +-
.../statistics/src/Tests/Views/IntegrationTest.php | 4 ++--
.../Menu/ContextualLink/TestContextualLink.php | 3 ++-
.../src/Plugin/Menu/LocalAction/TestLocalAction.php | 3 ++-
.../src/Plugin/Menu/LocalAction/TestLocalAction4.php | 3 ++-
.../src/Plugin/Menu/LocalAction/TestLocalAction5.php | 3 ++-
.../Menu/LocalAction/TestLocalActionWithConfig.php | 3 ++-
.../Plugin/Menu/LocalTask/TestTasksSettingsSub1.php | 3 ++-
.../tracker/src/Tests/Views/TrackerTestBase.php | 4 ++--
core/modules/views/src/Tests/FieldApiDataTest.php | 4 ++--
.../views/src/Tests/Plugin/DisplayFeedTest.php | 4 ++--
.../modules/views/src/Tests/Plugin/StyleOpmlTest.php | 4 ++--
core/modules/views/src/Tests/ViewAjaxTest.php | 4 ++--
.../views/src/Tests/Wizard/WizardTestBase.php | 4 ++--
core/modules/views_ui/src/Tests/UITestBase.php | 4 ++--
32 files changed, 60 insertions(+), 60 deletions(-)
diff --git a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
index 7c736e8adfa5..34ce858b13dc 100644
--- a/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityDisplayBase.php
@@ -250,7 +250,7 @@ public function id() {
/**
* {@inheritdoc}
*/
- public function preSave(EntityStorageInterface $storage, $update = TRUE) {
+ public function preSave(EntityStorageInterface $storage) {
// Ensure that a region is set on each component.
foreach ($this->getComponents() as $name => $component) {
$this->handleHiddenType($name, $component);
@@ -263,7 +263,7 @@ public function preSave(EntityStorageInterface $storage, $update = TRUE) {
ksort($this->content);
ksort($this->hidden);
- parent::preSave($storage, $update);
+ parent::preSave($storage);
}
/**
diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php
index 152005bcf34b..52bf9c9d23df 100644
--- a/core/lib/Drupal/Core/Field/FieldItemList.php
+++ b/core/lib/Drupal/Core/Field/FieldItemList.php
@@ -95,18 +95,6 @@ public function filterEmptyItems() {
return $this;
}
- /**
- * {@inheritdoc}
- * @todo Revisit the need when all entity types are converted to NG entities.
- */
- public function getValue($include_computed = FALSE) {
- $values = [];
- foreach ($this->list as $delta => $item) {
- $values[$delta] = $item->getValue($include_computed);
- }
- return $values;
- }
-
/**
* {@inheritdoc}
*/
diff --git a/core/modules/comment/src/Plugin/Menu/LocalTask/UnapprovedComments.php b/core/modules/comment/src/Plugin/Menu/LocalTask/UnapprovedComments.php
index 89542d34ff3f..e946bfb9f8ac 100644
--- a/core/modules/comment/src/Plugin/Menu/LocalTask/UnapprovedComments.php
+++ b/core/modules/comment/src/Plugin/Menu/LocalTask/UnapprovedComments.php
@@ -7,6 +7,7 @@
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
/**
* Provides a local task that shows the amount of unapproved comments.
@@ -53,7 +54,7 @@ public static function create(ContainerInterface $container, array $configuratio
/**
* {@inheritdoc}
*/
- public function getTitle() {
+ public function getTitle(Request $request = NULL) {
return $this->t('Unapproved comments (@count)', ['@count' => $this->commentStorage->getUnapprovedCount()]);
}
diff --git a/core/modules/comment/src/Tests/Views/CommentTestBase.php b/core/modules/comment/src/Tests/Views/CommentTestBase.php
index 3d10858351f3..c364e2631f52 100644
--- a/core/modules/comment/src/Tests/Views/CommentTestBase.php
+++ b/core/modules/comment/src/Tests/Views/CommentTestBase.php
@@ -63,8 +63,8 @@ abstract class CommentTestBase extends ViewTestBase {
*/
protected $comment;
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['comment_test_views']);
diff --git a/core/modules/config_translation/src/Plugin/Menu/ContextualLink/ConfigTranslationContextualLink.php b/core/modules/config_translation/src/Plugin/Menu/ContextualLink/ConfigTranslationContextualLink.php
index 63145a5d3b32..fff82f0c7299 100644
--- a/core/modules/config_translation/src/Plugin/Menu/ContextualLink/ConfigTranslationContextualLink.php
+++ b/core/modules/config_translation/src/Plugin/Menu/ContextualLink/ConfigTranslationContextualLink.php
@@ -5,6 +5,7 @@
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Menu\ContextualLinkDefault;
use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\HttpFoundation\Request;
/**
* Defines a contextual link plugin with a dynamic title.
@@ -22,7 +23,7 @@ class ConfigTranslationContextualLink extends ContextualLinkDefault {
/**
* {@inheritdoc}
*/
- public function getTitle() {
+ public function getTitle(Request $request = NULL) {
// Use the custom 'config_translation_plugin_id' plugin definition key to
// retrieve the title. We need to retrieve a runtime title (as opposed to
// storing the title on the plugin definition for the link) because it
diff --git a/core/modules/config_translation/src/Plugin/Menu/LocalTask/ConfigTranslationLocalTask.php b/core/modules/config_translation/src/Plugin/Menu/LocalTask/ConfigTranslationLocalTask.php
index 5a88d91d0abd..0aa84d224317 100644
--- a/core/modules/config_translation/src/Plugin/Menu/LocalTask/ConfigTranslationLocalTask.php
+++ b/core/modules/config_translation/src/Plugin/Menu/LocalTask/ConfigTranslationLocalTask.php
@@ -5,6 +5,7 @@
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Menu\LocalTaskDefault;
use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\HttpFoundation\Request;
/**
* Defines a local task plugin with a dynamic title.
@@ -22,7 +23,7 @@ class ConfigTranslationLocalTask extends LocalTaskDefault {
/**
* {@inheritdoc}
*/
- public function getTitle() {
+ public function getTitle(Request $request = NULL) {
// Take custom 'config_translation_plugin_id' plugin definition key to
// retrieve title. We need to retrieve a runtime title (as opposed to
// storing the title on the plugin definition for the link) because
diff --git a/core/modules/datetime/src/DateTimeComputed.php b/core/modules/datetime/src/DateTimeComputed.php
index 2208ab7cb770..73fb11786270 100644
--- a/core/modules/datetime/src/DateTimeComputed.php
+++ b/core/modules/datetime/src/DateTimeComputed.php
@@ -37,7 +37,7 @@ public function __construct(DataDefinitionInterface $definition, $name = NULL, T
/**
* {@inheritdoc}
*/
- public function getValue($langcode = NULL) {
+ public function getValue() {
if ($this->date !== NULL) {
return $this->date;
}
diff --git a/core/modules/datetime/src/Tests/Views/DateTimeHandlerTestBase.php b/core/modules/datetime/src/Tests/Views/DateTimeHandlerTestBase.php
index 0f068ab9ee0d..854352eb5bd2 100644
--- a/core/modules/datetime/src/Tests/Views/DateTimeHandlerTestBase.php
+++ b/core/modules/datetime/src/Tests/Views/DateTimeHandlerTestBase.php
@@ -43,8 +43,8 @@ abstract class DateTimeHandlerTestBase extends HandlerTestBase {
/**
* {@inheritdoc}
*/
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
// Add a date field to page nodes.
$node_type = NodeType::create([
diff --git a/core/modules/field/src/Tests/Views/FieldTestBase.php b/core/modules/field/src/Tests/Views/FieldTestBase.php
index f04e19fb9bdf..c01e9604cf15 100644
--- a/core/modules/field/src/Tests/Views/FieldTestBase.php
+++ b/core/modules/field/src/Tests/Views/FieldTestBase.php
@@ -42,8 +42,8 @@ abstract class FieldTestBase extends ViewTestBase {
*/
public $fields;
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
// Ensure the page node type exists.
NodeType::create([
diff --git a/core/modules/field/src/Tests/Views/FieldUITest.php b/core/modules/field/src/Tests/Views/FieldUITest.php
index 851b7bdff9a9..efe768505b67 100644
--- a/core/modules/field/src/Tests/Views/FieldUITest.php
+++ b/core/modules/field/src/Tests/Views/FieldUITest.php
@@ -38,8 +38,8 @@ class FieldUITest extends FieldTestBase {
/**
* {@inheritdoc}
*/
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
$this->account = $this->drupalCreateUser(['administer views']);
$this->drupalLogin($this->account);
diff --git a/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php b/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php
index dfbb89bd209d..da958e90f510 100644
--- a/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php
+++ b/core/modules/field/src/Tests/Views/HandlerFieldFieldTest.php
@@ -41,8 +41,8 @@ class HandlerFieldFieldTest extends FieldTestBase {
/**
* {@inheritdoc}
*/
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
// Setup basic fields.
$this->setUpFieldStorages(3);
diff --git a/core/modules/file/src/Tests/Views/RelationshipUserFileDataTest.php b/core/modules/file/src/Tests/Views/RelationshipUserFileDataTest.php
index 6a5f53b5d368..45e016d1698c 100644
--- a/core/modules/file/src/Tests/Views/RelationshipUserFileDataTest.php
+++ b/core/modules/file/src/Tests/Views/RelationshipUserFileDataTest.php
@@ -30,8 +30,8 @@ class RelationshipUserFileDataTest extends ViewTestBase {
*/
public static $testViews = ['test_file_user_file_data'];
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
// Create the user profile field and instance.
FieldStorageConfig::create([
diff --git a/core/modules/forum/src/Form/Overview.php b/core/modules/forum/src/Form/Overview.php
index a51446b17311..efc926641818 100644
--- a/core/modules/forum/src/Form/Overview.php
+++ b/core/modules/forum/src/Form/Overview.php
@@ -8,6 +8,7 @@
use Drupal\Core\Url;
use Drupal\taxonomy\Form\OverviewTerms;
use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\taxonomy\VocabularyInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
@@ -47,7 +48,7 @@ public function getFormId() {
/**
* {@inheritdoc}
*/
- public function buildForm(array $form, FormStateInterface $form_state) {
+ public function buildForm(array $form, FormStateInterface $form_state, VocabularyInterface $taxonomy_vocabulary = NULL) {
$forum_config = $this->config('forum.settings');
$vid = $forum_config->get('vocabulary');
$vocabulary = $this->entityManager->getStorage('taxonomy_vocabulary')->load($vid);
diff --git a/core/modules/image/src/Tests/Views/RelationshipUserImageDataTest.php b/core/modules/image/src/Tests/Views/RelationshipUserImageDataTest.php
index 55cecc0fc09d..374981f63aa0 100644
--- a/core/modules/image/src/Tests/Views/RelationshipUserImageDataTest.php
+++ b/core/modules/image/src/Tests/Views/RelationshipUserImageDataTest.php
@@ -30,8 +30,8 @@ class RelationshipUserImageDataTest extends ViewTestBase {
*/
public static $testViews = ['test_image_user_image_data'];
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
// Create the user profile field and instance.
FieldStorageConfig::create([
diff --git a/core/modules/layout_builder/src/Form/UpdateBlockForm.php b/core/modules/layout_builder/src/Form/UpdateBlockForm.php
index 3cc36585a11a..443deedaeb82 100644
--- a/core/modules/layout_builder/src/Form/UpdateBlockForm.php
+++ b/core/modules/layout_builder/src/Form/UpdateBlockForm.php
@@ -35,11 +35,13 @@ public function getFormId() {
* The region of the block.
* @param string $uuid
* The UUID of the block being updated.
+ * @param array $configuration
+ * (optional) The array of configuration for the block.
*
* @return array
* The form array.
*/
- public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $entity = NULL, $delta = NULL, $region = NULL, $uuid = NULL) {
+ public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $entity = NULL, $delta = NULL, $region = NULL, $uuid = NULL, array $configuration = []) {
/** @var \Drupal\layout_builder\SectionStorageInterface $field_list */
$field_list = $entity->layout_builder__layout;
$plugin = $field_list->getSection($delta)->getComponent($uuid)->getPlugin();
diff --git a/core/modules/link/src/Tests/Views/LinkViewsTokensTest.php b/core/modules/link/src/Tests/Views/LinkViewsTokensTest.php
index 80913ece8dd6..968c02f8306a 100644
--- a/core/modules/link/src/Tests/Views/LinkViewsTokensTest.php
+++ b/core/modules/link/src/Tests/Views/LinkViewsTokensTest.php
@@ -38,8 +38,8 @@ class LinkViewsTokensTest extends ViewTestBase {
/**
* {@inheritdoc}
*/
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['link_test_views']);
// Create Basic page node type.
diff --git a/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php b/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php
index 8cee6ddbe4d5..11f32cf563ea 100644
--- a/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php
+++ b/core/modules/migrate/src/Plugin/migrate/source/EmbeddedDataSource.php
@@ -108,7 +108,7 @@ public function getIds() {
/**
* {@inheritdoc}
*/
- public function count() {
+ public function count($refresh = FALSE) {
return count($this->dataRows);
}
diff --git a/core/modules/migrate/src/Plugin/migrate/source/EmptySource.php b/core/modules/migrate/src/Plugin/migrate/source/EmptySource.php
index 5f44035e2545..09f8a95792b4 100644
--- a/core/modules/migrate/src/Plugin/migrate/source/EmptySource.php
+++ b/core/modules/migrate/src/Plugin/migrate/source/EmptySource.php
@@ -58,7 +58,7 @@ public function getIds() {
/**
* {@inheritdoc}
*/
- public function count() {
+ public function count($refresh = FALSE) {
return 1;
}
diff --git a/core/modules/statistics/src/Tests/Views/IntegrationTest.php b/core/modules/statistics/src/Tests/Views/IntegrationTest.php
index 18f55815879a..740ecf5e53f3 100644
--- a/core/modules/statistics/src/Tests/Views/IntegrationTest.php
+++ b/core/modules/statistics/src/Tests/Views/IntegrationTest.php
@@ -42,8 +42,8 @@ class IntegrationTest extends ViewTestBase {
*/
public static $testViews = ['test_statistics_integration'];
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['statistics_test_views']);
diff --git a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/ContextualLink/TestContextualLink.php b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/ContextualLink/TestContextualLink.php
index 271922936e19..8257d32232fd 100644
--- a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/ContextualLink/TestContextualLink.php
+++ b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/ContextualLink/TestContextualLink.php
@@ -3,6 +3,7 @@
namespace Drupal\menu_test\Plugin\Menu\ContextualLink;
use Drupal\Core\Menu\ContextualLinkDefault;
+use Symfony\Component\HttpFoundation\Request;
/**
* Defines a contextual link plugin with a dynamic title from user input.
@@ -12,7 +13,7 @@ class TestContextualLink extends ContextualLinkDefault {
/**
* {@inheritdoc}
*/
- public function getTitle() {
+ public function getTitle(Request $request = NULL) {
return "";
}
diff --git a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction.php b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction.php
index 4b9289eef123..13f2e5a0235c 100644
--- a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction.php
+++ b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction.php
@@ -3,6 +3,7 @@
namespace Drupal\menu_test\Plugin\Menu\LocalAction;
use Drupal\Core\Menu\LocalActionDefault;
+use Symfony\Component\HttpFoundation\Request;
/**
* Defines a test local action plugin class.
@@ -12,7 +13,7 @@ class TestLocalAction extends LocalActionDefault {
/**
* {@inheritdoc}
*/
- public function getTitle() {
+ public function getTitle(Request $request = NULL) {
return 'Title override';
}
diff --git a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction4.php b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction4.php
index 1f8300a4ba47..eb8b1893c5d7 100644
--- a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction4.php
+++ b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction4.php
@@ -4,6 +4,7 @@
use Drupal\Core\Menu\LocalActionDefault;
use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\HttpFoundation\Request;
/**
* Defines a local action plugin with a dynamic title.
@@ -15,7 +16,7 @@ class TestLocalAction4 extends LocalActionDefault {
/**
* {@inheritdoc}
*/
- public function getTitle() {
+ public function getTitle(Request $request = NULL) {
return $this->t('My @arg action', ['@arg' => 'dynamic-title']);
}
diff --git a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction5.php b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction5.php
index 0bd7fc38755a..e960e2661305 100644
--- a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction5.php
+++ b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalAction5.php
@@ -3,6 +3,7 @@
namespace Drupal\menu_test\Plugin\Menu\LocalAction;
use Drupal\Core\Menu\LocalActionDefault;
+use Symfony\Component\HttpFoundation\Request;
/**
* Defines a local action plugin with a dynamic title from user input.
@@ -12,7 +13,7 @@ class TestLocalAction5 extends LocalActionDefault {
/**
* {@inheritdoc}
*/
- public function getTitle() {
+ public function getTitle(Request $request = NULL) {
return "";
}
diff --git a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalActionWithConfig.php b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalActionWithConfig.php
index 54a2f42246fb..4f56977e8690 100644
--- a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalActionWithConfig.php
+++ b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalAction/TestLocalActionWithConfig.php
@@ -6,6 +6,7 @@
use Drupal\Core\Menu\LocalActionDefault;
use Drupal\Core\Routing\RouteProviderInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
/**
* Defines a test local action plugin class.
@@ -20,7 +21,7 @@ class TestLocalActionWithConfig extends LocalActionDefault {
/**
* {@inheritdoc}
*/
- public function getTitle() {
+ public function getTitle(Request $request = NULL) {
return $this->config->get('title');
}
diff --git a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalTask/TestTasksSettingsSub1.php b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalTask/TestTasksSettingsSub1.php
index 7ba7548846c0..61ac9705a083 100644
--- a/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalTask/TestTasksSettingsSub1.php
+++ b/core/modules/system/tests/modules/menu_test/src/Plugin/Menu/LocalTask/TestTasksSettingsSub1.php
@@ -4,6 +4,7 @@
use Drupal\Core\Menu\LocalTaskDefault;
use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\HttpFoundation\Request;
class TestTasksSettingsSub1 extends LocalTaskDefault {
@@ -12,7 +13,7 @@ class TestTasksSettingsSub1 extends LocalTaskDefault {
/**
* {@inheritdoc}
*/
- public function getTitle() {
+ public function getTitle(Request $request = NULL) {
return $this->t('Dynamic title for @class', ['@class' => 'TestTasksSettingsSub1']);
}
diff --git a/core/modules/tracker/src/Tests/Views/TrackerTestBase.php b/core/modules/tracker/src/Tests/Views/TrackerTestBase.php
index 9746a22a51d1..4dfaab03bc4f 100644
--- a/core/modules/tracker/src/Tests/Views/TrackerTestBase.php
+++ b/core/modules/tracker/src/Tests/Views/TrackerTestBase.php
@@ -41,8 +41,8 @@ abstract class TrackerTestBase extends ViewTestBase {
*/
protected $comment;
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
ViewTestData::createTestViews(get_class($this), ['tracker_test_views']);
diff --git a/core/modules/views/src/Tests/FieldApiDataTest.php b/core/modules/views/src/Tests/FieldApiDataTest.php
index f6d5159dd857..d2d035cfcc11 100644
--- a/core/modules/views/src/Tests/FieldApiDataTest.php
+++ b/core/modules/views/src/Tests/FieldApiDataTest.php
@@ -35,8 +35,8 @@ class FieldApiDataTest extends FieldTestBase {
*/
protected $translationNodes;
- protected function setUp() {
- parent::setUp(FALSE);
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
$field_names = $this->setUpFieldStorages(4);
diff --git a/core/modules/views/src/Tests/Plugin/DisplayFeedTest.php b/core/modules/views/src/Tests/Plugin/DisplayFeedTest.php
index e8d254917653..04ba4cab993e 100644
--- a/core/modules/views/src/Tests/Plugin/DisplayFeedTest.php
+++ b/core/modules/views/src/Tests/Plugin/DisplayFeedTest.php
@@ -26,8 +26,8 @@ class DisplayFeedTest extends PluginTestBase {
*/
public static $modules = ['block', 'node', 'views'];
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
$this->enableViewsTestModule();
diff --git a/core/modules/views/src/Tests/Plugin/StyleOpmlTest.php b/core/modules/views/src/Tests/Plugin/StyleOpmlTest.php
index 4e84433dca17..66a8bc826be4 100644
--- a/core/modules/views/src/Tests/Plugin/StyleOpmlTest.php
+++ b/core/modules/views/src/Tests/Plugin/StyleOpmlTest.php
@@ -27,8 +27,8 @@ class StyleOpmlTest extends PluginTestBase {
/**
* {@inheritdoc}
*/
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
$this->enableViewsTestModule();
diff --git a/core/modules/views/src/Tests/ViewAjaxTest.php b/core/modules/views/src/Tests/ViewAjaxTest.php
index 5a966a8f76ba..820d9e2bc040 100644
--- a/core/modules/views/src/Tests/ViewAjaxTest.php
+++ b/core/modules/views/src/Tests/ViewAjaxTest.php
@@ -19,8 +19,8 @@ class ViewAjaxTest extends ViewTestBase {
*/
public static $testViews = ['test_ajax_view', 'test_view'];
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
$this->enableViewsTestModule();
}
diff --git a/core/modules/views/src/Tests/Wizard/WizardTestBase.php b/core/modules/views/src/Tests/Wizard/WizardTestBase.php
index ba7509c8658d..29b96d38c198 100644
--- a/core/modules/views/src/Tests/Wizard/WizardTestBase.php
+++ b/core/modules/views/src/Tests/Wizard/WizardTestBase.php
@@ -21,8 +21,8 @@ abstract class WizardTestBase extends ViewTestBase {
*/
public static $modules = ['node', 'views_ui', 'block', 'rest'];
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
// Create and log in a user with administer views permission.
$views_admin = $this->drupalCreateUser(['administer views', 'administer blocks', 'bypass node access', 'access user profiles', 'view all revisions']);
diff --git a/core/modules/views_ui/src/Tests/UITestBase.php b/core/modules/views_ui/src/Tests/UITestBase.php
index 83f2f85980c4..752be6e339b4 100644
--- a/core/modules/views_ui/src/Tests/UITestBase.php
+++ b/core/modules/views_ui/src/Tests/UITestBase.php
@@ -36,8 +36,8 @@ abstract class UITestBase extends ViewTestBase {
/**
* {@inheritdoc}
*/
- protected function setUp() {
- parent::setUp();
+ protected function setUp($import_test_views = TRUE) {
+ parent::setUp($import_test_views);
$this->enableViewsTestModule();
From 26235da61b9616053a7eaa49b909a05bc921d346 Mon Sep 17 00:00:00 2001
From: Nathaniel Catchpole
Date: Fri, 22 Dec 2017 14:36:08 +0000
Subject: [PATCH 058/232] Issue #2930788 by marcoscano, phenaproxima, Berdir,
seanB: Do not show name by default in media displays
---
core/modules/media/src/Entity/Media.php | 7 +-
core/modules/media/templates/media.html.twig | 12 --
.../FunctionalJavascript/MediaDisplayTest.php | 170 ++++++++++++++++++
.../media/tests/src/Kernel/MediaTest.php | 7 +-
...entity_view_display.media.file.default.yml | 9 +-
...ntity_view_display.media.image.default.yml | 9 +-
6 files changed, 178 insertions(+), 36 deletions(-)
create mode 100644 core/modules/media/tests/src/FunctionalJavascript/MediaDisplayTest.php
diff --git a/core/modules/media/src/Entity/Media.php b/core/modules/media/src/Entity/Media.php
index 49f7283f7e08..dc8e0dcf0fd5 100644
--- a/core/modules/media/src/Entity/Media.php
+++ b/core/modules/media/src/Entity/Media.php
@@ -404,12 +404,7 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
'weight' => -5,
])
->setDisplayConfigurable('form', TRUE)
- ->setDisplayConfigurable('view', TRUE)
- ->setDisplayOptions('view', [
- 'label' => 'hidden',
- 'type' => 'string',
- 'weight' => -5,
- ]);
+ ->setDisplayConfigurable('view', TRUE);
$fields['thumbnail'] = BaseFieldDefinition::create('image')
->setLabel(t('Thumbnail'))
diff --git a/core/modules/media/templates/media.html.twig b/core/modules/media/templates/media.html.twig
index 41731992ec0f..28c0a83ca64e 100644
--- a/core/modules/media/templates/media.html.twig
+++ b/core/modules/media/templates/media.html.twig
@@ -32,17 +32,5 @@
*/
#}
- {#
- In the 'full' view mode the entity label is assumed to be displayed as the
- page title, so we do not display it here.
- #}
- {{ title_prefix }}
- {% if label and view_mode != 'full' %}
-
diff --git a/core/modules/media/tests/src/FunctionalJavascript/MediaDisplayTest.php b/core/modules/media/tests/src/FunctionalJavascript/MediaDisplayTest.php
new file mode 100644
index 000000000000..3bd1ccbc099f
--- /dev/null
+++ b/core/modules/media/tests/src/FunctionalJavascript/MediaDisplayTest.php
@@ -0,0 +1,170 @@
+container->get('config.installer')->installOptionalConfig($storage, '');
+ // Reset all the static caches and list caches.
+ $this->container->get('config.factory')->reset();
+ }
+
+ /**
+ * Test basic media display.
+ */
+ public function testMediaDisplay() {
+ $assert_session = $this->assertSession();
+ $page = $this->getSession()->getPage();
+
+ $media_type = $this->createMediaType();
+
+ // Create a media item.
+ $media = Media::create([
+ 'bundle' => $media_type->id(),
+ 'name' => 'Fantastic!',
+ ]);
+ $media->save();
+
+ $this->drupalGet('media/' . $media->id());
+ // Verify the "name" field is really not present.
+ $assert_session->elementNotExists('css', '.field--name-name');
+
+ // Enable the field on the display and verify it becomes visible on the UI.
+ $this->drupalGet("/admin/structure/media/manage/{$media_type->id()}/display");
+ $page->selectFieldOption('fields[name][region]', 'content');
+ $assert_session->waitForElementVisible('css', '#edit-fields-name-settings-edit');
+ $page->pressButton('Save');
+ $this->drupalGet('media/' . $media->id());
+ // Verify the name is present, and its text matches what is expected.
+ $assert_session->elementExists('css', '.field--name-name');
+ $name_field = $page->find('css', '.field--name-name .field__item');
+ $this->assertEquals($media->label(), $name_field->getText());
+
+ // In the standard profile, there are some pre-cooked types. Make sure the
+ // elements configured on their displays are the expected ones.
+ $this->drupalGet('media/add/image');
+ $image_media_name = 'Fantastic image asset!';
+ $page->fillField('name[0][value]', $image_media_name);
+ $page->attachFileToField('files[field_media_image_0]', \Drupal::root() . '/core/modules/media/tests/fixtures/example_1.jpeg');
+ $result = $assert_session->waitForButton('Remove');
+ $this->assertNotEmpty($result);
+ $page->fillField('field_media_image[0][alt]', 'Image Alt Text 1');
+ $page->pressButton('Save');
+ $image_media_id = $this->container->get('entity.query')->get('media')
+ ->sort('mid', 'DESC')
+ ->execute();
+ $image_media_id = reset($image_media_id);
+
+ // Here we expect to see only the image, nothing else.
+ // Assert only one element in the content region.
+ $this->assertEquals(1, count($page->findAll('css', '.media--type-image > div')));
+ // Assert the image is present inside the media element, with "medium"
+ // image style.
+ $media_item = $assert_session->elementExists('css', '.media--type-image > div');
+ $assert_session->elementExists('css', 'img.image-style-medium', $media_item);
+
+ $test_filename = $this->randomMachineName() . '.txt';
+ $test_filepath = 'public://' . $test_filename;
+ file_put_contents($test_filepath, $this->randomMachineName());
+ $this->drupalGet("media/add/file");
+ $page->fillField('name[0][value]', 'File media 1');
+ $page->attachFileToField("files[field_media_file_0]", \Drupal::service('file_system')->realpath($test_filepath));
+ $result = $assert_session->waitForButton('Remove');
+ $this->assertNotEmpty($result);
+ $page->pressButton('Save');
+
+ // Here we expect to see only the linked filename.
+ // Assert only one element in the content region.
+ $this->assertEquals(1, count($page->findAll('css', 'article.media--type-file > div')));
+ // Assert the file link is present, and its text matches the filename.
+ $assert_session->elementExists('css', 'article.media--type-file .field--name-field-media-file a');
+ $link = $page->find('css', 'article.media--type-file .field--name-field-media-file a');
+ $this->assertEquals($test_filename, $link->getText());
+
+ // Create a node type "page" to use as host entity.
+ $node_type = NodeType::create([
+ 'type' => 'page',
+ 'name' => 'Page',
+ ]);
+ $node_type->save();
+
+ // Reference the created media using an entity_refernce field and make sure
+ // the output is what we expect.
+ $storage = FieldStorageConfig::create([
+ 'entity_type' => 'node',
+ 'field_name' => 'field_related_media',
+ 'type' => 'entity_reference',
+ 'settings' => [
+ 'target_type' => 'media',
+ ],
+ ]);
+ $storage->save();
+
+ FieldConfig::create([
+ 'field_storage' => $storage,
+ 'entity_type' => 'node',
+ 'bundle' => $node_type->id(),
+ 'label' => 'Related media',
+ 'settings' => [
+ 'handler_settings' => [
+ 'target_bundles' => [
+ 'image' => 'image',
+ ],
+ ],
+ ],
+ ])->save();
+
+ entity_get_display('node', $node_type->id(), 'default')
+ ->setComponent('field_related_media', [
+ 'type' => 'entity_reference_entity_view',
+ 'label' => 'hidden',
+ 'settings' => [
+ 'view_mode' => 'full',
+ ],
+ ])->save();
+
+ $node = Node::create([
+ 'title' => 'Host node',
+ 'type' => $node_type->id(),
+ 'field_related_media' => [
+ 'target_id' => $image_media_id,
+ ],
+ ]);
+ $node->save();
+
+ $this->drupalGet('/node/' . $node->id());
+ // Media field is there.
+ $assert_session->elementExists('css', '.field--name-field-related-media');
+ // Media name element is not there.
+ $assert_session->elementNotExists('css', '.field--name-name');
+ $assert_session->pageTextNotContains($image_media_name);
+ // Only one element is present inside the media container.
+ $this->assertEquals(1, count($page->findAll('css', '.field--name-field-related-media article.media--type-image > div')));
+ // Assert the image is present, with "medium" image style.
+ $assert_session->elementExists('css', '.field--name-field-related-media article.media--type-image img.image-style-medium');
+ }
+
+}
diff --git a/core/modules/media/tests/src/Kernel/MediaTest.php b/core/modules/media/tests/src/Kernel/MediaTest.php
index d246fb669eb3..df48a9bf764d 100644
--- a/core/modules/media/tests/src/Kernel/MediaTest.php
+++ b/core/modules/media/tests/src/Kernel/MediaTest.php
@@ -21,14 +21,17 @@ public function testEntity() {
}
/**
- * Ensure media name is configurable on manage display.
+ * Tests the Media "name" base field behavior.
*/
- public function testNameIsConfigurable() {
+ public function testNameBaseField() {
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $field_definitions */
$field_definitions = $this->container->get('entity_field.manager')
->getBaseFieldDefinitions('media');
+ // Ensure media name is configurable on manage display.
$this->assertTrue($field_definitions['name']->isDisplayConfigurable('view'));
+ // Ensure it is not visible by default.
+ $this->assertEquals($field_definitions['name']->getDisplayOptions('view'), ['region' => 'hidden']);
}
}
diff --git a/core/profiles/standard/config/optional/core.entity_view_display.media.file.default.yml b/core/profiles/standard/config/optional/core.entity_view_display.media.file.default.yml
index f42bab7bb743..72b0bdc1846c 100644
--- a/core/profiles/standard/config/optional/core.entity_view_display.media.file.default.yml
+++ b/core/profiles/standard/config/optional/core.entity_view_display.media.file.default.yml
@@ -18,15 +18,8 @@ content:
type: file_default
weight: 1
region: content
- name:
- label: hidden
- type: string
- weight: 0
- region: content
- settings:
- link_to_entity: false
- third_party_settings: { }
hidden:
created: true
thumbnail: true
uid: true
+ name: true
diff --git a/core/profiles/standard/config/optional/core.entity_view_display.media.image.default.yml b/core/profiles/standard/config/optional/core.entity_view_display.media.image.default.yml
index c02d9084304a..eee9348a2399 100644
--- a/core/profiles/standard/config/optional/core.entity_view_display.media.image.default.yml
+++ b/core/profiles/standard/config/optional/core.entity_view_display.media.image.default.yml
@@ -21,15 +21,8 @@ content:
type: image
weight: 1
region: content
- name:
- label: hidden
- type: string
- weight: 0
- region: content
- settings:
- link_to_entity: false
- third_party_settings: { }
hidden:
created: true
thumbnail: true
uid: true
+ name: true
From 824fff57f3d5b77b7f2f1240b2f68a20a807b7bc Mon Sep 17 00:00:00 2001
From: webchick
Date: Fri, 22 Dec 2017 11:01:50 -0800
Subject: [PATCH 059/232] Issue #2899708 by gaurav.kapoor, tedbow, droplet, Wim
Leers: `quote` should be `blockquote` in off-canvas.base.css
---
core/misc/dialog/off-canvas.base.css | 2 +-
core/themes/stable/css/core/dialog/off-canvas.base.css | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/core/misc/dialog/off-canvas.base.css b/core/misc/dialog/off-canvas.base.css
index a0cb45698386..4326f61eedcf 100644
--- a/core/misc/dialog/off-canvas.base.css
+++ b/core/misc/dialog/off-canvas.base.css
@@ -149,7 +149,7 @@
#drupal-off-canvas ol li {
display: block;
}
-#drupal-off-canvas quote,
+#drupal-off-canvas blockquote,
#drupal-off-canvas code {
margin: 20px 0;
}
diff --git a/core/themes/stable/css/core/dialog/off-canvas.base.css b/core/themes/stable/css/core/dialog/off-canvas.base.css
index 3c4964600300..4eded024b9bc 100644
--- a/core/themes/stable/css/core/dialog/off-canvas.base.css
+++ b/core/themes/stable/css/core/dialog/off-canvas.base.css
@@ -149,7 +149,7 @@
#drupal-off-canvas ol li {
display: block;
}
-#drupal-off-canvas quote,
+#drupal-off-canvas blockquote,
#drupal-off-canvas code {
margin: 20px 0;
}
From abebbfb06dae1c7007aae8567356bef82bd227b2 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Sat, 23 Dec 2017 07:57:44 +1000
Subject: [PATCH 060/232] Issue #2845361 by claudiu.cristea, Munavijayalakshmi:
Don't compute children and parents of a new term on TermForm
---
core/modules/taxonomy/src/TermForm.php | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/core/modules/taxonomy/src/TermForm.php b/core/modules/taxonomy/src/TermForm.php
index bb0b10e8998f..6de48e0af4b6 100644
--- a/core/modules/taxonomy/src/TermForm.php
+++ b/core/modules/taxonomy/src/TermForm.php
@@ -38,14 +38,17 @@ public function form(array $form, FormStateInterface $form_state) {
// before loading the full vocabulary. Contrib modules can then intercept
// before hook_form_alter to provide scalable alternatives.
if (!$this->config('taxonomy.settings')->get('override_selector')) {
- $parent = array_keys($taxonomy_storage->loadParents($term->id()));
- $children = $taxonomy_storage->loadTree($vocabulary->id(), $term->id());
-
- // A term can't be the child of itself, nor of its children.
- foreach ($children as $child) {
- $exclude[] = $child->tid;
+ $exclude = [];
+ if (!$term->isNew()) {
+ $parent = array_keys($taxonomy_storage->loadParents($term->id()));
+ $children = $taxonomy_storage->loadTree($vocabulary->id(), $term->id());
+
+ // A term can't be the child of itself, nor of its children.
+ foreach ($children as $child) {
+ $exclude[] = $child->tid;
+ }
+ $exclude[] = $term->id();
}
- $exclude[] = $term->id();
$tree = $taxonomy_storage->loadTree($vocabulary->id());
$options = ['<' . $this->t('root') . '>'];
From f9548751b86a7dfb5842a5eb99b37c0b42c1d45d Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Sat, 23 Dec 2017 07:59:09 +1000
Subject: [PATCH 061/232] Issue #2346893 by lauriii, idebr, slashrsm,
RavindraSingh, Rade, Fabianx, alexpott, swentel, gauravjeet, darrenwh,
deepak_zyxware, joelpittet, Wim Leers, Yogesh Pawar, Vj, ivan.chavarro,
josephdpurcell, josmera01, rloos289, kattekrab, Tanvish Jha, csakiistvan,
xjm, larowlan, akalata: Duplicate AJAX wrapper around a file field
---
core/lib/Drupal/Core/Render/Renderer.php | 16 +++-
.../file/src/Tests/FileFieldValidateTest.php | 21 +++++
.../src/Tests/ImageFieldValidateTest.php | 22 +++++
.../KernelTests/Core/Render/RenderTest.php | 19 +++-
.../Core/Render/RendererBubblingTest.php | 36 ++++++++
.../Drupal/Tests/Core/Render/RendererTest.php | 91 +++++++++++++++++--
6 files changed, 191 insertions(+), 14 deletions(-)
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index 5038851edefd..00246943f547 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -509,11 +509,17 @@ protected function doRender(&$elements, $is_root_call = FALSE) {
// We store the resulting output in $elements['#markup'], to be consistent
// with how render cached output gets stored. This ensures that placeholder
// replacement logic gets the same data to work with, no matter if #cache is
- // disabled, #cache is enabled, there is a cache hit or miss.
- $prefix = isset($elements['#prefix']) ? $this->xssFilterAdminIfUnsafe($elements['#prefix']) : '';
- $suffix = isset($elements['#suffix']) ? $this->xssFilterAdminIfUnsafe($elements['#suffix']) : '';
-
- $elements['#markup'] = Markup::create($prefix . $elements['#children'] . $suffix);
+ // disabled, #cache is enabled, there is a cache hit or miss. If
+ // #render_children is set the #prefix and #suffix will have already been
+ // added.
+ if (isset($elements['#render_children'])) {
+ $elements['#markup'] = Markup::create($elements['#children']);
+ }
+ else {
+ $prefix = isset($elements['#prefix']) ? $this->xssFilterAdminIfUnsafe($elements['#prefix']) : '';
+ $suffix = isset($elements['#suffix']) ? $this->xssFilterAdminIfUnsafe($elements['#suffix']) : '';
+ $elements['#markup'] = Markup::create($prefix . $elements['#children'] . $suffix);
+ }
// We've rendered this element (and its subtree!), now update the context.
$context->update($elements);
diff --git a/core/modules/file/src/Tests/FileFieldValidateTest.php b/core/modules/file/src/Tests/FileFieldValidateTest.php
index 4698185c4f6a..be96f3c587ee 100644
--- a/core/modules/file/src/Tests/FileFieldValidateTest.php
+++ b/core/modules/file/src/Tests/FileFieldValidateTest.php
@@ -187,4 +187,25 @@ public function testFileRemoval() {
$this->assertText('Article ' . $node->getTitle() . ' has been updated.');
}
+ /**
+ * Test the validation message is displayed only once for ajax uploads.
+ */
+ public function testAJAXValidationMessage() {
+ $field_name = strtolower($this->randomMachineName());
+ $this->createFileField($field_name, 'node', 'article');
+
+ $this->drupalGet('node/add/article');
+ /** @var \Drupal\file\FileInterface $image_file */
+ $image_file = $this->getTestFile('image');
+ $edit = [
+ 'files[' . $field_name . '_0]' => $this->container->get('file_system')->realpath($image_file->getFileUri()),
+ 'title[0][value]' => $this->randomMachineName(),
+ ];
+ $this->drupalPostAjaxForm(NULL, $edit, $field_name . '_0_upload_button');
+ $elements = $this->xpath('//div[contains(@class, :class)]', [
+ ':class' => 'messages--error',
+ ]);
+ $this->assertEqual(count($elements), 1, 'Ajax validation messages are displayed once.');
+ }
+
}
diff --git a/core/modules/image/src/Tests/ImageFieldValidateTest.php b/core/modules/image/src/Tests/ImageFieldValidateTest.php
index f630a24f7cfb..98210d642199 100644
--- a/core/modules/image/src/Tests/ImageFieldValidateTest.php
+++ b/core/modules/image/src/Tests/ImageFieldValidateTest.php
@@ -161,4 +161,26 @@ protected function getFieldSettings($min_resolution, $max_resolution) {
];
}
+ /**
+ * Test the validation message is displayed only once for ajax uploads.
+ */
+ public function testAJAXValidationMessage() {
+ $field_name = strtolower($this->randomMachineName());
+ $this->createImageField($field_name, 'article', ['cardinality' => -1]);
+
+ $this->drupalGet('node/add/article');
+ /** @var \Drupal\file\FileInterface[] $text_files */
+ $text_files = $this->drupalGetTestFiles('text');
+ $text_file = reset($text_files);
+ $edit = [
+ 'files[' . $field_name . '_0][]' => $this->container->get('file_system')->realpath($text_file->uri),
+ 'title[0][value]' => $this->randomMachineName(),
+ ];
+ $this->drupalPostAjaxForm(NULL, $edit, $field_name . '_0_upload_button');
+ $elements = $this->xpath('//div[contains(@class, :class)]', [
+ ':class' => 'messages--error',
+ ]);
+ $this->assertEqual(count($elements), 1, 'Ajax validation messages are displayed once.');
+ }
+
}
diff --git a/core/tests/Drupal/KernelTests/Core/Render/RenderTest.php b/core/tests/Drupal/KernelTests/Core/Render/RenderTest.php
index a64b553a16dd..2acaefb873b6 100644
--- a/core/tests/Drupal/KernelTests/Core/Render/RenderTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Render/RenderTest.php
@@ -16,7 +16,7 @@ class RenderTest extends KernelTestBase {
*
* @var array
*/
- public static $modules = ['system', 'common_test'];
+ public static $modules = ['system', 'common_test', 'theme_test'];
/**
* Tests theme preprocess functions being able to attach assets.
@@ -43,6 +43,23 @@ public function testDrupalRenderThemePreprocessAttached() {
\Drupal::state()->set('theme_preprocess_attached_test', FALSE);
}
+ /**
+ * Ensures that render array children are processed correctly.
+ */
+ public function testRenderChildren() {
+ // Ensure that #prefix and #suffix is only being printed once since that is
+ // the behaviour the caller code expects.
+ $build = [
+ '#type' => 'container',
+ '#theme' => 'theme_test_render_element_children',
+ '#prefix' => 'kangaroo',
+ '#suffix' => 'kitten',
+ ];
+ $this->render($build);
+ $this->removeWhiteSpace();
+ $this->assertNoRaw('
kangarookitten
');
+ }
+
/**
* Tests that we get an exception when we try to attach an illegal type.
*/
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
index 573febcc2a4d..f55e34832e08 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererBubblingTest.php
@@ -291,6 +291,42 @@ public function providerTestContextBubblingEdgeCases() {
];
$data[] = [$test_element, ['bar', 'foo'], $expected_cache_items];
+ // Ensure that bubbleable metadata has been collected from children and set
+ // correctly to the main level of the render array. That ensures that correct
+ // bubbleable metadata exists if render array gets rendered multiple times.
+ $test_element = [
+ '#cache' => [
+ 'keys' => ['parent'],
+ 'tags' => ['yar', 'har']
+ ],
+ '#markup' => 'parent',
+ 'child' => [
+ '#render_children' => TRUE,
+ 'subchild' => [
+ '#cache' => [
+ 'contexts' => ['foo'],
+ 'tags' => ['fiddle', 'dee'],
+ ],
+ '#attached' => [
+ 'library' => ['foo/bar']
+ ],
+ '#markup' => '',
+ ]
+ ],
+ ];
+ $expected_cache_items = [
+ 'parent:foo' => [
+ '#attached' => ['library' => ['foo/bar']],
+ '#cache' => [
+ 'contexts' => ['foo'],
+ 'tags' => ['dee', 'fiddle', 'har', 'yar'],
+ 'max-age' => Cache::PERMANENT,
+ ],
+ '#markup' => 'parent',
+ ],
+ ];
+ $data[] = [$test_element, ['foo'], $expected_cache_items];
+
return $data;
}
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTest.php b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
index 52569eade9c5..eda1f9fc6057 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
@@ -403,6 +403,25 @@ public function providerTestRenderBasic() {
};
$data[] = [$build, 'baz', $setup_code];
+ // #theme is implemented but #render_children is TRUE. In this case the
+ // calling code is expecting only the children to be rendered. #prefix and
+ // #suffix should not be inherited for the children.
+ $build = [
+ '#theme' => 'common_test_foo',
+ '#children' => '',
+ '#prefix' => 'kangaroo',
+ '#suffix' => 'unicorn',
+ '#render_children' => TRUE,
+ 'child' => [
+ '#markup' => 'kitten',
+ ],
+ ];
+ $setup_code = function () {
+ $this->themeManager->expects($this->never())
+ ->method('render');
+ };
+ $data[] = [$build, 'kitten', $setup_code];
+
return $data;
}
@@ -562,25 +581,81 @@ public function testRenderAccessCacheabilityDependencyInheritance() {
}
/**
- * Tests that a first render returns the rendered output and a second doesn't.
+ * Tests rendering same render array twice.
*
- * (Because of the #printed property.)
+ * Tests that a first render returns the rendered output and a second doesn't
+ * because of the #printed property. Also tests that correct metadata has been
+ * set for re-rendering.
*
* @covers ::render
* @covers ::doRender
+ *
+ * @dataProvider providerRenderTwice
*/
- public function testRenderTwice() {
- $build = [
- '#markup' => 'test',
- ];
-
- $this->assertEquals('test', $this->renderer->renderRoot($build));
+ public function testRenderTwice($build) {
+ $this->assertEquals('kittens', $this->renderer->renderRoot($build));
+ $this->assertEquals('kittens', $build['#markup']);
+ $this->assertEquals(['kittens-147'], $build['#cache']['tags']);
$this->assertTrue($build['#printed']);
// We don't want to reprint already printed render arrays.
$this->assertEquals('', $this->renderer->renderRoot($build));
}
+ /**
+ * Provides a list of render array iterations.
+ *
+ * @return array
+ */
+ public function providerRenderTwice() {
+ return [
+ [
+ [
+ '#markup' => 'kittens',
+ '#cache' => [
+ 'tags' => ['kittens-147']
+ ],
+ ],
+ ],
+ [
+ [
+ 'child' => [
+ '#markup' => 'kittens',
+ '#cache' => [
+ 'tags' => ['kittens-147'],
+ ],
+ ],
+ ],
+ ],
+ [
+ [
+ '#render_children' => TRUE,
+ 'child' => [
+ '#markup' => 'kittens',
+ '#cache' => [
+ 'tags' => ['kittens-147'],
+ ],
+ ],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * Ensures that #access is taken in account when rendering #render_children.
+ */
+ public function testRenderChildrenAccess() {
+ $build = [
+ '#access' => FALSE,
+ '#render_children' => TRUE,
+ 'child' => [
+ '#markup' => 'kittens',
+ ],
+ ];
+
+ $this->assertEquals('', $this->renderer->renderRoot($build));
+ }
+
/**
* Provides a list of both booleans.
*
From e145f2a5438c567cae6f29cf2b93e226dd0b5f37 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Sat, 23 Dec 2017 08:58:54 +1000
Subject: [PATCH 062/232] Issue #2932551 by jeqq: Error when calling
ModerationStateFieldItemList::updateModeratedEntity() if the entity doesn't
have workflow
---
.../Field/ModerationStateFieldItemList.php | 2 +-
.../ModerationStateFieldItemListTest.php | 25 +++++++++++++++++++
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php b/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php
index 4270a1fb539e..b5263639e2f7 100644
--- a/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php
+++ b/core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php
@@ -153,7 +153,7 @@ protected function updateModeratedEntity($moderation_state_id) {
// Change the entity's default revision flag and the publishing status only
// if the new workflow state is a valid one.
- if ($workflow->getTypePlugin()->hasState($moderation_state_id)) {
+ if ($workflow && $workflow->getTypePlugin()->hasState($moderation_state_id)) {
/** @var \Drupal\content_moderation\ContentModerationState $current_state */
$current_state = $workflow->getTypePlugin()->getState($moderation_state_id);
diff --git a/core/modules/content_moderation/tests/src/Kernel/ModerationStateFieldItemListTest.php b/core/modules/content_moderation/tests/src/Kernel/ModerationStateFieldItemListTest.php
index a0d46da428c7..6281ab82bfa0 100644
--- a/core/modules/content_moderation/tests/src/Kernel/ModerationStateFieldItemListTest.php
+++ b/core/modules/content_moderation/tests/src/Kernel/ModerationStateFieldItemListTest.php
@@ -99,4 +99,29 @@ public function testModerationStateChanges() {
$this->assertFalse($this->testNode->isDefaultRevision());
}
+ /**
+ * Test updating the state for an entity without a workflow.
+ */
+ public function testEntityWithNoWorkflow() {
+ $node_type = NodeType::create([
+ 'type' => 'example_no_workflow',
+ ]);
+ $node_type->save();
+ $test_node = Node::create([
+ 'type' => 'example_no_workflow',
+ 'title' => 'Test node with no workflow',
+ ]);
+ $test_node->save();
+
+ /** @var \Drupal\content_moderation\ModerationInformationInterface $content_moderation_info */
+ $content_moderation_info = \Drupal::service('content_moderation.moderation_information');
+ $workflow = $content_moderation_info->getWorkflowForEntity($test_node);
+ $this->assertNull($workflow);
+
+ $this->assertTrue($test_node->isPublished());
+ $test_node->moderation_state->setValue('draft');
+ // The entity is still published because there is not a workflow.
+ $this->assertTrue($test_node->isPublished());
+ }
+
}
From f3b60edab6d22415a83d06d4be9fda1765563e68 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Sat, 23 Dec 2017 09:58:46 +1000
Subject: [PATCH 063/232] Issue #2932154 by jhedstrom:
ModerationInformation::getLatestRevisionId returns access-specific results
---
.../src/ModerationInformation.php | 6 ++
.../tests/src/Functional/NodeAccessTest.php | 35 +++++++-
.../tests/src/Kernel/NodeAccessTest.php | 88 +++++++++++++++++++
.../node_access_test/node_access_test.module | 6 ++
4 files changed, 134 insertions(+), 1 deletion(-)
create mode 100644 core/modules/content_moderation/tests/src/Kernel/NodeAccessTest.php
diff --git a/core/modules/content_moderation/src/ModerationInformation.php b/core/modules/content_moderation/src/ModerationInformation.php
index 0fc9af4d2ccb..7e3e513307fe 100644
--- a/core/modules/content_moderation/src/ModerationInformation.php
+++ b/core/modules/content_moderation/src/ModerationInformation.php
@@ -87,6 +87,9 @@ public function getLatestRevisionId($entity_type_id, $entity_id) {
$result = $storage->getQuery()
->latestRevision()
->condition($this->entityTypeManager->getDefinition($entity_type_id)->getKey('id'), $entity_id)
+ // No access check is performed here since this is an API function and
+ // should return the same ID regardless of the current user.
+ ->accessCheck(FALSE)
->execute();
if ($result) {
return key($result);
@@ -102,6 +105,9 @@ public function getDefaultRevisionId($entity_type_id, $entity_id) {
$result = $storage->getQuery()
->currentRevision()
->condition($this->entityTypeManager->getDefinition($entity_type_id)->getKey('id'), $entity_id)
+ // No access check is performed here since this is an API function and
+ // should return the same ID regardless of the current user.
+ ->accessCheck(FALSE)
->execute();
if ($result) {
return key($result);
diff --git a/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php b/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php
index ea79c2d0558c..76e9d97608c4 100644
--- a/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php
+++ b/core/modules/content_moderation/tests/src/Functional/NodeAccessTest.php
@@ -2,6 +2,8 @@
namespace Drupal\Tests\content_moderation\Functional;
+use Drupal\node\Entity\NodeType;
+
/**
* Tests permission access control around nodes.
*
@@ -19,7 +21,7 @@ class NodeAccessTest extends ModerationStateTestBase {
'block',
'block_content',
'node',
- 'node_access_test_empty',
+ 'node_access_test',
];
/**
@@ -49,6 +51,9 @@ protected function setUp() {
$this->createContentTypeFromUi('Moderated content', 'moderated_content', FALSE);
$this->grantUserPermissionToCreateContentOfType($this->adminUser, 'moderated_content');
+ // Add the private field to the node type.
+ node_access_test_add_field(NodeType::load('moderated_content'));
+
// Rebuild permissions because hook_node_grants() is implemented by the
// node_access_test_empty module.
node_access_rebuild();
@@ -58,6 +63,10 @@ protected function setUp() {
* Verifies that a non-admin user can still access the appropriate pages.
*/
public function testPageAccess() {
+ // Initially disable access grant records in
+ // node_access_test_node_access_records().
+ \Drupal::state()->set('node_access_test.private', TRUE);
+
$this->drupalLogin($this->adminUser);
// Access the node form before moderation is enabled, the publication state
@@ -149,6 +158,30 @@ public function testPageAccess() {
$this->assertResponse(403);
$this->drupalGet($view_path);
$this->assertResponse(200);
+
+ // Now create a private node that the user is not granted access to by the
+ // node grants, but is granted access via hook_node_access().
+ // @see node_access_test_node_access
+ $node = $this->createNode([
+ 'type' => 'moderated_content',
+ 'private' => TRUE,
+ 'uid' => $this->adminUser->id(),
+ ]);
+ $user = $this->createUser([
+ 'use editorial transition publish',
+ ]);
+ $this->drupalLogin($user);
+
+ // Grant access to the node via node_access_test_node_access().
+ \Drupal::state()->set('node_access_test.allow_uid', $user->id());
+
+ $this->drupalGet($node->toUrl());
+ $this->assertResponse(200);
+
+ // Verify the moderation form is in place by publishing the node.
+ $this->drupalPostForm(NULL, [], t('Apply'));
+ $node = \Drupal::entityTypeManager()->getStorage('node')->loadUnchanged($node->id());
+ $this->assertEquals('published', $node->moderation_state->value);
}
}
diff --git a/core/modules/content_moderation/tests/src/Kernel/NodeAccessTest.php b/core/modules/content_moderation/tests/src/Kernel/NodeAccessTest.php
new file mode 100644
index 000000000000..6716ca36100c
--- /dev/null
+++ b/core/modules/content_moderation/tests/src/Kernel/NodeAccessTest.php
@@ -0,0 +1,88 @@
+installEntitySchema('content_moderation_state');
+ $this->installEntitySchema('node');
+ $this->installEntitySchema('user');
+ $this->installEntitySchema('workflow');
+ $this->installConfig(['content_moderation', 'filter']);
+ $this->installSchema('system', ['sequences']);
+ $this->installSchema('node', ['node_access']);
+
+ // Add a moderated node type.
+ $node_type = NodeType::create([
+ 'type' => 'page',
+ 'label' => 'Page',
+ ]);
+ $node_type->save();
+ $workflow = Workflow::load('editorial');
+ $workflow->getTypePlugin()->addEntityTypeAndBundle('node', 'page');
+ $workflow->save();
+
+ $this->moderationInformation = \Drupal::service('content_moderation.moderation_information');
+ }
+
+ /**
+ * Tests for moderation information methods with node access.
+ */
+ public function testModerationInformation() {
+ // Create an admin user.
+ $user = $this->createUser([], NULL, TRUE);
+ \Drupal::currentUser()->setAccount($user);
+
+ // Create a node.
+ $node = $this->createNode(['type' => 'page']);
+ $this->assertEquals($node->getRevisionId(), $this->moderationInformation->getDefaultRevisionId('node', $node->id()));
+ $this->assertEquals($node->getRevisionId(), $this->moderationInformation->getLatestRevisionId('node', $node->id()));
+
+ // Create a non-admin user.
+ $user = $this->createUser();
+ \Drupal::currentUser()->setAccount($user);
+ $this->assertEquals($node->getRevisionId(), $this->moderationInformation->getDefaultRevisionId('node', $node->id()));
+ $this->assertEquals($node->getRevisionId(), $this->moderationInformation->getLatestRevisionId('node', $node->id()));
+ }
+
+}
diff --git a/core/modules/node/tests/modules/node_access_test/node_access_test.module b/core/modules/node/tests/modules/node_access_test/node_access_test.module
index 40baeb53eded..5a9756b51adc 100644
--- a/core/modules/node/tests/modules/node_access_test/node_access_test.module
+++ b/core/modules/node/tests/modules/node_access_test/node_access_test.module
@@ -152,6 +152,12 @@ function node_access_test_node_access(NodeInterface $node, $op, AccountInterface
// Make all Catalan content secret.
return AccessResult::forbidden()->setCacheMaxAge(0);
}
+
+ // Grant access if a specific user is specified.
+ if (\Drupal::state()->get('node_access_test.allow_uid') === $account->id()) {
+ return AccessResult::allowed();
+ }
+
// No opinion.
return AccessResult::neutral()->setCacheMaxAge(0);
}
From a351d726a6c031b2851c2b125bccb7302feddf46 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Sun, 24 Dec 2017 10:03:14 +1000
Subject: [PATCH 064/232] Issue #2931598 by kim.pepper, markcarver, almaudoh,
Sam152: Messenger methods drop repeat flag
---
.../Drupal/Core/Messenger/LegacyMessenger.php | 11 +-
core/lib/Drupal/Core/Messenger/Messenger.php | 6 +-
.../Core/Messenger/MessengerLegacyTest.php | 108 ++++++++++
.../Core/Messenger/MessengerTest.php | 197 ++++++++++++------
4 files changed, 257 insertions(+), 65 deletions(-)
create mode 100644 core/tests/Drupal/KernelTests/Core/Messenger/MessengerLegacyTest.php
diff --git a/core/lib/Drupal/Core/Messenger/LegacyMessenger.php b/core/lib/Drupal/Core/Messenger/LegacyMessenger.php
index bdc66ffe39bc..86034258315e 100644
--- a/core/lib/Drupal/Core/Messenger/LegacyMessenger.php
+++ b/core/lib/Drupal/Core/Messenger/LegacyMessenger.php
@@ -34,7 +34,7 @@ class LegacyMessenger implements MessengerInterface {
* {@inheritdoc}
*/
public function addError($message, $repeat = FALSE) {
- return $this->addMessage($message, static::TYPE_ERROR);
+ return $this->addMessage($message, static::TYPE_ERROR, $repeat);
}
/**
@@ -67,14 +67,14 @@ public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE)
* {@inheritdoc}
*/
public function addStatus($message, $repeat = FALSE) {
- return $this->addMessage($message, static::TYPE_STATUS);
+ return $this->addMessage($message, static::TYPE_STATUS, $repeat);
}
/**
* {@inheritdoc}
*/
public function addWarning($message, $repeat = FALSE) {
- return $this->addMessage($message, static::TYPE_WARNING);
+ return $this->addMessage($message, static::TYPE_WARNING, $repeat);
}
/**
@@ -100,13 +100,16 @@ protected function getMessengerService() {
if (\Drupal::hasService('messenger')) {
// Note: because the container has the potential to be rebuilt during
// requests, this service cannot be directly stored on this class.
+ /** @var \Drupal\Core\Messenger\MessengerInterface $messenger */
$messenger = \Drupal::service('messenger');
// Transfer any messages into the service.
if (isset($this->messages)) {
foreach ($this->messages as $type => $messages) {
foreach ($messages as $message) {
- $messenger->addMessage($message, $type);
+ // Force repeat to TRUE since this is merging existing messages to
+ // the Messenger service and would have already checked this prior.
+ $messenger->addMessage($message, $type, TRUE);
}
}
unset($this->messages);
diff --git a/core/lib/Drupal/Core/Messenger/Messenger.php b/core/lib/Drupal/Core/Messenger/Messenger.php
index b8b6c3ddf369..c0949438cc56 100644
--- a/core/lib/Drupal/Core/Messenger/Messenger.php
+++ b/core/lib/Drupal/Core/Messenger/Messenger.php
@@ -43,7 +43,7 @@ public function __construct(FlashBagInterface $flash_bag, KillSwitch $killSwitch
* {@inheritdoc}
*/
public function addError($message, $repeat = FALSE) {
- return $this->addMessage($message, static::TYPE_ERROR);
+ return $this->addMessage($message, static::TYPE_ERROR, $repeat);
}
/**
@@ -70,14 +70,14 @@ public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE)
* {@inheritdoc}
*/
public function addStatus($message, $repeat = FALSE) {
- return $this->addMessage($message, static::TYPE_STATUS);
+ return $this->addMessage($message, static::TYPE_STATUS, $repeat);
}
/**
* {@inheritdoc}
*/
public function addWarning($message, $repeat = FALSE) {
- return $this->addMessage($message, static::TYPE_WARNING);
+ return $this->addMessage($message, static::TYPE_WARNING, $repeat);
}
/**
diff --git a/core/tests/Drupal/KernelTests/Core/Messenger/MessengerLegacyTest.php b/core/tests/Drupal/KernelTests/Core/Messenger/MessengerLegacyTest.php
new file mode 100644
index 000000000000..29334647db17
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/Messenger/MessengerLegacyTest.php
@@ -0,0 +1,108 @@
+setAccessible(TRUE);
+ return $method->invoke($legacy_messenger);
+ }
+
+ /**
+ * @covers \Drupal::messenger
+ * @covers ::getMessengerService
+ * @covers ::all
+ * @covers ::addMessage
+ * @covers ::addError
+ * @covers ::addStatus
+ * @covers ::addWarning
+ */
+ public function testMessages() {
+ // Save the current container for later use.
+ $container = \Drupal::getContainer();
+
+ // Unset the container to mimic not having one.
+ \Drupal::unsetContainer();
+
+ /** @var \Drupal\Core\Messenger\LegacyMessenger $messenger */
+ // Verify that the Messenger service doesn't exists.
+ $messenger = \Drupal::messenger();
+ $this->assertNull($this->getMessengerService($messenger));
+
+ // Add messages.
+ $messenger->addMessage('Foobar', 'custom');
+ $messenger->addMessage('Foobar', 'custom', TRUE);
+ $messenger->addError('Foo');
+ $messenger->addError('Foo', TRUE);
+
+ // Verify that retrieving another instance and adding more messages works.
+ $messenger = \Drupal::messenger();
+ $messenger->addStatus('Bar');
+ $messenger->addStatus('Bar', TRUE);
+ $messenger->addWarning('Fiz');
+ $messenger->addWarning('Fiz', TRUE);
+
+ // Restore the container.
+ \Drupal::setContainer($container);
+
+ // Verify that the Messenger service exists.
+ $messenger = \Drupal::messenger();
+ $this->assertInstanceOf(Messenger::class, $this->getMessengerService($messenger));
+
+ // Add more messages.
+ $messenger->addMessage('Platypus', 'custom');
+ $messenger->addMessage('Platypus', 'custom', TRUE);
+ $messenger->addError('Rhinoceros');
+ $messenger->addError('Rhinoceros', TRUE);
+ $messenger->addStatus('Giraffe');
+ $messenger->addStatus('Giraffe', TRUE);
+ $messenger->addWarning('Cheetah');
+ $messenger->addWarning('Cheetah', TRUE);
+
+ // Verify all messages added via LegacyMessenger are accounted for.
+ $messages = $messenger->all();
+ $this->assertContains('Foobar', $messages['custom']);
+ $this->assertContains('Foo', $messages[MessengerInterface::TYPE_ERROR]);
+ $this->assertContains('Bar', $messages[MessengerInterface::TYPE_STATUS]);
+ $this->assertContains('Fiz', $messages[MessengerInterface::TYPE_WARNING]);
+
+ // Verify all messages added via Messenger service are accounted for.
+ $this->assertContains('Platypus', $messages['custom']);
+ $this->assertContains('Rhinoceros', $messages[MessengerInterface::TYPE_ERROR]);
+ $this->assertContains('Giraffe', $messages[MessengerInterface::TYPE_STATUS]);
+ $this->assertContains('Cheetah', $messages[MessengerInterface::TYPE_WARNING]);
+
+ // Verify repeat counts.
+ $this->assertCount(4, $messages['custom']);
+ $this->assertCount(4, $messages[MessengerInterface::TYPE_STATUS]);
+ $this->assertCount(4, $messages[MessengerInterface::TYPE_WARNING]);
+ $this->assertCount(4, $messages[MessengerInterface::TYPE_ERROR]);
+ }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Messenger/MessengerTest.php b/core/tests/Drupal/KernelTests/Core/Messenger/MessengerTest.php
index 362069756eae..32c4743d93d4 100644
--- a/core/tests/Drupal/KernelTests/Core/Messenger/MessengerTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Messenger/MessengerTest.php
@@ -2,83 +2,164 @@
namespace Drupal\KernelTests\Core\Messenger;
-use Drupal\Core\Messenger\LegacyMessenger;
-use Drupal\Core\Messenger\Messenger;
use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\Core\Render\Markup;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\KernelTests\KernelTestBase;
/**
* @group Messenger
- * @coversDefaultClass \Drupal\Core\Messenger\LegacyMessenger
+ * @coversDefaultClass \Drupal\Core\Messenger\Messenger
*/
class MessengerTest extends KernelTestBase {
/**
- * Retrieves the Messenger service from LegacyMessenger.
+ * The messenger under test.
*
- * @param \Drupal\Core\Messenger\LegacyMessenger $legacy_messenger
- *
- * @return \Drupal\Core\Messenger\MessengerInterface|null
+ * @var \Drupal\Core\Messenger\MessengerInterface
+ */
+ protected $messenger;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+ $this->messenger = \Drupal::service('messenger');
+ }
+
+ /**
+ * @covers ::addStatus
+ * @covers ::deleteByType
+ * @covers ::messagesByType
*/
- protected function getMessengerService(LegacyMessenger $legacy_messenger) {
- $method = new \ReflectionMethod($legacy_messenger, 'getMessengerService');
- $method->setAccessible(TRUE);
- return $method->invoke($legacy_messenger);
+ public function testRemoveSingleMessage() {
+
+ // Set two messages.
+ $this->messenger->addStatus('First message (removed).');
+ $this->messenger->addStatus(t('Second message with markup! (not removed).'));
+ $messages = $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS);
+ // Remove the first.
+ unset($messages[0]);
+
+ // Re-add the second.
+ foreach ($messages as $message) {
+ $this->messenger->addStatus($message);
+ }
+
+ // Check we only have the second one.
+ $this->assertCount(1, $this->messenger->messagesByType(MessengerInterface::TYPE_STATUS));
+ $this->assertContains('Second message with markup! (not removed).', $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS));
+
}
/**
- * @covers \Drupal::messenger
- * @covers ::getMessengerService
+ * Tests we don't add duplicates.
+ *
* @covers ::all
- * @covers ::addMessage
+ * @covers ::addStatus
+ * @covers ::addWarning
* @covers ::addError
+ * @covers ::deleteByType
+ * @covers ::deleteAll
+ */
+ public function testAddNoDuplicates() {
+
+ $this->messenger->addStatus('Non Duplicated status message');
+ $this->messenger->addStatus('Non Duplicated status message');
+
+ $this->assertCount(1, $this->messenger->messagesByType(MessengerInterface::TYPE_STATUS));
+
+ $this->messenger->addWarning('Non Duplicated warning message');
+ $this->messenger->addWarning('Non Duplicated warning message');
+
+ $this->assertCount(1, $this->messenger->messagesByType(MessengerInterface::TYPE_WARNING));
+
+ $this->messenger->addError('Non Duplicated error message');
+ $this->messenger->addError('Non Duplicated error message');
+
+ $messages = $this->messenger->messagesByType(MessengerInterface::TYPE_ERROR);
+ $this->assertCount(1, $messages);
+
+ // Check getting all messages.
+ $messages = $this->messenger->all();
+ $this->assertCount(3, $messages);
+ $this->assertArrayHasKey(MessengerInterface::TYPE_STATUS, $messages);
+ $this->assertArrayHasKey(MessengerInterface::TYPE_WARNING, $messages);
+ $this->assertArrayHasKey(MessengerInterface::TYPE_ERROR, $messages);
+
+ // Check deletion.
+ $this->messenger->deleteAll();
+ $this->assertCount(0, $this->messenger->messagesByType(MessengerInterface::TYPE_STATUS));
+ $this->assertCount(0, $this->messenger->messagesByType(MessengerInterface::TYPE_WARNING));
+ $this->assertCount(0, $this->messenger->messagesByType(MessengerInterface::TYPE_ERROR));
+
+ }
+
+ /**
+ * Tests we do add duplicates with repeat flag.
+ *
* @covers ::addStatus
* @covers ::addWarning
+ * @covers ::addError
+ * @covers ::deleteByType
*/
- public function testMessages() {
- // Save the current container for later use.
- $container = \Drupal::getContainer();
-
- // Unset the container to mimic not having one.
- \Drupal::unsetContainer();
-
- /** @var \Drupal\Core\Messenger\LegacyMessenger $messenger */
- // Verify that the Messenger service doesn't exists.
- $messenger = \Drupal::messenger();
- $this->assertNull($this->getMessengerService($messenger));
-
- // Add messages.
- $messenger->addMessage('Foobar');
- $messenger->addError('Foo');
-
- // Verify that retrieving another instance and adding more messages works.
- $messenger = \Drupal::messenger();
- $messenger->addStatus('Bar');
- $messenger->addWarning('Fiz');
-
- // Restore the container.
- \Drupal::setContainer($container);
-
- // Verify that the Messenger service exists.
- $messenger = \Drupal::messenger();
- $this->assertInstanceOf(Messenger::class, $this->getMessengerService($messenger));
-
- // Add more messages.
- $messenger->addMessage('Platypus');
- $messenger->addError('Rhinoceros');
- $messenger->addStatus('Giraffe');
- $messenger->addWarning('Cheetah');
-
- // Verify that all the messages are present and accounted for.
- $messages = $messenger->all();
- $this->assertContains('Foobar', $messages[MessengerInterface::TYPE_STATUS]);
- $this->assertContains('Foo', $messages[MessengerInterface::TYPE_ERROR]);
- $this->assertContains('Bar', $messages[MessengerInterface::TYPE_STATUS]);
- $this->assertContains('Fiz', $messages[MessengerInterface::TYPE_WARNING]);
- $this->assertContains('Platypus', $messages[MessengerInterface::TYPE_STATUS]);
- $this->assertContains('Rhinoceros', $messages[MessengerInterface::TYPE_ERROR]);
- $this->assertContains('Giraffe', $messages[MessengerInterface::TYPE_STATUS]);
- $this->assertContains('Cheetah', $messages[MessengerInterface::TYPE_WARNING]);
+ public function testAddWithDuplicates() {
+
+ $this->messenger->addStatus('Duplicated status message', TRUE);
+ $this->messenger->addStatus('Duplicated status message', TRUE);
+
+ $this->assertCount(2, $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS));
+
+ $this->messenger->addWarning('Duplicated warning message', TRUE);
+ $this->messenger->addWarning('Duplicated warning message', TRUE);
+
+ $this->assertCount(2, $this->messenger->deleteByType(MessengerInterface::TYPE_WARNING));
+
+ $this->messenger->addError('Duplicated error message', TRUE);
+ $this->messenger->addError('Duplicated error message', TRUE);
+
+ $this->assertCount(2, $this->messenger->deleteByType(MessengerInterface::TYPE_ERROR));
+
+ }
+
+ /**
+ * Test adding markup.
+ *
+ * @covers ::addStatus
+ * @covers ::deleteByType
+ * @covers ::messagesByType
+ */
+ public function testAddMarkup() {
+
+ // Add a Markup message.
+ $this->messenger->addStatus(Markup::create('Markup with markup!'));
+ // Test duplicate Markup messages.
+ $this->messenger->addStatus(Markup::create('Markup with markup!'));
+
+ $this->assertCount(1, $this->messenger->messagesByType(MessengerInterface::TYPE_STATUS));
+
+ // Ensure that multiple Markup messages work.
+ $this->messenger->addStatus(Markup::create('Markup2 with markup!'));
+
+ $this->assertCount(2, $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS));
+
+ // Test mixing of types.
+ $this->messenger->addStatus(Markup::create('Non duplicate Markup / string.'));
+ $this->messenger->addStatus('Non duplicate Markup / string.');
+ $this->messenger->addStatus(Markup::create('Duplicate Markup / string.'), TRUE);
+ $this->messenger->addStatus('Duplicate Markup / string.', TRUE);
+
+ $this->assertCount(3, $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS));
+
+ $this->messenger->deleteAll();
+
+ // Check translatable string is converted to Markup.
+ $this->messenger->addStatus(new TranslatableMarkup('Translatable message'));
+ $messages = $this->messenger->deleteByType(MessengerInterface::TYPE_STATUS);
+
+ $this->assertInstanceOf(Markup::class, $messages[0]);
+
}
}
From 8946662d754d59dd8feffb8f2ccad139020df5be Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Wed, 27 Dec 2017 18:01:45 +1000
Subject: [PATCH 065/232] Issue #2928450 by tim.plunkett: Remove dead code in
the Layout Builder following Section refactoring
---
.../layout_builder/layout_builder.module | 23 +---
.../layout_builder.services.yml | 5 -
.../src/Access/LayoutSectionAccessCheck.php | 18 ---
.../Controller/LayoutBuilderController.php | 38 +-----
.../src/Form/ConfigureSectionForm.php | 14 +-
.../src/LayoutSectionBuilder.php | 121 ------------------
.../FieldFormatter/LayoutSectionFormatter.php | 54 +-------
.../Field/FieldType/LayoutSectionItem.php | 1 -
...outBuilderFieldLayoutCompatibilityTest.php | 20 +--
...nBuilderTest.php => SectionRenderTest.php} | 68 ++++------
10 files changed, 38 insertions(+), 324 deletions(-)
delete mode 100644 core/modules/layout_builder/src/LayoutSectionBuilder.php
rename core/modules/layout_builder/tests/src/Unit/{LayoutSectionBuilderTest.php => SectionRenderTest.php} (75%)
diff --git a/core/modules/layout_builder/layout_builder.module b/core/modules/layout_builder/layout_builder.module
index 2192168cb0cd..0ba9a21f6b6b 100644
--- a/core/modules/layout_builder/layout_builder.module
+++ b/core/modules/layout_builder/layout_builder.module
@@ -41,7 +41,7 @@ function layout_builder_entity_type_alter(array &$entity_types) {
* Removes the Layout Builder field both visually and from the #fields handling.
*
* This prevents any interaction with this field. It is rendered directly
- * in layout_builder_entity_view_display_alter().
+ * in layout_builder_entity_view_alter().
*
* @internal
*/
@@ -161,27 +161,16 @@ function layout_builder_add_layout_section_field($entity_type_id, $bundle, $fiel
return $field;
}
-/**
- * Implements hook_entity_view_display_alter().
- */
-function layout_builder_entity_view_display_alter(EntityViewDisplayInterface $display, array $context) {
- if ($display->getThirdPartySetting('layout_builder', 'allow_custom', FALSE)) {
- // Force the layout to render with no label.
- $display->setComponent('layout_builder__layout', [
- 'label' => 'hidden',
- 'region' => '__layout_builder',
- ]);
- }
- else {
- $display->removeComponent('layout_builder__layout');
- }
-}
-
/**
* Implements hook_entity_view_alter().
*/
function layout_builder_entity_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
if ($display->getThirdPartySetting('layout_builder', 'allow_custom', FALSE) && !$entity->layout_builder__layout->isEmpty()) {
+ $sections = $entity->layout_builder__layout->getSections();
+ foreach ($sections as $delta => $section) {
+ $build['_layout_builder'][$delta] = $section->toRenderArray();
+ }
+
// If field layout is active, that is all that needs to be removed.
if (\Drupal::moduleHandler()->moduleExists('field_layout') && isset($build['_field_layout'])) {
unset($build['_field_layout']);
diff --git a/core/modules/layout_builder/layout_builder.services.yml b/core/modules/layout_builder/layout_builder.services.yml
index 518d9ee9421f..b71f6b1887b7 100644
--- a/core/modules/layout_builder/layout_builder.services.yml
+++ b/core/modules/layout_builder/layout_builder.services.yml
@@ -1,13 +1,9 @@
services:
- layout_builder.builder:
- class: Drupal\layout_builder\LayoutSectionBuilder
- arguments: ['@current_user', '@plugin.manager.core.layout', '@plugin.manager.block', '@context.handler', '@context.repository']
layout_builder.tempstore_repository:
class: Drupal\layout_builder\LayoutTempstoreRepository
arguments: ['@user.shared_tempstore', '@entity_type.manager']
access_check.entity.layout:
class: Drupal\layout_builder\Access\LayoutSectionAccessCheck
- arguments: ['@entity_type.manager']
tags:
- { name: access_check, applies_to: _has_layout_section }
layout_builder.routes:
@@ -15,7 +11,6 @@ services:
arguments: ['@entity_type.manager', '@entity_field.manager']
layout_builder.route_enhancer:
class: Drupal\layout_builder\Routing\LayoutBuilderRouteEnhancer
- arguments: ['@entity_type.manager']
tags:
- { name: route_enhancer }
layout_builder.param_converter:
diff --git a/core/modules/layout_builder/src/Access/LayoutSectionAccessCheck.php b/core/modules/layout_builder/src/Access/LayoutSectionAccessCheck.php
index e13a1492386a..231a70fac5b5 100644
--- a/core/modules/layout_builder/src/Access/LayoutSectionAccessCheck.php
+++ b/core/modules/layout_builder/src/Access/LayoutSectionAccessCheck.php
@@ -3,7 +3,6 @@
namespace Drupal\layout_builder\Access;
use Drupal\Core\Access\AccessResult;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Routing\RouteMatchInterface;
@@ -16,23 +15,6 @@
*/
class LayoutSectionAccessCheck implements AccessInterface {
- /**
- * The entity type manager.
- *
- * @var \Drupal\Core\Entity\EntityTypeManagerInterface
- */
- protected $entityTypeManager;
-
- /**
- * Constructs a new LayoutSectionAccessCheck.
- *
- * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
- * The entity type manager.
- */
- public function __construct(EntityTypeManagerInterface $entity_type_manager) {
- $this->entityTypeManager = $entity_type_manager;
- }
-
/**
* Checks routing access to layout for the entity.
*
diff --git a/core/modules/layout_builder/src/Controller/LayoutBuilderController.php b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php
index 959ce1d5b414..9e09a61c6d52 100644
--- a/core/modules/layout_builder/src/Controller/LayoutBuilderController.php
+++ b/core/modules/layout_builder/src/Controller/LayoutBuilderController.php
@@ -2,14 +2,11 @@
namespace Drupal\layout_builder\Controller;
-use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Layout\LayoutPluginManagerInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
-use Drupal\layout_builder\LayoutSectionBuilder;
use Drupal\layout_builder\LayoutTempstoreRepositoryInterface;
use Drupal\layout_builder\Section;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -24,27 +21,6 @@ class LayoutBuilderController implements ContainerInjectionInterface {
use StringTranslationTrait;
- /**
- * The layout builder.
- *
- * @var \Drupal\layout_builder\LayoutSectionBuilder
- */
- protected $builder;
-
- /**
- * The layout manager.
- *
- * @var \Drupal\Core\Layout\LayoutPluginManagerInterface
- */
- protected $layoutManager;
-
- /**
- * The block manager.
- *
- * @var \Drupal\Core\Block\BlockManagerInterface
- */
- protected $blockManager;
-
/**
* The layout tempstore repository.
*
@@ -55,19 +31,10 @@ class LayoutBuilderController implements ContainerInjectionInterface {
/**
* LayoutBuilderController constructor.
*
- * @param \Drupal\layout_builder\LayoutSectionBuilder $builder
- * The layout section builder.
- * @param \Drupal\Core\Layout\LayoutPluginManagerInterface $layout_manager
- * The layout manager.
- * @param \Drupal\Core\Block\BlockManagerInterface $block_manager
- * The block manager.
* @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository
* The layout tempstore repository.
*/
- public function __construct(LayoutSectionBuilder $builder, LayoutPluginManagerInterface $layout_manager, BlockManagerInterface $block_manager, LayoutTempstoreRepositoryInterface $layout_tempstore_repository) {
- $this->builder = $builder;
- $this->layoutManager = $layout_manager;
- $this->blockManager = $block_manager;
+ public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository) {
$this->layoutTempstoreRepository = $layout_tempstore_repository;
}
@@ -76,9 +43,6 @@ public function __construct(LayoutSectionBuilder $builder, LayoutPluginManagerIn
*/
public static function create(ContainerInterface $container) {
return new static(
- $container->get('layout_builder.builder'),
- $container->get('plugin.manager.core.layout'),
- $container->get('plugin.manager.block'),
$container->get('layout_builder.tempstore_repository')
);
}
diff --git a/core/modules/layout_builder/src/Form/ConfigureSectionForm.php b/core/modules/layout_builder/src/Form/ConfigureSectionForm.php
index 6993d218be50..0d43c3d23e32 100644
--- a/core/modules/layout_builder/src/Form/ConfigureSectionForm.php
+++ b/core/modules/layout_builder/src/Form/ConfigureSectionForm.php
@@ -8,7 +8,6 @@
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Layout\LayoutInterface;
-use Drupal\Core\Layout\LayoutPluginManagerInterface;
use Drupal\Core\Plugin\PluginFormFactoryInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\Plugin\PluginWithFormsInterface;
@@ -41,13 +40,6 @@ class ConfigureSectionForm extends FormBase {
*/
protected $layout;
- /**
- * The layout manager.
- *
- * @var \Drupal\Core\Layout\LayoutPluginManagerInterface
- */
- protected $layoutManager;
-
/**
* The plugin form manager.
*
@@ -81,16 +73,13 @@ class ConfigureSectionForm extends FormBase {
*
* @param \Drupal\layout_builder\LayoutTempstoreRepositoryInterface $layout_tempstore_repository
* The layout tempstore repository.
- * @param \Drupal\Core\Layout\LayoutPluginManagerInterface $layout_manager
- * The layout manager.
* @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
* The class resolver.
* @param \Drupal\Core\Plugin\PluginFormFactoryInterface $plugin_form_manager
* The plugin form manager.
*/
- public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, LayoutPluginManagerInterface $layout_manager, ClassResolverInterface $class_resolver, PluginFormFactoryInterface $plugin_form_manager) {
+ public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore_repository, ClassResolverInterface $class_resolver, PluginFormFactoryInterface $plugin_form_manager) {
$this->layoutTempstoreRepository = $layout_tempstore_repository;
- $this->layoutManager = $layout_manager;
$this->classResolver = $class_resolver;
$this->pluginFormFactory = $plugin_form_manager;
}
@@ -101,7 +90,6 @@ public function __construct(LayoutTempstoreRepositoryInterface $layout_tempstore
public static function create(ContainerInterface $container) {
return new static(
$container->get('layout_builder.tempstore_repository'),
- $container->get('plugin.manager.core.layout'),
$container->get('class_resolver'),
$container->get('plugin_form.factory')
);
diff --git a/core/modules/layout_builder/src/LayoutSectionBuilder.php b/core/modules/layout_builder/src/LayoutSectionBuilder.php
deleted file mode 100644
index 525849d9cef3..000000000000
--- a/core/modules/layout_builder/src/LayoutSectionBuilder.php
+++ /dev/null
@@ -1,121 +0,0 @@
-account = $account;
- $this->layoutPluginManager = $layoutPluginManager;
- $this->blockManager = $blockManager;
- $this->contextHandler = $context_handler;
- $this->contextRepository = $context_repository;
- }
-
- /**
- * Builds the render array for the layout section.
- *
- * @param \Drupal\Core\Layout\LayoutInterface $layout
- * The ID of the layout.
- * @param \Drupal\layout_builder\SectionComponent[] $components
- * An array of components.
- *
- * @return array
- * The render array for a given section.
- */
- public function buildSectionFromLayout(LayoutInterface $layout, array $components) {
- $regions = [];
- foreach ($components as $component) {
- if ($output = $component->toRenderArray()) {
- $regions[$component->getRegion()][$component->getUuid()] = $output;
- }
- }
-
- return $layout->build($regions);
- }
-
- /**
- * Builds the render array for the layout section.
- *
- * @param string $layout_id
- * The ID of the layout.
- * @param array $layout_settings
- * The configuration for the layout.
- * @param \Drupal\layout_builder\SectionComponent[] $components
- * An array of components.
- *
- * @return array
- * The render array for a given section.
- */
- public function buildSection($layout_id, array $layout_settings, array $components) {
- $layout = $this->layoutPluginManager->createInstance($layout_id, $layout_settings);
- return $this->buildSectionFromLayout($layout, $components);
- }
-
-}
diff --git a/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php b/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php
index c321585fdf55..c579b3e4fc51 100644
--- a/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php
+++ b/core/modules/layout_builder/src/Plugin/Field/FieldFormatter/LayoutSectionFormatter.php
@@ -2,12 +2,8 @@
namespace Drupal\layout_builder\Plugin\Field\FieldFormatter;
-use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
-use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
-use Drupal\layout_builder\LayoutSectionBuilder;
-use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation of the 'layout_section' formatter.
@@ -22,55 +18,7 @@
* }
* )
*/
-class LayoutSectionFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
-
- /**
- * The layout section builder.
- *
- * @var \Drupal\layout_builder\LayoutSectionBuilder
- */
- protected $builder;
-
- /**
- * Constructs a LayoutSectionFormatter object.
- *
- * @param \Drupal\layout_builder\LayoutSectionBuilder $builder
- * The layout section builder.
- * @param string $plugin_id
- * The plugin ID for the formatter.
- * @param mixed $plugin_definition
- * The plugin implementation definition.
- * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
- * The definition of the field to which the formatter is associated.
- * @param array $settings
- * The formatter settings.
- * @param string $label
- * The formatter label display setting.
- * @param string $view_mode
- * The view mode.
- * @param array $third_party_settings
- * Any third party settings.
- */
- public function __construct(LayoutSectionBuilder $builder, $plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings) {
- $this->builder = $builder;
- parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings);
- }
-
- /**
- * {@inheritdoc}
- */
- public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
- return new static(
- $container->get('layout_builder.builder'),
- $plugin_id,
- $plugin_definition,
- $configuration['field_definition'],
- $configuration['settings'],
- $configuration['label'],
- $configuration['view_mode'],
- $configuration['third_party_settings']
- );
- }
+class LayoutSectionFormatter extends FormatterBase {
/**
* {@inheritdoc}
diff --git a/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
index 2001d1b5ff19..a8d5c7074bd4 100644
--- a/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
+++ b/core/modules/layout_builder/src/Plugin/Field/FieldType/LayoutSectionItem.php
@@ -18,7 +18,6 @@
* id = "layout_section",
* label = @Translation("Layout Section"),
* description = @Translation("Layout Section"),
- * default_formatter = "layout_section",
* list_class = "\Drupal\layout_builder\Field\LayoutSectionItemList",
* no_ui = TRUE,
* cardinality = \Drupal\Core\Field\FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
diff --git a/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php b/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php
index c1b340dea411..1ea23218ccc2 100644
--- a/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php
+++ b/core/modules/layout_builder/tests/src/Kernel/LayoutBuilderFieldLayoutCompatibilityTest.php
@@ -4,18 +4,17 @@
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Entity\EntityInterface;
-use Drupal\entity_test\Entity\EntityTestBaseFieldDisplay;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
-use Drupal\KernelTests\KernelTestBase;
use Drupal\layout_builder\Section;
+use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
/**
* Ensures that Layout Builder and Field Layout are compatible with each other.
*
* @group layout_builder
*/
-class LayoutBuilderFieldLayoutCompatibilityTest extends KernelTestBase {
+class LayoutBuilderFieldLayoutCompatibilityTest extends EntityKernelTestBase {
/**
* {@inheritdoc}
@@ -23,12 +22,6 @@ class LayoutBuilderFieldLayoutCompatibilityTest extends KernelTestBase {
public static $modules = [
'layout_discovery',
'field_layout',
- 'user',
- 'field',
- 'entity_test',
- 'system',
- 'text',
- 'filter',
];
/**
@@ -45,9 +38,7 @@ protected function setUp() {
parent::setUp();
$this->installEntitySchema('entity_test_base_field_display');
- $this->installEntitySchema('user');
- $this->installSchema('system', ['sequences', 'key_value']);
- $this->installConfig(['field', 'filter', 'user', 'system']);
+ $this->installConfig(['filter']);
\Drupal::service('theme_handler')->install(['classy']);
$this->config('system.theme')->set('default', 'classy')->save();
@@ -100,9 +91,10 @@ public function testCompatibility() {
// Install the Layout Builder, configure it for this entity display, and
// reload the entity.
- $this->enableModules(['layout_builder']);
+ $this->installModule('layout_builder');
+ $this->display = $this->reloadEntity($this->display);
$this->display->setThirdPartySetting('layout_builder', 'allow_custom', TRUE)->save();
- $entity = EntityTestBaseFieldDisplay::load($entity->id());
+ $entity = $this->reloadEntity($entity);
// Without using Layout Builder for an override, the result has not changed.
$new_markup = $this->renderEntity($entity);
diff --git a/core/modules/layout_builder/tests/src/Unit/LayoutSectionBuilderTest.php b/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php
similarity index 75%
rename from core/modules/layout_builder/tests/src/Unit/LayoutSectionBuilderTest.php
rename to core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php
index 28f22557e75b..634030e70bf6 100644
--- a/core/modules/layout_builder/tests/src/Unit/LayoutSectionBuilderTest.php
+++ b/core/modules/layout_builder/tests/src/Unit/SectionRenderTest.php
@@ -15,16 +15,16 @@
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
use Drupal\Core\Plugin\ContextAwarePluginInterface;
use Drupal\Core\Session\AccountInterface;
-use Drupal\layout_builder\LayoutSectionBuilder;
+use Drupal\layout_builder\Section;
use Drupal\layout_builder\SectionComponent;
use Drupal\Tests\UnitTestCase;
use Prophecy\Argument;
/**
- * @coversDefaultClass \Drupal\layout_builder\LayoutSectionBuilder
+ * @coversDefaultClass \Drupal\layout_builder\Section
* @group layout_builder
*/
-class LayoutSectionBuilderTest extends UnitTestCase {
+class SectionRenderTest extends UnitTestCase {
/**
* The current user.
@@ -33,13 +33,6 @@ class LayoutSectionBuilderTest extends UnitTestCase {
*/
protected $account;
- /**
- * The layout plugin manager.
- *
- * @var \Drupal\Core\Layout\LayoutPluginManagerInterface
- */
- protected $layoutPluginManager;
-
/**
* The block plugin manager.
*
@@ -61,20 +54,6 @@ class LayoutSectionBuilderTest extends UnitTestCase {
*/
protected $contextRepository;
- /**
- * The object under test.
- *
- * @var \Drupal\layout_builder\LayoutSectionBuilder
- */
- protected $layoutSectionBuilder;
-
- /**
- * The layout plugin.
- *
- * @var \Drupal\Core\Layout\LayoutInterface
- */
- protected $layout;
-
/**
* {@inheritdoc}
*/
@@ -82,29 +61,29 @@ protected function setUp() {
parent::setUp();
$this->account = $this->prophesize(AccountInterface::class);
- $this->layoutPluginManager = $this->prophesize(LayoutPluginManagerInterface::class);
+ $layout_plugin_manager = $this->prophesize(LayoutPluginManagerInterface::class);
$this->blockManager = $this->prophesize(BlockManagerInterface::class);
$this->contextHandler = $this->prophesize(ContextHandlerInterface::class);
$this->contextRepository = $this->prophesize(ContextRepositoryInterface::class);
- $this->layoutSectionBuilder = new LayoutSectionBuilder($this->account->reveal(), $this->layoutPluginManager->reveal(), $this->blockManager->reveal(), $this->contextHandler->reveal(), $this->contextRepository->reveal());
- $this->layout = $this->prophesize(LayoutInterface::class);
- $this->layout->getPluginDefinition()->willReturn(new LayoutDefinition([]));
- $this->layout->build(Argument::type('array'))->willReturnArgument(0);
- $this->layoutPluginManager->createInstance('layout_onecol', [])->willReturn($this->layout->reveal());
+ $layout = $this->prophesize(LayoutInterface::class);
+ $layout->getPluginDefinition()->willReturn(new LayoutDefinition([]));
+ $layout->build(Argument::type('array'))->willReturnArgument(0);
+ $layout_plugin_manager->createInstance('layout_onecol', [])->willReturn($layout->reveal());
$container = new ContainerBuilder();
$container->set('current_user', $this->account->reveal());
$container->set('plugin.manager.block', $this->blockManager->reveal());
+ $container->set('plugin.manager.core.layout', $layout_plugin_manager->reveal());
$container->set('context.handler', $this->contextHandler->reveal());
$container->set('context.repository', $this->contextRepository->reveal());
\Drupal::setContainer($container);
}
/**
- * @covers ::buildSection
+ * @covers ::toRenderArray
*/
- public function testBuildSection() {
+ public function testToRenderArray() {
$block_content = ['#markup' => 'The block content.'];
$render_array = [
'#theme' => 'block',
@@ -143,15 +122,14 @@ public function testBuildSection() {
'some_uuid' => $render_array,
],
];
- $result = $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
+ $result = (new Section('layout_onecol', [], $section))->toRenderArray();
$this->assertEquals($expected, $result);
}
/**
- * @covers ::buildSection
+ * @covers ::toRenderArray
*/
- public function testBuildSectionAccessDenied() {
-
+ public function testToRenderArrayAccessDenied() {
$block = $this->prophesize(BlockPluginInterface::class);
$this->blockManager->createInstance('block_plugin_id', ['id' => 'block_plugin_id'])->willReturn($block->reveal());
@@ -173,22 +151,22 @@ public function testBuildSectionAccessDenied() {
],
],
];
- $result = $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
+ $result = (new Section('layout_onecol', [], $section))->toRenderArray();
$this->assertEquals($expected, $result);
}
/**
- * @covers ::buildSection
+ * @covers ::toRenderArray
*/
- public function testBuildSectionEmpty() {
+ public function testToRenderArrayEmpty() {
$section = [];
$expected = [];
- $result = $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
+ $result = (new Section('layout_onecol', [], $section))->toRenderArray();
$this->assertEquals($expected, $result);
}
/**
- * @covers ::buildSection
+ * @covers ::toRenderArray
*/
public function testContextAwareBlock() {
$render_array = [
@@ -232,16 +210,16 @@ public function testContextAwareBlock() {
'some_uuid' => $render_array,
],
];
- $result = $this->layoutSectionBuilder->buildSection('layout_onecol', [], $section);
+ $result = (new Section('layout_onecol', [], $section))->toRenderArray();
$this->assertEquals($expected, $result);
}
/**
- * @covers ::buildSection
+ * @covers ::toRenderArray
*/
- public function testBuildSectionMissingPluginId() {
+ public function testToRenderArrayMissingPluginId() {
$this->setExpectedException(PluginException::class, 'No plugin ID specified for component with "some_uuid" UUID');
- $this->layoutSectionBuilder->buildSection('layout_onecol', [], [new SectionComponent('some_uuid', 'content')]);
+ (new Section('layout_onecol', [], [new SectionComponent('some_uuid', 'content')]))->toRenderArray();
}
}
From 984a268454fabfe0a84a25cf3c4db24459d51a8d Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Wed, 27 Dec 2017 18:05:16 +1000
Subject: [PATCH 066/232] Issue #2931264 by markcarver, claudiu.cristea: Remove
static \Drupal::$legacyMessenger property
---
core/lib/Drupal.php | 21 +---------
.../Drupal/Core/Messenger/LegacyMessenger.php | 38 ++++++++++---------
2 files changed, 22 insertions(+), 37 deletions(-)
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 8b98ccd1dc71..1697ff62a240 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -101,22 +101,6 @@ class Drupal {
*/
protected static $container;
- /**
- * The LegacyMessenger instance.
- *
- * Note: this is merely used to ensure that the instance survives when
- * \Drupal::messenger() is invoked. It is required to ensure that messages
- * are properly transferred to the Messenger service once the container has
- * been initialized. Do not store the Messenger service here.
- *
- * @todo Remove once LegacyMessenger has been removed before 9.0.0.
- *
- * @see https://www.drupal.org/node/2928994
- *
- * @var \Drupal\Core\Messenger\LegacyMessenger|null
- */
- protected static $legacyMessenger;
-
/**
* Sets a new global container.
*
@@ -783,10 +767,7 @@ public static function time() {
public static function messenger() {
// @todo Replace with service once LegacyMessenger is removed in 9.0.0.
// @see https://www.drupal.org/node/2928994
- if (!isset(static::$legacyMessenger)) {
- static::$legacyMessenger = new LegacyMessenger();
- }
- return static::$legacyMessenger;
+ return new LegacyMessenger();
}
}
diff --git a/core/lib/Drupal/Core/Messenger/LegacyMessenger.php b/core/lib/Drupal/Core/Messenger/LegacyMessenger.php
index 86034258315e..ff323d20f94d 100644
--- a/core/lib/Drupal/Core/Messenger/LegacyMessenger.php
+++ b/core/lib/Drupal/Core/Messenger/LegacyMessenger.php
@@ -26,9 +26,13 @@ class LegacyMessenger implements MessengerInterface {
/**
* The messages.
*
+ * Note: this property must remain static because it must behave in a
+ * persistent manner, similar to $_SESSION['messages']. Creating a new class
+ * each time would destroy any previously set messages.
+ *
* @var array
*/
- protected $messages;
+ protected static $messages;
/**
* {@inheritdoc}
@@ -46,8 +50,8 @@ public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE)
return $messenger->addMessage($message, $type, $repeat);
}
- if (!isset($this->messages[$type])) {
- $this->messages[$type] = [];
+ if (!isset(static::$messages[$type])) {
+ static::$messages[$type] = [];
}
if (!($message instanceof Markup) && $message instanceof MarkupInterface) {
@@ -56,8 +60,8 @@ public function addMessage($message, $type = self::TYPE_STATUS, $repeat = FALSE)
// Do not use strict type checking so that equivalent string and
// MarkupInterface objects are detected.
- if ($repeat || !in_array($message, $this->messages[$type])) {
- $this->messages[$type][] = $message;
+ if ($repeat || !in_array($message, static::$messages[$type])) {
+ static::$messages[$type][] = $message;
}
return $this;
@@ -86,7 +90,7 @@ public function all() {
return $messenger->all();
}
- return $this->messages;
+ return static::$messages;
}
/**
@@ -104,15 +108,15 @@ protected function getMessengerService() {
$messenger = \Drupal::service('messenger');
// Transfer any messages into the service.
- if (isset($this->messages)) {
- foreach ($this->messages as $type => $messages) {
+ if (isset(static::$messages)) {
+ foreach (static::$messages as $type => $messages) {
foreach ($messages as $message) {
// Force repeat to TRUE since this is merging existing messages to
// the Messenger service and would have already checked this prior.
$messenger->addMessage($message, $type, TRUE);
}
}
- unset($this->messages);
+ static::$messages = NULL;
}
return $messenger;
@@ -128,18 +132,18 @@ protected function getMessengerService() {
// reasonable to assume that if the container becomes available in a
// subsequent request, a new instance of this class will be created and
// this code will never be reached. This is merely for BC purposes.
- if (!isset($this->messages)) {
+ if (!isset(static::$messages)) {
// A "session" was already created, perhaps to simply allow usage of
// the previous method core used to store messages, use it.
if (isset($_SESSION)) {
if (!isset($_SESSION['messages'])) {
$_SESSION['messages'] = [];
}
- $this->messages = &$_SESSION['messages'];
+ static::$messages = &$_SESSION['messages'];
}
// Otherwise, just set an empty array.
else {
- $this->messages = [];
+ static::$messages = [];
}
}
}
@@ -153,7 +157,7 @@ public function messagesByType($type) {
return $messenger->messagesByType($type);
}
- return $this->messages[$type];
+ return static::$messages[$type];
}
/**
@@ -165,8 +169,8 @@ public function deleteAll() {
return $messenger->deleteAll();
}
- $messages = $this->messages;
- unset($this->messages);
+ $messages = static::$messages;
+ static::$messages = NULL;
return $messages;
}
@@ -179,8 +183,8 @@ public function deleteByType($type) {
return $messenger->messagesByType($type);
}
- $messages = $this->messages[$type];
- unset($this->messages[$type]);
+ $messages = static::$messages[$type];
+ unset(static::$messages[$type]);
return $messages;
}
From 6585630775fd1d1f276b7a3747eac405554ff8df Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Wed, 27 Dec 2017 18:07:19 +1000
Subject: [PATCH 067/232] Issue #2931709 by marcoscano, claudiu.cristea: Wrong
constant name in
\Drupal\image\Plugin\Field\FieldType\ImageItem::generateSampleValue()
---
core/modules/image/src/Plugin/Field/FieldType/ImageItem.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php
index 14335d8ecec2..67a15a7d0084 100644
--- a/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php
+++ b/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php
@@ -344,7 +344,7 @@ public static function generateSampleValue(FieldDefinitionInterface $field_defin
if (!isset($images[$extension][$min_resolution][$max_resolution]) || count($images[$extension][$min_resolution][$max_resolution]) <= 5) {
$tmp_file = drupal_tempnam('temporary://', 'generateImage_');
$destination = $tmp_file . '.' . $extension;
- file_unmanaged_move($tmp_file, $destination, FILE_CREATE_DIRECTORY);
+ file_unmanaged_move($tmp_file, $destination);
if ($path = $random->image(\Drupal::service('file_system')->realpath($destination), $min_resolution, $max_resolution)) {
$image = File::create();
$image->setFileUri($path);
@@ -354,7 +354,7 @@ public static function generateSampleValue(FieldDefinitionInterface $field_defin
$destination_dir = static::doGetUploadLocation($settings);
file_prepare_directory($destination_dir, FILE_CREATE_DIRECTORY);
$destination = $destination_dir . '/' . basename($path);
- $file = file_move($image, $destination, FILE_CREATE_DIRECTORY);
+ $file = file_move($image, $destination);
$images[$extension][$min_resolution][$max_resolution][$file->id()] = $file;
}
else {
From 1872e83be50457bf4c39a4b157a5c90aef756445 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Wed, 27 Dec 2017 18:19:18 +1000
Subject: [PATCH 068/232] Issue #2931294 by claudiu.cristea, Wim Leers:
Timestamp field type misses schema for value
---
core/config/schema/core.data_types.schema.yml | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/core/config/schema/core.data_types.schema.yml b/core/config/schema/core.data_types.schema.yml
index 3b5bbe41906a..8e98d7c2fdb4 100644
--- a/core/config/schema/core.data_types.schema.yml
+++ b/core/config/schema/core.data_types.schema.yml
@@ -743,6 +743,16 @@ field.value.float:
type: float
label: 'Value'
+# Schema for the configuration of the Timestamp field type.
+
+field.value.timestamp:
+ type: mapping
+ label: 'Timestamp value'
+ mapping:
+ value:
+ type: timestamp
+ label: 'Value'
+
# Text with a text format.
text_format:
type: mapping
From 384a35caf64ffcd872167b7aba5e73c5c1c43a1b Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Sun, 31 Dec 2017 08:13:45 +1000
Subject: [PATCH 069/232] Issue #2779921 by kiamlaluno, alexpott:
hook_field_widget_form_alter() still reference a hook that is not used
anymore
---
core/modules/field/field.api.php | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php
index b0899ea02b07..42bdd1e7125b 100644
--- a/core/modules/field/field.api.php
+++ b/core/modules/field/field.api.php
@@ -138,7 +138,8 @@ function hook_field_widget_info_alter(array &$info) {
* Alter forms for field widgets provided by other modules.
*
* @param $element
- * The field widget form element as constructed by hook_field_widget_form().
+ * The field widget form element as constructed by
+ * \Drupal\Core\Field\WidgetBaseInterface::form().
* @param $form_state
* The current state of the form.
* @param $context
@@ -152,6 +153,7 @@ function hook_field_widget_info_alter(array &$info) {
* - default: A boolean indicating whether the form is being shown as a dummy
* form to set default values.
*
+ * @see \Drupal\Core\Field\WidgetBaseInterface::form()
* @see \Drupal\Core\Field\WidgetBase::formSingleElement()
* @see hook_field_widget_WIDGET_TYPE_form_alter()
*/
@@ -172,13 +174,15 @@ function hook_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInte
* checking the widget type.
*
* @param $element
- * The field widget form element as constructed by hook_field_widget_form().
+ * The field widget form element as constructed by
+ * \Drupal\Core\Field\WidgetBaseInterface::form().
* @param $form_state
* The current state of the form.
* @param $context
* An associative array. See hook_field_widget_form_alter() for the structure
* and content of the array.
*
+ * @see \Drupal\Core\Field\WidgetBaseInterface::form()
* @see \Drupal\Core\Field\WidgetBase::formSingleElement()
* @see hook_field_widget_form_alter()
*/
From ed45b4d5dc0524b7120eb7cb8acd13c3b0f8ff74 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Sun, 31 Dec 2017 08:21:22 +1000
Subject: [PATCH 070/232] Issue #2840257 by kiamlaluno: The documentation makes
reference to a function that doesn't exist
---
core/modules/toolbar/toolbar.api.php | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/core/modules/toolbar/toolbar.api.php b/core/modules/toolbar/toolbar.api.php
index 548b488992a4..eb087d36e28a 100644
--- a/core/modules/toolbar/toolbar.api.php
+++ b/core/modules/toolbar/toolbar.api.php
@@ -152,10 +152,10 @@ function hook_toolbar() {
/**
* Alter the toolbar menu after hook_toolbar() is invoked.
*
- * This hook is invoked by toolbar_view() immediately after hook_toolbar(). The
- * toolbar definitions are passed in by reference. Each element of the $items
- * array is one item returned by a module from hook_toolbar(). Additional items
- * may be added, or existing items altered.
+ * This hook is invoked by Toolbar::preRenderToolbar() immediately after
+ * hook_toolbar(). The toolbar definitions are passed in by reference. Each
+ * element of the $items array is one item returned by a module from
+ * hook_toolbar(). Additional items may be added, or existing items altered.
*
* @param $items
* Associative array of toolbar menu definitions returned from hook_toolbar().
From d372f6d8eee746ccab8089fbe00357ea270b4211 Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Sun, 31 Dec 2017 08:25:45 +1000
Subject: [PATCH 071/232] Issue #2323459 by harsha012, jhodgdon, joachim:
Change wording of annotation keys to properties
---
core/lib/Drupal/Core/Entity/entity.api.php | 54 +++++++++++-----------
1 file changed, 28 insertions(+), 26 deletions(-)
diff --git a/core/lib/Drupal/Core/Entity/entity.api.php b/core/lib/Drupal/Core/Entity/entity.api.php
index 0af0b532328b..6b4029c71b08 100644
--- a/core/lib/Drupal/Core/Entity/entity.api.php
+++ b/core/lib/Drupal/Core/Entity/entity.api.php
@@ -331,11 +331,11 @@
* out-of-the-box support for Entity API's revisioning and publishing
* features, which will allow your entity type to be used with Drupal's
* editorial workflow provided by the Content Moderation module.
- * - The 'id' annotation gives the entity type ID, and the 'label' annotation
- * gives the human-readable name of the entity type. If you are defining a
- * content entity type that uses bundles, the 'bundle_label' annotation gives
- * the human-readable name to use for a bundle of this entity type (for
- * example, "Content type" for the Node entity).
+ * - In the annotation, the 'id' property gives the entity type ID, and the
+ * 'label' property gives the human-readable name of the entity type. If you
+ * are defining a content entity type that uses bundles, the 'bundle_label'
+ * property gives the human-readable name to use for a bundle of this entity
+ * type (for example, "Content type" for the Node entity).
* - The annotation will refer to several handler classes, which you will also
* need to define:
* - list_builder: Define a class that extends
@@ -354,16 +354,17 @@
* \Drupal\Core\Entity\EntityViewBuilderInterface (usually extending
* \Drupal\Core\Entity\EntityViewBuilder), to display a single entity.
* - translation: For translatable content entities (if the 'translatable'
- * annotation has value TRUE), define a class that extends
+ * annotation property has value TRUE), define a class that extends
* \Drupal\content_translation\ContentTranslationHandler, to translate
* the content. Configuration translation is handled automatically by the
* Configuration Translation module, without the need of a handler class.
* - access: If your configuration entity has complex permissions, you might
* need an access control handling, implementing
- * \Drupal\Core\Entity\EntityAccessControlHandlerInterface, but most entities
- * can just use the 'admin_permission' annotation instead. Note that if you
- * are creating your own access control handler, you should override the
- * checkAccess() and checkCreateAccess() methods, not access().
+ * \Drupal\Core\Entity\EntityAccessControlHandlerInterface, but most
+ * entities can just use the 'admin_permission' annotation property
+ * instead. Note that if you are creating your own access control handler,
+ * you should override the checkAccess() and checkCreateAccess() methods,
+ * not access().
* - storage: A class implementing
* \Drupal\Core\Entity\EntityStorageInterface. If not specified, content
* entities will use \Drupal\Core\Entity\Sql\SqlContentEntityStorage, and
@@ -396,25 +397,26 @@
* - delete-form: Confirmation form to delete the entity.
* - edit-form: Editing form.
* - Other link types specific to your entity type can also be defined.
- * - If your content entity is fieldable, provide 'field_ui_base_route'
- * annotation, giving the name of the route that the Manage Fields, Manage
- * Display, and Manage Form Display pages from the Field UI module will be
- * attached to. This is usually the bundle settings edit page, or an entity
- * type settings page if there are no bundles.
+ * - If your content entity is fieldable, provide the 'field_ui_base_route'
+ * annotation property, giving the name of the route that the Manage Fields,
+ * Manage Display, and Manage Form Display pages from the Field UI module
+ * will be attached to. This is usually the bundle settings edit page, or an
+ * entity type settings page if there are no bundles.
* - If your content entity has bundles, you will also need to define a second
* plugin to handle the bundles. This plugin is itself a configuration entity
* type, so follow the steps here to define it. The machine name ('id'
- * annotation) of this configuration entity class goes into the
- * 'bundle_entity_type' annotation on the entity type class. For example, for
- * the Node entity, the bundle class is \Drupal\node\Entity\NodeType, whose
- * machine name is 'node_type'. This is the annotation value for
- * 'bundle_entity_type' on the \Drupal\node\Entity\Node class. Also, the
- * bundle config entity type annotation must have a 'bundle_of' entry,
+ * annotation property) of this configuration entity class goes into the
+ * 'bundle_entity_type' annotation property on the entity type class. For
+ * example, for the Node entity, the bundle class is
+ * \Drupal\node\Entity\NodeType, whose machine name is 'node_type'. This is
+ * the annotation property 'bundle_entity_type' on the
+ * \Drupal\node\Entity\Node class. Also, the
+ * bundle config entity type annotation must have a 'bundle_of' property,
* giving the machine name of the entity type it is acting as a bundle for.
* These machine names are considered permanent, they may not be renamed.
- * - Additional annotations can be seen on entity class examples such as
- * \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role
- * (configuration). These annotations are documented on
+ * - Additional annotation properties can be seen on entity class examples such
+ * as \Drupal\node\Entity\Node (content) and \Drupal\user\Entity\Role
+ * (configuration). These annotation properties are documented on
* \Drupal\Core\Entity\EntityType.
*
* @section sec_routes Entity routes
@@ -500,8 +502,8 @@
* $storage = $container->get('entity.manager')->getStorage('your_entity_type');
* @endcode
* Here, 'your_entity_type' is the machine name of your entity type ('id'
- * annotation on the entity class), and note that you should use dependency
- * injection to retrieve this object if possible. See the
+ * annotation property on the entity class), and note that you should use
+ * dependency injection to retrieve this object if possible. See the
* @link container Services and Dependency Injection topic @endlink for more
* about how to properly retrieve services.
*
From efab31b7f212ab9ec6bc7c60f9a19527453ca37a Mon Sep 17 00:00:00 2001
From: Francesco Placella
Date: Sun, 31 Dec 2017 17:31:41 +0100
Subject: [PATCH 072/232] Issue #2862894 by fgm, Wim Leers, bkosborne,
borisson_, catch: Docs for Internal Page Cache incorrectly state that it
respects the maximum age performance setting
---
core/modules/page_cache/page_cache.module | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/modules/page_cache/page_cache.module b/core/modules/page_cache/page_cache.module
index ff7286199d7e..2879b08e0c7c 100644
--- a/core/modules/page_cache/page_cache.module
+++ b/core/modules/page_cache/page_cache.module
@@ -23,7 +23,7 @@ function page_cache_help($route_name, RouteMatchInterface $route_match) {
$output .= '
' . t('Pages are usually identical for all anonymous users, while they can be personalized for each authenticated user. This is why entire pages can be cached for anonymous users, whereas they will have to be rebuilt for every authenticated user.') . '
';
$output .= '
' . t('To speed up your site for authenticated users, see the Dynamic Page Cache module.', [':dynamic_page_cache-help' => (\Drupal::moduleHandler()->moduleExists('dynamic_page_cache')) ? Url::fromRoute('help.page', ['name' => 'dynamic_page_cache'])->toString() : '#']) . '
';
$output .= '
' . t('Configuring the internal page cache') . '
';
- $output .= '
' . t('On the Performance page, you can configure how long browsers and proxies may cache pages; that setting is also respected by the Internal Page Cache module. There is no other configuration.', [':cache-settings' => \Drupal::url('system.performance_settings')]) . '
';
+ $output .= '
' . t('On the Performance page, you can configure how long browsers and proxies may cache pages based on the Cache-Control header; this setting is ignored by the Internal Page Cache module, which caches pages permanently until invalidation, unless they carry an Expires header. There is no other configuration.', [':cache-settings' => \Drupal::url('system.performance_settings')]) . '
';
$output .= '';
return $output;
From 9ba7824211cc1bc2659cd9d2de815ee9aa62287f Mon Sep 17 00:00:00 2001
From: Lee Rowlands
Date: Mon, 1 Jan 2018 16:16:51 +1000
Subject: [PATCH 073/232] Issue #2626924 by Wim Leers, tedbow, dawehner, frob,
martin107, damiankloip, dagmar, almaudoh, Berdir, larowlan, amateescu:
Include processed text in normalizations: "text" field type's "processed"
computed property should be non-internal and carry cacheability metadata
---
.../filter/src/Element/ProcessedText.php | 8 +-
.../filter/src/FilterProcessResult.php | 2 +-
.../hal/tests/src/Kernel/NormalizeTest.php | 25 ++-
.../BlockContentResourceTestBase.php | 16 ++
.../Comment/CommentResourceTestBase.php | 16 ++
.../EntityTestTextItemNormalizerTest.php | 195 ++++++++++++++++++
.../Term/TermResourceTestBase.php | 19 +-
.../src/Normalizer/TypedDataNormalizer.php | 7 +-
.../src/Kernel/EntitySerializationTest.php | 33 ++-
.../Plugin/Field/FieldType/TextItemBase.php | 3 +-
core/modules/text/src/TextProcessed.php | 59 +++++-
11 files changed, 366 insertions(+), 17 deletions(-)
create mode 100644 core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php
diff --git a/core/modules/filter/src/Element/ProcessedText.php b/core/modules/filter/src/Element/ProcessedText.php
index 5c7a21234ecc..1a3392ba42ed 100644
--- a/core/modules/filter/src/Element/ProcessedText.php
+++ b/core/modules/filter/src/Element/ProcessedText.php
@@ -3,6 +3,7 @@
namespace Drupal\filter\Element;
use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Element\RenderElement;
use Drupal\filter\Entity\FilterFormat;
@@ -69,7 +70,12 @@ public static function preRenderText($element) {
$langcode = $element['#langcode'];
if (!isset($format_id)) {
- $format_id = static::configFactory()->get('filter.settings')->get('fallback_format');
+ $filter_settings = static::configFactory()->get('filter.settings');
+ $format_id = $filter_settings->get('fallback_format');
+ // Ensure 'filter.settings' config's cacheability is respected.
+ CacheableMetadata::createFromRenderArray($element)
+ ->addCacheableDependency($filter_settings)
+ ->applyTo($element);
}
/** @var \Drupal\filter\Entity\FilterFormat $format **/
$format = FilterFormat::load($format_id);
diff --git a/core/modules/filter/src/FilterProcessResult.php b/core/modules/filter/src/FilterProcessResult.php
index 3fa592b6277e..2a8a71bd1f18 100644
--- a/core/modules/filter/src/FilterProcessResult.php
+++ b/core/modules/filter/src/FilterProcessResult.php
@@ -78,7 +78,7 @@ class FilterProcessResult extends BubbleableMetadata {
* @param string $processed_text
* The text as processed by a text filter.
*/
- public function __construct($processed_text) {
+ public function __construct($processed_text = '') {
$this->processedText = $processed_text;
}
diff --git a/core/modules/hal/tests/src/Kernel/NormalizeTest.php b/core/modules/hal/tests/src/Kernel/NormalizeTest.php
index d837b79a02ee..ffea170ba201 100644
--- a/core/modules/hal/tests/src/Kernel/NormalizeTest.php
+++ b/core/modules/hal/tests/src/Kernel/NormalizeTest.php
@@ -5,6 +5,7 @@
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url;
use Drupal\entity_test\Entity\EntityTest;
+use Drupal\filter\Entity\FilterFormat;
/**
* Tests HAL normalization edge cases for EntityResource.
@@ -19,6 +20,27 @@ class NormalizeTest extends NormalizerTestBase {
protected function setUp() {
parent::setUp();
+ FilterFormat::create([
+ 'format' => 'my_text_format',
+ 'name' => 'My Text Format',
+ 'filters' => [
+ 'filter_html' => [
+ 'module' => 'filter',
+ 'status' => TRUE,
+ 'weight' => 10,
+ 'settings' => [
+ 'allowed_html' => '
",
],
],
];
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php
index 5d7329feb96a..576681794041 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/BlockContent/BlockContentResourceTestBase.php
@@ -4,6 +4,7 @@
use Drupal\block_content\Entity\BlockContent;
use Drupal\block_content\Entity\BlockContentType;
+use Drupal\Core\Cache\Cache;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
@@ -131,6 +132,7 @@ protected function getExpectedNormalizedEntity() {
'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
'format' => 'plain_text',
'summary' => NULL,
+ 'processed' => "
The name "llama" was adopted by European settlers from native Peruvians.
\n",
],
],
'status' => [
@@ -180,4 +182,18 @@ protected function getExpectedUnauthorizedAccessCacheability() {
->addCacheTags(['block_content:1']);
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheTags() {
+ return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:filter.format.plain_text']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheContexts() {
+ return Cache::mergeContexts(['url.site'], $this->container->getParameter('renderer.config')['required_cache_contexts']);
+ }
+
}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
index 60b5930e1efd..d3098545cdfe 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Comment/CommentResourceTestBase.php
@@ -5,6 +5,7 @@
use Drupal\comment\Entity\Comment;
use Drupal\comment\Entity\CommentType;
use Drupal\comment\Tests\CommentTestTrait;
+use Drupal\Core\Cache\Cache;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
@@ -197,6 +198,7 @@ protected function getExpectedNormalizedEntity() {
[
'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
'format' => 'plain_text',
+ 'processed' => '
The name "llama" was adopted by European settlers from native Peruvians.
' . "\n",
],
],
];
@@ -248,6 +250,20 @@ protected function getNormalizedPatchEntity() {
return array_diff_key($this->getNormalizedPostEntity(), ['entity_type' => TRUE, 'entity_id' => TRUE, 'field_name' => TRUE]);
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheTags() {
+ return Cache::mergeTags(parent::getExpectedCacheTags(), ['config:filter.format.plain_text']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheContexts() {
+ return Cache::mergeContexts(['languages:language_interface', 'theme'], parent::getExpectedCacheContexts());
+ }
+
/**
* Tests POSTing a comment without critical base fields.
*
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php
new file mode 100644
index 000000000000..cd8654df4d81
--- /dev/null
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityTest/EntityTestTextItemNormalizerTest.php
@@ -0,0 +1,195 @@
+grantPermissionsToTestedRole(['use text format my_text_format']);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedNormalizedEntity() {
+ $expected = parent::getExpectedNormalizedEntity();
+ $expected['field_test_text'] = [
+ [
+ 'value' => 'Cádiz is the oldest continuously inhabited city in Spain and a nice place to spend a Sunday with friends.',
+ 'format' => 'my_text_format',
+ 'processed' => '
Cádiz is the oldest continuously inhabited city in Spain and a nice place to spend a Sunday with friends.
' . "\n" . '
This is a dynamic llama.
',
+ ],
+ ];
+ return $expected;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function createEntity() {
+ $entity = parent::createEntity();
+ if (!FilterFormat::load('my_text_format')) {
+ FilterFormat::create([
+ 'format' => 'my_text_format',
+ 'name' => 'My Text Format',
+ 'filters' => [
+ 'filter_test_assets' => [
+ 'weight' => -1,
+ 'status' => TRUE,
+ ],
+ 'filter_test_cache_tags' => [
+ 'weight' => 0,
+ 'status' => TRUE,
+ ],
+ 'filter_test_cache_contexts' => [
+ 'weight' => 0,
+ 'status' => TRUE,
+ ],
+ 'filter_test_cache_merge' => [
+ 'weight' => 0,
+ 'status' => TRUE,
+ ],
+ 'filter_test_placeholders' => [
+ 'weight' => 1,
+ 'status' => TRUE,
+ ],
+ 'filter_autop' => [
+ 'status' => TRUE,
+ ],
+ ],
+ ])->save();
+ }
+ $entity->field_test_text = [
+ 'value' => 'Cádiz is the oldest continuously inhabited city in Spain and a nice place to spend a Sunday with friends.',
+ 'format' => 'my_text_format',
+ ];
+ $entity->save();
+ return $entity;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getNormalizedPostEntity() {
+ $post_entity = parent::getNormalizedPostEntity();
+ $post_entity['field_test_text'] = [
+ [
+ 'value' => 'Llamas are awesome.',
+ 'format' => 'my_text_format',
+ ],
+ ];
+ return $post_entity;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheTags() {
+ return Cache::mergeTags([
+ // The cache tag set by the processed_text element itself.
+ 'config:filter.format.my_text_format',
+ // The cache tags set by the filter_test_cache_tags filter.
+ 'foo:bar',
+ 'foo:baz',
+ // The cache tags set by the filter_test_cache_merge filter.
+ 'merge:tag'
+ ], parent::getExpectedCacheTags());
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getExpectedCacheContexts() {
+ return Cache::mergeContexts([
+ // The cache context set by the filter_test_cache_contexts filter.
+ 'languages:' . LanguageInterface::TYPE_CONTENT,
+ // The default cache contexts for Renderer.
+ 'languages:' . LanguageInterface::TYPE_INTERFACE,
+ 'theme',
+ // The cache tags set by the filter_test_cache_merge filter.
+ 'user.permissions',
+ ], parent::getExpectedCacheContexts());
+ }
+
+ /**
+ * Tests GETting an entity with the test text field set to a specific format.
+ *
+ * @dataProvider providerTestGetWithFormat
+ */
+ public function testGetWithFormat($text_format_id, array $expected_cache_tags) {
+ FilterFormat::create([
+ 'name' => 'Pablo Piccasso',
+ 'format' => 'pablo',
+ 'langcode' => 'es',
+ 'filters' => [],
+ ])->save();
+
+ // Set TextItemBase field's value for testing, using the given text format.
+ $value = [
+ 'value' => $this->randomString(),
+ ];
+ if ($text_format_id !== FALSE) {
+ $value['format'] = $text_format_id;
+ }
+ $this->entity->set('field_test_text', $value)->save();
+
+ $this->initAuthentication();
+ $url = $this->getEntityResourceUrl();
+ $url->setOption('query', ['_format' => static::$format]);
+ $request_options = $this->getAuthenticationRequestOptions('GET');
+ $this->provisionEntityResource();
+ $this->setUpAuthorization('GET');
+ $response = $this->request('GET', $url, $request_options);
+ $expected_cache_tags = Cache::mergeTags($expected_cache_tags, parent::getExpectedCacheTags());
+ $this->assertSame($expected_cache_tags, explode(' ', $response->getHeader('X-Drupal-Cache-Tags')[0]));
+ }
+
+ public function providerTestGetWithFormat() {
+ return [
+ 'format specified (different from fallback format)' => [
+ 'pablo',
+ ['config:filter.format.pablo'],
+ ],
+ 'format specified (happens to be the same as fallback format)' => [
+ 'plain_text',
+ ['config:filter.format.plain_text'],
+ ],
+ 'no format specified: fallback format used automatically' => [
+ FALSE,
+ ['config:filter.format.plain_text', 'config:filter.settings'],
+ ],
+ ];
+ }
+
+}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
index e0a15813a82f..713c1e5c1bf0 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Term/TermResourceTestBase.php
@@ -2,6 +2,7 @@
namespace Drupal\Tests\rest\Functional\EntityResource\Term;
+use Drupal\Core\Cache\Cache;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\rest\Functional\BcTimestampNormalizerUnixTestTrait;
@@ -79,6 +80,7 @@ protected function createEntity() {
// Create a "Llama" taxonomy term.
$term = Term::create(['vid' => $vocabulary->id()])
->setName('Llama')
+ ->setDescription("It is a little known fact that llamas cannot count higher than seven.")
->setChangedTime(123456789)
->set('path', '/llama');
$term->save();
@@ -109,8 +111,9 @@ protected function getExpectedNormalizedEntity() {
],
'description' => [
[
- 'value' => NULL,
+ 'value' => 'It is a little known fact that llamas cannot count higher than seven.',
'format' => NULL,
+ 'processed' => "
It is a little known fact that llamas cannot count higher than seven.