| <!DOCTYPE html> |
| <meta charset="utf-8"> |
| <title>Toast: action tests</title> |
| |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| |
| <main></main> |
| |
| <script type="module"> |
| import { testActionToast, testToastElement, assertActionButtonOnToast } from './resources/helpers.js'; |
| import { showToast } from 'std:elements/toast'; |
| |
| testActionToast((toast) => { |
| assert_equals(toast.action.textContent, 'action'); |
| }, 'the action element gets properly captured with this.action'); |
| |
| testActionToast((toast) => { |
| toast.innerHTML = `<button slot='action'>new action</button>` |
| assert_equals(toast.action.textContent, 'new action'); |
| }, 'changing the action button changes this.action'); |
| |
| testToastElement((toast) => { |
| assert_equals(toast.action, null); |
| }, 'the action property of a toast without an action is null'); |
| |
| testToastElement((toast) => { |
| toast.innerHTML = `<button slot="action" id="first">first</button> |
| <button slot="action" id="second">second</button>`; |
| |
| assert_equals(toast.action, toast.querySelector('#first')); |
| }, 'toast action returns the first item with the action slot'); |
| |
| test(() => { |
| const toast = showToast('Message', {action: 'action'}); |
| const actionButton = toast.querySelector('button'); |
| |
| assertActionButtonOnToast(actionButton, toast); |
| }, 'passing an action via showToast creates a button'); |
| |
| test(() => { |
| const actionMarkup = '<b>strong text</b>'; |
| const toast = showToast('Message', {action: actionMarkup}); |
| const actionButton = toast.querySelector('button'); |
| |
| assert_equals(actionButton.textContent, actionMarkup); |
| assert_equals(toast.querySelector('b'), null); |
| }, 'passing markup to the action option represents as text'); |
| |
| test(() => { |
| const toast = document.createElement('std-toast'); |
| toast.textContent = 'Message'; |
| toast.show({action: 'action'}); |
| const actionButton = toast.querySelector('button'); |
| |
| assert_equals(actionButton, null); |
| }, 'passing action option to show does not create a button'); |
| |
| test(() => { |
| const toast = showToast('Message', {action: null}); |
| const actionButton = toast.querySelector('button'); |
| |
| assertActionButtonOnToast(actionButton, toast); |
| assert_equals(actionButton.textContent, 'null'); |
| }, 'passing non-string (null) as action option stringifies it and creates an action button'); |
| |
| test(() => { |
| const toast = showToast('Message', {action: false}); |
| const actionButton = toast.querySelector('button'); |
| |
| assertActionButtonOnToast(actionButton, toast); |
| assert_equals(actionButton.textContent, 'false'); |
| }, 'passing non-string (false) as action option stringifies it and creates an action button'); |
| |
| test(() => { |
| const toast = showToast('Message', {action: 0}); |
| const actionButton = toast.querySelector('button'); |
| |
| assertActionButtonOnToast(actionButton, toast); |
| assert_equals(actionButton.textContent, '0'); |
| }, 'passing non-string (0) as action option stringifies it and creates an action button'); |
| |
| test(() => { |
| const toast = showToast('Message', {action: 1}); |
| const actionButton = toast.querySelector('button'); |
| |
| assertActionButtonOnToast(actionButton, toast); |
| assert_equals(actionButton.textContent, '1'); |
| }, 'passing non-string (1) as action option stringifies it and creates an action button'); |
| |
| test(() => { |
| const toast = showToast('Message', {action: {field: 'value'}}); |
| const actionButton = toast.querySelector('button'); |
| |
| assertActionButtonOnToast(actionButton, toast); |
| assert_equals(actionButton.textContent, '[object Object]'); |
| }, 'passing non-string ({field: value}) as action option stringifies it and creates an action button'); |
| |
| test(() => { |
| const toast = showToast('Message', {}); |
| const actionButton = toast.querySelector('button'); |
| |
| assert_equals(actionButton, null); |
| }, 'passing non-string (undefined) as action option does not create an action button'); |
| |
| testToastElement((toast) => { |
| const actionButton = document.createElement('button'); |
| actionButton.textContent = 'action'; |
| toast.action = actionButton; |
| |
| assertActionButtonOnToast(actionButton, toast); |
| }, 'setting the action on an actionless toast inserts the element into the slot'); |
| |
| testActionToast((toast, action) => { |
| const actionButton = document.createElement('button'); |
| actionButton.textContent = 'replacement'; |
| toast.action = actionButton; |
| |
| assert_false(document.contains(action)); |
| assertActionButtonOnToast(actionButton, toast); |
| }, 'resetting the action on an action toast changes the action element'); |
| |
| testToastElement((toast) => { |
| const text = document.createTextNode('some text'); |
| assert_throws_js(TypeError, () => { |
| toast.action = text; |
| }); |
| }, 'setting the action to an invalid type (Text node) throws an error'); |
| |
| testToastElement((toast) => { |
| const text = 'some text'; |
| assert_throws_js(TypeError, () => { |
| toast.action = text; |
| }); |
| }, 'setting the action to an invalid type (string) throws an error'); |
| |
| test(() => { |
| const actionButton = document.createElement('button'); |
| actionButton.textContent = 'action'; |
| const toast = showToast('Message', {action: actionButton}); |
| |
| assertActionButtonOnToast(actionButton, toast); |
| }, 'showToast can take an Element as the action parameter'); |
| |
| testActionToast((toast, action) => { |
| toast.action = null; |
| |
| assert_not_equals(toast.action, action); |
| assert_equals(toast.querySelector('button'), null); |
| }, 'setting toast.action to null removes the action from the toast'); |
| |
| testActionToast((toast, action) => { |
| const wrongAction = document.createElement('button'); |
| wrongAction.textContent = 'wrong'; |
| wrongAction.setAttribute('slot', 'action'); |
| toast.appendChild(wrongAction); |
| |
| const correctAction = document.createElement('button'); |
| correctAction.textContent = 'correct'; |
| toast.action = correctAction; |
| |
| assertActionButtonOnToast(correctAction, toast); |
| }, 'resetting toast.action on a toast with multiple actions slotted sets properly'); |
| |
| test(() => { |
| try { |
| Object.defineProperty(Element, Symbol.hasInstance, { |
| value: () => true, |
| configurable: true |
| }); |
| |
| const fakeElement = {}; |
| const toast = showToast('Message'); |
| assert_throws_js(TypeError, () => toast.action = fakeElement); |
| } finally { |
| delete Element[Symbol.hasInstance]; |
| } |
| }, 'spoofing element instance will not register as element to action setter'); |
| |
| test(() => { |
| const iframe = document.createElement('iframe'); |
| document.body.append(iframe); |
| iframe.contentDocument.body.innerHTML = '<div></div>'; |
| const elementFromAnotherFrame = iframe.contentDocument.querySelector('div'); |
| |
| // Should not throw: |
| const toast = showToast('Message'); |
| toast.action = elementFromAnotherFrame; |
| }, 'element from iframe instance will pass correctly to action without throwing an error'); |
| </script> |