|
1 | 1 | import { assert, describe, it } from '@netflix/x-test/x-test.js'; |
2 | 2 | import XElement from '../x-element.js'; |
3 | 3 |
|
4 | | -// Long-term interface. |
5 | 4 | const { render, html } = XElement.templateEngine; |
6 | 5 |
|
7 | | -// Deprecated interface. We will eventually delete these. |
8 | | -const { ifDefined, repeat } = XElement.templateEngine; |
9 | | - |
10 | | -// Overwrite console warn for testing so we don’t get spammed with our own |
11 | | -// deprecation warnings. |
12 | | -const seen = new Set(); |
13 | | -const warn = console.warn; // eslint-disable-line no-console |
14 | | -const localMessages = [ |
15 | | - 'Deprecated "ifDefined" from default templating engine interface.', |
16 | | - 'Deprecated "repeat" from default templating engine interface.', |
17 | | -]; |
18 | | -console.warn = (...args) => { // eslint-disable-line no-console |
19 | | - if (!localMessages.includes(args[0]?.message)) { |
20 | | - warn(...args); |
21 | | - } else { |
22 | | - seen.add(args[0].message); |
23 | | - } |
24 | | -}; |
25 | | - |
26 | 6 | // Simple helper for asserting thrown messages. |
27 | 7 | const assertThrows = (callback, expectedMessage, options) => { |
28 | 8 | let thrown = false; |
@@ -1169,346 +1149,3 @@ describe('value issues', () => { |
1169 | 1149 | }); |
1170 | 1150 | }); |
1171 | 1151 | }); |
1172 | | - |
1173 | | -describe('html updaters', () => { |
1174 | | - it('ifDefined', () => { |
1175 | | - const getTemplate = ({ maybe }) => { |
1176 | | - return html`<div id="target" maybe="${ifDefined(maybe)}"></div>`; |
1177 | | - }; |
1178 | | - const container = document.createElement('div'); |
1179 | | - document.body.append(container); |
1180 | | - render(container, getTemplate({ maybe: 'yes' })); |
1181 | | - assert(container.querySelector('#target').getAttribute('maybe') === 'yes'); |
1182 | | - render(container, getTemplate({ maybe: undefined })); |
1183 | | - assert(container.querySelector('#target').getAttribute('maybe') === null); |
1184 | | - render(container, getTemplate({ maybe: false })); |
1185 | | - assert(container.querySelector('#target').getAttribute('maybe') === 'false'); |
1186 | | - render(container, getTemplate({ maybe: null })); |
1187 | | - assert(container.querySelector('#target').getAttribute('maybe') === null); |
1188 | | - container.remove(); |
1189 | | - }); |
1190 | | - |
1191 | | - // This is mainly for backwards compat, TBD if we deprecate or not. |
1192 | | - it('repeat works when called with all arguments', () => { |
1193 | | - const getTemplate = ({ items }) => { |
1194 | | - return html` |
1195 | | - <div id="target"> |
1196 | | - ${repeat(items, item => item.id, item => { |
1197 | | - return html`<div id="${item.id}" class="item">${item.id}</div>`; |
1198 | | - })} |
1199 | | - </div> |
1200 | | - `; |
1201 | | - }; |
1202 | | - const container = document.createElement('div'); |
1203 | | - document.body.append(container); |
1204 | | - render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); |
1205 | | - const foo = container.querySelector('#foo'); |
1206 | | - const bar = container.querySelector('#bar'); |
1207 | | - const baz = container.querySelector('#baz'); |
1208 | | - assert(container.querySelector('#target').childElementCount === 3); |
1209 | | - assert(!!foo); |
1210 | | - assert(!!bar); |
1211 | | - assert(!!baz); |
1212 | | - assert(container.querySelector('#target').children[0] === foo); |
1213 | | - assert(container.querySelector('#target').children[1] === bar); |
1214 | | - assert(container.querySelector('#target').children[2] === baz); |
1215 | | - render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); |
1216 | | - assert(container.querySelector('#target').childElementCount === 3); |
1217 | | - assert(container.querySelector('#target').children[0] === foo); |
1218 | | - assert(container.querySelector('#target').children[1] === bar); |
1219 | | - assert(container.querySelector('#target').children[2] === baz); |
1220 | | - render(container, getTemplate({ items: [{ id: 'baz' }, { id: 'foo' }, { id: 'bar'}] })); |
1221 | | - assert(container.querySelector('#target').childElementCount === 3); |
1222 | | - assert(container.querySelector('#target').children[0] === baz); |
1223 | | - assert(container.querySelector('#target').children[1] === foo); |
1224 | | - assert(container.querySelector('#target').children[2] === bar); |
1225 | | - render(container, getTemplate({ items: [{ id: 'bar'}, { id: 'baz' }, { id: 'foo' }] })); |
1226 | | - assert(container.querySelector('#target').childElementCount === 3); |
1227 | | - assert(container.querySelector('#target').children[0] === bar); |
1228 | | - assert(container.querySelector('#target').children[1] === baz); |
1229 | | - assert(container.querySelector('#target').children[2] === foo); |
1230 | | - render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); |
1231 | | - assert(container.querySelector('#target').childElementCount === 3); |
1232 | | - assert(container.querySelector('#target').children[0] === foo); |
1233 | | - assert(container.querySelector('#target').children[1] === bar); |
1234 | | - assert(container.querySelector('#target').children[2] === baz); |
1235 | | - render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}] })); |
1236 | | - assert(container.querySelector('#target').childElementCount === 2); |
1237 | | - assert(container.querySelector('#target').children[0] === foo); |
1238 | | - assert(container.querySelector('#target').children[1] === bar); |
1239 | | - render(container, getTemplate({ items: [{ id: 'foo' }] })); |
1240 | | - assert(container.querySelector('#target').childElementCount === 1); |
1241 | | - assert(container.querySelector('#target').children[0] === foo); |
1242 | | - render(container, getTemplate({ items: [] })); |
1243 | | - assert(container.querySelector('#target').childElementCount === 0); |
1244 | | - render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); |
1245 | | - assert(container.querySelector('#target').childElementCount === 3); |
1246 | | - assert(container.querySelector('#target').children[0] !== foo); |
1247 | | - assert(container.querySelector('#target').children[1] !== bar); |
1248 | | - assert(container.querySelector('#target').children[2] !== baz); |
1249 | | - container.remove(); |
1250 | | - }); |
1251 | | - |
1252 | | - it('repeat works when called with omitted lookup', () => { |
1253 | | - const getTemplate = ({ items }) => { |
1254 | | - return html` |
1255 | | - <div id="target"> |
1256 | | - ${repeat(items, item => { |
1257 | | - return html`<div id="${item.id}" class="item">${item.id}</div>`; |
1258 | | - })} |
1259 | | - </div> |
1260 | | - `; |
1261 | | - }; |
1262 | | - const container = document.createElement('div'); |
1263 | | - document.body.append(container); |
1264 | | - render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); |
1265 | | - const foo = container.querySelector('#foo'); |
1266 | | - const bar = container.querySelector('#bar'); |
1267 | | - const baz = container.querySelector('#baz'); |
1268 | | - assert(container.querySelector('#target').childElementCount === 3); |
1269 | | - assert(!!foo); |
1270 | | - assert(!!bar); |
1271 | | - assert(!!baz); |
1272 | | - assert(container.querySelector('#target').children[0] === foo); |
1273 | | - assert(container.querySelector('#target').children[1] === bar); |
1274 | | - assert(container.querySelector('#target').children[2] === baz); |
1275 | | - render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); |
1276 | | - assert(container.querySelector('#target').childElementCount === 3); |
1277 | | - assert(container.querySelector('#target').children[0] === foo); |
1278 | | - assert(container.querySelector('#target').children[1] === bar); |
1279 | | - assert(container.querySelector('#target').children[2] === baz); |
1280 | | - |
1281 | | - // Because "lookup" is omitted, we don't expect DOM nodes to remain after a shift. |
1282 | | - render(container, getTemplate({ items: [{ id: 'baz' }, { id: 'foo' }, { id: 'bar'}] })); |
1283 | | - assert(container.querySelector('#target').childElementCount === 3); |
1284 | | - assert(container.querySelector('#target').children[0] !== baz); |
1285 | | - assert(container.querySelector('#target').children[1] !== foo); |
1286 | | - assert(container.querySelector('#target').children[2] !== bar); |
1287 | | - container.remove(); |
1288 | | - }); |
1289 | | - |
1290 | | - it('repeat re-runs each time', () => { |
1291 | | - const getTemplate = ({ items, lookup }) => { |
1292 | | - return html` |
1293 | | - <div> |
1294 | | - <ul id="target"> |
1295 | | - ${repeat(items, item => item.id, item => { |
1296 | | - return html`<li id="${item.id}">${lookup?.[item.id]}</li>`; |
1297 | | - })} |
1298 | | - </ul> |
1299 | | - </div> |
1300 | | - `; |
1301 | | - }; |
1302 | | - const container = document.createElement('div'); |
1303 | | - document.body.append(container); |
1304 | | - const items = [{ id: 'a' }, { id: 'b'}, { id: 'c' }]; |
1305 | | - let lookup = { a: 'foo', b: 'bar', c: 'baz' }; |
1306 | | - render(container, getTemplate({ items, lookup })); |
1307 | | - assert(container.querySelector('#target').childElementCount === 3); |
1308 | | - assert(container.querySelector('#a').textContent === 'foo'); |
1309 | | - assert(container.querySelector('#b').textContent === 'bar'); |
1310 | | - assert(container.querySelector('#c').textContent === 'baz'); |
1311 | | - lookup = { a: 'fizzle', b: 'bop', c: 'fuzz' }; |
1312 | | - render(container, getTemplate({ items, lookup })); |
1313 | | - assert(container.querySelector('#target').childElementCount === 3); |
1314 | | - assert(container.querySelector('#a').textContent === 'fizzle'); |
1315 | | - assert(container.querySelector('#b').textContent === 'bop'); |
1316 | | - assert(container.querySelector('#c').textContent === 'fuzz'); |
1317 | | - container.remove(); |
1318 | | - }); |
1319 | | - |
1320 | | - it('repeat callbacks are provided args from underlying “.map” call', () => { |
1321 | | - // The identify callback is written in a bizarre way to test that all the |
1322 | | - // expected function arguments are actually passed in. If they weren’t you |
1323 | | - // would get duplicated key errors or undefined key errors. |
1324 | | - const getTemplate = ({ items }) => { |
1325 | | - return html` |
1326 | | - <div id="target"> |
1327 | | - ${repeat(items, (_, index, array) => array?.[index]?.id, (_, index, array) => { |
1328 | | - return html`<div id="${array?.[index]?.id}" class="item">${array?.[index]?.id}</div>`; |
1329 | | - })} |
1330 | | - </div> |
1331 | | - `; |
1332 | | - }; |
1333 | | - const container = document.createElement('div'); |
1334 | | - document.body.append(container); |
1335 | | - render(container, getTemplate({ items: [{ id: 'foo' }, { id: 'bar'}, { id: 'baz' }] })); |
1336 | | - const foo = container.querySelector('#foo'); |
1337 | | - const bar = container.querySelector('#bar'); |
1338 | | - const baz = container.querySelector('#baz'); |
1339 | | - assert(container.querySelector('#target').childElementCount === 3); |
1340 | | - assert(!!foo); |
1341 | | - assert(!!bar); |
1342 | | - assert(!!baz); |
1343 | | - assert(container.querySelector('#target').children[0] === foo); |
1344 | | - assert(container.querySelector('#target').children[1] === bar); |
1345 | | - assert(container.querySelector('#target').children[2] === baz); |
1346 | | - assert(foo.textContent === 'foo'); |
1347 | | - assert(bar.textContent === 'bar'); |
1348 | | - assert(baz.textContent === 'baz'); |
1349 | | - container.remove(); |
1350 | | - }); |
1351 | | -}); |
1352 | | - |
1353 | | -describe('updater errors', () => { |
1354 | | - describe('ifDefined', () => { |
1355 | | - it('throws if used on a "boolean"', () => { |
1356 | | - const expected = 'The ifDefined update must be used on an attribute, not on a boolean attribute.'; |
1357 | | - const getTemplate = ({ maybe }) => { |
1358 | | - return html`<div id="target" ?maybe="${ifDefined(maybe)}"></div>`; |
1359 | | - }; |
1360 | | - const container = document.createElement('div'); |
1361 | | - document.body.append(container); |
1362 | | - let actual; |
1363 | | - try { |
1364 | | - render(container, getTemplate({ maybe: 'yes' })); |
1365 | | - } catch (error) { |
1366 | | - actual = error.message; |
1367 | | - } |
1368 | | - assert(!!actual, 'No error was thrown.'); |
1369 | | - assert(actual === expected, actual); |
1370 | | - container.remove(); |
1371 | | - }); |
1372 | | - |
1373 | | - it('throws if used on a "defined"', () => { |
1374 | | - const expected = 'The ifDefined update must be used on an attribute, not on a defined attribute.'; |
1375 | | - const getTemplate = ({ maybe }) => { |
1376 | | - return html`<div id="target" ??maybe="${ifDefined(maybe)}"></div>`; |
1377 | | - }; |
1378 | | - const container = document.createElement('div'); |
1379 | | - document.body.append(container); |
1380 | | - let actual; |
1381 | | - try { |
1382 | | - render(container, getTemplate({ maybe: 'yes' })); |
1383 | | - } catch (error) { |
1384 | | - actual = error.message; |
1385 | | - } |
1386 | | - assert(!!actual, 'No error was thrown.'); |
1387 | | - assert(actual === expected, actual); |
1388 | | - container.remove(); |
1389 | | - }); |
1390 | | - |
1391 | | - it('throws if used on a "property"', () => { |
1392 | | - const expected = 'The ifDefined update must be used on an attribute, not on a property.'; |
1393 | | - const getTemplate = ({ maybe }) => { |
1394 | | - return html`<div id="target" .maybe="${ifDefined(maybe)}"></div>`; |
1395 | | - }; |
1396 | | - const container = document.createElement('div'); |
1397 | | - document.body.append(container); |
1398 | | - let actual; |
1399 | | - try { |
1400 | | - render(container, getTemplate({ maybe: 'yes' })); |
1401 | | - } catch (error) { |
1402 | | - actual = error.message; |
1403 | | - } |
1404 | | - assert(!!actual, 'No error was thrown.'); |
1405 | | - assert(actual === expected, actual); |
1406 | | - container.remove(); |
1407 | | - }); |
1408 | | - }); |
1409 | | - |
1410 | | - describe('repeat', () => { |
1411 | | - it('throws if callback is not a function (1)', () => { |
1412 | | - const expected = 'Unexpected repeat identify "undefined" provided, expected a function.'; |
1413 | | - const getTemplate = ({ maybe }) => { |
1414 | | - return html`<div id="target" maybe="${repeat(maybe)}"></div>`; |
1415 | | - }; |
1416 | | - const container = document.createElement('div'); |
1417 | | - document.body.append(container); |
1418 | | - let actual; |
1419 | | - try { |
1420 | | - render(container, getTemplate({ maybe: ['yes'] })); |
1421 | | - } catch (error) { |
1422 | | - actual = error.message; |
1423 | | - } |
1424 | | - assert(!!actual, 'No error was thrown.'); |
1425 | | - assert(actual === expected, actual); |
1426 | | - container.remove(); |
1427 | | - }); |
1428 | | - |
1429 | | - it('throws if callback is not a function (2)', () => { |
1430 | | - const expected = 'Unexpected repeat callback "5" provided, expected a function.'; |
1431 | | - const getTemplate = ({ maybe }) => { |
1432 | | - return html`<div id="target" maybe="${repeat(maybe, 5)}"></div>`; |
1433 | | - }; |
1434 | | - const container = document.createElement('div'); |
1435 | | - document.body.append(container); |
1436 | | - let actual; |
1437 | | - try { |
1438 | | - render(container, getTemplate({ maybe: ['yes'] })); |
1439 | | - } catch (error) { |
1440 | | - actual = error.message; |
1441 | | - } |
1442 | | - assert(!!actual, 'No error was thrown.'); |
1443 | | - assert(actual === expected, actual); |
1444 | | - container.remove(); |
1445 | | - }); |
1446 | | - |
1447 | | - it('throws for non-array value', () => { |
1448 | | - const getTemplate = ({ array }) => { |
1449 | | - return html` |
1450 | | - <div id="target"> |
1451 | | - ${repeat(array, () => {}, () => html``)} |
1452 | | - </div> |
1453 | | - `; |
1454 | | - }; |
1455 | | - const container = document.createElement('div'); |
1456 | | - document.body.append(container); |
1457 | | - let error; |
1458 | | - try { |
1459 | | - render(container, getTemplate({ array: 5 })); |
1460 | | - } catch (e) { |
1461 | | - error = e; |
1462 | | - } |
1463 | | - assert(error?.message === 'Unexpected repeat items "5" provided, expected an array.', error?.message); |
1464 | | - container.remove(); |
1465 | | - }); |
1466 | | - |
1467 | | - it('throws for non-template callback value', () => { |
1468 | | - const getTemplate = ({ array }) => { |
1469 | | - return html` |
1470 | | - <div id="target"> |
1471 | | - ${repeat(array, item => item.id, item => item.value ? html`<div>${item.value}</div>` : null)} |
1472 | | - </div> |
1473 | | - `; |
1474 | | - }; |
1475 | | - const container = document.createElement('div'); |
1476 | | - document.body.append(container); |
1477 | | - let error; |
1478 | | - try { |
1479 | | - render(container, getTemplate({ array: [{ id: 'foo', value: null }] })); |
1480 | | - } catch (e) { |
1481 | | - error = e; |
1482 | | - } |
1483 | | - assert(error?.message === 'Unexpected non-template value found in map entry at 0 "null".', error?.message); |
1484 | | - container.remove(); |
1485 | | - }); |
1486 | | - |
1487 | | - it('throws for non-template callback value (on re-render)', () => { |
1488 | | - const getTemplate = ({ array }) => { |
1489 | | - return html` |
1490 | | - <div id="target"> |
1491 | | - ${repeat(array, item => item.id, item => item.value ? html`<div>${item.value}</div>` : null)} |
1492 | | - </div> |
1493 | | - `; |
1494 | | - }; |
1495 | | - const container = document.createElement('div'); |
1496 | | - document.body.append(container); |
1497 | | - render(container, getTemplate({ array: [{ id: 'foo', value: 'oh hai' }] })); |
1498 | | - let error; |
1499 | | - try { |
1500 | | - render(container, getTemplate({ array: [{ id: 'foo', value: null }] })); |
1501 | | - } catch (e) { |
1502 | | - error = e; |
1503 | | - } |
1504 | | - assert(error?.message === 'Unexpected non-template value found in map entry at 0 "null".', error?.message); |
1505 | | - container.remove(); |
1506 | | - }); |
1507 | | - }); |
1508 | | -}); |
1509 | | - |
1510 | | -it('confirm that deprecation warnings are still necessary', () => { |
1511 | | - for (const message of localMessages) { |
1512 | | - assert(seen.has(message), `Unused deprecation warning: ${message}`); |
1513 | | - } |
1514 | | -}); |
0 commit comments