import { haveAttribute, haveLength, haveText, haveValue, haveHtml, html, test } from '../../utils' test('can morph components and preserve Alpine state', [html`
`], ({ get }, reload, window, document) => { let toHtml = document.querySelector('div').outerHTML get('span').should(haveText('bar')) get('button').click() get('span').should(haveText('baz')) get('div').then(([el]) => window.Alpine.morph(el, toHtml)) get('span').should(haveText('baz')) }, ) test('morphing target uses outer Alpine scope', [html`
`], ({ get }, reload, window, document) => { let toHtml = document.querySelector('div').outerHTML get('span').should(haveText('bar')) get('button').click() get('span').should(haveText('baz')) get('div').then(([el]) => window.Alpine.morph(el, toHtml)) get('span').should(haveText('baz')) }, ) test('can morph with HTML change and preserve Alpine state', [html`
`], ({ get }, reload, window, document) => { let toHtml = document.querySelector('div').outerHTML.replace('Change Foo', 'Changed Foo') get('span').should(haveText('bar')) get('button').click() get('span').should(haveText('baz')) get('button').should(haveText('Change Foo')) get('div').then(([el]) => window.Alpine.morph(el, toHtml)) get('span').should(haveText('baz')) get('button').should(haveText('Changed Foo')) }, ) test('morphing an element with multiple nested Alpine components preserves scope', [html`
Change Bob

`], ({ get }, reload, window, document) => { let toHtml = document.querySelector('div').outerHTML get('span').should(haveText('bar')) get('h1').should(haveText('lob')) get('button').click() get('a').click() get('span').should(haveText('baz')) get('h1').should(haveText('law')) get('div').then(([el]) => window.Alpine.morph(el, toHtml)) get('span').should(haveText('baz')) get('h1').should(haveText('law')) }, ) test('can morph teleports', [html`
`], ({ get }, reload, window, document) => { let toHtml = html`
` get('h1').should(haveText('1')) get('h2').should(haveText('hey')) get('button').click() get('h1').should(haveText('2')) get('h2').should(haveText('hey')) get('div#a').then(([el]) => window.Alpine.morph(el, toHtml)) get('h1').should(haveText('2')) get('h2').should(haveText('there')) }, ) test('can morph teleports in different places with IDs', [html`
moving placeholder
`], ({ get }, reload, window, document) => { let toHtml = html`
moving placeholder
` get('h1').should(haveText('1')) get('h2').should(haveText('hey')) get('button').click() get('h1').should(haveText('2')) get('h2').should(haveText('hey')) get('div#a').then(([el]) => window.Alpine.morph(el, toHtml)) get('h1').should(haveText('2')) get('h2').should(haveText('there')) }, ) test('can morph', [html` `], ({ get }, reload, window, document) => { let toHtml = html` ` get('input').type('foo') get('ul').then(([el]) => window.Alpine.morph(el, toHtml)) get('li').should(haveLength(2)) get('li:nth-of-type(1)').should(haveText('bar')) get('li:nth-of-type(2)').should(haveText('foo')) get('li:nth-of-type(1) input').should(haveValue('foo')) get('li:nth-of-type(2) input').should(haveValue('')) }, ) test('can morph using lookahead', [html` `], ({ get }, reload, window, document) => { let toHtml = html` ` get('input').type('foo') get('ul').then(([el]) => window.Alpine.morph(el, toHtml, {lookahead: true})) get('li').should(haveLength(3)) get('li:nth-of-type(1)').should(haveText('bar')) get('li:nth-of-type(2)').should(haveText('baz')) get('li:nth-of-type(3)').should(haveText('foo')) get('li:nth-of-type(1) input').should(haveValue('')) get('li:nth-of-type(2) input').should(haveValue('')) get('li:nth-of-type(3) input').should(haveValue('foo')) }, ) test('can morph using keys', [html` `], ({ get }, reload, window, document) => { let toHtml = html` ` get('input').type('foo') get('ul').then(([el]) => window.Alpine.morph(el, toHtml)) get('li').should(haveLength(3)) get('li:nth-of-type(1)').should(haveText('bar')) get('li:nth-of-type(2)').should(haveText('baz')) get('li:nth-of-type(3)').should(haveText('foo')) get('li:nth-of-type(1) input').should(haveValue('')) get('li:nth-of-type(2) input').should(haveValue('')) get('li:nth-of-type(3) input').should(haveValue('foo')) }, ) test('can morph using a custom key function', [html` `], ({ get }, reload, window, document) => { let toHtml = html` ` get('input').type('foo') get('ul').then(([el]) => window.Alpine.morph(el, toHtml, {key(el) {return el.dataset.key}})) get('li').should(haveLength(3)) get('li:nth-of-type(1)').should(haveText('bar')) get('li:nth-of-type(2)').should(haveText('baz')) get('li:nth-of-type(3)').should(haveText('foo')) get('li:nth-of-type(1) input').should(haveValue('')) get('li:nth-of-type(2) input').should(haveValue('')) get('li:nth-of-type(3) input').should(haveValue('foo')) }, ) test('can morph using keys with existing key to be moved up', [html` `], ({ get }, reload, window, document) => { let toHtml = html` ` get('li:nth-of-type(1) input').type('foo') get('li:nth-of-type(3) input').type('baz') get('ul').then(([el]) => window.Alpine.morph(el, toHtml)) get('li').should(haveLength(2)) get('li:nth-of-type(1)').should(haveText('foo')) get('li:nth-of-type(2)').should(haveText('baz')) get('li:nth-of-type(1) input').should(haveValue('foo')) get('li:nth-of-type(2) input').should(haveValue('baz')) }, ) test('can morph text nodes', [html`

Foo
Bar

`], ({ get }, reload, window, document) => { let toHtml = html`

Foo
Baz

` get('h2').then(([el]) => window.Alpine.morph(el, toHtml)) get('h2').should(haveHtml('Foo
Baz')) }, ) test('can morph with added element before and siblings are different', [html` `], ({ get }, reload, window, document) => { let toHtml = html` ` get('button').then(([el]) => window.Alpine.morph(el, toHtml)) get('button > div').should(haveLength(2)) get('button > div:nth-of-type(1)').should(haveText('first')) get('button > div:nth-of-type(2)').should(haveHtml(`
second
third
`)) }, ) test('can morph using different keys', [html` `], ({ get }, reload, window, document) => { let toHtml = html` ` get('ul').then(([el]) => window.Alpine.morph(el, toHtml)) get('li').should(haveLength(1)) get('li:nth-of-type(1)').should(haveText('bar')) get('li:nth-of-type(1)').should(haveAttribute('key', '2')) }, ) test('can morph elements with dynamic ids', [html` `], ({ get }, reload, window, document) => { let toHtml = html` ` get('input').type('foo') get('ul').then(([el]) => window.Alpine.morph(el, toHtml, { key(el) { return el.id } })) get('li:nth-of-type(1) input').should(haveValue('foo')) }, ) test('can morph different inline nodes', [html`
Hello World
`], ({ get }, reload, window, document) => { let toHtml = html`
Welcome Person!
` get('div').then(([el]) => window.Alpine.morph(el, toHtml)) get('div').should(haveHtml('\n Welcome Person!\n ')) }, ) test('can morph multiple nodes', [html`

`], ({ get }, reload, window, document) => { let paragraphs = document.querySelectorAll('p') window.Alpine.morph(paragraphs[0], '

12 1 `], ({ get }, reload, window, document) => { let tr = document.querySelector('tr') window.Alpine.morph(tr, '2') get('td').should(haveText('2')) }, ) test('can morph with conditional markers', [html`

foo
bar
`], ({ get }, reload, window, document) => { let toHtml = html`
foo
baz
bar
` get('div:nth-of-type(1) input').type('foo') get('div:nth-of-type(2) input').type('bar') get('main').then(([el]) => window.Alpine.morph(el, toHtml)) get('div:nth-of-type(1) input').should(haveValue('foo')) get('div:nth-of-type(2) input').should(haveValue('')) get('div:nth-of-type(3) input').should(haveValue('bar')) }, ) test('can morph with flat-nested conditional markers', [html`
foo
bar
`], ({ get }, reload, window, document) => { let toHtml = html`
foo
baz
bar
` get('div:nth-of-type(1) input').type('foo') get('div:nth-of-type(2) input').type('bar') get('main').then(([el]) => window.Alpine.morph(el, toHtml)) get('div:nth-of-type(1) input').should(haveValue('foo')) get('div:nth-of-type(2) input').should(haveValue('')) get('div:nth-of-type(3) input').should(haveValue('bar')) }, ) // '@event' handlers cannot be assigned directly on the element without Alpine's internl monkey patching... test('can morph @event handlers', [ html`
`], ({ get, click }, reload, window, document) => { let toHtml = html` `; get('button').should(haveText('bar')); get('button').then(([el]) => window.Alpine.morph(el, toHtml)); get('button').click(); get('button').should(haveText('buzz')); } ); test('can morph menu', [html`
`], ({ get }, reload, window, document) => { let toHtml = html`
` get('[data-trigger]').should(haveText('ready')); get('button[data-trigger').click() get('input').type('foo') get('main').then(([el]) => window.Alpine.morph(el, toHtml, { key(el) { return el.id } })) get('input').should(haveValue('foo')) }, ) test('can morph teleports with x-for', [html`
`], ({ get }, reload, window, document) => { let toHtml = html`
` get('button').should(haveText('1')); get('button').click() get('button').should(haveText('2')); get('main').then(([el]) => window.Alpine.morph(el, toHtml)); get('button').should(haveText('2')); get('button').click() get('button').should(haveText('3')); }, ) test('can morph teleports with root-level state', [html`
`], ({ get }, reload, window, document) => { let toHtml = html`
` get('h1').should(haveText('bar')); get('main').then(([el]) => window.Alpine.morph(el, toHtml)); get('h1').should(haveText('bar')); }, ) test('can use morphBetween with comment markers', [html`

Header

Original content

Footer

`], ({ get }, reload, window, document) => { // Find the comment markers let startMarker, endMarker; const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_COMMENT, null, false ); let node; while (node = walker.nextNode()) { if (node.textContent === 'start') startMarker = node; if (node.textContent === 'end') endMarker = node; } window.Alpine.morphBetween(startMarker, endMarker, '

New content

More content

') get('h2:nth-of-type(1)').should(haveText('Header')) get('h2:nth-of-type(2)').should(haveText('Footer')) get('p').should(haveLength(2)) get('p:nth-of-type(1)').should(haveText('New content')) get('p:nth-of-type(2)').should(haveText('More content')) }, ) test('morphBetween preserves Alpine state', [html`

Static content
`], ({ get }, reload, window, document) => { // Find markers let startMarker, endMarker; const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_COMMENT, null, false ); let node; while (node = walker.nextNode()) { if (node.textContent === 'morph-start') startMarker = node; if (node.textContent === 'morph-end') endMarker = node; } get('p').should(haveText('1')) get('button').click() get('p').should(haveText('2')) window.Alpine.morphBetween(startMarker, endMarker, `

New element
`) get('p').should(haveText('2')) get('article').should(haveText('New element')) get('input').should(haveValue('2')) get('input').clear().type('5') get('p').should(haveText('5')) }, ) test('morphBetween with keyed elements', [html` `], ({ get }, reload, window, document) => { // Find markers let startMarker, endMarker; const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_COMMENT, null, false ); let node; while (node = walker.nextNode()) { if (node.textContent === 'items-start') startMarker = node; if (node.textContent === 'items-end') endMarker = node; } get('li:nth-of-type(1) input').type('first') get('li:nth-of-type(2) input').type('second') get('ul').then(([el]) => window.Alpine.morphBetween(startMarker, endMarker, `
  • baz
  • foo
  • bar
  • `, { key(el) { return el.getAttribute('key') } })) get('li').should(haveLength(3)) get('li:nth-of-type(1)').should(haveText('baz')) get('li:nth-of-type(2)').should(haveText('foo')) get('li:nth-of-type(3)').should(haveText('bar')) // Need to verify by the key attribute since the elements have been reordered get('li[key="1"] input').should(haveValue('first')) get('li[key="2"] input').should(haveValue('second')) get('li[key="3"] input').should(haveValue('')) }, ) test('morphBetween with custom key function', [html`
    Item A
    Item B
    `], ({ get }, reload, window, document) => { // Find markers let startMarker, endMarker; const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_COMMENT, null, false ); let node; while (node = walker.nextNode()) { if (node.textContent === 'start') startMarker = node; if (node.textContent === 'end') endMarker = node; } get('div[data-id="a"] input').type('aaa') get('div[data-id="b"] input').type('bbb') window.Alpine.morphBetween(startMarker, endMarker, `
    Item B Updated
    Item C
    Item A Updated
    `, { key(el) { return el.dataset.id } }) get('div[data-id]').should(haveLength(3)) get('div[data-id="b"]').should(haveText('Item B Updated')) get('div[data-id="a"]').should(haveText('Item A Updated')) get('div[data-id="a"] input').should(haveValue('aaa')) get('div[data-id="b"] input').should(haveValue('bbb')) get('div[data-id="c"] input').should(haveValue('')) }, ) test('morphBetween with hooks', [html`

    Old paragraph

    Old span
    `], ({ get }, reload, window, document) => { // Find markers let startMarker, endMarker; const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_COMMENT, null, false ); let node; while (node = walker.nextNode()) { if (node.textContent === 'region-start') startMarker = node; if (node.textContent === 'region-end') endMarker = node; } let removedElements = [] let addedElements = [] window.Alpine.morphBetween(startMarker, endMarker, `

    New paragraph

    New article
    `, { removing(el) { if (el.nodeType === 1) removedElements.push(el.tagName) }, adding(el) { if (el.nodeType === 1) addedElements.push(el.tagName) } }) get('p').should(haveText('New paragraph')) get('article').should(haveText('New article')) // Check hooks were called cy.wrap(removedElements).should('deep.equal', ['SPAN']) cy.wrap(addedElements).should('deep.equal', ['ARTICLE']) }, ) test('morphBetween with empty content', [html`

    Title

    Content 1

    Content 2

    End

    `], ({ get }, reload, window, document) => { // Find markers let startMarker, endMarker; const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_COMMENT, null, false ); let node; while (node = walker.nextNode()) { if (node.textContent === 'content-start') startMarker = node; if (node.textContent === 'content-end') endMarker = node; } window.Alpine.morphBetween(startMarker, endMarker, '') get('h3').should(haveLength(2)) get('p').should(haveLength(0)) // Verify markers are still there let found = false; const walker2 = document.createTreeWalker( document.body, NodeFilter.SHOW_COMMENT, null, false ); while (node = walker2.nextNode()) { if (node.textContent === 'content-start' || node.textContent === 'content-end') { found = true; } } cy.wrap(found).should('be.true') }, ) test('morphBetween with nested Alpine components', [html`
    `], ({ get }, reload, window, document) => { // Find markers let startMarker, endMarker; const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_COMMENT, null, false ); let node; while (node = walker.nextNode()) { if (node.textContent === 'nested-start') startMarker = node; if (node.textContent === 'nested-end') endMarker = node; } get('span:nth-of-type(1)').should(haveText('foo')) get('span:nth-of-type(2)').should(haveText('bar')) get('input').clear().type('baz') get('span:nth-of-type(2)').should(haveText('baz')) window.Alpine.morphBetween(startMarker, endMarker, `

    New heading

    `) get('h4').should(haveText('New heading')) get('span:nth-of-type(1)').should(haveText('foo')) get('span:nth-of-type(2)').should(haveText('baz')) get('input').should(haveValue('baz')) }, ) test('morphBetween with conditional blocks', [html`
    conditional content

    regular content

    `], ({ get }, reload, window, document) => { // Find markers let startMarker, endMarker; const walker = document.createTreeWalker( document.body, NodeFilter.SHOW_COMMENT, null, false ); let node; while (node = walker.nextNode()) { if (node.textContent === 'section-start') startMarker = node; if (node.textContent === 'section-end') endMarker = node; } get('div input').type('div-value') get('p input').type('p-value') window.Alpine.morphBetween(startMarker, endMarker, `
    conditional content
    new conditional

    regular content

    `) get('div input').should(haveValue('div-value')) get('span input').should(haveValue('')) get('p input').should(haveValue('p-value')) }, ) test('can ignore region between comment markers using skipUntil', [html` `], ({ get }, reload, window, document) => { // Generate "to" html without the items between Slot markers let toHtml = html` ` // The original list should have 3 li's get('li').should(haveLength(3)) get('li:nth-of-type(1)').should(haveText('foo')) get('li:nth-of-type(2)').should(haveText('bar')) get('li:nth-of-type(3)').should(haveText('baz')) // Run morph with custom updating hook that calls skipUntil let isStart = node => node && node.nodeType === 8 && node.textContent.trim() === '[Slot]' let isEnd = node => node && node.nodeType === 8 && node.textContent.trim() === '[EndSlot]' get('ul').then(([el]) => window.Alpine.morph(el, toHtml, { updating(from, to, childrenOnly, skip, skipChildren, skipUntil) { if (isStart(from) && isStart(to)) { skipUntil(node => isEnd(node)) } }, })) // After morph, the list should still contain the items inside the slot get('li').should(haveLength(3)) get('li:nth-of-type(2)').should(haveText('bar')) get('li:nth-of-type(3)').should(haveText('baz')) }, )